@jungherz-de/glasskit-elements 1.19.1 → 1.20.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +40 -27
  3. package/SKILL.md +107 -25
  4. package/dist/components/base.js +145 -10
  5. package/dist/components/glk-accordion-item.js +20 -5
  6. package/dist/components/glk-button.js +69 -2
  7. package/dist/components/glk-calendar.js +7 -4
  8. package/dist/components/glk-checkbox.js +12 -2
  9. package/dist/components/glk-date-strip.js +5 -4
  10. package/dist/components/glk-input.js +25 -4
  11. package/dist/components/glk-modal.js +99 -21
  12. package/dist/components/glk-pill.js +14 -1
  13. package/dist/components/glk-popover.js +21 -3
  14. package/dist/components/glk-radio.js +26 -2
  15. package/dist/components/glk-range.js +3 -1
  16. package/dist/components/glk-search.js +14 -1
  17. package/dist/components/glk-segmented.js +4 -0
  18. package/dist/components/glk-select.js +8 -2
  19. package/dist/components/glk-textarea.js +8 -2
  20. package/dist/components/glk-toggle.js +18 -11
  21. package/dist/components/shared/{dates-CNWaJf09.js → dates-D4JTqoiG.js} +25 -4
  22. package/dist/glasskit-elements.esm.js +519 -75
  23. package/dist/glasskit-elements.js +519 -75
  24. package/dist/glasskit-elements.min.js +1 -1
  25. package/package.json +3 -3
  26. package/src/base.js +144 -9
  27. package/src/components/buttons/glk-button.js +69 -2
  28. package/src/components/containers/glk-accordion-item.js +20 -5
  29. package/src/components/feedback/glk-modal.js +99 -21
  30. package/src/components/feedback/glk-popover.js +21 -3
  31. package/src/components/forms/glk-calendar.js +6 -3
  32. package/src/components/forms/glk-checkbox.js +12 -2
  33. package/src/components/forms/glk-input.js +25 -4
  34. package/src/components/forms/glk-radio.js +26 -2
  35. package/src/components/forms/glk-range.js +3 -1
  36. package/src/components/forms/glk-search.js +14 -1
  37. package/src/components/forms/glk-segmented.js +4 -0
  38. package/src/components/forms/glk-select.js +8 -2
  39. package/src/components/forms/glk-textarea.js +8 -2
  40. package/src/components/forms/glk-toggle.js +18 -11
  41. package/src/components/navigation/glk-date-strip.js +4 -3
  42. package/src/components/navigation/glk-pill.js +14 -1
  43. package/src/dates.js +25 -4
package/CHANGELOG.md CHANGED
@@ -7,6 +7,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ---
9
9
 
10
+ ## [1.20.0] – 2026-10-05
11
+
12
+ Versions realign with GlassKit at 1.20.0, which this release needs (peer dependency `>=1.20.0`). The first batch of findings filed as GitHub issues, from a product that is adopting GlassKit at full depth — mostly accessibility and forms. Everything below was measured in Chromium and WebKit.
13
+
14
+ ### Fixed
15
+
16
+ - **Importing the elements no longer turns the page dark.** The token defaults went onto the document with GlassKit's `color-scheme: dark` included, so on a page that does not use GlassKit's theme the whole document switched: `color-scheme` of `:root` and of every native control to `dark`, unstyled text from black to white, links from `rgb(0, 0, 238)` to `rgb(158, 158, 255)`. The injected layer now carries the `--gl-*` tokens only. Each element sets `color-scheme` on its own theme wrapper, following `data-theme`, so native parts inside it — a select's list, a date picker — still match the element. Measured on a white page without GlassKit CSS: scheme, text and link colour stay as they were; the select inside `<glk-select>` is `dark`, under `data-theme="light"` `light`. **New:** `<html data-glk-defaults="off">` switches the defaults off, for a page that declares the tokens itself or wants them on part of the page only; the attribute is watched, so it may also be set or removed later. Until now the only way was the internal flag meant to detect duplicate bundles. Compatibility: a page that counted on the elements to make it dark sets `color-scheme` itself, or loads `glasskit.css`, whose token blocks set it. ([#1](https://github.com/JUNGHERZ/glasskit-elements/issues/1))
17
+ - **Form fields have accessible names, and focus reaches them.** The `<label>` of `<glk-input>`, `<glk-select>`, `<glk-textarea>` and `<glk-range>` sat next to the field without `for`, so the field had no name — with a placeholder, screen readers announced only that — and a click on the label text did nothing. Label and field are tied by id inside the shadow root now: `textbox "Vorname"`, `combobox "Land"`, `slider "Lautstärke"`. The `hint` of `<glk-input>` describes the field (`aria-describedby`), and `error` marks it `aria-invalid`. `<glk-search>`, which has no visible label, takes `label` as its accessible name. Form elements with one field open their shadow root with `delegatesFocus`, so `element.focus()`, a click on the label text and a click on a `<label for>` naming the element all land in the field. `<glk-segmented>` and `<glk-calendar>` keep it off: their first button is not the chosen one, and Tab already reaches the chosen one. An outer `<label for>` focuses the field but cannot name it across the shadow boundary — use `label` for the name. ([#2](https://github.com/JUNGHERZ/glasskit-elements/issues/2))
18
+ - **The form learns whether the fields are valid.** `GlkFormElement` had a `setValidity()` that nothing called, so an empty `required` field counted as valid: `form.checkValidity()` returned `true` and `requestSubmit()` sent the form. Every element built on one native field now copies that field's verdict onto the host after each change — value, attribute, reset — with the field as anchor, so the browser's bubble points at it and `reportValidity()` focuses it. `required`, `pattern`, `min`/`max`, `minlength`/`maxlength`, `type="email"` and the like stop a submit with the browser's own message; `validity`, `validationMessage`, `willValidate`, `checkValidity()` and `reportValidity()` answer on the element. `<glk-checkbox>`, `<glk-toggle>` and `<glk-radio>` gain `required`, radios by the native rule: one required radio makes its group required, any checked one satisfies it. `<glk-textarea>` and `<glk-select>` now also follow a `required` set after they rendered. A disabled field is barred from validation, as natively. `error` stays a marker and does not block — a server's verdict arrives that way, and it must not stop the next attempt. Measured: empty required fields make `checkValidity()` false, `requestSubmit()` sends nothing, `reportValidity()` focuses the first field; "nope" in a `type="email"` field is a `typeMismatch`; checking the second radio of a required group makes both valid; `form.reset()` validates again. ([#3](https://github.com/JUNGHERZ/glasskit-elements/issues/3))
19
+ - **`<glk-button type="submit">` submits its form, `type="reset"` resets it.** The native button lives in the shadow root, outside every form, so both types only emitted `glk-click`. The element is form-associated now and acts on its form once the click has finished bubbling — `preventDefault()` on the click stops it, as on a native button. Invalid fields stop the submit with the browser's message; the submit event names the element as its `submitter`; `form` and `type` are properties; `element.click()` acts like a click. Measured: one submit with complete `FormData` and the element as `submitter`, none after `preventDefault()`, one from `element.click()`; reset restores values and validity. Not covered: implicit submission — Enter in a `<glk-input>` does not submit the form. ([#4](https://github.com/JUNGHERZ/glasskit-elements/issues/4))
20
+ - **`<glk-modal>` is a native dialog.** It had no dialog role, the focus stayed behind it, Tab walked through the page before it reached the modal's buttons, and its content was reachable while it was closed. The overlay is a `<dialog>` now, opened with `showModal()`: top layer, the page behind it inert for pointer, keyboard and screen readers, focus moving in and back to the opener on closing, named by its title (`aria-labelledby`), the content out of reach while closed. The fade stays — Escape arrives as the dialog's `cancel`, and the element turns it into the same animated close as a click on the dimmed area; both emit `glk-close`. The browsers pick the first focus differently, Chrome the first field of the content, Safari the first action; `autofocus` on an element in the content or on an action settles it. Moved while open, it opens as a modal again. Measured: `:modal` matches, `dialog "Eintrag löschen?"`, Tab cycles inside, a point over the page hits the overlay, Escape emits `glk-close` once and returns the focus to the opener. Compatibility: everything outside an open modal is inert — a `<glk-toast>` shown while it is open is neither clickable nor announced (moved into the top layer as a popover, it stays inert too; measured), so give feedback inside the modal or close it first; a `<glk-sheet>` opened from an open modal ends up behind it. ([#5](https://github.com/JUNGHERZ/glasskit-elements/issues/5))
21
+ - **`<glk-toggle>` is one switch, and clicks aimed at the element work.** The host carried `role="switch"` and `aria-checked` with a focusable native checkbox inside, so screen readers met a switch that contained a checkbox. The role moved onto the checkbox; the host carries no `role`, `aria-checked` or `aria-disabled` any more, and the tree shows `switch "Benachrichtigungen"`, nothing else. `toggle.click()` changed nothing, where a native checkbox toggles — and a `<label>` around the element or naming it clicks the host, too. A click aimed at the host itself is now handed to the input and the original stopped, so listeners see one click and one change; the same holds for `<glk-checkbox>`, `<glk-radio>` and `<glk-button>`. A press on the host's empty width (it is a block, the control narrower) still does nothing: Chrome gives a label's click the same `detail` and `isTrusted` as a real one, so the two are told apart by where the pointer went down. With GlassKit 1.20.0 the input lies on top of the track, so Playwright's `getByRole('switch').click()`, `check()` and `uncheck()` reach it instead of timing out on the covering track — `getByRole('checkbox')` and `getByRole('radio')` gain the same. Compatibility: CSS or tests that select `glk-toggle[aria-checked]` or `[role="switch"]` on the host use `glk-toggle[checked]` now. ([#6](https://github.com/JUNGHERZ/glasskit-elements/issues/6))
22
+ - **Accordion and popover triggers say whether they are open.** The accordion trigger carries `aria-expanded` and `aria-controls` now, and a closed section is `inert`: before, it was only squeezed to zero height, so Tab still walked into its links and screen readers read them. `<glk-popover>` sets `aria-expanded` on its trigger, again whenever the trigger is swapped; `<glk-button>` and `<glk-pill>` hand `aria-expanded`, `aria-haspopup` and `aria-pressed` on to their native button. Measured: `button "Abschnitt 1" [expanded=false]`, the closed section's link takes no focus and is missing from Chromium's accessibility tree, `button "Menü" [expanded]` while the popover is open. ([#7](https://github.com/JUNGHERZ/glasskit-elements/issues/7))
23
+ - **A toggle without label is as large as its track.** The host measured 64 × 34 px around a 52 × 30 track: the empty label kept its 12 px gap (GlassKit 1.20.0 hides an empty label), and the inline control sat on the text baseline with room for descenders below it — in a centred row the track sat 2 px high. The control aligns to the top of its line now: 52 × 30, centred at 9 px in a 48 px row. `<glk-checkbox>` and `<glk-radio>` lose the same descender room. Compatibility: stacked toggles, checkboxes and radios move closer — on the docs page a labelled toggle shrinks from 37.6 to 30 px, a checkbox from 27.4 to 25.6 px. ([#8](https://github.com/JUNGHERZ/glasskit-elements/issues/8))
24
+
25
+ ### Changed
26
+
27
+ - **With GlassKit 1.20.0** the elements also get its fixes: `<glk-list-item>` without an icon no longer keeps 28 px for it — title at 20 px, row 56 instead of 60 px, divider from 20 px ([GlassKit #4](https://github.com/JUNGHERZ/GlassKit/issues/4)); the select's chevron is visible in the light theme ([GlassKit #1](https://github.com/JUNGHERZ/GlassKit/issues/1)); under `dir="rtl"` the elements mirror ([GlassKit #2](https://github.com/JUNGHERZ/GlassKit/issues/2)); `--gl-modal-max-width` widens `<glk-modal>` ([GlassKit #3](https://github.com/JUNGHERZ/GlassKit/issues/3)).
28
+
29
+ ### Documentation
30
+
31
+ - **Component count and browser support in the README.** The badge said 33 components, the intro 29, the docs pages 29 and 33; the package ships 36 elements, as SKILL.md and the landing pages already said — now all of them do. Browser support named Chrome 90 and Firefox 103, but GlassKit's tokens are derived with `color-mix()`: the floor is Chrome and Edge 111, Safari 16.4, Firefox 113, and what the elements need themselves — constructable stylesheets, `ElementInternals`, `delegatesFocus`, `<dialog>`, `inert` — falls inside it. The refinements that come later are named. ([#9](https://github.com/JUNGHERZ/glasskit-elements/issues/9))
32
+ - Docs (English and German), README and SKILL.md describe validity, labels and focus, submit buttons, the dialog and its effect on toasts, the switch role, the triggers' states and the opt-out from the token defaults.
33
+
34
+ ---
35
+
36
+ ## [1.19.2] – 2026-09-26
37
+
38
+ ### Fixed
39
+
40
+ - **`<glk-date-strip>` and `<glk-calendar>` name days in the page's language, not the browser's.** Without `locale` they went straight to `navigator.language`, so a German page opened on a phone set to English showed "SUN MON TUE" in the strip and started the calendar on Sunday — next to German text everywhere else. They now take the `lang` of the nearest ancestor, usually `<html lang>`, looked up across shadow roots through their hosts, so an element inside another component's shadow tree — a hybrids view, say — reaches the page's language, and a `<section lang="en">` in a German page stays English. Only without any `lang` does the browser language follow, then English; an empty `lang=""` means "unknown" in HTML and falls through to the browser. `locale` still wins over all of it. The page language decides weekday and month names, the chips' and days' `aria-label`s and the calendar's first day of the week. Measured in Chromium and WebKit with an `en-US` browser on `<html lang="de">`: "So Mo Di" and "Sonntag, 27. September 2026" instead of "Sun Mon Tue" and "Sunday, September 27, 2026", the calendar starts on Monday; a page without `lang` looks as before. Compatibility: a page whose `lang` differs from its visitors' browsers now shows its own language in these two elements — set `locale` to keep another. NotionKit Elements falls back to `<html lang>` since 1.17; this looks at the nearest ancestor instead. (EhrenPfoten, Elements finding 10.)
41
+
42
+ ---
43
+
10
44
  ## [1.19.1] – 2026-09-26
11
45
 
12
46
  ### Fixed
@@ -701,6 +735,8 @@ Starting with this release, GlassKit Elements version numbers are aligned with G
701
735
 
702
736
  ---
703
737
 
738
+ [1.20.0]: https://github.com/JUNGHERZ/GlassKit-Elements/releases/tag/v1.20.0
739
+ [1.19.2]: https://github.com/JUNGHERZ/GlassKit-Elements/releases/tag/v1.19.2
704
740
  [1.19.1]: https://github.com/JUNGHERZ/GlassKit-Elements/releases/tag/v1.19.1
705
741
  [1.19.0]: https://github.com/JUNGHERZ/GlassKit-Elements/releases/tag/v1.19.0
706
742
  [1.18.0]: https://github.com/JUNGHERZ/GlassKit-Elements/releases/tag/v1.18.0
package/README.md CHANGED
@@ -3,15 +3,15 @@
3
3
  <p align="center">
4
4
  <a href="https://www.npmjs.com/package/@jungherz-de/glasskit-elements"><img src="https://img.shields.io/npm/v/@jungherz-de/glasskit-elements?style=flat-square&color=f5a623&label=version" alt="Version"></a>
5
5
  <a href="#"><img src="https://img.shields.io/badge/vanilla_JS-no_dependencies-44cc11?style=flat-square" alt="Vanilla JS"></a>
6
- <a href="#"><img src="https://img.shields.io/badge/components-33-7ec8e3?style=flat-square" alt="33 Components"></a>
6
+ <a href="#"><img src="https://img.shields.io/badge/components-36-7ec8e3?style=flat-square" alt="36 Components"></a>
7
7
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue?style=flat-square" alt="MIT License"></a>
8
- <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/changelog-v1.19.1-lightgrey?style=flat-square" alt="Changelog"></a>
8
+ <a href="CHANGELOG.md"><img src="https://img.shields.io/badge/changelog-v1.20.0-lightgrey?style=flat-square" alt="Changelog"></a>
9
9
  <a href="https://www.npmjs.com/package/@jungherz-de/glasskit-elements"><img src="https://img.shields.io/badge/npm-%40jungherz--de%2Fglasskit--elements-cb3837?style=flat-square&logo=npm" alt="npm"></a>
10
10
  </p>
11
11
 
12
12
  <p align="center">
13
13
  <strong>Drop-in Web Components for <a href="https://github.com/JUNGHERZ/GlassKit">GlassKit CSS</a></strong><br>
14
- 29 vanilla JavaScript custom elements wrapping GlassKit's glassmorphism components.<br>
14
+ 36 vanilla JavaScript custom elements wrapping GlassKit's glassmorphism components.<br>
15
15
  Shadow DOM &middot; Native form participation &middot; Zero dependencies.
16
16
  </p>
17
17
 
@@ -26,7 +26,7 @@
26
26
 
27
27
  ## ✨ What is GlassKit Elements?
28
28
 
29
- GlassKit Elements is a companion library to [GlassKit CSS](https://github.com/JUNGHERZ/GlassKit). It provides **29 Web Components** (incl. the iOS 26-style floating Tab-Bar + Accessory) that encapsulate the verbose HTML markup required by GlassKit into simple, declarative custom elements.
29
+ GlassKit Elements is a companion library to [GlassKit CSS](https://github.com/JUNGHERZ/GlassKit). It provides **36 Web Components** (incl. the iOS 26-style floating Tab-Bar + Accessory) that encapsulate the verbose HTML markup required by GlassKit into simple, declarative custom elements.
30
30
 
31
31
  It is the **app layer** of the GlassKit family — three layers, one design language: [GlassKit](https://glasskit.jungherz.com) is the pure-CSS foundation, GlassKit Elements wraps it into web components for application UIs, and [GlassKit Web](https://glasskit-web.jungherz.com) is the official Astro template for complete websites on top of the same foundation.
32
32
 
@@ -51,10 +51,10 @@ It is the **app layer** of the GlassKit family — three layers, one design lang
51
51
  | Feature | Details |
52
52
  |---|---|
53
53
  | 🔌 **Shadow DOM** | Style encapsulation via `adoptedStyleSheets` — no CSS leaking |
54
- | 🧩 **33 Components** | Buttons, cards, toggles, modals, accordions, lists, popovers, tab bars (incl. floating + accessory), and more |
55
- | 🪶 **Lightweight** | 117 KB raw / 92 KB minified / 15 KB gzipped (IIFE bundle), no external dependencies |
54
+ | 🧩 **36 Components** | Buttons, cards, toggles, modals, accordions, lists, popovers, tab bars (incl. floating + accessory), and more |
55
+ | 🪶 **Lightweight** | 156 KB minified / 28 KB gzipped (IIFE bundle, GlassKit's component rules included), no external dependencies |
56
56
  | 📦 **Three bundle formats** | IIFE for `<script>`, minified IIFE for production, ESM for bundlers & tree-shaking |
57
- | 🎛️ **Form Participation** | Input, toggle, checkbox, radio, select — all work natively with `<form>` via `ElementInternals` |
57
+ | 🎛️ **Form Participation** | Input, toggle, checkbox, radio, select — all work natively with `<form>` via `ElementInternals`: value, reset and, since 1.20.0, validity; `<glk-button type="submit">` submits |
58
58
  | 🌗 **Theme Sync** | Automatic dark/light mode sync via `data-theme` on `<html>` |
59
59
  | 📱 **Mobile-first** | Inherits GlassKit's mobile-optimized design with `safe-area-inset` support |
60
60
 
@@ -66,7 +66,7 @@ It is the **app layer** of the GlassKit family — three layers, one design lang
66
66
 
67
67
  ```html
68
68
  <!-- 1. GlassKit CSS -->
69
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/glasskit@1.19/glasskit.min.css">
69
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/glasskit@1.20/glasskit.min.css">
70
70
 
71
71
  <!-- 2. GlassKit Elements -->
72
72
  <script src="https://cdn.jsdelivr.net/npm/@jungherz-de/glasskit-elements/dist/glasskit-elements.min.js"></script>
@@ -104,7 +104,7 @@ The per-component files leave `@jungherz-de/glasskit/glasskit-styles.js` as an e
104
104
  <!DOCTYPE html>
105
105
  <html data-theme="dark">
106
106
  <head>
107
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/glasskit@1.19/glasskit.min.css">
107
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@jungherz-de/glasskit@1.20/glasskit.min.css">
108
108
  <script src="https://cdn.jsdelivr.net/npm/@jungherz-de/glasskit-elements/dist/glasskit-elements.min.js"></script>
109
109
  </head>
110
110
  <body>
@@ -148,21 +148,21 @@ The per-component files leave `@jungherz-de/glasskit/glasskit-styles.js` as an e
148
148
 
149
149
  | Component | Description | Key Attributes |
150
150
  |---|---|---|
151
- | `<glk-button>` | Glass-styled button | `variant` (primary, secondary, tertiary), `size` (sm, md, lg, auto), `disabled`, `type` |
151
+ | `<glk-button>` | Glass-styled button; `type="submit"` / `"reset"` act on its form | `variant` (primary, secondary, tertiary), `size` (sm, md, lg, auto), `disabled`, `type` (button, submit, reset) |
152
152
 
153
153
  ### Form Elements
154
154
 
155
- All form components support `name`, `value`, `disabled` and participate in native `<form>` submission via `ElementInternals` — except `<glk-image-picker>`, which hands its result to `glk-change` instead: a data URL is no form value.
155
+ All form components support `name`, `value`, `disabled` and participate in native `<form>` submission via `ElementInternals` — except `<glk-image-picker>`, which hands its result to `glk-change` instead: a data URL is no form value. Since 1.20.0 they also report their validity to the form (`required`, `pattern`, `min`, `type="email"` … stop a submit with the browser's own message), their `label` names the field for screen readers, and a click on it, `element.focus()` or a `<label for>` lands in the field.
156
156
 
157
157
  | Component | Description | Key Attributes |
158
158
  |---|---|---|
159
159
  | `<glk-input>` | Text input with label & hint | `label`, `type`, `placeholder`, `hint`, `error`, `required`, `min`, `max`, `step`, `maxlength`, `pattern`, `autocomplete`, `inputmode` |
160
- | `<glk-textarea>` | Multi-line text input | `label`, `rows`, `placeholder` |
161
- | `<glk-select>` | Dropdown select | `label` (children: `<option>`) |
162
- | `<glk-search>` | Search input with icon | `placeholder` |
163
- | `<glk-toggle>` | Switch toggle | `label`, `checked`, `disabled` |
164
- | `<glk-checkbox>` | Checkbox | `label`, `checked`, `disabled` |
165
- | `<glk-radio>` | Radio button | `label`, `name`, `value`, `checked` |
160
+ | `<glk-textarea>` | Multi-line text input | `label`, `rows`, `placeholder`, `required` |
161
+ | `<glk-select>` | Dropdown select | `label`, `required` (children: `<option>`) |
162
+ | `<glk-search>` | Search input with icon | `placeholder`, `label` (accessible name) |
163
+ | `<glk-toggle>` | Switch toggle (`role="switch"` on its native checkbox) | `label`, `checked`, `disabled`, `required` |
164
+ | `<glk-checkbox>` | Checkbox | `label`, `checked`, `disabled`, `required` |
165
+ | `<glk-radio>` | Radio button | `label`, `name`, `value`, `checked`, `required` (per group) |
166
166
  | `<glk-range>` | Range slider | `label`, `min`, `max`, `value`, `step` |
167
167
  | `<glk-segmented>` | Small exclusive choice as one control; form-associated | `options` (JSON), `value`, `full`, `overflow` (scroll, wrap), `label`, `name` |
168
168
  | `<glk-calendar>` | One month with a day to pick; keyboard navigation, `Intl` names; form-associated | `month`, `value`, `today`, `min`, `max`, `marks`, `locale`, `week-start`, `label`, `name` |
@@ -173,7 +173,7 @@ All form components support `name`, `value`, `disabled` and participate in nativ
173
173
  | Component | Description | Key Attributes |
174
174
  |---|---|---|
175
175
  | `<glk-progress>` | Progress bar | `value`, `label`, `variant` (success, error), `size` (sm, lg) |
176
- | `<glk-modal>` | Modal dialog | `open`, `title` |
176
+ | `<glk-modal>` | Modal dialog — a native `<dialog>`: page behind inert, focus in and back | `open`, `title` |
177
177
  | `<glk-toast>` | Notification that hides itself, or an offer with one action and an × | `message`, `variant` (success, error, warning), `duration`, `action-label`, `action-value`, `dismissible` |
178
178
  | `<glk-popover>` | Anchored dropdown / menu | `open`, `placement` (top, bottom, start, end) |
179
179
  | `<glk-sheet>` | Bottom sheet, the mobile sibling of the modal | `open`, `inline`, `title` |
@@ -239,6 +239,18 @@ inherits them — no JavaScript, no per-element setup:
239
239
  Token defaults are placed on the document once, inside `@layer glasskit-defaults`, so
240
240
  pages that never load `glasskit.css` still work and your brand file always wins.
241
241
 
242
+ Since 1.20.0 that layer carries the `--gl-*` tokens only — GlassKit's `color-scheme` stays
243
+ off the page. Each element sets it on its own theme wrapper, so a select's list or a date
244
+ picker inside matches the element while the page keeps its scheme. (Before, importing the
245
+ elements turned the whole page to `color-scheme: dark`: unstyled text white, links light
246
+ blue, native controls dark.) The token names are still global, so a component of yours
247
+ that reads `var(--gl-…, fallback)` gets GlassKit's value. A page that declares the tokens
248
+ itself, or wants them on part of the page only, switches the defaults off:
249
+
250
+ ```html
251
+ <html data-glk-defaults="off"> <!-- watched: may be set or removed later -->
252
+ ```
253
+
242
254
  ---
243
255
 
244
256
  ## 🛠️ Architecture
@@ -247,7 +259,8 @@ pages that never load `glasskit.css` still work and your brand file always wins.
247
259
  - **Theme wrapper** with `display: contents` — layout-transparent `<div>` for `data-theme` CSS selectors
248
260
  - **Global `MutationObserver`** — single observer watches `data-theme` changes and syncs all instances
249
261
  - **`GlkElement`** base class — handles Shadow DOM setup, theme sync, attribute reflection
250
- - **`GlkFormElement`** extends `GlkElement` — adds `ElementInternals` for native form participation
262
+ - **`GlkFormElement`** extends `GlkElement` — adds `ElementInternals` for native form participation, delegates focus to its field and reports the field's validity
263
+ - **Native semantics** — labels tied to their fields, the toggle's `role="switch"` on its native checkbox, `<glk-modal>` a native `<dialog>`, `aria-expanded` on accordion and popover triggers
251
264
 
252
265
  ### Build your own element
253
266
 
@@ -274,7 +287,7 @@ class DemoCounter extends GlkElement {
274
287
  customElements.define('demo-counter', DemoCounter);
275
288
  ```
276
289
 
277
- Hooks: `render()` builds into `this._wrapper`, `setupEvents()` / `teardownEvents()` run on every connect / disconnect, `onAttributeChanged(name, old, value)` after the first render. `static get displayInline()` → `true` for inline elements; `static get observesLightDom()` → `true` re-runs `projectLightDom()` whenever light-DOM children change. `GlkFormElement` adds `setFormValue()`, `setValidity()`, `resetValue()` / `restoreValue()`.
290
+ Hooks: `render()` builds into `this._wrapper`, `setupEvents()` / `teardownEvents()` run on every connect / disconnect, `onAttributeChanged(name, old, value)` after the first render. `static get displayInline()` → `true` for inline elements; `static get observesLightDom()` → `true` re-runs `projectLightDom()` whenever light-DOM children change. `GlkFormElement` adds `setFormValue()`, `setValidity()`, `resetValue()` / `restoreValue()`; since 1.20.0 it delegates focus (`static get delegatesFocus()`) and, when `get _validityField()` returns your native field, reports that field's validity on the host after every `setFormValue()` and attribute change.
278
291
 
279
292
  ---
280
293
 
@@ -293,9 +306,9 @@ glasskit-elements/
293
306
  feedback/ # glk-progress, glk-modal, glk-toast, glk-popover
294
307
  containers/ # glk-accordion, glk-accordion-item, glk-list, glk-list-item
295
308
  dist/
296
- glasskit-elements.js # IIFE bundle (117 KB raw / 18 KB gzipped)
297
- glasskit-elements.min.js # IIFE minified (92 KB raw / 15 KB gzipped)
298
- glasskit-elements.esm.js # ES module bundle (112 KB raw / 17 KB gzipped)
309
+ glasskit-elements.js # IIFE bundle (229 KB raw / 46 KB gzipped)
310
+ glasskit-elements.min.js # IIFE minified (156 KB raw / 28 KB gzipped)
311
+ glasskit-elements.esm.js # ES module bundle (221 KB raw / 45 KB gzipped)
299
312
  components/ # one ESM file per element + base.js (GlkElement, GlkFormElement)
300
313
  index.html # Landing page
301
314
  docs.html # Documentation
@@ -309,12 +322,12 @@ glasskit-elements/
309
322
 
310
323
  | Browser | Support |
311
324
  |---|---|
312
- | Chrome | 90+ |
313
- | Edge | 90+ |
325
+ | Chrome | 111+ |
326
+ | Edge | 111+ |
314
327
  | Safari | 16.4+ |
315
- | Firefox | 103+ |
328
+ | Firefox | 113+ |
316
329
 
317
- Requires `adoptedStyleSheets`, `ElementInternals`, and `customElements` v1.
330
+ The floor is GlassKit's: its tokens are derived with `color-mix()`. The elements also need constructable stylesheets, `ElementInternals`, `delegatesFocus`, `<dialog>` and `inert` — all inside that floor. A few refinements come later and fall back quietly: first-line alignment of toggle, checkbox and radio (`lh`, Firefox 120), the divider of list rows without icon (`:has()`, Firefox 121), right-to-left mirroring of select chevron, toggle thumb and progress fill (`:dir()`, Chrome 120).
318
331
 
319
332
  ---
320
333
 
package/SKILL.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: glasskit-elements
3
- description: GlassKit Elements is a vanilla-JS Web Components library (v1.19.1) wrapping GlassKit CSS v1.19.1. It provides 36 custom elements with the `glk-` prefix, Dark/Light mode with automatic theme sync, Shadow DOM encapsulation, and form-associated custom elements. Use this reference whenever generating HTML that uses `<glk-*>` tags to ensure correct attributes, slots, events, and composition.
3
+ description: GlassKit Elements is a vanilla-JS Web Components library (v1.20.0) wrapping GlassKit CSS v1.20.0. It provides 36 custom elements with the `glk-` prefix, Dark/Light mode with automatic theme sync, Shadow DOM encapsulation, and form-associated custom elements. Use this reference whenever generating HTML that uses `<glk-*>` tags to ensure correct attributes, slots, events, and composition.
4
4
  ---
5
5
 
6
6
  # GlassKit Elements – AI Component Reference
@@ -18,7 +18,7 @@ description: GlassKit Elements is a vanilla-JS Web Components library (v1.19.1)
18
18
  npm install @jungherz-de/glasskit-elements @jungherz-de/glasskit
19
19
  ```
20
20
 
21
- Peer dependency `@jungherz-de/glasskit >=1.19.1` is required — 1.9.0 is the release that made the stylesheet splittable, which is what lets document-level branding reach the elements at all.
21
+ Peer dependency `@jungherz-de/glasskit >=1.20.0` is required — 1.20.0 carries the rules the elements now build on (the dialog overlay, hidden list slots, empty control labels, control inputs on top); 1.9.0 is the release that made the stylesheet splittable, which is what lets document-level branding reach the elements at all.
22
22
 
23
23
  ### Import (ES modules)
24
24
 
@@ -37,8 +37,9 @@ import { GlkButton } from '@jungherz-de/glasskit-elements/components/glk-button.
37
37
  ```
38
38
 
39
39
  Every element has its own entry at `components/glk-{name}.js`; they import `./base.js` and leave `@jungherz-de/glasskit/glasskit-styles.js` external (bundler or import map resolves it). Importing one pulls that
40
- file plus a single shared chunk (`base.js` + the GlassKit stylesheet, ~48 KB) rather than
41
- the 112 KB full bundle; several imports share that chunk. Each module registers its
40
+ file plus `base.js` (~16 KB, shared by all of them) and GlassKit's `glasskit-styles.js`
41
+ (~70 KB, resolved once by your project) rather than the 221 KB full bundle; several imports
42
+ share both. Each module registers its
42
43
  custom element as a side effect, so a bare `import '…'` is enough.
43
44
 
44
45
  ### CDN
@@ -99,7 +100,8 @@ A single module-level `MutationObserver` watches `data-theme` on `<html>` and sy
99
100
  | Stylesheet sharing | GlassKit's `componentsSheet` is the same `CSSStyleSheet` object in every element — no CSS duplication. Token declarations live on the document, not in the shadow roots |
100
101
  | Theme sync | Global MutationObserver on `<html data-theme>` |
101
102
  | Events | Custom `glk-*` events, all `bubbles: true, composed: true` |
102
- | Form participation | `GlkFormElement` uses `ElementInternals` (`static formAssociated = true`) |
103
+ | Form participation | `GlkFormElement` uses `ElementInternals` (`static formAssociated = true`): value, reset and — since 1.20.0 — the inner field's validity |
104
+ | Focus | Form elements with one field delegate focus (since 1.20.0): `element.focus()`, a click on the label, a `<label for>` land in the field |
103
105
  | API style | Declarative HTML attributes + reflected JS properties |
104
106
 
105
107
  Custom properties (`--gl-*`) defined on `:root` or `<html>` pass through shadow boundaries by inheritance, so custom theming works with a single global stylesheet.
@@ -185,6 +187,23 @@ this module puts them on the document once, wrapped in `@layer glasskit-defaults
185
187
  If your app assigns `document.adoptedStyleSheets = [...]` wholesale at some later point,
186
188
  re-append the existing entries rather than replacing them, or the defaults are lost.
187
189
 
190
+ **Only the tokens, not `color-scheme` (since 1.20.0).** GlassKit's token blocks also set
191
+ `color-scheme`; on the document that switched the whole page to dark — unstyled text white,
192
+ links light blue, native controls dark — on pages that never asked for GlassKit's theme. The
193
+ layer now carries the `--gl-*` tokens only, and every element sets `color-scheme` on its own
194
+ theme wrapper, so native parts inside it (a select's list, a date picker) match the element.
195
+
196
+ **Opting out.** The token names are global, so a component of the page that reads
197
+ `var(--gl-…, fallback)` sees GlassKit's values. A page that declares the tokens itself, or
198
+ wants them in part of the page only, switches the defaults off — the attribute is watched:
199
+
200
+ ```html
201
+ <html data-glk-defaults="off">
202
+ ```
203
+
204
+ The elements then inherit the tokens from `glasskit.css` or from a block of the page's own,
205
+ for instance on a container around them.
206
+
188
207
  ### Passing icons (fixed in 1.10.0)
189
208
 
190
209
  Pass an icon as a **direct child** — the `<svg>` itself, not wrapped:
@@ -277,6 +296,9 @@ Default slot: icon SVG (24×24).
277
296
 
278
297
  Events: `glk-click` when clicked.
279
298
 
299
+ `aria-expanded`, `aria-haspopup` and `aria-pressed` set on the element are handed on to the
300
+ native button inside (since 1.20.0) — `<glk-popover>` sets `aria-expanded` on its trigger.
301
+
280
302
  ---
281
303
 
282
304
  ### 3.3 `<glk-tab-bar>` + `<glk-tab-item>`
@@ -521,10 +543,21 @@ Full-width glass button (56 px). Three variants, four sizes.
521
543
  | `variant` | String | — | `primary`, `secondary`, `tertiary` |
522
544
  | `size` | String | `md` | `sm`, `md`, `lg`, `auto` |
523
545
  | `disabled` | Boolean | `false` | Disabled state |
524
- | `type` | String | `button` | Native button type |
546
+ | `type` | String | `button` | `button`, `submit`, `reset` — submit and reset act on the element's form (since 1.20.0) |
525
547
 
526
548
  Events: `glk-click` (suppressed when disabled).
527
549
 
550
+ **Submitting (since 1.20.0).** The element is form-associated: `type="submit"` submits the
551
+ form it sits in and `type="reset"` resets it. Before 1.20.0 both did nothing — the native
552
+ button lives in the shadow root, outside every form. It acts once the click has finished
553
+ bubbling, so `preventDefault()` on the click stops it, as on a native button; invalid fields
554
+ stop the submit with the browser's message; the submit event's `submitter` is the element;
555
+ `.form` is its form; `element.click()` acts like a click. No implicit submission: Enter in a
556
+ `<glk-input>` does not submit the form.
557
+
558
+ `aria-expanded`, `aria-haspopup` and `aria-pressed` set on the element are handed on to the
559
+ native button inside, which is what screen readers read (since 1.20.0).
560
+
528
561
  Default slot: button label (text or SVG + text).
529
562
 
530
563
  ---
@@ -545,9 +578,14 @@ Form-associated checkbox with glass styling.
545
578
  | `disabled` | Boolean | Disabled state |
546
579
  | `name` | String | Form field name |
547
580
  | `value` | String | Value submitted when checked (default `"on"`) |
581
+ | `required` | Boolean | The form does not submit until it is ticked (since 1.20.0) |
548
582
 
549
583
  Events: `glk-change` → `{ checked }`; also dispatches native `change`. Property: `.checked`.
550
584
 
585
+ `element.click()` and a `<label>` around the element or naming it tick it, as they do a
586
+ native checkbox (since 1.20.0). A press on the element's empty width (it is a block, the
587
+ control narrower) still does nothing.
588
+
551
589
  ---
552
590
 
553
591
  ### 3.12 `<glk-input>`
@@ -565,10 +603,10 @@ Text input with label + hint.
565
603
  | `label` | String | Label above the input |
566
604
  | `type` | String | Native input type (`text`, `email`, `password`, `number`, …) |
567
605
  | `placeholder` | String | Placeholder text |
568
- | `hint` | String | Helper text below the input |
569
- | `error` | Boolean | Error styling (red border + red hint) |
606
+ | `hint` | String | Helper text below the input, read as the field's description (`aria-describedby`) |
607
+ | `error` | Boolean | Error styling (red border + red hint) and `aria-invalid` on the field — it does not block the form |
570
608
  | `disabled` | Boolean | Disabled state |
571
- | `required` | Boolean | Required field |
609
+ | `required` | Boolean | Required field — the form does not submit while it is empty (since 1.20.0) |
572
610
  | `name` | String | Form field name |
573
611
  | `value` | String | Current value |
574
612
  | `min`, `max`, `step` | String | Passed to the inner field — range and step of the picker for dates, times and numbers (since 1.17.0) |
@@ -577,7 +615,18 @@ Text input with label + hint.
577
615
 
578
616
  Events: `glk-input` → `{ value }`, `glk-change` → `{ value }`. Native `input` / `change` also dispatched. Property: `.value`.
579
617
 
580
- The inner field's validity is not reported to the surrounding form: `required`, `pattern` and `min` constrain the field and its picker, but they do not block a submit — validate before sending.
618
+ **Validity (since 1.20.0).** The element reports its inner field's validity to the form:
619
+ `required`, `pattern`, `min`/`max`, `minlength`, `type="email"` … make `form.checkValidity()`
620
+ false and stop a submit at the first invalid field with the browser's own message;
621
+ `reportValidity()` focuses the field. `element.validity`, `.validationMessage`,
622
+ `.willValidate`, `.checkValidity()` and `.reportValidity()` answer on the element. Before
623
+ 1.20.0 none of this reached the form. `error` stays a marker: a server's verdict arrives that
624
+ way, and it must not block the next attempt.
625
+
626
+ **Label and focus (since 1.20.0).** The `label` is tied to the field: it names it for screen
627
+ readers, and a click on it focuses it — as do `element.focus()` and a `<label for>` naming the
628
+ element. An outer `<label for>` focuses, but cannot name, the field inside the shadow root;
629
+ use `label` for the name.
581
630
 
582
631
  ---
583
632
 
@@ -598,6 +647,7 @@ Radio button. Group multiple with the same `name`.
598
647
  | `value` | String | Value submitted when checked |
599
648
  | `checked` | Boolean | Checked state |
600
649
  | `disabled` | Boolean | Disabled state |
650
+ | `required` | Boolean | On any radio of the group: the group is required, any checked radio satisfies it (since 1.20.0) |
601
651
 
602
652
  Events: `glk-change` → `{ checked, value }`.
603
653
 
@@ -660,6 +710,7 @@ Search input with leading icon.
660
710
  | Attribute | Type | Description |
661
711
  |---|---|---|
662
712
  | `placeholder` | String | Placeholder text |
713
+ | `label` | String | Accessible name of the field — it has no visible label (since 1.20.0) |
663
714
  | `value` | String | Current value |
664
715
  | `name` | String | Form field name |
665
716
  | `disabled` | Boolean | Disabled state |
@@ -683,10 +734,11 @@ Dropdown select. Pass native `<option>` elements as children.
683
734
 
684
735
  | Attribute | Type | Description |
685
736
  |---|---|---|
686
- | `label` | String | Label text |
737
+ | `label` | String | Label text, tied to the field |
687
738
  | `name` | String | Form field name |
688
739
  | `value` | String | Selected value |
689
740
  | `disabled` | Boolean | Disabled state |
741
+ | `required` | Boolean | With an empty first option (`value=""`), the form does not submit until a choice is made (since 1.20.0) |
690
742
 
691
743
  Children: native `<option>` elements, read from the light DOM.
692
744
 
@@ -755,8 +807,17 @@ Switch-style toggle (form-associated).
755
807
  | `disabled` | Boolean | Disabled state |
756
808
  | `name` | String | Form field name |
757
809
  | `value` | String | Submitted value when checked (default `"on"`) |
810
+ | `required` | Boolean | Must be on before the form submits (since 1.20.0) |
811
+
812
+ Events: `glk-change` → `{ checked }`. Property: `.checked`.
758
813
 
759
- Events: `glk-change` → `{ checked }`. Property: `.checked`. Has `role="switch"` and syncs `aria-checked`.
814
+ **One control (since 1.20.0).** The switch is the native checkbox inside, with
815
+ `role="switch"`, named by `label`. The element itself no longer carries `role`,
816
+ `aria-checked` or `aria-disabled` — before 1.20.0 screen readers met a switch with a checkbox
817
+ inside. Select and style on `[checked]` / `[disabled]`; in tests,
818
+ `getByRole('switch', { name })` finds the input and `.click()`, `.check()` work on it.
819
+ `element.click()` and a `<label>` around the element or naming it toggle it, like a native
820
+ checkbox. Without `label` the element is exactly its track, 52 × 30 px (it was 64 × 34).
760
821
 
761
822
  ---
762
823
 
@@ -783,7 +844,7 @@ Modal dialog with overlay, title, body, and footer actions.
783
844
  | Attribute | Type | Description |
784
845
  |---|---|---|
785
846
  | `open` | Boolean | Visibility state (reflected as `.is-active` on the overlay) |
786
- | `title` | String | Header title |
847
+ | `title` | String | Header title; names the dialog |
787
848
 
788
849
  Slots:
789
850
  - default — modal body content
@@ -791,7 +852,17 @@ Slots:
791
852
 
792
853
  Methods: `.show()`, `.close()`. Property: `.open`. Events: `glk-close`.
793
854
 
794
- Closes automatically on overlay click and <kbd>Escape</kbd>.
855
+ Closes automatically on overlay click and <kbd>Escape</kbd>, with its fade, and emits
856
+ `glk-close` then; `.close()` and removing `open` do not emit.
857
+
858
+ **A native `<dialog>` (since 1.20.0).** The overlay is opened with `showModal()`: it lies in
859
+ the top layer, the page behind it is inert for pointer, keyboard and screen readers, focus
860
+ moves in and returns to where it came from on close, and the content is out of reach while
861
+ closed. Chrome focuses the first field of the content, Safari the first action — put
862
+ `autofocus` on the element that should get the focus, in the content or on an action.
863
+
864
+ While it is open, **everything outside is inert** — a `<glk-toast>` shown then is neither
865
+ clickable nor announced. Give feedback inside the modal, or close it first.
795
866
 
796
867
  ---
797
868
 
@@ -822,6 +893,9 @@ Slots:
822
893
  Methods: `.show()`, `.close()`, `.toggle()`. Property: `.open`.
823
894
  Events: `glk-open`, `glk-close`.
824
895
 
896
+ The trigger gets `aria-expanded`, kept in step with `open` and set again when the trigger is
897
+ swapped (since 1.20.0); `<glk-button>` and `<glk-pill>` hand it on to their native button.
898
+
825
899
  **Naming warning:** the toggle method is deliberately called `.toggle()`, **not** `.togglePopover()` — the latter collides with the native `HTMLElement.togglePopover()` API from the HTML Popover specification.
826
900
 
827
901
  ---
@@ -908,6 +982,10 @@ Collapsible sections.
908
982
 
909
983
  Default slot: body content. Events: `glk-toggle` → `{ open }`. Property: `.open`.
910
984
 
985
+ The trigger carries `aria-expanded` and `aria-controls`; a closed section is `inert`, so its
986
+ links and fields are out of the tab order and the accessibility tree until it opens (since
987
+ 1.20.0 — before, they were only squeezed to zero height and Tab still reached them).
988
+
911
989
  ---
912
990
 
913
991
  ### 3.24 `<glk-list>` + `<glk-list-item>`
@@ -1097,7 +1175,7 @@ A row of day chips that scrolls sideways (since 1.16.0) — a booking horizon, t
1097
1175
  | `value` | String | Chosen day `YYYY-MM-DD` |
1098
1176
  | `today` | String | The underlined day; the browser's day when missing |
1099
1177
  | `marks` | JSON | `{ date: tone \| { tone, disabled } }` — tone `primary` / `success` / `warning` / `error`; `disabled` makes the chip unpickable |
1100
- | `locale` | String | BCP 47 tag for the names; the browser language when missing |
1178
+ | `locale` | String | BCP 47 tag for the names; without it the page language — the `lang` of the nearest ancestor, usually `<html lang>`, also across shadow roots — else the browser language (page language since 1.19.2) |
1101
1179
  | `label` | String | `aria-label` of the group |
1102
1180
 
1103
1181
  Event: `glk-change { value }` — only on a change made by the user. Properties: `value`, `marks` (object or JSON text), `locale`, `label`. Part: `strip`.
@@ -1120,7 +1198,7 @@ One month with a day to pick (since 1.16.0). The days are buttons in a `role="gr
1120
1198
  | `today` | String | The day with the warm border; the browser's day when missing |
1121
1199
  | `min`, `max` | String | Days outside cannot be picked |
1122
1200
  | `marks` | JSON | `{ date: tone \| [tone, …] }` — up to three tones per day |
1123
- | `locale` | String | BCP 47 tag for names and week start; the browser language when missing |
1201
+ | `locale` | String | BCP 47 tag for names and week start; without it the page language — the `lang` of the nearest ancestor, usually `<html lang>`, also across shadow roots — else the browser language (page language since 1.19.2) |
1124
1202
  | `week-start` | Number | 0 = Sunday … 6 = Saturday; from the locale when missing |
1125
1203
  | `label` | String | `aria-label` of the day group |
1126
1204
  | `prev-label`, `next-label` | String | Names of the nav buttons ("Previous month" / "Next month") |
@@ -1216,8 +1294,9 @@ Events: `glk-change { dataUrl, width, height, size }` after a pick, on remove wi
1216
1294
  <form id="contract-form">
1217
1295
  <glk-title>New Contract</glk-title>
1218
1296
 
1297
+ <!-- required stops the submit with the browser's message (since 1.20.0) -->
1219
1298
  <glk-input label="Contract Name" name="name" required></glk-input>
1220
- <glk-select label="Category" name="category">
1299
+ <glk-select label="Category" name="category" required>
1221
1300
  <option value="">Please select…</option>
1222
1301
  <option value="insurance">Insurance</option>
1223
1302
  <option value="rental">Rental</option>
@@ -1376,6 +1455,9 @@ All `glk-*` events bubble and are `composed: true`, so they pierce shadow bounda
1376
1455
  | `<glk-tab-bar>` without `.glass-bg--has-tab-bar` on the outer background | Tab bar covers bottom content. Add the modifier to the outer `.glass-bg` container. |
1377
1456
  | `<glk-tab-bar floating>` without `<glk-tab-dock>` wrapper | Floating bar relies on the dock for fixed centered positioning. Always wrap it in `<glk-tab-dock>`. |
1378
1457
  | `<glk-tab-dock>` without `.glass-bg--has-tab-bar-floating` on the outer background | Use `--has-tab-bar-floating` (not `--has-tab-bar`) for the floating variant — the padding budget differs. |
1458
+ | `<glk-toast>` shown while a `<glk-modal>` is open | Everything outside the open modal is inert — the toast is neither clickable nor announced. Show the message inside the modal, or close it first. |
1459
+ | `glk-toggle[aria-checked="true"]` / `[role="switch"]` on the host | Since 1.20.0 the host carries neither; use `glk-toggle[checked]`. The role sits on the native checkbox inside. |
1460
+ | Validating `<glk-input required>` by hand before submit | Not needed since 1.20.0 — the form's own validation stops the submit. |
1379
1461
 
1380
1462
  ---
1381
1463
 
@@ -1403,14 +1485,14 @@ All `glk-*` events bubble and are `composed: true`, so they pierce shadow bounda
1403
1485
  | `<glk-status>` | Content | `message` | — | — |
1404
1486
  | `<glk-title>` | Content | — | default | — |
1405
1487
  | `<glk-button>` | Buttons | `variant`, `size`, `disabled`, `type` | default | `glk-click` |
1406
- | `<glk-checkbox>` | Forms | `label`, `checked`, `disabled`, `name`, `value` | — | `glk-change` |
1488
+ | `<glk-checkbox>` | Forms | `label`, `checked`, `disabled`, `name`, `value`, `required` | — | `glk-change` |
1407
1489
  | `<glk-input>` | Forms | `label`, `type`, `placeholder`, `hint`, `error`, `disabled`, `required`, `name`, `value`, `min`, `max`, `step`, `minlength`, `maxlength`, `pattern`, `autocomplete`, `inputmode` | — | `glk-input`, `glk-change` |
1408
- | `<glk-radio>` | Forms | `label`, `name`, `value`, `checked`, `disabled` | — | `glk-change` |
1490
+ | `<glk-radio>` | Forms | `label`, `name`, `value`, `checked`, `disabled`, `required` | — | `glk-change` |
1409
1491
  | `<glk-range>` | Forms | `label`, `min`, `max`, `value`, `step`, `name`, `disabled` | — | `glk-input`, `glk-change` |
1410
- | `<glk-search>` | Forms | `placeholder`, `value`, `name`, `disabled` | — | `glk-input`, `glk-change` |
1411
- | `<glk-select>` | Forms | `label`, `name`, `value`, `disabled` | default (`<option>`) | `glk-change` |
1492
+ | `<glk-search>` | Forms | `placeholder`, `label`, `value`, `name`, `disabled` | — | `glk-input`, `glk-change` |
1493
+ | `<glk-select>` | Forms | `label`, `name`, `value`, `disabled`, `required` | default (`<option>`) | `glk-change` |
1412
1494
  | `<glk-textarea>` | Forms | `label`, `rows`, `placeholder`, `name`, `value`, `disabled`, `required` | — | `glk-input`, `glk-change` |
1413
- | `<glk-toggle>` | Forms | `label`, `checked`, `disabled`, `name`, `value` | — | `glk-change` |
1495
+ | `<glk-toggle>` | Forms | `label`, `checked`, `disabled`, `name`, `value`, `required` | — | `glk-change` |
1414
1496
  | `<glk-modal>` | Feedback | `open`, `title` | default, `actions` | `glk-close` |
1415
1497
  | `<glk-popover>` | Feedback | `open`, `placement` | `trigger`, default | `glk-open`, `glk-close` |
1416
1498
  | `<glk-progress>` | Feedback | `value`, `label`, `variant`, `size` | — | — |
@@ -1538,18 +1620,18 @@ See the class-based [GlassKit CSS `SKILL.md`](https://github.com/JUNGHERZ/GlassK
1538
1620
  |---|---|
1539
1621
  | Base class | `src/base.js` → `GlkElement`, `GlkFormElement` |
1540
1622
  | Theme sync | Single module-level `MutationObserver` in `base.js` |
1541
- | Adopted stylesheets | `glassSheet` (from `@jungherz-de/glasskit/glasskit-styles.js`) + module-level `hostSheet` / `inlineHostSheet` |
1623
+ | Adopted stylesheets | `componentsSheet` (from `@jungherz-de/glasskit/glasskit-styles.js`) + module-level `hostSheet` / `inlineHostSheet` (they also set `color-scheme` per theme) + an element's own `hostStyles` |
1542
1624
  | Per-component structure | One `.js` file per element in `src/components/{category}/glk-{name}.js` |
1543
1625
  | Barrel | `src/index.js` — exports and registers all 36 elements, and exports `GlkElement` / `GlkFormElement` (since 1.14.0) |
1544
1626
  | Build | Rollup → IIFE (`glasskit-elements.js`), minified IIFE, ESM (`glasskit-elements.esm.js`), and per-element ESM in `dist/components/` with `base.js` as a stable entry |
1545
1627
 
1546
1628
  Each element is a subclass of `GlkElement` (or `GlkFormElement` for form controls) and follows a consistent lifecycle:
1547
1629
 
1548
- 1. `constructor()` — attach open shadow, adopt GlassKit stylesheet + host display sheet
1630
+ 1. `constructor()` — attach open shadow (`delegatesFocus` for form elements with one field), adopt GlassKit stylesheet + host display sheet
1549
1631
  2. `connectedCallback()` — create theme wrapper, call `render()`, call `setupEvents()`, register for global theme sync
1550
1632
  3. `render()` — build shadow DOM tree (override per component)
1551
1633
  4. `setupEvents()` — attach DOM listeners (override per component)
1552
1634
  5. `attributeChangedCallback()` — call `onAttributeChanged()` (override per component)
1553
1635
  6. `disconnectedCallback()` — unregister, call `teardownEvents()`
1554
1636
 
1555
- The `GlkFormElement` subclass adds `ElementInternals` for native form participation via `this.setFormValue(value)`, plus `formResetCallback` → `resetValue()` and `formStateRestoreCallback` → `restoreValue()`.
1637
+ The `GlkFormElement` subclass adds `ElementInternals` for native form participation via `this.setFormValue(value)`, plus `formResetCallback` → `resetValue()` and `formStateRestoreCallback` → `restoreValue()`. Since 1.20.0 it mirrors the validity of the native field returned by `get _validityField()` onto the host (`syncValidity()`, run after every `setFormValue()` and attribute change); `<glk-radio>` overrides it with the group's verdict.