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.

src/styles/_tokens.scss
: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:

src/styles/custom.scss
: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.

docs/guide/styling.scss
.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.

surface
sunken
accent
border

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.