Skip to main content

max / goingson

22.9 KB · 337 lines History Blame Raw
1 # GoingsOn TODO
2
3 ## Launch readiness (Monday 2026-06-01) — IN PROGRESS
4
5 ### §3.4 fuzz pass — DONE 2026-05-31
6 Four-axis parallel audit (rust-fuzz + use-fuzz + creator-fuzz + ux-audit) complete. 10 launch-critical fixes committed (emoji sweep in 3 empty-state strings, About modal + Settings → About both populated with publisher/license/contact/source/privacy/copyright, hard-fail error copy humanized, `questions@``info@` in privacy policy, README trust block + Your Data section, bulk-bar CSS color contradiction removed, four bulk Delete buttons given `.btn-danger`). CSS rebuilt via `build-css.sh`.
7
8 ### Deferred to post-launch (audited but not blocking)
9
10 **rust-fuzz**
11 - `src-tauri/src/commands/email.rs:598``std::fs::read` for attachments inside async command; wrap in `spawn_blocking` (pattern already at email.rs:422 and email_sync.rs:224).
12 - `src-tauri/src/email/smtp_client.rs:291``ContentType::parse("application/octet-stream").unwrap()` on a static; replace with infallible mime constant.
13 - `src-tauri/src/state.rs:173`, `commands/sync.rs:{75,177,203}`, `commands/oauth.rs:{131,166}`, `commands/email_sync.rs:49``.expect("...poisoned")` on `std::sync::Mutex`; switch to `unwrap_or_else(|e| e.into_inner())` since protected data is recoverable.
14 - `src-tauri/src/db_watcher.rs:61` — shutdown latency = `MIN_EVENT_INTERVAL_MS`, thread unjoinable. Cosmetic.
15
16 **use-fuzz**
17 - `src-tauri/frontend/js/seed-data.js:393` — production `console.log` for demo seed instructions; strip from prod bundles or gate on debug flag.
18 - `src-tauri/frontend/js/app.js:{12,28,37,...}` — chatty `console.log` on every launch; trim or gate.
19 - `src-tauri/frontend/js/{emails,tasks,task-board}.js:*` — bare "Failed to load" without recovery actions; add "Try again" buttons per `ERROR_CODE_HINTS` pattern.
20 - `src-tauri/frontend/js/settings.js:196` — empty-plugins hint hardcodes `~/.config/goingson/plugins/` (Linux path); platform-detect or replace with doc link.
21 - `src-tauri/frontend/index.html:603` — task drawer close button uses literal `x` not `×`.
22 - `src-tauri/frontend/index.html:18-57``?ui=mobile` URL param + localStorage override not gated for production per `ui_mode_separation_plan.md` Phase 5; risk of users landing in untested mobile UI on desktop.
23
24 **creator-fuzz**
25 - `CHANGELOG.md:24-26` — "Fixed: all issues identified in audit runs 1-12" is internal-process language; replace with concrete user-visible fixes.
26 - `docs/data-export.md` (missing) — document JSON backup schema so "portable" is verifiable.
27 - `tauri.conf.json:48-54` — add Settings toggle "Check for updates on launch" for parity with privacy-policy framing (currently no opt-out, though install is user-initiated).
28 - README has no pricing / commercial-license-contact line for PolyForm-NC.
29
30 **ux-audit**
31 - `styles.css:590-660``.btn`/`.btn-primary`/`.btn-danger` lack the offset-shadow brand cue that `.card` and `.bulk-actions-bar` use; controls look flatter than chrome. One-pass fix on interactive primitives also resolves disabled-state contrast and `.pill.active`/`.tab.active` lift issues.
32 - `styles.css:615` — disabled-button signal is opacity-only; swap to `transparent bg + muted color/border`.
33 - `styles.css:4712``.sync-label` hidden in `ui-mode-mobile` leaves dot color as the only state cue; add shape variation or keep label for `error`/`warn`.
34 - Single-item destructive actions (`tasks.js:657` etc.) lack `.btn-loading` during round-trip.
35 - `index.html:466` straggler `row-flex-2` vs neighbors using `.row-flex row-flex-N` — will be caught by ongoing CSS dedup.
36 - Back-arrow buttons inconsistent: `← Back` (index.html:220, 538) vs literal `` (index.html:270).
37
38 ### §3.1 — DONE for emoji/About/error-copy. Remaining:
39 - Mobile UI mode override hardening (see use-fuzz deferral above).
40
41 ### §3.2 distribution — NOT STARTED
42 - Signed builds + notarization stapled + DMG smoke test on a clean macOS account.
43
44 ### §3.3 repo hygiene — NOT STARTED
45 - Resolve stray root planning files (`css_refactor_plan_2026-05-24.md`, `css_refactor_plan.md`, `css_state_audit.md`, `ui_mode_separation_plan.md`); decide on `generate_pitch_pdf.py` + `GoingsOn-Pitch.pdf`; review `_archive/`.
46
47 CSS dedup work below is the active in-progress stream; launch readiness is its own track.
48
49 ---
50
51 # GoingsOn CSS Dedup TODO
52
53 Charter + primitive vocabulary lives at the top of
54 `src-tauri/frontend/css/styles.css`. The constraint that "visual output
55 must not change" has been relaxed: visual *changes* are OK as long as
56 they're not worse. Prefer one consistent visual per element family.
57
58 Always rebuild after changes:
59 ```
60 cd src-tauri/frontend && ./build-css.sh
61 ```
62
63 ---
64
65 ## A. Buttons — consolidate to one family
66
67 53 button-related selectors live in `styles.css`. They cluster around 4
68 canonical primitives (`.btn`, `.btn-icon`, `.btn-link`, `.btn-loading`)
69 plus ~15 bespoke `*-btn` classes that each invented their own padding /
70 font / hover.
71
72 ### Canonical button family (target)
73 - `.btn` — base. `inline-flex`, padding `0.625rem 1.25rem`, border, radius-sm, font 0.9rem/600.
74 - Intents: `.btn-primary`, `.btn-secondary`, `.btn-danger`.
75 - Sizes: `.btn-sm` (+ new `.btn-lg` if any of the bespoke ones genuinely need it).
76 - Variants: `.btn-icon` (chrome icon button, no border), `.btn-link` (text-link button).
77 - States: `:hover`, `:active`, `:disabled`, `.btn-loading`.
78
79 ### Bespoke buttons to migrate
80 For each, find the closest canonical and migrate. Visual deltas are
81 acceptable when they bring the button into the system.
82
83 | Bespoke class | Maps to | Notes |
84 |---------------|---------|-------|
85 | ~~`.settings-btn`~~ | `.btn` | Done 2026-05-24. Structural styling (bg-card, border, radius-sm, hover/active) already matched `.btn`; only deltas were font-size (1.25rem → 0.9rem) and padding (tighter horizontal). Accepted smaller per charter. `margin-left` dropped — `.header-actions` parent already sets `gap: 0.5rem`. Mobile path unaffected (`.app-header` is `display:none` on `.ui-mode-mobile`). |
86 | ~~`.shortcut-hint-btn`~~ | `.btn.shortcut-hint-btn` | Done 2026-05-24. Marker class retained for mono font + min-width + tighter padding override (single-glyph `?` button) and for the touch/mobile `display:none` rule. Bespoke chrome (border/bg/hover) dropped — now inherited from `.btn`. |
87 | ~~`.settings-back-btn`~~ | `.btn-link.mb-1.settings-back` | Done 2026-05-24. Marker class kept for mobile-pass override. |
88 | `.settings-nav-item` | New `.btn-nav` *or* not-a-button | It's a nav item, not really a button. Keep as is. |
89 | ~~`.undo-btn`~~ | `.btn.btn-sm.btn-primary` | Done 2026-05-24. Bespoke was custom accent-blue; .btn-primary matches. |
90 | ~~`.account-delete-btn`~~ | `.btn.btn-sm.btn-danger` | Done 2026-05-24. |
91 | ~~`.bulk-modal-option-btn`~~ | `.btn.btn-sm.text-left.w-full` + marker class | Done 2026-05-24. New `.w-full` utility; `.bulk-modal-scroll` now flex column with gap; marker class retained for mobile padding override. Stale `btn-ghost` reference removed from callsites. |
92 | ~~`.email-attachment-remove-btn`~~ | (dead code) | Done 2026-05-24: rule deleted, no callsites. |
93 | ~~`.toggle-cc-btn`~~ | `.btn-link` | Done 2026-05-24. Accepts underline visual delta. |
94 | ~~`.attachment-delete-btn`~~ | `.text-accent-red` | Done 2026-05-24: already had `.btn.btn-sm.btn-secondary`; bespoke was only the red color. |
95 | ~~`.welcome-step-btn`~~ | `.text-left` | Done 2026-05-24: already had `.btn.btn-secondary`; new `.text-left` utility added next to `.text-center`. |
96 | ~~`.kebab-btn`~~ | `.btn-icon` + slim bespoke for hover-reveal/font-override | Done 2026-05-24. 5 callsites now `btn-icon kebab-btn`. |
97 | ~~`.schedule-task-btn`~~ | (dead code) | Done 2026-05-24: rule deleted, no callsites. |
98 | ~~`.time-block-quick-btn`~~ | `.btn.btn-sm.btn-secondary` + marker class | Done 2026-05-24. Bespoke base styles dropped; only `.selected` state override retained. `.selected` JS class kept (not renamed to `.is-selected` — out of scope). |
99 | ~~`.month-goal-status-btn`~~ | `.btn-icon` + slim bespoke for color/sizing | Done 2026-05-24. |
100 | ~~`.month-goal-delete-btn`~~ | `.btn-icon` + slim bespoke for hover-reveal/color | Done 2026-05-24. |
101 | `.view-toggle-btn` (+active) | Keep as-is | Reviewed 2026-05-24. Already its own primitive (joined borders, zero-gap segmented look); not duplication of `.btn`. Rename to `.segmented-control` deferred — cosmetic only. |
102 | ~~`.btn-danger-text`~~ | `.text-accent-red` (generic utility) | Done 2026-05-24. Kept the secondary chrome on Delete buttons — button-shaped affordance matters for destructive actions; the red text is enough to signal intent. 5 callsites (emails.js, events.js, contact-dashboard.js, task-overview.js, tasks.js) now read `.btn.btn-secondary.text-accent-red`. Bespoke 1-line rule deleted; the existing `.text-accent-red` utility already provided the color. |
103
104 Expected savings: ~80-120 source lines, ~2-3 KB min.css.
105
106 ### Migration approach
107 1. Audit each bespoke class's callsites: `grep -rn "<class>" js/ *.html`.
108 2. Edit one bespoke class at a time:
109 a. Decide the canonical mapping.
110 b. Update callsites to use canonical class(es).
111 c. Delete bespoke rule from styles.css.
112 3. After each migration, rebuild + eyeball the affected surface.
113 4. If migration reveals a missing modifier (e.g. `.btn-icon--lg`), add
114 it next to `.btn-icon` and document in the charter inventory.
115
116 ---
117
118 ## B. Form inputs & selects — one input family
119
120 ### Canonical (target)
121 - `.form-input` — base text input.
122 - `.form-select` — base select.
123 - Sizes: `.form-input--w-200`, `.form-select--compact` (already in place).
124 - Variants: new `.form-input--ghost` (borderless, used by compose
125 header rows), new `.form-input--search` (search affordance).
126
127 ### Migrate
128 - ~~`.quick-add-input`~~ — Done 2026-05-24: dead code. `.quick-add` parent + `.quick-add-input` rules deleted (~28 lines). The actual quick-add modal at `keyboard.js:openQuickAddModal` uses `.form-input`. §10 section header vacated; navigation numbering preserved.
129 - `.filter-select`**Skip.** Reviewed 2026-05-24: not duplication. Bespoke focus styling (fills with accent-blue) is a distinctive UX cue, and the smaller padding/font-weight differ enough from `.form-select.form-select--compact` that the migration would lose visual identity for a ~5-line CSS win. Leave as-is.
130 - ~~`.header-input`~~ → `.form-input.form-input--ghost.flex-1`. Done 2026-05-24. New `.form-input--ghost` modifier added next to base in §20.
131 - ~~`.header-select`~~ → `.form-select.form-select--ghost.flex-1`. Done 2026-05-24. New `.form-select--ghost` modifier added.
132
133 Savings achieved: ~29 lines (29/(40-60) of estimate).
134
135 ---
136
137 ## C. Cards — one card family with shell + muted variants
138
139 ### Canonical
140 - `.card` — interactive tile (current).
141 - `.card--list-item` — compact list row (already added).
142 - New `.card--muted` — bg-secondary, no shadow/hover, no cursor (mini
143 info card).
144 - New `.card--shell` — container shell: no padding, no cursor, no
145 hover, flex column, overflow handling. For list shells like
146 `.email-list` and `.dashboard-column`.
147
148 ### Migrate
149 - ~~`.email-list`~~ → `.card.card--shell.email-list`. Done 2026-05-24. Slim feature rule keeps only `flex: 1; min-height: 0;`.
150 - `.dashboard-column`**Skip.** Has no shadow + thinner border, doesn't actually match `.card`'s look; migrating would change visuals across project dashboard. Leave page-scoped.
151 - ~~`.contact-info-section`~~ → `.card.card--muted.contact-info-section`. Done 2026-05-24. Slim feature rule keeps gap + margin + tighter padding.
152 - `.contact-summary-stat`**Skip.** Page-scoped is appropriate (feature-specific stat tile with min-width 80px); not duplication of `.card`.
153 - ~~`.review-card`~~ → `.card.card--static.review-card`. Done 2026-05-24. New `.card--static` variant added next to `--shell` and `--muted` in §11 (cursor default + no-op hover, full card chrome retained). `.review-card` base slimmed to a single `padding: 1.5rem` declaration; descendants (`.card-header`, `.card-title`, `.card-icon`, `.card-badge`) trimmed to only their review-specific deltas (font-family/font-size/font-weight all inherit from the canonical `.card-*` descendants now). 14 callsites migrated across `plan-review-toggle.js`, `weekly-review-render.js`, `monthly-review-render.js`. Eyeball: weekly + monthly review screens.
154 - ~~`.kanban-card`~~ → `.card.kanban-card`. Done 2026-05-24. Bespoke bg/border/box-shadow/transition/hover declarations dropped — all inherited from `.card`. Marker class slimmed to its deltas: `border-width-sm` (thinner), `border-radius: 0` (square), `padding: 0.75rem` (tighter), `cursor: grab`, `border-left: 4px solid transparent` (priority hook). Hover transform now inherited (was duplicate). 1 callsite in `tasks-kanban.js`. Eyeball: kanban board view + drag interaction.
155
156 Savings achieved: ~30 net source lines (~17 lines from .review-card bespoke chrome + ~10 lines from .kanban-card duplicate hover/chrome; ~10 lines added for .card--static infra). min.css 152970 → 152465 (-505 bytes).
157
158 ---
159
160 ## D. Badges & tags — unify the chip family
161
162 ### Canonical
163 - `.tag, .badge` — bordered chip primitive (current).
164 - Color variants via `[data-color="green|yellow|red|cyan|purple|muted"]`
165 (already exists).
166 - New size modifiers: `.badge--sm`, `.badge--xs`.
167 - New intent modifier: `.badge--filled` (solid accent fill, no border).
168
169 ### Migrate
170 - `.thread-badge`**Skip.** Already audited 2026-05-21 as net-zero dedup; composing `.badge` would require overriding every declaration. Leave standalone.
171 - ~~`.email-label-badge`~~ → `.badge.badge--xs.badge--filled[data-color="blue"]`. Done 2026-05-24. 2 callsites in `emails.js`. Visual delta: text color now white-on-blue instead of lavender-on-blue (acceptable per charter).
172 - `.subtask-linked-tag`**Skip.** Tiny 2-property rule (color + font-size), 1 callsite, not really a chip — it's an inline "[Linked]" label. Page-scoped is appropriate.
173 - `.card-badge--success/-warning/-info/-danger`**Skip.** Self-contained BEM system inside `.review-card` context; base lives in parent-scoped rule. Already minimal; not duplication of `.badge`.
174 - ~~`.badge-completed`, `.badge-started`, `.badge-pending`, `.badge-priority-h/m/l`, `.badge-focus`, `.badge-overdue`, `.badge-snoozed`~~ → Done 2026-05-24. All 9 bespoke rules deleted. `task-overview.js:322-326` rewritten with a `chip(color, text)` helper that emits `.badge.badge--xs.badge--filled[data-color="X"]`. New modifiers added in §12: `.badge--xs`, `.badge--sm`, `.badge--filled` (with per-color overrides), plus a new `data-color="blue"` variant on the base.
175
176 Savings achieved: ~4 net source lines (added 22 of modifier infra to delete 22 of bespoke + 9 lines deleted from emails.js + task-overview.js path). Real win is qualitative: one composable badge system, no more invent-a-badge-per-feature.
177
178 ---
179
180 ## E. Avatars — DONE (2026-05-24)
181
182 Promoted `.avatar` primitive in §47 Contacts with `.avatar--sm` (32px, no border), `.avatar--lg` (60px), `.avatar--unknown` (muted bg). All 5 callsites migrated: emails.js (×2), contact-dashboard.js (×1), contacts-render.js (×2). Bespoke rules deleted: `.contact-avatar`, `.contact-avatar-lg`, `.contact-avatar-large`, `.contact-avatar-sm`, `.contact-avatar-unknown`. Net ~30 source lines removed. `.contact-avatar-large` was 64px; new `.avatar--lg` is 60px (4px visual delta, accepted).
183
184 ---
185
186 ## F. Status dots — REVIEWED, SKIP (2026-05-24)
187
188 Audited 2026-05-24. Three dot patterns exist: `.sync-dot` (8px, connect/sync/warn/error), `.sync-status-dot` (10px, fixed green), `.tab-status-dot` (8px, none/green/yellow/red with pulse animations). Variation in animations and positioning is real; unifying under `.status-dot` would either lose animation timing or require many overrides. Combined surface is ~25 lines; consolidation savings would be <10 lines after adding the primitive. Leave as-is.
189
190 ---
191
192 ## G. Toggles — REVIEWED, CLEAN (2026-05-24)
193
194 `.toggle-switch` is the only on/off switch primitive in the codebase. `.past-events-toggle` and `.form-more-toggle` are `<summary>`-style disclosure controls (different pattern), not duplicates. Nothing to consolidate.
195
196 ---
197
198 ## H. Empty states — REVIEWED, CLEAN (2026-05-24)
199
200 Audited 2026-05-24. No ad-hoc "No X yet" / "Nothing X" markup found in js/ or html. The `.empty-state` primitive + variants (`--compact`, `--dashboard`, `--error`) are the only empty-state surface. Nothing to migrate.
201
202 ---
203
204 ## I. Misplaced / global utilities to relocate — DONE (2026-05-24)
205
206 - ~~`.kbd-hint`~~ relocated from §41 Settings to §12 Tags & Badges (right after the `.badge--filled` color overrides). Comment added explaining the move.
207 - `.visually-hidden` was already deleted previously.
208 - Audited `.address-highlight-mirror` and `.addr-*` color classes: tightly coupled to the address-highlighter overlay element (positional, feature-specific). Not generic text-color utilities. Leave page-scoped.
209
210 ---
211
212 ## J. Tier 3 (deferred, regression-prone)
213
214 These were called out earlier as needing the dev server running for
215 visual diffs. Do AFTER A–F so the consolidated primitives reduce
216 surface area first.
217
218 - ~~**Consolidate the 3 mobile sections** (§25 / §59 / §60) into one
219 RESPONSIVE band at end-of-file.~~ Superseded by **K. UI Mode
220 Separation** (2026-05-22). Mobile rules now live behind
221 `.ui-mode-mobile` class selectors, not media queries — the original
222 "one RESPONSIVE band" goal is moot.
223 - ~~**Resolve V1/V2 Weekly Review.**~~ Done 2026-05-24. Audited every
224 §49 class against actual JS/HTML usage: 20+ V1 classes had zero
225 callsites (`.stat-card*`, `.review-section`, `.section-title`,
226 `.review-details*`, `.review-task-list`, `.review-event-list`,
227 `.review-task-item*`, `.review-event-item*`, `.project-badge`,
228 `.due-badge*`, `.focus-task-list*`, `.focus-toggle*`,
229 `.no-focus-message`, `.focused-projects`, `.project-tag`,
230 `.notes-section`, `.review-notes-input*`, `.review-actions`,
231 `.week-info`). Deleted all. Kept the surviving chrome still composed
232 by `weekly-review.js` (header, status pill, week-dates, event-time,
233 focus-section gradient) and tab indicators (`.tab-badge`,
234 `.tab-status-dot`) used by tab nav + events.js. §49 went from 326
235 lines to 106. `mobile.js` had no V1 weekly-review classes — only a
236 `getElementById('weekly-review-content')` lookup, still valid.
237 Section header renamed to "Weekly Review — chrome around the V2 grid
238 + tab indicators."
239 - ~~**Print consolidation** (§48 + §52).~~ Done 2026-05-24. §52 (152
240 lines, weekly-review-specific `@media print`) folded into §48 inside
241 a single consolidated `@media print` block. Organized with three
242 subsection comments: "Global chrome", "Tables", "Weekly review".
243 Hidden-selector list deduplicated (e.g. one `display: none` block
244 now lists global + weekly-review hide targets together). §52
245 vacated; only one `@media print` block remains in the file.
246
247 ---
248
249 ## K. UI Mode Separation — DONE (2026-05-22)
250
251 Architectural refactor: mobile vs desktop UI is no longer selected by
252 viewport width. Set once at boot via `<html class="ui-mode-mobile|desktop">`
253 from the inline detection script in `index.html`. Desktop binaries stay
254 desktop even when the window is narrowed.
255
256 Phases 1–5 complete (~3 hours total). Full plan and post-audit live at:
257 - `ui_mode_separation_plan.md` (the plan)
258 - `css_state_audit.md` (where things landed)
259
260 ### Key outcomes
261 - 8 `@media (max-width: 768px)` blocks → 0. 228 selectors prefixed `.ui-mode-mobile`.
262 - 6 stray non-768 mobile breakpoints (600/640/900/1100) → converted to `.ui-mode-mobile` rules.
263 - Remaining `@media` queries: 1 `(hover: none)` (hover suppression),
264 1 `(max-width: 1024px)` + 1 `(min-width: 1400px)` (both intra-desktop,
265 selectors `.ui-mode-desktop`-prefixed), 2 `print`. Zero mode-switching queries.
266 - `[style*="display:none"]` selector hack removed.
267 - JS layout-mode checks (`innerWidth <= 768`/`<= 600`) → `GoingsOn.viewport.isMobile()`.
268 - New `js/viewport.js` module; new inline detection script in `index.html` head.
269 - Section header at `styles.css:159–185` rewritten to document the new model.
270 - `#task-view-toggle { display: none }` moved out of `@media (hover: none)`
271 (was wrong axis — input capability instead of UI mode).
272 - Latent bug fixed: mobile `@keyframes modalSlideIn/Out` were globally
273 overriding the desktop variants (last-keyframes-wins). Renamed to
274 `*Mobile` and applied via `animation-name` override.
275
276 ### Phase 6 — remaining (not blocking)
277 model for new contributors. Done 2026-05-24. Placed after "CSS
278 Workflow"; covers CSS/JS surfaces, detection precedence, dev preview,
279 the "mode is a build property, not viewport size" invariant, and the
280 separate input-capability axis.
281 detection script in `index.html` to read `window.__TAURI__.platform`
282 before the UA fallback. Done 2026-05-24. Tauri 2's platform plugin
283 is async (no sync `window.__TAURI__.platform`), which doesn't fit an
284 inline-head script that must set the class before the stylesheet
285 evaluates. Substituted: `navigator.userAgentData.mobile` (UA Client
286 Hints) as a third tier, plus iPad-as-Mac detection
287 (`navigator.maxTouchPoints > 1` on a Mac-reporting platform) in the
288 UA regex tier. Works for WKWebView (iOS), Android WebView, and
289 catches Safari 13+ iPads that report as Mac. Detail in
290 `css_state_audit.md` "Detection script — updated 2026-05-24".
291 - [ ] For production lockdown of the override (so users can't set
292 `?ui=mobile` on a desktop build and break things): inject
293 `__GO_BUILD_MODE__` at build time and gate the override branches.
294 Defer until a production mobile-only desktop-binary distribution
295 channel exists. (TestFlight iOS is its own binary; no override
296 risk there.)
297
298 ### Divergences from the plan
299 - Kept `.mobile-hide` utility class (plan called for removing it).
300 Rationale: it's one rule, the marker class makes intent visible in
301 markup, and removing it would shift verbosity from CSS to HTML.
302 - Phase 4 scope: prefixed only the *layout* rules that mobile UI
303 hides entirely (header, top tab nav, pills, sidebars). Did not
304 prefix shared component rules (buttons, forms, modals, cards) —
305 they correctly stay in the base scope.
306
307 ---
308
309 ## Suggested order
310
311 1. **A. Buttons** — biggest leverage, sets the design language.
312 2. **B. Form inputs** — closely related (form chrome consistency).
313 3. **D. Badges** — small surface, quick win once button pattern is set.
314 4. **C. Cards** — falls into place once buttons + inputs unified.
315 5. **E. Avatars / F. Status dots / G. Toggles / H. Empty states** — cleanup.
316 6. **I. Relocate globals** — janitorial, do last.
317 7. **J. Tier 3** — only when surface area is reduced.
318
319 ---
320
321 ## Process notes
322
323 - Rebuild `styles.min.css` via `./build-css.sh` after every batch.
324 - The composition charter at the top of `styles.css` is the source of
325 truth for what primitives exist. Update the EXISTING VOCABULARY
326 inventory there whenever you add or rename a primitive/modifier.
327 - Don't ship two ways to do the same thing during a migration; if A
328 half-done blocks B, finish A first.
329 - Spot-check after each surface migration:
330 - Settings overlay (gear button, back button, nav)
331 - Email list, email reader, compose modal, compose window
332 - Project dashboard tiles
333 - Task table action buttons
334 - Toast undo
335 - Bulk-select modal
336 - Welcome wizard
337