@ntmats/hc-ui 0.1.1 → 0.2.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 +6 -2
- package/dist/index.d.ts +27 -3
- package/dist/index.js +42 -5
- package/dist/styles.css +70 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ Imported as `@hc/ui`: `npm install --save-exact @hc/ui@npm:@ntmats/hc-ui@<versio
|
|
|
5
5
|
React presentation primitives, with React and React DOM supplied by each consumer. The initial contract has no router, data, persistence, icon-library or product-specific dependencies.
|
|
6
6
|
|
|
7
7
|
```ts
|
|
8
|
-
import { Button, Chip, IconButton, SegmentedControl, Skeleton, Surface } from '@hc/ui';
|
|
8
|
+
import { Button, Chip, Field, IconButton, SegmentedControl, Skeleton, Surface } from '@hc/ui';
|
|
9
9
|
import '@hc/ui/styles.css';
|
|
10
10
|
import '@hc/tokens/css';
|
|
11
11
|
```
|
|
@@ -20,7 +20,11 @@ Styles are not in a cascade layer: unlayered consumer resets (HCUI's `button` ru
|
|
|
20
20
|
|
|
21
21
|
HCUI uses `SegmentedControl` through its `ViewToggle` wrapper. During the bootstrap it compiles the source through Vite/TypeScript aliases; switch to the pinned package once published.
|
|
22
22
|
|
|
23
|
-
`Button` is a pill with `secondary` (default), `primary`, `outline` and `danger` variants, `md`/`lg` sizes,
|
|
23
|
+
`Button` is a pill with `secondary` (default), `primary`, `outline` and `danger` variants, `sm`/`md`/`lg` sizes (36/44/52px), optional `icon` and `iconEnd`, `fullWidth`, and `loading`, which keeps focus and label, shows a spinner and ignores clicks. Long labels are cut with an ellipsis. `IconButton` requires `label` (accessible name and tooltip) and comes in 36/40/44px; the 36px size keeps a 44px touch target. Pass `selected` to make it a toggle. `Chip` is a filter/option pill; pass `selected` to make it a toggle (`aria-pressed`), omit it for a plain action. All three read the shared `rond` roundness, default to `type="button"`, forward refs, scale slightly on press, show the shared focus ring and drop motion under `prefers-reduced-motion`.
|
|
24
|
+
|
|
25
|
+
`Field` is a filled pill text input. `label` is required (`hideLabel` keeps it for screen readers), with `icon`, an `end` slot, `hint`, `error` and comfortable/compact density (44/40px). It forwards its ref and input props to the `input`; value and validation stay with the caller.
|
|
26
|
+
|
|
27
|
+
Focus rings read `--hc-focus-ring-offset` (default 2px). Set it to `-2px` on a control inside an `overflow: hidden` group so the ring is not cut.
|
|
24
28
|
|
|
25
29
|
`Skeleton` is a static shape that pulses, hidden from assistive technology; the enclosing region announces loading once. Its base rule has zero specificity (`:where`), so the consumer sizes each shape with `width`/`height` or its own class.
|
|
26
30
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ButtonHTMLAttributes, type HTMLAttributes, type ReactNode } from 'react';
|
|
1
|
+
import { type ButtonHTMLAttributes, type HTMLAttributes, type InputHTMLAttributes, type ReactNode } from 'react';
|
|
2
2
|
export type SurfaceTone = 'page' | 'main' | 'content' | 'elevated' | 'overlay';
|
|
3
3
|
export interface SurfaceProps extends HTMLAttributes<HTMLDivElement> {
|
|
4
4
|
tone?: SurfaceTone;
|
|
@@ -15,7 +15,8 @@ export interface SegmentedControlProps<T extends string> extends Omit<HTMLAttrib
|
|
|
15
15
|
label: string;
|
|
16
16
|
value: T;
|
|
17
17
|
options: readonly SegmentedOption<T>[];
|
|
18
|
-
|
|
18
|
+
/** NoInfer: `T` comes from `value` and `options`, so a state setter cannot widen it to string. */
|
|
19
|
+
onChange: (value: NoInfer<T>) => void;
|
|
19
20
|
density?: 'comfortable' | 'compact';
|
|
20
21
|
orientation?: 'horizontal' | 'vertical';
|
|
21
22
|
/** Icon-only options: labels stay as accessible names and tooltips. */
|
|
@@ -26,10 +27,15 @@ export declare function SegmentedControl<T extends string>({ label, value, optio
|
|
|
26
27
|
export type ButtonVariant = 'secondary' | 'primary' | 'outline' | 'danger';
|
|
27
28
|
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
28
29
|
variant?: ButtonVariant;
|
|
29
|
-
|
|
30
|
+
/** 36, 44 or 52px. */
|
|
31
|
+
size?: 'sm' | 'md' | 'lg';
|
|
30
32
|
/** Decorative icon before the label; pass it already sized (18px in HCUI). */
|
|
31
33
|
icon?: ReactNode;
|
|
34
|
+
/** Decorative icon after the label. */
|
|
35
|
+
iconEnd?: ReactNode;
|
|
32
36
|
fullWidth?: boolean;
|
|
37
|
+
/** Busy state: keeps focus and label, shows a spinner and ignores clicks. */
|
|
38
|
+
loading?: boolean;
|
|
33
39
|
}
|
|
34
40
|
/** Pill button. The caller supplies the label text and handles the action. */
|
|
35
41
|
export declare const Button: import("react").ForwardRefExoticComponent<ButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
@@ -40,6 +46,8 @@ export interface IconButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonEle
|
|
|
40
46
|
/** 36, 40 or 44px. The 36px size keeps a 44px touch target. */
|
|
41
47
|
size?: 'sm' | 'md' | 'lg';
|
|
42
48
|
tone?: 'primary' | 'secondary';
|
|
49
|
+
/** Toggle state (`aria-pressed`); omit for a plain action. */
|
|
50
|
+
selected?: boolean;
|
|
43
51
|
}
|
|
44
52
|
/** Round icon-only button (back, close, options). */
|
|
45
53
|
export declare const IconButton: import("react").ForwardRefExoticComponent<IconButtonProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
@@ -50,6 +58,22 @@ export interface ChipProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
|
50
58
|
}
|
|
51
59
|
/** Filter or option chip. Selected chips are filled with the accent color. */
|
|
52
60
|
export declare const Chip: import("react").ForwardRefExoticComponent<ChipProps & import("react").RefAttributes<HTMLButtonElement>>;
|
|
61
|
+
export interface FieldProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> {
|
|
62
|
+
/** Accessible name. Visible above the field unless `hideLabel` is set. */
|
|
63
|
+
label: string;
|
|
64
|
+
hideLabel?: boolean;
|
|
65
|
+
/** 40 or 44px. */
|
|
66
|
+
density?: 'comfortable' | 'compact';
|
|
67
|
+
/** Decorative icon at the start of the field. */
|
|
68
|
+
icon?: ReactNode;
|
|
69
|
+
/** Slot at the end of the field, such as a clear button. */
|
|
70
|
+
end?: ReactNode;
|
|
71
|
+
hint?: ReactNode;
|
|
72
|
+
/** Error message; also marks the input as invalid. */
|
|
73
|
+
error?: ReactNode;
|
|
74
|
+
}
|
|
75
|
+
/** Filled pill text field. Value, validation and messages stay with the caller. */
|
|
76
|
+
export declare const Field: import("react").ForwardRefExoticComponent<FieldProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
53
77
|
export interface SkeletonProps extends HTMLAttributes<HTMLSpanElement> {
|
|
54
78
|
width?: string | number;
|
|
55
79
|
height?: string | number;
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/index.tsx
|
|
2
|
-
import { forwardRef, useRef } from "react";
|
|
2
|
+
import { forwardRef, useId, useRef } from "react";
|
|
3
3
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
function Surface({ tone = "content", className, ...props }) {
|
|
5
5
|
return /* @__PURE__ */ jsx("div", { ...props, className: ["hc-ui-surface", className].filter(Boolean).join(" "), "data-tone": tone });
|
|
@@ -80,7 +80,11 @@ function SegmentedControl({
|
|
|
80
80
|
);
|
|
81
81
|
}
|
|
82
82
|
var classes = (...names) => names.filter(Boolean).join(" ");
|
|
83
|
-
var Button = forwardRef(function Button2({ variant = "secondary", size = "md", icon, fullWidth = false, type = "button", className, children, ...props }, ref) {
|
|
83
|
+
var Button = forwardRef(function Button2({ variant = "secondary", size = "md", icon, iconEnd, fullWidth = false, loading = false, type = "button", className, children, onClick, ...props }, ref) {
|
|
84
|
+
const handleClick = (event) => {
|
|
85
|
+
if (loading) event.preventDefault();
|
|
86
|
+
else onClick?.(event);
|
|
87
|
+
};
|
|
84
88
|
return /* @__PURE__ */ jsxs(
|
|
85
89
|
"button",
|
|
86
90
|
{
|
|
@@ -88,17 +92,21 @@ var Button = forwardRef(function Button2({ variant = "secondary", size = "md", i
|
|
|
88
92
|
ref,
|
|
89
93
|
type,
|
|
90
94
|
className: classes("hc-ui-button", className),
|
|
95
|
+
onClick: handleClick,
|
|
96
|
+
"aria-busy": loading || void 0,
|
|
97
|
+
"aria-disabled": loading || props["aria-disabled"],
|
|
91
98
|
"data-variant": variant,
|
|
92
99
|
"data-size": size,
|
|
93
100
|
"data-full-width": fullWidth || void 0,
|
|
94
101
|
children: [
|
|
95
|
-
icon && /* @__PURE__ */ jsx("span", { className: "hc-ui-button__icon", "aria-hidden": "true", children: icon }),
|
|
96
|
-
children
|
|
102
|
+
loading ? /* @__PURE__ */ jsx("span", { className: "hc-ui-button__spinner", "aria-hidden": "true" }) : icon && /* @__PURE__ */ jsx("span", { className: "hc-ui-button__icon", "aria-hidden": "true", children: icon }),
|
|
103
|
+
/* @__PURE__ */ jsx("span", { className: "hc-ui-button__label", children }),
|
|
104
|
+
iconEnd && /* @__PURE__ */ jsx("span", { className: "hc-ui-button__icon", "aria-hidden": "true", children: iconEnd })
|
|
97
105
|
]
|
|
98
106
|
}
|
|
99
107
|
);
|
|
100
108
|
});
|
|
101
|
-
var IconButton = forwardRef(function IconButton2({ label, icon, size = "md", tone = "primary", type = "button", title, className, ...props }, ref) {
|
|
109
|
+
var IconButton = forwardRef(function IconButton2({ label, icon, size = "md", tone = "primary", selected, type = "button", title, className, ...props }, ref) {
|
|
102
110
|
return /* @__PURE__ */ jsx(
|
|
103
111
|
"button",
|
|
104
112
|
{
|
|
@@ -107,6 +115,7 @@ var IconButton = forwardRef(function IconButton2({ label, icon, size = "md", ton
|
|
|
107
115
|
type,
|
|
108
116
|
"aria-label": label,
|
|
109
117
|
title: title ?? label,
|
|
118
|
+
"aria-pressed": selected ?? props["aria-pressed"],
|
|
110
119
|
className: classes("hc-ui-icon-button", className),
|
|
111
120
|
"data-size": size,
|
|
112
121
|
"data-tone": tone,
|
|
@@ -120,6 +129,33 @@ var Chip = forwardRef(function Chip2({ selected, icon, type = "button", classNam
|
|
|
120
129
|
children
|
|
121
130
|
] });
|
|
122
131
|
});
|
|
132
|
+
var Field = forwardRef(function Field2({ label, hideLabel = false, density = "comfortable", icon, end, hint, error, id, className, disabled, "aria-describedby": describedBy, ...props }, ref) {
|
|
133
|
+
const generated = useId();
|
|
134
|
+
const inputId = id ?? generated;
|
|
135
|
+
const hintId = hint ? `${inputId}-hint` : void 0;
|
|
136
|
+
const errorId = error ? `${inputId}-error` : void 0;
|
|
137
|
+
return /* @__PURE__ */ jsxs("div", { className: classes("hc-ui-field", className), "data-density": density, "data-invalid": error ? true : void 0, "data-disabled": disabled || void 0, children: [
|
|
138
|
+
/* @__PURE__ */ jsx("label", { className: "hc-ui-field__label", htmlFor: inputId, "data-hidden": hideLabel || void 0, children: label }),
|
|
139
|
+
/* @__PURE__ */ jsxs("div", { className: "hc-ui-field__control", children: [
|
|
140
|
+
icon && /* @__PURE__ */ jsx("span", { className: "hc-ui-field__icon", "aria-hidden": "true", children: icon }),
|
|
141
|
+
/* @__PURE__ */ jsx(
|
|
142
|
+
"input",
|
|
143
|
+
{
|
|
144
|
+
...props,
|
|
145
|
+
ref,
|
|
146
|
+
id: inputId,
|
|
147
|
+
disabled,
|
|
148
|
+
className: "hc-ui-field__input",
|
|
149
|
+
"aria-invalid": error ? true : props["aria-invalid"],
|
|
150
|
+
"aria-describedby": classes(describedBy, hintId, errorId) || void 0
|
|
151
|
+
}
|
|
152
|
+
),
|
|
153
|
+
end && /* @__PURE__ */ jsx("span", { className: "hc-ui-field__end", children: end })
|
|
154
|
+
] }),
|
|
155
|
+
hint && /* @__PURE__ */ jsx("p", { className: "hc-ui-field__hint", id: hintId, children: hint }),
|
|
156
|
+
error && /* @__PURE__ */ jsx("p", { className: "hc-ui-field__error", id: errorId, children: error })
|
|
157
|
+
] });
|
|
158
|
+
});
|
|
123
159
|
function Skeleton({ width, height, circle = false, className, style, ...props }) {
|
|
124
160
|
const size = { ...width !== void 0 && { width }, ...height !== void 0 && { height }, ...style };
|
|
125
161
|
return /* @__PURE__ */ jsx(
|
|
@@ -136,6 +172,7 @@ function Skeleton({ width, height, circle = false, className, style, ...props })
|
|
|
136
172
|
export {
|
|
137
173
|
Button,
|
|
138
174
|
Chip,
|
|
175
|
+
Field,
|
|
139
176
|
IconButton,
|
|
140
177
|
SegmentedControl,
|
|
141
178
|
Skeleton,
|
package/dist/styles.css
CHANGED
|
@@ -14,13 +14,11 @@
|
|
|
14
14
|
}
|
|
15
15
|
[data-hc-ds] .hc-ui-surface[data-tone='elevated'] {
|
|
16
16
|
background: var(--hc-color-bg-elevated);
|
|
17
|
-
border: 1px solid var(--hc-color-border);
|
|
18
17
|
border-radius: var(--hc-radius-md);
|
|
19
18
|
box-shadow: var(--hc-shadow-card);
|
|
20
19
|
}
|
|
21
20
|
[data-hc-ds] .hc-ui-surface[data-tone='overlay'] {
|
|
22
21
|
background: var(--hc-color-bg-elevated);
|
|
23
|
-
border: 1px solid var(--hc-color-border);
|
|
24
22
|
border-radius: var(--hc-radius-md);
|
|
25
23
|
box-shadow: var(--hc-shadow-float);
|
|
26
24
|
}
|
|
@@ -29,6 +27,7 @@
|
|
|
29
27
|
[data-hc-ds] .hc-ui-segmented {
|
|
30
28
|
display: inline-flex;
|
|
31
29
|
align-items: center;
|
|
30
|
+
flex: 0 0 auto;
|
|
32
31
|
gap: 2px;
|
|
33
32
|
max-width: 100%;
|
|
34
33
|
padding: 3px;
|
|
@@ -83,7 +82,7 @@
|
|
|
83
82
|
|
|
84
83
|
[data-hc-ds] .hc-ui-segmented__option:focus-visible {
|
|
85
84
|
outline: 2px solid var(--hc-focus-ring);
|
|
86
|
-
outline-offset: 2px;
|
|
85
|
+
outline-offset: var(--hc-focus-ring-offset, 2px);
|
|
87
86
|
}
|
|
88
87
|
|
|
89
88
|
[data-hc-ds] .hc-ui-segmented__option:disabled { opacity: 0.48; cursor: not-allowed; }
|
|
@@ -116,7 +115,7 @@
|
|
|
116
115
|
|
|
117
116
|
[data-hc-ds] :is(.hc-ui-button, .hc-ui-icon-button, .hc-ui-chip):focus-visible {
|
|
118
117
|
outline: 2px solid var(--hc-focus-ring);
|
|
119
|
-
outline-offset: 2px;
|
|
118
|
+
outline-offset: var(--hc-focus-ring-offset, 2px);
|
|
120
119
|
}
|
|
121
120
|
|
|
122
121
|
[data-hc-ds] :is(.hc-ui-button__icon, .hc-ui-icon-button__icon, .hc-ui-chip__icon) { display: inline-flex; flex: 0 0 auto; }
|
|
@@ -157,9 +156,23 @@
|
|
|
157
156
|
[data-hc-ds] .hc-ui-button[data-variant='primary'] { color: var(--hc-color-text-inverse); background-color: var(--hc-color-accent); }
|
|
158
157
|
[data-hc-ds] .hc-ui-button[data-variant='outline'] { background-color: transparent; box-shadow: inset 0 0 0 1px var(--hc-color-border-strong); }
|
|
159
158
|
[data-hc-ds] .hc-ui-button[data-variant='danger'] { color: var(--hc-color-text-on-danger); background-color: var(--hc-color-bg-danger); }
|
|
159
|
+
[data-hc-ds] .hc-ui-button[data-size='sm'] { min-height: var(--hc-control-height-chip); padding: 0 var(--hc-space-4); font-size: var(--hc-font-size-sm); }
|
|
160
160
|
[data-hc-ds] .hc-ui-button[data-size='lg'] { min-height: var(--hc-control-height-lg); padding: 0 var(--hc-space-6); font-size: var(--hc-font-size-base); }
|
|
161
161
|
[data-hc-ds] .hc-ui-button[data-full-width] { width: 100%; }
|
|
162
162
|
[data-hc-ds] .hc-ui-button:disabled { opacity: 0.4; cursor: default; }
|
|
163
|
+
[data-hc-ds] .hc-ui-button { max-width: 100%; }
|
|
164
|
+
[data-hc-ds] .hc-ui-button__label { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
|
165
|
+
[data-hc-ds] .hc-ui-button[aria-busy='true'] { cursor: progress; }
|
|
166
|
+
[data-hc-ds] .hc-ui-button__spinner {
|
|
167
|
+
flex: 0 0 auto;
|
|
168
|
+
width: 16px;
|
|
169
|
+
height: 16px;
|
|
170
|
+
border: 2px solid currentColor;
|
|
171
|
+
border-right-color: transparent;
|
|
172
|
+
border-radius: var(--hc-radius-full);
|
|
173
|
+
animation: hc-ui-spin 700ms linear infinite;
|
|
174
|
+
}
|
|
175
|
+
@keyframes hc-ui-spin { to { transform: rotate(360deg); } }
|
|
163
176
|
|
|
164
177
|
/* IconButton — HCUI icon-button / close-button. */
|
|
165
178
|
[data-hc-ds] .hc-ui-icon-button {
|
|
@@ -175,6 +188,7 @@
|
|
|
175
188
|
[data-hc-ds] .hc-ui-icon-button[data-tone='secondary'] { color: var(--hc-color-text-secondary); }
|
|
176
189
|
/* 36px keeps a 44px touch target; the veil stays clipped to the circle. */
|
|
177
190
|
[data-hc-ds] .hc-ui-icon-button[data-size='sm']::before { content: ''; position: absolute; inset: -4px; }
|
|
191
|
+
[data-hc-ds] .hc-ui-icon-button[aria-pressed='true'] { color: var(--hc-color-text-inverse); background-color: var(--hc-color-accent); }
|
|
178
192
|
[data-hc-ds] .hc-ui-icon-button:disabled { opacity: 0.4; cursor: default; }
|
|
179
193
|
|
|
180
194
|
/* Chip — HCUI chip / chip-active. */
|
|
@@ -197,6 +211,56 @@
|
|
|
197
211
|
[data-hc-ds] .hc-ui-chip[aria-pressed='true'] { color: var(--hc-color-text-inverse); background-color: var(--hc-color-accent); box-shadow: none; }
|
|
198
212
|
[data-hc-ds] .hc-ui-chip:disabled { opacity: 0.4; cursor: default; }
|
|
199
213
|
|
|
214
|
+
/* Field — HCUI field. One focus ring, on the pill, never on the inner input. */
|
|
215
|
+
[data-hc-ds] .hc-ui-field { display: grid; align-content: start; gap: var(--hc-space-1); min-width: 0; font-family: var(--hc-font-body); font-variation-settings: 'ROND' var(--hc-rond); }
|
|
216
|
+
[data-hc-ds] .hc-ui-field__label { color: var(--hc-color-text-secondary); font-size: var(--hc-font-size-sm); font-weight: var(--hc-font-weight-medium); line-height: var(--hc-line-height-sm); }
|
|
217
|
+
[data-hc-ds] .hc-ui-field__label[data-hidden] {
|
|
218
|
+
position: absolute;
|
|
219
|
+
width: 1px;
|
|
220
|
+
height: 1px;
|
|
221
|
+
overflow: hidden;
|
|
222
|
+
clip-path: inset(50%);
|
|
223
|
+
white-space: nowrap;
|
|
224
|
+
}
|
|
225
|
+
[data-hc-ds] .hc-ui-field__control {
|
|
226
|
+
display: flex;
|
|
227
|
+
align-items: center;
|
|
228
|
+
gap: var(--hc-space-2);
|
|
229
|
+
min-height: var(--hc-control-height-md);
|
|
230
|
+
padding: 0 var(--hc-space-4);
|
|
231
|
+
color: var(--hc-color-text-primary);
|
|
232
|
+
background-color: var(--hc-color-bg-muted);
|
|
233
|
+
border-radius: var(--hc-radius-full);
|
|
234
|
+
transition: box-shadow var(--hc-transition-fast), background-color var(--hc-transition-fast);
|
|
235
|
+
}
|
|
236
|
+
[data-hc-ds] .hc-ui-field[data-density='compact'] .hc-ui-field__control { min-height: var(--hc-control-height-sm); padding: 0 var(--hc-space-3); }
|
|
237
|
+
[data-hc-ds] .hc-ui-field__control:focus-within { box-shadow: inset 0 0 0 1.5px var(--hc-color-text-primary); }
|
|
238
|
+
[data-hc-ds] .hc-ui-field[data-invalid] .hc-ui-field__control { box-shadow: inset 0 0 0 1.5px var(--hc-color-danger); }
|
|
239
|
+
[data-hc-ds] .hc-ui-field[data-disabled] .hc-ui-field__control { opacity: 0.4; }
|
|
240
|
+
[data-hc-ds] .hc-ui-field__input {
|
|
241
|
+
appearance: none;
|
|
242
|
+
flex: 1 1 auto;
|
|
243
|
+
width: 100%;
|
|
244
|
+
min-width: 0;
|
|
245
|
+
margin: 0;
|
|
246
|
+
padding: 0;
|
|
247
|
+
color: inherit;
|
|
248
|
+
font-family: inherit;
|
|
249
|
+
font-size: var(--hc-font-size-base);
|
|
250
|
+
font-variation-settings: inherit;
|
|
251
|
+
line-height: var(--hc-line-height-base);
|
|
252
|
+
background: transparent;
|
|
253
|
+
border: 0;
|
|
254
|
+
outline: 0;
|
|
255
|
+
box-shadow: none;
|
|
256
|
+
}
|
|
257
|
+
[data-hc-ds] .hc-ui-field__input::placeholder { color: var(--hc-color-placeholder); opacity: 1; }
|
|
258
|
+
[data-hc-ds] :is(.hc-ui-field__icon, .hc-ui-field__end) { display: inline-flex; flex: 0 0 auto; align-items: center; color: var(--hc-color-text-muted); }
|
|
259
|
+
[data-hc-ds] .hc-ui-field__icon svg { display: block; }
|
|
260
|
+
[data-hc-ds] :is(.hc-ui-field__hint, .hc-ui-field__error) { margin: 0; font-size: var(--hc-font-size-xs); line-height: var(--hc-line-height-xs); }
|
|
261
|
+
[data-hc-ds] .hc-ui-field__hint { color: var(--hc-color-text-muted); }
|
|
262
|
+
[data-hc-ds] .hc-ui-field__error { color: var(--hc-color-text-on-danger); }
|
|
263
|
+
|
|
200
264
|
/* Skeleton — zero specificity on purpose: the consumer sizes each shape. */
|
|
201
265
|
@keyframes hc-ui-skeleton-pulse {
|
|
202
266
|
0%, 100% { opacity: 1; }
|
|
@@ -216,6 +280,8 @@
|
|
|
216
280
|
@media (prefers-reduced-motion: reduce) {
|
|
217
281
|
[data-hc-ds] .hc-ui-segmented__option { transition: none; }
|
|
218
282
|
[data-hc-ds] :is(.hc-ui-button, .hc-ui-icon-button, .hc-ui-chip) { transition: none; }
|
|
283
|
+
[data-hc-ds] .hc-ui-field__control { transition: none; }
|
|
284
|
+
[data-hc-ds] .hc-ui-button__spinner { animation: none; }
|
|
219
285
|
[data-hc-ds] :is(.hc-ui-button, .hc-ui-icon-button, .hc-ui-chip):active:not(:disabled) { transform: none; }
|
|
220
286
|
:where([data-hc-ds] .hc-ui-skeleton) { animation: none; }
|
|
221
287
|
}
|