Create a theme
We start from solid, change the colors, add a paint, then a motion. Each step is rendered next to its code, with the real CSS generated from this site’s configuration file.
The principle: a theme only needs to describe what changes. extends inherits the rest. The options are detailed in the reference.
1. Recolor with tokens
Section titled “1. Recolor with tokens”The shortest theme: it extends solid and only changes its tokens. The colors are semi-transparent to stay readable in both light and dark.
import { defineCharpente } from 'charpente'
export default defineCharpente({ themes: { brume: { extends: 'solid', tokens: { base: 'rgba(96, 165, 250, 0.3)', strong: 'rgba(59, 130, 246, 0.55)', shine: 'rgba(255, 255, 255, 0.6)', }, }, },})A short introduction spanning two or three lines, to show how text is painted line by line.
2. Add a paint
Section titled “2. Add a paint”A second theme extends the first and redefines the paint of three leaf types. Only the given fields are replaced: here, the radius of media and controls and the background of blocks.
themes: { 'brume-ronde': { extends: 'brume', paint: { media: { style: { borderRadius: '50%' } }, control: { style: { borderRadius: '999px' } }, block: { fill: 'repeating-linear-gradient(45deg, var(--charpente-base) 0 10px, var(--charpente-strong) 10px 20px)', }, }, },}A short introduction spanning two or three lines, to show how text is painted line by line.
Add a hover
Section titled “Add a hover”A theme can also react to the pointer. This one extends brume, gives each type a hover and pauses the animation under the pointer. Hover a shape to see it. Hover needs an area without inert, as in this demo.
themes: { 'survol-fin': { extends: 'brume', hover: { text: { fill: 'linear-gradient(var(--charpente-accent), var(--charpente-accent))', radius: '999px' }, media: { border: '3px solid var(--charpente-accent)' }, control: { style: { opacity: '0.6' } }, block: { fill: 'linear-gradient(var(--charpente-accent2), var(--charpente-accent2))' }, }, hoverMotion: 'pause', },}A short introduction spanning two or three lines, to show how text is painted line by line.
For a plain color change, hover: 'accent' is enough: a token name or a color paints every type. Hover is ignored on React Native.
Scale-aware short format
Section titled “Scale-aware short format”Media and blocks are larger than text, so their pattern is usually drawn about 15% bigger. In the short format, fill, size, radius and every style value accept a function of the step: 1 for text and controls, scale (default 1.15) for media and blocks.
import { defineTheme } from 'charpente'
export default defineTheme({ colors: ['#dbeafe', '#93c5fd'], pattern: { fill: 'linear-gradient(var(--charpente-strong) 1px, transparent 1px), linear-gradient(90deg, var(--charpente-strong) 1px, transparent 1px)', size: (step) => `${8 * step}px ${8 * step}px`, }, radius: (step) => `${2 * step}px`, style: { boxShadow: 'inset 0 0 0 1px #93c5fd' }, paint: { text: { style: { borderRadius: '1px' } } },})A short introduction spanning two or three lines, to show how text is painted line by line.
The grid is 8px on text and controls and 9.2px on media and blocks. style adds declarations to every type. An explicit paint still wins for the type it names, and its style is merged into the expanded one.
Border from a file and variants
Section titled “Border from a file and variants”A border can be an image. Put an SVG next to theme.ts and point border at it: the file is read at build time and drawn with border-image on every skeleton, text and blocks alike. slice is the size of the corners in the SVG, width the thickness drawn, repeat how the edges fill (round by default).
variants derives several themes from one: each key becomes the theme <name>-<variant> and replaces the fields it gives. The Christmas recipe keeps one gift border and offers several wrapping papers:
export default { ...paper('#fecaca', '#b91c1c', '#fca5a5'), border: { image: './border.svg', slice: 30, width: '14px' }, variants: { green: paper('#bbf7d0', '#15803d', '#86efac'), gold: paper('#fde68a', '#a16207', '#fcd34d'), snow: paper('#e0f2fe', '#0369a1', '#bae6fd'), },} satisfies ThemeDefA short introduction spanning two or three lines, to show how text is painted line by line.
A short introduction spanning two or three lines, to show how text is painted line by line.
The SVG is an ordinary file: edit it and the stylesheet is regenerated. schedule and include use the derived names, such as christmas-green.
3. Write a motion
Section titled “3. Write a motion”A motion is a CSS animation applied to every leaf. It starts after delay + rank × stagger, so every motion follows the stagger of the site. The animated properties are free.
motions: { rebond: { duration: '1.2s', keyframes: { '0%, 100%': { transform: 'translateY(0)' }, '50%': { transform: 'translateY(-3px)' }, }, },}<div data-charpente-loading data-charpente-theme="brume-ronde" data-charpente-motion="rebond" aria-busy="true" inert> ...</div>With a wrapper: <Charpente loading theme="brume-ronde" motion="rebond">.
A short introduction spanning two or three lines, to show how text is painted line by line.
4. Enable it everywhere
Section titled “4. Enable it everywhere”Make it the default theme, or assign it to a period of the year with the seasonal calendar.
export default defineCharpente({ defaultTheme: 'brume-ronde', motion: 'rebond',})Edit a theme visually
Section titled “Edit a theme visually”The optional charpente-studio package gives you a live editor: npx charpente-studio, tune the colors, pattern and radius on a skeleton card, then save. It writes the same short-format themes/<name>/theme.ts file described above. Borders and variants are still edited by hand.