@nebutra/theme 2.0.0 → 3.0.0

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # @nebutra/theme
2
2
 
3
+ ## 3.0.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies []:
8
+ - @nebutra/tokens@3.0.0
9
+
3
10
  ## 2.0.0
4
11
 
5
12
  ### Patch Changes
@@ -12,8 +12,8 @@ var skin_fonts_generated_default = {
12
12
  "var(--font-reg-fraunces)",
13
13
  "Fraunces",
14
14
  "GT Super",
15
- "var(--font-noto-sans-sc",
16
- 'Noto Sans SC")',
15
+ "var(--font-misans",
16
+ 'MiSans")',
17
17
  "PingFang SC",
18
18
  "var(--font-jetbrains-mono)",
19
19
  "JetBrains Mono"
@@ -23,16 +23,16 @@ var skin_fonts_generated_default = {
23
23
  "Mori",
24
24
  "Inter Tight",
25
25
  "DM Sans",
26
- "var(--font-noto-sans-sc",
27
- 'Noto Sans SC")',
26
+ "var(--font-misans",
27
+ 'MiSans")',
28
28
  "PingFang SC"
29
29
  ],
30
30
  linear: [
31
31
  "var(--font-inter)",
32
32
  "Inter Variable",
33
33
  "Inter",
34
- "var(--font-noto-sans-sc",
35
- 'Noto Sans SC")',
34
+ "var(--font-misans",
35
+ 'MiSans")',
36
36
  "PingFang SC",
37
37
  "var(--font-jetbrains-mono)",
38
38
  "Berkeley Mono",
@@ -42,15 +42,15 @@ var skin_fonts_generated_default = {
42
42
  "var(--font-inter)",
43
43
  "NotionInter",
44
44
  "Inter",
45
- "var(--font-noto-sans-sc",
46
- 'Noto Sans SC")',
45
+ "var(--font-misans",
46
+ 'MiSans")',
47
47
  "PingFang SC"
48
48
  ],
49
49
  raycast: [
50
50
  "var(--font-inter)",
51
51
  "Inter",
52
- "var(--font-noto-sans-sc",
53
- 'Noto Sans SC")',
52
+ "var(--font-misans",
53
+ 'MiSans")',
54
54
  "PingFang SC",
55
55
  "var(--font-geist-mono)",
56
56
  "Geist Mono",
@@ -62,16 +62,16 @@ var skin_fonts_generated_default = {
62
62
  "sohne-var",
63
63
  "Inter Tight",
64
64
  "Inter",
65
- "var(--font-noto-sans-sc",
66
- 'Noto Sans SC")',
65
+ "var(--font-misans",
66
+ 'MiSans")',
67
67
  "PingFang SC"
68
68
  ],
69
69
  vanta: [
70
70
  "var(--font-inter)",
71
71
  "Inter Variable",
72
72
  "Inter",
73
- "var(--font-noto-sans-sc",
74
- 'Noto Sans SC")',
73
+ "var(--font-misans",
74
+ 'MiSans")',
75
75
  "PingFang SC",
76
76
  "var(--font-reg-source-serif-4)",
77
77
  "Reckless",
@@ -82,8 +82,8 @@ var skin_fonts_generated_default = {
82
82
  "var(--font-geist-sans)",
83
83
  "Geist Sans",
84
84
  "Geist",
85
- "var(--font-noto-sans-sc",
86
- 'Noto Sans SC")',
85
+ "var(--font-misans",
86
+ 'MiSans")',
87
87
  "PingFang SC",
88
88
  "var(--font-geist-mono)",
89
89
  "Geist Mono",
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/language-switcher.tsx","../src/skin-fonts.generated.json"],"sourcesContent":["\"use client\";\n\n/**\n * The control that switches design language, shared by every surface that has one.\n *\n * This was built inside the design site, where it was the whole point of the\n * page. When the marketing site wanted the same thing, the choice was to copy it\n * — a second list of eight languages, a second morph, a second storage key that\n * disagrees the moment either is edited — or to move it next to the mechanism.\n * `@nebutra/tokens` already ships the light/dark control; the language control\n * is its sibling and belongs here, beside skins.css and the catalog.\n *\n * Consumers import `@nebutra/theme/skins.css` and `@nebutra/theme/morph.css`,\n * then mount one of these. Nothing else is needed: the languages come from the\n * registry, the captions from the catalog, the typefaces from the map emitted\n * with the sheet.\n */\n\nimport * as React from \"react\";\nimport { type DesignLanguageEntry, LANGUAGE_REGISTRY } from \"./languages\";\nimport fontsByBrand from \"./skin-fonts.generated.json\";\n\nconst STORAGE_KEY = \"nebutra.design.brand\";\nconst CHANGE_EVENT = \"nebutra.design.brandchange\";\n\n/** How long the fallback blanket transition stays armed. Matches\n * --brand-morph-duration in morph.css, plus a frame so the last interpolation\n * lands before it is removed. */\nconst MORPH_MS = 460;\n\n/** How long a font may hold the switch. A proprietary family that will never\n * resolve on this origin must not make the control look unresponsive; a\n * dissolve of the fallback is a far better failure than one that does not run. */\nconst FONT_BUDGET_MS = 220;\n\nlet morphTimer: ReturnType<typeof setTimeout> | undefined;\n\nconst FONTS: Record<string, string[]> = fontsByBrand;\n\n/** Catalog order, factory first — it is the way back. */\nexport const LANGUAGES: DesignLanguageEntry[] = [\n ...LANGUAGE_REGISTRY.languages.filter((l) => l.id === \"factory\"),\n ...LANGUAGE_REGISTRY.languages.filter((l) => l.id !== \"factory\"),\n];\n\nfunction readStored(): string {\n if (typeof window === \"undefined\") return \"factory\";\n try {\n const value = sessionStorage.getItem(STORAGE_KEY);\n if (value && LANGUAGES.some((l) => l.id === value)) return value;\n } catch {\n /* private mode */\n }\n return \"factory\";\n}\n\n/**\n * Make the incoming typefaces resident before the snapshot is taken.\n *\n * Without this the dissolve photographs a fallback face and the real one swaps\n * in after the animation has finished — a jump landing on a settled page, which\n * is the one that actually reads as broken. `document.fonts.load` resolves\n * immediately for a face already resident, so the common path costs nothing.\n */\nasync function ensureFontsFor(id: string): Promise<void> {\n const families = FONTS[id];\n if (!families?.length || !document.fonts) return;\n await Promise.race([\n Promise.allSettled(\n families.flatMap((family) => [\n document.fonts.load(`400 1rem \"${family}\"`),\n document.fonts.load(`600 1rem \"${family}\"`),\n ]),\n ),\n new Promise((resolve) => setTimeout(resolve, FONT_BUDGET_MS)),\n ]);\n}\n\nfunction supportsViewTransition(): boolean {\n if (typeof document === \"undefined\") return false;\n if (typeof document.startViewTransition !== \"function\") return false;\n return !window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n}\n\n/**\n * Flip the language, with the change animated rather than cut.\n *\n * The attribute swap rewrites some two hundred custom properties in one frame,\n * and custom properties do not interpolate — so the whole page used to change\n * between two frames and read as a reload. The properties that *read* those\n * variables do interpolate, so a blanket transition is armed just before the\n * flip and disarmed after: alive for the length of the change and no longer,\n * because leaving it on would put 400ms of lag on every hover afterwards.\n *\n * Where the browser has view transitions, they replace that entirely. A\n * cross-fade carries the discrete changes a property transition cannot — the\n * typeface, gradients with different stop counts, shadow stacks of different\n * lengths — because by then they are pixels rather than properties. Only one of\n * the two ever runs: both together animate the same change twice, and two clocks\n * beating against each other is its own kind of jitter.\n *\n * `requestAnimationFrame` before writing the attribute matters on the fallback\n * path — set the class and the attribute in the same frame and the browser has\n * no previous computed value to transition from, so it cuts anyway.\n */\nexport function applyLanguageAttribute(id: string, animate = true): void {\n const root = document.documentElement;\n\n const write = () => {\n if (id === \"factory\") delete root.dataset.brand;\n else root.dataset.brand = id;\n };\n\n if (!animate) {\n write();\n } else if (supportsViewTransition()) {\n void ensureFontsFor(id).then(() => {\n document.startViewTransition?.(() => {\n write();\n });\n });\n } else {\n void ensureFontsFor(id).then(() => {\n root.classList.add(\"brand-morphing\");\n clearTimeout(morphTimer);\n requestAnimationFrame(() => {\n write();\n morphTimer = setTimeout(() => root.classList.remove(\"brand-morphing\"), MORPH_MS);\n });\n });\n }\n\n try {\n sessionStorage.setItem(STORAGE_KEY, id);\n } catch {\n /* private mode */\n }\n}\n\n/**\n * Shared state for every mounted switcher.\n *\n * The state lives on the document element rather than in any one component, so\n * two mounted controls broadcast to each other instead of each keeping a private\n * copy that goes stale the moment the other is clicked.\n */\nexport function useDesignLanguage(): {\n active: string;\n select: (id: string) => void;\n ready: boolean;\n} {\n const [active, setActive] = React.useState(\"factory\");\n const [ready, setReady] = React.useState(false);\n\n React.useEffect(() => {\n const stored = readStored();\n // No animation on restore: there is no previous language to move away from,\n // and a page that fades into its own colours on load looks like a bug.\n applyLanguageAttribute(stored, false);\n setActive(stored);\n setReady(true);\n\n const onChange = (event: Event) => setActive((event as CustomEvent<string>).detail);\n window.addEventListener(CHANGE_EVENT, onChange);\n return () => window.removeEventListener(CHANGE_EVENT, onChange);\n }, []);\n\n const select = React.useCallback((id: string) => {\n applyLanguageAttribute(id);\n window.dispatchEvent(new CustomEvent(CHANGE_EVENT, { detail: id }));\n }, []);\n\n return { active, select, ready };\n}\n\nfunction join(...parts: (string | false | null | undefined)[]): string {\n return parts.filter(Boolean).join(\" \");\n}\n\nexport interface DesignLanguageSwitcherProps {\n className?: string;\n /** `compact` for a header rail, `picker` for a primary on-page control. */\n variant?: \"compact\" | \"picker\";\n /** Shown under a `picker`: names the active language and what it changes. */\n caption?: boolean;\n /**\n * Visible \"Design language\" label. Off where the surrounding surface already\n * says it — on the marketing card the legend sat directly under a heading\n * reading the same thing. Still rendered for assistive tech either way.\n */\n showLegend?: boolean;\n}\n\n/**\n * Eight buttons and, optionally, a line saying what the current one does.\n *\n * Deliberately built from plain elements rather than the component library: this\n * ships inside the package that defines the languages, and a control that\n * re-skins itself mid-press is a distraction from the page it is re-skinning.\n */\nexport function DesignLanguageSwitcher({\n className,\n variant = \"compact\",\n caption = false,\n showLegend = true,\n}: DesignLanguageSwitcherProps) {\n const { active, select, ready } = useDesignLanguage();\n const current = LANGUAGES.find((l) => l.id === active) ?? LANGUAGES[0];\n const isPicker = variant === \"picker\";\n\n return (\n <div className={join(\"flex flex-col gap-3\", className)} data-ready={ready ? \"true\" : \"false\"}>\n <fieldset\n className={join(\n \"m-0 flex flex-wrap items-center border-0 p-0\",\n isPicker ? \"gap-1.5\" : \"gap-1.5\",\n )}\n >\n <legend\n className={\n isPicker || !showLegend\n ? \"sr-only\"\n : \"mr-1 float-left w-auto font-mono text-[10px] text-muted-foreground uppercase tracking-wider\"\n }\n >\n Design language\n </legend>\n {LANGUAGES.map((language) => {\n const isActive = active === language.id;\n return (\n <button\n aria-pressed={isActive}\n className={join(\n \"transition-[background-color,color,box-shadow] duration-micro ease-out\",\n isPicker\n ? \"rounded-panel px-3.5 py-2 font-medium text-[13px]\"\n : \"rounded-[var(--radius-sm,0.25rem)] px-2 py-1 font-medium text-[11px]\",\n isActive\n ? join(\"bg-foreground text-background\", isPicker && \"shadow-ambient-sm\")\n : \"bg-muted/70 text-muted-foreground hover:bg-muted hover:text-foreground\",\n )}\n key={language.id}\n onClick={() => select(language.id)}\n type=\"button\"\n >\n {language.shortName}\n </button>\n );\n })}\n </fieldset>\n {caption && current && (\n <p className=\"text-[13px] text-muted-foreground leading-relaxed\">\n <span className=\"text-foreground\">{current.name}</span>\n {current.tagline ? ` — ${current.tagline}.` : \".\"} Colour roles, radii, elevation, type\n and easing move together, not just the palette.\n </p>\n )}\n </div>\n );\n}\n","{\n \"cosmos\": [\n \"var(--font-reg-fraunces)\",\n \"Fraunces\",\n \"GT Super\",\n \"var(--font-noto-sans-sc\",\n \"Noto Sans SC\\\")\",\n \"PingFang SC\",\n \"var(--font-jetbrains-mono)\",\n \"JetBrains Mono\"\n ],\n \"gsap\": [\n \"var(--font-reg-inter-tight)\",\n \"Mori\",\n \"Inter Tight\",\n \"DM Sans\",\n \"var(--font-noto-sans-sc\",\n \"Noto Sans SC\\\")\",\n \"PingFang SC\"\n ],\n \"linear\": [\n \"var(--font-inter)\",\n \"Inter Variable\",\n \"Inter\",\n \"var(--font-noto-sans-sc\",\n \"Noto Sans SC\\\")\",\n \"PingFang SC\",\n \"var(--font-jetbrains-mono)\",\n \"Berkeley Mono\",\n \"JetBrains Mono\"\n ],\n \"notion\": [\n \"var(--font-inter)\",\n \"NotionInter\",\n \"Inter\",\n \"var(--font-noto-sans-sc\",\n \"Noto Sans SC\\\")\",\n \"PingFang SC\"\n ],\n \"raycast\": [\n \"var(--font-inter)\",\n \"Inter\",\n \"var(--font-noto-sans-sc\",\n \"Noto Sans SC\\\")\",\n \"PingFang SC\",\n \"var(--font-geist-mono)\",\n \"Geist Mono\",\n \"GeistMono\",\n \"Menlo\"\n ],\n \"stripe\": [\n \"var(--font-reg-inter-tight)\",\n \"sohne-var\",\n \"Inter Tight\",\n \"Inter\",\n \"var(--font-noto-sans-sc\",\n \"Noto Sans SC\\\")\",\n \"PingFang SC\"\n ],\n \"vanta\": [\n \"var(--font-inter)\",\n \"Inter Variable\",\n \"Inter\",\n \"var(--font-noto-sans-sc\",\n \"Noto Sans SC\\\")\",\n \"PingFang SC\",\n \"var(--font-reg-source-serif-4)\",\n \"Reckless\",\n \"Source Serif 4\",\n \"Lora\"\n ],\n \"vercel\": [\n \"var(--font-geist-sans)\",\n \"Geist Sans\",\n \"Geist\",\n \"var(--font-noto-sans-sc\",\n \"Noto Sans SC\\\")\",\n \"PingFang SC\",\n \"var(--font-geist-mono)\",\n \"Geist Mono\",\n \"Menlo\"\n ]\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/language-switcher.tsx","../src/skin-fonts.generated.json"],"sourcesContent":["\"use client\";\n\n/**\n * The control that switches design language, shared by every surface that has one.\n *\n * This was built inside the design site, where it was the whole point of the\n * page. When the marketing site wanted the same thing, the choice was to copy it\n * — a second list of eight languages, a second morph, a second storage key that\n * disagrees the moment either is edited — or to move it next to the mechanism.\n * `@nebutra/tokens` already ships the light/dark control; the language control\n * is its sibling and belongs here, beside skins.css and the catalog.\n *\n * Consumers import `@nebutra/theme/skins.css` and `@nebutra/theme/morph.css`,\n * then mount one of these. Nothing else is needed: the languages come from the\n * registry, the captions from the catalog, the typefaces from the map emitted\n * with the sheet.\n */\n\nimport * as React from \"react\";\nimport { type DesignLanguageEntry, LANGUAGE_REGISTRY } from \"./languages\";\nimport fontsByBrand from \"./skin-fonts.generated.json\";\n\nconst STORAGE_KEY = \"nebutra.design.brand\";\nconst CHANGE_EVENT = \"nebutra.design.brandchange\";\n\n/** How long the fallback blanket transition stays armed. Matches\n * --brand-morph-duration in morph.css, plus a frame so the last interpolation\n * lands before it is removed. */\nconst MORPH_MS = 460;\n\n/** How long a font may hold the switch. A proprietary family that will never\n * resolve on this origin must not make the control look unresponsive; a\n * dissolve of the fallback is a far better failure than one that does not run. */\nconst FONT_BUDGET_MS = 220;\n\nlet morphTimer: ReturnType<typeof setTimeout> | undefined;\n\nconst FONTS: Record<string, string[]> = fontsByBrand;\n\n/** Catalog order, factory first — it is the way back. */\nexport const LANGUAGES: DesignLanguageEntry[] = [\n ...LANGUAGE_REGISTRY.languages.filter((l) => l.id === \"factory\"),\n ...LANGUAGE_REGISTRY.languages.filter((l) => l.id !== \"factory\"),\n];\n\nfunction readStored(): string {\n if (typeof window === \"undefined\") return \"factory\";\n try {\n const value = sessionStorage.getItem(STORAGE_KEY);\n if (value && LANGUAGES.some((l) => l.id === value)) return value;\n } catch {\n /* private mode */\n }\n return \"factory\";\n}\n\n/**\n * Make the incoming typefaces resident before the snapshot is taken.\n *\n * Without this the dissolve photographs a fallback face and the real one swaps\n * in after the animation has finished — a jump landing on a settled page, which\n * is the one that actually reads as broken. `document.fonts.load` resolves\n * immediately for a face already resident, so the common path costs nothing.\n */\nasync function ensureFontsFor(id: string): Promise<void> {\n const families = FONTS[id];\n if (!families?.length || !document.fonts) return;\n await Promise.race([\n Promise.allSettled(\n families.flatMap((family) => [\n document.fonts.load(`400 1rem \"${family}\"`),\n document.fonts.load(`600 1rem \"${family}\"`),\n ]),\n ),\n new Promise((resolve) => setTimeout(resolve, FONT_BUDGET_MS)),\n ]);\n}\n\nfunction supportsViewTransition(): boolean {\n if (typeof document === \"undefined\") return false;\n if (typeof document.startViewTransition !== \"function\") return false;\n return !window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n}\n\n/**\n * Flip the language, with the change animated rather than cut.\n *\n * The attribute swap rewrites some two hundred custom properties in one frame,\n * and custom properties do not interpolate — so the whole page used to change\n * between two frames and read as a reload. The properties that *read* those\n * variables do interpolate, so a blanket transition is armed just before the\n * flip and disarmed after: alive for the length of the change and no longer,\n * because leaving it on would put 400ms of lag on every hover afterwards.\n *\n * Where the browser has view transitions, they replace that entirely. A\n * cross-fade carries the discrete changes a property transition cannot — the\n * typeface, gradients with different stop counts, shadow stacks of different\n * lengths — because by then they are pixels rather than properties. Only one of\n * the two ever runs: both together animate the same change twice, and two clocks\n * beating against each other is its own kind of jitter.\n *\n * `requestAnimationFrame` before writing the attribute matters on the fallback\n * path — set the class and the attribute in the same frame and the browser has\n * no previous computed value to transition from, so it cuts anyway.\n */\nexport function applyLanguageAttribute(id: string, animate = true): void {\n const root = document.documentElement;\n\n const write = () => {\n if (id === \"factory\") delete root.dataset.brand;\n else root.dataset.brand = id;\n };\n\n if (!animate) {\n write();\n } else if (supportsViewTransition()) {\n void ensureFontsFor(id).then(() => {\n document.startViewTransition?.(() => {\n write();\n });\n });\n } else {\n void ensureFontsFor(id).then(() => {\n root.classList.add(\"brand-morphing\");\n clearTimeout(morphTimer);\n requestAnimationFrame(() => {\n write();\n morphTimer = setTimeout(() => root.classList.remove(\"brand-morphing\"), MORPH_MS);\n });\n });\n }\n\n try {\n sessionStorage.setItem(STORAGE_KEY, id);\n } catch {\n /* private mode */\n }\n}\n\n/**\n * Shared state for every mounted switcher.\n *\n * The state lives on the document element rather than in any one component, so\n * two mounted controls broadcast to each other instead of each keeping a private\n * copy that goes stale the moment the other is clicked.\n */\nexport function useDesignLanguage(): {\n active: string;\n select: (id: string) => void;\n ready: boolean;\n} {\n const [active, setActive] = React.useState(\"factory\");\n const [ready, setReady] = React.useState(false);\n\n React.useEffect(() => {\n const stored = readStored();\n // No animation on restore: there is no previous language to move away from,\n // and a page that fades into its own colours on load looks like a bug.\n applyLanguageAttribute(stored, false);\n setActive(stored);\n setReady(true);\n\n const onChange = (event: Event) => setActive((event as CustomEvent<string>).detail);\n window.addEventListener(CHANGE_EVENT, onChange);\n return () => window.removeEventListener(CHANGE_EVENT, onChange);\n }, []);\n\n const select = React.useCallback((id: string) => {\n applyLanguageAttribute(id);\n window.dispatchEvent(new CustomEvent(CHANGE_EVENT, { detail: id }));\n }, []);\n\n return { active, select, ready };\n}\n\nfunction join(...parts: (string | false | null | undefined)[]): string {\n return parts.filter(Boolean).join(\" \");\n}\n\nexport interface DesignLanguageSwitcherProps {\n className?: string;\n /** `compact` for a header rail, `picker` for a primary on-page control. */\n variant?: \"compact\" | \"picker\";\n /** Shown under a `picker`: names the active language and what it changes. */\n caption?: boolean;\n /**\n * Visible \"Design language\" label. Off where the surrounding surface already\n * says it — on the marketing card the legend sat directly under a heading\n * reading the same thing. Still rendered for assistive tech either way.\n */\n showLegend?: boolean;\n}\n\n/**\n * Eight buttons and, optionally, a line saying what the current one does.\n *\n * Deliberately built from plain elements rather than the component library: this\n * ships inside the package that defines the languages, and a control that\n * re-skins itself mid-press is a distraction from the page it is re-skinning.\n */\nexport function DesignLanguageSwitcher({\n className,\n variant = \"compact\",\n caption = false,\n showLegend = true,\n}: DesignLanguageSwitcherProps) {\n const { active, select, ready } = useDesignLanguage();\n const current = LANGUAGES.find((l) => l.id === active) ?? LANGUAGES[0];\n const isPicker = variant === \"picker\";\n\n return (\n <div className={join(\"flex flex-col gap-3\", className)} data-ready={ready ? \"true\" : \"false\"}>\n <fieldset\n className={join(\n \"m-0 flex flex-wrap items-center border-0 p-0\",\n isPicker ? \"gap-1.5\" : \"gap-1.5\",\n )}\n >\n <legend\n className={\n isPicker || !showLegend\n ? \"sr-only\"\n : \"mr-1 float-left w-auto font-mono text-[10px] text-muted-foreground uppercase tracking-wider\"\n }\n >\n Design language\n </legend>\n {LANGUAGES.map((language) => {\n const isActive = active === language.id;\n return (\n <button\n aria-pressed={isActive}\n className={join(\n \"transition-[background-color,color,box-shadow] duration-micro ease-out\",\n isPicker\n ? \"rounded-panel px-3.5 py-2 font-medium text-[13px]\"\n : \"rounded-[var(--radius-sm,0.25rem)] px-2 py-1 font-medium text-[11px]\",\n isActive\n ? join(\"bg-foreground text-background\", isPicker && \"shadow-ambient-sm\")\n : \"bg-muted/70 text-muted-foreground hover:bg-muted hover:text-foreground\",\n )}\n key={language.id}\n onClick={() => select(language.id)}\n type=\"button\"\n >\n {language.shortName}\n </button>\n );\n })}\n </fieldset>\n {caption && current && (\n <p className=\"text-[13px] text-muted-foreground leading-relaxed\">\n <span className=\"text-foreground\">{current.name}</span>\n {current.tagline ? ` — ${current.tagline}.` : \".\"} Colour roles, radii, elevation, type\n and easing move together, not just the palette.\n </p>\n )}\n </div>\n );\n}\n","{\n \"cosmos\": [\n \"var(--font-reg-fraunces)\",\n \"Fraunces\",\n \"GT Super\",\n \"var(--font-misans\",\n \"MiSans\\\")\",\n \"PingFang SC\",\n \"var(--font-jetbrains-mono)\",\n \"JetBrains Mono\"\n ],\n \"gsap\": [\n \"var(--font-reg-inter-tight)\",\n \"Mori\",\n \"Inter Tight\",\n \"DM Sans\",\n \"var(--font-misans\",\n \"MiSans\\\")\",\n \"PingFang SC\"\n ],\n \"linear\": [\n \"var(--font-inter)\",\n \"Inter Variable\",\n \"Inter\",\n \"var(--font-misans\",\n \"MiSans\\\")\",\n \"PingFang SC\",\n \"var(--font-jetbrains-mono)\",\n \"Berkeley Mono\",\n \"JetBrains Mono\"\n ],\n \"notion\": [\n \"var(--font-inter)\",\n \"NotionInter\",\n \"Inter\",\n \"var(--font-misans\",\n \"MiSans\\\")\",\n \"PingFang SC\"\n ],\n \"raycast\": [\n \"var(--font-inter)\",\n \"Inter\",\n \"var(--font-misans\",\n \"MiSans\\\")\",\n \"PingFang SC\",\n \"var(--font-geist-mono)\",\n \"Geist Mono\",\n \"GeistMono\",\n \"Menlo\"\n ],\n \"stripe\": [\n \"var(--font-reg-inter-tight)\",\n \"sohne-var\",\n \"Inter Tight\",\n \"Inter\",\n \"var(--font-misans\",\n \"MiSans\\\")\",\n \"PingFang SC\"\n ],\n \"vanta\": [\n \"var(--font-inter)\",\n \"Inter Variable\",\n \"Inter\",\n \"var(--font-misans\",\n \"MiSans\\\")\",\n \"PingFang SC\",\n \"var(--font-reg-source-serif-4)\",\n \"Reckless\",\n \"Source Serif 4\",\n \"Lora\"\n ],\n \"vercel\": [\n \"var(--font-geist-sans)\",\n \"Geist Sans\",\n \"Geist\",\n \"var(--font-misans\",\n \"MiSans\\\")\",\n \"PingFang SC\",\n \"var(--font-geist-mono)\",\n \"Geist Mono\",\n \"Menlo\"\n ]\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nebutra/theme",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "type": "module",
5
5
  "description": "Design-language catalog for global product chrome swap (Brand Packages). Multi-mood oklch catalog removed.",
6
6
  "private": false,
@@ -51,7 +51,7 @@
51
51
  "./styles/sources.css": "./styles/sources.css"
52
52
  },
53
53
  "dependencies": {
54
- "@nebutra/tokens": "2.0.0"
54
+ "@nebutra/tokens": "3.0.0"
55
55
  },
56
56
  "devDependencies": {
57
57
  "@types/react": "^19.2.14",
package/skins.css CHANGED
@@ -128,17 +128,17 @@ html[data-brand="cosmos"] {
128
128
 
129
129
  /* Typography */
130
130
  --font-sans:
131
- var(--font-reg-fraunces), "Fraunces", "GT Super", Georgia,
132
- var(--font-noto-sans-sc, "Noto Sans SC"), "PingFang SC", ui-serif, serif;
131
+ var(--font-reg-fraunces), "Fraunces", "GT Super", Georgia, var(--font-misans, "MiSans"),
132
+ "PingFang SC", ui-serif, serif;
133
133
  --font-cn:
134
- var(--font-reg-fraunces), "Fraunces", "GT Super", Georgia,
135
- var(--font-noto-sans-sc, "Noto Sans SC"), "PingFang SC", ui-serif, serif;
134
+ var(--font-reg-fraunces), "Fraunces", "GT Super", Georgia, var(--font-misans, "MiSans"),
135
+ "PingFang SC", ui-serif, serif;
136
136
  --font-heading:
137
- var(--font-reg-fraunces), "Fraunces", "GT Super", Georgia,
138
- var(--font-noto-sans-sc, "Noto Sans SC"), "PingFang SC", ui-serif, serif;
137
+ var(--font-reg-fraunces), "Fraunces", "GT Super", Georgia, var(--font-misans, "MiSans"),
138
+ "PingFang SC", ui-serif, serif;
139
139
  --font-display:
140
- var(--font-reg-fraunces), "Fraunces", "GT Super", Georgia,
141
- var(--font-noto-sans-sc, "Noto Sans SC"), "PingFang SC", ui-serif, serif;
140
+ var(--font-reg-fraunces), "Fraunces", "GT Super", Georgia, var(--font-misans, "MiSans"),
141
+ "PingFang SC", ui-serif, serif;
142
142
  --font-mono: var(--font-jetbrains-mono), "JetBrains Mono", ui-monospace, monospace;
143
143
  --font-weight-heading: 350;
144
144
 
@@ -373,17 +373,17 @@ html[data-brand="gsap"] {
373
373
 
374
374
  /* Typography */
375
375
  --font-sans:
376
- var(--font-reg-inter-tight), "Mori", "Inter Tight", "DM Sans",
377
- var(--font-noto-sans-sc, "Noto Sans SC"), "PingFang SC", ui-sans-serif, system-ui, sans-serif;
376
+ var(--font-reg-inter-tight), "Mori", "Inter Tight", "DM Sans", var(--font-misans, "MiSans"),
377
+ "PingFang SC", ui-sans-serif, system-ui, sans-serif;
378
378
  --font-cn:
379
- var(--font-reg-inter-tight), "Mori", "Inter Tight", "DM Sans",
380
- var(--font-noto-sans-sc, "Noto Sans SC"), "PingFang SC", ui-sans-serif, system-ui, sans-serif;
381
- --font-heading:
382
- var(--font-reg-inter-tight), "Mori", "Inter Tight", var(--font-noto-sans-sc, "Noto Sans SC"),
379
+ var(--font-reg-inter-tight), "Mori", "Inter Tight", "DM Sans", var(--font-misans, "MiSans"),
383
380
  "PingFang SC", ui-sans-serif, system-ui, sans-serif;
381
+ --font-heading:
382
+ var(--font-reg-inter-tight), "Mori", "Inter Tight", var(--font-misans, "MiSans"), "PingFang SC",
383
+ ui-sans-serif, system-ui, sans-serif;
384
384
  --font-display:
385
- var(--font-reg-inter-tight), "Mori", "Inter Tight", var(--font-noto-sans-sc, "Noto Sans SC"),
386
- "PingFang SC", ui-sans-serif, system-ui, sans-serif;
385
+ var(--font-reg-inter-tight), "Mori", "Inter Tight", var(--font-misans, "MiSans"), "PingFang SC",
386
+ ui-sans-serif, system-ui, sans-serif;
387
387
  --font-weight-heading: 600;
388
388
 
389
389
  /* Motion */
@@ -543,17 +543,17 @@ html[data-brand="linear"] {
543
543
 
544
544
  /* Typography */
545
545
  --font-sans:
546
- var(--font-inter), "Inter Variable", "Inter", var(--font-noto-sans-sc, "Noto Sans SC"),
547
- "PingFang SC", ui-sans-serif, system-ui, sans-serif;
546
+ var(--font-inter), "Inter Variable", "Inter", var(--font-misans, "MiSans"), "PingFang SC",
547
+ ui-sans-serif, system-ui, sans-serif;
548
548
  --font-cn:
549
- var(--font-inter), "Inter Variable", "Inter", var(--font-noto-sans-sc, "Noto Sans SC"),
550
- "PingFang SC", ui-sans-serif, system-ui, sans-serif;
549
+ var(--font-inter), "Inter Variable", "Inter", var(--font-misans, "MiSans"), "PingFang SC",
550
+ ui-sans-serif, system-ui, sans-serif;
551
551
  --font-heading:
552
- var(--font-inter), "Inter Variable", "Inter", var(--font-noto-sans-sc, "Noto Sans SC"),
553
- "PingFang SC", ui-sans-serif, system-ui, sans-serif;
552
+ var(--font-inter), "Inter Variable", "Inter", var(--font-misans, "MiSans"), "PingFang SC",
553
+ ui-sans-serif, system-ui, sans-serif;
554
554
  --font-display:
555
- var(--font-inter), "Inter Variable", "Inter", var(--font-noto-sans-sc, "Noto Sans SC"),
556
- "PingFang SC", ui-sans-serif, system-ui, sans-serif;
555
+ var(--font-inter), "Inter Variable", "Inter", var(--font-misans, "MiSans"), "PingFang SC",
556
+ ui-sans-serif, system-ui, sans-serif;
557
557
  --font-mono:
558
558
  var(--font-jetbrains-mono), "Berkeley Mono", "JetBrains Mono", ui-monospace, monospace;
559
559
  --font-weight-heading: 510;
@@ -772,17 +772,17 @@ html[data-brand="notion"] {
772
772
 
773
773
  /* Typography */
774
774
  --font-sans:
775
- var(--font-inter), "NotionInter", "Inter", var(--font-noto-sans-sc, "Noto Sans SC"),
776
- "PingFang SC", ui-sans-serif, system-ui, sans-serif;
775
+ var(--font-inter), "NotionInter", "Inter", var(--font-misans, "MiSans"), "PingFang SC",
776
+ ui-sans-serif, system-ui, sans-serif;
777
777
  --font-cn:
778
- var(--font-inter), "NotionInter", "Inter", var(--font-noto-sans-sc, "Noto Sans SC"),
779
- "PingFang SC", ui-sans-serif, system-ui, sans-serif;
778
+ var(--font-inter), "NotionInter", "Inter", var(--font-misans, "MiSans"), "PingFang SC",
779
+ ui-sans-serif, system-ui, sans-serif;
780
780
  --font-heading:
781
- var(--font-inter), "NotionInter", "Inter", var(--font-noto-sans-sc, "Noto Sans SC"),
782
- "PingFang SC", ui-sans-serif, system-ui, sans-serif;
781
+ var(--font-inter), "NotionInter", "Inter", var(--font-misans, "MiSans"), "PingFang SC",
782
+ ui-sans-serif, system-ui, sans-serif;
783
783
  --font-display:
784
- var(--font-inter), "NotionInter", "Inter", var(--font-noto-sans-sc, "Noto Sans SC"),
785
- "PingFang SC", ui-sans-serif, system-ui, sans-serif;
784
+ var(--font-inter), "NotionInter", "Inter", var(--font-misans, "MiSans"), "PingFang SC",
785
+ ui-sans-serif, system-ui, sans-serif;
786
786
  --font-weight-heading: 700;
787
787
 
788
788
  /* Motion */
@@ -981,17 +981,17 @@ html[data-brand="raycast"] {
981
981
 
982
982
  /* Typography */
983
983
  --font-sans:
984
- var(--font-inter), "Inter", var(--font-noto-sans-sc, "Noto Sans SC"), "PingFang SC",
985
- ui-sans-serif, system-ui, -apple-system, sans-serif;
984
+ var(--font-inter), "Inter", var(--font-misans, "MiSans"), "PingFang SC", ui-sans-serif,
985
+ system-ui, -apple-system, sans-serif;
986
986
  --font-cn:
987
- var(--font-inter), "Inter", var(--font-noto-sans-sc, "Noto Sans SC"), "PingFang SC",
988
- ui-sans-serif, system-ui, -apple-system, sans-serif;
987
+ var(--font-inter), "Inter", var(--font-misans, "MiSans"), "PingFang SC", ui-sans-serif,
988
+ system-ui, -apple-system, sans-serif;
989
989
  --font-heading:
990
- var(--font-inter), "Inter", var(--font-noto-sans-sc, "Noto Sans SC"), "PingFang SC",
991
- ui-sans-serif, system-ui, sans-serif;
990
+ var(--font-inter), "Inter", var(--font-misans, "MiSans"), "PingFang SC", ui-sans-serif,
991
+ system-ui, sans-serif;
992
992
  --font-display:
993
- var(--font-inter), "Inter", var(--font-noto-sans-sc, "Noto Sans SC"), "PingFang SC",
994
- ui-sans-serif, system-ui, sans-serif;
993
+ var(--font-inter), "Inter", var(--font-misans, "MiSans"), "PingFang SC", ui-sans-serif,
994
+ system-ui, sans-serif;
995
995
  --font-mono: var(--font-geist-mono), "Geist Mono", "GeistMono", ui-monospace, Menlo, monospace;
996
996
  --font-weight-heading: 400;
997
997
 
@@ -1141,17 +1141,17 @@ html[data-brand="stripe"] {
1141
1141
 
1142
1142
  /* Typography */
1143
1143
  --font-sans:
1144
- var(--font-reg-inter-tight), "sohne-var", "Inter Tight", "Inter",
1145
- var(--font-noto-sans-sc, "Noto Sans SC"), "PingFang SC", ui-sans-serif, system-ui, sans-serif;
1144
+ var(--font-reg-inter-tight), "sohne-var", "Inter Tight", "Inter", var(--font-misans, "MiSans"),
1145
+ "PingFang SC", ui-sans-serif, system-ui, sans-serif;
1146
1146
  --font-cn:
1147
- var(--font-reg-inter-tight), "sohne-var", "Inter Tight", "Inter",
1148
- var(--font-noto-sans-sc, "Noto Sans SC"), "PingFang SC", ui-sans-serif, system-ui, sans-serif;
1147
+ var(--font-reg-inter-tight), "sohne-var", "Inter Tight", "Inter", var(--font-misans, "MiSans"),
1148
+ "PingFang SC", ui-sans-serif, system-ui, sans-serif;
1149
1149
  --font-heading:
1150
- var(--font-reg-inter-tight), "sohne-var", "Inter Tight",
1151
- var(--font-noto-sans-sc, "Noto Sans SC"), "PingFang SC", ui-sans-serif, system-ui, sans-serif;
1150
+ var(--font-reg-inter-tight), "sohne-var", "Inter Tight", var(--font-misans, "MiSans"),
1151
+ "PingFang SC", ui-sans-serif, system-ui, sans-serif;
1152
1152
  --font-display:
1153
- var(--font-reg-inter-tight), "sohne-var", "Inter Tight",
1154
- var(--font-noto-sans-sc, "Noto Sans SC"), "PingFang SC", ui-sans-serif, system-ui, sans-serif;
1153
+ var(--font-reg-inter-tight), "sohne-var", "Inter Tight", var(--font-misans, "MiSans"),
1154
+ "PingFang SC", ui-sans-serif, system-ui, sans-serif;
1155
1155
  --font-weight-heading: 300;
1156
1156
 
1157
1157
  /* Motion */
@@ -1308,17 +1308,17 @@ html[data-brand="vanta"] {
1308
1308
 
1309
1309
  /* Typography */
1310
1310
  --font-sans:
1311
- var(--font-inter), "Inter Variable", "Inter", var(--font-noto-sans-sc, "Noto Sans SC"),
1312
- "PingFang SC", ui-sans-serif, system-ui, sans-serif;
1311
+ var(--font-inter), "Inter Variable", "Inter", var(--font-misans, "MiSans"), "PingFang SC",
1312
+ ui-sans-serif, system-ui, sans-serif;
1313
1313
  --font-cn:
1314
- var(--font-inter), "Inter Variable", "Inter", var(--font-noto-sans-sc, "Noto Sans SC"),
1315
- "PingFang SC", ui-sans-serif, system-ui, sans-serif;
1314
+ var(--font-inter), "Inter Variable", "Inter", var(--font-misans, "MiSans"), "PingFang SC",
1315
+ ui-sans-serif, system-ui, sans-serif;
1316
1316
  --font-heading:
1317
1317
  var(--font-reg-source-serif-4), "Reckless", "Source Serif 4", "Lora",
1318
- var(--font-noto-sans-sc, "Noto Sans SC"), "PingFang SC", ui-serif, Georgia, serif;
1318
+ var(--font-misans, "MiSans"), "PingFang SC", ui-serif, Georgia, serif;
1319
1319
  --font-display:
1320
1320
  var(--font-reg-source-serif-4), "Reckless", "Source Serif 4", "Lora",
1321
- var(--font-noto-sans-sc, "Noto Sans SC"), "PingFang SC", ui-serif, Georgia, serif;
1321
+ var(--font-misans, "MiSans"), "PingFang SC", ui-serif, Georgia, serif;
1322
1322
  --font-weight-heading: 500;
1323
1323
 
1324
1324
  /* Motion */
@@ -1478,17 +1478,17 @@ html[data-brand="vercel"] {
1478
1478
 
1479
1479
  /* Typography */
1480
1480
  --font-sans:
1481
- var(--font-geist-sans), "Geist Sans", "Geist", var(--font-noto-sans-sc, "Noto Sans SC"),
1482
- "PingFang SC", ui-sans-serif, system-ui, sans-serif;
1481
+ var(--font-geist-sans), "Geist Sans", "Geist", var(--font-misans, "MiSans"), "PingFang SC",
1482
+ ui-sans-serif, system-ui, sans-serif;
1483
1483
  --font-cn:
1484
- var(--font-geist-sans), "Geist Sans", "Geist", var(--font-noto-sans-sc, "Noto Sans SC"),
1485
- "PingFang SC", ui-sans-serif, system-ui, sans-serif;
1484
+ var(--font-geist-sans), "Geist Sans", "Geist", var(--font-misans, "MiSans"), "PingFang SC",
1485
+ ui-sans-serif, system-ui, sans-serif;
1486
1486
  --font-heading:
1487
- var(--font-geist-sans), "Geist Sans", "Geist", var(--font-noto-sans-sc, "Noto Sans SC"),
1488
- "PingFang SC", ui-sans-serif, system-ui, sans-serif;
1487
+ var(--font-geist-sans), "Geist Sans", "Geist", var(--font-misans, "MiSans"), "PingFang SC",
1488
+ ui-sans-serif, system-ui, sans-serif;
1489
1489
  --font-display:
1490
- var(--font-geist-sans), "Geist Sans", "Geist", var(--font-noto-sans-sc, "Noto Sans SC"),
1491
- "PingFang SC", ui-sans-serif, system-ui, sans-serif;
1490
+ var(--font-geist-sans), "Geist Sans", "Geist", var(--font-misans, "MiSans"), "PingFang SC",
1491
+ ui-sans-serif, system-ui, sans-serif;
1492
1492
  --font-mono: var(--font-geist-mono), "Geist Mono", ui-monospace, Menlo, monospace;
1493
1493
  --font-weight-heading: 450;
1494
1494