From dafbce762b7c5d7e7e5e27f318d8c2e7bb2ac249 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath <2+defunkt@users.noreply.github.com> Date: Mon, 29 Dec 2025 14:26:54 -0800 Subject: [PATCH] themes --- README.md | 105 +++++++++++++++++++++++++++++++++++++--- examples/ssr/themes.tsx | 13 +++-- src/index.tsx | 13 +++++ 3 files changed, 118 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 3a74802..7137ac2 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,24 @@ self-contained TSX components out of discrete parts. ## examples +### styles + +```tsx +import { define } from "forge" + +export const Button = define("button", { + base: "button", + + padding: 20, + background: "blue", +}) + +// Usage + +``` + +### variants + ```tsx import { define } from "forge" @@ -51,23 +69,27 @@ export const Button = define("button", { +``` +### parts + `render()` + +```typescript export const Profile = define("div", { padding: 50, background: "red", parts: { Header: { display: "flex" }, - Avatar: { base: 'img', width: 50 }, + Avatar: { base: "img", width: 50 }, Bio: { color: "gray" }, }, variants: { size: { small: { - parts: { Avatar: { width: 20 }} - } - } + parts: { Avatar: { width: 20 } }, + }, + }, }, render({ props, parts: { Root, Header, Avatar, Bio } }) { @@ -83,9 +105,80 @@ export const Profile = define("div", { }) // Usage: -import { Profile } from './whatever' +import { Profile } from "./whatever" - +console.log() +console.log() +``` + +## themes + +built-in support for CSS variables with full type safety: + +```tsx +// themes.tsx - Define your themes +import { createThemes } from "forge" + +export const theme = createThemes({ + dark: { + bgColor: "#0a0a0a", + fgColor: "#00ff00", + sm: 12, + lg: 24, + }, + light: { + bgColor: "#f5f5f0", + fgColor: "#0a0a0a", + sm: 12, + lg: 24, + }, +}) + +// Use theme() in your components +import { define } from "forge" +import { theme } from "./themes" + +const Button = define("Button", { + padding: theme("spacing-sm"), + background: theme("colors-bg"), + color: theme("colors-fg"), +}) +``` + +Theme switching is done via the `data-theme` attribute: + +```tsx +// Toggle between themes +document.body.setAttribute("data-theme", "dark") +document.body.setAttribute("data-theme", "light") +``` + +The `theme()` function is fully typed based on your theme keys, giving +you autocomplete and type checking throughout your codebase. + +## scopes + +Sometimes you want your parts named things like ButtonRow, ButtonCell, +ButtonTable, etc, but all those Button's are repetitive: + +```typescript +const { define } = createScope("Button") + +// css class becomes "Button" +const Button = define("Root", { + // becomes "Button" + // ... +}) + +// css class becomes "ButtonRow" +const ButtonRow = define("Row", { + // ... +}) + +// css class becomes "ButtonContainer" +const ButtonContainer = define("Container", { + // ... +}) ``` ## see it diff --git a/examples/ssr/themes.tsx b/examples/ssr/themes.tsx index d6aba63..d95d8bd 100644 --- a/examples/ssr/themes.tsx +++ b/examples/ssr/themes.tsx @@ -1,10 +1,9 @@ -import { createTheme, createThemedVar } from '../../src' +import { createThemes } from '../../src' import darkTheme from './darkTheme' import lightTheme from './lightTheme' -// Register themes and get typed keys back -const dark = createTheme('dark', darkTheme) -const light = createTheme('light', lightTheme) - -// Create a typed themeVar function -export const theme = createThemedVar({ dark, light }) \ No newline at end of file +// Register themes and get a typed theme function in one step +export const theme = createThemes({ + dark: darkTheme, + light: lightTheme +}) \ No newline at end of file diff --git a/src/index.tsx b/src/index.tsx index 1b3cbce..45cf5ee 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -50,6 +50,19 @@ export function createThemedVar>(_themes: T) { } } +// Simplified API: register multiple themes and get typed themeVar in one call +export function createThemes< + T extends Record> +>(themes: T) { + const registeredThemes: Record> = {} + + for (const [name, values] of Object.entries(themes)) { + registeredThemes[name] = createTheme(name, values) + } + + return createThemedVar(registeredThemes) +} + // Generic themeVar (untyped fallback) export function themeVar(name: string): string { return `var(--theme-${name as string})`