Files
README.md

web_themes #

Shared Web UI themes for ATProto CRA apps (altifier, verifier, sifter, ...). One repo to own the theme, apps pull it at build time — no more copy-paste drift between repos.

Structure #

web_themes/
  scripts/sync-theme.sh      build-time pull script (copy into consumers)
  themes/
    <theme-name>/
      theme.css              the theme: tokens + shared component styles
      index.css              body/base boilerplate (font, smoothing, transition)

Themes #

witchsky-cyan #

The original shared base theme from altifier + verifier (dark, cyan accent, 18px base font, WCAG AA, 48px touch targets). Altifier's home/login selectors are the superset, so it serves both apps.

Design tokens (defined in :root, components use var() only):

token value use
--background #111822 page background
--text #F9FAFB primary text
--text-muted #A5B2C5 secondary text
--button-bg hsl(174, 83%, 38%) primary buttons, links
--button-text #FFFFFF text on primary buttons
--button-hover-bg hsl(174, 78%, 32%) primary buttons hover
--card-bg #1C2B35 cards, forms, inputs
--card-border #394A58 borders
--navbar-bg #15232C navbar, footer, inset fields
--success-bg rgba(45, 242, 143, 0.12) success status box
--success-text #2CF28F success text, approve btn
--success-border rgba(45, 242, 143, 0.25) success status box border
--error-bg rgba(246, 90, 127, 0.12) error status box
--error-text hsl(348, 92%, 64%) error text, destructive btn
--error-border rgba(246, 90, 127, 0.25) error status box border

Shared component styles: body, typography, app/main containers, forms, inputs/selects, buttons, navbar, footer, home/login cards, status boxes, suggestions list, list items, revoke button (flash-red-warning), load-more, mobile media query.

Consuming a theme (build-time pull) #

  1. Copy scripts/sync-theme.sh AND scripts/theme-contract.sh into the consumer repo's scripts/.

  2. Wire package.json:

    "sync:theme": "bash scripts/sync-theme.sh",
    "build": "npm run sync:theme && react-scripts build",
    "start": "npm run sync:theme && react-scripts start"
    
  3. Import the synced files BEFORE the app's own CSS so app rules can override theme rules (index.js, imports are order-sensitive):

    import "./styles/theme/index.css";
    import "./styles/theme/theme.css";
    import App from './App';   // App.css comes after — overrides theme
    
  4. Gitignore the generated copy: add /src/styles/theme to .gitignore.

The script shallow-clones into ~/.cache/web_themes and copies themes/<name>/theme.css + themes/<name>/index.css into src/styles/theme/. Always pulls latest main. Override the repo with WEB_THEMES_REPO (e.g. a local path for testing, or the HTTPS form https://tangled.org/psingletary.com/web_themes for machines without a Tangled SSH key).

Versioning model (decision 2026-10-16) #

Pull-main, enforced at build: themes are NOT versioned/tagged yet. A theme change must keep the token NAMES stable (that is the contract) and is verified by the contract check — see below — which fails the consumer build at the exact commit that broke a token. If explicit historical themes are ever wanted, tag themes/<name>/vN and let consumers pin via a new THEME_REF env in sync-theme.sh. Until then: only change token values, never names, and update every consumer's build after a token-name change.

Contract check (part of every sync) #

After copying, sync-theme.sh runs scripts/theme-contract.sh --consumer: every var(--x) in the consumer's CSS must resolve to a token defined in the theme OR in the consumer's own CSS (App.css may define app-local tokens). Undefined tokens fail the build with the list. THEME_CONTRACT=0 disables the check. The theme side is checked with theme-contract.sh --theme <css>: no hardcoded hex/hsl/rgb colors outside :root.

Consumer matrix #

Consumer Repo Theme Sync wired
verifier tangled.org/psingletary.com/verifier witchsky-cyan yes
altifier tangled.org/psingletary.com/altifier witchsky-cyan yes
sifter tangled.org/psingletary.com/sifter witchsky-cyan yes
timer tangled.org/psingletary.com/timer witchsky-cyan yes (2026-10-16)
slicer tangled.org/psingletary.com/slicer witchsky-cyan yes (2026-10-16)

To add a consumer: copy both scripts, wire the npm scripts, import the theme files before App.css, gitignore the generated copy, and add a row above.

Adding a new theme #

  1. Create themes/<name>/ with theme.css (tokens in :root, then component styles using only var()) and index.css (body base).
  2. Keep the token NAMES stable across themes — consumers reference the variable names, not values. Only the values change per theme.
  3. Update this README's theme list.

Conventions #

  • AGENTS.md is the primary agent instructions file.
  • No hardcoded hex/Tailwind colors outside :root token definitions; all component rules use var(). Zero emojis in code/docs.
  • Any frontend design/polish/audit work loads the impeccable skill first.