mirror of
https://github.com/Luchexx/wacrm.git
synced 2026-11-02 17:29:45 +00:00
Self-review of the light/dark sweep surfaced neutral colors that lived in raw inline-style / <style jsx> / gradient values (so the className sweep correctly skipped them) and a few contrast/affordance nits: - reply-quote: adapt to the outbound (primary) bubble via onPrimary so the quote/author stay legible on the violet fill in light mode; wire it from message-bubble. - dashboard SVG chrome -> tokens: conversations-chart gridline/crosshair, pipeline-donut background ring, automation-builder dot-grid gradient. - members-tab viewer chip: bg-card -> bg-transparent (outline) so it doesn't blend into a card surface in light mode. - tag-manager / pipeline-settings color-swatch selection rings: raw slate/white -> var(--popover)/var(--foreground) so they flip. - pipeline-board scrollbar (<style jsx>): slate rgb -> var tokens. - activity-feed zebra stripe: bg-card/40 -> bg-muted/40 (survives light). - forgot-password / import-modal: restore real hover (dead no-op hovers). - themed-toaster: gate theme via useSyncExternalStore to avoid a hydration mismatch on sonner's theme attribute (no setState-in-effect). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
52 lines
1.5 KiB
TypeScript
52 lines
1.5 KiB
TypeScript
"use client";
|
|
|
|
import { useSyncExternalStore } from "react";
|
|
import { Toaster } from "sonner";
|
|
|
|
import { useTheme } from "@/hooks/use-theme";
|
|
import { DEFAULT_MODE } from "@/lib/themes";
|
|
|
|
// Returns false during SSR and the first hydration render, true after —
|
|
// the sanctioned (warning-free, no setState-in-effect) way to diverge
|
|
// server vs client. Lets us match the server-rendered default on first
|
|
// paint, then adopt the real mode.
|
|
const noopSubscribe = () => () => {};
|
|
function useIsClient() {
|
|
return useSyncExternalStore(
|
|
noopSubscribe,
|
|
() => true,
|
|
() => false,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Toaster wrapper that tracks the active light/dark mode.
|
|
*
|
|
* Lives inside <ThemeProvider> (see layout.tsx) so it can read the
|
|
* current mode and hand it to sonner. Colors are driven off the same
|
|
* CSS tokens as the rest of the app, so a toast looks at home in
|
|
* either mode without a second palette to maintain.
|
|
*
|
|
* The theme is gated behind `useIsClient`: the server renders
|
|
* DEFAULT_MODE, so first client paint must too, otherwise a light-mode
|
|
* user hydrates with a different sonner `theme` attribute than the
|
|
* server emitted and React logs a hydration mismatch.
|
|
*/
|
|
export function ThemedToaster() {
|
|
const { mode } = useTheme();
|
|
const isClient = useIsClient();
|
|
return (
|
|
<Toaster
|
|
theme={isClient ? mode : DEFAULT_MODE}
|
|
position="top-right"
|
|
toastOptions={{
|
|
style: {
|
|
background: "var(--popover)",
|
|
border: "1px solid var(--border)",
|
|
color: "var(--popover-foreground)",
|
|
},
|
|
}}
|
|
/>
|
|
);
|
|
}
|