# Design System

Private, versioned CSS shared across Spencer Barnes's apps (Texta, Simple Invoice,
Dateline, and future builds) so they feel like one family and can be updated
from a single place.

**This repo is private.** The compiled CSS files it publishes are necessarily
fetchable by any browser loading one of the apps (that's how a `<link
rel="stylesheet">` works) — but the source, structure, and this README never
go public. Treat it the same as any other proprietary codebase.

## How an app links to it

There are three "recipes" depending on what kind of page you're building.

**App/dashboard pages** (Texta's Dashboard, Compose, Contacts, etc.):

```html
<link rel="stylesheet" href="https://design.spencerbarnes.dev/v1/tokens.css">
<link rel="stylesheet" href="https://design.spencerbarnes.dev/v1/base.css">
<link rel="stylesheet" href="https://design.spencerbarnes.dev/v1/components.css">
<link rel="stylesheet" href="https://design.spencerbarnes.dev/v1/shell.css">
```

**Public/marketing pages** (landing pages, pricing pages, help center):

```html
<link rel="stylesheet" href="https://design.spencerbarnes.dev/v1/tokens.css">
<link rel="stylesheet" href="https://design.spencerbarnes.dev/v1/base.css">
<link rel="stylesheet" href="https://design.spencerbarnes.dev/v1/components.css">
<link rel="stylesheet" href="https://design.spencerbarnes.dev/v1/marketing.css">
```

**Never load `shell.css` and `marketing.css` on the same page** — they represent
two different visual contexts (app scaffolding vs. public content) and aren't
designed to combine. `components.css` (buttons, badges, forms, tables, modals)
is shared by both, since both contexts need the same base UI pieces.

Order always matters: `tokens.css` → `base.css` → `components.css` → the
optional 4th layer (`shell.css` or `marketing.css`), which loads last since it
sometimes overrides earlier rules.

If the app is React/Vite instead of plain HTML, `@import` the same URLs at
the top of the app's own root stylesheet instead of using `<link>` tags —
same order, same effect.

## Setting the brand color

Every app looks like itself, not like Texta, by setting two CSS variables at
runtime — usually right after the app's account/settings data loads:

```js
document.documentElement.style.setProperty('--brand', '#f97316');
document.documentElement.style.setProperty('--brand-rgb', '249, 115, 22');
```

Everything else — button glows, badge tints, orb colors, focus rings — derives
automatically from those two values. Don't hardcode any other brand-specific
color anywhere in the app; if something needs a brand tint, it should already
have a token for it (`--brand-light`, `--brand-shadow`, `--brand-border`). If
it doesn't, that's a sign the token is missing from `tokens.css`, not a reason
to hardcode a color in the app.

The default is `transparent`, on purpose — this avoids a flash of the wrong
color before the app's real brand color has loaded.

## Versioning

- `/v1/`, `/v2/`, etc. are permanent once published. An app pinned to `/v1/`
  will never break, no matter what changes in `/v2/`.
- To evolve the system: work on a `dev` branch (see **Previewing changes**
  below), get it right, then copy the finished files into a new `/v2/` folder
  on `main` and merge. Apps upgrade to `/v2/` on your own schedule, one at a
  time, by changing their `<link>` URLs.
- Never edit files inside an already-published version folder. If a real bug
  in `/v1/` needs fixing (not a style change, an actual bug), that's the one
  exception — fix it in place and note it in the changelog, since apps expect
  `/v1/` to be stable, not evolving.

## Previewing changes before they go live

Cloudflare Pages generates a unique preview URL for every branch you push,
completely separate from the production `design.spencerbarnes.dev` domain.

1. Create a branch: `git checkout -b dev`
2. Make your changes, commit, push: `git push origin dev`
3. Cloudflare Pages auto-builds it and gives you a URL like
   `dev.design-system-xyz.pages.dev`
4. Open `test.html` with its `<link>` tags pointed at that preview URL instead
   of production, and eyeball every component
5. Happy with it? Merge `dev` into `main`. Not happy? Keep pushing to `dev` —
   production never sees any of this until you merge.

The preview URL is gated behind Cloudflare Access (your email only) so
in-progress work never becomes publicly visible, even accidentally.

## Adding a new component

1. Add the class to `components.css`, following the existing patterns (use
   tokens for every color/spacing/radius value — never a raw hex or px)
2. Add an example of it to `test.html`
3. Preview it via the `dev` branch workflow above
4. Note it in `CHANGELOG.md` under "Unreleased"
5. When you're ready to publish, fold "Unreleased" into a new version

## Source of truth

- `v1/tokens.css` — every color, font, spacing, radius, shadow, and transition
  value, as CSS custom properties. Nothing outside this file should contain a
  raw hex color, a raw px value for spacing/radius, or a font name — reference
  the token instead.
- `v1/base.css` — resets and bare-element styling (body, headings, links,
  buttons, inputs, tables) using those tokens, so an unstyled page looks
  reasonably on-brand immediately.
- `v1/components.css` — the shared UI class library used by both app and
  marketing contexts: buttons, cards, badges, chips, tables, modals, toasts,
  forms (including checkbox/radio/search/stepper/dropzone), the slider system.
  This is the layer most day-to-day design work happens in.
- `v1/shell.css` — app scaffolding: sidebar, topbar, mobile nav, profile
  card/avatar, the app switcher, drawer/side-panel, breadcrumbs, tabs,
  pagination. Only app pages load this.
- `v1/marketing.css` — public-page patterns: navbar, hero, feature grid,
  stat strip, pricing table, testimonials, FAQ accordion, CTA banner, footer.
  Only marketing pages load this.

### The app switcher

`shell.css` includes a Google-style app switcher (`.app-switcher-*` classes) —
a grid of your products that opens from a topbar icon. Per the ecosystem plan,
**only render this when a user has more than one active product.** A
single-product user (most Texta customers) should never see it; the shell
should look identical to a single-app experience for them. Once Simple Invoice
ships and a user has both, the switcher appears automatically for that user.

If this repo is ever handed to a designer, these five files plus this README
are the entire onboarding — there's no other documentation to find.
