@antadesign/anta 0.3.5 → 0.3.7

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 (43) hide show
  1. package/dist/anta_helpers.d.ts +10 -0
  2. package/dist/anta_helpers.js +8 -0
  3. package/dist/components/Card.d.ts +86 -0
  4. package/dist/components/Card.js +72 -0
  5. package/dist/components/Dialog.d.ts +92 -0
  6. package/dist/components/Dialog.js +71 -0
  7. package/dist/components/Input.d.ts +3 -0
  8. package/dist/components/InputAutocomplete.d.ts +69 -0
  9. package/dist/components/InputAutocomplete.js +95 -0
  10. package/dist/components/InputDate.js +1 -2
  11. package/dist/components/Menu.d.ts +4 -1
  12. package/dist/components/Menu.js +2 -1
  13. package/dist/components/MenuItem.d.ts +9 -1
  14. package/dist/components/MenuItem.js +2 -1
  15. package/dist/components/Select.d.ts +5 -2
  16. package/dist/components/Select.js +11 -34
  17. package/dist/components/SelectFaceted.d.ts +7 -1
  18. package/dist/components/SelectFaceted.js +12 -11
  19. package/dist/components/select-options.d.ts +14 -0
  20. package/dist/components/select-options.js +31 -0
  21. package/dist/elements/a-card.css +211 -0
  22. package/dist/elements/a-card.d.ts +10 -0
  23. package/dist/elements/a-card.js +213 -0
  24. package/dist/elements/a-checkbox.css +1 -1
  25. package/dist/elements/a-dialog.css +1 -0
  26. package/dist/elements/a-dialog.d.ts +26 -0
  27. package/dist/elements/a-dialog.js +386 -0
  28. package/dist/elements/a-expander.js +2 -2
  29. package/dist/elements/a-input-time.js +2 -1
  30. package/dist/elements/a-input.d.ts +2 -0
  31. package/dist/elements/a-input.js +34 -1
  32. package/dist/elements/a-menu.d.ts +5 -2
  33. package/dist/elements/a-menu.js +52 -15
  34. package/dist/elements/a-radio.css +1 -1
  35. package/dist/elements/a-tooltip.d.ts +17 -0
  36. package/dist/elements/a-tooltip.js +28 -0
  37. package/dist/elements/index.d.ts +1 -0
  38. package/dist/elements/index.js +3 -0
  39. package/dist/general_types.d.ts +45 -0
  40. package/dist/index.d.ts +4 -0
  41. package/dist/index.js +4 -0
  42. package/dist/jsx-runtime.d.ts +2 -1
  43. package/package.json +1 -1
@@ -1,5 +1,7 @@
1
1
  import { Fragment, jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
2
  import { useState, useId } from "../jsx-runtime";
3
+ import { ISOLATE_HINT } from "../anta_helpers";
4
+ import { normalizeOpt, matchQueryRegex, matchesQuery, highlight } from "./select-options";
3
5
  import { Input } from "./Input";
4
6
  import { Icon } from "./Icon";
5
7
  import { Menu } from "./Menu";
@@ -8,8 +10,6 @@ import { MenuGroup } from "./MenuGroup";
8
10
  import { MenuSeparator } from "./MenuSeparator";
9
11
  import { Tooltip } from "./Tooltip";
10
12
  import styles from "./Select.module.css";
11
- const IS_MAC = typeof navigator !== "undefined" && /Mac|iPhone|iPad/i.test(navigator.userAgent || "");
12
- const normalize = (o) => typeof o === "string" ? { value: o, label: o } : o;
13
13
  function optionsWithSelection(options, values) {
14
14
  const set = new Set(values == null ? [] : Array.isArray(values) ? values : [values]);
15
15
  const state = (on, total) => on === 0 ? "none" : on === total ? "all" : "some";
@@ -28,7 +28,7 @@ function optionsWithSelection(options, values) {
28
28
  const total = children.reduce((n, c) => n + c.total, 0);
29
29
  return { node: { ...g, options: children.map((c) => c.node), selectionState: state(on, total) }, on, total };
30
30
  }
31
- const o = normalize(raw);
31
+ const o = normalizeOpt(raw);
32
32
  const selected = set.has(o.value);
33
33
  return { node: { ...o, selected }, on: selected ? 1 : 0, total: 1 };
34
34
  });
@@ -54,7 +54,7 @@ const Select = (props) => {
54
54
  disabled,
55
55
  toneSelected,
56
56
  filter,
57
- selectAll,
57
+ selectAll = true,
58
58
  selectAllLabel = "Select all",
59
59
  clearable,
60
60
  clearLabel = "Clear",
@@ -85,7 +85,7 @@ const Select = (props) => {
85
85
  if (typeof raw !== "string" && isSubmenu(raw)) collectLeaves(raw.submenu, raw.label, disabled2 || !!raw.disabled, out);
86
86
  else if (typeof raw !== "string" && isGroup(raw)) collectLeaves(raw.options, raw.label, disabled2 || !!raw.disabled, out);
87
87
  else {
88
- const o = normalize(raw);
88
+ const o = normalizeOpt(raw);
89
89
  out.push({ opt: o, disabled: disabled2 || !!o.disabled, group });
90
90
  }
91
91
  }
@@ -101,28 +101,11 @@ const Select = (props) => {
101
101
  const byValue = new Map(allLeaves.map((l) => [l.opt.value, l.opt]));
102
102
  const filtering = filter !== void 0 && filter !== false;
103
103
  const q = query.trim();
104
- const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
105
- const queryRe = filtering && typeof filter !== "function" && q ? new RegExp(q.split(/\s+/).map(escapeRe).join("\\s+"), "i") : null;
106
- const matches = (o) => typeof filter === "function" ? filter(o, q) : !queryRe || [o.value, o.label ?? "", o.hint ?? ""].some((s) => queryRe.test(s));
104
+ const queryRe = filtering && typeof filter !== "function" ? matchQueryRegex(q) : null;
105
+ const matches = (o) => typeof filter === "function" ? filter(o, q) : matchesQuery(o, queryRe);
107
106
  const prune = typeof filter === "function" || filtering && !!q;
108
107
  const visibleLeaves = prune ? allLeaves.filter((l) => matches(l.opt)) : allLeaves;
109
108
  const flattening = filtering && !!q;
110
- const highlight = (text) => {
111
- if (!queryRe) return text;
112
- const re = new RegExp(queryRe.source, "gi");
113
- const out = [];
114
- let last = 0;
115
- let m;
116
- while ((m = re.exec(text)) !== null) {
117
- if (m.index > last) out.push(text.slice(last, m.index));
118
- out.push(/* @__PURE__ */ jsx("b", { children: m[0] }, out.length));
119
- last = m.index + m[0].length;
120
- if (m[0].length === 0) re.lastIndex++;
121
- }
122
- if (out.length === 0) return text;
123
- if (last < text.length) out.push(text.slice(last));
124
- return out;
125
- };
126
109
  const selectedValues = Array.isArray(currentRaw) ? currentRaw : currentRaw != null ? [currentRaw] : [];
127
110
  const isSelected = (v) => selectedValues.includes(v);
128
111
  const selectedOptions = selectedValues.map((v) => byValue.get(v)).filter(Boolean);
@@ -171,7 +154,7 @@ const Select = (props) => {
171
154
  const custom = renderOption?.(o, optState);
172
155
  const customMark = renderIndicator?.(optState);
173
156
  const ariaSelectable = !multiple && mark === "none" ? { role: "menuitemradio", "aria-checked": isSelected(o.value) ? "true" : "false" } : void 0;
174
- const isolateHint = multiple && selectAll && !disabled2 ? IS_MAC ? "\u2325+Click to select only this" : "Alt+Click to select only this" : void 0;
157
+ const isolateHint = multiple && selectAll && !disabled2 ? ISOLATE_HINT : void 0;
175
158
  const tip = o.tooltip ?? isolateHint;
176
159
  const hintOnly = o.tooltip == null && isolateHint != null;
177
160
  return /* @__PURE__ */ jsxs(
@@ -181,8 +164,8 @@ const Select = (props) => {
181
164
  selectionIndicator: menuItemIndicator,
182
165
  ...ariaSelectable,
183
166
  indicator: customMark ?? void 0,
184
- label: custom ? void 0 : queryRe ? highlight(o.label ?? o.value) : o.label ?? o.value,
185
- hint: custom ? void 0 : queryRe && o.hint ? highlight(o.hint) : o.hint,
167
+ label: custom ? void 0 : highlight(o.label ?? o.value, queryRe),
168
+ hint: custom ? void 0 : o.hint ? highlight(o.hint, queryRe) : o.hint,
186
169
  icon: custom ? void 0 : o.icon,
187
170
  tone: o.tone,
188
171
  toneSelected,
@@ -227,7 +210,7 @@ const Select = (props) => {
227
210
  if (inner.length) out.push(/* @__PURE__ */ jsx(MenuGroup, { label: raw.label, children: inner }, `grp-${i}-${raw.label}`));
228
211
  return;
229
212
  }
230
- const o = normalize(raw);
213
+ const o = normalizeOpt(raw);
231
214
  if (typeof filter === "function" && !matches(o)) return;
232
215
  out.push(renderOptionRow(o, disabled2 || !!o.disabled));
233
216
  });
@@ -271,12 +254,6 @@ const Select = (props) => {
271
254
  round,
272
255
  "aria-haspopup": "menu",
273
256
  "aria-expanded": open ? "true" : "false",
274
- onKeyDown: (e) => {
275
- if (!open && (e.key === "Enter" || e.key === " " || e.key === "ArrowDown")) {
276
- e.preventDefault();
277
- e.currentTarget.click();
278
- }
279
- },
280
257
  trailing: (
281
258
  // Chevron rotates down → up when open (spacing + rotation in the CSS
282
259
  // module, matching the Input "Select dropdown" example).
@@ -36,7 +36,9 @@ export interface SelectFacetMultiple extends FacetBase {
36
36
  options: SelectItem[];
37
37
  /** Add a search field atop this facet's flyout that filters its options. */
38
38
  filter?: FacetFilter;
39
- /** Add a "Select all" row that toggles every option. */
39
+ /** A "Select all" row that toggles every option. On by default — set `false`
40
+ * to drop it.
41
+ * @defaultValue true */
40
42
  selectAll?: boolean;
41
43
  /** Label for the `selectAll` row.
42
44
  * @defaultValue Select all */
@@ -129,6 +131,10 @@ export interface SelectFacetedProps extends Omit<BaseProps, 'children'> {
129
131
  priority?: 'primary' | 'secondary';
130
132
  /** Disable the whole control. */
131
133
  disabled?: boolean;
134
+ /** Tone applied to a selected option row in the facet flyouts (label, selected
135
+ * tint, and the check / checkbox indicator). A named tone or a custom CSS
136
+ * colour, matching `Select`'s `toneSelected`. Defaults to a neutral selection. */
137
+ toneSelected?: 'neutral' | 'brand' | 'info' | 'success' | 'warning' | 'critical' | (string & {});
132
138
  /** Add a single search field to the top of the root menu that flattens every
133
139
  * `single` / `multiple` facet's options into one list and searches across them
134
140
  * — typing "alice" surfaces it under both an Assignee and an Owner facet, each
@@ -1,6 +1,7 @@
1
1
  import { Fragment, jsx, jsxs } from "@antadesign/anta/jsx-runtime";
2
2
  import { useState, useMemo } from "../jsx-runtime";
3
- import { nativeStateChange } from "../anta_helpers";
3
+ import { nativeStateChange, ISOLATE_HINT } from "../anta_helpers";
4
+ import { normalizeOpt, matchQueryRegex, matchesQuery } from "./select-options";
4
5
  import { Button } from "./Button";
5
6
  import { Menu } from "./Menu";
6
7
  import { MenuItem } from "./MenuItem";
@@ -8,8 +9,8 @@ import { MenuGroup } from "./MenuGroup";
8
9
  import { MenuSeparator } from "./MenuSeparator";
9
10
  import { Tag } from "./Tag";
10
11
  import { Input } from "./Input";
12
+ import { Tooltip } from "./Tooltip";
11
13
  import styles from "./SelectFaceted.module.css";
12
- const normalizeOpt = (o) => typeof o === "string" ? { value: o, label: o } : o;
13
14
  const leavesOf = (items) => {
14
15
  const out = [];
15
16
  for (const it of items) {
@@ -22,13 +23,7 @@ const leavesOf = (items) => {
22
23
  return out;
23
24
  };
24
25
  const isEmpty = (v) => v == null || v === "" || Array.isArray(v) && v.length === 0;
25
- const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
26
- const defaultMatch = (o, query) => {
27
- const q = query.trim();
28
- if (!q) return true;
29
- const re = new RegExp(q.split(/\s+/).map(escapeRe).join("\\s+"), "i");
30
- return [o.value, o.label, o.hint].some((v) => typeof v === "string" && re.test(v));
31
- };
26
+ const defaultMatch = (o, query) => matchesQuery(o, matchQueryRegex(query));
32
27
  const SelectFaceted = (props) => {
33
28
  const {
34
29
  facets,
@@ -40,6 +35,7 @@ const SelectFaceted = (props) => {
40
35
  size,
41
36
  priority = "secondary",
42
37
  disabled,
38
+ toneSelected,
43
39
  searchable,
44
40
  searchPlaceholder = "Filter\u2026",
45
41
  clearable = true,
@@ -125,6 +121,7 @@ const SelectFaceted = (props) => {
125
121
  label: opt.label ?? opt.value,
126
122
  hint: opt.hint,
127
123
  tone: opt.tone,
124
+ toneSelected,
128
125
  disabled: opt.disabled,
129
126
  "data-menu-open": ""
130
127
  };
@@ -142,13 +139,17 @@ const SelectFaceted = (props) => {
142
139
  );
143
140
  }
144
141
  const arr = current[facet.key] ?? [];
142
+ const isolable = facet.selectAll !== false && !opt.disabled;
143
+ const tip = opt.tooltip ?? (isolable ? ISOLATE_HINT : void 0);
144
+ const hintOnly = opt.tooltip == null && isolable;
145
145
  return /* @__PURE__ */ jsx(
146
146
  MenuItem,
147
147
  {
148
148
  ...shared,
149
149
  selectionIndicator: "checkbox",
150
150
  selected: arr.includes(opt.value),
151
- onSelect: () => setFacet(facet, arr.includes(opt.value) ? arr.filter((v) => v !== opt.value) : [...arr, opt.value])
151
+ onSelect: (e) => isolable && e?.altKey ? setFacet(facet, [opt.value]) : setFacet(facet, arr.includes(opt.value) ? arr.filter((v) => v !== opt.value) : [...arr, opt.value]),
152
+ children: tip && /* @__PURE__ */ jsx(Tooltip, { ...hintOnly ? { follow: true, delay: 700 } : {}, children: tip })
152
153
  },
153
154
  `${keyPrefix}${opt.value}`
154
155
  );
@@ -161,7 +162,7 @@ const SelectFaceted = (props) => {
161
162
  const allOn = enabled.length > 0 && enabled.every((v) => arr.includes(v));
162
163
  const someOn = enabled.some((v) => arr.includes(v));
163
164
  return /* @__PURE__ */ jsxs(Fragment, { children: [
164
- facet.selectAll && leaves.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
165
+ facet.selectAll !== false && leaves.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
165
166
  /* @__PURE__ */ jsx(
166
167
  MenuItem,
167
168
  {
@@ -0,0 +1,14 @@
1
+ import type { SelectOption } from './Select';
2
+ /** Coerce a bare string option to `{ value, label }`; pass an object through. */
3
+ export declare const normalizeOpt: (o: string | SelectOption) => SelectOption;
4
+ /** The built-in filter regex: case-insensitive, with each run of whitespace in the
5
+ * query matching any gap (`\s+`). Returns null for an empty query — the signal to
6
+ * match everything and to skip match-highlighting. */
7
+ export declare function matchQueryRegex(query: string): RegExp | null;
8
+ /** Whether an option matches the built-in query regex, testing value / label /
9
+ * hint. A null regex (empty query) matches everything. */
10
+ export declare const matchesQuery: (o: SelectOption, queryRe: RegExp | null) => boolean;
11
+ /** Bold the matched substring(s) of `text` for display (built-in matcher only).
12
+ * A null regex returns the text unchanged. The regex is re-created global so
13
+ * `exec` walks every match; the zero-width guard stops an empty match looping. */
14
+ export declare const highlight: (text: string, queryRe: RegExp | null) => React.ReactNode;
@@ -0,0 +1,31 @@
1
+ import { jsx } from "@antadesign/anta/jsx-runtime";
2
+ const normalizeOpt = (o) => typeof o === "string" ? { value: o, label: o } : o;
3
+ function matchQueryRegex(query) {
4
+ const q = query.trim();
5
+ if (!q) return null;
6
+ const escape = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7
+ return new RegExp(q.split(/\s+/).map(escape).join("\\s+"), "i");
8
+ }
9
+ const matchesQuery = (o, queryRe) => !queryRe || [o.value, o.label ?? "", o.hint ?? ""].some((s) => queryRe.test(s));
10
+ const highlight = (text, queryRe) => {
11
+ if (!queryRe) return text;
12
+ const re = new RegExp(queryRe.source, "gi");
13
+ const out = [];
14
+ let last = 0;
15
+ let m;
16
+ while ((m = re.exec(text)) !== null) {
17
+ if (m.index > last) out.push(text.slice(last, m.index));
18
+ out.push(/* @__PURE__ */ jsx("b", { children: m[0] }, out.length));
19
+ last = m.index + m[0].length;
20
+ if (m[0].length === 0) re.lastIndex++;
21
+ }
22
+ if (out.length === 0) return text;
23
+ if (last < text.length) out.push(text.slice(last));
24
+ return out;
25
+ };
26
+ export {
27
+ highlight,
28
+ matchQueryRegex,
29
+ matchesQuery,
30
+ normalizeOpt
31
+ };
@@ -0,0 +1,211 @@
1
+ @layer anta {
2
+ /* Host + token definitions for <a-card>. The visible box lives here (not in the
3
+ shadow) so plain consumer CSS overrides it and it paints before the element
4
+ upgrades; the shadow <style> owns layout + region typography and only
5
+ REFERENCES these --card-* tokens with literal fallbacks. Named tones use
6
+ Anta's theme-aware role tokens, so dark mode is mostly free (same as
7
+ <a-expander> / <a-tag>); only the custom-tone branch needs a .dark re-tune. */
8
+
9
+ a-card {
10
+ /* Text roles: title = the strong foreground, text = the body (a step softer). */
11
+ --card-title: var(--text-1);
12
+ --card-text: var(--text-2);
13
+
14
+ /* Surface pairs per priority. `primary` (the default) is a clean sheet —
15
+ bg-1 in light, bg-4 in dark (see the `.dark` neutral rule), and
16
+ bg-4-{tone} for a toned card; `secondary` is a subtle fill (bg-2);
17
+ `tertiary` is a frosted panel (the primary surface, semi-transparent, +
18
+ a backdrop blur — see below). */
19
+ --card-bg-secondary: var(--bg-2);
20
+ --card-bg-primary: var(--bg-1);
21
+
22
+ /* `primary` is the default surface. */
23
+ --card-bg: var(--card-bg-primary);
24
+ /* Neutral edge hairline: text-2 at 10% (via --card-text). Toned cards override
25
+ this with their border-4-{tone} in the tone rules below. Flat across
26
+ priorities. */
27
+ --card-border: color-mix(in oklch, var(--card-text) 10%, transparent);
28
+
29
+ /* Selected ring (a 2px inset stroke) — the tone's strongest border. */
30
+ --card-selected-ring: var(--border-1);
31
+
32
+ /* Closed-surface hover lift for link cards: an oklch lightness bump that keeps
33
+ hue + chroma, so it works for every priority and tone in light and dark
34
+ (a transparent tertiary surface keeps zero alpha). Tune the amount here. */
35
+ --card-bg-hover-lift: 0.01;
36
+
37
+ --card-radius: 8px;
38
+ --card-padding: 16px;
39
+
40
+ display: block;
41
+ box-sizing: border-box;
42
+ /* Fill the container (grid cell / flex item / block) so width is always
43
+ definite. This is also what keeps `container-type` below from collapsing
44
+ the card to zero width in a shrink-to-fit context (a query container can't
45
+ size to its own content). Same as <a-expander>. */
46
+ width: 100%;
47
+ /* Establish the card as a query container so both the shadow layout and
48
+ consumer slotted content can `@container card (…)` on its width. */
49
+ container-type: inline-size;
50
+ container-name: card;
51
+
52
+ color: var(--card-text);
53
+ background: var(--card-bg);
54
+ /* The edge is a 1px OUTSET shadow ring (spread 1px, no blur), not a border:
55
+ it sits outside the box so full-bleed media reaches the very edge (no 1px
56
+ border inset) and it never adds to layout. `overflow: clip` doesn't clip an
57
+ element's own outset shadow, so the ring shows while the media still clips
58
+ to the rounded corners. */
59
+ box-shadow: 0 0 0 1px var(--card-border);
60
+ border-radius: var(--card-radius);
61
+ /* Clip full-bleed media to the rounded corners. */
62
+ overflow: clip;
63
+ }
64
+
65
+ a-card[priority="secondary"] {
66
+ --card-bg: var(--card-bg-secondary);
67
+ }
68
+
69
+ /* `round` re-points --card-radius, so both the host clip-radius and the shadow
70
+ container's ring follow. Bare `round` → 999px (fully round, clamped to the
71
+ box); a length (`round="16px"`, or the wrapper's `round={16}` → inline
72
+ --card-round) sets a custom radius. The typed attr() covers raw-HTML authors. */
73
+ a-card[round] {
74
+ --card-round: attr(round type(<length>));
75
+ --card-radius: var(--card-round, 999px);
76
+ }
77
+ a-card[priority="tertiary"] {
78
+ /* Frosted glass: the primary surface at 1% over a backdrop blur, so the card
79
+ reads over busy / colored / image backgrounds. It keeps the same hairline
80
+ ring as the other priorities (no --card-border override). The blur only
81
+ shows where there's detail behind it; the translucency reads everywhere. */
82
+ --card-bg: color-mix(in oklch, var(--card-bg-primary) 1%, transparent);
83
+ backdrop-filter: blur(8px);
84
+ -webkit-backdrop-filter: blur(8px);
85
+ }
86
+
87
+ /* Neutral dark: bg-1 (primary's light surface) is pure black in dark mode, so
88
+ step neutral primary up to bg-4. Secondary stays bg-2 via its token, and a
89
+ toned card's --bg-*-{tone} tokens self-flip, so this is scoped to neutral. */
90
+ .dark a-card:not([tone]),
91
+ .dark a-card[tone="neutral"] {
92
+ --card-bg-primary: var(--bg-4);
93
+ }
94
+
95
+ /* Hover lift, link cards only — gated so it doesn't stick after a tap. */
96
+ @media (hover: hover) and (pointer: fine) {
97
+ a-card[href]:not([loading]):hover {
98
+ background: oklch(from var(--card-bg) calc(l + var(--card-bg-hover-lift)) c h);
99
+ }
100
+ }
101
+
102
+ /* Pre-upgrade skeleton: the host border / background / radius already paint
103
+ (they're defined here, not in the shadow), so only the content inset is
104
+ missing before the shadow's .content exists — supply it here, then the
105
+ shadow owns padding once :defined. */
106
+ a-card:not(:defined) {
107
+ padding: var(--card-padding);
108
+ }
109
+
110
+ /* A slotted heading node — <a-title> (any level) or a raw <h1>–<h6> — carries a
111
+ document TOP margin (Title level 4 is 20px) that would add a leading gap in the
112
+ header; zero it. The BOTTOM margin stays, so a heading header keeps its gap to
113
+ the body. (A plain-STRING header has no margin of its own, so the `<Card>`
114
+ wrapper wraps it in a div with the matching 8px bottom margin — CSS can't tell
115
+ a string from a styled node, so that split lives in the wrapper.) Specificity
116
+ is bumped past `a-title[level="N"]` (0,1,1) and the reset's `hN` (0,0,1);
117
+ `@layer anta` keeps it overridable by un-layered CSS. */
118
+ a-card :is(h1, h2, h3, h4, h5, h6),
119
+ a-card a-title[level] {
120
+ margin-block-start: 0;
121
+ }
122
+
123
+ /* Named tones — re-point the text roles + the per-priority surface pairs to the
124
+ matching theme tokens (which self-flip for dark mode). */
125
+ a-card[tone="brand"] {
126
+ --card-title: var(--text-1-brand);
127
+ --card-text: var(--text-2-brand);
128
+ --card-bg-secondary: var(--bg-2-brand);
129
+ --card-bg-primary: var(--bg-4-brand);
130
+ --card-border: var(--border-4-brand);
131
+ --card-selected-ring: var(--border-1-brand);
132
+ }
133
+ a-card[tone="info"] {
134
+ --card-title: var(--text-1-info);
135
+ --card-text: var(--text-2-info);
136
+ --card-bg-secondary: var(--bg-2-info);
137
+ --card-bg-primary: var(--bg-4-info);
138
+ --card-border: var(--border-4-info);
139
+ --card-selected-ring: var(--border-1-info);
140
+ }
141
+ a-card[tone="success"] {
142
+ --card-title: var(--text-1-success);
143
+ --card-text: var(--text-2-success);
144
+ --card-bg-secondary: var(--bg-2-success);
145
+ --card-bg-primary: var(--bg-4-success);
146
+ --card-border: var(--border-4-success);
147
+ --card-selected-ring: var(--border-1-success);
148
+ }
149
+ a-card[tone="warning"] {
150
+ --card-title: var(--text-1-warning);
151
+ --card-text: var(--text-2-warning);
152
+ --card-bg-secondary: var(--bg-2-warning);
153
+ --card-bg-primary: var(--bg-4-warning);
154
+ --card-border: var(--border-4-warning);
155
+ --card-selected-ring: var(--border-1-warning);
156
+ }
157
+ a-card[tone="critical"] {
158
+ --card-title: var(--text-1-critical);
159
+ --card-text: var(--text-2-critical);
160
+ --card-bg-secondary: var(--bg-2-critical);
161
+ --card-bg-primary: var(--bg-4-critical);
162
+ --card-border: var(--border-4-critical);
163
+ --card-selected-ring: var(--border-1-critical);
164
+ }
165
+
166
+ /* Custom tone — any non-named `tone` value keeps the source hue while
167
+ lightness/chroma are pinned per token in oklch, the <a-expander> mechanism.
168
+ The JSX wrapper writes --card-tone-source inline; the typed attr() fallback
169
+ picks up raw <a-card tone="…"> on engines that support it. */
170
+ a-card[tone]:not(
171
+ [tone="neutral"],
172
+ [tone="brand"],
173
+ [tone="info"],
174
+ [tone="success"],
175
+ [tone="warning"],
176
+ [tone="critical"]
177
+ ) {
178
+ --card-tone-source: attr(tone type(<color>), currentColor);
179
+
180
+ --card-text: oklch(from var(--card-tone-source) 0.41 0.15 h);
181
+ --card-title: oklch(from var(--card-tone-source) 0.33 0.15 h);
182
+ --card-bg-secondary: oklch(from var(--card-tone-source) 0.99 0.006 h);
183
+ --card-bg-primary: oklch(from var(--card-tone-source) 0.955 0.02 h);
184
+ --card-border: oklch(from var(--card-tone-source) 0.895 0.045 h);
185
+ --card-selected-ring: oklch(from var(--card-tone-source) 0.6 0.15 h);
186
+ }
187
+
188
+ .dark a-card[tone]:not(
189
+ [tone="neutral"],
190
+ [tone="brand"],
191
+ [tone="info"],
192
+ [tone="success"],
193
+ [tone="warning"],
194
+ [tone="critical"]
195
+ ) {
196
+ --card-text: oklch(from var(--card-tone-source) 0.75 0.11 h);
197
+ --card-title: oklch(from var(--card-tone-source) 0.82 0.10 h);
198
+ --card-bg-secondary: oklch(from var(--card-tone-source) 0.13 0.02 h);
199
+ --card-bg-primary: oklch(from var(--card-tone-source) 0.19 0.04 h);
200
+ --card-border: oklch(from var(--card-tone-source) 0.26 0.07 h);
201
+ --card-selected-ring: oklch(from var(--card-tone-source) 0.7 0.13 h);
202
+ }
203
+
204
+ /* Size — padding scale (medium is the default, set on the base rule). */
205
+ a-card[size="small"] {
206
+ --card-padding: 12px;
207
+ }
208
+ a-card[size="large"] {
209
+ --card-padding: 20px;
210
+ }
211
+ }
@@ -0,0 +1,10 @@
1
+ import { HTMLElementBase } from '../anta_helpers';
2
+ import './a-card.css';
3
+ export declare class ACardElement extends HTMLElementBase {
4
+ #private;
5
+ static observedAttributes: string[];
6
+ constructor();
7
+ connectedCallback(): void;
8
+ attributeChangedCallback(): void;
9
+ }
10
+ export declare function register_a_card(): void;