@vectoriox/iox-ui 5.31.2 → 5.32.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.
- package/actions-core/index.d.ts +52 -2
- package/design-core/index.d.ts +650 -6
- package/fesm2022/vectoriox-iox-ui-actions-core.mjs +69 -1
- package/fesm2022/vectoriox-iox-ui-actions-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui-design-core.mjs +791 -20
- package/fesm2022/vectoriox-iox-ui-design-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui-generation-core.mjs +568 -22
- package/fesm2022/vectoriox-iox-ui-generation-core.mjs.map +1 -1
- package/fesm2022/vectoriox-iox-ui.mjs +365 -24
- package/fesm2022/vectoriox-iox-ui.mjs.map +1 -1
- package/generation-core/index.d.ts +126 -6
- package/index.d.ts +245 -2
- package/package.json +1 -1
- package/src/actions-core/dialog-opener.ts +89 -0
- package/src/actions-core/public-api.ts +1 -0
- package/src/design-core/contrast-audit.ts +11 -2
- package/src/design-core/element-styles.ts +6 -3
- package/src/design-core/flavor-spec.generated.ts +737 -11
- package/src/design-core/node-classes.ts +39 -4
- package/src/generation-core/compose-site.ts +9 -1
- package/src/generation-core/nav-shape.ts +555 -0
- package/src/generation-core/node-baselines.generated.ts +23 -8
- package/src/generation-core/patterns.ts +9 -3
- package/src/generation-core/public-api.ts +1 -0
- package/src/generation-core/serialized-node.ts +2 -1
- package/src/generation-core/site-spec.generated.ts +8 -2
- package/styles/iox-design.css +94 -0
|
@@ -200,7 +200,8 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
|
|
|
200
200
|
"href": "",
|
|
201
201
|
"pageRoute": "",
|
|
202
202
|
"target": "_self",
|
|
203
|
-
"type": "button"
|
|
203
|
+
"type": "button",
|
|
204
|
+
"ariaLabel": ""
|
|
204
205
|
},
|
|
205
206
|
"styleProps": {
|
|
206
207
|
"position": "static",
|
|
@@ -292,7 +293,9 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
|
|
|
292
293
|
"href",
|
|
293
294
|
"pageRoute",
|
|
294
295
|
"target",
|
|
295
|
-
"type"
|
|
296
|
+
"type",
|
|
297
|
+
"__accessibilityLabel",
|
|
298
|
+
"ariaLabel"
|
|
296
299
|
],
|
|
297
300
|
"styleTraits": [
|
|
298
301
|
"position",
|
|
@@ -1337,7 +1340,8 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
|
|
|
1337
1340
|
"linkType": "external",
|
|
1338
1341
|
"url": "https://",
|
|
1339
1342
|
"pageRoute": "",
|
|
1340
|
-
"target": "_self"
|
|
1343
|
+
"target": "_self",
|
|
1344
|
+
"ariaLabel": ""
|
|
1341
1345
|
},
|
|
1342
1346
|
"styleProps": {
|
|
1343
1347
|
"position": "static",
|
|
@@ -1428,7 +1432,9 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
|
|
|
1428
1432
|
"linkType",
|
|
1429
1433
|
"url",
|
|
1430
1434
|
"pageRoute",
|
|
1431
|
-
"target"
|
|
1435
|
+
"target",
|
|
1436
|
+
"__accessibilityLabel",
|
|
1437
|
+
"ariaLabel"
|
|
1432
1438
|
],
|
|
1433
1439
|
"styleTraits": [
|
|
1434
1440
|
"position",
|
|
@@ -1529,7 +1535,8 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
|
|
|
1529
1535
|
"url": "https://",
|
|
1530
1536
|
"pageRoute": "",
|
|
1531
1537
|
"target": "_self",
|
|
1532
|
-
"colorScheme": "default"
|
|
1538
|
+
"colorScheme": "default",
|
|
1539
|
+
"ariaLabel": ""
|
|
1533
1540
|
},
|
|
1534
1541
|
"styleProps": {
|
|
1535
1542
|
"position": "static",
|
|
@@ -1620,7 +1627,9 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
|
|
|
1620
1627
|
"url",
|
|
1621
1628
|
"pageRoute",
|
|
1622
1629
|
"target",
|
|
1623
|
-
"colorScheme"
|
|
1630
|
+
"colorScheme",
|
|
1631
|
+
"__accessibilityLabel",
|
|
1632
|
+
"ariaLabel"
|
|
1624
1633
|
],
|
|
1625
1634
|
"styleTraits": [
|
|
1626
1635
|
"position",
|
|
@@ -4669,7 +4678,9 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
|
|
|
4669
4678
|
"placement": "center",
|
|
4670
4679
|
"closeOnBackdrop": true,
|
|
4671
4680
|
"showCloseButton": true,
|
|
4672
|
-
"consentRole": false
|
|
4681
|
+
"consentRole": false,
|
|
4682
|
+
"closeAbove": "",
|
|
4683
|
+
"ariaLabel": ""
|
|
4673
4684
|
},
|
|
4674
4685
|
"styleProps": {
|
|
4675
4686
|
"position": "static",
|
|
@@ -4759,7 +4770,11 @@ export const NODE_BASELINES: Readonly<Record<string, NodeBaseline>> = {
|
|
|
4759
4770
|
"placement",
|
|
4760
4771
|
"closeOnBackdrop",
|
|
4761
4772
|
"showCloseButton",
|
|
4762
|
-
"consentRole"
|
|
4773
|
+
"consentRole",
|
|
4774
|
+
"__responsiveLabel",
|
|
4775
|
+
"closeAbove",
|
|
4776
|
+
"__accessibilityLabel",
|
|
4777
|
+
"ariaLabel"
|
|
4763
4778
|
],
|
|
4764
4779
|
"styleTraits": [
|
|
4765
4780
|
"position",
|
|
@@ -16,9 +16,11 @@ import { SPEC_SITE_PATTERNS } from './site-spec.generated';
|
|
|
16
16
|
* per page in the navigation (`$navPages`) · `href` a page route (`$page.route` = the stamped page's) ·
|
|
17
17
|
* `param` the value of the route's `:param` · `optional` a part a page may leave out (`without`) ·
|
|
18
18
|
* `preset` a forms-core form preset (its fields, submit and Success/Error words) · `control` / `label` /
|
|
19
|
-
* `required` a Field's · `
|
|
20
|
-
* its `layout`, the optional `parts` it leaves out, and
|
|
21
|
-
* `eyebrow`, `title`, `text`; `href` + `param` link the whole card
|
|
19
|
+
* `required` a Field's · `role` the node's part in its composite (`nav.brand`…) · `c` children · `recipe` a
|
|
20
|
+
* composite-core recipe stamped whole (`card`: Card v2 — its `layout`, the optional `parts` it leaves out, and
|
|
21
|
+
* `binds`, a binding path per part: `image`, `eyebrow`, `title`, `text`; `href` + `param` link the whole card
|
|
22
|
+
* through its title). A header pattern's `phoneMenu` names its phone shape (drawer · bar · both), stamped by
|
|
23
|
+
* `planNavShape` (CC1).
|
|
22
24
|
*/
|
|
23
25
|
export interface PatternNode {
|
|
24
26
|
readonly t: string;
|
|
@@ -44,6 +46,8 @@ export interface PatternNode {
|
|
|
44
46
|
readonly control?: string;
|
|
45
47
|
readonly label?: string;
|
|
46
48
|
readonly required?: boolean;
|
|
49
|
+
/** The node's part in its composite (`nav.brand`, `nav.links`…), written to the node's `role`. */
|
|
50
|
+
readonly role?: string;
|
|
47
51
|
readonly c?: ReadonlyArray<PatternNode>;
|
|
48
52
|
readonly recipe?: string;
|
|
49
53
|
readonly layout?: string;
|
|
@@ -79,6 +83,8 @@ export interface SitePattern {
|
|
|
79
83
|
readonly label: string;
|
|
80
84
|
/** Set on a header/footer pattern: it renders on every page, before or after the page. */
|
|
81
85
|
readonly global?: `${PatternGlobalPosition}`;
|
|
86
|
+
/** A navigation pattern's phone shape (CC1): `planNavShape` adds it after the pattern is stamped. */
|
|
87
|
+
readonly phoneMenu?: 'drawer' | 'bar' | 'both';
|
|
82
88
|
readonly node: PatternNode;
|
|
83
89
|
}
|
|
84
90
|
|
|
@@ -27,7 +27,8 @@ export interface SerializedNode {
|
|
|
27
27
|
stateStyles?: Partial<Record<string, Record<string, unknown>>>;
|
|
28
28
|
responsive?: SerializedResponsive;
|
|
29
29
|
elementStyle?: string;
|
|
30
|
-
/** The part this node plays in a composite (composite-core: `card.title`
|
|
30
|
+
/** The part this node plays in a composite (composite-core: `card.title`; a navigation's `nav.drawer`,
|
|
31
|
+
* written by `planNavShape`). Additive; a node without one is a plain node. */
|
|
31
32
|
role?: string;
|
|
32
33
|
bindings: SerializedBinding[];
|
|
33
34
|
interactions: unknown[];
|
|
@@ -109,10 +109,12 @@ export const SPEC_SITE_PATTERNS = {
|
|
|
109
109
|
"family": "nav",
|
|
110
110
|
"label": "Navigation bar",
|
|
111
111
|
"global": "before",
|
|
112
|
+
"phoneMenu": "drawer",
|
|
112
113
|
"node": {
|
|
113
114
|
"t": "Container",
|
|
114
115
|
"es": "nav",
|
|
115
116
|
"tag": "nav",
|
|
117
|
+
"role": "nav",
|
|
116
118
|
"lay": {
|
|
117
119
|
"display": "flex",
|
|
118
120
|
"flexWrap": "wrap",
|
|
@@ -125,6 +127,7 @@ export const SPEC_SITE_PATTERNS = {
|
|
|
125
127
|
"t": "LinkedContainer",
|
|
126
128
|
"es": "box",
|
|
127
129
|
"href": "/",
|
|
130
|
+
"role": "nav.brand",
|
|
128
131
|
"c": [
|
|
129
132
|
{
|
|
130
133
|
"t": "Heading",
|
|
@@ -138,6 +141,7 @@ export const SPEC_SITE_PATTERNS = {
|
|
|
138
141
|
{
|
|
139
142
|
"t": "Container",
|
|
140
143
|
"es": "box",
|
|
144
|
+
"role": "nav.links",
|
|
141
145
|
"lay": {
|
|
142
146
|
"display": "flex",
|
|
143
147
|
"flexWrap": "wrap",
|
|
@@ -151,14 +155,16 @@ export const SPEC_SITE_PATTERNS = {
|
|
|
151
155
|
"es": "link",
|
|
152
156
|
"each": "$navPages",
|
|
153
157
|
"bind": "page.name",
|
|
154
|
-
"href": "$page.route"
|
|
158
|
+
"href": "$page.route",
|
|
159
|
+
"role": "nav.link"
|
|
155
160
|
},
|
|
156
161
|
{
|
|
157
162
|
"t": "ButtonBlock",
|
|
158
163
|
"es": "button-primary",
|
|
159
164
|
"href": "/contact",
|
|
160
165
|
"slot": "cta",
|
|
161
|
-
"text": "Get in touch"
|
|
166
|
+
"text": "Get in touch",
|
|
167
|
+
"role": "nav.cta"
|
|
162
168
|
}
|
|
163
169
|
]
|
|
164
170
|
}
|
package/styles/iox-design.css
CHANGED
|
@@ -113,3 +113,97 @@ iox-gallery.iox-es-media[data-iox-empty] > .iox-gallery {
|
|
|
113
113
|
.iox-role-card:has(.iox-card-link:focus-visible) {
|
|
114
114
|
box-shadow: var(--iox-focus-ring);
|
|
115
115
|
}
|
|
116
|
+
|
|
117
|
+
/* ── CC1: mobile navigation (complex-components plan §7a, R1; boards 1–2) ─────────────────────── */
|
|
118
|
+
|
|
119
|
+
/* The DRAWER (board 1, board-8 Q9): a Dialog wearing an element style, placed at the start or end edge, is
|
|
120
|
+
a full-height sheet on that edge — 100dvh (never 100vh: Safari's toolbar), no gap to the edge, the
|
|
121
|
+
radius kept on its LEADING edge only (a placement rule, not a token). It slides in over dur-base, which
|
|
122
|
+
the reduced-motion token block sets to 0ms: under reduced motion it simply appears. Beats the engine's
|
|
123
|
+
placement margins (0,2,1) and the node rule; a Dialog saved before element styles keeps its look. */
|
|
124
|
+
dialog[data-iox-dialog].iox-es:is([data-iox-dialog-placement='end'], [data-iox-dialog-placement='start']) {
|
|
125
|
+
margin-block: 0;
|
|
126
|
+
height: 100dvh;
|
|
127
|
+
max-height: 100dvh;
|
|
128
|
+
transition: translate var(--iox-dur-base) var(--iox-ease-standard), overlay var(--iox-dur-base) allow-discrete, display var(--iox-dur-base) allow-discrete;
|
|
129
|
+
}
|
|
130
|
+
dialog[data-iox-dialog].iox-es[data-iox-dialog-placement='end'] {
|
|
131
|
+
margin-inline: auto 0;
|
|
132
|
+
border-start-end-radius: 0;
|
|
133
|
+
border-end-end-radius: 0;
|
|
134
|
+
}
|
|
135
|
+
dialog[data-iox-dialog].iox-es[data-iox-dialog-placement='start'] {
|
|
136
|
+
margin-inline: 0 auto;
|
|
137
|
+
border-start-start-radius: 0;
|
|
138
|
+
border-end-start-radius: 0;
|
|
139
|
+
}
|
|
140
|
+
@starting-style {
|
|
141
|
+
dialog[data-iox-dialog].iox-es[data-iox-dialog-placement='end'][open] { translate: 100% 0; }
|
|
142
|
+
dialog[data-iox-dialog].iox-es[data-iox-dialog-placement='start'][open] { translate: -100% 0; }
|
|
143
|
+
[dir='rtl'] dialog[data-iox-dialog].iox-es[data-iox-dialog-placement='end'][open] { translate: -100% 0; }
|
|
144
|
+
[dir='rtl'] dialog[data-iox-dialog].iox-es[data-iox-dialog-placement='start'][open] { translate: 100% 0; }
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* A navigation's link lists (board 1): no underline at rest — the current page's link is the one
|
|
148
|
+
underlined (the link's `current` state, link-current-decoration). Keyed on the list's ROLE, so a
|
|
149
|
+
composed or adopted header and its drawer read it and no Link holds a literal over its element style. */
|
|
150
|
+
.iox-role-nav-links,
|
|
151
|
+
.iox-role-nav-drawer-links {
|
|
152
|
+
--iox-link-decoration: none;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/* The BOTTOM BAR (board 2, "The CSS, exactly"): fixed to the bottom of the viewport (on the canvas, of the
|
|
156
|
+
global overlay — a transform contains fixed children). Attached flavors pad it above the home
|
|
157
|
+
indicator / gesture bar with env(safe-area-inset-bottom) (needs viewport-fit=cover); a FLOATING flavor
|
|
158
|
+
(tabbar-inset > 0: Glass) lifts the whole bar by the inset plus the safe area instead. The
|
|
159
|
+
`min(env(), inset × 1000)` term is the safe area when the bar floats and 0 when it is attached — no flag
|
|
160
|
+
token. Nothing reads 100vh or window.innerHeight. */
|
|
161
|
+
.iox-es.iox-es-bottom-bar {
|
|
162
|
+
position: fixed;
|
|
163
|
+
z-index: 40;
|
|
164
|
+
inset-inline: var(--iox-tabbar-inset, 0px);
|
|
165
|
+
bottom: calc(var(--iox-tabbar-inset, 0px) + min(env(safe-area-inset-bottom, 0px), var(--iox-tabbar-inset, 0px) * 1000));
|
|
166
|
+
padding-bottom: calc(6px + env(safe-area-inset-bottom, 0px) - min(env(safe-area-inset-bottom, 0px), var(--iox-tabbar-inset, 0px) * 1000));
|
|
167
|
+
box-sizing: border-box;
|
|
168
|
+
}
|
|
169
|
+
/* An item's icon reads the item's ink and size; its label is centred and never wraps. Icon 24 + gap 3 + label 15 + padding 8 fits the 52px item, so the bar stays inside --iox-tabbar-height (the page reserve). Descendant, not child,
|
|
170
|
+
selectors: on the builder canvas the element-style class sits on the item's host, one level above its parts. */
|
|
171
|
+
.iox-es-bottom-bar-item .iox-es-icon { font-size: 24px; line-height: 1; }
|
|
172
|
+
.iox-es-bottom-bar-item .iox-es-button-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; text-align: center; line-height: 1.25; }
|
|
173
|
+
|
|
174
|
+
/* The page RESERVES the bar's height where the bar shows (the class comes from design-core
|
|
175
|
+
`barReserveClass`), so its last content is never under it, and scroll-padding keeps a focused link
|
|
176
|
+
clear of it (WCAG 2.4.11): the bar's height, plus its edge, the safe area and a floating bar's two
|
|
177
|
+
insets. The live page scrolls the document; the admin's own body never takes it
|
|
178
|
+
(the canvas reserves inside its own frame). */
|
|
179
|
+
html:has(.iox-bar-reserve) { scroll-padding-bottom: calc(var(--iox-tabbar-height, 64px) + var(--iox-border-width, 1px) + env(safe-area-inset-bottom, 0px) + var(--iox-tabbar-inset, 0px) * 2); }
|
|
180
|
+
body:not(.builder-active):has(.iox-bar-reserve) { padding-bottom: calc(var(--iox-tabbar-height, 64px) + var(--iox-border-width, 1px) + env(safe-area-inset-bottom, 0px) + var(--iox-tabbar-inset, 0px) * 2); }
|
|
181
|
+
@media (max-width: 1200px) {
|
|
182
|
+
html:has(.iox-bar-reserve-laptop) { scroll-padding-bottom: calc(var(--iox-tabbar-height, 64px) + var(--iox-border-width, 1px) + env(safe-area-inset-bottom, 0px) + var(--iox-tabbar-inset, 0px) * 2); }
|
|
183
|
+
body:not(.builder-active):has(.iox-bar-reserve-laptop) { padding-bottom: calc(var(--iox-tabbar-height, 64px) + var(--iox-border-width, 1px) + env(safe-area-inset-bottom, 0px) + var(--iox-tabbar-inset, 0px) * 2); }
|
|
184
|
+
}
|
|
185
|
+
@media (max-width: 991px) {
|
|
186
|
+
html:has(.iox-bar-reserve-tablet) { scroll-padding-bottom: calc(var(--iox-tabbar-height, 64px) + var(--iox-border-width, 1px) + env(safe-area-inset-bottom, 0px) + var(--iox-tabbar-inset, 0px) * 2); }
|
|
187
|
+
body:not(.builder-active):has(.iox-bar-reserve-tablet) { padding-bottom: calc(var(--iox-tabbar-height, 64px) + var(--iox-border-width, 1px) + env(safe-area-inset-bottom, 0px) + var(--iox-tabbar-inset, 0px) * 2); }
|
|
188
|
+
}
|
|
189
|
+
@media (max-width: 767px) {
|
|
190
|
+
html:has(.iox-bar-reserve-mobile) { scroll-padding-bottom: calc(var(--iox-tabbar-height, 64px) + var(--iox-border-width, 1px) + env(safe-area-inset-bottom, 0px) + var(--iox-tabbar-inset, 0px) * 2); }
|
|
191
|
+
body:not(.builder-active):has(.iox-bar-reserve-mobile) { padding-bottom: calc(var(--iox-tabbar-height, 64px) + var(--iox-border-width, 1px) + env(safe-area-inset-bottom, 0px) + var(--iox-tabbar-inset, 0px) * 2); }
|
|
192
|
+
}
|
|
193
|
+
@media (max-width: 479px) {
|
|
194
|
+
html:has(.iox-bar-reserve-small) { scroll-padding-bottom: calc(var(--iox-tabbar-height, 64px) + var(--iox-border-width, 1px) + env(safe-area-inset-bottom, 0px) + var(--iox-tabbar-inset, 0px) * 2); }
|
|
195
|
+
body:not(.builder-active):has(.iox-bar-reserve-small) { padding-bottom: calc(var(--iox-tabbar-height, 64px) + var(--iox-border-width, 1px) + env(safe-area-inset-bottom, 0px) + var(--iox-tabbar-inset, 0px) * 2); }
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/* A closed live dialog never shows, whatever display its node sets (a drawer lays its parts out as a
|
|
199
|
+
flex column): an author rule would otherwise beat the UA's `dialog:not([open]) { display: none }`. */
|
|
200
|
+
dialog[data-iox-dialog]:not([open]) {
|
|
201
|
+
display: none;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/* A bottom-bar item reads the ITEM's ink (the contract's inkScopes): its icon and label change together. */
|
|
205
|
+
.iox-es-bottom-bar-item {
|
|
206
|
+
--iox-icon-color: currentColor;
|
|
207
|
+
--iox-heading-color: currentColor;
|
|
208
|
+
--iox-text-muted: currentColor;
|
|
209
|
+
}
|