Rime

Dark mode

Dark is a mode, not a preset: Rime Default and Rime Flat both have a dark mode. Press D anywhere on this site to try it.

How it works

tokens.css repaints every colour under data-mode="dark" or the .dark class, on <html> or any wrapper. The light channel (--rgb-white) points at the raised surface colour, so every translucent fill darkens with it; Flat stays opaque. Portalled overlays follow because the selectors also match :root:has(...).

Next.js setup

Set the attributes before paint so a reload never flashes: a tiny inline script in <head> that reads the stored choice, falling back to the system preference.

const THEME_SCRIPT = `try{var d=document.documentElement,m=localStorage.getItem("mode");
if(m!=="light"&&m!=="dark")m=matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light";
if(m==="dark"){d.classList.add("dark");d.setAttribute("data-mode","dark")}d.style.colorScheme=m}catch(e){}`;

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <head>
        <script dangerouslySetInnerHTML={{ __html: THEME_SCRIPT }} />
      </head>
      <body>{children}</body>
    </html>
  );
}

To toggle: add or remove the dark class (and data-mode), store the choice, and switch transitions off for one frame so colours don't tween.

With next-themes

Rime accepts the .dark class, so shadcn's setup works unchanged:

<ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange>
  {children}
</ThemeProvider>