@nebutra/ui 0.2.2 → 0.2.3

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,42 +1,37 @@
1
1
  /**
2
- * Font CSS Variables — SSOT for typography across Nebutra packages.
3
- * "Precision Stack" — Geist × Geist Mono × Noto Sans SC
2
+ * Font rules — the typography BEHAVIOUR layer (which element gets which stack).
3
+ * The STACKS themselves are owned by @nebutra/tokens, generated from
4
+ * packages/design/design-tokens/tokens/core.json → fontFamily.*.
4
5
  *
5
- * Font loading strategy (Next.js apps):
6
- * Each app's layout.tsx loads Latin fonts via geist/font:
7
- * GeistSans → injects --font-geist-sans (variable font, 100–900)
8
- * GeistMono → injects --font-geist-mono (variable font, 100–900)
6
+ * Font loading (Next.js apps): each app's layout.tsx applies the loader classes
7
+ * GeistSans → --font-geist-sans (variable, 100–900) Latin + numerals
8
+ * GeistMono → --font-geist-mono (variable, 100–900) code
9
+ * cjkFontClassName → --font-vivo-sans-sc (@nebutra/fonts/next/cjk) CJK
10
+ * and the token stacks reference those variables in that order, so Geist keeps
11
+ * Latin and only CJK falls through to vivo Sans SC.
9
12
  *
10
- * The CSS custom properties below reference those injected variables and
11
- * fall back gracefully when font loaders are unavailable (e.g., Storybook, tests).
12
- * CJK uses local/system font fallback to avoid build-time Google font fetches.
13
+ * IMPORT ORDER MATTERS: this file must come AFTER @nebutra/tokens/styles.css
14
+ * (see ../styles/preset.css). It used to re-declare --font-sans / --font-heading /
15
+ * --font-mono here as Geist-only literals; being the later import, those copies
16
+ * won, and the CJK half of every token stack was silently discarded. Declare
17
+ * stacks in the tokens, never here.
13
18
  */
14
19
 
15
20
  /* ============================================
16
- CSS Custom Properties (SSOT)
21
+ CSS Custom Properties — derived aliases only
17
22
  ============================================ */
18
23
 
19
24
  :root {
20
- /* Primary font stack — Geist Sans (geometric precision, variable) */
21
- --font-primary:
22
- var(--font-geist-sans), "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui,
23
- sans-serif;
25
+ /* Legacy alias for the body/UI stack — the stack lives in the tokens. */
26
+ --font-primary: var(--font-sans);
24
27
 
25
- /* Heading font stack — matches primary for Precision Stack */
26
- --font-heading:
27
- var(--font-geist-sans), "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui,
28
- sans-serif;
29
-
30
- /* Monospace — Geist Mono */
31
- --font-mono:
32
- var(--font-geist-mono), "Geist Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
33
-
34
- /* CJK — local/system fallback, no build-time font fetch */
28
+ /* CJK — appended as a fallback on the rules below (see the note there). The
29
+ token already leads with the self-hosted face; the system families repeat
30
+ here so this alias still degrades on its own. */
35
31
  --font-cjk:
36
32
  var(--font-cn), "Noto Sans SC", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
37
33
 
38
- /* Aliases for Tailwind/shadcn compatibility */
39
- --font-sans: var(--font-primary);
34
+ /* Not a token: no Nebutra surface uses a serif, this exists for shadcn parity. */
40
35
  --font-serif: Georgia, Cambria, "Times New Roman", Times, serif;
41
36
  }
42
37
 
@@ -1,7 +1,37 @@
1
1
  import { ClassValue } from 'clsx';
2
+ import * as React from 'react';
2
3
 
3
4
  declare function cn(...inputs: ClassValue[]): string;
4
5
 
6
+ /**
7
+ * Base UI (and some Radix peers) type `style` as CSSProperties | state-function.
8
+ * Token merge helpers only need the plain object branch — drop functions so DTS
9
+ * builds stay green across monorepo and standalone package mirrors.
10
+ * Returns `{}` (never undefined) so spreads stay exactOptionalPropertyTypes-safe.
11
+ */
12
+ declare function asPlainStyle(style: unknown): React.CSSProperties;
13
+ type PrimitiveProps<T> = React.PropsWithChildren<T & {
14
+ className?: string | undefined;
15
+ }>;
16
+ type PrimitiveComponent<TElement, TProps> = React.ForwardRefExoticComponent<PrimitiveProps<TProps> & React.RefAttributes<TElement>>;
17
+ /**
18
+ * Resolve the HTMLElement type for a given intrinsic element tag.
19
+ */
20
+ type ElementForTag<E extends keyof React.JSX.IntrinsicElements> = E extends "button" ? HTMLButtonElement : E extends "span" ? HTMLSpanElement : E extends "img" ? HTMLImageElement : E extends "label" ? HTMLLabelElement : E extends "input" ? HTMLInputElement : E extends "a" ? HTMLAnchorElement : E extends "p" ? HTMLParagraphElement : E extends "h1" | "h2" | "h3" | "h4" | "h5" | "h6" ? HTMLHeadingElement : HTMLElement;
21
+ /**
22
+ * Workaround for Radix UI types not resolving HTML element props (className,
23
+ * children, etc.) with React 19 + exactOptionalPropertyTypes.
24
+ *
25
+ * The optional second type parameter `Extra` allows preserving Radix-specific
26
+ * props (e.g. `asChild`, `align`, `sideOffset`) that are not standard HTML attrs.
27
+ *
28
+ * Usage:
29
+ * const Header = withHtmlProps<"h3">(AccordionPrimitive.Header);
30
+ * const Trigger = withHtmlProps<"button", { asChild?: boolean }>(DialogPrimitive.Trigger);
31
+ * const Content = withHtmlProps<"div", { align?: string; sideOffset?: number }>(PopoverPrimitive.Content);
32
+ */
33
+ declare function withHtmlProps<E extends keyof React.JSX.IntrinsicElements, Extra = object>(Component: any): React.ForwardRefExoticComponent<React.ComponentPropsWithoutRef<E> & Extra & React.RefAttributes<ElementForTag<E>>>;
34
+
5
35
  /**
6
36
  * Runtime color resolution for APIs that cannot consume CSS variables
7
37
  * (Canvas, WebGL, some animation libs).
@@ -80,4 +110,4 @@ type Breakpoint = keyof typeof BREAKPOINTS;
80
110
  */
81
111
  declare function isBreakpointUp(breakpoint: Breakpoint): boolean;
82
112
 
83
- export { BRAND_FALLBACK, BREAKPOINTS, type Breakpoint, cn, getBrandAccent, getBrandMark, getBrandMarkForeground, getBrandPrimary, getBrandTertiary, getProductPrimary, isBreakpointUp, readCssVar };
113
+ export { BRAND_FALLBACK, BREAKPOINTS, type Breakpoint, type PrimitiveComponent, type PrimitiveProps, asPlainStyle, cn, getBrandAccent, getBrandMark, getBrandMarkForeground, getBrandPrimary, getBrandTertiary, getProductPrimary, isBreakpointUp, readCssVar, withHtmlProps };
@@ -7,6 +7,15 @@ function cn(...inputs) {
7
7
  return twMerge(clsx(inputs));
8
8
  }
9
9
 
10
+ // src/utils/primitive-props.ts
11
+ function asPlainStyle(style) {
12
+ if (style == null || typeof style === "function") return {};
13
+ return style;
14
+ }
15
+ function withHtmlProps(Component) {
16
+ return Component;
17
+ }
18
+
10
19
  // src/utils/brand-colors.ts
11
20
  var BRAND_FALLBACK = {
12
21
  /** VI 云毓蓝 — identity only */
@@ -84,6 +93,6 @@ function isBreakpointUp(breakpoint) {
84
93
  return window.innerWidth >= BREAKPOINTS[breakpoint];
85
94
  }
86
95
 
87
- export { BRAND_FALLBACK, BREAKPOINTS, cn, getBrandAccent, getBrandMark, getBrandMarkForeground, getBrandPrimary, getBrandTertiary, getProductPrimary, isBreakpointUp, readCssVar };
96
+ export { BRAND_FALLBACK, BREAKPOINTS, asPlainStyle, cn, getBrandAccent, getBrandMark, getBrandMarkForeground, getBrandPrimary, getBrandTertiary, getProductPrimary, isBreakpointUp, readCssVar, withHtmlProps };
88
97
  //# sourceMappingURL=index.js.map
89
98
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/cn.ts","../../src/utils/brand-colors.ts","../../src/utils/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","/**\n * Runtime color resolution for APIs that cannot consume CSS variables\n * (Canvas, WebGL, some animation libs).\n *\n * Layers (do not mix):\n * - Product action / CTA: getProductPrimary() → semantic `--primary` (roles.action)\n * - Brand mark / AI badge / logo tint: getBrandMark() → `--brand-mark` (roles.brand)\n * - VI lock (print / legal assets): getBrandPrimary() → `--brand-primary` hex\n *\n * Prefer CSS / Tailwind in components:\n * - `bg-primary` for CTAs\n * - `bg-brand-mark` / `text-brand-mark` for Logo/AI badge (see recipe.css)\n *\n * @see packages/design/ARCHITECTURE.md\n * @see packages/design/tokens/recipe.css\n */\n\n/** SSR fallbacks when document is unavailable */\nexport const BRAND_FALLBACK = {\n /** VI 云毓蓝 — identity only */\n primary: \"#0033FE\",\n accent: \"#0BF1C3\",\n tertiary: \"#5c7cfa\",\n primaryDark: \"#002ad4\",\n backDark: \"#000830\",\n /** Soft product action (matches themes/light --primary ≈ #254bfa) */\n productPrimary: \"#254bfa\",\n /**\n * Brand mark fallback (= product action when factory brand has action≡brand).\n * Skins may diverge (e.g. Linear purple action + indigo mark).\n */\n brandMark: \"#254bfa\",\n brandMarkForeground: \"#ffffff\",\n} as const;\n\n/**\n * Read a CSS variable from :root and return its trimmed value.\n */\nexport function readCssVar(name: string, fallback: string): string {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return fallback;\n }\n const value = getComputedStyle(document.documentElement).getPropertyValue(name).trim();\n return value || fallback;\n}\n\n/** Turn HSL channel triple or full color into a CSS color string. */\nfunction asCssColor(value: string, fallback: string): string {\n const v = value.trim() || fallback;\n if (\n v.startsWith(\"#\") ||\n v.startsWith(\"rgb\") ||\n v.startsWith(\"hsl\") ||\n v.startsWith(\"oklch\") ||\n v.startsWith(\"color(\")\n ) {\n return v;\n }\n // shadcn-style \"228 85% 56%\"\n return `hsl(${v})`;\n}\n\n/**\n * Product action color — follows the active skin (`--primary`).\n * Use for shaders/canvas that must match buttons/CTAs.\n */\nexport function getProductPrimary(): string {\n return asCssColor(readCssVar(\"--primary\", \"228 85% 56%\"), BRAND_FALLBACK.productPrimary);\n}\n\n/** VI lock color — legal / print lockups only (not product chrome). */\nexport function getBrandPrimary(): string {\n return asCssVarColor(\"--brand-primary\", BRAND_FALLBACK.primary);\n}\n\n/**\n * Product brand-mark (roles.brand) — Logo tile, AI badge, identity chips.\n * Never use for default CTA (that is getProductPrimary / --primary).\n */\nexport function getBrandMark(): string {\n const raw = readCssVar(\"--brand-mark\", \"228 85% 56%\");\n // Factory recipe defaults `--brand-mark: var(--primary)` — resolve to action color.\n if (/var\\(\\s*--primary\\s*\\)/u.test(raw)) {\n return getProductPrimary();\n }\n return asCssColor(raw, BRAND_FALLBACK.brandMark);\n}\n\n/** Foreground on brand-mark surfaces. */\nexport function getBrandMarkForeground(): string {\n const raw = readCssVar(\"--brand-mark-foreground\", \"0 0% 100%\");\n if (/var\\(\\s*--primary-foreground\\s*\\)/u.test(raw)) {\n return asCssColor(\n readCssVar(\"--primary-foreground\", \"0 0% 100%\"),\n BRAND_FALLBACK.brandMarkForeground,\n );\n }\n return asCssColor(raw, BRAND_FALLBACK.brandMarkForeground);\n}\n\nfunction asCssVarColor(name: string, fallback: string): string {\n return asCssColor(readCssVar(name, fallback), fallback);\n}\n\nexport function getBrandAccent(): string {\n return asCssVarColor(\"--brand-accent\", BRAND_FALLBACK.accent);\n}\n\nexport function getBrandTertiary(): string {\n return asCssVarColor(\"--brand-tertiary\", BRAND_FALLBACK.tertiary);\n}\n","/**\n * Utility Functions\n */\n\n/**\n * Merge class names — canonical implementation from ./cn.ts\n * Uses twMerge(clsx(...)) to correctly resolve Tailwind class conflicts.\n */\nexport { cn } from \"./cn\";\n\n/**\n * Breakpoint values (matches Primer)\n */\nexport const BREAKPOINTS = {\n xs: 0,\n sm: 544,\n md: 768,\n lg: 1012,\n xl: 1280,\n} as const;\n\nexport type Breakpoint = keyof typeof BREAKPOINTS;\n\n/**\n * Check if viewport is at or above breakpoint\n */\nexport function isBreakpointUp(breakpoint: Breakpoint): boolean {\n if (typeof window === \"undefined\") return true;\n return window.innerWidth >= BREAKPOINTS[breakpoint];\n}\n\nexport {\n BRAND_FALLBACK,\n getBrandAccent,\n getBrandMark,\n getBrandMarkForeground,\n getBrandPrimary,\n getBrandTertiary,\n getProductPrimary,\n readCssVar,\n} from \"./brand-colors\";\n"]}
1
+ {"version":3,"sources":["../../src/utils/cn.ts","../../src/utils/primitive-props.ts","../../src/utils/brand-colors.ts","../../src/utils/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import type * as React from \"react\";\n\n/**\n * Base UI (and some Radix peers) type `style` as CSSProperties | state-function.\n * Token merge helpers only need the plain object branch — drop functions so DTS\n * builds stay green across monorepo and standalone package mirrors.\n * Returns `{}` (never undefined) so spreads stay exactOptionalPropertyTypes-safe.\n */\nexport function asPlainStyle(style: unknown): React.CSSProperties {\n if (style == null || typeof style === \"function\") return {};\n return style as React.CSSProperties;\n}\n\nexport type PrimitiveProps<T> = React.PropsWithChildren<T & { className?: string | undefined }>;\n\nexport type PrimitiveComponent<TElement, TProps> = React.ForwardRefExoticComponent<\n PrimitiveProps<TProps> & React.RefAttributes<TElement>\n>;\n\n/**\n * Resolve the HTMLElement type for a given intrinsic element tag.\n */\ntype ElementForTag<E extends keyof React.JSX.IntrinsicElements> = E extends \"button\"\n ? HTMLButtonElement\n : E extends \"span\"\n ? HTMLSpanElement\n : E extends \"img\"\n ? HTMLImageElement\n : E extends \"label\"\n ? HTMLLabelElement\n : E extends \"input\"\n ? HTMLInputElement\n : E extends \"a\"\n ? HTMLAnchorElement\n : E extends \"p\"\n ? HTMLParagraphElement\n : E extends \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\"\n ? HTMLHeadingElement\n : HTMLElement;\n\n/**\n * Workaround for Radix UI types not resolving HTML element props (className,\n * children, etc.) with React 19 + exactOptionalPropertyTypes.\n *\n * The optional second type parameter `Extra` allows preserving Radix-specific\n * props (e.g. `asChild`, `align`, `sideOffset`) that are not standard HTML attrs.\n *\n * Usage:\n * const Header = withHtmlProps<\"h3\">(AccordionPrimitive.Header);\n * const Trigger = withHtmlProps<\"button\", { asChild?: boolean }>(DialogPrimitive.Trigger);\n * const Content = withHtmlProps<\"div\", { align?: string; sideOffset?: number }>(PopoverPrimitive.Content);\n */\n\nexport function withHtmlProps<E extends keyof React.JSX.IntrinsicElements, Extra = object>(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n Component: any,\n): React.ForwardRefExoticComponent<\n React.ComponentPropsWithoutRef<E> & Extra & React.RefAttributes<ElementForTag<E>>\n> {\n return Component;\n}\n","/**\n * Runtime color resolution for APIs that cannot consume CSS variables\n * (Canvas, WebGL, some animation libs).\n *\n * Layers (do not mix):\n * - Product action / CTA: getProductPrimary() → semantic `--primary` (roles.action)\n * - Brand mark / AI badge / logo tint: getBrandMark() → `--brand-mark` (roles.brand)\n * - VI lock (print / legal assets): getBrandPrimary() → `--brand-primary` hex\n *\n * Prefer CSS / Tailwind in components:\n * - `bg-primary` for CTAs\n * - `bg-brand-mark` / `text-brand-mark` for Logo/AI badge (see recipe.css)\n *\n * @see packages/design/ARCHITECTURE.md\n * @see packages/design/tokens/recipe.css\n */\n\n/** SSR fallbacks when document is unavailable */\nexport const BRAND_FALLBACK = {\n /** VI 云毓蓝 — identity only */\n primary: \"#0033FE\",\n accent: \"#0BF1C3\",\n tertiary: \"#5c7cfa\",\n primaryDark: \"#002ad4\",\n backDark: \"#000830\",\n /** Soft product action (matches themes/light --primary ≈ #254bfa) */\n productPrimary: \"#254bfa\",\n /**\n * Brand mark fallback (= product action when factory brand has action≡brand).\n * Skins may diverge (e.g. Linear purple action + indigo mark).\n */\n brandMark: \"#254bfa\",\n brandMarkForeground: \"#ffffff\",\n} as const;\n\n/**\n * Read a CSS variable from :root and return its trimmed value.\n */\nexport function readCssVar(name: string, fallback: string): string {\n if (typeof window === \"undefined\" || typeof document === \"undefined\") {\n return fallback;\n }\n const value = getComputedStyle(document.documentElement).getPropertyValue(name).trim();\n return value || fallback;\n}\n\n/** Turn HSL channel triple or full color into a CSS color string. */\nfunction asCssColor(value: string, fallback: string): string {\n const v = value.trim() || fallback;\n if (\n v.startsWith(\"#\") ||\n v.startsWith(\"rgb\") ||\n v.startsWith(\"hsl\") ||\n v.startsWith(\"oklch\") ||\n v.startsWith(\"color(\")\n ) {\n return v;\n }\n // shadcn-style \"228 85% 56%\"\n return `hsl(${v})`;\n}\n\n/**\n * Product action color — follows the active skin (`--primary`).\n * Use for shaders/canvas that must match buttons/CTAs.\n */\nexport function getProductPrimary(): string {\n return asCssColor(readCssVar(\"--primary\", \"228 85% 56%\"), BRAND_FALLBACK.productPrimary);\n}\n\n/** VI lock color — legal / print lockups only (not product chrome). */\nexport function getBrandPrimary(): string {\n return asCssVarColor(\"--brand-primary\", BRAND_FALLBACK.primary);\n}\n\n/**\n * Product brand-mark (roles.brand) — Logo tile, AI badge, identity chips.\n * Never use for default CTA (that is getProductPrimary / --primary).\n */\nexport function getBrandMark(): string {\n const raw = readCssVar(\"--brand-mark\", \"228 85% 56%\");\n // Factory recipe defaults `--brand-mark: var(--primary)` — resolve to action color.\n if (/var\\(\\s*--primary\\s*\\)/u.test(raw)) {\n return getProductPrimary();\n }\n return asCssColor(raw, BRAND_FALLBACK.brandMark);\n}\n\n/** Foreground on brand-mark surfaces. */\nexport function getBrandMarkForeground(): string {\n const raw = readCssVar(\"--brand-mark-foreground\", \"0 0% 100%\");\n if (/var\\(\\s*--primary-foreground\\s*\\)/u.test(raw)) {\n return asCssColor(\n readCssVar(\"--primary-foreground\", \"0 0% 100%\"),\n BRAND_FALLBACK.brandMarkForeground,\n );\n }\n return asCssColor(raw, BRAND_FALLBACK.brandMarkForeground);\n}\n\nfunction asCssVarColor(name: string, fallback: string): string {\n return asCssColor(readCssVar(name, fallback), fallback);\n}\n\nexport function getBrandAccent(): string {\n return asCssVarColor(\"--brand-accent\", BRAND_FALLBACK.accent);\n}\n\nexport function getBrandTertiary(): string {\n return asCssVarColor(\"--brand-tertiary\", BRAND_FALLBACK.tertiary);\n}\n","/**\n * Utility Functions\n */\n\n/**\n * Merge class names — canonical implementation from ./cn.ts\n * Uses twMerge(clsx(...)) to correctly resolve Tailwind class conflicts.\n */\nexport { cn } from \"./cn\";\nexport { asPlainStyle, withHtmlProps, type PrimitiveProps, type PrimitiveComponent } from \"./primitive-props\";\n\n/**\n * Breakpoint values (matches Primer)\n */\nexport const BREAKPOINTS = {\n xs: 0,\n sm: 544,\n md: 768,\n lg: 1012,\n xl: 1280,\n} as const;\n\nexport type Breakpoint = keyof typeof BREAKPOINTS;\n\n/**\n * Check if viewport is at or above breakpoint\n */\nexport function isBreakpointUp(breakpoint: Breakpoint): boolean {\n if (typeof window === \"undefined\") return true;\n return window.innerWidth >= BREAKPOINTS[breakpoint];\n}\n\nexport {\n BRAND_FALLBACK,\n getBrandAccent,\n getBrandMark,\n getBrandMarkForeground,\n getBrandPrimary,\n getBrandTertiary,\n getProductPrimary,\n readCssVar,\n} from \"./brand-colors\";\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nebutra/ui",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Nebutra design-system UI primitives, patterns, layouts, and token-integrated components",
5
5
  "private": false,
6
6
  "license": "MIT",
@@ -95,11 +95,13 @@
95
95
  "@lobehub/fluent-emoji": "^4.1.0",
96
96
  "@lobehub/icons": "^5.0.1",
97
97
  "@lobehub/ui": "^5.4.0",
98
- "@xyflow/react": "^12.10.2",
99
98
  "@number-flow/react": "^0.5.5",
100
99
  "@paper-design/shaders-react": "^0.0.71",
101
100
  "@phosphor-icons/react": "^2.1.10",
102
101
  "@react-types/shared": "^3.33.1",
102
+ "@tanstack/react-table": "^8.21.3",
103
+ "@tanstack/react-virtual": "^3.13.24",
104
+ "@xyflow/react": "^12.10.2",
103
105
  "antd": "^6.3.1",
104
106
  "antd-style": "^4.1.0",
105
107
  "canvas-confetti": "^1.9.4",
@@ -120,24 +122,26 @@
120
122
  "react-markdown": "^10.1.0",
121
123
  "react-resizable-panels": "^4.7.1",
122
124
  "react-syntax-highlighter": "^16.1.1",
123
- "remark-gfm": "^4.0.1",
124
125
  "react-tweet": "^3.3.0",
125
126
  "react-use-measure": "^2.1.7",
126
127
  "recharts": "^3.7.0",
128
+ "remark-gfm": "^4.0.1",
127
129
  "rough-notation": "^0.5.1",
128
130
  "sonner": "^2.0.7",
129
131
  "streamdown": "^2.5.0",
130
132
  "tailwind-merge": "^3.5.0",
131
133
  "usehooks-ts": "^3.1.1",
132
134
  "vaul": "^1.1.2",
133
- "@nebutra/brand": "0.1.2",
134
- "@nebutra/graph-model": "0.2.2",
135
+ "@nebutra/tokens": "0.1.3",
135
136
  "@nebutra/icons": "0.1.2",
136
- "@nebutra/tokens": "0.1.2"
137
+ "@nebutra/graph-model": "0.2.3",
138
+ "@nebutra/brand": "0.1.2"
137
139
  },
138
140
  "peerDependencies": {
139
141
  "@clerk/nextjs": ">=5.0.0",
142
+ "driver.js": "^1.4.0",
140
143
  "next": ">=14.0.0",
144
+ "next-intl": "^4.12.0",
141
145
  "react": ">=19.0.0",
142
146
  "react-dom": ">=19.0.0"
143
147
  },
@@ -145,11 +149,18 @@
145
149
  "@clerk/nextjs": {
146
150
  "optional": true
147
151
  },
152
+ "driver.js": {
153
+ "optional": true
154
+ },
148
155
  "next": {
149
156
  "optional": true
157
+ },
158
+ "next-intl": {
159
+ "optional": true
150
160
  }
151
161
  },
152
162
  "devDependencies": {
163
+ "@storybook/react": "8.6.17",
153
164
  "@storybook/test": "^8.6.17",
154
165
  "@testing-library/jest-dom": "^6.9.1",
155
166
  "@testing-library/react": "^16.3.2",
@@ -158,13 +169,16 @@
158
169
  "@types/react": "^19.2.14",
159
170
  "@types/react-syntax-highlighter": "^15.5.13",
160
171
  "@types/three": "^0.183.1",
172
+ "@vitejs/plugin-react-swc": "^4.3.1",
173
+ "driver.js": "^1.4.0",
161
174
  "next": "^16.2.6",
175
+ "next-intl": "^4.12.0",
162
176
  "storybook": "^8.6.17",
163
- "@vitejs/plugin-react-swc": "^4.3.1",
164
177
  "tsup": "^8.5.1",
165
178
  "tsx": "^4.21.0",
166
179
  "typescript": "^5.9.3",
167
180
  "vitest": "^4.0.18",
181
+ "jsdom": "^29.1.1",
168
182
  "@nebutra/design-tokens": "0.1.2"
169
183
  },
170
184
  "homepage": "https://github.com/Nebutra/Nebutra-Sailor/tree/main/packages/design/ui#readme",
@@ -183,6 +197,8 @@
183
197
  "@react-three/fiber": "^9.5.0",
184
198
  "three": "^0.183.2"
185
199
  },
200
+ "main": "./dist/index.js",
201
+ "types": "./dist/index.d.ts",
186
202
  "scripts": {
187
203
  "test": "vitest run",
188
204
  "typecheck": "tsc --noEmit",
@@ -25,3 +25,13 @@
25
25
 
26
26
  /* Icon components often appear in class strings next to UI primitives */
27
27
  @source "../../../icons/src/**/*.{ts,tsx,js,jsx}";
28
+
29
+ /*
30
+ * @nebutra/i18n ships rendered components (locale-switcher, market-locale-picker),
31
+ * so its class strings need compiling too. Without this the utilities silently
32
+ * do not exist: the locale switcher asked for `bg-neutral-1` to opt out of the
33
+ * translucent popover surface and got nothing, so the DS default `bg-popover/95`
34
+ * kept winning and the panel stayed see-through — a fix that was correct in the
35
+ * source and absent from the stylesheet.
36
+ */
37
+ @source "../../../../platform/i18n/src/**/*.{ts,tsx,js,jsx}";
@@ -1,42 +1,37 @@
1
1
  /**
2
- * Font CSS Variables — SSOT for typography across Nebutra packages.
3
- * "Precision Stack" — Geist × Geist Mono × Noto Sans SC
2
+ * Font rules — the typography BEHAVIOUR layer (which element gets which stack).
3
+ * The STACKS themselves are owned by @nebutra/tokens, generated from
4
+ * packages/design/design-tokens/tokens/core.json → fontFamily.*.
4
5
  *
5
- * Font loading strategy (Next.js apps):
6
- * Each app's layout.tsx loads Latin fonts via geist/font:
7
- * GeistSans → injects --font-geist-sans (variable font, 100–900)
8
- * GeistMono → injects --font-geist-mono (variable font, 100–900)
6
+ * Font loading (Next.js apps): each app's layout.tsx applies the loader classes
7
+ * GeistSans → --font-geist-sans (variable, 100–900) Latin + numerals
8
+ * GeistMono → --font-geist-mono (variable, 100–900) code
9
+ * cjkFontClassName → --font-vivo-sans-sc (@nebutra/fonts/next/cjk) CJK
10
+ * and the token stacks reference those variables in that order, so Geist keeps
11
+ * Latin and only CJK falls through to vivo Sans SC.
9
12
  *
10
- * The CSS custom properties below reference those injected variables and
11
- * fall back gracefully when font loaders are unavailable (e.g., Storybook, tests).
12
- * CJK uses local/system font fallback to avoid build-time Google font fetches.
13
+ * IMPORT ORDER MATTERS: this file must come AFTER @nebutra/tokens/styles.css
14
+ * (see ../styles/preset.css). It used to re-declare --font-sans / --font-heading /
15
+ * --font-mono here as Geist-only literals; being the later import, those copies
16
+ * won, and the CJK half of every token stack was silently discarded. Declare
17
+ * stacks in the tokens, never here.
13
18
  */
14
19
 
15
20
  /* ============================================
16
- CSS Custom Properties (SSOT)
21
+ CSS Custom Properties — derived aliases only
17
22
  ============================================ */
18
23
 
19
24
  :root {
20
- /* Primary font stack — Geist Sans (geometric precision, variable) */
21
- --font-primary:
22
- var(--font-geist-sans), "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui,
23
- sans-serif;
25
+ /* Legacy alias for the body/UI stack — the stack lives in the tokens. */
26
+ --font-primary: var(--font-sans);
24
27
 
25
- /* Heading font stack — matches primary for Precision Stack */
26
- --font-heading:
27
- var(--font-geist-sans), "Geist", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui,
28
- sans-serif;
29
-
30
- /* Monospace — Geist Mono */
31
- --font-mono:
32
- var(--font-geist-mono), "Geist Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
33
-
34
- /* CJK — local/system fallback, no build-time font fetch */
28
+ /* CJK — appended as a fallback on the rules below (see the note there). The
29
+ token already leads with the self-hosted face; the system families repeat
30
+ here so this alias still degrades on its own. */
35
31
  --font-cjk:
36
32
  var(--font-cn), "Noto Sans SC", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
37
33
 
38
- /* Aliases for Tailwind/shadcn compatibility */
39
- --font-sans: var(--font-primary);
34
+ /* Not a token: no Nebutra surface uses a serif, this exists for shadcn parity. */
40
35
  --font-serif: Georgia, Cambria, "Times New Roman", Times, serif;
41
36
  }
42
37