Files
codium_nostr_extension/plans/sidebar-aesthetics.md
T
2026-07-27 19:01:14 -04:00

8.9 KiB
Raw Blame History

Sidebar Aesthetics Plan

Apply the design rules from ~/lt/aesthetics/WEB.md to the Nostr sidebar webview in src/ui/sidebar.ts.

Context

WEB.md defines a minimalist web aesthetic:

  • Monochrome first: black, white, one red accent, plus one muted grey. No other hues.
  • Monospaced by default: one fixed-width family for body, buttons, inputs, labels.
  • Minimal CSS surface: few variables, few classes, reuse shell styles.
  • Readable over clever: 1px borders, single radius token, no gradients/shadows as decoration.
  • Grayscale media: images desaturated at rest, color returns on hover.
  • Dark mode = swapped variables, not swapped styles.
  • Spacing ladder: 5, 10, 15, 20, 40 px only.
  • Transitions: 0.2s for interactive feedback; 0.5s max for slide-outs.
  • Accessibility: tab-reachable, accent focus ring, alt text, reduced-motion respected.

The current sidebar is a VS Code webview that leans on --vscode-* theme variables with a generic VS Code look: mixed monospace/sans, blue-ish buttons, 2px radii, ad-hoc spacing (6px, 8px, 12px, 16px), no grayscale on avatars, no transitions.

Reconciliation: WEB.md tokens vs VS Code theme

The sidebar lives inside VS Code, so it must respect the user's light/dark theme. We map the WEB.md four-token palette onto VS Code theme variables so the monochrome+accent system still tracks the active theme:

WEB.md token Sidebar value
--primary-color var(--vscode-foreground)
--secondary-color var(--vscode-sideBar-background)
--accent-color var(--vscode-statusBarItem-errorBackground, #ff0000) — red, used only for hover/active/focus/destructive
--muted-color var(--vscode-descriptionForeground)
--border-color var(--vscode-panel-border, var(--muted-color))

This keeps the "monochrome + one red scalpel" intent while remaining theme-aware. The accent resolves to red in most themes; where a theme overrides the error background we still get a single semantic accent.

Changes

All changes are confined to the inline <style> and HTML structure inside NostrSidebarProvider.getHtml(). No host-side TypeScript logic changes are required.

1. Variable block (:root)

Replace the current ad-hoc :root with the reconciled token set plus the WEB.md minimums:

  • --font-family: var(--vscode-editor-font-family), "Courier", monospace; (monospace everywhere)
  • --primary-color, --secondary-color, --accent-color, --muted-color, --border-color as in the table above.
  • --border-radius: 5px; (single token, replaces the current 2px)
  • --border-width: 1px; and --border: var(--border-width) solid var(--border-color);
  • --avatar-size-md: 32px; (identity avatar)
  • --image-grayscale: 100%; and --image-grayscale-hover: 20%;
  • Spacing ladder documented as a comment: 5, 10, 15, 20, 40.

2. Typography

  • * { font-family: var(--font-family); box-sizing: border-box; }
  • Body uses var(--font-family) and var(--vscode-font-size).
  • Section headings (h2) keep 11px, font-weight: 600, uppercase, letter-spacing: 0.5px, color var(--muted-color) — already close to WEB.md's "UI chrome 14px / compact 12px" guidance; we keep 11px for the uppercase label style.
  • Relay URLs and npub already use the editor font; ensure all inputs/buttons/selects also inherit var(--font-family).

3. Buttons

Adopt the WEB.md .btn model adapted to the sidebar's full-width buttons:

  • Default: color: var(--primary-color); background: var(--secondary-color); border: 1px solid var(--primary-color); border-radius: var(--border-radius); padding: 8px 10px; font-weight: bold; cursor: pointer; transition: border-color 0.2s, background-color 0.2s, color 0.2s;
  • :hover → border-color: var(--accent-color); (never fill red on hover).
  • :active → background: var(--accent-color); color: var(--secondary-color); border-color: var(--accent-color);
  • :disabled → opacity: 0.5; cursor: not-allowed; (no recolor).
  • .secondary → swap to muted border: border-color: var(--muted-color); color: var(--muted-color); hover still goes to accent border.
  • Remove the old --btn-bg/--btn2-bg blue button styling.

4. Inputs / select

  • Share border, radius, padding with buttons so they line up.
  • border: var(--border); border-radius: var(--border-radius); padding: 8px 10px; background: var(--vscode-inputBackground); color: var(--vscode-inputForeground);
  • :focus → outline: none; border-color: var(--accent-color); (accent focus ring, per WEB.md §7 and §14).
  • Labels (.field-label) stay muted grey, above inputs, smaller than input text.

5. Avatar / images

  • Global img { filter: grayscale(var(--image-grayscale)); transition: filter 0.2s; } and img:hover { filter: grayscale(var(--image-grayscale-hover)); }.
  • .avatar and .avatar-placeholder use --avatar-size-md: 32px, border-radius: var(--border-radius) by default (square-with-radius per WEB.md §8; round variant only if we later opt in via 999px).
  • Avatar alt text set to the profile name or "avatar" (currently alt="avatar" — keep, but prefer name when available).

6. Spacing normalization

Migrate current values onto the ladder:

Current New
margin-bottom: 16px (section) 20px
margin: 0 0 8px 0 (h2) 0 0 10px 0
padding: 6px 10px (button) 8px 10px
margin-bottom: 6px (button) 10px
padding: 4px 6px (input) 8px 10px
margin: 4px 0 2px (label) 5px 0
padding: 12px (body) 10px (or 15px)
gap: 6px (relay row) 10px
gap: 8px (identity row) 10px

7. Relay list and identity block

  • Relay rows: keep flex row, gap: 10px, padding: 5px 0, monospace URL with text-overflow: ellipsis; white-space: nowrap; (already present).
  • Identity block: treat as a single card — border: var(--border); border-radius: var(--border-radius); padding: 10px; margin-bottom: 15px; containing the avatar row, name, npub, backend label, and Sign Out button. This matches WEB.md §9 card conventions (1px border, 5–10px radius, 12px-ish padding, vertical flex).
  • Empty-state text stays italic muted grey (WEB.md allows italic for empty-state).

8. Transitions and reduced motion

  • Add transition: border-color 0.2s, background-color 0.2s, color 0.2s, filter 0.2s; to buttons, inputs, checkboxes, and images.
  • Add @media (prefers-reduced-motion: reduce) { * { transition: none !important; } }.

9. Accessibility pass

  • Ensure every button/input/select has visible focus: accent border on inputs; for buttons add :focus-visible { outline: 2px solid var(--accent-color); outline-offset: 1px; }.
  • Relay checkboxes remain native (WEB.md §7: don't reinvent).
  • Confirm tab order: Actions → Relays → Fetch button → Identity fields → Sign In/Out.
  • Avatar alt text uses profile name when available, else "avatar".

10. CSP

No CSP change needed. style-src 'unsafe-inline' already permits the inline styles; img-src https: data: already permits avatar loading. We are not adding any external resources.

Out of scope

  • No changes to host-side message handling, state shape, or commands.
  • No new files; everything stays in sidebar.ts.
  • No layout shell refactor (the sidebar is a single pane, not the five-region web shell from WEB.md §4 — that section informs token choices, not structure).
  • No dark-mode class toggle: VS Code drives light/dark via theme variables, which our token mapping already honors.

Verification

  1. Build the extension (npm run build via esbuild.mjs) and load it in VS Code.
  2. Open the Nostr sidebar and confirm:
    • All text is monospaced.
    • Buttons are outlined monochrome; hover shows red border; click flashes red fill.
    • Inputs show red border on focus.
    • Avatar (when signed in with a profile picture) is grayscale until hover.
    • Spacing feels even (no 6/8/12/16 oddities).
    • Disabled buttons are dimmed, not recolored.
  3. Toggle a light and a dark VS Code theme; confirm the palette tracks correctly and the accent stays red.
  4. Tab through the sidebar; confirm visible focus rings and logical order.
  5. Enable OS reduced-motion; confirm no transitions animate.

Mermaid overview

flowchart LR
  A[WEB.md rules] --> B[Token mapping]
  B --> C[Typography: monospace]
  B --> D[Buttons: outline + red hover/active]
  B --> E[Inputs: accent focus]
  B --> F[Avatar: grayscale + hover reveal]
  B --> G[Spacing ladder 5/10/15/20/40]
  B --> H[Transitions 0.2s + reduced-motion]
  C --> I[sidebar.ts inline style + HTML]
  D --> I
  E --> I
  F --> I
  G --> I
  H --> I
  I --> J[Build + manual verify in VS Code]