@antadesign/anta 0.2.2 → 0.3.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.
Files changed (82) hide show
  1. package/README.md +14 -0
  2. package/dist/anta_helpers.d.ts +39 -1
  3. package/dist/anta_helpers.js +30 -2
  4. package/dist/components/Button.d.ts +7 -4
  5. package/dist/components/Button.js +6 -11
  6. package/dist/components/Checkbox.d.ts +97 -0
  7. package/dist/components/Checkbox.js +77 -0
  8. package/dist/components/Expander.d.ts +74 -0
  9. package/dist/components/Expander.js +53 -0
  10. package/dist/components/Input.d.ts +159 -0
  11. package/dist/components/Input.js +150 -0
  12. package/dist/components/Menu.d.ts +73 -0
  13. package/dist/components/Menu.js +42 -0
  14. package/dist/components/MenuGroup.d.ts +24 -0
  15. package/dist/components/MenuGroup.js +19 -0
  16. package/dist/components/MenuItem.d.ts +61 -0
  17. package/dist/components/MenuItem.js +50 -0
  18. package/dist/components/MenuSeparator.d.ts +14 -0
  19. package/dist/components/MenuSeparator.js +7 -0
  20. package/dist/components/Progress.d.ts +12 -6
  21. package/dist/components/Progress.js +7 -4
  22. package/dist/components/Radio.d.ts +37 -0
  23. package/dist/components/Radio.js +33 -0
  24. package/dist/components/RadioGroup.d.ts +119 -0
  25. package/dist/components/RadioGroup.js +108 -0
  26. package/dist/components/Tag.d.ts +38 -5
  27. package/dist/components/Tag.js +9 -5
  28. package/dist/components/Text.d.ts +27 -12
  29. package/dist/components/Text.js +6 -3
  30. package/dist/components/Title.d.ts +10 -1
  31. package/dist/elements/a-button.css +1 -1
  32. package/dist/elements/a-button.d.ts +56 -0
  33. package/dist/elements/a-button.js +13 -11
  34. package/dist/elements/a-checkbox.css +1 -0
  35. package/dist/elements/a-checkbox.d.ts +52 -0
  36. package/dist/elements/a-checkbox.js +130 -0
  37. package/dist/elements/a-expander.css +1 -0
  38. package/dist/elements/a-expander.d.ts +28 -0
  39. package/dist/elements/a-expander.js +237 -0
  40. package/dist/elements/a-icon.d.ts +14 -0
  41. package/dist/elements/a-icon.shapes.css +1 -1
  42. package/dist/elements/a-icon.shapes.d.ts +10 -1
  43. package/dist/elements/a-icon.shapes.js +11 -1
  44. package/dist/elements/a-input.css +1 -0
  45. package/dist/elements/a-input.d.ts +68 -0
  46. package/dist/elements/a-input.js +511 -0
  47. package/dist/elements/a-menu-group.css +1 -0
  48. package/dist/elements/a-menu-group.d.ts +13 -0
  49. package/dist/elements/a-menu-group.js +15 -0
  50. package/dist/elements/a-menu-item.css +1 -0
  51. package/dist/elements/a-menu-item.d.ts +47 -0
  52. package/dist/elements/a-menu-item.js +30 -0
  53. package/dist/elements/a-menu-separator.css +1 -0
  54. package/dist/elements/a-menu-separator.d.ts +13 -0
  55. package/dist/elements/a-menu-separator.js +15 -0
  56. package/dist/elements/a-menu.css +1 -0
  57. package/dist/elements/a-menu.d.ts +183 -0
  58. package/dist/elements/a-menu.js +763 -0
  59. package/dist/elements/a-progress.css +1 -1
  60. package/dist/elements/a-progress.d.ts +12 -0
  61. package/dist/elements/a-progress.js +1 -0
  62. package/dist/elements/a-radio-group.css +1 -0
  63. package/dist/elements/a-radio-group.d.ts +33 -0
  64. package/dist/elements/a-radio-group.js +160 -0
  65. package/dist/elements/a-radio.css +1 -0
  66. package/dist/elements/a-radio.d.ts +14 -0
  67. package/dist/elements/a-radio.js +46 -0
  68. package/dist/elements/a-tag.css +1 -1
  69. package/dist/elements/a-text.css +1 -1
  70. package/dist/elements/a-text.d.ts +42 -3
  71. package/dist/elements/a-text.js +73 -33
  72. package/dist/elements/a-tooltip.d.ts +43 -11
  73. package/dist/elements/a-tooltip.js +46 -51
  74. package/dist/elements/index.d.ts +9 -0
  75. package/dist/elements/index.js +27 -0
  76. package/dist/general_types.d.ts +468 -15
  77. package/dist/index.d.ts +16 -0
  78. package/dist/index.js +16 -0
  79. package/dist/jsx-runtime.d.ts +42 -7
  80. package/dist/jsx-runtime.js +14 -2
  81. package/dist/tokens.css +1 -1
  82. package/package.json +1 -1
@@ -0,0 +1,119 @@
1
+ import type { BaseProps } from "../general_types";
2
+ /** The element's `statechange` payload. `next`/`prev` are values (`null` = nothing
3
+ * selected); `reason` distinguishes a user pick from a form reset / bfcache restore. */
4
+ type StateReason = "user" | "reset" | "restore";
5
+ type StateDetail = {
6
+ next: string | null;
7
+ prev: string | null;
8
+ reason: StateReason;
9
+ };
10
+ type StateChangeEvent = CustomEvent<StateDetail>;
11
+ /** Snapshot passed as the 2nd argument to `onAnyChange` — the new value plus the
12
+ * field name, so you don't poke at `event.target`. Mirrors `Input`/`Checkbox`. */
13
+ export interface RadioChangeAttrs {
14
+ value: string | null;
15
+ name?: string;
16
+ }
17
+ /** One option in a `<RadioGroup>`. The wrapper renders an `<a-radio>` per entry. */
18
+ export interface RadioOption {
19
+ /** This option's identity and the value submitted when it's selected.
20
+ * Must be unique within the group (a dev-only warning fires on duplicates). */
21
+ value: string;
22
+ /** The visible label. */
23
+ label?: React.ReactNode;
24
+ /** Secondary text under the label — explanatory copy, like `Input`'s hint. */
25
+ hint?: React.ReactNode;
26
+ /** Disable just this option (skipped by keyboard, dropped from the tab order). */
27
+ disabled?: boolean;
28
+ /** Override this one option's tone (defaults to the group's `tone`). */
29
+ tone?: "brand" | "neutral" | "info" | "success" | "warning" | "critical" | (string & {});
30
+ /** Override this one option's size (defaults to the group's `size`). */
31
+ size?: "small" | "medium" | "large";
32
+ /** Override this one option's priority (defaults to the group's `priority`). */
33
+ priority?: "primary" | "secondary";
34
+ }
35
+ /** Public props for `<RadioGroup>` — the single-select container. */
36
+ export interface RadioGroupProps extends Omit<BaseProps, "children" | "onChange"> {
37
+ /** The options. The wrapper renders one `<a-radio>` per entry and computes its
38
+ * `selected` / roving `tabindex` / `role` declaratively. */
39
+ options: RadioOption[];
40
+ /** Controlled selected value. When provided, the consumer owns selection: the
41
+ * group follows this prop and a pick only *requests* a change via
42
+ * `onStateChange` (reject by not updating). Leave undefined for uncontrolled. */
43
+ value?: string;
44
+ /** Initial selected value for the uncontrolled case (the wrapper then owns the
45
+ * selection in local state). */
46
+ defaultValue?: string;
47
+ /** Fired whenever selection changes — event-first. `detail` is
48
+ * `{ next, prev, reason }`: `next`/`prev` are values (`null` = nothing selected);
49
+ * `reason` is `'user'` | `'reset'` | `'restore'`. A `'user'` pick fires *before*
50
+ * applying and is **cancelable** — `event.preventDefault()` vetoes it
51
+ * (uncontrolled), or in controlled mode answer by updating `value` (reject by
52
+ * doing nothing). `'reset'` (form reset) and `'restore'` (bfcache / autofill) are
53
+ * not cancelable — filter on `reason` if you only track user picks. */
54
+ onStateChange?: (event: StateChangeEvent, detail: StateDetail) => void;
55
+ /** Fired *after* the selection changes — a native `change` event (the post-apply
56
+ * counterpart to `onStateChange`). Not cancelable; for a controlled group it
57
+ * fires once you've updated `value`. */
58
+ onChange?: (event: Event) => void;
59
+ /** Like `onChange`, but with a `{ value, name }` snapshot as the 2nd argument —
60
+ * the ergonomic "just give me the new value" callback (mirrors `Input`). */
61
+ onAnyChange?: (event: Event, attrs: RadioChangeAttrs) => void;
62
+ /** Fired when focus enters the group (any option) — wired to `focusin`, since
63
+ * focus lands on an individual option, not the group element itself. */
64
+ onFocus?: (event: FocusEvent) => void;
65
+ /** Fired when focus leaves the group entirely — wired to `focusout`. */
66
+ onBlur?: (event: FocusEvent) => void;
67
+ /** Form field name — the group submits one `name=value` (it's the
68
+ * form-associated element). */
69
+ name?: string;
70
+ /** Plain-text label for the whole group, rendered above the options. */
71
+ label?: string;
72
+ /** Plain-text description for the group, rendered directly under `label` (above
73
+ * the options) — typically instructional copy. Per-option helper text goes on
74
+ * the option's own `hint` instead. */
75
+ hint?: string;
76
+ /** Validation/feedback tone for the group `hint` — recolours it (same tone set
77
+ * as `Input`'s `status`). Use `critical` for an error message, etc.; omit for
78
+ * the neutral default.
79
+ * @defaultValue 'neutral' */
80
+ status?: "neutral" | "brand" | "info" | "success" | "warning" | "critical";
81
+ /** Tone applied to every option (an option's own `tone` wins), or any literal
82
+ * CSS color for a one-off custom tone. Tints fill, label, hint, and the
83
+ * unselected ring border. Named tones track light/dark mode.
84
+ * @defaultValue 'neutral' */
85
+ tone?: "brand" | "neutral" | "info" | "success" | "warning" | "critical" | (string & {});
86
+ /** Size applied to every option (an option's own `size` wins).
87
+ * @defaultValue 'medium' */
88
+ size?: "small" | "medium" | "large";
89
+ /** Visual priority applied to every option (an option's own `priority` wins).
90
+ * `primary` fills the selected ring with the tone colour and draws a white dot;
91
+ * `secondary` keeps the ring unfilled and draws the border + dot in the tone
92
+ * colour (an outlined look).
93
+ * @defaultValue 'primary' */
94
+ priority?: "primary" | "secondary";
95
+ /** Disable the whole group. */
96
+ disabled?: boolean;
97
+ /** Layout + arrow-key axis.
98
+ * @defaultValue 'vertical' */
99
+ orientation?: "vertical" | "horizontal";
100
+ }
101
+ /**
102
+ * `<RadioGroup>` — a single-select radio control, rendered from `options`.
103
+ *
104
+ * This wrapper is convenience over the web components: it renders an `<a-radio>`
105
+ * per option and, crucially, owns the two **declarative** DOM concerns the
106
+ * elements deliberately don't touch — the roving **`tabindex`** (so keyboard Tab
107
+ * lands on the right option) and each radio's **`role`**. Selection itself lives
108
+ * in `<a-radio-group>` off-DOM (it sets each radio's `selected` property), so the
109
+ * elements never mutate the DOM; this wrapper just reflects the current value into
110
+ * `tabindex` on re-render. (Hand-assembling raw `<a-radio-group>` / `<a-radio>`
111
+ * also works — see the element docs — this wrapper is the ergonomic path.)
112
+ *
113
+ * Controlled (`value` + `onStateChange`) or uncontrolled (`defaultValue`); either
114
+ * way the wrapper holds the value so it can compute the roving `tabindex`.
115
+ *
116
+ * Requires `@antadesign/anta/elements` (client-side only).
117
+ */
118
+ export declare const RadioGroup: ({ options, value, defaultValue, onStateChange, onChange, onAnyChange, onFocus, onBlur, name, label, hint, status, tone, size, priority, disabled, orientation, className, style, ...rest }: RadioGroupProps) => any;
119
+ export {};
@@ -0,0 +1,108 @@
1
+ import { jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
+ import { useId, useState } from "../jsx-runtime";
3
+ import { nativeStateChange, toneStyle } from "../anta_helpers";
4
+ const radioAttrsOf = (el) => ({
5
+ value: el?.value ?? null,
6
+ name: el?.getAttribute?.("name") ?? void 0
7
+ });
8
+ const RadioGroup = ({
9
+ options,
10
+ value,
11
+ defaultValue,
12
+ onStateChange,
13
+ onChange,
14
+ onAnyChange,
15
+ onFocus,
16
+ onBlur,
17
+ name,
18
+ label,
19
+ hint,
20
+ status,
21
+ tone,
22
+ size,
23
+ priority,
24
+ disabled,
25
+ orientation,
26
+ className,
27
+ style,
28
+ ...rest
29
+ }) => {
30
+ const controlled = value !== void 0;
31
+ const [internalValue, setInternalValue] = useState(defaultValue);
32
+ const currentValue = controlled ? value : internalValue;
33
+ const seen = /* @__PURE__ */ new Set();
34
+ for (const o of options) {
35
+ if (seen.has(o.value))
36
+ console.warn(`[anta] <RadioGroup> duplicate option value ${JSON.stringify(o.value)} \u2014 values must be unique.`);
37
+ seen.add(o.value);
38
+ }
39
+ const labelId = useId();
40
+ const hintId = useId();
41
+ const tabStopValue = disabled ? void 0 : options.find((o) => o.value === currentValue)?.value ?? options.find((o) => !o.disabled)?.value;
42
+ const onstatechange = (e) => {
43
+ const { event, detail } = nativeStateChange(e);
44
+ if (!detail) return;
45
+ onStateChange?.(event, detail);
46
+ if (controlled) return;
47
+ if (detail.reason === "user" && event.defaultPrevented) return;
48
+ setInternalValue(detail.next ?? void 0);
49
+ };
50
+ const onchange = onChange || onAnyChange ? (e) => {
51
+ onChange?.(e);
52
+ onAnyChange?.(e, radioAttrsOf(e.currentTarget));
53
+ } : void 0;
54
+ return /* @__PURE__ */ jsxs(
55
+ "a-radio-group",
56
+ {
57
+ role: "radiogroup",
58
+ "aria-disabled": disabled ? "true" : void 0,
59
+ "aria-labelledby": label ? labelId : void 0,
60
+ "aria-describedby": hint ? hintId : void 0,
61
+ ...rest,
62
+ state: controlled ? value : void 0,
63
+ "default-state": !controlled ? defaultValue : void 0,
64
+ name,
65
+ status: status && status !== "neutral" ? status : void 0,
66
+ tone: tone && tone !== "neutral" ? tone : void 0,
67
+ size: size && size !== "medium" ? size : void 0,
68
+ priority: priority && priority !== "primary" ? priority : void 0,
69
+ disabled: disabled ? "" : void 0,
70
+ orientation: orientation && orientation !== "vertical" ? orientation : void 0,
71
+ onstatechange,
72
+ onchange,
73
+ onfocusin: onFocus,
74
+ onfocusout: onBlur,
75
+ class: className,
76
+ style: toneStyle(tone, "--radio-tone-source", style),
77
+ children: [
78
+ label && /* @__PURE__ */ jsx("a-radio-group-label", { id: labelId, children: label }),
79
+ hint && /* @__PURE__ */ jsx("a-radio-group-hint", { id: hintId, children: hint }),
80
+ /* @__PURE__ */ jsx("a-radio-list", { children: options.map((o) => {
81
+ const optDisabled = disabled || o.disabled;
82
+ return /* @__PURE__ */ jsxs(
83
+ "a-radio",
84
+ {
85
+ role: "radio",
86
+ value: o.value,
87
+ "aria-disabled": optDisabled ? "true" : void 0,
88
+ tabIndex: o.value === tabStopValue ? 0 : -1,
89
+ tone: o.tone && o.tone !== "neutral" ? o.tone : void 0,
90
+ size: o.size && o.size !== "medium" ? o.size : void 0,
91
+ priority: o.priority && o.priority !== "primary" ? o.priority : void 0,
92
+ disabled: o.disabled ? "" : void 0,
93
+ style: toneStyle(o.tone, "--radio-tone-source"),
94
+ children: [
95
+ /* @__PURE__ */ jsx("a-radio-label", { children: o.label }),
96
+ o.hint != null && /* @__PURE__ */ jsx("a-radio-hint", { children: o.hint })
97
+ ]
98
+ },
99
+ o.value
100
+ );
101
+ }) })
102
+ ]
103
+ }
104
+ );
105
+ };
106
+ export {
107
+ RadioGroup
108
+ };
@@ -10,12 +10,19 @@ export interface TagProps extends BaseProps {
10
10
  * label; the color + weight contrast does the separating. */
11
11
  value?: string;
12
12
  /** Semantic tone, or any literal CSS color (`'#ff1493'`, `'rebeccapurple'`)
13
- * for a one-off custom tone. Named tones map to the `--text-2-{tone}` /
14
- * `--bg-4-{tone}` palette; a custom color keeps its hue while lightness
15
- * and chroma are pinned. `'neutral'` (the default) is the gray tag —
16
- * the same as omitting `tone`.
13
+ * for a one-off custom tone. Each tone renders the secondary tag style:
14
+ * `--text-3-{tone}` text over an alpha tint of the tone's hue (fill + a
15
+ * slightly stronger hairline border). A custom color is tinted the same
16
+ * way, with the text deepened to a readable foreground. `'neutral'` (the
17
+ * default) is the gray tag — the same as omitting `tone`.
17
18
  * @defaultValue neutral */
18
19
  tone?: 'neutral' | 'brand' | 'info' | 'success' | 'warning' | 'critical' | (string & {});
20
+ /** Emphasis level. `secondary` (the default) is the subtle alpha-tint
21
+ * fill; `primary` is a solid fill with white text; `tertiary` is a
22
+ * transparent outline. Omitting it (or passing `'secondary'`) renders
23
+ * the default and emits no DOM attribute.
24
+ * @defaultValue secondary */
25
+ priority?: 'primary' | 'secondary' | 'tertiary';
19
26
  /** Size variant. `small` = 16px tall, `medium` = 20px, `large` = 24px
20
27
  * (matching `Button`). Omit the attribute or pass `'medium'` for the
21
28
  * default — both render identically and emit no DOM attribute.
@@ -45,6 +52,32 @@ export interface TagProps extends BaseProps {
45
52
  * Requires `@antadesign/anta/elements` to be imported (client-side only)
46
53
  * so the CSS ships with the page.
47
54
  *
55
+ * Styling notes (`a-tag.css` ships comment-free):
56
+ * - Sizing is intrinsic — no fixed height, like `<a-button>`: height falls
57
+ * out of `line-height` + `padding-block` + the 0.5px border (the base
58
+ * 1.5px block padding compensates the half-pixel border to keep the pill
59
+ * at 20px), so text is never clipped and the padding tokens can be
60
+ * retuned freely. An edge icon trims ~2px off its side's padding
61
+ * (optical), and icons scale to `--tag-icon-size`.
62
+ * - Color comes from the theme-aware semantic tokens, so named tones need
63
+ * no `.dark` rules; the hairline border and the segment divider both
64
+ * derive from `--tag-text`, so every tone gets a matching edge. A lone
65
+ * `label` is dropped into `<a-tag-value>` by this wrapper so it keeps the
66
+ * default weight; `<a-tag-label>` only renders as the bold key before a
67
+ * value (weight contrast does the separating — no divider).
68
+ * - Segmented children: raw children (not the structured label/value/icon
69
+ * elements) get a hairline left border per segment after the first; the
70
+ * flex `gap` sits left of the border and `padding-left` balances it.
71
+ * - Custom tones (any non-named `tone`) keep the source hue with
72
+ * lightness/chroma pinned via oklch relative color; the `--_tag-*` knobs
73
+ * are the only numbers to tune and the `.dark` block re-tunes them. The
74
+ * wrapper writes `--tag-tone-source` inline; a typed `attr()` fallback
75
+ * picks up raw `<a-tag tone="…">` on Chrome 133+/Safari 18.2+.
76
+ * - `nocaps` drops the uppercase transform and tightens tracking to the
77
+ * body-text 0.02ch (uppercase needs 0.08ch); tabular figures + `ss05`
78
+ * stay on. Large + nocaps bumps to 13px, keeping the same +1px
79
+ * over-uppercase relationship medium has, with height unchanged.
80
+ *
48
81
  * @example Basic usage
49
82
  * ```tsx
50
83
  * <Tag tone="success" label="Running" />
@@ -52,4 +85,4 @@ export interface TagProps extends BaseProps {
52
85
  * <Tag tone="brand" size="small" nocaps value="v2.1.0" />
53
86
  * ```
54
87
  */
55
- export declare const Tag: ({ icon, iconTrailing, label, value, tone, size, nocaps, className, style, children, ...rest }: TagProps) => any;
88
+ export declare const Tag: ({ icon, iconTrailing, label, value, tone, priority, size, nocaps, className, style, children, ...rest }: TagProps) => any;
@@ -1,11 +1,13 @@
1
1
  import { jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
- const NAMED_TONES = /* @__PURE__ */ new Set(["neutral", "brand", "info", "success", "warning", "critical"]);
2
+ import { toneStyle } from "../anta_helpers";
3
+ import { Icon } from "./Icon";
3
4
  const Tag = ({
4
5
  icon,
5
6
  iconTrailing,
6
7
  label,
7
8
  value,
8
9
  tone,
10
+ priority,
9
11
  size,
10
12
  nocaps,
11
13
  className,
@@ -13,24 +15,26 @@ const Tag = ({
13
15
  children,
14
16
  ...rest
15
17
  }) => {
16
- const isCustomTone = tone != null && !NAMED_TONES.has(tone);
17
- const computedStyle = isCustomTone ? { ...style, ["--tag-tone-source"]: tone } : style;
18
+ const computedStyle = toneStyle(tone, "--tag-tone-source", style);
18
19
  const hasValue = value != null;
20
+ const isIconOnly = icon != null && label == null && value == null && children == null && iconTrailing == null;
19
21
  return /* @__PURE__ */ jsxs(
20
22
  "a-tag",
21
23
  {
22
24
  tone,
25
+ "aria-label": isIconOnly ? `${icon} tag` : void 0,
26
+ priority: priority && priority !== "secondary" ? priority : void 0,
23
27
  size: size && size !== "medium" ? size : void 0,
24
28
  nocaps: nocaps ? "" : void 0,
25
29
  class: className,
26
30
  style: computedStyle,
27
31
  ...rest,
28
32
  children: [
29
- icon && /* @__PURE__ */ jsx("a-icon", { shape: icon, "aria-hidden": "true" }),
33
+ icon && /* @__PURE__ */ jsx(Icon, { shape: icon }),
30
34
  label != null && (hasValue ? /* @__PURE__ */ jsx("a-tag-label", { children: label }) : /* @__PURE__ */ jsx("a-tag-value", { children: label })),
31
35
  hasValue && /* @__PURE__ */ jsx("a-tag-value", { children: value }),
32
36
  children,
33
- iconTrailing && /* @__PURE__ */ jsx("a-icon", { shape: iconTrailing, "aria-hidden": "true" })
37
+ iconTrailing && /* @__PURE__ */ jsx(Icon, { shape: iconTrailing })
34
38
  ]
35
39
  }
36
40
  );
@@ -1,28 +1,42 @@
1
1
  import type { BaseProps } from "../general_types";
2
- export interface TextProps extends BaseProps {
2
+ /** Truncation / expansion axis. `expandable` only takes effect with `truncate`;
3
+ * `collapsible` only with `expandable` — so the type forbids `collapsible`
4
+ * unless `expandable` is set (a dynamic `expandable={cond}` is still allowed). */
5
+ type ExpandMode = {
6
+ expandable?: false;
7
+ collapsible?: never;
8
+ } | {
9
+ /** Show a fade hint and chevron over the truncated text and let the user
10
+ * expand it by clicking the chevron region or pressing Enter while the
11
+ * chevron has keyboard focus. Only takes effect together with `truncate`.
12
+ * On its own, expanding is **one-way** — the control is removed once
13
+ * expanded; add `collapsible` for a two-way toggle. */
14
+ expandable: boolean;
15
+ /** Let the reader collapse back after expanding: the chevron becomes a
16
+ * "Show more" / "Show less" toggle that stays visible while expanded.
17
+ * Only takes effect together with `expandable`. */
18
+ collapsible?: boolean;
19
+ };
20
+ export type TextProps = BaseProps & {
3
21
  /** Visual priority. Maps to text-1..text-5 (`primary` = text-1, the
4
22
  * strongest). The default is `secondary` (text-2) — body text reads a
5
23
  * step softer than the strongest foreground; pass `primary` for emphasis.
6
24
  * @defaultValue secondary */
7
25
  priority?: 'primary' | 'secondary' | 'tertiary' | 'quaternary' | 'quinary';
8
26
  /** Color tint. Applies the matching `--text-{N}-{tone}` palette. */
9
- tone?: 'brand' | 'success' | 'critical' | 'warning' | 'info';
27
+ tone?: 'brand' | 'info' | 'success' | 'warning' | 'critical';
10
28
  /** Type scale. `small` = 13/16, `medium` = 15/20, `large` = 17/24.
11
29
  * @defaultValue medium */
12
30
  size?: 'small' | 'medium' | 'large';
13
31
  /** Render as inline-block instead of the default block element. */
14
32
  inline?: boolean;
15
33
  /** Truncate with a trailing ellipsis. `true` (or `1`) clamps to a
16
- * single line; any integer ≥ 2 clamps to that many lines. Uses the
17
- * `-webkit-line-clamp` technique, supported in all major browsers
18
- * (Firefox 68+, Chrome, Safari, Edge). */
34
+ * single line; any integer ≥ 2 clamps to that many lines; `0` or a
35
+ * negative value means no truncation. Uses the `-webkit-line-clamp`
36
+ * technique, supported in all major browsers (Firefox 68+, Chrome,
37
+ * Safari, Edge). */
19
38
  truncate?: boolean | number;
20
- /** Show a fade hint and chevron over the truncated text and let the
21
- * user expand it by clicking the chevron region or pressing Enter
22
- * while the chevron has keyboard focus. Only takes effect together
23
- * with `truncate`. */
24
- expandable?: boolean;
25
- }
39
+ } & ExpandMode;
26
40
  /**
27
41
  * Block-level text container with priority and tone support.
28
42
  *
@@ -45,4 +59,5 @@ export interface TextProps extends BaseProps {
45
59
  * <Text truncate={3} expandable>…long paragraph…</Text>
46
60
  * ```
47
61
  */
48
- export declare const Text: ({ priority, tone, size, inline, truncate, expandable, className, style, children, ...rest }: TextProps) => any;
62
+ export declare const Text: ({ priority, tone, size, inline, truncate, expandable, collapsible, className, style, children, ...rest }: TextProps) => any;
63
+ export {};
@@ -1,7 +1,9 @@
1
1
  import { jsx } from "@antadesign/anta/jsx-runtime";
2
- const Text = ({ priority, tone, size, inline, truncate, expandable, className, style, children, ...rest }) => {
3
- const lineCount = typeof truncate === "number" ? truncate : truncate ? 1 : null;
2
+ const Text = ({ priority, tone, size, inline, truncate, expandable, collapsible, className, style, children, ...rest }) => {
3
+ const n = typeof truncate === "number" ? truncate : truncate ? 1 : null;
4
+ const lineCount = n != null && n >= 1 ? n : null;
4
5
  const computedStyle = lineCount != null ? { ...style, ["--line-clamp"]: lineCount } : style;
6
+ const canExpand = expandable && lineCount != null;
5
7
  return /* @__PURE__ */ jsx(
6
8
  "a-text",
7
9
  {
@@ -10,7 +12,8 @@ const Text = ({ priority, tone, size, inline, truncate, expandable, className, s
10
12
  size,
11
13
  inline: inline ? "" : void 0,
12
14
  truncate: lineCount != null ? String(lineCount) : void 0,
13
- expandable: expandable && truncate ? "" : void 0,
15
+ expandable: canExpand ? "" : void 0,
16
+ collapsible: canExpand && collapsible ? "" : void 0,
14
17
  class: className,
15
18
  style: computedStyle,
16
19
  ...rest,
@@ -9,7 +9,7 @@ export interface TitleProps extends BaseProps {
9
9
  * @defaultValue primary */
10
10
  priority?: 'primary' | 'secondary' | 'tertiary' | 'quaternary' | 'quinary';
11
11
  /** Color tint. Applies the matching `--text-{N}-{tone}` palette. */
12
- tone?: 'brand' | 'success' | 'critical' | 'warning' | 'info';
12
+ tone?: 'brand' | 'info' | 'success' | 'warning' | 'critical';
13
13
  }
14
14
  /**
15
15
  * Block-level heading with level 1-6, priority, and tone.
@@ -24,6 +24,15 @@ export interface TitleProps extends BaseProps {
24
24
  * so use a real heading tag if SEO matters and you don't need the
25
25
  * `tone` / `priority` props.
26
26
  *
27
+ * Styling notes (`a-title.css` ships comment-free): `<a-title>` is
28
+ * intentionally CSS-only — no JS, no shadow DOM, not even
29
+ * `customElements.define`; the browser treats it as a generic unknown
30
+ * element and the CSS gives it block layout, the demi-bold variable-font
31
+ * weight, and the per-level type scale + vertical rhythm. The matching
32
+ * `h1`–`h6` rules in `src/reset.css` are kept in lockstep so raw markup
33
+ * looks the same as `<Title level={n}>`. The tone × priority color matrix
34
+ * has the same shape as `a-text`'s.
35
+ *
27
36
  * Requires `@antadesign/anta/elements` to be imported (client-side only)
28
37
  * so the CSS ships with the page.
29
38
  *
@@ -1 +1 @@
1
- @layer anta{a-button,a[role=button]{--button-primary-fg: #ffffff;--button-focus: #503cb4;--bg-primary-disabled: #ece9ec;--bg-secondary-disabled: #f6f4f6;--fg-disabled-primary: #afa9b1;--fg-disabled: #afa9b1;--button-loading-stripe: 14px;--button-loading-stripe-gap: 16px;--button-loading-period: 30px;--button-loading-angle: 125deg;--button-loading-x-period: calc( var(--button-loading-period) / cos(var(--button-loading-angle) - 90deg) );--button-loading-opacity: .15;--button-loading-blur: 2.5px;--button-bg-primary-rest: #635b65;--button-bg-primary-hover: #534c57;--button-bg-primary-active: #49424c;--button-fg-primary-rest: var(--button-primary-fg);--button-bg-secondary-rest: #45374c12;--button-bg-secondary-hover: #44374b1a;--button-bg-secondary-active: #44374b26;--button-fg-secondary-rest: #534c57;--button-fg-secondary-hover: #49424c;--button-fg-secondary-l-shift: .05;--button-fg-tertiary-rest: #635b65;--button-fg-tertiary-hover: #534c57;--button-bg-tertiary-hover: #44374b0d;--button-bg-tertiary-active: #44374b1a;--button-fg-quaternary-rest: #635b65;--button-fg-quaternary-hover: #49424c;--button-padding-x: 9px;--button-padding-y: 5px;--button-max-width: 100%;--button-max-height: 100%;--button-timing-in: 75ms ease-in;--button-timing-active: 50ms linear;--button-timing-out: .15s ease-out;--button-loading-duration: .5s}a-button[tone=brand],a[role=button][tone=brand]{--button-bg-primary-rest: #5f4bc3;--button-bg-primary-hover: #503cb4;--button-bg-primary-active: #483493;--button-fg-primary-rest: var(--button-primary-fg);--button-bg-secondary-rest: #7460d71a;--button-bg-secondary-hover: #7460d726;--button-bg-secondary-active: #7460d733;--button-fg-secondary-rest: #5f4bc3;--button-fg-secondary-hover: #503cb4;--button-fg-tertiary-rest: #5f4bc3;--button-fg-tertiary-hover: #503cb4;--button-bg-tertiary-hover: #7460d71a;--button-bg-tertiary-active: #7460d726;--button-fg-quaternary-rest: #5f4bc3;--button-fg-quaternary-hover: #483493}a-button[tone=critical],a[role=button][tone=critical]{--button-bg-primary-rest: #c9302c;--button-bg-primary-hover: #b02120;--button-bg-primary-active: #a01c1c;--button-fg-primary-rest: var(--button-primary-fg);--button-bg-secondary-rest: #de45451a;--button-bg-secondary-hover: #de454526;--button-bg-secondary-active: #de454533;--button-fg-secondary-rest: #c9302c;--button-fg-secondary-hover: #b02120;--button-fg-tertiary-rest: #c9302c;--button-fg-tertiary-hover: #b02120;--button-bg-tertiary-hover: #de45451a;--button-bg-tertiary-active: #de454533;--button-fg-quaternary-rest: #c9302c;--button-fg-quaternary-hover: #a01c1c}a-button[tone=info],a[role=button][tone=info]{--button-bg-primary-rest: #1f6eb2;--button-bg-primary-hover: #1a5b93;--button-bg-primary-active: #175082;--button-fg-primary-rest: var(--button-primary-fg);--button-bg-secondary-rest: #2686d91a;--button-bg-secondary-hover: #2686d926;--button-bg-secondary-active: #2686d933;--button-fg-secondary-rest: #1f6eb2;--button-fg-secondary-hover: #1a5b93;--button-fg-tertiary-rest: #1f6eb2;--button-fg-tertiary-hover: #1a5b93;--button-bg-tertiary-hover: #2686d91a;--button-bg-tertiary-active: #2686d926;--button-fg-quaternary-rest: #1f6eb2;--button-fg-quaternary-hover: #175082}a-button[tone=success],a[role=button][tone=success]{--button-bg-primary-rest: #2a7e43;--button-bg-primary-hover: #226737;--button-bg-primary-active: #1f5c31;--button-fg-primary-rest: var(--button-primary-fg);--button-bg-secondary-rest: #3295501a;--button-bg-secondary-hover: #32955026;--button-bg-secondary-active: #32955033;--button-fg-secondary-rest: #2a7e43;--button-fg-secondary-hover: #226737;--button-fg-tertiary-rest: #2a7e43;--button-fg-tertiary-hover: #226737;--button-bg-tertiary-hover: #3295501a;--button-bg-tertiary-active: #32955026;--button-fg-quaternary-rest: #2a7e43;--button-fg-quaternary-hover: #1f5c31}a-button[tone=warning],a[role=button][tone=warning]{--button-bg-primary-rest: #c37416;--button-bg-primary-hover: #ae6613;--button-bg-primary-active: #995200;--button-fg-primary-rest: var(--button-primary-fg);--button-bg-secondary-rest: #c374161a;--button-bg-secondary-hover: #c3741626;--button-bg-secondary-active: #c3741633;--button-fg-secondary-rest: #c37416;--button-fg-secondary-hover: #ae6613;--button-fg-tertiary-rest: #c37416;--button-fg-tertiary-hover: #ae6613;--button-bg-tertiary-hover: #c374161a;--button-bg-tertiary-active: #c3741633;--button-fg-quaternary-rest: #c37416;--button-fg-quaternary-hover: #995200}.dark a-button,.dark a[role=button]{--button-focus: #a897fc;--bg-primary-disabled: #272329;--bg-secondary-disabled: #1d1a1e;--fg-disabled-primary: #776e77;--fg-disabled: #635b65;--button-bg-primary-rest: #534c57;--button-bg-primary-hover: #635b65;--button-bg-primary-active: #938d96;--button-bg-secondary-rest: #e4d1ef1c;--button-bg-secondary-hover: #e4d1ef24;--button-bg-secondary-active: #e4d1ef2e;--button-fg-secondary-rest: #c1b9c1;--button-fg-secondary-hover: #d4ced4;--button-fg-secondary-l-shift: 0;--button-fg-tertiary-rest: #afa9b1;--button-fg-tertiary-hover: #afa9b1;--button-bg-tertiary-hover: #e4d1ef14;--button-bg-tertiary-active: #e4d1ef21;--button-fg-quaternary-rest: #afa9b1;--button-fg-quaternary-hover: #cac4ca}.dark a-button[tone=brand],.dark a[role=button][tone=brand]{--button-bg-primary-rest: #503cb4;--button-bg-primary-hover: #5f4bc3;--button-bg-primary-active: #7460d7;--button-bg-secondary-rest: #7460d73b;--button-bg-secondary-hover: #7460d747;--button-bg-secondary-active: #7460d754;--button-fg-secondary-rest: #c5baff;--button-fg-secondary-hover: #d2cbf6;--button-fg-tertiary-rest: #ada0ee;--button-fg-tertiary-hover: #bcb1f1;--button-bg-tertiary-hover: #7460d73b;--button-bg-tertiary-active: #7460d747;--button-fg-quaternary-rest: #ada0ee;--button-fg-quaternary-hover: #c7bef4}.dark a-button[tone=critical],.dark a[role=button][tone=critical]{--button-bg-primary-rest: #b02120;--button-bg-primary-hover: #c9302c;--button-bg-primary-active: #de4545;--button-bg-secondary-rest: #de45453b;--button-bg-secondary-hover: #de454547;--button-bg-secondary-active: #de454554;--button-fg-secondary-rest: #ffabac;--button-fg-secondary-hover: #f4c2c2;--button-fg-tertiary-rest: #e78e90;--button-fg-tertiary-hover: #efa4a4;--button-bg-tertiary-hover: #de45453b;--button-bg-tertiary-active: #de454547;--button-fg-quaternary-rest: #e78e90;--button-fg-quaternary-hover: #ffabac}.dark a-button[tone=info],.dark a[role=button][tone=info]{--button-bg-primary-rest: #1a5b93;--button-bg-primary-hover: #1f6eb2;--button-bg-primary-active: #2686d9;--button-bg-secondary-rest: #2686d93b;--button-bg-secondary-hover: #2686d947;--button-bg-secondary-active: #2686d954;--button-fg-secondary-rest: #9ed2ff;--button-fg-secondary-hover: #bad6f3;--button-fg-tertiary-rest: #7db6e8;--button-fg-tertiary-hover: #93c5ec;--button-bg-tertiary-hover: #2686d93b;--button-bg-tertiary-active: #2686d947;--button-fg-quaternary-rest: #7db6e8;--button-fg-quaternary-hover: #a6cdef}.dark a-button[tone=success],.dark a[role=button][tone=success]{--button-bg-primary-rest: #226737;--button-bg-primary-hover: #2a7e43;--button-bg-primary-active: #329550;--button-bg-secondary-rest: #3295503b;--button-bg-secondary-hover: #32955047;--button-bg-secondary-active: #32955054;--button-fg-secondary-rest: #8ceca9;--button-fg-secondary-hover: #b3e5c2;--button-fg-tertiary-rest: #74cd8e;--button-fg-tertiary-hover: #88d7a0;--button-bg-tertiary-hover: #3295503b;--button-bg-tertiary-active: #32955047;--button-fg-quaternary-rest: #74cd8e;--button-fg-quaternary-hover: #9ddeb1}.dark a-button[tone=warning],.dark a[role=button][tone=warning]{--button-bg-primary-rest: #7f410b;--button-bg-primary-hover: #995200;--button-bg-primary-active: #ae6613;--button-bg-secondary-rest: #c374163b;--button-bg-secondary-hover: #c3741647;--button-bg-secondary-active: #c3741654;--button-fg-secondary-rest: #f7c06e;--button-fg-secondary-hover: #f3cc91;--button-fg-tertiary-rest: #e1a452;--button-fg-tertiary-hover: #edb25a;--button-bg-tertiary-hover: #c374163b;--button-bg-tertiary-active: #c3741647;--button-fg-quaternary-rest: #e1a452;--button-fg-quaternary-hover: #f0bf75}a-button,a[role=button]{user-select:none;display:inline-flex;align-items:center;gap:.5ch;overflow:hidden;cursor:pointer;flex-shrink:0;color:var(--button-fg);background-color:var(--button-bg);border-radius:4px;text-decoration:none;box-shadow:inset 0 0 1px color-mix(in oklch,currentColor,transparent 70%);font-size:15px;font-family:var(--sans-serif);font-weight:450;font-variation-settings:"wdth" 88,"slnt" 0,"ital" 0;font-feature-settings:"ss02","ss05";line-height:18px;letter-spacing:.05ch;text-overflow:ellipsis;text-wrap:nowrap;white-space:nowrap;max-width:var(--button-max-width);max-height:var(--button-max-height);padding-inline:var(--button-padding-x);padding-block:var(--button-padding-y);>a-button-label{display:inline-block;text-overflow:ellipsis;text-wrap:nowrap;white-space:nowrap;overflow:hidden;min-width:0;line-height:17px;padding-bottom:1px;&:has(>*){display:inline-flex;gap:.25ch}}>*{pointer-events:none}& a-icon{transition:color var(--button-timing-out)}transition:color var(--button-timing-out),background-color var(--button-timing-out);&:hover,&:focus-visible{transition:color var(--button-timing-in),background-color var(--button-timing-in);& a-icon{transition:color var(--button-timing-in)}}&:active,&[selected]{transition:color var(--button-timing-active),background-color var(--button-timing-active);& a-icon{transition:color var(--button-timing-active)}}&[disabled]{pointer-events:none;cursor:not-allowed;transition:none;& a-icon{transition:none}}&:focus-visible{outline:1px solid var(--button-focus);outline-offset:1px}&[priority=quaternary]{--button-timing-in: 0s;--button-timing-active: 0s;--button-timing-out: 0s}&[size=small]{--button-padding-y: 3px;--button-padding-x: 7px}&[size=large]{--button-padding-y: 7px;--button-padding-x: 13px}&[priority=tertiary],&[priority=quaternary]{&[underline=solid]{text-decoration:underline solid}&[underline=dashed]{text-decoration:underline dashed}&[underline=dotted]{text-decoration:underline dotted}&[underline]{text-decoration-color:color-mix(in srgb,currentColor 75%,transparent);text-decoration-thickness:1px;text-underline-offset:3px;&[selected]{text-decoration-color:var(--button-fg);text-decoration-thickness:1px}@media(hover:hover)and (pointer:fine){&:hover{text-decoration-color:var(--button-fg);text-decoration-thickness:1px}}}}&[priority=quaternary][paddingless]{--button-padding-x: 0;--button-padding-y: 0}&:has(>a-icon):not(:has(>a-icon~a-icon)):not(:has(>:not(a-icon):not(a-tooltip))){--button-padding-y: 5px;--button-padding-x: 5px;min-width:28px;min-height:28px;justify-content:center;&[size=small]{--button-padding-y: 3px;--button-padding-x: 3px;min-width:24px;min-height:24px}&[size=large]{--button-padding-y: 7px;--button-padding-x: 7px;min-width:32px;min-height:32px}}&:is(:has(>a-icon:first-child),:has(>a-tooltip:first-child+a-icon)):is(:has(>a-icon~a-icon),:has(>:not(a-icon):not(a-tooltip))){padding-inline-start:max(0px,var(--button-padding-x) - 2px)}&:is(:has(>a-icon:last-child),:has(>a-icon+a-tooltip:last-child)):is(:has(>a-icon~a-icon),:has(>:not(a-icon):not(a-tooltip))){padding-inline-end:max(0px,var(--button-padding-x) - 2px)}&[loading]{position:relative;pointer-events:none;cursor:wait;&:before{content:"";position:absolute;top:0;bottom:0;left:calc(0px - var(--button-loading-x-period));right:0;pointer-events:none;background:repeating-linear-gradient(var(--button-loading-angle),transparent 0,transparent var(--button-loading-stripe-gap),currentColor var(--button-loading-stripe-gap),currentColor calc(var(--button-loading-stripe-gap) + var(--button-loading-stripe)));opacity:var(--button-loading-opacity);filter:blur(var(--button-loading-blur));will-change:transform;animation:btn-loading-slide var(--button-loading-duration) linear infinite;animation-delay:-9999s}}&[tone]:not([tone=""],[tone=brand],[tone=neutral],[tone=critical],[tone=info],[tone=success],[tone=warning]){--button-tone-source: attr(tone type(<color>), transparent);--_tone-fg-l: .46;--_tone-fg-strong-l: .4;--_tone-fg-c: .17;--_tone-bg-l: .54;--_tone-bg-c: .16;--_tone-bg-a-rest: .1;--_tone-bg-a-hover: .15;--_tone-bg-a-active: .2;--_tone-primary-l-rest: .5;--_tone-primary-l-hover: .45;--_tone-primary-l-active: .4;--button-bg-primary-rest: oklch(from var(--button-tone-source) var(--_tone-primary-l-rest) c h);--button-bg-primary-hover: oklch(from var(--button-tone-source) var(--_tone-primary-l-hover) c h);--button-bg-primary-active: oklch(from var(--button-tone-source) var(--_tone-primary-l-active) c h);--button-fg-primary-rest: var(--button-primary-fg);--button-fg-secondary-rest: oklch(from var(--button-tone-source) var(--_tone-fg-l) var(--_tone-fg-c) h);--button-fg-secondary-hover: oklch(from var(--button-tone-source) var(--_tone-fg-strong-l) var(--_tone-fg-c) h);--button-fg-tertiary-rest: oklch(from var(--button-tone-source) var(--_tone-fg-l) var(--_tone-fg-c) h);--button-fg-tertiary-hover: oklch(from var(--button-tone-source) var(--_tone-fg-strong-l) var(--_tone-fg-c) h);--button-fg-quaternary-rest: oklch(from var(--button-tone-source) var(--_tone-fg-l) var(--_tone-fg-c) h);--button-fg-quaternary-hover: oklch(from var(--button-tone-source) var(--_tone-fg-strong-l) var(--_tone-fg-c) h);--button-bg-secondary-rest: oklch(from var(--button-tone-source) var(--_tone-bg-l) var(--_tone-bg-c) h / var(--_tone-bg-a-rest));--button-bg-secondary-hover: oklch(from var(--button-tone-source) var(--_tone-bg-l) var(--_tone-bg-c) h / var(--_tone-bg-a-hover));--button-bg-secondary-active: oklch(from var(--button-tone-source) var(--_tone-bg-l) var(--_tone-bg-c) h / var(--_tone-bg-a-active));--button-bg-tertiary-hover: oklch(from var(--button-tone-source) var(--_tone-bg-l) var(--_tone-bg-c) h / var(--_tone-bg-a-rest));--button-bg-tertiary-active: oklch(from var(--button-tone-source) var(--_tone-bg-l) var(--_tone-bg-c) h / var(--_tone-bg-a-hover))}.dark &[tone]:not([tone=""],[tone=brand],[tone=neutral],[tone=critical],[tone=info],[tone=success],[tone=warning]){--_tone-fg-l: .78;--_tone-fg-strong-l: .85;--_tone-fg-c: .11;--_tone-bg-l: .58;--_tone-bg-a-rest: .23;--_tone-bg-a-hover: .28;--_tone-bg-a-active: .33;--_tone-primary-l-rest: .45;--_tone-primary-l-hover: .5;--_tone-primary-l-active: .57}--button-fg: oklch(from var(--button-fg-secondary-rest) calc(l - var(--button-fg-secondary-l-shift)) c h);--button-bg: var(--button-bg-secondary-rest);@media(hover:hover)and (pointer:fine){&:hover{--button-fg: var(--button-fg-secondary-hover);--button-bg: var(--button-bg-secondary-hover)}}&:active,&[selected]{--button-fg: var(--button-fg-secondary-hover);--button-bg: var(--button-bg-secondary-active)}&[priority=primary]{--button-fg: var(--button-fg-primary-rest);--button-bg: var(--button-bg-primary-rest);box-shadow:none;@media(hover:hover)and (pointer:fine){&:hover{--button-bg: var(--button-bg-primary-hover)}}&:active,&[selected]{--button-bg: var(--button-bg-primary-active)}}&[priority=tertiary]{--button-fg: var(--button-fg-tertiary-rest);--button-bg: transparent;box-shadow:none;@media(hover:hover)and (pointer:fine){&:hover{--button-fg: var(--button-fg-tertiary-hover);--button-bg: var(--button-bg-tertiary-hover)}}&:active,&[selected]{--button-fg: var(--button-fg-tertiary-hover);--button-bg: var(--button-bg-tertiary-active)}}&[priority=quaternary]{--button-fg: var(--button-fg-quaternary-rest);--button-bg: transparent;box-shadow:none;font-weight:400;letter-spacing:.06ch;@media(hover:hover)and (pointer:fine){&:hover{--button-fg: var(--button-fg-quaternary-hover)}}&[selected]{--button-fg: var(--button-fg-quaternary-hover)}&:active{--button-fg: oklch(from var(--button-fg-quaternary-rest) calc(l + .05) c h)}.dark &:active{--button-fg: var(--button-fg-quaternary-rest)}}&[selected]{box-shadow:inset 0 0 0 1px currentColor}&[disabled]{background-color:var(--bg-secondary-disabled);color:var(--fg-disabled);&[priority=primary]{background-color:var(--bg-primary-disabled);color:var(--fg-disabled-primary)}&[priority=tertiary],&[priority=quaternary]{background-color:transparent;color:var(--fg-disabled)}}}}@keyframes btn-loading-slide{to{transform:translate(var(--button-loading-x-period))}}
1
+ @layer anta{a-button,a[role=button]{--button-primary-fg: #ffffff;--bg-primary-disabled: #ece9ec;--bg-secondary-disabled: #f6f4f6;--fg-disabled-primary: #afa9b1;--fg-disabled: #afa9b1;--button-loading-stripe: 14px;--button-loading-stripe-gap: 16px;--button-loading-period: 30px;--button-loading-angle: 125deg;--button-loading-x-period: calc( var(--button-loading-period) / cos(var(--button-loading-angle) - 90deg) );--button-loading-opacity: .15;--button-loading-blur: 2.5px;--button-bg-primary-rest: #635b65;--button-bg-primary-hover: #534c57;--button-bg-primary-active: #49424c;--button-fg-primary-rest: var(--button-primary-fg);--button-bg-secondary-rest: #45374c12;--button-bg-secondary-hover: #44374b1a;--button-bg-secondary-active: #44374b26;--button-fg-secondary-rest: #534c57;--button-fg-secondary-hover: #49424c;--button-fg-secondary-l-shift: .05;--button-fg-tertiary-rest: #635b65;--button-fg-tertiary-hover: #534c57;--button-bg-tertiary-hover: #44374b0d;--button-bg-tertiary-active: #44374b1a;--button-fg-quaternary-rest: #635b65;--button-fg-quaternary-hover: #49424c;--_fs: 15px;--_lh: 20px;--_pb: 1px;--button-padding-x: 9px;--button-padding-y: calc((8px - var(--_pb)) / 2);--button-max-width: 100%;--button-max-height: 100%;--button-timing-in: 75ms ease-in;--button-timing-active: 50ms linear;--button-timing-out: .15s ease-out;--button-loading-duration: .5s}a-button[tone=brand],a[role=button][tone=brand]{--button-bg-primary-rest: #5f4bc3;--button-bg-primary-hover: #503cb4;--button-bg-primary-active: #483493;--button-fg-primary-rest: var(--button-primary-fg);--button-bg-secondary-rest: #7460d71a;--button-bg-secondary-hover: #7460d726;--button-bg-secondary-active: #7460d733;--button-fg-secondary-rest: #5f4bc3;--button-fg-secondary-hover: #503cb4;--button-fg-tertiary-rest: #5f4bc3;--button-fg-tertiary-hover: #503cb4;--button-bg-tertiary-hover: #7460d71a;--button-bg-tertiary-active: #7460d726;--button-fg-quaternary-rest: #5f4bc3;--button-fg-quaternary-hover: #483493}a-button[tone=critical],a[role=button][tone=critical]{--button-bg-primary-rest: #c9302c;--button-bg-primary-hover: #b02120;--button-bg-primary-active: #a01c1c;--button-fg-primary-rest: var(--button-primary-fg);--button-bg-secondary-rest: #de45451a;--button-bg-secondary-hover: #de454526;--button-bg-secondary-active: #de454533;--button-fg-secondary-rest: #c9302c;--button-fg-secondary-hover: #b02120;--button-fg-tertiary-rest: #c9302c;--button-fg-tertiary-hover: #b02120;--button-bg-tertiary-hover: #de45451a;--button-bg-tertiary-active: #de454533;--button-fg-quaternary-rest: #c9302c;--button-fg-quaternary-hover: #a01c1c}a-button[tone=info],a[role=button][tone=info]{--button-bg-primary-rest: #1f6eb2;--button-bg-primary-hover: #1a5b93;--button-bg-primary-active: #175082;--button-fg-primary-rest: var(--button-primary-fg);--button-bg-secondary-rest: #2686d91a;--button-bg-secondary-hover: #2686d926;--button-bg-secondary-active: #2686d933;--button-fg-secondary-rest: #1f6eb2;--button-fg-secondary-hover: #1a5b93;--button-fg-tertiary-rest: #1f6eb2;--button-fg-tertiary-hover: #1a5b93;--button-bg-tertiary-hover: #2686d91a;--button-bg-tertiary-active: #2686d926;--button-fg-quaternary-rest: #1f6eb2;--button-fg-quaternary-hover: #175082}a-button[tone=success],a[role=button][tone=success]{--button-bg-primary-rest: #2a7e43;--button-bg-primary-hover: #226737;--button-bg-primary-active: #1f5c31;--button-fg-primary-rest: var(--button-primary-fg);--button-bg-secondary-rest: #3295501a;--button-bg-secondary-hover: #32955026;--button-bg-secondary-active: #32955033;--button-fg-secondary-rest: #2a7e43;--button-fg-secondary-hover: #226737;--button-fg-tertiary-rest: #2a7e43;--button-fg-tertiary-hover: #226737;--button-bg-tertiary-hover: #3295501a;--button-bg-tertiary-active: #32955026;--button-fg-quaternary-rest: #2a7e43;--button-fg-quaternary-hover: #1f5c31}a-button[tone=warning],a[role=button][tone=warning]{--button-bg-primary-rest: #c37416;--button-bg-primary-hover: #ae6613;--button-bg-primary-active: #995200;--button-fg-primary-rest: var(--button-primary-fg);--button-bg-secondary-rest: #c374161a;--button-bg-secondary-hover: #c3741626;--button-bg-secondary-active: #c3741633;--button-fg-secondary-rest: #c37416;--button-fg-secondary-hover: #ae6613;--button-fg-tertiary-rest: #c37416;--button-fg-tertiary-hover: #ae6613;--button-bg-tertiary-hover: #c374161a;--button-bg-tertiary-active: #c3741633;--button-fg-quaternary-rest: #c37416;--button-fg-quaternary-hover: #995200}.dark a-button,.dark a[role=button]{--bg-primary-disabled: #272329;--bg-secondary-disabled: #1d1a1e;--fg-disabled-primary: #776e77;--fg-disabled: #635b65;--button-bg-primary-rest: #534c57;--button-bg-primary-hover: #635b65;--button-bg-primary-active: #938d96;--button-bg-secondary-rest: #e4d1ef1c;--button-bg-secondary-hover: #e4d1ef24;--button-bg-secondary-active: #e4d1ef2e;--button-fg-secondary-rest: #c1b9c1;--button-fg-secondary-hover: #d4ced4;--button-fg-secondary-l-shift: 0;--button-fg-tertiary-rest: #afa9b1;--button-fg-tertiary-hover: #afa9b1;--button-bg-tertiary-hover: #e4d1ef14;--button-bg-tertiary-active: #e4d1ef21;--button-fg-quaternary-rest: #afa9b1;--button-fg-quaternary-hover: #cac4ca}.dark a-button[tone=brand],.dark a[role=button][tone=brand]{--button-bg-primary-rest: #503cb4;--button-bg-primary-hover: #5f4bc3;--button-bg-primary-active: #7460d7;--button-bg-secondary-rest: #7460d73b;--button-bg-secondary-hover: #7460d747;--button-bg-secondary-active: #7460d754;--button-fg-secondary-rest: #c5baff;--button-fg-secondary-hover: #d2cbf6;--button-fg-tertiary-rest: #ada0ee;--button-fg-tertiary-hover: #bcb1f1;--button-bg-tertiary-hover: #7460d73b;--button-bg-tertiary-active: #7460d747;--button-fg-quaternary-rest: #ada0ee;--button-fg-quaternary-hover: #c7bef4}.dark a-button[tone=critical],.dark a[role=button][tone=critical]{--button-bg-primary-rest: #b02120;--button-bg-primary-hover: #c9302c;--button-bg-primary-active: #de4545;--button-bg-secondary-rest: #de45453b;--button-bg-secondary-hover: #de454547;--button-bg-secondary-active: #de454554;--button-fg-secondary-rest: #ffabac;--button-fg-secondary-hover: #f4c2c2;--button-fg-tertiary-rest: #e78e90;--button-fg-tertiary-hover: #efa4a4;--button-bg-tertiary-hover: #de45453b;--button-bg-tertiary-active: #de454547;--button-fg-quaternary-rest: #e78e90;--button-fg-quaternary-hover: #ffabac}.dark a-button[tone=info],.dark a[role=button][tone=info]{--button-bg-primary-rest: #1a5b93;--button-bg-primary-hover: #1f6eb2;--button-bg-primary-active: #2686d9;--button-bg-secondary-rest: #2686d93b;--button-bg-secondary-hover: #2686d947;--button-bg-secondary-active: #2686d954;--button-fg-secondary-rest: #9ed2ff;--button-fg-secondary-hover: #bad6f3;--button-fg-tertiary-rest: #7db6e8;--button-fg-tertiary-hover: #93c5ec;--button-bg-tertiary-hover: #2686d93b;--button-bg-tertiary-active: #2686d947;--button-fg-quaternary-rest: #7db6e8;--button-fg-quaternary-hover: #a6cdef}.dark a-button[tone=success],.dark a[role=button][tone=success]{--button-bg-primary-rest: #226737;--button-bg-primary-hover: #2a7e43;--button-bg-primary-active: #329550;--button-bg-secondary-rest: #3295503b;--button-bg-secondary-hover: #32955047;--button-bg-secondary-active: #32955054;--button-fg-secondary-rest: #8ceca9;--button-fg-secondary-hover: #b3e5c2;--button-fg-tertiary-rest: #74cd8e;--button-fg-tertiary-hover: #88d7a0;--button-bg-tertiary-hover: #3295503b;--button-bg-tertiary-active: #32955047;--button-fg-quaternary-rest: #74cd8e;--button-fg-quaternary-hover: #9ddeb1}.dark a-button[tone=warning],.dark a[role=button][tone=warning]{--button-bg-primary-rest: #7f410b;--button-bg-primary-hover: #995200;--button-bg-primary-active: #ae6613;--button-bg-secondary-rest: #c374163b;--button-bg-secondary-hover: #c3741647;--button-bg-secondary-active: #c3741654;--button-fg-secondary-rest: #f7c06e;--button-fg-secondary-hover: #f3cc91;--button-fg-tertiary-rest: #e1a452;--button-fg-tertiary-hover: #edb25a;--button-bg-tertiary-hover: #c374163b;--button-bg-tertiary-active: #c3741647;--button-fg-quaternary-rest: #e1a452;--button-fg-quaternary-hover: #f0bf75}a-button,a[role=button]{user-select:none;display:inline-flex;align-items:center;gap:.5ch;overflow:hidden;cursor:pointer;flex-shrink:0;color:var(--button-fg);background-color:var(--button-bg);border-radius:4px;text-decoration:none;box-shadow:inset 0 0 1px color-mix(in oklch,currentColor,transparent 70%);font-size:var(--_fs);font-family:var(--sans-serif);font-weight:450;font-variation-settings:"wdth" 88,"slnt" 0,"ital" 0;font-feature-settings:"ss02","ss05";line-height:var(--_lh);letter-spacing:.05ch;text-overflow:ellipsis;text-wrap:nowrap;white-space:nowrap;max-width:var(--button-max-width);max-height:var(--button-max-height);padding-inline:var(--button-padding-x);padding-block:var(--button-padding-y);>a-button-label{display:inline-block;text-overflow:ellipsis;text-wrap:nowrap;white-space:nowrap;overflow:hidden;min-width:0;line-height:var(--_lh);padding-bottom:var(--_pb);&:has(>*){display:inline-flex;gap:.25ch}}>*{pointer-events:none}& a-icon{transition:color var(--button-timing-out)}transition:color var(--button-timing-out),background-color var(--button-timing-out);&:hover,&:focus-visible{transition:color var(--button-timing-in),background-color var(--button-timing-in);& a-icon{transition:color var(--button-timing-in)}}&:active,&[selected]{transition:color var(--button-timing-active),background-color var(--button-timing-active);& a-icon{transition:color var(--button-timing-active)}}&[disabled]{pointer-events:none;cursor:not-allowed;transition:none;& a-icon{transition:none}}&:focus-visible{outline:1px solid var(--focus-ring);outline-offset:1px}&[priority=quaternary]{--button-timing-in: 0s;--button-timing-active: 0s;--button-timing-out: 0s}&[size=small]{--button-padding-x: 7px;--_fs: 13px;--_lh: 16px;--_pb: .5px;& a-icon{--icon-size: 14px}}&[size=large]{--button-padding-x: 13px;--_fs: 17px;--_lh: 24px;--_pb: 1.5px;& a-icon{--icon-size: 18px}}&[priority=tertiary],&[priority=quaternary]{&[underline=solid]{text-decoration:underline solid}&[underline=dashed]{text-decoration:underline dashed}&[underline=dotted]{text-decoration:underline dotted}&[underline]{text-decoration-color:color-mix(in srgb,currentColor 75%,transparent);text-decoration-thickness:1px;text-underline-offset:3px;&[selected]{text-decoration-color:var(--button-fg);text-decoration-thickness:1px}@media(hover:hover)and (pointer:fine){&:hover{text-decoration-color:var(--button-fg);text-decoration-thickness:1px}}}}&[priority=quaternary][paddingless]{--button-padding-x: 0;--button-padding-y: 0}&:has(>a-icon):not(:has(>a-icon~a-icon)):not(:has(>:not(a-icon):not(a-tooltip))){--button-padding-y: 5px;--button-padding-x: 5px;min-width:28px;min-height:28px;justify-content:center;&[size=small]{--button-padding-y: 3px;--button-padding-x: 3px;min-width:24px;min-height:24px}&[size=large]{--button-padding-y: 7px;--button-padding-x: 7px;min-width:32px;min-height:32px}}&:is(:has(>a-icon:first-child),:has(>a-tooltip:first-child+a-icon)):is(:has(>a-icon~a-icon),:has(>:not(a-icon):not(a-tooltip))){padding-inline-start:max(0px,var(--button-padding-x) - 2px)}&:is(:has(>a-icon:last-child),:has(>a-icon+a-tooltip:last-child)):is(:has(>a-icon~a-icon),:has(>:not(a-icon):not(a-tooltip))){padding-inline-end:max(0px,var(--button-padding-x) - 2px)}&[loading]{position:relative;pointer-events:none;cursor:wait;&:before{content:"";position:absolute;top:0;bottom:0;left:calc(0px - var(--button-loading-x-period));right:0;pointer-events:none;background:repeating-linear-gradient(var(--button-loading-angle),transparent 0,transparent var(--button-loading-stripe-gap),currentColor var(--button-loading-stripe-gap),currentColor calc(var(--button-loading-stripe-gap) + var(--button-loading-stripe)));opacity:var(--button-loading-opacity);filter:blur(var(--button-loading-blur));will-change:transform;animation:btn-loading-slide var(--button-loading-duration) linear infinite;animation-delay:-9999s}}&[tone]:not([tone=""],[tone=brand],[tone=neutral],[tone=critical],[tone=info],[tone=success],[tone=warning]){--button-tone-source: attr(tone type(<color>), transparent);--_tone-fg-l: .46;--_tone-fg-strong-l: .4;--_tone-fg-c: .17;--_tone-bg-l: .54;--_tone-bg-c: .16;--_tone-bg-a-rest: .1;--_tone-bg-a-hover: .15;--_tone-bg-a-active: .2;--_tone-primary-l-rest: .5;--_tone-primary-l-hover: .45;--_tone-primary-l-active: .4;--button-bg-primary-rest: oklch(from var(--button-tone-source) var(--_tone-primary-l-rest) c h);--button-bg-primary-hover: oklch(from var(--button-tone-source) var(--_tone-primary-l-hover) c h);--button-bg-primary-active: oklch(from var(--button-tone-source) var(--_tone-primary-l-active) c h);--button-fg-primary-rest: var(--button-primary-fg);--button-fg-secondary-rest: oklch(from var(--button-tone-source) var(--_tone-fg-l) var(--_tone-fg-c) h);--button-fg-secondary-hover: oklch(from var(--button-tone-source) var(--_tone-fg-strong-l) var(--_tone-fg-c) h);--button-fg-tertiary-rest: oklch(from var(--button-tone-source) var(--_tone-fg-l) var(--_tone-fg-c) h);--button-fg-tertiary-hover: oklch(from var(--button-tone-source) var(--_tone-fg-strong-l) var(--_tone-fg-c) h);--button-fg-quaternary-rest: oklch(from var(--button-tone-source) var(--_tone-fg-l) var(--_tone-fg-c) h);--button-fg-quaternary-hover: oklch(from var(--button-tone-source) var(--_tone-fg-strong-l) var(--_tone-fg-c) h);--button-bg-secondary-rest: oklch(from var(--button-tone-source) var(--_tone-bg-l) var(--_tone-bg-c) h / var(--_tone-bg-a-rest));--button-bg-secondary-hover: oklch(from var(--button-tone-source) var(--_tone-bg-l) var(--_tone-bg-c) h / var(--_tone-bg-a-hover));--button-bg-secondary-active: oklch(from var(--button-tone-source) var(--_tone-bg-l) var(--_tone-bg-c) h / var(--_tone-bg-a-active));--button-bg-tertiary-hover: oklch(from var(--button-tone-source) var(--_tone-bg-l) var(--_tone-bg-c) h / var(--_tone-bg-a-rest));--button-bg-tertiary-active: oklch(from var(--button-tone-source) var(--_tone-bg-l) var(--_tone-bg-c) h / var(--_tone-bg-a-hover))}.dark &[tone]:not([tone=""],[tone=brand],[tone=neutral],[tone=critical],[tone=info],[tone=success],[tone=warning]){--_tone-fg-l: .78;--_tone-fg-strong-l: .85;--_tone-fg-c: .11;--_tone-bg-l: .58;--_tone-bg-a-rest: .23;--_tone-bg-a-hover: .28;--_tone-bg-a-active: .33;--_tone-primary-l-rest: .45;--_tone-primary-l-hover: .5;--_tone-primary-l-active: .57}--button-fg: oklch(from var(--button-fg-secondary-rest) calc(l - var(--button-fg-secondary-l-shift)) c h);--button-bg: var(--button-bg-secondary-rest);@media(hover:hover)and (pointer:fine){&:hover{--button-fg: var(--button-fg-secondary-hover);--button-bg: var(--button-bg-secondary-hover)}}&:active,&[selected]{--button-fg: var(--button-fg-secondary-hover);--button-bg: var(--button-bg-secondary-active)}&[priority=primary]{--button-fg: var(--button-fg-primary-rest);--button-bg: var(--button-bg-primary-rest);box-shadow:none;@media(hover:hover)and (pointer:fine){&:hover{--button-bg: var(--button-bg-primary-hover)}}&:active,&[selected]{--button-bg: var(--button-bg-primary-active)}}&[priority=tertiary]{--button-fg: var(--button-fg-tertiary-rest);--button-bg: transparent;box-shadow:none;@media(hover:hover)and (pointer:fine){&:hover{--button-fg: var(--button-fg-tertiary-hover);--button-bg: var(--button-bg-tertiary-hover)}}&:active,&[selected]{--button-fg: var(--button-fg-tertiary-hover);--button-bg: var(--button-bg-tertiary-active)}}&[priority=quaternary]{--button-fg: var(--button-fg-quaternary-rest);--button-bg: transparent;box-shadow:none;font-weight:400;letter-spacing:.06ch;@media(hover:hover)and (pointer:fine){&:hover{--button-fg: var(--button-fg-quaternary-hover)}}&[selected]{--button-fg: var(--button-fg-quaternary-hover)}&:active{--button-fg: oklch(from var(--button-fg-quaternary-rest) calc(l + .05) c h)}.dark &:active{--button-fg: var(--button-fg-quaternary-rest)}}&[selected]{box-shadow:inset 0 0 0 1px currentColor}&[disabled]{background-color:var(--bg-secondary-disabled);color:var(--fg-disabled);&[priority=primary]{background-color:var(--bg-primary-disabled);color:var(--fg-disabled-primary)}&[priority=tertiary],&[priority=quaternary]{background-color:transparent;color:var(--fg-disabled)}}}}@keyframes btn-loading-slide{to{transform:translate(var(--button-loading-x-period))}}
@@ -5,6 +5,62 @@ declare global {
5
5
  hasKeyListenerForAButton?: boolean;
6
6
  }
7
7
  }
8
+ /**
9
+ * `<a-button>` — button element (also styles `a[role="button"]`).
10
+ *
11
+ * Styling notes (`a-button.css` ships comment-free):
12
+ * - **Token architecture.** The base rule carries shared constants plus the
13
+ * neutral tone tokens (neutral is the implicit default; its chip uses a
14
+ * softer 5% rest alpha vs 10% for named tones). Named tones override via
15
+ * `[tone="X"]`; critical/warning tertiary active steps heavier (20% vs
16
+ * 15%). Final colors are wired through `--button-fg`/`--button-bg`:
17
+ * secondary is declared unscoped so a bare `a-button` consumer selector
18
+ * can override it without bumping specificity; primary/tertiary/quaternary
19
+ * match an explicit `[priority]` and win on specificity. Secondary's rest
20
+ * label is darkened by `--button-fg-secondary-l-shift` oklch lightness
21
+ * (0.05 light mode, zeroed in dark) at the `--button-fg` wiring, so it
22
+ * covers named and custom tones alike; secondary also carries a 1px
23
+ * hairline box-shadow in the current fg tone at 50% alpha, which the
24
+ * other priorities cancel in their own blocks. `[selected]` adds a 1px
25
+ * inset ring in `currentColor`, declared after the priority blocks so it
26
+ * survives their `box-shadow: none` cancels.
27
+ * - **Dark mode** re-tunes with heavier alphas (30/40% vs 10/15%) and flips
28
+ * neutral's anchor to a lilac — mixing dark gray into a dark bg yields no
29
+ * contrast.
30
+ * - **Custom tones** (any non-named `tone` value): primary uses the literal
31
+ * color; other priorities derive from the source HUE via oklch relative
32
+ * color with lightness/chroma/alpha pinned near Brand. The `--_tone-*`
33
+ * knobs are the only numbers to tune (the `.dark` block re-tunes them).
34
+ * The JSX wrapper writes `--button-tone-source` inline; a typed `attr()`
35
+ * fallback picks up raw `<a-button tone="…">` on Chrome 133+/Safari 18.2+.
36
+ * - **Hover is gated** to `(hover: hover) and (pointer: fine)` so it doesn't
37
+ * stick after a tap; `:active`/`[selected]` stay ungated (correct press
38
+ * feedback on touch). Quaternary rests at 90% fg alpha with a slightly
39
+ * heavier `font-weight: 415` (quieter than tertiary but legible), restores
40
+ * full opacity on hover, lightens by 0.05 oklch L on press (dark mode
41
+ * returns to rest instead — it's already light), and has instant
42
+ * transitions in every direction.
43
+ * - **Layout gotchas:** `flex-shrink: 0` (shrinking + `overflow: hidden`
44
+ * would clip the label silently); `font-variation-settings` must restate
45
+ * ALL axes — it replaces rather than merges, and dropping "slnt"/"ital"
46
+ * renders italic in Safari; the label uses a 17px line box + 1px bottom
47
+ * padding for optical vertical centering at an unchanged 18px box.
48
+ * - **Icon-only** is purely structural — `:has(> a-icon:only-child)` gives
49
+ * square padding + a min-size pin (and centers when the host is sized
50
+ * bigger). A non-only edge icon trims ~2px off its side's padding
51
+ * (optical), `max(0px, …)` keeping paddingless at 0.
52
+ * - **Underline** renders only on tertiary/quaternary: 0.5px hairline at 75%
53
+ * alpha at rest, 1px at full alpha on hover/`[selected]`, mirroring the
54
+ * prose-link rule in `<Text>`.
55
+ * - **Loading** paints a blurred diagonal stripe overlay; the x-period is
56
+ * the diagonal period projected onto x so the slide loops seamlessly
57
+ * (overshooting left by one period), and a large negative
58
+ * `animation-delay` desyncs instances so a row doesn't pulse in lockstep.
59
+ * - **Disabled** sets `background-color`/`color` directly (not the vars) so
60
+ * an inline `--button-bg` override can't keep a disabled button alive, and
61
+ * skips transitions — a `.dark` toggle would flash the tone hue
62
+ * mid-resolve.
63
+ */
8
64
  export declare class AButtonElement extends HTMLElementBase {
9
65
  connectedCallback(): void;
10
66
  }