Rendering customization
Zero-config by default, themable in one line, fully hookable when you need it.
See it live on the theming demo.
The layering model#
Every visual decision resolves in this order (lowest → highest priority):
- Default look — the built-in indigo style. Nothing to configure.
- Global CSS variables — set
--ca-*on:rootfor a page-wide brand. - Preset — a named token bundle passed per instance.
- Theme tokens — individual overrides passed per instance.
1. Presets#
createAnatomy({ root, panel, preset: 'blueprint' });
| Preset | Look |
|---|---|
default |
Indigo fill + solid border, filled label chip. |
minimal |
No fill, thin neutral border, subdued gray label. |
contrast |
Yellow/black, thick square border. High visibility. |
blueprint |
Blue dashed outline on a light wash — technical drawing style. |
Presets are plain objects — import, spread, and tweak them:
import { presets } from '@component-anatomy/core';
createAnatomy({ root, theme: { ...presets.blueprint, labelBg: '#0f172a' } });
2. Theme tokens#
The accent shorthand derives the overlay border, a 15% background wash, and the label background from a single brand color:
createAnatomy({ root, panel, theme: { accent: '#0d9488' } });
Because it is a theme token, accent outranks a preset: combine the two and the accent wins for the three tokens it derives, while the rest of the preset stays. A token you spell out yourself beats the shorthand, as always.
createAnatomy({
root,
preset: 'contrast', // thick square border, yellow label text…
theme: { accent: '#0d9488' }, // …but teal border, wash and label background
});
| Token | Type | Maps to |
|---|---|---|
accent |
color | Shorthand → border, bg wash, label bg |
overlayBg |
color | --ca-overlay-bg |
overlayBorder |
color | --ca-overlay-border |
overlayBorderWidth |
px | CSS length |
overlayBorderStyle |
solid | dashed |
overlayRadius |
px | CSS length |
labelBg / labelFg |
color | --ca-label-bg / --ca-label-fg |
labelFont / labelFontSize |
font | px |
zIndex |
number | --ca-overlay-z |
transitionMs |
ms | CSS time |
Numbers get sensible units automatically (8 → 8px, 300 → 300ms for transitionMs).
Runtime switching#
controller.setTheme({ accent: picker.value }, 'minimal');
Global CSS variables (no JS)#
:root {
--ca-overlay-border: #e11d48;
--ca-overlay-bg: rgb(225 29 72 / 0.12);
--ca-label-bg: #e11d48;
}
The font of the part id chips and of inline code in descriptions is a variable too, --ca-code-font, so they can follow your site’s monospace font (unset, they use ui-monospace, 'Cascadia Code', 'Fira Mono', monospace):
:root {
--ca-code-font: var(--font-family-mono);
}
Instances without a preset/theme pick these up automatically — useful for theming a whole docs site in CSS only.
3. Overlay hooks#
createAnatomy({
root,
overlay: {
label: true, // false hides the name chips entirely
padding: 4, // inflate every highlight box by 4px
className: 'glow', // your own CSS class on every box
// Custom label text/content per box
renderLabel: ({ part, element, index }) =>
part.id === 'tab' ? `${part.name} #${index + 1}` : part.name,
// Full access to the box element after creation
decorateOverlay: (box, { part }) => {
box.style.boxShadow = '0 4px 16px rgb(13 148 136 / .3)';
if (part.id === 'thumb') box.append(makeCornerBadge());
},
},
});
renderLabelreturns a string (used as text) or a Node (appended). Return null to fall back to the part name.decorateOverlayruns once per box per highlight — keep it cheap; it executes on every hover.
Recipes#
Match your brand in one line#
createAnatomy({ root, panel, theme: { accent: 'var(--brand-500)' } });
Screenshot-friendly (no animation, no labels)#
createAnatomy({ root, theme: { transitionMs: 0 }, overlay: { label: false } });
Dark page#
createAnatomy({ root, theme: {
overlayBg: 'rgb(255 255 255 / 0.08)',
overlayBorder: '#a5b4fc',
labelBg: '#e0e7ff', labelFg: '#1e1b4b',
}});