@motir/design-system 0.8.2 → 0.10.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.
@@ -1,60 +1,11 @@
1
1
  'use client';
2
- import { createContext, forwardRef, useId, useState, useSyncExternalStore, useEffect, useRef, useCallback, useMemo } from 'react';
3
- import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { forwardRef, useId } from 'react';
4
3
  import { cva } from 'class-variance-authority';
5
4
  import { clsx } from 'clsx';
6
5
  import { twMerge } from 'tailwind-merge';
6
+ import { jsx, jsxs } from 'react/jsx-runtime';
7
7
  import { BookOpen, Search, SlidersHorizontal } from 'lucide-react';
8
8
 
9
- // src/theme/typography.ts
10
- var TYPE_REGISTRY = {
11
- motir: {
12
- id: "motir",
13
- name: "Motir",
14
- tagline: "Editorial serif headlines over a clean sans body \u2014 the house pairing.",
15
- faces: "Source Serif 4 headlines \xB7 Inter body \xB7 JetBrains Mono meta.",
16
- designDoc: "docs/typography/motir.md"
17
- },
18
- "motir-sans": {
19
- id: "motir-sans",
20
- name: "Motir Sans",
21
- tagline: "All-sans \u2014 Inter for headlines and body; structural, no serif.",
22
- faces: "Inter headlines + body \xB7 JetBrains Mono meta.",
23
- designDoc: "docs/typography/motir-sans.md"
24
- },
25
- "motir-mono": {
26
- id: "motir-mono",
27
- name: "Motir Mono",
28
- tagline: "Monospace throughout \u2014 a code-native UI, headlines, body and all.",
29
- faces: "JetBrains Mono headlines + body/UI + meta.",
30
- designDoc: "docs/typography/motir-mono.md"
31
- },
32
- grotesk: {
33
- id: "grotesk",
34
- name: "Grotesk",
35
- tagline: "Geometric neo-grotesque across the whole UI \u2014 tight, confident, product-y.",
36
- faces: "Space Grotesk headlines + body/UI \xB7 JetBrains Mono meta.",
37
- designDoc: "docs/typography/grotesk.md"
38
- },
39
- editorial: {
40
- id: "editorial",
41
- name: "Editorial",
42
- tagline: "A characterful display serif over a humanist sans \u2014 magazine, considered.",
43
- faces: "Fraunces display headlines \xB7 Inter body \xB7 JetBrains Mono meta.",
44
- designDoc: "docs/typography/editorial.md"
45
- },
46
- "mono-technical": {
47
- id: "mono-technical",
48
- name: "Mono-Technical",
49
- tagline: "IBM Plex Mono throughout \u2014 a precise, developer-grade UI.",
50
- faces: "IBM Plex Mono headlines + body/UI + meta/code.",
51
- designDoc: "docs/typography/mono-technical.md"
52
- }
53
- };
54
- function isTypeId(value) {
55
- return typeof value === "string" && Object.prototype.hasOwnProperty.call(TYPE_REGISTRY, value);
56
- }
57
-
58
9
  // src/theme/styles.ts
59
10
  var STYLE_REGISTRY = {
60
11
  "warm-editorial": {
@@ -257,13 +208,6 @@ var STYLE_REGISTRY = {
257
208
  }
258
209
  };
259
210
  var STYLE_IDS = Object.keys(STYLE_REGISTRY);
260
- var DEFAULT_STYLE_ID = "warm-editorial";
261
- function isStyleId(value) {
262
- return typeof value === "string" && Object.prototype.hasOwnProperty.call(STYLE_REGISTRY, value);
263
- }
264
- function resolveStyle(value) {
265
- return STYLE_REGISTRY[isStyleId(value) ? value : DEFAULT_STYLE_ID];
266
- }
267
211
  Object.fromEntries(
268
212
  STYLE_IDS.map((id) => [id, STYLE_REGISTRY[id].defaultTypeId])
269
213
  );
@@ -342,234 +286,6 @@ var PALETTE_REGISTRY = {
342
286
  }
343
287
  };
344
288
  var PALETTE_IDS = Object.keys(PALETTE_REGISTRY);
345
- var DEFAULT_PALETTE_ID = "motir";
346
- function isPaletteId(value) {
347
- return typeof value === "string" && Object.prototype.hasOwnProperty.call(PALETTE_REGISTRY, value);
348
- }
349
-
350
- // src/theme/types.ts
351
- var THEME_STORAGE_KEYS = {
352
- pattern: "motir.theme.pattern",
353
- style: "motir.theme.style",
354
- palette: "motir.theme.palette",
355
- type: "motir.theme.type",
356
- /**
357
- * The version marker of the STORED palette id's meaning. Absent (or anything
358
- * but {@link PALETTE_IDS_VERSION}) means the stored `palette` was written
359
- * before MOTIR-6471 swapped the palette identities, so it is read through
360
- * {@link PALETTE_ID_MIGRATION} once and the marker is then written.
361
- */
362
- paletteIds: "motir.theme.paletteIds"
363
- };
364
- var PALETTE_IDS_VERSION = "2";
365
- var PALETTE_ID_MIGRATION = {
366
- motir: "amethyst",
367
- graphite: "motir"
368
- };
369
- function migrateStoredPaletteId(value, marker) {
370
- if (value === null || marker === PALETTE_IDS_VERSION) return value;
371
- if (!Object.prototype.hasOwnProperty.call(PALETTE_ID_MIGRATION, value)) return value;
372
- return PALETTE_ID_MIGRATION[value] ?? value;
373
- }
374
- var THEME_DEFAULTS = {
375
- pattern: "system",
376
- style: DEFAULT_STYLE_ID};
377
- var SYNC_DEBOUNCE_MS = 250;
378
- var ThemeContext = createContext(null);
379
- function readStorage(key, fallback) {
380
- if (typeof window === "undefined") return fallback;
381
- try {
382
- return window.localStorage.getItem(key) ?? fallback;
383
- } catch {
384
- return fallback;
385
- }
386
- }
387
- function subscribeColorScheme(callback) {
388
- if (typeof window === "undefined") return () => {
389
- };
390
- const mql = window.matchMedia("(prefers-color-scheme: dark)");
391
- mql.addEventListener("change", callback);
392
- return () => mql.removeEventListener("change", callback);
393
- }
394
- function getColorSchemeSnapshot() {
395
- if (typeof window === "undefined") return "light";
396
- return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
397
- }
398
- function getColorSchemeServerSnapshot() {
399
- return "light";
400
- }
401
- function ThemeProvider({
402
- children,
403
- initialPreference = null,
404
- signedIn = false
405
- }) {
406
- const [pattern, setPatternState] = useState(
407
- () => initialPreference ? initialPreference.pattern : readStorage(THEME_STORAGE_KEYS.pattern, THEME_DEFAULTS.pattern)
408
- );
409
- const [styleId, setStyleIdState] = useState(() => {
410
- if (initialPreference) {
411
- return isStyleId(initialPreference.styleId) ? initialPreference.styleId : DEFAULT_STYLE_ID;
412
- }
413
- const stored = readStorage(THEME_STORAGE_KEYS.style, THEME_DEFAULTS.style);
414
- return isStyleId(stored) ? stored : DEFAULT_STYLE_ID;
415
- });
416
- const [palette, setPaletteState] = useState(() => {
417
- if (initialPreference) {
418
- return isPaletteId(initialPreference.paletteId) ? initialPreference.paletteId : DEFAULT_PALETTE_ID;
419
- }
420
- const stored = migrateStoredPaletteId(
421
- readStorage(THEME_STORAGE_KEYS.palette, "") || null,
422
- readStorage(THEME_STORAGE_KEYS.paletteIds, "") || null
423
- );
424
- return isPaletteId(stored) ? stored : DEFAULT_PALETTE_ID;
425
- });
426
- const [typeChoice, setTypeChoiceState] = useState(() => {
427
- if (initialPreference) {
428
- return initialPreference.typePinned && isTypeId(initialPreference.typeId) ? initialPreference.typeId : null;
429
- }
430
- const stored = readStorage(THEME_STORAGE_KEYS.type, "");
431
- return isTypeId(stored) ? stored : null;
432
- });
433
- const osColorScheme = useSyncExternalStore(
434
- subscribeColorScheme,
435
- getColorSchemeSnapshot,
436
- getColorSchemeServerSnapshot
437
- );
438
- const resolvedPattern = pattern === "system" ? osColorScheme : pattern;
439
- const type = typeChoice ?? resolveStyle(styleId).defaultTypeId;
440
- useEffect(() => {
441
- document.documentElement.setAttribute("data-theme", resolvedPattern);
442
- }, [resolvedPattern]);
443
- useEffect(() => {
444
- document.documentElement.setAttribute("data-style", styleId);
445
- }, [styleId]);
446
- useEffect(() => {
447
- document.documentElement.setAttribute("data-palette", palette);
448
- }, [palette]);
449
- useEffect(() => {
450
- document.documentElement.setAttribute("data-type", type);
451
- }, [type]);
452
- const [syncState, setSyncState] = useState("idle");
453
- const syncSeqRef = useRef(0);
454
- const pendingPatchRef = useRef({});
455
- const debounceRef = useRef(null);
456
- const reconcile = useCallback((sent, resolved) => {
457
- if ("pattern" in sent) setPatternState(resolved.pattern);
458
- if ("styleId" in sent && isStyleId(resolved.styleId)) setStyleIdState(resolved.styleId);
459
- if ("paletteId" in sent && isPaletteId(resolved.paletteId)) setPaletteState(resolved.paletteId);
460
- if ("typeId" in sent && isTypeId(resolved.typeId)) setTypeChoiceState(resolved.typeId);
461
- }, []);
462
- const flushSync = useCallback(() => {
463
- debounceRef.current = null;
464
- const patch = pendingPatchRef.current;
465
- pendingPatchRef.current = {};
466
- if (Object.keys(patch).length === 0) return;
467
- const seq = ++syncSeqRef.current;
468
- fetch("/api/appearance-preference", {
469
- method: "PATCH",
470
- headers: { "Content-Type": "application/json" },
471
- body: JSON.stringify(patch)
472
- }).then(async (res) => {
473
- if (!res.ok) throw new Error(`appearance sync failed: ${res.status}`);
474
- const body = await res.json();
475
- if (seq !== syncSeqRef.current) return;
476
- reconcile(patch, body.preference);
477
- setSyncState("idle");
478
- }).catch(() => {
479
- if (seq !== syncSeqRef.current) return;
480
- setSyncState("error");
481
- });
482
- }, [reconcile]);
483
- const queueSync = useCallback(
484
- (patch) => {
485
- if (!signedIn) return;
486
- pendingPatchRef.current = { ...pendingPatchRef.current, ...patch };
487
- if (debounceRef.current) clearTimeout(debounceRef.current);
488
- debounceRef.current = setTimeout(flushSync, SYNC_DEBOUNCE_MS);
489
- },
490
- [signedIn, flushSync]
491
- );
492
- useEffect(() => {
493
- return () => {
494
- if (debounceRef.current) {
495
- clearTimeout(debounceRef.current);
496
- flushSync();
497
- }
498
- };
499
- }, [flushSync]);
500
- const setPattern = useCallback(
501
- (next) => {
502
- setPatternState(next);
503
- try {
504
- window.localStorage.setItem(THEME_STORAGE_KEYS.pattern, next);
505
- } catch {
506
- }
507
- queueSync({ pattern: next });
508
- },
509
- [queueSync]
510
- );
511
- const setStyleId = useCallback(
512
- (next) => {
513
- setStyleIdState(next);
514
- try {
515
- window.localStorage.setItem(THEME_STORAGE_KEYS.style, next);
516
- } catch {
517
- }
518
- queueSync({ styleId: next });
519
- },
520
- [queueSync]
521
- );
522
- const setPalette = useCallback(
523
- (next) => {
524
- setPaletteState(next);
525
- try {
526
- window.localStorage.setItem(THEME_STORAGE_KEYS.palette, next);
527
- window.localStorage.setItem(THEME_STORAGE_KEYS.paletteIds, PALETTE_IDS_VERSION);
528
- } catch {
529
- }
530
- queueSync({ paletteId: next });
531
- },
532
- [queueSync]
533
- );
534
- const setType = useCallback(
535
- (next) => {
536
- setTypeChoiceState(next);
537
- try {
538
- window.localStorage.setItem(THEME_STORAGE_KEYS.type, next);
539
- } catch {
540
- }
541
- queueSync({ typeId: next });
542
- },
543
- [queueSync]
544
- );
545
- const value = useMemo(
546
- () => ({
547
- pattern,
548
- resolvedPattern,
549
- styleId,
550
- palette,
551
- type,
552
- setPattern,
553
- setStyleId,
554
- setPalette,
555
- setType,
556
- syncState
557
- }),
558
- [
559
- pattern,
560
- resolvedPattern,
561
- styleId,
562
- palette,
563
- type,
564
- setPattern,
565
- setStyleId,
566
- setPalette,
567
- setType,
568
- syncState
569
- ]
570
- );
571
- return /* @__PURE__ */ jsx(ThemeContext.Provider, { value, children });
572
- }
573
289
  function cn(...inputs) {
574
290
  return twMerge(clsx(inputs));
575
291
  }
@@ -1124,7 +840,7 @@ function Swatches() {
1124
840
  ] }, token)) });
1125
841
  }
1126
842
  function TokensSpecimen({ className }) {
1127
- return /* @__PURE__ */ jsx(ThemeProvider, { children: /* @__PURE__ */ jsx("div", { className, "data-surface": "page", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto flex max-w-[64rem] flex-col gap-8 p-8", children: [
843
+ return /* @__PURE__ */ jsx("div", { className, "data-surface": "page", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto flex max-w-[64rem] flex-col gap-8 p-8", children: [
1128
844
  /* @__PURE__ */ jsxs("header", { className: "flex flex-col gap-1", children: [
1129
845
  /* @__PURE__ */ jsx("h1", { className: "font-serif text-2xl font-semibold text-(--el-text)", children: "@motir/design-system" }),
1130
846
  /* @__PURE__ */ jsx("p", { className: "text-sm text-(--el-text-muted)", children: "Colour \xB7 Style \xB7 Type \u2014 the extracted 3-axis system, rendered in isolation." })
@@ -1154,7 +870,7 @@ function TokensSpecimen({ className }) {
1154
870
  /* @__PURE__ */ jsx(SectionLabel, { children: "Palette axis (data-palette)" }),
1155
871
  /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", children: PALETTE_IDS.map((palette) => /* @__PURE__ */ jsx(StyleVignette, { palette, label: `Palette: ${palette}` }, palette)) })
1156
872
  ] })
1157
- ] }) }) });
873
+ ] }) });
1158
874
  }
1159
875
  var TokensSpecimen_default = TokensSpecimen;
1160
876