mirror of
https://github.com/hanzoai/rfc.git
synced 2026-10-30 18:23:14 +00:00
Two things in this tree were done twice, and the second one was broken.
BUTTON. `.rfc-btn` (a local CSS class) and `<Button>` (@hanzo/ui) were both
"the button". Every call site now uses the component and the CSS is deleted —
variant, size, hover, focus ring and the 44px touch floor come from one
definition, shared with every other Hanzo surface and with native.
Imported per member (`@hanzo/ui/primitives/Button`, which the package publishes
"for hosts that modularize @hanzo/ui imports"), not off the barrel. The barrel
re-exports the whole library, so one button pulled Dialog, Command, Select and
Slider into the page: 270 kB First Load, 1033 kB of raw chunks. Now 168 kB and
726 kB. `experimental.optimizePackageImports` does NOT fix this — it was tried,
and Slider/Toaster/AspectRatio still shipped, because transpilePackages opts the
package out of that transform.
THEME. Two token sources read two different classes: `@hanzo/ui/theme.css` keys
the identity off `.dark`, gui resolves `$color…` off `t_light`/`t_dark`. Only
the gui side was wired, and to `'system'` — a name gui has no theme for — so
<html> carried a `t_system` class that nothing matches. Chromium, before:
DARK : --background: hsla(0,0%,97%,1) <- near-white page
--foreground: oklch(0.985 0 0) <- near-white text
Dark mode was unreadable. `--background` is the one token gui also declares (at
bare `:root`, injected after the stylesheet, so it beat `.dark` on source order
at equal specificity); the other 30 switched correctly, which is why the text
went white and the page did not.
Now one state writes both classes, one writer each — NextThemeProvider owns
`.dark` via its `value` map, GuiProvider owns `t_dark` via `defaultTheme` — and
next-themes is gone, since @hanzogui/next-theme IS next-themes with the gui hook
already on it. Chromium, after:
LIGHT html="t_light light" body=rgb(247,247,247)/rgb(5,5,5) card=oklch(1 0 0)
DARK html="dark t_dark" body=rgb(20,20,20)/rgb(255,255,255) card=oklch(0.205 0 0)
RELOADED html="dark t_dark" body=rgb(20,20,20)/rgb(255,255,255)
button bg rgb(255,255,255) on dark, rgb(5,5,5) on light — inverts with the theme
/docs, /contribute, /docs/category/core all dark; 0 console errors
$ pnpm build
✓ Compiled successfully in 22.0s
✓ Generating static pages (11/11)
✓ Exporting (2/2)
$ pnpm typecheck # exit 0
Co-authored-by: Hanzo Dev <[email protected]>
30 lines
874 B
TypeScript
30 lines
874 B
TypeScript
'use client';
|
|
|
|
import { useEffect, useState } from 'react';
|
|
import { Moon, Sun } from 'lucide-react';
|
|
import { useThemeSetting } from '@hanzogui/next-theme';
|
|
import { Button } from '@hanzo/ui/primitives/Button';
|
|
|
|
export function ThemeToggle() {
|
|
const { resolvedTheme, set } = useThemeSetting();
|
|
const [mounted, setMounted] = useState(false);
|
|
|
|
useEffect(() => setMounted(true), []);
|
|
|
|
// Before hydration the resolved theme is unknown; render the same button
|
|
// shape so the header does not reflow when it arrives.
|
|
const isDark = mounted && resolvedTheme === 'dark';
|
|
|
|
return (
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="icon"
|
|
aria-label={isDark ? 'Switch to light theme' : 'Switch to dark theme'}
|
|
onPress={() => set(isDark ? 'light' : 'dark')}
|
|
>
|
|
{isDark ? <Moon size={18} /> : <Sun size={18} />}
|
|
</Button>
|
|
);
|
|
}
|