@ascribed/elements 0.2.1-next.9 → 0.3.0

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/CONTRACT.md CHANGED
@@ -11,12 +11,13 @@ The site output (SPEC §9.4) is markdown plus custom elements. This contract is
11
11
  | `@details` | [`<details>`](#5-details) with `<summary>` | Optional CSS |
12
12
  | Project widget | [An element named after the widget](#6-project-widgets), and `<ascribe-group>` for groups | The project's own; `<ascribe-group>` is the library's |
13
13
  | Glossary term | [An ordinary link](#7-glossary-terms), marked with `data-ascribe-term` | Optional CSS |
14
+ | The pointer to `llms.txt`, with `[consumer] agents = true` | [`<ascribe-for-agents>`](#8-ascribe-for-agents) | CSS only |
14
15
 
15
16
  A change to this contract changes the site emitter (`crates/ascribe-emit`), the element library, and the site-render fixtures (`tests/render/`) together.
16
17
 
17
18
  ## 0. Rules for every element
18
19
 
19
- **Light DOM, CSS, no behavior.** Elements render into the light DOM, so site styles apply and content stays visible to search engines and assistive technology (SPEC §9.7). They're styled with CSS and themed with custom properties whose names start with `--ascribe-`; the package README lists them. Only `<ascribe-tabs>` runs JavaScript. Everything else must render meaningfully with the library's CSS and no script, and must stay readable with neither.
20
+ **Light DOM, CSS, no behavior.** Elements render into the light DOM, so site styles apply and content stays visible to search engines and assistive technology (SPEC §9.7). They're styled with CSS and themed with custom properties whose names start with `--ascribe-`; the package README lists them. The names are part of this contract; their default values aren't, and a release may change them, noting it in the changelog. Only `<ascribe-tabs>` runs JavaScript. Everything else must render meaningfully with the library's CSS and no script, and must stay readable with neither.
20
21
 
21
22
  **How the emitter writes elements.** Custom elements are raw HTML blocks in CommonMark, so the layout matters:
22
23
 
@@ -30,7 +31,7 @@ A change to this contract changes the site emitter (`crates/ascribe-emit`), the
30
31
 
31
32
  **Plain text of inline content.** Where an attribute holds a title or label written as inline markdown, its value is the inline content's text: phrases substituted (they already are, in the resolved page), code spans as their text, emphasis and links as their text, images as their alt text, raw inline HTML dropped, and line breaks as single spaces. So ``.Try the *new* `quill` CLI`` becomes `Try the new quill CLI`.
32
33
 
33
- **Labels** come from the content model: note types' `label` (docs/content/contracts/content-model.md §11), dimensions' and dimension values' `labels` (§7), and lifecycle states' `label` (§9). A value without a declared label is shown as itself.
34
+ **Labels** come from the content model: note types' `label` (docs/content/contracts/content-model.md §10), dimensions' and dimension values' `labels` (§6), and lifecycle states' `label` (§8). A value without a declared label is shown as itself.
34
35
 
35
36
  ## 1. `<ascribe-note>`
36
37
 
@@ -130,6 +131,7 @@ pnpm add -g @quill/agent
130
131
  - **Initial selection:** for a group with `sync`, the first tab whose `value` contains the remembered value for that dimension; otherwise, and for labeled groups, the first tab.
131
132
  - **Syncing:** selecting a tab in a group with `sync` records the first value in the tab's `value` as the choice for that dimension, then selects, in every `<ascribe-tabs>` on the page with the same `sync`, the first tab whose `value` contains it. A group with no matching tab keeps its selection.
132
133
  - **Remembering:** the choice is stored in `localStorage` under the key `ascribe-tabs:<sync>`, such as `ascribe-tabs:pm`, and read when a group starts. Storage can be unavailable (private windows, blocked storage): every read and write is guarded, and without storage the choice lasts for the page only.
134
+ - **Choosing from a script:** the script exports `choose(dimension, value)`, which does what selecting a tab whose first value is `value`, in a group whose `sync` is `dimension`, does: it records the choice and selects it in every group on the page with that `sync`, and a group with no matching tab keeps its selection. It doesn't check the value. A page with no groups can call it before sending the reader on, and the next page's groups start on the choice, as a permalink's address does with its parameters.
133
135
 
134
136
  ## 4. `<ascribe-availability>` and `<ascribe-availability-target>`
135
137
 
@@ -199,7 +201,7 @@ The element library may style `details` and `summary`, and implements no element
199
201
 
200
202
  ## 6. Project widgets
201
203
 
202
- A project widget (SPEC §6) becomes a custom element whose tag is the widget's name (SPEC §9.4). Widget names are valid custom-element names: they contain a hyphen, and docs/content/contracts/content-model.md §15 rejects names starting with `ascribe-` and HTML's reserved names. The project supplies the element's implementation; the element library doesn't.
204
+ A project widget (SPEC §6) becomes a custom element whose tag is the widget's name (SPEC §9.4). Widget names are valid custom-element names: they contain a hyphen, and docs/content/contracts/content-model.md §14 rejects names starting with `ascribe-` and HTML's reserved names. The project supplies the element's implementation; the element library doesn't.
203
205
 
204
206
  **Attributes**, in this order:
205
207
 
@@ -207,7 +209,7 @@ A project widget (SPEC §6) becomes a custom element whose tag is the widget's n
207
209
  2. `primary`: an identifier primary, if the widget takes one and it's given. A text primary is content instead (below).
208
210
  3. Each attribute in the widget's declaration, in declared order, with the value given or, if absent, its declared default. A value set's members are joined by single spaces; booleans are `true` or `false`; other values are their text. An attribute with neither a value nor a default is left out. A declaration can't use `heading`, `primary`, or HTML's global or event-handler attribute names (SPEC §7.2; content-model.md, `model-attribute-reserved`), so these never clash.
209
211
 
210
- **Children and placement**, by the widget's form and binding (docs/content/contracts/content-model.md §15):
212
+ **Children and placement**, by the widget's form and binding (docs/content/contracts/content-model.md §14):
211
213
 
212
214
  | Widget | Element |
213
215
  |---|---|
@@ -223,7 +225,7 @@ A wrapping widget follows §0's layout for elements that wrap markdown; an empty
223
225
 
224
226
  ## 7. Glossary terms
225
227
 
226
- There's no element for glossary terms. In the site output, an occurrence the glossary links (docs/content/contracts/content-model.md §13) is an ordinary markdown link to the term's `link` target, as a route, with the term's definition as the link title, followed directly by an attribute marker (docs/content/contracts/site-render.md §2.3) that gives the `<a>` a `data-ascribe-term` attribute holding the term's id:
228
+ There's no element for glossary terms. In the site output, an occurrence the glossary links (docs/content/contracts/content-model.md §12) is an ordinary markdown link to the term's `link` target, as a route, with the term's definition as the link title, followed directly by an attribute marker (docs/content/contracts/site-render.md §2.3) that gives the `<a>` a `data-ascribe-term` attribute holding the term's id:
227
229
 
228
230
  ```markdown
229
231
  [API key](/docs/reference/glossary/#api-key "A secret token that authenticates the Quill agent.")<ascribe-attributes data-ascribe-term="api-key"></ascribe-attributes>
@@ -236,3 +238,19 @@ There's no element for glossary terms. In the site output, an occurrence the glo
236
238
  Terms without a `link` stay plain text (content-model.md Q7).
237
239
 
238
240
  **Rendering:** a link, which the library's CSS underlines with dots (`a[data-ascribe-term]`). A site restyles it, or shows the definition as a popover, by selecting on the attribute.
241
+
242
+ ## 8. `<ascribe-for-agents>`
243
+
244
+ With `[consumer] agents = true` (docs/content/contracts/content-model.md §15), each page of the site output opens with a pointer for AI agents: where the index of every page, `llms.txt`, is, and where this page's Markdown version is (SPEC §9.4). It has no attributes, and wraps one paragraph of markdown with two links, both root-relative like every link in the site output:
245
+
246
+ ```markdown
247
+ <ascribe-for-agents>
248
+
249
+ For AI agents: the documentation index is at [llms.txt](/docs/llms.txt), and this page is available as [Markdown](/docs/guides/my-setup.md).
250
+
251
+ </ascribe-for-agents>
252
+ ```
253
+
254
+ It's the first block of the page's body, before any other content, so an agent that reads only the start of a page still finds it. Without `agents = true` the emitter writes no `<ascribe-for-agents>`.
255
+
256
+ **Rendering:** visually hidden: in the page, and read by an agent or a screen reader, but out of sight, as a site's "skip to content" link is (a clipped 1-pixel box, not `display: none`, which some readers skip). A site that wants readers to see it too restyles it.
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  The custom elements [Ascribe](https://github.com/ascribed-dev/ascribe)'s site output uses. They implement
4
4
  [CONTRACT.md](CONTRACT.md) exactly: `<ascribe-note>`, `<ascribe-steps>`,
5
5
  `<ascribe-tabs>` with `<ascribe-tab>`, `<ascribe-availability>` with
6
- `<ascribe-availability-target>`, and `<ascribe-group>`.
6
+ `<ascribe-availability-target>`, `<ascribe-group>`, and `<ascribe-for-agents>`.
7
7
 
8
8
  - Elements render into the light DOM and are styled by CSS.
9
9
  - Only `<ascribe-tabs>` uses JavaScript. Without the script, every tab shows
@@ -24,90 +24,31 @@ import "@ascribed/elements/style.css";
24
24
  The CSS works with no script at all. A site can also link `css/style.css`
25
25
  directly.
26
26
 
27
- ## Theming
28
-
29
- Set any of these custom properties on `:root`, or on any ancestor of the
30
- elements. Defaults are in `css/style.css`.
31
-
32
- | Group | Properties |
33
- |---|---|
34
- | Type | `--ascribe-font-family`, `--ascribe-font-size-small`, `--ascribe-font-weight-strong` |
35
- | Spacing and borders | `--ascribe-space`, `--ascribe-space-small`, `--ascribe-border-width`, `--ascribe-border-color`, `--ascribe-radius` |
36
- | Surfaces and text | `--ascribe-text-color`, `--ascribe-muted-color`, `--ascribe-surface-color` |
37
- | Notes | `--ascribe-<type>-color` and `--ascribe-<type>-background`, for `note`, `tip`, `important`, `warning`, and `caution` |
38
- | Steps | `--ascribe-steps-color`, `--ascribe-steps-marker-size`, `--ascribe-steps-marker-text-color` |
39
- | Tabs | `--ascribe-tab-color`, `--ascribe-tab-active-color`, `--ascribe-tab-focus-color`, `--ascribe-tab-hover-background` |
40
- | Availability | `--ascribe-state-color` and `--ascribe-state-background`, and `--ascribe-state-<state>-color` and `--ascribe-state-<state>-background` for `ga`, `preview`, `beta`, `deprecated`, and `removed` |
41
-
42
- ### Light and dark
43
-
44
- The default colors are light or dark as the page is. They're dark where the
45
- page's `color-scheme` is `dark`, or `light dark` while the reader's system is in
46
- dark mode, and light otherwise, including on a page that declares no
47
- `color-scheme`. So a site with a dark mode gets dark elements by declaring it:
48
-
49
- ```css
50
- :root {
51
- color-scheme: light dark;
52
- }
53
- ```
54
-
55
- A page whose colors don't follow its `color-scheme` sets
56
- `data-ascribe-scheme="light"` or `"dark"` on its root element, as the review
57
- marks read it. The defaults use CSS `light-dark()`; a browser without it shows
58
- the light colors. A property a site sets replaces both colors, so set it for
59
- each scheme when you set one:
60
-
61
- ```css
62
- :root {
63
- --ascribe-note-color: light-dark(#0550ae, #79c0ff);
64
- }
65
- ```
27
+ A script can make a reader's choice for a dimension, as selecting a tab does:
66
28
 
67
- ### Glossary terms
68
-
69
- A glossary link carries `data-ascribe-term` with the term's id, and its title
70
- is the definition. The library underlines it with dots; restyle it by
71
- selecting on the attribute, such as `a[data-ascribe-term]`.
72
-
73
- ### A project's own note types and lifecycle states
74
-
75
- Notes and availability targets carry their type or states as attributes, so a
76
- project styles its own by selecting on them. The elements take their colors
77
- from `--_color` and `--_background`:
78
-
79
- ```css
80
- ascribe-note[type="security"] {
81
- --_color: #b62324;
82
- --_background: #fff0f0;
83
- }
84
-
85
- ascribe-availability-target[states="sunset"],
86
- ascribe-availability-target[states$=" sunset"] {
87
- --_color: #6e7781;
88
- --_background: #eaeef2;
89
- }
29
+ ```js
30
+ import { choose } from "@ascribed/elements";
31
+ // Remembered across pages, and selected in this page's `pm` groups.
32
+ choose("pm", "pnpm");
90
33
  ```
91
34
 
92
- `states` lists a target's states in order and the last one styles the badge, so match it as a whole token, as above: `[states="x"], [states$=" x"]`. A bare `[states$="x"]` would also match a state named `pre-x`. Unknown types and states get the default (`note`, or the neutral state) style.
93
-
94
- ### Generated text
95
-
96
- Headings and labels come from the elements' attributes through CSS generated
97
- content, and the availability lead-in is `ascribe-availability::before`
98
- (`"Available: "`). A site can restyle or translate them:
35
+ ## Theming
99
36
 
100
- ```css
101
- ascribe-availability::before {
102
- content: "Disponible : ";
103
- }
104
- ```
37
+ The [element library reference](https://ascribed-dev.com/go/elements/) lists
38
+ the CSS custom properties that theme the elements, says what each one sets and
39
+ how they follow the page's light or dark scheme, and shows how to style your
40
+ own note types and lifecycle states. The properties' names are part of the
41
+ [contract](CONTRACT.md) and don't change; their defaults, in `css/style.css`,
42
+ may change in a release, as the changelog says.
105
43
 
106
44
  ## Tests
107
45
 
108
- `pnpm --filter @ascribed/elements test` compiles the library and drives Chromium
109
- through Playwright in Chromium, Firefox, and WebKit. Install them with
46
+ `pnpm --filter @ascribed/elements test` compiles the library and drives it
47
+ through Playwright. Locally it runs Chromium alone; in CI (`CI` set, as Actions
48
+ sets it) it runs Chromium, Firefox, and WebKit, in Playwright's container
49
+ image, which has all three. `ASCRIBE_ENGINES=chromium,firefox,webkit` (any
50
+ comma-separated subset) chooses the engines either way; install them with
110
51
  `pnpm --filter @ascribed/elements exec playwright-core install chromium firefox webkit`.
111
52
  Chromium uses `/opt/pw-browsers/chromium` when present, or the binary in
112
- `ASCRIBE_CHROMIUM`. `ASCRIBE_ENGINES=chromium` (a comma-separated subset) runs
113
- fewer engines on a machine that can't install all three.
53
+ `ASCRIBE_CHROMIUM`. The compile is skipped when every file in `dist/` is newer
54
+ than every file in `src/` and both tsconfigs.
package/css/style.css CHANGED
@@ -19,53 +19,65 @@
19
19
  --ascribe-font-weight-strong: 600;
20
20
 
21
21
  /* Spacing, borders */
22
+ --ascribe-border-width: 1px;
23
+ /* Generated from design/tokens.toml by scripts/design/tokens.ts: spacing, light. */
22
24
  --ascribe-space: 1rem;
23
25
  --ascribe-space-small: 0.5rem;
24
- --ascribe-border-width: 1px;
25
- --ascribe-border-color: #d0d7de;
26
+ --ascribe-border-color: #e3e6ec;
26
27
  --ascribe-radius: 0.375rem;
28
+ /* End of generated spacing. */
27
29
 
28
30
  /* Surfaces and text */
29
31
  --ascribe-text-color: inherit;
30
- --ascribe-muted-color: #57606a;
31
32
  --ascribe-surface-color: transparent;
33
+ /* Generated from design/tokens.toml by scripts/design/tokens.ts: text, light. */
34
+ --ascribe-muted-color: #676c75;
35
+ /* End of generated text. */
32
36
 
33
37
  /* Note types: an accent color and a tinted background each. */
34
- --ascribe-note-color: #0969da;
35
- --ascribe-note-background: #ddf4ff;
36
- --ascribe-tip-color: #1a7f37;
37
- --ascribe-tip-background: #dafbe1;
38
- --ascribe-important-color: #8250df;
39
- --ascribe-important-background: #fbefff;
40
- --ascribe-warning-color: #9a6700;
41
- --ascribe-warning-background: #fff8c5;
42
- --ascribe-caution-color: #cf222e;
43
- --ascribe-caution-background: #ffebe9;
38
+ /* Generated from design/tokens.toml by scripts/design/tokens.ts: notes, light. */
39
+ --ascribe-note-color: #474dc6;
40
+ --ascribe-note-background: #f5f6fe;
41
+ --ascribe-tip-color: #057256;
42
+ --ascribe-tip-background: #e1fff2;
43
+ --ascribe-important-color: #932f8e;
44
+ --ascribe-important-background: #fff2fd;
45
+ --ascribe-warning-color: #845502;
46
+ --ascribe-warning-background: #fef5eb;
47
+ --ascribe-caution-color: #a73303;
48
+ --ascribe-caution-background: #fef4f1;
49
+ /* End of generated notes. */
44
50
 
45
51
  /* Steps */
46
- --ascribe-steps-color: #0969da;
47
52
  --ascribe-steps-marker-size: 1.75rem;
53
+ /* Generated from design/tokens.toml by scripts/design/tokens.ts: steps, light. */
54
+ --ascribe-steps-color: #5962e8;
48
55
  --ascribe-steps-marker-text-color: #ffffff;
56
+ /* End of generated steps. */
49
57
 
50
58
  /* Tabs */
51
- --ascribe-tab-color: #57606a;
52
- --ascribe-tab-active-color: #0969da;
53
- --ascribe-tab-focus-color: #0969da;
54
- --ascribe-tab-hover-background: #f6f8fa;
59
+ /* Generated from design/tokens.toml by scripts/design/tokens.ts: tabs, light. */
60
+ --ascribe-tab-color: #676c75;
61
+ --ascribe-tab-active-color: #5962e8;
62
+ --ascribe-tab-focus-color: #5962e8;
63
+ --ascribe-tab-hover-background: #f7f9fd;
64
+ /* End of generated tabs. */
55
65
 
56
66
  /* Availability lifecycle states: an accent color and a background each. */
57
- --ascribe-state-color: #57606a;
58
- --ascribe-state-background: #eaeef2;
59
- --ascribe-state-ga-color: #1a7f37;
60
- --ascribe-state-ga-background: #dafbe1;
61
- --ascribe-state-preview-color: #8250df;
62
- --ascribe-state-preview-background: #fbefff;
63
- --ascribe-state-beta-color: #0969da;
64
- --ascribe-state-beta-background: #ddf4ff;
65
- --ascribe-state-deprecated-color: #9a6700;
66
- --ascribe-state-deprecated-background: #fff8c5;
67
- --ascribe-state-removed-color: #cf222e;
68
- --ascribe-state-removed-background: #ffebe9;
67
+ /* Generated from design/tokens.toml by scripts/design/tokens.ts: states, light. */
68
+ --ascribe-state-color: #676c75;
69
+ --ascribe-state-background: #f0f3f7;
70
+ --ascribe-state-ga-color: #057256;
71
+ --ascribe-state-ga-background: #e1fff2;
72
+ --ascribe-state-preview-color: #932f8e;
73
+ --ascribe-state-preview-background: #fff2fd;
74
+ --ascribe-state-beta-color: #474dc6;
75
+ --ascribe-state-beta-background: #f5f6fe;
76
+ --ascribe-state-deprecated-color: #845502;
77
+ --ascribe-state-deprecated-background: #fef5eb;
78
+ --ascribe-state-removed-color: #a73303;
79
+ --ascribe-state-removed-background: #fef4f1;
80
+ /* End of generated states. */
69
81
  }
70
82
 
71
83
  /* The same colors, light and dark. `light-dark()` picks by the element's
@@ -73,40 +85,53 @@
73
85
  `light-dark()` keeps the light colors above. */
74
86
  @supports (color: light-dark(#000, #fff)) {
75
87
  :root {
76
- --ascribe-border-color: light-dark(#d0d7de, #3d444d);
77
- --ascribe-muted-color: light-dark(#57606a, #9198a1);
78
-
79
- --ascribe-note-color: light-dark(#0969da, #4493f8);
80
- --ascribe-note-background: light-dark(#ddf4ff, #121d2f);
81
- --ascribe-tip-color: light-dark(#1a7f37, #3fb950);
82
- --ascribe-tip-background: light-dark(#dafbe1, #12261e);
83
- --ascribe-important-color: light-dark(#8250df, #ab7df8);
84
- --ascribe-important-background: light-dark(#fbefff, #1f1a33);
85
- --ascribe-warning-color: light-dark(#9a6700, #d29922);
86
- --ascribe-warning-background: light-dark(#fff8c5, #272115);
87
- --ascribe-caution-color: light-dark(#cf222e, #f85149);
88
- --ascribe-caution-background: light-dark(#ffebe9, #2d1517);
89
-
90
- --ascribe-steps-color: light-dark(#0969da, #4493f8);
91
- --ascribe-steps-marker-text-color: light-dark(#ffffff, #0d1117);
92
-
93
- --ascribe-tab-color: light-dark(#57606a, #9198a1);
94
- --ascribe-tab-active-color: light-dark(#0969da, #4493f8);
95
- --ascribe-tab-focus-color: light-dark(#0969da, #4493f8);
96
- --ascribe-tab-hover-background: light-dark(#f6f8fa, #151b23);
97
-
98
- --ascribe-state-color: light-dark(#57606a, #9198a1);
99
- --ascribe-state-background: light-dark(#eaeef2, #212830);
100
- --ascribe-state-ga-color: light-dark(#1a7f37, #3fb950);
101
- --ascribe-state-ga-background: light-dark(#dafbe1, #12261e);
102
- --ascribe-state-preview-color: light-dark(#8250df, #ab7df8);
103
- --ascribe-state-preview-background: light-dark(#fbefff, #1f1a33);
104
- --ascribe-state-beta-color: light-dark(#0969da, #4493f8);
105
- --ascribe-state-beta-background: light-dark(#ddf4ff, #121d2f);
106
- --ascribe-state-deprecated-color: light-dark(#9a6700, #d29922);
107
- --ascribe-state-deprecated-background: light-dark(#fff8c5, #272115);
108
- --ascribe-state-removed-color: light-dark(#cf222e, #f85149);
109
- --ascribe-state-removed-background: light-dark(#ffebe9, #2d1517);
88
+ /* Generated from design/tokens.toml by scripts/design/tokens.ts: spacing, light-dark. */
89
+ --ascribe-border-color: light-dark(#e3e6ec, #393d46);
90
+ /* End of generated spacing. */
91
+
92
+ /* Generated from design/tokens.toml by scripts/design/tokens.ts: text, light-dark. */
93
+ --ascribe-muted-color: light-dark(#676c75, #a5a9b2);
94
+ /* End of generated text. */
95
+
96
+ /* Generated from design/tokens.toml by scripts/design/tokens.ts: notes, light-dark. */
97
+ --ascribe-note-color: light-dark(#474dc6, #91a1fe);
98
+ --ascribe-note-background: light-dark(#f5f6fe, #161b3a);
99
+ --ascribe-tip-color: light-dark(#057256, #22c598);
100
+ --ascribe-tip-background: light-dark(#e1fff2, #02251a);
101
+ --ascribe-important-color: light-dark(#932f8e, #ffc8f9);
102
+ --ascribe-important-background: light-dark(#fff2fd, #2d132b);
103
+ --ascribe-warning-color: light-dark(#845502, #e0992e);
104
+ --ascribe-warning-background: light-dark(#fef5eb, #2b1a03);
105
+ --ascribe-caution-color: light-dark(#a73303, #fe7f57);
106
+ --ascribe-caution-background: light-dark(#fef4f1, #341308);
107
+ /* End of generated notes. */
108
+
109
+ /* Generated from design/tokens.toml by scripts/design/tokens.ts: steps, light-dark. */
110
+ --ascribe-steps-color: light-dark(#5962e8, #91a1fe);
111
+ --ascribe-steps-marker-text-color: light-dark(#ffffff, #0c0f16);
112
+ /* End of generated steps. */
113
+
114
+ /* Generated from design/tokens.toml by scripts/design/tokens.ts: tabs, light-dark. */
115
+ --ascribe-tab-color: light-dark(#676c75, #a5a9b2);
116
+ --ascribe-tab-active-color: light-dark(#5962e8, #91a1fe);
117
+ --ascribe-tab-focus-color: light-dark(#5962e8, #b3c0ff);
118
+ --ascribe-tab-hover-background: light-dark(#f7f9fd, #13171f);
119
+ /* End of generated tabs. */
120
+
121
+ /* Generated from design/tokens.toml by scripts/design/tokens.ts: states, light-dark. */
122
+ --ascribe-state-color: light-dark(#676c75, #a5a9b2);
123
+ --ascribe-state-background: light-dark(#f0f3f7, #22272e);
124
+ --ascribe-state-ga-color: light-dark(#057256, #22c598);
125
+ --ascribe-state-ga-background: light-dark(#e1fff2, #02251a);
126
+ --ascribe-state-preview-color: light-dark(#932f8e, #ffc8f9);
127
+ --ascribe-state-preview-background: light-dark(#fff2fd, #2d132b);
128
+ --ascribe-state-beta-color: light-dark(#474dc6, #91a1fe);
129
+ --ascribe-state-beta-background: light-dark(#f5f6fe, #161b3a);
130
+ --ascribe-state-deprecated-color: light-dark(#845502, #e0992e);
131
+ --ascribe-state-deprecated-background: light-dark(#fef5eb, #2b1a03);
132
+ --ascribe-state-removed-color: light-dark(#a73303, #fe7f57);
133
+ --ascribe-state-removed-background: light-dark(#fef4f1, #341308);
134
+ /* End of generated states. */
110
135
  }
111
136
 
112
137
  /* A page that says which it is. */
@@ -370,6 +395,22 @@ ascribe-group {
370
395
  display: block;
371
396
  }
372
397
 
398
+ /* <ascribe-for-agents> --------------------------------------------------- */
399
+
400
+ /* The pointer to llms.txt: out of sight, but in the page for an agent (and a
401
+ screen reader) to read. Not `display: none`, which some readers skip. */
402
+ ascribe-for-agents {
403
+ position: absolute;
404
+ width: 1px;
405
+ height: 1px;
406
+ margin: -1px;
407
+ padding: 0;
408
+ overflow: hidden;
409
+ clip-path: inset(50%);
410
+ white-space: nowrap;
411
+ border: 0;
412
+ }
413
+
373
414
  /* Glossary terms ---------------------------------------------------------- */
374
415
 
375
416
  /* A link to a glossary term, whose title is the term's definition. */
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  import { AscribeGroup } from "./group.js";
2
- import { AscribeTab, AscribeTabs } from "./tabs.js";
3
- export { AscribeGroup, AscribeTab, AscribeTabs };
2
+ import { AscribeTab, AscribeTabs, choose } from "./tabs.js";
3
+ export { AscribeGroup, AscribeTab, AscribeTabs, choose };
package/dist/index.js CHANGED
@@ -2,9 +2,9 @@
2
2
  // <ascribe-tabs> has behavior; the rest are styled by the CSS file
3
3
  // (`@ascribed/elements/style.css`), which works without this script.
4
4
  import { AscribeGroup } from "./group.js";
5
- import { AscribeTab, AscribeTabs } from "./tabs.js";
5
+ import { AscribeTab, AscribeTabs, choose } from "./tabs.js";
6
6
  import { ELEMENT_GROUP, ELEMENT_TAB, ELEMENT_TABS } from "./names.js";
7
- export { AscribeGroup, AscribeTab, AscribeTabs };
7
+ export { AscribeGroup, AscribeTab, AscribeTabs, choose };
8
8
  const registry = [
9
9
  [ELEMENT_TABS, AscribeTabs],
10
10
  [ELEMENT_TAB, AscribeTab],
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,mEAAmE;AACnE,qEAAqE;AAErE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEtE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC;AAEjD,MAAM,QAAQ,GAAyC;IACrD,CAAC,YAAY,EAAE,WAAW,CAAC;IAC3B,CAAC,WAAW,EAAE,UAAU,CAAC;IACzB,CAAC,aAAa,EAAE,YAAY,CAAC;CAC9B,CAAC;AAEF,KAAK,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,QAAQ,EAAE,CAAC;IAC3C,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,cAAc,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;AAC1E,CAAC","sourcesContent":["// @ascribed/elements: registers Ascribe's custom elements. Only\n// <ascribe-tabs> has behavior; the rest are styled by the CSS file\n// (`@ascribed/elements/style.css`), which works without this script.\n\nimport { AscribeGroup } from \"./group.js\";\nimport { AscribeTab, AscribeTabs } from \"./tabs.js\";\nimport { ELEMENT_GROUP, ELEMENT_TAB, ELEMENT_TABS } from \"./names.js\";\n\nexport { AscribeGroup, AscribeTab, AscribeTabs };\n\nconst registry: [string, CustomElementConstructor][] = [\n [ELEMENT_TABS, AscribeTabs],\n [ELEMENT_TAB, AscribeTab],\n [ELEMENT_GROUP, AscribeGroup],\n];\n\nfor (const [name, constructor] of registry) {\n if (!customElements.get(name)) customElements.define(name, constructor);\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,mEAAmE;AACnE,qEAAqE;AAErE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEtE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;AAEzD,MAAM,QAAQ,GAAyC;IACrD,CAAC,YAAY,EAAE,WAAW,CAAC;IAC3B,CAAC,WAAW,EAAE,UAAU,CAAC;IACzB,CAAC,aAAa,EAAE,YAAY,CAAC;CAC9B,CAAC;AAEF,KAAK,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,QAAQ,EAAE,CAAC;IAC3C,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,cAAc,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;AAC1E,CAAC","sourcesContent":["// @ascribed/elements: registers Ascribe's custom elements. Only\n// <ascribe-tabs> has behavior; the rest are styled by the CSS file\n// (`@ascribed/elements/style.css`), which works without this script.\n\nimport { AscribeGroup } from \"./group.js\";\nimport { AscribeTab, AscribeTabs, choose } from \"./tabs.js\";\nimport { ELEMENT_GROUP, ELEMENT_TAB, ELEMENT_TABS } from \"./names.js\";\n\nexport { AscribeGroup, AscribeTab, AscribeTabs, choose };\n\nconst registry: [string, CustomElementConstructor][] = [\n [ELEMENT_TABS, AscribeTabs],\n [ELEMENT_TAB, AscribeTab],\n [ELEMENT_GROUP, AscribeGroup],\n];\n\nfor (const [name, constructor] of registry) {\n if (!customElements.get(name)) customElements.define(name, constructor);\n}\n"]}
package/dist/names.d.ts CHANGED
@@ -15,6 +15,11 @@ export declare const ELEMENT_AVAILABILITY = "ascribe-availability";
15
15
  export declare const ELEMENT_AVAILABILITY_TARGET = "ascribe-availability-target";
16
16
  /** `<ascribe-group>`: a project widget's group (packages/elements/CONTRACT.md §6). */
17
17
  export declare const ELEMENT_GROUP = "ascribe-group";
18
+ /**
19
+ * `<ascribe-for-agents>`: the visually hidden pointer to `llms.txt` at the top
20
+ * of a page, for agents (packages/elements/CONTRACT.md §8).
21
+ */
22
+ export declare const ELEMENT_FOR_AGENTS = "ascribe-for-agents";
18
23
  /** `<ascribe-attributes>`: the attribute marker (site-render contract §1). */
19
24
  export declare const ELEMENT_ATTRIBUTES = "ascribe-attributes";
20
25
  /** The name an anchor comment starts with, `<!--ascribe-anchor` (site-render contract §7.3). */
package/dist/names.js CHANGED
@@ -18,6 +18,11 @@ export const ELEMENT_AVAILABILITY = "ascribe-availability";
18
18
  export const ELEMENT_AVAILABILITY_TARGET = "ascribe-availability-target";
19
19
  /** `<ascribe-group>`: a project widget's group (packages/elements/CONTRACT.md §6). */
20
20
  export const ELEMENT_GROUP = "ascribe-group";
21
+ /**
22
+ * `<ascribe-for-agents>`: the visually hidden pointer to `llms.txt` at the top
23
+ * of a page, for agents (packages/elements/CONTRACT.md §8).
24
+ */
25
+ export const ELEMENT_FOR_AGENTS = "ascribe-for-agents";
21
26
  /** `<ascribe-attributes>`: the attribute marker (site-render contract §1). */
22
27
  export const ELEMENT_ATTRIBUTES = "ascribe-attributes";
23
28
  /** The name an anchor comment starts with, `<!--ascribe-anchor` (site-render contract §7.3). */
package/dist/names.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"names.js","sourceRoot":"","sources":["../src/names.ts"],"names":[],"mappings":"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","sourcesContent":["// The names Ascribe puts on a page: elements, attributes, classes, and ids.\n// Generated from crates/ascribe-core/src/names.rs by\n// `ASCRIBE_BLESS=1 cargo test -p ascribe-core --test names`. Don't edit it.\n\n/** `<ascribe-note>`: a note (packages/elements/CONTRACT.md §1). */\nexport const ELEMENT_NOTE = \"ascribe-note\";\n\n/** `<ascribe-steps>`: a procedure (packages/elements/CONTRACT.md §2). */\nexport const ELEMENT_STEPS = \"ascribe-steps\";\n\n/** `<ascribe-tabs>`: a variant group of several arms (packages/elements/CONTRACT.md §3). */\nexport const ELEMENT_TABS = \"ascribe-tabs\";\n\n/** `<ascribe-tab>`: one arm of `<ascribe-tabs>` (packages/elements/CONTRACT.md §3). */\nexport const ELEMENT_TAB = \"ascribe-tab\";\n\n/** `<ascribe-availability>`: a badge or filter (packages/elements/CONTRACT.md §4). */\nexport const ELEMENT_AVAILABILITY = \"ascribe-availability\";\n\n/**\n * `<ascribe-availability-target>`: one target of `<ascribe-availability>`\n * (packages/elements/CONTRACT.md §4).\n */\nexport const ELEMENT_AVAILABILITY_TARGET = \"ascribe-availability-target\";\n\n/** `<ascribe-group>`: a project widget's group (packages/elements/CONTRACT.md §6). */\nexport const ELEMENT_GROUP = \"ascribe-group\";\n\n/** `<ascribe-attributes>`: the attribute marker (site-render contract §1). */\nexport const ELEMENT_ATTRIBUTES = \"ascribe-attributes\";\n\n/** The name an anchor comment starts with, `<!--ascribe-anchor` (site-render contract §7.3). */\nexport const COMMENT_ANCHOR = \"ascribe-anchor\";\n\n/** A block's source anchor (site-render contract §7.1). */\nexport const DATA_SOURCE = \"data-ascribe-source\";\n\n/** The includes a block came through (site-render contract §7.1). */\nexport const DATA_VIA = \"data-ascribe-via\";\n\n/** A glossary term's id, on its link (packages/elements/CONTRACT.md §7). */\nexport const DATA_TERM = \"data-ascribe-term\";\n\n/** The element the report draws into. */\nexport const ID_REPORT = \"ascribe-review\";\n\n/** The `<script type=\"application/json\">` holding the report's data. */\nexport const ID_REPORT_DATA = \"ascribe-review-data\";\n\n/**\n * The page's light or dark scheme, `light` or `dark`, on the root\n * element (docs/content/guides/astro.md).\n */\nexport const DATA_SCHEME = \"data-ascribe-scheme\";\n\n/** On a marked page's root: what it shows, `changes`, `will`, or `was`. */\nexport const DATA_SHOW = \"data-ascribe-show\";\n\n/**\n * On a marked block: its change, `added`, `changed`, `removed`, `moved`,\n * or `moved-from`.\n */\nexport const DATA_CHANGE = \"data-ascribe-change\";\n\n/** On every element review adds to the page, so it can be taken away again. */\nexport const DATA_UI = \"data-ascribe-ui\";\n\n/** On a change's label: the change. */\nexport const DATA_LABEL = \"data-ascribe-label\";\n\n/** On a tab's hint: `added` or `changed`. */\nexport const DATA_HINT = \"data-ascribe-hint\";\n\n/** On a moved block: the id its \"moved from\" link points at. */\nexport const DATA_MOVE = \"data-ascribe-move\";\n\n/** On a changed block whose copy as it was follows it. */\nexport const DATA_HAS_WAS = \"data-ascribe-has-was\";\n\n/** On a copy of a block as it was, shown only with the page as it was. */\nexport const DATA_WAS_ONLY = \"data-ascribe-was-only\";\n\n/** On a changed, removed, or moved block: its source anchor as it was. */\nexport const DATA_WAS_SOURCE = \"data-ascribe-was-source\";\n\n/** On the elements that hold the threads' overlay. */\nexport const DATA_OVERLAY = \"data-ascribe-overlay\";\n\n/** On the style element the Astro dev toolbar adds for the marks. */\nexport const DATA_REVIEW = \"data-ascribe-review\";\n\n/** On the root of a page whose changes are marked. */\nexport const CLASS_MARKS = \"ascribe-marks\";\n\n/** A change's label. */\nexport const CLASS_LABEL = \"ascribe-label\";\n\n/** A label placed before its block rather than over it. */\nexport const CLASS_LABEL_BEFORE = \"ascribe-label-before\";\n\n/** Inserted text. */\nexport const CLASS_INS = \"ascribe-ins\";\n\n/** Deleted text. */\nexport const CLASS_DEL = \"ascribe-del\";\n\n/** A removed block. */\nexport const CLASS_REMOVED = \"ascribe-removed\";\n\n/** A removed block's content. */\nexport const CLASS_REMOVED_BODY = \"ascribe-removed-body\";\n\n/** The button that shows or collapses a removed block. */\nexport const CLASS_TOGGLE = \"ascribe-toggle\";\n\n/** On a removed block that's shown. */\nexport const CLASS_OPEN = \"ascribe-open\";\n\n/** What stands where a moved block was. */\nexport const CLASS_MOVED_FROM = \"ascribe-moved-from\";\n\n/** The note with a moved block's link. */\nexport const CLASS_MOVE_NOTE = \"ascribe-move-note\";\n\n/** A move note after its block, not inside it. */\nexport const CLASS_MOVE_AFTER = \"ascribe-move-after\";\n\n/** The link between a moved block and where it was. */\nexport const CLASS_MOVE_LINK = \"ascribe-move-link\";\n\n/** A moved block's copy as it was, where it was. */\nexport const CLASS_WAS_COPY = \"ascribe-was-copy\";\n\n/** A tab's hint that it holds changes. */\nexport const CLASS_HINT = \"ascribe-hint\";\n\n/** A block flashed after going to it. */\nexport const CLASS_FLASH = \"ascribe-flash\";\n\n/** The tip that shows a block's source on hover and focus. */\nexport const CLASS_WHERE = \"ascribe-where\";\n"]}
1
+ {"version":3,"file":"names.js","sourceRoot":"","sources":["../src/names.ts"],"names":[],"mappings":"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","sourcesContent":["// The names Ascribe puts on a page: elements, attributes, classes, and ids.\n// Generated from crates/ascribe-core/src/names.rs by\n// `ASCRIBE_BLESS=1 cargo test -p ascribe-core --test names`. Don't edit it.\n\n/** `<ascribe-note>`: a note (packages/elements/CONTRACT.md §1). */\nexport const ELEMENT_NOTE = \"ascribe-note\";\n\n/** `<ascribe-steps>`: a procedure (packages/elements/CONTRACT.md §2). */\nexport const ELEMENT_STEPS = \"ascribe-steps\";\n\n/** `<ascribe-tabs>`: a variant group of several arms (packages/elements/CONTRACT.md §3). */\nexport const ELEMENT_TABS = \"ascribe-tabs\";\n\n/** `<ascribe-tab>`: one arm of `<ascribe-tabs>` (packages/elements/CONTRACT.md §3). */\nexport const ELEMENT_TAB = \"ascribe-tab\";\n\n/** `<ascribe-availability>`: a badge or filter (packages/elements/CONTRACT.md §4). */\nexport const ELEMENT_AVAILABILITY = \"ascribe-availability\";\n\n/**\n * `<ascribe-availability-target>`: one target of `<ascribe-availability>`\n * (packages/elements/CONTRACT.md §4).\n */\nexport const ELEMENT_AVAILABILITY_TARGET = \"ascribe-availability-target\";\n\n/** `<ascribe-group>`: a project widget's group (packages/elements/CONTRACT.md §6). */\nexport const ELEMENT_GROUP = \"ascribe-group\";\n\n/**\n * `<ascribe-for-agents>`: the visually hidden pointer to `llms.txt` at the top\n * of a page, for agents (packages/elements/CONTRACT.md §8).\n */\nexport const ELEMENT_FOR_AGENTS = \"ascribe-for-agents\";\n\n/** `<ascribe-attributes>`: the attribute marker (site-render contract §1). */\nexport const ELEMENT_ATTRIBUTES = \"ascribe-attributes\";\n\n/** The name an anchor comment starts with, `<!--ascribe-anchor` (site-render contract §7.3). */\nexport const COMMENT_ANCHOR = \"ascribe-anchor\";\n\n/** A block's source anchor (site-render contract §7.1). */\nexport const DATA_SOURCE = \"data-ascribe-source\";\n\n/** The includes a block came through (site-render contract §7.1). */\nexport const DATA_VIA = \"data-ascribe-via\";\n\n/** A glossary term's id, on its link (packages/elements/CONTRACT.md §7). */\nexport const DATA_TERM = \"data-ascribe-term\";\n\n/** The element the report draws into. */\nexport const ID_REPORT = \"ascribe-review\";\n\n/** The `<script type=\"application/json\">` holding the report's data. */\nexport const ID_REPORT_DATA = \"ascribe-review-data\";\n\n/**\n * The page's light or dark scheme, `light` or `dark`, on the root\n * element (docs/content/guides/astro.md).\n */\nexport const DATA_SCHEME = \"data-ascribe-scheme\";\n\n/** On a marked page's root: what it shows, `changes`, `will`, or `was`. */\nexport const DATA_SHOW = \"data-ascribe-show\";\n\n/**\n * On a marked block: its change, `added`, `changed`, `removed`, `moved`,\n * or `moved-from`.\n */\nexport const DATA_CHANGE = \"data-ascribe-change\";\n\n/** On every element review adds to the page, so it can be taken away again. */\nexport const DATA_UI = \"data-ascribe-ui\";\n\n/** On a change's label: the change. */\nexport const DATA_LABEL = \"data-ascribe-label\";\n\n/** On a tab's hint: `added` or `changed`. */\nexport const DATA_HINT = \"data-ascribe-hint\";\n\n/** On a moved block: the id its \"moved from\" link points at. */\nexport const DATA_MOVE = \"data-ascribe-move\";\n\n/** On a changed block whose copy as it was follows it. */\nexport const DATA_HAS_WAS = \"data-ascribe-has-was\";\n\n/** On a copy of a block as it was, shown only with the page as it was. */\nexport const DATA_WAS_ONLY = \"data-ascribe-was-only\";\n\n/** On a changed, removed, or moved block: its source anchor as it was. */\nexport const DATA_WAS_SOURCE = \"data-ascribe-was-source\";\n\n/** On the elements that hold the threads' overlay. */\nexport const DATA_OVERLAY = \"data-ascribe-overlay\";\n\n/** On the style element the Astro dev toolbar adds for the marks. */\nexport const DATA_REVIEW = \"data-ascribe-review\";\n\n/** On the root of a page whose changes are marked. */\nexport const CLASS_MARKS = \"ascribe-marks\";\n\n/** A change's label. */\nexport const CLASS_LABEL = \"ascribe-label\";\n\n/** A label placed before its block rather than over it. */\nexport const CLASS_LABEL_BEFORE = \"ascribe-label-before\";\n\n/** Inserted text. */\nexport const CLASS_INS = \"ascribe-ins\";\n\n/** Deleted text. */\nexport const CLASS_DEL = \"ascribe-del\";\n\n/** A removed block. */\nexport const CLASS_REMOVED = \"ascribe-removed\";\n\n/** A removed block's content. */\nexport const CLASS_REMOVED_BODY = \"ascribe-removed-body\";\n\n/** The button that shows or collapses a removed block. */\nexport const CLASS_TOGGLE = \"ascribe-toggle\";\n\n/** On a removed block that's shown. */\nexport const CLASS_OPEN = \"ascribe-open\";\n\n/** What stands where a moved block was. */\nexport const CLASS_MOVED_FROM = \"ascribe-moved-from\";\n\n/** The note with a moved block's link. */\nexport const CLASS_MOVE_NOTE = \"ascribe-move-note\";\n\n/** A move note after its block, not inside it. */\nexport const CLASS_MOVE_AFTER = \"ascribe-move-after\";\n\n/** The link between a moved block and where it was. */\nexport const CLASS_MOVE_LINK = \"ascribe-move-link\";\n\n/** A moved block's copy as it was, where it was. */\nexport const CLASS_WAS_COPY = \"ascribe-was-copy\";\n\n/** A tab's hint that it holds changes. */\nexport const CLASS_HINT = \"ascribe-hint\";\n\n/** A block flashed after going to it. */\nexport const CLASS_FLASH = \"ascribe-flash\";\n\n/** The tip that shows a block's source on hover and focus. */\nexport const CLASS_WHERE = \"ascribe-where\";\n"]}
package/dist/tabs.d.ts CHANGED
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Choose `value` for `dimension`, as a reader selecting a tab with that value
3
+ * does: remember it across pages, and select it in every group on this page
4
+ * that switches on the dimension. A group with no tab for it keeps its
5
+ * selection. The value isn't checked.
6
+ */
7
+ export declare function choose(dimension: string, value: string): void;
1
8
  /** A tab panel: one arm of a `@variant` group. Styled by CSS; no behavior. */
2
9
  export declare class AscribeTab extends HTMLElement {
3
10
  }
package/dist/tabs.js CHANGED
@@ -35,6 +35,18 @@ function writeChoice(dimension, value) {
35
35
  function valuesOf(tab) {
36
36
  return (tab.getAttribute("value") ?? "").split(/\s+/).filter(Boolean);
37
37
  }
38
+ /** Select `value` in every group on the page that switches on `dimension`, but `except`. */
39
+ let followChoice;
40
+ /**
41
+ * Choose `value` for `dimension`, as a reader selecting a tab with that value
42
+ * does: remember it across pages, and select it in every group on this page
43
+ * that switches on the dimension. A group with no tab for it keeps its
44
+ * selection. The value isn't checked.
45
+ */
46
+ export function choose(dimension, value) {
47
+ writeChoice(dimension, value);
48
+ followChoice(dimension, value);
49
+ }
38
50
  /** A tab panel: one arm of a `@variant` group. Styled by CSS; no behavior. */
39
51
  export class AscribeTab extends HTMLElement {
40
52
  }
@@ -44,6 +56,14 @@ export class AscribeTabs extends HTMLElement {
44
56
  #buttons = [];
45
57
  #selected = 0;
46
58
  #ready = false;
59
+ static {
60
+ followChoice = (dimension, value, except) => {
61
+ for (const group of syncedGroups) {
62
+ if (group !== except && group.getAttribute("sync") === dimension)
63
+ group.#follow(value);
64
+ }
65
+ };
66
+ }
47
67
  connectedCallback() {
48
68
  // A parser-created element is connected before its children are parsed.
49
69
  if (document.readyState === "loading") {
@@ -132,10 +152,7 @@ export class AscribeTabs extends HTMLElement {
132
152
  if (sync === null || choice === undefined)
133
153
  return;
134
154
  writeChoice(sync, choice);
135
- for (const group of syncedGroups) {
136
- if (group !== this && group.getAttribute("sync") === sync)
137
- group.#follow(choice);
138
- }
155
+ followChoice(sync, choice, this);
139
156
  }
140
157
  /** Another group with the same `sync` made a choice; keep our selection if we have no match. */
141
158
  #follow(value) {
package/dist/tabs.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tabs.js","sourceRoot":"","sources":["../src/tabs.ts"],"names":[],"mappings":"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","sourcesContent":["// <ascribe-tabs> and <ascribe-tab> (CONTRACT.md §3): the library's only\n// element with behavior. Without this script every tab shows with its label\n// (CSS generated content); with it, the group follows the WAI-ARIA tabs\n// pattern.\n\nimport { ELEMENT_TAB } from \"./names.js\";\n\nconst STORAGE_PREFIX = \"ascribe-tabs:\";\n\n/** Choices made this page load, for readers whose storage is unavailable. */\nconst pageChoices = new Map<string, string>();\n\n/** Every connected, initialized group with a `sync` attribute. */\nconst syncedGroups = new Set<AscribeTabs>();\n\nlet idCounter = 0;\n\nfunction readChoice(dimension: string): string | null {\n // This page's choices are always at least as fresh as storage, which a\n // failed write leaves behind.\n const chosen = pageChoices.get(dimension);\n if (chosen !== undefined) return chosen;\n try {\n return localStorage.getItem(STORAGE_PREFIX + dimension);\n } catch {\n // Storage can be blocked or unavailable.\n return null;\n }\n}\n\nfunction writeChoice(dimension: string, value: string): void {\n pageChoices.set(dimension, value);\n try {\n localStorage.setItem(STORAGE_PREFIX + dimension, value);\n } catch {\n // The choice lasts for the page only.\n }\n}\n\nfunction valuesOf(tab: Element): string[] {\n return (tab.getAttribute(\"value\") ?? \"\").split(/\\s+/).filter(Boolean);\n}\n\n/** A tab panel: one arm of a `@variant` group. Styled by CSS; no behavior. */\nexport class AscribeTab extends HTMLElement {}\n\nexport class AscribeTabs extends HTMLElement {\n #tablist: HTMLElement | null = null;\n #tabs: HTMLElement[] = [];\n #buttons: HTMLButtonElement[] = [];\n #selected = 0;\n #ready = false;\n\n connectedCallback(): void {\n // A parser-created element is connected before its children are parsed.\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", () => this.#setup(), { once: true });\n } else {\n this.#setup();\n }\n }\n\n disconnectedCallback(): void {\n this.#teardown();\n }\n\n #setup(): void {\n if (!this.isConnected || this.#ready) return;\n this.#tabs = Array.from(this.children).filter(\n (child): child is HTMLElement => child.localName === ELEMENT_TAB,\n );\n if (this.#tabs.length === 0) return;\n this.#ready = true;\n\n const tablist = document.createElement(\"div\");\n tablist.setAttribute(\"role\", \"tablist\");\n this.#tablist = tablist;\n\n this.#buttons = this.#tabs.map((tab) => {\n if (!tab.id) tab.id = `ascribe-tab-${++idCounter}`;\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.id = `${tab.id}-button`;\n button.setAttribute(\"role\", \"tab\");\n button.setAttribute(\"aria-controls\", tab.id);\n button.textContent = tab.getAttribute(\"label\") ?? \"\";\n button.addEventListener(\"click\", () => this.#choose(this.#buttons.indexOf(button)));\n button.addEventListener(\"keydown\", (event) => this.#onKeydown(event));\n tablist.append(button);\n tab.setAttribute(\"role\", \"tabpanel\");\n tab.setAttribute(\"aria-labelledby\", button.id);\n return button;\n });\n this.prepend(tablist);\n\n const sync = this.getAttribute(\"sync\");\n let initial = 0;\n if (sync !== null) {\n syncedGroups.add(this);\n const remembered = readChoice(sync);\n const match = remembered === null ? -1 : this.#indexOfValue(remembered);\n if (match >= 0) initial = match;\n }\n this.#select(initial);\n }\n\n #teardown(): void {\n if (!this.#ready) return;\n syncedGroups.delete(this);\n this.#tablist?.remove();\n for (const tab of this.#tabs) {\n tab.removeAttribute(\"role\");\n tab.removeAttribute(\"aria-labelledby\");\n tab.removeAttribute(\"hidden\");\n }\n this.#tablist = null;\n this.#tabs = [];\n this.#buttons = [];\n this.#ready = false;\n }\n\n #indexOfValue(value: string): number {\n return this.#tabs.findIndex((tab) => valuesOf(tab).includes(value));\n }\n\n /** Select a tab without recording or syncing the choice. */\n #select(index: number): void {\n this.#selected = index;\n this.#tabs.forEach((tab, i) => {\n tab.toggleAttribute(\"hidden\", i !== index);\n });\n this.#buttons.forEach((button, i) => {\n button.setAttribute(\"aria-selected\", String(i === index));\n button.tabIndex = i === index ? 0 : -1;\n });\n }\n\n /** The reader picked a tab: select it and, for a synced group, tell the rest. */\n #choose(index: number): void {\n this.#select(index);\n const sync = this.getAttribute(\"sync\");\n const tab = this.#tabs[index];\n const choice = tab ? valuesOf(tab)[0] : undefined;\n if (sync === null || choice === undefined) return;\n writeChoice(sync, choice);\n for (const group of syncedGroups) {\n if (group !== this && group.getAttribute(\"sync\") === sync) group.#follow(choice);\n }\n }\n\n /** Another group with the same `sync` made a choice; keep our selection if we have no match. */\n #follow(value: string): void {\n const index = this.#indexOfValue(value);\n if (index >= 0) this.#select(index);\n }\n\n #onKeydown(event: KeyboardEvent): void {\n const last = this.#tabs.length - 1;\n let next: number;\n switch (event.key) {\n case \"ArrowRight\":\n next = this.#selected === last ? 0 : this.#selected + 1;\n break;\n case \"ArrowLeft\":\n next = this.#selected === 0 ? last : this.#selected - 1;\n break;\n case \"Home\":\n next = 0;\n break;\n case \"End\":\n next = last;\n break;\n default:\n return;\n }\n event.preventDefault();\n this.#choose(next);\n this.#buttons[next]?.focus();\n }\n}\n"]}
1
+ {"version":3,"file":"tabs.js","sourceRoot":"","sources":["../src/tabs.ts"],"names":[],"mappings":"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","sourcesContent":["// <ascribe-tabs> and <ascribe-tab> (CONTRACT.md §3): the library's only\n// element with behavior. Without this script every tab shows with its label\n// (CSS generated content); with it, the group follows the WAI-ARIA tabs\n// pattern.\n\nimport { ELEMENT_TAB } from \"./names.js\";\n\nconst STORAGE_PREFIX = \"ascribe-tabs:\";\n\n/** Choices made this page load, for readers whose storage is unavailable. */\nconst pageChoices = new Map<string, string>();\n\n/** Every connected, initialized group with a `sync` attribute. */\nconst syncedGroups = new Set<AscribeTabs>();\n\nlet idCounter = 0;\n\nfunction readChoice(dimension: string): string | null {\n // This page's choices are always at least as fresh as storage, which a\n // failed write leaves behind.\n const chosen = pageChoices.get(dimension);\n if (chosen !== undefined) return chosen;\n try {\n return localStorage.getItem(STORAGE_PREFIX + dimension);\n } catch {\n // Storage can be blocked or unavailable.\n return null;\n }\n}\n\nfunction writeChoice(dimension: string, value: string): void {\n pageChoices.set(dimension, value);\n try {\n localStorage.setItem(STORAGE_PREFIX + dimension, value);\n } catch {\n // The choice lasts for the page only.\n }\n}\n\nfunction valuesOf(tab: Element): string[] {\n return (tab.getAttribute(\"value\") ?? \"\").split(/\\s+/).filter(Boolean);\n}\n\n/** Select `value` in every group on the page that switches on `dimension`, but `except`. */\nlet followChoice: (dimension: string, value: string, except?: AscribeTabs) => void;\n\n/**\n * Choose `value` for `dimension`, as a reader selecting a tab with that value\n * does: remember it across pages, and select it in every group on this page\n * that switches on the dimension. A group with no tab for it keeps its\n * selection. The value isn't checked.\n */\nexport function choose(dimension: string, value: string): void {\n writeChoice(dimension, value);\n followChoice(dimension, value);\n}\n\n/** A tab panel: one arm of a `@variant` group. Styled by CSS; no behavior. */\nexport class AscribeTab extends HTMLElement {}\n\nexport class AscribeTabs extends HTMLElement {\n #tablist: HTMLElement | null = null;\n #tabs: HTMLElement[] = [];\n #buttons: HTMLButtonElement[] = [];\n #selected = 0;\n #ready = false;\n\n static {\n followChoice = (dimension, value, except) => {\n for (const group of syncedGroups) {\n if (group !== except && group.getAttribute(\"sync\") === dimension) group.#follow(value);\n }\n };\n }\n\n connectedCallback(): void {\n // A parser-created element is connected before its children are parsed.\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", () => this.#setup(), { once: true });\n } else {\n this.#setup();\n }\n }\n\n disconnectedCallback(): void {\n this.#teardown();\n }\n\n #setup(): void {\n if (!this.isConnected || this.#ready) return;\n this.#tabs = Array.from(this.children).filter(\n (child): child is HTMLElement => child.localName === ELEMENT_TAB,\n );\n if (this.#tabs.length === 0) return;\n this.#ready = true;\n\n const tablist = document.createElement(\"div\");\n tablist.setAttribute(\"role\", \"tablist\");\n this.#tablist = tablist;\n\n this.#buttons = this.#tabs.map((tab) => {\n if (!tab.id) tab.id = `ascribe-tab-${++idCounter}`;\n const button = document.createElement(\"button\");\n button.type = \"button\";\n button.id = `${tab.id}-button`;\n button.setAttribute(\"role\", \"tab\");\n button.setAttribute(\"aria-controls\", tab.id);\n button.textContent = tab.getAttribute(\"label\") ?? \"\";\n button.addEventListener(\"click\", () => this.#choose(this.#buttons.indexOf(button)));\n button.addEventListener(\"keydown\", (event) => this.#onKeydown(event));\n tablist.append(button);\n tab.setAttribute(\"role\", \"tabpanel\");\n tab.setAttribute(\"aria-labelledby\", button.id);\n return button;\n });\n this.prepend(tablist);\n\n const sync = this.getAttribute(\"sync\");\n let initial = 0;\n if (sync !== null) {\n syncedGroups.add(this);\n const remembered = readChoice(sync);\n const match = remembered === null ? -1 : this.#indexOfValue(remembered);\n if (match >= 0) initial = match;\n }\n this.#select(initial);\n }\n\n #teardown(): void {\n if (!this.#ready) return;\n syncedGroups.delete(this);\n this.#tablist?.remove();\n for (const tab of this.#tabs) {\n tab.removeAttribute(\"role\");\n tab.removeAttribute(\"aria-labelledby\");\n tab.removeAttribute(\"hidden\");\n }\n this.#tablist = null;\n this.#tabs = [];\n this.#buttons = [];\n this.#ready = false;\n }\n\n #indexOfValue(value: string): number {\n return this.#tabs.findIndex((tab) => valuesOf(tab).includes(value));\n }\n\n /** Select a tab without recording or syncing the choice. */\n #select(index: number): void {\n this.#selected = index;\n this.#tabs.forEach((tab, i) => {\n tab.toggleAttribute(\"hidden\", i !== index);\n });\n this.#buttons.forEach((button, i) => {\n button.setAttribute(\"aria-selected\", String(i === index));\n button.tabIndex = i === index ? 0 : -1;\n });\n }\n\n /** The reader picked a tab: select it and, for a synced group, tell the rest. */\n #choose(index: number): void {\n this.#select(index);\n const sync = this.getAttribute(\"sync\");\n const tab = this.#tabs[index];\n const choice = tab ? valuesOf(tab)[0] : undefined;\n if (sync === null || choice === undefined) return;\n writeChoice(sync, choice);\n followChoice(sync, choice, this);\n }\n\n /** Another group with the same `sync` made a choice; keep our selection if we have no match. */\n #follow(value: string): void {\n const index = this.#indexOfValue(value);\n if (index >= 0) this.#select(index);\n }\n\n #onKeydown(event: KeyboardEvent): void {\n const last = this.#tabs.length - 1;\n let next: number;\n switch (event.key) {\n case \"ArrowRight\":\n next = this.#selected === last ? 0 : this.#selected + 1;\n break;\n case \"ArrowLeft\":\n next = this.#selected === 0 ? last : this.#selected - 1;\n break;\n case \"Home\":\n next = 0;\n break;\n case \"End\":\n next = last;\n break;\n default:\n return;\n }\n event.preventDefault();\n this.#choose(next);\n this.#buttons[next]?.focus();\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ascribed/elements",
3
- "version": "0.2.1-next.9",
3
+ "version": "0.3.0",
4
4
  "description": "Ascribe's web component library for the site output.",
5
5
  "type": "module",
6
6
  "license": "MPL-2.0",
@@ -30,13 +30,12 @@
30
30
  },
31
31
  "devDependencies": {
32
32
  "@types/node": "^24.0.0",
33
- "axe-core": "^4.10.0",
33
+ "axe-core": "^4.14.0",
34
34
  "playwright-core": "^1.50.0"
35
35
  },
36
36
  "publishConfig": {
37
37
  "access": "public"
38
38
  },
39
- "gitHead": "52da4c48b9cb985c8e97d926aafa4041fc47f2eb",
40
39
  "scripts": {
41
40
  "build": "tsc -p tsconfig.build.json",
42
41
  "typecheck": "tsc --noEmit",