8 tasks, 7 commits, no worktree. Tasks 1-3 build assets; Task 4 sweeps styles.css palette; Task 5 renames sig-block--blue to --gold; Tasks 6-7 sweep inline colors in 6 TS files + setup.html; Task 8 verifies. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
21 KiB
Logo Refresh + Extension Palette Shift Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Replace the current arched-niche-with-blue-gem logo with a round chapel-style theca + fleur-de-lis finial in burnished gold/deep red, and shift the extension's primary accent from GitHub-blue to the matching gold ramp.
Architecture: No new code paths or behavior changes — this is asset replacement (2 SVGs + 3 PNGs) and a static color palette swap across CSS + inline TS/HTML colors. The CLI/dark feel is preserved (backgrounds and text colors untouched). One CSS class rename (sig-block--blue → sig-block--gold) sweeps through the consumers + a test.
Tech Stack: SVG (hand-authored), ImageMagick (magick — preferred per project memory) for SVG → PNG, CSS, TypeScript, vitest, webpack.
Spec: docs/superpowers/specs/2026-04-24-relicario-logo-refresh-design.md (commit 4b7f1fd).
Task 1: Replace master logo SVG
Files:
-
Modify:
extension/icons/relicario-logo.svg(overwrite entirely) -
Step 1: Overwrite the master SVG
Replace the file contents with:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 220 240" fill="none">
<defs>
<radialGradient id="redTheca" cx="0.4" cy="0.35">
<stop offset="0%" stop-color="#9a1a1a"/>
<stop offset="100%" stop-color="#3a0a0a"/>
</radialGradient>
<linearGradient id="goldRing" x1="0" x2="1">
<stop offset="0%" stop-color="#d2ab43"/>
<stop offset="50%" stop-color="#f5d97a"/>
<stop offset="100%" stop-color="#7c5719"/>
</linearGradient>
<linearGradient id="goldHi" x1="0" x2="1">
<stop offset="0%" stop-color="#fde9a8"/>
<stop offset="100%" stop-color="#d2ab43"/>
</linearGradient>
</defs>
<!-- Pedestal (compact) -->
<ellipse cx="110" cy="226" rx="44" ry="5" fill="url(#goldRing)"/>
<rect x="78" y="212" width="64" height="14" rx="2" fill="url(#goldRing)"/>
<rect x="98" y="202" width="24" height="12" fill="url(#goldRing)"/>
<ellipse cx="110" cy="208" rx="14" ry="3" fill="#7c5719"/>
<ellipse cx="110" cy="202" rx="18" ry="4" fill="url(#goldRing)"/>
<!-- Body, bezel, theca -->
<circle cx="110" cy="130" r="72" fill="url(#goldRing)"/>
<path d="M 110 58 A 72 72 0 0 0 38 130" stroke="#fde9a8" stroke-width="2" fill="none" opacity="0.6"/>
<circle cx="110" cy="130" r="60" fill="#7c5719"/>
<circle cx="110" cy="130" r="56" fill="url(#redTheca)"/>
<ellipse cx="86" cy="108" rx="16" ry="7" fill="#ffffff" opacity="0.14" transform="rotate(-30 86 108)"/>
<!-- Asterisk gem with pinwheel facets -->
<g transform="translate(110, 130)">
<g transform="rotate(0)">
<path d="M 0 0 L -4.5 -3.5 C -5.5 -16, -3.5 -29, 0 -36 Z" fill="#f5d97a"/>
<path d="M 0 0 L 4.5 -3.5 C 5.5 -16, 3.5 -29, 0 -36 Z" fill="#8a5e1c"/>
</g>
<g transform="rotate(60)">
<path d="M 0 0 L -4.5 -3.5 C -5.5 -16, -3.5 -29, 0 -36 Z" fill="#f5d97a"/>
<path d="M 0 0 L 4.5 -3.5 C 5.5 -16, 3.5 -29, 0 -36 Z" fill="#8a5e1c"/>
</g>
<g transform="rotate(120)">
<path d="M 0 0 L -4.5 -3.5 C -5.5 -16, -3.5 -29, 0 -36 Z" fill="#f5d97a"/>
<path d="M 0 0 L 4.5 -3.5 C 5.5 -16, 3.5 -29, 0 -36 Z" fill="#8a5e1c"/>
</g>
<g transform="rotate(180)">
<path d="M 0 0 L -4.5 -3.5 C -5.5 -16, -3.5 -29, 0 -36 Z" fill="#f5d97a"/>
<path d="M 0 0 L 4.5 -3.5 C 5.5 -16, 3.5 -29, 0 -36 Z" fill="#8a5e1c"/>
</g>
<g transform="rotate(240)">
<path d="M 0 0 L -4.5 -3.5 C -5.5 -16, -3.5 -29, 0 -36 Z" fill="#f5d97a"/>
<path d="M 0 0 L 4.5 -3.5 C 5.5 -16, 3.5 -29, 0 -36 Z" fill="#8a5e1c"/>
</g>
<g transform="rotate(300)">
<path d="M 0 0 L -4.5 -3.5 C -5.5 -16, -3.5 -29, 0 -36 Z" fill="#f5d97a"/>
<path d="M 0 0 L 4.5 -3.5 C 5.5 -16, 3.5 -29, 0 -36 Z" fill="#8a5e1c"/>
</g>
<polygon points="0,-6 5.2,-3 5.2,3 0,6 -5.2,3 -5.2,-3" fill="#d2ab43" stroke="#7c5719" stroke-width="0.6"/>
<circle cx="-1.5" cy="-2" r="1.4" fill="#fff3cf"/>
</g>
<!-- Hinge collar -->
<rect x="98" y="50" width="24" height="10" rx="2" fill="url(#goldRing)"/>
<line x1="100" y1="55" x2="120" y2="55" stroke="#7c5719" stroke-width="0.8"/>
<!-- Fleur-de-lis -->
<g transform="translate(110, 50)">
<rect x="-3.5" y="-12" width="7" height="12" fill="url(#goldRing)"/>
<rect x="-16" y="-18" width="32" height="7" rx="1.5" fill="url(#goldRing)"/>
<rect x="-3" y="-19" width="6" height="9" rx="0.8" fill="#7c5719"/>
<path d="M 0 -18 Q -8 -36, -4 -54 Q -1 -62, 0 -64 Q 1 -62, 4 -54 Q 8 -36, 0 -18 Z" fill="url(#goldRing)"/>
<path d="M 0 -22 Q -2.5 -36, 0 -52 Q 2.5 -36, 0 -22 Z" fill="#7c5719" opacity="0.55"/>
<circle cx="0" cy="-66" r="2.5" fill="url(#goldHi)"/>
<path d="M -4 -18 Q -22 -22, -26 -38 Q -22 -50, -16 -50 Q -16 -38, -10 -32 Q -6 -28, -4 -28 Z" fill="url(#goldRing)"/>
<ellipse cx="-25" cy="-44" rx="2" ry="3" fill="#7c5719" opacity="0.4" transform="rotate(-20 -25 -44)"/>
<path d="M 4 -18 Q 22 -22, 26 -38 Q 22 -50, 16 -50 Q 16 -38, 10 -32 Q 6 -28, 4 -28 Z" fill="url(#goldRing)"/>
<ellipse cx="25" cy="-44" rx="2" ry="3" fill="#7c5719" opacity="0.4" transform="rotate(20 25 -44)"/>
</g>
</svg>
- Step 2: Verify it parses
Run: xmllint --noout extension/icons/relicario-logo.svg && echo OK
Expected: OK
If xmllint isn't installed, fallback: python3 -c "import xml.etree.ElementTree as T; T.parse('extension/icons/relicario-logo.svg'); print('OK')"
- Step 3: Commit
git add extension/icons/relicario-logo.svg
git commit -m "feat(icons): replace master logo with reliquary theca + fleur"
Task 2: Replace 16 px logo SVG
Files:
-
Modify:
extension/icons/relicario-logo-16.svg(overwrite entirely) -
Step 1: Overwrite the 16 px SVG
Replace the file contents with:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none">
<defs>
<radialGradient id="redThecaSm" cx="0.4" cy="0.35">
<stop offset="0%" stop-color="#9a1a1a"/>
<stop offset="100%" stop-color="#3a0a0a"/>
</radialGradient>
<linearGradient id="goldRingSm" x1="0" x2="1">
<stop offset="0%" stop-color="#d2ab43"/>
<stop offset="50%" stop-color="#f5d97a"/>
<stop offset="100%" stop-color="#7c5719"/>
</linearGradient>
</defs>
<!-- Body + theca -->
<circle cx="8" cy="9" r="6.5" fill="url(#goldRingSm)"/>
<circle cx="8" cy="9" r="4.8" fill="url(#redThecaSm)"/>
<!-- Asterisk-as-3-bars -->
<g transform="translate(8, 9)" stroke="#f5d97a" stroke-width="1.2" stroke-linecap="round">
<line x1="0" y1="-3" x2="0" y2="3"/>
<line x1="-2.6" y1="-1.5" x2="2.6" y2="1.5"/>
<line x1="-2.6" y1="1.5" x2="2.6" y2="-1.5"/>
</g>
<circle cx="8" cy="9" r="0.7" fill="#fff3cf"/>
<!-- Fleur (3 tips) -->
<path d="M 8 0 L 7.2 2.5 L 8.8 2.5 Z" fill="url(#goldRingSm)"/>
<path d="M 5.6 2.5 L 6.5 1 L 7.3 2.5 Z" fill="url(#goldRingSm)"/>
<path d="M 10.4 2.5 L 9.5 1 L 8.7 2.5 Z" fill="url(#goldRingSm)"/>
</svg>
- Step 2: Verify it parses
Run: xmllint --noout extension/icons/relicario-logo-16.svg && echo OK
Expected: OK
- Step 3: Commit
git add extension/icons/relicario-logo-16.svg
git commit -m "feat(icons): replace 16px logo with bare medallion variant"
Task 3: Regenerate icon PNGs
Files:
- Modify:
extension/icons/icon-16.png(regenerate) - Modify:
extension/icons/icon-48.png(regenerate) - Modify:
extension/icons/icon-128.png(regenerate)
ImageMagick (magick, NOT rsvg-convert) is the project preference per memory. Density flag controls source-rasterization sharpness; 384 = 4× standard 96dpi.
- Step 1: Generate icon-16.png from the 16 px SVG
Run:
magick -background none extension/icons/relicario-logo-16.svg -resize 16x16 extension/icons/icon-16.png
Verify: file extension/icons/icon-16.png
Expected: PNG image data, 16 x 16, ...
- Step 2: Generate icon-48.png from the master SVG
The master SVG has aspect ratio 220:240 (slightly taller than 1:1 because of the pedestal). ImageMagick's -resize 48x48 preserves aspect ratio by default — output will be 44 × 48 (constrained by height). Use -extent 48x48 -gravity center to pad to a 48 × 48 square with transparent margins.
Run:
magick -background none -density 384 extension/icons/relicario-logo.svg \
-resize 48x48 -gravity center -extent 48x48 \
extension/icons/icon-48.png
Verify: file extension/icons/icon-48.png
Expected: PNG image data, 48 x 48, ...
- Step 3: Generate icon-128.png from the master SVG
Run:
magick -background none -density 384 extension/icons/relicario-logo.svg \
-resize 128x128 -gravity center -extent 128x128 \
extension/icons/icon-128.png
Verify: file extension/icons/icon-128.png
Expected: PNG image data, 128 x 128, ...
- Step 4: Visual sanity check
Open each PNG to confirm the gold/red logo is visible at the right size. From the terminal:
ls -la extension/icons/icon-*.png
Expected file sizes: icon-16 < icon-48 < icon-128. Each non-empty.
If a viewer is available (eog, feh, xdg-open), open extension/icons/icon-128.png and verify visually: gold ring, red theca with gold asterisk gem, fleur-de-lis on top, compact pedestal at bottom. Centered with transparent margins.
- Step 5: Commit
git add extension/icons/icon-16.png extension/icons/icon-48.png extension/icons/icon-128.png
git commit -m "feat(icons): regenerate PNGs from refreshed SVG masters"
Task 4: Palette swap in styles.css
Files:
- Modify:
extension/src/popup/styles.css
The complete mapping from old to new hex values:
| Old | New | Note |
|---|---|---|
#58a6ff |
#d2ab43 |
bright gold replaces primary blue |
#1f6feb |
#7c5719 |
deep gold replaces deep blue |
#388bfd |
#aa812a |
mid gold replaces mid blue (hover state) |
#f85149 |
#ab2b20 |
theca-toned red replaces danger fg |
#da3633 |
#791111 |
deep theca-red replaces danger emphasis |
rgba(88, 166, 255, 0.3) |
rgba(170, 129, 42, 0.4) |
focus ring tint (slightly more saturated) |
Note: #3fb950 (success green) and #d29922 (warning yellow) are NOT changed.
- Step 1: Apply find-and-replace to styles.css
Use sed (in-place) for the bulk swap:
sed -i \
-e 's/#58a6ff/#d2ab43/g' \
-e 's/#1f6feb/#7c5719/g' \
-e 's/#388bfd/#aa812a/g' \
-e 's/#f85149/#ab2b20/g' \
-e 's/#da3633/#791111/g' \
-e 's/rgba(88, *166, *255, *\([0-9.]*\))/rgba(170, 129, 42, \1)/g' \
-e 's/rgba(31, *111, *235, *\([0-9.]*\))/rgba(124, 87, 25, \1)/g' \
-e 's/rgba(248, *81, *73, *\([0-9.]*\))/rgba(171, 43, 32, \1)/g' \
-e 's/rgba(218, *54, *51, *\([0-9.]*\))/rgba(121, 17, 17, \1)/g' \
extension/src/popup/styles.css
- Step 2: Verify no old colors remain
Run:
grep -nE '#(58a6ff|1f6feb|388bfd|f85149|da3633)' extension/src/popup/styles.css
Expected: no output (zero hits).
Run:
grep -nE 'rgba\(88|rgba\(31, *111|rgba\(248, *81|rgba\(218, *54' extension/src/popup/styles.css
Expected: no output.
- Step 3: Run vitest (CSS changes shouldn't break behavior tests)
Run: cd extension && bun run test
Expected: 124 passed (one test inspects HTML for sig-block--blue and will still pass at this point — that fix lands in Task 5).
- Step 4: Commit
git add extension/src/popup/styles.css
git commit -m "feat(ext/popup): swap blue accent palette for burnished gold"
Task 5: Rename sig-block--blue to sig-block--gold
The --blue variant of the signature block now renders gold. Rename the class for semantic correctness.
Files:
-
Modify:
extension/src/popup/styles.css(the class definition) -
Modify:
extension/src/popup/components/fields.ts(the consumer that emits the class string) -
Modify:
extension/src/popup/components/__tests__/fields.test.ts(the assertion) -
Step 1: Find all consumers of
sig-block--blueandaccent: 'blue'
Run:
grep -rn "sig-block--blue\|accent: 'blue'\|accent=\"blue\"\|accent: \"blue\"" extension/src/
Expected hits:
extension/src/popup/styles.css:507—.sig-block--blue { ... }extension/src/popup/components/__tests__/fields.test.ts— string literals'sig-block--blue',accent: 'blue'
The extension/src/popup/components/fields.ts template uses sig-block--${accent} so it accepts whatever string the caller passes. We need to find any caller that passes 'blue'.
Run:
grep -rn "renderSignatureBlock" extension/src/
Inspect each call site for an accent: 'blue' argument; rename to accent: 'gold'. Likely zero or one site outside the test, since most signature-block consumers use 'green' / 'amber' / 'red' for status semantics.
- Step 2: Rename in styles.css
Edit extension/src/popup/styles.css line 507:
.sig-block--gold { border-left-color: #7c5719; }
(was .sig-block--blue { border-left-color: #1f6feb; } — the color was already swapped to #7c5719 in Task 4; now we rename the class.)
- Step 3: Rename
accenttype union in fields.ts
Open extension/src/popup/components/fields.ts. The renderSignatureBlock opts type likely has accent: 'blue' | 'green' | 'amber' | 'red'. Replace 'blue' with 'gold':
Find:
accent: 'blue' | 'green' | 'amber' | 'red'
(or however it's typed — also check for accent?: ... and Accent aliases)
Replace 'blue' with 'gold'. Adjust any default value (e.g. accent = 'blue' → accent = 'gold').
- Step 4: Rename in fields.test.ts
Edit extension/src/popup/components/__tests__/fields.test.ts:
Line 72-area: change expect(html).toContain('sig-block--blue'); to expect(html).toContain('sig-block--gold');
Line 77-area: change accent: 'blue' to accent: 'gold'. The assertion line 77 likely reads:
expect(renderSignatureBlock({ accent: 'blue', children: '' })).toContain('sig-block--blue');
Becomes:
expect(renderSignatureBlock({ accent: 'gold', children: '' })).toContain('sig-block--gold');
- Step 5: Update any non-test callers found in Step 1
For each non-test call site that passes accent: 'blue', change to accent: 'gold'. If Step 1 found zero such sites, skip this step.
- Step 6: Run vitest
Run: cd extension && bun run test
Expected: 124 passed (the renamed test now asserts on 'gold' and matches the renamed class).
- Step 7: Verify type-check is clean
Run: cd extension && bunx tsc --noEmit
Expected: zero errors. (If the accent type union missed a spot, this is where it'll surface.)
- Step 8: Commit
git add extension/src/popup/styles.css \
extension/src/popup/components/fields.ts \
extension/src/popup/components/__tests__/fields.test.ts
git commit -m "feat(ext/popup): rename sig-block--blue to --gold for accuracy"
Task 6: Inline color sweep in TS files
Six TS files have inline hex colors in template literals or DOM-style assignments. Each is a 1–2 line touch.
Files:
-
Modify:
extension/src/popup/components/types/login.ts -
Modify:
extension/src/popup/components/types/totp.ts -
Modify:
extension/src/popup/components/generator-popover.ts -
Modify:
extension/src/popup/components/settings.ts -
Modify:
extension/src/content/capture.ts -
Modify:
extension/src/content/icon.ts -
Step 1: Sweep all six files with sed
Same color mapping as Task 4. Run:
sed -i \
-e 's/#58a6ff/#d2ab43/g' \
-e 's/#1f6feb/#7c5719/g' \
-e 's/#388bfd/#aa812a/g' \
-e 's/#f85149/#ab2b20/g' \
-e 's/#da3633/#791111/g' \
extension/src/popup/components/types/login.ts \
extension/src/popup/components/types/totp.ts \
extension/src/popup/components/generator-popover.ts \
extension/src/popup/components/settings.ts \
extension/src/content/capture.ts \
extension/src/content/icon.ts
- Step 2: Verify no old colors remain in
extension/src/
Run:
grep -rnE '#(58a6ff|1f6feb|388bfd|f85149|da3633)' extension/src/
Expected: no output.
- Step 3: Run vitest + type-check
cd extension && bun run test && bunx tsc --noEmit
Expected: 124 passed; zero TS errors.
- Step 4: Commit
git add extension/src/popup/components/types/login.ts \
extension/src/popup/components/types/totp.ts \
extension/src/popup/components/generator-popover.ts \
extension/src/popup/components/settings.ts \
extension/src/content/capture.ts \
extension/src/content/icon.ts
git commit -m "feat(ext): sweep inline blue/red colors to gold/theca-red"
Task 7: Inline color sweep in setup.html
Same swap pattern, but in HTML/CSS context.
Files:
-
Modify:
extension/setup.html -
Step 1: Apply sed sweep to setup.html
sed -i \
-e 's/#58a6ff/#d2ab43/g' \
-e 's/#1f6feb/#7c5719/g' \
-e 's/#388bfd/#aa812a/g' \
-e 's/#f85149/#ab2b20/g' \
-e 's/#da3633/#791111/g' \
extension/setup.html
- Step 2: Verify no old colors remain in setup.html
grep -nE '#(58a6ff|1f6feb|388bfd|f85149|da3633)' extension/setup.html
Expected: no output.
- Step 3: Verify final scope: zero stale colors anywhere in extension/src/ + setup.html
grep -rnE '#(58a6ff|1f6feb|388bfd|f85149|da3633)' extension/src/ extension/setup.html
Expected: no output. This is the spec's primary acceptance gate.
- Step 4: Commit
git add extension/setup.html
git commit -m "feat(ext/setup): sweep inline colors for palette refresh"
Task 8: Build, full verification, and close-out
- Step 1: Build both extension bundles
cd extension && bun run build:all
Expected: "compiled with 2 warnings" (WASM size warnings only) for both Chrome and Firefox.
If webpack errors appear, the most likely cause is a TS type mismatch from Task 5's accent type union. Re-run bunx tsc --noEmit and fix.
- Step 2: Run full test sweep
cd /home/alee/Sources/relicario && cargo test --workspace
cd /home/alee/Sources/relicario/extension && bun run test
Expected: 155 Rust + 124 Vitest, all green.
- Step 3: Manual visual smoke check (instructions for the implementer to relay to user)
Have the user load extension/dist/ in Chrome (chrome://extensions → "Update" if already loaded, or "Load unpacked" otherwise) and verify:
- Toolbar icon shows the new gold/red reliquary medallion (16 px treatment).
- Open popup → unlock — primary buttons (
+ New,autofill,save) have gold backgrounds (#7c5719). - Selected list row has a gold left-border (
#aa812a) + gold tint background. - Focus ring on search input + form fields is gold (
#aa812a@ 40%). - Reveal/copy links in detail view are bright gold (
#d2ab43). - Trash button (and any danger states) shows theca-red (
#ab2b20). - TOTP countdown ring is gold (
#d2ab43). - Signature blocks:
--goldaccent renders gold (was the old blue accent). - Setup tab: strength bar's "very weak" segment is theca-red; advice block left-border is gold.
- Capture prompt and origin-ack icon (content script) use gold + theca-red.
Repeat in Firefox via about:debugging → "Update" or "Load Temporary Add-on" → extension/dist-firefox/manifest.json.
- Step 4: Final acceptance grep (paranoia check)
git grep -nE '#(58a6ff|1f6feb|388bfd|f85149|da3633)' -- 'extension/src/**' 'extension/setup.html'
Expected: no output. Anything in dist/, dist-firefox/, node_modules/, or .superpowers/ is out of scope.
- Step 5: No close-out commit needed
If steps 1–4 all passed without changes, there's nothing left to commit. The seven prior commits cover all changes.
If a fix was needed in step 1 or step 3 (e.g., a missed accent: 'blue' consumer), commit that fix as fix(ext): <description> before closing out.
Verification summary (run after Task 8)
# Bundles compile clean
cd extension && bun run build:all
# All tests pass
cd /home/alee/Sources/relicario && cargo test --workspace
cd /home/alee/Sources/relicario/extension && bun run test
# Stale palette purged
git grep -nE '#(58a6ff|1f6feb|388bfd|f85149|da3633)' -- 'extension/src/**' 'extension/setup.html' # zero hits
# Type-check clean
cd extension && bunx tsc --noEmit
All four checks must succeed for the plan to be considered complete.
Notes for the implementer
- No new tests — palette + logo are visual changes; existing tests cover behavior. The one test touched (
fields.test.ts) is updated for the renamed--goldclass. - No worktree required — this is a small, atomic change set. Commits go directly to main per the project's single-maintainer flow.
- Order matters slightly: Task 4 swaps
#1f6feb→#7c5719everywhere in styles.css, including inside the old.sig-block--bluerule. Task 5 then renames the class. Don't reverse the order or the sed sweep in Task 4 will skip the value because the class context changed. - PNG generation order matters: Task 3 needs the SVGs from Tasks 1–2 to exist first.
- Brainstorm artifacts in
.superpowers/brainstorm/contain the old hex values in mockup HTML — those are gitignored and out of scope; do NOT sed-sweep them.