Files
FabledScribe/docs/design-system.md
T
bvandeusenandClaude Opus 5.5 47d40bffd8 docs: architecture, configuration, development and features describe Scribe as it is today
Milestone 544, step 6217 (follow-up to #1778, which rewrote features.md and
api-reference.md in July but left architecture.md and configuration.md alone).

- architecture.md, configuration.md, development.md: rewritten against the
  code. They still described Ollama, chat, SSE generation, voice, project
  summaries, image caching, Docker secrets that the compose files don't use,
  a raw-SQL migration convention the migrations no longer follow, a 27-entry
  migration list, CalVer YY.MM.DD.N tags and the removed infra/ files.
- features.md: adds what shipped since July (spikes, claims, moments, rule
  versions and verification, lessons, reply shapes, snippets and the shape
  ledger, forge integration, family canon, design systems, retrieval tuning)
  and corrects the Settings tabs and keyboard shortcuts (g g is the graph).
- api-reference.md: adds the endpoints for snippets, lessons, design
  systems, family canon, platforms, shared areas, retrieval, rule versions,
  relations and verification, forge connections, webhooks, scheduled
  backups and the newer plugin hooks.
- api-keys-and-mcp.md: Settings tab is "MCP Access"; a plugin install
  section; the tool-family table matches the tools that exist.
- design-system.md: the dated "decisions in progress" changelog (April
  PR log, chat/journal/calendar surfaces) becomes a short present-tense
  section; the stored design system is named as the authority.
- sso-oauth.md: the app is Scribe, not "Fabled Assistant".
- README: feature summary and doc table brought current.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 19:23:56 -04:00

367 lines
17 KiB
Markdown

# 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
```css
: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).
---
# Scribe
The design system Scribe renders with is stored in Scribe itself: **Scribe**, which inherits **FabledSword**, edited at `/design-systems`. `frontend/src/assets/theme.css` is generated from it, and CI rejects a `var()` the sheet doesn't define. Where this page and the stored system differ, the stored system is right. Scribe applies the baseline above with these choices:
- **Accent footprint.** Dusty violet marks Scribe's own moments: the wordmark, active navigation, the editor's cursor and selection, tags and wikilinks, in-progress status, focus rings, and the call to action that starts something new (a first note, say). Operating the software uses the action colours: Moss to save or confirm, Bronze for alternatives, Oxblood with an icon to delete, Pewter ghost for tertiary actions.
- **Light and dark.** Both are supported. Light mode is warm parchment (`#F5F1E8` page, `#FBF8F0` cards, `#14171A` text) rather than white, with a cool wash on code blocks so syntax colours stay readable. The accent, action and semantic colours are the same in both.
- **Status and priority.** Status: todo Pewter, in progress the accent, done Moss, cancelled Ash, paused Warning. Priority runs cool to warm: low Info, medium Warning, high Error. Overdue uses Error. Each has a low-alpha `-bg` companion.
- **Type.** Inter for UI and body, Fraunces at 18px and up (display, H1, H2), JetBrains Mono for code. Two weights only, 400 and 500. Line height 1.5 for body, 1.3 for headings, 1.7 on reading surfaces such as a note.
- **Icons.** Lucide, at 16px inline and 24px for navigation and primary actions; no emoji in the interface.
- **Borders are structural.** Cards, dialogs, code blocks, focus rings and major dividers get borders; list rows, chips and empty states are separated by surface tint and spacing instead.