@adia-ai/web-components 0.6.36 → 0.6.37
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/CHANGELOG.md +28 -1
- package/components/badge/badge.a2ui.json +10 -0
- package/components/badge/badge.css +70 -0
- package/components/badge/badge.yaml +20 -0
- package/components/blockquote/blockquote.a2ui.json +121 -0
- package/components/blockquote/blockquote.class.js +68 -0
- package/components/blockquote/blockquote.css +46 -0
- package/components/blockquote/blockquote.d.ts +31 -0
- package/components/blockquote/blockquote.js +17 -0
- package/components/blockquote/blockquote.yaml +124 -0
- package/components/button/button.css +11 -3
- package/components/calendar-picker/calendar-picker.a2ui.json +15 -0
- package/components/calendar-picker/calendar-picker.class.js +7 -1
- package/components/calendar-picker/calendar-picker.yaml +14 -0
- package/components/color-input/color-input.a2ui.json +2 -2
- package/components/color-input/color-input.class.js +9 -2
- package/components/color-input/color-input.yaml +2 -2
- package/components/combobox/combobox.class.js +4 -0
- package/components/context-menu/context-menu.a2ui.json +159 -0
- package/components/context-menu/context-menu.class.js +275 -0
- package/components/context-menu/context-menu.css +56 -0
- package/components/context-menu/context-menu.d.ts +70 -0
- package/components/context-menu/context-menu.js +17 -0
- package/components/context-menu/context-menu.yaml +136 -0
- package/components/date-range-picker/date-range-picker.a2ui.json +15 -0
- package/components/date-range-picker/date-range-picker.class.js +2 -0
- package/components/date-range-picker/date-range-picker.yaml +14 -0
- package/components/datetime-picker/datetime-picker.a2ui.json +15 -0
- package/components/datetime-picker/datetime-picker.class.js +3 -1
- package/components/datetime-picker/datetime-picker.d.ts +2 -0
- package/components/datetime-picker/datetime-picker.yaml +14 -0
- package/components/empty-state/empty-state.class.js +2 -0
- package/components/feed/feed.class.js +13 -5
- package/components/feed/feed.css +14 -0
- package/components/index.js +9 -0
- package/components/integration-card/integration-card.class.js +9 -0
- package/components/integration-card/integration-card.test.js +4 -3
- package/components/nav-group/nav-group.css +7 -1
- package/components/number-format/number-format.a2ui.json +180 -0
- package/components/number-format/number-format.class.js +96 -0
- package/components/number-format/number-format.css +18 -0
- package/components/number-format/number-format.d.ts +68 -0
- package/components/number-format/number-format.js +17 -0
- package/components/number-format/number-format.yaml +204 -0
- package/components/pagination/pagination.a2ui.json +19 -2
- package/components/pagination/pagination.class.js +90 -37
- package/components/pagination/pagination.css +32 -127
- package/components/pagination/pagination.d.ts +8 -2
- package/components/pagination/pagination.test.js +195 -0
- package/components/pagination/pagination.yaml +22 -1
- package/components/password-strength/password-strength.a2ui.json +152 -0
- package/components/password-strength/password-strength.class.js +157 -0
- package/components/password-strength/password-strength.css +80 -0
- package/components/password-strength/password-strength.d.ts +59 -0
- package/components/password-strength/password-strength.js +17 -0
- package/components/password-strength/password-strength.yaml +153 -0
- package/components/popover/popover.css +43 -23
- package/components/popover/popover.yaml +8 -4
- package/components/qr-code/QR-TEST.svg +4 -0
- package/components/qr-code/qr-code.a2ui.json +154 -0
- package/components/qr-code/qr-code.class.js +129 -0
- package/components/qr-code/qr-code.css +41 -0
- package/components/qr-code/qr-code.d.ts +83 -0
- package/components/qr-code/qr-code.js +17 -0
- package/components/qr-code/qr-code.yaml +203 -0
- package/components/qr-code/qr-encoder.js +633 -0
- package/components/relative-time/relative-time.a2ui.json +120 -0
- package/components/relative-time/relative-time.class.js +136 -0
- package/components/relative-time/relative-time.css +22 -0
- package/components/relative-time/relative-time.d.ts +51 -0
- package/components/relative-time/relative-time.js +17 -0
- package/components/relative-time/relative-time.yaml +133 -0
- package/components/segmented/segmented.class.js +5 -1
- package/components/select/select.class.js +4 -0
- package/components/skip-nav/skip-nav.a2ui.json +92 -0
- package/components/skip-nav/skip-nav.class.js +45 -0
- package/components/skip-nav/skip-nav.css +54 -0
- package/components/skip-nav/skip-nav.d.ts +27 -0
- package/components/skip-nav/skip-nav.js +12 -0
- package/components/skip-nav/skip-nav.yaml +68 -0
- package/components/slider/slider.a2ui.json +16 -1
- package/components/slider/slider.class.js +264 -122
- package/components/slider/slider.css +82 -2
- package/components/slider/slider.d.ts +19 -3
- package/components/slider/slider.test.js +55 -0
- package/components/slider/slider.yaml +28 -6
- package/components/table/table.class.js +29 -6
- package/components/table/table.css +31 -4
- package/components/table-toolbar/table-toolbar.class.js +3 -1
- package/components/tag/tag.a2ui.json +3 -2
- package/components/tag/tag.css +35 -11
- package/components/tag/tag.d.ts +14 -0
- package/components/tag/tag.test.js +35 -11
- package/components/tag/tag.yaml +13 -7
- package/components/toast/toast.class.js +12 -4
- package/components/toc/toc.a2ui.json +159 -0
- package/components/toc/toc.class.js +222 -0
- package/components/toc/toc.css +92 -0
- package/components/toc/toc.d.ts +61 -0
- package/components/toc/toc.js +17 -0
- package/components/toc/toc.yaml +180 -0
- package/components/toolbar/toolbar.class.js +3 -0
- package/components/visually-hidden/visually-hidden.a2ui.json +71 -0
- package/components/visually-hidden/visually-hidden.class.js +14 -0
- package/components/visually-hidden/visually-hidden.css +25 -0
- package/components/visually-hidden/visually-hidden.d.ts +26 -0
- package/components/visually-hidden/visually-hidden.js +12 -0
- package/components/visually-hidden/visually-hidden.yaml +54 -0
- package/core/anchor.js +19 -3
- package/dist/web-components.min.css +1 -1
- package/dist/web-components.min.js +100 -89
- package/package.json +1 -1
- package/styles/colors/semantics.css +11 -2
- package/styles/components.css +9 -0
- package/styles/resets.css +10 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
$schema: ../../../../scripts/schemas/component.yaml.schema.json
|
|
2
|
+
name: UITableOfContents
|
|
3
|
+
tag: toc-ui
|
|
4
|
+
status: stable
|
|
5
|
+
component: TableOfContents
|
|
6
|
+
category: navigation
|
|
7
|
+
version: 1
|
|
8
|
+
description: |
|
|
9
|
+
Auto-generated in-page table of contents. Scans a target container
|
|
10
|
+
for headings (default `h2,h3`), ensures each has an `id` (slugifies
|
|
11
|
+
the text content if missing), and stamps a `<nav>` list of anchor
|
|
12
|
+
links. An `IntersectionObserver` tracks the active heading and
|
|
13
|
+
applies `[data-active]` to the matching link so consumers can style
|
|
14
|
+
the currently-visible section. Smooth-scroll on click is handled by
|
|
15
|
+
the global `scroll-behavior: smooth` set in resets.css.
|
|
16
|
+
|
|
17
|
+
Pair with a sticky container (`position: sticky; top: <offset>;`) in
|
|
18
|
+
an aside / right rail for the classic docs-site outline pattern.
|
|
19
|
+
props:
|
|
20
|
+
target:
|
|
21
|
+
description: |
|
|
22
|
+
CSS selector pointing to the container to scan. Empty (default)
|
|
23
|
+
scans the toc-ui's parent element. Set to `#article` /
|
|
24
|
+
`[data-toc-target]` / `main` for a specific scope.
|
|
25
|
+
type: string
|
|
26
|
+
default: ""
|
|
27
|
+
reflect: true
|
|
28
|
+
headings:
|
|
29
|
+
description: |
|
|
30
|
+
CSS selector listing the heading tags to include (e.g. `h2,h3`).
|
|
31
|
+
The selector is matched against the target container's
|
|
32
|
+
descendants. Default `h2,h3` produces the common two-level
|
|
33
|
+
outline.
|
|
34
|
+
type: string
|
|
35
|
+
default: "h2,h3"
|
|
36
|
+
reflect: true
|
|
37
|
+
offset:
|
|
38
|
+
description: |
|
|
39
|
+
Top offset in pixels for active-state detection. The
|
|
40
|
+
IntersectionObserver's `rootMargin` is set to
|
|
41
|
+
`-<offset>px 0px -50% 0px` so the active item becomes the
|
|
42
|
+
heading nearest the top of the viewport BELOW any sticky header
|
|
43
|
+
chrome. Tune to match the height of your sticky topbar (default
|
|
44
|
+
`80` is a reasonable docs-shell value).
|
|
45
|
+
type: number
|
|
46
|
+
default: 80
|
|
47
|
+
reflect: true
|
|
48
|
+
label:
|
|
49
|
+
description: |
|
|
50
|
+
`aria-label` for the nav. Defaults to `Table of contents`.
|
|
51
|
+
type: string
|
|
52
|
+
default: "Table of contents"
|
|
53
|
+
reflect: true
|
|
54
|
+
events:
|
|
55
|
+
section-change:
|
|
56
|
+
description: Fired when the active heading changes (the user scrolls into a new section). Detail carries the new active id.
|
|
57
|
+
detail:
|
|
58
|
+
activeId: string
|
|
59
|
+
slots:
|
|
60
|
+
default:
|
|
61
|
+
description: Stamped automatically — a `<nav>` containing a flat `<ul>` of `<a href="#id">` links. Override by authoring your own content; auto-stamp is skipped when a `<nav>` is already present.
|
|
62
|
+
states:
|
|
63
|
+
- name: idle
|
|
64
|
+
description: Default — no active section yet.
|
|
65
|
+
- name: active
|
|
66
|
+
description: A section is in view; the matching `<a>` carries `[data-active]`.
|
|
67
|
+
attribute: data-active
|
|
68
|
+
tokens:
|
|
69
|
+
--toc-fg:
|
|
70
|
+
description: Default link color.
|
|
71
|
+
default: var(--a-fg-muted)
|
|
72
|
+
--toc-fg-active:
|
|
73
|
+
description: Active-link color.
|
|
74
|
+
default: var(--a-fg)
|
|
75
|
+
--toc-fg-hover:
|
|
76
|
+
description: Hover color.
|
|
77
|
+
default: var(--a-fg)
|
|
78
|
+
--toc-indent:
|
|
79
|
+
description: Per-depth-level indent.
|
|
80
|
+
default: var(--a-space-3)
|
|
81
|
+
--toc-gap:
|
|
82
|
+
description: Vertical gap between links.
|
|
83
|
+
default: var(--a-space-1)
|
|
84
|
+
--toc-rule-active:
|
|
85
|
+
description: Active-item indicator rule color (left border).
|
|
86
|
+
default: var(--a-accent-bg)
|
|
87
|
+
--toc-padding-block:
|
|
88
|
+
description: Per-link block padding (top/bottom).
|
|
89
|
+
default: var(--a-space-1)
|
|
90
|
+
--toc-padding-inline:
|
|
91
|
+
description: Per-link inline padding.
|
|
92
|
+
default: var(--a-space-2)
|
|
93
|
+
--toc-size:
|
|
94
|
+
description: Link font-size.
|
|
95
|
+
default: var(--a-ui-sm)
|
|
96
|
+
requiredIcons: []
|
|
97
|
+
a2ui:
|
|
98
|
+
rules:
|
|
99
|
+
- rule: "Use for in-page section navigation. Pair with a sticky container in an aside / right rail for the docs-site outline pattern."
|
|
100
|
+
reason: "Single-use-case primitive."
|
|
101
|
+
- rule: "Default scans the toc-ui's parent for h2/h3 headings. Set [target] to a CSS selector for a specific container; set [headings] to a comma-separated tag list (e.g. h1,h2,h3) for different depth coverage."
|
|
102
|
+
reason: "Configuration contract."
|
|
103
|
+
- rule: "Headings missing an [id] receive an auto-generated slug from their text content. Existing ids are preserved."
|
|
104
|
+
reason: "Id-assignment behavior."
|
|
105
|
+
- rule: "Smooth-scroll on click is handled by the global `scroll-behavior: smooth` in resets.css (gated by prefers-reduced-motion). Do NOT add per-toc-ui smooth-scroll JS — the global wins."
|
|
106
|
+
reason: "No duplicate scroll wiring."
|
|
107
|
+
anti_patterns:
|
|
108
|
+
- wrong: |
|
|
109
|
+
<toc-ui></toc-ui>
|
|
110
|
+
<h2>...</h2><h3>...</h3>
|
|
111
|
+
why: |
|
|
112
|
+
toc-ui's default scans its PARENT for headings. If toc-ui is a
|
|
113
|
+
sibling of the content (not inside it), the scan finds the
|
|
114
|
+
headings inside the toc-ui's parent which usually means the
|
|
115
|
+
headings + the toc itself — works only by coincidence. Set
|
|
116
|
+
[target] explicitly for clarity.
|
|
117
|
+
fix: |
|
|
118
|
+
<toc-ui target="#article"></toc-ui>
|
|
119
|
+
<article id="article">
|
|
120
|
+
<h2>...</h2><h3>...</h3>
|
|
121
|
+
</article>
|
|
122
|
+
examples:
|
|
123
|
+
- name: default
|
|
124
|
+
description: TOC scanning the parent container.
|
|
125
|
+
a2ui: |
|
|
126
|
+
[
|
|
127
|
+
{
|
|
128
|
+
"id": "page",
|
|
129
|
+
"component": "Section",
|
|
130
|
+
"children": ["toc", "h1", "h2-1", "p-1", "h2-2", "p-2"]
|
|
131
|
+
},
|
|
132
|
+
{"id": "toc", "component": "TableOfContents"},
|
|
133
|
+
{"id": "h1", "component": "Text", "variant": "title", "textContent": "Article title"},
|
|
134
|
+
{"id": "h2-1", "component": "Text", "variant": "heading", "textContent": "Introduction"},
|
|
135
|
+
{"id": "p-1", "component": "Text", "textContent": "..."},
|
|
136
|
+
{"id": "h2-2", "component": "Text", "variant": "heading", "textContent": "Conclusion"},
|
|
137
|
+
{"id": "p-2", "component": "Text", "textContent": "..."}
|
|
138
|
+
]
|
|
139
|
+
- name: targeted
|
|
140
|
+
description: TOC scanning a specific article container.
|
|
141
|
+
a2ui: |
|
|
142
|
+
[
|
|
143
|
+
{
|
|
144
|
+
"id": "shell",
|
|
145
|
+
"component": "Row",
|
|
146
|
+
"children": ["toc", "article"]
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"id": "toc",
|
|
150
|
+
"component": "TableOfContents",
|
|
151
|
+
"target": "#main-article",
|
|
152
|
+
"headings": "h2,h3,h4"
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"id": "article",
|
|
156
|
+
"component": "Section",
|
|
157
|
+
"attrs": { "id": "main-article" },
|
|
158
|
+
"children": []
|
|
159
|
+
}
|
|
160
|
+
]
|
|
161
|
+
keywords:
|
|
162
|
+
- toc
|
|
163
|
+
- table-of-contents
|
|
164
|
+
- outline
|
|
165
|
+
- sub-nav
|
|
166
|
+
- page-nav
|
|
167
|
+
- in-page-nav
|
|
168
|
+
- heading-nav
|
|
169
|
+
synonyms:
|
|
170
|
+
toc:
|
|
171
|
+
- table-of-contents
|
|
172
|
+
- outline
|
|
173
|
+
outline:
|
|
174
|
+
- toc
|
|
175
|
+
- table-of-contents
|
|
176
|
+
related:
|
|
177
|
+
- aside
|
|
178
|
+
- nav
|
|
179
|
+
- link
|
|
180
|
+
- text
|
|
@@ -94,6 +94,9 @@ export class UIToolbar extends UIElement {
|
|
|
94
94
|
gap: { type: String, default: 'sm', reflect: true },
|
|
95
95
|
align: { type: String, default: 'start', reflect: true },
|
|
96
96
|
overflow: { type: String, default: 'menu', reflect: true },
|
|
97
|
+
// yaml documented reflect:true since v1 — closing the gap so
|
|
98
|
+
// el.bordered = true also updates [bordered] CSS state.
|
|
99
|
+
bordered: { type: Boolean, default: false, reflect: true },
|
|
97
100
|
};
|
|
98
101
|
|
|
99
102
|
static template = () => null;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://adiaui.dev/a2ui/v0_9/components/VisuallyHidden.json",
|
|
4
|
+
"title": "VisuallyHidden",
|
|
5
|
+
"description": "Accessibility utility — content visible to screen readers but hidden\nvisually (the canonical \"sr-only\" pattern). Use for icon-only buttons,\ncontextual labels on duplicate controls, status announcements via\nlive regions, and any text the AT needs but sighted users don't.\n\nDistinct from `[hidden]` / `display:none` (hidden from EVERYONE\nincluding AT) and from `aria-label` (which replaces visible text but\ndoesn't add invisible text). When you need both visible and invisible\ntext on the same element, `<visually-hidden-ui>` is the wrapper for\nthe invisible part.\n",
|
|
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
|
+
"component": {
|
|
17
|
+
"const": "VisuallyHidden"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"required": [
|
|
21
|
+
"component"
|
|
22
|
+
],
|
|
23
|
+
"unevaluatedProperties": false,
|
|
24
|
+
"x-adiaui": {
|
|
25
|
+
"anti_patterns": [
|
|
26
|
+
{
|
|
27
|
+
"fix": "<button-ui icon=\"trash\"><visually-hidden-ui>Delete</visually-hidden-ui></button-ui> OR set aria-label=\"Delete\" — but visually-hidden-ui scales to longer context (\"Delete row 42 in the orders table\") without overloading the label attribute.",
|
|
28
|
+
"why": "Icon-only buttons with no accessible name are unreadable for screen-reader users.",
|
|
29
|
+
"wrong": "<button-ui icon=\"trash\"></button-ui>"
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
"category": "utility",
|
|
33
|
+
"composes": [],
|
|
34
|
+
"events": {},
|
|
35
|
+
"examples": [
|
|
36
|
+
{
|
|
37
|
+
"description": "Sr-only label for an icon-only button.",
|
|
38
|
+
"a2ui": "[\n { \"id\": \"btn\", \"component\": \"Button\", \"icon\": \"trash\", \"children\": [\"lbl\"] },\n { \"id\": \"lbl\", \"component\": \"VisuallyHidden\", \"textContent\": \"Delete row\" }\n]\n",
|
|
39
|
+
"name": "icon-button-label"
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
"keywords": [
|
|
43
|
+
"visually-hidden",
|
|
44
|
+
"sr-only",
|
|
45
|
+
"screen-reader",
|
|
46
|
+
"a11y",
|
|
47
|
+
"accessibility"
|
|
48
|
+
],
|
|
49
|
+
"name": "UIVisuallyHidden",
|
|
50
|
+
"related": [
|
|
51
|
+
"text"
|
|
52
|
+
],
|
|
53
|
+
"slots": {
|
|
54
|
+
"default": {
|
|
55
|
+
"description": "The text or content to hide visually while keeping accessible."
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"states": [
|
|
59
|
+
{
|
|
60
|
+
"description": "Default — content is visually hidden but in the accessibility tree.",
|
|
61
|
+
"name": "idle"
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
"status": "stable",
|
|
65
|
+
"synonyms": {},
|
|
66
|
+
"tag": "visually-hidden-ui",
|
|
67
|
+
"tokens": {},
|
|
68
|
+
"traits": [],
|
|
69
|
+
"version": 1
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<visually-hidden-ui>` — sr-only utility.
|
|
3
|
+
*
|
|
4
|
+
* Pure CSS atom — no template, no JS behavior. Stamps the canonical
|
|
5
|
+
* sr-only pattern (see visually-hidden.css) so consumers don't need to
|
|
6
|
+
* memorize the spell.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { UIElement } from '../../core/element.js';
|
|
10
|
+
|
|
11
|
+
export class UIVisuallyHidden extends UIElement {
|
|
12
|
+
static properties = {};
|
|
13
|
+
static template = () => null;
|
|
14
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
@scope (visually-hidden-ui) {
|
|
2
|
+
/* Canonical sr-only pattern — content available to assistive tech but
|
|
3
|
+
not painted. Avoid `display: none` (removes from a11y tree) and
|
|
4
|
+
`visibility: hidden` (removes too).
|
|
5
|
+
|
|
6
|
+
- position:absolute + clip-path:inset(100%) — hides without affecting
|
|
7
|
+
layout, with the clip-path doing the real visual removal
|
|
8
|
+
- width/height/padding/margin:0 — prevents layout impact
|
|
9
|
+
- white-space:nowrap — keeps multi-word strings on one line so the
|
|
10
|
+
clip works (a wrapping span can break the technique on some UAs)
|
|
11
|
+
- overflow:hidden + border:0 — belt-and-suspenders for cross-UA
|
|
12
|
+
*/
|
|
13
|
+
:scope {
|
|
14
|
+
position: absolute !important;
|
|
15
|
+
width: 1px !important;
|
|
16
|
+
height: 1px !important;
|
|
17
|
+
padding: 0 !important;
|
|
18
|
+
margin: -1px !important;
|
|
19
|
+
overflow: hidden !important;
|
|
20
|
+
clip: rect(0, 0, 0, 0) !important;
|
|
21
|
+
clip-path: inset(100%) !important;
|
|
22
|
+
white-space: nowrap !important;
|
|
23
|
+
border: 0 !important;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<visually-hidden-ui>` — Accessibility utility — content visible to screen readers but hidden
|
|
3
|
+
visually (the canonical "sr-only" pattern). Use for icon-only buttons,
|
|
4
|
+
contextual labels on duplicate controls, status announcements via
|
|
5
|
+
live regions, and any text the AT needs but sighted users don't.
|
|
6
|
+
|
|
7
|
+
Distinct from `[hidden]` / `display:none` (hidden from EVERYONE
|
|
8
|
+
including AT) and from `aria-label` (which replaces visible text but
|
|
9
|
+
doesn't add invisible text). When you need both visible and invisible
|
|
10
|
+
text on the same element, `<visually-hidden-ui>` is the wrapper for
|
|
11
|
+
the invisible part.
|
|
12
|
+
|
|
13
|
+
*
|
|
14
|
+
* @see https://ui-kit.exe.xyz/site/components/visually-hidden
|
|
15
|
+
*
|
|
16
|
+
* Type declarations generated by scripts/build/dts-codegen.mjs from
|
|
17
|
+
* the component's `.a2ui.json` sidecar(s). Edit the source `.yaml`,
|
|
18
|
+
* run `npm run build:components`, then `npm run codegen:dts` to
|
|
19
|
+
* regenerate; or hand-author this file fully if rich event types are
|
|
20
|
+
* needed beyond what the yaml `events:` block can express.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { UIElement } from '../../core/element.js';
|
|
24
|
+
|
|
25
|
+
export class UIVisuallyHidden extends UIElement {
|
|
26
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<visually-hidden-ui>` — auto-registers the tag on import.
|
|
3
|
+
*
|
|
4
|
+
* @see ../../USAGE.md#registration--auto-vs-explicit
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { defineIfFree } from '../../core/register.js';
|
|
8
|
+
import { UIVisuallyHidden } from './visually-hidden.class.js';
|
|
9
|
+
|
|
10
|
+
defineIfFree('visually-hidden-ui', UIVisuallyHidden);
|
|
11
|
+
|
|
12
|
+
export { UIVisuallyHidden };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
$schema: ../../../../scripts/schemas/component.yaml.schema.json
|
|
2
|
+
name: UIVisuallyHidden
|
|
3
|
+
tag: visually-hidden-ui
|
|
4
|
+
status: stable
|
|
5
|
+
component: VisuallyHidden
|
|
6
|
+
category: utility
|
|
7
|
+
version: 1
|
|
8
|
+
description: |
|
|
9
|
+
Accessibility utility — content visible to screen readers but hidden
|
|
10
|
+
visually (the canonical "sr-only" pattern). Use for icon-only buttons,
|
|
11
|
+
contextual labels on duplicate controls, status announcements via
|
|
12
|
+
live regions, and any text the AT needs but sighted users don't.
|
|
13
|
+
|
|
14
|
+
Distinct from `[hidden]` / `display:none` (hidden from EVERYONE
|
|
15
|
+
including AT) and from `aria-label` (which replaces visible text but
|
|
16
|
+
doesn't add invisible text). When you need both visible and invisible
|
|
17
|
+
text on the same element, `<visually-hidden-ui>` is the wrapper for
|
|
18
|
+
the invisible part.
|
|
19
|
+
props: {}
|
|
20
|
+
events: {}
|
|
21
|
+
slots:
|
|
22
|
+
default:
|
|
23
|
+
description: The text or content to hide visually while keeping accessible.
|
|
24
|
+
states:
|
|
25
|
+
- name: idle
|
|
26
|
+
description: Default — content is visually hidden but in the accessibility tree.
|
|
27
|
+
traits: []
|
|
28
|
+
tokens: {}
|
|
29
|
+
a2ui:
|
|
30
|
+
rules:
|
|
31
|
+
- rule: 'Wrap text that screen readers need but sighted users do not — icon-only-button labels, contextual disambiguation ("Edit profile" inside a row whose visible button just says "Edit").'
|
|
32
|
+
reason: 'Accessibility — AT users need the missing context.'
|
|
33
|
+
- rule: 'Distinct from [hidden] / display:none (hides from EVERYONE) and from aria-label (replaces visible text). Use <visually-hidden-ui> when both visible AND invisible text need to coexist on the same element.'
|
|
34
|
+
reason: 'Three different hide-mechanisms with different semantics.'
|
|
35
|
+
anti_patterns:
|
|
36
|
+
- wrong: '<button-ui icon="trash"></button-ui>'
|
|
37
|
+
why: 'Icon-only buttons with no accessible name are unreadable for screen-reader users.'
|
|
38
|
+
fix: '<button-ui icon="trash"><visually-hidden-ui>Delete</visually-hidden-ui></button-ui> OR set aria-label="Delete" — but visually-hidden-ui scales to longer context ("Delete row 42 in the orders table") without overloading the label attribute.'
|
|
39
|
+
examples:
|
|
40
|
+
- name: icon-button-label
|
|
41
|
+
description: Sr-only label for an icon-only button.
|
|
42
|
+
a2ui: |
|
|
43
|
+
[
|
|
44
|
+
{ "id": "btn", "component": "Button", "icon": "trash", "children": ["lbl"] },
|
|
45
|
+
{ "id": "lbl", "component": "VisuallyHidden", "textContent": "Delete row" }
|
|
46
|
+
]
|
|
47
|
+
keywords:
|
|
48
|
+
- visually-hidden
|
|
49
|
+
- sr-only
|
|
50
|
+
- screen-reader
|
|
51
|
+
- a11y
|
|
52
|
+
- accessibility
|
|
53
|
+
related:
|
|
54
|
+
- text
|
package/core/anchor.js
CHANGED
|
@@ -45,6 +45,9 @@ function anchorNative(anchor, popover, { placement, gap, matchWidth }) {
|
|
|
45
45
|
positionTryFallbacks: popover.style.positionTryFallbacks,
|
|
46
46
|
margin: popover.style.margin,
|
|
47
47
|
width: popover.style.width,
|
|
48
|
+
maxWidth: popover.style.maxWidth,
|
|
49
|
+
maxHeight: popover.style.maxHeight,
|
|
50
|
+
overflowY: popover.style.overflowY,
|
|
48
51
|
top: popover.style.top,
|
|
49
52
|
left: popover.style.left,
|
|
50
53
|
};
|
|
@@ -59,9 +62,22 @@ function anchorNative(anchor, popover, { placement, gap, matchWidth }) {
|
|
|
59
62
|
// Let the browser flip across the opposite side + both perpendicular edges.
|
|
60
63
|
popover.style.positionTryFallbacks = 'flip-block, flip-inline, flip-block flip-inline';
|
|
61
64
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
+
// Sizing concerns that follow from anchor positioning — kept here (not in
|
|
66
|
+
// popover.css) because they're the anchor module's contract:
|
|
67
|
+
// • width: max-content — position-area defines a bounding span, NOT a
|
|
68
|
+
// content width; without a width hint the browser stretches the popover
|
|
69
|
+
// to fill the entire span (e.g. span-left can yield a 1300 px-wide
|
|
70
|
+
// popover on a wide viewport). max-content sizes to content; consumer
|
|
71
|
+
// min-width still clamps the lower bound.
|
|
72
|
+
// • max-width / max-height — viewport safety so popovers don't escape
|
|
73
|
+
// the visible area when the anchored content overflows.
|
|
74
|
+
// • overflow-y: auto — required when max-height clamps (otherwise content
|
|
75
|
+
// past the cap is silently hidden).
|
|
76
|
+
// Consumers can override per-instance via --popover-* tokens / inline style.
|
|
77
|
+
popover.style.width = matchWidth ? `anchor-size(${name} width)` : 'max-content';
|
|
78
|
+
popover.style.maxWidth = 'min(calc(100vw - 1rem), 32rem)';
|
|
79
|
+
popover.style.maxHeight = 'calc(100vh - 3rem)';
|
|
80
|
+
popover.style.overflowY = 'auto';
|
|
65
81
|
|
|
66
82
|
// top/left are controlled by position-area; make sure no stale values fight it.
|
|
67
83
|
popover.style.top = '';
|