@zeyos/zx 4.0.0 → 4.1.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 (51) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/DESIGN-SYSTEM.md +59 -3
  3. package/dist/types/components/app-icon/app-icon.d.ts +88 -0
  4. package/dist/types/components/filter/filter-model.d.ts +87 -0
  5. package/dist/types/components/filter/filter.d.ts +331 -0
  6. package/dist/types/components/select/select.d.ts +46 -4
  7. package/dist/types/components/table/table.d.ts +103 -0
  8. package/dist/types/components/tag-picker/tag-picker.d.ts +16 -0
  9. package/dist/types/core/dom.d.ts +1 -1
  10. package/dist/types/core/util.d.ts +9 -0
  11. package/dist/types/index.d.ts +4 -1
  12. package/dist/types/zeyos/icons.d.ts +21 -4
  13. package/dist/types/zeyos/index.d.ts +1 -1
  14. package/dist/types/zeyos/select.d.ts +2 -0
  15. package/dist/zx-zeyos.esm.js +703 -75
  16. package/dist/zx-zeyos.esm.min.js +1 -1
  17. package/dist/zx.css +564 -9
  18. package/dist/zx.esm.js +1569 -115
  19. package/dist/zx.esm.min.js +3 -3
  20. package/dist/zx.global.js +1569 -115
  21. package/dist/zx.global.min.js +3 -3
  22. package/dist/zx.min.css +1 -1
  23. package/docs/api.json +379 -0
  24. package/docs/llms.md +97 -18
  25. package/docs/llms.txt +11 -9
  26. package/package.json +1 -1
  27. package/src/components/app-icon/app-icon.css +189 -0
  28. package/src/components/app-icon/app-icon.js +137 -0
  29. package/src/components/button/button.css +34 -2
  30. package/src/components/card/card.css +25 -3
  31. package/src/components/filter/filter-model.js +226 -0
  32. package/src/components/filter/filter.css +147 -0
  33. package/src/components/filter/filter.js +554 -0
  34. package/src/components/grid/grid.css +1 -1
  35. package/src/components/grid/grid.js +4 -1
  36. package/src/components/message/message.css +40 -3
  37. package/src/components/select/select.css +17 -0
  38. package/src/components/select/select.js +105 -24
  39. package/src/components/table/table.css +85 -0
  40. package/src/components/table/table.js +418 -24
  41. package/src/components/tag-picker/tag-picker.css +19 -0
  42. package/src/components/tag-picker/tag-picker.js +42 -5
  43. package/src/core/dom.js +1 -1
  44. package/src/core/util.js +20 -0
  45. package/src/index.js +7 -1
  46. package/src/zeyos/icons.js +37 -24
  47. package/src/zeyos/index.js +1 -1
  48. package/src/zeyos/select.js +6 -5
  49. package/styles/tokens/semantic.css +17 -0
  50. package/styles/tokens/themes.css +2 -2
  51. package/styles/zx.css +2 -0
package/docs/llms.txt CHANGED
@@ -22,30 +22,32 @@ exports and as `window.zx.*`.
22
22
 
23
23
  ## Components
24
24
  - Inputs and identity: `button()`, `buttonGroup()`, `badge()`/`badgeGroup()`, `CheckButton`,
25
- `Toggle`, `Search`, `Launcher`, `Avatar`, `AccountMenu`, `Select` (APG combobox;
26
- `filter: false|'local'|async`; priority, status, permission, and optional ZeyOS entity presets),
27
- `Checklist`, `TagPicker`, `NumberField`, `Rating`, `Slider`,
25
+ `Toggle`, `Search`, `AppIcon` (+ `zeyosAppIcon()` presets), `Avatar`,
26
+ `Select` (APG combobox; `filter: false|'local'|async`; priority, status, permission, and
27
+ `Select.entity()` presets), `Checklist`, `TagPicker` (icon/color identity), `NumberField`, `Rating`, `Slider`,
28
28
  `copyButton()`/`CopyInput`, `DatePicker`, `MonthPicker`, `TimePicker`,
29
29
  `Datebox`/`DateTimeBox`, `DateRangePicker`/`DateRangeBox`, `Timebox`.
30
30
  - Overlays: `Message` (toasts/inline/progress), `Modal`, `Dialog` (+ `alert`/`confirm`/`prompt` Promises), `Sheet` (edge-anchored Dialog; `side`, `modal: true|'trap-focus'|false`, `backdrop`, `snap` detents, swipe-to-dismiss), `SheetStack` (drill-down; `stack`/`cascade`), `Dropdown`, `MenuButton`, `ContextMenu` (right-click; `selector` for per-row menus), `Tooltip`/`tooltip()`/`describe()`.
31
- - Data: `Table` (sortable, selectable, sticky header, opt-in inline cell/row editing via
32
- `editMode`), `Grid` with `Grid.BillingItems()`, engine-neutral `Chart` with injected
33
- `ChartJsAdapter`, `DataFilter`, `Pagination` (+ pure `paginationRange()`), `TreeView` (APG tree),
31
+ - Data: `Table` (sortable, selectable, sticky header, opt-in inline cell/row editing, row reorder,
32
+ column visibility), `Grid` with single-click editing and `Grid.BillingItems()`, engine-neutral
33
+ `Chart` with injected `ChartJsAdapter`, backend-neutral nested `Filter`, simple client-side
34
+ `DataFilter`, `Pagination` (+ pure `paginationRange()`), `TreeView` (APG tree),
34
35
  `Finder` (Miller columns).
35
36
  - Forms: `Form`, `Fieldset`, `Questionnaire` (guided one-question-at-a-time flow with branching), `Field` (type registry), `ValueList`, `MultiValueEditor`, `FieldUpload`.
36
37
  - Layout: `stack()`/`grid()`/`aspect()` (spacing, an intrinsically reflowing grid, a fixed-ratio
37
38
  box), `Groupbox`, `Card` (media/title link/actions/footer), `Panel` (header/footer action
38
39
  buttons), `MasterPanel`, `Tabbox`
39
40
  (`variant: 'divided'|'bracket'|'line'|'segmented'`), `NavigationBar`, `AppSidebar` (expanded and
40
- minimized vertical states plus horizontal rail layouts), `Toolbar` (APG toolbar with overflow menu), `SplitView`, `Dock`
41
+ minimized vertical states plus horizontal rail layouts), `AccountMenu`, `Launcher`,
42
+ `spinner()`/`ProgressBar`/`InlineLoading`, `skeleton()`/`skeletonText()`/`skeletonTable()`,
43
+ `Toolbar` (APG toolbar with overflow menu), `SplitView`, `Dock`
41
44
  (collapsible resizable pane stack; tab groups, regions, adopts a `Sheet`), `Stepper`,
42
45
  `Breadcrumb`, `emptyState()`.
43
- - Feedback: `spinner()` (unknown duration), `ProgressBar` (known share; determinate or indeterminate), `InlineLoading` (a wait resolving into its outcome in place), `skeleton()`/`skeletonText()`/`skeletonTable()` (placeholders shaped like the content; `aria-hidden`, set `aria-busy` on the region).
44
46
  - Core: `Component`, `h`/`__`/`icon`, `position`, `breakpoints`/`breakpointOf`/`matchBreakpoint`/`onBreakpoint` (named width bands, observed on an element or the window), `Http`/`zeyosService`, i18n, date utils, `defineElements` (declarative `<zx-*>` custom elements).
45
47
  - Helpers: `formatNumber`/`formatCurrency`/`formatPercent`/`formatFileSize`/`formatRelativeTime` (locale-aware, cached `Intl`), `debounce`/`throttle`, `groupBy`/`sortBy`/`uniqueBy`, `escapeRegExp`/`highlightMatch`, `storage()` (namespaced JSON, degrades to memory), `toCsv`/`downloadBlob`/`copyToClipboard`, `truncate()`/`isTruncated()` (line clamp with the full value on hover).
46
48
 
47
49
  ## Theming
48
- Load `zx.css` once; set `data-zx-theme` (`light`|`dark`|`auto`) and `data-zx-density` (`cozy`|`compact`) on any ancestor. Define a product theme by overriding semantic `--zx-color-*` / `--zx-control-*` tokens under `[data-zx-theme="name"]`. Never use tier-1 palette tokens or raw color literals in application CSS.
50
+ Load `zx.css` once; set `data-zx-theme` (`light`|`dark`|`auto`) and `data-zx-density` (`cozy`|`compact`) on any ancestor. Define a product theme by overriding semantic `--zx-color-*` / `--zx-control-*` tokens under `[data-zx-theme="name"]`. The theme studio controls accent, neutral tint, density, font, type size, radius, control height, and CSS-first glass material. Never use tier-1 palette tokens or raw color literals in application CSS.
49
51
 
50
52
  ## Talking to ZeyOS
51
53
  Use the dedicated ZeyOS client library **`@zeyos/client`** (`npm install @zeyos/client`) for ZeyOS business data: `createZeyosClient({ platform, auth })`, typed `client.api.list/get/create/update<Resource>` over the full OpenAPI surface, and `normalizeListResult`. Zx accepts the client by injection and does not bundle it. See the `website/layouts/zeyos-invoices.layout.js` layout for a Zx-components-on-@zeyos/client reference. Zx also ships a minimal `Http`/`zeyosService` for ad-hoc in-app `remotecall`, but `@zeyos/client` is the recommended default.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeyos/zx",
3
- "version": "4.0.0",
3
+ "version": "4.1.1",
4
4
  "description": "Dependency-free JavaScript implementation of the Xenon Design System.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -0,0 +1,189 @@
1
+ .zx-app-icon {
2
+ --zx-app-icon-size: 36px;
3
+ --zx-app-icon-glyph-size: 52%;
4
+ --zx-app-icon-color: var(--zx-color-accent);
5
+ position: relative;
6
+ isolation: isolate;
7
+ display: inline-grid;
8
+ place-items: center;
9
+ flex: none;
10
+ inline-size: var(--zx-app-icon-size);
11
+ block-size: var(--zx-app-icon-size);
12
+ overflow: hidden;
13
+ border: 1px solid var(--zx-color-glass-border);
14
+ border-radius: max(var(--zx-radius-sm), calc(var(--zx-app-icon-size) * .12));
15
+ background: var(--zx-app-icon-color);
16
+ box-shadow: var(--zx-app-icon-shadow);
17
+ color: var(--zx-app-icon-glyph, var(--zx-color-app-icon-glyph));
18
+ font-size: var(--zx-app-icon-size);
19
+ vertical-align: middle;
20
+ transform: translateZ(0);
21
+ }
22
+
23
+ .zx-app-icon[data-glass="subtle"],
24
+ .zx-app-icon[data-glass="strong"] {
25
+ background:
26
+ linear-gradient(145deg, var(--zx-color-glass-highlight-strong), transparent 38%),
27
+ linear-gradient(to bottom right, transparent 42%, var(--zx-color-app-icon-depth)),
28
+ color-mix(in srgb, var(--zx-app-icon-color) var(--zx-glass-color-strength), transparent);
29
+ -webkit-backdrop-filter: blur(calc(var(--zx-glass-blur) * .55)) saturate(var(--zx-glass-saturation));
30
+ backdrop-filter: blur(calc(var(--zx-glass-blur) * .55)) saturate(var(--zx-glass-saturation));
31
+ }
32
+
33
+ .zx-app-icon[data-glass="strong"] {
34
+ -webkit-backdrop-filter: blur(var(--zx-glass-blur)) saturate(var(--zx-glass-saturation));
35
+ backdrop-filter: blur(var(--zx-glass-blur)) saturate(var(--zx-glass-saturation));
36
+ }
37
+
38
+ .zx-app-icon[data-glass="none"] {
39
+ border-color: var(--zx-color-border);
40
+ box-shadow: none;
41
+ }
42
+
43
+ .zx-app-icon[data-glass="none"] .zx-app-icon__shine { display: none; }
44
+ .zx-app-icon[data-glass="none"]::after { display: none; }
45
+
46
+ .zx-app-icon[data-selected] {
47
+ outline: 2px solid var(--zx-color-bg-page);
48
+ box-shadow: 0 0 0 3px var(--zx-color-ring), var(--zx-overlay-shadow);
49
+ }
50
+
51
+ .zx-app-icon__shine {
52
+ position: absolute;
53
+ z-index: 1;
54
+ inset: 1px 13% auto;
55
+ block-size: 38%;
56
+ border-radius: 999px;
57
+ background: linear-gradient(to bottom, var(--zx-color-glass-highlight-strong), transparent);
58
+ filter: blur(1px);
59
+ pointer-events: none;
60
+ }
61
+
62
+ .zx-app-icon::after {
63
+ content: "";
64
+ position: absolute;
65
+ z-index: 1;
66
+ inset: 0;
67
+ border-radius: inherit;
68
+ box-shadow: inset 0 0 0 1px var(--zx-color-glass-highlight);
69
+ pointer-events: none;
70
+ }
71
+
72
+ .zx-app-icon__glyph {
73
+ position: absolute;
74
+ z-index: 2;
75
+ inset-block-start: 50%;
76
+ inset-inline-start: 50%;
77
+ display: grid;
78
+ place-items: center;
79
+ inline-size: var(--zx-app-icon-glyph-size);
80
+ block-size: var(--zx-app-icon-glyph-size);
81
+ margin: 0;
82
+ padding: 0;
83
+ font-size: var(--zx-app-icon-glyph-size);
84
+ line-height: 1;
85
+ filter: drop-shadow(var(--zx-glass-glyph-shadow));
86
+ transform: translate(-50%, -50%);
87
+ }
88
+
89
+ .zx-app-icon__glyph > .zx-icon,
90
+ .zx-app-icon__glyph > svg,
91
+ .zx-app-icon__glyph > i {
92
+ display: grid;
93
+ place-items: center;
94
+ inline-size: 1em;
95
+ block-size: 1em;
96
+ margin: 0;
97
+ color: inherit;
98
+ font-size: 1em;
99
+ line-height: 1;
100
+ text-align: center;
101
+ vertical-align: middle;
102
+ }
103
+
104
+ .zx-app-icon__glyph > i::before {
105
+ display: block;
106
+ line-height: 1;
107
+ }
108
+
109
+ .zx-app-icon__glyph svg [fill]:not([fill="none"]) {
110
+ fill: currentColor;
111
+ }
112
+
113
+ .zx-app-icon__glyph svg [stroke]:not([stroke="none"]) {
114
+ stroke: currentColor;
115
+ }
116
+
117
+ .zx-app-icon__badge {
118
+ position: absolute;
119
+ z-index: 3;
120
+ inset-block-end: 1px;
121
+ inset-inline-end: 1px;
122
+ display: grid;
123
+ place-items: center;
124
+ min-inline-size: 14px;
125
+ block-size: 14px;
126
+ padding-inline: 3px;
127
+ border: 1px solid var(--zx-color-bg-raised);
128
+ border-radius: var(--zx-radius-full);
129
+ background: var(--zx-color-danger);
130
+ color: var(--zx-color-text-invert);
131
+ font-size: 9px;
132
+ font-weight: 700;
133
+ line-height: 1;
134
+ }
135
+
136
+ .zx-app-icon:hover {
137
+ border-color: var(--zx-color-glass-highlight-strong);
138
+ box-shadow: var(--zx-app-icon-shadow-hover);
139
+ filter: brightness(1.06) saturate(1.08);
140
+ }
141
+
142
+ .zx-app-icon:focus-visible,
143
+ :is(a, button, [role="button"]):focus-visible > .zx-app-icon {
144
+ outline: none;
145
+ box-shadow: var(--zx-focus-ring), var(--zx-app-icon-shadow-hover);
146
+ }
147
+
148
+ @media (prefers-reduced-motion: no-preference) {
149
+ .zx-app-icon {
150
+ transition: border-color var(--zx-dur-fast) var(--zx-ease),
151
+ box-shadow var(--zx-dur-fast) var(--zx-ease),
152
+ filter var(--zx-dur-fast) var(--zx-ease),
153
+ transform var(--zx-dur-fast) var(--zx-ease);
154
+ }
155
+
156
+ .zx-app-icon:hover {
157
+ transform: translateY(-2px) scale(1.035);
158
+ }
159
+ }
160
+
161
+ @media (prefers-reduced-transparency: reduce) {
162
+ .zx-app-icon[data-glass="strong"] {
163
+ background: var(--zx-app-icon-color);
164
+ -webkit-backdrop-filter: none;
165
+ backdrop-filter: none;
166
+ }
167
+
168
+ .zx-app-icon[data-glass="subtle"] {
169
+ background: var(--zx-app-icon-color);
170
+ -webkit-backdrop-filter: none;
171
+ backdrop-filter: none;
172
+ }
173
+ }
174
+
175
+ @media (forced-colors: active) {
176
+ .zx-app-icon {
177
+ border-color: ButtonText;
178
+ background: ButtonFace;
179
+ color: ButtonText;
180
+ box-shadow: none;
181
+ filter: none;
182
+ transform: none;
183
+ -webkit-backdrop-filter: none;
184
+ backdrop-filter: none;
185
+ }
186
+
187
+ .zx-app-icon::after,
188
+ .zx-app-icon__shine { display: none; }
189
+ }
@@ -0,0 +1,137 @@
1
+ import { Component } from '../../core/component.js';
2
+ import { h, restoreTarget, snapshotTarget } from '../../core/dom.js';
3
+ import { icon as renderIcon } from '../../core/icons.js';
4
+ import { isCssColor } from '../../core/util.js';
5
+
6
+ /**
7
+ * @typedef {Object} AppIconOptions
8
+ * @property {string|Node|(()=>string|Node)|null} [icon=null] Icon name, node, or lazy renderer.
9
+ * @property {string|null} [color=null] Identity colour; falls back to the accent colour.
10
+ * @property {number|string} [size=36] Edge length in pixels or any CSS length.
11
+ * @property {number|string} [iconSize='52%'] Glyph size.
12
+ * @property {string|null} [label=null] Accessible image label; null makes the icon decorative.
13
+ * @property {string|number|null} [badge=null] Optional corner badge.
14
+ * @property {boolean} [selected=false] Whether the icon represents the active application.
15
+ * @property {false|'subtle'|'strong'} [glass='subtle'] CSS-first glass treatment.
16
+ * @property {string|string[]} [class] Additional class names.
17
+ */
18
+
19
+ /**
20
+ * Stable application-identity tile with progressive CSS glass treatment.
21
+ * @extends {Component<AppIconOptions>}
22
+ */
23
+ export class AppIcon extends Component {
24
+ static cssName = 'app-icon';
25
+
26
+ /** @type {Readonly<AppIconOptions>} */
27
+ static defaults = {
28
+ icon: null,
29
+ color: null,
30
+ size: 36,
31
+ iconSize: '52%',
32
+ label: null,
33
+ badge: null,
34
+ selected: false,
35
+ glass: 'subtle',
36
+ class: []
37
+ };
38
+
39
+ /** @returns {HTMLElement} */
40
+ render() {
41
+ this._createdRoot = this.el === null;
42
+ const root = /** @type {HTMLElement} */ (this.el ?? h('span'));
43
+ this.el = root;
44
+ this._snapshot = this._createdRoot ? null : snapshotTarget(root);
45
+ this._state = { ...this.options };
46
+ this._appliedClasses = [];
47
+ this._sync();
48
+ return root;
49
+ }
50
+
51
+ /** Updates identity or presentation without replacing the root. @param {Partial<AppIconOptions>} values @returns {this} */
52
+ set(values = {}) {
53
+ if (!values || typeof values !== 'object') return this;
54
+ Object.assign(this._state, values);
55
+ this._sync();
56
+ return this;
57
+ }
58
+
59
+ /** Restores an enhanced target. @returns {void} */
60
+ destroy() {
61
+ const root = this.el;
62
+ const snapshot = this._snapshot;
63
+ super.destroy();
64
+ if (!this._createdRoot && root) restoreTarget(root, snapshot);
65
+ }
66
+
67
+ /** @returns {void} */
68
+ _sync() {
69
+ const state = this._state;
70
+ this.el.classList.remove(...this._appliedClasses);
71
+ this._appliedClasses = [state.class].flat()
72
+ .flatMap((value) => String(value ?? '').split(/\s+/)).filter(Boolean);
73
+ this.el.classList.add(...this._appliedClasses);
74
+ const size = cssLength(state.size, '36px');
75
+ const glyphSize = cssLength(state.iconSize, '52%');
76
+ this.el.style.setProperty('--zx-app-icon-size', size);
77
+ this.el.style.setProperty('--zx-app-icon-glyph-size', glyphSize);
78
+ if (isCssColor(state.color)) this.el.style.setProperty('--zx-app-icon-color', String(state.color));
79
+ else this.el.style.removeProperty('--zx-app-icon-color');
80
+ this.el.dataset.glass = normalizeGlass(state.glass);
81
+ this.el.toggleAttribute('data-selected', Boolean(state.selected));
82
+
83
+ const label = state.label == null ? '' : String(state.label).trim();
84
+ if (label) {
85
+ this.el.setAttribute('role', 'img');
86
+ this.el.setAttribute('aria-label', label);
87
+ this.el.removeAttribute('aria-hidden');
88
+ } else {
89
+ this.el.removeAttribute('role');
90
+ this.el.removeAttribute('aria-label');
91
+ this.el.setAttribute('aria-hidden', 'true');
92
+ }
93
+
94
+ const visual = resolveVisual(state.icon);
95
+ const badge = state.badge == null || state.badge === '' ? null : h('span', {
96
+ class: 'zx-app-icon__badge', ariaHidden: 'true'
97
+ }, String(state.badge));
98
+ const children = [
99
+ h('span', { class: 'zx-app-icon__shine', ariaHidden: 'true' }),
100
+ h('span', { class: 'zx-app-icon__glyph', ariaHidden: 'true' }, visual)
101
+ ];
102
+ // Native replaceChildren() stringifies nullish arguments. Only append a badge when it exists,
103
+ // otherwise the icon gets a visible "null" text node that can also disturb optical centring.
104
+ if (badge) children.push(badge);
105
+ this.el.replaceChildren(...children);
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Creates an AppIcon element without retaining a component lifecycle handle.
111
+ * @param {AppIconOptions} [options={}] Icon options.
112
+ * @returns {HTMLElement}
113
+ */
114
+ export function appIcon(options = {}) {
115
+ return new AppIcon(null, options).toElement();
116
+ }
117
+
118
+ /** @param {AppIconOptions['icon']} value @returns {Node|string|null} */
119
+ function resolveVisual(value) {
120
+ const resolved = typeof value === 'function' ? value() : value;
121
+ if (typeof resolved === 'string') return renderIcon(resolved, { size: '1em' });
122
+ return resolved && typeof resolved === 'object' && typeof resolved.nodeType === 'number'
123
+ ? /** @type {Node} */ (resolved) : null;
124
+ }
125
+
126
+ /** @param {unknown} value @param {string} fallback @returns {string} */
127
+ function cssLength(value, fallback) {
128
+ if (typeof value === 'number' && Number.isFinite(value)) return `${Math.max(1, value)}px`;
129
+ const text = String(value ?? '').trim();
130
+ return text && !/[;{}]/.test(text) ? text : fallback;
131
+ }
132
+
133
+ /** @param {unknown} value @returns {'none'|'subtle'|'strong'} */
134
+ function normalizeGlass(value) {
135
+ if (value === false || value === 'none') return 'none';
136
+ return value === 'strong' ? 'strong' : 'subtle';
137
+ }
@@ -6,7 +6,7 @@
6
6
  min-block-size: var(--zx-control-height);
7
7
  border: 1px solid var(--zx-color-border-control);
8
8
  border-radius: var(--zx-button-radius, var(--zx-control-radius));
9
- background: var(--zx-color-bg-control);
9
+ background: var(--zx-color-glass-control);
10
10
  color: var(--zx-color-text);
11
11
  padding-inline: var(--zx-control-pad-x);
12
12
  font: inherit;
@@ -14,6 +14,9 @@
14
14
  font-weight: var(--zx-control-font-weight);
15
15
  line-height: 1;
16
16
  cursor: pointer;
17
+ -webkit-backdrop-filter: blur(var(--zx-glass-blur)) saturate(var(--zx-glass-saturation));
18
+ backdrop-filter: blur(var(--zx-glass-blur)) saturate(var(--zx-glass-saturation));
19
+ box-shadow: var(--zx-glass-control-shadow);
17
20
  transition: color var(--zx-dur-fast) var(--zx-ease),
18
21
  background-color var(--zx-dur-fast) var(--zx-ease),
19
22
  border-color var(--zx-dur-fast) var(--zx-ease),
@@ -21,7 +24,7 @@
21
24
  }
22
25
 
23
26
  .zx-btn:hover:not(:disabled) {
24
- background: var(--zx-color-bg-hover);
27
+ background: var(--zx-color-glass-control-hover);
25
28
  }
26
29
 
27
30
  .zx-btn:active:not(:disabled) {
@@ -66,6 +69,9 @@
66
69
  .zx-btn[data-kind="ghost"] {
67
70
  border-color: transparent;
68
71
  background: none;
72
+ -webkit-backdrop-filter: none;
73
+ backdrop-filter: none;
74
+ box-shadow: none;
69
75
  }
70
76
 
71
77
  .zx-btn[data-kind="ghost"]:hover:not(:disabled) {
@@ -101,3 +107,29 @@
101
107
  .zx-btn-group > .zx-btn:focus-visible {
102
108
  z-index: 1;
103
109
  }
110
+
111
+ @media (prefers-reduced-transparency: reduce) {
112
+ .zx-btn {
113
+ background: var(--zx-color-bg-control);
114
+ -webkit-backdrop-filter: none;
115
+ backdrop-filter: none;
116
+ box-shadow: none;
117
+ }
118
+ }
119
+
120
+ @media (forced-colors: active) {
121
+ .zx-btn {
122
+ border-color: ButtonText;
123
+ background: ButtonFace;
124
+ color: ButtonText;
125
+ box-shadow: none;
126
+ -webkit-backdrop-filter: none;
127
+ backdrop-filter: none;
128
+ }
129
+
130
+ .zx-btn[data-kind="primary"] {
131
+ border-color: Highlight;
132
+ background: Highlight;
133
+ color: HighlightText;
134
+ }
135
+ }
@@ -14,9 +14,11 @@
14
14
  }
15
15
 
16
16
  .zx-card[data-variant="raised"] {
17
- border: 1px solid transparent;
18
- background: var(--zx-color-bg-surface);
19
- box-shadow: var(--zx-overlay-shadow);
17
+ border: 1px solid var(--zx-color-glass-border);
18
+ background: var(--zx-color-glass-surface);
19
+ -webkit-backdrop-filter: blur(var(--zx-glass-blur)) saturate(var(--zx-glass-saturation));
20
+ backdrop-filter: blur(var(--zx-glass-blur)) saturate(var(--zx-glass-saturation));
21
+ box-shadow: var(--zx-glass-raised-shadow);
20
22
  }
21
23
 
22
24
  .zx-card[data-variant="filled"] {
@@ -24,6 +26,26 @@
24
26
  background: var(--zx-color-bg-muted);
25
27
  }
26
28
 
29
+ @media (prefers-reduced-transparency: reduce) {
30
+ .zx-card[data-variant="raised"] {
31
+ border-color: var(--zx-color-border);
32
+ background: var(--zx-color-bg-surface);
33
+ -webkit-backdrop-filter: none;
34
+ backdrop-filter: none;
35
+ box-shadow: var(--zx-overlay-shadow);
36
+ }
37
+ }
38
+
39
+ @media (forced-colors: active) {
40
+ .zx-card[data-variant="raised"] {
41
+ border-color: ButtonText;
42
+ background: Canvas;
43
+ box-shadow: none;
44
+ -webkit-backdrop-filter: none;
45
+ backdrop-filter: none;
46
+ }
47
+ }
48
+
27
49
  .zx-card[data-linked="true"]:hover {
28
50
  background: var(--zx-color-bg-hover);
29
51
  }