creatordesign system
GitHub
Usage — CSS · SCSS · Tailwind
Start · Creator Design System

Usage — CSS · SCSS · Tailwind

How to actually use this: a complete first page, the token contract, and how it composes with Tailwind, SCSS, your own CSS and any framework — plus what to check when it looks wrong.

Getting it

Two ways in today, and a third once the package is published. They give you the same CSS.

html
<link rel="stylesheet"
      href="https://cdn.jsdelivr.net/gh/imswarnil/Creator-Design-System@main/dist/creator.min.css">

the CDN — no install, no build, nothing to configure. Pin @main to a tag for production

Or download dist/creator.css from the repository and link it. That is the whole installation — no init step, no config file, no runtime.

bash
npm install creator-design-system

coming with the first tagged release; the examples below use these paths

The npm package is not published yet, so that command does not work today. Everything below is written against the package paths it will use, because they are the paths the exports map in package.json already declares — the CDN link above gives you the same stylesheet in the meantime.

Your first page

A complete document — nothing omitted. Copy it into an .html file, open it, and the system is running.

html
<!doctype html>
<html lang="en" data-theme="light">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>My site</title>

  <!-- 1 - the type the system expects -->
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Inter:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap">

  <!-- 2 - the system -->
  <link rel="stylesheet" href="node_modules/creator-design-system/dist/creator.css">

  <!-- 3 - your overrides, always AFTER the system -->
  <style>
    :root {
      --accent: #f04e2e;
      --font-display: 'Space Grotesk', sans-serif;
    }
  </style>
</head>
<body>
  <header class="nav-shell">
    <nav class="nav-bar" aria-label="Main">
      <a class="logo logo-sm" href="/">Your name</a>
      <div class="nav-links">
        <a class="nav-link" href="/watch/" aria-current="page">Watch</a>
        <a class="nav-link" href="/learn/">Learn</a>
      </div>
      <div class="nav-actions">
        <a class="btn btn-primary btn-sm btn-pill" href="/subscribe/">Subscribe</a>
      </div>
    </nav>
  </header>

  <main class="container section">
    <h1 class="t-display-2">A page, already styled</h1>
    <p class="t-lead u-mt-5">Every class above exists. Nothing was invented.</p>

    <div class="deck deck-sm u-mt-8">
      <article class="card">
        <div class="card__body">
          <p class="card__meta">Build log - 12 min</p>
          <h2 class="card__title"><a class="card__link" href="/post/">A post</a></h2>
          <p class="card__excerpt">The card is the same object in every collection.</p>
        </div>
      </article>
    </div>
  </main>
</body>
</html>

no build step, no bundler, no framework: this is the whole integration

The fonts are the only external dependency, and even they are a choice. Point --font-display, --font-body and --font-slate at anything you like and drop the <link>.

Taking less than all of it

Import a layer directly when you do not want the whole system. Each layer needs only the ones below it, so you can stop at any floor.

css
/* everything, in one line */
@import "creator-design-system";

/* or floor by floor - later layers need the earlier ones */
@import "creator-design-system/foundation";   /* tokens only                */
@import "creator-design-system/elements";     /* + text, badge, table, code */
@import "creator-design-system/components";   /* + buttons ... navbar       */
@import "creator-design-system/sections";     /* + hero, stats, CTA, footer */
@import "creator-design-system/utilities";    /* + u- helpers               */

/* not included by default: it ships to YouTube and Instagram, not a website */
@import "creator-design-system/broadcast";

foundation alone is already usable — it is where every decision lives

Customising — the token contract

Never edit the source. Override tokens after the import; every component reads them live, in both themes. This is the entire customisation API.

css
@import "creator-design-system";

:root {
  --accent:        #6d4aff;                    /* your signal colour */
  --font-display:  'Clash Display', sans-serif;
  --radius-card:   1.25rem;
}

three lines rebrands the site, the buttons, the cards and the thumbnails

Tokens come in two tiers, and that distinction is the whole reason one override reaches everything. Primitives are the ladders — --ink-500, --signal-500, --space-4. Semantic tokens name a job — --fg-muted, --accent, --line-default — and point at a primitive. Components read the semantic tier only, so re-pointing one semantic token changes every component that meant that thing.

TokenWhat it decides
--accentthe one rationed colour: buttons, active states, the record light
--bg-canvas · --bg-surface · --bg-sunkenthe three background depths
--fg-default · --fg-muted · --fg-subtle · --fg-faintthe text ladder, strongest first
--line-subtle · --line-default · --line-strongthe three hairline weights
--font-display · --font-body · --font-slateheadings · reading · labels and code
--space-1 … --space-16the spacing ladder — nothing invents a gap outside it
--radius-sm … --radius-pillthe corner ladder, plus --radius-card
--dur-1 … --dur-5 · --ease-outmotion: durations and curves

The full list lives on Color, Typography and Spacing — or in one read at llms-full.txt.

Light and dark

Both themes come from the same variables. Nothing is duplicated, and no component knows which theme it is in.

html
<html data-theme="dark">

the switch — that is all the system needs

js
// Follow the OS, remember a choice, and set it before first paint so the page
// never flashes the wrong theme.
(function () {
  var saved = localStorage.getItem('theme');
  var dark = saved ? saved === 'dark'
                   : matchMedia('(prefers-color-scheme: dark)').matches;
  document.documentElement.dataset.theme = dark ? 'dark' : 'light';
})();

inline in <head>, above the stylesheet link

With Tailwind

The two do different jobs and compose well: Tailwind gives you utilities, this gives you decided components. The u- prefix means there are no class collisions.

Order matters more than anything else here. Both ship a reset — Tailwind calls its one Preflight — and the last one loaded wins. Import Tailwind first, so this system’s reset and components sit on top of it.

css
/* app.css - Tailwind 4 */
@import "tailwindcss";
@import "creator-design-system";        /* after Tailwind, always */

/* Hand the tokens to Tailwind so bg-accent, text-muted and friends exist.
   `inline` is the important word: it tells Tailwind these values reference
   other custom properties, which keeps modifiers like bg-accent/50 working. */
@theme inline {
  --color-accent:  var(--accent);
  --color-canvas:  var(--bg-canvas);
  --color-surface: var(--bg-surface);
  --color-ink:     var(--fg-default);
  --color-muted:   var(--fg-muted);
  --radius-card:   var(--radius-card);
}

Tailwind 4 — one import each, then map the tokens

js
// tailwind.config.js - Tailwind 3
module.exports = {
  theme: {
    extend: {
      colors: {
        accent:  'var(--accent)',
        canvas:  'var(--bg-canvas)',
        surface: 'var(--bg-surface)',
        ink:     'var(--fg-default)',
        muted:   'var(--fg-muted)',
      },
      borderRadius: { card: 'var(--radius-card)' },
    },
  },
  // Optional: this system brings its own reset, so you may not want two.
  corePlugins: { preflight: false },
};

Tailwind 3 — the same idea, in the config file

Now both vocabularies work on one element, and they agree by construction, because they are reading the same variable:

html
<article class="card mt-8 lg:mt-12">
  <div class="card__body">
    <h2 class="card__title">Component from the system</h2>
    <p class="text-muted">Spacing and layout from Tailwind</p>
  </div>
</article>

<!-- change --accent once and both of these follow -->
<button class="btn btn-primary">System button</button>
<span class="bg-accent text-white px-3 py-1 rounded-full">Tailwind chip</span>

no collisions — every helper here is u- prefixed and every component is a noun

Which oneFor what
Use the system forcomponents with parts and states — cards, navbar, forms, buttons, sections. The things where the decisions matter.
Use Tailwind forone-off layout on a page: grid, flex, margins, responsive tweaks.
Do notrebuild a card out of utilities. That is how two pages stop matching.

If you would rather not load two resets at all: import creator-design-system/foundation and skip 00-reset.css, or turn Preflight off as above. Either is fine. Loading both in the wrong order is not.

With SCSS

Nothing special is required, and one thing is worth knowing.

scss
// main.scss
@use "creator-design-system/dist/creator.css";

.promo {
  // SCSS variables are compile-time; the tokens are not, and that is the point
  padding: var(--space-6);
  border-radius: var(--radius-card);
  background: var(--bg-sunken);
}

the tokens stay custom properties, so runtime theming survives compilation

Resist copying tokens into SCSS variables. A SCSS variable is resolved when you build; a custom property is resolved when the page runs. Only the second can flip to dark mode, or be re-pointed by a reader setting, without a rebuild.

With your own CSS

The system is meant to be built on. There is a right way to add to it, and it is the same way the system adds to itself.

css
@import "creator-design-system";

/* Your component. Written in the system's tokens, so it inherits the theme,
   dark mode, the motion rules and any future rebrand - for free, forever. */
.testimonial {
  display: grid;
  gap: var(--space-4);
  padding: var(--space-6);
  background: var(--bg-surface);
  border: var(--border-hair) solid var(--line-default);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-1);
}

.testimonial__quote {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  line-height: var(--leading-snug);
  color: var(--fg-default);
  text-wrap: balance;
}

.testimonial__who {
  font-family: var(--font-slate);
  font-size: var(--text-2xs);
  letter-spacing: var(--tracking-slate);
  text-transform: uppercase;
  color: var(--fg-faint);
}

/* State goes in ARIA, not in a class the accessibility tree cannot see. */
.testimonial[aria-current] { border-color: var(--accent); }

a new component that behaves like a native one, because it reads the same variables

Extending the system
Dowrite new components in tokens — var(--space-4), never 16px
Dofollow the naming: .block, .block__part, .block-variant
Dostyle from ARIA — [aria-current], [aria-expanded], [data-*]
Don’tedit anything inside src/ — the next update overwrites it
Don’tadd a second accent hue; the palette is closed so one colour can mean something
Don’tinvent an .active class. It can disagree with the accessibility tree, and then the two of them tell different people different things

In a framework

It is a stylesheet, so it goes where stylesheets go.

StackHow
React · Next · Vueimport the CSS once at the app root, then use className="card" as normal. No provider, no theme object, no runtime
Astro · 11ty · Hugolink it in the base layout
Ghostlink it in default.hbs — the collection cards were designed against routes.yaml
Svelteimport in the root component, or link it in app.html

The two optional scripts — nav.js and highlight.js — are plain, framework-free and additive. Without them you get sticky bars, click-only dropdowns and uncoloured code. Nothing breaks.

Reading the class names

The shape of a name tells you what kind of thing it is, so you can guess correctly.

ShapeMeans
.carda component — a noun, on its own
.card__titlea part of it — two underscores
.card-inversea variant of it — one hyphen
.btn-primary · .btn-smintent and size on the same component
.u-mt-6 · .u-fg-subtlea utility — always u- prefixed, always one job
.t-display-1 · .t-leada type role, not a size
[aria-current] · [aria-expanded]state — an attribute, never a class

When it looks wrong

Five things account for nearly all of it.

SymptomAlmost always
Everything is unstyledthe class does not exist. Check it against llms-full.txt — nothing in this system is invented, so a name either is or is not real
Overrides do nothingthey are above the import. Tokens must be set after the system loads
The type looks wrongthe fonts are not loaded. Add the <link>, or re-point --font-* at what you do have
Tailwind flattened itPreflight loaded last. Import Tailwind first, or turn Preflight off
Dark mode does nothingdata-theme is missing from <html> — it does not go on <body>