@theruntime/components 0.0.0 → 0.0.1

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 (91) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +113 -4
  3. package/src/accordion.tsx +62 -0
  4. package/src/alert-dialog.tsx +177 -0
  5. package/src/alert.tsx +60 -0
  6. package/src/aspect-ratio.tsx +7 -0
  7. package/src/attachment.tsx +192 -0
  8. package/src/avatar.tsx +94 -0
  9. package/src/badge.tsx +46 -0
  10. package/src/breadcrumb.tsx +102 -0
  11. package/src/bubble.tsx +125 -0
  12. package/src/button-group.tsx +82 -0
  13. package/src/button.tsx +62 -0
  14. package/src/calendar.tsx +178 -0
  15. package/src/card.tsx +75 -0
  16. package/src/carousel.tsx +228 -0
  17. package/src/chart/container.tsx +72 -0
  18. package/src/chart/context.tsx +39 -0
  19. package/src/chart/index.ts +4 -0
  20. package/src/chart/legend.tsx +63 -0
  21. package/src/chart/payload.ts +31 -0
  22. package/src/chart/tooltip.tsx +149 -0
  23. package/src/checkbox.tsx +27 -0
  24. package/src/cn.ts +36 -0
  25. package/src/collapsible.tsx +19 -0
  26. package/src/combobox/chips.tsx +71 -0
  27. package/src/combobox/content.tsx +123 -0
  28. package/src/combobox/index.ts +13 -0
  29. package/src/combobox/input.tsx +39 -0
  30. package/src/combobox/trigger.tsx +44 -0
  31. package/src/command.tsx +153 -0
  32. package/src/context-menu.tsx +222 -0
  33. package/src/dialog.tsx +142 -0
  34. package/src/direction.tsx +18 -0
  35. package/src/drawer.tsx +122 -0
  36. package/src/dropdown-menu.tsx +226 -0
  37. package/src/empty.tsx +94 -0
  38. package/src/field.tsx +232 -0
  39. package/src/form.tsx +153 -0
  40. package/src/hover-card.tsx +36 -0
  41. package/src/index.ts +90 -0
  42. package/src/input-group.tsx +156 -0
  43. package/src/input-otp.tsx +68 -0
  44. package/src/input.tsx +21 -0
  45. package/src/item.tsx +172 -0
  46. package/src/kbd.tsx +28 -0
  47. package/src/label.tsx +19 -0
  48. package/src/marker.tsx +66 -0
  49. package/src/menubar.tsx +250 -0
  50. package/src/message-scroller.tsx +128 -0
  51. package/src/message.tsx +85 -0
  52. package/src/native-select.tsx +56 -0
  53. package/src/navigation-menu.tsx +161 -0
  54. package/src/pagination.tsx +106 -0
  55. package/src/popover.tsx +72 -0
  56. package/src/progress.tsx +26 -0
  57. package/src/questionnaire/actions.tsx +118 -0
  58. package/src/questionnaire/choices.tsx +113 -0
  59. package/src/questionnaire/index.ts +21 -0
  60. package/src/questionnaire/root.tsx +75 -0
  61. package/src/radio-group.tsx +43 -0
  62. package/src/resizable.tsx +45 -0
  63. package/src/scroll-area.tsx +54 -0
  64. package/src/select.tsx +173 -0
  65. package/src/separator.tsx +26 -0
  66. package/src/sheet.tsx +132 -0
  67. package/src/shell/app-tab.tsx +67 -0
  68. package/src/shell/entry-row.tsx +137 -0
  69. package/src/shell/field.tsx +83 -0
  70. package/src/shell/group-head.tsx +19 -0
  71. package/src/shell/scope-chip.tsx +32 -0
  72. package/src/shell/suggest-panel.tsx +92 -0
  73. package/src/sidebar/context.tsx +128 -0
  74. package/src/sidebar/index.ts +23 -0
  75. package/src/sidebar/menu.tsx +239 -0
  76. package/src/sidebar/sections.tsx +118 -0
  77. package/src/sidebar/sidebar.tsx +183 -0
  78. package/src/skeleton.tsx +13 -0
  79. package/src/slider.tsx +56 -0
  80. package/src/sonner.tsx +41 -0
  81. package/src/spinner.tsx +16 -0
  82. package/src/styles.css +149 -0
  83. package/src/switch.tsx +33 -0
  84. package/src/table.tsx +90 -0
  85. package/src/tabs.tsx +79 -0
  86. package/src/textarea.tsx +18 -0
  87. package/src/toggle-group.tsx +81 -0
  88. package/src/toggle.tsx +44 -0
  89. package/src/tooltip.tsx +51 -0
  90. package/src/use-mobile.ts +21 -0
  91. package/README.md +0 -3
@@ -0,0 +1,183 @@
1
+ import * as React from "react";
2
+ import { SidebarSimpleIcon } from "@phosphor-icons/react";
3
+
4
+ import { cn, type CSSVarStyle } from "../cn";
5
+ import { Button } from "../button";
6
+ import { Input } from "../input";
7
+ import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from "../sheet";
8
+ import { SIDEBAR_WIDTH_MOBILE, useSidebar } from "./context";
9
+
10
+ export function Sidebar({
11
+ side = "left",
12
+ variant = "sidebar",
13
+ collapsible = "offcanvas",
14
+ className,
15
+ children,
16
+ ...props
17
+ }: React.ComponentProps<"div"> & {
18
+ side?: "left" | "right";
19
+ variant?: "sidebar" | "floating" | "inset";
20
+ collapsible?: "offcanvas" | "icon" | "none";
21
+ }) {
22
+ const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
23
+
24
+ if (collapsible === "none") {
25
+ return (
26
+ <div
27
+ data-slot="sidebar"
28
+ className={cn(
29
+ "flex h-full w-(--sidebar-width) flex-col bg-sidebar text-sidebar-foreground",
30
+ className,
31
+ )}
32
+ {...props}
33
+ >
34
+ {children}
35
+ </div>
36
+ );
37
+ }
38
+
39
+ if (isMobile) {
40
+ const mobileStyle: CSSVarStyle = { "--sidebar-width": SIDEBAR_WIDTH_MOBILE };
41
+ return (
42
+ <Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}>
43
+ <SheetContent
44
+ data-sidebar="sidebar"
45
+ data-slot="sidebar"
46
+ data-mobile="true"
47
+ className="w-(--sidebar-width) bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden"
48
+ style={mobileStyle}
49
+ side={side}
50
+ >
51
+ <SheetHeader className="sr-only">
52
+ <SheetTitle>Sidebar</SheetTitle>
53
+ <SheetDescription>Displays the mobile sidebar.</SheetDescription>
54
+ </SheetHeader>
55
+ <div className="flex h-full w-full flex-col">{children}</div>
56
+ </SheetContent>
57
+ </Sheet>
58
+ );
59
+ }
60
+
61
+ return (
62
+ <div
63
+ className="group peer hidden text-sidebar-foreground md:block"
64
+ data-state={state}
65
+ data-collapsible={state === "collapsed" ? collapsible : ""}
66
+ data-variant={variant}
67
+ data-side={side}
68
+ data-slot="sidebar"
69
+ >
70
+ {/* This is what handles the sidebar gap on desktop */}
71
+ <div
72
+ data-slot="sidebar-gap"
73
+ className={cn(
74
+ "relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear",
75
+ "group-data-[collapsible=offcanvas]:w-0",
76
+ "group-data-[side=right]:rotate-180",
77
+ variant === "floating" || variant === "inset"
78
+ ? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]"
79
+ : "group-data-[collapsible=icon]:w-(--sidebar-width-icon)",
80
+ )}
81
+ />
82
+ <div
83
+ data-slot="sidebar-container"
84
+ className={cn(
85
+ "fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex",
86
+ side === "left"
87
+ ? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
88
+ : "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
89
+ // Adjust the padding for floating and inset variants.
90
+ variant === "floating" || variant === "inset"
91
+ ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]"
92
+ : "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l",
93
+ className,
94
+ )}
95
+ {...props}
96
+ >
97
+ <div
98
+ data-sidebar="sidebar"
99
+ data-slot="sidebar-inner"
100
+ className="flex h-full w-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:border-sidebar-border group-data-[variant=floating]:shadow-sm"
101
+ >
102
+ {children}
103
+ </div>
104
+ </div>
105
+ </div>
106
+ );
107
+ }
108
+
109
+ export function SidebarTrigger({
110
+ className,
111
+ onClick,
112
+ ...props
113
+ }: React.ComponentProps<typeof Button>) {
114
+ const { toggleSidebar } = useSidebar();
115
+
116
+ return (
117
+ <Button
118
+ data-sidebar="trigger"
119
+ data-slot="sidebar-trigger"
120
+ variant="ghost"
121
+ size="icon"
122
+ className={cn("size-7", className)}
123
+ onClick={(event) => {
124
+ onClick?.(event);
125
+ toggleSidebar();
126
+ }}
127
+ {...props}
128
+ >
129
+ <SidebarSimpleIcon />
130
+ <span className="sr-only">Toggle Sidebar</span>
131
+ </Button>
132
+ );
133
+ }
134
+
135
+ export function SidebarRail({ className, ...props }: React.ComponentProps<"button">) {
136
+ const { toggleSidebar } = useSidebar();
137
+
138
+ return (
139
+ <button
140
+ data-sidebar="rail"
141
+ data-slot="sidebar-rail"
142
+ aria-label="Toggle Sidebar"
143
+ tabIndex={-1}
144
+ onClick={toggleSidebar}
145
+ title="Toggle Sidebar"
146
+ className={cn(
147
+ "absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] hover:after:bg-sidebar-border sm:flex",
148
+ "in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize",
149
+ "[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize",
150
+ "group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full hover:group-data-[collapsible=offcanvas]:bg-sidebar",
151
+ "[[data-side=left][data-collapsible=offcanvas]_&]:-right-2",
152
+ "[[data-side=right][data-collapsible=offcanvas]_&]:-left-2",
153
+ className,
154
+ )}
155
+ {...props}
156
+ />
157
+ );
158
+ }
159
+
160
+ export function SidebarInset({ className, ...props }: React.ComponentProps<"main">) {
161
+ return (
162
+ <main
163
+ data-slot="sidebar-inset"
164
+ className={cn(
165
+ "relative flex w-full flex-1 flex-col bg-background",
166
+ "md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2",
167
+ className,
168
+ )}
169
+ {...props}
170
+ />
171
+ );
172
+ }
173
+
174
+ export function SidebarInput({ className, ...props }: React.ComponentProps<typeof Input>) {
175
+ return (
176
+ <Input
177
+ data-slot="sidebar-input"
178
+ data-sidebar="input"
179
+ className={cn("h-8 w-full bg-background shadow-none", className)}
180
+ {...props}
181
+ />
182
+ );
183
+ }
@@ -0,0 +1,13 @@
1
+ import { cn } from "./cn";
2
+
3
+ function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
4
+ return (
5
+ <div
6
+ data-slot="skeleton"
7
+ className={cn("animate-pulse rounded-md bg-accent", className)}
8
+ {...props}
9
+ />
10
+ );
11
+ }
12
+
13
+ export { Skeleton };
package/src/slider.tsx ADDED
@@ -0,0 +1,56 @@
1
+ import * as React from "react";
2
+ import { Slider as SliderPrimitive } from "radix-ui";
3
+
4
+ import { cn } from "./cn";
5
+
6
+ function Slider({
7
+ className,
8
+ defaultValue,
9
+ value,
10
+ min = 0,
11
+ max = 100,
12
+ ...props
13
+ }: React.ComponentProps<typeof SliderPrimitive.Root>) {
14
+ const _values = React.useMemo(
15
+ () => (Array.isArray(value) ? value : Array.isArray(defaultValue) ? defaultValue : [min, max]),
16
+ [value, defaultValue, min, max],
17
+ );
18
+
19
+ return (
20
+ <SliderPrimitive.Root
21
+ data-slot="slider"
22
+ defaultValue={defaultValue}
23
+ value={value}
24
+ min={min}
25
+ max={max}
26
+ className={cn(
27
+ "relative flex w-full touch-none items-center select-none data-[disabled]:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col",
28
+ className,
29
+ )}
30
+ {...props}
31
+ >
32
+ <SliderPrimitive.Track
33
+ data-slot="slider-track"
34
+ className={cn(
35
+ "relative grow overflow-hidden rounded-full bg-muted data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5",
36
+ )}
37
+ >
38
+ <SliderPrimitive.Range
39
+ data-slot="slider-range"
40
+ className={cn(
41
+ "absolute bg-primary data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full",
42
+ )}
43
+ />
44
+ </SliderPrimitive.Track>
45
+ {Array.from({ length: _values.length }, (_, index) => (
46
+ <SliderPrimitive.Thumb
47
+ data-slot="slider-thumb"
48
+ key={index}
49
+ className="block size-4 shrink-0 rounded-full border border-primary bg-white shadow-sm ring-ring/50 transition-[color,box-shadow] hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
50
+ />
51
+ ))}
52
+ </SliderPrimitive.Root>
53
+ );
54
+ }
55
+
56
+ export { Slider };
package/src/sonner.tsx ADDED
@@ -0,0 +1,41 @@
1
+ import {
2
+ CheckCircleIcon,
3
+ CircleNotchIcon,
4
+ InfoIcon,
5
+ WarningIcon,
6
+ XCircleIcon,
7
+ } from "@phosphor-icons/react";
8
+ import { useTheme } from "next-themes";
9
+ import { Toaster as Sonner, type ToasterProps } from "sonner";
10
+
11
+ import type { CSSVarStyle } from "./cn";
12
+
13
+ const Toaster = ({ ...props }: ToasterProps) => {
14
+ const { theme = "system" } = useTheme();
15
+ const sonnerTheme: ToasterProps["theme"] =
16
+ theme === "light" || theme === "dark" ? theme : "system";
17
+ const style: CSSVarStyle = {
18
+ "--normal-bg": "var(--popover)",
19
+ "--normal-text": "var(--popover-foreground)",
20
+ "--normal-border": "var(--border)",
21
+ "--border-radius": "var(--radius)",
22
+ };
23
+
24
+ return (
25
+ <Sonner
26
+ theme={sonnerTheme}
27
+ className="toaster group"
28
+ icons={{
29
+ success: <CheckCircleIcon className="size-4" />,
30
+ info: <InfoIcon className="size-4" />,
31
+ warning: <WarningIcon className="size-4" />,
32
+ error: <XCircleIcon className="size-4" />,
33
+ loading: <CircleNotchIcon className="size-4 animate-spin" />,
34
+ }}
35
+ style={style}
36
+ {...props}
37
+ />
38
+ );
39
+ };
40
+
41
+ export { Toaster };
@@ -0,0 +1,16 @@
1
+ import { CircleNotchIcon } from "@phosphor-icons/react";
2
+
3
+ import { cn } from "./cn";
4
+
5
+ function Spinner({ className, ...props }: React.ComponentProps<"svg">) {
6
+ return (
7
+ <CircleNotchIcon
8
+ role="status"
9
+ aria-label="Loading"
10
+ className={cn("size-4 animate-spin", className)}
11
+ {...props}
12
+ />
13
+ );
14
+ }
15
+
16
+ export { Spinner };
package/src/styles.css ADDED
@@ -0,0 +1,149 @@
1
+ /**
2
+ * The name map every runtime component resolves against, and the base rules they assume.
3
+ *
4
+ * It ships with the components rather than with the SDK because an app imports those components
5
+ * live, from this package, and a utility they write compiles to nothing unless this map reached
6
+ * the app's Tailwind root. The app reaches it by @import from app.css.
7
+ *
8
+ * This file opens no Tailwind root. It is reached FROM a root, never one itself, and a second
9
+ * root is refused. @theme, @custom-variant and @layer base all merge across that hop, which
10
+ * packages/daemon/test/tailwind-facts.test.ts holds against the installed Tailwind.
11
+ *
12
+ * VALUES are not here. A token's value comes from the app's own tokens.css and fonts.css.
13
+ */
14
+
15
+ /* tw-animate-css: the v4 successor to tailwindcss-animate. shadcn v4 components use
16
+ animate-in / fade-in-0 / zoom-in-95 / slide-in-* (Dialog, Popover, Tooltip, Sheet, Dropdown…) and the
17
+ accordion-up/down + collapsible-up/down keyframes (Accordion, Collapsible), all of which live here, not
18
+ in core Tailwind v4. Without this they silently no-op (no error; component shows but never animates open). */
19
+ @import "tw-animate-css";
20
+
21
+ /* `dark:` is a CLASS variant, not a media query, so a register's own `.dark` overrides decide
22
+ what it means rather than the browser deciding for them. The marker is stamped on <html> by
23
+ followColorScheme(), from the OS preference. Selector is shadcn v4's, so a hand-set `.dark`
24
+ works too. */
25
+ @custom-variant dark (&:is(.dark *));
26
+
27
+ @theme inline {
28
+ /* Scale-axis type utilities (register Tier 3): text-title/body/caption/meta/control
29
+ resolve per scope (.scale-content default / .scale-chrome / .scale-touch). Heights
30
+ use the shorthand h-(--control-h) at call sites, no spacing key needed. */
31
+ --text-title: var(--type-title, 16px);
32
+ --text-title--line-height: 1.3;
33
+ --text-title--font-weight: var(--w-title, 600);
34
+ --text-body: var(--type-body, 14px);
35
+ --text-body--line-height: var(--lh-body, 1.5);
36
+ --text-caption: var(--type-caption, 12px);
37
+ --text-caption--line-height: 1.4;
38
+ --text-meta: var(--type-meta, 11px);
39
+ --text-meta--line-height: 1;
40
+ --text-control: var(--type-control, 14px);
41
+ --text-control--line-height: 1;
42
+ --text-stat: var(--type-stat, 24px);
43
+ --text-stat--line-height: 1.15;
44
+ --text-stat--font-weight: 600;
45
+ /* Radius: shadcn v4's four rungs, derived from --radius. Without these mappings the
46
+ rounded-* utilities compile to Tailwind's stock literals and --radius is inert, a
47
+ theme could define it eight times and every card still rendered off-register. */
48
+ --radius-sm: calc(var(--radius) - 4px);
49
+ --radius-md: calc(var(--radius) - 2px);
50
+ --radius-lg: var(--radius);
51
+ --radius-xl: calc(var(--radius) + 4px);
52
+ --color-background: var(--background);
53
+ --color-foreground: var(--foreground);
54
+ --color-card: var(--card);
55
+ --color-card-foreground: var(--card-foreground);
56
+ --color-muted: var(--muted);
57
+ --color-muted-foreground: var(--muted-foreground);
58
+ --color-border: var(--border);
59
+ --color-input: var(--input);
60
+ --color-primary: var(--primary);
61
+ --color-primary-foreground: var(--primary-foreground);
62
+ --color-accent: var(--accent);
63
+ --color-accent-foreground: var(--accent-foreground);
64
+ --color-ring: var(--ring);
65
+ /* shadcn full token set, added so bg-secondary/bg-destructive/bg-popover/bg-chart-* generate as
66
+ utilities. Values come from each theme block below (undefined → inert for themes that omit them). */
67
+ --color-secondary: var(--secondary);
68
+ --color-secondary-foreground: var(--secondary-foreground);
69
+ --color-destructive: var(--destructive);
70
+ --color-destructive-foreground: var(--destructive-foreground);
71
+ --color-popover: var(--popover);
72
+ --color-popover-foreground: var(--popover-foreground);
73
+ --color-chart-1: var(--chart-1);
74
+ --color-chart-2: var(--chart-2);
75
+ --color-chart-3: var(--chart-3);
76
+ --color-chart-4: var(--chart-4);
77
+ --color-chart-5: var(--chart-5);
78
+ --color-chart-6: var(--chart-6);
79
+ /* Sidebar token family. shadcn's Sidebar is the one component with its own colour
80
+ vocabulary, bg-sidebar, text-sidebar-foreground, bg-sidebar-accent, border-sidebar-border,
81
+ ring-sidebar-ring. Without these keys those utilities never generate and the sidebar renders
82
+ transparent on the page ground. Every theme block below defines the values by aliasing its own
83
+ register tokens, so a re-theme carries the navigation surface with it. */
84
+ --color-sidebar: var(--sidebar);
85
+ --color-sidebar-foreground: var(--sidebar-foreground);
86
+ --color-sidebar-primary: var(--sidebar-primary);
87
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
88
+ --color-sidebar-accent: var(--sidebar-accent);
89
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
90
+ --color-sidebar-border: var(--sidebar-border);
91
+ --color-sidebar-ring: var(--sidebar-ring);
92
+
93
+ --font-sans: "Inter Variable", ui-sans-serif, system-ui, sans-serif;
94
+ --font-serif: "Source Serif 4 Variable", Palatino, Georgia, serif;
95
+ --font-mono: "JetBrains Mono Variable", ui-monospace, SFMono-Regular, Menlo, monospace;
96
+
97
+ --radius: 0.625rem;
98
+ }
99
+
100
+ /* Display face for headings: a register sets --font-display to override, otherwise headings fall
101
+ back to the sans body face. In @layer base, not unlayered: unlayered CSS beats every layer at
102
+ any specificity, so an unlayered rule here silently outranked the register's own reading
103
+ surface, whose heading face is set in @layer components. Utilities still win over both. */
104
+ @layer base {
105
+ h1,
106
+ h2,
107
+ h3,
108
+ h4 {
109
+ font-family: var(--font-display, var(--font-sans));
110
+ }
111
+ }
112
+
113
+ /* shadcn base, default border color. Tailwind v4 changed the bare `border` utility's default
114
+ color to currentColor (it was gray-200 in v3), so an uncolored `border`/`border-b`/`border-t`
115
+ falls through to the ink text color. shadcn's globals.css fixes this with exactly this base rule
116
+ so every uncolored border/divider resolves to the active theme's hairline (--border). Without it,
117
+ Table dividers, Card edges, Separator, etc. render as ink. Components that name their own border
118
+ token (border-input, border-primary…) are unaffected, those win on specificity/cascade. */
119
+ @layer base {
120
+ *,
121
+ ::after,
122
+ ::before {
123
+ border-color: var(--border);
124
+ }
125
+ }
126
+
127
+ /* The app-root contract. Every app root fills the viewport, so an app's h-full, min-h-full and
128
+ height:100% resolve against it. In @layer base, so a utility at a call site still wins.
129
+
130
+ The themed ground extends onto html and #root, not body alone: the overscroll area paints from
131
+ html, so an app on a dark ground would otherwise flash the white default there. */
132
+ @layer base {
133
+ html,
134
+ body,
135
+ #root {
136
+ height: 100%;
137
+ overscroll-behavior: none;
138
+ }
139
+ html,
140
+ #root {
141
+ background-color: var(--background);
142
+ }
143
+ body {
144
+ background-color: var(--background);
145
+ color: var(--foreground);
146
+ font-family: var(--font-sans);
147
+ -webkit-font-smoothing: antialiased;
148
+ }
149
+ }
package/src/switch.tsx ADDED
@@ -0,0 +1,33 @@
1
+ import * as React from "react";
2
+ import { Switch as SwitchPrimitive } from "radix-ui";
3
+
4
+ import { cn } from "./cn";
5
+
6
+ function Switch({
7
+ className,
8
+ size = "default",
9
+ ...props
10
+ }: React.ComponentProps<typeof SwitchPrimitive.Root> & {
11
+ size?: "sm" | "default";
12
+ }) {
13
+ return (
14
+ <SwitchPrimitive.Root
15
+ data-slot="switch"
16
+ data-size={size}
17
+ className={cn(
18
+ "peer group/switch inline-flex shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-[1.15rem] data-[size=default]:w-8 data-[size=sm]:h-3.5 data-[size=sm]:w-6 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input dark:data-[state=unchecked]:bg-input/80",
19
+ className,
20
+ )}
21
+ {...props}
22
+ >
23
+ <SwitchPrimitive.Thumb
24
+ data-slot="switch-thumb"
25
+ className={cn(
26
+ "pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0 dark:data-[state=checked]:bg-primary-foreground dark:data-[state=unchecked]:bg-foreground",
27
+ )}
28
+ />
29
+ </SwitchPrimitive.Root>
30
+ );
31
+ }
32
+
33
+ export { Switch };
package/src/table.tsx ADDED
@@ -0,0 +1,90 @@
1
+ import * as React from "react";
2
+
3
+ import { cn } from "./cn";
4
+
5
+ function Table({ className, ...props }: React.ComponentProps<"table">) {
6
+ return (
7
+ <div data-slot="table-container" className="relative w-full overflow-x-auto">
8
+ <table
9
+ data-slot="table"
10
+ className={cn("w-full caption-bottom text-sm", className)}
11
+ {...props}
12
+ />
13
+ </div>
14
+ );
15
+ }
16
+
17
+ function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
18
+ return <thead data-slot="table-header" className={cn("[&_tr]:border-b", className)} {...props} />;
19
+ }
20
+
21
+ function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
22
+ return (
23
+ <tbody
24
+ data-slot="table-body"
25
+ className={cn("[&_tr:last-child]:border-0", className)}
26
+ {...props}
27
+ />
28
+ );
29
+ }
30
+
31
+ function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
32
+ return (
33
+ <tfoot
34
+ data-slot="table-footer"
35
+ className={cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", className)}
36
+ {...props}
37
+ />
38
+ );
39
+ }
40
+
41
+ function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
42
+ return (
43
+ <tr
44
+ data-slot="table-row"
45
+ className={cn(
46
+ "border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted",
47
+ className,
48
+ )}
49
+ {...props}
50
+ />
51
+ );
52
+ }
53
+
54
+ function TableHead({ className, ...props }: React.ComponentProps<"th">) {
55
+ return (
56
+ <th
57
+ data-slot="table-head"
58
+ className={cn(
59
+ "h-10 px-2 text-left align-middle font-medium whitespace-nowrap text-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
60
+ className,
61
+ )}
62
+ {...props}
63
+ />
64
+ );
65
+ }
66
+
67
+ function TableCell({ className, ...props }: React.ComponentProps<"td">) {
68
+ return (
69
+ <td
70
+ data-slot="table-cell"
71
+ className={cn(
72
+ "p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
73
+ className,
74
+ )}
75
+ {...props}
76
+ />
77
+ );
78
+ }
79
+
80
+ function TableCaption({ className, ...props }: React.ComponentProps<"caption">) {
81
+ return (
82
+ <caption
83
+ data-slot="table-caption"
84
+ className={cn("mt-4 text-sm text-muted-foreground", className)}
85
+ {...props}
86
+ />
87
+ );
88
+ }
89
+
90
+ export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption };
package/src/tabs.tsx ADDED
@@ -0,0 +1,79 @@
1
+ import * as React from "react";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import { Tabs as TabsPrimitive } from "radix-ui";
4
+
5
+ import { cn } from "./cn";
6
+
7
+ function Tabs({
8
+ className,
9
+ orientation = "horizontal",
10
+ ...props
11
+ }: React.ComponentProps<typeof TabsPrimitive.Root>) {
12
+ return (
13
+ <TabsPrimitive.Root
14
+ data-slot="tabs"
15
+ data-orientation={orientation}
16
+ orientation={orientation}
17
+ className={cn("group/tabs flex gap-2 data-[orientation=horizontal]:flex-col", className)}
18
+ {...props}
19
+ />
20
+ );
21
+ }
22
+
23
+ const tabsListVariants = cva(
24
+ "group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-[orientation=horizontal]/tabs:h-9 group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col data-[variant=line]:rounded-none",
25
+ {
26
+ variants: {
27
+ variant: {
28
+ default: "bg-muted",
29
+ line: "gap-1 bg-transparent",
30
+ },
31
+ },
32
+ defaultVariants: {
33
+ variant: "default",
34
+ },
35
+ },
36
+ );
37
+
38
+ function TabsList({
39
+ className,
40
+ variant = "default",
41
+ ...props
42
+ }: React.ComponentProps<typeof TabsPrimitive.List> & VariantProps<typeof tabsListVariants>) {
43
+ return (
44
+ <TabsPrimitive.List
45
+ data-slot="tabs-list"
46
+ data-variant={variant}
47
+ className={cn(tabsListVariants({ variant }), className)}
48
+ {...props}
49
+ />
50
+ );
51
+ }
52
+
53
+ function TabsTrigger({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
54
+ return (
55
+ <TabsPrimitive.Trigger
56
+ data-slot="tabs-trigger"
57
+ className={cn(
58
+ "relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 group-data-[variant=default]/tabs-list:data-[state=active]:shadow-sm group-data-[variant=line]/tabs-list:data-[state=active]:shadow-none dark:text-muted-foreground dark:hover:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
59
+ "group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:border-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent",
60
+ "data-[state=active]:bg-background data-[state=active]:text-foreground dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 dark:data-[state=active]:text-foreground",
61
+ "after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100",
62
+ className,
63
+ )}
64
+ {...props}
65
+ />
66
+ );
67
+ }
68
+
69
+ function TabsContent({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Content>) {
70
+ return (
71
+ <TabsPrimitive.Content
72
+ data-slot="tabs-content"
73
+ className={cn("flex-1 outline-none", className)}
74
+ {...props}
75
+ />
76
+ );
77
+ }
78
+
79
+ export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants };
@@ -0,0 +1,18 @@
1
+ import * as React from "react";
2
+
3
+ import { cn } from "./cn";
4
+
5
+ function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
6
+ return (
7
+ <textarea
8
+ data-slot="textarea"
9
+ className={cn(
10
+ "flex field-sizing-content min-h-16 w-full rounded-md border border-input bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:ring-destructive/40",
11
+ className,
12
+ )}
13
+ {...props}
14
+ />
15
+ );
16
+ }
17
+
18
+ export { Textarea };