Files
FabledScribe/docs/design-system.md
T
bvandeusen 64ab24864a docs(design-system): mark surface phase shipped end-to-end
Updates the Scribe-specific decisions section header and replaces
the Surface-Phase TODO checklist with a per-PR ship table covering
the seven PRs that landed today (93a3beb3c1ec40). Adds an
explicit "Out of scope — deferred indefinitely" subsection so
future-me knows what's intentionally not done (stroke-weight
overrides, filled-as-active state, type-scale tokens, etc.).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 23:23:36 -04:00

32 KiB

FabledSword Design System

A house-style design system for the FabledSword family of self-hosted applications. FabledSword is the umbrella identity; individual apps share a common visual language but each carries its own signature accent color.

Brand model

FabledSword is a house style, not a single brand. Apps share:

  • Typography
  • Surfaces and dark-mode foundation
  • Component shapes (pills, cards, buttons)
  • Spacing system
  • Semantic colors (success, warning, error, info)
  • Action button colors (moss primary, bronze secondary)
  • Voice and tone

Each public app has its own signature accent used for: the wordmark, the app icon, active nav state, "you are here" indicators, cursor/selection color, and key brand moments. Accents do not appear on action buttons — those stay system-wide.

Aesthetic direction

Modern mythic with heraldic restraint. Tech-forward execution, but the visual language borrows from manuscripts, heraldry, and forged objects rather than from gaming or fantasy iconography. Dark-mode-first because that's where these apps live.

The reference points: a well-printed book, a well-kept armory, a steward's ledger. Not: a fantasy novel cover, a tabletop RPG character sheet, a Renaissance Faire poster.


Color system

Universal surfaces (dark mode foundation)

Token Hex Usage
Obsidian #14171A Page background, deepest surface
Iron #1E2228 Card surfaces, raised elements
Slate #2C313A Hovered surfaces, secondary elevation
Pewter #3F4651 Borders, dividers, ghost button outlines

Universal text and parchment

Token Hex Usage
Parchment #E8E4D8 Primary text on dark surfaces
Vellum #C2BFB4 Secondary text, captions
Ash #9C9A92 Tertiary text, hints, metadata

Parchment is intentionally not pure white — it's slightly warm to feel like aged paper. Pure white (#FFFFFF) is never used as text color.

Universal action colors

Token Hex Usage
Moss #4A5D3F Primary action buttons (Save, Submit, Confirm)
Bronze #8B7355 Secondary action buttons (Cancel-but-not-destructive, alternate paths)
Pewter #3F4651 Tertiary/ghost buttons

Critical rule: Action button colors are universal across all apps. A Save button in Scribe and a Save button in Minstrel look identical. Per-app accents do not appear on buttons.

Semantic colors

Token Hex Usage
Success #4A5D3F Success states (same as Moss — they're aligned by design)
Warning #8B6F1E Warnings, caution states
Error #C04A1F Error messages, validation failures
Info #3D5A6E Informational callouts
Destructive #6B2118 Destructive action buttons (Delete, Remove, irreversible)

Why error and destructive are different: Error is the orange-red used in alerts and validation messages. Destructive (oxblood) is reserved for buttons that perform irreversible actions — it carries more weight precisely because it's used sparingly. Pair destructive buttons with an icon (trash, X) so color is reinforcement, not the only signal.

Per-app signature accents

App Hex Mood
Fabled Scribe #5B4A8A Dusty violet — ink, manuscripts
Minstrel #4A6B5C Forest teal — music, performance
Fabled Forge #8B5A2B Forge bronze — creation, craft
Roundtable #4A5D7E Slate blue — stewardship, infrastructure
FabledSword (umbrella) #6B2118 Oxblood — house identity, ceremonial use only

Accent usage rules:

  • The accent appears on the app's wordmark and icon.
  • The accent indicates active/current state in nav (the selected page, the active tab).
  • The accent is the cursor color and text-selection color in long-form surfaces (Scribe notes, Forge story drafts).
  • The accent does NOT appear on primary or secondary action buttons.
  • The accent does NOT appear in body text or chrome.
  • One accent per app. Don't mix accents within a single app.

Color contrast

All text-on-surface combinations meet WCAG AA at minimum. Parchment on Obsidian is the maximum-contrast pairing; Vellum on Iron is the lowest-contrast pairing still considered acceptable for body text. Ash is for hints only — never load-bearing information.


Typography

Type families

Family Role Source
Fraunces Display, headings, wordmarks Google Fonts
Inter Body, UI, labels Google Fonts
JetBrains Mono Code, terminal output, monospaced data Google Fonts

Why this pairing

Fraunces is a contemporary serif with personality — it has the warmth and authority of a book serif without feeling like costume. It signals "this is considered" without signaling "this is a fantasy product." Inter is the workhorse — neutral, ubiquitous, designed for screens, doesn't compete with the serif. JetBrains Mono is the natural choice for any developer-adjacent product and supports ligatures.

Type scale

Token Size Weight Family Usage
Display 40px 500 Fraunces App wordmark, hero text
H1 32px 500 Fraunces Page titles
H2 24px 500 Fraunces Section headings
H3 18px 500 Inter Subsection headings, card titles
Body 15px 400 Inter Paragraphs, default text
Body small 13px 400 Inter Captions, metadata
Label 12px 500 Inter Buttons, form labels, badges
Code 13px 400 JetBrains Mono Inline code, code blocks
Tiny 11px 500 Inter Micro-labels (UPPERCASE LETTERSPACED)

Typography rules

  • Sentence case everywhere. Never Title Case for headings, never ALL CAPS except for the Tiny micro-label style.
  • Two weights only: 400 regular and 500 medium. Never 600 or 700 — they read heavy in dark mode.
  • Fraunces only at 18px and above. Below that it loses too much detail and feels fragile. For h3 and below, use Inter.
  • Line height 1.5 for body, 1.3 for headings, 1.7 for long-form reading surfaces (Scribe notes, Forge drafts).
  • Letter-spacing at default for everything except the Tiny micro-label, which gets 0.08em letter-spacing and uppercase styling.

Spacing and layout

Spacing scale (px)

4, 8, 12, 16, 20, 24, 32, 48, 64, 96

Use rem units for vertical rhythm in long-form content (paragraph spacing). Use px for component-internal spacing (padding, gaps).

Border radius

Token Size Usage
Small 4px Pills, tags, code spans
Medium 8px Buttons, inputs, small cards
Large 12px Cards, panels, modals
Extra large 16px Hero containers, major surfaces

Borders

  • Default border: 0.5px solid Pewter (#3F4651)
  • Hovered/emphasized border: 0.5px solid Vellum at 30% opacity
  • Featured/active border: 2px solid [accent] (only for emphasizing a selected card or active tab)

The 0.5px default is deliberate — it reads as a hairline at most pixel densities and avoids the heavy "boxed-in" feeling that 1px+ borders create on dark backgrounds.


Components

Buttons

Variant Background Text Border
Primary Moss #4A5D3F Parchment None
Secondary Bronze #8B7355 Parchment None
Ghost Transparent Parchment 0.5px Pewter
Destructive Oxblood #6B2118 Parchment None — pair with icon

Padding: 8px 16px for default, 6px 12px for compact, 10px 20px for prominent. Border-radius: 8px. Font: Inter 12px/500 with default letter-spacing.

Pills and tags

Used for tags, hashtags, code spans, status badges. Background is the accent color at ~15% opacity, text is the accent at full strength. Border-radius 4px, padding 2px 8px, Inter 11px/500.

In Scribe specifically, hashtags and tags use the dusty violet accent. In Minstrel, they'd use forest teal. The pattern is shared; the color follows the app.

Cards

  • Background: Iron (#1E2228)
  • Border: 0.5px Pewter
  • Border-radius: 12px
  • Padding: 20px

For featured/selected cards, swap to a 2px solid accent border. Don't change the background.

Inputs

  • Background: Obsidian (#14171A) — darker than the page surface to feel "inset"
  • Border: 0.5px Pewter
  • Border-radius: 8px
  • Padding: 8px 12px
  • Focus state: 2px solid accent ring (using box-shadow: 0 0 0 2px [accent] to avoid layout shift)

Code blocks

  • Background: Obsidian (#14171A)
  • Border: 0.5px Pewter
  • Border-radius: 8px
  • Padding: 12px 16px
  • Font: JetBrains Mono 13px/400
  • Inline code: same family, with 4px-radius pill background using the app accent at 15% opacity

The FabledSword lockup

A small, persistent FS mark appears in the navigation chrome of every app — the way an Apple logo persists across macOS apps. This is the only place oxblood appears in normal app usage.

Specification:

  • 16-20px height in nav contexts
  • Oxblood (#6B2118) on dark surfaces
  • Positioned in the bottom-left of nav rails or top-left when there's no rail
  • Hover/click reveals a small menu: link to other apps in the family, link to FabledSword.com, version info

The lockup itself is a small heraldic mark — a stylized FS monogram — not a literal sword icon. We're avoiding sword imagery in app chrome because it would clash with the restrained, modern-mythic aesthetic. The wordmark "FabledSword" appears only on the umbrella site and in About/Settings dialogs.


Voice and tone

The FabledSword voice is understated mythic — it borrows the register of stewardship, craft, and considered making, but never tips into roleplay or affectation.

Do

  • Use plain language for everything functional. ("Save", "Cancel", "Add note")
  • Reserve flavored language for moments where the user is waiting or failing — loading states, empty states, error pages, 404s.
  • Borrow vocabulary from craft and stewardship: "draft", "ledger", "kept", "set aside", "to come", "in progress", "abandoned".
  • Be brief. The mythic register is undermined by verbosity.

Don't

  • Don't use thee/thou/thy or pseudo-archaic spelling.
  • Don't address the user as "traveler", "wanderer", "adventurer", or any RPG-adjacent epithet.
  • Don't use sword/blade/forge metaphors in error messages. ("Your save was forged successfully" — no.)
  • Don't make the user feel like they're playing a game when they're just trying to use software.

Examples

Context Plain FabledSword voice
Empty list "No items yet" "Nothing kept here yet."
404 "Page not found" "This page is not in the ledger."
Loading "Loading..." "Fetching..." (just keep it plain — the mythic note is reserved for moments with more space)
Save success "Saved!" "Saved." (plain — success doesn't need flavor)
Save error "Error saving" "Couldn't save. The change has been kept locally — try again in a moment."
Delete confirm "Delete this?" "Remove this from the ledger? This can't be undone."

The pattern: action-adjacent language stays plain; absence/failure/waiting gets the flavor.


Iconography

Style

  • Stroke-based, 1.5px stroke weight at 24px, 1px at 16px
  • Rounded line caps and joins
  • 24px or 16px grid
  • Outline style by default; filled style only for active/selected states

Use Lucide (https://lucide.dev) as the base icon set — it matches this style exactly and is open-source. Only commission custom icons for app-specific concepts that Lucide doesn't cover.

Don't

  • No filled icons in default UI (reserve for active states)
  • No icon styles that mix stroke and fill chaotically
  • No literal medieval imagery (swords, scrolls with curls, banners) in functional UI
  • No emoji as icons

Per-app application

Fabled Scribe (#5B4A8A — dusty violet)

A second-brain notes and task management tool. The accent appears in: the wordmark, hashtags and tag pills, the active nav item, text selection color, and the cursor in the editor. Notes are presented on Iron-surfaced cards with generous reading line-height. The hashtag system uses Scribe's accent for visual continuity.

Minstrel (#4A6B5C — forest teal)

Self-hosted music. The accent appears on: now-playing indicators, active track highlights, the wordmark, equalizer/visualization elements. Album art dominates visually, so the accent should appear in chrome and metadata, never overlapping cover imagery.

Fabled Forge (#8B5A2B — forge bronze)

Story-building and worldbuilding tool. The accent appears on: the wordmark, character/location/object markers in story trees, the editor cursor, "kept/canon" indicators distinguishing finalized story elements from drafts. This app benefits from Fraunces being used more aggressively — for entity titles, chapter headings, etc.

Roundtable (#4A5D7E — slate blue)

Home server management. The accent appears on: the wordmark, healthy/online status indicators, the active dashboard panel border. Status colors here are critical — green for healthy, amber for warning, red (the orange-red error tone, not oxblood) for failed. The accent itself indicates "this is the panel I'm currently looking at."

Naming note: "Roundtable" leans the wrong direction — its connotation is equal participants in discussion rather than one steward managing a domain. Consider "Steward" or "Castellan" if you revisit naming. Castellan in particular is good — it specifically means "the officer in charge of a castle."


Implementation notes

CSS custom properties

:root {
  /* Surfaces */
  --fs-obsidian: #14171A;
  --fs-iron: #1E2228;
  --fs-slate: #2C313A;
  --fs-pewter: #3F4651;

  /* Text */
  --fs-parchment: #E8E4D8;
  --fs-vellum: #C2BFB4;
  --fs-ash: #9C9A92;

  /* Action */
  --fs-moss: #4A5D3F;
  --fs-bronze: #8B7355;

  /* Semantic */
  --fs-warning: #8B6F1E;
  --fs-error: #C04A1F;
  --fs-info: #3D5A6E;
  --fs-oxblood: #6B2118;

  /* Typography */
  --fs-font-display: 'Fraunces', Georgia, serif;
  --fs-font-body: 'Inter', system-ui, sans-serif;
  --fs-font-mono: 'JetBrains Mono', ui-monospace, monospace;

  /* Layout */
  --fs-radius-sm: 4px;
  --fs-radius-md: 8px;
  --fs-radius-lg: 12px;
  --fs-radius-xl: 16px;
}

/* Per-app accent — set ONE of these on the root for each app */
[data-app="scribe"]    { --fs-accent: #5B4A8A; }
[data-app="minstrel"]  { --fs-accent: #4A6B5C; }
[data-app="forge"]     { --fs-accent: #8B5A2B; }
[data-app="roundtable"]{ --fs-accent: #4A5D7E; }

Tailwind integration

If using Tailwind, extend the theme with these tokens rather than relying on default colors. The default Tailwind palette will fight this system — you'll get drift back toward bright defaults if you don't lock down the palette explicitly.


What this kit deliberately does NOT include

  • Logo files. The lockup design is described conceptually but the actual mark needs to be drawn. Hire a designer or use Claude Design to iterate on a heraldic FS monogram.
  • Marketing site design. This kit is for application UI. The umbrella marketing site (FabledSword.com) can use this system but will need additional patterns (hero layouts, feature grids, etc.).
  • Email templates. Different constraints, different problem.
  • Print collateral. Not in scope.
  • Mobile native app patterns. This is web-first. iOS/Android conventions would override several choices here (button shapes, navigation patterns).

Last updated: April 25, 2026. Iterate as the family of apps grows.


Scribe-specific decisions in progress

This section tracks decisions made while adapting the FabledSword baseline above for Scribe specifically. Items here are in progress — once they feel solid, they get folded into the main body of the document (either as Scribe-specific extensions in the per-app section, or as updates to the universal rules where Scribe's needs reveal a gap in the baseline).

Iteration started: 2026-04-26. Foundation pass shipped 2026-04-27 in 7a9a8b7 (palette, fonts, light mode, action tokens, hardcoded indigo cleanup, warm-gold deprecation). Surface phase shipped 2026-04-27 across 93a3beb3c1ec40 (Lucide migration, Hybrid-rule button reclassification per surface, long-form line-height, two-weights-only). The system is now applied end-to-end; this section will fold into the main body once the result has had time to settle in real use.

Decisions made so far

Accent footprint — Hybrid rule (not Strict)

The doc baseline says the per-app accent only appears on wordmark, active nav, cursor, and text selection — never on action buttons. Scribe currently uses indigo on essentially every interactive surface (CTAs, scrollbars, glows, borders, focus rings). Hard-cutting to the doc baseline would lose too much identity in one swing.

Hybrid rule: the accent reserves a slightly larger footprint than the doc baseline, but still much smaller than today.

  • Accent (dusty violet) lives on: wordmark; active nav; cursor and text selection in editor surfaces; tags/pills/wikilinks; in-progress task badge; focus rings; brand-moment CTAs — chat Send, "Create note" empty-state CTA, journal Send, "Start journaling" empty-state.
  • Moss (sage-green primary) lives on: Save / Submit / Confirm in forms and modals; generic affirmative actions where the button just means "do this thing" with no brand pretense.
  • Bronze (secondary): Cancel-but-not-destructive, alternative paths.
  • Oxblood (destructive): Delete / Remove (paired with an icon).
  • Pewter ghost: tertiary actions, "later", "skip", "see also".

Rule of thumb: if the user is engaging with a Scribe-feature moment (sending a chat, opening a fresh note, jumping into the journal), accent. If they're just operating the software (saving an edit, confirming a dialog), Moss.

Light mode — warm parchment, matched aesthetic

The doc is dark-only. Scribe today supports both light and dark, and we keep both. The light mode is derived to match the dark mode aesthetic rather than defaulting to system white-and-ink.

  • Page background: in the #F5F1E8 warm cream family (specific values TBD)
  • Cards: near-white but slightly tinted
  • Text: deep ink #14171A (mirroring Obsidian)
  • Accent: same dusty violet #5B4A8A (works on both themes)

The metaphor stays consistent across themes: ink on aged paper (light) ↔ parchment text on graphite (dark). Light mode is not the system standard look.

Known downside: warm parchment backgrounds can fight with embedded color content. Mitigation: code blocks get a slight cool wash in light mode specifically, to keep syntax highlighting readable.

Status and priority palette — extend the doc's semantic set

The doc's semantic colors (Success / Warning / Error / Info / Destructive) are leaner than what Scribe needs for task management. Rather than running a parallel palette, Scribe extends the doc by mapping its status/priority tokens onto doc primitives where they fit and defining new app-level tokens for the rest.

Status (task lifecycle):

Token Color Source Logic
status-todo Pewter #3F4651 doc Neutral, "not started yet"
status-in-progress dusty violet #5B4A8A accent Active = brand moment per Hybrid
status-done Moss #4A5D3F doc Success Affirmative completion
status-cancelled Ash #9C9A92 doc Faded, "let go"
status-paused Warning #8B6F1E doc Stalled, needs attention — replaces the old warm gold treatment

Priority (loudness scale):

Token Color Source Logic
priority-low Info #3D5A6E doc Cool, FYI — quietest end of the spectrum
priority-medium Warning #8B6F1E doc Golden, mid-attention
priority-high Error #C04A1F doc Terracotta, urgent
priority-none Vellum/Ash doc No signal

The priority row reads as a clean cool→warm gradient (slate blue → golden brown → terracotta), which matches the semantic loudness — coherence the current ad-hoc palette doesn't have.

Other functional tokens:

Token Color Logic
wikilink dusty violet Editorial brand moment per Hybrid
overdue Error #C04A1F Same as priority-high — overdue IS a priority signal
toast-success Moss doc semantic
toast-error Error doc semantic
toast-info Info doc semantic
tag-bg / tag-text accent at 15% / accent Per doc pill recipe

Each token gets a *-bg companion at low alpha (matching the existing pattern in theme.css).

Removed: the warm gold accent (--color-accent-warm: #b8860b). Its two jobs split:

  • Dates and timestamps (knowledge cards, event details, chat) → use text-secondary instead. Dates are metadata, not a brand surface; muted is the correct register.
  • Paused project status → use the new status-paused (Warning #8B6F1E) row above. Same golden-brown family, semantically aligned.

Typography — adopt the doc's stack and scale

Adopt the doc's type stack and scale verbatim, with one deferred verification (long-form line-height in practice).

  • Body font: Inter. Replaces Scribe's current system-stack body font. Doc-defined; no Scribe-specific divergence.
  • Type scale: as in the doc table — Display 40 / H1 32 / H2 24 / H3 18 / Body 15 / Body small 13 / Label 12 / Code 13 / Tiny 11.
  • Two weights only: 400 regular, 500 medium. No 600/700 (reads heavy in dark mode and against the muted palette).
  • Family rules: Fraunces at 18px+ only (Display, H1, H2). H3 and below = Inter. Code = JetBrains Mono.
  • Line height: 1.5 body, 1.3 headings, 1.7 for long-form reading surfaces (notes, journal entries).
  • Sentence case for everything, except the Tiny micro-label style which gets uppercase + 0.08em letter-spacing.

Mechanical rollout — value swaps in theme.css plus loading Inter and JetBrains Mono from Google Fonts (Fraunces is already loaded).

Light mode — concrete palette

Fills in the warm-parchment direction picked earlier. Treat these as starting values; tune in practice.

Token Hex Role
Page bg #F5F1E8 Warm cream — the "paper"
Card bg #FBF8F0 Near-white, slightly warm — raised surfaces
Inset bg (inputs, code) #EFEAE0 Slightly darker than page, "inset" feeling
Text primary #14171A Deep ink — Obsidian inverted
Text secondary #5A5852 Warm mid-grey
Text muted #9A9890 Warm light grey for hints/metadata
Border default #D9D6CE Warm light pewter, hairline weight

Code-block exception: in light mode specifically, code blocks use a slight cool wash (e.g. #EBEDF0) instead of the warm inset bg, so syntax highlighting reads cleanly. This is the mitigation for the "warm bg fights colored content" downside.

The accent (#5B4A8A dusty violet), Moss, Bronze, Oxblood, and the semantic color set are identical across themes — only the surface and text palettes flip.

Chat-bubble codification — keep the Illuminated Transcript pattern

The existing chat-bubble pattern (informally called "Illuminated Transcript") gets written into the design system as a documented chat component. Other apps in the family that add a chat surface inherit the pattern; Scribe's existing implementation continues to work with only color shifts.

User bubble (whisper):

  • Background: transparent
  • Border: 0.5px Pewter (was: indigo-tinted)
  • Text color: secondary (Vellum dark / #5A5852 light)
  • Right-aligned, rounded except bottom-right (subtle "from-me" tail)

Assistant bubble (lit):

  • Background: card surface (Iron dark / #FBF8F0 light)
  • Border: none on top/right/bottom; 2px solid accent (dusty violet) on left edge only
  • Box-shadow: accent-tinted glow + standard depth shadow (formula: 0 4px 28px rgba(<accent>, 0.14), 0 2px 8px rgba(0,0,0,0.4) in dark; lower alphas in light)
  • Text color: primary (Parchment dark / Obsidian-inverted light)
  • Left-aligned, rounded except bottom-left

The 2px-accent left edge is the "illumination" — like an illuminated capital in a manuscript. The shadow is the lift. Together they make the assistant bubble read as the primary voice, while the user bubble is the margin note.

Inline tool-call cards (ToolCallCard) rendered inside an assistant bubble do NOT get their own border (per the border philosophy — the bubble already contains them). They use a slight surface tint to differentiate.

Iconography — adopt Lucide, enforce a scale

Scribe currently hand-inlines SVG paths in 16+ Vue files, with 5 different stroke weights and 8+ different sizes. The visual style is already outline + rounded caps + currentColor stroke (matches the doc's intent), but there's no shared source and no scale discipline.

Migration policy:

  1. Install lucide-vue-next as the icon source. Replace hand-inlined SVGs with imported components. Single source of truth.
  2. Strict size scale: 16px and 24px only. Today's mix of 12/13/14/15/17/18/20 collapses to those two. 16 for inline-with-text and small affordances; 24 for nav and primary actions.
  3. Stroke weight per the doc: 1.5 at 24px, 1 at 16px. Lighter than the current default of 2 — reads more refined, matches the muted palette philosophy. Overrides Lucide's default.
  4. Outline by default; filled only for active/selected state. Introduces a new affordance Scribe doesn't currently use — bookmark/pin/star icons can switch outline → filled to indicate active state. Reserve filled style strictly for this.
  5. No emoji in chrome. Replace the 3 files' emoji usage in UI labels/buttons/badges/empty states with Lucide equivalents. Emoji remain fine in user content (note bodies, chat messages the user typed).

Work cost: ~30-60 individual icon swaps across the 16 files. Mechanical; doesn't require redesign of any component.

Voice and tone — adopt principles, defer formal audit

The doc's voice register applies to Scribe (understated mythic — plain for functional UI, flavored for empty/error/loading states). No formal sweep of every UI string yet.

Approach: apply the voice opportunistically as components are touched in the polish pass — when redesigning a settings tab, an empty state, or an error toast, rewrite the copy at the same time using the doc's register and examples table as the guide. A standalone audit pass is deferred unless drift becomes visible.

Border philosophy — structural, not decorative

The doc treats borders as structural (Pewter neutral hairlines that say "boundary"), not decorative (Scribe today uses indigo-tinted borders that say "branded edge"). That principle suggests removing borders in places where surface tint and spacing already communicate separation.

Borders to remove:

  • List rows (NotesListView, TasksListView, conversation history) — surface contrast + spacing should separate rows; current border reads as "boxed-in"
  • Inline ToolCallCard inside chat bubbles — the bubble is already a container; an extra border feels like double-wrapping
  • Filter chips and search-bar pills with a background tint — background does the work
  • Empty-state callouts with dashed/bordered "nothing here yet" boxes — tinted background reads cleaner

Borders to keep (genuinely structural):

  • Standalone card containers (Notes viewer, Task viewer, the new daily prep card)
  • Modal / dialog edges
  • Code blocks (separates content type, not just space)
  • Focus rings (accessibility)
  • Major section dividers within a panel

Border weight is not load-bearing for Scribe — happy to use the doc's 0.5px hairline default; the placement discipline matters more than the weight.

Open threads (next iterations)

Foundation pass — shipped 2026-04-27 (7a9a8b7)

Mechanical token + font + light-mode rewrite of frontend/src/assets/theme.css, plus a sweep of hardcoded indigo and --color-accent-warm references across ~14 component files. Action tokens (--color-action-primary Moss, --color-action-secondary Bronze, --color-action-destructive Oxblood, --color-action-ghost-border Pewter) are defined but not yet applied — buttons still flow through --color-primary and read as dusty-violet gradients in the meantime, by design. Spec lives at docs/superpowers/specs/2026-04-27-design-system-polish-foundation-design.md (gitignored, local-only).

Surface phase — shipped 2026-04-27 (93a3beb3c1ec40)

Bundled as Hybrid (option C from the brainstorm): Lucide cross-cutting first, then surface-by-surface for the judgment work. Spec lives at docs/superpowers/specs/2026-04-27-design-system-polish-surface-design.md (gitignored, local-only). Seven PRs landed on dev:

PR Commit Surface Notes
1 93a3beb Lucide cross-cutting 60 hand-inlined SVGs across 15 files → lucide-vue-next. Every chrome icon at 16 or 24. Emoji-as-icons (, , 🎤, 📎, , , &times;, /) swept across the chrome. AppLogo wordmark and the GraphView D3 mount kept as the legitimate exceptions.
2 3d916d7 Journal Buttons audited; weather refresh → Lucide RotateCcw; assistant bubble line-height bumped; redundant .journal-title font-family dropped; dead .news-section CSS removed.
3 4192a64 Chat .message-content long-form 1.7 line-height on assistant bubbles; ToolCallCard outer border removed (bubble already contains it; error state moved to a left-edge accent); bulk-delete recolored to Oxblood with Trash2 icon; .bulk-link accent → muted text.
4 efb3534 Knowledge cluster .prose line-height bumped to 1.7 globally so all reading surfaces inherit. Save → Moss; Delete → Oxblood + Trash2; Edit / Advance → Moss; Convert / Share → Bronze.
5 ff498ce Project + Workspace Project save panel → Moss; milestone confirm/cancel → Moss/Bronze; modal-btn-danger and per-row delete affordances → Oxblood; "Open Workspace" stays accent (project-surface brand moment).
6 541e2ed Settings Densest button surface — every .btn-save, .btn-primary, .btn-secondary, .btn-danger, .btn-danger-outline, .btn-toggle-open reclassified per Hybrid; missing .btn-danger style block added (was unstyled before).
7 3c1ec40 Edge surfaces Calendar New Event → Moss; EventSlideOver Save/Cancel/Delete reclassified; HomeView hero CTA stays accent (brand moment); two-weights-only sweep across Header/Home/Calendar/Graph.

Cross-cutting changes folded in as the work touched files:

  • Long-form 1.7 line-height on .prose (PR 4) — applies to Note viewer, Task viewer, chat assistant bubbles, anywhere markdown renders into a reading surface.
  • Two-weights-only (400 + 500) — every font-weight: 600 and 700 snapped to 500 across all surface PRs.
  • Hardcoded --color-danger in destructive button contexts → --color-action-destructive (Oxblood). --color-danger (Error terracotta) preserved for validation/error messages, per the doc's distinction between Error and Destructive.
  • Adjacent &times; / / unicode-arrow emoji swept opportunistically as files were touched (PRs 5, 7).

Out of scope — deferred indefinitely

Items deliberately not addressed in this round; revisit when a real need surfaces:

  • Lucide stroke-weight overrides (doc spec: 1.5 at 24, 1 at 16; current: Lucide default 2). Touched components if they read too heavy in practice.
  • Filled-as-active icon state — no current affordance uses it; introduce when bookmark/pin/star toggles are added.
  • Type-scale / spacing-scale CSS variables — components keep literal values.
  • Token rename to --fs-* namespace — Scribe is the only FabledSword app sharing this codebase.
  • FabledSword lockup placement — waiting on the actual heraldic mark to be drawn.
  • Standalone voice/tone audit across every UI string — opportunistic-only; full sweep deferred unless drift becomes visible.
  • A handful of editor utility buttons (.btn-suggest-tags, .btn-link-all, AI assist generate/proofread/accept/reject set, etc.) — currently ghost-styled and visually compliant; revisited only if they read off in practice.

Open threads

New threads will accumulate here as gaps surface in real use.