# Style Guide Adoption: Single Source of Truth

> **Active Project** — design and implementation in progress.

**Status:** In Progress (Phase 2)
**Date:** 2026-03-13 (updated 2026-03-16)
**Style guide:** `public/styles/acequia-tokens.css` + `public/styles/index.html` (style book)
**Scope:** System pages (`public/*.html`), web components (`public/components/`), acequia apps (`acequia/stigmergic/*/`)

---

## Problem

Every system page and acequia app has its own inline CSS with hardcoded colors, spacing, and typography. The same values (`#1a1a2e`, `rgba(76, 175, 80, 0.2)`, `#e0e0e0`, etc.) appear in 15+ files. Changing the design requires editing every file individually, and inconsistencies creep in (e.g. some pages use `#888` for muted text, others `#666`).

The `acequia-tokens.css` file already defines the canonical values as CSS custom properties (`--acq-*`), but nothing uses them yet.

## Goal

Change a color or spacing value in **one file** → every system page, web component, and acequia app updates automatically.

## Architecture

### Single source of truth

```
public/styles/acequia-tokens.css    ← THE canonical file (CSS custom properties only)
public/styles/index.html            ← Visual style book / reference
public/styles/system.css            ← shared component classes (to be created)
```

All style infrastructure lives in `public/styles/`, served as static files by Express before WebDAV. This means `/styles/acequia-tokens.css` is always available on every subdomain — no dependency on per-subdomain WebDAV content.

The tokens file defines only CSS custom properties on `:root` — no selectors, no classes.

### Shared component stylesheet

`system.css` provides reusable CSS classes (`.acq-badge`, `.acq-btn`, `.acq-status`, `.acq-card`, `.acq-info-row`, etc.) built entirely on `--acq-*` tokens. System pages link to it instead of duplicating these patterns inline.

### How tokens reach each consumer

| Consumer | How it gets tokens |
|----------|-------------------|
| **System pages** (`public/*.html`) | `<link href="/styles/acequia-tokens.css">` — served as static file |
| **Web components** (`public/components/`) | `@import url('/styles/acequia-tokens.css')` inside shadow DOM `<style>`, or adopt from host page |
| **Acequia apps** (`acequia/stigmergic/*/`) | `<link href="/styles/acequia-tokens.css">` — resolves to the static file |
| **Style book** (`public/styles/index.html`) | `<link href="./acequia-tokens.css">` — same directory |

### Flow diagram

```
acequia-tokens.css (single source)
        │
        ├──→ system.css (imports tokens, defines .badge, .btn, .card, etc.)
        │       │
        │       ├──→ dashboard.html
        │       ├──→ login.html
        │       ├──→ register-user.html
        │       ├──→ accept-invite.html
        │       ├──→ approve-link.html
        │       ├──→ user-info.html
        │       ├──→ logout.html
        │       └──→ editor.html
        │
        ├──→ Web components (import in shadow DOM or inherit from host)
        │       ├──→ acequia-file-browser
        │       ├──→ token-keychain-manager
        │       ├──→ acequia-editor
        │       └──→ worker-pool-dashboard
        │
        └──→ Acequia apps (link directly)
                ├──→ Chat/
                ├──→ BrowserDAV/
                ├──→ Camera/
                └──→ ...
```

## What `system.css` provides

Extracted from the duplicated patterns across all 6 system pages:

### Base
```css
*, *::before, *::after { box-sizing: border-box; }

body {
  font-family: var(--acq-font);
  background: var(--acq-bg-gradient);
  color: var(--acq-text);
  min-height: 100vh;
  line-height: var(--acq-line-height);
  padding: var(--acq-space-xl);
  margin: 0;
}

h1, h2, h3 { color: var(--acq-text-heading); }

a { color: var(--acq-text-link); }
```

### Containers
```css
.acq-container       { max-width: 900px; margin: 0 auto; }
.acq-container--narrow { max-width: 500px; margin: 40px auto; }

.acq-card {
  background: var(--acq-surface-2);
  border: 1px solid var(--acq-border);
  border-radius: var(--acq-radius-xl);
  padding: var(--acq-space-3xl);
  backdrop-filter: blur(10px);
}
```

### Status messages
```css
.acq-status          { padding: 12px 16px; border-radius: var(--acq-radius-md); text-align: center; }
.acq-status--success { background: var(--acq-success-bg); color: var(--acq-success); }
.acq-status--error   { background: var(--acq-error-bg); color: var(--acq-error); }
.acq-status--info    { background: var(--acq-info-bg); color: var(--acq-info); }
.acq-status--warning { background: var(--acq-warning-bg); color: var(--acq-warning); }
```

### Badges
```css
.acq-badge {
  display: inline-block;
  padding: 4px 12px;
  border-radius: var(--acq-radius-pill);
  font-size: var(--acq-font-size-xs);
  font-weight: 600;
}
.acq-badge--owner   { background: var(--acq-role-owner-bg); color: var(--acq-role-owner); }
.acq-badge--editor  { background: var(--acq-role-editor-bg); color: var(--acq-role-editor); }
.acq-badge--viewer  { background: var(--acq-role-viewer-bg); color: var(--acq-role-viewer); }
.acq-badge--pending { background: var(--acq-role-pending-bg); color: var(--acq-role-pending); }
```

### Buttons
```css
.acq-btn {
  display: inline-block;
  padding: 14px 24px;
  font-size: var(--acq-font-size-lg);
  font-weight: 600;
  color: var(--acq-text-heading);
  border: none;
  border-radius: var(--acq-radius-md);
  cursor: pointer;
  text-align: center;
  text-decoration: none;
  transition: background var(--acq-transition);
}
.acq-btn--block     { display: block; width: 100%; }
.acq-btn--primary   { background: var(--acq-accent); }
.acq-btn--primary:hover { background: var(--acq-accent-hover); }
.acq-btn--success   { background: var(--acq-success-solid); }
.acq-btn--success:hover { background: var(--acq-success-hover); }
.acq-btn--muted     { background: var(--acq-text-dim); }
.acq-btn:disabled   { background: var(--acq-text-dim); cursor: not-allowed; }
```

### Forms
```css
.acq-input {
  width: 100%;
  padding: 12px 14px;
  background: var(--acq-surface-1);
  border: 1px solid var(--acq-border);
  border-radius: var(--acq-radius-md);
  color: var(--acq-text-heading);
  font-size: var(--acq-font-size-lg);
  font-family: var(--acq-font);
  transition: border-color var(--acq-transition);
}
.acq-input:focus { outline: none; border-color: var(--acq-accent); }
.acq-input--error { border-color: var(--acq-error); }
.acq-input--valid { border-color: var(--acq-success); }

.acq-label {
  display: block;
  color: var(--acq-text-muted);
  font-size: var(--acq-font-size);
  margin-bottom: 6px;
}
```

### Data rows
```css
.acq-info-row {
  display: flex;
  justify-content: space-between;
  padding: 8px 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.acq-info-row:last-child { border-bottom: none; }
.acq-info-row__label { color: var(--acq-text-muted); }
.acq-info-row__value { color: var(--acq-text-heading); font-family: var(--acq-font-mono); }
```

### Utilities
```css
.hidden { display: none; }
.text-mono { font-family: var(--acq-font-mono); }
.text-center { text-align: center; }
.text-muted { color: var(--acq-text-muted); }
```

## Migration Plan

### Phase 1: Infrastructure (no visual changes)

1. **Done.** `acequia-tokens.css` and `index.html` (style book) are in `public/styles/`, served as static files at `/styles/acequia-tokens.css` on every subdomain.

2. **Done.** `public/styles/system.css` created with shared `acq-*` component classes, importing tokens via `@import url('/styles/acequia-tokens.css')`. Style book updated to link `system.css` and use `acq-*` class names instead of its own inline component definitions.

### Phase 2: Migrate system pages (smallest first)

For each page:
1. Add `<link>` tags for tokens + system.css in `<head>`
2. Replace hardcoded color/spacing values with `var(--acq-*)` references
3. Replace duplicated class definitions with `system.css` classes (e.g. `.status-message.success` → `.acq-status.acq-status--success`)
4. Keep page-specific CSS inline (tabs, grids, unique layouts)
5. Visual regression check

**Order:**
1. **Done.** `approve-link.html` — migrated to system.css, inline CSS reduced to page-specific styles only
2. **Done.** `user-info.html` — migrated to system.css, inline CSS reduced to page-specific styles only
3. **Done.** `accept-invite.html` — migrated to system.css, inline CSS reduced to page-specific styles only
4. **Done.** `login.html` — migrated to system.css, inline CSS reduced to page-specific styles only
5. **Done.** `register-user.html` — migrated to system.css, inline CSS reduced to page-specific styles only
6. **Done.** `dashboard.html` + `dashboard.js` — migrated to system.css, inline CSS reduced to page-specific styles only
7. **Done.** `logout.html` — migrated to system.css, inline CSS reduced to page-specific styles only
8. **Done.** `editor.html` — migrated to system.css (layout-focused page, mostly page-specific CSS; base styles and tokens adopted)
9. **Skipped.** `register.html` — legacy device registration page with no inline CSS (uses Shoelace only)

### Phase 3: Migrate web components

Web components use shadow DOM, so they can't inherit `system.css` classes directly. Two approaches:

- **Constructable stylesheets** (modern browsers): Adopt the tokens sheet into shadow roots
- **@import inside shadow `<style>`**: `@import url('/styles/acequia-tokens.css')` at top of each component's style string

The components already define their own CSS variables (e.g. `--fb-bg`, `--fb-accent`). Migration means: map those internal variables to `--acq-*` tokens as defaults:
```css
:host {
  --fb-bg: var(--acq-surface-2);
  --fb-accent: var(--acq-accent);
}
```
This preserves per-instance override capability while pulling defaults from the shared tokens.

### Phase 4: Migrate acequia apps

Apps like Chat, Camera, Scribbler each have `src/styles/base.css` with their own `--color-*` variables. Migration:
1. Replace `<link href="src/styles/base.css">` with `<link href="/styles/acequia-tokens.css">` + app-specific overrides
2. Map app-specific variables to `--acq-*` tokens
3. Keep app-unique styles (chat bubbles, camera controls, etc.)

This is lower priority — apps work fine with their own tokens. The benefit is consistency when the design evolves.

## Class naming convention

All shared classes use the `acq-` prefix to avoid collisions with page-specific styles and Shoelace components:
- `.acq-btn`, `.acq-card`, `.acq-badge`, `.acq-status`, `.acq-input`, `.acq-info-row`
- BEM modifiers: `.acq-btn--primary`, `.acq-status--error`, `.acq-badge--owner`

## Verification

After each page migration:
1. **Visual diff** — page should look pixel-identical
2. **Token override test** — change `--acq-accent` in tokens file → verify all migrated pages reflect the change
3. **Mobile check** — verify responsive behavior unchanged
4. **Style book** — `public/styles/index.html` shows all tokens and components in one place for visual reference

## Files

| File | Role |
|------|------|
| `public/styles/acequia-tokens.css` | Canonical token definitions (single source of truth) |
| `public/styles/index.html` | Visual style book / reference |
| `public/styles/system.css` | Shared component classes for system pages |
