Hudson Theming
Hudson ships with runtime-switchable themes and curated templates for any app that mounts AppShell.
Quick Start
import '@hudson/sdk/styles';
import { HudsonThemeScript, ThemeProvider } from '@hudson/sdk';
import { AppShell } from '@hudson/sdk/app-shell';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" data-hudson-template="hudson" data-hudson-theme="dark">
<head>
<HudsonThemeScript />
</head>
<body>
<ThemeProvider>
{children}
</ThemeProvider>
</body>
</html>
);
}
export function MyPage() {
return <AppShell app={myApp} defaultTheme="system" defaultTemplate="hudson" />;
}
HudsonThemeScript applies the saved preference before hydration, and ThemeProvider exposes the runtime controls through useTheme().
Runtime API
import { useTheme } from '@hudson/sdk';
function ThemeSwitcher() {
const { theme, resolvedTheme, template, setTheme, setTemplate } = useTheme();
return (
<>
<button onClick={() => setTheme('light')}>Light</button>
<button onClick={() => setTheme('dark')}>Dark</button>
<button onClick={() => setTheme('system')}>System</button>
<button onClick={() => setTemplate('hudson')}>Hudson</button>
<button onClick={() => setTemplate('editorial')}>Editorial</button>
<span>{resolvedTheme}</span>
<span>{template}</span>
</>
);
}
Tokens
Hudson follows a shadcn-style semantic token surface. Chrome components consume tokens like --background, --foreground, --card, --accent, --border, and --ring, while legacy --hud-* tokens are re-pointed to the active template so existing app content continues to work.
The SDK publishes these Tailwind color names:
backgroundforegroundcardcard-foregroundpopoverpopover-foregroundprimaryprimary-foregroundsecondarysecondary-foregroundmutedmuted-foregroundaccentaccent-foregrounddestructivedestructive-foregroundwarningsuccessinfoborderinputring
Customizing a Single App
If you want an app-specific aesthetic, override the semantic tokens on a wrapper element inside your app while keeping the shell on the shared template:
export function NotesContent() {
return (
<div
style={{
'--background': '0.97 0.004 230',
'--foreground': '0.2 0.02 250',
'--accent': '0.62 0.16 200',
} as React.CSSProperties}
className="bg-background text-foreground"
>
...
</div>
);
}
Adding a New Template
- Add a new
[data-hudson-template="your-template"]block in packages/hudson-sdk/src/styles/tokens.css. - Define light and dark token values for the semantic surface.
- Re-point the
--hud-*aliases in the template scope if your app content relies on them. - Extend the
HudsonTemplateunion in packages/hudson-sdk/src/theme/ThemeProvider.tsx. - Add any switcher UI or command-palette entries that should expose the new template.
Notes
editorialexpects consumers to loadSource Serif 4themselves if they want the serif body treatment.WorkspaceShellremains intentionally dark-focused for now; the current theming work targetsAppShelland shared SDK chrome.