Rendering Content
Design System
CSS custom properties, the color token reference, typography scale, and spacing conventions used across Bolt CMS sites.
CSS Variables
Bolt CMS defines its design as CSS custom properties (design tokens) holding complete color strings. Reference a token directly with var(--token) in any css="" attribute or <style> block — no rgb() wrapper needed. The tokens are injected once, globally, by the front controller, so every page reads the same values.
The palette is GitHub-inspired and ships in two modes. The light values live on :root; the dark values override them under html[data-theme="dark"]. The same var(--token) resolves to whichever mode is active, so it themes automatically.
:root {
--background: #ffffff; --card: #ffffff;
--foreground: #1f2328; --muted: #656d76;
--border: #d0d7de; --accent: #0969da; /* links, active nav */
--primary: #1f883d; /* primary buttons (green) */
--destructive: #cf222e; --radius: 6px;
/* …plus surface, text, and callout tokens — see the table below */
}
html[data-theme="dark"] {
--background: #0d1117; --card: #161b22;
--foreground: #e6edf3; --muted: #7d8590;
--border: #30363d; --accent: #2f81f7;
--primary: #238636; --destructive: #f85149;
}
Usage in css=""
Reference a token inside any css="" declaration directly with var() — no rgb() wrapper:
<div css="background: var(--background); color: var(--foreground);">
<p css="color: var(--muted);">Muted text</p>
<button css="background: var(--primary); color: #ffffff;">Action</button>
</div>
Because each token holds a complete color string, the full rgb() / rgba() forms are valid token values too — for example a token could be defined as --primary-tint: rgba(59,130,246,0.1) for a 10% primary tint (write the rgba() with no internal spaces so the css="" tokenizer keeps it intact). The --radius token is a plain length, so it also works directly: border-radius: var(--radius).
Light & dark mode
The active mode is the data-theme attribute on the <html> element — light or dark. A small inline script sets it before first paint from the visitor's saved choice (localStorage['bolt-theme']), falling back to the operating system's prefers-color-scheme, so there is no flash of the wrong theme. The toggle in the header flips it:
// flip the mode and remember it
boltToggleTheme();
Because every surface uses var(--token), switching data-theme re-resolves the whole page through the cascade — no stylesheet re-render is required.
Editing the palette
Administrators can tweak every token value — for both modes — from Appearance in the admin dashboard. Saved changes are stored as overrides and applied site-wide; Reset to defaults restores the GitHub values shown here.
Color Token Reference
The full token set. Color tokens carry a light and a dark value; --radius is shared across modes.
| Token | Role | Light | Dark |
|---|---|---|---|
--background | Page background | #ffffff | #0d1117 |
--card | Card / panel surface | #ffffff | #161b22 |
--surface-subtle | Subtle surface (table head, hover) | #f6f8fa | #161b22 |
--surface-inset | Inset surface (inline code) | #eaeef2 | #21262d |
--foreground | Primary text | #1f2328 | #e6edf3 |
--text-secondary | Secondary / body text | #3a4148 | #c9d1d9 |
--muted | Muted text, captions | #656d76 | #7d8590 |
--text-faint | Faint text, placeholders | #818b98 | #6e7681 |
--border | Default border | #d0d7de | #30363d |
--border-strong | Strong / hover border | #afb8c1 | #444c56 |
--accent | Links, active nav, focus | #0969da | #2f81f7 |
--accent-hover | Accent hover | #0860ca | #388bfd |
--primary | Primary button (green) | #1f883d | #238636 |
--primary-foreground | Text on primary | #ffffff | #ffffff |
--destructive | Danger text / delete | #cf222e | #f85149 |
--info-fg / --info-bg | Info callout | #0969da / #ddf4ff | #2f81f7 / rgba(56,139,253,.12) |
--success-fg / --success-bg | Success callout | #1a7f37 / #dafbe1 | #3fb950 / rgba(46,160,67,.15) |
--warning-fg / --warning-bg | Warning callout | #9a6700 / #fff8c5 | #d29922 / rgba(187,128,9,.15) |
--violet-fg / --violet-bg | Note callout | #8250df / #fbefff | #a371f7 / rgba(163,113,247,.15) |
--danger-bg | Danger background | #ffebe9 | rgba(248,81,73,.15) |
--radius | Corner radius | 6px (shared) | |
Typography
Font
Bolt CMS uses Inter from Google Fonts as the primary typeface, loaded with weights 300-700 for body text and UI. Code blocks use JetBrains Mono.
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
Type Scale
Each role below lists the css="" declaration that produces it. Sizes are in rem (1rem = 16px), and responsive jumps use breakpoint tokens.
| Context | css="" |
Size |
|---|---|---|
| Hero title | font-size: 3rem md:4.5rem; font-weight: 700; letter-spacing: -0.025em; |
3rem / 4.5rem |
| Section heading | font-size: 1.875rem md:2.25rem; font-weight: 700; |
1.875rem / 2.25rem |
| Card title | font-size: 1.125rem; font-weight: 600; |
1.125rem |
| Body text | font-size: 1rem; |
1rem |
| Meta/caption | font-size: 0.875rem; color: var(--muted); |
0.875rem |
| Eyebrow label | font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; font-weight: 500; |
0.75rem |
Spacing Conventions
Consistent spacing creates visual rhythm across pages. Follow these standard patterns.
Container
<div css="max-width: 80rem; margin-left: auto; margin-right: auto; padding-left: 1rem sm:1.5rem lg:2rem; padding-right: 1rem sm:1.5rem lg:2rem;">
<!-- Page content -->
</div>
Section Padding
| Context | css="" |
|---|---|
| Standard section | padding-top: 3rem; padding-bottom: 3rem; to 5rem |
| Hero section | padding-top: 6rem md:8rem; padding-bottom: 6rem md:8rem; |
| Tight section (FAQ, footer) | padding-top: 2rem; padding-bottom: 2rem; to 3rem |
Internal Spacing
- Between heading and content:
margin-top: 1remto1.5rem - Between heading and grid:
margin-top: 3remto4rem - Card internal padding:
padding: 1.5remto2rem - Grid gap:
gap: 1.5remto2rem
Responsive Grid Patterns
Grids stack on mobile, expand to 2 columns on tablet, and 3 columns on desktop. Add the column counts as breakpoint tokens so the base layout is the single-column mobile case.
<!-- Standard 3-column feature grid -->
<div css="display: grid; gap: 2rem; grid-template-columns: repeat(1,minmax(0,1fr)) md:repeat(2,minmax(0,1fr)) lg:repeat(3,minmax(0,1fr));">
<!-- Cards -->
</div>
<!-- 2-column comparison -->
<div css="display: grid; gap: 3rem; grid-template-columns: repeat(1,minmax(0,1fr)) md:repeat(2,minmax(0,1fr));">
<!-- Columns -->
</div>
<!-- 4-column stats/metrics -->
<div css="display: grid; gap: 1.5rem; grid-template-columns: repeat(2,minmax(0,1fr)) md:repeat(4,minmax(0,1fr));">
<!-- Stat cards -->
</div>