Token reference¶
The --theme-* contract as the base theme emits it, beside the same token in the two custom themes the component pages are shot on. Where a row repeats a value, that theme had no reason to diverge — a custom theme is expected to redeclare the whole contract, not to change every value in it.
Read this next to a component page
Each component page lists the subset of these it actually reads.
Colour¶
What the set is painted with. -ink is body text, -surface is the page, -border-strong is a rule that carries structure rather than merely separating.
Every colour token here falls back to its Bootstrap equivalent before it falls back to a
literal — var(--theme-color-primary, var(--bs-primary, #0d6efd)). So a theme that declares
only part of the contract still gets its own colours for the rest, rather than stock
Bootstrap blue: the token wins if set, the theme's --bs-* wins next, and the literal is
reached only when neither exists. --theme-color-primary-ink is the exception — it is a
computed contrast colour and Bootstrap emits no variable for it, so it keeps a literal.
| Token | Base | Light | Dark |
|---|---|---|---|
--theme-color-primary |
#0d6efd |
#8a6a4f |
#ffb443 |
--theme-color-primary-ink |
#fff |
#fff |
#050505 |
--theme-color-secondary |
#6c757d |
#6e6b66 |
#b8b8b8 |
--theme-color-ink |
#212529 |
#1c1b19 |
#f5f5f5 |
--theme-color-ink-muted |
rgba(33, 37, 41, 0.75) |
#6e6b66 |
#b8b8b8 |
--theme-color-surface |
#fff |
#fbfaf8 |
#050505 |
--theme-color-surface-subtle |
#f8f9fa |
#f4f1ec |
#0a0a0a |
--theme-color-surface-inverse |
#212529 |
#1c1b19 |
#f5f5f5 |
--theme-color-border |
#dee2e6 |
#e8e5e0 |
#222 |
--theme-color-border-strong |
#dee2e6 |
#e8e5e0 |
#333 |
Type¶
One ramp for the whole set. Sizes may be fluid clamp() or fixed — components never assume either.
| Token | Base | Light | Dark |
|---|---|---|---|
--theme-font-heading |
system-ui, -apple-system, Segoe UI, roboto, H… |
Figtree, system-ui, -apple-system, Segoe UI, … |
Manrope, system-ui, -apple-system, Segoe UI, … |
--theme-font-body |
system-ui, -apple-system, Segoe UI, roboto, H… |
Figtree, system-ui, -apple-system, Segoe UI, … |
Manrope, system-ui, -apple-system, Segoe UI, … |
--theme-font-mono |
SFMono-Regular, Menlo, Monaco, Consolas, Liberation… |
SFMono-Regular, Menlo, Monaco, Consolas, Liberation… |
JetBrains Mono, sfmono-regular, menlo, monaco… |
--theme-font-size-sm |
0.875rem |
0.8125rem |
0.8125rem |
--theme-font-size-base |
1rem |
clamp(1rem, 0.96rem + 0.14vw, 1.0625rem) |
clamp(1rem, 0.96rem + 0.14vw, 1.0625rem) |
--theme-font-size-lg |
1.25rem |
clamp(1.0625rem, 0.99rem + 0.26vw, 1.1875rem) |
clamp(1.0625rem, 0.99rem + 0.26vw, 1.1875rem) |
--theme-font-size-h6 |
1rem |
0.9375rem |
0.9375rem |
--theme-font-size-h5 |
1.25rem |
1.0625rem |
1.0625rem |
--theme-font-size-h4 |
1.5rem |
clamp(1.125rem, 1.078rem + 0.19vw, 1.25rem) |
clamp(1.125rem, 1.06rem + 0.28vw, 1.3125rem) |
--theme-font-size-h3 |
1.75rem |
clamp(1.3125rem, 1.242rem + 0.29vw, 1.5rem) |
clamp(1.375rem, 1.24rem + 0.58vw, 1.75rem) |
--theme-font-size-h2 |
2rem |
clamp(1.625rem, 1.393rem + 0.95vw, 2.25rem) |
clamp(1.875rem, 1.46rem + 1.72vw, 3rem) |
--theme-font-size-h1 |
2.5rem |
clamp(2rem, 1.63rem + 1.52vw, 3rem) |
clamp(2.375rem, 1.62rem + 3.1vw, 4.25rem) |
--theme-font-size-display |
3.5rem |
clamp(2.375rem, 1.863rem + 2.1vw, 3.75rem) |
clamp(2.75rem, 1.55rem + 4.9vw, 6rem) |
--theme-leading-tight |
1.2 |
1.12 |
1.04 |
--theme-leading-heading |
1.2 |
1.22 |
1.2 |
--theme-leading-normal |
1.5 |
1.75 |
1.55 |
--theme-leading-relaxed |
2 |
1.9 |
1.72 |
--theme-tracking-tight |
0 |
-0.012em |
-0.03em |
--theme-tracking-eyebrow |
0 |
0.01em |
0.14em |
--theme-heading-weight |
500 |
500 |
800 |
--theme-eyebrow-case |
none |
none |
uppercase |
--theme-measure |
44rem |
38rem |
44rem |
Space¶
--theme-space-section is the band rhythm and does more for the out-of-the-box feel than any colour here.
| Token | Base | Light | Dark |
|---|---|---|---|
--theme-spacer |
1rem |
1rem |
1rem |
--theme-spacer-1 |
0.25rem |
0.25rem |
0.25rem |
--theme-spacer-2 |
0.5rem |
0.5rem |
0.5rem |
--theme-spacer-3 |
1rem |
1rem |
1rem |
--theme-spacer-4 |
1.5rem |
1.5rem |
1.5rem |
--theme-spacer-5 |
3rem |
3rem |
3rem |
--theme-space-section |
3rem |
clamp(4.5rem, 9vw, 9rem) |
clamp(3.5rem, 7vw, 7rem) |
--theme-space-gutter |
1.5rem |
0rem |
0rem |
Container geometry¶
The split hero aligns its text column with the page's centered container by reproducing that
container's max-width per breakpoint. Each token falls back to Bootstrap's stock
$container-max-widths value, so a stock-Bootstrap theme declares nothing. A theme that
customizes those widths — or any non-Bootstrap theme — must declare all five, or the
full-bleed hero's content drifts out of step with every other section on the page.
| Token | Breakpoint | Fallback |
|---|---|---|
--theme-container-sm |
≥576px | 540px |
--theme-container-md |
≥768px | 720px |
--theme-container-lg |
≥992px | 960px |
--theme-container-xl |
≥1200px | 1140px |
--theme-container-xxl |
≥1400px | 1320px |
Shape¶
One radius decision, spent everywhere.
| Token | Base | Light | Dark |
|---|---|---|---|
--theme-radius |
0.375rem |
0.25rem |
0.25rem |
--theme-radius-lg |
0.5rem |
0.375rem |
0.375rem |
--theme-radius-pill |
50rem |
50rem |
0.25rem |
Elevation¶
A flat theme sets both to none; they exist so a warmer theme can opt back in without touching a component.
| Token | Base | Light | Dark |
|---|---|---|---|
--theme-shadow-sm |
0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) |
none |
none |
--theme-shadow-md |
0 0.5rem 1rem rgba(0, 0, 0, 0.15) |
none |
none |
Motion¶
Every component transition reads these, so one change retimes the whole set.
| Token | Base | Light | Dark |
|---|---|---|---|
--theme-duration |
0.2s |
220ms |
220ms |
--theme-ease |
ease-in-out |
cubic-bezier(0.22, 0.61, 0.36, 1) |
cubic-bezier(0.22, 0.61, 0.36, 1) |
Declared by the bridge¶
These are not part of the core contract — the shared Bootstrap bridge declares them so bootstrap_components and the carousel have a documented extension point. Override them the same way.
| Token | Purpose |
|---|---|
--theme-accordion-btn-active-icon-color |
Accordion chevron colour while the item is open. |
--theme-accordion-btn-hover-bg |
Accordion header hover — Bootstrap ships no hover state for the header at all. |
--theme-accordion-btn-hover-color |
Accordion header hover — heading text colour. |
--theme-accordion-btn-hover-icon-color |
Accordion header hover — chevron colour. |
--theme-accordion-btn-icon-color |
Accordion chevron colour at rest. Defaults to currentcolor. |
--theme-carousel-caption-color-dark |
Carousel chrome — scrim, control colour, opacity, hit padding. |
--theme-carousel-caption-scrim |
Carousel chrome — scrim, control colour, opacity, hit padding. |
--theme-carousel-control-color |
Carousel chrome — scrim, control colour, opacity, hit padding. |
--theme-carousel-control-color-dark |
Carousel chrome — scrim, control colour, opacity, hit padding. |
--theme-carousel-control-icon-size |
Carousel chrome — scrim, control colour, opacity, hit padding. |
--theme-carousel-control-opacity |
Carousel chrome — scrim, control colour, opacity, hit padding. |
--theme-carousel-control-opacity-active |
Carousel chrome — scrim, control colour, opacity, hit padding. |
--theme-carousel-control-width |
Carousel chrome — scrim, control colour, opacity, hit padding. |
--theme-carousel-indicator-padding-block |
Carousel chrome — scrim, control colour, opacity, hit padding. |
--theme-carousel-indicator-padding-inline |
Carousel chrome — scrim, control colour, opacity, hit padding. |
--theme-close-btn-padding |
Sized in em so it scales with .btn-close's glyph, not a spacer. |
--theme-color-focus |
Focus-ring colour; defaults to the accent. |
--theme-shadow-sm-2 |
Second shadow layer for stacked card elevation. |
--theme-touch-target-min |
WCAG 2.5.5 minimum hit area. Do not lower it. |
--theme-touch-target-min is an accessibility floor
Buttons buy their 44px target through padding rather than a min-height rule, so a theme
that resizes buttons keeps the target by default. Lowering this token silently drops the set
below WCAG 2.5.5.
Component tokens¶
Every component that declares its own --<component>-* tokens lists them here, alongside the
subset of the --theme-* contract above that it actually reads. See the component's own Theming
tab for the short version of what to override in context.
Hero Banner¶
The diagram below places the ones tied to a specific region; the rest — spacing between regions, breakpoint-driven, or shared across layouts — are called out underneath it.
--hero-eyebrow-color / --hero-eyebrow-color-light
CTA --hero-banner-cta-radius
↕ --hero-content-pad-bottom
--hero-split-media-min-height
--hero-split-content-width how wide the text column is, which the arrows line up with ·
--hero-split-content-gap space between the two columns ·
--hero-split-container-inset outer margin each side, recalculated at every Bootstrap breakpoint from --theme-container-* (see below) ·
--hero-carousel-control-width how far the prev/next arrows sit from the edge ·
--hero-nav-* the round prev/next buttons' fill and border, at rest and on hover, for both carousel engines ·
--swiper-pagination-* the dots below a Swiper carousel ·
--hero-controls-bg background behind a video slide's audio controls, unset (transparent) by default
| Token | Default |
|---|---|
--hero-eyebrow-color |
var(--bs-primary, #0a58ca) |
--hero-eyebrow-color-light |
#fff |
--hero-content-pad-bottom |
clamp(1.5rem, 0.5rem + 2vw, 2.5rem) |
--hero-banner-cta-radius |
var(--theme-radius, 0) |
--hero-controls-bg |
(unset — transparent until a theme sets it) |
--hero-carousel-control-width |
15% |
--hero-nav-bg |
rgb(255 255 255 / 8%) ( rgb(0 0 0 / 5%) on a light banner) |
--hero-nav-border |
rgb(255 255 255 / 65%) ( rgb(0 0 0 / 50%) on a light banner) |
--hero-nav-bg-hover |
rgb(255 255 255 / 18%) ( rgb(0 0 0 / 10%) on a light banner) |
--hero-nav-border-hover |
#fff ( rgb(0 0 0 / 80%) on a light banner) |
--hero-gradient-horizontal-angle |
270deg |
--hero-gradient-horizontal-color |
#000 |
--hero-gradient-horizontal-opacity |
0.55 |
--hero-gradient-vertical |
linear-gradient(180deg, rgb(0 0 0 / 0%) 0%, rgb(0 0 0 … |
--hero-gradient-vertical-opacity |
1 |
--hero-indicator-hit-padding |
8px |
--hero-preloader-size |
60px |
--hero-split-container-inset |
0.75rem below the sm breakpoint; see note above |
--hero-split-content-gap |
2.5rem |
--hero-split-content-width |
40% |
--hero-split-control-inline |
follows --hero-split-content-width |
--hero-split-media-min-height |
240px |
--hero-video-max-height |
56.25cqw |
--hero-video-max-width |
177.78cqh |
--swiper-pagination-bullet-border-radius |
4px |
--swiper-pagination-bullet-height |
8px |
--swiper-pagination-bullet-horizontal-gap |
5px |
--swiper-pagination-bullet-inactive-color |
#fff |
--swiper-pagination-bullet-inactive-opacity |
0.5 |
--swiper-pagination-bullet-width |
8px |
--swiper-pagination-color |
#fff |
--swiper-theme-color |
var(--bs-light) |
Layout-specific min-heights (--hero-min-height-mobile / -desktop) and suggested image aspect ratios per layout are covered in Retheme a Hero Banner, next to the rest of that layout-level guidance.
Theme tokens it reads¶
Change either of these and this component follows, along with everything else that reads them. This is the component's whole --theme-* surface — no other --theme-* token affects Hero Banner's own rendering.
| Token | Bootstrap default | Custom theme |
|---|---|---|
--theme-font-size-sm |
0.875rem |
0.8125rem |
--theme-radius |
(not set by default — the CTA's radius is pinned to 0 directly instead, see Retheme a Hero Banner) |
0.75rem |
Split layout also reads --theme-container-sm through --theme-container-xxl at each breakpoint, to keep its content column aligned with the rest of the page's containers — see Container geometry above. The live builder's Theme toggle also sets --theme-container-lg: 1100px for Custom theme, but that one's too subtle to see at iframe scale — it nudges Split's content-column inset, not anything with a visible edge in the demo.
The live builder's placeholder image also changes color between the two Theme options — that's --theme-color-primary, but it's not a Hero Banner token. It's read by the shared media-placeholder.css used across every "no image selected" state in IXM Blocks, not by hero_banner.css itself; the demo loads that shared file too, so toggling Theme there shows #10564f — the exact brand colour the retheme walkthrough picks — standing in for Bootstrap's default blue, the same as it would on any other component's placeholder.