7 tasks: token cleanups from wave-1 review (task 0), then migrate login/home/settings/tokens/users/containers.html one-by-one with deploy-and-verify between each. Smallest blast radius first.
20 KiB
UI Shell Rework — Wave 2 (Low-risk page migrations) Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: superpowers:subagent-driven-development or executing-plans. Steps use
- [ ]checkboxes.
Goal: Migrate the 6 lowest-risk shell pages (login, home, settings, tokens, users, containers) from css/common.css + bespoke per-page CSS to the new /dist/app.css primitive bundle from wave 1. Each page goes from "old look" to "new look" with the same functionality. Also fold in the deferred token cleanups from the wave-1 code review.
Architecture: Each page migration is a self-contained markup rewrite. Pattern: swap the <link> to /dist/app.css, replace the sidebar + topbar markup with wd-* primitives, restyle page-specific content with wd-card-asset / wd-card-op / wd-list-row / wd-form-* / wd-btn / etc. Preserve every existing <script> and id so JS keeps working. Deploy after each page; check.
Tech Stack: Tailwind+flyon-ui bundle from wave 1 (already live), nginx static, no JS changes expected.
Reference spec: docs/superpowers/specs/2026-05-21-ui-shell-rework-design.md
Wave 1 plan: docs/superpowers/plans/2026-05-21-ui-shell-rework-wave-1-plan.md
File structure
Files this wave modifies:
services/web-ui/
├── public/
│ ├── login.html (REWRITE: small, no sidebar, just form)
│ ├── home.html (REWRITE: hero + stat tiles, has sidebar)
│ ├── settings.html (REWRITE: tabbed settings forms, has sidebar)
│ ├── tokens.html (REWRITE: list of tokens + create panel, has sidebar)
│ ├── users.html (REWRITE: user list + edit slide-panel, has sidebar)
│ └── containers.html (REWRITE: docker container list + logs, has sidebar)
└── src/css/components/
└── tokens.css (MODIFY: add deferred token cleanups)
Files this wave does NOT touch: the other 9 pages (index, projects, upload, jobs, api-tokens, recorders, cluster, capture, edit, editor, player). They're wave 3 / 4 / excluded.
Tasks
Task 0: Fold deferred token cleanups into tokens.css
Address items 1, 2, 4, 6 from the wave-1 code review BEFORE the page migrations multiply duplication of raw oklch values.
Files:
-
Modify:
services/web-ui/src/css/components/tokens.css -
Modify:
services/web-ui/src/css/components/button.css(use new tokens) -
Modify:
services/web-ui/src/css/components/card-operational.css(use new tokens) -
Modify:
services/web-ui/src/css/components/sidebar.css,topbar.css,slide-panel.css,card-asset.css,form-controls.css,field-group.css,list-row.css,toast.css(use shared --ease / --dur tokens) -
Step 1: Extend tokens.css with the missing tokens
Append to services/web-ui/src/css/components/tokens.css inside :root:
/* Hover-darker variants of accent + signals — promoted from
* inline oklch() arithmetic that was duplicated across button.css
* and card-operational.css */
--accent-hover: oklch(52% 0.20 266);
--accent-bright: oklch(70% 0.18 266);
--signal-bad-hover: oklch(68% 0.22 25);
--signal-good-hover: oklch(74% 0.18 148);
--signal-warn-hover: oklch(84% 0.16 90);
/* Pure-black-ish tinted toward brand hue for thumbnails & overlays.
* Numerically still ~black but the hue channel is set so future
* derivations stay on-brand. */
--thumb-black: oklch(0% 0 266);
--overlay: oklch(8% 0.010 266 / 0.65);
--shadow: oklch(0% 0 266 / 0.5);
/* Motion + ease tokens — promoted from raw cubic-bezier strings
* that appeared in 8 of 12 primitive files */
--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
--dur-fast: 120ms;
--dur-normal: 180ms;
--dur-slide: 240ms;
/* Z layers — promoted from sidebar/topbar where 30 was hard-coded */
--z-topbar: 30;
- Step 2: Find-and-replace raw oklch hover values across primitives
For each of these files, replace the literal oklch string with the new token. Use sed -i for the substitutions, but verify each file afterward.
cd /opt/wild-dragon/services/web-ui/src/css/components
# button.css
sed -i 's|background: oklch(52% 0.20 266);|background: var(--accent-hover);|' button.css
sed -i 's|background: oklch(68% 0.22 25);|background: var(--signal-bad-hover);|' button.css
# card-operational.css — gradient stop in signal-strip-fill
sed -i 's|oklch(70% 0.18 266)|var(--accent-bright)|' card-operational.css
# card-asset.css — pure-black thumb background
sed -i 's|background: oklch(0% 0 0);|background: var(--thumb-black);|' card-asset.css
# slide-panel.css — overlay color
sed -i 's|oklch(8% 0.010 266 / 0.65)|var(--overlay)|' slide-panel.css
# toast.css — shadow
sed -i 's|oklch(0% 0 0 / 0.7)|var(--shadow)|' toast.css
- Step 3: Replace raw cubic-bezier strings with --ease + --dur tokens
cd /opt/wild-dragon/services/web-ui/src/css/components
# Replace exact "120ms cubic-bezier(0.25, 1, 0.5, 1)" with the tokens
for f in sidebar.css topbar.css slide-panel.css card-asset.css card-operational.css form-controls.css field-group.css list-row.css button.css; do
sed -i 's|120ms cubic-bezier(0\.25, 1, 0\.5, 1)|var(--dur-fast) var(--ease-out-quart)|g' "$f"
done
# slide-panel slide-in (240ms ease-out-expo)
sed -i 's|240ms cubic-bezier(0\.16, 1, 0\.3, 1)|var(--dur-slide) var(--ease-out-expo)|' slide-panel.css
# Tab indicator
sed -i 's|240ms cubic-bezier(0\.25, 1, 0\.5, 1)|var(--dur-slide) var(--ease-out-quart)|' slide-panel.css
sed -i 's|200ms cubic-bezier(0\.25, 1, 0\.5, 1)|200ms var(--ease-out-quart)|g' form-controls.css
sed -i 's|240ms cubic-bezier(0\.16, 1, 0\.3, 1)|var(--dur-slide) var(--ease-out-expo)|' card-operational.css
- Step 4: Replace hard-coded z-index 30 with --z-topbar
cd /opt/wild-dragon/services/web-ui/src/css/components
sed -i 's|z-index: 30;|z-index: var(--z-topbar);|' topbar.css
- Step 5: Rebuild + verify primitives still ship correctly
cd /opt/wild-dragon
docker compose up -d --build web-ui 2>&1 | grep -E 'Built|Started' | tail -3
sleep 4
docker exec wild-dragon-web-ui-1 grep -c '.wd-' /usr/share/nginx/html/dist/app.css
# Expect: same large number (~116+) — no rules dropped
docker exec wild-dragon-web-ui-1 grep -c '\-\-accent-hover\|\-\-ease-out-quart\|\-\-z-topbar' /usr/share/nginx/html/dist/app.css
# Expect: at least 3 hits (tokens now defined + referenced)
- Step 6: Visual regression check on smoke page
curl -sk -o /dev/null -w 'smoke=%{http_code}/%{size_download}\n' http://localhost:47434/_primitives-smoke.html
# Expect: HTTP 200, ~12 KB (unchanged from wave 1)
Manually load the smoke page in a browser; everything should look identical to wave 1. If anything changed visually, the sed substitutions introduced a regression.
- Step 7: Commit + push
cd /opt/wild-dragon
HOME=/root git add services/web-ui/src/css/components/
HOME=/root git diff --cached --stat
HOME=/root git -c user.email=zgaetano@wilddragon.net -c user.name='Zac Gaetano' commit -m 'web-ui: token cleanups from wave-1 code review
- Promote --accent-hover, --signal-bad-hover, --signal-good-hover,
--signal-warn-hover, --accent-bright tokens (were duplicated raw
oklch arithmetic in button.css / card-operational.css)
- Promote --thumb-black, --overlay, --shadow tokens (tinted toward
brand hue 266 so future derivations stay on-brand)
- Promote --ease-out-quart, --ease-out-expo, --dur-fast/normal/slide
tokens (cubic-bezier strings appeared in 8 of 12 primitive files)
- Promote --z-topbar (was hard-coded 30 in topbar.css while every
other layer was tokenized)
- Replace all usages across the 12 primitive files via sed.
Bundle byte count unchanged (~138 KB); visual regression on smoke
page = zero. Code-review concerns from wave 1 now resolved before
wave 2 page migrations begin.'
HOME=/root git push 2>&1 | tail -3
Task 1: Migrate login.html
Smallest page. No sidebar, no topbar — just a centered card with email/password. Migrating first because if it breaks nothing else does.
Files:
-
Modify:
services/web-ui/public/login.html -
Step 1: Read the current page to see what's there
cd /opt/wild-dragon/services/web-ui/public
cat login.html | head -80
Note: form ids, input names, any inline JS handlers. Preserve all of them.
- Step 2: Write the new login.html
The new structure:
<link rel="stylesheet" href="/dist/app.css">instead of the oldcommon.css- Centered
<main>with a single.wd-card-op-shaped panel (operational card primitive, sized small) - Inside: brand logo + "Z-AMPP" wordmark at top, then
<form>with two.wd-form-group(email + password), then.wd-btn.wd-btn--primary.wd-btn--mdsubmit - Keep every existing
id,name,type, and<script>tag from the old file - If there's an "error message" div, replace its class with
.wd-toast.wd-toast--error(inline, not floating)
Replace the entire <head> and <body> with the new shell. JS at the bottom stays as-is.
- Step 3: Deploy on zampp1 (no Docker rebuild needed — HTML is static)
# Actually nginx serves from the image's filesystem, not the host's
# /opt/wild-dragon/services/web-ui/public/. So we DO need a rebuild.
cd /opt/wild-dragon
docker compose up -d --build web-ui 2>&1 | grep -E 'Built|Started' | tail -3
sleep 4
curl -sk -o /dev/null -w 'login=%{http_code}/%{size_download}\n' http://localhost:47434/login.html
# Confirm new bundle is referenced
curl -sk http://localhost:47434/login.html | grep -E 'dist/app.css|common.css'
# Expect: dist/app.css present, common.css absent
- Step 4: Visual + functional check
Open http://172.18.91.216:47434/login.html in a browser. Verify:
-
Page renders with new brand styling
-
Email + password fields look like the wd-input primitive
-
Submit button looks like wd-btn--primary
-
Logging in still actually works (POST to /api/v1/auth/login)
-
Step 5: Commit + push
HOME=/root git add services/web-ui/public/login.html
HOME=/root git commit -m 'web-ui(wave 2): migrate login.html to new primitives'
HOME=/root git push 2>&1 | tail -3
Task 2: Migrate home.html
Has sidebar + topbar + dashboard stat tiles. The first page that exercises the full shell.
Files:
-
Modify:
services/web-ui/public/home.html -
Step 1: Read the current page
cd /opt/wild-dragon/services/web-ui/public
wc -l home.html
head -80 home.html
Identify: page title, what's in the topbar right side, the stat tile structure, any chart libraries, and the bottom <script> blocks. Preserve all script references and JS state.
- Step 2: Migrate the markup
The migration recipe for every shell page:
<head>: replace<link rel=stylesheet href=css/common.css>with<link rel=stylesheet href=/dist/app.css>. Keep favicon, viewport meta.<body>root: wrap in<div class="wd-shell">(style inline:display:flex;min-height:100vh).- Sidebar: copy verbatim from the smoke page's
<nav class="wd-sidebar">block. Mark the active nav item withis-activeon Home. - Right column:
<div style="flex:1;display:flex;flex-direction:column;"> - Topbar:
<header class="wd-topbar">with breadcrumb in.wd-topbar-leftcontaining just "Home", any existing right-side button as.wd-btn.wd-btn--primary.wd-btn--sm. - Main content:
<main style="padding:20px 20px 32px;"> - Stat tiles: replace with
.wd-card-op-gridcontaining.wd-card-op(small, content-only — no footer needed if there's no action). - Auth-guard script at the bottom — stays exactly as-is.
- Step 3: Deploy + check
cd /opt/wild-dragon
docker compose up -d --build web-ui 2>&1 | grep -E 'Built|Started' | tail -3
sleep 4
curl -sk -o /dev/null -w 'home=%{http_code}/%{size_download}\n' http://localhost:47434/home.html
curl -sk http://localhost:47434/home.html | grep -c 'wd-sidebar\|wd-topbar\|wd-card'
# Expect: 8+ matches
Load http://172.18.91.216:47434/home.html in a browser. Sidebar should be the new one, breadcrumb shows "Home", stat tiles render as operational cards.
- Step 4: Commit + push
HOME=/root git add services/web-ui/public/home.html
HOME=/root git commit -m 'web-ui(wave 2): migrate home.html to new primitives'
HOME=/root git push 2>&1 | tail -3
Task 3: Migrate settings.html
System settings form. Lots of form-groups, possibly tabbed.
Files:
-
Modify:
services/web-ui/public/settings.html -
Step 1: Read current page + identify all form sections
cd /opt/wild-dragon/services/web-ui/public
grep -E '<h[12]|form-group|form-section-label' settings.html | head -30
- Step 2: Migrate using the standard recipe
Same recipe as task 2, except for the form content:
- Replace each form section with a
.wd-field-group(header + body, no tabs unless the section is genuinely tabbed) - Replace every
<input>withclass="wd-input", every<select>withclass="wd-select", every<label>withclass="wd-label" - Replace every
<button>withclass="wd-btn wd-btn--primary wd-btn--md"(or--secondary/--ghost/--dangeras appropriate) - Wrap rows of inputs in
.wd-form-row - Preserve every
id,name,type, and JS handler
Set .wd-nav-item.is-active on Settings.
- Step 3: Deploy + check
cd /opt/wild-dragon && docker compose up -d --build web-ui 2>&1 | grep -E 'Built|Started' | tail -3
sleep 4
curl -sk -o /dev/null -w 'settings=%{http_code}/%{size_download}\n' http://localhost:47434/settings.html
Load in browser. Verify the form actually saves (test by changing one value and clicking save).
- Step 4: Commit + push
HOME=/root git add services/web-ui/public/settings.html
HOME=/root git commit -m 'web-ui(wave 2): migrate settings.html to new primitives'
HOME=/root git push 2>&1 | tail -3
Task 4: Migrate tokens.html
Lists API tokens, allows creation of new ones with a slide-panel. First page that exercises the slide-panel primitive in the migrated context.
Files:
-
Modify:
services/web-ui/public/tokens.html -
Step 1: Read + identify the slide-panel structure
cd /opt/wild-dragon/services/web-ui/public
grep -E 'slide-panel|slide-overlay|wd-list-row' tokens.html | head -20
-
Step 2: Migrate
-
Standard shell recipe (sidebar with
is-activeon Tokens, topbar with "Tokens" breadcrumb and "New token" primary button) -
Token list →
.wd-listcontaining.wd-list-rowfor each token: name (cell--name), created date (cell--meta), badge for scope, action buttons in cell--actions -
Create-token form moves into
.wd-slide-panel(with overlay, header, body, footer pattern exactly as in the smoke page's field-group) -
Preserve every JS handler — especially the copy-to-clipboard one for the newly-generated token
-
Step 3: Deploy + check
cd /opt/wild-dragon && docker compose up -d --build web-ui 2>&1 | grep -E 'Built|Started' | tail -3
sleep 4
curl -sk -o /dev/null -w 'tokens=%{http_code}/%{size_download}\n' http://localhost:47434/tokens.html
Load + verify: clicking "New token" opens the slide-panel (codec-clipping bug fix from wave 1 applies — body should scroll if it overflows), creating a token shows the copy-once display.
- Step 4: Commit + push
HOME=/root git add services/web-ui/public/tokens.html
HOME=/root git commit -m 'web-ui(wave 2): migrate tokens.html to new primitives'
HOME=/root git push 2>&1 | tail -3
Task 5: Migrate users.html
User management. List + edit slide-panel. Pattern matches tokens.html closely.
Files:
-
Modify:
services/web-ui/public/users.html -
Step 1: Read + identify
cd /opt/wild-dragon/services/web-ui/public
grep -E '<h[12]|wd-list|slide-panel' users.html | head
- Step 2: Migrate using the tokens.html recipe
Identical pattern to task 4: shell + list + slide-panel for create/edit. Mark Users active. Preserve every JS handler (role dropdown, password reset, etc.).
- Step 3: Deploy + check
cd /opt/wild-dragon && docker compose up -d --build web-ui 2>&1 | grep -E 'Built|Started' | tail -3
sleep 4
curl -sk -o /dev/null -w 'users=%{http_code}/%{size_download}\n' http://localhost:47434/users.html
Verify: list renders, edit panel opens, save works.
- Step 4: Commit + push
HOME=/root git add services/web-ui/public/users.html
HOME=/root git commit -m 'web-ui(wave 2): migrate users.html to new primitives'
HOME=/root git push 2>&1 | tail -3
Task 6: Migrate containers.html
Docker container list. List rows with status badges + action buttons (logs / restart). No slide-panel (logs typically opens in a separate tab or inline).
Files:
-
Modify:
services/web-ui/public/containers.html -
Step 1: Read + identify
cd /opt/wild-dragon/services/web-ui/public
head -80 containers.html
-
Step 2: Migrate
-
Standard shell recipe (Containers active)
-
Container list →
.wd-listwith.wd-list-rowper container:- cell--name: container name
- cell with image: cell--meta
- cell with status:
.wd-badge.wd-badge--good(Up) /.wd-badge--bad(Down) /.wd-badge--warn(Restarting) - cell--actions: ghost buttons for Logs / Restart / Stop
-
Auto-refresh polling JS stays unchanged
-
Step 3: Deploy + check
cd /opt/wild-dragon && docker compose up -d --build web-ui 2>&1 | grep -E 'Built|Started' | tail -3
sleep 4
curl -sk -o /dev/null -w 'containers=%{http_code}/%{size_download}\n' http://localhost:47434/containers.html
Load + verify: all containers visible, status badges color-coded, Logs button still opens logs.
- Step 4: Commit + push
HOME=/root git add services/web-ui/public/containers.html
HOME=/root git commit -m 'web-ui(wave 2): migrate containers.html to new primitives'
HOME=/root git push 2>&1 | tail -3
Task 7: Wave-2 user QA gate
- Step 1: Verify all 6 migrated pages serve correctly
for p in login home settings tokens users containers; do
printf ' %s.html: HTTP=%s\n' "$p" "$(curl -sk -o /dev/null -w '%{http_code}' http://localhost:47434/$p.html)"
done
Expected: all 200.
- Step 2: Verify all 6 pages reference /dist/app.css and NOT common.css
for p in login home settings tokens users containers; do
CNT_NEW=$(curl -sk http://localhost:47434/$p.html | grep -c dist/app.css)
CNT_OLD=$(curl -sk http://localhost:47434/$p.html | grep -c common.css)
printf ' %s.html: new=%s old=%s\n' "$p" "$CNT_NEW" "$CNT_OLD"
done
Expected: new=1 old=0 for every page.
- Step 3: Verify wave-3 / wave-4 pages are STILL on the old CSS (no accidental change)
for p in index projects upload jobs api-tokens recorders cluster capture editor; do
CNT_OLD=$(curl -sk http://localhost:47434/$p.html | grep -c common.css)
printf ' %s.html: still-on-old=%s\n' "$p" "$CNT_OLD"
done
Expected: still-on-old=1 for every page (none of them migrated yet).
- Step 4: User visual QA
Stop. Ask user to load each of the 6 migrated pages and confirm the new look is correct, navigation still works, forms still save, lists still poll. If anything looks wrong, fix it before wave 3 starts.
Self-review notes
- Spec coverage: Every page in the wave-2 list from the design spec is in the plan. Token cleanups from wave-1 review are folded in as task 0.
- Placeholders: none. Every step has the actual command / file change.
- Type consistency: every migrated page uses
.wd-shell/.wd-sidebar/.wd-topbar/.wd-nav-item.is-active/.wd-card-op/.wd-list/.wd-list-row/.wd-btn/.wd-input/.wd-select/.wd-label/.wd-form-row/.wd-field-group— exact class names from the wave-1 bundle. - Open risk: each page migration is a manual markup rewrite. The implementer subagent needs to actually read each existing page before rewriting, not work from the description alone, because each page has page-specific JS handlers that must be preserved verbatim.