Styling
Three levels of SCSS — design tokens, site-wide overrides, and styles scoped to a single page.
Styling happens at three levels, and the right one is almost always the narrowest that does the job.
1. Design tokens
Every colour, radius, font and layout width in the theme is a CSS custom property
declared in src/styles/_tokens.scss. Change one there and it changes everywhere,
in both light and dark mode.
:root {
--fd-content-max: 820px;
--fd-sidebar-width: 280px;
--fd-accent: var(--fd-accent-light);
}The accent colour is the exception: it comes from feastdocs.config.mjs and is
written to :root at runtime, so you can change your brand colour without touching
SCSS at all.
Prefer tokens
If you find yourself hard-coding a hex value in a page stylesheet, check whether a token already exists. Using the token means the page follows dark mode for free.
2. Built-in utility classes
Two classes ship globally and work on any page, attached with the
{.class} attribute syntax:
The opening paragraph of a page.{.lead}
Ship it.{.callout}The opening paragraph of a page.
Ship it.
3. Site-wide overrides
src/styles/custom.scss is loaded last, so anything in it wins. This is where
your own site-wide rules belong — including new utility classes for markup you
write inside Markdown:
:root {
--fd-content-max: 940px;
}
.fd-markdown .highlight-box {
padding: 1rem;
border: 1px dashed var(--fd-accent);
}4. Page-scoped SCSS
Put a .scss file next to a page and it belongs to that page alone. This page has a
styling.scss sitting beside it, and everything below is styled by it.
.swatches {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 0.6rem;
}The build compiles it and wraps every rule in [data-doc-slug="guide/styling"]
before injecting it, which is why .swatches here cannot affect a .swatches
somewhere else. @use and @forward at the top of the file are hoisted out first,
so partials and mixins work normally.
Page stylesheets combine with {.class} attributes the same way the built-in
utilities do — the only difference is the class exists on this page alone.
Which level to use
| Situation | Level |
|---|---|
| Brand colour, content width, fonts | Tokens |
| A quick emphasis or intro paragraph | Built-in {.lead} / {.callout} |
| A class you use across many pages | custom.scss |
| A layout that exists on exactly one page | Page .scss |
Scoping is not isolation
Page styles are scoped by selector, not by shadow DOM. A rule like
p { color: red } in a page stylesheet still hits every paragraph on that page —
including ones the theme rendered. Prefer a class.