open-ds 0.0.0-stage → 0.1.0

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 (201) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +66 -2
  3. package/assets/schemes/accessible-first.json +3093 -0
  4. package/assets/schemes/brand-expressive.json +3124 -0
  5. package/assets/schemes/content-first.json +3110 -0
  6. package/assets/schemes/dense-data.json +3489 -0
  7. package/assets/schemes/grammar.json +3903 -0
  8. package/assets/schemes/multi-brand.json +4359 -0
  9. package/assets/skills/open-ds/SKILL.md +50 -0
  10. package/assets/skills/open-ds-build/SKILL.md +19 -0
  11. package/assets/skills/open-ds-change/SKILL.md +19 -0
  12. package/assets/skills/open-ds-choose/SKILL.md +18 -0
  13. package/assets/skills/open-ds-review/SKILL.md +22 -0
  14. package/assets/skills/open-ds-write/SKILL.md +13 -0
  15. package/assets/stories/accordion.stories.tsx +46 -0
  16. package/assets/stories/alert-dialog.stories.tsx +65 -0
  17. package/assets/stories/alert.stories.tsx +44 -0
  18. package/assets/stories/aspect-ratio.stories.tsx +37 -0
  19. package/assets/stories/avatar.stories.tsx +42 -0
  20. package/assets/stories/badge.stories.tsx +39 -0
  21. package/assets/stories/breadcrumb.stories.tsx +54 -0
  22. package/assets/stories/button.stories.tsx +46 -0
  23. package/assets/stories/calendar.stories.tsx +30 -0
  24. package/assets/stories/card.stories.tsx +50 -0
  25. package/assets/stories/carousel.stories.tsx +64 -0
  26. package/assets/stories/chart.stories.tsx +61 -0
  27. package/assets/stories/checkbox.stories.tsx +29 -0
  28. package/assets/stories/collapsible.stories.tsx +39 -0
  29. package/assets/stories/combobox.stories.tsx +57 -0
  30. package/assets/stories/command.stories.tsx +60 -0
  31. package/assets/stories/context-menu.stories.tsx +55 -0
  32. package/assets/stories/data-table.stories.tsx +93 -0
  33. package/assets/stories/date-picker.stories.tsx +27 -0
  34. package/assets/stories/dialog.stories.tsx +70 -0
  35. package/assets/stories/drawer.stories.tsx +62 -0
  36. package/assets/stories/dropdown-menu.stories.tsx +65 -0
  37. package/assets/stories/empty.stories.tsx +39 -0
  38. package/assets/stories/field.stories.tsx +42 -0
  39. package/assets/stories/form.stories.tsx +56 -0
  40. package/assets/stories/input-group.stories.tsx +41 -0
  41. package/assets/stories/input-otp.stories.tsx +48 -0
  42. package/assets/stories/input.stories.tsx +32 -0
  43. package/assets/stories/kbd.stories.tsx +30 -0
  44. package/assets/stories/label.stories.tsx +26 -0
  45. package/assets/stories/native-select.stories.tsx +39 -0
  46. package/assets/stories/navigation-menu.stories.tsx +41 -0
  47. package/assets/stories/pagination.stories.tsx +61 -0
  48. package/assets/stories/popover.stories.tsx +37 -0
  49. package/assets/stories/progress.stories.tsx +30 -0
  50. package/assets/stories/radio-group.stories.tsx +45 -0
  51. package/assets/stories/resizable.stories.tsx +54 -0
  52. package/assets/stories/scroll-area.stories.tsx +47 -0
  53. package/assets/stories/select.stories.tsx +57 -0
  54. package/assets/stories/separator.stories.tsx +28 -0
  55. package/assets/stories/sheet.stories.tsx +46 -0
  56. package/assets/stories/sidebar.stories.tsx +62 -0
  57. package/assets/stories/skeleton.stories.tsx +27 -0
  58. package/assets/stories/slider.stories.tsx +44 -0
  59. package/assets/stories/spinner.stories.tsx +25 -0
  60. package/assets/stories/switch.stories.tsx +29 -0
  61. package/assets/stories/table.stories.tsx +97 -0
  62. package/assets/stories/tabs.stories.tsx +55 -0
  63. package/assets/stories/textarea.stories.tsx +28 -0
  64. package/assets/stories/toast.stories.tsx +30 -0
  65. package/assets/stories/toggle-group.stories.tsx +41 -0
  66. package/assets/stories/toggle.stories.tsx +20 -0
  67. package/assets/stories/tooltip.stories.tsx +49 -0
  68. package/assets/tokens/accessible-first/content.json +32 -0
  69. package/assets/tokens/accessible-first/dark.tokens.json +581 -0
  70. package/assets/tokens/accessible-first/light.tokens.json +581 -0
  71. package/assets/tokens/accessible-first/shadcn.css +21 -0
  72. package/assets/tokens/accessible-first/theme.css +101 -0
  73. package/assets/tokens/accessible-first/tokens.css +331 -0
  74. package/assets/tokens/accessible-first/tokens.json +1616 -0
  75. package/assets/tokens/accessible-first/tokens.scoped.css +333 -0
  76. package/assets/tokens/brand-expressive/content.json +32 -0
  77. package/assets/tokens/brand-expressive/dark.tokens.json +585 -0
  78. package/assets/tokens/brand-expressive/light.tokens.json +585 -0
  79. package/assets/tokens/brand-expressive/shadcn.css +21 -0
  80. package/assets/tokens/brand-expressive/theme.css +101 -0
  81. package/assets/tokens/brand-expressive/tokens.css +331 -0
  82. package/assets/tokens/brand-expressive/tokens.json +1651 -0
  83. package/assets/tokens/brand-expressive/tokens.scoped.css +333 -0
  84. package/assets/tokens/content-first/content.json +32 -0
  85. package/assets/tokens/content-first/dark.tokens.json +582 -0
  86. package/assets/tokens/content-first/light.tokens.json +582 -0
  87. package/assets/tokens/content-first/shadcn.css +21 -0
  88. package/assets/tokens/content-first/theme.css +101 -0
  89. package/assets/tokens/content-first/tokens.css +331 -0
  90. package/assets/tokens/content-first/tokens.json +1631 -0
  91. package/assets/tokens/content-first/tokens.scoped.css +333 -0
  92. package/assets/tokens/dense-data/content.json +32 -0
  93. package/assets/tokens/dense-data/dark.tokens.json +580 -0
  94. package/assets/tokens/dense-data/light.tokens.json +580 -0
  95. package/assets/tokens/dense-data/shadcn.css +21 -0
  96. package/assets/tokens/dense-data/theme.css +101 -0
  97. package/assets/tokens/dense-data/tokens.css +331 -0
  98. package/assets/tokens/dense-data/tokens.json +1623 -0
  99. package/assets/tokens/dense-data/tokens.scoped.css +333 -0
  100. package/assets/tokens/index.json +143 -0
  101. package/assets/tokens/multi-brand/aster-dark.tokens.json +625 -0
  102. package/assets/tokens/multi-brand/aster-light.tokens.json +625 -0
  103. package/assets/tokens/multi-brand/birch-dark.tokens.json +625 -0
  104. package/assets/tokens/multi-brand/birch-light.tokens.json +625 -0
  105. package/assets/tokens/multi-brand/content.json +32 -0
  106. package/assets/tokens/multi-brand/coral-dark.tokens.json +625 -0
  107. package/assets/tokens/multi-brand/coral-light.tokens.json +625 -0
  108. package/assets/tokens/multi-brand/shadcn.css +21 -0
  109. package/assets/tokens/multi-brand/theme.css +101 -0
  110. package/assets/tokens/multi-brand/tokens.css +1002 -0
  111. package/assets/tokens/multi-brand/tokens.json +1754 -0
  112. package/assets/tokens/multi-brand/tokens.scoped.css +1022 -0
  113. package/assets/tokens/shadcn-map.json +34 -0
  114. package/assets/ui/components/accordion.tsx +60 -0
  115. package/assets/ui/components/alert-dialog.tsx +171 -0
  116. package/assets/ui/components/alert.tsx +56 -0
  117. package/assets/ui/components/aspect-ratio.tsx +17 -0
  118. package/assets/ui/components/avatar.tsx +96 -0
  119. package/assets/ui/components/badge.tsx +36 -0
  120. package/assets/ui/components/breadcrumb.tsx +99 -0
  121. package/assets/ui/components/button.tsx +53 -0
  122. package/assets/ui/components/calendar.tsx +169 -0
  123. package/assets/ui/components/card.tsx +72 -0
  124. package/assets/ui/components/carousel.tsx +224 -0
  125. package/assets/ui/components/chart.tsx +315 -0
  126. package/assets/ui/components/checkbox.tsx +29 -0
  127. package/assets/ui/components/collapsible.tsx +25 -0
  128. package/assets/ui/components/combobox.tsx +275 -0
  129. package/assets/ui/components/command.tsx +145 -0
  130. package/assets/ui/components/context-menu.tsx +218 -0
  131. package/assets/ui/components/data-table.tsx +307 -0
  132. package/assets/ui/components/date-picker.tsx +79 -0
  133. package/assets/ui/components/dialog.tsx +68 -0
  134. package/assets/ui/components/drawer.tsx +115 -0
  135. package/assets/ui/components/dropdown-menu.tsx +58 -0
  136. package/assets/ui/components/empty.tsx +92 -0
  137. package/assets/ui/components/field.tsx +234 -0
  138. package/assets/ui/components/form.tsx +146 -0
  139. package/assets/ui/components/input-group.tsx +154 -0
  140. package/assets/ui/components/input-otp.tsx +75 -0
  141. package/assets/ui/components/input.tsx +25 -0
  142. package/assets/ui/components/kbd.tsx +30 -0
  143. package/assets/ui/components/label.tsx +22 -0
  144. package/assets/ui/components/native-select.tsx +59 -0
  145. package/assets/ui/components/navigation-menu.tsx +149 -0
  146. package/assets/ui/components/pagination.tsx +108 -0
  147. package/assets/ui/components/popover.tsx +61 -0
  148. package/assets/ui/components/progress.tsx +31 -0
  149. package/assets/ui/components/radio-group.tsx +40 -0
  150. package/assets/ui/components/resizable.tsx +54 -0
  151. package/assets/ui/components/scroll-area.tsx +55 -0
  152. package/assets/ui/components/select.tsx +81 -0
  153. package/assets/ui/components/separator.tsx +35 -0
  154. package/assets/ui/components/sheet.tsx +114 -0
  155. package/assets/ui/components/sidebar.tsx +685 -0
  156. package/assets/ui/components/skeleton.tsx +15 -0
  157. package/assets/ui/components/slider.tsx +70 -0
  158. package/assets/ui/components/spinner.tsx +16 -0
  159. package/assets/ui/components/switch.tsx +32 -0
  160. package/assets/ui/components/table.tsx +78 -0
  161. package/assets/ui/components/tabs.tsx +49 -0
  162. package/assets/ui/components/textarea.tsx +25 -0
  163. package/assets/ui/components/toast.tsx +54 -0
  164. package/assets/ui/components/toggle-group.tsx +88 -0
  165. package/assets/ui/components/toggle.tsx +48 -0
  166. package/assets/ui/components/tooltip.tsx +34 -0
  167. package/assets/ui/lib/use-mobile.ts +20 -0
  168. package/assets/ui/lib/utils.ts +7 -0
  169. package/assets/ui/styles.css +27 -0
  170. package/bin/open-ds.js +6 -0
  171. package/package.json +56 -4
  172. package/src/agents.mjs +150 -0
  173. package/src/cli.mjs +445 -0
  174. package/src/components.mjs +69 -0
  175. package/src/context.mjs +143 -0
  176. package/src/create.mjs +377 -0
  177. package/src/docs.mjs +570 -0
  178. package/src/guidance.mjs +63 -0
  179. package/src/index.mjs +20 -0
  180. package/src/mcp.mjs +660 -0
  181. package/src/readmes.mjs +106 -0
  182. package/src/schemes.mjs +188 -0
  183. package/src/tokens.mjs +127 -0
  184. package/templates/project/.github/workflows/ci.yml +15 -0
  185. package/templates/project/.storybook/component-docs.mdx +26 -0
  186. package/templates/project/.storybook/main.ts +17 -0
  187. package/templates/project/.storybook/manager-head.html +3 -0
  188. package/templates/project/.storybook/manager.ts +37 -0
  189. package/templates/project/.storybook/preview-head.html +3 -0
  190. package/templates/project/.storybook/preview.tsx +82 -0
  191. package/templates/project/docs/docusaurus.config.ts +64 -0
  192. package/templates/project/docs/sidebars.ts +8 -0
  193. package/templates/project/docs/src/css/custom.css +275 -0
  194. package/templates/project/gitignore +6 -0
  195. package/templates/project/src/styles/index.css +16 -0
  196. package/templates/project/src/vite-env.d.ts +7 -0
  197. package/templates/project/stories/docs-blocks.tsx +190 -0
  198. package/templates/project/stories/samples.tsx +102 -0
  199. package/templates/project/stories/usage.tsx +31 -0
  200. package/templates/project/tokens/build.mjs +468 -0
  201. package/templates/project/tsconfig.json +15 -0
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Form. Form state and validation wired to fields.
3
+ *
4
+ * From shadcn/ui (new-york-v4, MIT), carried by the schemes whose inventory names it.
5
+ * Reads the shadcn variables, which theme.css maps onto the scheme's semantic tokens, and the
6
+ * radius, shadow and text scale that shadcn.css maps onto the scheme's tokens. Dark mode comes
7
+ * from the tokens, so no dark: classes.
8
+ */
9
+ "use client";
10
+
11
+ import * as React from "react";
12
+ import { cn } from "../lib/utils";
13
+ import type { Label as LabelPrimitive } from "radix-ui";
14
+ import { Slot } from "radix-ui";
15
+ import {
16
+ Controller,
17
+ FormProvider,
18
+ useFormContext,
19
+ useFormState,
20
+ type ControllerProps,
21
+ type FieldPath,
22
+ type FieldValues,
23
+ } from "react-hook-form";
24
+
25
+ import { Label } from "./label";
26
+
27
+ const Form = FormProvider;
28
+
29
+ type FormFieldContextValue<
30
+ TFieldValues extends FieldValues = FieldValues,
31
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
32
+ > = {
33
+ name: TName;
34
+ };
35
+
36
+ const FormFieldContext = React.createContext<FormFieldContextValue>({} as FormFieldContextValue);
37
+
38
+ const FormField = <
39
+ TFieldValues extends FieldValues = FieldValues,
40
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
41
+ >({
42
+ ...props
43
+ }: ControllerProps<TFieldValues, TName>) => {
44
+ return (
45
+ <FormFieldContext.Provider value={{ name: props.name }}>
46
+ <Controller {...props} />
47
+ </FormFieldContext.Provider>
48
+ );
49
+ };
50
+
51
+ const useFormField = () => {
52
+ const fieldContext = React.useContext(FormFieldContext);
53
+ const itemContext = React.useContext(FormItemContext);
54
+ const { getFieldState } = useFormContext();
55
+ const formState = useFormState({ name: fieldContext.name });
56
+ const fieldState = getFieldState(fieldContext.name, formState);
57
+
58
+ if (!fieldContext) {
59
+ throw new Error("useFormField should be used within <FormField>");
60
+ }
61
+
62
+ const { id } = itemContext;
63
+
64
+ return {
65
+ id,
66
+ name: fieldContext.name,
67
+ formItemId: `${id}-form-item`,
68
+ formDescriptionId: `${id}-form-item-description`,
69
+ formMessageId: `${id}-form-item-message`,
70
+ ...fieldState,
71
+ };
72
+ };
73
+
74
+ type FormItemContextValue = {
75
+ id: string;
76
+ };
77
+
78
+ const FormItemContext = React.createContext<FormItemContextValue>({} as FormItemContextValue);
79
+
80
+ function FormItem({ className, ...props }: React.ComponentProps<"div">) {
81
+ const id = React.useId();
82
+
83
+ return (
84
+ <FormItemContext.Provider value={{ id }}>
85
+ <div data-slot="form-item" className={cn("grid gap-2", className)} {...props} />
86
+ </FormItemContext.Provider>
87
+ );
88
+ }
89
+
90
+ function FormLabel({ className, ...props }: React.ComponentProps<typeof LabelPrimitive.Root>) {
91
+ const { error, formItemId } = useFormField();
92
+
93
+ return (
94
+ <Label
95
+ data-slot="form-label"
96
+ data-error={!!error}
97
+ className={cn("data-[error=true]:text-destructive", className)}
98
+ htmlFor={formItemId}
99
+ {...props}
100
+ />
101
+ );
102
+ }
103
+
104
+ function FormControl({ ...props }: React.ComponentProps<typeof Slot.Root>) {
105
+ const { error, formItemId, formDescriptionId, formMessageId } = useFormField();
106
+
107
+ return (
108
+ <Slot.Root
109
+ data-slot="form-control"
110
+ id={formItemId}
111
+ aria-describedby={!error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`}
112
+ aria-invalid={!!error}
113
+ {...props}
114
+ />
115
+ );
116
+ }
117
+
118
+ function FormDescription({ className, ...props }: React.ComponentProps<"p">) {
119
+ const { formDescriptionId } = useFormField();
120
+
121
+ return (
122
+ <p
123
+ data-slot="form-description"
124
+ id={formDescriptionId}
125
+ className={cn("text-sm text-muted-foreground", className)}
126
+ {...props}
127
+ />
128
+ );
129
+ }
130
+
131
+ function FormMessage({ className, ...props }: React.ComponentProps<"p">) {
132
+ const { error, formMessageId } = useFormField();
133
+ const body = error ? String(error?.message ?? "") : props.children;
134
+
135
+ if (!body) {
136
+ return null;
137
+ }
138
+
139
+ return (
140
+ <p data-slot="form-message" id={formMessageId} className={cn("text-sm text-destructive", className)} {...props}>
141
+ {body}
142
+ </p>
143
+ );
144
+ }
145
+
146
+ export { useFormField, Form, FormItem, FormLabel, FormControl, FormDescription, FormMessage, FormField };
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Input group. An input with addons such as icons or buttons.
3
+ *
4
+ * From shadcn/ui (new-york-v4, MIT), carried by the schemes whose inventory names it.
5
+ * Reads the shadcn variables, which theme.css maps onto the scheme's semantic tokens, and the
6
+ * radius, shadow and text scale that shadcn.css maps onto the scheme's tokens. Dark mode comes
7
+ * from the tokens, so no dark: classes.
8
+ */
9
+ "use client";
10
+
11
+ import * as React from "react";
12
+ import { cva, type VariantProps } from "class-variance-authority";
13
+ import { cn } from "../lib/utils";
14
+
15
+ import { Button } from "./button";
16
+ import { Input } from "./input";
17
+ import { Textarea } from "./textarea";
18
+
19
+ function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
20
+ return (
21
+ <div
22
+ data-slot="input-group"
23
+ role="group"
24
+ className={cn(
25
+ "group/input-group relative flex w-full items-center rounded-md border border-input shadow-xs transition-[color,box-shadow] outline-none",
26
+ "h-9 min-w-0 has-[>textarea]:h-auto",
27
+
28
+ // Variants based on alignment.
29
+ "has-[>[data-align=inline-start]]:[&>input]:pl-2",
30
+ "has-[>[data-align=inline-end]]:[&>input]:pr-2",
31
+ "has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3",
32
+ "has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3",
33
+
34
+ // Focus state.
35
+ "has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-[3px] has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50",
36
+
37
+ // Error state.
38
+ "has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-destructive/20",
39
+
40
+ className,
41
+ )}
42
+ {...props}
43
+ />
44
+ );
45
+ }
46
+
47
+ const inputGroupAddonVariants = cva(
48
+ "flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium text-muted-foreground select-none group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4",
49
+ {
50
+ variants: {
51
+ align: {
52
+ "inline-start": "order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]",
53
+ "inline-end": "order-last pr-3 has-[>button]:mr-[-0.45rem] has-[>kbd]:mr-[-0.35rem]",
54
+ "block-start":
55
+ "order-first w-full justify-start px-3 pt-3 group-has-[>input]/input-group:pt-2.5 [.border-b]:pb-3",
56
+ "block-end": "order-last w-full justify-start px-3 pb-3 group-has-[>input]/input-group:pb-2.5 [.border-t]:pt-3",
57
+ },
58
+ },
59
+ defaultVariants: {
60
+ align: "inline-start",
61
+ },
62
+ },
63
+ );
64
+
65
+ function InputGroupAddon({
66
+ className,
67
+ align = "inline-start",
68
+ ...props
69
+ }: React.ComponentProps<"div"> & VariantProps<typeof inputGroupAddonVariants>) {
70
+ return (
71
+ <div
72
+ role="group"
73
+ data-slot="input-group-addon"
74
+ data-align={align}
75
+ className={cn(inputGroupAddonVariants({ align }), className)}
76
+ onClick={(e) => {
77
+ if ((e.target as HTMLElement).closest("button")) {
78
+ return;
79
+ }
80
+ e.currentTarget.parentElement?.querySelector("input")?.focus();
81
+ }}
82
+ {...props}
83
+ />
84
+ );
85
+ }
86
+
87
+ const inputGroupButtonVariants = cva("flex items-center gap-2 text-sm shadow-none", {
88
+ variants: {
89
+ size: {
90
+ xs: "h-6 gap-1 rounded-[calc(var(--radius)-5px)] px-2 has-[>svg]:px-2 [&>svg:not([class*='size-'])]:size-3.5",
91
+ sm: "h-8 gap-1.5 rounded-md px-2.5 has-[>svg]:px-2.5",
92
+ "icon-xs": "size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0",
93
+ "icon-sm": "size-8 p-0 has-[>svg]:p-0",
94
+ },
95
+ },
96
+ defaultVariants: {
97
+ size: "xs",
98
+ },
99
+ });
100
+
101
+ function InputGroupButton({
102
+ className,
103
+ type = "button",
104
+ variant = "ghost",
105
+ size = "xs",
106
+ ...props
107
+ }: Omit<React.ComponentProps<typeof Button>, "size"> & VariantProps<typeof inputGroupButtonVariants>) {
108
+ return (
109
+ <Button
110
+ type={type}
111
+ data-size={size}
112
+ variant={variant}
113
+ className={cn(inputGroupButtonVariants({ size }), className)}
114
+ {...props}
115
+ />
116
+ );
117
+ }
118
+
119
+ function InputGroupText({ className, ...props }: React.ComponentProps<"span">) {
120
+ return (
121
+ <span
122
+ className={cn(
123
+ "flex items-center gap-2 text-sm text-muted-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
124
+ className,
125
+ )}
126
+ {...props}
127
+ />
128
+ );
129
+ }
130
+
131
+ function InputGroupInput({ className, ...props }: React.ComponentProps<"input">) {
132
+ return (
133
+ <Input
134
+ data-slot="input-group-control"
135
+ className={cn("flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0", className)}
136
+ {...props}
137
+ />
138
+ );
139
+ }
140
+
141
+ function InputGroupTextarea({ className, ...props }: React.ComponentProps<"textarea">) {
142
+ return (
143
+ <Textarea
144
+ data-slot="input-group-control"
145
+ className={cn(
146
+ "flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus-visible:ring-0",
147
+ className,
148
+ )}
149
+ {...props}
150
+ />
151
+ );
152
+ }
153
+
154
+ export { InputGroup, InputGroupAddon, InputGroupButton, InputGroupText, InputGroupInput, InputGroupTextarea };
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Input otp. Separate boxes for a one-time code.
3
+ *
4
+ * From shadcn/ui (new-york-v4, MIT), carried by the schemes whose inventory names it.
5
+ * Reads the shadcn variables, which theme.css maps onto the scheme's semantic tokens, and the
6
+ * radius, shadow and text scale that shadcn.css maps onto the scheme's tokens. Dark mode comes
7
+ * from the tokens, so no dark: classes.
8
+ */
9
+ "use client";
10
+
11
+ import * as React from "react";
12
+ import { cn } from "../lib/utils";
13
+ import { OTPInput, OTPInputContext } from "input-otp";
14
+ import { MinusIcon } from "lucide-react";
15
+
16
+ function InputOTP({
17
+ className,
18
+ containerClassName,
19
+ ...props
20
+ }: React.ComponentProps<typeof OTPInput> & {
21
+ containerClassName?: string;
22
+ }) {
23
+ return (
24
+ <OTPInput
25
+ data-slot="input-otp"
26
+ containerClassName={cn("flex items-center gap-2 has-disabled:opacity-50", containerClassName)}
27
+ className={cn("disabled:cursor-not-allowed", className)}
28
+ {...props}
29
+ />
30
+ );
31
+ }
32
+
33
+ function InputOTPGroup({ className, ...props }: React.ComponentProps<"div">) {
34
+ return <div data-slot="input-otp-group" className={cn("flex items-center", className)} {...props} />;
35
+ }
36
+
37
+ function InputOTPSlot({
38
+ index,
39
+ className,
40
+ ...props
41
+ }: React.ComponentProps<"div"> & {
42
+ index: number;
43
+ }) {
44
+ const inputOTPContext = React.useContext(OTPInputContext);
45
+ const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {};
46
+
47
+ return (
48
+ <div
49
+ data-slot="input-otp-slot"
50
+ data-active={isActive}
51
+ className={cn(
52
+ "relative flex h-9 w-9 items-center justify-center border-y border-r border-input text-sm shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md aria-invalid:border-destructive data-[active=true]:z-10 data-[active=true]:border-ring data-[active=true]:ring-[3px] data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:border-destructive data-[active=true]:aria-invalid:ring-destructive/20",
53
+ className,
54
+ )}
55
+ {...props}
56
+ >
57
+ {char}
58
+ {hasFakeCaret && (
59
+ <div className="pointer-events-none absolute inset-0 flex items-center justify-center">
60
+ <div className="h-4 w-px animate-caret-blink bg-foreground duration-1000" />
61
+ </div>
62
+ )}
63
+ </div>
64
+ );
65
+ }
66
+
67
+ function InputOTPSeparator({ ...props }: React.ComponentProps<"div">) {
68
+ return (
69
+ <div data-slot="input-otp-separator" role="separator" {...props}>
70
+ <MinusIcon />
71
+ </div>
72
+ );
73
+ }
74
+
75
+ export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Input. Single-line text entry. Pair it with a Label that stays visible.
3
+ *
4
+ * Reads only component keys: --input-*.
5
+ */
6
+ import * as React from "react";
7
+ import { cn } from "../lib/utils";
8
+
9
+ export function Input({ className, type = "text", ...props }: React.InputHTMLAttributes<HTMLInputElement>) {
10
+ return (
11
+ <input
12
+ type={type}
13
+ data-slot="input"
14
+ className={cn(
15
+ "w-full min-w-0 h-[var(--input-height)] px-[var(--input-padding-x)] rounded-[var(--input-radius)]",
16
+ "bg-[var(--input-background)] text-[var(--input-text)] text-[length:var(--input-font-size)] [font-family:var(--type-body-font-family)]",
17
+ "border-[length:var(--border-width-default)] border-[var(--input-border)] placeholder:text-[var(--input-placeholder)]",
18
+ "outline-none focus-visible:border-[var(--input-border-focus)] focus-visible:ring-[length:var(--border-width-focus)] focus-visible:ring-[color:var(--input-border-focus)]",
19
+ "aria-invalid:border-[var(--color-status-danger)] disabled:bg-[var(--input-disabled-background)] disabled:border-[var(--input-disabled-border)] disabled:text-[var(--input-disabled-text)]",
20
+ className,
21
+ )}
22
+ {...props}
23
+ />
24
+ );
25
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Kbd. A keyboard key or shortcut.
3
+ *
4
+ * From shadcn/ui (new-york-v4, MIT), carried by the schemes whose inventory names it.
5
+ * Reads the shadcn variables, which theme.css maps onto the scheme's semantic tokens, and the
6
+ * radius, shadow and text scale that shadcn.css maps onto the scheme's tokens. Dark mode comes
7
+ * from the tokens, so no dark: classes.
8
+ */
9
+ import { cn } from "../lib/utils";
10
+
11
+ function Kbd({ className, ...props }: React.ComponentProps<"kbd">) {
12
+ return (
13
+ <kbd
14
+ data-slot="kbd"
15
+ className={cn(
16
+ "pointer-events-none inline-flex h-5 w-fit min-w-5 items-center justify-center gap-1 rounded-sm bg-muted px-1 font-sans text-xs font-medium text-muted-foreground select-none",
17
+ "[&_svg:not([class*='size-'])]:size-3",
18
+ "[[data-slot=tooltip-content]_&]:bg-background/20 [[data-slot=tooltip-content]_&]:text-background",
19
+ className,
20
+ )}
21
+ {...props}
22
+ />
23
+ );
24
+ }
25
+
26
+ function KbdGroup({ className, ...props }: React.ComponentProps<"div">) {
27
+ return <kbd data-slot="kbd-group" className={cn("inline-flex items-center gap-1", className)} {...props} />;
28
+ }
29
+
30
+ export { Kbd, KbdGroup };
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Label. Names a field. Keep it visible above the field; never use a placeholder instead.
3
+ *
4
+ * Reads the label text role: --type-label-*.
5
+ */
6
+ import { Label as LabelPrimitive } from "radix-ui";
7
+ import * as React from "react";
8
+ import { cn } from "../lib/utils";
9
+
10
+ export function Label({ className, ...props }: React.ComponentProps<typeof LabelPrimitive.Root>) {
11
+ return (
12
+ <LabelPrimitive.Root
13
+ data-slot="label"
14
+ className={cn(
15
+ "inline-flex items-center gap-2 text-[var(--color-text-primary)] [font-family:var(--type-label-font-family)]",
16
+ "text-[length:var(--type-label-font-size)] [font-weight:var(--type-label-font-weight)] leading-[var(--type-label-line-height)]",
17
+ className,
18
+ )}
19
+ {...props}
20
+ />
21
+ );
22
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Native select. The browser select, styled.
3
+ *
4
+ * From shadcn/ui (new-york-v4, MIT), carried by the schemes whose inventory names it.
5
+ * Reads the shadcn variables, which theme.css maps onto the scheme's semantic tokens, and the
6
+ * radius, shadow and text scale that shadcn.css maps onto the scheme's tokens. Dark mode comes
7
+ * from the tokens, so no dark: classes.
8
+ */
9
+ import * as React from "react";
10
+ import { cn } from "../lib/utils";
11
+ import { ChevronDownIcon } from "lucide-react";
12
+
13
+ function NativeSelect({
14
+ className,
15
+ size = "default",
16
+ ...props
17
+ }: Omit<React.ComponentProps<"select">, "size"> & { size?: "sm" | "default" }) {
18
+ return (
19
+ <div
20
+ className="group/native-select relative w-fit has-[select:disabled]:opacity-50"
21
+ data-slot="native-select-wrapper"
22
+ >
23
+ <select
24
+ data-slot="native-select"
25
+ data-size={size}
26
+ className={cn(
27
+ "h-9 w-full min-w-0 appearance-none rounded-md border border-input bg-transparent px-3 py-2 pr-9 text-sm shadow-xs transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed data-[size=sm]:h-8 data-[size=sm]:py-1",
28
+ "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
29
+ "aria-invalid:border-destructive aria-invalid:ring-destructive/20",
30
+ className,
31
+ )}
32
+ {...props}
33
+ />
34
+ <ChevronDownIcon
35
+ className="pointer-events-none absolute top-1/2 right-3.5 size-4 -translate-y-1/2 text-muted-foreground opacity-50 select-none"
36
+ aria-hidden="true"
37
+ data-slot="native-select-icon"
38
+ />
39
+ </div>
40
+ );
41
+ }
42
+
43
+ function NativeSelectOption({ className, ...props }: React.ComponentProps<"option">) {
44
+ return (
45
+ <option data-slot="native-select-option" className={cn("bg-[Canvas] text-[CanvasText]", className)} {...props} />
46
+ );
47
+ }
48
+
49
+ function NativeSelectOptGroup({ className, ...props }: React.ComponentProps<"optgroup">) {
50
+ return (
51
+ <optgroup
52
+ data-slot="native-select-optgroup"
53
+ className={cn("bg-[Canvas] text-[CanvasText]", className)}
54
+ {...props}
55
+ />
56
+ );
57
+ }
58
+
59
+ export { NativeSelect, NativeSelectOptGroup, NativeSelectOption };
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Navigation menu. Site navigation with links and panels.
3
+ *
4
+ * From shadcn/ui (new-york-v4, MIT), carried by the schemes whose inventory names it.
5
+ * Reads the shadcn variables, which theme.css maps onto the scheme's semantic tokens, and the
6
+ * radius, shadow and text scale that shadcn.css maps onto the scheme's tokens. Dark mode comes
7
+ * from the tokens, so no dark: classes.
8
+ */
9
+ import * as React from "react";
10
+ import { cva } from "class-variance-authority";
11
+ import { cn } from "../lib/utils";
12
+ import { ChevronDownIcon } from "lucide-react";
13
+ import { NavigationMenu as NavigationMenuPrimitive } from "radix-ui";
14
+
15
+ function NavigationMenu({
16
+ className,
17
+ children,
18
+ viewport = true,
19
+ ...props
20
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Root> & {
21
+ viewport?: boolean;
22
+ }) {
23
+ return (
24
+ <NavigationMenuPrimitive.Root
25
+ data-slot="navigation-menu"
26
+ data-viewport={viewport}
27
+ className={cn("group/navigation-menu relative flex max-w-max flex-1 items-center justify-center", className)}
28
+ {...props}
29
+ >
30
+ {children}
31
+ {viewport && <NavigationMenuViewport />}
32
+ </NavigationMenuPrimitive.Root>
33
+ );
34
+ }
35
+
36
+ function NavigationMenuList({ className, ...props }: React.ComponentProps<typeof NavigationMenuPrimitive.List>) {
37
+ return (
38
+ <NavigationMenuPrimitive.List
39
+ data-slot="navigation-menu-list"
40
+ className={cn("group flex flex-1 list-none items-center justify-center gap-1", className)}
41
+ {...props}
42
+ />
43
+ );
44
+ }
45
+
46
+ function NavigationMenuItem({ className, ...props }: React.ComponentProps<typeof NavigationMenuPrimitive.Item>) {
47
+ return (
48
+ <NavigationMenuPrimitive.Item data-slot="navigation-menu-item" className={cn("relative", className)} {...props} />
49
+ );
50
+ }
51
+
52
+ const navigationMenuTriggerStyle = cva(
53
+ "group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-[color,box-shadow] outline-none hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=open]:bg-accent/50 data-[state=open]:text-accent-foreground data-[state=open]:hover:bg-accent data-[state=open]:focus:bg-accent",
54
+ );
55
+
56
+ function NavigationMenuTrigger({
57
+ className,
58
+ children,
59
+ ...props
60
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Trigger>) {
61
+ return (
62
+ <NavigationMenuPrimitive.Trigger
63
+ data-slot="navigation-menu-trigger"
64
+ className={cn(navigationMenuTriggerStyle(), "group", className)}
65
+ {...props}
66
+ >
67
+ {children}{" "}
68
+ <ChevronDownIcon
69
+ className="relative top-[1px] ml-1 size-3 transition duration-300 group-data-[state=open]:rotate-180"
70
+ aria-hidden="true"
71
+ />
72
+ </NavigationMenuPrimitive.Trigger>
73
+ );
74
+ }
75
+
76
+ function NavigationMenuContent({ className, ...props }: React.ComponentProps<typeof NavigationMenuPrimitive.Content>) {
77
+ return (
78
+ <NavigationMenuPrimitive.Content
79
+ data-slot="navigation-menu-content"
80
+ className={cn(
81
+ "top-0 left-0 w-full p-2 pr-2.5 data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 data-[motion^=from-]:animate-in data-[motion^=from-]:fade-in data-[motion^=to-]:animate-out data-[motion^=to-]:fade-out md:absolute md:w-auto",
82
+ "group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden group-data-[viewport=false]/navigation-menu:rounded-md group-data-[viewport=false]/navigation-menu:border group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:duration-200 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none group-data-[viewport=false]/navigation-menu:data-[state=closed]:animate-out group-data-[viewport=false]/navigation-menu:data-[state=closed]:fade-out-0 group-data-[viewport=false]/navigation-menu:data-[state=closed]:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:animate-in group-data-[viewport=false]/navigation-menu:data-[state=open]:fade-in-0 group-data-[viewport=false]/navigation-menu:data-[state=open]:zoom-in-95",
83
+ className,
84
+ )}
85
+ {...props}
86
+ />
87
+ );
88
+ }
89
+
90
+ function NavigationMenuViewport({
91
+ className,
92
+ ...props
93
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Viewport>) {
94
+ return (
95
+ <div className={cn("absolute top-full left-0 isolate z-50 flex justify-center")}>
96
+ <NavigationMenuPrimitive.Viewport
97
+ data-slot="navigation-menu-viewport"
98
+ className={cn(
99
+ "origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]",
100
+ className,
101
+ )}
102
+ {...props}
103
+ />
104
+ </div>
105
+ );
106
+ }
107
+
108
+ function NavigationMenuLink({ className, ...props }: React.ComponentProps<typeof NavigationMenuPrimitive.Link>) {
109
+ return (
110
+ <NavigationMenuPrimitive.Link
111
+ data-slot="navigation-menu-link"
112
+ className={cn(
113
+ "flex flex-col gap-1 rounded-sm p-2 text-sm transition-all outline-none hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 data-[active=true]:bg-accent/50 data-[active=true]:text-accent-foreground data-[active=true]:hover:bg-accent data-[active=true]:focus:bg-accent [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
114
+ className,
115
+ )}
116
+ {...props}
117
+ />
118
+ );
119
+ }
120
+
121
+ function NavigationMenuIndicator({
122
+ className,
123
+ ...props
124
+ }: React.ComponentProps<typeof NavigationMenuPrimitive.Indicator>) {
125
+ return (
126
+ <NavigationMenuPrimitive.Indicator
127
+ data-slot="navigation-menu-indicator"
128
+ className={cn(
129
+ "top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:animate-in data-[state=visible]:fade-in",
130
+ className,
131
+ )}
132
+ {...props}
133
+ >
134
+ <div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" />
135
+ </NavigationMenuPrimitive.Indicator>
136
+ );
137
+ }
138
+
139
+ export {
140
+ NavigationMenu,
141
+ NavigationMenuList,
142
+ NavigationMenuItem,
143
+ NavigationMenuContent,
144
+ NavigationMenuTrigger,
145
+ NavigationMenuLink,
146
+ NavigationMenuIndicator,
147
+ NavigationMenuViewport,
148
+ navigationMenuTriggerStyle,
149
+ };