|
| 1 | +# Architecture: css-base Demo Skins |
| 2 | + |
| 3 | +## High-Level Structure |
| 4 | + |
| 5 | +``` |
| 6 | +dist/ |
| 7 | +├── app.css (existing — the library) |
| 8 | +├── *.css (existing — individual modules) |
| 9 | +└── demo/ |
| 10 | + ├── index.html (hub — lists all demos with links) |
| 11 | + ├── shared/ |
| 12 | + │ ├── nav.html (shared nav snippet — inlined via copy, no JS import) |
| 13 | + │ └── theme-toggle.js (3-line script: toggle data-theme, persist localStorage) |
| 14 | + ├── netflix/ |
| 15 | + │ ├── index.html |
| 16 | + │ └── skin.css |
| 17 | + ├── amazon/ |
| 18 | + │ ├── index.html |
| 19 | + │ └── skin.css |
| 20 | + ├── whatsapp/ |
| 21 | + │ ├── index.html |
| 22 | + │ └── skin.css |
| 23 | + ├── windows/ |
| 24 | + │ ├── index.html |
| 25 | + │ └── skin.css |
| 26 | + ├── macos/ |
| 27 | + │ ├── index.html |
| 28 | + │ └── skin.css |
| 29 | + ├── spotify/ |
| 30 | + │ ├── index.html |
| 31 | + │ └── skin.css |
| 32 | + ├── slack/ |
| 33 | + │ ├── index.html |
| 34 | + │ └── skin.css |
| 35 | + ├── github/ |
| 36 | + │ ├── index.html |
| 37 | + │ └── skin.css |
| 38 | + ├── dashboard/ |
| 39 | + │ ├── index.html |
| 40 | + │ └── skin.css |
| 41 | + └── terminal/ |
| 42 | + ├── index.html |
| 43 | + └── skin.css |
| 44 | +``` |
| 45 | + |
| 46 | +## Component Breakdown |
| 47 | + |
| 48 | +### 1. Hub Page (`demo/index.html`) |
| 49 | +- **Responsibility:** Entry point, lists all demos as a grid of cards |
| 50 | +- **Loads:** `../app.css` (default css-base styling) |
| 51 | +- **Content:** Grid of 10 cards, each with demo name, short description, link |
| 52 | +- **Theme toggle:** Yes (proves default css-base light/dark works) |
| 53 | + |
| 54 | +### 2. Shared Nav (copy-pasted HTML block) |
| 55 | +- **Responsibility:** Consistent navigation across all demos |
| 56 | +- **Content:** Demo name, links to all 10 demos + hub, theme toggle button |
| 57 | +- **Why copy-paste, not JS include:** Zero dependencies, works with `file://`, no build step |
| 58 | +- **The nav HTML is defined in `shared/nav.html` as a reference** — developers copy it into each demo's `index.html` |
| 59 | + |
| 60 | +### 3. Theme Toggle (`shared/theme-toggle.js`) |
| 61 | +- **Responsibility:** Toggle `data-theme` attribute on `<html>`, persist to `localStorage` |
| 62 | +- **Loaded via:** `<script src="../shared/theme-toggle.js"></script>` in each demo |
| 63 | +- **Implementation:** |
| 64 | +```js |
| 65 | +// theme-toggle.js |
| 66 | +const t = localStorage.getItem('theme') || |
| 67 | + (matchMedia('(prefers-color-scheme:dark)').matches ? 'dark' : 'light'); |
| 68 | +document.documentElement.dataset.theme = t; |
| 69 | +function toggleTheme() { |
| 70 | + const next = document.documentElement.dataset.theme === 'dark' ? 'light' : 'dark'; |
| 71 | + document.documentElement.dataset.theme = next; |
| 72 | + localStorage.setItem('theme', next); |
| 73 | +} |
| 74 | +``` |
| 75 | + |
| 76 | +### 4. Demo Page (each `{name}/index.html`) |
| 77 | +- **Responsibility:** Showcase a specific UI style using css-base |
| 78 | +- **Loads:** `../../app.css` + `./skin.css` + `../shared/theme-toggle.js` |
| 79 | +- **Structure:** Nav (copied) + demo-specific HTML content |
| 80 | +- **No demo-specific JS** (pure CSS demos) |
| 81 | + |
| 82 | +### 5. Skin File (each `{name}/skin.css`) |
| 83 | +- **Responsibility:** Override CSS custom properties to achieve the target look |
| 84 | +- **Layer:** Loads AFTER `app.css`, no `@layer` needed (cascade order wins) |
| 85 | +- **Pattern:** Override `:root` variables for light, `[data-theme="dark"]` for dark overrides |
| 86 | + |
| 87 | +## Skin Override Strategy |
| 88 | + |
| 89 | +Each `skin.css` follows this structure: |
| 90 | + |
| 91 | +```css |
| 92 | +/* skin.css — {DemoName} skin for @medyll/css-base */ |
| 93 | + |
| 94 | +:root { |
| 95 | + /* ── Light mode overrides ── */ |
| 96 | + --default-color-text-light: oklch(...); |
| 97 | + --default-color-surface-light: oklch(...); |
| 98 | + --color-primary: oklch(...); |
| 99 | + --color-primary-hover: oklch(from var(--color-primary) calc(l - 0.08) c h); |
| 100 | + |
| 101 | + /* ── Typography (optional) ── */ |
| 102 | + --default-font-size: 0.875rem; |
| 103 | + |
| 104 | + /* ── Radius (optional) ── */ |
| 105 | + --radius-sm: ...; |
| 106 | + --radius-md: ...; |
| 107 | +} |
| 108 | + |
| 109 | +/* ── Dark mode overrides ── */ |
| 110 | +:root { |
| 111 | + --default-color-text-dark: oklch(...); |
| 112 | + --default-color-surface-dark: oklch(...); |
| 113 | +} |
| 114 | + |
| 115 | +/* ── Demo-specific layout (minimal, non-token) ── */ |
| 116 | +/* Only structural CSS that css-base doesn't provide */ |
| 117 | +/* e.g., fixed bottom player bar height, sidebar width */ |
| 118 | +``` |
| 119 | + |
| 120 | +> Tradeoff: Skin files may need a few structural CSS rules beyond variable overrides (e.g., sidebar widths, fixed positioning). This is acceptable — the goal is to minimize them, not eliminate them entirely. The PRD constraint "variable overrides only" is aspirational; structural layout rules that css-base can't express via tokens are documented as gaps. |
| 121 | +
|
| 122 | +## Data Flow |
| 123 | + |
| 124 | +``` |
| 125 | +Browser loads demo/netflix/index.html |
| 126 | + → <link> loads ../../app.css |
| 127 | + → @layer cascade: base → theme → variables → tokens → typography → palette → components → utilities → attr |
| 128 | + → <link> loads ./skin.css |
| 129 | + → :root overrides replace token values |
| 130 | + → light-dark() + color-scheme pick correct surface/text colors |
| 131 | + → Derived tokens (--color-surface-alt, shadows) auto-recalculate via --shade()/oklch() |
| 132 | + → <script> loads ../shared/theme-toggle.js |
| 133 | + → Sets data-theme from localStorage or prefers-color-scheme |
| 134 | + → Toggle button switches data-theme, palette.css [data-theme] rules kick in |
| 135 | +``` |
| 136 | + |
| 137 | +## Key Design Decisions |
| 138 | + |
| 139 | +1. **No build step** — HTML files are static, opened directly. This keeps the demo accessible to anyone evaluating the library. |
| 140 | + |
| 141 | +2. **No JS framework** — Pure HTML + CSS proves css-base works without tooling overhead. |
| 142 | + |
| 143 | +3. **Copy-paste nav vs. JS include** — Simpler, works offline, no CORS issues with `file://`. The reference copy lives in `shared/nav.html`. |
| 144 | + |
| 145 | +4. **skin.css after app.css** — CSS cascade order ensures skin overrides win without needing `!important` or higher-specificity selectors. |
| 146 | + |
| 147 | +5. **Single theme-toggle.js** — The only shared JS. Minimal footprint, loaded from `shared/`. |
| 148 | + |
| 149 | +6. **Playwright validation** — Screenshots at 1920x1080, served via `npx serve dist` to avoid `file://` limitations with Playwright. |
0 commit comments