@fragments-sdk/ui 0.16.1 → 0.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/ui.css +2021 -1606
- package/dist/components/Accordion/Accordion.module.scss.cjs +8 -8
- package/dist/components/Accordion/Accordion.module.scss.js +8 -8
- package/dist/components/Alert/Alert.module.scss.cjs +12 -12
- package/dist/components/Alert/Alert.module.scss.js +12 -12
- package/dist/components/Alert/index.cjs +2 -1
- package/dist/components/Alert/index.d.ts.map +1 -1
- package/dist/components/Alert/index.js +2 -1
- package/dist/components/Avatar/index.cjs +12 -4
- package/dist/components/Avatar/index.d.ts.map +1 -1
- package/dist/components/Avatar/index.js +12 -4
- package/dist/components/Badge/Badge.module.scss.cjs +13 -13
- package/dist/components/Badge/Badge.module.scss.js +13 -13
- package/dist/components/Button/Button.module.scss.cjs +11 -11
- package/dist/components/Button/Button.module.scss.js +11 -11
- package/dist/components/Button/index.cjs +51 -4
- package/dist/components/Button/index.d.ts.map +1 -1
- package/dist/components/Button/index.js +51 -4
- package/dist/components/Card/Card.module.scss.cjs +14 -14
- package/dist/components/Card/Card.module.scss.js +14 -14
- package/dist/components/Card/index.cjs +35 -2
- package/dist/components/Card/index.d.ts.map +1 -1
- package/dist/components/Card/index.js +35 -2
- package/dist/components/Checkbox/Checkbox.module.scss.cjs +10 -10
- package/dist/components/Checkbox/Checkbox.module.scss.js +10 -10
- package/dist/components/Chip/Chip.module.scss.cjs +15 -15
- package/dist/components/Chip/Chip.module.scss.js +15 -15
- package/dist/components/CodeBlock/CodeBlock.module.scss.cjs +21 -21
- package/dist/components/CodeBlock/CodeBlock.module.scss.js +21 -21
- package/dist/components/Collapsible/Collapsible.module.scss.cjs +10 -10
- package/dist/components/Collapsible/Collapsible.module.scss.js +10 -10
- package/dist/components/ColorPicker/ColorPicker.module.scss.cjs +14 -14
- package/dist/components/ColorPicker/ColorPicker.module.scss.js +14 -14
- package/dist/components/Combobox/Combobox.module.scss.cjs +24 -24
- package/dist/components/Combobox/Combobox.module.scss.js +24 -24
- package/dist/components/DataTable/DataTable.module.scss.cjs +26 -26
- package/dist/components/DataTable/DataTable.module.scss.js +26 -26
- package/dist/components/DatePicker/DatePicker.module.scss.cjs +31 -31
- package/dist/components/DatePicker/DatePicker.module.scss.js +31 -31
- package/dist/components/Dialog/Dialog.module.scss.cjs +14 -14
- package/dist/components/Dialog/Dialog.module.scss.js +14 -14
- package/dist/components/Drawer/Drawer.module.scss.cjs +26 -26
- package/dist/components/Drawer/Drawer.module.scss.js +26 -26
- package/dist/components/Editor/Editor.module.scss.cjs +17 -17
- package/dist/components/Editor/Editor.module.scss.js +17 -17
- package/dist/components/Fieldset/Fieldset.module.scss.cjs +6 -3
- package/dist/components/Fieldset/Fieldset.module.scss.js +6 -3
- package/dist/components/Fieldset/index.cjs +7 -1
- package/dist/components/Fieldset/index.d.ts +6 -1
- package/dist/components/Fieldset/index.d.ts.map +1 -1
- package/dist/components/Fieldset/index.js +7 -1
- package/dist/components/Header/Header.module.scss.cjs +42 -21
- package/dist/components/Header/Header.module.scss.js +42 -21
- package/dist/components/Header/index.cjs +121 -3
- package/dist/components/Header/index.d.ts +26 -3
- package/dist/components/Header/index.d.ts.map +1 -1
- package/dist/components/Header/index.js +122 -4
- package/dist/components/Input/Input.module.scss.cjs +27 -15
- package/dist/components/Input/Input.module.scss.js +27 -15
- package/dist/components/Input/index.cjs +20 -7
- package/dist/components/Input/index.d.ts +8 -2
- package/dist/components/Input/index.d.ts.map +1 -1
- package/dist/components/Input/index.js +20 -7
- package/dist/components/Link/Link.module.scss.cjs +10 -10
- package/dist/components/Link/Link.module.scss.js +10 -10
- package/dist/components/Listbox/Listbox.module.scss.cjs +8 -8
- package/dist/components/Listbox/Listbox.module.scss.js +8 -8
- package/dist/components/NavigationMenu/NavigationMenu.module.scss.cjs +28 -28
- package/dist/components/NavigationMenu/NavigationMenu.module.scss.js +28 -28
- package/dist/components/Pagination/Pagination.module.scss.cjs +7 -7
- package/dist/components/Pagination/Pagination.module.scss.js +7 -7
- package/dist/components/Popover/Popover.module.scss.cjs +10 -10
- package/dist/components/Popover/Popover.module.scss.js +10 -10
- package/dist/components/Prompt/Prompt.module.scss.cjs +14 -14
- package/dist/components/Prompt/Prompt.module.scss.js +14 -14
- package/dist/components/RadioGroup/RadioGroup.module.scss.cjs +16 -16
- package/dist/components/RadioGroup/RadioGroup.module.scss.js +16 -16
- package/dist/components/Select/Select.module.scss.cjs +17 -17
- package/dist/components/Select/Select.module.scss.js +17 -17
- package/dist/components/Sidebar/Sidebar.module.scss.cjs +42 -42
- package/dist/components/Sidebar/Sidebar.module.scss.js +42 -42
- package/dist/components/Slider/Slider.module.scss.cjs +12 -12
- package/dist/components/Slider/Slider.module.scss.js +12 -12
- package/dist/components/Tabs/Tabs.module.scss.cjs +9 -9
- package/dist/components/Tabs/Tabs.module.scss.js +9 -9
- package/dist/components/Textarea/Textarea.module.scss.cjs +34 -19
- package/dist/components/Textarea/Textarea.module.scss.js +34 -19
- package/dist/components/Textarea/index.cjs +36 -6
- package/dist/components/Textarea/index.d.ts +6 -2
- package/dist/components/Textarea/index.d.ts.map +1 -1
- package/dist/components/Textarea/index.js +36 -6
- package/dist/components/Theme/ThemeToggle.module.scss.cjs +6 -6
- package/dist/components/Theme/ThemeToggle.module.scss.js +6 -6
- package/dist/components/Toast/Toast.module.scss.cjs +22 -22
- package/dist/components/Toast/Toast.module.scss.js +22 -22
- package/dist/components/Toggle/Toggle.module.scss.cjs +13 -13
- package/dist/components/Toggle/Toggle.module.scss.js +13 -13
- package/dist/components/Toggle/index.cjs +7 -3
- package/dist/components/Toggle/index.d.ts.map +1 -1
- package/dist/components/Toggle/index.js +7 -3
- package/dist/components/ToggleGroup/ToggleGroup.module.scss.cjs +17 -17
- package/dist/components/ToggleGroup/ToggleGroup.module.scss.js +17 -17
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/fragments.json +1 -1
- package/package.json +1 -1
- package/src/components/Alert/Alert.module.scss +8 -8
- package/src/components/Alert/index.tsx +2 -1
- package/src/components/Avatar/index.tsx +6 -2
- package/src/components/Badge/Badge.module.scss +17 -11
- package/src/components/Button/Button.module.scss +6 -5
- package/src/components/Button/index.tsx +60 -4
- package/src/components/Card/index.tsx +48 -3
- package/src/components/Checkbox/Checkbox.module.scss +16 -9
- package/src/components/ColorPicker/ColorPicker.module.scss +5 -13
- package/src/components/Combobox/Combobox.module.scss +30 -25
- package/src/components/DatePicker/DatePicker.module.scss +18 -28
- package/src/components/Editor/Editor.module.scss +23 -15
- package/src/components/Fieldset/Fieldset.module.scss +12 -6
- package/src/components/Fieldset/index.tsx +11 -1
- package/src/components/Header/Header.module.scss +99 -0
- package/src/components/Header/index.tsx +191 -10
- package/src/components/Input/Input.module.scss +97 -26
- package/src/components/Input/index.tsx +31 -12
- package/src/components/Listbox/Listbox.module.scss +9 -2
- package/src/components/RadioGroup/RadioGroup.module.scss +8 -6
- package/src/components/Select/Select.module.scss +10 -24
- package/src/components/Sidebar/Sidebar.module.scss +6 -4
- package/src/components/Slider/Slider.module.scss +12 -22
- package/src/components/Textarea/Textarea.module.scss +49 -18
- package/src/components/Textarea/index.tsx +43 -12
- package/src/components/Toast/Toast.module.scss +48 -8
- package/src/components/Toggle/Toggle.module.scss +21 -35
- package/src/components/Toggle/index.tsx +11 -3
- package/src/components/ToggleGroup/ToggleGroup.module.scss +23 -19
- package/src/index.ts +2 -0
- package/src/styles/globals.scss +4 -1
- package/src/tokens/_mixins.scss +57 -4
- package/src/tokens/_variables.scss +20 -1
|
@@ -27,6 +27,10 @@ export interface TextareaProps extends Omit<
|
|
|
27
27
|
disabled?: boolean;
|
|
28
28
|
/** Error state */
|
|
29
29
|
error?: boolean;
|
|
30
|
+
/** Success state */
|
|
31
|
+
success?: boolean;
|
|
32
|
+
/** Show character count when maxLength is set */
|
|
33
|
+
showCharCount?: boolean;
|
|
30
34
|
/** Label text above the textarea */
|
|
31
35
|
label?: string;
|
|
32
36
|
/** Helper text below the textarea */
|
|
@@ -36,9 +40,9 @@ export interface TextareaProps extends Omit<
|
|
|
36
40
|
/** Alias for onChange (value-first callback) */
|
|
37
41
|
onValueChange?: (value: string) => void;
|
|
38
42
|
/** Called when textarea loses focus */
|
|
39
|
-
onBlur?:
|
|
43
|
+
onBlur?: React.FocusEventHandler<HTMLTextAreaElement>;
|
|
40
44
|
/** Called when textarea receives focus */
|
|
41
|
-
onFocus?:
|
|
45
|
+
onFocus?: React.FocusEventHandler<HTMLTextAreaElement>;
|
|
42
46
|
/** Form field name */
|
|
43
47
|
name?: string;
|
|
44
48
|
/** Maximum character length */
|
|
@@ -72,6 +76,8 @@ const TextareaRoot = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
72
76
|
size = 'md',
|
|
73
77
|
disabled = false,
|
|
74
78
|
error = false,
|
|
79
|
+
success = false,
|
|
80
|
+
showCharCount = false,
|
|
75
81
|
label,
|
|
76
82
|
helperText,
|
|
77
83
|
onChange,
|
|
@@ -100,16 +106,25 @@ const TextareaRoot = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
100
106
|
const labelId = label ? `${textareaId}-label` : undefined;
|
|
101
107
|
const helperId = `${textareaId}-helper`;
|
|
102
108
|
|
|
109
|
+
const [charCount, setCharCount] = React.useState(() =>
|
|
110
|
+
(value ?? defaultValue ?? '').length
|
|
111
|
+
);
|
|
112
|
+
|
|
103
113
|
const textareaClasses = [
|
|
104
114
|
styles.textarea,
|
|
105
115
|
styles[size],
|
|
106
116
|
error && styles.error,
|
|
117
|
+
success && styles.success,
|
|
107
118
|
styles[`resize-${resize}`],
|
|
108
119
|
]
|
|
109
120
|
.filter(Boolean)
|
|
110
121
|
.join(' ');
|
|
111
122
|
|
|
112
|
-
const helperClasses = [
|
|
123
|
+
const helperClasses = [
|
|
124
|
+
styles.helper,
|
|
125
|
+
error && styles.helperError,
|
|
126
|
+
success && styles.helperSuccess,
|
|
127
|
+
]
|
|
113
128
|
.filter(Boolean)
|
|
114
129
|
.join(' ');
|
|
115
130
|
|
|
@@ -125,6 +140,7 @@ const TextareaRoot = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
125
140
|
return (
|
|
126
141
|
<div
|
|
127
142
|
{...rootProps}
|
|
143
|
+
data-success={success || undefined}
|
|
128
144
|
className={[styles.wrapper, rootProps?.className, className].filter(Boolean).join(' ')}
|
|
129
145
|
style={{ ...(rootProps?.style ?? {}), ...(wrapperStyle ?? {}) }}
|
|
130
146
|
>
|
|
@@ -154,19 +170,34 @@ const TextareaRoot = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
154
170
|
helperText ? helperId : undefined
|
|
155
171
|
)}
|
|
156
172
|
onChange={(e) => {
|
|
157
|
-
|
|
158
|
-
|
|
173
|
+
const val = e.target.value;
|
|
174
|
+
setCharCount(val.length);
|
|
175
|
+
onChange?.(val);
|
|
176
|
+
onValueChange?.(val);
|
|
159
177
|
}}
|
|
160
|
-
onBlur={() => onBlur?.()}
|
|
161
|
-
onFocus={() => onFocus?.()}
|
|
178
|
+
onBlur={(e) => onBlur?.(e)}
|
|
179
|
+
onFocus={(e) => onFocus?.(e)}
|
|
162
180
|
className={textareaClasses}
|
|
163
181
|
style={Object.keys(textareaInlineStyle).length > 0 ? textareaInlineStyle : undefined}
|
|
164
182
|
/>
|
|
165
|
-
{
|
|
166
|
-
|
|
167
|
-
{
|
|
168
|
-
|
|
169
|
-
|
|
183
|
+
<div className={styles.footer}>
|
|
184
|
+
{helperText && (
|
|
185
|
+
<span id={helperId} className={helperClasses}>
|
|
186
|
+
{helperText}
|
|
187
|
+
</span>
|
|
188
|
+
)}
|
|
189
|
+
{showCharCount && maxLength != null && (
|
|
190
|
+
<span
|
|
191
|
+
className={[
|
|
192
|
+
styles.charCount,
|
|
193
|
+
charCount > maxLength && styles.charCountOver,
|
|
194
|
+
].filter(Boolean).join(' ')}
|
|
195
|
+
aria-live="polite"
|
|
196
|
+
>
|
|
197
|
+
{charCount}/{maxLength}
|
|
198
|
+
</span>
|
|
199
|
+
)}
|
|
200
|
+
</div>
|
|
170
201
|
</div>
|
|
171
202
|
);
|
|
172
203
|
}
|
|
@@ -97,34 +97,74 @@
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
.success {
|
|
100
|
-
|
|
100
|
+
background: color-mix(in srgb, var(--fui-color-success, #{$fui-color-success}) 15%, var(--fui-bg-elevated, #{$fui-bg-elevated}));
|
|
101
|
+
border-color: color-mix(in srgb, var(--fui-color-success, #{$fui-color-success}) 40%, transparent);
|
|
102
|
+
|
|
103
|
+
.title {
|
|
104
|
+
color: var(--fui-color-success-text, $fui-color-success-text);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.description {
|
|
108
|
+
color: var(--fui-color-success-text, $fui-color-success-text);
|
|
109
|
+
opacity: 0.8;
|
|
110
|
+
}
|
|
101
111
|
|
|
102
112
|
.icon {
|
|
103
|
-
color: var(--fui-color-success, $fui-color-success);
|
|
113
|
+
color: var(--fui-color-success-text, $fui-color-success-text);
|
|
104
114
|
}
|
|
105
115
|
}
|
|
106
116
|
|
|
107
117
|
.error {
|
|
108
|
-
|
|
118
|
+
background: color-mix(in srgb, var(--fui-color-danger, #{$fui-color-danger}) 15%, var(--fui-bg-elevated, #{$fui-bg-elevated}));
|
|
119
|
+
border-color: color-mix(in srgb, var(--fui-color-danger, #{$fui-color-danger}) 40%, transparent);
|
|
120
|
+
|
|
121
|
+
.title {
|
|
122
|
+
color: var(--fui-color-danger-text, $fui-color-danger-text);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.description {
|
|
126
|
+
color: var(--fui-color-danger-text, $fui-color-danger-text);
|
|
127
|
+
opacity: 0.8;
|
|
128
|
+
}
|
|
109
129
|
|
|
110
130
|
.icon {
|
|
111
|
-
color: var(--fui-color-danger, $fui-color-danger);
|
|
131
|
+
color: var(--fui-color-danger-text, $fui-color-danger-text);
|
|
112
132
|
}
|
|
113
133
|
}
|
|
114
134
|
|
|
115
135
|
.warning {
|
|
116
|
-
|
|
136
|
+
background: color-mix(in srgb, var(--fui-color-warning, #{$fui-color-warning}) 15%, var(--fui-bg-elevated, #{$fui-bg-elevated}));
|
|
137
|
+
border-color: color-mix(in srgb, var(--fui-color-warning, #{$fui-color-warning}) 40%, transparent);
|
|
138
|
+
|
|
139
|
+
.title {
|
|
140
|
+
color: var(--fui-color-warning-text, $fui-color-warning-text);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.description {
|
|
144
|
+
color: var(--fui-color-warning-text, $fui-color-warning-text);
|
|
145
|
+
opacity: 0.8;
|
|
146
|
+
}
|
|
117
147
|
|
|
118
148
|
.icon {
|
|
119
|
-
color: var(--fui-color-warning, $fui-color-warning);
|
|
149
|
+
color: var(--fui-color-warning-text, $fui-color-warning-text);
|
|
120
150
|
}
|
|
121
151
|
}
|
|
122
152
|
|
|
123
153
|
.info {
|
|
124
|
-
|
|
154
|
+
background: color-mix(in srgb, var(--fui-color-info, #{$fui-color-info}) 15%, var(--fui-bg-elevated, #{$fui-bg-elevated}));
|
|
155
|
+
border-color: color-mix(in srgb, var(--fui-color-info, #{$fui-color-info}) 40%, transparent);
|
|
156
|
+
|
|
157
|
+
.title {
|
|
158
|
+
color: var(--fui-color-info-text, $fui-color-info-text);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.description {
|
|
162
|
+
color: var(--fui-color-info-text, $fui-color-info-text);
|
|
163
|
+
opacity: 0.8;
|
|
164
|
+
}
|
|
125
165
|
|
|
126
166
|
.icon {
|
|
127
|
-
color: var(--fui-color-info, $fui-color-info);
|
|
167
|
+
color: var(--fui-color-info-text, $fui-color-info-text);
|
|
128
168
|
}
|
|
129
169
|
}
|
|
130
170
|
|
|
@@ -29,32 +29,31 @@
|
|
|
29
29
|
display: inline-flex;
|
|
30
30
|
align-items: center;
|
|
31
31
|
flex-shrink: 0;
|
|
32
|
-
box-sizing:
|
|
32
|
+
box-sizing: border-box;
|
|
33
33
|
border-radius: var(--fui-radius-full, $fui-radius-full);
|
|
34
|
-
border:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
border: 1px solid var(--fui-field-border, $fui-border-strong);
|
|
35
|
+
background-color: var(--fui-field-bg, $fui-bg-elevated);
|
|
36
|
+
box-shadow: inset 0 1px 0 color-mix(in srgb, var(--fui-bg-primary, $fui-bg-primary) 45%, transparent);
|
|
37
|
+
transition:
|
|
38
|
+
background-color var(--fui-transition-normal, $fui-transition-normal),
|
|
39
|
+
border-color var(--fui-transition-normal, $fui-transition-normal),
|
|
40
|
+
box-shadow var(--fui-transition-normal, $fui-transition-normal);
|
|
39
41
|
|
|
40
|
-
// Checked: accent fill
|
|
41
42
|
.root[data-checked] & {
|
|
42
|
-
background-color: var(--fui-
|
|
43
|
+
background-color: var(--fui-field-selection-bg, $fui-bg-hover);
|
|
44
|
+
border-color: var(--fui-field-selection-border, $fui-color-accent);
|
|
43
45
|
}
|
|
44
46
|
|
|
45
|
-
// Hover (unchecked): slightly more opaque
|
|
46
47
|
.root:not([data-disabled]):not([data-checked]) &:hover {
|
|
47
|
-
|
|
48
|
+
border-color: var(--fui-field-border-hover, $fui-text-tertiary);
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
// Hover (checked): accent hover
|
|
51
51
|
.root:not([data-disabled])[data-checked] &:hover {
|
|
52
|
-
background-color: var(--fui-
|
|
52
|
+
background-color: var(--fui-field-selection-bg-hover, $fui-bg-hover);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
// Focus ring (consistent box-shadow approach across all form controls)
|
|
56
55
|
.root:focus-visible & {
|
|
57
|
-
@include focus
|
|
56
|
+
@include field-shell-focus;
|
|
58
57
|
}
|
|
59
58
|
}
|
|
60
59
|
|
|
@@ -97,10 +96,6 @@
|
|
|
97
96
|
);
|
|
98
97
|
}
|
|
99
98
|
|
|
100
|
-
// iOS-style thumb: always white, multi-layer shadow.
|
|
101
|
-
// The thumb is intentionally always white (#fff) to match iOS toggle UX — this is
|
|
102
|
-
// a hardcoded design decision (like CodeBlock/Tooltip dark surfaces). Override via
|
|
103
|
-
// --fui-toggle-thumb-bg and --fui-toggle-thumb-shadow CSS custom properties.
|
|
104
99
|
.thumb {
|
|
105
100
|
position: absolute;
|
|
106
101
|
top: var(--_toggle-inset, $fui-toggle-thumb-offset);
|
|
@@ -110,32 +105,23 @@
|
|
|
110
105
|
height: var(--_toggle-thumb-size, $fui-toggle-thumb-md);
|
|
111
106
|
border-radius: 50%;
|
|
112
107
|
pointer-events: none;
|
|
113
|
-
background-color: var(--fui-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
0 3px 8px rgba(0, 0, 0, 0.15),
|
|
117
|
-
0 1px 1px rgba(0, 0, 0, 0.06),
|
|
118
|
-
0 3px 1px rgba(0, 0, 0, 0.06)
|
|
119
|
-
);
|
|
108
|
+
background-color: var(--fui-bg-primary, $fui-bg-primary);
|
|
109
|
+
border: 1px solid color-mix(in srgb, var(--fui-border-strong, $fui-border-strong) 55%, transparent);
|
|
110
|
+
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.12);
|
|
120
111
|
transition:
|
|
121
112
|
transform var(--fui-transition-normal, $fui-transition-normal),
|
|
122
113
|
background-color var(--fui-transition-normal, $fui-transition-normal),
|
|
123
|
-
|
|
114
|
+
border-color var(--fui-transition-normal, $fui-transition-normal),
|
|
115
|
+
box-shadow var(--fui-transition-fast, $fui-transition-fast);
|
|
124
116
|
|
|
125
|
-
// Slide right when checked — inverse color for contrast against accent
|
|
126
117
|
.root[data-checked] & {
|
|
127
118
|
transform: translateX(var(--_toggle-translate, 0));
|
|
128
|
-
background-color: var(--fui-
|
|
119
|
+
background-color: var(--fui-color-accent, $fui-color-accent);
|
|
120
|
+
border-color: color-mix(in srgb, var(--fui-color-accent, $fui-color-accent) 60%, transparent);
|
|
129
121
|
}
|
|
130
122
|
|
|
131
|
-
// Press: stretch thumb wider (iOS 17+)
|
|
132
123
|
.root:active:not([data-disabled]) & {
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Press + checked: compensate translate for wider thumb
|
|
137
|
-
.root:active:not([data-disabled])[data-checked] & {
|
|
138
|
-
transform: translateX(calc(var(--_toggle-translate, 0) - 4px));
|
|
124
|
+
box-shadow: 0 2px 7px rgba(0, 0, 0, 0.16);
|
|
139
125
|
}
|
|
140
126
|
}
|
|
141
127
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
import { Switch as BaseSwitch } from '@base-ui/react/switch';
|
|
5
|
+
import { mergeAriaIds } from '../../utils/aria';
|
|
5
6
|
import styles from './Toggle.module.scss';
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -56,6 +57,9 @@ const SwitchRoot = React.forwardRef<HTMLButtonElement, SwitchProps>(
|
|
|
56
57
|
ref
|
|
57
58
|
) {
|
|
58
59
|
const resolvedHelperText = helperText ?? description;
|
|
60
|
+
const generatedId = React.useId();
|
|
61
|
+
const resolvedId = id ?? `switch-${generatedId}`;
|
|
62
|
+
const helperId = resolvedHelperText ? `${resolvedId}-helper` : undefined;
|
|
59
63
|
const trackClasses = [
|
|
60
64
|
styles.track,
|
|
61
65
|
size === 'sm' ? styles.trackSm : size === 'lg' ? styles.trackLg : styles.trackMd,
|
|
@@ -89,7 +93,7 @@ const SwitchRoot = React.forwardRef<HTMLButtonElement, SwitchProps>(
|
|
|
89
93
|
return (
|
|
90
94
|
<BaseSwitch.Root
|
|
91
95
|
ref={ref}
|
|
92
|
-
id={
|
|
96
|
+
id={resolvedId}
|
|
93
97
|
checked={checked}
|
|
94
98
|
defaultChecked={defaultChecked}
|
|
95
99
|
onCheckedChange={onCheckedChange ?? onChange}
|
|
@@ -98,7 +102,7 @@ const SwitchRoot = React.forwardRef<HTMLButtonElement, SwitchProps>(
|
|
|
98
102
|
className={rootClasses}
|
|
99
103
|
aria-label={ariaLabel}
|
|
100
104
|
aria-labelledby={ariaLabelledBy}
|
|
101
|
-
aria-describedby={ariaDescribedBy}
|
|
105
|
+
aria-describedby={mergeAriaIds(ariaDescribedBy, helperId)}
|
|
102
106
|
>
|
|
103
107
|
<span className={trackClasses} aria-hidden="true">
|
|
104
108
|
<BaseSwitch.Thumb className={thumbClasses} />
|
|
@@ -107,7 +111,11 @@ const SwitchRoot = React.forwardRef<HTMLButtonElement, SwitchProps>(
|
|
|
107
111
|
{(label || resolvedHelperText) && (
|
|
108
112
|
<div className={styles.content}>
|
|
109
113
|
{label && <span className={labelClasses}>{label}</span>}
|
|
110
|
-
{resolvedHelperText &&
|
|
114
|
+
{resolvedHelperText && (
|
|
115
|
+
<span id={helperId} className={helperClasses}>
|
|
116
|
+
{resolvedHelperText}
|
|
117
|
+
</span>
|
|
118
|
+
)}
|
|
111
119
|
</div>
|
|
112
120
|
)}
|
|
113
121
|
</BaseSwitch.Root>
|
|
@@ -24,19 +24,20 @@
|
|
|
24
24
|
// ============================================
|
|
25
25
|
|
|
26
26
|
.default {
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
@include form-group-surface;
|
|
28
|
+
|
|
29
29
|
padding: calc(var(--fui-space-px, $fui-space-px) * 2);
|
|
30
30
|
gap: calc(var(--fui-space-px, $fui-space-px) * 2);
|
|
31
31
|
|
|
32
32
|
.item {
|
|
33
33
|
background-color: transparent;
|
|
34
|
-
border:
|
|
34
|
+
border: 1px solid transparent;
|
|
35
35
|
border-radius: calc(var(--fui-radius-md, $fui-radius-md) - 2px);
|
|
36
36
|
|
|
37
37
|
&.selected {
|
|
38
|
-
background-color: var(--fui-bg
|
|
39
|
-
|
|
38
|
+
background-color: var(--fui-field-bg, $fui-bg-elevated);
|
|
39
|
+
border-color: var(--fui-field-selection-border, $fui-color-accent);
|
|
40
|
+
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--fui-field-selection-border, $fui-color-accent) 55%, transparent);
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
43
|
}
|
|
@@ -47,17 +48,17 @@
|
|
|
47
48
|
|
|
48
49
|
.pills {
|
|
49
50
|
.item {
|
|
50
|
-
background-color:
|
|
51
|
-
border: 1px solid
|
|
51
|
+
background-color: var(--fui-field-bg, $fui-bg-elevated);
|
|
52
|
+
border: 1px solid var(--fui-form-group-border, $fui-border);
|
|
52
53
|
border-radius: var(--fui-radius-md, $fui-radius-md);
|
|
53
54
|
|
|
54
55
|
&:hover:not(:disabled):not(.selected) {
|
|
55
|
-
background-color: var(--fui-bg-
|
|
56
|
+
background-color: var(--fui-bg-hover, $fui-bg-hover);
|
|
57
|
+
border-color: var(--fui-field-border-hover, $fui-text-tertiary);
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
&.selected {
|
|
59
|
-
|
|
60
|
-
border-color: var(--fui-border, $fui-border);
|
|
61
|
+
@include popup-item-selected;
|
|
61
62
|
}
|
|
62
63
|
}
|
|
63
64
|
}
|
|
@@ -69,17 +70,16 @@
|
|
|
69
70
|
.outline {
|
|
70
71
|
.item {
|
|
71
72
|
background-color: transparent;
|
|
72
|
-
border: 1px solid var(--fui-border, $fui-border);
|
|
73
|
+
border: 1px solid var(--fui-field-border, $fui-border);
|
|
73
74
|
border-radius: var(--fui-radius-md, $fui-radius-md);
|
|
74
75
|
|
|
75
76
|
&:hover:not(:disabled):not(.selected) {
|
|
76
|
-
background-color: var(--fui-bg-
|
|
77
|
+
background-color: var(--fui-bg-hover, $fui-bg-hover);
|
|
78
|
+
border-color: var(--fui-field-border-hover, $fui-text-tertiary);
|
|
77
79
|
}
|
|
78
80
|
|
|
79
81
|
&.selected {
|
|
80
|
-
|
|
81
|
-
border-color: var(--fui-color-accent, $fui-color-accent);
|
|
82
|
-
color: var(--fui-color-accent, $fui-color-accent);
|
|
82
|
+
@include popup-item-selected;
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
}
|
|
@@ -101,7 +101,11 @@
|
|
|
101
101
|
color: var(--fui-text-secondary, $fui-text-secondary);
|
|
102
102
|
white-space: nowrap;
|
|
103
103
|
user-select: none;
|
|
104
|
-
transition:
|
|
104
|
+
transition:
|
|
105
|
+
background-color var(--fui-transition-fast, $fui-transition-fast),
|
|
106
|
+
border-color var(--fui-transition-fast, $fui-transition-fast),
|
|
107
|
+
box-shadow var(--fui-transition-fast, $fui-transition-fast),
|
|
108
|
+
color var(--fui-transition-fast, $fui-transition-fast);
|
|
105
109
|
|
|
106
110
|
&.selected {
|
|
107
111
|
color: var(--fui-text-primary, $fui-text-primary);
|
|
@@ -119,7 +123,7 @@
|
|
|
119
123
|
|
|
120
124
|
.size-sm {
|
|
121
125
|
.item {
|
|
122
|
-
min-height:
|
|
126
|
+
min-height: var(--fui-button-height-sm, $fui-button-height-sm);
|
|
123
127
|
padding: 0 var(--fui-space-2, $fui-space-2);
|
|
124
128
|
font-size: var(--fui-font-size-xs, $fui-font-size-xs);
|
|
125
129
|
}
|
|
@@ -127,7 +131,7 @@
|
|
|
127
131
|
|
|
128
132
|
.size-md {
|
|
129
133
|
.item {
|
|
130
|
-
min-height:
|
|
134
|
+
min-height: var(--fui-button-height-md, $fui-button-height-md);
|
|
131
135
|
padding: 0 var(--fui-space-3, $fui-space-3);
|
|
132
136
|
font-size: var(--fui-font-size-sm, $fui-font-size-sm);
|
|
133
137
|
}
|
|
@@ -135,7 +139,7 @@
|
|
|
135
139
|
|
|
136
140
|
.size-lg {
|
|
137
141
|
.item {
|
|
138
|
-
min-height:
|
|
142
|
+
min-height: var(--fui-button-height-lg, $fui-button-height-lg);
|
|
139
143
|
padding: 0 var(--fui-space-4, $fui-space-4);
|
|
140
144
|
font-size: var(--fui-font-size-base, $fui-font-size-base);
|
|
141
145
|
}
|
package/src/index.ts
CHANGED
|
@@ -279,6 +279,7 @@ export {
|
|
|
279
279
|
Fieldset,
|
|
280
280
|
type FieldsetProps,
|
|
281
281
|
type FieldsetLegendProps,
|
|
282
|
+
type FieldsetDescriptionProps,
|
|
282
283
|
} from './components/Fieldset';
|
|
283
284
|
|
|
284
285
|
// Form
|
|
@@ -334,6 +335,7 @@ export {
|
|
|
334
335
|
type HeaderSearchProps,
|
|
335
336
|
type HeaderActionsProps,
|
|
336
337
|
type HeaderTriggerProps,
|
|
338
|
+
type HeaderMobileNavProps,
|
|
337
339
|
} from './components/Header';
|
|
338
340
|
|
|
339
341
|
// AppShell
|
package/src/styles/globals.scss
CHANGED
|
@@ -148,7 +148,10 @@ $fui-info: #3b82f6 !default;
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
:where(:focus-visible) {
|
|
151
|
-
outline:
|
|
151
|
+
outline: 1px solid color-mix(in srgb, var(--fui-focus-ring-color, #18181b) 24%, transparent);
|
|
152
152
|
outline-offset: var(--fui-focus-ring-offset, 2px);
|
|
153
|
+
box-shadow:
|
|
154
|
+
0 0 0 calc(var(--fui-focus-ring-width, 2px) + 1px) color-mix(in srgb, var(--fui-focus-ring-color, #18181b) 10%, transparent),
|
|
155
|
+
0 10px 24px -16px color-mix(in srgb, var(--fui-focus-ring-color, #18181b) 18%, transparent);
|
|
153
156
|
}
|
|
154
157
|
}
|
package/src/tokens/_mixins.scss
CHANGED
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
@mixin focus-ring {
|
|
9
9
|
outline: none;
|
|
10
10
|
box-shadow:
|
|
11
|
-
0 0 0 var(--fui-focus-ring-
|
|
12
|
-
0 0 0 calc(var(--fui-focus-ring-
|
|
11
|
+
0 0 0 1px color-mix(in srgb, var(--fui-focus-ring-color, #{$fui-focus-ring-color}) 28%, transparent),
|
|
12
|
+
0 0 0 calc(var(--fui-focus-ring-width, #{$fui-focus-ring-width}) + 1px) color-mix(in srgb, var(--fui-focus-ring-color, #{$fui-focus-ring-color}) 12%, transparent),
|
|
13
|
+
0 10px 24px -16px color-mix(in srgb, var(--fui-focus-ring-color, #{$fui-focus-ring-color}) 32%, transparent);
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
// Reset button styles
|
|
@@ -139,6 +140,48 @@
|
|
|
139
140
|
letter-spacing: 0.05em;
|
|
140
141
|
}
|
|
141
142
|
|
|
143
|
+
// Shared shell for field-like controls.
|
|
144
|
+
@mixin field-shell {
|
|
145
|
+
background-color: var(--fui-field-bg, var(--fui-bg-elevated, #{$fui-bg-elevated}));
|
|
146
|
+
border: 1px solid var(--fui-field-border, var(--fui-border-strong, #{$fui-border-strong}));
|
|
147
|
+
border-radius: var(--fui-radius-md, #{$fui-radius-md});
|
|
148
|
+
transition:
|
|
149
|
+
background-color var(--fui-transition-fast, #{$fui-transition-fast}),
|
|
150
|
+
border-color var(--fui-transition-fast, #{$fui-transition-fast}),
|
|
151
|
+
box-shadow var(--fui-transition-fast, #{$fui-transition-fast}),
|
|
152
|
+
color var(--fui-transition-fast, #{$fui-transition-fast});
|
|
153
|
+
|
|
154
|
+
&:hover:not(:disabled):not([data-disabled]):not(:focus-visible) {
|
|
155
|
+
border-color: var(--fui-field-border-hover, var(--fui-text-tertiary, #{$fui-text-tertiary}));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
&:disabled,
|
|
159
|
+
&[data-disabled] {
|
|
160
|
+
background-color: var(--fui-field-bg-disabled, var(--fui-bg-tertiary, #{$fui-bg-tertiary}));
|
|
161
|
+
color: var(--fui-text-tertiary, #{$fui-text-tertiary});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
@mixin field-shell-focus {
|
|
166
|
+
@include focus-ring;
|
|
167
|
+
border-color: var(
|
|
168
|
+
--fui-field-border-focus,
|
|
169
|
+
color-mix(in srgb, var(--fui-focus-ring-color, #{$fui-focus-ring-color}) 20%, var(--fui-border-strong, #{$fui-border-strong}))
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
@mixin popup-item-selected {
|
|
174
|
+
background-color: var(--fui-field-selection-bg, var(--fui-bg-hover, #{$fui-bg-hover}));
|
|
175
|
+
color: var(--fui-field-selection-color, var(--fui-text-primary, #{$fui-text-primary}));
|
|
176
|
+
box-shadow: inset 0 0 0 1px var(--fui-field-selection-border, var(--fui-color-accent, #{$fui-color-accent}));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
@mixin form-group-surface {
|
|
180
|
+
background-color: var(--fui-form-group-bg, var(--fui-bg-secondary, #{$fui-bg-secondary}));
|
|
181
|
+
border: 1px solid var(--fui-form-group-border, var(--fui-border, #{$fui-border}));
|
|
182
|
+
border-radius: var(--fui-radius-lg, #{$fui-radius-lg});
|
|
183
|
+
}
|
|
184
|
+
|
|
142
185
|
// Responsive breakpoint mixins (mobile-first, min-width)
|
|
143
186
|
@mixin breakpoint-sm { @media (min-width: $fui-breakpoint-sm) { @content; } }
|
|
144
187
|
@mixin breakpoint-md { @media (min-width: $fui-breakpoint-md) { @content; } }
|
|
@@ -240,12 +283,22 @@
|
|
|
240
283
|
}
|
|
241
284
|
}
|
|
242
285
|
|
|
286
|
+
// Focus ring for success states (e.g., validated form fields)
|
|
287
|
+
@mixin focus-ring-success {
|
|
288
|
+
outline: none;
|
|
289
|
+
box-shadow:
|
|
290
|
+
0 0 0 1px color-mix(in srgb, var(--fui-color-success, #{$fui-color-success}) 42%, transparent),
|
|
291
|
+
0 0 0 calc(var(--fui-focus-ring-width, #{$fui-focus-ring-width}) + 1px) color-mix(in srgb, var(--fui-color-success, #{$fui-color-success}) 14%, transparent),
|
|
292
|
+
0 10px 24px -16px color-mix(in srgb, var(--fui-color-success, #{$fui-color-success}) 28%, transparent);
|
|
293
|
+
}
|
|
294
|
+
|
|
243
295
|
// Focus ring for error states (e.g., invalid form fields)
|
|
244
296
|
@mixin focus-ring-error {
|
|
245
297
|
outline: none;
|
|
246
298
|
box-shadow:
|
|
247
|
-
0 0 0
|
|
248
|
-
0 0 0 calc(var(--fui-focus-ring-
|
|
299
|
+
0 0 0 1px color-mix(in srgb, var(--fui-color-danger, #{$fui-color-danger}) 42%, transparent),
|
|
300
|
+
0 0 0 calc(var(--fui-focus-ring-width, #{$fui-focus-ring-width}) + 1px) color-mix(in srgb, var(--fui-color-danger, #{$fui-color-danger}) 14%, transparent),
|
|
301
|
+
0 10px 24px -16px color-mix(in srgb, var(--fui-color-danger, #{$fui-color-danger}) 28%, transparent);
|
|
249
302
|
}
|
|
250
303
|
|
|
251
304
|
// ============================================
|
|
@@ -505,6 +505,19 @@ $fui-dark-tooltip-shadow:
|
|
|
505
505
|
--fui-border-default: light-dark(#{$fui-border-default}, #{$fui-dark-border-default});
|
|
506
506
|
--fui-border-strong: light-dark(#{$fui-border-strong}, #{$fui-dark-border-strong});
|
|
507
507
|
|
|
508
|
+
// Form chrome (derived from theme surfaces + seed-driven accent)
|
|
509
|
+
--fui-field-bg: color-mix(in srgb, var(--fui-bg-elevated) 92%, var(--fui-bg-primary));
|
|
510
|
+
--fui-field-bg-disabled: color-mix(in srgb, var(--fui-bg-tertiary) 88%, var(--fui-bg-primary));
|
|
511
|
+
--fui-field-border: color-mix(in srgb, var(--fui-border-strong) 78%, var(--fui-bg-primary));
|
|
512
|
+
--fui-field-border-hover: color-mix(in srgb, var(--fui-text-tertiary) 58%, var(--fui-border-strong));
|
|
513
|
+
--fui-field-border-focus: color-mix(in srgb, var(--fui-focus-ring-color) 18%, var(--fui-border-strong));
|
|
514
|
+
--fui-field-selection-bg: color-mix(in srgb, var(--fui-color-accent) 12%, var(--fui-bg-elevated));
|
|
515
|
+
--fui-field-selection-bg-hover: color-mix(in srgb, var(--fui-color-accent) 16%, var(--fui-bg-hover));
|
|
516
|
+
--fui-field-selection-border: color-mix(in srgb, var(--fui-color-accent) 38%, var(--fui-border));
|
|
517
|
+
--fui-field-selection-color: var(--fui-text-primary);
|
|
518
|
+
--fui-form-group-bg: color-mix(in srgb, var(--fui-bg-secondary) 58%, var(--fui-bg-primary));
|
|
519
|
+
--fui-form-group-border: color-mix(in srgb, var(--fui-border) 82%, transparent);
|
|
520
|
+
|
|
508
521
|
// Shadows (not <color> type — light-dark() can't be used, handled in dark overrides)
|
|
509
522
|
--fui-shadow-sm: #{$fui-shadow-sm};
|
|
510
523
|
--fui-shadow-md: #{$fui-shadow-md};
|
|
@@ -559,7 +572,8 @@ $fui-dark-tooltip-shadow:
|
|
|
559
572
|
}
|
|
560
573
|
|
|
561
574
|
// Light mode explicit override (for manual toggle back to light)
|
|
562
|
-
:root[data-theme="light"]
|
|
575
|
+
:root[data-theme="light"],
|
|
576
|
+
:root.light {
|
|
563
577
|
color-scheme: light;
|
|
564
578
|
}
|
|
565
579
|
|
|
@@ -694,6 +708,11 @@ $fui-dark-tooltip-shadow:
|
|
|
694
708
|
// Ensure minimum contrast on interactive elements
|
|
695
709
|
--fui-bg-hover: rgba(0, 0, 0, 0.12);
|
|
696
710
|
--fui-bg-active: rgba(0, 0, 0, 0.18);
|
|
711
|
+
--fui-field-border: var(--fui-text-primary);
|
|
712
|
+
--fui-field-border-hover: var(--fui-text-primary);
|
|
713
|
+
--fui-field-border-focus: var(--fui-color-accent);
|
|
714
|
+
--fui-field-selection-border: var(--fui-color-accent);
|
|
715
|
+
--fui-form-group-border: var(--fui-text-primary);
|
|
697
716
|
}
|
|
698
717
|
|
|
699
718
|
// Private mixin for dark mode token values
|