@motir/design-system 0.20.0 → 0.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +150 -5
  2. package/dist/components/theme/AppearancePickers.js +30 -2
  3. package/dist/components/theme/AppearancePickers.js.map +1 -1
  4. package/dist/components/ui/DatePicker.d.ts +31 -0
  5. package/dist/components/ui/DatePicker.js +442 -0
  6. package/dist/components/ui/DatePicker.js.map +1 -0
  7. package/dist/components/ui/DatePickerFormatContext.d.ts +26 -0
  8. package/dist/components/ui/DatePickerFormatContext.js +58 -0
  9. package/dist/components/ui/DatePickerFormatContext.js.map +1 -0
  10. package/dist/components/ui/datePickerFormat.d.ts +40 -0
  11. package/dist/components/ui/datePickerFormat.js +30 -0
  12. package/dist/components/ui/datePickerFormat.js.map +1 -0
  13. package/dist/contexts/theme-context.js +30 -2
  14. package/dist/contexts/theme-context.js.map +1 -1
  15. package/dist/index.d.ts +3 -0
  16. package/dist/index.js +5 -0
  17. package/dist/mock/index.js +30 -2
  18. package/dist/mock/index.js.map +1 -1
  19. package/dist/mock/renderMock.js +30 -2
  20. package/dist/mock/renderMock.js.map +1 -1
  21. package/dist/specimen/TokensSpecimen.js +2 -2
  22. package/dist/specimen/TokensSpecimen.js.map +1 -1
  23. package/dist/theme/appearance-resolution.js +30 -2
  24. package/dist/theme/appearance-resolution.js.map +1 -1
  25. package/dist/theme/init-script.js +30 -2
  26. package/dist/theme/init-script.js.map +1 -1
  27. package/dist/theme/styles.d.ts +1 -1
  28. package/dist/theme/styles.js +2 -2
  29. package/dist/theme/styles.js.map +1 -1
  30. package/dist/theme/types.js +2 -2
  31. package/dist/theme/types.js.map +1 -1
  32. package/dist/theme/typography.d.ts +46 -3
  33. package/dist/theme/typography.js +28 -0
  34. package/dist/theme/typography.js.map +1 -1
  35. package/package.json +1 -1
  36. package/theme.css +114 -1
@@ -41,6 +41,34 @@ var TYPE_REGISTRY = {
41
41
  tagline: "IBM Plex Mono throughout \u2014 a precise, developer-grade UI.",
42
42
  faces: "IBM Plex Mono headlines + body/UI + meta/code.",
43
43
  designDoc: "docs/typography/mono-technical.md"
44
+ },
45
+ marker: {
46
+ id: "marker",
47
+ name: "Marker",
48
+ tagline: "Hand-lettered marker headlines over a clean sans body \u2014 sketchy, friendly, human.",
49
+ faces: "Caveat marker headlines \xB7 Inter body \xB7 JetBrains Mono meta.",
50
+ designDoc: "docs/typography/marker.md"
51
+ },
52
+ rounded: {
53
+ id: "rounded",
54
+ name: "Rounded",
55
+ tagline: "A soft rounded sans across the whole UI \u2014 friendly and approachable.",
56
+ faces: "Nunito headlines + body/UI \xB7 JetBrains Mono meta.",
57
+ designDoc: "docs/typography/rounded.md"
58
+ },
59
+ slab: {
60
+ id: "slab",
61
+ name: "Slab",
62
+ tagline: "Sturdy slab-serif headlines over a clean sans body \u2014 confident, print-like.",
63
+ faces: "Roboto Slab headlines \xB7 Inter body \xB7 JetBrains Mono meta.",
64
+ designDoc: "docs/typography/slab.md"
65
+ },
66
+ humanist: {
67
+ id: "humanist",
68
+ name: "Humanist",
69
+ tagline: "A warm humanist sans across the whole UI \u2014 open and readable.",
70
+ faces: "Fira Sans headlines + body/UI \xB7 JetBrains Mono meta.",
71
+ designDoc: "docs/typography/humanist.md"
44
72
  }
45
73
  };
46
74
  var TYPE_IDS = Object.keys(TYPE_REGISTRY);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/theme/typography.ts"],"names":[],"mappings":";AAuFO,IAAM,aAAA,GAAgB;AAAA,EAC3B,KAAA,EAAO;AAAA,IACL,EAAA,EAAI,OAAA;AAAA,IACJ,IAAA,EAAM,OAAA;AAAA,IACN,OAAA,EAAS,4EAAA;AAAA,IACT,KAAA,EAAO,oEAAA;AAAA,IACP,SAAA,EAAW;AAAA,GACb;AAAA,EACA,YAAA,EAAc;AAAA,IACZ,EAAA,EAAI,YAAA;AAAA,IACJ,IAAA,EAAM,YAAA;AAAA,IACN,OAAA,EAAS,qEAAA;AAAA,IACT,KAAA,EAAO,kDAAA;AAAA,IACP,SAAA,EAAW;AAAA,GACb;AAAA,EACA,YAAA,EAAc;AAAA,IACZ,EAAA,EAAI,YAAA;AAAA,IACJ,IAAA,EAAM,YAAA;AAAA,IACN,OAAA,EAAS,wEAAA;AAAA,IACT,KAAA,EAAO,4CAAA;AAAA,IACP,SAAA,EAAW;AAAA,GACb;AAAA,EACA,OAAA,EAAS;AAAA,IACP,EAAA,EAAI,SAAA;AAAA,IACJ,IAAA,EAAM,SAAA;AAAA,IACN,OAAA,EAAS,iFAAA;AAAA,IACT,KAAA,EAAO,6DAAA;AAAA,IACP,SAAA,EAAW;AAAA,GACb;AAAA,EACA,SAAA,EAAW;AAAA,IACT,EAAA,EAAI,WAAA;AAAA,IACJ,IAAA,EAAM,WAAA;AAAA,IACN,OAAA,EAAS,gFAAA;AAAA,IACT,KAAA,EAAO,sEAAA;AAAA,IACP,SAAA,EAAW;AAAA,GACb;AAAA,EACA,gBAAA,EAAkB;AAAA,IAChB,EAAA,EAAI,gBAAA;AAAA,IACJ,IAAA,EAAM,gBAAA;AAAA,IACN,OAAA,EAAS,gEAAA;AAAA,IACT,KAAA,EAAO,gDAAA;AAAA,IACP,SAAA,EAAW;AAAA;AAEf;AAMO,IAAM,QAAA,GAAW,MAAA,CAAO,IAAA,CAAK,aAAa;AAM1C,IAAM,eAAA,GAA0B;AAGhC,SAAS,SAAS,KAAA,EAAiC;AACxD,EAAA,OAAO,OAAO,UAAU,QAAA,IAAY,MAAA,CAAO,UAAU,cAAA,CAAe,IAAA,CAAK,eAAe,KAAK,CAAA;AAC/F;AAGO,SAAS,YAAY,KAAA,EAAsC;AAChE,EAAA,OAAO,aAAA,CAAc,QAAA,CAAS,KAAK,CAAA,GAAI,QAAQ,eAAe,CAAA;AAChE","file":"typography.js","sourcesContent":["/**\n * The Motir TYPOGRAPHY LIBRARY — the named-pairing registry (Subtask 7.3.53).\n *\n * A \"type pairing\" is a named TYPOGRAPHY scheme the whole UI can swap to at\n * runtime — the THIRD design axis, completing the token decomposition\n * (Colour `data-palette` · Type `data-type` · Shape/feel `data-style`). It is\n * deliberately INDEPENDENT of the other two: a pairing decides which TYPEFACES\n * the product wears (the headline / body / mono role mapping); it never touches\n * a hue (palette axis) or a radius (style axis).\n *\n * ── The three-axis runtime contract ─────────────────────────────────────\n * data-type=\"<type-id>\" — THIS axis. A `[data-type='<id>']` block in\n * app/globals.css re-points the `--font-*`\n * role tokens (sans / serif / mono) — and ONLY\n * font tokens, never colour or shape.\n * data-palette=\"<palette-id>\" — the INDEPENDENT colour axis (`--el-*`).\n * data-style=\"<style-id>\" — the INDEPENDENT shape/feel axis.\n *\n * The shared `--font-size-*` SCALE stays common across pairings — sizes are\n * layout, not brand; a pairing remaps the FACES, not the type scale.\n *\n * ── A style declares a DEFAULT pairing (overridable) ────────────────────\n * Typography used to be BUNDLED into the style axis: `swiss-minimal-flat`,\n * `neo-brutalism` and `cybercore-y2k` each re-pointed `--font-serif` inside\n * their own `[data-style]` block. That is now a disjointness violation (two\n * axes setting `--font-*`). The fix: each style declares a `defaultTypeId`\n * (see ./styles.ts) — the pairing applied when the user has NOT pinned an\n * explicit type — and the per-style font overrides move here, as pairings.\n * So out-of-the-box looks are UNCHANGED; the new freedom is that type is now\n * independently overridable (e.g. Swiss shape + a serif pairing).\n *\n * ── v1 — the base faces only (no new payload) ───────────────────────────\n * v1 registered three pairings built from the THREE already-loaded next/font\n * faces (Inter · Source Serif 4 · JetBrains Mono) — so they add ZERO font\n * payload and carry NO new-typeface design decision:\n * - `motir` — the base: editorial serif headlines over a sans body.\n * - `motir-sans` — all-sans (Inter headlines); reproduces the Swiss look.\n * - `motir-mono` — mono THROUGHOUT (headlines + body/UI); a code-native UI.\n * `motir` is the base — like the `motir` palette / `warm-editorial` style it\n * needs NO `[data-type]` override block; the base `--font-*` tokens already are\n * it.\n *\n * ── New-typeface pairings (Grotesk / Editorial / Mono-Technical, 7.3.54–56) ──\n * Each adds a real type-design decision and loads its own next/font face(s),\n * re-pointing a role in its own `[data-type]` block:\n * - `grotesk` (7.3.54) — Space Grotesk THROUGHOUT (headlines + body/UI).\n * - `editorial` (7.3.55) — Fraunces display-serif headlines over the Inter\n * body; a magazine, considered pairing (one new face — Inter + JetBrains reused).\n * Body STAYS Inter — a display serif is unreadable as UI, so this pairing\n * re-types headlines only (the legibility exception to whole-UI re-typing).\n * - `mono-technical` (7.3.56) — IBM Plex Mono THROUGHOUT (headlines + body/UI +\n * meta); a precise, developer-grade UI (one new face — Inter reused for nothing\n * now, kept as the ultimate fallback).\n *\n * ── Whole-UI re-typing (Yue, 2026-06-19) ────────────────────────────────\n * A type pairing re-types the WHOLE app, chrome included (nav, header, buttons),\n * not just headlines: a pairing whose signature face reads as a UI sans/mono\n * (`grotesk`, `motir-mono`, `mono-technical`) ALSO re-points the body/UI role\n * (`--font-sans`) in its `[data-type]` block. The display-serif `editorial` and\n * the all-Inter `motir` / `motir-sans` keep the Inter body for legibility.\n * Each later \"Type: …\" subtask ADDS its entry here + a `[data-type]` block (its\n * face loaded in `app/layout.tsx`) + its `docs/typography/<id>.md`.\n */\n\n/** A registered, runtime-selectable type pairing. */\nexport interface TypographyDefinition {\n /** The `data-type` attribute value. Stable; used as the localStorage value. */\n id: string;\n /** Display name shown in the gallery / `/tokens` toggle. */\n name: string;\n /** One-line characterization of the typographic feel. */\n tagline: string;\n /** The headline / body / mono face mapping (credit / what it loads). */\n faces: string;\n /**\n * Repo-relative path to this pairing's doc — the type-role reference a later\n * getdesign-style swap or the `/tokens` composer reads. Every registered\n * pairing maps to exactly one.\n */\n designDoc: string;\n}\n\n/**\n * The registry. Insertion order is the gallery order. Each non-base `id`\n * matches a `[data-type='<id>']` block in app/globals.css (`motir` is the base,\n * so its block is the Tier-0 `--font-*` defaults — no override block needed).\n */\nexport const TYPE_REGISTRY = {\n motir: {\n id: 'motir',\n name: 'Motir',\n tagline: 'Editorial serif headlines over a clean sans body — the house pairing.',\n faces: 'Source Serif 4 headlines · Inter body · JetBrains Mono meta.',\n designDoc: 'docs/typography/motir.md',\n },\n 'motir-sans': {\n id: 'motir-sans',\n name: 'Motir Sans',\n tagline: 'All-sans — Inter for headlines and body; structural, no serif.',\n faces: 'Inter headlines + body · JetBrains Mono meta.',\n designDoc: 'docs/typography/motir-sans.md',\n },\n 'motir-mono': {\n id: 'motir-mono',\n name: 'Motir Mono',\n tagline: 'Monospace throughout — a code-native UI, headlines, body and all.',\n faces: 'JetBrains Mono headlines + body/UI + meta.',\n designDoc: 'docs/typography/motir-mono.md',\n },\n grotesk: {\n id: 'grotesk',\n name: 'Grotesk',\n tagline: 'Geometric neo-grotesque across the whole UI — tight, confident, product-y.',\n faces: 'Space Grotesk headlines + body/UI · JetBrains Mono meta.',\n designDoc: 'docs/typography/grotesk.md',\n },\n editorial: {\n id: 'editorial',\n name: 'Editorial',\n tagline: 'A characterful display serif over a humanist sans — magazine, considered.',\n faces: 'Fraunces display headlines · Inter body · JetBrains Mono meta.',\n designDoc: 'docs/typography/editorial.md',\n },\n 'mono-technical': {\n id: 'mono-technical',\n name: 'Mono-Technical',\n tagline: 'IBM Plex Mono throughout — a precise, developer-grade UI.',\n faces: 'IBM Plex Mono headlines + body/UI + meta/code.',\n designDoc: 'docs/typography/mono-technical.md',\n },\n} satisfies Record<string, TypographyDefinition>;\n\n/** The id of every registered pairing — `data-type` value space. */\nexport type TypeId = keyof typeof TYPE_REGISTRY;\n\n/** All registered pairing ids, in gallery order. */\nexport const TYPE_IDS = Object.keys(TYPE_REGISTRY) as TypeId[];\n\n/**\n * The default pairing a fresh install / unset preference resolves to when the\n * active style declares none. Motir is the house pairing and the Tier-0 base.\n */\nexport const DEFAULT_TYPE_ID: TypeId = 'motir';\n\n/** Narrowing guard — is an arbitrary string a registered pairing id? */\nexport function isTypeId(value: unknown): value is TypeId {\n return typeof value === 'string' && Object.prototype.hasOwnProperty.call(TYPE_REGISTRY, value);\n}\n\n/** Resolve a (possibly stale / unknown) value to a valid pairing definition. */\nexport function resolveType(value: unknown): TypographyDefinition {\n return TYPE_REGISTRY[isTypeId(value) ? value : DEFAULT_TYPE_ID];\n}\n"]}
1
+ {"version":3,"sources":["../../src/theme/typography.ts"],"names":[],"mappings":";AAsGO,IAAM,aAAA,GAAgB;AAAA,EAC3B,KAAA,EAAO;AAAA,IACL,EAAA,EAAI,OAAA;AAAA,IACJ,IAAA,EAAM,OAAA;AAAA,IACN,OAAA,EAAS,4EAAA;AAAA,IACT,KAAA,EAAO,oEAAA;AAAA,IACP,SAAA,EAAW;AAAA,GACb;AAAA,EACA,YAAA,EAAc;AAAA,IACZ,EAAA,EAAI,YAAA;AAAA,IACJ,IAAA,EAAM,YAAA;AAAA,IACN,OAAA,EAAS,qEAAA;AAAA,IACT,KAAA,EAAO,kDAAA;AAAA,IACP,SAAA,EAAW;AAAA,GACb;AAAA,EACA,YAAA,EAAc;AAAA,IACZ,EAAA,EAAI,YAAA;AAAA,IACJ,IAAA,EAAM,YAAA;AAAA,IACN,OAAA,EAAS,wEAAA;AAAA,IACT,KAAA,EAAO,4CAAA;AAAA,IACP,SAAA,EAAW;AAAA,GACb;AAAA,EACA,OAAA,EAAS;AAAA,IACP,EAAA,EAAI,SAAA;AAAA,IACJ,IAAA,EAAM,SAAA;AAAA,IACN,OAAA,EAAS,iFAAA;AAAA,IACT,KAAA,EAAO,6DAAA;AAAA,IACP,SAAA,EAAW;AAAA,GACb;AAAA,EACA,SAAA,EAAW;AAAA,IACT,EAAA,EAAI,WAAA;AAAA,IACJ,IAAA,EAAM,WAAA;AAAA,IACN,OAAA,EAAS,gFAAA;AAAA,IACT,KAAA,EAAO,sEAAA;AAAA,IACP,SAAA,EAAW;AAAA,GACb;AAAA,EACA,gBAAA,EAAkB;AAAA,IAChB,EAAA,EAAI,gBAAA;AAAA,IACJ,IAAA,EAAM,gBAAA;AAAA,IACN,OAAA,EAAS,gEAAA;AAAA,IACT,KAAA,EAAO,gDAAA;AAAA,IACP,SAAA,EAAW;AAAA,GACb;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,EAAA,EAAI,QAAA;AAAA,IACJ,IAAA,EAAM,QAAA;AAAA,IACN,OAAA,EAAS,wFAAA;AAAA,IACT,KAAA,EAAO,mEAAA;AAAA,IACP,SAAA,EAAW;AAAA,GACb;AAAA,EACA,OAAA,EAAS;AAAA,IACP,EAAA,EAAI,SAAA;AAAA,IACJ,IAAA,EAAM,SAAA;AAAA,IACN,OAAA,EAAS,2EAAA;AAAA,IACT,KAAA,EAAO,sDAAA;AAAA,IACP,SAAA,EAAW;AAAA,GACb;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,EAAA,EAAI,MAAA;AAAA,IACJ,IAAA,EAAM,MAAA;AAAA,IACN,OAAA,EAAS,kFAAA;AAAA,IACT,KAAA,EAAO,iEAAA;AAAA,IACP,SAAA,EAAW;AAAA,GACb;AAAA,EACA,QAAA,EAAU;AAAA,IACR,EAAA,EAAI,UAAA;AAAA,IACJ,IAAA,EAAM,UAAA;AAAA,IACN,OAAA,EAAS,oEAAA;AAAA,IACT,KAAA,EAAO,yDAAA;AAAA,IACP,SAAA,EAAW;AAAA;AAEf;AAMO,IAAM,QAAA,GAAW,MAAA,CAAO,IAAA,CAAK,aAAa;AAM1C,IAAM,eAAA,GAA0B;AAGhC,SAAS,SAAS,KAAA,EAAiC;AACxD,EAAA,OAAO,OAAO,UAAU,QAAA,IAAY,MAAA,CAAO,UAAU,cAAA,CAAe,IAAA,CAAK,eAAe,KAAK,CAAA;AAC/F;AAGO,SAAS,YAAY,KAAA,EAAsC;AAChE,EAAA,OAAO,aAAA,CAAc,QAAA,CAAS,KAAK,CAAA,GAAI,QAAQ,eAAe,CAAA;AAChE","file":"typography.js","sourcesContent":["/**\n * The Motir TYPOGRAPHY LIBRARY — the named-pairing registry (Subtask 7.3.53).\n *\n * A \"type pairing\" is a named TYPOGRAPHY scheme the whole UI can swap to at\n * runtime — the THIRD design axis, completing the token decomposition\n * (Colour `data-palette` · Type `data-type` · Shape/feel `data-style`). It is\n * deliberately INDEPENDENT of the other two: a pairing decides which TYPEFACES\n * the product wears (the headline / body / mono role mapping); it never touches\n * a hue (palette axis) or a radius (style axis).\n *\n * ── The three-axis runtime contract ─────────────────────────────────────\n * data-type=\"<type-id>\" — THIS axis. A `[data-type='<id>']` block in\n * app/globals.css re-points the `--font-*`\n * role tokens (sans / serif / mono) — and ONLY\n * font tokens, never colour or shape.\n * data-palette=\"<palette-id>\" — the INDEPENDENT colour axis (`--el-*`).\n * data-style=\"<style-id>\" — the INDEPENDENT shape/feel axis.\n *\n * The shared `--font-size-*` SCALE stays common across pairings — sizes are\n * layout, not brand; a pairing remaps the FACES, not the type scale.\n *\n * ── A style declares a DEFAULT pairing (overridable) ────────────────────\n * Typography used to be BUNDLED into the style axis: `swiss-minimal-flat`,\n * `neo-brutalism` and `cybercore-y2k` each re-pointed `--font-serif` inside\n * their own `[data-style]` block. That is now a disjointness violation (two\n * axes setting `--font-*`). The fix: each style declares a `defaultTypeId`\n * (see ./styles.ts) — the pairing applied when the user has NOT pinned an\n * explicit type — and the per-style font overrides move here, as pairings.\n * So out-of-the-box looks are UNCHANGED; the new freedom is that type is now\n * independently overridable (e.g. Swiss shape + a serif pairing).\n *\n * ── v1 — the base faces only (no new payload) ───────────────────────────\n * v1 registered three pairings built from the THREE already-loaded next/font\n * faces (Inter · Source Serif 4 · JetBrains Mono) — so they add ZERO font\n * payload and carry NO new-typeface design decision:\n * - `motir` — the base: editorial serif headlines over a sans body.\n * - `motir-sans` — all-sans (Inter headlines); reproduces the Swiss look.\n * - `motir-mono` — mono THROUGHOUT (headlines + body/UI); a code-native UI.\n * `motir` is the base — like the `motir` palette / `warm-editorial` style it\n * needs NO `[data-type]` override block; the base `--font-*` tokens already are\n * it.\n *\n * ── New-typeface pairings (Grotesk / Editorial / Mono-Technical, 7.3.54–56) ──\n * Each adds a real type-design decision and loads its own next/font face(s),\n * re-pointing a role in its own `[data-type]` block:\n * - `grotesk` (7.3.54) — Space Grotesk THROUGHOUT (headlines + body/UI).\n * - `editorial` (7.3.55) — Fraunces display-serif headlines over the Inter\n * body; a magazine, considered pairing (one new face — Inter + JetBrains reused).\n * Body STAYS Inter — a display serif is unreadable as UI, so this pairing\n * re-types headlines only (the legibility exception to whole-UI re-typing).\n * - `mono-technical` (7.3.56) — IBM Plex Mono THROUGHOUT (headlines + body/UI +\n * meta); a precise, developer-grade UI (one new face — Inter reused for nothing\n * now, kept as the ultimate fallback).\n *\n * ── New-typeface pairings (Story MOTIR-10548) ─────────────────────────────\n * - `marker` (MOTIR-10553) — Caveat hand-lettered marker headlines over the\n * Inter body; Hand-Drawn / Indie's default. Display-only, like `editorial`: a\n * marker hand is unreadable as UI text, so only the headline role is re-typed.\n * - `rounded` (MOTIR-10554) — Nunito THROUGHOUT (headlines + body/UI); a\n * soft, friendly rounded sans that reads cleanly as UI text, so it re-types\n * the whole UI like `grotesk`.\n * - `slab` (MOTIR-10555) — Roboto Slab headlines over the Inter body;\n * sturdy and print-like. Display-only, like `editorial`: heavy slab serifs\n * crowd UI text, so only the headline role is re-typed.\n * - `humanist` (MOTIR-10556) — Fira Sans THROUGHOUT (headlines + body/UI); a\n * warm humanist sans drawn as a screen UI face, so it re-types the whole UI\n * like `grotesk`.\n *\n * ── Whole-UI re-typing (Yue, 2026-06-19) ────────────────────────────────\n * A type pairing re-types the WHOLE app, chrome included (nav, header, buttons),\n * not just headlines: a pairing whose signature face reads as a UI sans/mono\n * (`grotesk`, `motir-mono`, `mono-technical`, `rounded`, `humanist`) ALSO\n * re-points the body/UI role (`--font-sans`) in its `[data-type]` block. The\n * display-serif `editorial` and `slab`, the display-only `marker` and the\n * all-Inter `motir` / `motir-sans` keep the Inter body for legibility.\n * Each later \"Type: …\" subtask ADDS its entry here + a `[data-type]` block (its\n * face loaded in `app/layout.tsx`) + its `docs/typography/<id>.md`.\n */\n\n/** A registered, runtime-selectable type pairing. */\nexport interface TypographyDefinition {\n /** The `data-type` attribute value. Stable; used as the localStorage value. */\n id: string;\n /** Display name shown in the gallery / `/tokens` toggle. */\n name: string;\n /** One-line characterization of the typographic feel. */\n tagline: string;\n /** The headline / body / mono face mapping (credit / what it loads). */\n faces: string;\n /**\n * Repo-relative path to this pairing's doc — the type-role reference a later\n * getdesign-style swap or the `/tokens` composer reads. Every registered\n * pairing maps to exactly one.\n */\n designDoc: string;\n}\n\n/**\n * The registry. Insertion order is the gallery order. Each non-base `id`\n * matches a `[data-type='<id>']` block in app/globals.css (`motir` is the base,\n * so its block is the Tier-0 `--font-*` defaults — no override block needed).\n */\nexport const TYPE_REGISTRY = {\n motir: {\n id: 'motir',\n name: 'Motir',\n tagline: 'Editorial serif headlines over a clean sans body — the house pairing.',\n faces: 'Source Serif 4 headlines · Inter body · JetBrains Mono meta.',\n designDoc: 'docs/typography/motir.md',\n },\n 'motir-sans': {\n id: 'motir-sans',\n name: 'Motir Sans',\n tagline: 'All-sans — Inter for headlines and body; structural, no serif.',\n faces: 'Inter headlines + body · JetBrains Mono meta.',\n designDoc: 'docs/typography/motir-sans.md',\n },\n 'motir-mono': {\n id: 'motir-mono',\n name: 'Motir Mono',\n tagline: 'Monospace throughout — a code-native UI, headlines, body and all.',\n faces: 'JetBrains Mono headlines + body/UI + meta.',\n designDoc: 'docs/typography/motir-mono.md',\n },\n grotesk: {\n id: 'grotesk',\n name: 'Grotesk',\n tagline: 'Geometric neo-grotesque across the whole UI — tight, confident, product-y.',\n faces: 'Space Grotesk headlines + body/UI · JetBrains Mono meta.',\n designDoc: 'docs/typography/grotesk.md',\n },\n editorial: {\n id: 'editorial',\n name: 'Editorial',\n tagline: 'A characterful display serif over a humanist sans — magazine, considered.',\n faces: 'Fraunces display headlines · Inter body · JetBrains Mono meta.',\n designDoc: 'docs/typography/editorial.md',\n },\n 'mono-technical': {\n id: 'mono-technical',\n name: 'Mono-Technical',\n tagline: 'IBM Plex Mono throughout — a precise, developer-grade UI.',\n faces: 'IBM Plex Mono headlines + body/UI + meta/code.',\n designDoc: 'docs/typography/mono-technical.md',\n },\n marker: {\n id: 'marker',\n name: 'Marker',\n tagline: 'Hand-lettered marker headlines over a clean sans body — sketchy, friendly, human.',\n faces: 'Caveat marker headlines · Inter body · JetBrains Mono meta.',\n designDoc: 'docs/typography/marker.md',\n },\n rounded: {\n id: 'rounded',\n name: 'Rounded',\n tagline: 'A soft rounded sans across the whole UI — friendly and approachable.',\n faces: 'Nunito headlines + body/UI · JetBrains Mono meta.',\n designDoc: 'docs/typography/rounded.md',\n },\n slab: {\n id: 'slab',\n name: 'Slab',\n tagline: 'Sturdy slab-serif headlines over a clean sans body — confident, print-like.',\n faces: 'Roboto Slab headlines · Inter body · JetBrains Mono meta.',\n designDoc: 'docs/typography/slab.md',\n },\n humanist: {\n id: 'humanist',\n name: 'Humanist',\n tagline: 'A warm humanist sans across the whole UI — open and readable.',\n faces: 'Fira Sans headlines + body/UI · JetBrains Mono meta.',\n designDoc: 'docs/typography/humanist.md',\n },\n} satisfies Record<string, TypographyDefinition>;\n\n/** The id of every registered pairing — `data-type` value space. */\nexport type TypeId = keyof typeof TYPE_REGISTRY;\n\n/** All registered pairing ids, in gallery order. */\nexport const TYPE_IDS = Object.keys(TYPE_REGISTRY) as TypeId[];\n\n/**\n * The default pairing a fresh install / unset preference resolves to when the\n * active style declares none. Motir is the house pairing and the Tier-0 base.\n */\nexport const DEFAULT_TYPE_ID: TypeId = 'motir';\n\n/** Narrowing guard — is an arbitrary string a registered pairing id? */\nexport function isTypeId(value: unknown): value is TypeId {\n return typeof value === 'string' && Object.prototype.hasOwnProperty.call(TYPE_REGISTRY, value);\n}\n\n/** Resolve a (possibly stale / unknown) value to a valid pairing definition. */\nexport function resolveType(value: unknown): TypographyDefinition {\n return TYPE_REGISTRY[isTypeId(value) ? value : DEFAULT_TYPE_ID];\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@motir/design-system",
3
- "version": "0.20.0",
3
+ "version": "0.21.1",
4
4
  "description": "Motir's 3-axis design system (colour · style · type): distributable token CSS, the axis registries, the theme-apply API, and framework-agnostic React primitives. Shared by motir-core and planner-scaffolded products.",
5
5
  "license": "GPL-3.0-only",
6
6
  "repository": {
package/theme.css CHANGED
@@ -189,6 +189,12 @@
189
189
  --shadow-elevated: 0 8px 24px rgba(15, 15, 15, 0.12);
190
190
  --shadow-modal: 0 16px 48px -8px rgba(15, 15, 15, 0.16);
191
191
  --shadow-hero-mockup: 0 24px 48px -8px rgba(15, 15, 15, 0.2);
192
+ /* How far the SOLID edge of `--shadow-elevated` falls below the box that casts
193
+ it. A soft, blurred lift has none (0); a hard-offset style sets its Y offset.
194
+ A control hung over other content (the canvas card's next-action button,
195
+ MOTIR-10631) lifts itself by this much more, so its shadow clears what it
196
+ floats above as its box does. */
197
+ --spacing-shadow-elevated-drop: 0px;
192
198
 
193
199
  /* ── Spacing ────────────────────────────────────────────────────── */
194
200
  --spacing-xxs: 4px;
@@ -481,6 +487,7 @@
481
487
  --shadow-subtle: 0 1px 2px rgba(15, 15, 15, 0.04);
482
488
  --shadow-card: 0 4px 12px rgba(15, 15, 15, 0.08);
483
489
  --shadow-elevated: 0 8px 24px rgba(15, 15, 15, 0.12);
490
+ --spacing-shadow-elevated-drop: 0px;
484
491
  --shadow-modal: 0 16px 48px -8px rgba(15, 15, 15, 0.16);
485
492
  --shadow-hero-mockup: 0 24px 48px -8px rgba(15, 15, 15, 0.2);
486
493
  --spacing-xxs: 4px;
@@ -540,6 +547,7 @@
540
547
  /* Softer, more diffused shadows */
541
548
  --shadow-card: 0 2px 8px rgba(15, 15, 15, 0.06);
542
549
  --shadow-elevated: 0 6px 20px rgba(15, 15, 15, 0.1);
550
+ --spacing-shadow-elevated-drop: 0px;
543
551
  --shadow-modal: 0 12px 40px rgba(15, 15, 15, 0.15);
544
552
 
545
553
  /* Roomier spacing */
@@ -598,6 +606,7 @@
598
606
  --shadow-subtle: none;
599
607
  --shadow-card: none;
600
608
  --shadow-elevated: none;
609
+ --spacing-shadow-elevated-drop: 0px;
601
610
  --shadow-modal: none;
602
611
  --shadow-hero-mockup: none;
603
612
 
@@ -665,6 +674,7 @@
665
674
  --shadow-subtle: 0 1px 2px rgba(15, 15, 15, 0.05);
666
675
  --shadow-card: 0 8px 32px -8px rgba(15, 15, 15, 0.18);
667
676
  --shadow-elevated: 0 16px 48px -12px rgba(15, 15, 15, 0.24);
677
+ --spacing-shadow-elevated-drop: 0px;
668
678
  --shadow-modal: 0 28px 70px -16px rgba(15, 15, 15, 0.34);
669
679
  --shadow-hero-mockup: 0 32px 80px -16px rgba(15, 15, 15, 0.38);
670
680
 
@@ -897,6 +907,7 @@
897
907
  --shadow-subtle: 2px 2px 0 0 rgba(15, 15, 15, 1);
898
908
  --shadow-card: 4px 4px 0 0 rgba(15, 15, 15, 1);
899
909
  --shadow-elevated: 6px 6px 0 0 rgba(15, 15, 15, 1);
910
+ --spacing-shadow-elevated-drop: 6px; /* the Y offset of --shadow-elevated above */
900
911
  --shadow-modal: 8px 8px 0 0 rgba(15, 15, 15, 1);
901
912
  --shadow-hero-mockup: 12px 12px 0 0 rgba(15, 15, 15, 1);
902
913
 
@@ -985,6 +996,7 @@
985
996
  --shadow-elevated:
986
997
  0 0 0 1px color-mix(in srgb, currentColor 16%, transparent),
987
998
  0 0 22px -4px color-mix(in srgb, currentColor 32%, transparent);
999
+ --spacing-shadow-elevated-drop: 0px;
988
1000
  --shadow-modal:
989
1001
  0 0 0 1px color-mix(in srgb, currentColor 20%, transparent),
990
1002
  0 0 40px -6px color-mix(in srgb, currentColor 40%, transparent);
@@ -1081,6 +1093,7 @@
1081
1093
  --shadow-subtle: 0 1px 2px rgba(20, 20, 45, 0.05);
1082
1094
  --shadow-card: 0 8px 30px -12px rgba(20, 20, 45, 0.16);
1083
1095
  --shadow-elevated: 0 16px 44px -14px rgba(20, 20, 45, 0.22);
1096
+ --spacing-shadow-elevated-drop: 0px;
1084
1097
  --shadow-modal: 0 26px 64px -18px rgba(20, 20, 45, 0.3);
1085
1098
  --shadow-hero-mockup: 0 32px 78px -18px rgba(20, 20, 45, 0.34);
1086
1099
 
@@ -1289,6 +1302,7 @@
1289
1302
  --shadow-elevated:
1290
1303
  inset 0 1px 0 0 rgba(255, 255, 255, 0.65), 0 4px 8px rgba(15, 15, 15, 0.06),
1291
1304
  0 20px 40px -8px rgba(15, 15, 15, 0.22), 0 44px 80px -16px rgba(15, 15, 15, 0.38);
1305
+ --spacing-shadow-elevated-drop: 0px;
1292
1306
  --shadow-modal:
1293
1307
  inset 0 1px 0 0 rgba(255, 255, 255, 0.7), 0 8px 16px rgba(15, 15, 15, 0.08),
1294
1308
  0 32px 64px -12px rgba(15, 15, 15, 0.28), 0 72px 120px -24px rgba(15, 15, 15, 0.46);
@@ -1994,7 +2008,7 @@
1994
2008
  hover/press bounce. Colour stays the palette axis: the token block sets no
1995
2009
  `--color-` / `--el-` colour token (AA preserved), and the rough-border rule
1996
2010
  takes its ink from the palette (var(--el-border-strong)). Out-of-the-box type
1997
- is the characterful Editorial (Fraunces) pairing via `defaultTypeId`. See
2011
+ is the hand-lettered Marker (Caveat) pairing via `defaultTypeId`. See
1998
2012
  docs/styles/hand-drawn-indie.md. */
1999
2013
  [data-style='hand-drawn-indie'] {
2000
2014
  /* Silhouette — WONKY hand-drawn corners. The slash splits horizontal /
@@ -2019,6 +2033,7 @@
2019
2033
  --shadow-subtle: 1px 2px 4px -1px rgba(38, 30, 22, 0.1);
2020
2034
  --shadow-card: 2px 4px 9px -3px rgba(38, 30, 22, 0.14);
2021
2035
  --shadow-elevated: 3px 6px 16px -4px rgba(38, 30, 22, 0.17);
2036
+ --spacing-shadow-elevated-drop: 0px;
2022
2037
  --shadow-modal: 5px 9px 30px -8px rgba(38, 30, 22, 0.22);
2023
2038
  --shadow-hero-mockup: 6px 12px 40px -10px rgba(38, 30, 22, 0.22);
2024
2039
 
@@ -2240,6 +2255,7 @@
2240
2255
  --shadow-subtle: 0 1px 2px rgba(20, 20, 30, 0.05);
2241
2256
  --shadow-card: 0 6px 18px -10px rgba(20, 20, 30, 0.14);
2242
2257
  --shadow-elevated: 0 12px 30px -14px rgba(20, 20, 30, 0.18);
2258
+ --spacing-shadow-elevated-drop: 0px;
2243
2259
  --shadow-modal: 0 22px 50px -18px rgba(20, 20, 30, 0.24);
2244
2260
  --shadow-hero-mockup: 0 26px 60px -18px rgba(20, 20, 30, 0.28);
2245
2261
 
@@ -2436,6 +2452,7 @@
2436
2452
  --shadow-subtle: 0 1px 2px rgba(10, 12, 30, 0.06);
2437
2453
  --shadow-card: 0 10px 26px -12px rgba(10, 12, 30, 0.22);
2438
2454
  --shadow-elevated: 0 18px 40px -14px rgba(10, 12, 30, 0.28);
2455
+ --spacing-shadow-elevated-drop: 0px;
2439
2456
  --shadow-modal: 0 28px 60px -18px rgba(10, 12, 30, 0.34);
2440
2457
  --shadow-hero-mockup: 0 34px 74px -18px rgba(10, 12, 30, 0.4);
2441
2458
 
@@ -6224,6 +6241,102 @@
6224
6241
  --font-sans-tail: monospace;
6225
6242
  }
6226
6243
 
6244
+ [data-type='marker'] {
6245
+ /* Marker (MOTIR-10553) — hand-lettered marker headlines over the Inter body.
6246
+ Adds one new face: Caveat, loaded in app/fonts.ts as --font-marker-source.
6247
+ Re-point the headline (serif) role at it; body (Inter, --font-sans) and meta
6248
+ (JetBrains, --font-mono) keep the base roles. Display-only, like editorial:
6249
+ a marker hand is unreadable as UI or body text, so only headlines re-type.
6250
+ The pairing chain names no generic keyword (the FONT SETS rule below slots
6251
+ the CJK face between it and the `cursive` tail). ONLY --font-* role tokens
6252
+ here (the disjointness guard): no colour (--el/--color) or shape
6253
+ (--radius/--spacing/--shadow) token. */
6254
+ --font-serif-pairing: var(--font-marker-source), 'Caveat';
6255
+ --font-serif-tail: cursive;
6256
+ /* Re-asserted at the BASE value, not omitted (MOTIR-3933). An omitted role
6257
+ inherits the ANCESTOR's, which is invisible on `<html>` and wrong in a
6258
+ nested scope: this pairing under a mono ancestor rendered mono. Unlike the
6259
+ colour axis there is no `[data-appearance-scope]` layer to fall back
6260
+ through — `--font-*` are role tokens, not `--el-*` — so every
6261
+ `[data-type]` block must be TOTAL over the three roles. */
6262
+ --font-sans-pairing: var(--font-sans-source, 'Segoe UI'), 'Segoe UI', Roboto;
6263
+ --font-sans-tail: -apple-system, BlinkMacSystemFont, sans-serif;
6264
+ --font-mono-pairing: var(--font-mono-source, 'SF Mono'), 'SF Mono', Menlo;
6265
+ --font-mono-tail: monospace;
6266
+ }
6267
+
6268
+ [data-type='rounded'] {
6269
+ /* Rounded (MOTIR-10554) — a soft rounded sans across headlines AND body/UI.
6270
+ Re-point BOTH the headline (serif) role AND the body/UI (sans) role at
6271
+ Nunito (loaded in app/fonts.ts as --font-rounded-source), so the WHOLE app
6272
+ (nav, header, buttons, content) re-types — Nunito reads cleanly as a UI sans
6273
+ (Yue, 2026-06-19: the type axis must re-type the whole UI, chrome included).
6274
+ Meta stays JetBrains Mono (--font-mono). The pairing chains name no generic
6275
+ keyword, so the FONT SETS rule below slots the CJK face behind Nunito. ONLY
6276
+ font tokens here (the disjointness guard): no colour (--el / --color) or
6277
+ shape token. */
6278
+ --font-serif-pairing: var(--font-rounded-source), 'Nunito';
6279
+ --font-serif-tail: ui-sans-serif, system-ui, sans-serif;
6280
+ --font-sans-pairing: var(--font-rounded-source), 'Nunito';
6281
+ --font-sans-tail: ui-sans-serif, system-ui, sans-serif;
6282
+ /* Re-asserted at the BASE value, not omitted (MOTIR-3933). An omitted role
6283
+ inherits the ANCESTOR's, which is invisible on `<html>` and wrong in a
6284
+ nested scope: this pairing under a mono ancestor rendered mono. Unlike the
6285
+ colour axis there is no `[data-appearance-scope]` layer to fall back
6286
+ through — `--font-*` are role tokens, not `--el-*` — so every
6287
+ `[data-type]` block must be TOTAL over the three roles. */
6288
+ --font-mono-pairing: var(--font-mono-source, 'SF Mono'), 'SF Mono', Menlo;
6289
+ --font-mono-tail: monospace;
6290
+ }
6291
+
6292
+ [data-type='slab'] {
6293
+ /* Slab (MOTIR-10555) — sturdy slab-serif headlines over the Inter body. Adds
6294
+ one new face: Roboto Slab, loaded in app/fonts.ts as --font-slab-source.
6295
+ Re-point the headline (serif) role at it; body (Inter, --font-sans) and meta
6296
+ (JetBrains, --font-mono) keep the base roles. Display-only, like editorial:
6297
+ heavy rectangular serifs crowd dense UI text at 12–14px, so only headlines
6298
+ re-type. The pairing chain names no generic keyword (the FONT SETS rule
6299
+ below slots the CJK face between it and the `serif` tail). ONLY --font-*
6300
+ role tokens here (the disjointness guard): no colour (--el/--color) or shape
6301
+ (--radius/--spacing/--shadow) token. */
6302
+ --font-serif-pairing: var(--font-slab-source), 'Roboto Slab', Rockwell;
6303
+ --font-serif-tail: serif;
6304
+ /* Re-asserted at the BASE value, not omitted (MOTIR-3933). An omitted role
6305
+ inherits the ANCESTOR's, which is invisible on `<html>` and wrong in a
6306
+ nested scope: this pairing under a mono ancestor rendered mono. Unlike the
6307
+ colour axis there is no `[data-appearance-scope]` layer to fall back
6308
+ through — `--font-*` are role tokens, not `--el-*` — so every
6309
+ `[data-type]` block must be TOTAL over the three roles. */
6310
+ --font-sans-pairing: var(--font-sans-source, 'Segoe UI'), 'Segoe UI', Roboto;
6311
+ --font-sans-tail: -apple-system, BlinkMacSystemFont, sans-serif;
6312
+ --font-mono-pairing: var(--font-mono-source, 'SF Mono'), 'SF Mono', Menlo;
6313
+ --font-mono-tail: monospace;
6314
+ }
6315
+
6316
+ [data-type='humanist'] {
6317
+ /* Humanist (MOTIR-10556) — a warm humanist sans across headlines AND body/UI.
6318
+ Re-point BOTH the headline (serif) role AND the body/UI (sans) role at Fira
6319
+ Sans (loaded in app/fonts.ts as --font-humanist-source), so the WHOLE app
6320
+ (nav, header, buttons, content) re-types — Fira Sans was drawn as a screen UI
6321
+ face (Yue, 2026-06-19: the type axis must re-type the whole UI, chrome
6322
+ included). Meta stays JetBrains Mono (--font-mono). The pairing chains name
6323
+ no generic keyword, so the FONT SETS rule below slots the CJK face behind
6324
+ Fira Sans. ONLY font tokens here (the disjointness guard): no colour
6325
+ (--el / --color) or shape token. */
6326
+ --font-serif-pairing: var(--font-humanist-source), 'Fira Sans';
6327
+ --font-serif-tail: ui-sans-serif, system-ui, sans-serif;
6328
+ --font-sans-pairing: var(--font-humanist-source), 'Fira Sans';
6329
+ --font-sans-tail: ui-sans-serif, system-ui, sans-serif;
6330
+ /* Re-asserted at the BASE value, not omitted (MOTIR-3933). An omitted role
6331
+ inherits the ANCESTOR's, which is invisible on `<html>` and wrong in a
6332
+ nested scope: this pairing under a mono ancestor rendered mono. Unlike the
6333
+ colour axis there is no `[data-appearance-scope]` layer to fall back
6334
+ through — `--font-*` are role tokens, not `--el-*` — so every
6335
+ `[data-type]` block must be TOTAL over the three roles. */
6336
+ --font-mono-pairing: var(--font-mono-source, 'SF Mono'), 'SF Mono', Menlo;
6337
+ --font-mono-tail: monospace;
6338
+ }
6339
+
6227
6340
  /* ═══════════════════════════════════════════════════════════════════════
6228
6341
  FONT SETS — the script face behind the pairing (Story MOTIR-7733 · MOTIR-7845)
6229
6342
  A Type pairing (the [data-type] blocks above) decides each role's LATIN