Core Concepts
Toned is built around four core ideas: design tokens, systems, stylesheets, and variants. Together they give you type-safe, cross-platform styling with a single authoring model.
Design Tokens
Tokens are the atomic building blocks of your design system. Each token maps a semantic name to one or more platform-specific values. For example, the bgColor token accepts values like 'action', 'muted', or 'elevated'. On the web these resolve to CSS custom properties; on React Native they resolve to colour values directly.
// Tokens are used directly inside stylesheet definitions:
Root: {
bgColor: 'action', // semantic background colour
borderRadius: 'medium', // semantic border radius
paddingX: 3, // spacing scale value
}The base system ships with tokens for colour (bgColor, textColor, borderColor), borders (borderRadius, borderWidth), typography (typography), shadows (shadow), layout (paddingX, paddingY, gap, flexLayout), and sizing (width, height).
Systems
A system is a collection of tokens plus configuration (breakpoints, selectors, rules). You create one with defineSystem:
import { defineSystem, defineToken } from '@toned/core'
export const ui = defineSystem({
id: 'example',
tokens: {
padding: defineToken({
values: [0, 1, 2, 3] as const,
resolve: value => ({ padding: value * 4 }),
}),
},
})The system's ui.stylesheet function is bound to that system's token set, giving you full autocompletion and type checking for every token property.
Stylesheets
A stylesheet defines one or more named elements, each with a set of token values and an optional $style escape hatch for raw CSS properties:
const cardStyles = stylesheet({
Root: {
bgColor: 'elevated',
borderRadius: 'large',
borderColor: 'subtle',
borderWidth: 'thin',
shadow: 'small',
padding: 3,
},
})Stylesheets are plain objects until they are consumed by a framework adapter (like useStyles in React). This keeps your style definitions platform-agnostic.
Variants
Variants let you conditionally apply different token values based on component state. Chain .variants() onto a stylesheet and use the dollar-sign builder to declare variant conditions:
import type { Variants } from '@toned/core'
const buttonStyles = stylesheet({
Root: { $kind: 'pressable', bgColor: 'action', borderRadius: 'medium' },
Label: { $kind: 'text', textColor: 'on_action' },
}).variants(($: Variants<{
size: 'm' | 's'
variant: 'accent' | 'danger'
}>) => ({
[$.variant('danger')]: {
Root: { bgColor: 'destructive' },
Label: { textColor: 'on_destructive' },
},
[$.size('m')]: {
Root: { paddingX: 3 },
},
[$.size('s')]: {
Root: { paddingX: 2, paddingY: 1 },
},
}))Variant keys are fully typed: your component will get compile-time errors if it passes invalid variant values to the element family or to useStyles.
Media Queries / Breakpoints
The base system defines breakpoints (xs, sm, md, lg, xl). You can apply responsive token values under an @media key that names the breakpoint:
const responsiveCard = stylesheet({
Root: {
paddingX: 2,
'@media md': {
paddingX: 4,
},
'@media lg': {
paddingX: 6,
},
},
})The same @ blocks work inline with t, alongside ':hover' and friends.
With a web renderer these are real CSS @media rules, generated at build time. A native renderer evaluates a stylesheet's breakpoints against the host's viewport. Inline t blocks compile to CSS custom properties and apply to web output only. See Media Queries.
States and Conditions
Breakpoints are one kind of condition. A part can also change in a state (':hover', ':focus-visible'), inside a container of a given width ('@container card wide'), on one platform ('@platform web'), or when another part is in a state ('Root:hover'). Every key and its forms are listed in Conditions and selectors.