@repraze/kernelkit 0.1.0-alpha.2 → 0.1.0-alpha.4

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 (186) hide show
  1. package/README.md +17 -0
  2. package/dist/components/alert.d.ts.map +1 -1
  3. package/dist/components/alert.js +4 -6
  4. package/dist/components/alert.js.map +1 -1
  5. package/dist/components/avatar.js +1 -1
  6. package/dist/components/avatar.js.map +1 -1
  7. package/dist/components/badge.js +1 -1
  8. package/dist/components/badge.js.map +1 -1
  9. package/dist/components/breadcrumb.d.ts +1 -0
  10. package/dist/components/breadcrumb.d.ts.map +1 -1
  11. package/dist/components/breadcrumb.js +7 -6
  12. package/dist/components/breadcrumb.js.map +1 -1
  13. package/dist/components/callout.d.ts +8 -4
  14. package/dist/components/callout.d.ts.map +1 -1
  15. package/dist/components/callout.js +22 -12
  16. package/dist/components/callout.js.map +1 -1
  17. package/dist/components/chart.js +2 -2
  18. package/dist/components/chart.js.map +1 -1
  19. package/dist/components/code-block.js +1 -1
  20. package/dist/components/code-block.js.map +1 -1
  21. package/dist/components/command.js +1 -1
  22. package/dist/components/command.js.map +1 -1
  23. package/dist/components/data-table/elements/data-table-info-row.js +1 -1
  24. package/dist/components/data-table/elements/data-table-info-row.js.map +1 -1
  25. package/dist/components/dropdown-menu.d.ts +1 -1
  26. package/dist/components/dropdown-menu.d.ts.map +1 -1
  27. package/dist/components/dropdown-menu.js +1 -1
  28. package/dist/components/dropdown-menu.js.map +1 -1
  29. package/dist/components/kbd.d.ts.map +1 -1
  30. package/dist/components/kbd.js +2 -4
  31. package/dist/components/kbd.js.map +1 -1
  32. package/dist/components/message.js +2 -2
  33. package/dist/components/message.js.map +1 -1
  34. package/dist/components/meter.js +1 -1
  35. package/dist/components/meter.js.map +1 -1
  36. package/dist/components/progress.js +1 -1
  37. package/dist/components/progress.js.map +1 -1
  38. package/dist/components/questionnaire.js +1 -1
  39. package/dist/components/questionnaire.js.map +1 -1
  40. package/dist/components/search-dialog.d.ts +6 -1
  41. package/dist/components/search-dialog.d.ts.map +1 -1
  42. package/dist/components/search-dialog.js +13 -1
  43. package/dist/components/search-dialog.js.map +1 -1
  44. package/dist/components/search-trigger.d.ts +1 -1
  45. package/dist/components/search-trigger.d.ts.map +1 -1
  46. package/dist/components/search-trigger.js +2 -2
  47. package/dist/components/search-trigger.js.map +1 -1
  48. package/dist/components/section.js +1 -1
  49. package/dist/components/section.js.map +1 -1
  50. package/dist/components/sidebar.js +1 -1
  51. package/dist/components/sidebar.js.map +1 -1
  52. package/dist/components/site-footer.js +1 -1
  53. package/dist/components/site-footer.js.map +1 -1
  54. package/dist/components/site-header.js +2 -2
  55. package/dist/components/site-header.js.map +1 -1
  56. package/dist/components/stat.js +2 -2
  57. package/dist/components/stat.js.map +1 -1
  58. package/dist/components/tabs.d.ts +4 -2
  59. package/dist/components/tabs.d.ts.map +1 -1
  60. package/dist/components/tabs.js +13 -7
  61. package/dist/components/tabs.js.map +1 -1
  62. package/dist/components/theme-menu.js +1 -1
  63. package/dist/components/theme-menu.js.map +1 -1
  64. package/dist/components/toc.js +1 -1
  65. package/dist/components/toc.js.map +1 -1
  66. package/dist/components/tooltip.d.ts.map +1 -1
  67. package/dist/components/tooltip.js +5 -2
  68. package/dist/components/tooltip.js.map +1 -1
  69. package/dist/components/trace.js +1 -1
  70. package/dist/components/trace.js.map +1 -1
  71. package/dist/docs/doc-kit.d.ts +72 -0
  72. package/dist/docs/doc-kit.d.ts.map +1 -0
  73. package/dist/docs/doc-kit.js +45 -0
  74. package/dist/docs/doc-kit.js.map +1 -0
  75. package/dist/docs/doc-page.d.ts +27 -0
  76. package/dist/docs/doc-page.d.ts.map +1 -0
  77. package/dist/docs/doc-page.js +33 -0
  78. package/dist/docs/doc-page.js.map +1 -0
  79. package/dist/docs/docs-index.d.ts +60 -0
  80. package/dist/docs/docs-index.d.ts.map +1 -0
  81. package/dist/docs/docs-index.js +45 -0
  82. package/dist/docs/docs-index.js.map +1 -0
  83. package/dist/docs/docs-nav.d.ts +13 -0
  84. package/dist/docs/docs-nav.d.ts.map +1 -0
  85. package/dist/docs/docs-nav.js +8 -0
  86. package/dist/docs/docs-nav.js.map +1 -0
  87. package/dist/docs/docs-search.d.ts +19 -0
  88. package/dist/docs/docs-search.d.ts.map +1 -0
  89. package/dist/docs/docs-search.js +33 -0
  90. package/dist/docs/docs-search.js.map +1 -0
  91. package/dist/docs/index.d.ts +7 -0
  92. package/dist/docs/index.d.ts.map +1 -0
  93. package/dist/docs/index.js +7 -0
  94. package/dist/docs/index.js.map +1 -0
  95. package/dist/docs/review-matrix.d.ts +25 -0
  96. package/dist/docs/review-matrix.d.ts.map +1 -0
  97. package/dist/docs/review-matrix.js +31 -0
  98. package/dist/docs/review-matrix.js.map +1 -0
  99. package/dist/hooks/use-theme-palette.d.ts +8 -0
  100. package/dist/hooks/use-theme-palette.d.ts.map +1 -0
  101. package/dist/hooks/use-theme-palette.js +24 -0
  102. package/dist/hooks/use-theme-palette.js.map +1 -0
  103. package/dist/lib/recipes.d.ts +10 -0
  104. package/dist/lib/recipes.d.ts.map +1 -1
  105. package/dist/lib/recipes.js +12 -0
  106. package/dist/lib/recipes.js.map +1 -1
  107. package/dist/provider/theme-provider.d.ts +10 -1
  108. package/dist/provider/theme-provider.d.ts.map +1 -1
  109. package/dist/provider/theme-provider.js +23 -8
  110. package/dist/provider/theme-provider.js.map +1 -1
  111. package/dist/site/brand.d.ts +20 -0
  112. package/dist/site/brand.d.ts.map +1 -0
  113. package/dist/site/brand.js +17 -0
  114. package/dist/site/brand.js.map +1 -0
  115. package/dist/site/github-icon.d.ts +4 -0
  116. package/dist/site/github-icon.d.ts.map +1 -0
  117. package/dist/site/github-icon.js +6 -0
  118. package/dist/site/github-icon.js.map +1 -0
  119. package/dist/site/index.d.ts +6 -0
  120. package/dist/site/index.d.ts.map +1 -0
  121. package/dist/site/index.js +6 -0
  122. package/dist/site/index.js.map +1 -0
  123. package/dist/site/suite-footer.d.ts +24 -0
  124. package/dist/site/suite-footer.d.ts.map +1 -0
  125. package/dist/site/suite-footer.js +19 -0
  126. package/dist/site/suite-footer.js.map +1 -0
  127. package/dist/site/suite-header.d.ts +40 -0
  128. package/dist/site/suite-header.d.ts.map +1 -0
  129. package/dist/site/suite-header.js +19 -0
  130. package/dist/site/suite-header.js.map +1 -0
  131. package/dist/site/use-page-title.d.ts +3 -0
  132. package/dist/site/use-page-title.d.ts.map +1 -0
  133. package/dist/site/use-page-title.js +8 -0
  134. package/dist/site/use-page-title.js.map +1 -0
  135. package/dist/vite/index.d.ts +65 -0
  136. package/dist/vite/index.d.ts.map +1 -0
  137. package/dist/vite/index.js +242 -0
  138. package/dist/vite/index.js.map +1 -0
  139. package/package.json +39 -3
  140. package/src/components/alert.tsx +4 -6
  141. package/src/components/avatar.tsx +1 -1
  142. package/src/components/badge.tsx +1 -1
  143. package/src/components/breadcrumb.tsx +8 -7
  144. package/src/components/callout.tsx +27 -12
  145. package/src/components/chart.tsx +2 -2
  146. package/src/components/code-block.tsx +1 -1
  147. package/src/components/command.tsx +1 -1
  148. package/src/components/data-table/elements/data-table-info-row.tsx +1 -1
  149. package/src/components/dropdown-menu.tsx +1 -1
  150. package/src/components/kbd.tsx +2 -4
  151. package/src/components/message.tsx +2 -2
  152. package/src/components/meter.tsx +1 -1
  153. package/src/components/progress.tsx +1 -1
  154. package/src/components/questionnaire.tsx +1 -1
  155. package/src/components/search-dialog.tsx +14 -1
  156. package/src/components/search-trigger.tsx +2 -3
  157. package/src/components/section.tsx +1 -1
  158. package/src/components/sidebar.tsx +1 -1
  159. package/src/components/site-footer.tsx +1 -1
  160. package/src/components/site-header.tsx +2 -2
  161. package/src/components/stat.tsx +2 -2
  162. package/src/components/tabs.tsx +17 -9
  163. package/src/components/theme-menu.tsx +1 -1
  164. package/src/components/toc.tsx +1 -1
  165. package/src/components/tooltip.tsx +15 -3
  166. package/src/components/trace.tsx +1 -1
  167. package/src/docs/doc-kit.tsx +176 -0
  168. package/src/docs/doc-page.tsx +126 -0
  169. package/src/docs/docs-index.ts +108 -0
  170. package/src/docs/docs-nav.tsx +47 -0
  171. package/src/docs/docs-search.tsx +88 -0
  172. package/src/docs/index.ts +6 -0
  173. package/src/docs/review-matrix.tsx +67 -0
  174. package/src/hooks/use-theme-palette.ts +25 -0
  175. package/src/lib/recipes.ts +14 -0
  176. package/src/provider/theme-provider.tsx +34 -9
  177. package/src/site/brand.tsx +41 -0
  178. package/src/site/github-icon.tsx +10 -0
  179. package/src/site/index.ts +5 -0
  180. package/src/site/suite-footer.tsx +68 -0
  181. package/src/site/suite-header.tsx +114 -0
  182. package/src/site/use-page-title.ts +8 -0
  183. package/src/styles/kernelkit.css +6 -0
  184. package/src/styles/site.css +15 -0
  185. package/src/vite/client.d.ts +11 -0
  186. package/src/vite/index.ts +298 -0
@@ -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
  );
@@ -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
  );
@@ -84,4 +84,17 @@ function SearchFooter({className, ...props}: React.ComponentProps<"div">) {
84
84
  );
85
85
  }
86
86
 
87
- export {SearchDialog, SearchFooter, SearchItem};
87
+ /**
88
+ * The suite's search filter for `<Command filter={substringFilter}>`: a plain substring match (prefix
89
+ * matches rank first). cmdk's default fuzzy match finds "t…a…b" in nearly every title.
90
+ */
91
+ function substringFilter(value: string, search: string, keywords?: string[]) {
92
+ const haystack = [value, ...(keywords ?? [])].join(" ").toLowerCase();
93
+ const needle = search.trim().toLowerCase();
94
+ if (haystack.startsWith(needle)) {
95
+ return 1;
96
+ }
97
+ return haystack.includes(needle) ? 0.5 : 0;
98
+ }
99
+
100
+ export {SearchDialog, SearchFooter, SearchItem, substringFilter};
@@ -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,
@@ -592,7 +592,7 @@ function SidebarMenuBadge({className, ...props}: React.ComponentProps<"div">) {
592
592
  data-slot="sidebar-menu-badge"
593
593
  data-sidebar="menu-badge"
594
594
  className={cn(
595
- "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",
596
596
  className,
597
597
  )}
598
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,
@@ -105,7 +105,7 @@ 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
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",
@@ -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) {
@@ -116,7 +116,7 @@ function ThemeRadiusPicker({className}: {className?: string}) {
116
116
  className={cn("w-full", className)}
117
117
  >
118
118
  {RADII.map((radius) => (
119
- <ToggleGroupItem key={radius} value={radius} className="flex-1 font-mono">
119
+ <ToggleGroupItem key={radius} value={radius} className="flex-1 font-alt">
120
120
  {radius}
121
121
  </ToggleGroupItem>
122
122
  ))}
@@ -35,7 +35,7 @@ function TocTitle({className, render, ...props}: useRender.ComponentProps<"p">)
35
35
  return useRender({
36
36
  defaultTagName: "p",
37
37
  props: mergeProps<"p">(
38
- {className: cn("font-mono text-xs font-medium text-muted-foreground uppercase", className)},
38
+ {className: cn("font-alt text-xs font-medium text-muted-foreground uppercase", className)},
39
39
  props,
40
40
  ),
41
41
  render,
@@ -39,15 +39,27 @@ function TooltipContent({
39
39
  <TooltipPrimitive.Popup
40
40
  data-slot="tooltip-content"
41
41
  className={cn(
42
- // Inverted chip: max contrast against any surface, so it reads in high contrast too.
42
+ // Popover-coloured chip: the popup surface and border, small mono text.
43
43
  popupMotion,
44
- "z-50 inline-flex min-h-6 w-fit max-w-xs items-center gap-1.5 rounded-md bg-foreground px-2 py-1 text-xs text-background shadow-md shadow-(color:--shadow-color) has-data-[slot=kbd]:pr-1 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:h-4 **:data-[slot=kbd]:min-w-4 **:data-[slot=kbd]:rounded-xs",
44
+ "z-50 inline-flex min-h-6 w-fit max-w-xs items-center gap-1.5 rounded-md border border-border bg-popover px-2 py-1 font-alt text-xs text-popover-foreground shadow-md shadow-(color:--shadow-color) has-data-[slot=kbd]:pr-1 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:h-4 **:data-[slot=kbd]:min-w-4 **:data-[slot=kbd]:rounded-xs",
45
45
  className,
46
46
  )}
47
47
  {...props}
48
48
  >
49
49
  {children}
50
- <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-xs bg-foreground fill-foreground data-[side=bottom]:top-1 data-[side=inline-end]:top-1/2! data-[side=inline-end]:-left-1 data-[side=inline-end]:-translate-y-1/2 data-[side=inline-start]:top-1/2! data-[side=inline-start]:-right-1 data-[side=inline-start]:-translate-y-1/2 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5" />
50
+ <TooltipPrimitive.Arrow
51
+ className={cn(
52
+ // A rotated square in the popup colours; the border sits on the two edges that face out,
53
+ // centred on the popup's border line so the outline runs on without a seam.
54
+ "z-50 size-2.5 rotate-45 rounded-xs border-border bg-popover",
55
+ "data-[side=top]:-bottom-1.25 data-[side=top]:border-r data-[side=top]:border-b",
56
+ "data-[side=bottom]:-top-1.25 data-[side=bottom]:border-t data-[side=bottom]:border-l",
57
+ "data-[side=left]:top-1/2! data-[side=left]:-right-1.25 data-[side=left]:-translate-y-1/2 data-[side=left]:border-t data-[side=left]:border-r",
58
+ "data-[side=inline-start]:top-1/2! data-[side=inline-start]:-right-1.25 data-[side=inline-start]:-translate-y-1/2 data-[side=inline-start]:border-t data-[side=inline-start]:border-r",
59
+ "data-[side=right]:top-1/2! data-[side=right]:-left-1.25 data-[side=right]:-translate-y-1/2 data-[side=right]:border-b data-[side=right]:border-l",
60
+ "data-[side=inline-end]:top-1/2! data-[side=inline-end]:-left-1.25 data-[side=inline-end]:-translate-y-1/2 data-[side=inline-end]:border-b data-[side=inline-end]:border-l",
61
+ )}
62
+ />
51
63
  </TooltipPrimitive.Popup>
52
64
  </TooltipPrimitive.Positioner>
53
65
  </TooltipPrimitive.Portal>
@@ -67,7 +67,7 @@ function TraceDescription({className, ...props}: React.ComponentProps<"span">) {
67
67
  return (
68
68
  <span
69
69
  data-slot="trace-description"
70
- className={cn("shrink-0 font-mono text-xs text-muted-foreground tabular-nums", className)}
70
+ className={cn("shrink-0 font-alt text-xs text-muted-foreground tabular-nums", className)}
71
71
  {...props}
72
72
  />
73
73
  );
@@ -0,0 +1,176 @@
1
+ "use client";
2
+
3
+ import {
4
+ CodeBlock,
5
+ CodeBlockActions,
6
+ CodeBlockContent,
7
+ CodeBlockCopyButton,
8
+ CodeBlockHeader,
9
+ CodeBlockTitle,
10
+ } from "@repraze/kernelkit/components/code-block";
11
+ import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "@repraze/kernelkit/components/table";
12
+ import {Tabs, TabsContent, TabsList, TabsTrigger} from "@repraze/kernelkit/components/tabs";
13
+ import {useHighlightedCode} from "@repraze/kernelkit/hooks/use-highlighted-code";
14
+ import {cn} from "cn";
15
+ import type * as React from "react";
16
+
17
+ export interface PropRow {
18
+ name: string;
19
+ type: string;
20
+ default?: string;
21
+ description?: string;
22
+ }
23
+
24
+ export interface DemoDef {
25
+ title: string;
26
+ description?: string;
27
+ component: React.ComponentType;
28
+ /** Preview frame: "center" (default), "start" (top-left), "full" (no padding), "tall". */
29
+ layout?: "center" | "start" | "full" | "tall";
30
+ }
31
+
32
+ /** A docs page: one `defineDoc({...})` per file in the site's docs folder (read statically by docsMetaPlugin). */
33
+ export interface DocDef<Category extends string = string> {
34
+ title: string;
35
+ category: Category;
36
+ description: string;
37
+ /** A link badge after the category in the page header: the primitive it wraps, the source file it documents. */
38
+ source?: {label: string; href: string};
39
+ demos: DemoDef[];
40
+ props?: {component: string; rows: PropRow[]}[];
41
+ /** Freeform content after the demos. */
42
+ notes?: React.ComponentType;
43
+ /** The notes' own h2 sections (`id` on each), listed in the table of contents after the demos. */
44
+ sections?: {id: string; title: string}[];
45
+ /** Sort order inside the category (lower first, default 50). */
46
+ order?: number;
47
+ }
48
+
49
+ export interface DocEntry<Category extends string = string> extends DocDef<Category> {
50
+ slug: string;
51
+ /** Source of each demo's own function, by demo index. */
52
+ demoSources: string[];
53
+ }
54
+
55
+ /**
56
+ * Declare a docs page. The site narrows the categories once:
57
+ *
58
+ * ```ts
59
+ * export const CATEGORIES = ["Foundations", "Components"] as const;
60
+ * export const defineDoc = (doc: DocDef<(typeof CATEGORIES)[number]>) => doc;
61
+ * ```
62
+ */
63
+ export function defineDoc<Category extends string>(doc: DocDef<Category>): DocDef<Category> {
64
+ return doc;
65
+ }
66
+
67
+ export function slugify(text: string) {
68
+ return text
69
+ .toLowerCase()
70
+ .replace(/[^a-z0-9]+/g, "-")
71
+ .replace(/^-|-$/g, "");
72
+ }
73
+
74
+ /** A highlighted, copyable code sample (the kit's CodeBlock), titled with its language by default. */
75
+ export function Code({
76
+ code,
77
+ language = "tsx",
78
+ title,
79
+ className,
80
+ }: {
81
+ code: string;
82
+ language?: string;
83
+ title?: string;
84
+ className?: string;
85
+ }) {
86
+ const lines = useHighlightedCode(code, language);
87
+ return (
88
+ <CodeBlock className={className}>
89
+ <CodeBlockHeader>
90
+ <CodeBlockTitle>{title ?? language}</CodeBlockTitle>
91
+ <CodeBlockActions>
92
+ <CodeBlockCopyButton value={code} />
93
+ </CodeBlockActions>
94
+ </CodeBlockHeader>
95
+ <CodeBlockContent className="max-h-[480px] text-[13px]">
96
+ {lines.map((line, i) => (
97
+ <span key={i} className="block min-h-lh">
98
+ {line}
99
+ </span>
100
+ ))}
101
+ </CodeBlockContent>
102
+ </CodeBlock>
103
+ );
104
+ }
105
+
106
+ const layoutClass: Record<NonNullable<DemoDef["layout"]>, string> = {
107
+ center: "flex min-h-56 items-center justify-center p-8",
108
+ start: "flex min-h-56 items-start justify-start p-8",
109
+ full: "overflow-hidden",
110
+ tall: "flex min-h-[28rem] items-center justify-center p-8",
111
+ };
112
+
113
+ /** One demo: its title and description, then Preview and Code tabs. The section's id is the slugified title. */
114
+ export function Demo({demo, code}: {demo: DemoDef; code: string}) {
115
+ const Component = demo.component;
116
+ return (
117
+ <section className="flex scroll-mt-24 flex-col gap-3" id={slugify(demo.title)}>
118
+ <div className="flex flex-col gap-1">
119
+ <h3 className="font-mono text-sm font-medium">{demo.title}</h3>
120
+ {demo.description ? <p className="text-sm text-muted-foreground">{demo.description}</p> : null}
121
+ </div>
122
+ <Tabs defaultValue="preview">
123
+ <TabsList>
124
+ <TabsTrigger value="preview">Preview</TabsTrigger>
125
+ <TabsTrigger value="code">Code</TabsTrigger>
126
+ </TabsList>
127
+ <TabsContent value="preview">
128
+ <div
129
+ data-slot="demo-frame"
130
+ className={cn("rounded-xl border bg-card", layoutClass[demo.layout ?? "center"])}
131
+ >
132
+ <Component />
133
+ </div>
134
+ </TabsContent>
135
+ <TabsContent value="code">
136
+ <Code code={code} />
137
+ </TabsContent>
138
+ </Tabs>
139
+ </section>
140
+ );
141
+ }
142
+
143
+ /** A component's props: name, type, default and description. */
144
+ export function PropsTable({component, rows}: {component: string; rows: PropRow[]}) {
145
+ return (
146
+ <section className="flex flex-col gap-3">
147
+ <h3 className="font-mono text-sm font-medium">{component}</h3>
148
+ <div className="overflow-hidden rounded-xl border bg-card">
149
+ <Table>
150
+ <TableHeader>
151
+ <TableRow>
152
+ <TableHead>Prop</TableHead>
153
+ <TableHead>Type</TableHead>
154
+ <TableHead>Default</TableHead>
155
+ <TableHead>Description</TableHead>
156
+ </TableRow>
157
+ </TableHeader>
158
+ <TableBody>
159
+ {rows.map((row) => (
160
+ <TableRow key={row.name}>
161
+ <TableCell className="font-mono text-xs text-primary-text">{row.name}</TableCell>
162
+ <TableCell className="font-mono text-xs whitespace-normal">{row.type}</TableCell>
163
+ <TableCell className="font-mono text-xs text-muted-foreground">
164
+ {row.default ?? "-"}
165
+ </TableCell>
166
+ <TableCell className="text-sm whitespace-normal text-muted-foreground">
167
+ {row.description}
168
+ </TableCell>
169
+ </TableRow>
170
+ ))}
171
+ </TableBody>
172
+ </Table>
173
+ </div>
174
+ </section>
175
+ );
176
+ }
@@ -0,0 +1,126 @@
1
+ "use client";
2
+
3
+ import {Badge} from "@repraze/kernelkit/components/badge";
4
+ import {
5
+ PageHeader,
6
+ PageHeaderDescription,
7
+ PageHeaderEyebrow,
8
+ PageHeaderTitle,
9
+ } from "@repraze/kernelkit/components/page-header";
10
+ import {Pager, PagerLink} from "@repraze/kernelkit/components/pager";
11
+ import {SiteAside, SiteLayout, SiteMain, SiteSidebar} from "@repraze/kernelkit/components/site-layout";
12
+ import {Toc, TocItem, TocList, TocTitle, useTocActive} from "@repraze/kernelkit/components/toc";
13
+ import {ExternalLinkIcon} from "lucide-react";
14
+ import type * as React from "react";
15
+
16
+ import {Demo, type DocEntry, PropsTable, slugify} from "./doc-kit";
17
+ import type {DocsIndex} from "./docs-index";
18
+
19
+ /** A router link to a docs page, used as a `render` element: `(slug) => <Link to="/docs/$slug" params={{slug}} />`. */
20
+ export type DocLinkFor = (slug: string) => React.ReactElement;
21
+
22
+ /** The table of contents of a page: its demos, the notes' sections, then the API. */
23
+ export function docToc(doc: DocEntry): {id: string; title: string}[] {
24
+ return [
25
+ ...doc.demos.map((demo) => ({id: slugify(demo.title), title: demo.title})),
26
+ ...(doc.sections ?? []),
27
+ ...(doc.props?.length ? [{id: "api", title: "API"}] : []),
28
+ ];
29
+ }
30
+
31
+ export interface DocPageProps<Category extends string = string> {
32
+ /** The loaded page (`index.loadDoc(slug)`, usually from the route's loader). */
33
+ doc: DocEntry<Category>;
34
+ index: DocsIndex<Category>;
35
+ linkFor: DocLinkFor;
36
+ }
37
+
38
+ /**
39
+ * A docs page in the suite's layout (GUIDELINES §7b): PageHeader (category, source badge, title,
40
+ * description), the demos, the notes, the API tables and the Pager in SiteMain; the Toc in SiteAside.
41
+ */
42
+ export function DocPage<Category extends string>({doc, index, linkFor}: DocPageProps<Category>) {
43
+ const {prev, next} = index.neighbours(doc.slug);
44
+ const Notes = doc.notes;
45
+ const sections = docToc(doc);
46
+ const active = useTocActive(sections.map((s) => s.id));
47
+
48
+ return (
49
+ <>
50
+ <SiteMain>
51
+ <PageHeader>
52
+ <PageHeaderEyebrow>
53
+ <Badge variant="outline">{doc.category}</Badge>
54
+ {doc.source ? (
55
+ <Badge
56
+ variant="secondary"
57
+ className="font-mono"
58
+ render={<a href={doc.source.href} target="_blank" rel="noreferrer" />}
59
+ >
60
+ {doc.source.label} <ExternalLinkIcon />
61
+ </Badge>
62
+ ) : null}
63
+ </PageHeaderEyebrow>
64
+ <PageHeaderTitle>{doc.title}</PageHeaderTitle>
65
+ <PageHeaderDescription>{doc.description}</PageHeaderDescription>
66
+ </PageHeader>
67
+
68
+ {doc.demos.map((demo, i) => (
69
+ <Demo key={demo.title} demo={demo} code={doc.demoSources[i] ?? ""} />
70
+ ))}
71
+
72
+ {Notes ? (
73
+ <div className="flex flex-col gap-4">
74
+ <Notes />
75
+ </div>
76
+ ) : null}
77
+
78
+ {doc.props?.length ? (
79
+ <section id="api" className="flex scroll-mt-24 flex-col gap-6">
80
+ <h2 className="font-heading text-xl font-semibold">API</h2>
81
+ {doc.props.map((p) => (
82
+ <PropsTable key={p.component} {...p} />
83
+ ))}
84
+ </section>
85
+ ) : null}
86
+
87
+ <Pager>
88
+ {prev ? (
89
+ <PagerLink direction="previous" render={linkFor(prev.slug)}>
90
+ {prev.title}
91
+ </PagerLink>
92
+ ) : null}
93
+ {next ? (
94
+ <PagerLink direction="next" render={linkFor(next.slug)}>
95
+ {next.title}
96
+ </PagerLink>
97
+ ) : null}
98
+ </Pager>
99
+ </SiteMain>
100
+ {sections.length > 1 ? (
101
+ <SiteAside>
102
+ <Toc>
103
+ <TocTitle>On this page</TocTitle>
104
+ <TocList>
105
+ {sections.map((s) => (
106
+ <TocItem key={s.id} href={`#${s.id}`} active={s.id === active}>
107
+ {s.title}
108
+ </TocItem>
109
+ ))}
110
+ </TocList>
111
+ </Toc>
112
+ </SiteAside>
113
+ ) : null}
114
+ </>
115
+ );
116
+ }
117
+
118
+ /** The suite's docs layout: SiteLayout with the docs nav in a sticky SiteSidebar, then the page (which adds its Toc). */
119
+ export function DocsLayout({nav, children}: {nav: React.ReactNode; children: React.ReactNode}) {
120
+ return (
121
+ <SiteLayout>
122
+ <SiteSidebar>{nav}</SiteSidebar>
123
+ {children}
124
+ </SiteLayout>
125
+ );
126
+ }