@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.
- package/dist/components/alert.d.ts.map +1 -1
- package/dist/components/alert.js +4 -6
- package/dist/components/alert.js.map +1 -1
- package/dist/components/avatar.js +1 -1
- package/dist/components/avatar.js.map +1 -1
- package/dist/components/badge.js +1 -1
- package/dist/components/badge.js.map +1 -1
- package/dist/components/breadcrumb.d.ts +1 -0
- package/dist/components/breadcrumb.d.ts.map +1 -1
- package/dist/components/breadcrumb.js +7 -6
- package/dist/components/breadcrumb.js.map +1 -1
- package/dist/components/calendar.js +1 -1
- package/dist/components/calendar.js.map +1 -1
- package/dist/components/callout.d.ts +8 -4
- package/dist/components/callout.d.ts.map +1 -1
- package/dist/components/callout.js +22 -12
- package/dist/components/callout.js.map +1 -1
- package/dist/components/chart.js +2 -2
- package/dist/components/chart.js.map +1 -1
- package/dist/components/code-block.js +1 -1
- package/dist/components/code-block.js.map +1 -1
- package/dist/components/command.d.ts +1 -1
- package/dist/components/command.d.ts.map +1 -1
- package/dist/components/command.js +3 -3
- package/dist/components/command.js.map +1 -1
- package/dist/components/data-table/elements/data-table-info-row.js +1 -1
- package/dist/components/data-table/elements/data-table-info-row.js.map +1 -1
- package/dist/components/dropdown-menu.d.ts +1 -1
- package/dist/components/dropdown-menu.d.ts.map +1 -1
- package/dist/components/dropdown-menu.js +2 -2
- package/dist/components/dropdown-menu.js.map +1 -1
- package/dist/components/kbd.d.ts.map +1 -1
- package/dist/components/kbd.js +2 -4
- package/dist/components/kbd.js.map +1 -1
- package/dist/components/message.js +2 -2
- package/dist/components/message.js.map +1 -1
- package/dist/components/meter.js +1 -1
- package/dist/components/meter.js.map +1 -1
- package/dist/components/navigation-menu.d.ts.map +1 -1
- package/dist/components/navigation-menu.js +3 -1
- package/dist/components/navigation-menu.js.map +1 -1
- package/dist/components/progress.js +1 -1
- package/dist/components/progress.js.map +1 -1
- package/dist/components/questionnaire.js +1 -1
- package/dist/components/questionnaire.js.map +1 -1
- package/dist/components/search-trigger.d.ts +1 -1
- package/dist/components/search-trigger.d.ts.map +1 -1
- package/dist/components/search-trigger.js +2 -2
- package/dist/components/search-trigger.js.map +1 -1
- package/dist/components/section.js +1 -1
- package/dist/components/section.js.map +1 -1
- package/dist/components/sidebar.d.ts.map +1 -1
- package/dist/components/sidebar.js +4 -3
- package/dist/components/sidebar.js.map +1 -1
- package/dist/components/site-footer.js +1 -1
- package/dist/components/site-footer.js.map +1 -1
- package/dist/components/site-header.d.ts +3 -3
- package/dist/components/site-header.d.ts.map +1 -1
- package/dist/components/site-header.js +7 -9
- package/dist/components/site-header.js.map +1 -1
- package/dist/components/stat.js +2 -2
- package/dist/components/stat.js.map +1 -1
- package/dist/components/tabs.d.ts +4 -2
- package/dist/components/tabs.d.ts.map +1 -1
- package/dist/components/tabs.js +13 -7
- package/dist/components/tabs.js.map +1 -1
- package/dist/components/theme-menu.d.ts +49 -0
- package/dist/components/theme-menu.d.ts.map +1 -0
- package/dist/components/theme-menu.js +75 -0
- package/dist/components/theme-menu.js.map +1 -0
- package/dist/components/toc.js +1 -1
- package/dist/components/toc.js.map +1 -1
- package/dist/components/tooltip.d.ts.map +1 -1
- package/dist/components/tooltip.js +5 -2
- package/dist/components/tooltip.js.map +1 -1
- package/dist/components/trace.js +1 -1
- package/dist/components/trace.js.map +1 -1
- package/dist/lib/recipes.d.ts +17 -1
- package/dist/lib/recipes.d.ts.map +1 -1
- package/dist/lib/recipes.js +19 -1
- package/dist/lib/recipes.js.map +1 -1
- package/dist/provider/theme-provider.d.ts +4 -0
- package/dist/provider/theme-provider.d.ts.map +1 -1
- package/dist/provider/theme-provider.js +14 -1
- package/dist/provider/theme-provider.js.map +1 -1
- package/package.json +1 -1
- package/src/components/alert.tsx +4 -6
- package/src/components/avatar.tsx +1 -1
- package/src/components/badge.tsx +1 -1
- package/src/components/breadcrumb.tsx +8 -7
- package/src/components/calendar.tsx +1 -1
- package/src/components/callout.tsx +27 -12
- package/src/components/chart.tsx +2 -2
- package/src/components/code-block.tsx +1 -1
- package/src/components/command.tsx +3 -4
- package/src/components/data-table/elements/data-table-info-row.tsx +1 -1
- package/src/components/dropdown-menu.tsx +2 -2
- package/src/components/kbd.tsx +2 -4
- package/src/components/message.tsx +2 -2
- package/src/components/meter.tsx +1 -1
- package/src/components/navigation-menu.tsx +4 -0
- package/src/components/progress.tsx +1 -1
- package/src/components/questionnaire.tsx +1 -1
- package/src/components/search-trigger.tsx +2 -3
- package/src/components/section.tsx +1 -1
- package/src/components/sidebar.tsx +4 -3
- package/src/components/site-footer.tsx +1 -1
- package/src/components/site-header.tsx +8 -9
- package/src/components/stat.tsx +2 -2
- package/src/components/tabs.tsx +17 -9
- package/src/components/theme-menu.tsx +243 -0
- package/src/components/toc.tsx +1 -1
- package/src/components/tooltip.tsx +15 -3
- package/src/components/trace.tsx +1 -1
- package/src/lib/recipes.ts +23 -2
- package/src/provider/theme-provider.tsx +18 -1
- package/src/styles/kernelkit.css +4 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import {Button} from "@repraze/kernelkit/components/button";
|
|
4
|
+
import {Label} from "@repraze/kernelkit/components/label";
|
|
5
|
+
import {
|
|
6
|
+
Popover,
|
|
7
|
+
PopoverContent,
|
|
8
|
+
PopoverHeader,
|
|
9
|
+
PopoverTitle,
|
|
10
|
+
PopoverTrigger,
|
|
11
|
+
} from "@repraze/kernelkit/components/popover";
|
|
12
|
+
import {Switch} from "@repraze/kernelkit/components/switch";
|
|
13
|
+
import {ToggleGroup, ToggleGroupItem} from "@repraze/kernelkit/components/toggle-group";
|
|
14
|
+
import {Tooltip, TooltipContent, TooltipTrigger} from "@repraze/kernelkit/components/tooltip";
|
|
15
|
+
import {type Accent, ACCENTS, type Mode, RADII, type Radius, useTheme} from "@repraze/kernelkit/provider";
|
|
16
|
+
import {cn} from "cn";
|
|
17
|
+
import {CheckIcon, type LucideIcon, MonitorIcon, MoonIcon, RotateCcwIcon, SunIcon} from "lucide-react";
|
|
18
|
+
import * as React from "react";
|
|
19
|
+
|
|
20
|
+
/** Mode choices in display order, with the icon the trigger shows for each. */
|
|
21
|
+
const THEME_MODES: readonly {value: Mode; label: string; icon: LucideIcon}[] = [
|
|
22
|
+
{value: "light", label: "Light", icon: SunIcon},
|
|
23
|
+
{value: "dark", label: "Dark", icon: MoonIcon},
|
|
24
|
+
{value: "system", label: "System", icon: MonitorIcon},
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const modeOption = (mode: Mode) => THEME_MODES.find((m) => m.value === mode) ?? THEME_MODES[2]!;
|
|
28
|
+
|
|
29
|
+
/** The icon for the chosen mode (not the resolved one): sun, moon, or monitor for "system". */
|
|
30
|
+
function ThemeModeIcon({mode, ...props}: {mode?: Mode} & React.ComponentProps<LucideIcon>) {
|
|
31
|
+
const theme = useTheme();
|
|
32
|
+
const Icon = modeOption(mode ?? theme.mode).icon;
|
|
33
|
+
return <Icon data-slot="theme-mode-icon" {...props} />;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Light / dark / system as a segmented control. */
|
|
37
|
+
function ThemeModePicker({className}: {className?: string}) {
|
|
38
|
+
const theme = useTheme();
|
|
39
|
+
return (
|
|
40
|
+
<ToggleGroup
|
|
41
|
+
data-slot="theme-mode-picker"
|
|
42
|
+
aria-label="Mode"
|
|
43
|
+
variant="outline"
|
|
44
|
+
size="sm"
|
|
45
|
+
value={[theme.mode]}
|
|
46
|
+
onValueChange={(v) => v[0] && theme.setMode(v[0] as Mode)}
|
|
47
|
+
className={cn("w-full", className)}
|
|
48
|
+
>
|
|
49
|
+
{THEME_MODES.map(({value, label, icon: Icon}) => (
|
|
50
|
+
<ToggleGroupItem key={value} value={value} className="flex-1">
|
|
51
|
+
<Icon /> {label}
|
|
52
|
+
</ToggleGroupItem>
|
|
53
|
+
))}
|
|
54
|
+
</ToggleGroup>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Swatch per accent: the vivid fill tier, with that accent's own on-fill color for the check mark. */
|
|
59
|
+
const swatch = (accent: Accent) => ({
|
|
60
|
+
background: `var(--tc-accent-${accent}-accent-fill)`,
|
|
61
|
+
color: `var(--tc-accent-${accent}-on-accent-fill)`,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
/** The eight TermiColor accents as swatches; sets the theme's accent. */
|
|
65
|
+
function ThemeAccentPicker({className}: {className?: string}) {
|
|
66
|
+
const theme = useTheme();
|
|
67
|
+
return (
|
|
68
|
+
<div
|
|
69
|
+
data-slot="theme-accent-picker"
|
|
70
|
+
role="group"
|
|
71
|
+
aria-label="Accent"
|
|
72
|
+
className={cn("grid grid-cols-8 gap-1.5", className)}
|
|
73
|
+
>
|
|
74
|
+
{ACCENTS.map((accent) => {
|
|
75
|
+
const on = theme.accent === accent;
|
|
76
|
+
return (
|
|
77
|
+
<Tooltip key={accent}>
|
|
78
|
+
<TooltipTrigger
|
|
79
|
+
render={
|
|
80
|
+
<button
|
|
81
|
+
type="button"
|
|
82
|
+
aria-label={accent}
|
|
83
|
+
aria-pressed={on}
|
|
84
|
+
onClick={() => theme.setAccent(accent)}
|
|
85
|
+
style={swatch(accent)}
|
|
86
|
+
className={cn(
|
|
87
|
+
"flex aspect-square items-center justify-center rounded-md outline-none",
|
|
88
|
+
"ring-offset-2 ring-offset-transparent transition-shadow duration-100 motion-reduce:transition-none",
|
|
89
|
+
"hover:ring-2 hover:ring-input focus-visible:ring-2 focus-visible:ring-ring",
|
|
90
|
+
on && "ring-2 ring-foreground hover:ring-foreground",
|
|
91
|
+
)}
|
|
92
|
+
/>
|
|
93
|
+
}
|
|
94
|
+
>
|
|
95
|
+
{on ? <CheckIcon className="size-3.5" strokeWidth={3} /> : null}
|
|
96
|
+
</TooltipTrigger>
|
|
97
|
+
<TooltipContent className="capitalize">{accent}</TooltipContent>
|
|
98
|
+
</Tooltip>
|
|
99
|
+
);
|
|
100
|
+
})}
|
|
101
|
+
</div>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** The radius scale as a segmented control; sets the theme's radius. */
|
|
106
|
+
function ThemeRadiusPicker({className}: {className?: string}) {
|
|
107
|
+
const theme = useTheme();
|
|
108
|
+
return (
|
|
109
|
+
<ToggleGroup
|
|
110
|
+
data-slot="theme-radius-picker"
|
|
111
|
+
aria-label="Radius"
|
|
112
|
+
variant="outline"
|
|
113
|
+
size="sm"
|
|
114
|
+
value={[theme.radius]}
|
|
115
|
+
onValueChange={(v) => v[0] && theme.setRadius(v[0] as Radius)}
|
|
116
|
+
className={cn("w-full", className)}
|
|
117
|
+
>
|
|
118
|
+
{RADII.map((radius) => (
|
|
119
|
+
<ToggleGroupItem key={radius} value={radius} className="flex-1 font-alt">
|
|
120
|
+
{radius}
|
|
121
|
+
</ToggleGroupItem>
|
|
122
|
+
))}
|
|
123
|
+
</ToggleGroup>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** High contrast on/off, with its label. */
|
|
128
|
+
function ThemeContrastSwitch({className}: {className?: string}) {
|
|
129
|
+
const theme = useTheme();
|
|
130
|
+
const id = React.useId();
|
|
131
|
+
return (
|
|
132
|
+
<div data-slot="theme-contrast-switch" className={cn("flex items-center justify-between gap-2", className)}>
|
|
133
|
+
<Label htmlFor={id}>High contrast</Label>
|
|
134
|
+
<Switch
|
|
135
|
+
id={id}
|
|
136
|
+
checked={theme.contrast === "high"}
|
|
137
|
+
onCheckedChange={(on) => theme.setContrast(on ? "high" : "normal")}
|
|
138
|
+
/>
|
|
139
|
+
</div>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** A labelled block inside the menu. */
|
|
144
|
+
function ThemeMenuSection({
|
|
145
|
+
label,
|
|
146
|
+
children,
|
|
147
|
+
className,
|
|
148
|
+
}: {
|
|
149
|
+
label: string;
|
|
150
|
+
children: React.ReactNode;
|
|
151
|
+
className?: string;
|
|
152
|
+
}) {
|
|
153
|
+
return (
|
|
154
|
+
<div data-slot="theme-menu-section" className={cn("flex flex-col gap-2", className)}>
|
|
155
|
+
<span className="text-xs font-medium text-muted-foreground">{label}</span>
|
|
156
|
+
{children}
|
|
157
|
+
</div>
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
interface ThemeMenuProps extends Omit<React.ComponentProps<typeof Button>, "children" | "title"> {
|
|
162
|
+
/** Popover title. */
|
|
163
|
+
title?: React.ReactNode;
|
|
164
|
+
/** Extra controls, after the built-in ones and before Reset. */
|
|
165
|
+
children?: React.ReactNode;
|
|
166
|
+
/** Popover alignment against the trigger. */
|
|
167
|
+
align?: "start" | "center" | "end";
|
|
168
|
+
contentClassName?: string;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* The suite's theme control (GUIDELINES §7b): one ghost icon button in `SiteHeaderActions` showing the
|
|
173
|
+
* chosen mode (sun, moon, monitor), opening a popover with mode, accent, high contrast, radius and a
|
|
174
|
+
* Reset to the provider's defaults. Reads and writes `useTheme()`, so it needs a `ThemeProvider`.
|
|
175
|
+
* Trigger props (`aria-label`, `className`, `data-*`) pass through to the button.
|
|
176
|
+
*/
|
|
177
|
+
function ThemeMenu({title = "Theme", children, align = "end", contentClassName, className, ...props}: ThemeMenuProps) {
|
|
178
|
+
const theme = useTheme();
|
|
179
|
+
const {defaults} = theme;
|
|
180
|
+
const isDefault =
|
|
181
|
+
theme.mode === defaults.mode &&
|
|
182
|
+
theme.accent === defaults.accent &&
|
|
183
|
+
theme.contrast === defaults.contrast &&
|
|
184
|
+
theme.radius === defaults.radius;
|
|
185
|
+
return (
|
|
186
|
+
<Popover>
|
|
187
|
+
<PopoverTrigger
|
|
188
|
+
render={
|
|
189
|
+
<Button
|
|
190
|
+
variant="ghost"
|
|
191
|
+
size="icon"
|
|
192
|
+
aria-label={`Theme: ${modeOption(theme.mode).label}`}
|
|
193
|
+
data-slot="theme-menu-trigger"
|
|
194
|
+
data-mode={theme.mode}
|
|
195
|
+
className={className}
|
|
196
|
+
{...props}
|
|
197
|
+
/>
|
|
198
|
+
}
|
|
199
|
+
>
|
|
200
|
+
<ThemeModeIcon />
|
|
201
|
+
</PopoverTrigger>
|
|
202
|
+
<PopoverContent data-slot="theme-menu-content" align={align} className={cn("w-72 gap-4", contentClassName)}>
|
|
203
|
+
<PopoverHeader>
|
|
204
|
+
<PopoverTitle>{title}</PopoverTitle>
|
|
205
|
+
</PopoverHeader>
|
|
206
|
+
<ThemeMenuSection label="Mode">
|
|
207
|
+
<ThemeModePicker />
|
|
208
|
+
</ThemeMenuSection>
|
|
209
|
+
<ThemeMenuSection label="Accent">
|
|
210
|
+
<ThemeAccentPicker />
|
|
211
|
+
</ThemeMenuSection>
|
|
212
|
+
<ThemeContrastSwitch />
|
|
213
|
+
<ThemeMenuSection label="Radius">
|
|
214
|
+
<ThemeRadiusPicker />
|
|
215
|
+
</ThemeMenuSection>
|
|
216
|
+
{children}
|
|
217
|
+
<div className="-mx-3 -mb-3 border-t border-border p-3">
|
|
218
|
+
<Button
|
|
219
|
+
variant="outline"
|
|
220
|
+
size="sm"
|
|
221
|
+
data-slot="theme-menu-reset"
|
|
222
|
+
className="w-full"
|
|
223
|
+
disabled={isDefault}
|
|
224
|
+
onClick={theme.resetTheme}
|
|
225
|
+
>
|
|
226
|
+
<RotateCcwIcon data-icon="inline-start" /> Reset to defaults
|
|
227
|
+
</Button>
|
|
228
|
+
</div>
|
|
229
|
+
</PopoverContent>
|
|
230
|
+
</Popover>
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export {
|
|
235
|
+
ThemeAccentPicker,
|
|
236
|
+
ThemeContrastSwitch,
|
|
237
|
+
ThemeMenu,
|
|
238
|
+
ThemeMenuSection,
|
|
239
|
+
ThemeModeIcon,
|
|
240
|
+
ThemeModePicker,
|
|
241
|
+
ThemeRadiusPicker,
|
|
242
|
+
};
|
|
243
|
+
export type {ThemeMenuProps};
|
package/src/components/toc.tsx
CHANGED
|
@@ -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-
|
|
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
|
-
//
|
|
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-
|
|
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
|
|
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>
|
package/src/components/trace.tsx
CHANGED
|
@@ -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-
|
|
70
|
+
className={cn("shrink-0 font-alt text-xs text-muted-foreground tabular-nums", className)}
|
|
71
71
|
{...props}
|
|
72
72
|
/>
|
|
73
73
|
);
|
package/src/lib/recipes.ts
CHANGED
|
@@ -23,9 +23,16 @@ export const popupSurface =
|
|
|
23
23
|
export const popupMotion =
|
|
24
24
|
"origin-(--transform-origin) duration-100 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 motion-reduce:animate-none";
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* The sidebar's current-row indicator (§7c): a 2px accent pill inset on the row's left edge, hidden until
|
|
28
|
+
* the row's state shows it (`data-highlighted:after:opacity-100`, `data-active:after:opacity-100`...).
|
|
29
|
+
* The row keeps its own rounding on every corner.
|
|
30
|
+
*/
|
|
31
|
+
export const rowIndicator =
|
|
32
|
+
"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";
|
|
33
|
+
|
|
26
34
|
/** Row inside a popup or list: menu item, select item, command item (§4, §6). */
|
|
27
|
-
export const listItem =
|
|
28
|
-
"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-highlighted:bg-accent data-highlighted:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-highlighted:[&_svg:not([class*='text-'])]:text-accent-foreground";
|
|
35
|
+
export const listItem = `${rowIndicator} data-highlighted:after:opacity-100 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-highlighted:bg-accent data-highlighted:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-highlighted:[&_svg:not([class*='text-'])]:text-accent-foreground`;
|
|
29
36
|
|
|
30
37
|
/** Group label inside a popup or list. */
|
|
31
38
|
export const listLabel = "px-2 py-1.5 text-xs font-medium text-muted-foreground";
|
|
@@ -45,3 +52,17 @@ export const placeholderFill = "bg-foreground/10 hc:bg-border";
|
|
|
45
52
|
|
|
46
53
|
/** Selected row (table, data table, code line, list): accent tint + 2px inset primary indicator (§6). */
|
|
47
54
|
export const selectedRow = "bg-accent text-accent-foreground shadow-[inset_2px_0_0_var(--primary-text)]";
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Status surface shared by Alert and Callout (§2): one `soft` / `outline` axis, same values in both.
|
|
58
|
+
* Expects a color variant (`--c`, `--c-tint`) on the same element. Neutral swaps the hue border for the
|
|
59
|
+
* plain border on a card surface (`statusSurfaceNeutral`).
|
|
60
|
+
*/
|
|
61
|
+
export const statusSurface = {
|
|
62
|
+
// status tint behind foreground text; the hue lives in the border, icon and accents (TermiColor rule)
|
|
63
|
+
soft: "border-(--c)/30 bg-(--c-tint) hc:border-(--c)",
|
|
64
|
+
// no wash: card surface with a hue border, for dense layouts
|
|
65
|
+
outline: "border-(--c)/50 bg-card hc:border-(--c)",
|
|
66
|
+
} as const;
|
|
67
|
+
|
|
68
|
+
export const statusSurfaceNeutral = "border-border bg-card hc:border-border";
|
|
@@ -32,6 +32,10 @@ export interface ThemeContextValue extends ThemeState {
|
|
|
32
32
|
setContrast: (contrast: ThemeState["contrast"]) => void;
|
|
33
33
|
setAccent: (accent: ThemeState["accent"]) => void;
|
|
34
34
|
setRadius: (radius: ThemeState["radius"]) => void;
|
|
35
|
+
/** The values `resetTheme` restores: the provider's `defaultTheme` over the kit defaults. */
|
|
36
|
+
defaults: ThemeState;
|
|
37
|
+
/** Forget the stored choice and go back to `defaults`. */
|
|
38
|
+
resetTheme: () => void;
|
|
35
39
|
}
|
|
36
40
|
|
|
37
41
|
const ThemeContext = createContext<ThemeContextValue | null>(null);
|
|
@@ -139,6 +143,17 @@ export function ThemeProvider({
|
|
|
139
143
|
[storageKey, fallback],
|
|
140
144
|
);
|
|
141
145
|
|
|
146
|
+
const resetTheme = useCallback(() => {
|
|
147
|
+
if (storageKey) {
|
|
148
|
+
try {
|
|
149
|
+
window.localStorage.removeItem(storageKey);
|
|
150
|
+
} catch {
|
|
151
|
+
// storage blocked: the in-memory reset still applies
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
setStored(fallback);
|
|
155
|
+
}, [storageKey, fallback]);
|
|
156
|
+
|
|
142
157
|
const value = useMemo<ThemeContextValue>(
|
|
143
158
|
() => ({
|
|
144
159
|
...theme,
|
|
@@ -148,8 +163,10 @@ export function ThemeProvider({
|
|
|
148
163
|
setContrast: (contrast) => setTheme({contrast}),
|
|
149
164
|
setAccent: (accent) => setTheme({accent}),
|
|
150
165
|
setRadius: (radius) => setTheme({radius}),
|
|
166
|
+
defaults: fallback,
|
|
167
|
+
resetTheme,
|
|
151
168
|
}),
|
|
152
|
-
[theme, resolvedMode, setTheme],
|
|
169
|
+
[theme, resolvedMode, setTheme, fallback, resetTheme],
|
|
153
170
|
);
|
|
154
171
|
|
|
155
172
|
return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
|
package/src/styles/kernelkit.css
CHANGED
|
@@ -133,6 +133,9 @@
|
|
|
133
133
|
--kk-font-mono: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
|
|
134
134
|
/* Titles: card, dialog, sheet, empty, alert. Mono by default (the TermiColor look). */
|
|
135
135
|
--kk-font-heading: var(--kk-font-mono);
|
|
136
|
+
/* Labels and readouts: badges, tooltips, breadcrumbs, kbd, shortcuts, eyebrows, nav, values. Mono by
|
|
137
|
+
default (the TermiColor look); point it at any face. Code always stays on --kk-font-mono. */
|
|
138
|
+
--kk-font-alt: var(--kk-font-mono);
|
|
136
139
|
/* Site shell (site-header, site-layout): content width, and the sticky header's height that
|
|
137
140
|
sidebars and anchors offset by. A site with a banner above the header row adds it here. */
|
|
138
141
|
--kk-site-width: 80rem;
|
|
@@ -159,6 +162,7 @@
|
|
|
159
162
|
--font-sans: var(--kk-font-sans);
|
|
160
163
|
--font-mono: var(--kk-font-mono);
|
|
161
164
|
--font-heading: var(--kk-font-heading);
|
|
165
|
+
--font-alt: var(--kk-font-alt);
|
|
162
166
|
|
|
163
167
|
--radius-xs: calc(var(--kk-radius) * 0.25);
|
|
164
168
|
--radius-sm: calc(var(--kk-radius) * 0.5);
|