@repraze/kernelkit 0.1.0-alpha.1 → 0.1.0-alpha.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.
Files changed (117) hide show
  1. package/dist/components/alert.d.ts.map +1 -1
  2. package/dist/components/alert.js +4 -6
  3. package/dist/components/alert.js.map +1 -1
  4. package/dist/components/avatar.js +1 -1
  5. package/dist/components/avatar.js.map +1 -1
  6. package/dist/components/badge.js +1 -1
  7. package/dist/components/badge.js.map +1 -1
  8. package/dist/components/breadcrumb.d.ts +1 -0
  9. package/dist/components/breadcrumb.d.ts.map +1 -1
  10. package/dist/components/breadcrumb.js +7 -6
  11. package/dist/components/breadcrumb.js.map +1 -1
  12. package/dist/components/calendar.js +1 -1
  13. package/dist/components/calendar.js.map +1 -1
  14. package/dist/components/callout.d.ts +8 -4
  15. package/dist/components/callout.d.ts.map +1 -1
  16. package/dist/components/callout.js +22 -12
  17. package/dist/components/callout.js.map +1 -1
  18. package/dist/components/chart.js +2 -2
  19. package/dist/components/chart.js.map +1 -1
  20. package/dist/components/code-block.js +1 -1
  21. package/dist/components/code-block.js.map +1 -1
  22. package/dist/components/command.d.ts +1 -1
  23. package/dist/components/command.d.ts.map +1 -1
  24. package/dist/components/command.js +3 -3
  25. package/dist/components/command.js.map +1 -1
  26. package/dist/components/data-table/elements/data-table-info-row.js +1 -1
  27. package/dist/components/data-table/elements/data-table-info-row.js.map +1 -1
  28. package/dist/components/dropdown-menu.d.ts +1 -1
  29. package/dist/components/dropdown-menu.d.ts.map +1 -1
  30. package/dist/components/dropdown-menu.js +2 -2
  31. package/dist/components/dropdown-menu.js.map +1 -1
  32. package/dist/components/kbd.d.ts.map +1 -1
  33. package/dist/components/kbd.js +2 -4
  34. package/dist/components/kbd.js.map +1 -1
  35. package/dist/components/message.js +2 -2
  36. package/dist/components/message.js.map +1 -1
  37. package/dist/components/meter.js +1 -1
  38. package/dist/components/meter.js.map +1 -1
  39. package/dist/components/navigation-menu.d.ts.map +1 -1
  40. package/dist/components/navigation-menu.js +3 -1
  41. package/dist/components/navigation-menu.js.map +1 -1
  42. package/dist/components/progress.js +1 -1
  43. package/dist/components/progress.js.map +1 -1
  44. package/dist/components/questionnaire.js +1 -1
  45. package/dist/components/questionnaire.js.map +1 -1
  46. package/dist/components/search-trigger.d.ts +1 -1
  47. package/dist/components/search-trigger.d.ts.map +1 -1
  48. package/dist/components/search-trigger.js +2 -2
  49. package/dist/components/search-trigger.js.map +1 -1
  50. package/dist/components/section.js +1 -1
  51. package/dist/components/section.js.map +1 -1
  52. package/dist/components/sidebar.d.ts.map +1 -1
  53. package/dist/components/sidebar.js +4 -3
  54. package/dist/components/sidebar.js.map +1 -1
  55. package/dist/components/site-footer.js +1 -1
  56. package/dist/components/site-footer.js.map +1 -1
  57. package/dist/components/site-header.d.ts +3 -3
  58. package/dist/components/site-header.d.ts.map +1 -1
  59. package/dist/components/site-header.js +7 -9
  60. package/dist/components/site-header.js.map +1 -1
  61. package/dist/components/stat.js +2 -2
  62. package/dist/components/stat.js.map +1 -1
  63. package/dist/components/tabs.d.ts +4 -2
  64. package/dist/components/tabs.d.ts.map +1 -1
  65. package/dist/components/tabs.js +13 -7
  66. package/dist/components/tabs.js.map +1 -1
  67. package/dist/components/theme-menu.d.ts +49 -0
  68. package/dist/components/theme-menu.d.ts.map +1 -0
  69. package/dist/components/theme-menu.js +75 -0
  70. package/dist/components/theme-menu.js.map +1 -0
  71. package/dist/components/toc.js +1 -1
  72. package/dist/components/toc.js.map +1 -1
  73. package/dist/components/tooltip.d.ts.map +1 -1
  74. package/dist/components/tooltip.js +5 -2
  75. package/dist/components/tooltip.js.map +1 -1
  76. package/dist/components/trace.js +1 -1
  77. package/dist/components/trace.js.map +1 -1
  78. package/dist/lib/recipes.d.ts +17 -1
  79. package/dist/lib/recipes.d.ts.map +1 -1
  80. package/dist/lib/recipes.js +19 -1
  81. package/dist/lib/recipes.js.map +1 -1
  82. package/dist/provider/theme-provider.d.ts +4 -0
  83. package/dist/provider/theme-provider.d.ts.map +1 -1
  84. package/dist/provider/theme-provider.js +14 -1
  85. package/dist/provider/theme-provider.js.map +1 -1
  86. package/package.json +1 -1
  87. package/src/components/alert.tsx +4 -6
  88. package/src/components/avatar.tsx +1 -1
  89. package/src/components/badge.tsx +1 -1
  90. package/src/components/breadcrumb.tsx +8 -7
  91. package/src/components/calendar.tsx +1 -1
  92. package/src/components/callout.tsx +27 -12
  93. package/src/components/chart.tsx +2 -2
  94. package/src/components/code-block.tsx +1 -1
  95. package/src/components/command.tsx +3 -4
  96. package/src/components/data-table/elements/data-table-info-row.tsx +1 -1
  97. package/src/components/dropdown-menu.tsx +2 -2
  98. package/src/components/kbd.tsx +2 -4
  99. package/src/components/message.tsx +2 -2
  100. package/src/components/meter.tsx +1 -1
  101. package/src/components/navigation-menu.tsx +4 -0
  102. package/src/components/progress.tsx +1 -1
  103. package/src/components/questionnaire.tsx +1 -1
  104. package/src/components/search-trigger.tsx +2 -3
  105. package/src/components/section.tsx +1 -1
  106. package/src/components/sidebar.tsx +4 -3
  107. package/src/components/site-footer.tsx +1 -1
  108. package/src/components/site-header.tsx +8 -9
  109. package/src/components/stat.tsx +2 -2
  110. package/src/components/tabs.tsx +17 -9
  111. package/src/components/theme-menu.tsx +243 -0
  112. package/src/components/toc.tsx +1 -1
  113. package/src/components/tooltip.tsx +15 -3
  114. package/src/components/trace.tsx +1 -1
  115. package/src/lib/recipes.ts +23 -2
  116. package/src/provider/theme-provider.tsx +18 -1
  117. package/src/styles/kernelkit.css +4 -0
@@ -32,7 +32,7 @@ function Calendar({
32
32
  <DayPicker
33
33
  showOutsideDays={showOutsideDays}
34
34
  className={cn(
35
- "group/calendar bg-background p-2 [--cell-radius:var(--radius-md)] [--cell-size:--spacing(8)] in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent",
35
+ "group/calendar bg-background p-2 [--cell-radius:var(--radius-md)] [--cell-size:--spacing(8)] in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent in-data-[slot=date-picker-content]:bg-transparent",
36
36
  String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
37
37
  String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
38
38
  className,
@@ -2,44 +2,55 @@
2
2
 
3
3
  import {Collapsible, CollapsibleContent, CollapsibleTrigger} from "@repraze/kernelkit/components/collapsible";
4
4
  import {type Color, colorVariants} from "@repraze/kernelkit/lib/color";
5
- import {heading} from "@repraze/kernelkit/lib/recipes";
5
+ import {heading, statusSurface, statusSurfaceNeutral} from "@repraze/kernelkit/lib/recipes";
6
6
  import {cva} from "class-variance-authority";
7
7
  import {cn} from "cn";
8
8
  import {ChevronDownIcon} from "lucide-react";
9
9
  import type * as React from "react";
10
10
 
11
11
  /**
12
- * Long-form aside for docs and articles (note, tip, warning). A left accent bar carries the color;
13
- * the surface is a faint wash so body text stays foreground. For app status use Alert.
12
+ * Long-form aside for docs and articles (note, tip, warning). The color rides on the suite's row
13
+ * indicator (the sidebar and menu pill: 2px, rounded, flush left, inset from the top and bottom).
14
+ * Surfaces share Alert's `soft` / `outline` variants. For app status use Alert.
14
15
  */
15
16
  const calloutVariants = cva(
16
17
  [
17
- "group/callout relative overflow-hidden rounded-lg border border-(--c)/25 bg-(--c)/5 pl-1 text-sm text-foreground hc:border-(--c)",
18
- "before:absolute before:inset-y-0 before:left-0 before:w-1 before:bg-(--c)",
18
+ "group/callout relative overflow-hidden rounded-lg border text-sm text-foreground",
19
+ // header/body divider: half-strength hue, so it reads on the soft tint too (the hue itself in hc)
20
+ "[--callout-line:color-mix(in_oklab,var(--c)_50%,transparent)] hc:[--callout-line:var(--c)]",
21
+ // the row indicator (recipes rowIndicator), always on, in the callout's color, above the header's hover.
22
+ // The box has no padding of its own: header and body span the full width (pl-4 clears the pill).
23
+ "before:pointer-events-none before:absolute before:z-10 before:top-1.5 before:bottom-1.5 before:left-0 before:w-0.5 before:rounded-pill before:bg-(--c)",
19
24
  ],
20
25
  {
21
26
  variants: {
22
27
  color: colorVariants,
28
+ variant: statusSurface,
23
29
  },
24
30
  compoundVariants: [
25
31
  {
26
32
  color: "neutral",
27
- class: "border-border bg-card before:bg-muted-foreground hc:border-border",
33
+ class: [
34
+ statusSurfaceNeutral,
35
+ "before:bg-muted-foreground [--callout-line:var(--border)] hc:[--callout-line:var(--border)]",
36
+ ],
28
37
  },
29
38
  ],
30
- defaultVariants: {color: "neutral"},
39
+ defaultVariants: {color: "neutral", variant: "soft"},
31
40
  },
32
41
  );
33
42
 
34
43
  const calloutHeader =
35
- "flex min-h-8 w-full items-center gap-1.5 px-3 py-1.5 text-left text-xs text-(--c) group-data-[color=neutral]/callout:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5";
44
+ "flex min-h-8 w-full items-center gap-1.5 py-1.5 pr-3 pl-4 text-left text-xs text-(--c) group-data-[color=neutral]/callout:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5";
36
45
 
37
46
  const calloutBody =
38
- "px-3 py-2 text-sm leading-relaxed [&_a]:text-(--c) [&_a]:underline [&_a]:underline-offset-3 group-data-[color=neutral]/callout:[&_a]:text-foreground [&_p:not(:last-child)]:mb-2 border-(--c)/25 group-data-[color=neutral]/callout:border-border hc:border-(--c)";
47
+ "py-2 pr-3 pl-4 text-sm leading-relaxed [&_a]:text-(--c) [&_a]:underline [&_a]:underline-offset-3 group-data-[color=neutral]/callout:[&_a]:text-foreground [&_p:not(:last-child)]:mb-2 border-(--callout-line)";
39
48
 
40
49
  interface CalloutProps extends Omit<React.ComponentProps<"div">, "color" | "title"> {
41
- /** Status or hue of the bar, header and border. */
50
+ /** Status or hue of the indicator, header and border. */
42
51
  color?: Color | null;
52
+ /** `soft` (tinted, default) or `outline` (card surface, stronger border): the same axis as Alert. */
53
+ variant?: "soft" | "outline" | null;
43
54
  /** Leading icon element, e.g. `<InfoIcon />`. */
44
55
  icon?: React.ReactNode;
45
56
  /** Header label. The header renders when `title` or `icon` is set. */
@@ -53,6 +64,7 @@ interface CalloutProps extends Omit<React.ComponentProps<"div">, "color" | "titl
53
64
  function Callout({
54
65
  className,
55
66
  color,
67
+ variant,
56
68
  icon,
57
69
  title,
58
70
  collapsible = false,
@@ -61,6 +73,7 @@ function Callout({
61
73
  ...props
62
74
  }: CalloutProps) {
63
75
  const resolved = color ?? "neutral";
76
+ const surface = variant ?? "soft";
64
77
  const hasHeader = Boolean(title || icon);
65
78
  const headerContent = (
66
79
  <>
@@ -78,8 +91,9 @@ function Callout({
78
91
  <Collapsible
79
92
  data-slot="callout"
80
93
  data-color={resolved}
94
+ data-variant={surface}
81
95
  defaultOpen={!defaultCollapsed}
82
- className={cn(calloutVariants({color: resolved}), className)}
96
+ className={cn(calloutVariants({color: resolved, variant: surface}), className)}
83
97
  {...props}
84
98
  >
85
99
  <CollapsibleTrigger
@@ -110,7 +124,8 @@ function Callout({
110
124
  <div
111
125
  data-slot="callout"
112
126
  data-color={resolved}
113
- className={cn(calloutVariants({color: resolved}), className)}
127
+ data-variant={surface}
128
+ className={cn(calloutVariants({color: resolved, variant: surface}), className)}
114
129
  {...props}
115
130
  >
116
131
  {hasHeader ? (
@@ -21,7 +21,7 @@ const rechartsDefaults = [
21
21
 
22
22
  const chartContainerClass = [
23
23
  "flex aspect-video justify-center text-xs",
24
- "[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-axis-tick_text]:font-mono",
24
+ "[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-axis-tick_text]:font-alt",
25
25
  "[&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-hover",
26
26
  "[&_.recharts-radial-bar-background-sector]:fill-muted",
27
27
  "[&_.recharts-layer]:outline-hidden [&_.recharts-sector]:outline-hidden [&_.recharts-surface]:outline-hidden",
@@ -230,7 +230,7 @@ function ChartTooltipContent({
230
230
  </span>
231
231
  </div>
232
232
  {item.value != null && (
233
- <span className="font-mono font-medium text-foreground tabular-nums">
233
+ <span className="font-alt font-medium text-foreground tabular-nums">
234
234
  {typeof item.value === "number"
235
235
  ? item.value.toLocaleString()
236
236
  : String(item.value)}
@@ -65,7 +65,7 @@ function CodeBlockTitle({className, ...props}: React.ComponentProps<"div">) {
65
65
  <div
66
66
  data-slot="code-block-title"
67
67
  className={cn(
68
- "min-w-0 truncate font-mono text-xs font-medium text-foreground group-data-[variant=danger]/code-block:text-danger [&_svg]:pointer-events-none [&_svg]:inline-block [&_svg]:align-middle [&_svg:not([class*='size-'])]:size-3.5 *:[svg]:me-1.5",
68
+ "min-w-0 truncate font-alt text-xs font-medium text-foreground group-data-[variant=danger]/code-block:text-danger [&_svg]:pointer-events-none [&_svg]:inline-block [&_svg]:align-middle [&_svg:not([class*='size-'])]:size-3.5 *:[svg]:me-1.5",
69
69
  className,
70
70
  )}
71
71
  {...props}
@@ -7,7 +7,7 @@ import {
7
7
  DialogHeader,
8
8
  DialogTitle,
9
9
  } from "@repraze/kernelkit/components/dialog";
10
- import {fieldSurface, listSeparator} from "@repraze/kernelkit/lib/recipes";
10
+ import {fieldSurface, listSeparator, rowIndicator} from "@repraze/kernelkit/lib/recipes";
11
11
  import {Command as CommandPrimitive} from "cmdk";
12
12
  import {cn} from "cn";
13
13
  import {CheckIcon, SearchIcon} from "lucide-react";
@@ -17,8 +17,7 @@ import * as React from "react";
17
17
  * cmdk renders `data-selected` / `data-disabled` as "true" | "false", so the `listItem` recipe (which keys on
18
18
  * attribute presence) can't be used as is: this is the same row, keyed on `=true`.
19
19
  */
20
- const commandItemClass =
21
- "group/command-item relative flex min-h-8 cursor-default items-center gap-2 rounded-md px-2 py-1.5 text-sm outline-hidden select-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[selected=true]:[&_svg:not([class*='text-'])]:text-accent-foreground";
20
+ const commandItemClass = `${rowIndicator} data-[selected=true]:after:opacity-100 group/command-item relative flex min-h-8 cursor-default items-center gap-2 rounded-md px-2 py-1.5 text-sm outline-hidden select-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[selected=true]:[&_svg:not([class*='text-'])]:text-accent-foreground`;
22
21
 
23
22
  function Command({className, ...props}: React.ComponentProps<typeof CommandPrimitive>) {
24
23
  return (
@@ -152,7 +151,7 @@ function CommandShortcut({className, ...props}: React.ComponentProps<"span">) {
152
151
  <span
153
152
  data-slot="command-shortcut"
154
153
  className={cn(
155
- "ml-auto pl-4 font-mono text-xs text-muted-foreground group-data-[selected=true]/command-item:text-accent-foreground",
154
+ "ml-auto pl-4 font-alt text-xs text-muted-foreground group-data-[selected=true]/command-item:text-accent-foreground",
156
155
  className,
157
156
  )}
158
157
  {...props}
@@ -19,7 +19,7 @@ export function DataTableInfoRow<T>({className, view, ...props}: DataTableInfoRo
19
19
  const infoColumn: DataTableViewColumn<undefined> = convertColumn({
20
20
  id: "info",
21
21
  cell: () => (
22
- <span className="font-mono tabular-nums">
22
+ <span className="font-alt tabular-nums">
23
23
  {rowCount.toLocaleString()} rows · {columnCount} columns
24
24
  </span>
25
25
  ),
@@ -23,7 +23,7 @@ const menuItemVariants = cva([listItem, "group/menu-item data-inset:pl-8"], {
23
23
  variants: {
24
24
  variant: {
25
25
  default: "",
26
- danger: "text-danger data-highlighted:bg-danger/15 data-highlighted:text-danger [&_svg:not([class*='text-'])]:text-danger data-highlighted:[&_svg:not([class*='text-'])]:text-danger",
26
+ danger: "text-danger after:bg-danger data-highlighted:bg-danger/15 data-highlighted:text-danger [&_svg:not([class*='text-'])]:text-danger data-highlighted:[&_svg:not([class*='text-'])]:text-danger",
27
27
  },
28
28
  },
29
29
  defaultVariants: {variant: "default"},
@@ -35,7 +35,7 @@ function resolveItemVariant(variant: MenuItemVariant | undefined) {
35
35
 
36
36
  /** Keyboard shortcut hint at the end of a menu row. */
37
37
  const menuShortcutClass =
38
- "ml-auto pl-4 font-mono text-xs text-muted-foreground group-data-highlighted/menu-item:text-accent-foreground";
38
+ "ml-auto pl-4 font-alt text-xs text-muted-foreground group-data-highlighted/menu-item:text-accent-foreground";
39
39
 
40
40
  /** Right-side indicator of checkbox and radio rows. */
41
41
  const menuIndicatorClass = "pointer-events-none absolute right-2 flex size-4 items-center justify-center";
@@ -7,9 +7,7 @@ import type * as React from "react";
7
7
  /** A keycap: mono, `rounded-sm`, a thicker bottom border for the key look (readable in hc). */
8
8
  const kbdVariants = cva(
9
9
  [
10
- "pointer-events-none inline-flex w-fit items-center justify-center gap-1 rounded-sm border border-b-2 border-border bg-muted font-mono font-medium text-muted-foreground select-none [&_svg]:shrink-0",
11
- // inside a tooltip (inverted surface) the key becomes a translucent chip
12
- "in-data-[slot=tooltip-content]:border-transparent in-data-[slot=tooltip-content]:bg-background/20 in-data-[slot=tooltip-content]:text-background dark:in-data-[slot=tooltip-content]:bg-background/10",
10
+ "pointer-events-none inline-flex w-fit items-center justify-center gap-1 rounded-sm border border-b-2 border-border bg-muted font-alt font-medium text-muted-foreground select-none [&_svg]:shrink-0",
13
11
  ],
14
12
  {
15
13
  variants: {
@@ -30,7 +28,7 @@ function KbdGroup({className, ...props}: React.ComponentProps<"kbd">) {
30
28
  return (
31
29
  <kbd
32
30
  data-slot="kbd-group"
33
- className={cn("inline-flex items-center gap-1 font-mono text-xs text-muted-foreground", className)}
31
+ className={cn("inline-flex items-center gap-1 font-alt text-xs text-muted-foreground", className)}
34
32
  {...props}
35
33
  />
36
34
  );
@@ -65,7 +65,7 @@ function MessageHeader({className, ...props}: React.ComponentProps<"div">) {
65
65
  <div
66
66
  data-slot="message-header"
67
67
  className={cn(
68
- "flex max-w-full min-w-0 items-center gap-1.5 px-3 text-xs font-medium text-muted-foreground group-has-data-[variant=ghost]/message:px-0 group-data-[align=end]/message:justify-end [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5 [&_time]:font-mono [&_time]:font-normal [&_time]:tabular-nums",
68
+ "flex max-w-full min-w-0 items-center gap-1.5 px-3 text-xs font-medium text-muted-foreground group-has-data-[variant=ghost]/message:px-0 group-data-[align=end]/message:justify-end [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5 [&_time]:font-alt [&_time]:font-normal [&_time]:tabular-nums",
69
69
  className,
70
70
  )}
71
71
  {...props}
@@ -78,7 +78,7 @@ function MessageFooter({className, ...props}: React.ComponentProps<"div">) {
78
78
  <div
79
79
  data-slot="message-footer"
80
80
  className={cn(
81
- "flex max-w-full min-w-0 items-center gap-1 px-3 text-xs text-muted-foreground group-has-data-[variant=ghost]/message:px-0 group-data-[align=end]/message:justify-end [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5 [&_time]:font-mono [&_time]:tabular-nums",
81
+ "flex max-w-full min-w-0 items-center gap-1 px-3 text-xs text-muted-foreground group-has-data-[variant=ghost]/message:px-0 group-data-[align=end]/message:justify-end [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5 [&_time]:font-alt [&_time]:tabular-nums",
82
82
  className,
83
83
  )}
84
84
  {...props}
@@ -59,7 +59,7 @@ function MeterValue({className, ...props}: MeterPrimitive.Value.Props) {
59
59
  return (
60
60
  <MeterPrimitive.Value
61
61
  data-slot="meter-value"
62
- className={cn("ms-auto font-mono text-xs text-muted-foreground tabular-nums", className)}
62
+ className={cn("ms-auto font-alt text-xs text-muted-foreground tabular-nums", className)}
63
63
  {...props}
64
64
  />
65
65
  );
@@ -152,6 +152,10 @@ function NavigationMenuLink({className, ...props}: NavigationMenuPrimitive.Link.
152
152
  "transition-[color,background-color,border-color,box-shadow] duration-100 motion-reduce:transition-none",
153
153
  "hover:bg-hover focus-visible:ring-3 focus-visible:ring-ring/50",
154
154
  "data-active:bg-accent data-active:font-medium data-active:text-accent-foreground",
155
+ // current page (§7c): the sidebar's pill, on the bottom edge in the bar and on the left edge in a panel
156
+ "relative after:pointer-events-none after:absolute after:rounded-pill after:bg-primary-text after:opacity-0 data-active:after:opacity-100",
157
+ "in-data-[slot=navigation-menu-list]:after:inset-x-2.5 in-data-[slot=navigation-menu-list]:after:bottom-0 in-data-[slot=navigation-menu-list]:after:h-0.5",
158
+ "in-data-[slot=navigation-menu-content]:after:top-1.5 in-data-[slot=navigation-menu-content]:after:bottom-1.5 in-data-[slot=navigation-menu-content]:after:left-0 in-data-[slot=navigation-menu-content]:after:w-0.5",
155
159
  "in-data-[slot=navigation-menu-list]:h-8 in-data-[slot=navigation-menu-list]:font-medium",
156
160
  "in-data-[slot=navigation-menu-content]:rounded-md in-data-[slot=navigation-menu-content]:px-2 in-data-[slot=navigation-menu-content]:focus-visible:bg-accent in-data-[slot=navigation-menu-content]:focus-visible:text-accent-foreground in-data-[slot=navigation-menu-content]:focus-visible:ring-0",
157
161
  "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
@@ -86,7 +86,7 @@ function ProgressValue({className, ...props}: ProgressPrimitive.Value.Props) {
86
86
  return (
87
87
  <ProgressPrimitive.Value
88
88
  data-slot="progress-value"
89
- className={cn("ms-auto font-mono text-xs text-muted-foreground tabular-nums", className)}
89
+ className={cn("ms-auto font-alt text-xs text-muted-foreground tabular-nums", className)}
90
90
  {...props}
91
91
  />
92
92
  );
@@ -122,7 +122,7 @@ function QuestionnaireChoice({
122
122
  </QuestionnairePrimitive.ChoiceLabel>
123
123
  <QuestionnairePrimitive.ChoiceShortcut
124
124
  data-slot="questionnaire-choice-shortcut"
125
- className="pointer-events-none ms-auto hidden size-5 shrink-0 translate-y-[--spacing(0.45)] items-center justify-center rounded-sm border border-border bg-muted font-mono text-[0.625rem] leading-none font-medium text-muted-foreground group-has-data-[slot=questionnaire-choice-description]/questionnaire-choice:translate-y-0.5 group-data-[shortcut]/questionnaire-choice:inline-flex"
125
+ className="pointer-events-none ms-auto hidden size-5 shrink-0 translate-y-[--spacing(0.45)] items-center justify-center rounded-sm border border-border bg-muted font-alt text-[0.625rem] leading-none font-medium text-muted-foreground group-has-data-[slot=questionnaire-choice-description]/questionnaire-choice:translate-y-0.5 group-data-[shortcut]/questionnaire-choice:inline-flex"
126
126
  />
127
127
  </QuestionnairePrimitive.Choice>
128
128
  );
@@ -51,16 +51,15 @@ interface SearchTriggerProps extends React.ComponentProps<typeof Button> {
51
51
  children?: React.ReactNode;
52
52
  }
53
53
 
54
- /** Header search button: icon, placeholder, OS-aware shortcut. Open your dialog in `onClick`. */
54
+ /** Header search button: icon, placeholder, OS-aware shortcut. Same 32px height as the header's icon buttons. Open your dialog in `onClick`. */
55
55
  function SearchTrigger({children = "Search…", className, ...props}: SearchTriggerProps) {
56
56
  const mod = useModKey();
57
57
  return (
58
58
  <Button
59
59
  variant="outline"
60
- size="sm"
61
60
  aria-label="Search"
62
61
  data-slot="search-trigger"
63
- className={cn("w-9 justify-start px-0 text-muted-foreground sm:w-48 sm:px-2.5 md:w-56", className)}
62
+ className={cn("w-8 justify-start px-0 text-muted-foreground sm:w-48 sm:px-2.5 md:w-56", className)}
64
63
  {...props}
65
64
  >
66
65
  <SearchIcon data-icon="inline-start" className="mx-auto sm:mx-0" />
@@ -50,7 +50,7 @@ function SectionEyebrow({className, render, ...props}: useRender.ComponentProps<
50
50
  return useRender({
51
51
  defaultTagName: "div",
52
52
  props: mergeProps<"div">(
53
- {className: cn("font-mono text-xs font-medium text-primary-text uppercase sm:col-start-1", className)},
53
+ {className: cn("font-alt text-xs font-medium text-primary-text uppercase sm:col-start-1", className)},
54
54
  props,
55
55
  ),
56
56
  render,
@@ -10,7 +10,7 @@ import {Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle} from "@r
10
10
  import {Skeleton} from "@repraze/kernelkit/components/skeleton";
11
11
  import {Tooltip, TooltipContent, TooltipTrigger} from "@repraze/kernelkit/components/tooltip";
12
12
  import {useIsMobile} from "@repraze/kernelkit/hooks/use-mobile";
13
- import {focusRing} from "@repraze/kernelkit/lib/recipes";
13
+ import {focusRing, rowIndicator} from "@repraze/kernelkit/lib/recipes";
14
14
  import {cva, type VariantProps} from "class-variance-authority";
15
15
  import {cn} from "cn";
16
16
  import {PanelLeftIcon} from "lucide-react";
@@ -482,7 +482,8 @@ const sidebarMenuButtonVariants = cva(
482
482
  "hover:bg-hover data-popup-open:bg-hover",
483
483
  "disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50",
484
484
  // current row (GUIDELINES §7c): accent tint, foreground text, 2px accent indicator on the left edge
485
- "relative after:pointer-events-none after:absolute after:top-1.5 after:bottom-1.5 after:left-0 after:w-0.5 after:rounded-pill after:bg-primary-text after:opacity-0 data-active:after:opacity-100",
485
+ "relative data-active:after:opacity-100",
486
+ rowIndicator,
486
487
  "data-active:bg-primary-tint data-active:font-medium data-active:text-foreground data-active:[&_svg:not([class*='text-'])]:text-primary-text",
487
488
  "[&_svg]:size-4 [&_svg]:shrink-0 [&_svg:not([class*='text-'])]:text-muted-foreground [&>span:last-child]:truncate",
488
489
  ],
@@ -591,7 +592,7 @@ function SidebarMenuBadge({className, ...props}: React.ComponentProps<"div">) {
591
592
  data-slot="sidebar-menu-badge"
592
593
  data-sidebar="menu-badge"
593
594
  className={cn(
594
- "pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-sm px-1 font-mono text-xs font-medium text-muted-foreground tabular-nums select-none group-data-[collapsible=icon]:hidden peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1 peer-data-active/menu-button:text-sidebar-accent-foreground",
595
+ "pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-sm px-1 font-alt text-xs font-medium text-muted-foreground tabular-nums select-none group-data-[collapsible=icon]:hidden peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1 peer-data-active/menu-button:text-sidebar-accent-foreground",
595
596
  className,
596
597
  )}
597
598
  {...props}
@@ -22,7 +22,7 @@ import type * as React from "react";
22
22
  function SiteFooter({className, children, ...props}: React.ComponentProps<"footer">) {
23
23
  return (
24
24
  <footer data-slot="site-footer" className={cn("border-t border-border", className)} {...props}>
25
- <div className="mx-auto flex w-full max-w-(--kk-site-width) flex-col gap-3 px-4 py-6 font-mono text-xs text-muted-foreground sm:flex-row sm:items-center sm:justify-between md:px-6">
25
+ <div className="mx-auto flex w-full max-w-(--kk-site-width) flex-col gap-3 px-4 py-6 font-alt text-xs text-muted-foreground sm:flex-row sm:items-center sm:justify-between md:px-6">
26
26
  {children}
27
27
  </div>
28
28
  </footer>
@@ -66,7 +66,7 @@ function SiteHeaderBrand({className, render, ...props}: useRender.ComponentProps
66
66
  props: mergeProps<"a">(
67
67
  {
68
68
  className: cn(
69
- "flex shrink-0 items-center gap-2 rounded-md font-mono text-sm font-semibold tracking-tight",
69
+ "flex shrink-0 items-center gap-2 rounded-md font-alt text-sm font-semibold tracking-tight",
70
70
  focusRing,
71
71
  "[&_svg]:shrink-0",
72
72
  className,
@@ -95,9 +95,9 @@ function SiteNav({className, render, ...props}: useRender.ComponentProps<"nav">)
95
95
  /**
96
96
  * A nav link, styled like the suite's other navigation (GUIDELINES §7c): muted at rest, a hover wash,
97
97
  * and when current (`aria-current="page"`, `data-status="active"` from TanStack Router, or `data-active`)
98
- * foreground text plus a 2px accent indicator. In SiteNav the indicator sits on the header's bottom
99
- * border (like line Tabs); elsewhere (the phone menu) the link is a sidebar row with a tint and the
100
- * indicator on its left edge.
98
+ * the sidebar's current-row look: accent tint, foreground text and a 2px accent pill. In SiteNav the pill
99
+ * runs along the link's bottom edge (like NavigationMenu); elsewhere (the phone menu) it sits on the
100
+ * left edge, like a sidebar row.
101
101
  */
102
102
  function SiteNavLink({className, render, ...props}: useRender.ComponentProps<"a">) {
103
103
  return useRender({
@@ -105,14 +105,13 @@ function SiteNavLink({className, render, ...props}: useRender.ComponentProps<"a"
105
105
  props: mergeProps<"a">(
106
106
  {
107
107
  className: cn(
108
- "relative flex h-8 items-center rounded-md px-2.5 font-mono text-sm text-muted-foreground transition-colors hover:bg-hover hover:text-foreground motion-reduce:transition-none",
108
+ "relative flex h-8 items-center rounded-md px-2.5 font-alt text-sm text-muted-foreground transition-colors hover:bg-hover hover:text-foreground motion-reduce:transition-none",
109
109
  focusRing,
110
- // indicator: vertical by default, on the header border inside SiteNav
110
+ // indicator: left edge by default, bottom edge inside SiteNav
111
111
  "after:pointer-events-none after:absolute after:top-1.5 after:bottom-1.5 after:left-0 after:w-0.5 after:rounded-pill after:bg-primary-text after:opacity-0",
112
- "in-data-[slot=site-nav]:after:top-auto in-data-[slot=site-nav]:after:right-2.5 in-data-[slot=site-nav]:after:-bottom-[calc(0.75rem+1px)] in-data-[slot=site-nav]:after:left-2.5 in-data-[slot=site-nav]:after:h-0.5 in-data-[slot=site-nav]:after:w-auto",
112
+ "in-data-[slot=site-nav]:after:top-auto in-data-[slot=site-nav]:after:right-2.5 in-data-[slot=site-nav]:after:bottom-0 in-data-[slot=site-nav]:after:left-2.5 in-data-[slot=site-nav]:after:h-0.5 in-data-[slot=site-nav]:after:w-auto",
113
113
  "aria-[current=page]:text-foreground aria-[current=page]:after:opacity-100 data-[status=active]:text-foreground data-[status=active]:after:opacity-100 data-active:text-foreground data-active:after:opacity-100",
114
- // outside the header row: the sidebar's current-row tint
115
- "not-in-data-[slot=site-nav]:aria-[current=page]:bg-primary-tint not-in-data-[slot=site-nav]:data-[status=active]:bg-primary-tint not-in-data-[slot=site-nav]:data-active:bg-primary-tint",
114
+ "aria-[current=page]:bg-primary-tint data-[status=active]:bg-primary-tint data-active:bg-primary-tint",
116
115
  className,
117
116
  ),
118
117
  },
@@ -51,7 +51,7 @@ function StatValue({className, ...props}: React.ComponentProps<"div">) {
51
51
  <div
52
52
  data-slot="stat-value"
53
53
  className={cn(
54
- "col-start-1 flex items-baseline gap-1 font-mono text-2xl leading-tight font-medium tracking-tight text-foreground tabular-nums",
54
+ "col-start-1 flex items-baseline gap-1 font-alt text-2xl leading-tight font-medium tracking-tight text-foreground tabular-nums",
55
55
  "group-data-[size=sm]/stat:text-xl group-data-[size=lg]/stat:text-3xl",
56
56
  className,
57
57
  )}
@@ -74,7 +74,7 @@ function StatUnit({className, ...props}: React.ComponentProps<"span">) {
74
74
  type StatTrend = "up" | "down" | "flat";
75
75
 
76
76
  const statDeltaVariants = cva(
77
- "col-start-1 inline-flex w-fit items-center gap-1 font-mono text-xs font-medium tabular-nums [&_svg]:size-3.5 [&_svg]:shrink-0",
77
+ "col-start-1 inline-flex w-fit items-center gap-1 font-alt text-xs font-medium tabular-nums [&_svg]:size-3.5 [&_svg]:shrink-0",
78
78
  {
79
79
  variants: {
80
80
  intent: {
@@ -18,8 +18,10 @@ function Tabs({className, orientation = "horizontal", ...props}: TabsPrimitive.R
18
18
  }
19
19
 
20
20
  /**
21
- * `default`: a tray with the active tab raised on a bordered surface (the border keeps it readable in
22
- * high contrast). `line`: a baseline with a 2px primary indicator under the active tab.
21
+ * `default`: a track with the active tab tinted with the accent and marked by a 2px pill along its bottom
22
+ * edge, like a current nav link. Track border + padding = `--radius-xs` (lg − md), so the `rounded-md` chips
23
+ * stay concentric with the `rounded-lg` track at every radius. `line`: a baseline with the same 2px pill
24
+ * on it under the active tab.
23
25
  * Sizes follow the control table (GUIDELINES §4) so a tabs list lines up with buttons and inputs.
24
26
  */
25
27
  const tabsListVariants = cva(
@@ -27,7 +29,8 @@ const tabsListVariants = cva(
27
29
  {
28
30
  variants: {
29
31
  variant: {
30
- default: "rounded-lg border border-transparent bg-muted p-[3px] data-vertical:gap-0.5 hc:border-border",
32
+ default:
33
+ "rounded-lg border border-transparent bg-muted p-[max(0px,calc(var(--radius-xs)-1px))] data-vertical:gap-0.5 hc:border-border",
31
34
  line: "gap-1 rounded-none border-border bg-transparent data-horizontal:border-b data-horizontal:py-[3px] data-vertical:border-r data-vertical:px-[3px]",
32
35
  },
33
36
  size: {
@@ -73,13 +76,18 @@ const tabsTriggerClass = cn(
73
76
  "group-data-[size=lg]/tabs-list:gap-2 group-data-[size=lg]/tabs-list:px-2.5",
74
77
  // vertical: full-width rows
75
78
  "data-vertical:w-full data-vertical:flex-none data-vertical:justify-start data-vertical:py-1",
76
- // default: raised, bordered surface + foreground text (readable without the shade in hc)
79
+ // active: foreground text and a 2px primary-text pill; default adds the accent tint (nav link look)
77
80
  "data-active:text-foreground",
78
- "group-data-[variant=default]/tabs-list:data-active:border-input group-data-[variant=default]/tabs-list:data-active:bg-background group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=default]/tabs-list:data-active:shadow-(color:--shadow-color) dark:group-data-[variant=default]/tabs-list:data-active:bg-input/30",
79
- // line: 2px primary indicator over the list's baseline
80
- "after:pointer-events-none after:absolute after:rounded-pill after:bg-primary-text after:opacity-0 after:transition-opacity group-data-[variant=line]/tabs-list:data-active:after:opacity-100",
81
- "data-horizontal:after:inset-x-1 data-horizontal:after:-bottom-1 data-horizontal:after:h-0.5",
82
- "data-vertical:after:inset-y-1 data-vertical:after:-right-1 data-vertical:after:w-0.5",
81
+ "group-data-[variant=default]/tabs-list:data-active:bg-accent group-data-[variant=default]/tabs-list:data-active:text-accent-foreground",
82
+ "after:pointer-events-none after:absolute after:rounded-pill after:bg-primary-text after:opacity-0 after:transition-opacity data-active:after:opacity-100",
83
+ // default: the pill sits inside the chip on its bottom edge (vertical: its start edge, like sidebar rows)
84
+ "group-data-[variant=default]/tabs-list:data-horizontal:after:inset-x-2 group-data-[variant=default]/tabs-list:data-horizontal:after:-bottom-px group-data-[variant=default]/tabs-list:data-horizontal:after:h-0.5",
85
+ // the pill spans the label: inset = the trigger's padding per size
86
+ "group-data-[size=sm]/tabs-list:group-data-[variant=default]/tabs-list:data-horizontal:after:inset-x-1.5 group-data-[size=lg]/tabs-list:group-data-[variant=default]/tabs-list:data-horizontal:after:inset-x-2.5",
87
+ "group-data-[variant=default]/tabs-list:data-vertical:after:inset-y-1.5 group-data-[variant=default]/tabs-list:data-vertical:after:-left-px group-data-[variant=default]/tabs-list:data-vertical:after:w-0.5",
88
+ // line: the pill sits on the list's baseline
89
+ "group-data-[variant=line]/tabs-list:data-horizontal:after:inset-x-1 group-data-[variant=line]/tabs-list:data-horizontal:after:-bottom-1 group-data-[variant=line]/tabs-list:data-horizontal:after:h-0.5",
90
+ "group-data-[variant=line]/tabs-list:data-vertical:after:inset-y-1 group-data-[variant=line]/tabs-list:data-vertical:after:-right-1 group-data-[variant=line]/tabs-list:data-vertical:after:w-0.5",
83
91
  );
84
92
 
85
93
  function TabsTrigger({className, ...props}: TabsPrimitive.Tab.Props) {