selectors

This commit is contained in:
Chris Wanstrath 2025-12-29 15:11:18 -08:00
parent 8a4408fe22
commit 718dc3b73d
4 changed files with 62 additions and 14 deletions

View File

@ -111,6 +111,55 @@ console.log(<Profile pic={user.pic} bio={user.bio} />)
console.log(<Profile size="small" pic={user.pic} bio={user.bio} />)
```
### selectors
Use `selectors` to write custom CSS selectors. Reference the current
element with `&` and other parts with `@PartName`:
```tsx
const Checkbox = define("Checkbox", {
parts: {
Input: {
base: "input[type=checkbox]",
display: "none",
},
Label: {
base: "label",
padding: 10,
cursor: "pointer",
color: "gray",
selectors: {
// style Label when Input is checked
"@Input:checked + &": {
color: "green",
fontWeight: "bold",
},
// style Label when Input is disabled
"@Input:disabled + &": {
opacity: 0.5,
cursor: "not-allowed",
},
},
},
},
render({ props, parts: { Root, Input, Label } }) {
return (
<Root>
<Label>
<Input checked={props.checked} />
{props.label}
</Label>
</Root>
)
},
})
// Usage
<Checkbox label="Agree to terms" checked />
```
## themes
built-in support for CSS variables with full type safety:

View File

@ -9,8 +9,8 @@ const UserProfile = define('UserProfile', {
maxWidth: 600,
margin: "0 auto",
background: theme('colors-bgElevated'),
border: `1px solid ${theme('colors-border')}`,
borderRadius: theme('radius-md'),
border: theme('colors-accent'),
parts: {
Header: {

View File

@ -35,16 +35,9 @@ const ThemePicker = define('SpaThemePicker', {
},
render({ parts: { Root, Select } }) {
const handleChange = (e: Event) => {
const target = e.target as HTMLSelectElement
const themeName = target.value
document.body.setAttribute('data-theme', themeName)
localStorage.setItem('theme', themeName)
}
return (
<Root>
<Select id="theme-select" onchange={handleChange}>
<Select id="theme-select" onChange={themeChanged}>
<option value="dark">Dark</option>
<option value="light">Light</option>
</Select>
@ -53,6 +46,13 @@ const ThemePicker = define('SpaThemePicker', {
}
})
function themeChanged(e: Event) {
const target = e.target as HTMLSelectElement
const themeName = target.value
document.body.setAttribute('data-theme', themeName)
localStorage.setItem('theme', themeName)
}
export const Main = define('SpaMain', {
base: 'div',

View File

@ -51,13 +51,12 @@ 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>> = {}
type Theme = Record<string, string | number>
export function createThemes<T extends Record<string, Theme>>(themes: T) {
const registeredThemes = {} as T
for (const [name, values] of Object.entries(themes)) {
registeredThemes[name] = createTheme(name, values)
(registeredThemes as any)[name] = createTheme(name, values)
}
return createThemedVar(registeredThemes)