Your first style
You'll go from a blank app to a themed, hover-reactive card — and then promote it into a reusable component with two visual tones. Six steps, ten minutes.
What you'll build
A <Card> that takes a tone prop. tone="neutral" is cream paper on ink text;
tone="warning" is ochre on ink. Hover deepens the surface. The component reads everything from
the active theme — no hex codes at the call site.
You'll write the theme first, then a one-off styled <Box>, then the reusable component. Each
step builds on the previous one.
Define a theme
Create
theme.tsnext to your entry file. A theme is a name plus a token tree.The
surface.neutral/surface.warningpaths are semantic — they reference primitives by$, so swapping themes later is just changing the right side of those references.Mount the provider
Wrap the root of your app once.
The provider emits a
<style>element with every theme's CSS variables and setsdata-theme="light"on a wrapping<div>. Everything below that node can read tokens.Render a Box
In
App.tsx, render a single<Box>that reads the surface and text tokens.You should now see a cream rectangle. The
bg,color,p,borderRadiusprops are typed against the active theme; each$-reference compiles to avar(--…)lookup at render.Add a hover response
The
_hoverprop accepts a style bag that applies on:hover. Add it to deepen the surface.Hover the rectangle — the colour deepens. Motif emits one CSS rule for the hover state, hashes it into a class, and dedupes across the page. The same component on a hundred pages produces the same class.
Promote to a styled component
The component will be reused with two tones. Move the styles into a
styled()call and add atonevariant.<Card>now accepts an optionaltoneprop with two legal values. The component still takes every Box prop (p,bg,borderRadius, …), so call sites can override anything.Use it
Back in
App.tsx, render two cards — one of each tone.Both cards share the base padding, radius, and text colour. Only the surface tokens differ. Add a third tone (
success,danger) by adding one entry to the variants map.
What just happened
You wrote one set of tokens, one provider, and one component — and got a typed, themed, hover-reactive surface that scales with the design.
A few moving parts to keep in mind:
- The theme is the source of truth. Every colour and spacing on the page came from the tokens
tree. Swapping in a
darkThemewould change every value at once. <Box>is the atom. Style props, token references, pseudo-state bags. Every primitive in the library and everystyled()component renders through<Box>underneath.styled()does not invent a new concept. It builds a regular React component that applies the same style-prop pipeline you already used inline.