Skip to content

CSS tokens

These are the CSS custom properties moss defines. Override any of them in .moss/theme/style.css — no !important needed.

The full size scale runs --moss-size-{2xs,xs,sm,md,lg,xl,2xl,3xl}.

CategoryVariableDefaultDescription
code--moss-code-accent-primary#2d5a2dCode accent primary — moss green, systems/primary languages. Dark: pinned to #6a9a5a (was color-mix(accent 80%, white)) — since --moss-color-accent now carries its own lightened dark value, re-deriving here would double-lighten the syntax green; pinning keeps code colors stable.
code--moss-code-accent-quaternary#6b5b4fCode accent quaternary — deeper brown, config/data
code--moss-code-accent-secondary#5d5853Code accent secondary — warm gray, web languages
code--moss-code-accent-tertiary#74706cCode accent tertiary — muted brown, scripting
code--moss-code-background#f8f6f3Code block background
code--moss-code-border#e0ddd6Code block border
color--moss-border-light#e6e2dbLight border color
color--moss-border-medium#d1cdc4Medium border color
color--moss-border-strong#8e8b85Border for elements whose boundary IS the control — the .moss-input underline. Meets WCAG 1.4.11 non-text contrast (3:1) against the page. Decorative hairlines stay on border-light/medium, which are exempt.
color--moss-color-accent#2d5a2dLinks, highlights, accent elements. Dark: lightened forest green (#6a9a5a ≈ 5.3:1 on --moss-color-bg #1c1914) — the raw #2d5a2d is only ~2.2:1 in dark, failing WCAG AA as text/border/icon. #6a9a5a matches the green the codebase already derives for accent-hover/code in dark.
color--moss-color-accent-hovercolor-mix(in oklch, var(--moss-color-accent) 85%, black)Accent color on hover — auto-derived from --moss-color-accent. Light: darkens accent by mixing 85% accent + 15% black (hover = press-down cue). Dark: lightens accent by mixing 80% accent + 20% white (hover = surface lift). Override only if the mix produces a wrong result for a custom palette.
color--moss-color-accent-quietrgba(45, 90, 45, 0.28)Quiet accent for ambient indicators (hairlines, tree-row left-borders). ~28% opacity of moss-color-accent. Consumed by the breadcrumb and preview hairlines (the ambient renderer). Ambient only — never a focus indicator: it composites too close to the page to satisfy 1.4.11. Use moss-color-ui-accent for focus.
color--moss-color-bg#faf8f5Page background
color--moss-color-muted#716d69Secondary/muted text. Light value meets 4.5:1 on bg, surface and code backgrounds (WCAG 1.4.3) — it is body text at 14px in ~32 places, not decoration.
color--moss-color-surface#f4f1ecCard and surface background
color--moss-color-text#2c2825Primary text
color--moss-color-text-secondary#5d5853Secondary text color — darker than --moss-color-muted, used for readable secondary content (nav links, dates, excerpts). Renamed from --moss-text-secondary in v1.3.
color--moss-color-ui-accentvar(--moss-color-accent)Accent for navigation, buttons, and site controls (chrome). Defaults to the content accent; set to a neutral (e.g. var(--moss-color-text)) for quiet chrome.
elevation--moss-edge-lightrgba(255, 255, 255, 0.45)The hairline highlight on the edge that faces the light. Nearly invisible on paper; on the dark ground it is what lifts a surface, since the shadow alone cannot.
elevation--moss-elevation-1inset calc(var(--moss-light-x) * -1px) calc(var(--moss-light-y) * -1px) 0 var(--moss-edge-light), calc(var(--moss-light-x) * -6.8px) calc(var(--moss-light-y) * -6.8px) 4.8px 0.8px color-mix(in srgb, var(--moss-shadow-color) calc(32% * var(--moss-shadow-strength)), transparent)A resting control on its parent: pills, toggle thumbs, close buttons, a hovered card. The lit edge plus Ambient's sheet cast at elevation 1: 6.8px along the light, 4.8px blur, 32% × strength.
elevation--moss-elevation-2inset calc(var(--moss-light-x) * -1px) calc(var(--moss-light-y) * -1px) 0 var(--moss-edge-light), calc(var(--moss-light-x) * -13.6px) calc(var(--moss-light-y) * -13.6px) 9.6px 0.8px color-mix(in srgb, var(--moss-shadow-color) calc(24% * var(--moss-shadow-strength)), transparent)Floating chrome over the shell or page: tooltips, menus, popovers, toasts, the nav island. Elevation 2: 13.6px along the light, 9.6px blur, 24% × strength.
elevation--moss-elevation-3inset calc(var(--moss-light-x) * -1px) calc(var(--moss-light-y) * -1px) 0 var(--moss-edge-light), calc(var(--moss-light-x) * -20.4px) calc(var(--moss-light-y) * -20.4px) 14.4px 0.8px color-mix(in srgb, var(--moss-shadow-color) calc(16% * var(--moss-shadow-strength)), transparent)A layer over everything, usually with a backdrop: modal, command palette. Elevation 3: 20.4px along the light, 14.4px blur, 16% × strength.
elevation--moss-light-x0Where the light comes from, horizontally: -1 is the left edge, 1 the right, 0 straight above. Shadows fall the other way.
elevation--moss-light-y-0.5Where the light comes from, vertically: -1 is above, 1 below. moss is lit from directly above, like paper under a lamp.
elevation--moss-shadow-color#2b2117The colour of every shadow before opacity. Warm near-black on paper, because a black shadow on a warm ground is the wrong colour; black on the dark ground.
elevation--moss-shadow-strength0.5Multiplier on every shadow opacity. A dark ground needs a much stronger shadow to read at all.
layout--moss-container-paddingclamp(1rem, 5vw, 2rem)Container side padding
layout--moss-content-widthcalc(42 * var(--moss-reading-size))Maximum content width. Font-stable: a multiple of --moss-reading-size (a length), NOT a ch value, so text, inline images, and figures resolve to the same column at any element font-size (a ch measure drifts per-element and desyncs text from figures when a theme changes the prose font). 42 ≈ the prior 67ch for the default font; tracks the scale-* presets via --moss-reading-size.
layout--moss-content-width-sidebarcalc(39 * var(--moss-reading-size))Content width when sidebar is active. Font-stable multiple of --moss-reading-size (≈ prior 62ch); see moss-content-width.
layout--moss-sidebar-width280pxSidebar width
layout--moss-site-max-width1200pxMaximum overall site width
layout--moss-width-widemin(calc(56 * var(--moss-reading-size)), var(--moss-site-max-width))Width of a data-width="wide" content band. Font-stable multiple of --moss-reading-size like moss-content-width; the min() clamp is load-bearing — the reader font-scale control scales --moss-reading-size (scale-xlarge = 1.25x), and without the clamp a wide band at xlarge overruns --moss-site-max-width and stops aligning with the nav. data-width="page" reuses --moss-site-max-width directly (it must stay identical to the .container width or the band stops aligning with nav/footer); there is deliberately no second token for it.
spacing--moss-space-2xl4remDouble extra large (64px)
spacing--moss-space-lg2remLarge (32px)
spacing--moss-space-md1.5remMedium (24px)
spacing--moss-space-sm1remSmall (16px)
spacing--moss-space-xl3remExtra large (48px)
spacing--moss-space-xs0.5remExtra small (8px)
syntax--moss-hl-addition-bgrgba(74, 106, 42, 0.1)Diff addition background.
syntax--moss-hl-attr#5a6a4aSyntax: attribute
syntax--moss-hl-builtin#2d5a2dSyntax: builtin
syntax--moss-hl-comment#787064Syntax: comment. Both values meet 4.5:1 on the code background — a comment is text a reader is expected to read, so it is not exempt as decoration.
syntax--moss-hl-deletion#8a4a3aSyntax: deletion
syntax--moss-hl-deletion-bgrgba(138, 74, 58, 0.1)Diff deletion background.
syntax--moss-hl-function#3a5a6aSyntax: function
syntax--moss-hl-keyword#7a5834Syntax: keyword (also used for tags — intentionally shared)
syntax--moss-hl-meta#7c6e5fSyntax: meta
syntax--moss-hl-number#6b5b8aSyntax: number
syntax--moss-hl-operator#5d5853Syntax: operator
syntax--moss-hl-string#4a6a2aSyntax: string
syntax--moss-hl-tag#7a5834Syntax: tag (same value as moss-hl-keyword — intentionally shared)
syntax--moss-hl-type#8a5a3aSyntax: type
typography--moss-font-body-apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Moss PingFang SC', 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Noto Sans SC', 'WenQuanYi Micro Hei', sans-serifBody text font stack. 'Moss PingFang SC' is the weight-pinned PingFang @font-face defined in site.css (see the PingFang weight pin comment there).
typography--moss-font-heading'Source Han Serif SC', 'Noto Serif CJK SC', 'Songti SC', 'STSongti', 'NSimSun', 'SimSun', 'Source Serif 4', 'Source Serif Pro', 'Times New Roman', Times, serifHeading font stack: serif by default for editorial weight. Han faces lead, because CSS fallback is per-glyph: with Latin serifs in front, every mixed heading drew its Latin from one file and its Han from another. The Latin fallbacks behind them are high-contrast transitionals (Source Serif, Times) — the closest Latin match for a Ming's hairline horizontals, where Iowan/Palatino are as unlike 宋體 as a serif gets. This mitigates the split; it does not remove it (a heading set in a Han face still borrows that face's own Latin). Only a webfont covering both scripts does that.
typography--moss-font-heading-weight480Weight for EVERY heading — the h1-h6 rule in site.css and article section headings alike. Article subheads used to pin 500 of their own, which made this token an authority over some headings and not others; on the CJK sans the two values are one face anyway (the PingFang pin resolves 400-449 to Regular, so 480 and 500 both land on Medium).
typography--moss-font-monoui-monospace, 'Cascadia Code', 'SF Mono', SFMono-Regular, 'JetBrains Mono', Menlo, Consolas, 'Liberation Mono', 'Courier New', monospaceMonospace stack for code
typography--moss-font-weight-body320Default body font weight (renamed from --moss-font-weight in v1.3)
typography--moss-read-captioncalc(var(--moss-reading-size) * 0.722)Figure caption — 13px at the default reading size. See --moss-read-title for what the ratio is measured against.
typography--moss-read-h1calc(var(--moss-reading-size) * 1.667)Article # section heading — 30px at the default reading size.
typography--moss-read-h2calc(var(--moss-reading-size) * 1.333)Article ## section heading — 24px at the default reading size.
typography--moss-read-h3calc(var(--moss-reading-size) * 1.125)Article ### section heading — 19px at the default reading size. Deliberately close to body: at this depth the level is carried by weight and the space above it, not by competing on size.
typography--moss-read-h4calc(var(--moss-reading-size) * 1.028)Article #### section heading — 18.5px at the default reading size.
typography--moss-read-h5calc(var(--moss-reading-size) * 0.889)Article ##### section heading — 16px at the default reading size.
typography--moss-read-h6calc(var(--moss-reading-size) * 0.778)Article ###### section heading — 14px at the default reading size.
typography--moss-read-leading1.75Body line-height, unitless. The Latin default; html[lang^="zh"] overrides it to 1.8 (CJK needs looser leading — see that rule in site.css) rather than setting line-height on body directly, so --moss-read-line (below) picks up the bump for both scripts from one place.
typography--moss-read-linecalc(var(--moss-reading-size) * var(--moss-read-leading))One line of body text — the unit of prose vertical spacing (article paragraph/heading/figure margins), the way --moss-reading-size is the unit of prose type size. Before this token, --moss-space-* rem chrome geometry did that job: fixed while body text ran 16px-24px across the four reader font-scale steps, so the paragraph gap shrank from 0.78 line to 0.56 line as the reader enlarged text, and the gap above a ## tied the figure gap instead of standing apart from it.
typography--moss-read-titlecalc(var(--moss-reading-size) * 2.222)Article / folder masthead — 40px at the default reading size. Every prose size is a ratio of --moss-reading-size, so the reader's font-scale control and the CJK size bump move the whole ladder together instead of sliding body copy past fixed headings. A theme retunes prose by overriding these ratios; --moss-size-* stays fixed because it is chrome geometry (the nav island reserves against --moss-size-lg).
typography--moss-reading-script-scale1Per-script multiplier folded into --moss-reading-size. site.css sets 1.06 for html[lang^="zh"]: CJK ideographs fill the em square, so they need ~+1px over Latin at the same nominal size. It lives on the reading size rather than on body { font-size } so headings and captions get the same bump.
typography--moss-reading-sizecalc(var(--moss-reading-size-base) * var(--moss-reading-script-scale) * var(--moss-reading-step))Effective reading text size, and the base of the whole prose scale (--moss-read-*): --moss-reading-size-base times the per-script multiplier times the reader's font-scale step (--moss-reading-step).
typography--moss-reading-size-base1.125remBody / reading text size before the per-script multiplier and the reader font-scale step. Prose headings and captions are ratios of the result (--moss-read-), so they follow this token; --moss-size- is chrome and does not.
typography--moss-reading-step1The reader's font-scale control, as a bare multiplier — the one variable site.css's html.scale-* classes write (0.89/1.12/1.25). Folded into --moss-reading-size below rather than left for each html.scale-* rule to re-derive the whole formula, so a second base (vertical typesetting's own body text, sized from --moss-vertical-font-size instead of --moss-reading-size-base) can multiply by this same token and pick up the control too, instead of the control silently doing nothing there.
typography--moss-size-2xl1.625rem26px
typography--moss-size-2xs0.75rem12px — metadata, captions, chip labels
typography--moss-size-lg1.25rem20px
typography--moss-size-md1.125rem18px. Chrome geometry, deliberately a fixed rem: it used to alias --moss-reading-size-base, which made it the one --moss-size-* that moved with the reading scale, so a non-prose h4 shrank on mobile while every sibling token held. Prose reads --moss-read-* instead.
typography--moss-size-sm1rem16px
typography--moss-size-xl1.375rem22px
typography--moss-size-xs0.875rem14px
typography--moss-vertical-font-sizeclamp(1rem, 1.9svh, 1.4rem)Body font size under vertical typesetting (data-typesetting="vertical"), read by crates/moss-build/src/assets/css/site/vertical.css. Scales with viewport height rather than width so the 38em column measure keeps the same share of the page on a short and a tall screen. The site-tunable knob for vertical type size; column placement (golden-section head margin) is a separate, non-tunable moss default.

Run moss describe --json for the live token list of your installed moss.

moss