@robr0/design-system 0.1.0 → 0.3.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 (91) hide show
  1. package/README.md +13 -9
  2. package/components/Accordion/Accordion.js +0 -1
  3. package/components/Avatar/Avatar.js +1 -0
  4. package/components/Breadcrumb/Breadcrumb.css +9 -1
  5. package/components/Breadcrumb/Breadcrumb.d.ts +7 -1
  6. package/components/Breadcrumb/Breadcrumb.js +3 -2
  7. package/components/Button/Button.css +18 -0
  8. package/components/Button/Button.d.ts +38 -16
  9. package/components/Button/Button.js +76 -58
  10. package/components/Card/Card.css +6 -0
  11. package/components/Card/Card.d.ts +17 -5
  12. package/components/Card/Card.js +101 -80
  13. package/components/Checkbox/Checkbox.d.ts +24 -12
  14. package/components/Checkbox/Checkbox.js +99 -85
  15. package/components/CircularButton/CircularButton.css +11 -0
  16. package/components/CircularButton/CircularButton.d.ts +7 -1
  17. package/components/CircularButton/CircularButton.js +15 -4
  18. package/components/CodeBlock/CodeBlock.js +10 -1
  19. package/components/ColorPicker/ColorPicker.css +278 -0
  20. package/components/ColorPicker/ColorPicker.d.ts +50 -0
  21. package/components/ColorPicker/ColorPicker.js +338 -0
  22. package/components/Combobox/Combobox.css +0 -36
  23. package/components/Combobox/Combobox.d.ts +19 -7
  24. package/components/Combobox/Combobox.js +106 -89
  25. package/components/CommandPalette/CommandPalette.css +1 -15
  26. package/components/CommandPalette/CommandPalette.js +6 -5
  27. package/components/ContextMenu/ContextMenu.css +262 -0
  28. package/components/ContextMenu/ContextMenu.d.ts +26 -0
  29. package/components/ContextMenu/ContextMenu.js +350 -0
  30. package/components/DateInput/DateInput.css +0 -36
  31. package/components/DateInput/DateInput.d.ts +18 -21
  32. package/components/DateInput/DateInput.js +76 -64
  33. package/components/DatePicker/DatePicker.d.ts +9 -2
  34. package/components/DatePicker/DatePicker.js +137 -128
  35. package/components/Dialog/Dialog.d.ts +15 -4
  36. package/components/Dialog/Dialog.js +136 -118
  37. package/components/Drawer/Drawer.d.ts +15 -4
  38. package/components/Drawer/Drawer.js +138 -119
  39. package/components/Dropdown/Dropdown.css +0 -36
  40. package/components/Dropdown/Dropdown.d.ts +24 -8
  41. package/components/Dropdown/Dropdown.js +219 -169
  42. package/components/DropdownMenu/DropdownMenu.js +10 -7
  43. package/components/Field/Field.css +80 -0
  44. package/components/Field/Field.d.ts +50 -0
  45. package/components/Field/Field.js +58 -0
  46. package/components/Field/FieldContext.d.ts +42 -0
  47. package/components/Field/FieldContext.js +7 -0
  48. package/components/FileInput/FileInput.css +0 -36
  49. package/components/FileInput/FileInput.d.ts +13 -17
  50. package/components/FileInput/FileInput.js +168 -141
  51. package/components/Input/Input.css +0 -43
  52. package/components/Input/Input.d.ts +23 -22
  53. package/components/Input/Input.js +93 -66
  54. package/components/Kbd/Kbd.css +28 -0
  55. package/components/Kbd/Kbd.d.ts +19 -0
  56. package/components/Kbd/Kbd.js +14 -0
  57. package/components/Popover/Popover.d.ts +0 -6
  58. package/components/Popover/Popover.js +22 -16
  59. package/components/ProgressBar/ProgressBar.js +1 -1
  60. package/components/RadioButton/RadioButton.d.ts +26 -13
  61. package/components/RadioButton/RadioButton.js +95 -70
  62. package/components/SelectionCard/SelectionCard.css +6 -2
  63. package/components/SelectionCard/SelectionCard.d.ts +10 -4
  64. package/components/SelectionCard/SelectionCard.js +72 -56
  65. package/components/Skeleton/Skeleton.css +2 -0
  66. package/components/Slider/Slider.d.ts +19 -10
  67. package/components/Slider/Slider.js +50 -40
  68. package/components/Spinner/Spinner.css +11 -0
  69. package/components/Spinner/Spinner.d.ts +2 -2
  70. package/components/Swatch/Swatch.css +52 -0
  71. package/components/Swatch/Swatch.d.ts +34 -0
  72. package/components/Swatch/Swatch.js +44 -0
  73. package/components/Table/Table.css +19 -0
  74. package/components/Table/Table.d.ts +10 -2
  75. package/components/Table/Table.js +58 -58
  76. package/components/Textarea/Textarea.css +0 -43
  77. package/components/Textarea/Textarea.d.ts +21 -22
  78. package/components/Textarea/Textarea.js +78 -67
  79. package/components/ToggleGroup/ToggleGroup.d.ts +18 -8
  80. package/components/ToggleGroup/ToggleGroup.js +61 -44
  81. package/components/ToggleSwitch/ToggleSwitch.css +7 -2
  82. package/components/ToggleSwitch/ToggleSwitch.d.ts +17 -9
  83. package/components/ToggleSwitch/ToggleSwitch.js +57 -44
  84. package/components/registry.d.ts +47 -10
  85. package/components/registry.js +5 -1
  86. package/components/registry.json +498 -56
  87. package/components/registry.json.d.ts +498 -56
  88. package/components/registry.json.js +4 -1
  89. package/index.d.ts +8 -1
  90. package/index.js +16 -1
  91. package/package.json +1 -1
@@ -1,5 +1,6 @@
1
1
  import { default as React } from 'react';
2
- export interface CheckboxProps {
2
+ /** Props owned by Checkbox itself — everything else falls through to the wrapper. */
3
+ type CheckboxOwnProps = {
3
4
  /** Label text */
4
5
  label?: string;
5
6
  /** Whether the checkbox is checked */
@@ -10,18 +11,25 @@ export interface CheckboxProps {
10
11
  disabled?: boolean;
11
12
  /** Component size */
12
13
  size?: 'default' | 'compact';
13
- /** Callback when toggled */
14
- onChange?: (checked: boolean) => void;
14
+ /** Called with the next checked state when toggled */
15
+ onCheckedChange?: (checked: boolean) => void;
15
16
  /** Additional CSS classes */
16
17
  className?: string;
17
- /** Accessible label override */
18
+ /** @deprecated Use `onCheckedChange` instead. */
19
+ onChange?: (checked: boolean) => void;
20
+ /** @deprecated Pass the native `aria-label` attribute instead. */
18
21
  ariaLabel?: string;
19
- /** HTML name attribute */
22
+ /**
23
+ * @deprecated No-op. This component renders a `<div role="checkbox">`, not a
24
+ * native `<input>`, so it cannot participate in native form submission.
25
+ * Declared only so the attribute is not forwarded to an element that rejects it.
26
+ */
20
27
  name?: string;
21
- /** HTML id attribute */
22
- id?: string;
28
+ };
29
+ export interface CheckboxProps extends CheckboxOwnProps, Omit<React.ComponentPropsWithoutRef<'div'>, keyof CheckboxOwnProps> {
23
30
  }
24
- export interface CheckboxGroupProps {
31
+ export declare const Checkbox: React.ForwardRefExoticComponent<CheckboxProps & React.RefAttributes<HTMLDivElement>>;
32
+ type CheckboxGroupOwnProps = {
25
33
  /** Group label */
26
34
  label?: string;
27
35
  /** Checkbox options */
@@ -36,10 +44,14 @@ export interface CheckboxGroupProps {
36
44
  direction?: 'vertical' | 'horizontal';
37
45
  /** Component size */
38
46
  size?: 'default' | 'compact';
39
- /** Callback when selection changes */
40
- onChange?: (values: string[]) => void;
47
+ /** Called with the next selected values */
48
+ onValuesChange?: (values: string[]) => void;
41
49
  /** Additional CSS classes */
42
50
  className?: string;
51
+ /** @deprecated Use `onValuesChange` instead. */
52
+ onChange?: (values: string[]) => void;
53
+ };
54
+ export interface CheckboxGroupProps extends CheckboxGroupOwnProps, Omit<React.ComponentPropsWithoutRef<'div'>, keyof CheckboxGroupOwnProps> {
43
55
  }
44
- export declare const CheckboxGroup: ({ label, items, values, direction, size, onChange, className, }: CheckboxGroupProps) => React.JSX.Element;
45
- export declare const Checkbox: ({ label, checked, indeterminate, disabled, size, onChange, className, ariaLabel, }: CheckboxProps) => React.JSX.Element;
56
+ export declare const CheckboxGroup: React.ForwardRefExoticComponent<CheckboxGroupProps & React.RefAttributes<HTMLDivElement>>;
57
+ export {};
@@ -1,5 +1,5 @@
1
1
  import { jsxs, jsx } from "react/jsx-runtime";
2
- import "react";
2
+ import React from "react";
3
3
  import "./Checkbox.css";
4
4
  const CheckIcon = () => /* @__PURE__ */ jsx(
5
5
  "svg",
@@ -31,94 +31,108 @@ const MinusIcon = () => /* @__PURE__ */ jsx(
31
31
  viewBox: "0 0 14 14",
32
32
  fill: "none",
33
33
  "aria-hidden": "true",
34
- children: /* @__PURE__ */ jsx(
35
- "path",
34
+ children: /* @__PURE__ */ jsx("path", { d: "M2 7H12", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })
35
+ }
36
+ );
37
+ const Checkbox = React.forwardRef(
38
+ ({
39
+ label,
40
+ checked = false,
41
+ indeterminate = false,
42
+ disabled = false,
43
+ size = "default",
44
+ onCheckedChange,
45
+ onChange,
46
+ className = "",
47
+ ariaLabel,
48
+ name: _name,
49
+ onClick,
50
+ onKeyDown,
51
+ ...rest
52
+ }, ref) => {
53
+ const baseClass = "ds-checkbox";
54
+ const classes = [
55
+ baseClass,
56
+ size === "compact" ? `${baseClass}--compact` : "",
57
+ checked ? `${baseClass}--checked` : "",
58
+ indeterminate && !checked ? `${baseClass}--indeterminate` : "",
59
+ disabled ? `${baseClass}--disabled` : "",
60
+ className
61
+ ].filter(Boolean).join(" ");
62
+ const toggle = () => {
63
+ if (disabled) return;
64
+ onCheckedChange?.(!checked);
65
+ onChange?.(!checked);
66
+ };
67
+ const handleClick = (e) => {
68
+ onClick?.(e);
69
+ toggle();
70
+ };
71
+ const handleKeyDown = (e) => {
72
+ onKeyDown?.(e);
73
+ if (e.key === " " || e.key === "Enter") {
74
+ e.preventDefault();
75
+ toggle();
76
+ }
77
+ };
78
+ return /* @__PURE__ */ jsxs(
79
+ "div",
36
80
  {
37
- d: "M2 7H12",
38
- stroke: "currentColor",
39
- strokeWidth: "2",
40
- strokeLinecap: "round"
81
+ ...rest,
82
+ ref,
83
+ className: classes,
84
+ onClick: handleClick,
85
+ onKeyDown: handleKeyDown,
86
+ role: "checkbox",
87
+ "aria-checked": indeterminate && !checked ? "mixed" : checked,
88
+ "aria-disabled": disabled,
89
+ "aria-label": ariaLabel || rest["aria-label"] || label,
90
+ tabIndex: disabled ? -1 : 0,
91
+ children: [
92
+ /* @__PURE__ */ jsx("div", { className: `${baseClass}__box`, children: indeterminate && !checked ? /* @__PURE__ */ jsx(MinusIcon, {}) : /* @__PURE__ */ jsx(CheckIcon, {}) }),
93
+ label && /* @__PURE__ */ jsx("span", { className: `${baseClass}__label`, children: label })
94
+ ]
41
95
  }
42
- )
96
+ );
43
97
  }
44
98
  );
45
- const CheckboxGroup = ({
46
- label,
47
- items,
48
- values = [],
49
- direction = "vertical",
50
- size = "default",
51
- onChange,
52
- className = ""
53
- }) => {
54
- const baseClass = "ds-checkbox-group";
55
- const directionClass = `${baseClass}--${direction}`;
56
- const classes = [baseClass, directionClass, className].filter(Boolean).join(" ");
57
- const handleToggle = (itemValue) => {
58
- if (!onChange) return;
59
- const next = values.includes(itemValue) ? values.filter((v) => v !== itemValue) : [...values, itemValue];
60
- onChange(next);
61
- };
62
- return /* @__PURE__ */ jsxs("div", { className: classes, role: "group", "aria-label": label, children: [
63
- label && /* @__PURE__ */ jsx("span", { className: `${baseClass}__label`, children: label }),
64
- /* @__PURE__ */ jsx("div", { className: `${baseClass}__items`, children: items.map((item) => /* @__PURE__ */ jsx(
65
- Checkbox,
66
- {
67
- label: item.label,
68
- checked: values.includes(item.value),
69
- disabled: item.disabled,
70
- size,
71
- onChange: () => handleToggle(item.value)
72
- },
73
- item.value
74
- )) })
75
- ] });
76
- };
77
- const Checkbox = ({
78
- label,
79
- checked = false,
80
- indeterminate = false,
81
- disabled = false,
82
- size = "default",
83
- onChange,
84
- className = "",
85
- ariaLabel
86
- }) => {
87
- const baseClass = "ds-checkbox";
88
- const checkedClass = checked ? `${baseClass}--checked` : "";
89
- const indeterminateClass = indeterminate && !checked ? `${baseClass}--indeterminate` : "";
90
- const disabledClass = disabled ? `${baseClass}--disabled` : "";
91
- const sizeClass = size === "compact" ? `${baseClass}--compact` : "";
92
- const classes = [baseClass, sizeClass, checkedClass, indeterminateClass, disabledClass, className].filter(Boolean).join(" ");
93
- const handleClick = () => {
94
- if (!disabled && onChange) {
95
- onChange(!checked);
96
- }
97
- };
98
- const handleKeyDown = (e) => {
99
- if (e.key === " " || e.key === "Enter") {
100
- e.preventDefault();
101
- handleClick();
102
- }
103
- };
104
- return /* @__PURE__ */ jsxs(
105
- "div",
106
- {
107
- className: classes,
108
- onClick: handleClick,
109
- onKeyDown: handleKeyDown,
110
- role: "checkbox",
111
- "aria-checked": indeterminate && !checked ? "mixed" : checked,
112
- "aria-disabled": disabled,
113
- "aria-label": ariaLabel || label,
114
- tabIndex: disabled ? -1 : 0,
115
- children: [
116
- /* @__PURE__ */ jsx("div", { className: `${baseClass}__box`, children: indeterminate && !checked ? /* @__PURE__ */ jsx(MinusIcon, {}) : /* @__PURE__ */ jsx(CheckIcon, {}) }),
117
- label && /* @__PURE__ */ jsx("span", { className: `${baseClass}__label`, children: label })
118
- ]
119
- }
120
- );
121
- };
99
+ Checkbox.displayName = "Checkbox";
100
+ const CheckboxGroup = React.forwardRef(
101
+ ({
102
+ label,
103
+ items,
104
+ values = [],
105
+ direction = "vertical",
106
+ size = "default",
107
+ onValuesChange,
108
+ onChange,
109
+ className = "",
110
+ ...rest
111
+ }, ref) => {
112
+ const baseClass = "ds-checkbox-group";
113
+ const classes = [baseClass, `${baseClass}--${direction}`, className].filter(Boolean).join(" ");
114
+ const handleToggle = (itemValue) => {
115
+ const next = values.includes(itemValue) ? values.filter((v) => v !== itemValue) : [...values, itemValue];
116
+ onValuesChange?.(next);
117
+ onChange?.(next);
118
+ };
119
+ return /* @__PURE__ */ jsxs("div", { ...rest, ref, className: classes, role: "group", "aria-label": label, children: [
120
+ label && /* @__PURE__ */ jsx("span", { className: `${baseClass}__label`, children: label }),
121
+ /* @__PURE__ */ jsx("div", { className: `${baseClass}__items`, children: items.map((item) => /* @__PURE__ */ jsx(
122
+ Checkbox,
123
+ {
124
+ label: item.label,
125
+ checked: values.includes(item.value),
126
+ disabled: item.disabled,
127
+ size,
128
+ onCheckedChange: () => handleToggle(item.value)
129
+ },
130
+ item.value
131
+ )) })
132
+ ] });
133
+ }
134
+ );
135
+ CheckboxGroup.displayName = "CheckboxGroup";
122
136
  export {
123
137
  Checkbox,
124
138
  CheckboxGroup
@@ -196,6 +196,17 @@
196
196
  background-color: var(--color-action-passive-bg-active);
197
197
  }
198
198
 
199
+ /* ============================================
200
+ LOADING
201
+ Keeps the variant's full-colour appearance (no disabled dim);
202
+ the spinner replaces the icon and inherits its colour.
203
+ ============================================ */
204
+
205
+ .ds-circular-button--loading {
206
+ cursor: progress;
207
+ pointer-events: none;
208
+ }
209
+
199
210
  /* ============================================
200
211
  FOCUS
201
212
  ============================================ */
@@ -7,6 +7,12 @@ export interface CircularButtonProps {
7
7
  state?: 'default' | 'hover' | 'active' | 'disabled';
8
8
  /** Button size */
9
9
  size?: 'default' | 'compact';
10
+ /**
11
+ * Shows a spinner in place of the icon and blocks interaction while an
12
+ * async action runs. Keeps the variant's full-colour appearance (unlike
13
+ * the disabled state) and sets `aria-busy` on the rendered element.
14
+ */
15
+ loading?: boolean;
10
16
  /** Accessible label */
11
17
  ariaLabel: string;
12
18
  /** Optional click handler */
@@ -27,4 +33,4 @@ export interface CircularButtonProps {
27
33
  * A round button containing a single icon, available in
28
34
  * primary and secondary variants with default and compact sizes.
29
35
  */
30
- export declare const CircularButton: ({ icon, priority, state, size, ariaLabel, onClick, href, target, rel, "aria-describedby": ariaDescribedby, className, }: CircularButtonProps) => import("react").JSX.Element;
36
+ export declare const CircularButton: ({ icon, priority, state, size, loading, ariaLabel, onClick, href, target, rel, "aria-describedby": ariaDescribedby, className, }: CircularButtonProps) => import("react").JSX.Element;
@@ -1,4 +1,5 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
+ import { Spinner } from "../Spinner/Spinner.js";
2
3
  import "./CircularButton.css";
3
4
  import "../../fonts/material-symbols.css";
4
5
  const CircularButton = ({
@@ -6,6 +7,7 @@ const CircularButton = ({
6
7
  priority = "primary",
7
8
  state = "default",
8
9
  size = "default",
10
+ loading,
9
11
  ariaLabel,
10
12
  onClick,
11
13
  href,
@@ -18,10 +20,18 @@ const CircularButton = ({
18
20
  const variantClass = `${baseClass}--${priority}`;
19
21
  const stateClass = `${baseClass}--${state}`;
20
22
  const sizeClass = `${baseClass}--${size}`;
21
- const classes = [baseClass, variantClass, stateClass, sizeClass, className].filter(Boolean).join(" ");
23
+ const isLoading = Boolean(loading);
24
+ const classes = [
25
+ baseClass,
26
+ variantClass,
27
+ stateClass,
28
+ sizeClass,
29
+ isLoading && `${baseClass}--loading`,
30
+ className
31
+ ].filter(Boolean).join(" ");
22
32
  const isDisabled = state === "disabled";
23
- const children = /* @__PURE__ */ jsx("span", { className: `${baseClass}__icon material-symbols-rounded`, "aria-hidden": "true", children: icon });
24
- if (href && !isDisabled) {
33
+ const children = isLoading ? /* @__PURE__ */ jsx("span", { className: `${baseClass}__icon`, "aria-hidden": "true", children: /* @__PURE__ */ jsx(Spinner, { size: size === "compact" ? "sm" : "md", variant: "inherit" }) }) : /* @__PURE__ */ jsx("span", { className: `${baseClass}__icon material-symbols-rounded`, "aria-hidden": "true", children: icon });
34
+ if (href && !isDisabled && !isLoading) {
25
35
  return /* @__PURE__ */ jsx(
26
36
  "a",
27
37
  {
@@ -42,7 +52,8 @@ const CircularButton = ({
42
52
  type: "button",
43
53
  className: classes,
44
54
  onClick,
45
- disabled: isDisabled,
55
+ disabled: isDisabled || isLoading,
56
+ "aria-busy": isLoading || void 0,
46
57
  "aria-label": ariaLabel,
47
58
  "aria-describedby": ariaDescribedby,
48
59
  children
@@ -77,7 +77,16 @@ const CodeBlock = ({
77
77
  }
78
78
  )
79
79
  ] }),
80
- /* @__PURE__ */ jsx("div", { id: panelId, className: `${baseClass}__panel`, "aria-hidden": collapsed || void 0, children: /* @__PURE__ */ jsx("div", { className: `${baseClass}__panel-inner`, children: /* @__PURE__ */ jsx("pre", { className: `${baseClass}__pre`, children: /* @__PURE__ */ jsx("code", { className: `${baseClass}__code`, children: code }) }) }) })
80
+ /* @__PURE__ */ jsx("div", { id: panelId, className: `${baseClass}__panel`, "aria-hidden": collapsed || void 0, children: /* @__PURE__ */ jsx("div", { className: `${baseClass}__panel-inner`, children: /* @__PURE__ */ jsx(
81
+ "pre",
82
+ {
83
+ className: `${baseClass}__pre`,
84
+ tabIndex: collapsed ? -1 : 0,
85
+ role: "region",
86
+ "aria-label": filename ? `Code: ${filename}` : language ? `Code: ${language}` : "Code",
87
+ children: /* @__PURE__ */ jsx("code", { className: `${baseClass}__code`, children: code })
88
+ }
89
+ ) }) })
81
90
  ] });
82
91
  };
83
92
  export {
@@ -0,0 +1,278 @@
1
+ /* ============================================
2
+ COLORPICKER COMPONENT
3
+ Swatch trigger + saturation/hue/alpha panel
4
+
5
+ Colour-mixing constants: the white/black
6
+ gradient overlays and the hue spectrum are
7
+ colour-space physics, not theme decisions —
8
+ they are written as hsl() literals because no
9
+ semantic token can represent them. Everything
10
+ themeable uses tokens as usual.
11
+
12
+ ds-allow-file(color): colour-space physics —
13
+ the gradients and spectrum cannot be theme
14
+ tokens.
15
+ ============================================ */
16
+
17
+ /* Base */
18
+
19
+ .ds-colorpicker {
20
+ position: relative;
21
+ /* Field owns no layout — the root class stacks label, trigger and helper */
22
+ display: inline-flex;
23
+ flex-direction: column;
24
+ align-items: flex-start;
25
+ gap: var(--gap-sm);
26
+ }
27
+
28
+ /* Trigger */
29
+
30
+ .ds-colorpicker__trigger {
31
+ display: inline-flex;
32
+ align-items: center;
33
+ gap: var(--gap-sm);
34
+ padding: var(--padding-xxs);
35
+ border: var(--border-xs) solid var(--color-input-border-primary);
36
+ border-radius: var(--radius-md);
37
+ background-color: transparent;
38
+ cursor: pointer;
39
+ transition: border-color var(--motion-duration-base) var(--motion-ease-standard);
40
+ }
41
+
42
+ .ds-colorpicker__trigger:hover:not(:disabled) {
43
+ border-color: var(--color-input-border-hover);
44
+ }
45
+
46
+ .ds-colorpicker__trigger:focus-visible {
47
+ outline: none;
48
+ border-color: var(--color-input-border-selected);
49
+ }
50
+
51
+ .ds-colorpicker--open .ds-colorpicker__trigger {
52
+ border-color: var(--color-input-border-selected);
53
+ }
54
+
55
+ .ds-colorpicker__trigger-swatch {
56
+ width: 24px;
57
+ height: 24px;
58
+ border-radius: var(--radius-sm);
59
+ /* Checkerboard shows through translucent colours */
60
+ background-color: var(--color-bg-page-primary);
61
+ background-image:
62
+ linear-gradient(var(--ds-cp-color), var(--ds-cp-color)),
63
+ repeating-conic-gradient(var(--color-bg-container-tertiary) 0% 25%, transparent 0% 50%);
64
+ background-size: 100% 100%, 8px 8px;
65
+ box-shadow: inset 0 0 0 var(--border-xs) var(--color-bg-container-border);
66
+ flex-shrink: 0;
67
+ }
68
+
69
+ .ds-colorpicker__trigger-text {
70
+ font-family: var(--font-paragraph-sm-family);
71
+ font-size: var(--font-paragraph-sm-size);
72
+ line-height: var(--font-paragraph-sm-line-height);
73
+ color: var(--color-text-primary);
74
+ font-variant-numeric: tabular-nums;
75
+ padding-right: var(--padding-xxs);
76
+ }
77
+
78
+ /* Sizes */
79
+
80
+ .ds-colorpicker--compact .ds-colorpicker__trigger-swatch {
81
+ width: 20px;
82
+ height: 20px;
83
+ }
84
+
85
+ /* Panel */
86
+
87
+ .ds-colorpicker__panel {
88
+ position: absolute;
89
+ top: 100%;
90
+ left: 0;
91
+ margin-top: var(--gap-xxs);
92
+ width: 240px;
93
+ padding: var(--padding-sm-md);
94
+ display: flex;
95
+ flex-direction: column;
96
+ gap: var(--gap-sm-md);
97
+ background-color: var(--color-bg-page-primary);
98
+ border: var(--border-xs) solid var(--color-input-border-primary);
99
+ border-radius: var(--radius-md);
100
+ box-shadow: var(--shadow-floating);
101
+ z-index: 10;
102
+ }
103
+
104
+ /* Saturation / brightness area */
105
+
106
+ .ds-colorpicker__saturation {
107
+ position: relative;
108
+ height: 150px;
109
+ border-radius: var(--radius-sm);
110
+ cursor: crosshair;
111
+ touch-action: none;
112
+ background:
113
+ linear-gradient(to top, hsl(0 0% 0%), transparent),
114
+ linear-gradient(to right, hsl(0 0% 100%), transparent),
115
+ hsl(var(--ds-cp-hue) 100% 50%);
116
+ }
117
+
118
+ .ds-colorpicker__saturation:focus-visible {
119
+ outline: var(--border-md) solid var(--color-input-border-selected);
120
+ outline-offset: var(--border-md);
121
+ }
122
+
123
+ .ds-colorpicker__handle {
124
+ position: absolute;
125
+ left: var(--ds-cp-x);
126
+ top: var(--ds-cp-y);
127
+ transform: translate(-50%, -50%);
128
+ width: 14px;
129
+ height: 14px;
130
+ border-radius: var(--radius-full);
131
+ border: var(--border-md) solid hsl(0 0% 100%);
132
+ box-shadow: var(--shadow-floating);
133
+ pointer-events: none;
134
+ }
135
+
136
+ /* Sliders + preview */
137
+
138
+ .ds-colorpicker__controls {
139
+ display: flex;
140
+ align-items: center;
141
+ gap: var(--gap-sm-md);
142
+ }
143
+
144
+ .ds-colorpicker__sliders {
145
+ flex: 1;
146
+ display: flex;
147
+ flex-direction: column;
148
+ gap: var(--gap-sm);
149
+ min-width: 0;
150
+ }
151
+
152
+ .ds-colorpicker__hue,
153
+ .ds-colorpicker__alpha {
154
+ appearance: none;
155
+ -webkit-appearance: none;
156
+ width: 100%;
157
+ height: 12px;
158
+ border-radius: var(--radius-full);
159
+ outline: none;
160
+ cursor: pointer;
161
+ }
162
+
163
+ .ds-colorpicker__hue {
164
+ background: linear-gradient(
165
+ to right,
166
+ hsl(0 100% 50%),
167
+ hsl(60 100% 50%),
168
+ hsl(120 100% 50%),
169
+ hsl(180 100% 50%),
170
+ hsl(240 100% 50%),
171
+ hsl(300 100% 50%),
172
+ hsl(360 100% 50%)
173
+ );
174
+ }
175
+
176
+ .ds-colorpicker__alpha {
177
+ background-color: var(--color-bg-page-primary);
178
+ background-image:
179
+ linear-gradient(to right, transparent, var(--ds-cp-solid)),
180
+ repeating-conic-gradient(var(--color-bg-container-tertiary) 0% 25%, transparent 0% 50%);
181
+ background-size: 100% 100%, 8px 8px;
182
+ }
183
+
184
+ .ds-colorpicker__hue::-webkit-slider-thumb,
185
+ .ds-colorpicker__alpha::-webkit-slider-thumb {
186
+ appearance: none;
187
+ -webkit-appearance: none;
188
+ width: 16px;
189
+ height: 16px;
190
+ border-radius: var(--radius-full);
191
+ background: hsl(0 0% 100%);
192
+ border: var(--border-xs) solid var(--color-bg-container-border);
193
+ box-shadow: var(--shadow-floating);
194
+ }
195
+
196
+ .ds-colorpicker__hue::-moz-range-thumb,
197
+ .ds-colorpicker__alpha::-moz-range-thumb {
198
+ width: 16px;
199
+ height: 16px;
200
+ border-radius: var(--radius-full);
201
+ background: hsl(0 0% 100%);
202
+ border: var(--border-xs) solid var(--color-bg-container-border);
203
+ box-shadow: var(--shadow-floating);
204
+ }
205
+
206
+ .ds-colorpicker__hue:focus-visible,
207
+ .ds-colorpicker__alpha:focus-visible {
208
+ outline: var(--border-md) solid var(--color-input-border-selected);
209
+ outline-offset: var(--border-md);
210
+ }
211
+
212
+ .ds-colorpicker__preview {
213
+ width: 32px;
214
+ height: 32px;
215
+ border-radius: var(--radius-sm);
216
+ background-color: var(--color-bg-page-primary);
217
+ background-image:
218
+ linear-gradient(var(--ds-cp-color), var(--ds-cp-color)),
219
+ repeating-conic-gradient(var(--color-bg-container-tertiary) 0% 25%, transparent 0% 50%);
220
+ background-size: 100% 100%, 8px 8px;
221
+ box-shadow: inset 0 0 0 var(--border-xs) var(--color-bg-container-border);
222
+ flex-shrink: 0;
223
+ }
224
+
225
+ /* Hex + alpha inputs */
226
+
227
+ .ds-colorpicker__inputs {
228
+ display: flex;
229
+ align-items: center;
230
+ gap: var(--gap-sm);
231
+ padding: var(--padding-xxs) var(--padding-sm);
232
+ border: var(--border-xs) solid var(--color-input-border-primary);
233
+ border-radius: var(--radius-md);
234
+ transition: border-color var(--motion-duration-base) var(--motion-ease-standard);
235
+ }
236
+
237
+ .ds-colorpicker__inputs:focus-within {
238
+ border-color: var(--color-input-border-selected);
239
+ }
240
+
241
+ .ds-colorpicker__hash {
242
+ font-family: var(--font-paragraph-sm-family);
243
+ font-size: var(--font-paragraph-sm-size);
244
+ color: var(--color-text-tertiary);
245
+ }
246
+
247
+ .ds-colorpicker__hex-field {
248
+ flex: 1;
249
+ min-width: 0;
250
+ border: none;
251
+ background: transparent;
252
+ outline: none;
253
+ padding: 0;
254
+ font-family: var(--font-paragraph-sm-family);
255
+ font-size: var(--font-paragraph-sm-size);
256
+ line-height: var(--font-paragraph-sm-line-height);
257
+ color: var(--color-text-primary);
258
+ text-transform: uppercase;
259
+ font-variant-numeric: tabular-nums;
260
+ }
261
+
262
+ .ds-colorpicker__alpha-value {
263
+ font-family: var(--font-paragraph-sm-family);
264
+ font-size: var(--font-paragraph-sm-size);
265
+ color: var(--color-text-secondary);
266
+ font-variant-numeric: tabular-nums;
267
+ }
268
+
269
+ /* States */
270
+
271
+ .ds-colorpicker--disabled .ds-colorpicker__trigger {
272
+ opacity: 0.4;
273
+ cursor: not-allowed;
274
+ }
275
+
276
+ .ds-colorpicker--error .ds-colorpicker__trigger {
277
+ border-color: var(--color-status-error-border);
278
+ }