This commit is contained in:
Chris Wanstrath 2025-12-29 14:26:54 -08:00
parent 855112ac82
commit dafbce762b
3 changed files with 118 additions and 13 deletions

105
README.md
View File

@ -30,6 +30,24 @@ self-contained TSX components out of discrete parts.
## examples ## examples
### styles
```tsx
import { define } from "forge"
export const Button = define("button", {
base: "button",
padding: 20,
background: "blue",
})
// Usage
<Button>Click me</Button>
```
### variants
```tsx ```tsx
import { define } from "forge" import { define } from "forge"
@ -51,23 +69,27 @@ export const Button = define("button", {
<Button>Click me</Button> <Button>Click me</Button>
<Button status="danger">Click me carefully</Button> <Button status="danger">Click me carefully</Button>
<Button status="warning">Click me?</Button> <Button status="warning">Click me?</Button>
```
### parts + `render()`
```typescript
export const Profile = define("div", { export const Profile = define("div", {
padding: 50, padding: 50,
background: "red", background: "red",
parts: { parts: {
Header: { display: "flex" }, Header: { display: "flex" },
Avatar: { base: 'img', width: 50 }, Avatar: { base: "img", width: 50 },
Bio: { color: "gray" }, Bio: { color: "gray" },
}, },
variants: { variants: {
size: { size: {
small: { small: {
parts: { Avatar: { width: 20 }} parts: { Avatar: { width: 20 } },
} },
} },
}, },
render({ props, parts: { Root, Header, Avatar, Bio } }) { render({ props, parts: { Root, Header, Avatar, Bio } }) {
@ -83,9 +105,80 @@ export const Profile = define("div", {
}) })
// Usage: // Usage:
import { Profile } from './whatever' import { Profile } from "./whatever"
<Profile pic={user.pic} bio={user.bio} /> console.log(<Profile pic={user.pic} bio={user.bio} />)
console.log(<Profile size="small" pic={user.pic} bio={user.bio} />)
```
## 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 ## see it

View File

@ -1,10 +1,9 @@
import { createTheme, createThemedVar } from '../../src' import { createThemes } from '../../src'
import darkTheme from './darkTheme' import darkTheme from './darkTheme'
import lightTheme from './lightTheme' import lightTheme from './lightTheme'
// Register themes and get typed keys back // Register themes and get a typed theme function in one step
const dark = createTheme('dark', darkTheme) export const theme = createThemes({
const light = createTheme('light', lightTheme) dark: darkTheme,
light: lightTheme
// Create a typed themeVar function })
export const theme = createThemedVar({ dark, light })

View File

@ -50,6 +50,19 @@ export function createThemedVar<T extends Record<string, any>>(_themes: T) {
} }
} }
// Simplified API: register multiple themes and get typed themeVar in one call
export function createThemes<
T extends Record<string, Record<string, string | number>>
>(themes: T) {
const registeredThemes: Record<string, Record<string, string>> = {}
for (const [name, values] of Object.entries(themes)) {
registeredThemes[name] = createTheme(name, values)
}
return createThemedVar(registeredThemes)
}
// Generic themeVar (untyped fallback) // Generic themeVar (untyped fallback)
export function themeVar(name: string): string { export function themeVar(name: string): string {
return `var(--theme-${name as string})` return `var(--theme-${name as string})`