@adia-ai/web-components 0.0.15 → 0.0.17
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/components/alert/alert.css +5 -0
- package/components/alert/alert.js +4 -2
- package/components/button/button.js +4 -1
- package/components/chat/chat-input.js +13 -2
- package/components/description-list/description-list.js +4 -3
- package/components/drawer/drawer.css +13 -6
- package/components/field/field.css +113 -63
- package/components/field/field.js +44 -142
- package/components/icon/icon.a2ui.json +1 -1
- package/components/icon/icon.css +16 -0
- package/components/icon/icon.js +18 -0
- package/components/icon/icon.yaml +6 -2
- package/components/index.js +1 -0
- package/components/input/input.a2ui.json +1 -1
- package/components/input/input.css +29 -24
- package/components/input/input.js +36 -9
- package/components/input/input.yaml +3 -1
- package/components/option-card/option-card.a2ui.json +262 -0
- package/components/option-card/option-card.css +219 -0
- package/components/option-card/option-card.js +158 -0
- package/components/option-card/option-card.yaml +234 -0
- package/components/rating/rating.a2ui.json +10 -0
- package/components/rating/rating.yaml +8 -0
- package/components/segment/segment.a2ui.json +5 -0
- package/components/segment/segment.css +14 -0
- package/components/segment/segment.js +21 -1
- package/components/segment/segment.yaml +5 -0
- package/components/select/select.css +6 -2
- package/components/textarea/textarea.css +1 -1
- package/components/textarea/textarea.js +2 -2
- package/core/data-stream.js +21 -0
- package/core/form.js +5 -0
- package/core/index.js +2 -0
- package/core/streams-bridge.js +96 -0
- package/package.json +1 -1
- package/patterns/app-nav-group/app-nav-group.css +2 -2
- package/patterns/app-shell/css/app-shell.tokens.css +5 -1
- package/patterns/section-nav/section-nav.css +4 -3
- package/styles/colors/semantics.css +11 -6
- package/styles/components.css +1 -0
- package/styles/prose.css +3 -7
- package/styles/tokens.css +7 -4
- package/styles/typography.css +3 -3
package/components/icon/icon.css
CHANGED
|
@@ -17,4 +17,20 @@
|
|
|
17
17
|
width: 100%;
|
|
18
18
|
height: 100%;
|
|
19
19
|
}
|
|
20
|
+
|
|
21
|
+
/* ── Named size scale ─────────────────────────────────────────────
|
|
22
|
+
sm/md/lg are also driven by the universal `[size]` token system
|
|
23
|
+
on ancestors (tokens.css §SIZE PRESETS) — these rules let an
|
|
24
|
+
icon-ui set its own size locally. xs / xl / 2xl / 3xl / 4xl
|
|
25
|
+
extend the scale beyond the universal range for hero placeholder
|
|
26
|
+
contexts. `fill` matches the parent box. */
|
|
27
|
+
:scope[size="xs"] { --icon-size: 0.75rem; } /* 12px */
|
|
28
|
+
:scope[size="sm"] { --icon-size: 0.875rem; } /* 14px */
|
|
29
|
+
:scope[size="md"] { --icon-size: 1rem; } /* 16px */
|
|
30
|
+
:scope[size="lg"] { --icon-size: 1.25rem; } /* 20px */
|
|
31
|
+
:scope[size="xl"] { --icon-size: 2rem; } /* 32px */
|
|
32
|
+
:scope[size="2xl"] { --icon-size: 3rem; } /* 48px */
|
|
33
|
+
:scope[size="3xl"] { --icon-size: 4rem; } /* 64px */
|
|
34
|
+
:scope[size="4xl"] { --icon-size: 6rem; } /* 96px */
|
|
35
|
+
:scope[size="fill"] { --icon-size: 100%; }
|
|
20
36
|
}
|
package/components/icon/icon.js
CHANGED
|
@@ -19,6 +19,24 @@ class AdiaIcon extends AdiaElement {
|
|
|
19
19
|
if (this.label) this.setAttribute('aria-label', this.label);
|
|
20
20
|
const svg = getIcon(this.name, this.weight || 'regular');
|
|
21
21
|
if (svg && this.innerHTML !== svg) this.innerHTML = svg;
|
|
22
|
+
|
|
23
|
+
// Pixel / rem / em passthrough — `size="48"` → 48px,
|
|
24
|
+
// `size="3.5rem"` → 3.5rem. Named-scale values (xs/sm/md/lg/xl/
|
|
25
|
+
// 2xl/3xl/4xl/fill) are handled by `:scope[size="…"]` rules in
|
|
26
|
+
// icon.css; we only set --icon-size inline for free-form values.
|
|
27
|
+
if (this.size && this.#isFreeFormSize(this.size)) {
|
|
28
|
+
this.style.setProperty('--icon-size', this.#normalizeSize(this.size));
|
|
29
|
+
} else if (this.style.getPropertyValue('--icon-size')) {
|
|
30
|
+
this.style.removeProperty('--icon-size');
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
#isFreeFormSize(s) {
|
|
35
|
+
return /^\d+(\.\d+)?(px|rem|em)?$/.test(s);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
#normalizeSize(s) {
|
|
39
|
+
return /^\d+(\.\d+)?$/.test(s) ? `${s}px` : s;
|
|
22
40
|
}
|
|
23
41
|
}
|
|
24
42
|
customElements.define('icon-ui', AdiaIcon);
|
|
@@ -18,8 +18,12 @@ props:
|
|
|
18
18
|
default: ""
|
|
19
19
|
size:
|
|
20
20
|
description: >-
|
|
21
|
-
Icon size. Accepts the named scale
|
|
22
|
-
|
|
21
|
+
Icon size. Accepts the named scale
|
|
22
|
+
(`xs` 12px / `sm` 14px / `md` 16px / `lg` 20px / `xl` 32px /
|
|
23
|
+
`2xl` 48px / `3xl` 64px / `4xl` 96px / `fill` 100% of parent)
|
|
24
|
+
or a free-form pixel / rem / em value as a string ("48", "3rem",
|
|
25
|
+
"1.25em"). Overrides the inherited `--a-icon-size` from the
|
|
26
|
+
universal `[size]` system on ancestors.
|
|
23
27
|
type: string
|
|
24
28
|
default: ""
|
|
25
29
|
weight:
|
package/components/index.js
CHANGED
|
@@ -15,6 +15,7 @@ export { AdiaInput } from './input/input.js';
|
|
|
15
15
|
export { AdiaTextarea } from './textarea/textarea.js';
|
|
16
16
|
export { AdiaCheck } from './check/check.js';
|
|
17
17
|
export { AdiaRadio } from './radio/radio.js';
|
|
18
|
+
export { AdiaOptionCard } from './option-card/option-card.js';
|
|
18
19
|
export { AdiaSwitch } from './switch/switch.js';
|
|
19
20
|
export { AdiaSlider } from './slider/slider.js';
|
|
20
21
|
export { AdiaSelect } from './select/select.js';
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"default": ""
|
|
53
53
|
},
|
|
54
54
|
"label": {
|
|
55
|
-
"description": "
|
|
55
|
+
"description": "Inline label rendered as a leading caption inside the input chrome, between any prefix and the value. Wires aria-labelledby on the editable surface. For stacked label / hint / error compositions, wrap with field-ui.",
|
|
56
56
|
"type": "string",
|
|
57
57
|
"default": ""
|
|
58
58
|
},
|
|
@@ -11,11 +11,8 @@
|
|
|
11
11
|
--input-height: var(--a-size);
|
|
12
12
|
--input-px: var(--a-ui-px);
|
|
13
13
|
--input-font-size: var(--a-ui-size);
|
|
14
|
-
--input-label-size: var(--a-label-size);
|
|
15
|
-
--input-label-fg: var(--a-label-color);
|
|
16
14
|
--input-placeholder-fg: var(--a-ui-text-placeholder);
|
|
17
15
|
--input-affix-fg: var(--a-ui-text-placeholder);
|
|
18
|
-
--input-gap: var(--a-ui-py);
|
|
19
16
|
--input-field-gap: var(--a-space-1);
|
|
20
17
|
|
|
21
18
|
/* ── Transitions ── */
|
|
@@ -26,8 +23,7 @@
|
|
|
26
23
|
--input-bg-hover: var(--a-ui-bg-hover);
|
|
27
24
|
--input-fg-hover: var(--a-fg);
|
|
28
25
|
--input-affix-fg-hover: var(--a-fg-subtle);
|
|
29
|
-
--input-fg-focus: var(--a-fg);
|
|
30
|
-
--input-label-fg-focus: var(--a-fg-subtle);
|
|
26
|
+
--input-fg-focus: var(--a-fg-strong);
|
|
31
27
|
|
|
32
28
|
/* ── State: disabled ── */
|
|
33
29
|
--input-bg-disabled: var(--a-ui-bg-disabled);
|
|
@@ -37,23 +33,20 @@
|
|
|
37
33
|
:scope {
|
|
38
34
|
/* ── Base ── */
|
|
39
35
|
box-sizing: border-box;
|
|
40
|
-
display:
|
|
41
|
-
flex-direction: column;
|
|
42
|
-
gap: var(--input-gap);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
:scope[data-direction="row"] {
|
|
46
|
-
display: grid;
|
|
47
|
-
grid-template-columns: 1fr 1fr;
|
|
48
|
-
align-items: center;
|
|
36
|
+
display: block;
|
|
49
37
|
}
|
|
50
38
|
|
|
51
|
-
/*
|
|
39
|
+
/* Inline label — sits inside [slot="field"] as a leading caption,
|
|
40
|
+
between [slot="prefix"] and [slot="text"]. Dim by default; shares
|
|
41
|
+
the input chrome border. For stacked label / hint / error, wrap
|
|
42
|
+
with field-ui. */
|
|
52
43
|
[slot="label"] {
|
|
53
|
-
|
|
54
|
-
color: var(--input-
|
|
44
|
+
flex-shrink: 0;
|
|
45
|
+
color: var(--input-affix-fg);
|
|
46
|
+
font-size: var(--input-font-size);
|
|
47
|
+
user-select: none;
|
|
48
|
+
pointer-events: none;
|
|
55
49
|
}
|
|
56
|
-
[slot="label"][label]::after { content: attr(label); }
|
|
57
50
|
|
|
58
51
|
/* Field container */
|
|
59
52
|
[slot="field"] {
|
|
@@ -68,7 +61,13 @@
|
|
|
68
61
|
color: var(--input-fg);
|
|
69
62
|
font: inherit;
|
|
70
63
|
font-size: var(--input-font-size);
|
|
71
|
-
line-height: 1
|
|
64
|
+
/* line-height: 1.4 (not 1) so descender-bearing glyphs (g, j, p, q, y)
|
|
65
|
+
have room inside [slot="text"]'s line box. With line-height: 1 the
|
|
66
|
+
line box equals the em-square and descenders extend below it; the
|
|
67
|
+
overflow: hidden on [slot="text"] then clips them. The min-height
|
|
68
|
+
still controls overall chrome height — align-items: center keeps the
|
|
69
|
+
baseline centered regardless of line-height. */
|
|
70
|
+
line-height: 1.4;
|
|
72
71
|
cursor: text;
|
|
73
72
|
transition: border-color var(--input-duration) var(--input-easing);
|
|
74
73
|
}
|
|
@@ -92,9 +91,6 @@
|
|
|
92
91
|
:scope[error]:not([disabled]):focus-within [slot="field"] {
|
|
93
92
|
box-shadow: var(--input-focus-ring-invalid);
|
|
94
93
|
}
|
|
95
|
-
:scope:not([disabled]):focus-within [slot="label"] {
|
|
96
|
-
color: var(--input-label-fg-focus);
|
|
97
|
-
}
|
|
98
94
|
|
|
99
95
|
/* Text (contenteditable span) */
|
|
100
96
|
[slot="text"] {
|
|
@@ -122,17 +118,26 @@
|
|
|
122
118
|
|
|
123
119
|
/* Placeholder (contenteditable only) */
|
|
124
120
|
span[slot="text"][data-empty]::before {
|
|
125
|
-
content: attr(
|
|
121
|
+
content: attr(data-placeholder);
|
|
126
122
|
color: var(--input-placeholder-fg);
|
|
127
123
|
pointer-events: none;
|
|
128
124
|
}
|
|
129
125
|
|
|
130
|
-
/* Prefix + Suffix
|
|
126
|
+
/* Prefix + Suffix — inline-flex so icon-ui (or any non-text affix
|
|
127
|
+
content) centers vertically within the slot wrapper. Without
|
|
128
|
+
this, an icon inside a default <span slot="prefix"> sits at the
|
|
129
|
+
baseline of the field's 1.4 line-box and renders visually below
|
|
130
|
+
the field's center. With inline-flex + align-items: center the
|
|
131
|
+
icon centers in the slot box, which is itself centered in the
|
|
132
|
+
field via the field's align-items: center. */
|
|
131
133
|
[slot="prefix"],
|
|
132
134
|
[slot="suffix"] {
|
|
135
|
+
display: inline-flex;
|
|
136
|
+
align-items: center;
|
|
133
137
|
flex-shrink: 0;
|
|
134
138
|
color: var(--input-affix-fg);
|
|
135
139
|
font-size: var(--input-font-size);
|
|
140
|
+
line-height: 1;
|
|
136
141
|
user-select: none;
|
|
137
142
|
pointer-events: none;
|
|
138
143
|
}
|
|
@@ -2,9 +2,17 @@
|
|
|
2
2
|
* <input-ui> — Text input. The host IS the interactive surface.
|
|
3
3
|
* Uses contenteditable for text entry, ElementInternals for form participation.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Slots inside [slot="field"]:
|
|
6
|
+
* prefix → label → text → suffix
|
|
7
|
+
*
|
|
8
|
+
* <input-ui label="Email" placeholder="you@acme.com"></input-ui>
|
|
9
|
+
* <input-ui label="Email" prefix="user" placeholder="you@acme.com"></input-ui>
|
|
10
|
+
* <input-ui placeholder="Search" prefix="magnifying-glass"></input-ui>
|
|
11
|
+
* <input-ui prefix="@" value="kim"></input-ui>
|
|
12
|
+
*
|
|
13
|
+
* label renders as a dim leading caption inside the chrome (next to the
|
|
14
|
+
* value, sharing the input's border) — for stacked label / hint / error
|
|
15
|
+
* compositions, wrap with field-ui.
|
|
8
16
|
*/
|
|
9
17
|
|
|
10
18
|
import { AdiaFormElement } from '../../core/form.js';
|
|
@@ -15,6 +23,13 @@ const renderAffix = (v) => isIconName(v)
|
|
|
15
23
|
: v;
|
|
16
24
|
|
|
17
25
|
class AdiaInput extends AdiaFormElement {
|
|
26
|
+
// Opt out of AdiaFormElement's per-control `label` deprecation warning.
|
|
27
|
+
// input-ui's `label` is a first-class API rendering an inline-leading
|
|
28
|
+
// caption inside the chrome with `aria-labelledby` wiring on the
|
|
29
|
+
// editable surface — not the inert above-the-field rendering that
|
|
30
|
+
// motivated the deprecation.
|
|
31
|
+
static labelDeprecated = false;
|
|
32
|
+
|
|
18
33
|
static properties = {
|
|
19
34
|
...AdiaFormElement.properties,
|
|
20
35
|
placeholder: { type: String, default: '', reflect: true },
|
|
@@ -28,6 +43,8 @@ class AdiaInput extends AdiaFormElement {
|
|
|
28
43
|
static template = () => null;
|
|
29
44
|
|
|
30
45
|
#textEl = null;
|
|
46
|
+
#labelEl = null;
|
|
47
|
+
static #labelSeq = 0;
|
|
31
48
|
|
|
32
49
|
get #isNativeInput() {
|
|
33
50
|
return this.type === 'password' || this.type === 'number';
|
|
@@ -39,24 +56,28 @@ class AdiaInput extends AdiaFormElement {
|
|
|
39
56
|
|
|
40
57
|
if (!this.querySelector('[slot="text"]')) {
|
|
41
58
|
const useNative = this.#isNativeInput;
|
|
59
|
+
const labelId = this.label ? `input-label-${++AdiaInput.#labelSeq}` : '';
|
|
42
60
|
this.innerHTML = `
|
|
43
|
-
${this.label ? `<label slot="label" label="${this.label}"></label>` : ''}
|
|
44
61
|
<div slot="field">
|
|
45
62
|
${this.prefix ? `<span slot="prefix">${renderAffix(this.prefix)}</span>` : ''}
|
|
63
|
+
${this.label ? `<span slot="label" id="${labelId}">${this.label}</span>` : ''}
|
|
46
64
|
${useNative
|
|
47
65
|
? `<input slot="text" type="${this.type}" tabindex="0"
|
|
48
66
|
placeholder="${this.placeholder}" value="${this.value || ''}"
|
|
49
67
|
autocomplete="${this.type === 'password' ? 'current-password' : 'off'}"
|
|
68
|
+
${labelId ? `aria-labelledby="${labelId}"` : ''}
|
|
50
69
|
${this.disabled ? 'disabled' : ''} ${this.readonly ? 'readonly' : ''} />`
|
|
51
70
|
: `<span slot="text" contenteditable="plaintext-only" tabindex="0"
|
|
52
71
|
${this.value ? '' : 'data-empty=""'}
|
|
53
|
-
aria-
|
|
72
|
+
${labelId ? `aria-labelledby="${labelId}"` : ''}
|
|
73
|
+
data-placeholder="${this.placeholder}"></span>`}
|
|
54
74
|
${this.suffix ? `<span slot="suffix">${renderAffix(this.suffix)}</span>` : ''}
|
|
55
75
|
</div>
|
|
56
76
|
`;
|
|
57
77
|
}
|
|
58
78
|
|
|
59
79
|
this.#textEl = this.querySelector('[slot="text"]');
|
|
80
|
+
this.#labelEl = this.querySelector('[slot="label"]');
|
|
60
81
|
if (!this.#isNativeInput && this.value) this.#textEl.textContent = this.value;
|
|
61
82
|
|
|
62
83
|
if (this.#textEl) {
|
|
@@ -75,7 +96,7 @@ class AdiaInput extends AdiaFormElement {
|
|
|
75
96
|
this.#textEl.disabled = this.disabled;
|
|
76
97
|
this.#textEl.readOnly = this.readonly;
|
|
77
98
|
} else {
|
|
78
|
-
this.#textEl.setAttribute('
|
|
99
|
+
this.#textEl.setAttribute('data-placeholder', this.placeholder);
|
|
79
100
|
if (this.disabled || this.readonly) {
|
|
80
101
|
this.#textEl.contentEditable = 'false';
|
|
81
102
|
} else {
|
|
@@ -83,10 +104,15 @@ class AdiaInput extends AdiaFormElement {
|
|
|
83
104
|
}
|
|
84
105
|
}
|
|
85
106
|
|
|
86
|
-
|
|
87
|
-
if (label && this.label) label.setAttribute('label', this.label);
|
|
107
|
+
if (this.#labelEl) this.#labelEl.textContent = this.label || '';
|
|
88
108
|
|
|
89
|
-
|
|
109
|
+
if (this.label) {
|
|
110
|
+
this.removeAttribute('aria-label');
|
|
111
|
+
} else if (this.placeholder) {
|
|
112
|
+
this.setAttribute('aria-label', this.placeholder);
|
|
113
|
+
} else {
|
|
114
|
+
this.removeAttribute('aria-label');
|
|
115
|
+
}
|
|
90
116
|
}
|
|
91
117
|
|
|
92
118
|
#onInput = () => {
|
|
@@ -141,6 +167,7 @@ class AdiaInput extends AdiaFormElement {
|
|
|
141
167
|
this.#textEl.removeEventListener('paste', this.#onPaste);
|
|
142
168
|
}
|
|
143
169
|
this.#textEl = null;
|
|
170
|
+
this.#labelEl = null;
|
|
144
171
|
}
|
|
145
172
|
}
|
|
146
173
|
customElements.define('input-ui', AdiaInput);
|
|
@@ -47,7 +47,9 @@ props:
|
|
|
47
47
|
type: string
|
|
48
48
|
default: ""
|
|
49
49
|
label:
|
|
50
|
-
description:
|
|
50
|
+
description: Inline label rendered as a leading caption inside the input chrome,
|
|
51
|
+
between any prefix and the value. Wires aria-labelledby on the editable
|
|
52
|
+
surface. For stacked label / hint / error compositions, wrap with field-ui.
|
|
51
53
|
type: string
|
|
52
54
|
default: ""
|
|
53
55
|
maxlength:
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://adiaui.dev/a2ui/v0_9/components/OptionCard.json",
|
|
4
|
+
"title": "OptionCard",
|
|
5
|
+
"description": "Selectable card with radio semantics. A \"rich radio\" — single-select one of N where each option carries a heading, optional description, and optional leading icon. Siblings sharing a `name` form a radiogroup. The whole card is the click target; a CSS-rendered radio circle in the top-left signals state. Form-associated, so `name=value` submits with the parent form when checked.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"allOf": [
|
|
8
|
+
{
|
|
9
|
+
"$ref": "common_types.json#/$defs/ComponentCommon"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"$ref": "common_types.json#/$defs/CatalogComponentCommon"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"properties": {
|
|
16
|
+
"description": {
|
|
17
|
+
"description": "Description text. Stamped into a [slot=\"description\"] span when no slotted description is provided.",
|
|
18
|
+
"type": "string",
|
|
19
|
+
"default": ""
|
|
20
|
+
},
|
|
21
|
+
"required": {
|
|
22
|
+
"description": "Marks the radiogroup as requiring a selection for form validation.",
|
|
23
|
+
"type": "boolean",
|
|
24
|
+
"default": false
|
|
25
|
+
},
|
|
26
|
+
"checked": {
|
|
27
|
+
"description": "Whether this card is currently selected.",
|
|
28
|
+
"type": "boolean",
|
|
29
|
+
"default": false
|
|
30
|
+
},
|
|
31
|
+
"component": {
|
|
32
|
+
"const": "OptionCard"
|
|
33
|
+
},
|
|
34
|
+
"disabled": {
|
|
35
|
+
"description": "Disables interaction and dims the card.",
|
|
36
|
+
"type": "boolean",
|
|
37
|
+
"default": false
|
|
38
|
+
},
|
|
39
|
+
"heading": {
|
|
40
|
+
"description": "Heading text. Stamped into a [slot=\"heading\"] span when no slotted heading is provided.",
|
|
41
|
+
"type": "string",
|
|
42
|
+
"default": ""
|
|
43
|
+
},
|
|
44
|
+
"icon": {
|
|
45
|
+
"description": "Optional Phosphor icon name. Stamped as a leading <icon-ui slot=\"icon\"> when set.",
|
|
46
|
+
"type": "string",
|
|
47
|
+
"default": ""
|
|
48
|
+
},
|
|
49
|
+
"layout": {
|
|
50
|
+
"description": "Internal layout. `default` puts the indicator on the left and the icon adjacent. `tile` stacks vertically — icon top-left, indicator top-right, heading + description below — for hero pickers (source / role / plan tiles) where the icon is a primary brand cue.",
|
|
51
|
+
"type": "string",
|
|
52
|
+
"enum": [
|
|
53
|
+
"default",
|
|
54
|
+
"tile"
|
|
55
|
+
],
|
|
56
|
+
"default": "default"
|
|
57
|
+
},
|
|
58
|
+
"name": {
|
|
59
|
+
"description": "Form control name. Siblings sharing a name form a radiogroup.",
|
|
60
|
+
"type": "string",
|
|
61
|
+
"default": ""
|
|
62
|
+
},
|
|
63
|
+
"value": {
|
|
64
|
+
"description": "Form value submitted when checked (defaults to `on` if empty).",
|
|
65
|
+
"type": "string",
|
|
66
|
+
"default": ""
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"required": [
|
|
70
|
+
"component"
|
|
71
|
+
],
|
|
72
|
+
"unevaluatedProperties": false,
|
|
73
|
+
"x-adiaui": {
|
|
74
|
+
"anti_patterns": [],
|
|
75
|
+
"category": "input",
|
|
76
|
+
"events": {
|
|
77
|
+
"change": {
|
|
78
|
+
"description": "Fired when this card becomes selected (bubbles)."
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"examples": [
|
|
82
|
+
{
|
|
83
|
+
"description": "A four-option pick-one for \"what brings you here\" — radio-card behavior with heading + description per option.",
|
|
84
|
+
"a2ui": "[\n {\n \"id\": \"root\",\n \"component\": \"Column\",\n \"gap\": \"2\",\n \"children\": [\"build\", \"explore\", \"migrate\", \"evaluate\"]\n },\n {\n \"id\": \"build\",\n \"component\": \"OptionCard\",\n \"name\": \"use-case\",\n \"value\": \"build\",\n \"checked\": true,\n \"heading\": \"I'm building a product\",\n \"description\": \"Spinning up a new project — design, ship, iterate.\"\n },\n {\n \"id\": \"explore\",\n \"component\": \"OptionCard\",\n \"name\": \"use-case\",\n \"value\": \"explore\",\n \"heading\": \"I'm exploring the product\",\n \"description\": \"Kicking the tires before bringing a team along.\"\n },\n {\n \"id\": \"migrate\",\n \"component\": \"OptionCard\",\n \"name\": \"use-case\",\n \"value\": \"migrate\",\n \"heading\": \"I'm migrating from another tool\",\n \"description\": \"Moving an existing workspace and want a smooth port.\"\n },\n {\n \"id\": \"evaluate\",\n \"component\": \"OptionCard\",\n \"name\": \"use-case\",\n \"value\": \"evaluate\",\n \"heading\": \"I'm evaluating for my team\",\n \"description\": \"Comparing options and want to dig into specifics.\"\n }\n]",
|
|
85
|
+
"name": "use-case-picker"
|
|
86
|
+
}
|
|
87
|
+
],
|
|
88
|
+
"keywords": [
|
|
89
|
+
"option",
|
|
90
|
+
"card",
|
|
91
|
+
"radio",
|
|
92
|
+
"select",
|
|
93
|
+
"choice",
|
|
94
|
+
"picker",
|
|
95
|
+
"tier",
|
|
96
|
+
"plan",
|
|
97
|
+
"onboarding",
|
|
98
|
+
"registration"
|
|
99
|
+
],
|
|
100
|
+
"name": "AdiaOptionCard",
|
|
101
|
+
"related": [
|
|
102
|
+
"radio",
|
|
103
|
+
"card",
|
|
104
|
+
"check",
|
|
105
|
+
"segmented"
|
|
106
|
+
],
|
|
107
|
+
"slots": {
|
|
108
|
+
"description": {
|
|
109
|
+
"description": "Rich description content. Overrides the `description` attribute when present."
|
|
110
|
+
},
|
|
111
|
+
"default": {
|
|
112
|
+
"description": "Spillover content revealed only when the card is checked — typically a follow-up form field (e.g. a textarea on an \"Other\" option, conditional inputs that depend on the selection). Aligns with the heading/description column; hidden via `display: none` when not checked."
|
|
113
|
+
},
|
|
114
|
+
"heading": {
|
|
115
|
+
"description": "Rich heading content. Overrides the `heading` attribute when present."
|
|
116
|
+
},
|
|
117
|
+
"icon": {
|
|
118
|
+
"description": "Custom icon element. Overrides the `icon` attribute when present."
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
"states": [
|
|
122
|
+
{
|
|
123
|
+
"description": "Default, ready for interaction.",
|
|
124
|
+
"name": "idle"
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"description": "Pointer over a non-checked card.",
|
|
128
|
+
"name": "hover",
|
|
129
|
+
"selector": ":not([checked]):not([disabled]):hover"
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"description": "Selected — accent border, tinted background, filled radio circle.",
|
|
133
|
+
"attribute": "checked",
|
|
134
|
+
"name": "checked"
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"description": "Non-interactive; dimmed.",
|
|
138
|
+
"attribute": "disabled",
|
|
139
|
+
"name": "disabled"
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"description": "Keyboard focus ring.",
|
|
143
|
+
"name": "focused",
|
|
144
|
+
"selector": ":focus-visible"
|
|
145
|
+
}
|
|
146
|
+
],
|
|
147
|
+
"synonyms": {
|
|
148
|
+
"picker": [
|
|
149
|
+
"option-card",
|
|
150
|
+
"radio",
|
|
151
|
+
"select"
|
|
152
|
+
],
|
|
153
|
+
"radio": [
|
|
154
|
+
"option-card",
|
|
155
|
+
"radio",
|
|
156
|
+
"select"
|
|
157
|
+
],
|
|
158
|
+
"tier": [
|
|
159
|
+
"option-card",
|
|
160
|
+
"card",
|
|
161
|
+
"plan"
|
|
162
|
+
]
|
|
163
|
+
},
|
|
164
|
+
"tag": "option-card-ui",
|
|
165
|
+
"tokens": {
|
|
166
|
+
"--option-card-bg": {
|
|
167
|
+
"description": "Default background."
|
|
168
|
+
},
|
|
169
|
+
"--option-card-bg-checked": {
|
|
170
|
+
"description": "Background when checked."
|
|
171
|
+
},
|
|
172
|
+
"--option-card-bg-hover": {
|
|
173
|
+
"description": "Hover background (non-checked)."
|
|
174
|
+
},
|
|
175
|
+
"--option-card-border": {
|
|
176
|
+
"description": "Default border color."
|
|
177
|
+
},
|
|
178
|
+
"--option-card-border-checked": {
|
|
179
|
+
"description": "Border color when checked."
|
|
180
|
+
},
|
|
181
|
+
"--option-card-border-hover": {
|
|
182
|
+
"description": "Hover border color (non-checked)."
|
|
183
|
+
},
|
|
184
|
+
"--option-card-desc-color": {
|
|
185
|
+
"description": "Description text color."
|
|
186
|
+
},
|
|
187
|
+
"--option-card-desc-line-height": {
|
|
188
|
+
"description": "Description line height."
|
|
189
|
+
},
|
|
190
|
+
"--option-card-desc-size": {
|
|
191
|
+
"description": "Description font size."
|
|
192
|
+
},
|
|
193
|
+
"--option-card-disabled-opacity": {
|
|
194
|
+
"description": "Opacity multiplier when disabled."
|
|
195
|
+
},
|
|
196
|
+
"--option-card-duration": {
|
|
197
|
+
"description": "Transition duration for hover / checked state changes."
|
|
198
|
+
},
|
|
199
|
+
"--option-card-easing": {
|
|
200
|
+
"description": "Transition easing."
|
|
201
|
+
},
|
|
202
|
+
"--option-card-focus-ring": {
|
|
203
|
+
"description": "Focus ring (box-shadow value)."
|
|
204
|
+
},
|
|
205
|
+
"--option-card-gap-x": {
|
|
206
|
+
"description": "Horizontal gap between indicator (and icon) and content."
|
|
207
|
+
},
|
|
208
|
+
"--option-card-gap-y": {
|
|
209
|
+
"description": "Vertical gap between heading and description."
|
|
210
|
+
},
|
|
211
|
+
"--option-card-heading-color": {
|
|
212
|
+
"description": "Heading text color."
|
|
213
|
+
},
|
|
214
|
+
"--option-card-heading-color-checked": {
|
|
215
|
+
"description": "Heading text color when the card is checked (defaults to `--a-fg-strong` so the selected card reads with extra emphasis on top of the bg/border state)."
|
|
216
|
+
},
|
|
217
|
+
"--option-card-heading-size": {
|
|
218
|
+
"description": "Heading font size."
|
|
219
|
+
},
|
|
220
|
+
"--option-card-heading-weight": {
|
|
221
|
+
"description": "Heading font weight."
|
|
222
|
+
},
|
|
223
|
+
"--option-card-icon-color": {
|
|
224
|
+
"description": "Leading icon color when the card is not checked."
|
|
225
|
+
},
|
|
226
|
+
"--option-card-icon-color-checked": {
|
|
227
|
+
"description": "Leading icon color when the card is checked."
|
|
228
|
+
},
|
|
229
|
+
"--option-card-icon-size": {
|
|
230
|
+
"description": "Leading icon size (sets `--a-icon-size` on the slotted icon-ui)."
|
|
231
|
+
},
|
|
232
|
+
"--option-card-padding-block": {
|
|
233
|
+
"description": "Vertical padding inside the card."
|
|
234
|
+
},
|
|
235
|
+
"--option-card-padding-inline": {
|
|
236
|
+
"description": "Horizontal padding inside the card."
|
|
237
|
+
},
|
|
238
|
+
"--option-card-radio-bg": {
|
|
239
|
+
"description": "Indicator background when not checked."
|
|
240
|
+
},
|
|
241
|
+
"--option-card-radio-border": {
|
|
242
|
+
"description": "Indicator border when not checked."
|
|
243
|
+
},
|
|
244
|
+
"--option-card-radio-dot": {
|
|
245
|
+
"description": "Inner dot color when checked."
|
|
246
|
+
},
|
|
247
|
+
"--option-card-radio-fill": {
|
|
248
|
+
"description": "Indicator fill color when checked."
|
|
249
|
+
},
|
|
250
|
+
"--option-card-radio-size": {
|
|
251
|
+
"description": "Diameter of the indicator circle."
|
|
252
|
+
},
|
|
253
|
+
"--option-card-radius": {
|
|
254
|
+
"description": "Card corner radius."
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
"traits": [
|
|
258
|
+
"focusable"
|
|
259
|
+
],
|
|
260
|
+
"version": 1
|
|
261
|
+
}
|
|
262
|
+
}
|