Adds a "Flutter app port — shipped 2026-04-28" section between the web Surface phase and Open threads. Records the two FabledApp commits (foundation 0f05f47, surface b9e68e3), explains the ActionColors ThemeExtension shape, points to reference call sites for the ActionColors.primary (calendar event Save) and ActionColors.destructive (confirm-Delete dialogs across notes/tasks/chat/calendar) patterns so downstream screens have a template to follow when reclassifying their own buttons opportunistically. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
34 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.08emletter-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 Vellumat 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 93a3beb → 3c1ec40 (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
#F5F1E8warm 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-secondaryinstead. 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 /
#5A5852light) - Right-aligned, rounded except bottom-right (subtle "from-me" tail)
Assistant bubble (lit):
- Background: card surface (Iron dark /
#FBF8F0light) - 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:
- Install
lucide-vue-nextas the icon source. Replace hand-inlined SVGs with imported components. Single source of truth. - 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.
- 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.
- 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.
- 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
ToolCallCardinside 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 (93a3beb → 3c1ec40)
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 (✕, ✓, 🎤, 📎, ↻, ↑, ×, ☀/☾) 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: 600and700snapped to500across all surface PRs. - Hardcoded
--color-dangerin 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
×/✕/ 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.
Flutter app port — shipped 2026-04-28
The companion mobile app (fabled_app / FabledApp repo) tracks the same design system. Two commits:
- Foundation port —
0f05f47.lib/core/theme.dartrewritten with the Obsidian/Iron/Pewter dark palette, warm parchment light palette, dusty violet#5B4A8Aprimary. Inter loaded for body, JetBrains Mono available at call sites, Fraunces for headlines ≥18px. NewActionColorsThemeExtension exposes Moss/Bronze/Oxblood/Pewter outside theColorScheme(Material's primary/secondary/tertiary slots all carry brand accent, so action tokens need their own home).GradientButtonrecolored to dusty-violet gradient. - Surface phase —
b9e68e3.lucide_icons ^0.257.0installed; 107Icons.*references across 21 files swapped toLucideIcons.*. Input border radius 24 → 8 in both themes. ChatMessageBubble Illuminated Transcript fixes — neutral border on user bubbles,surface/Iron bg on assistant bubbles, asymmetric corner restoration (only bottom-left clipped, not both left corners), accent-tinted glow shadow added. 5 destructive confirm buttons across notes / tasks / chat / calendar wired toActionColors.destructive. Calendar event Save wired toActionColors.primaryas the reference Moss site. 4 hardcoded indigo Color literals → dusty-violet equivalents.
The Flutter port doesn't decompose into 7 PRs the way web did because Flutter's centralized theme.dart means most palette/font work happens in one file. Per-screen Save / Cancel reclassification beyond the calendar event Save is opportunistic — the wiring pattern (Theme.of(context).extension<ActionColors>()!.primary) is established and applied incrementally as files are touched.
Pattern reference for downstream screens: see lib/screens/calendar/event_form_sheet.dart for ActionColors.primary usage on Save buttons; see the dialog spots in note_edit_screen.dart / task_edit_screen.dart / note_detail_screen.dart / conversations_tab_screen.dart for ActionColors.destructive on confirm-Delete buttons.
Open threads
New threads will accumulate here as gaps surface in real use.