diff --git a/docs/superpowers/plans/2026-04-30-fullscreen-ux-phase-1-visual-foundation.md b/docs/superpowers/plans/2026-04-30-fullscreen-ux-phase-1-visual-foundation.md
index addd42c..08f555b 100644
--- a/docs/superpowers/plans/2026-04-30-fullscreen-ux-phase-1-visual-foundation.md
+++ b/docs/superpowers/plans/2026-04-30-fullscreen-ux-phase-1-visual-foundation.md
@@ -4,7 +4,7 @@
**Goal:** Establish the shared visual language (glyph constants, color tokens, focus ring, required pill, header subtitle) and clean up vestigial popup-only UI in the fullscreen vault. No structural or behavioral changes; pure visual foundation that the next three phases will build on.
-**Architecture:** A new `extension/src/shared/glyphs.ts` module exports unicode glyph constants and a `REQUIRED_PILL` HTML snippet, consumed by both popup and fullscreen surfaces. CSS custom properties added to `popup/styles.css` and `vault/vault.css` provide the shared color/focus tokens. All eight type forms migrate from `*` to the pill; sidebar nav buttons replace emoji with glyph constants; the popout-to-tab button is gated behind `!isInTab()` so it disappears in fullscreen. Fullscreen forms gain a static "esc to cancel" subtitle (dynamic dirty-state lands in Phase 3).
+**Architecture:** A new `extension/src/shared/glyphs.ts` module exports unicode glyph constants and a `REQUIRED_PILL_HTML` HTML snippet, consumed by both popup and fullscreen surfaces. CSS custom properties added to `popup/styles.css` and `vault/vault.css` provide the shared color/focus tokens. All eight type forms migrate from `*` to the pill; sidebar nav buttons replace emoji with glyph constants; the popout-to-tab button is gated behind `!isInTab()` so it disappears in fullscreen. Fullscreen forms gain a static "esc to cancel" subtitle (dynamic dirty-state lands in Phase 3).
**Tech stack:** TypeScript, vanilla DOM (no framework), Vitest + happy-dom for unit tests. No new runtime dependencies.
@@ -39,8 +39,8 @@ describe('glyphs', () => {
expect(glyphs.GLYPH_LOCK).toBe('⏻');
});
- it('exports REQUIRED_PILL as an HTML snippet', () => {
- expect(glyphs.REQUIRED_PILL).toBe('required');
+ it('exports REQUIRED_PILL_HTML as an HTML snippet', () => {
+ expect(glyphs.REQUIRED_PILL_HTML).toBe('required');
});
});
```
@@ -74,8 +74,8 @@ export const GLYPH_SETTINGS = '⚙'; // sidebar settings nav
export const GLYPH_LOCK = '⏻'; // sidebar lock nav
/// Inline HTML snippet for the required-field pill. Use after a label's text:
-/// ``
-export const REQUIRED_PILL = 'required';
+/// ``
+export const REQUIRED_PILL_HTML = 'required';
```
- [ ] **Step 4: Run test to verify it passes**
@@ -90,7 +90,7 @@ git -C /home/alee/Sources/relicario add extension/src/shared/glyphs.ts extension
git -C /home/alee/Sources/relicario commit -m "feat(ext/shared): glyph constants module for unified icon language
Centralizes the unicode glyphs used by sidebar nav and form action buttons
-so popup and fullscreen surfaces stay in sync. Includes the REQUIRED_PILL
+so popup and fullscreen surfaces stay in sync. Includes the REQUIRED_PILL_HTML
snippet used to replace the trailing-asterisk required-field marker.
Plan 2026-04-30 fullscreen UX phase 1 task 1.
@@ -330,7 +330,7 @@ Co-Authored-By: Claude Opus 4.7 "
---
-## Task 4: Migrate required-marker sites to REQUIRED_PILL
+## Task 4: Migrate required-marker sites to REQUIRED_PILL_HTML
**Files (10 sites across 7 files):**
- Modify: `extension/src/popup/components/types/card.ts:182`
@@ -392,7 +392,7 @@ In `extension/src/popup/components/types/login.ts`:
Add an import near the top (after the existing imports):
```typescript
-import { REQUIRED_PILL } from '../../../shared/glyphs';
+import { REQUIRED_PILL_HTML } from '../../../shared/glyphs';
```
Find line 252:
@@ -402,7 +402,7 @@ Find line 252:
Replace with:
```typescript
-
+
```
- [ ] **Step 4: Run the test to verify it passes for login**
@@ -413,8 +413,8 @@ Expected: PASS.
- [ ] **Step 5: Migrate the remaining six files**
Apply the same pattern to each of these six files. For each:
-1. Add `import { REQUIRED_PILL } from '../../../shared/glyphs';`
-2. Replace each `*` with `${REQUIRED_PILL}`
+1. Add `import { REQUIRED_PILL_HTML } from '../../../shared/glyphs';`
+2. Replace each `*` with `${REQUIRED_PILL_HTML}`
| File | Line(s) |
|---|---|
@@ -444,10 +444,10 @@ Expected: `compiled with 2 warnings`.
```bash
git -C /home/alee/Sources/relicario add extension/src/popup/components/types/ extension/src/shared/
-git -C /home/alee/Sources/relicario commit -m "refactor(ext/popup): migrate required-field markers to REQUIRED_PILL
+git -C /home/alee/Sources/relicario commit -m "refactor(ext/popup): migrate required-field markers to REQUIRED_PILL_HTML
Replaces ten * sites across all seven type
-forms with the shared REQUIRED_PILL snippet ('required' badge). Adds a
+forms with the shared REQUIRED_PILL_HTML snippet ('required' badge). Adds a
regression test pinning the new HTML in the login form.
Plan 2026-04-30 fullscreen UX phase 1 task 4.
diff --git a/extension/src/popup/components/form-header.ts b/extension/src/popup/components/form-header.ts
new file mode 100644
index 0000000..e3f92c2
--- /dev/null
+++ b/extension/src/popup/components/form-header.ts
@@ -0,0 +1,24 @@
+/// Shared header chrome for typed form views (login, secure-note, identity, card,
+/// key, totp, document). Renders the title row plus a fullscreen-only "esc to
+/// cancel" subtitle. Use the existing `${...}` template-literal interpolation
+/// at call sites: `${renderFormHeader({ titleText: 'new login' })}`.
+///
+/// item-form.ts (the type-selection screen) uses a different header structure
+/// and does NOT consume this helper.
+
+import { isInTab } from '../../shared/state';
+
+export interface FormHeaderOpts {
+ titleText: string;
+}
+
+export function renderFormHeader(opts: FormHeaderOpts): string {
+ return `
+
+
${opts.titleText}
+
+ ${isInTab() ? '' : ''}
+
+ ${isInTab() ? '
esc to cancel
' : ''}
+ `;
+}
diff --git a/extension/src/popup/components/item-form.ts b/extension/src/popup/components/item-form.ts
index 6f513c2..c9956ae 100644
--- a/extension/src/popup/components/item-form.ts
+++ b/extension/src/popup/components/item-form.ts
@@ -1,7 +1,7 @@
/// Typed-item add/edit form dispatcher. Each type's renderForm lives in
/// its own module under ./types/. Document stays "coming soon" until γ.
-import { navigate, getState, setState, escapeHtml, popOutToTab } from '../../shared/state';
+import { navigate, getState, setState, escapeHtml, popOutToTab, isInTab } from '../../shared/state';
import type { Item, ItemType } from '../../shared/types';
const TYPE_OPTIONS: Array<{ type: ItemType; icon: string; label: string }> = [
@@ -54,12 +54,13 @@ export function renderItemForm(app: HTMLElement, mode: 'add' | 'edit'): void {
function renderTypeSelection(app: HTMLElement): void {
app.innerHTML = `