@openui-xio/stage-ui 0.0.2

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 (41) hide show
  1. package/.storybook/main.ts +47 -0
  2. package/.storybook/preview.ts +15 -0
  3. package/CHANGELOG.md +13 -0
  4. package/LICENSE +661 -0
  5. package/README.md +13 -0
  6. package/package.json +57 -0
  7. package/postcss.config.cjs +5 -0
  8. package/postcss.config.js +5 -0
  9. package/src/components/accordion.tsx +56 -0
  10. package/src/components/button.tsx +41 -0
  11. package/src/components/checkbox.tsx +22 -0
  12. package/src/components/field.tsx +13 -0
  13. package/src/components/index.ts +21 -0
  14. package/src/components/menu.tsx +133 -0
  15. package/src/components/switch.tsx +17 -0
  16. package/src/lib/shared-styles.ts +5 -0
  17. package/src/lib/utils.ts +7 -0
  18. package/src/stories/Accordion.stories.tsx +39 -0
  19. package/src/stories/Button.stories.ts +39 -0
  20. package/src/stories/Checkbox.stories.ts +30 -0
  21. package/src/stories/Configure.mdx +364 -0
  22. package/src/stories/Switch.stories.ts +30 -0
  23. package/src/stories/assets/accessibility.png +0 -0
  24. package/src/stories/assets/accessibility.svg +1 -0
  25. package/src/stories/assets/addon-library.png +0 -0
  26. package/src/stories/assets/assets.png +0 -0
  27. package/src/stories/assets/avif-test-image.avif +0 -0
  28. package/src/stories/assets/context.png +0 -0
  29. package/src/stories/assets/discord.svg +1 -0
  30. package/src/stories/assets/docs.png +0 -0
  31. package/src/stories/assets/figma-plugin.png +0 -0
  32. package/src/stories/assets/github.svg +1 -0
  33. package/src/stories/assets/share.png +0 -0
  34. package/src/stories/assets/styling.png +0 -0
  35. package/src/stories/assets/testing.png +0 -0
  36. package/src/stories/assets/theming.png +0 -0
  37. package/src/stories/assets/tutorials.svg +1 -0
  38. package/src/stories/assets/youtube.svg +1 -0
  39. package/src/style.css +103 -0
  40. package/tailwind.config.ts +11 -0
  41. package/tsconfig.json +15 -0
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@openui-xio/stage-ui",
3
+ "type": "module",
4
+ "private": false,
5
+ "peerDependencies": {
6
+ "react": "19.1.2",
7
+ "react-dom": "19.1.2"
8
+ },
9
+ "dependencies": {
10
+ "@base-ui-components/react": "1.0.0-beta.2",
11
+ "@hookform/resolvers": "^5.2.1",
12
+ "class-variance-authority": "^0.7.1",
13
+ "clsx": "^2.1.1",
14
+ "input-otp": "^1.4.2",
15
+ "lucide-react": "^0.523.0",
16
+ "tailwind-merge": "^3.2.0",
17
+ "tw-animate-css": "^1.3.5"
18
+ },
19
+ "devDependencies": {
20
+ "@storybook/addon-docs": "^9.1.3",
21
+ "@storybook/addon-onboarding": "9.1.3",
22
+ "@storybook/react-vite": "^9.1.3",
23
+ "@tailwindcss/postcss": "^4.1.5",
24
+ "@tailwindcss/vite": "^4.1.12",
25
+ "@turbo/gen": "^2.5.3",
26
+ "@types/node": "22.15.2",
27
+ "@types/react": "^19.1.3",
28
+ "@types/react-dom": "^19.1.3",
29
+ "esbuild": "^0.24.2",
30
+ "glob": "^11.0.0",
31
+ "postcss": "^8.5.6",
32
+ "postcss-cli": "^11.0.1",
33
+ "react": "19.1.2",
34
+ "react-dom": "19.1.2",
35
+ "storybook": "^9.1.3",
36
+ "tailwindcss": "^4.1.5",
37
+ "tsx": "^4.19.2",
38
+ "typescript": "^5.8.3",
39
+ "vite": "^7.1.3",
40
+ "@openui-xio/typescript-config": "0.0.1"
41
+ },
42
+ "main": "./dist/components/index.js",
43
+ "types": "./dist/components/index.d.ts",
44
+ "exports": {
45
+ "./style.css": "./src/style.css",
46
+ "./postcss.config": "./postcss.config.mjs",
47
+ "./lib/*": "./src/lib/*.ts",
48
+ "./components/*": "./src/components/*.tsx",
49
+ "./hooks/*": "./src/hooks/*.ts"
50
+ },
51
+ "version": "0.0.2",
52
+ "scripts": {
53
+ "lint": "biome lint . --max-warnings 0",
54
+ "storybook": "storybook dev -p 6006",
55
+ "build-storybook": "storybook build"
56
+ }
57
+ }
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ plugins: {
3
+ '@tailwindcss/postcss': {},
4
+ },
5
+ };
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ plugins: {
3
+ '@tailwindcss/postcss': {},
4
+ },
5
+ };
@@ -0,0 +1,56 @@
1
+ import { cn } from '../lib/utils.js';
2
+ import { Accordion as BaseAccordion } from '@base-ui-components/react/accordion';
3
+ import { ChevronDownIcon } from 'lucide-react';
4
+ import type { ReactNode } from 'react';
5
+
6
+ export type AccordionProps = React.ComponentProps<typeof BaseAccordion.Root> & {
7
+ variant?: 'clear' | 'glass';
8
+ };
9
+
10
+ export function Accordion({ variant = 'glass', ...props }: AccordionProps) {
11
+ return (
12
+ <BaseAccordion.Root
13
+ {...props}
14
+ className={cn(
15
+ variant === 'glass' ? 'glass-body overflow-hidden rounded-xl' : '',
16
+ props.className,
17
+ )}
18
+ />
19
+ );
20
+ }
21
+
22
+ export type AccordionItemProps = React.ComponentProps<
23
+ typeof BaseAccordion.Item
24
+ > & {
25
+ title: ReactNode;
26
+ icon?: ReactNode;
27
+ children?: ReactNode;
28
+ };
29
+
30
+ export function AccordionItem({
31
+ title,
32
+ icon,
33
+ children,
34
+ ...props
35
+ }: AccordionItemProps) {
36
+ return (
37
+ <BaseAccordion.Item
38
+ {...props}
39
+ className={cn(
40
+ 'border-zinc-500/10 border-b shadow-none hover:bg-zinc-500/5',
41
+ props.className,
42
+ )}
43
+ >
44
+ <BaseAccordion.Header>
45
+ <BaseAccordion.Trigger className="group flex w-full flex-row items-center justify-between gap-4 bg-zinc-50/10 px-3 py-2 transition-colors duration-150 ease-out hover:bg-white/20">
46
+ {icon && <div className="size-5">{icon}</div>}
47
+ <div className="block flex-1 text-start font-medium">{title}</div>
48
+ <ChevronDownIcon className="size-4 origin-center rotate-0 opacity-40 transition-transform duration-150 ease-out group-hover:opacity-60 group-data-[panel-open]:rotate-180" />
49
+ </BaseAccordion.Trigger>
50
+ </BaseAccordion.Header>
51
+ <BaseAccordion.Panel className="h-[var(--accordion-panel-height)] px-3 pt-1.5 pb-3 transition-[height] duration-150 ease-out data-[ending-style]:h-0 data-[starting-style]:h-0">
52
+ {children}
53
+ </BaseAccordion.Panel>
54
+ </BaseAccordion.Item>
55
+ );
56
+ }
@@ -0,0 +1,41 @@
1
+ import { cva, type VariantProps } from 'class-variance-authority';
2
+ import { cn } from '../lib/utils.js';
3
+
4
+ export const buttonVariants = cva(
5
+ 'relative flex flex-row items-center justify-center gap-2 font-normal disabled:opacity-50',
6
+ {
7
+ variants: {
8
+ variant: {
9
+ primary:
10
+ 'glass-body glass-body-interactive glass-body-motion glass-body-motion-interactive bg-primary text-primary-foreground',
11
+ secondary:
12
+ 'glass-body glass-body-interactive glass-body-motion glass-body-motion-interactive text-foreground',
13
+ ghost: 'bg-transparent font-medium text-foreground hover:bg-zinc-500/5',
14
+ },
15
+ size: {
16
+ sm: 'h-8 rounded-xl px-2 py-1 text-sm',
17
+ md: 'h-10 rounded-xl px-4 py-2 text-sm',
18
+ lg: 'h-12 rounded-xl px-6 py-3 text-base',
19
+ xl: 'h-14 rounded-xl px-8 py-4 text-lg',
20
+ 'icon-sm': 'size-8 rounded-full',
21
+ 'icon-md': 'size-10 rounded-full',
22
+ },
23
+ },
24
+ },
25
+ );
26
+
27
+ export type ButtonProps = React.HTMLAttributes<HTMLButtonElement> &
28
+ VariantProps<typeof buttonVariants>;
29
+
30
+ export function Button({
31
+ variant = 'primary',
32
+ size = 'md',
33
+ ...props
34
+ }: ButtonProps) {
35
+ return (
36
+ <button
37
+ {...props}
38
+ className={cn(buttonVariants({ variant, size }), props.className)}
39
+ />
40
+ );
41
+ }
@@ -0,0 +1,22 @@
1
+ import * as React from 'react';
2
+ import { Checkbox as BaseCheckbox } from '@base-ui-components/react/checkbox';
3
+ import { CheckIcon } from 'lucide-react';
4
+ import { cn } from '../lib/utils.js';
5
+
6
+ export function Checkbox(
7
+ props: React.ComponentProps<typeof BaseCheckbox.Root>,
8
+ ) {
9
+ return (
10
+ <BaseCheckbox.Root
11
+ {...props}
12
+ className={cn(
13
+ 'glass-inset relative flex size-6 rounded-md p-1 transition-[background-position,box-shadow,background-color] duration-[150ms] ease-[cubic-bezier(0.26,0.75,0.38,0.45)] disabled:pointer-events-none disabled:opacity-50 data-[checked]:bg-blue-600',
14
+ props.className,
15
+ )}
16
+ >
17
+ <BaseCheckbox.Indicator>
18
+ <CheckIcon className="h-4 w-4 stroke-3 text-white" />
19
+ </BaseCheckbox.Indicator>
20
+ </BaseCheckbox.Root>
21
+ );
22
+ }
@@ -0,0 +1,13 @@
1
+ import { Field as FieldBase } from '@base-ui-components/react/field';
2
+
3
+ export const Field = FieldBase.Root;
4
+
5
+ export const FieldLabel = FieldBase.Label;
6
+
7
+ export const FieldControl = FieldBase.Control;
8
+
9
+ export const FieldDescription = FieldBase.Description;
10
+
11
+ export const FieldError = FieldBase.Error;
12
+
13
+ export const FieldValidity = FieldBase.Validity;
@@ -0,0 +1,21 @@
1
+ // Export all components
2
+ export { Button, type ButtonProps } from './button.js';
3
+ export { Switch } from './switch.js';
4
+ export { Checkbox } from './checkbox.js';
5
+ export {
6
+ Accordion,
7
+ AccordionItem,
8
+ type AccordionProps,
9
+ type AccordionItemProps,
10
+ } from './accordion.js';
11
+ export {
12
+ Field,
13
+ FieldLabel,
14
+ FieldControl,
15
+ FieldDescription,
16
+ FieldError,
17
+ FieldValidity,
18
+ } from './field.js';
19
+
20
+ // Re-export utilities
21
+ export { cn } from '../lib/utils.js';
@@ -0,0 +1,133 @@
1
+ import { Menu as MenuBase } from '@base-ui-components/react/menu';
2
+ import type { ComponentProps, ReactElement } from 'react';
3
+ import { cn } from '../lib/utils.js';
4
+ import { ChevronRightIcon } from 'lucide-react';
5
+
6
+ export const Menu = MenuBase.Root;
7
+
8
+ export type MenuTriggerProps = Omit<
9
+ ComponentProps<typeof MenuBase.Trigger>,
10
+ 'render' | 'className'
11
+ > & {
12
+ children: React.ReactElement;
13
+ };
14
+ export function MenuTrigger({ children, ...props }: MenuTriggerProps) {
15
+ return (
16
+ <MenuBase.Trigger
17
+ {...props}
18
+ /* We do this because it works just fine but for some reason the types bitch around... */
19
+ render={children as unknown as () => ReactElement}
20
+ />
21
+ );
22
+ }
23
+
24
+ export type MenuContentProps = Omit<
25
+ ComponentProps<typeof MenuBase.Positioner> &
26
+ ComponentProps<typeof MenuBase.Popup>,
27
+ 'render'
28
+ > & {
29
+ children: React.ReactNode;
30
+ };
31
+ export function MenuContent({
32
+ align = 'center',
33
+ alignOffset,
34
+ side,
35
+ sideOffset = 8,
36
+ sticky,
37
+ className,
38
+ children,
39
+ ...props
40
+ }: MenuContentProps) {
41
+ return (
42
+ <MenuBase.Portal>
43
+ <MenuBase.Positioner
44
+ align={align}
45
+ alignOffset={alignOffset}
46
+ side={side}
47
+ sideOffset={sideOffset}
48
+ sticky={sticky}
49
+ >
50
+ <MenuBase.Popup
51
+ {...props}
52
+ className={cn(
53
+ 'glass-body glass-body-motion origin-[var(--transform-origin)] rounded-lg bg-white/60 p-1 shadow-lg backdrop-blur-xs transition-[transform,scale,opacity] data-[ending-style]:scale-90 data-[starting-style]:scale-90 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0 dark:bg-black/60',
54
+ className,
55
+ )}
56
+ >
57
+ {children}
58
+ </MenuBase.Popup>
59
+ </MenuBase.Positioner>
60
+ </MenuBase.Portal>
61
+ );
62
+ }
63
+
64
+ export type MenuItemProps = ComponentProps<typeof MenuBase.Item>;
65
+ export function MenuItem({ children, className, ...props }: MenuItemProps) {
66
+ return (
67
+ <MenuBase.Item
68
+ {...props}
69
+ className={cn(
70
+ 'flex w-full min-w-24 cursor-default flex-row items-center justify-start gap-2 rounded-md py-1.5 pr-6 pl-2 text-sm transition-all duration-150 ease-out hover:bg-black/5 hover:pl-2.25 dark:hover:bg-white/5',
71
+ className,
72
+ )}
73
+ >
74
+ {children}
75
+ </MenuBase.Item>
76
+ );
77
+ }
78
+
79
+ export const MenuSeparator = MenuBase.Separator;
80
+ export const MenuGroup = MenuBase.Group;
81
+ export const MenuGroupLabel = MenuBase.GroupLabel;
82
+ export const MenuRadioGroup = MenuBase.RadioGroup;
83
+ export const MenuRadioItem = MenuBase.RadioItem;
84
+ export const MenuCheckboxItem = MenuBase.CheckboxItem;
85
+ export const MenuSubmenu = MenuBase.SubmenuRoot;
86
+
87
+ export type MenuSubmenuTriggerProps = ComponentProps<
88
+ typeof MenuBase.SubmenuTrigger
89
+ >;
90
+ export function MenuSubmenuTrigger({
91
+ children,
92
+ className,
93
+ ...props
94
+ }: MenuSubmenuTriggerProps) {
95
+ return (
96
+ <MenuBase.SubmenuTrigger
97
+ {...props}
98
+ className={cn(
99
+ 'group flex w-full min-w-24 cursor-default flex-row items-center justify-start gap-2 rounded-md px-2 py-1.5 text-sm transition-all duration-150 ease-out hover:bg-black/5 hover:pl-2.25 dark:hover:bg-white/5',
100
+ className,
101
+ )}
102
+ >
103
+ {children}
104
+ <ChevronRightIcon className="ml-2 size-3 opacity-50 group-hover:opacity-100" />
105
+ </MenuBase.SubmenuTrigger>
106
+ );
107
+ }
108
+
109
+ export type MenuSubmenuContentProps = MenuContentProps;
110
+ export function MenuSubmenuContent({
111
+ align = 'center',
112
+ alignOffset,
113
+ side,
114
+ sideOffset = 0,
115
+ sticky,
116
+ className,
117
+ children,
118
+ ...props
119
+ }: MenuSubmenuContentProps) {
120
+ return (
121
+ <MenuContent
122
+ {...props}
123
+ align={align}
124
+ alignOffset={alignOffset}
125
+ side={side}
126
+ sideOffset={sideOffset}
127
+ sticky={sticky}
128
+ className={className}
129
+ >
130
+ {children}
131
+ </MenuContent>
132
+ );
133
+ }
@@ -0,0 +1,17 @@
1
+ import * as React from 'react';
2
+ import { Switch as BaseSwitch } from '@base-ui-components/react/switch';
3
+ import { cn } from '../lib/utils.js';
4
+
5
+ export function Switch(props: React.ComponentProps<typeof BaseSwitch.Root>) {
6
+ return (
7
+ <BaseSwitch.Root
8
+ {...props}
9
+ className={cn(
10
+ 'glass-inset relative flex h-6 w-10 rounded-full p-1 transition-[background-position,box-shadow,background-color] duration-[150ms] ease-[cubic-bezier(0.26,0.75,0.38,0.45)] disabled:pointer-events-none disabled:opacity-50 data-[checked]:bg-blue-600',
11
+ props.className,
12
+ )}
13
+ >
14
+ <BaseSwitch.Thumb className="glass-body glass-body-motion aspect-square h-full rounded-full bg-black/50 transition-transform duration-200 data-[checked]:translate-x-4 data-[checked]:bg-white/80" />
15
+ </BaseSwitch.Root>
16
+ );
17
+ }
@@ -0,0 +1,5 @@
1
+ export const glassOverlayBaseClassName = `
2
+ z-0
3
+ before:absolute before:content-normal before:size-full before:inset-0 before:border before:border-zinc-950/20 before:ring-inset before:ring-[1.5px] before:ring-white/30 before:backdrop-blur-sm before:-z-20 before:bg-white/85 before:rounded-[inherit]
4
+ after:absolute after:pointer-events-none after:rounded-[inherit] after:block after:size-full after:inset-0 after:shadow-glass
5
+ `;
@@ -0,0 +1,7 @@
1
+ import type { ClassValue } from 'class-variance-authority/types';
2
+ import { twMerge } from 'tailwind-merge';
3
+ import { clsx } from 'clsx';
4
+
5
+ export function cn(...inputs: ClassValue[]) {
6
+ return twMerge(clsx(inputs));
7
+ }
@@ -0,0 +1,39 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+
3
+ import { fn } from 'storybook/test';
4
+
5
+ import { Accordion, AccordionItem } from '../components/accordion.js';
6
+ import { CogIcon } from 'lucide-react';
7
+
8
+ // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
9
+ const meta = {
10
+ title: 'Example/Accordion',
11
+ component: () => (
12
+ <Accordion>
13
+ <AccordionItem title="item1" icon={<CogIcon className="size-5" />}>
14
+ This is content for Item 1.
15
+ </AccordionItem>
16
+ <AccordionItem title="item2">This is content for Item 2.</AccordionItem>
17
+ <AccordionItem title="item3">This is content for Item 3.</AccordionItem>
18
+ </Accordion>
19
+ ),
20
+ parameters: {},
21
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
22
+ tags: ['autodocs'],
23
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
24
+ argTypes: {
25
+ disabled: { control: 'boolean' },
26
+ },
27
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
28
+ args: { onClick: fn() },
29
+ } satisfies Meta<typeof Accordion>;
30
+
31
+ export default meta;
32
+ type Story = StoryObj<typeof meta>;
33
+
34
+ // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
35
+ export const Enabled: Story = {
36
+ args: {
37
+ defaultChecked: true,
38
+ },
39
+ };
@@ -0,0 +1,39 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+
3
+ import { fn } from 'storybook/test';
4
+
5
+ import { Button } from '../components/button.js';
6
+
7
+ // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
8
+ const meta = {
9
+ title: 'Example/Button',
10
+ component: Button,
11
+ parameters: {},
12
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
13
+ tags: ['autodocs'],
14
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
15
+ argTypes: {
16
+ variant: { control: 'select', options: ['primary', 'secondary', 'ghost'] },
17
+ size: {
18
+ control: 'select',
19
+ options: ['sm', 'md', 'lg', 'xl', 'icon-sm', 'icon-md'],
20
+ },
21
+ disabled: { control: 'boolean' },
22
+ children: { control: 'text' },
23
+ },
24
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
25
+ args: { onClick: fn() },
26
+ } satisfies Meta<typeof Button>;
27
+
28
+ export default meta;
29
+ type Story = StoryObj<typeof meta>;
30
+
31
+ // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
32
+ export const Primary: Story = {
33
+ args: {
34
+ variant: 'primary',
35
+ size: 'md',
36
+ shape: 'default',
37
+ children: 'Test button',
38
+ },
39
+ };
@@ -0,0 +1,30 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+
3
+ import { fn } from 'storybook/test';
4
+
5
+ import { Checkbox } from '../components/checkbox.js';
6
+
7
+ // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
8
+ const meta = {
9
+ title: 'Example/Checkbox',
10
+ component: Checkbox,
11
+ parameters: {},
12
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
13
+ tags: ['autodocs'],
14
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
15
+ argTypes: {
16
+ disabled: { control: 'boolean' },
17
+ },
18
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
19
+ args: { onClick: fn() },
20
+ } satisfies Meta<typeof Checkbox>;
21
+
22
+ export default meta;
23
+ type Story = StoryObj<typeof meta>;
24
+
25
+ // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
26
+ export const Enabled: Story = {
27
+ args: {
28
+ defaultChecked: true,
29
+ },
30
+ };