|
|
@@ -56,7 +56,8 @@ per-cell audit trail.
|
|
|
│ └── assets/
|
|
|
│ ├── css/app.css # GENERATED at image-build time (gitignored)
|
|
|
│ └── js/
|
|
|
-│ ├── app.js # site-wide; data-href click handler
|
|
|
+│ ├── theme-init.js # Phase 16: synchronous dark-class set from localStorage (no FOUC)
|
|
|
+│ ├── app.js # site-wide; data-href click handler + hamburger menu + theme toggle
|
|
|
│ ├── sprint-planner.js # /sprints/{id} Arbeitstage + task list
|
|
|
│ └── sprint-settings.js # /sprints/{id}/settings
|
|
|
├── src/
|
|
|
@@ -360,205 +361,68 @@ with a `BOOTSTRAP_ADMIN` audit row.
|
|
|
"Phase 15 — Big-screen viewer" section with the six manual
|
|
|
scenarios from the plan.
|
|
|
|
|
|
+- [x] **Phase 16 — Dark-mode toggle + light-mode contrast
|
|
|
+ cleanup** (`94b2841`). Two small palette issues addressed
|
|
|
+ at once: (a) both body and table-header bands used
|
|
|
+ `bg-slate-50`, so table headers blended into the page —
|
|
|
+ body bumps one shade cooler to `bg-slate-100` (the
|
|
|
+ user's explicit ask), `<thead>` bands stay at `bg-slate-50`
|
|
|
+ and now read as a distinct lighter strip; (b) no dark
|
|
|
+ palette existed at all, painful for the Phase 15 beamer
|
|
|
+ view in dim conference rooms. Manual toggle only — no
|
|
|
+ `prefers-color-scheme` auto-detect. `tailwind.config.js`
|
|
|
+ gains `darkMode: 'class'`. New
|
|
|
+ `public/assets/js/theme-init.js` (8 lines, synchronous
|
|
|
+ `<script src>` in `<head>` before the stylesheet) reads
|
|
|
+ `localStorage['sp:theme']` inside a try/catch and sets
|
|
|
+ `<html class="dark">` if the value is `'dark'` — no FOUC.
|
|
|
+ `views/layout.php` and `views/sprints/present.php` both
|
|
|
+ include the init script; the present route emits its own
|
|
|
+ `<!doctype html>`, hence two tags. `public/assets/js/app.js`
|
|
|
+ grows a third vanilla-JS IIFE (~17 lines) wiring
|
|
|
+ `[data-theme-toggle]`: toggle the class on `<html>`, write
|
|
|
+ `sp:theme`, stamp the `[data-theme-label]` text; writes
|
|
|
+ wrapped in try/catch so private-window denials no-op. The
|
|
|
+ toggle lives in the hamburger menu in `views/layout.php`
|
|
|
+ as a new "Theme" row above the `<hr>` divider, visible to
|
|
|
+ admins and non-admins alike (theme is a personal
|
|
|
+ preference, not an admin action); the divider now always
|
|
|
+ renders (previously only admins saw one) because the
|
|
|
+ Theme row always renders. Every view file (`views/layout`,
|
|
|
+ `views/home`, `views/auth/local`, `views/workers/index`,
|
|
|
+ `views/users/index`, `views/sprints/{new,settings,show,
|
|
|
+ present}`, `views/audit/index`) gets a systematic
|
|
|
+ `dark:` sweep: body/card/header surfaces on the
|
|
|
+ `slate-900/800/700` ramp, borders on `slate-700` (600 for
|
|
|
+ inputs), primary text `slate-100`, secondary `slate-400`,
|
|
|
+ inputs `dark:bg-slate-800 dark:border-slate-600
|
|
|
+ dark:text-slate-100 dark:focus:ring-slate-500`, links
|
|
|
+ `dark:text-blue-400 dark:hover:text-blue-300`, success /
|
|
|
+ error / amber flash chips on `*-900` backgrounds with
|
|
|
+ `*-200` text and `*-800` borders, admin badge
|
|
|
+ `dark:bg-amber-900 dark:text-amber-200`, Phase 12 weekday
|
|
|
+ dots active `dark:bg-green-400` / off `dark:bg-slate-600`,
|
|
|
+ capacity "available" red `dark:text-red-400`, audit action
|
|
|
+ chips similarly remapped. `assets/css/input.css` needed no
|
|
|
+ edits — it carries no colour classes (only the
|
|
|
+ `.focus-auto-hidden` utility and the `.beamer-root`
|
|
|
+ typography block, both colour-free). Strict CSP stays
|
|
|
+ intact (theme-init.js is a standard `<script src>` under
|
|
|
+ `script-src 'self'`). Sign-out form block untouched — still
|
|
|
+ a native POST with the `_csrf` hidden input. Zero PHPUnit
|
|
|
+ — same pattern as Phases 10, 13, 14, 15; the change is CSS
|
|
|
+ class additions plus ~25 lines of vanilla JS without a unit
|
|
|
+ surface the existing harness can reach. Tests at 88 / 208.
|
|
|
+ ACCEPTANCE.md gains a "Phase 16 — Dark mode + light
|
|
|
+ contrast" section with the six scenarios from the plan
|
|
|
+ (light-band separation, toggle flip, reload persistence
|
|
|
+ under Network throttling + no FOUC, present view inherits
|
|
|
+ dark, admin-pages contrast sweep, private-window
|
|
|
+ localStorage denial fallback).
|
|
|
+
|
|
|
### Upcoming
|
|
|
|
|
|
-- [ ] **Phase 16 — Dark-mode toggle + light-mode contrast cleanup**
|
|
|
-
|
|
|
- **Problem.** Two things are slightly off in the current theme:
|
|
|
- 1. Every page uses `bg-slate-50` on `<body>` and the table
|
|
|
- `<thead>` bands *also* use `bg-slate-50` — so the headers
|
|
|
- blend into the page. Cards (white) pop, but table
|
|
|
- headers vanish.
|
|
|
- 2. Users working in low-light rooms or on projectors/beamers
|
|
|
- (Phase 15!) want a dark palette. Today there's no switch,
|
|
|
- no CSS scoping for it, and Tailwind's dark variants are
|
|
|
- disabled — adding `dark:` classes right now would be
|
|
|
- dead weight.
|
|
|
-
|
|
|
- **Goal.** One toggle, global per-browser, that flips the whole
|
|
|
- app between a **light** and **dark** palette. In light mode,
|
|
|
- push the page background one shade cooler so the table header
|
|
|
- bands visibly separate from the page; in dark mode, pick a
|
|
|
- neutral-slate ramp that's legible at a distance on a beamer
|
|
|
- without being pure black. No system-preference auto-detect —
|
|
|
- the user asked for manual control.
|
|
|
-
|
|
|
- **Scope (plan).**
|
|
|
-
|
|
|
- 1. **Tailwind config (`tailwind.config.js`).**
|
|
|
- - Add `darkMode: 'class'` so `dark:` variants activate only
|
|
|
- when `<html class="dark">` is set. No change to the
|
|
|
- content globs — the JIT already scans `views/**/*.php`
|
|
|
- and `public/assets/js/**/*.js`, so every new `dark:bg-…`
|
|
|
- class we type ends up in the compiled CSS.
|
|
|
-
|
|
|
- 2. **Theme init — new `public/assets/js/theme-init.js`.**
|
|
|
- - Tiny synchronous script (≤ 6 lines, no deps). Reads
|
|
|
- `localStorage.getItem('sp:theme')` and, if it is
|
|
|
- `'dark'`, calls
|
|
|
- `document.documentElement.classList.add('dark')`. Empty /
|
|
|
- missing / any other value means light.
|
|
|
- - Loaded from `<head>` **synchronously** (no `defer`, no
|
|
|
- `async`) right before the stylesheet, so there's no FOUC
|
|
|
- flash between bright and dark on page load.
|
|
|
- - Same file is injected by both `views/layout.php` and
|
|
|
- `views/sprints/present.php` (Phase 15 emits its own
|
|
|
- `<!doctype html>`). Since the CSP already allows
|
|
|
- `script-src 'self'`, the new file just needs a standard
|
|
|
- `<script src>` tag — no inline, still CSP-clean.
|
|
|
-
|
|
|
- 3. **Light-mode rebalance (the user's explicit ask).**
|
|
|
- - Body: `bg-slate-50 → bg-slate-100`. Slightly cooler page
|
|
|
- gray.
|
|
|
- - Table headers stay at `bg-slate-50`, so against the new
|
|
|
- page background they now read as a distinct lighter band.
|
|
|
- No structural change — just the body tint shifts.
|
|
|
- - Card sections (`bg-white`) already pop above slate-100 —
|
|
|
- unchanged.
|
|
|
- - Hamburger-menu dropdown and sprint toolbar bg stay white
|
|
|
- — the existing shadow + border already separate them.
|
|
|
-
|
|
|
- 4. **Dark-mode palette.**
|
|
|
- - Ramp (all Tailwind defaults so they work with the vendored
|
|
|
- build):
|
|
|
- * body: `dark:bg-slate-900`
|
|
|
- * cards / panels: `dark:bg-slate-800`
|
|
|
- * table header bands: `dark:bg-slate-700`
|
|
|
- * borders: `dark:border-slate-700`
|
|
|
- * primary text: `dark:text-slate-100`
|
|
|
- * secondary text: `dark:text-slate-400`
|
|
|
- * inputs / selects / textareas:
|
|
|
- `dark:bg-slate-800 dark:border-slate-600
|
|
|
- dark:text-slate-100 dark:focus:ring-slate-500`
|
|
|
- * links: `dark:text-blue-400 dark:hover:text-blue-300`
|
|
|
- * success flash: `dark:bg-green-900 dark:text-green-200
|
|
|
- dark:border-green-800`
|
|
|
- * error flash: `dark:bg-red-900 dark:text-red-200
|
|
|
- dark:border-red-800`
|
|
|
- * admin badge: `dark:bg-amber-900 dark:text-amber-200`
|
|
|
- * Phase 12 weekday dots: active
|
|
|
- `dark:bg-green-400`, off `dark:bg-slate-600` (brighter
|
|
|
- on-state so they still read from the back of a room)
|
|
|
- * capacity "available" red:
|
|
|
- `dark:text-red-400`
|
|
|
- - Apply these in one sweep across every view plus
|
|
|
- `public/assets/css/app.css`'s input classes (`input.css`
|
|
|
- is the source; it's the only hand-edited CSS).
|
|
|
-
|
|
|
- 5. **Toggle UI — hamburger menu (Phase 14).**
|
|
|
- - Above the `<hr>` that separates admin items from Sign out
|
|
|
- in `views/layout.php`, insert a **Theme** row:
|
|
|
- ```html
|
|
|
- <button type="button" role="menuitem" data-theme-toggle
|
|
|
- class="w-full text-left px-3 py-2 text-sm
|
|
|
- text-slate-700 hover:bg-slate-50 flex
|
|
|
- items-center justify-between
|
|
|
- dark:text-slate-200 dark:hover:bg-slate-700">
|
|
|
- <span>Theme</span>
|
|
|
- <span data-theme-label class="text-slate-500
|
|
|
- dark:text-slate-400">
|
|
|
- Light
|
|
|
- </span>
|
|
|
- </button>
|
|
|
- ```
|
|
|
- - Non-admin users also get this row (not gated on
|
|
|
- `$currentUser->isAdmin`) — theme is a personal preference,
|
|
|
- not an admin action.
|
|
|
- - The Phase 14 menu-controller in `app.js` already closes
|
|
|
- the dropdown on any `role="menuitem"` click; we piggyback
|
|
|
- on that and add a separate handler that flips the class.
|
|
|
-
|
|
|
- 6. **JS — extend `public/assets/js/app.js`.**
|
|
|
- - ~10 new lines. On click of `[data-theme-toggle]`:
|
|
|
- toggle `dark` on `<html>`, persist
|
|
|
- `localStorage['sp:theme']` (`'dark'` or `'light'`),
|
|
|
- update the `[data-theme-label]` text. On boot (after
|
|
|
- theme-init.js already decided the initial state), read
|
|
|
- the class state and stamp the label so the hamburger
|
|
|
- opens with the correct word showing.
|
|
|
-
|
|
|
- 7. **Sweep — views to touch.**
|
|
|
- - `views/layout.php` — body bg, header border/bg, hamburger
|
|
|
- button colours, menu panel, badge, status chip, main
|
|
|
- container.
|
|
|
- - `views/home.php` — sprint-row hover states, empty
|
|
|
- banner.
|
|
|
- - `views/auth/local.php` — login card + inputs.
|
|
|
- - `views/workers/index.php`, `views/users/index.php` —
|
|
|
- list tables + buttons.
|
|
|
- - `views/sprints/new.php`, `settings.php` — form cards,
|
|
|
- inputs, "Apply" button, weekday checkboxes.
|
|
|
- - `views/sprints/show.php` — Arbeitstage table, capacity
|
|
|
- card, task toolbar, dropdowns, task table, weekday dots,
|
|
|
- focus/reset buttons.
|
|
|
- - `views/sprints/present.php` — same task-list markup as
|
|
|
- show; reuses input.css. Also needs to include
|
|
|
- theme-init.js in its own `<head>`.
|
|
|
- - `views/audit/index.php` — filter pills, audit rows,
|
|
|
- collapsible diffs (if present).
|
|
|
- - `assets/css/input.css` — base form-control layer: add
|
|
|
- `dark:` siblings for every `class` used by form inputs.
|
|
|
-
|
|
|
- 8. **Edge cases.**
|
|
|
- - **FOUC on a slow connection.** theme-init.js is
|
|
|
- synchronous + before the stylesheet, so the class is set
|
|
|
- before any style resolves. Should look seamless. Test
|
|
|
- with devtools throttling on the acceptance walkthrough.
|
|
|
- - **Private-mode / localStorage denied.** `try/catch`
|
|
|
- around the read and write; default to light.
|
|
|
- - **Dark-mode beamer view.** People projecting in a
|
|
|
- dim conference room will appreciate this most. Make sure
|
|
|
- the Phase 15 present route picks up the class. Verified
|
|
|
- by adding theme-init.js to present.php's own `<head>`.
|
|
|
- - **Existing inline styles that use Tailwind colours**
|
|
|
- (e.g. `bg-green-500` on the weekday dots) stay — we add
|
|
|
- `dark:bg-green-400` next to them. Tailwind handles both
|
|
|
- states from the same class list.
|
|
|
- - **Third-party CSS — jQuery UI base theme.** The
|
|
|
- code.jquery.com base CSS still loads from CDN and is not
|
|
|
- dark-aware. jQuery UI only shows up during drag
|
|
|
- operations (worker reorder, task reorder); its ghost
|
|
|
- element looks slightly out of place in dark mode. Accept
|
|
|
- and note in §10 deferred items.
|
|
|
-
|
|
|
- 9. **Tests.** Zero PHPUnit. The phase is CSS class additions
|
|
|
- plus ~10 lines of vanilla JS — pattern-matches Phases 10,
|
|
|
- 13, 14, 15 which all skipped automated coverage. Manual
|
|
|
- acceptance in `ACCEPTANCE.md` under a new
|
|
|
- "Phase 16 — Dark mode + light contrast" section:
|
|
|
- (a) fresh page load in light mode: body is cooler than
|
|
|
- the table headers (visible band separation);
|
|
|
- (b) click Theme in the hamburger menu → whole app flips
|
|
|
- to dark; label reads "Dark";
|
|
|
- (c) reload the page → dark persists, no flash of light
|
|
|
- before styles apply;
|
|
|
- (d) open `/sprints/{id}/present` in a new tab → picks up
|
|
|
- dark too (theme-init.js included in its head);
|
|
|
- (e) back to light mode, open Workers / Users / Audit /
|
|
|
- Settings pages: no stray white-on-white or
|
|
|
- unreadable text anywhere;
|
|
|
- (f) private-window (localStorage denied) → defaults to
|
|
|
- light, toggle no-ops without throwing.
|
|
|
-
|
|
|
- **Out of scope.**
|
|
|
- - `prefers-color-scheme` auto-detection. User explicitly
|
|
|
- asked for manual control only.
|
|
|
- - Per-user server-side theme preference (storing in the
|
|
|
- `users` table). Browser-local is enough for now.
|
|
|
- - High-contrast accessibility mode (WCAG AAA). Separate
|
|
|
- concern; revisit if someone needs it.
|
|
|
- - Re-theming jQuery UI. See edge cases; deferred to §10.
|
|
|
- - A third "sepia" / "paper" mode. Two modes only.
|
|
|
-
|
|
|
- **Spec alignment.**
|
|
|
- - §3 layout: add `public/assets/js/theme-init.js` to the
|
|
|
- `public/assets/js/` block.
|
|
|
- - §4 / §5 / §7: no changes — no new schema, math, or audit
|
|
|
- surface.
|
|
|
- - §6 Routes: unchanged — CSS + JS only.
|
|
|
- - §11 phpunit count stays at 88.
|
|
|
- - Strict CSP (§ Phase 11) stays intact — theme-init.js is a
|
|
|
- standard `<script src>` under `script-src 'self'`.
|
|
|
+Nothing scheduled.
|
|
|
|
|
|
## 10. Residual known gaps / deferred items
|
|
|
|
|
|
@@ -572,6 +436,15 @@ with a `BOOTSTRAP_ADMIN` audit row.
|
|
|
are E_DEPRECATED but still emit — harmless, and silenced by
|
|
|
`ini_set('display_errors','0')` in production. Upstream library needs a
|
|
|
release.
|
|
|
+- **jQuery UI CDN CSS is not dark-aware.** The base theme on
|
|
|
+ `code.jquery.com/ui/1.13.3/themes/base/jquery-ui.css` is a light-only
|
|
|
+ stylesheet. It only shows up during drag operations (worker reorder on
|
|
|
+ `/sprints/{id}/settings`, sprint-worker / task reorder on
|
|
|
+ `/sprints/{id}`); the sortable ghost element reads slightly out of
|
|
|
+ place on a `dark:bg-slate-900` body. Accepted for now — the alternative
|
|
|
+ is self-hosting a custom jQuery UI theme inside the Docker css-builder
|
|
|
+ stage, which is a larger chunk of work than the cosmetic mismatch
|
|
|
+ warrants.
|
|
|
- **Manual acceptance walkthrough** ([ACCEPTANCE.md](ACCEPTANCE.md))
|
|
|
hasn't been executed end-to-end by a human yet — it's a documentary
|
|
|
follow-up that should happen in the running container.
|
|
|
@@ -615,15 +488,13 @@ vendor/bin/phpunit
|
|
|
Tell Claude:
|
|
|
|
|
|
> Working on `/Users/achiappa/Development/claude_code_private/sprint_planer_web`.
|
|
|
-> Read `HANDOFF.md`, the git log, and `ACCEPTANCE.md`. Phases 1–15
|
|
|
-> are shipped (see §9); Phase 16 (manual dark-mode toggle + light-
|
|
|
-> mode contrast cleanup) is planned but not implemented — the
|
|
|
-> scope is in §9 under Upcoming. Other outstanding items are in
|
|
|
-> §10 (mostly a human-run acceptance walkthrough in the running
|
|
|
-> container). If I ask you to work Phase 16, follow the maintenance
|
|
|
-> rule in §14 — commit code, then commit a HANDOFF.md update
|
|
|
-> separately that moves the entry from Upcoming → Shipped with its
|
|
|
-> SHA.
|
|
|
+> Read `HANDOFF.md`, the git log, and `ACCEPTANCE.md`. Phases 1–16
|
|
|
+> are shipped (see §9) — nothing is currently scheduled. Outstanding
|
|
|
+> items are in §10 (mostly a human-run acceptance walkthrough in the
|
|
|
+> running container, plus the jQuery UI dark-mode cosmetic gap noted
|
|
|
+> there). If I ask you to plan or work a new phase, follow the
|
|
|
+> maintenance rule in §14 — commit code, then commit a HANDOFF.md
|
|
|
+> update separately that marks the new work shipped with its SHA.
|
|
|
|
|
|
Claude should verify what's described here against actual repo state
|
|
|
before acting — nothing here is load-bearing once it grows stale.
|
|
|
@@ -631,6 +502,9 @@ before acting — nothing here is load-bearing once it grows stale.
|
|
|
## 13. Git history (as of this handoff)
|
|
|
|
|
|
```
|
|
|
+94b2841 Phase 16: dark-mode toggle + light-mode contrast cleanup
|
|
|
+0d7124a HANDOFF.md: add Phase 16 plan (dark-mode toggle + light-mode contrast)
|
|
|
+d4738d7 HANDOFF.md: note buildTaskRow owner-dropdown hotfix on Phase 10
|
|
|
7c298d3 Fix: buildTaskRow owner dropdown was empty until a page refresh
|
|
|
c70e442 HANDOFF.md: mark Phase 15 shipped
|
|
|
d1dda4f Phase 15: big-screen (beamer) task viewer at /sprints/{id}/present
|