- STATE/ROADMAP/config updated to reflect Phase 09.1 execution - 09-01 plan refreshed (gap-closure detail) - 09-02..09-05 plans updated during execution - Add untracked 09-06 plan + 01-01/01-02 PWA scaffolding plans (orphaned from earlier sessions) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
20 KiB
| phase | plan | type | wave | depends_on | gap_closure | files_modified | autonomous | requirements | must_haves | |||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 01-pwa-scaffolding | 02 | execute | 1 | true |
|
true |
|
|
Purpose: PWA-04 — make a safe-area utility available so any sticky top/bottom bar can opt in. ROADMAP Phase 1 SC #3 requires the utility to be available in Phase 1; Phase 2's contract (SHELL-05, SHELL-06) only mandates consumption. This plan restores the broken phase boundary identified by 01-VERIFICATION.md.
Output: app/styles/brand.css updated with two new @utility blocks (pt-safe, pb-safe) appended to the existing utility section. No other files touched. Verifiable by grep -E '@utility (pt-safe|pb-safe)' app/styles/brand.css and npm run build.
Why app/styles/brand.css (not app/globals.css):
- All named project utilities (
num,num-lg,num-xl,metric-label,surface-brand,surface-brand-ink,rule-brand,text-chrome,border-chrome,tagline,has-mark-watermark) already live there. brand.cssis already imported intoglobals.css(line 125) — no extra wiring needed.- Keeps utilities co-located so Phase 2 has a single file to scan when looking for project helpers.
globals.cssis reserved for Tailwind imports,@theme inlinetoken mapping, and:root/.darkvariable definitions — adding utility classes there would muddy that separation.
Note on traceability: this plan claims PWA-04 in its requirements frontmatter, restoring the orphaned-requirement state flagged by 01-VERIFICATION.md. The executor's SUMMARY (01-02-SUMMARY.md) should explicitly call out that PWA-04 is now satisfied, closing the requirements traceability table.
<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/REQUIREMENTS.md @.planning/phases/01-pwa-scaffolding/01-VERIFICATION.md @.planning/phases/01-pwa-scaffolding/01-01-SUMMARY.md @docs/superpowers/specs/2026-05-03-mobile-shell-design.md @CLAUDE.md @app/styles/brand.css @app/globals.cssTailwind 4 @utility syntax (already in use in this project):
- This project is Tailwind 4 with NO
tailwind.config.*file. Custom utilities are declared inline in CSS using the@utilityat-rule. - Pattern:
@utility name { /* CSS declarations */ }— Tailwind compiles this to a class.name { ... }that participates in the variant system (hover:name,md:name, etc.). - See existing examples in
app/styles/brand.csslines 70-140 (e.g.,@utility num { ... },@utility metric-label { ... },@utility surface-brand { ... }). - Each
@utilityblock holds plain CSS property declarations. No@applyis required for simplepadding-*cases.
env() CSS environment variables for safe areas:
env(safe-area-inset-top)— top safe-area inset (e.g., iPhone notch / Dynamic Island area).env(safe-area-inset-bottom)— bottom safe-area inset (e.g., iPhone home indicator area).- Browser-side CSS feature; no JavaScript involvement. Falls back to
0on browsers/devices without safe-area insets. - Requires
<meta name="viewport" content="...viewport-fit=cover">to take non-zero values. This is already shipped by 01-01 (viewportFit: "cover"inapp/layout.tsx).
Existing app/styles/brand.css structure (line numbers from current file):
- Lines 1-21: file header / brand documentation comment.
- Lines 23-52:
:rootoverrides (--wulf-blue, etc.) for the light theme. - Lines 54-62:
.darkoverrides. - Lines 64-68:
/* === Utility classes === */section header comment. - Lines 70-140: existing
@utilityblocks —num,num-lg,num-xl,metric-label,surface-brand,surface-brand-ink,rule-brand,text-chrome,border-chrome,tagline. - Lines 142-147:
/* === Wolf-mark watermark === */section header comment. - Lines 149-152:
@utility has-mark-watermark. - Lines 154-170:
.mark-watermarkplain rule +.dark .mark-watermarkoverride. - Insertion point for new utilities: after the
taglineutility (line 140) and before the watermark section header (line 142). This keeps utilities grouped before the watermark block, which has its own thematic header.
app/globals.css import wiring (already in place — DO NOT change):
- Line 125:
@import "./styles/brand.css";— pullsbrand.cssinto the global stylesheet at the end. Anything added tobrand.cssis automatically available app-wide. No additional wiring needed.
Spec wording (docs/superpowers/specs/2026-05-03-mobile-shell-design.md §5/§6):
- The mobile shell's sticky top header must respect
env(safe-area-inset-top). - The fixed bottom nav must respect
env(safe-area-inset-bottom)(often combined with the bottom-nav height). - The spec accepts either a named utility or Tailwind 4 arbitrary values (
pt-[env(safe-area-inset-top)]).
Why ship a named utility (not arbitrary values):
- Phase 2 will use these classes in 2+ places (header, bottom nav, drawer footer, possibly modals). A named utility is one source of truth — if the iOS rules ever change (e.g., add
max(env(safe-area-inset-top), 0.5rem)), it's a one-line edit instead of a multi-file find-and-replace. pt-safe/pb-safereads more clearly in JSX class lists thanpt-[env(safe-area-inset-top)].- ROADMAP Phase 1 SC #3 explicitly mentions "shared utility class" as one acceptable form — picking that form removes ambiguity for Phase 2.
Verification commands the executor will use:
grep -E '@utility pt-safe' app/styles/brand.cssgrep -E '@utility pb-safe' app/styles/brand.cssgrep -E 'env\(safe-area-inset-top\)' app/styles/brand.cssgrep -E 'env\(safe-area-inset-bottom\)' app/styles/brand.cssnpm run build(CSS @utility blocks must parse — broken syntax fails the Tailwind compile step in Next.js)npx tsc --noEmit --pretty(sanity check; CSS doesn't affect TS but pre-existing baseline must hold)
Insert exactly this block (including the leading section comment and the two @utility definitions):
/* === Safe-area insets =================================================
*
* Opt-in padding helpers for sticky top / fixed bottom bars on devices
* with notches, dynamic islands, or gesture home indicators. Pair with
* the viewport-fit=cover viewport meta (set in app/layout.tsx) — without
* that, env(safe-area-inset-*) resolves to 0 and these utilities are
* no-ops, which is the desired fallback on non-PWA / non-mobile contexts.
*
* Usage:
* <header class="sticky top-0 pt-safe ..."> // header clears notch
* <nav class="fixed bottom-0 pb-safe ..."> // bottom bar clears home bar
*
* Closes PWA-04 (REQUIREMENTS.md) and ROADMAP Phase 1 SC #3.
* ==================================================================== */
@utility pt-safe {
padding-top: env(safe-area-inset-top);
}
@utility pb-safe {
padding-bottom: env(safe-area-inset-bottom);
}
Notes on the choices (so a reviewer doesn't have to ask):
- Two utilities only (
pt-safe,pb-safe). The spec only requires top + bottom safe-area handling — that's what notches and home indicators occupy. Left/right insets (safe-area-inset-left,safe-area-inset-right) only matter for landscape mode on devices with notches, and the manifest pins orientation toportrait(per 01-01-SUMMARY.md). Addingpl-safe/pr-safenow would be speculative; Phase 2 can add them in 30 seconds if a real consumer appears. - No additive variants like
pt-safe-4. ROADMAP Phase 1 SC #3 only requires the base utility be available. Combinations like "safe-area inset PLUS 1rem" can be expressed at consume time withpt-safe pt-4or arbitrary values. Don't pre-build helpers that nothing yet calls. - Plain
env(safe-area-inset-top)(notmax(env(safe-area-inset-top), 0px)). The CSS env value already returns0when no inset is reported — wrapping it inmax(..., 0)is a no-op and adds noise. Wrap it later if a real device misbehaves. - Insertion location: alongside the other
@utilityblocks inbrand.css. Already justified in<objective>: keeps named utilities co-located, requires no new import wiring, fits the project's "Wulf design helpers" theme (these are layout primitives that go with the rest of the brand kit).
Do NOT:
- Edit
app/globals.css— the existing@import "./styles/brand.css"(line 125) is already correct. - Add a new file — these are utilities, not a new module.
- Add
pl-safe,pr-safe, or stacked variants — out of scope for PWA-04. - Modify any existing
@utilityblock (num,metric-label,tagline, etc.) — they are unrelated. - Modify the
:root/.darktoken sections at the top ofbrand.css— these are CSS custom properties, not utilities. - Touch the
.mark-watermarkplain rule or the watermark section header — they live below the insertion point and are unrelated. - Add a
tailwind.config.*file — this is a Tailwind 4 project with no config file, by design (CLAUDE.mdConventions to follow).
After editing, the file should be ~12-13 lines longer than before (1 comment block + 2 @utility definitions = ~13 lines including blank lines). The diff should show only additions, no deletions.
grep -q '@utility pt-safe' app/styles/brand.css && grep -q '@utility pb-safe' app/styles/brand.css && grep -q 'padding-top: env(safe-area-inset-top)' app/styles/brand.css && grep -q 'padding-bottom: env(safe-area-inset-bottom)' app/styles/brand.css && npx tsc --noEmit --pretty && npm run build
<acceptance_criteria>
- grep -E '@utility pt-safe' app/styles/brand.css matches exactly one line.
- grep -E '@utility pb-safe' app/styles/brand.css matches exactly one line.
- grep -E 'padding-top:\s*env\(safe-area-inset-top\)' app/styles/brand.css matches exactly one line.
- grep -E 'padding-bottom:\s*env\(safe-area-inset-bottom\)' app/styles/brand.css matches exactly one line.
- The existing @utility tagline block is still present and unchanged: grep -E '@utility tagline' app/styles/brand.css matches.
- The existing @utility num block is still present: grep -E '@utility num \{' app/styles/brand.css matches.
- The existing .mark-watermark rule is still present: grep -E '\.mark-watermark \{' app/styles/brand.css matches.
- The :root --wulf-blue token is still present: grep -E '\-\-wulf-blue:' app/styles/brand.css matches.
- No tailwind.config file was created: test ! -f tailwind.config.ts && test ! -f tailwind.config.js && test ! -f tailwind.config.mjs.
- app/globals.css is unchanged from baseline: grep -E '@import "\./styles/brand\.css";' app/globals.css matches (the existing import is intact).
- No pl-safe / pr-safe utilities were added (out of scope): ! grep -E '@utility (pl-safe|pr-safe)' app/styles/brand.css.
- No service worker file shipped: test ! -f public/sw.js && test ! -f public/service-worker.js.
- next-pwa is not in dependencies: ! grep '"next-pwa"' package.json.
- Type check passes: npx tsc --noEmit --pretty exits 0 (CSS changes don't affect TS, but the pre-existing baseline is preserved).
- Production build succeeds: npm run build exits 0. This is the load-bearing gate — Tailwind 4 will fail the build if the @utility syntax is malformed, so a green build confirms the new utilities compile and are emitted into the production CSS bundle.
</acceptance_criteria>
app/styles/brand.css contains two new @utility blocks — pt-safe (sets padding-top: env(safe-area-inset-top)) and pb-safe (sets padding-bottom: env(safe-area-inset-bottom)) — placed between the existing @utility tagline block and the /* === Wolf-mark watermark === */ section header. The block is preceded by a section comment that documents the intent, usage, and the PWA-04 / SC #3 requirement IDs being closed. No other file is modified. npm run build and npx tsc --noEmit --pretty both exit 0. PWA-04 is satisfied; the orphaned-requirement state from 01-VERIFICATION.md is closed.
<threat_model>
Trust Boundaries
| Boundary | Description |
|---|---|
| Browser ↔ static CSS bundle | Public client read of compiled Tailwind CSS containing .pt-safe / .pb-safe rules. No auth, no input, no data flow. |
STRIDE Threat Register
No trust boundaries crossed. CSS utilities are public client-side styles; env(safe-area-inset-*) is a CSS environment variable resolved by the browser based on the device viewport. There is no user input, no data flow, no auth surface, no new endpoint. ASVS-L1 baseline preserved — no new attack surface introduced beyond the (already-public) Tailwind CSS bundle.
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|---|---|---|---|---|
| T-01.02-01 | Information Disclosure | Compiled CSS bundle (.pt-safe, .pb-safe class rules) |
accept | CSS class rules are world-readable by design — same risk profile as every other Tailwind utility class. Contains zero secrets, zero user data, zero endpoints. |
| </threat_model> |
# New utilities exist in brand.css
grep -E '@utility pt-safe' app/styles/brand.css
grep -E '@utility pb-safe' app/styles/brand.css
# They reference the correct CSS env() variables
grep -E 'padding-top:\s*env\(safe-area-inset-top\)' app/styles/brand.css
grep -E 'padding-bottom:\s*env\(safe-area-inset-bottom\)' app/styles/brand.css
# Existing utilities and brand tokens are untouched
grep -E '@utility tagline' app/styles/brand.css
grep -E '@utility num \{' app/styles/brand.css
grep -E '\-\-wulf-blue:' app/styles/brand.css
# globals.css import wiring is unchanged
grep -E '@import "\./styles/brand\.css";' app/globals.css
# Out-of-scope items NOT added
! grep -E '@utility (pl-safe|pr-safe)' app/styles/brand.css
test ! -f tailwind.config.ts && test ! -f tailwind.config.js && test ! -f tailwind.config.mjs
test ! -f public/sw.js
! grep '"next-pwa"' package.json
# Type check (sanity, baseline preserved)
npx tsc --noEmit --pretty
# Production build (load-bearing — Tailwind 4 fails the build on malformed @utility syntax)
npm run build
With dev server running on port 3100, an OPTIONAL spot check (not required for the automated gate; useful for human verification on a real iPhone):
# Confirm the compiled .pt-safe / .pb-safe classes are present in the served CSS bundle
curl -s http://localhost:3100/ | grep -oE '/_next/static/css/[^"]+\.css' | head -1 | xargs -I {} curl -s "http://localhost:3100{}" | grep -E '\.pt-safe|\.pb-safe'
<success_criteria>
app/styles/brand.cssdefines@utility pt-safe { padding-top: env(safe-area-inset-top); }(PWA-04, SC #3).app/styles/brand.cssdefines@utility pb-safe { padding-bottom: env(safe-area-inset-bottom); }(PWA-04, SC #3).- The new utilities are placed in the existing utilities section of
brand.css(between@utility taglineand the watermark section), preceded by a section comment that documents intent and references PWA-04. - All pre-existing
@utilityblocks (num,num-lg,num-xl,metric-label,surface-brand,surface-brand-ink,rule-brand,text-chrome,border-chrome,tagline,has-mark-watermark) and the.mark-watermarkplain rule remain unchanged. app/globals.cssis unchanged (the existing@import "./styles/brand.css"on line 125 already pulls the new utilities into the global stylesheet).npm run buildexits 0 — confirms Tailwind 4 accepts the new@utilitysyntax and compiles.pt-safe/.pb-safeinto the production CSS bundle.npx tsc --noEmit --prettyexits 0 (baseline preserved; CSS edits don't affect TS).- No
tailwind.config.*file is created (Tailwind 4 + CLAUDE.md convention). - No service worker, no
next-pwa, no new dependencies introduced. - PWA-04 is satisfied; the orphaned-requirement state from
01-VERIFICATION.mdis closed (SUMMARY explicitly claims PWA-04). </success_criteria>