@plexui/ui 0.6.0 → 0.7.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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @plexui/ui
2
2
 
3
- A modern React component library built on the same design foundations as OpenAI's ChatGPT UI. 35 production-ready components, 14 hooks, a three-layer design token system, and a unified 9-step size scale — all powered by Radix primitives and Tailwind CSS 4.
3
+ A modern React component library with 35 production-ready components, 14 hooks, a three-layer design token system, and a unified 9-step size scale — all powered by Radix primitives and Tailwind CSS 4.
4
4
 
5
5
  [Documentation](https://plexui.com) • [GitHub](https://github.com/plex-ui/docs) • [Figma Kit](https://plexui.com/#pricing)
6
6
 
@@ -10,7 +10,7 @@ A modern React component library built on the same design foundations as OpenAI'
10
10
 
11
11
  ## Highlights
12
12
 
13
- - **Battle-tested at ChatGPT scale** — components validated in a product used by hundreds of millions.
13
+ - **Production-grade** — components designed and tested for real products at scale.
14
14
  - **9-step size scale** — all key controls (Button, Input, Select, SegmentedControl, etc.) share a unified `ControlSize` scale from `3xs` (22 px) to `3xl` (48 px). Most competitors offer only 3–4.
15
15
  - **Three-layer design tokens** — primitive → semantic → component CSS custom properties with `light-dark()` theming, alpha transparency scale, and 4-level elevation system.
16
16
  - **Radix + Tailwind 4** — accessible primitives under the hood, utility-first styling on top.
@@ -5,18 +5,31 @@
5
5
  background-color: var(--codeblock-background-color);
6
6
  }.CopyButtonContainer {
7
7
  position: absolute;
8
- top: 0;
9
- right: 0;
10
- padding: 12px 8px 0 0;
11
- border-radius: inherit;
12
- color: #24292e;
8
+ top: 8px;
9
+ right: 8px;
10
+ box-sizing: border-box;
11
+ display: grid;
12
+ place-items: center;
13
+ width: 32px;
14
+ height: 32px;
15
+ border-radius: 8px;
16
+ background-color: var(--color-surface-elevated-secondary);
17
+ color: var(--color-text-secondary);
13
18
  }/* Force the CopyButton to inherit the container's color on all states.
14
19
  Ghost hover background (::before) is preserved — it's the correct behavior. */.CopyButtonContainer button {
15
20
  color: inherit !important;
16
- }@media (prefers-color-scheme: dark) {
17
- .CopyButtonContainer {
18
- color: #d0d0d0;
19
- }
21
+ padding: 0 !important;
22
+ -webkit-tap-highlight-color: transparent;
23
+ }.CopyButtonContainer button::before {
24
+ opacity: 0 !important;
25
+ transform: none !important;
26
+ background-color: transparent !important;
27
+ box-shadow: none !important;
28
+ }.CopyButtonContainer button:focus,
29
+ .CopyButtonContainer button:focus-visible {
30
+ outline: none !important;
31
+ }.CopyButtonContainer button:focus-visible::after {
32
+ outline: none !important;
20
33
  }.SyntaxHighlighter {
21
34
  --syntax1: var(--codeblock-syntax-1);
22
35
  --syntax2: var(--codeblock-syntax-2);
@@ -35,7 +48,7 @@
35
48
  /* 14px to a 16px baseline */
36
49
  font-weight: var(--font-weight-normal);
37
50
  line-height: 1.714em;
38
- /* 24px at 14px — matches OpenAI reference */
51
+ /* 24px at 14px */
39
52
 
40
53
  /* stylelint-disable selector-class-pattern */
41
54
 
@@ -115,4 +128,4 @@
115
128
  align-items: center;
116
129
  justify-content: center;
117
130
  }
118
- }
131
+ }
@@ -1,6 +1,5 @@
1
1
  @layer components {/* =============================================
2
2
  Sidebar Component Styles
3
- Based on OpenAI Platform styling
4
3
  ============================================= *//* Layout container (wraps sidebar + content) */.SidebarLayout {
5
4
  display: flex;
6
5
  width: 100%;
@@ -4,9 +4,10 @@ import clsx from "clsx";
4
4
  import { Switch as RadixSwitch } from "radix-ui";
5
5
  import { useId } from "react";
6
6
  import s from "./Switch.module.css";
7
- export const Switch = ({ className, label, id: propsId, disabled, labelPosition = "end", ...restProps }) => {
7
+ export const Switch = ({ className, label, description, id: propsId, disabled, labelPosition = "end", ...restProps }) => {
8
8
  const reactId = useId();
9
9
  const id = propsId ?? reactId;
10
- return (_jsxs("div", { className: clsx(s.Container, className), "data-disabled": disabled ? "" : undefined, "data-has-label": label ? "" : undefined, "data-label-position": labelPosition, children: [_jsx(RadixSwitch.Root, { id: id, className: s.Track, disabled: disabled, ...restProps, children: _jsx(RadixSwitch.Thumb, { className: s.Thumb }) }), label && (_jsx("label", { htmlFor: id, className: s.Label, children: label }))] }));
10
+ const descriptionId = description ? `${id}-description` : undefined;
11
+ return (_jsxs("div", { className: clsx(s.Container, className), "data-disabled": disabled ? "" : undefined, "data-has-label": label ? "" : undefined, "data-label-position": labelPosition, children: [_jsx(RadixSwitch.Root, { id: id, className: s.Track, disabled: disabled, "aria-describedby": descriptionId, ...restProps, children: _jsx(RadixSwitch.Thumb, { className: s.Thumb }) }), label && (description ? (_jsxs("div", { className: s.LabelGroup, children: [_jsx("label", { htmlFor: id, className: s.Label, children: label }), _jsx("span", { id: descriptionId, className: s.Description, children: description })] })) : (_jsx("label", { htmlFor: id, className: s.Label, children: label })))] }));
11
12
  };
12
13
  //# sourceMappingURL=Switch.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Switch.js","sourceRoot":"","sources":["../../../../src/components/Switch/Switch.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,IAAI,MAAM,MAAM,CAAA;AACvB,OAAO,EAAE,MAAM,IAAI,WAAW,EAAE,MAAM,UAAU,CAAA;AAChD,OAAO,EAA0C,KAAK,EAAE,MAAM,OAAO,CAAA;AACrE,OAAO,CAAC,MAAM,qBAAqB,CAAA;AAkCnC,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,EACrB,SAAS,EACT,KAAK,EACL,EAAE,EAAE,OAAO,EACX,QAAQ,EACR,aAAa,GAAG,KAAK,EACrB,GAAG,SAAS,EACA,EAAE,EAAE;IAChB,MAAM,OAAO,GAAG,KAAK,EAAE,CAAA;IACvB,MAAM,EAAE,GAAG,OAAO,IAAI,OAAO,CAAA;IAE7B,OAAO,CACL,eACE,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,SAAS,EAAE,SAAS,CAAC,mBACxB,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,oBACxB,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,yBACjB,aAAa,aAElC,KAAC,WAAW,CAAC,IAAI,IAAC,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,KAAM,SAAS,YAC7E,KAAC,WAAW,CAAC,KAAK,IAAC,SAAS,EAAE,CAAC,CAAC,KAAK,GAAI,GACxB,EAElB,KAAK,IAAI,CACR,gBAAO,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,KAAK,YACnC,KAAK,GACA,CACT,IACG,CACP,CAAA;AACH,CAAC,CAAA","sourcesContent":["\"use client\"\n\nimport clsx from \"clsx\"\nimport { Switch as RadixSwitch } from \"radix-ui\"\nimport { type FocusEventHandler, type ReactNode, useId } from \"react\"\nimport s from \"./Switch.module.css\"\n\nexport type SwitchProps = {\n /** The `id` of the switch. */\n id?: string\n /** The state of the switch when it is initially rendered. Use when you do not need to control its state. */\n defaultChecked?: boolean\n /** The controlled state of the switch. Must be used in conjunction with `onCheckedChange`. */\n checked?: boolean\n /** Optional accessible label rendered to the right of the checkbox. */\n label?: ReactNode\n /** Event handler called when the state of the switch changes. */\n onCheckedChange?: (nextState: boolean) => void\n /** Event handler called when the checkbox looses focus. */\n onBlur?: FocusEventHandler<HTMLButtonElement>\n /** Event handler called when the checkbox gains focus. */\n onFocus?: FocusEventHandler<HTMLButtonElement>\n /** When `true`, prevents the user from interacting with the switch. */\n disabled?: boolean\n /** When `true`, indicates that the user must check the switch before the owning form can be submitted. */\n required?: boolean\n /** The name of the switch. Submitted with its owning form as part of a name/value pair. */\n name?: string\n /** The value given as data when submitted with a `name`. */\n value?: string\n /** CSS classes applied to wrapper node */\n className?: string\n /**\n * The position of the label relative to the switch.\n * @default end\n */\n labelPosition?: \"start\" | \"end\"\n}\n\nexport const Switch = ({\n className,\n label,\n id: propsId,\n disabled,\n labelPosition = \"end\",\n ...restProps\n}: SwitchProps) => {\n const reactId = useId()\n const id = propsId ?? reactId\n\n return (\n <div\n className={clsx(s.Container, className)}\n data-disabled={disabled ? \"\" : undefined}\n data-has-label={label ? \"\" : undefined}\n data-label-position={labelPosition}\n >\n <RadixSwitch.Root id={id} className={s.Track} disabled={disabled} {...restProps}>\n <RadixSwitch.Thumb className={s.Thumb} />\n </RadixSwitch.Root>\n\n {label && (\n <label htmlFor={id} className={s.Label}>\n {label}\n </label>\n )}\n </div>\n )\n}\n"]}
1
+ {"version":3,"file":"Switch.js","sourceRoot":"","sources":["../../../../src/components/Switch/Switch.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,IAAI,MAAM,MAAM,CAAA;AACvB,OAAO,EAAE,MAAM,IAAI,WAAW,EAAE,MAAM,UAAU,CAAA;AAChD,OAAO,EAA0C,KAAK,EAAE,MAAM,OAAO,CAAA;AACrE,OAAO,CAAC,MAAM,qBAAqB,CAAA;AAoCnC,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,EACrB,SAAS,EACT,KAAK,EACL,WAAW,EACX,EAAE,EAAE,OAAO,EACX,QAAQ,EACR,aAAa,GAAG,KAAK,EACrB,GAAG,SAAS,EACA,EAAE,EAAE;IAChB,MAAM,OAAO,GAAG,KAAK,EAAE,CAAA;IACvB,MAAM,EAAE,GAAG,OAAO,IAAI,OAAO,CAAA;IAC7B,MAAM,aAAa,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,SAAS,CAAA;IAEnE,OAAO,CACL,eACE,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,SAAS,EAAE,SAAS,CAAC,mBACxB,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,oBACxB,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,yBACjB,aAAa,aAElC,KAAC,WAAW,CAAC,IAAI,IACf,EAAE,EAAE,EAAE,EACN,SAAS,EAAE,CAAC,CAAC,KAAK,EAClB,QAAQ,EAAE,QAAQ,sBACA,aAAa,KAC3B,SAAS,YAEb,KAAC,WAAW,CAAC,KAAK,IAAC,SAAS,EAAE,CAAC,CAAC,KAAK,GAAI,GACxB,EAElB,KAAK,IAAI,CACR,WAAW,CAAC,CAAC,CAAC,CACZ,eAAK,SAAS,EAAE,CAAC,CAAC,UAAU,aAC1B,gBAAO,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,KAAK,YACnC,KAAK,GACA,EACR,eAAM,EAAE,EAAE,aAAa,EAAE,SAAS,EAAE,CAAC,CAAC,WAAW,YAC9C,WAAW,GACP,IACH,CACP,CAAC,CAAC,CAAC,CACF,gBAAO,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,KAAK,YACnC,KAAK,GACA,CACT,CACF,IACG,CACP,CAAA;AACH,CAAC,CAAA","sourcesContent":["\"use client\"\n\nimport clsx from \"clsx\"\nimport { Switch as RadixSwitch } from \"radix-ui\"\nimport { type FocusEventHandler, type ReactNode, useId } from \"react\"\nimport s from \"./Switch.module.css\"\n\nexport type SwitchProps = {\n /** The `id` of the switch. */\n id?: string\n /** The state of the switch when it is initially rendered. Use when you do not need to control its state. */\n defaultChecked?: boolean\n /** The controlled state of the switch. Must be used in conjunction with `onCheckedChange`. */\n checked?: boolean\n /** Optional accessible label rendered next to the switch. */\n label?: ReactNode\n /** Optional description rendered below the label. Linked via `aria-describedby`. */\n description?: ReactNode\n /** Event handler called when the state of the switch changes. */\n onCheckedChange?: (nextState: boolean) => void\n /** Event handler called when the checkbox looses focus. */\n onBlur?: FocusEventHandler<HTMLButtonElement>\n /** Event handler called when the checkbox gains focus. */\n onFocus?: FocusEventHandler<HTMLButtonElement>\n /** When `true`, prevents the user from interacting with the switch. */\n disabled?: boolean\n /** When `true`, indicates that the user must check the switch before the owning form can be submitted. */\n required?: boolean\n /** The name of the switch. Submitted with its owning form as part of a name/value pair. */\n name?: string\n /** The value given as data when submitted with a `name`. */\n value?: string\n /** CSS classes applied to wrapper node */\n className?: string\n /**\n * The position of the label relative to the switch.\n * @default end\n */\n labelPosition?: \"start\" | \"end\"\n}\n\nexport const Switch = ({\n className,\n label,\n description,\n id: propsId,\n disabled,\n labelPosition = \"end\",\n ...restProps\n}: SwitchProps) => {\n const reactId = useId()\n const id = propsId ?? reactId\n const descriptionId = description ? `${id}-description` : undefined\n\n return (\n <div\n className={clsx(s.Container, className)}\n data-disabled={disabled ? \"\" : undefined}\n data-has-label={label ? \"\" : undefined}\n data-label-position={labelPosition}\n >\n <RadixSwitch.Root\n id={id}\n className={s.Track}\n disabled={disabled}\n aria-describedby={descriptionId}\n {...restProps}\n >\n <RadixSwitch.Thumb className={s.Thumb} />\n </RadixSwitch.Root>\n\n {label && (\n description ? (\n <div className={s.LabelGroup}>\n <label htmlFor={id} className={s.Label}>\n {label}\n </label>\n <span id={descriptionId} className={s.Description}>\n {description}\n </span>\n </div>\n ) : (\n <label htmlFor={id} className={s.Label}>\n {label}\n </label>\n )\n )}\n </div>\n )\n}\n"]}
@@ -75,20 +75,46 @@
75
75
  .Thumb[data-disabled] {
76
76
  background: var(--switch-thumb-color-disabled);
77
77
  box-shadow: none;
78
+ }.LabelGroup {
79
+ display: flex;
80
+ flex-direction: column;
81
+ gap: 4px;
82
+ }
83
+
84
+ /* Use padding so that the spacing is intrinsic to the group, remaining clickable. */
85
+ [data-label-position="end"] .LabelGroup {
86
+ padding-left: var(--switch-label-gap);
87
+ }
88
+
89
+ [data-label-position="start"] .LabelGroup {
90
+ padding-right: var(--switch-label-gap);
91
+ }
92
+
93
+ [data-disabled] .LabelGroup {
94
+ cursor: not-allowed;
78
95
  }.Label {
79
96
  cursor: pointer;
80
97
  }
81
98
 
82
99
  /* Use padding so that the spacing is intrinsic to the label, remaining clickable. */
83
- [data-label-position="end"] .Label {
100
+ [data-label-position="end"] > .Label {
84
101
  padding-left: var(--switch-label-gap);
85
102
  }
86
103
 
87
- [data-label-position="start"] .Label {
104
+ [data-label-position="start"] > .Label {
88
105
  padding-right: var(--switch-label-gap);
89
106
  }
90
107
 
91
108
  [data-disabled] .Label {
92
109
  cursor: not-allowed;
110
+ }.Description {
111
+ color: var(--color-text-secondary);
112
+ font-size: var(--font-text-xs-size);
113
+ line-height: var(--font-text-xs-line-height);
114
+ cursor: pointer;
115
+ }
116
+
117
+ [data-disabled] .Description {
118
+ cursor: not-allowed;
93
119
  }
94
120
  }
@@ -6,8 +6,10 @@ export type SwitchProps = {
6
6
  defaultChecked?: boolean;
7
7
  /** The controlled state of the switch. Must be used in conjunction with `onCheckedChange`. */
8
8
  checked?: boolean;
9
- /** Optional accessible label rendered to the right of the checkbox. */
9
+ /** Optional accessible label rendered next to the switch. */
10
10
  label?: ReactNode;
11
+ /** Optional description rendered below the label. Linked via `aria-describedby`. */
12
+ description?: ReactNode;
11
13
  /** Event handler called when the state of the switch changes. */
12
14
  onCheckedChange?: (nextState: boolean) => void;
13
15
  /** Event handler called when the checkbox looses focus. */
@@ -30,4 +32,4 @@ export type SwitchProps = {
30
32
  */
31
33
  labelPosition?: "start" | "end";
32
34
  };
33
- export declare const Switch: ({ className, label, id: propsId, disabled, labelPosition, ...restProps }: SwitchProps) => import("react/jsx-runtime").JSX.Element;
35
+ export declare const Switch: ({ className, label, description, id: propsId, disabled, labelPosition, ...restProps }: SwitchProps) => import("react/jsx-runtime").JSX.Element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plexui/ui",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Modern design system for building high-quality applications",
5
5
  "type": "module",
6
6
  "license": "MIT",