Skip to content
NativeCSS
Docs contents

Build Your Own Theme

This page is the practical path from an empty project to a fully-branded page - step by step, with a ready-to-copy theme.css. The mechanism BEHIND it (cascade layers, seed values vs. derived scale) is explained separately under Theming.

Step 1 - Copy the files into your project

ncss needs no build step and no npm package - the whole dist/ folder is already finished, ready-to-serve CSS/JS. Just copy it:

# Copy dist/ into your own project, e.g. to assets/ncss/
cp -r dist/ my-project/assets/ncss/

Two lines in the HTML head - ncss.css first, your own theme.css right after it (that file doesn't exist until step 2, but the base system already works fine without it):

<link rel="stylesheet" href="assets/ncss/ncss.css">
<link rel="stylesheet" href="assets/ncss/theme.css">

Add Web Awesome/Font Awesome only if you actually need them - see Web Awesome Bridge; most projects are entirely fine with native CSS/HTML.

Step 2 - Create your own theme.css

The bundled dist/theme.css holds NativeCSS' own brand values (this site's bold blue) - as a starting point for your own project, use this block instead and replace the marked spots. Anything NOT overridden here simply stays at the ncss default:

/* theme.css - your own brand values, load after ncss.css */
:root, :host {
  /* Primary brand (buttons, links, focus ring) - LIGHT, DARK */
  --ncss-color-brand: light-dark(#4f46e5, #a5b4fc);
  --ncss-color-brand-contrast: light-dark(#ffffff, #1a1533);

  /* Secondary color (accents, secondary buttons) */
  --ncss-color-brand-2: light-dark(#0f766e, #5eead4);
  --ncss-color-brand-2-contrast: light-dark(#ffffff, #04211d);

  /* Page background and text colors - defaults are neutral/white, only change if needed */
  --ncss-color-bg: light-dark(#ffffff, #0f1115);
  --ncss-color-surface: light-dark(#ffffff, #1a1d24);
  --ncss-color-text: light-dark(#1a1a1a, #eef2f8);

  /* Font - system-ui needs no load time, enter your own web font here */
  --ncss-font-family-base: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

  /* Corners - 0 for sharp, larger values for rounder */
  --ncss-radius-sm: 0.25rem;
  --ncss-radius-md: 0.5rem;
  --ncss-radius-lg: 1rem;
}

That's already enough: buttons, badges, card borders, the focus ring and the entire color scale (100/300/700/900, -on-soft) pick up the new brand automatically - ncss computes the scale from exactly these few values via color-mix(), see Only seed values, no derivations. Every other value (status colors, shadows, motion, layout widths) is documented in full in dist/theme.css - only add what you actually need, don't copy everything preemptively.

Step 3 - Test dark mode for free

Every light-dark(...) value above automatically covers both color schemes - no second stylesheet, no hand-written prefers-color-scheme media query needed. To test it, just switch light/dark at the OS level or in browser DevTools (Chrome: DevTools → Rendering → "Emulate CSS media feature prefers-color-scheme") and check that both values in each light-dark() pair actually work together - enough contrast, no glaring surface in dark mode.

Step 4 - Check contrast

A new brand color automatically shifts buttons, links, badges and the whole Web Awesome bridge - worth a quick check with real Lighthouse (Chrome DevTools → Lighthouse → Accessibility), not just eyeballing it:

  • White/light text on --ncss-color-brand (button labels) needs at least 4.5:1 contrast for body text, 3:1 for large text.
  • If contrast falls just short, darken/saturate the brand color itself rather than patching individual text colors afterward - see Accessibility.

Step 5 - Switch fonts (optional)

Load your own web font (e.g. self-hosted @font-face or a font provider), then just enter the name in theme.css - the entire fluid size scale (clamp()-based, see Typography) stays untouched:

--ncss-font-family-base: "Inter", system-ui, sans-serif;
--ncss-font-family-heading: "Fraunces", serif;

Heading weight and body weight are two dedicated semantic tokens (--ncss-font-weight-heading/-body) instead of being hardwired to the weight scale itself (-normal/-medium/-semibold/-bold) - useful when a chosen font only ships certain weights (e.g. a variable font offering just 300/600):

--ncss-font-weight-heading: var(--ncss-font-weight-semibold); /* instead of -bold */
--ncss-font-weight-body: var(--ncss-font-weight-normal);

<strong>/<b> stay unaffected (they keep referencing --ncss-font-weight-bold directly) - "emphasized as bold" should stay independent of whatever weight a heading currently uses.

Step 6 - Multiple themes (optional)

For multiple brands/domains on the same codebase: repeat the same setup in another file (e.g. theme-project-b.css) and load it instead of theme.css depending on the domain - details and the attribute-driven [data-palette] alternative under Multiple themes in parallel.

Step 7 - Tune individual components to your taste

theme.css changes seed values GLOBALLY - every card, every button, every badge follows automatically. Sometimes that's not enough: only the badges should be sharper-cornered, only the button shadow should feel bolder, without touching the rest of the page. That needs no copy of the ncss file and no !important - a single rule in your own UNLAYERED CSS file (the same trick as theme.css itself, see Theming) is enough, because unlayered CSS always wins against any layered rule per spec:

/* mine.css - load after theme.css */
.ncss-badge {
  border-radius: var(--ncss-radius-sm); /* instead of the full pill shape */
}
.ncss-card {
  box-shadow: var(--ncss-shadow-lg); /* bolder shadow than the default */
}

Which properties a component actually has and which modifier classes (--primary, --horizontal, ...) already exist is documented in the Component Reference - check there BEFORE hard-overriding a property: often there's already an intended variant or token for it (e.g. --ncss-radius-* instead of a fixed px value), which then keeps following theme.css automatically instead of needing to be re-applied by hand at the next brand update.

Still not enough - a completely new, custom piece that doesn't exist in ncss at all: Build Your Own Components.

Checklist

  • dist/ copied into the project, ncss.css included
  • Your own theme.css loaded after ncss.css, brand/secondary color set
  • Dark mode checked (DevTools emulation or system toggle)
  • Contrast checked with real Lighthouse
  • If needed: your own font, multiple themes, individual component tweaks