Keynctr/.impeccable/design.json
Avi 8366af7d86 feat: add Impeccable themes (light + dark) + unified ProfileEditModal
- settings: add Theme::Impeccable / ImpeccableDark (serde rename impeccable-dark)
- types: extend Theme union
- styles: add :root[data-theme=impeccable] (oklch lacquer/kinpaku) and impeccable-dark (lacquer-deep), Alumni Sans display, editorial refinements
- SettingsScreen: add Impeccable options with live swatch preview
- ProfilesScreen: wire ProfileEditModal (Paper Lift) with segmented tabs, keep legacy modals for compat
- DESIGN.md/PRODUCT.md + .impeccable/design.json from impeccable document
2026-08-27 14:12:52 -05:00

380 lines
No EOL
15 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"schemaVersion": 2,
"generatedAt": "2026-08-27T18:00:17.873Z",
"title": "Design System: Nostr Keynctr",
"extensions": {
"colorMeta": {
"warm-ivory": {
"role": "neutral",
"displayName": "Warm Ivory",
"canonical": "#f6f4f0",
"tonalRamp": [
"#1e1b15",
"#3d3830",
"#5c564e",
"#7c756d",
"#9c958d",
"#bdb9b3",
"#deddda",
"#f6f4f0"
]
},
"paper": {
"role": "neutral",
"displayName": "Paper",
"canonical": "#ffffff",
"tonalRamp": [
"#1f1b15",
"#3e3a34",
"#5d5953",
"#7d7872",
"#9c9791",
"#bcb6b0",
"#deddda",
"#ffffff"
]
},
"soft-stone": {
"role": "neutral",
"displayName": "Soft Stone",
"canonical": "#f1ede7",
"tonalRamp": [
"#1d1b18",
"#3c3936",
"#5b5854",
"#7a7772",
"#999690",
"#b8b5ae",
"#d7d4cc",
"#f1ede7"
]
},
"ink": {
"role": "neutral",
"displayName": "Graphite Ink",
"canonical": "#201b15",
"tonalRamp": [
"#201b15",
"#4d4741",
"#7a756d",
"#a7a39e",
"#d4d1ce",
"#eceae8",
"#f5f4f3",
"#faf9f8"
]
},
"ink-muted": {
"role": "neutral",
"displayName": "Ink Muted",
"canonical": "#6d655a",
"tonalRamp": [
"#2a2722",
"#403d38",
"#56544f",
"#6d655a",
"#8a857e",
"#a7a5a1",
"#c4c2be",
"#e1dfdc"
]
},
"line": {
"role": "neutral",
"displayName": "Line",
"canonical": "#e6e0d6",
"tonalRamp": [
"#2c2a27",
"#4a4845",
"#686663",
"#868481",
"#a4a29f",
"#c2c0bd",
"#e0dedb",
"#e6e0d6"
]
},
"antique-brass": {
"role": "primary",
"displayName": "Antique Brass",
"canonical": "#6d5ae6",
"tonalRamp": [
"#221b4a",
"#3a2d7a",
"#523faa",
"#6d5ae6",
"#8b7cff",
"#a99dff",
"#c7bdff",
"#efecfd"
]
},
"antique-brass-soft": {
"role": "primary",
"displayName": "Brass Wash",
"canonical": "#efecfd",
"tonalRamp": [
"#221b4a",
"#3a2d7a",
"#523faa",
"#6d5ae6",
"#8b7cff",
"#c7bdff",
"#efecfd",
"#f8f7ff"
]
},
"signal-coral": {
"role": "neutral",
"displayName": "Signal Coral",
"canonical": "#c0392b",
"tonalRamp": [
"#3a1512",
"#5a1f1b",
"#7a2a24",
"#9a352d",
"#c0392b",
"#d66a60",
"#e99a96",
"#fdecec"
]
},
"muted-sage": {
"role": "neutral",
"displayName": "Muted Sage",
"canonical": "#22713c",
"tonalRamp": [
"#0f2a18",
"#143d22",
"#1a502c",
"#22713c",
"#4a8a5f",
"#73a382",
"#9cbca5",
"#e7f5ec"
]
},
"antique-gold": {
"role": "neutral",
"displayName": "Antique Gold",
"canonical": "#9a6b13",
"tonalRamp": [
"#2e2110",
"#4a3418",
"#664720",
"#825a28",
"#9a6b13",
"#b88a3a",
"#d6aa62",
"#fcf3e0"
]
},
"clear-ink": {
"role": "neutral",
"displayName": "Clear Ink",
"canonical": "#2868a4",
"tonalRamp": [
"#0f2538",
"#163a58",
"#1d4f78",
"#2868a4",
"#4d82b4",
"#739dc4",
"#99b8d4",
"#e9f2fb"
]
}
},
"typographyMeta": {
"display": {
"displayName": "Display",
"purpose": "Page titles (h1) only — 24px, weight 650."
},
"headline": {
"displayName": "Headline",
"purpose": "Card and modal heads (h2) — 16px, weight 650."
},
"body": {
"displayName": "Body",
"purpose": "Reading text — 15px / 1.5, max 65–75ch."
},
"label": {
"displayName": "Label",
"purpose": "Field labels and hints — 13px / 600."
},
"mono": {
"displayName": "Mono",
"purpose": "Code and npub — SFMono 13.8px."
}
},
"shadows": [
{
"name": "paper-lift",
"value": "0 1px 2px rgba(30,25,18,0.05), 0 8px 24px rgba(30,25,18,0.06)",
"purpose": "Default card lift on Warm Ivory."
},
{
"name": "modal-lift",
"value": "0 12px 40px rgba(20,15,10,0.22)",
"purpose": "Modal over app."
},
{
"name": "neon-glow",
"value": "0 0 32px rgba(255,20,147,0.12)",
"purpose": "Gated neon modal glow only on dark."
}
],
"motion": [
{
"name": "ease-standard",
"value": "0.12s ease",
"purpose": "Button, nav, border-color transitions."
},
{
"name": "ease-glass",
"value": "blur 18px saturate(160%)",
"purpose": "Aurora glass panels."
}
],
"breakpoints": [
{
"name": "sidebar",
"value": "920px"
},
{
"name": "card-grid",
"value": "280px"
}
]
},
"components": [
{
"name": "Primary Button",
"kind": "button",
"refersTo": "button-primary",
"description": "Main action — Antique Brass on Paper.",
"html": "<button class=\"ds-btn-primary\">Publish</button>",
"css": ".ds-btn-primary{background:var(--primary);color:var(--on-primary);border:1px solid transparent;border-radius:9px;padding:10px 18px;font-weight:600;transition:background 0.12s ease} .ds-btn-primary:hover{background:var(--primary-hover)} .ds-btn-primary:focus-visible{outline:2px solid var(--focus);outline-offset:2px}"
},
{
"name": "Secondary Button",
"kind": "button",
"refersTo": "button-secondary",
"description": "Paper button with strong line border.",
"html": "<button class=\"ds-btn-secondary\">Select</button>",
"css": ".ds-btn-secondary{background:var(--surface);color:var(--text);border:1px solid var(--border-strong);border-radius:9px;padding:10px 18px;font-weight:600} .ds-btn-secondary:hover{background:var(--surface-hover)}"
},
{
"name": "Ghost Button",
"kind": "button",
"refersTo": "button-ghost",
"description": "Transparent for tertiary actions.",
"html": "<button class=\"ds-btn-ghost\">Edit name</button>",
"css": ".ds-btn-ghost{background:transparent;color:var(--text-muted);border:1px solid transparent;border-radius:9px;padding:7px 12px;font-weight:600} .ds-btn-ghost:hover{background:var(--surface-2);color:var(--text)}"
},
{
"name": "Danger Button",
"kind": "button",
"refersTo": "button-danger",
"description": "Signal Coral wash for destructive actions.",
"html": "<button class=\"ds-btn-danger\">Delete</button>",
"css": ".ds-btn-danger{background:var(--danger-soft);color:var(--danger);border:1px solid transparent;border-radius:9px;padding:7px 12px;font-weight:600} .ds-btn-danger:hover{background:var(--danger);color:#fff}"
},
{
"name": "Card",
"kind": "card",
"refersTo": "card",
"description": "Paper surface with soft lift.",
"html": "<div class=\"ds-card\"><div class=\"ds-card-head\">Active profile</div><div class=\"ds-card-body\">Profile content</div></div>",
"css": ".ds-card{background:var(--surface);border:1px solid var(--border);border-radius:14px;box-shadow:0 1px 2px rgba(30,25,18,0.05),0 8px 24px rgba(30,25,18,0.06);overflow:hidden} .ds-card-head{padding:16px 20px;border-bottom:1px solid var(--border);font-weight:650} .ds-card-body{padding:20px}"
},
{
"name": "Input",
"kind": "input",
"refersTo": "input",
"description": "Paper field with strong line border.",
"html": "<label class=\"ds-field\">Label<input class=\"ds-input\" placeholder=\"name@domain.com\" /></label>",
"css": ".ds-input{font:inherit;color:var(--text);background:var(--surface);border:1px solid var(--border-strong);border-radius:9px;padding:10px 12px;width:100%} .ds-input:focus{outline:none;border-color:var(--focus);box-shadow:0 0 0 3px color-mix(in srgb,var(--focus) 22%, transparent)}"
},
{
"name": "Navigation Item",
"kind": "nav",
"refersTo": "nav-item",
"description": "Sidebar nav with muted → brass active.",
"html": "<button class=\"ds-nav is-active\">Home</button>",
"css": ".ds-nav{display:flex;gap:12px;padding:11px 12px;border-radius:9px;border:none;background:transparent;color:var(--text-muted);font-weight:550} .ds-nav:hover{background:var(--surface-2);color:var(--text)} .ds-nav.is-active{background:var(--primary-soft);color:var(--primary)}"
},
{
"name": "Undo Bar",
"kind": "custom",
"refersTo": "undo-bar",
"description": "Themed dismissal bar after delete — 5s, primary-soft.",
"html": "<div class=\"ds-undo\"><span>Profile “Alice” deleted.</span><span>—</span><button class=\"ds-undo-link\">Undo and restore profile</button></div>",
"css": ".ds-undo{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin:12px 0;padding:10px 14px;background:var(--primary-soft);border:1px solid var(--border);border-radius:8px} .ds-undo-link{background:none;border:none;padding:0;color:var(--primary);font-weight:600;cursor:pointer;text-decoration:underline;text-underline-offset:2px}"
},
{
"name": "Badge",
"kind": "chip",
"refersTo": "badge",
"description": "Pill tag for state.",
"html": "<span class=\"ds-badge success\">Active</span>",
"css": ".ds-badge{display:inline-flex;gap:5px;padding:3px 10px;border-radius:999px;font-size:12px;font-weight:650;white-space:nowrap} .ds-badge.success{background:var(--success-soft);color:var(--success)}"
},
{
"name": "Profile Edit Modal",
"kind": "custom",
"refersTo": "modal-profile-edit",
"description": "Paper modal for Rename / Picture / NIP-05 — Paper Lift, vault separation.",
"html": "<div class=\"ds-backdrop\"><div class=\"ds-modal\"><div class=\"ds-modal-head\"><h3>Edit profile — Alice</h3><button class=\"ds-icon\">×</button></div><div class=\"ds-modal-body\"><div class=\"ds-avatar-lg\">A</div><p class=\"ds-muted\">Public npub is visible to relays; private key never leaves Rust.</p><label class=\"ds-field\"><span>Profile name</span><input class=\"ds-input\" value=\"Alice\" /></label><label class=\"ds-field\"><span>Picture URL</span><input class=\"ds-input\" placeholder=\"https://…/avatar.png\" /></label><label class=\"ds-field\"><span>NIP-05</span><input class=\"ds-input\" placeholder=\"alice@example.com\" /></label><div class=\"ds-modal-actions\"><button class=\"ds-btn-danger\">Remove profile</button><span style=\"flex:1\"></span><button class=\"ds-btn-ghost\">Cancel</button><button class=\"ds-btn-primary\">Save & publish</button></div></div></div></div>",
"css": ".ds-backdrop{position:fixed;inset:0;background:rgba(20,16,12,0.45);display:grid;place-items:center;padding:24px} .ds-modal{background:var(--surface);border:1px solid var(--border);border-radius:16px;box-shadow:0 12px 40px rgba(20,15,10,0.22);width:100%;max-width:520px;max-height:88vh;overflow:auto} .ds-modal-head{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:18px 22px;border-bottom:1px solid var(--border);font-weight:650} .ds-modal-body{padding:20px 22px;display:flex;flex-direction:column;gap:16px} .ds-avatar-lg{width:52px;height:52px;border-radius:50%;display:grid;place-items:center;background:var(--primary-soft);color:var(--primary);font-weight:700;font-size:21px} .ds-muted{color:var(--text-muted);font-size:13px;margin:0} .ds-field{display:flex;flex-direction:column;gap:6px;font-size:13px;font-weight:600;color:var(--text)} .ds-modal-actions{display:flex;justify-content:flex-end;gap:10px;align-items:center;margin-top:4px} .ds-icon{width:34px;height:34px;border-radius:999px;border:1px solid var(--border);background:var(--surface)} [data-theme=\"glass\"] .ds-modal{backdrop-filter:blur(18px) saturate(160%);-webkit-backdrop-filter:blur(18px) saturate(160%)} [data-theme=\"neon\"] .ds-modal{box-shadow:0 12px 40px rgba(0,0,0,0.85),0 0 32px rgba(255,20,147,0.12)}"
}
],
"narrative": {
"northStar": "Vault & Atelier",
"overview": "Nostr Keynctr is an atelier, not a dashboard — a warm, quiet workshop where identity work is done with care. The space feels like heavy paper and soft stone, with ink that is near-black, not pure black. Instruments are laid out plainly; nothing shouts for attention. Trust is built through precision: consistent edges, settled type, and state that is always legible.",
"keyCharacteristics": [
"Warm and human, precise and calm",
"Paper and stone material language — tonal layering over heavy shadows",
"Semantic tokens only — every color has a job",
"Operate density — tasks complete faster because hierarchy is consistent",
"Vault logic made visible — locked/muted states are distinct at a glance"
],
"rules": [
{
"name": "The One Accent Rule",
"body": "Antique Brass is the only saturated accent in the restrained palette. Its rarity is the point — use it for primary actions and active state only.",
"section": "colors"
},
{
"name": "The Vault Separation Rule",
"body": "Secret-dependent UI never shares color with public data: private operations are flagged via muted-sage/signal-coral and explicit Approve/Reject.",
"section": "colors"
},
{
"name": "The Weight Restraint Rule",
"body": "Only headings are 650. Do not add semibold to body or emphasize muted text with weight.",
"section": "typography"
},
{
"name": "The Flat-By-Default Rule",
"body": "Surfaces are flat at rest. Shadows appear only as a response to layer (card on canvas, modal over app).",
"section": "elevation"
}
],
"dos": [
"Do keep Warm Ivory as the only page canvas on light",
"Do use Muted Sage wash only for successful security confirmation",
"Do keep one primary per screen — Antique Brass is rare by design",
"Do use var(--focus) for every focus ring; keep the 2px + 2px offset invariant",
"Do preserve the side-by-side rate: cards on Paper with 1px Line borders before adding any shadow"
],
"donts": [
"Don't use Signal Coral outside error/destructive states",
"Don't introduce a new accent hue outside the four semantic washes",
"Don't bump heading weight beyond 650 or add bold to muted text",
"Don't apply neon glow outside data-theme='neon' modals",
"Don't invent a new radius — step on 9px/14px/16px/999px only",
"Don't replace the 5s auto-dismiss undo bar with a permanent modal"
]
}
}