Patterns

Rules of the road

The conventions that keep every surface converged — theming, responsiveness, and the right way to consume the system from each stack.

Theming

Dark mode & the token auto-inversion rule

Dark mode is system-preference by default, with an explicit <html data-theme="dark|light"> override — never a separate theming mechanism. The interesting part is how the palette gets there: the token ramps invert themselves. In dark mode --success-100 resolves to a deep teal and --success-800 to a pale one, so a light-mode recipe like "100 background, 800 text" is automatically correct on a dark ground.

See it live

These pills use single token classes — no dark: anything. Toggle the theme (top right): they stay legible because the ramp flips underneath them.

bg-success-100 · text-success-800 bg-warning-100 · text-warning-800 bg-error-100 · text-error-800

The corollary is the most common mistake in app code: stacking Tailwind dark: variants on token colors double-inverts. The token already flipped; the dark: override flips it back (or somewhere worse) and washes out contrast. This bit two dashboard components before it became a rule.

Do
<span class="bg-success-100 text-success-800">
  Stable
</span>

One token recipe. The ramp inversion makes it correct in both themes.

Don't
<span class="bg-success-100 text-success-800
      dark:bg-success-900 dark:text-success-200">
  Stable
</span>

Double inversion. In dark mode this renders pale-on-pale and loses all contrast.

If a component looks wrong in dark mode, the fix is choosing better token steps — never adding dark:. And when adding a new color, ship both halves: the 50–950 ramp and its dark inversion, in the same change.
Color plumbing

Using tokens

Tokens are RGB triplets, which keeps alpha compositing in the consumer's hands.

/* plain CSS */
color: rgb(var(--primary-600));
background: rgb(var(--primary-500) / 0.16);   /* alpha over any ground */

/* Tailwind (preset maps the scales onto tokens) */
<div class="bg-primary-600 text-primary-50">

/* the preset also aliases legacy names during migration:
   blue→primary · green/teal→success · red/rose→error
   orange/amber/yellow→warning · gray/slate→secondary */
Never hardcode a brand hex in an app. If you're typing #219EFF anywhere outside tokens.css, stop and use the token.
Small screens

The responsive contract

Components ship mobile-ready. Apps must not override these down.

Integration

Consuming the system

React apps

Install from npm, import the stylesheet once, adopt the Tailwind preset, and import components. Auth (@readysetcloud/ui/auth) and chat (@readysetcloud/ui/chat) live on subpaths so you only pull what you use.

import '@readysetcloud/ui/styles.css';
import { AppNav, StatTile, SegmentedControl } from '@readysetcloud/ui';
import { AuthProvider, useAuth } from '@readysetcloud/ui/auth';

Hugo / static sites

Link the hosted stylesheet — or tokens.css alone to adopt the palette while keeping your own components.

<link rel="stylesheet" href="https://<assets-host>/ui/latest/styles/index.css">
<!-- or variables only: -->
<link rel="stylesheet" href="https://<assets-host>/ui/latest/styles/tokens.css">

Vanilla JS

Three script-tag globals cover what CSS can't: auth, the shared navbar, and drawing helpers. Everything else — stat tiles, pills, segmented controls, heroes — is just the shipped classes. Point the tags at the hosted assets bucket (the same S3/CDN host as the stylesheet above), never a local copy — that's how every surface stays on one version.

<script src="https://<assets-host>/ui/latest/auth.global.js"></script> <!-- window.rscAuth -->
<script src="https://<assets-host>/ui/latest/nav.global.js"></script>  <!-- window.rscNav -->
<script src="https://<assets-host>/ui/latest/ui.global.js"></script>   <!-- window.rscUi -->
<script>
  rscAuth.configureAuth({ region: 'us-east-1', clientId: '…' });
  rscNav.mountAppNav('#nav', { appName: 'Bootcamp' /* … */ });
  rscUi.renderSparkline(document.querySelector('#trend'), [38, 42, 45, 48]);
</script>
Process

Contributing

The system only works if it stays the single source of truth. If an app needs a variant that doesn't exist, add it to rsc-core/ui first — never fork a component into an app.

Checklist for changesWhy
Keep class names stableApps (and Hugo partials) depend on them directly.
Both light and dark values for new tokensThe auto-inversion contract only holds if every ramp defines both halves.
Ship the CSS class with the React componentReact/CSS parity is what keeps vanilla surfaces pixel-identical.
Mobile-first (44px targets, ≥16px inputs)The responsive contract is part of the component API.
Bump version in ui/package.jsonDeploy publishes hosted assets under the version; CI publishes to npm when it's new.
This guide deploys from design-system/ on every push to main that touches it or the ui package — the workflow rebuilds the package and republishes, so docs and package can't drift.