# GoingsOn TODO ## Launch readiness (Monday 2026-06-01) — IN PROGRESS ### §3.4 fuzz pass — DONE 2026-05-31 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`. ### Deferred to post-launch (audited but not blocking) **rust-fuzz** - `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). - `src-tauri/src/email/smtp_client.rs:291` — `ContentType::parse("application/octet-stream").unwrap()` on a static; replace with infallible mime constant. - `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. - `src-tauri/src/db_watcher.rs:61` — shutdown latency = `MIN_EVENT_INTERVAL_MS`, thread unjoinable. Cosmetic. **use-fuzz** - `src-tauri/frontend/js/seed-data.js:393` — production `console.log` for demo seed instructions; strip from prod bundles or gate on debug flag. - `src-tauri/frontend/js/app.js:{12,28,37,...}` — chatty `console.log` on every launch; trim or gate. - `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. - `src-tauri/frontend/js/settings.js:196` — empty-plugins hint hardcodes `~/.config/goingson/plugins/` (Linux path); platform-detect or replace with doc link. - `src-tauri/frontend/index.html:603` — task drawer close button uses literal `x` not `×`. - `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. **creator-fuzz** - `CHANGELOG.md:24-26` — "Fixed: all issues identified in audit runs 1-12" is internal-process language; replace with concrete user-visible fixes. - `docs/data-export.md` (missing) — document JSON backup schema so "portable" is verifiable. - `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). - README has no pricing / commercial-license-contact line for PolyForm-NC. **ux-audit** - `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. - `styles.css:615` — disabled-button signal is opacity-only; swap to `transparent bg + muted color/border`. - `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`. - Single-item destructive actions (`tasks.js:657` etc.) lack `.btn-loading` during round-trip. - `index.html:466` straggler `row-flex-2` vs neighbors using `.row-flex row-flex-N` — will be caught by ongoing CSS dedup. - Back-arrow buttons inconsistent: `← Back` (index.html:220, 538) vs literal `←` (index.html:270). ### §3.1 — DONE for emoji/About/error-copy. Remaining: - Mobile UI mode override hardening (see use-fuzz deferral above). ### §3.2 distribution — NOT STARTED - Signed builds + notarization stapled + DMG smoke test on a clean macOS account. ### §3.3 repo hygiene — NOT STARTED - 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/`. CSS dedup work below is the active in-progress stream; launch readiness is its own track. --- # GoingsOn CSS Dedup TODO Charter + primitive vocabulary lives at the top of `src-tauri/frontend/css/styles.css`. The constraint that "visual output must not change" has been relaxed: visual *changes* are OK as long as they're not worse. Prefer one consistent visual per element family. Always rebuild after changes: ``` cd src-tauri/frontend && ./build-css.sh ``` --- ## A. Buttons — consolidate to one family 53 button-related selectors live in `styles.css`. They cluster around 4 canonical primitives (`.btn`, `.btn-icon`, `.btn-link`, `.btn-loading`) plus ~15 bespoke `*-btn` classes that each invented their own padding / font / hover. ### Canonical button family (target) - `.btn` — base. `inline-flex`, padding `0.625rem 1.25rem`, border, radius-sm, font 0.9rem/600. - Intents: `.btn-primary`, `.btn-secondary`, `.btn-danger`. - Sizes: `.btn-sm` (+ new `.btn-lg` if any of the bespoke ones genuinely need it). - Variants: `.btn-icon` (chrome icon button, no border), `.btn-link` (text-link button). - States: `:hover`, `:active`, `:disabled`, `.btn-loading`. ### Bespoke buttons to migrate For each, find the closest canonical and migrate. Visual deltas are acceptable when they bring the button into the system. | Bespoke class | Maps to | Notes | |---------------|---------|-------| | ~~`.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`). | | ~~`.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`. | | ~~`.settings-back-btn`~~ | `.btn-link.mb-1.settings-back` | Done 2026-05-24. Marker class kept for mobile-pass override. | | `.settings-nav-item` | New `.btn-nav` *or* not-a-button | It's a nav item, not really a button. Keep as is. | | ~~`.undo-btn`~~ | `.btn.btn-sm.btn-primary` | Done 2026-05-24. Bespoke was custom accent-blue; .btn-primary matches. | | ~~`.account-delete-btn`~~ | `.btn.btn-sm.btn-danger` | Done 2026-05-24. | | ~~`.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. | | ~~`.email-attachment-remove-btn`~~ | (dead code) | Done 2026-05-24: rule deleted, no callsites. | | ~~`.toggle-cc-btn`~~ | `.btn-link` | Done 2026-05-24. Accepts underline visual delta. | | ~~`.attachment-delete-btn`~~ | `.text-accent-red` | Done 2026-05-24: already had `.btn.btn-sm.btn-secondary`; bespoke was only the red color. | | ~~`.welcome-step-btn`~~ | `.text-left` | Done 2026-05-24: already had `.btn.btn-secondary`; new `.text-left` utility added next to `.text-center`. | | ~~`.kebab-btn`~~ | `.btn-icon` + slim bespoke for hover-reveal/font-override | Done 2026-05-24. 5 callsites now `btn-icon kebab-btn`. | | ~~`.schedule-task-btn`~~ | (dead code) | Done 2026-05-24: rule deleted, no callsites. | | ~~`.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). | | ~~`.month-goal-status-btn`~~ | `.btn-icon` + slim bespoke for color/sizing | Done 2026-05-24. | | ~~`.month-goal-delete-btn`~~ | `.btn-icon` + slim bespoke for hover-reveal/color | Done 2026-05-24. | | `.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. | | ~~`.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. | Expected savings: ~80-120 source lines, ~2-3 KB min.css. ### Migration approach 1. Audit each bespoke class's callsites: `grep -rn "" js/ *.html`. 2. Edit one bespoke class at a time: a. Decide the canonical mapping. b. Update callsites to use canonical class(es). c. Delete bespoke rule from styles.css. 3. After each migration, rebuild + eyeball the affected surface. 4. If migration reveals a missing modifier (e.g. `.btn-icon--lg`), add it next to `.btn-icon` and document in the charter inventory. --- ## B. Form inputs & selects — one input family ### Canonical (target) - `.form-input` — base text input. - `.form-select` — base select. - Sizes: `.form-input--w-200`, `.form-select--compact` (already in place). - Variants: new `.form-input--ghost` (borderless, used by compose header rows), new `.form-input--search` (search affordance). ### Migrate - ~~`.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. - `.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. - ~~`.header-input`~~ → `.form-input.form-input--ghost.flex-1`. Done 2026-05-24. New `.form-input--ghost` modifier added next to base in §20. - ~~`.header-select`~~ → `.form-select.form-select--ghost.flex-1`. Done 2026-05-24. New `.form-select--ghost` modifier added. Savings achieved: ~29 lines (29/(40-60) of estimate). --- ## C. Cards — one card family with shell + muted variants ### Canonical - `.card` — interactive tile (current). - `.card--list-item` — compact list row (already added). - New `.card--muted` — bg-secondary, no shadow/hover, no cursor (mini info card). - New `.card--shell` — container shell: no padding, no cursor, no hover, flex column, overflow handling. For list shells like `.email-list` and `.dashboard-column`. ### Migrate - ~~`.email-list`~~ → `.card.card--shell.email-list`. Done 2026-05-24. Slim feature rule keeps only `flex: 1; min-height: 0;`. - `.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. - ~~`.contact-info-section`~~ → `.card.card--muted.contact-info-section`. Done 2026-05-24. Slim feature rule keeps gap + margin + tighter padding. - `.contact-summary-stat` → **Skip.** Page-scoped is appropriate (feature-specific stat tile with min-width 80px); not duplication of `.card`. - ~~`.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. - ~~`.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. 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). --- ## D. Badges & tags — unify the chip family ### Canonical - `.tag, .badge` — bordered chip primitive (current). - Color variants via `[data-color="green|yellow|red|cyan|purple|muted"]` (already exists). - New size modifiers: `.badge--sm`, `.badge--xs`. - New intent modifier: `.badge--filled` (solid accent fill, no border). ### Migrate - `.thread-badge` → **Skip.** Already audited 2026-05-21 as net-zero dedup; composing `.badge` would require overriding every declaration. Leave standalone. - ~~`.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). - `.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. - `.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`. - ~~`.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. 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. --- ## E. Avatars — DONE (2026-05-24) 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). --- ## F. Status dots — REVIEWED, SKIP (2026-05-24) 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. --- ## G. Toggles — REVIEWED, CLEAN (2026-05-24) `.toggle-switch` is the only on/off switch primitive in the codebase. `.past-events-toggle` and `.form-more-toggle` are ``-style disclosure controls (different pattern), not duplicates. Nothing to consolidate. --- ## H. Empty states — REVIEWED, CLEAN (2026-05-24) 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. --- ## I. Misplaced / global utilities to relocate — DONE (2026-05-24) - ~~`.kbd-hint`~~ relocated from §41 Settings to §12 Tags & Badges (right after the `.badge--filled` color overrides). Comment added explaining the move. - `.visually-hidden` was already deleted previously. - 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. --- ## J. Tier 3 (deferred, regression-prone) These were called out earlier as needing the dev server running for visual diffs. Do AFTER A–F so the consolidated primitives reduce surface area first. - ~~**Consolidate the 3 mobile sections** (§25 / §59 / §60) into one RESPONSIVE band at end-of-file.~~ Superseded by **K. UI Mode Separation** (2026-05-22). Mobile rules now live behind `.ui-mode-mobile` class selectors, not media queries — the original "one RESPONSIVE band" goal is moot. - ~~**Resolve V1/V2 Weekly Review.**~~ Done 2026-05-24. Audited every §49 class against actual JS/HTML usage: 20+ V1 classes had zero callsites (`.stat-card*`, `.review-section`, `.section-title`, `.review-details*`, `.review-task-list`, `.review-event-list`, `.review-task-item*`, `.review-event-item*`, `.project-badge`, `.due-badge*`, `.focus-task-list*`, `.focus-toggle*`, `.no-focus-message`, `.focused-projects`, `.project-tag`, `.notes-section`, `.review-notes-input*`, `.review-actions`, `.week-info`). Deleted all. Kept the surviving chrome still composed by `weekly-review.js` (header, status pill, week-dates, event-time, focus-section gradient) and tab indicators (`.tab-badge`, `.tab-status-dot`) used by tab nav + events.js. §49 went from 326 lines to 106. `mobile.js` had no V1 weekly-review classes — only a `getElementById('weekly-review-content')` lookup, still valid. Section header renamed to "Weekly Review — chrome around the V2 grid + tab indicators." - ~~**Print consolidation** (§48 + §52).~~ Done 2026-05-24. §52 (152 lines, weekly-review-specific `@media print`) folded into §48 inside a single consolidated `@media print` block. Organized with three subsection comments: "Global chrome", "Tables", "Weekly review". Hidden-selector list deduplicated (e.g. one `display: none` block now lists global + weekly-review hide targets together). §52 vacated; only one `@media print` block remains in the file. --- ## K. UI Mode Separation — DONE (2026-05-22) Architectural refactor: mobile vs desktop UI is no longer selected by viewport width. Set once at boot via `` from the inline detection script in `index.html`. Desktop binaries stay desktop even when the window is narrowed. Phases 1–5 complete (~3 hours total). Full plan and post-audit live at: - `ui_mode_separation_plan.md` (the plan) - `css_state_audit.md` (where things landed) ### Key outcomes - 8 `@media (max-width: 768px)` blocks → 0. 228 selectors prefixed `.ui-mode-mobile`. - 6 stray non-768 mobile breakpoints (600/640/900/1100) → converted to `.ui-mode-mobile` rules. - Remaining `@media` queries: 1 `(hover: none)` (hover suppression), 1 `(max-width: 1024px)` + 1 `(min-width: 1400px)` (both intra-desktop, selectors `.ui-mode-desktop`-prefixed), 2 `print`. Zero mode-switching queries. - `[style*="display:none"]` selector hack removed. - JS layout-mode checks (`innerWidth <= 768`/`<= 600`) → `GoingsOn.viewport.isMobile()`. - New `js/viewport.js` module; new inline detection script in `index.html` head. - Section header at `styles.css:159–185` rewritten to document the new model. - `#task-view-toggle { display: none }` moved out of `@media (hover: none)` (was wrong axis — input capability instead of UI mode). - Latent bug fixed: mobile `@keyframes modalSlideIn/Out` were globally overriding the desktop variants (last-keyframes-wins). Renamed to `*Mobile` and applied via `animation-name` override. ### Phase 6 — remaining (not blocking) model for new contributors. Done 2026-05-24. Placed after "CSS Workflow"; covers CSS/JS surfaces, detection precedence, dev preview, the "mode is a build property, not viewport size" invariant, and the separate input-capability axis. detection script in `index.html` to read `window.__TAURI__.platform` before the UA fallback. Done 2026-05-24. Tauri 2's platform plugin is async (no sync `window.__TAURI__.platform`), which doesn't fit an inline-head script that must set the class before the stylesheet evaluates. Substituted: `navigator.userAgentData.mobile` (UA Client Hints) as a third tier, plus iPad-as-Mac detection (`navigator.maxTouchPoints > 1` on a Mac-reporting platform) in the UA regex tier. Works for WKWebView (iOS), Android WebView, and catches Safari 13+ iPads that report as Mac. Detail in `css_state_audit.md` "Detection script — updated 2026-05-24". - [ ] For production lockdown of the override (so users can't set `?ui=mobile` on a desktop build and break things): inject `__GO_BUILD_MODE__` at build time and gate the override branches. Defer until a production mobile-only desktop-binary distribution channel exists. (TestFlight iOS is its own binary; no override risk there.) ### Divergences from the plan - Kept `.mobile-hide` utility class (plan called for removing it). Rationale: it's one rule, the marker class makes intent visible in markup, and removing it would shift verbosity from CSS to HTML. - Phase 4 scope: prefixed only the *layout* rules that mobile UI hides entirely (header, top tab nav, pills, sidebars). Did not prefix shared component rules (buttons, forms, modals, cards) — they correctly stay in the base scope. --- ## Suggested order 1. **A. Buttons** — biggest leverage, sets the design language. 2. **B. Form inputs** — closely related (form chrome consistency). 3. **D. Badges** — small surface, quick win once button pattern is set. 4. **C. Cards** — falls into place once buttons + inputs unified. 5. **E. Avatars / F. Status dots / G. Toggles / H. Empty states** — cleanup. 6. **I. Relocate globals** — janitorial, do last. 7. **J. Tier 3** — only when surface area is reduced. --- ## Process notes - Rebuild `styles.min.css` via `./build-css.sh` after every batch. - The composition charter at the top of `styles.css` is the source of truth for what primitives exist. Update the EXISTING VOCABULARY inventory there whenever you add or rename a primitive/modifier. - Don't ship two ways to do the same thing during a migration; if A half-done blocks B, finish A first. - Spot-check after each surface migration: - Settings overlay (gear button, back button, nav) - Email list, email reader, compose modal, compose window - Project dashboard tiles - Task table action buttons - Toast undo - Bulk-select modal - Welcome wizard