Themes
PureStack generates theme CSS from palettes. Each skin supplies light and dark values for type, surfaces, borders, and semantic tones. Components read those shared values so a theme change stays consistent across the page.
See Semantic tone classes for visual examples of the roles generated from those palettes.
Start with the standard skin
Add this to siteConfig.json:
{
"style": {
"fileName": "site.css",
"href": "/assets/site.css",
"themes": ["light", "dark"],
"theme": { "skin": "standard" }
}
} The generator writes assets/site.css for light and assets/site.dark.css for dark. Its theme toggle uses the configured theme list and remembers a visitor's choice. Try the ThemeToggle example on this site.
Create a skin
Pass a color object to the standard skin to generate matching light and dark palettes. You can provide any subset of its semantic colors; omitted colors use the standard defaults:
import { definePlugin } from '@purestack/ts-ssg'
import { themeSkins } from '@purestack/ts-style'
import { merge } from '@purestack/ts-util'
export const mySitePlugin = definePlugin({
name: 'my-site',
skins: {
'my-site': {
create: () => {
const standard = themeSkins.standard.create({
accent: '#4b64c8',
neutral: '#293047',
secondary: '#168a75',
feature: '#b04783',
})
return {
light: merge(standard.light, {
font: { size: { body: '1rem' } },
}),
dark: merge(standard.dark, {
font: { size: { body: '1rem' } },
}),
}
},
},
},
}) Add the plugin to your build's plugins, then set "theme": { "skin": "my-site" } in the style config. A skin is a full light/dark pair, and merge preserves palette values you do not override. You can also pass a list of built-in preset names, such as ['green'], to themeSkins.standard.create().
Extend styles with palette roles
Use semantic roles when a custom section needs color, so it follows both themes:
import { styleBuilder, themes } from '@purestack/ts-style'
themes.forEach((theme, palette) => {
styleBuilder.get(theme).select('.feature-note').css({
background: palette.semanticTone.info.surface.rest.background,
color: palette.semanticTone.info.surface.rest.text,
borderColor: palette.semanticTone.info.surface.rest.border,
})
}) Register such styles from a plugin's onConfigResolved hook, which runs before the build writes theme assets. For a real example, inspect the Studio plugin, with its skin and styles.