najm-kit 0.0.20 → 0.0.22

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/dist/index.d.ts CHANGED
@@ -91,7 +91,7 @@ interface NajmThemeConfig {
91
91
  * Per-mode token overrides authored on top of the composed preset. Keeps
92
92
  * light and dark customizations independent: editing a token in one mode
93
93
  * records it here so it survives switching to the other mode and back.
94
- * The provider ignores this field consumers compose `tokens` from it.
94
+ * Providers resolve this field on top of the composed mode/accent preset.
95
95
  */
96
96
  overrides?: Partial<Record<NajmMode, NajmThemeTokens>>;
97
97
  accentOnly?: boolean;
@@ -170,6 +170,12 @@ interface NajmComponentStyleConfig {
170
170
  defaultVariant?: string;
171
171
  defaultSize?: string;
172
172
  density?: NajmDensity;
173
+ /** NTable-only: CSS color used for the table header and add button. */
174
+ headerColor?: string;
175
+ /** NTable-only: CSS color used for table header text and add button text. */
176
+ headerTextColor?: string;
177
+ /** NTable-only: CSS color used for table container and row borders. */
178
+ borderColor?: string;
173
179
  radius?: NajmComponentRadius;
174
180
  borderWidth?: string;
175
181
  slots?: Record<string, NajmSlotStyle>;
@@ -214,6 +220,7 @@ declare function useNajmDesign(): NajmDesignContextValue;
214
220
  declare function useNajmComponentStyle(name: NajmComponentName): NajmComponentStyleConfig | undefined;
215
221
  interface NajmDesignProviderProps {
216
222
  config: NajmDesignConfig;
223
+ mode?: NajmMode;
217
224
  className?: string;
218
225
  children: React$1.ReactNode;
219
226
  }
@@ -222,7 +229,7 @@ interface NajmDesignProviderProps {
222
229
  * tokens, applies typography CSS variables, and exposes component recipes via
223
230
  * context. NajmThemeProvider continues to work standalone for token-only needs.
224
231
  */
225
- declare function NajmDesignProvider({ config, className, children, }: NajmDesignProviderProps): react_jsx_runtime.JSX.Element;
232
+ declare function NajmDesignProvider({ config, mode, className, children, }: NajmDesignProviderProps): react_jsx_runtime.JSX.Element;
226
233
 
227
234
  /** Keeps authored TypeScript design objects type-checked without changing them. */
228
235
  declare function defineNajmDesignConfig(config: NajmDesignConfig): NajmDesignConfig;
@@ -2267,6 +2274,8 @@ interface TableState {
2267
2274
  bordered?: boolean;
2268
2275
  headerClassName: string;
2269
2276
  headerColor: string | undefined;
2277
+ headerTextColor: string | undefined;
2278
+ borderColor: string | undefined;
2270
2279
  showCheckbox: boolean;
2271
2280
  selectedRowId: string | null;
2272
2281
  headerSlot: ReactNode | null;
@@ -2380,6 +2389,8 @@ declare const createTableStore: () => {
2380
2389
  bordered?: () => boolean;
2381
2390
  headerClassName: () => string;
2382
2391
  headerColor: () => string;
2392
+ headerTextColor: () => string;
2393
+ borderColor: () => string;
2383
2394
  showCheckbox: () => boolean;
2384
2395
  selectedRowId: () => string;
2385
2396
  headerSlot: () => ReactNode;
@@ -2459,44 +2470,16 @@ declare const createTableStore: () => {
2459
2470
  };
2460
2471
  };
2461
2472
 
2462
- declare const HEADER_COLORS: {
2463
- readonly primary: {
2464
- readonly bg: "bg-primary/15";
2465
- readonly text: "[&_th]:text-foreground";
2466
- readonly row: "hover:bg-primary/5";
2467
- };
2468
- readonly violet: {
2469
- readonly bg: "bg-violet-600/30";
2470
- readonly text: "[&_th]:text-violet-800 dark:[&_th]:text-violet-300";
2471
- readonly row: "hover:bg-violet-500/5";
2472
- };
2473
- readonly blue: {
2474
- readonly bg: "bg-blue-600/30";
2475
- readonly text: "[&_th]:text-blue-800 dark:[&_th]:text-blue-300";
2476
- readonly row: "hover:bg-blue-500/5";
2477
- };
2478
- readonly emerald: {
2479
- readonly bg: "bg-emerald-600/30";
2480
- readonly text: "[&_th]:text-emerald-800 dark:[&_th]:text-emerald-400";
2481
- readonly row: "hover:bg-emerald-500/5";
2482
- };
2483
- readonly amber: {
2484
- readonly bg: "bg-amber-500/30";
2485
- readonly text: "[&_th]:text-amber-800 dark:[&_th]:text-amber-300";
2486
- readonly row: "hover:bg-amber-500/5";
2487
- };
2488
- readonly rose: {
2489
- readonly bg: "bg-rose-600/30";
2490
- readonly text: "[&_th]:text-rose-800 dark:[&_th]:text-rose-300";
2491
- readonly row: "hover:bg-rose-500/5";
2492
- };
2493
- readonly slate: {
2494
- readonly bg: "bg-slate-700/60";
2495
- readonly text: "[&_th]:text-slate-100 dark:[&_th]:text-slate-400";
2496
- readonly row: "hover:bg-slate-500/5";
2497
- };
2473
+ declare const TABLE_HEADER_COLOR_PRESETS: {
2474
+ readonly primary: "var(--primary)";
2475
+ readonly violet: "#7c3aed";
2476
+ readonly blue: "#2563eb";
2477
+ readonly emerald: "#059669";
2478
+ readonly amber: "#f59e0b";
2479
+ readonly rose: "#e11d48";
2480
+ readonly slate: "#475569";
2498
2481
  };
2499
- type TableHeaderColor = keyof typeof HEADER_COLORS;
2482
+ type TableHeaderColor = keyof typeof TABLE_HEADER_COLOR_PRESETS;
2500
2483
 
2501
2484
  interface NTableState {
2502
2485
  sorting: SortingState;
@@ -2595,7 +2578,12 @@ interface NTableProps<T = any, M extends ViewMode = ViewMode> {
2595
2578
  toolbarLabels?: boolean;
2596
2579
  dynamicHeight?: boolean;
2597
2580
  headerClassName?: string;
2598
- headerColor?: TableHeaderColor;
2581
+ /** CSS color or Najm token name for the table header background. Defaults to primary. */
2582
+ headerColor?: string;
2583
+ /** CSS color or Najm token name for table header text. Defaults to primary foreground. */
2584
+ headerTextColor?: string;
2585
+ /** CSS color or Najm token name for table borders. Defaults to border. */
2586
+ borderColor?: string;
2599
2587
  showCheckbox?: boolean;
2600
2588
  onRowClick?: (row: T) => void;
2601
2589
  onRowContextMenu?: (e: React__default.MouseEvent, row: T) => void;
@@ -2757,6 +2745,8 @@ declare const TableStoreContext: React$1.Context<{
2757
2745
  bordered?: () => boolean;
2758
2746
  headerClassName: () => string;
2759
2747
  headerColor: () => string;
2748
+ headerTextColor: () => string;
2749
+ borderColor: () => string;
2760
2750
  showCheckbox: () => boolean;
2761
2751
  selectedRowId: () => string;
2762
2752
  headerSlot: () => React$1.ReactNode;
@@ -2874,6 +2864,8 @@ declare function useStoreSync(props: any): {
2874
2864
  bordered?: () => boolean;
2875
2865
  headerClassName: () => string;
2876
2866
  headerColor: () => string;
2867
+ headerTextColor: () => string;
2868
+ borderColor: () => string;
2877
2869
  showCheckbox: () => boolean;
2878
2870
  selectedRowId: () => string;
2879
2871
  headerSlot: () => React__default.ReactNode;
package/dist/index.mjs CHANGED
@@ -228,6 +228,33 @@ function radiusToStyle(radius, scale) {
228
228
  }
229
229
  return style;
230
230
  }
231
+ function resolveThemeTokens({
232
+ preset,
233
+ mode,
234
+ accent,
235
+ explicitTokens,
236
+ configTokens,
237
+ configMode,
238
+ overrides
239
+ }) {
240
+ if (explicitTokens) return explicitTokens;
241
+ const tokenMode = mode ?? configMode;
242
+ const modeOverrides = tokenMode ? overrides?.[tokenMode] : void 0;
243
+ const tokensMatchMode = !configMode || !tokenMode || configMode === tokenMode;
244
+ const scopedTokens = tokensMatchMode ? configTokens : void 0;
245
+ if (preset) {
246
+ return { ...resolvePreset(preset), ...scopedTokens ?? {}, ...modeOverrides ?? {} };
247
+ }
248
+ if (tokenMode) {
249
+ return {
250
+ ...composePreset(tokenMode, accent ?? "neutral"),
251
+ ...scopedTokens ?? {},
252
+ ...modeOverrides ?? {}
253
+ };
254
+ }
255
+ if (scopedTokens || modeOverrides) return { ...scopedTokens ?? {}, ...modeOverrides ?? {} };
256
+ return null;
257
+ }
231
258
  var NajmThemeDepthContext = React.createContext(0);
232
259
  var NajmThemeContainerCtx = React.createContext(null);
233
260
  var DEFAULT_APPEARANCE = {};
@@ -255,19 +282,30 @@ function NajmThemeProvider({
255
282
  const effectivePreset = preset ?? config?.preset;
256
283
  const effectiveMode = mode ?? config?.mode;
257
284
  const effectiveAccent = accent ?? config?.accent;
258
- const effectiveTokens = tokens ?? config?.tokens;
259
285
  const effectiveAccentOnly = accentOnly ?? config?.accentOnly;
260
286
  const effectiveRadius = radius ?? config?.radius;
261
287
  const effectiveRadiusScale = radiusScale ?? config?.radiusScale ?? "shadcn";
262
288
  const effectiveSpacing = spacing ?? config?.spacing;
263
289
  const effectiveBorderWidth = appearance?.borderWidth ?? config?.appearance?.borderWidth;
264
- const shouldApplyThemeTokens = Boolean(effectivePreset || effectiveMode || effectiveTokens);
265
290
  const resolved = React.useMemo(() => {
266
- if (!shouldApplyThemeTokens) return null;
267
- if (effectiveTokens) return effectiveTokens;
268
- if (effectivePreset) return resolvePreset(effectivePreset);
269
- return composePreset(effectiveMode, effectiveAccent ?? "neutral");
270
- }, [effectivePreset, effectiveMode, effectiveAccent, effectiveTokens, shouldApplyThemeTokens]);
291
+ return resolveThemeTokens({
292
+ preset: effectivePreset,
293
+ mode: effectiveMode,
294
+ accent: effectiveAccent,
295
+ explicitTokens: tokens,
296
+ configTokens: config?.tokens,
297
+ configMode: config?.mode,
298
+ overrides: config?.overrides
299
+ });
300
+ }, [
301
+ effectivePreset,
302
+ effectiveMode,
303
+ effectiveAccent,
304
+ tokens,
305
+ config?.tokens,
306
+ config?.mode,
307
+ config?.overrides
308
+ ]);
271
309
  const style = React.useMemo(
272
310
  () => {
273
311
  const tokenStyle = resolved ? tokensToStyle(resolved, effectiveAccentOnly) : void 0;
@@ -344,12 +382,15 @@ var ROOT_KEYS = /* @__PURE__ */ new Set([
344
382
  "mode",
345
383
  "accent",
346
384
  "tokens",
385
+ "overrides",
347
386
  "accentOnly",
348
387
  "appearance",
349
388
  "radius",
350
- "radiusScale"
389
+ "radiusScale",
390
+ "spacing"
351
391
  ]);
352
392
  var TOKEN_KEY_SET = new Set(TOKEN_KEYS);
393
+ var MODE_KEY_SET = new Set(MODES);
353
394
  function isRecord(value) {
354
395
  return typeof value === "object" && value !== null && !Array.isArray(value);
355
396
  }
@@ -382,6 +423,18 @@ function parseTokens(value) {
382
423
  }
383
424
  return tokens;
384
425
  }
426
+ function parseOverrides(value) {
427
+ if (value === void 0) return void 0;
428
+ if (!isRecord(value)) throw new TypeError("theme.overrides must be an object");
429
+ assertKnownKeys(value, MODE_KEY_SET, "theme.overrides");
430
+ const overrides = {};
431
+ for (const mode of MODES) {
432
+ const entry = value[mode];
433
+ if (entry === void 0) continue;
434
+ overrides[mode] = parseTokens(entry) ?? {};
435
+ }
436
+ return overrides;
437
+ }
385
438
  function defineNajmThemeConfig(config) {
386
439
  return config;
387
440
  }
@@ -417,16 +470,20 @@ function parseNajmThemeConfig(input) {
417
470
  const mode = optionalEnum(value.mode, MODES, "theme.mode");
418
471
  const accent = optionalEnum(value.accent, ACCENTS, "theme.accent");
419
472
  const tokens = parseTokens(value.tokens);
473
+ const overrides = parseOverrides(value.overrides);
420
474
  const radius = optionalString(value.radius, "theme.radius");
421
475
  const radiusScale = optionalEnum(value.radiusScale, RADIUS_SCALES, "theme.radiusScale");
476
+ const spacing = optionalString(value.spacing, "theme.spacing");
422
477
  if (preset !== void 0) config.preset = preset;
423
478
  if (mode !== void 0) config.mode = mode;
424
479
  if (accent !== void 0) config.accent = accent;
425
480
  if (tokens !== void 0) config.tokens = tokens;
481
+ if (overrides !== void 0) config.overrides = overrides;
426
482
  if (parsedAccentOnly !== void 0) config.accentOnly = parsedAccentOnly;
427
483
  if (appearance !== void 0) config.appearance = appearance;
428
484
  if (radius !== void 0) config.radius = radius;
429
485
  if (radiusScale !== void 0) config.radiusScale = radiusScale;
486
+ if (spacing !== void 0) config.spacing = spacing;
430
487
  return config;
431
488
  }
432
489
  function stringifyNajmThemeConfig(config, space = 2) {
@@ -466,6 +523,7 @@ function layoutToStyle(layout) {
466
523
  }
467
524
  function NajmDesignProvider({
468
525
  config,
526
+ mode,
469
527
  className,
470
528
  children
471
529
  }) {
@@ -485,7 +543,7 @@ function NajmDesignProvider({
485
543
  () => ({ ...layoutStyle ?? {}, ...typographyStyle ?? {} }),
486
544
  [layoutStyle, typographyStyle]
487
545
  );
488
- return /* @__PURE__ */ jsx(NajmDesignContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(NajmThemeProvider, { config: config.theme, className, children: layoutStyle || typographyStyle ? (
546
+ return /* @__PURE__ */ jsx(NajmDesignContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(NajmThemeProvider, { config: config.theme, mode, className, children: layoutStyle || typographyStyle ? (
489
547
  // `display: contents` lets design CSS variables cascade to
490
548
  // children without introducing a layout box that would break height
491
549
  // chains (e.g. h-full) in the consuming app.
@@ -545,6 +603,9 @@ var COMPONENT_KEYS = /* @__PURE__ */ new Set([
545
603
  "defaultVariant",
546
604
  "defaultSize",
547
605
  "density",
606
+ "headerColor",
607
+ "headerTextColor",
608
+ "borderColor",
548
609
  "radius",
549
610
  "borderWidth",
550
611
  "slots",
@@ -639,6 +700,9 @@ function parseComponentStyle(value, path) {
639
700
  const defaultVariant = optionalString2(value.defaultVariant, `${path}.defaultVariant`);
640
701
  const defaultSize = optionalString2(value.defaultSize, `${path}.defaultSize`);
641
702
  const density = optionalEnum2(value.density, DENSITIES, `${path}.density`);
703
+ const headerColor = optionalString2(value.headerColor, `${path}.headerColor`);
704
+ const headerTextColor = optionalString2(value.headerTextColor, `${path}.headerTextColor`);
705
+ const borderColor = optionalString2(value.borderColor, `${path}.borderColor`);
642
706
  const radius = optionalString2(value.radius, `${path}.radius`);
643
707
  const borderWidth = optionalString2(value.borderWidth, `${path}.borderWidth`);
644
708
  if (card !== void 0) cfg.card = card;
@@ -647,6 +711,9 @@ function parseComponentStyle(value, path) {
647
711
  if (defaultVariant !== void 0) cfg.defaultVariant = defaultVariant;
648
712
  if (defaultSize !== void 0) cfg.defaultSize = defaultSize;
649
713
  if (density !== void 0) cfg.density = density;
714
+ if (headerColor !== void 0) cfg.headerColor = headerColor;
715
+ if (headerTextColor !== void 0) cfg.headerTextColor = headerTextColor;
716
+ if (borderColor !== void 0) cfg.borderColor = borderColor;
650
717
  if (radius !== void 0) cfg.radius = radius;
651
718
  if (borderWidth !== void 0) cfg.borderWidth = borderWidth;
652
719
  if (value.slots !== void 0) {
@@ -1233,41 +1300,41 @@ var badgeColorVariants = cva(
1233
1300
  }
1234
1301
  },
1235
1302
  compoundVariants: [
1236
- { color: "primary", look: "solid", class: "bg-pink-500 text-white" },
1237
- { color: "primary", look: "soft", class: "bg-pink-500/10 text-pink-400 border-pink-500/20" },
1238
- { color: "primary", look: ["outline", "dash"], class: "text-pink-400 border-pink-500/60" },
1239
- { color: "secondary", look: "solid", class: "bg-indigo-500 text-white" },
1240
- { color: "secondary", look: "soft", class: "bg-indigo-500/10 text-indigo-400 border-indigo-500/20" },
1241
- { color: "secondary", look: ["outline", "dash"], class: "text-indigo-400 border-indigo-500/60" },
1242
- { color: "accent", look: "solid", class: "bg-orange-500 text-white" },
1243
- { color: "accent", look: "soft", class: "bg-orange-500/10 text-orange-400 border-orange-500/20" },
1244
- { color: "accent", look: ["outline", "dash"], class: "text-orange-400 border-orange-500/60" },
1245
- { color: "neutral", look: "solid", class: "bg-slate-500 text-white" },
1246
- { color: "neutral", look: "soft", class: "bg-slate-500/10 text-slate-400 border-slate-500/20" },
1247
- { color: "neutral", look: ["outline", "dash"], class: "text-slate-400 border-slate-500/60" },
1248
- { color: "info", look: "solid", class: "bg-sky-500 text-white" },
1249
- { color: "info", look: "soft", class: "bg-sky-500/10 text-sky-400 border-sky-500/20" },
1250
- { color: "info", look: ["outline", "dash"], class: "text-sky-400 border-sky-500/60" },
1251
- { color: "success", look: "solid", class: "bg-emerald-500 text-white" },
1252
- { color: "success", look: "soft", class: "bg-emerald-500/10 text-emerald-400 border-emerald-500/20" },
1253
- { color: "success", look: ["outline", "dash"], class: "text-emerald-400 border-emerald-500/60" },
1254
- { color: "warning", look: "solid", class: "bg-amber-400 text-slate-900" },
1255
- { color: "warning", look: "soft", class: "bg-amber-400/10 text-amber-400 border-amber-400/20" },
1256
- { color: "warning", look: ["outline", "dash"], class: "text-amber-400 border-amber-400/60" },
1257
- { color: "destructive", look: "solid", class: "bg-red-500 text-white" },
1258
- { color: "destructive", look: "soft", class: "bg-red-500/10 text-red-400 border-red-500/20" },
1259
- { color: "destructive", look: ["outline", "dash"], class: "text-red-400 border-red-500/60" }
1303
+ { color: "primary", look: "solid", class: "bg-primary text-primary-foreground" },
1304
+ { color: "primary", look: "soft", class: "bg-primary/10 text-primary border-primary/20" },
1305
+ { color: "primary", look: ["outline", "dash"], class: "text-primary border-primary/60" },
1306
+ { color: "secondary", look: "solid", class: "bg-secondary text-secondary-foreground" },
1307
+ { color: "secondary", look: "soft", class: "bg-secondary/10 text-secondary-foreground border-secondary/30" },
1308
+ { color: "secondary", look: ["outline", "dash"], class: "text-secondary-foreground border-secondary/40" },
1309
+ { color: "accent", look: "solid", class: "bg-accent text-accent-foreground" },
1310
+ { color: "accent", look: "soft", class: "bg-accent/10 text-accent-foreground border-accent/30" },
1311
+ { color: "accent", look: ["outline", "dash"], class: "text-accent-foreground border-accent/40" },
1312
+ { color: "neutral", look: "solid", class: "bg-neutral text-neutral-foreground" },
1313
+ { color: "neutral", look: "soft", class: "bg-neutral/10 text-neutral-foreground border-neutral/30" },
1314
+ { color: "neutral", look: ["outline", "dash"], class: "text-neutral-foreground border-neutral/40" },
1315
+ { color: "info", look: "solid", class: "bg-info text-info-foreground" },
1316
+ { color: "info", look: "soft", class: "bg-info/10 text-info border-info/20" },
1317
+ { color: "info", look: ["outline", "dash"], class: "text-info border-info/60" },
1318
+ { color: "success", look: "solid", class: "bg-success text-success-foreground" },
1319
+ { color: "success", look: "soft", class: "bg-success/10 text-success border-success/20" },
1320
+ { color: "success", look: ["outline", "dash"], class: "text-success border-success/60" },
1321
+ { color: "warning", look: "solid", class: "bg-warning text-warning-foreground" },
1322
+ { color: "warning", look: "soft", class: "bg-warning/10 text-warning border-warning/20" },
1323
+ { color: "warning", look: ["outline", "dash"], class: "text-warning border-warning/60" },
1324
+ { color: "destructive", look: "solid", class: "bg-destructive text-destructive-foreground" },
1325
+ { color: "destructive", look: "soft", class: "bg-destructive/10 text-destructive border-destructive/20" },
1326
+ { color: "destructive", look: ["outline", "dash"], class: "text-destructive border-destructive/60" }
1260
1327
  ],
1261
1328
  defaultVariants: { color: "primary", look: "solid", size: "md", shape: "default" }
1262
1329
  }
1263
1330
  );
1264
1331
  var COLOR_TEXT_MAP = {
1265
- success: "text-emerald-600",
1266
- warning: "text-amber-500",
1267
- accent: "text-orange-500",
1268
- info: "text-sky-600",
1269
- neutral: "text-slate-500",
1270
- destructive: "text-red-600"
1332
+ success: "text-success",
1333
+ warning: "text-warning",
1334
+ accent: "text-accent-foreground",
1335
+ info: "text-info",
1336
+ neutral: "text-neutral-foreground",
1337
+ destructive: "text-destructive"
1271
1338
  };
1272
1339
  var SIZE_TEXT_MAP = {
1273
1340
  sm: "text-xs",
@@ -1306,7 +1373,7 @@ function Badge({
1306
1373
  const effVariant = variant ?? aliased.variant ?? recipe?.defaultVariant;
1307
1374
  const recipeRadius = shape === void 0 ? resolveRadiusValue(recipe?.radius) : void 0;
1308
1375
  const recipeStyle = recipeRadius ? { borderRadius: recipeRadius, ...style } : style;
1309
- const resolvedColor = statusMap?.[status ?? ""] ?? color;
1376
+ const resolvedColor = statusMap?.[status ?? ""] ?? color ?? "primary";
1310
1377
  const resolvedLabel = label ?? (typeof children === "string" ? children : void 0) ?? status;
1311
1378
  const content = typeof children !== "string" && children ? children : resolvedLabel ?? children;
1312
1379
  const resolvedIcon = icon ?? (resolvedColor ? iconMap?.[resolvedColor] : void 0);
@@ -7893,7 +7960,9 @@ var createTableStore = () => {
7893
7960
  classNames: {},
7894
7961
  bordered: void 0,
7895
7962
  headerClassName: "bg-card",
7896
- headerColor: "primary",
7963
+ headerColor: void 0,
7964
+ headerTextColor: void 0,
7965
+ borderColor: void 0,
7897
7966
  showCheckbox: true,
7898
7967
  selectedRowId: null,
7899
7968
  headerSlot: null,
@@ -8303,17 +8372,47 @@ function useTableKeyboard(options = {}) {
8303
8372
  }
8304
8373
 
8305
8374
  // src/components/table/tableColors.ts
8306
- var HEADER_COLORS = {
8307
- primary: { bg: "bg-primary/15", text: "[&_th]:text-foreground", row: "hover:bg-primary/5" },
8308
- violet: { bg: "bg-violet-600/30", text: "[&_th]:text-violet-800 dark:[&_th]:text-violet-300", row: "hover:bg-violet-500/5" },
8309
- blue: { bg: "bg-blue-600/30", text: "[&_th]:text-blue-800 dark:[&_th]:text-blue-300", row: "hover:bg-blue-500/5" },
8310
- emerald: { bg: "bg-emerald-600/30", text: "[&_th]:text-emerald-800 dark:[&_th]:text-emerald-400", row: "hover:bg-emerald-500/5" },
8311
- amber: { bg: "bg-amber-500/30", text: "[&_th]:text-amber-800 dark:[&_th]:text-amber-300", row: "hover:bg-amber-500/5" },
8312
- rose: { bg: "bg-rose-600/30", text: "[&_th]:text-rose-800 dark:[&_th]:text-rose-300", row: "hover:bg-rose-500/5" },
8313
- slate: { bg: "bg-slate-700/60", text: "[&_th]:text-slate-100 dark:[&_th]:text-slate-400", row: "hover:bg-slate-500/5" }
8314
- };
8315
- var HEADER_HEX = {
8316
- primary: "",
8375
+ var DEFAULT_TABLE_HEADER_COLOR = "var(--primary)";
8376
+ var DEFAULT_TABLE_HEADER_TEXT_COLOR = "var(--primary-foreground)";
8377
+ var DEFAULT_TABLE_BORDER_COLOR = "var(--border)";
8378
+ var THEME_COLOR_TOKENS = /* @__PURE__ */ new Set([
8379
+ "background",
8380
+ "foreground",
8381
+ "card",
8382
+ "card-foreground",
8383
+ "popover",
8384
+ "popover-foreground",
8385
+ "primary",
8386
+ "primary-foreground",
8387
+ "secondary",
8388
+ "secondary-foreground",
8389
+ "tertiary",
8390
+ "tertiary-foreground",
8391
+ "muted",
8392
+ "muted-foreground",
8393
+ "accent",
8394
+ "accent-foreground",
8395
+ "destructive",
8396
+ "destructive-foreground",
8397
+ "border",
8398
+ "input",
8399
+ "ring",
8400
+ "sidebar",
8401
+ "sidebar-foreground",
8402
+ "sidebar-primary",
8403
+ "sidebar-primary-foreground",
8404
+ "sidebar-accent",
8405
+ "sidebar-accent-foreground",
8406
+ "sidebar-border",
8407
+ "sidebar-ring",
8408
+ "chart-1",
8409
+ "chart-2",
8410
+ "chart-3",
8411
+ "chart-4",
8412
+ "chart-5"
8413
+ ]);
8414
+ var TABLE_HEADER_COLOR_PRESETS = {
8415
+ primary: DEFAULT_TABLE_HEADER_COLOR,
8317
8416
  violet: "#7c3aed",
8318
8417
  blue: "#2563eb",
8319
8418
  emerald: "#059669",
@@ -8321,6 +8420,17 @@ var HEADER_HEX = {
8321
8420
  rose: "#e11d48",
8322
8421
  slate: "#475569"
8323
8422
  };
8423
+ function resolveTableColor(value, fallback) {
8424
+ const color = value?.trim();
8425
+ if (!color) return fallback;
8426
+ if (color in TABLE_HEADER_COLOR_PRESETS) {
8427
+ return TABLE_HEADER_COLOR_PRESETS[color];
8428
+ }
8429
+ if (THEME_COLOR_TOKENS.has(color)) {
8430
+ return `var(--${color})`;
8431
+ }
8432
+ return color;
8433
+ }
8324
8434
  var ROW_CONTEXT_HANDLED = "__ntableRowContextHandled";
8325
8435
  function EditableCell({ cell, onCellEdit }) {
8326
8436
  const columnDef = cell.column.columnDef;
@@ -8388,7 +8498,20 @@ function NTableContent({ effectiveMode }) {
8388
8498
  const noResultsText = useTableStore.use.noResultsText();
8389
8499
  const headerClassName = useTableStore.use.headerClassName();
8390
8500
  const headerColor = useTableStore.use.headerColor();
8391
- const colorStyle = headerColor ? HEADER_COLORS[headerColor] : null;
8501
+ const headerTextColor = useTableStore.use.headerTextColor();
8502
+ const tableBorderColor = useTableStore.use.borderColor();
8503
+ const resolvedHeaderColor = resolveTableColor(headerColor, DEFAULT_TABLE_HEADER_COLOR);
8504
+ const resolvedHeaderTextColor = resolveTableColor(headerTextColor, DEFAULT_TABLE_HEADER_TEXT_COLOR);
8505
+ const resolvedBorderColor = resolveTableColor(tableBorderColor, DEFAULT_TABLE_BORDER_COLOR);
8506
+ const headerCellStyle = {
8507
+ backgroundColor: resolvedHeaderColor,
8508
+ color: resolvedHeaderTextColor
8509
+ };
8510
+ const contentStyle = recipeStyle || tableBorderColor ? {
8511
+ ...recipeStyle ?? {},
8512
+ ...tableBorderColor ? { borderColor: resolvedBorderColor } : {}
8513
+ } : void 0;
8514
+ const rowBorderStyle = tableBorderColor ? { borderColor: resolvedBorderColor } : void 0;
8392
8515
  const onRowClick = useTableStore.use.onRowClick();
8393
8516
  const onRowContextMenu = useTableStore.use.onRowContextMenu();
8394
8517
  const onBackgroundContextMenu = useTableStore.use.onBackgroundContextMenu();
@@ -8431,23 +8554,45 @@ function NTableContent({ effectiveMode }) {
8431
8554
  bordered === true ? surfaceBorderClasses(true) : "shadow-sm",
8432
8555
  classNames?.content
8433
8556
  ),
8434
- style: recipeStyle,
8557
+ style: contentStyle,
8435
8558
  onContextMenu: handleBackgroundContextMenu,
8436
8559
  children: /* @__PURE__ */ jsxs(Table, { children: [
8437
- /* @__PURE__ */ jsx(TableHeader, { "data-ntable-table-header": true, className: cn("bg-card sticky top-0 z-10", colorStyle?.text, headerClassName, bordered === true && "[&_tr]:border-border", classNames?.tableHeader), children: table.getHeaderGroups().map((hg) => /* @__PURE__ */ jsxs(TableRow, { className: cn("hover:bg-transparent", bordered === true && "border-border"), children: [
8438
- showCheckbox && /* @__PURE__ */ jsx(TableHead, { className: cn("w-10 text-foreground h-12 text-center", colorStyle?.bg), children: /* @__PURE__ */ jsx(
8439
- Checkbox,
8560
+ /* @__PURE__ */ jsx(TableHeader, { "data-ntable-table-header": true, className: cn("bg-card sticky top-0 z-10", headerClassName, bordered === true && "[&_tr]:border-border", classNames?.tableHeader), children: table.getHeaderGroups().map((hg) => /* @__PURE__ */ jsxs(TableRow, { style: rowBorderStyle, className: cn("hover:bg-transparent", bordered === true && "border-border"), children: [
8561
+ showCheckbox && /* @__PURE__ */ jsx(
8562
+ TableHead,
8440
8563
  {
8441
- "aria-label": "Select all rows",
8442
- checked: table.getIsAllPageRowsSelected(),
8443
- onCheckedChange: (value) => table.toggleAllPageRowsSelected(!!value)
8564
+ className: "w-10 text-foreground h-12 text-center",
8565
+ style: headerCellStyle,
8566
+ children: /* @__PURE__ */ jsx(
8567
+ Checkbox,
8568
+ {
8569
+ "aria-label": "Select all rows",
8570
+ checked: table.getIsAllPageRowsSelected(),
8571
+ onCheckedChange: (value) => table.toggleAllPageRowsSelected(!!value)
8572
+ }
8573
+ )
8444
8574
  }
8445
- ) }),
8446
- hasExpansion && /* @__PURE__ */ jsx(TableHead, { "aria-label": "Expand column", className: cn("w-10 text-foreground h-12", colorStyle?.bg) }),
8447
- hg.headers.map((header) => /* @__PURE__ */ jsx(TableHead, { className: cn("text-foreground h-12", colorStyle?.bg), children: header.isPlaceholder ? null : /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-2", header.column.getCanSort() && showSorting && "cursor-pointer select-none"), onClick: header.column.getToggleSortingHandler(), children: [
8448
- flexRender(header.column.columnDef.header, header.getContext()),
8449
- header.column.getCanSort() && showSorting && /* @__PURE__ */ jsx("span", { children: getSortIcon(header.column) })
8450
- ] }) }, header.id))
8575
+ ),
8576
+ hasExpansion && /* @__PURE__ */ jsx(
8577
+ TableHead,
8578
+ {
8579
+ "aria-label": "Expand column",
8580
+ className: "w-10 text-foreground h-12",
8581
+ style: headerCellStyle
8582
+ }
8583
+ ),
8584
+ hg.headers.map((header) => /* @__PURE__ */ jsx(
8585
+ TableHead,
8586
+ {
8587
+ className: "text-foreground h-12",
8588
+ style: headerCellStyle,
8589
+ children: header.isPlaceholder ? null : /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-2", header.column.getCanSort() && showSorting && "cursor-pointer select-none"), onClick: header.column.getToggleSortingHandler(), children: [
8590
+ flexRender(header.column.columnDef.header, header.getContext()),
8591
+ header.column.getCanSort() && showSorting && /* @__PURE__ */ jsx("span", { children: getSortIcon(header.column) })
8592
+ ] })
8593
+ },
8594
+ header.id
8595
+ ))
8451
8596
  ] }, hg.id)) }),
8452
8597
  /* @__PURE__ */ jsx(TableBody, { children: table.getRowModel().rows?.length ? table.getRowModel().rows.map((row) => {
8453
8598
  const isSelectedByRowId = Boolean(selectedRowId && row.original?.id === selectedRowId);
@@ -8467,7 +8612,8 @@ function NTableContent({ effectiveMode }) {
8467
8612
  e.nativeEvent[ROW_CONTEXT_HANDLED] = true;
8468
8613
  onRowContextMenu(e, row.original);
8469
8614
  } : void 0,
8470
- className: cn(colorStyle?.row, classNames?.row, onRowClick && "cursor-pointer", isSelectedByRowId && "bg-primary/5 hover:bg-primary/5", bordered === true && "border-border"),
8615
+ style: rowBorderStyle,
8616
+ className: cn(classNames?.row, onRowClick && "cursor-pointer", isSelectedByRowId && "bg-primary/5 hover:bg-primary/5", bordered === true && "border-border"),
8471
8617
  children: [
8472
8618
  showCheckbox && /* @__PURE__ */ jsx(TableCell, { className: "h-14 w-10 text-center", children: /* @__PURE__ */ jsx(
8473
8619
  Checkbox,
@@ -8504,9 +8650,9 @@ function NTableContent({ effectiveMode }) {
8504
8650
  ]
8505
8651
  }
8506
8652
  ),
8507
- isExpanded && renderSubRow && /* @__PURE__ */ jsx(TableRow, { "data-expanded-row": "true", className: "bg-muted/40 hover:bg-muted/40", children: /* @__PURE__ */ jsx(TableCell, { colSpan: totalCols, className: "p-0", children: renderSubRow(row.original) }) })
8653
+ isExpanded && renderSubRow && /* @__PURE__ */ jsx(TableRow, { "data-expanded-row": "true", style: rowBorderStyle, className: "bg-muted/40 hover:bg-muted/40", children: /* @__PURE__ */ jsx(TableCell, { colSpan: totalCols, className: "p-0", children: renderSubRow(row.original) }) })
8508
8654
  ] }, row.id);
8509
- }) : /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, { colSpan: columns.length + (showCheckbox ? 1 : 0) + (hasExpansion ? 1 : 0), className: "h-16 text-center", children: noResultsText }) }) })
8655
+ }) : /* @__PURE__ */ jsx(TableRow, { style: rowBorderStyle, children: /* @__PURE__ */ jsx(TableCell, { colSpan: columns.length + (showCheckbox ? 1 : 0) + (hasExpansion ? 1 : 0), className: "h-16 text-center", children: noResultsText }) }) })
8510
8656
  ] })
8511
8657
  }
8512
8658
  );
@@ -8979,11 +9125,13 @@ function TableAddButton() {
8979
9125
  const showAddButton = useTableStore.use.showAddButton();
8980
9126
  const addButtonText = useTableStore.use.addButtonText();
8981
9127
  const headerColor = useTableStore.use.headerColor();
9128
+ const headerTextColor = useTableStore.use.headerTextColor();
8982
9129
  const bordered = useTableStore.use.bordered();
8983
9130
  if (!showAddButton) return null;
8984
- const accentHex = headerColor ? HEADER_HEX[headerColor] : void 0;
8985
- const btnStyle = accentHex ? { backgroundColor: accentHex } : void 0;
8986
- const baseCls = accentHex ? "hover:opacity-90" : "bg-primary hover:bg-primary/90";
9131
+ const btnStyle = {
9132
+ backgroundColor: resolveTableColor(headerColor, DEFAULT_TABLE_HEADER_COLOR),
9133
+ color: resolveTableColor(headerTextColor, DEFAULT_TABLE_HEADER_TEXT_COLOR)
9134
+ };
8987
9135
  const borderedCls = bordered ? "border border-muted-foreground" : "";
8988
9136
  return /* @__PURE__ */ jsx(
8989
9137
  "button",
@@ -8994,7 +9142,7 @@ function TableAddButton() {
8994
9142
  "aria-label": addButtonText || "Create",
8995
9143
  title: addButtonText || "Create",
8996
9144
  "data-bordered": bordered ? "true" : void 0,
8997
- className: `h-10 w-10 shrink-0 cursor-pointer flex items-center justify-center rounded-lg text-white transition-opacity ${baseCls} ${borderedCls}`,
9145
+ className: `h-10 w-10 shrink-0 cursor-pointer flex items-center justify-center rounded-lg transition-opacity hover:opacity-90 ${borderedCls}`,
8998
9146
  children: /* @__PURE__ */ jsx(Plus, { className: "h-5 w-5" })
8999
9147
  }
9000
9148
  );
@@ -9263,6 +9411,8 @@ function TableLayout(props) {
9263
9411
  ] });
9264
9412
  }
9265
9413
  function NTable(props) {
9414
+ const recipe = useNajmComponentStyle("table");
9415
+ const recipeBordered = Boolean(recipe?.borderColor || recipe?.borderWidth);
9266
9416
  const availableModes = props.availableModes ?? ["table", "cards", "json"];
9267
9417
  const lastInvalidModeRef = useRef(void 0);
9268
9418
  let mode = props.mode;
@@ -9353,9 +9503,11 @@ function NTable(props) {
9353
9503
  CardComponent: props.renderCard ?? null,
9354
9504
  className: props.className ?? "",
9355
9505
  classNames: props.classNames ?? {},
9356
- bordered: props.bordered,
9506
+ bordered: props.bordered ?? (recipeBordered ? true : void 0),
9357
9507
  headerClassName: props.headerClassName ?? "bg-card",
9358
- headerColor: props.headerColor ?? "primary",
9508
+ headerColor: props.headerColor ?? recipe?.headerColor,
9509
+ headerTextColor: props.headerTextColor ?? recipe?.headerTextColor,
9510
+ borderColor: props.borderColor ?? recipe?.borderColor,
9359
9511
  showCheckbox: props.showCheckbox ?? true,
9360
9512
  selectedRowId: props.selectedRowId ?? null,
9361
9513
  headerSlot: props.headerSlot ?? null,
package/dist/json.mjs CHANGED
@@ -298,6 +298,7 @@ function resolveRadiusValue(radius) {
298
298
  }
299
299
 
300
300
  // src/theme/config.ts
301
+ var MODES = ["light", "dark"];
301
302
  var TOKEN_KEYS = [
302
303
  "background",
303
304
  "foreground",
@@ -336,6 +337,7 @@ var TOKEN_KEYS = [
336
337
  "radius"
337
338
  ];
338
339
  new Set(TOKEN_KEYS);
340
+ new Set(MODES);
339
341
 
340
342
  // src/theme/design-config.ts
341
343
  new Set(NAJM_COMPONENT_NAMES);
package/dist/theme.css CHANGED
@@ -47,6 +47,14 @@
47
47
  --color-muted-foreground: var(--muted-foreground);
48
48
  --color-accent: var(--accent);
49
49
  --color-accent-foreground: var(--accent-foreground);
50
+ --color-neutral: var(--neutral);
51
+ --color-neutral-foreground: var(--neutral-foreground);
52
+ --color-info: var(--info);
53
+ --color-info-foreground: var(--info-foreground);
54
+ --color-success: var(--success);
55
+ --color-success-foreground: var(--success-foreground);
56
+ --color-warning: var(--warning);
57
+ --color-warning-foreground: var(--warning-foreground);
50
58
  --color-destructive: var(--destructive);
51
59
  --color-destructive-foreground: var(--destructive-foreground);
52
60
  --color-border: var(--border);
@@ -86,6 +94,42 @@
86
94
  100% { transform: translateX(200%); width: 40%; }
87
95
  }
88
96
 
97
+ /* Typography variables emitted by NajmDesignProvider. */
98
+ [data-najm-design-vars] {
99
+ font-family: var(--font-sans, ui-sans-serif, system-ui, sans-serif);
100
+ font-size: var(--font-size-base, 14px);
101
+ line-height: var(--line-height-base, 1.5);
102
+ letter-spacing: var(--letter-spacing-base, 0);
103
+ --text-xs: calc(var(--font-size-base, 14px) * 0.857142857 * var(--font-scale, 1));
104
+ --text-xs--line-height: var(--line-height-base, 1.5);
105
+ --text-sm: calc(var(--font-size-base, 14px) * var(--font-scale, 1));
106
+ --text-sm--line-height: var(--line-height-base, 1.5);
107
+ --text-base: calc(var(--font-size-base, 14px) * 1.142857143 * var(--font-scale, 1));
108
+ --text-base--line-height: var(--line-height-base, 1.5);
109
+ --text-lg: calc(var(--font-size-base, 14px) * 1.285714286 * var(--font-scale, 1));
110
+ --text-lg--line-height: var(--line-height-base, 1.5);
111
+ --text-xl: calc(var(--font-size-base, 14px) * 1.428571429 * var(--font-scale, 1));
112
+ --text-xl--line-height: var(--line-height-base, 1.5);
113
+ }
114
+
115
+ [data-najm-design-vars] :where(
116
+ h1,
117
+ h2,
118
+ h3,
119
+ h4,
120
+ h5,
121
+ h6,
122
+ [data-slot="card-title"],
123
+ [data-slot="dialog-title"],
124
+ [data-slot="sheet-title"]
125
+ ) {
126
+ font-family: var(--font-heading, var(--font-sans, ui-sans-serif, system-ui, sans-serif));
127
+ }
128
+
129
+ [data-najm-design-vars] :where(code, kbd, pre, samp) {
130
+ font-family: var(--font-mono, ui-monospace, monospace);
131
+ }
132
+
89
133
  /* Default token values (oklch). Override globally via :root / .dark, or per
90
134
  subtree via <NajmThemeProvider>. */
91
135
  :root {
@@ -105,6 +149,14 @@
105
149
  --muted-foreground: oklch(0.5555 0 0);
106
150
  --accent: oklch(0.2747 0.006 286.001);
107
151
  --accent-foreground: oklch(0.9848 0 0);
152
+ --neutral: oklch(0.556 0 0);
153
+ --neutral-foreground: oklch(0.9848 0 0);
154
+ --info: oklch(0.6231 0.1679 254.149);
155
+ --info-foreground: oklch(0.9848 0 0);
156
+ --success: oklch(0.696 0.17 162.48);
157
+ --success-foreground: oklch(0.1445 0 0);
158
+ --warning: oklch(0.769 0.188 70.08);
159
+ --warning-foreground: oklch(0.1445 0 0);
108
160
  --destructive: oklch(0.6368 0.2078 25.326);
109
161
  --destructive-foreground: oklch(0.9848 0 0);
110
162
  --border: oklch(0.9219 0 0);
@@ -147,6 +199,14 @@
147
199
  --muted-foreground: oklch(0.6255 0.0171 285.896);
148
200
  --accent: oklch(0.2313 0.007 285.839);
149
201
  --accent-foreground: oklch(0.9683 0.0014 286.375);
202
+ --neutral: oklch(0.7155 0 0);
203
+ --neutral-foreground: oklch(0.1445 0 0);
204
+ --info: oklch(0.6231 0.1679 254.149);
205
+ --info-foreground: oklch(0.9683 0.0014 286.375);
206
+ --success: oklch(0.696 0.17 162.48);
207
+ --success-foreground: oklch(0.1445 0 0);
208
+ --warning: oklch(0.769 0.188 70.08);
209
+ --warning-foreground: oklch(0.1445 0 0);
150
210
  --destructive: oklch(0.6356 0.2082 25.378);
151
211
  --destructive-foreground: oklch(1 0 0);
152
212
  --border: oklch(0.262 0.0085 285.799);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "najm-kit",
3
- "version": "0.0.20",
3
+ "version": "0.0.22",
4
4
  "type": "module",
5
5
  "description": "Reusable React UI component package for Najm framework",
6
6
  "main": "./dist/index.mjs",