n-of-m UX Specification

v0.3.3 -- Living design reference for all UI elements

1. Design Tokens

CSS custom properties defined in src/app.css on :root and [data-theme='dark'].

Colors

--color-bg Primary background
--color-bg-secondary Secondary background
--color-bg-alt Alternate background (panels, info boxes)
--color-hover-bg Hover state background
--color-text Primary text
--color-text-muted Secondary / muted text
--color-border Default border
--color-border-light Light border
--color-border-dark Strong border (buttons, inputs focused)
--color-link Hyperlinks
--color-success Success states
--color-warning Warning states
--color-error Error states
--color-info Informational
--color-accent Accent / primary action
--color-crypto-bg Crypto-themed background
--color-crypto-text Crypto-themed text

Spacing

--spacing-xs 0.5rem
--spacing-sm 0.75rem
--spacing-md 1rem
--spacing-lg 1.5rem
--spacing-xl 2rem

Typography

--font-mono ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, monospace

h1 -- 1.75rem / 600

h2 -- 1.25rem / 600

h3 -- 1rem / 600

Key CSS

:root {
  --color-bg: #ffffff;
  --color-text: #111111;
  --color-border: #e5e5e5;
  --color-accent: #5c6bc0;
  --font-mono: ui-monospace, 'Cascadia Code', ...;
  --spacing-md: 1rem;
}

[data-theme='dark'] {
  --color-bg: #002b36;
  --color-text: #93a1a1;
  --color-border: #586e75;
  --color-accent: #6c71c4;
}

2. App Shell

Top-level layout elements from src/routes/+page.svelte and src/components/ThemeToggle.svelte.

.app-wrapper

Outer wrapper. Starts with margin-top: 25vh (centered on home), transitions to margin-top: 0 when .active (navigated to a section).

.app-wrapper (margin-top: 25vh on home, 0 when active)
.app-wrapper {
  margin-top: 25vh;
  transition: margin-top 0.3s ease;
}
.app-wrapper.active {
  margin-top: 0;
}

.nav-bar

Horizontal bar shown when navigated away from home. Contains back button, section title, and spacer.

GENERATE
.nav-bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: var(--spacing-sm) var(--spacing-lg);
  border-bottom: 1px solid var(--color-border);
}

.nav-btn

Ghost button for back navigation. No background, border, or shadow.

.nav-btn {
  background: none; border: none; box-shadow: none;
  padding: 0.3rem 0.5rem; font-size: 0.8rem;
  color: var(--color-text-muted);
}

.nav-title

Section name label centered in nav bar.

.nav-title {
  font-size: 0.8rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.05em;
}

.theme-toggle

Fixed-position button (top-right) to toggle light/dark theme. From ThemeToggle.svelte.

.theme-toggle {
  position: fixed; top: var(--spacing-md); right: var(--spacing-md);
  z-index: 200; font-size: 1.1rem;
}

.container

Content container. Max 900px, centered with horizontal padding.

.container {
  max-width: 900px;
  margin: 0 auto;
  padding: 0 var(--spacing-lg);
}

.app-footer

Footer with version string. Centered text at page bottom.

v0.3.3
.app-footer {
  text-align: center;
  padding: var(--spacing-lg) 0;
  margin-top: var(--spacing-xl);
}

3. Global Primitives

Buttons, inputs, badges, tables, and utility classes from src/app.css.

Buttons

Default: bordered with shadow. .primary: accent background, white text. :disabled: faded, no shadow.

button, .btn {
  font-family: var(--font-mono); font-size: 0.8rem; font-weight: 600;
  padding: 0.4rem 1rem; border: 1px solid var(--color-border-dark);
  border-radius: 0; background: var(--color-bg); color: var(--color-text);
  text-transform: uppercase; letter-spacing: 0.05em;
  box-shadow: 2px 2px 0px var(--color-shadow);
}
button.primary {
  background: var(--color-accent); color: #fff;
}
button:disabled {
  opacity: 0.4; cursor: not-allowed; box-shadow: none;
}

Inputs

input, textarea, select {
  font-family: var(--font-mono); font-size: 0.9rem;
  padding: 0.5rem 0.75rem; border: 1px solid var(--color-border);
  border-radius: 0; background: var(--color-bg); color: var(--color-text);
}
input:focus, textarea:focus, select:focus {
  border-color: var(--color-border-dark);
  box-shadow: 2px 2px 0px var(--color-shadow);
}

Badges

Inline status labels with semantic color variants.

SUCCESS WARNING ERROR INFO
.badge {
  display: inline-block; font-size: 0.7rem; font-weight: 600;
  padding: 0.15rem 0.4rem; border: 1px solid;
  text-transform: uppercase; letter-spacing: 0.03em;
}
.badge-success { background: rgba(40,167,69,0.15); border-color: #28a745; color: #28a745; }
.badge-warning { background: rgba(255,193,7,0.15); border-color: #ffc107; color: #92400e; }
.badge-error   { background: rgba(220,53,69,0.15); border-color: #dc3545; color: #dc3545; }
.badge-info    { background: rgba(92,107,192,0.15); border-color: var(--color-accent); color: var(--color-accent); }

.data-table

Minimal data table with hover highlight.

IndexAddressBalance
00xAb5801a7...93Fd1.234 ETH
10x1234abcd...56780.500 ETH
.data-table {
  width: 100%; border-collapse: collapse; font-size: 0.85rem;
}
.data-table th {
  font-weight: 600; text-transform: uppercase; font-size: 0.75rem;
  letter-spacing: 0.05em; color: var(--color-text-muted);
}
.data-table th, .data-table td {
  padding: 0.4rem 0.75rem; text-align: left;
  border-bottom: 1px solid var(--color-border);
}
.data-table tr:hover { background: var(--color-hover-bg); }

.terminal-log

Scrollable log output with colored line types. From src/app.css.

System initialized
Fetching entropy bytes...
Generated 24-word mnemonic
Connection refused
Share 3/5 matched
Cleared session data
.terminal-log {
  background: var(--color-bg-alt); border: 1px solid var(--color-border-light);
  font-size: 13px; padding: var(--spacing-md);
  max-height: 400px; overflow-y: auto;
  box-shadow: 2px 2px 0px var(--color-shadow);
}
.log-line::before { content: '> '; color: var(--color-text-muted); }
.log-line.fetch { color: var(--color-link); }
.log-line.found { color: var(--color-success); }
.log-line.match { color: var(--color-error); font-weight: 600; }
.log-line.clear { color: var(--color-text-muted); }
.log-line.error { color: var(--color-error); }

.copy-toast

Floating ephemeral toast that animates upward and fades out (1s). Position set via JS at click coordinates.

.copy-toast {
  position: fixed; pointer-events: none;
  font-size: 0.7rem; font-weight: 600; text-transform: uppercase;
  padding: 0.2rem 0.5rem; background: var(--color-text); color: var(--color-bg);
  border: 1px solid var(--color-border-dark); z-index: 9999;
  animation: copyToast 1s ease forwards;
}

Utility Classes

.text-muted Muted text color .text-sm 0.85rem font size .text-xs 0.75rem font size .mt-xs / .mt-sm / .mt-md / .mt-lg Margin-top utilities .mb-sm / .mb-md / .mb-lg Margin-bottom utilities .gap-sm / .gap-md Gap utilities

4. Hero / Home

Landing page elements from src/components/Hero.svelte. Centered layout, max-width 600px.

.hero

Centered container for the home screen. Contains title, subtitle, action buttons, and the explainer accordion.

n of m

Shamir's Secret Sharing for Seed Phrases

Split. Print. Recover.

.hero { text-align: center; max-width: 600px; margin: 0 auto; }
.hero-title { font-size: 2.5rem; letter-spacing: 0.1em; text-transform: lowercase; }
.hero-subtitle { font-size: 0.9rem; margin-top: var(--spacing-xs); }
.hero-tagline { letter-spacing: 0.15em; text-transform: uppercase; font-size: 0.75rem; }

.hero-actions

Flex row of navigation buttons. Wraps and stacks vertically on mobile.

.hero-actions {
  display: flex; gap: var(--spacing-sm);
  justify-content: center; flex-wrap: wrap;
}
.hero-actions button { min-width: 120px; }

.explainer / .math-block

Accordion section explaining Shamir's algorithm. Uses .explainer-section, .explainer-heading, and .math-block for formulas.

Polynomial Construction

For each byte s of the secret, a random polynomial is constructed:

f(x) = s + a1x + a2x2 + ... + at-1xt-1
.explainer {
  text-align: left; max-width: 520px; margin: 0 auto;
  border: 1px solid var(--color-border); background: var(--color-bg-alt);
  padding: var(--spacing-md);
}
.math-block {
  font-family: var(--font-mono); font-size: 0.75rem;
  background: var(--color-hover-bg); border: 1px solid var(--color-border);
  padding: 0.5rem 0.75rem; margin: 0.4rem 0; text-align: center;
}

5. Panel Component

Reusable container from src/components/Panel.svelte. Uses global .panel / .panel-header from src/app.css.

.panel / .panel-header / .panel-body

Panel Title header-right slot

Panel body content goes here. This is the default content area with padding: var(--spacing-md).

.panel {
  background: var(--color-bg); border: 1px solid var(--color-border);
  border-radius: 0; box-shadow: 2px 2px 0px var(--color-shadow);
}
.panel-header {
  padding: var(--spacing-sm) var(--spacing-md);
  border-bottom: 1px solid var(--color-border);
  font-weight: 600; font-size: 0.85rem;
  text-transform: uppercase; letter-spacing: 0.05em;
}
.panel-body {
  padding: var(--spacing-md);
}

6. GenerateFlow Wizard

Multi-step wizard from src/components/GenerateFlow.svelte and its sub-components.

.step-indicator / .step-badge

Horizontal step tracker from StepIndicator.svelte. Badges show active, completed, or default state.

Words Entropy 3 Mnemonic 4 Derivation 5 Shamir 6 Metadata 7 Preview
.step-indicator { display: flex; flex-wrap: wrap; gap: 0.35rem; }
.step-badge {
  display: inline-flex; align-items: center; gap: 0.35rem;
  padding: 0.2rem 0.5rem; font-size: 0.7rem; font-weight: 600;
  text-transform: uppercase; border: 1px solid var(--color-border);
  color: var(--color-text-muted);
}
.step-badge.active {
  background: rgba(92,107,192,0.15); border-color: var(--color-accent);
  color: var(--color-accent);
}
.step-badge.completed {
  background: rgba(40,167,69,0.15); border-color: var(--color-success);
  color: var(--color-success);
}

.word-count-buttons

Row of word count options (12, 15, 18, 21, 24). Selected button uses .primary.

.entropy-canvas

Canvas from EntropyCanvas.svelte for mouse/touch entropy collection. Dark background, crosshair cursor, progress bar below.

entropy canvas (crosshair cursor)
MOVE YOUR MOUSE OR FINGER ACROSS THE CANVAS
.entropy-canvas {
  width: 100%; height: 200px;
  border: 1px solid var(--color-border-dark);
  box-shadow: 2px 2px 0px var(--color-shadow);
  cursor: crosshair; touch-action: none;
}

.mnemonic-grid / .word-cell

4-column grid of word cells from MnemonicGrid.svelte. Each cell has a small index number and centered word text.

1 abandon
2 ability
3 able
4 about
5 above
6 absent
7 absorb
8 abstract
.mnemonic-grid {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.5rem;
}
.word-cell {
  border: 1px solid var(--color-border-dark);
  box-shadow: 2px 2px 0px var(--color-shadow);
  padding: 0.5rem; position: relative;
  display: flex; align-items: center; justify-content: center;
  min-height: 2.8rem;
}
.word-index { position: absolute; top: 2px; left: 4px; font-size: 0.6rem; }
.word-text { font-size: 0.85rem; font-weight: 600; text-align: center; }

.path-editor

Derivation path selector from PathEditor.svelte. Toggle buttons for MetaMask / Ledger / Custom, with path display below.

m/44'/60'/0'/0/{index}

.shamir-config

Threshold (N) and total shares (M) inputs side by side.

.color-swatches / .color-swatch

Color picker for card highlight. 32x32 buttons in a flex row.

.color-swatch {
  width: 32px; height: 32px;
  border: 2px solid var(--color-border);
  padding: 0; min-width: 0;
  box-shadow: 1px 1px 0px var(--color-shadow);
}
.color-swatch.selected {
  border-color: var(--color-border-dark);
  box-shadow: 3px 3px 0px var(--color-shadow);
  transform: translate(-1px, -1px);
}

.popup-overlay / .confirm-popup

Modal overlay (fixed, full-screen dim) and centered confirmation dialog. Used for PIN and passphrase confirmation.

CONFIRM PIN

Re-enter your 6-digit PIN to confirm.

.popup-overlay {
  position: fixed; inset: 0; background: rgba(0,0,0,0.3);
  z-index: 100;
}
.confirm-popup {
  position: fixed; top: 50%; left: 50%;
  transform: translate(-50%, -50%); z-index: 101;
  background: var(--color-bg); border: 2px solid var(--color-border-dark);
  box-shadow: 6px 6px 0px var(--color-shadow);
  padding: var(--spacing-lg); min-width: 320px;
}

.pin-input

Large centered password input for 6-digit PIN entry from PinInput.svelte.

.pin-input {
  font-size: 1.5rem; letter-spacing: 0.5em;
  text-align: center; padding: 0.75rem; max-width: 300px;
}

7. ScanFlow

QR scanning and file import UI from src/components/ScanFlow.svelte.

.camera-area

Camera viewport container. 4:3 aspect ratio, max 500px wide, dark border with shadow.

Camera not active

.camera-area {
  position: relative; width: 100%; max-width: 500px;
  margin: 0 auto; aspect-ratio: 4/3;
  background: var(--color-bg-alt);
  border: 1px solid var(--color-border-dark);
  box-shadow: 2px 2px 0px var(--color-shadow);
}

.scan-overlay / .reticle / .corner

AR-style scanning overlay with corner brackets and guidance pill.

Align QR code within frame

.shard-progress / .threshold-boxes

Progress indicator showing scanned shares vs. threshold. Boxes fill green as shares are found.

THRESHOLD
2/3
ALL SHARDS
1
2
3
4
5
.threshold-box {
  width: 20px; height: 20px;
  border: 2px solid var(--color-border-dark); background: var(--color-bg);
}
.threshold-box.filled { background: var(--color-success); }
.slot-box {
  width: 24px; height: 24px; border: 1px solid var(--color-border);
  font-size: 0.6rem; color: var(--color-text-muted);
}
.slot-box.scanned {
  background: var(--color-success-bg); border-color: var(--color-success);
  color: var(--color-success); font-weight: 600;
}

.upload-status / .braille-progress-bar

Status bar shown during file imports. Braille spinner + striped progress fill.

Scanning page 3/7 -- Found 2 QR codes

43%

.scan-formats

Accepted file formats info card with icon list and warning footer.

Accepted Formats
Camera - Live QR scanning
PDF - Exported share cards
Encrypted JSON - AES-256-GCM

.password-content

Password input for encrypted JSON import. Horizontal input + button layout.

8. VaultPanel

Secret management list from src/components/VaultPanel.svelte. Expandable items with export options.

.vault-list / .vault-item

Vertical list of saved secrets. Each item expands to show details on click.

My Wallet Backup
2026-02-25 -- 3-of-5 -- 24 words
Cold Storage Keys
2026-02-20 -- 2-of-3 -- 12 words

Expanded detail content area

.popup-card / .export-options / .export-option

Export/share dialog with multiple download options (Print, HTML, PDF, Image, JSON).

EXPORT SHARE CARDS

.detail-actions

Action buttons at the bottom of an expanded vault item (Reprint, Export, Delete).

.share-sets-table

Table listing share indices, share data preview, and actions within the vault detail view.

ShareDataStatus
1/5a1b2c3d4e5f6...OK
2/5f6e5d4c3b2a1...OK
3/51a2b3c4d5e6f...OK

9. SettingsPanel

App configuration from src/components/SettingsPanel.svelte. Stacked panels for each setting group.

.settings-panels

Vertical stack of Panel components with gap: var(--spacing-md).

.layout-options

Layout selector row: button + description + download icon.

One card per page, 80% QR code

.storage-row

Key-value rows showing storage info (localStorage, IndexedDB, encryption method).

LOCAL STORAGE Preferences, settings, PIN hash
INDEXEDDB Encrypted vault (AES-256-GCM)
ENCRYPTION AES-256-GCM with browser key

.algo-detail

Expandable cryptographic details section. Lists algorithm names and descriptions.

SECRET SPLITTING

Shamir's Secret Sharing over GF(2^8). Information-theoretically secure.

VAULT ENCRYPTION

AES-256-GCM with authenticated encryption.

.credits-list / .credit-badge

Linked badges for third-party libraries used in the project.

svelte sveltekit ethers qr-scanner

.install-steps

Platform-specific PWA install instructions. Key-value rows.

CHROME / EDGE Click install icon in address bar
SAFARI (iOS) Share -> Add to Home Screen
FIREFOX Use standalone HTML instead

10. PDF / Share Card Template

Share card layout from src/lib/pdf/templates.ts. The PDF template uses its own hardcoded CSS (Courier New, black borders, white background) independent of app theme variables. Layout config from src/lib/pdf/layouts.ts: qrSize=252, fontSize=11.

Shamir Secret Sharing Card
v0.3.3
3/5 SHAMIR T:3 · 24W · V2

This card is one fragment of a secret divided using Shamir's Secret Sharing. By itself this card reveals nothing about the original secret.

To reconstruct the secret, collect and scan at least 3 of the 5 total share cards using the Shamir recovery app.

During recovery you may need to provide a PIN. You may also be asked additional questions about your secret's configuration.

Do not store all shares in the same location. Each share should be kept secure and separate.

2026-02-25 14:00
QR CODE

This QR code contains your encrypted share data. Scan it with the Shamir recovery app to begin the reconstruction process. You will need to scan at least 3 cards total.

Handle with care. If this card is lost or damaged you will need the remaining shares to recover your secret. There are no backups.

m/44'/60'/0'/0/{index}
00xAb5801a7D398351b8bE11C439e05C5B3259aeC9B
10x1234567890abcdef1234567890abcdef12345678
20xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef

Template Class Reference

ClassElementKey Properties
.pagePage containerpage-break-after: always; min-height: 100vh; flex column
.page-titlePage headertext-align: center; padding-bottom: 0.5rem
.page-title-textTitle text14px bold; uppercase; letter-spacing: 2px
.page-title-versionVersion label10px; color: #666
.cardCard wrapperborder: 3px solid #000; flex column; full width
.headerColored header barpadding: 6px 12px; border-bottom: 3px solid #000; flex row
.header-titleShare label (e.g. "3/5 SHAMIR")13px bold uppercase
.header-metaConfig summary9px; opacity: 0.7
.sectionContent sectionpadding: 8px 12px; border-bottom: 2px solid #000
.section-labelSection heading8px bold uppercase; letter-spacing: 2px; color: #666
.instructions-textInstruction paragraphs10.5px; line-height: 1.5
.date-rowCreated dateflex row; gap: 12px; condensed padding
.notes-sectionBlank lines areamin-height: 60px
.note-lineRuled lineborder-bottom: 1px solid #ccc; height: 16px
.bottom-sectionQR + info areaflex row; gap: 12px; flex: 1
.share-qrQR code frameborder: 2px solid #000; padding: 5em
.bottom-rightText beside QRflex: 1; flex column; space-between
.qr-info-topQR scan instructions10px; line-height: 1.4
.qr-info-bottomCare warning10px; line-height: 1.4
.addresses-sectionAddress list areamargin-top: auto
.addr-listAddress containerflex column; gap: 1px
.addr-itemSingle address rowflex row; gap: 4px; font-size: 9px
.addr-idxAddress indexcolor: #666; bold; min-width: 12px
.addr-valAddress textword-break: break-all
.footerCard footerpadding: 6px 12px; border-top: 3px solid #000; bg: #f5f5f5
.footer-warningWarning text8px bold
.footer-infoPIN/passphrase status8px; color: #666
.footer-guidShare UUID7px; Courier New; text-align: right; color: #666