@jungherz-de/glasskit-elements 1.19.0 → 1.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +729 -0
- package/README.md +1 -1
- package/SKILL.md +1555 -0
- package/dist/glasskit-elements.esm.js +2 -2
- package/dist/glasskit-elements.js +2 -2
- package/dist/glasskit-elements.min.js +1 -1
- package/package.json +8 -4
package/SKILL.md
ADDED
|
@@ -0,0 +1,1555 @@
|
|
|
1
|
+
---
|
|
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.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# GlassKit Elements – AI Component Reference
|
|
7
|
+
|
|
8
|
+
> **Purpose:** AI-optimized reference for generating correct `<glk-*>` markup.
|
|
9
|
+
> Companion to the class-based `SKILL.md` in [`@jungherz-de/glasskit`](https://github.com/JUNGHERZ/GlassKit) — the element wrappers delegate visuals and tokens to GlassKit CSS, so both references are best used together.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 1. Setup & Boilerplate
|
|
14
|
+
|
|
15
|
+
### Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @jungherz-de/glasskit-elements @jungherz-de/glasskit
|
|
19
|
+
```
|
|
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.
|
|
22
|
+
|
|
23
|
+
### Import (ES modules)
|
|
24
|
+
|
|
25
|
+
```js
|
|
26
|
+
// Full bundle — registers all 36 elements
|
|
27
|
+
import '@jungherz-de/glasskit-elements';
|
|
28
|
+
|
|
29
|
+
// Named imports (for direct references to constructor classes)
|
|
30
|
+
import { GlkButton, GlkModal } from '@jungherz-de/glasskit-elements';
|
|
31
|
+
|
|
32
|
+
// Per-component import (since 1.7.0) — registers just this element
|
|
33
|
+
import '@jungherz-de/glasskit-elements/components/glk-button.js';
|
|
34
|
+
|
|
35
|
+
// …or with the constructor class
|
|
36
|
+
import { GlkButton } from '@jungherz-de/glasskit-elements/components/glk-button.js';
|
|
37
|
+
```
|
|
38
|
+
|
|
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
|
|
42
|
+
custom element as a side effect, so a bare `import '…'` is enough.
|
|
43
|
+
|
|
44
|
+
### CDN
|
|
45
|
+
|
|
46
|
+
```html
|
|
47
|
+
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/glasskit-elements/dist/glasskit-elements.min.js"></script>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Note: the elements bundle the GlassKit component rules via `adoptedStyleSheets`, and put the token defaults on the document themselves. You do **not** need to load `glasskit.css` separately — but do load it if you also use plain `.glass-*` markup outside the elements.
|
|
51
|
+
|
|
52
|
+
### Minimal template
|
|
53
|
+
|
|
54
|
+
```html
|
|
55
|
+
<!DOCTYPE html>
|
|
56
|
+
<html lang="en" data-theme="dark">
|
|
57
|
+
<head>
|
|
58
|
+
<meta charset="UTF-8">
|
|
59
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
60
|
+
<script src="https://cdn.jsdelivr.net/npm/@jungherz-de/glasskit-elements/dist/glasskit-elements.min.js"></script>
|
|
61
|
+
</head>
|
|
62
|
+
<body>
|
|
63
|
+
<div class="glass-bg">
|
|
64
|
+
<glk-title>Hello GlassKit</glk-title>
|
|
65
|
+
<glk-card>
|
|
66
|
+
<p>Welcome!</p>
|
|
67
|
+
</glk-card>
|
|
68
|
+
</div>
|
|
69
|
+
</body>
|
|
70
|
+
</html>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The outer `.glass-bg` class (from GlassKit CSS) still provides the aurora background. Inside the shadow roots, each element carries its own glass styling.
|
|
74
|
+
|
|
75
|
+
### Theme
|
|
76
|
+
|
|
77
|
+
```html
|
|
78
|
+
<html data-theme="dark"> <!-- or "light" -->
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
function toggleTheme() {
|
|
83
|
+
const html = document.documentElement;
|
|
84
|
+
const current = html.getAttribute('data-theme') || 'dark';
|
|
85
|
+
html.setAttribute('data-theme', current === 'dark' ? 'light' : 'dark');
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
A single module-level `MutationObserver` watches `data-theme` on `<html>` and syncs every live GlassKit Element instance — you never set `data-theme` on individual elements.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 2. Core Concepts
|
|
94
|
+
|
|
95
|
+
| Concept | Summary |
|
|
96
|
+
|---|---|
|
|
97
|
+
| Tag prefix | `glk-*` (analogous to the `glass-*` CSS prefix) |
|
|
98
|
+
| Rendering | Shadow DOM (`mode: 'open'`) with `adoptedStyleSheets` |
|
|
99
|
+
| 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
|
+
| Theme sync | Global MutationObserver on `<html data-theme>` |
|
|
101
|
+
| Events | Custom `glk-*` events, all `bubbles: true, composed: true` |
|
|
102
|
+
| Form participation | `GlkFormElement` uses `ElementInternals` (`static formAssociated = true`) |
|
|
103
|
+
| API style | Declarative HTML attributes + reflected JS properties |
|
|
104
|
+
|
|
105
|
+
Custom properties (`--gl-*`) defined on `:root` or `<html>` pass through shadow boundaries by inheritance, so custom theming works with a single global stylesheet.
|
|
106
|
+
|
|
107
|
+
### Building your own element (since 1.14.0)
|
|
108
|
+
|
|
109
|
+
`GlkElement` and `GlkFormElement` are exported — from the bundle (`import { GlkElement } from '@jungherz-de/glasskit-elements'`), from the stable subpath `@jungherz-de/glasskit-elements/base.js` (the very module the components import, so `instanceof GlkElement` holds across both), and as `GlassKitElements.GlkElement` from the CDN `<script>` bundle. A subclass inherits the whole setup: open shadow root with GlassKit's stylesheet adopted (`.glass-*` classes work inside), the theme wrapper following `data-theme`, listeners re-armed when the element moves, `emit()` for bubbling, composed events.
|
|
110
|
+
|
|
111
|
+
```js
|
|
112
|
+
class DemoCounter extends GlkElement {
|
|
113
|
+
static get observedAttributes() { return ['count']; }
|
|
114
|
+
render() { // build into this._wrapper
|
|
115
|
+
this._btn = this.createElement('button', ['glass-btn', 'glass-btn--secondary', 'glass-btn--sm', 'glass-btn--auto']);
|
|
116
|
+
this._wrapper.appendChild(this._btn);
|
|
117
|
+
}
|
|
118
|
+
setupEvents() { // runs on every connect
|
|
119
|
+
this._onClick = () => { this.count += 1; this.emit('demo-count', { count: this.count }); };
|
|
120
|
+
this._btn.addEventListener('click', this._onClick);
|
|
121
|
+
}
|
|
122
|
+
teardownEvents() { this._btn?.removeEventListener('click', this._onClick); } // every disconnect
|
|
123
|
+
onAttributeChanged() { this._btn.textContent = `Clicked ${this.count}×`; } // after first render
|
|
124
|
+
get count() { return Number(this.getAttribute('count')) || 0; }
|
|
125
|
+
set count(v) { this.setAttribute('count', v); }
|
|
126
|
+
}
|
|
127
|
+
customElements.define('demo-counter', DemoCounter);
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Which import to take depends on how the elements are loaded, and mixing them is the one mistake to avoid: the `<script>` / ESM **bundle** carries its own copy of `GlkElement`, so a subclass built on `base.js` next to it is a different class — `instanceof` fails across the two, and the GlassKit stylesheet lives twice. Bundle loaded → take `GlassKitElements.GlkElement` or `import { GlkElement } from '@jungherz-de/glasskit-elements'`. Per-component files loaded → `base.js`, which the components import themselves. The per-component files leave `@jungherz-de/glasskit/glasskit-styles.js` external, so a bundler resolves it from `node_modules` and an import-map project maps it to the one copy it already loads for its own elements.
|
|
131
|
+
|
|
132
|
+
Rules for a subclass: build only inside `this._wrapper` (it carries `data-theme`); keep listeners in `setupEvents()` / `teardownEvents()`, never in `render()`, or a moved element loses them; use `emit()` instead of `dispatchEvent()` so the event bubbles and crosses the shadow boundary — `emit(name, detail, { cancelable: true })` lets a listener call `preventDefault()`, and `emit()` returns `false` then (since 1.19.0); return `true` from `static get displayInline()` for inline elements; set `static get observesLightDom()` to `true` and implement `projectLightDom()` when copying light-DOM children into the shadow tree. `GlkFormElement` adds `setFormValue()`, `setValidity()`, `resetValue()` / `restoreValue()` and `static formAssociated = true`.
|
|
133
|
+
|
|
134
|
+
### Light-DOM children (since 1.12.0)
|
|
135
|
+
|
|
136
|
+
Most elements project children through a real `<slot>`, which is live by definition.
|
|
137
|
+
Three copy them into the shadow tree instead, because GlassKit's CSS has to reach
|
|
138
|
+
them: `<glk-select>` (its `<option>`s), `<glk-tab-item>` (its `<svg>`) and
|
|
139
|
+
`<glk-modal>` (its `[slot="actions"]` buttons).
|
|
140
|
+
|
|
141
|
+
Those copies are now kept in step by a `MutationObserver`, so swapping the children
|
|
142
|
+
— by hand or through a framework — updates the rendered element. Before 1.12.0 the
|
|
143
|
+
copy was made once on the first frame and then silently went stale.
|
|
144
|
+
|
|
145
|
+
If you ever need to force it, `element.refresh()` re-runs the copy immediately.
|
|
146
|
+
There should be no reason to reach into `element.shadowRoot`.
|
|
147
|
+
|
|
148
|
+
### Moving an element (since 1.12.0)
|
|
149
|
+
|
|
150
|
+
Moving a `<glk-*>` element in the DOM disconnects and reconnects it. Its event
|
|
151
|
+
listeners and its theme-sync registration are re-armed on every connect, so it keeps
|
|
152
|
+
working. Before 1.12.0 they were torn down on disconnect and never restored: the
|
|
153
|
+
element still looked right but no longer fired events.
|
|
154
|
+
|
|
155
|
+
### Branding (since 1.9.0)
|
|
156
|
+
|
|
157
|
+
Override `--gl-*` tokens anywhere in the document — a brand stylesheet, a `<style>` block,
|
|
158
|
+
inline on `<html>`. Every `<glk-*>` element inherits them:
|
|
159
|
+
|
|
160
|
+
```css
|
|
161
|
+
/* brand.css, loaded after glasskit.css */
|
|
162
|
+
:root, [data-theme='dark'] { --gl-color-primary: #2e9e8f; }
|
|
163
|
+
[data-theme='light'] { --gl-color-primary: #21786d; }
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
No JavaScript, no `::part`, no per-element setup. Both `.glass-btn--primary` in the
|
|
167
|
+
document and `<glk-button variant="primary">` end up the same color.
|
|
168
|
+
|
|
169
|
+
**This did not work before 1.9.0.** Up to and including 1.8.0 every element adopted the
|
|
170
|
+
full GlassKit sheet, whose `[data-theme]` blocks matched the element's own theme wrapper
|
|
171
|
+
and re-declared all tokens inside the shadow root. A matching rule beats an inherited
|
|
172
|
+
value, so document-level overrides were silently ignored inside every component — while
|
|
173
|
+
the same override *did* apply to plain `.glass-*` markup, which made projects look
|
|
174
|
+
half-branded. Since 1.9.0 the elements adopt only the component rules.
|
|
175
|
+
|
|
176
|
+
**How the defaults get there.** Because the tokens no longer live in the shadow roots,
|
|
177
|
+
this module puts them on the document once, wrapped in `@layer glasskit-defaults`:
|
|
178
|
+
|
|
179
|
+
- Pages that never load `glasskit.css` still get styled elements, as advertised above.
|
|
180
|
+
- The layer loses to any ordinary stylesheet, so your brand file wins regardless of
|
|
181
|
+
load order — and so does a linked `glasskit.css`, at identical values.
|
|
182
|
+
- It is appended to `document.adoptedStyleSheets`, never assigned, and only once even if
|
|
183
|
+
several copies of the bundle are loaded.
|
|
184
|
+
|
|
185
|
+
If your app assigns `document.adoptedStyleSheets = [...]` wholesale at some later point,
|
|
186
|
+
re-append the existing entries rather than replacing them, or the defaults are lost.
|
|
187
|
+
|
|
188
|
+
### Passing icons (fixed in 1.10.0)
|
|
189
|
+
|
|
190
|
+
Pass an icon as a **direct child** — the `<svg>` itself, not wrapped:
|
|
191
|
+
|
|
192
|
+
```html
|
|
193
|
+
<glk-button><svg viewBox="0 0 24 24">…</svg>Save</glk-button>
|
|
194
|
+
<glk-list-item title="Download"><svg slot="leading" viewBox="0 0 24 24">…</svg></glk-list-item>
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
GlassKit sizes and strokes it. Requires GlassKit CSS >= 1.10.0: the icon rules are
|
|
198
|
+
descendant selectors (`.glass-btn svg`), and a slotted icon stays in the light DOM, so it
|
|
199
|
+
is no descendant of the shadow tree. 1.10.0 added a `::slotted()` twin to every such rule.
|
|
200
|
+
Before that, icons came out at their intrinsic size — `glk-button` icons filled the whole
|
|
201
|
+
container, list and status icons collapsed to 0×0.
|
|
202
|
+
|
|
203
|
+
**Wrapping the icon does not work and cannot be made to work:**
|
|
204
|
+
|
|
205
|
+
```html
|
|
206
|
+
<!-- the assigned node is the <span>; the <svg> inside is unreachable from the shadow root -->
|
|
207
|
+
<glk-list-item title="…"><span slot="leading"><svg …></svg></span></glk-list-item>
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
`::slotted()` only ever matches the assigned node, never inside it. If you must wrap,
|
|
211
|
+
size the icon yourself in your own stylesheet.
|
|
212
|
+
|
|
213
|
+
Your own rules keep precedence: for slotted content the outer tree wins over `::slotted()`
|
|
214
|
+
from the shadow tree, so `glk-list-item svg { width: 32px }` in your CSS still applies.
|
|
215
|
+
Note that `.glass-list__leading` is a fixed 28×28 flex container — enlarging only the icon
|
|
216
|
+
shrinks it back on the main axis; size the container too.
|
|
217
|
+
|
|
218
|
+
### CSS Parts (since 1.8.0)
|
|
219
|
+
|
|
220
|
+
Custom properties cover colors, but not layout. For that, the structural elements of the
|
|
221
|
+
container components are exposed as parts:
|
|
222
|
+
|
|
223
|
+
| Element | Parts |
|
|
224
|
+
|---|---|
|
|
225
|
+
| `<glk-card>` | `card` |
|
|
226
|
+
| `<glk-list>` | `header`, `list` |
|
|
227
|
+
| `<glk-modal>` | `overlay`, `modal`, `header`, `body`, `footer` |
|
|
228
|
+
|
|
229
|
+
```css
|
|
230
|
+
glk-card::part(card) { border-radius: 8px; }
|
|
231
|
+
glk-modal::part(footer) { justify-content: flex-start; }
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Note that `height: 100%` on a part only resolves if the host gives it something definite
|
|
235
|
+
to resolve against. A host stretched by a grid keeps `height: auto`, which is why
|
|
236
|
+
`<glk-card fill>` makes the host a grid instead.
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## 3. Element Catalog (36 elements)
|
|
241
|
+
|
|
242
|
+
### 3.1 `<glk-nav>`
|
|
243
|
+
|
|
244
|
+
Horizontal navigation bar — flex container. Typically holds `<glk-pill>` buttons.
|
|
245
|
+
|
|
246
|
+
```html
|
|
247
|
+
<glk-nav>
|
|
248
|
+
<glk-pill label="Back" onclick="history.back()">
|
|
249
|
+
<svg viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg>
|
|
250
|
+
</glk-pill>
|
|
251
|
+
<glk-pill label="Settings">
|
|
252
|
+
<svg viewBox="0 0 24 24">...</svg>
|
|
253
|
+
</glk-pill>
|
|
254
|
+
</glk-nav>
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
No attributes. Default slot for children.
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
### 3.2 `<glk-pill>`
|
|
262
|
+
|
|
263
|
+
Circular icon button (46×46 px).
|
|
264
|
+
|
|
265
|
+
```html
|
|
266
|
+
<glk-pill label="Back">
|
|
267
|
+
<svg viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg>
|
|
268
|
+
</glk-pill>
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
| Attribute | Type | Description |
|
|
272
|
+
|---|---|---|
|
|
273
|
+
| `label` | String | `aria-label` for accessibility |
|
|
274
|
+
| `disabled` | Boolean | Disabled state |
|
|
275
|
+
|
|
276
|
+
Default slot: icon SVG (24×24).
|
|
277
|
+
|
|
278
|
+
Events: `glk-click` when clicked.
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
### 3.3 `<glk-tab-bar>` + `<glk-tab-item>`
|
|
283
|
+
|
|
284
|
+
Fixed bottom navigation. Requires `.glass-bg--has-tab-bar` on the outer background to reserve padding.
|
|
285
|
+
|
|
286
|
+
```html
|
|
287
|
+
<div class="glass-bg glass-bg--has-tab-bar">
|
|
288
|
+
<!-- page content -->
|
|
289
|
+
<glk-tab-bar>
|
|
290
|
+
<glk-tab-item label="Home" active>
|
|
291
|
+
<svg viewBox="0 0 24 24"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg>
|
|
292
|
+
</glk-tab-item>
|
|
293
|
+
<glk-tab-item label="Search">
|
|
294
|
+
<svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="8"/></svg>
|
|
295
|
+
</glk-tab-item>
|
|
296
|
+
<glk-tab-item label="Profile" badge="3">
|
|
297
|
+
<svg viewBox="0 0 24 24"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/></svg>
|
|
298
|
+
</glk-tab-item>
|
|
299
|
+
</glk-tab-bar>
|
|
300
|
+
</div>
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
**`<glk-tab-bar>`** — container.
|
|
304
|
+
|
|
305
|
+
| Attribute | Type | Description |
|
|
306
|
+
|---|---|---|
|
|
307
|
+
| `static` | Boolean | Switches from `position: fixed` to `position: relative` (useful for docs / previews) |
|
|
308
|
+
| `floating` | Boolean | Pill-shaped Liquid Glass variant — adds `.glass-tab-bar--floating`. Use inside `<glk-tab-dock>` |
|
|
309
|
+
|
|
310
|
+
**`<glk-tab-item>`**
|
|
311
|
+
|
|
312
|
+
| Attribute | Type | Description |
|
|
313
|
+
|---|---|---|
|
|
314
|
+
| `label` | String | Tab label |
|
|
315
|
+
| `active` | Boolean | Active state — gets a soft radial Spotlight halo when inside a `floating` bar |
|
|
316
|
+
| `badge` | String / Number | Numeric badge on the icon |
|
|
317
|
+
|
|
318
|
+
Default slot: icon SVG. Events: `glk-tab-select` on click.
|
|
319
|
+
|
|
320
|
+
#### Floating variant + Accessory (`<glk-tab-dock>` + `<glk-tab-accessory>`)
|
|
321
|
+
|
|
322
|
+
iOS 26 Liquid Glass-style floating tab bar. Wrap a `<glk-tab-bar floating>` in a `<glk-tab-dock>`, optionally with a `<glk-tab-accessory>` (search, compose…) capsule next to it. Use `.glass-bg--has-tab-bar-floating` on the outer background to reserve padding.
|
|
323
|
+
|
|
324
|
+
```html
|
|
325
|
+
<div class="glass-bg glass-bg--has-tab-bar-floating">
|
|
326
|
+
<!-- page content -->
|
|
327
|
+
<glk-tab-dock>
|
|
328
|
+
<glk-tab-bar floating>
|
|
329
|
+
<glk-tab-item label="Home" active>
|
|
330
|
+
<svg viewBox="0 0 24 24"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg>
|
|
331
|
+
</glk-tab-item>
|
|
332
|
+
<glk-tab-item label="Docs" badge="3">
|
|
333
|
+
<svg viewBox="0 0 24 24"><rect x="2" y="3" width="20" height="14" rx="2"/></svg>
|
|
334
|
+
</glk-tab-item>
|
|
335
|
+
<glk-tab-item label="Upload">
|
|
336
|
+
<svg viewBox="0 0 24 24"><path d="M12 16V4"/><polyline points="8 8 12 4 16 8"/></svg>
|
|
337
|
+
</glk-tab-item>
|
|
338
|
+
</glk-tab-bar>
|
|
339
|
+
<glk-tab-accessory variant="accent" label="Compose">
|
|
340
|
+
<svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
|
|
341
|
+
</glk-tab-accessory>
|
|
342
|
+
</glk-tab-dock>
|
|
343
|
+
</div>
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
**`<glk-tab-dock>`** — wrapper that holds the floating bar + optional accessory.
|
|
347
|
+
|
|
348
|
+
| Attribute | Type | Description |
|
|
349
|
+
|---|---|---|
|
|
350
|
+
| `accessory-left` | Boolean | Place the accessory on the left of the bar (default is right) |
|
|
351
|
+
|
|
352
|
+
**`<glk-tab-accessory>`** — standalone glass capsule next to the bar (e.g. search, compose). 56×56 px.
|
|
353
|
+
|
|
354
|
+
| Attribute | Type | Description |
|
|
355
|
+
|---|---|---|
|
|
356
|
+
| `label` | String | `aria-label` for accessibility |
|
|
357
|
+
| `variant` | String | `accent`, `success`, `error` — filled colored capsule with white icon |
|
|
358
|
+
| `disabled` | Boolean | Disabled state |
|
|
359
|
+
|
|
360
|
+
Default slot: icon SVG (22×22). Events: `glk-click` (suppressed when disabled).
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
### 3.4 `<glk-avatar>`
|
|
365
|
+
|
|
366
|
+
Inline avatar circle.
|
|
367
|
+
|
|
368
|
+
```html
|
|
369
|
+
<glk-avatar size="lg" src="/user.jpg"></glk-avatar>
|
|
370
|
+
<glk-avatar>MJ</glk-avatar>
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
| Attribute | Type | Description |
|
|
374
|
+
|---|---|---|
|
|
375
|
+
| `size` | String | `sm`, `md` (default), `lg` |
|
|
376
|
+
| `src` | String | Image URL; if omitted, text content is shown as initials |
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
### 3.5 `<glk-badge>`
|
|
381
|
+
|
|
382
|
+
Inline status badge — and, with `interactive`, a filter chip.
|
|
383
|
+
|
|
384
|
+
```html
|
|
385
|
+
<glk-badge>Default</glk-badge>
|
|
386
|
+
<glk-badge variant="primary">Active</glk-badge>
|
|
387
|
+
<glk-badge variant="success">Done</glk-badge>
|
|
388
|
+
<glk-badge variant="warning">Pending</glk-badge>
|
|
389
|
+
<glk-badge variant="error">Failed</glk-badge>
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
| Attribute | Type | Description |
|
|
393
|
+
|---|---|---|
|
|
394
|
+
| `variant` | String | `primary`, `success`, `warning` (since 1.18.0), `error`; any other value gives the neutral badge |
|
|
395
|
+
| `interactive` | Boolean | Renders the badge as a real `<button>`: pointer cursor, hover tint, focus ring, keyboard operable. Enables `glk-click`. |
|
|
396
|
+
| `selected` | Boolean | Marks the chip that is on. Mirrored to `aria-pressed` on the button. |
|
|
397
|
+
|
|
398
|
+
Default slot: label text. Events: `glk-click` (only when `interactive`).
|
|
399
|
+
|
|
400
|
+
**Filter chips (since 1.13.0, needs GlassKit 1.12.0).** A row of chips where one is picked: listen for `glk-click` on the row and move `selected`. The element keeps `aria-pressed` in step, so the row is announced as a group of toggles.
|
|
401
|
+
|
|
402
|
+
```html
|
|
403
|
+
<div id="filter">
|
|
404
|
+
<glk-badge interactive selected>Active</glk-badge>
|
|
405
|
+
<glk-badge interactive>Applied</glk-badge>
|
|
406
|
+
<glk-badge interactive>Paused</glk-badge>
|
|
407
|
+
</div>
|
|
408
|
+
<script>
|
|
409
|
+
filter.addEventListener('glk-click', e => {
|
|
410
|
+
for (const chip of filter.querySelectorAll('glk-badge')) chip.selected = chip === e.target;
|
|
411
|
+
});
|
|
412
|
+
</script>
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
`selected` deepens the colour the badge already has: a plain chip turns primary, `<glk-badge variant="success" interactive selected>` stays green. `interactive` can be toggled at runtime — the element swaps between `<span>` and `<button>` underneath, the slotted label stays put. Without `interactive`, clicks do not emit `glk-click`, exactly like `<glk-list-item>`.
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
### 3.6 `<glk-card>`
|
|
420
|
+
|
|
421
|
+
Glass container for content.
|
|
422
|
+
|
|
423
|
+
```html
|
|
424
|
+
<glk-card>
|
|
425
|
+
<p>Standard card content.</p>
|
|
426
|
+
</glk-card>
|
|
427
|
+
|
|
428
|
+
<glk-card glow>
|
|
429
|
+
<p>Glowing card variant with light streak.</p>
|
|
430
|
+
</glk-card>
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
| Attribute | Type | Description |
|
|
434
|
+
|---|---|---|
|
|
435
|
+
| `glow` | Boolean | Adds frosted glass gradient + light streak |
|
|
436
|
+
| `fill` | Boolean | Stretches the card to the full height of its grid or flex cell (since 1.8.0); the card yields to its cell, so nowrap content is truncated instead of widening it (since 1.15.2) |
|
|
437
|
+
|
|
438
|
+
Default slot: content. CSS part: `card`.
|
|
439
|
+
|
|
440
|
+
**Equal-height tiles.** In a grid the host stretches but the visible card keeps its
|
|
441
|
+
natural height, so tiles end up ragged and footer buttons sit at different heights.
|
|
442
|
+
Add `fill`:
|
|
443
|
+
|
|
444
|
+
```html
|
|
445
|
+
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(340px,1fr));gap:16px">
|
|
446
|
+
<glk-card fill>
|
|
447
|
+
<div style="display:flex;flex-direction:column;height:100%">
|
|
448
|
+
<h3>Title</h3>
|
|
449
|
+
<p>Short.</p>
|
|
450
|
+
<div style="margin-top:auto"><glk-button variant="primary">Action</glk-button></div>
|
|
451
|
+
</div>
|
|
452
|
+
</glk-card>
|
|
453
|
+
<glk-card fill>…</glk-card>
|
|
454
|
+
</div>
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
`fill` makes the card a flex column, so `margin-top: auto` on the last child pushes a
|
|
458
|
+
footer down and buttons line up across tiles. Give your own wrapper `height: 100%` as
|
|
459
|
+
shown — otherwise it sizes to its content inside the stretched card.
|
|
460
|
+
|
|
461
|
+
**The card yields to its cell (since 1.15.2).** `fill` makes the card a grid item of the host, and a grid item's `min-width: auto` is its min-content width — a nowrap subtitle in a `<glk-list-item>` used to widen the card past its cell instead of getting its ellipsis. The card and the host now carry `min-width: 0`, and the card `min-height: 0`, so long nowrap content truncates and a scrollable child (`overflow: auto`) inside a cell of fixed height shrinks and scrolls. Content that can neither wrap nor shrink overflows the card's edge rather than pushing the card out of the cell. A plain `<glk-card>` is unchanged: as a block in a `1fr` track it keeps `min-width: auto` like any block, so give it `min-width: 0` yourself when it holds nowrap content.
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
### 3.7 `<glk-divider>`
|
|
466
|
+
|
|
467
|
+
Horizontal separator with gradient fade.
|
|
468
|
+
|
|
469
|
+
```html
|
|
470
|
+
<glk-divider></glk-divider>
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
No attributes, no content.
|
|
474
|
+
|
|
475
|
+
---
|
|
476
|
+
|
|
477
|
+
### 3.8 `<glk-status>`
|
|
478
|
+
|
|
479
|
+
Inline status notice (muted surface).
|
|
480
|
+
|
|
481
|
+
```html
|
|
482
|
+
<glk-status message="No results found"></glk-status>
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
| Attribute | Type | Description |
|
|
486
|
+
|---|---|---|
|
|
487
|
+
| `message` | String | Status text |
|
|
488
|
+
|
|
489
|
+
---
|
|
490
|
+
|
|
491
|
+
### 3.9 `<glk-title>`
|
|
492
|
+
|
|
493
|
+
Page title with text shadow.
|
|
494
|
+
|
|
495
|
+
```html
|
|
496
|
+
<glk-title>Settings</glk-title>
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
No attributes. Default slot: heading text.
|
|
500
|
+
|
|
501
|
+
---
|
|
502
|
+
|
|
503
|
+
### 3.10 `<glk-button>`
|
|
504
|
+
|
|
505
|
+
Full-width glass button (56 px). Three variants, four sizes.
|
|
506
|
+
|
|
507
|
+
```html
|
|
508
|
+
<glk-button variant="primary">Save</glk-button>
|
|
509
|
+
<glk-button variant="secondary">Cancel</glk-button>
|
|
510
|
+
<glk-button variant="tertiary">Learn more</glk-button>
|
|
511
|
+
|
|
512
|
+
<glk-button variant="primary" size="sm">Small</glk-button>
|
|
513
|
+
<glk-button variant="primary" size="lg">Large</glk-button>
|
|
514
|
+
<glk-button variant="primary" size="auto">Auto width</glk-button>
|
|
515
|
+
|
|
516
|
+
<glk-button variant="primary" disabled>Disabled</glk-button>
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
| Attribute | Type | Default | Description |
|
|
520
|
+
|---|---|---|---|
|
|
521
|
+
| `variant` | String | — | `primary`, `secondary`, `tertiary` |
|
|
522
|
+
| `size` | String | `md` | `sm`, `md`, `lg`, `auto` |
|
|
523
|
+
| `disabled` | Boolean | `false` | Disabled state |
|
|
524
|
+
| `type` | String | `button` | Native button type |
|
|
525
|
+
|
|
526
|
+
Events: `glk-click` (suppressed when disabled).
|
|
527
|
+
|
|
528
|
+
Default slot: button label (text or SVG + text).
|
|
529
|
+
|
|
530
|
+
---
|
|
531
|
+
|
|
532
|
+
### 3.11 `<glk-checkbox>`
|
|
533
|
+
|
|
534
|
+
Form-associated checkbox with glass styling.
|
|
535
|
+
|
|
536
|
+
```html
|
|
537
|
+
<glk-checkbox label="I agree to the terms" checked></glk-checkbox>
|
|
538
|
+
<glk-checkbox label="Subscribe to newsletter"></glk-checkbox>
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
| Attribute | Type | Description |
|
|
542
|
+
|---|---|---|
|
|
543
|
+
| `label` | String | Label text |
|
|
544
|
+
| `checked` | Boolean | Checked state |
|
|
545
|
+
| `disabled` | Boolean | Disabled state |
|
|
546
|
+
| `name` | String | Form field name |
|
|
547
|
+
| `value` | String | Value submitted when checked (default `"on"`) |
|
|
548
|
+
|
|
549
|
+
Events: `glk-change` → `{ checked }`; also dispatches native `change`. Property: `.checked`.
|
|
550
|
+
|
|
551
|
+
---
|
|
552
|
+
|
|
553
|
+
### 3.12 `<glk-input>`
|
|
554
|
+
|
|
555
|
+
Text input with label + hint.
|
|
556
|
+
|
|
557
|
+
```html
|
|
558
|
+
<glk-input label="Email" type="email" placeholder="you@example.com" hint="We won't share."></glk-input>
|
|
559
|
+
<glk-input label="Password" type="password" required></glk-input>
|
|
560
|
+
<glk-input label="Broken field" error hint="This field is required."></glk-input>
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
| Attribute | Type | Description |
|
|
564
|
+
|---|---|---|
|
|
565
|
+
| `label` | String | Label above the input |
|
|
566
|
+
| `type` | String | Native input type (`text`, `email`, `password`, `number`, …) |
|
|
567
|
+
| `placeholder` | String | Placeholder text |
|
|
568
|
+
| `hint` | String | Helper text below the input |
|
|
569
|
+
| `error` | Boolean | Error styling (red border + red hint) |
|
|
570
|
+
| `disabled` | Boolean | Disabled state |
|
|
571
|
+
| `required` | Boolean | Required field |
|
|
572
|
+
| `name` | String | Form field name |
|
|
573
|
+
| `value` | String | Current value |
|
|
574
|
+
| `min`, `max`, `step` | String | Passed to the inner field — range and step of the picker for dates, times and numbers (since 1.17.0) |
|
|
575
|
+
| `minlength`, `maxlength`, `pattern` | String | Passed to the inner field (since 1.17.0) |
|
|
576
|
+
| `autocomplete`, `inputmode` | String | Autofill and on-screen keyboard, passed to the inner field (since 1.17.0) |
|
|
577
|
+
|
|
578
|
+
Events: `glk-input` → `{ value }`, `glk-change` → `{ value }`. Native `input` / `change` also dispatched. Property: `.value`.
|
|
579
|
+
|
|
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.
|
|
581
|
+
|
|
582
|
+
---
|
|
583
|
+
|
|
584
|
+
### 3.13 `<glk-radio>`
|
|
585
|
+
|
|
586
|
+
Radio button. Group multiple with the same `name`.
|
|
587
|
+
|
|
588
|
+
```html
|
|
589
|
+
<glk-radio name="plan" label="Free Plan" value="free" checked></glk-radio>
|
|
590
|
+
<glk-radio name="plan" label="Pro Plan" value="pro"></glk-radio>
|
|
591
|
+
<glk-radio name="plan" label="Enterprise" value="enterprise"></glk-radio>
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
| Attribute | Type | Description |
|
|
595
|
+
|---|---|---|
|
|
596
|
+
| `label` | String | Label text |
|
|
597
|
+
| `name` | String | Group name |
|
|
598
|
+
| `value` | String | Value submitted when checked |
|
|
599
|
+
| `checked` | Boolean | Checked state |
|
|
600
|
+
| `disabled` | Boolean | Disabled state |
|
|
601
|
+
|
|
602
|
+
Events: `glk-change` → `{ checked, value }`.
|
|
603
|
+
|
|
604
|
+
**Grouping (since 1.11.0).** Each `<glk-radio>` keeps its `<input type="radio">` in
|
|
605
|
+
its own shadow root, and native radio grouping works per tree — it does not cross
|
|
606
|
+
shadow boundaries. The group is therefore kept by the component, following the HTML
|
|
607
|
+
definition: **same `name`, same containing tree, same form owner**. Selecting one
|
|
608
|
+
clears the others, whether by click, by arrow key, by the `checked` property or by
|
|
609
|
+
the `checked` attribute, and only the selected value is submitted. Two `<glk-radio>`
|
|
610
|
+
in two different `<form>`s are two groups, and a radio without `name` is not grouped.
|
|
611
|
+
If several carry `checked` in the markup, the last one wins — as with native radios.
|
|
612
|
+
|
|
613
|
+
Keyboard: the group is a single tab stop. <kbd>↑</kbd> <kbd>↓</kbd> <kbd>←</kbd>
|
|
614
|
+
<kbd>→</kbd> move through it, wrap around, skip disabled entries and select as they
|
|
615
|
+
go, firing `glk-change` and `change` exactly as a click does.
|
|
616
|
+
|
|
617
|
+
Not covered: a screen reader still announces the entries as separate radios, not as
|
|
618
|
+
"1 of 3" — that needs a `role="radiogroup"` container, which no `<glk-*>` element
|
|
619
|
+
provides. Wrap them yourself if you need it:
|
|
620
|
+
|
|
621
|
+
```html
|
|
622
|
+
<div role="radiogroup" aria-label="Plan">
|
|
623
|
+
<glk-radio name="plan" label="Free Plan" value="free" checked></glk-radio>
|
|
624
|
+
<glk-radio name="plan" label="Pro Plan" value="pro"></glk-radio>
|
|
625
|
+
</div>
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
---
|
|
629
|
+
|
|
630
|
+
### 3.14 `<glk-range>`
|
|
631
|
+
|
|
632
|
+
Slider input.
|
|
633
|
+
|
|
634
|
+
```html
|
|
635
|
+
<glk-range label="Volume" min="0" max="100" value="65" step="5"></glk-range>
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
| Attribute | Type | Description |
|
|
639
|
+
|---|---|---|
|
|
640
|
+
| `label` | String | Label text |
|
|
641
|
+
| `min` | Number | Minimum value |
|
|
642
|
+
| `max` | Number | Maximum value |
|
|
643
|
+
| `value` | Number | Current value |
|
|
644
|
+
| `step` | Number | Step size |
|
|
645
|
+
| `name` | String | Form field name |
|
|
646
|
+
| `disabled` | Boolean | Disabled state |
|
|
647
|
+
|
|
648
|
+
Events: `glk-input`, `glk-change`. Property: `.value`.
|
|
649
|
+
|
|
650
|
+
---
|
|
651
|
+
|
|
652
|
+
### 3.15 `<glk-search>`
|
|
653
|
+
|
|
654
|
+
Search input with leading icon.
|
|
655
|
+
|
|
656
|
+
```html
|
|
657
|
+
<glk-search placeholder="Search components..."></glk-search>
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
| Attribute | Type | Description |
|
|
661
|
+
|---|---|---|
|
|
662
|
+
| `placeholder` | String | Placeholder text |
|
|
663
|
+
| `value` | String | Current value |
|
|
664
|
+
| `name` | String | Form field name |
|
|
665
|
+
| `disabled` | Boolean | Disabled state |
|
|
666
|
+
|
|
667
|
+
Events: `glk-input`, `glk-change`.
|
|
668
|
+
|
|
669
|
+
---
|
|
670
|
+
|
|
671
|
+
### 3.16 `<glk-select>`
|
|
672
|
+
|
|
673
|
+
Dropdown select. Pass native `<option>` elements as children.
|
|
674
|
+
|
|
675
|
+
```html
|
|
676
|
+
<glk-select label="Category" name="category">
|
|
677
|
+
<option value="">Please select…</option>
|
|
678
|
+
<option value="1">Insurance</option>
|
|
679
|
+
<option value="2">Rental</option>
|
|
680
|
+
<option value="3">Employment</option>
|
|
681
|
+
</glk-select>
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
| Attribute | Type | Description |
|
|
685
|
+
|---|---|---|
|
|
686
|
+
| `label` | String | Label text |
|
|
687
|
+
| `name` | String | Form field name |
|
|
688
|
+
| `value` | String | Selected value |
|
|
689
|
+
| `disabled` | Boolean | Disabled state |
|
|
690
|
+
|
|
691
|
+
Children: native `<option>` elements, read from the light DOM.
|
|
692
|
+
|
|
693
|
+
Events: `glk-change` → `{ value }`.
|
|
694
|
+
|
|
695
|
+
**Changing the options later (since 1.12.0).** The options are copied into the
|
|
696
|
+
shadow tree, and the copy is kept in step: replace, add or remove `<option>`
|
|
697
|
+
children at any time — by hand or through a framework — and the rendered select
|
|
698
|
+
follows. Before 1.12.0 the copy was made once and then silently went stale.
|
|
699
|
+
|
|
700
|
+
The selection survives a rebuild: if the selected value is still in the new list
|
|
701
|
+
it stays selected, otherwise the `value` attribute decides, otherwise the browser
|
|
702
|
+
falls back to the first option. The **empty string is a real value** throughout —
|
|
703
|
+
`value=""` selects `<option value="">` wherever it sits in the list, not just when
|
|
704
|
+
it happens to be first.
|
|
705
|
+
|
|
706
|
+
```html
|
|
707
|
+
<!-- "" is a genuine choice here, and it is honoured -->
|
|
708
|
+
<glk-select label="Language" value="">
|
|
709
|
+
<option value="de">Deutsch</option>
|
|
710
|
+
<option value="">Detect automatically</option>
|
|
711
|
+
</glk-select>
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
A `value` naming no existing option leaves the current selection alone rather than
|
|
715
|
+
clearing it — when the matching option arrives later, it is selected then.
|
|
716
|
+
|
|
717
|
+
---
|
|
718
|
+
|
|
719
|
+
### 3.17 `<glk-textarea>`
|
|
720
|
+
|
|
721
|
+
Multi-line text input.
|
|
722
|
+
|
|
723
|
+
```html
|
|
724
|
+
<glk-textarea label="Message" rows="4" placeholder="Type your message..."></glk-textarea>
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
| Attribute | Type | Description |
|
|
728
|
+
|---|---|---|
|
|
729
|
+
| `label` | String | Label text |
|
|
730
|
+
| `rows` | Number | Visible rows |
|
|
731
|
+
| `placeholder` | String | Placeholder text |
|
|
732
|
+
| `name` | String | Form field name |
|
|
733
|
+
| `value` | String | Current value |
|
|
734
|
+
| `disabled` | Boolean | Disabled state |
|
|
735
|
+
| `required` | Boolean | Required field |
|
|
736
|
+
|
|
737
|
+
Events: `glk-input`, `glk-change`.
|
|
738
|
+
|
|
739
|
+
---
|
|
740
|
+
|
|
741
|
+
### 3.18 `<glk-toggle>`
|
|
742
|
+
|
|
743
|
+
Switch-style toggle (form-associated).
|
|
744
|
+
|
|
745
|
+
```html
|
|
746
|
+
<glk-toggle label="Enable notifications" checked></glk-toggle>
|
|
747
|
+
<glk-toggle label="Dark mode"></glk-toggle>
|
|
748
|
+
<glk-toggle label="Disabled toggle" disabled></glk-toggle>
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
| Attribute | Type | Description |
|
|
752
|
+
|---|---|---|
|
|
753
|
+
| `label` | String | Label text |
|
|
754
|
+
| `checked` | Boolean | Checked state |
|
|
755
|
+
| `disabled` | Boolean | Disabled state |
|
|
756
|
+
| `name` | String | Form field name |
|
|
757
|
+
| `value` | String | Submitted value when checked (default `"on"`) |
|
|
758
|
+
|
|
759
|
+
Events: `glk-change` → `{ checked }`. Property: `.checked`. Has `role="switch"` and syncs `aria-checked`.
|
|
760
|
+
|
|
761
|
+
---
|
|
762
|
+
|
|
763
|
+
### 3.19 `<glk-modal>`
|
|
764
|
+
|
|
765
|
+
Modal dialog with overlay, title, body, and footer actions.
|
|
766
|
+
|
|
767
|
+
```html
|
|
768
|
+
<glk-button variant="secondary" size="auto"
|
|
769
|
+
onclick="document.getElementById('confirm').show()">
|
|
770
|
+
Open
|
|
771
|
+
</glk-button>
|
|
772
|
+
|
|
773
|
+
<glk-modal id="confirm" title="Delete contract?">
|
|
774
|
+
<p>This action cannot be undone.</p>
|
|
775
|
+
<div slot="actions">
|
|
776
|
+
<button class="glass-modal__action" onclick="document.getElementById('confirm').close()">Cancel</button>
|
|
777
|
+
<button class="glass-modal__action glass-modal__action--danger"
|
|
778
|
+
onclick="deleteContract(); document.getElementById('confirm').close()">Delete</button>
|
|
779
|
+
</div>
|
|
780
|
+
</glk-modal>
|
|
781
|
+
```
|
|
782
|
+
|
|
783
|
+
| Attribute | Type | Description |
|
|
784
|
+
|---|---|---|
|
|
785
|
+
| `open` | Boolean | Visibility state (reflected as `.is-active` on the overlay) |
|
|
786
|
+
| `title` | String | Header title |
|
|
787
|
+
|
|
788
|
+
Slots:
|
|
789
|
+
- default — modal body content
|
|
790
|
+
- `actions` — container for footer buttons (GlassKit expects `<button class="glass-modal__action">`)
|
|
791
|
+
|
|
792
|
+
Methods: `.show()`, `.close()`. Property: `.open`. Events: `glk-close`.
|
|
793
|
+
|
|
794
|
+
Closes automatically on overlay click and <kbd>Escape</kbd>.
|
|
795
|
+
|
|
796
|
+
---
|
|
797
|
+
|
|
798
|
+
### 3.20 `<glk-popover>`
|
|
799
|
+
|
|
800
|
+
Anchored dropdown / menu container. Wraps a trigger element (`slot="trigger"`) and floating content. The element handles `.is-open` toggling, outside-click dismiss, and <kbd>Escape</kbd>-key close internally.
|
|
801
|
+
|
|
802
|
+
```html
|
|
803
|
+
<glk-popover placement="end">
|
|
804
|
+
<glk-button slot="trigger" variant="secondary" size="auto">Open menu</glk-button>
|
|
805
|
+
<glk-list bare>
|
|
806
|
+
<glk-list-item title="Share" interactive></glk-list-item>
|
|
807
|
+
<glk-list-item title="Duplicate" interactive></glk-list-item>
|
|
808
|
+
<glk-list-item title="Delete" variant="danger" interactive></glk-list-item>
|
|
809
|
+
</glk-list>
|
|
810
|
+
</glk-popover>
|
|
811
|
+
```
|
|
812
|
+
|
|
813
|
+
| Attribute | Type | Default | Description |
|
|
814
|
+
|---|---|---|---|
|
|
815
|
+
| `open` | Boolean | `false` | Visibility state |
|
|
816
|
+
| `placement` | String | `bottom` | `top`, `bottom`, `start`, `end` |
|
|
817
|
+
|
|
818
|
+
Slots:
|
|
819
|
+
- `trigger` — element that toggles the popover on click
|
|
820
|
+
- default — floating content shown while open
|
|
821
|
+
|
|
822
|
+
Methods: `.show()`, `.close()`, `.toggle()`. Property: `.open`.
|
|
823
|
+
Events: `glk-open`, `glk-close`.
|
|
824
|
+
|
|
825
|
+
**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
|
+
|
|
827
|
+
---
|
|
828
|
+
|
|
829
|
+
### 3.21 `<glk-progress>`
|
|
830
|
+
|
|
831
|
+
Progress bar.
|
|
832
|
+
|
|
833
|
+
```html
|
|
834
|
+
<glk-progress label="Upload" value="75" variant="success"></glk-progress>
|
|
835
|
+
<glk-progress label="Storage" value="90" variant="error" size="lg"></glk-progress>
|
|
836
|
+
<glk-progress label="CPU" value="45" size="sm"></glk-progress>
|
|
837
|
+
```
|
|
838
|
+
|
|
839
|
+
| Attribute | Type | Description |
|
|
840
|
+
|---|---|---|
|
|
841
|
+
| `label` | String | Label text shown left of the bar |
|
|
842
|
+
| `value` | Number | 0–100 percentage |
|
|
843
|
+
| `variant` | String | `success`, `error` |
|
|
844
|
+
| `size` | String | `sm`, `md` (default), `lg` |
|
|
845
|
+
|
|
846
|
+
---
|
|
847
|
+
|
|
848
|
+
### 3.22 `<glk-toast>`
|
|
849
|
+
|
|
850
|
+
Notification at the top of the screen that hides itself (shown imperatively) — and, since 1.19.0, one that offers something: "A new version · Reload".
|
|
851
|
+
|
|
852
|
+
```html
|
|
853
|
+
<glk-toast id="toast"></glk-toast>
|
|
854
|
+
|
|
855
|
+
<script>
|
|
856
|
+
toast.show('Saved successfully!', 'success', 3000);
|
|
857
|
+
|
|
858
|
+
// An offer: a button and an ×, stays until one of them is used
|
|
859
|
+
toast.show('A new version is ready', { action: { label: 'Reload', value: 'reload' } });
|
|
860
|
+
toast.addEventListener('glk-action', (e) => { if (e.detail.action === 'reload') location.reload(); });
|
|
861
|
+
toast.addEventListener('glk-close', () => { /* dismissed with × or Escape */ });
|
|
862
|
+
</script>
|
|
863
|
+
```
|
|
864
|
+
|
|
865
|
+
| Attribute | Type | Description |
|
|
866
|
+
|---|---|---|
|
|
867
|
+
| `message` | String | The text |
|
|
868
|
+
| `variant` | String | `success`, `error`, `warning` — colours the icon and the action |
|
|
869
|
+
| `duration` | Number | Auto-dismiss time in ms (default `3000`); `0` keeps it up. With an action there is no timeout unless `duration` is set |
|
|
870
|
+
| `visible` | Boolean | Shown |
|
|
871
|
+
| `action-label`, `action-value` | String | The action button, and the value `glk-action` reports (the label when missing) (since 1.19.0) |
|
|
872
|
+
| `dismissible` | Boolean | An × without an action; an action always brings one (since 1.19.0) |
|
|
873
|
+
| `close-label` | String | Name of the ×, `"Close"` by default (since 1.19.0) |
|
|
874
|
+
|
|
875
|
+
Methods: `.show(message, variant, duration)`, `.show(message, { variant, duration, action: { label, value }, dismissible })`, `.dismiss()`. In the options form, left-out keys keep their attribute, except `action`: a message without one shows no button, and one with an action but no `duration` stays. Calling `show()` again while the toast is up restarts its timer.
|
|
876
|
+
|
|
877
|
+
Slot: `icon` — replaces the built-in icon; leave its stroke unset and it takes the variant colour.
|
|
878
|
+
|
|
879
|
+
Events: `glk-action { action, label }` — the action was used; the toast closes unless a listener calls `preventDefault()` or shows the next message (`show('Restored')`) from the handler. `glk-close` — closed with the × or Escape. `glk-dismiss` — timed out.
|
|
880
|
+
|
|
881
|
+
While the pointer or the focus is on a toast with buttons, it does not time out. The toast is a polite live region (`role="status"`), so the message is announced; hidden, its buttons are inert, and closing it from the keyboard hands the focus back. Where it sits comes from GlassKit's `--gl-toast-top` — set it on an ancestor to place the toast below your header (with `viewport-fit=cover`, add `env(safe-area-inset-top)`).
|
|
882
|
+
|
|
883
|
+
---
|
|
884
|
+
|
|
885
|
+
### 3.23 `<glk-accordion>` + `<glk-accordion-item>`
|
|
886
|
+
|
|
887
|
+
Collapsible sections.
|
|
888
|
+
|
|
889
|
+
```html
|
|
890
|
+
<glk-accordion>
|
|
891
|
+
<glk-accordion-item title="What is GlassKit?" open>
|
|
892
|
+
A glassmorphism CSS component library.
|
|
893
|
+
</glk-accordion-item>
|
|
894
|
+
<glk-accordion-item title="Do I need a framework?">
|
|
895
|
+
No — vanilla HTML works.
|
|
896
|
+
</glk-accordion-item>
|
|
897
|
+
</glk-accordion>
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
**`<glk-accordion>`** — container, no attributes.
|
|
901
|
+
|
|
902
|
+
**`<glk-accordion-item>`**
|
|
903
|
+
|
|
904
|
+
| Attribute | Type | Description |
|
|
905
|
+
|---|---|---|
|
|
906
|
+
| `title` | String | Trigger label |
|
|
907
|
+
| `open` | Boolean | Expanded state |
|
|
908
|
+
|
|
909
|
+
Default slot: body content. Events: `glk-toggle` → `{ open }`. Property: `.open`.
|
|
910
|
+
|
|
911
|
+
---
|
|
912
|
+
|
|
913
|
+
### 3.24 `<glk-list>` + `<glk-list-item>`
|
|
914
|
+
|
|
915
|
+
iOS-style grouped settings list. Items carry a leading icon, title + optional subtitle, and a trailing element. Dividers between items are drawn automatically by GlassKit CSS — **never** add `<hr>` or divider markup manually. Supports section headers, large icons (40×40), multi-line subtitles, trailing value text, and semantic `danger`/`accent` variants.
|
|
916
|
+
|
|
917
|
+
#### Settings-style list with section header and large icons
|
|
918
|
+
|
|
919
|
+
```html
|
|
920
|
+
<glk-list header="Recommendations">
|
|
921
|
+
<glk-list-item title="Review downloaded media" subtitle="Save up to 1.38 GB. Review files and remove as needed." leading-lg wrap interactive>
|
|
922
|
+
<svg slot="leading" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
923
|
+
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
|
924
|
+
<polyline points="7 10 12 15 17 10"/>
|
|
925
|
+
<line x1="12" y1="15" x2="12" y2="3"/>
|
|
926
|
+
</svg>
|
|
927
|
+
<svg slot="trailing" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
928
|
+
<polyline points="9 18 15 12 9 6"/>
|
|
929
|
+
</svg>
|
|
930
|
+
</glk-list-item>
|
|
931
|
+
<glk-list-item title="AusweisApp Bund" subtitle="Version 2.5.1" leading-lg detail="24 MB" interactive>
|
|
932
|
+
<svg slot="leading" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
933
|
+
<rect x="2" y="3" width="20" height="18" rx="2"/>
|
|
934
|
+
<path d="M9 3v18"/><path d="M15 3v18"/>
|
|
935
|
+
</svg>
|
|
936
|
+
<svg slot="trailing" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
937
|
+
<polyline points="9 18 15 12 9 6"/>
|
|
938
|
+
</svg>
|
|
939
|
+
</glk-list-item>
|
|
940
|
+
<glk-list-item title="View all subscriptions" variant="accent" center interactive></glk-list-item>
|
|
941
|
+
</glk-list>
|
|
942
|
+
```
|
|
943
|
+
|
|
944
|
+
#### Compact menu with danger variant
|
|
945
|
+
|
|
946
|
+
```html
|
|
947
|
+
<glk-list>
|
|
948
|
+
<glk-list-item title="Share" center interactive></glk-list-item>
|
|
949
|
+
<glk-list-item title="Duplicate" center interactive></glk-list-item>
|
|
950
|
+
<glk-list-item title="Delete" variant="danger" center interactive></glk-list-item>
|
|
951
|
+
</glk-list>
|
|
952
|
+
```
|
|
953
|
+
|
|
954
|
+
#### Attributes
|
|
955
|
+
|
|
956
|
+
**`<glk-list>`**
|
|
957
|
+
|
|
958
|
+
| Attribute | Type | Description |
|
|
959
|
+
|---|---|---|
|
|
960
|
+
| `header` | String | Section header text — renders as uppercase label above the list |
|
|
961
|
+
| `flush` | Boolean | Edge-to-edge variant — removes side margin and radius |
|
|
962
|
+
| `bare` | Boolean | Strips background, border, shadow — use inside `<glk-popover>` or `<glk-card>` to avoid double glass surfaces |
|
|
963
|
+
|
|
964
|
+
**`<glk-list-item>`**
|
|
965
|
+
|
|
966
|
+
| Attribute | Type | Description |
|
|
967
|
+
|---|---|---|
|
|
968
|
+
| `title` | String | Primary text (medium weight, truncated with ellipsis) |
|
|
969
|
+
| `subtitle` | String | Secondary text (muted, small, truncated) |
|
|
970
|
+
| `interactive` | Boolean | Enables hover / focus / active states and `glk-click` event |
|
|
971
|
+
| `center` | Boolean | Centered single-text variant (for action rows like "Reset to defaults") |
|
|
972
|
+
| `leading-lg` | Boolean | Large 40×40 leading icon with rounded corners (for app icons; use 32×32 SVG or `<img>`) |
|
|
973
|
+
| `wrap` | Boolean | Multi-line subtitle — allows up to 3 lines with ellipsis (requires `subtitle`) |
|
|
974
|
+
| `detail` | String | Muted trailing value text (e.g. file size "24 MB", version number) — rendered before `slot="trailing"` |
|
|
975
|
+
| `variant` | String | Semantic color: `"danger"` (red destructive) or `"accent"` (primary color). Inherits to title and leading icon |
|
|
976
|
+
|
|
977
|
+
#### Slots (on `<glk-list-item>`)
|
|
978
|
+
|
|
979
|
+
| Slot | Purpose |
|
|
980
|
+
|---|---|
|
|
981
|
+
| `leading` | Icon slot (24×24 SVG recommended, or 32×32 with `leading-lg`; use `stroke: currentColor`) |
|
|
982
|
+
| `trailing` | Chevron, badge, or button — rendered after `detail` value |
|
|
983
|
+
|
|
984
|
+
#### Events
|
|
985
|
+
|
|
986
|
+
`<glk-list-item>` emits `glk-click` when clicked, but only if `interactive` is set.
|
|
987
|
+
|
|
988
|
+
#### Internals (for framework authors)
|
|
989
|
+
|
|
990
|
+
Both elements use pure Shadow DOM with slot projection — no child node cloning — so they compose cleanly inside lit-html, HybridsJS, React, Vue, and Svelte templates. `<glk-list>` observes its direct children via `MutationObserver` and sets a `data-last` attribute on the final `<glk-list-item>`; each item's shadow carries a one-line override sheet (`:host([data-last]) .glass-list__item::after { content: none; }`) to suppress the auto-divider on the last row. Divider inset automatically adjusts for `leading-lg` via CSS `:has()`. Zero CSS duplication.
|
|
991
|
+
|
|
992
|
+
---
|
|
993
|
+
|
|
994
|
+
### 3.25 `<glk-segmented>`
|
|
995
|
+
|
|
996
|
+
A small, exclusive choice as one control (since 1.15.0) — traffic light, morning / afternoon, mode. One `<button>` per option, the chosen one marked `aria-pressed="true"` (GlassKit styles exactly that attribute); a value change only re-sets the attribute, never rebuilds, so focus stays on the pressed button. Form-associated (`GlkFormElement`): a surrounding `<form>` receives `name=value`, reset restores the initial value.
|
|
997
|
+
|
|
998
|
+
```html
|
|
999
|
+
<glk-segmented full name="status" label="Status" value="y"
|
|
1000
|
+
options='[{"value":"g","label":"Green","tone":"success"},{"value":"y","label":"Yellow","tone":"warning"},{"value":"r","label":"Red","tone":"error"},{"value":"x","label":"Locked","disabled":true}]'>
|
|
1001
|
+
</glk-segmented>
|
|
1002
|
+
```
|
|
1003
|
+
|
|
1004
|
+
| Attribute | Type | Description |
|
|
1005
|
+
|---|---|---|
|
|
1006
|
+
| `options` | JSON | `[{ value, label, tone?, disabled? }]`; `tone` is `success` \| `warning` \| `error` and puts a dot in that colour before the label |
|
|
1007
|
+
| `value` | String | Value of the chosen option (reflected property) |
|
|
1008
|
+
| `full` | Boolean | Buttons share the width |
|
|
1009
|
+
| `overflow` | String | What happens when the options do not fit: `scroll` — one row that scrolls sideways, the chosen option kept in view; `wrap` — breaks into lines. Without it the row stays one line (since 1.17.0) |
|
|
1010
|
+
| `label` | String | `aria-label` of the group (no default — set it) |
|
|
1011
|
+
| `name` | String | Form field name |
|
|
1012
|
+
|
|
1013
|
+
Properties: `value`, `options` (array or JSON text), `full`, `overflow`, `label`. Events: `glk-change` `{ value }` — only on a change made by the user, not on `.value = …`. Part: `group`.
|
|
1014
|
+
|
|
1015
|
+
With `overflow="scroll"` the chosen option is centred in the row after the first layout, on every value change and when the row's width changes — the row scrolls, never the page. Seven areas on a phone:
|
|
1016
|
+
|
|
1017
|
+
```html
|
|
1018
|
+
<glk-segmented full overflow="scroll" label="Area" value="g"
|
|
1019
|
+
options='[{"value":"a","label":"General"},{"value":"b","label":"Hours"},…,{"value":"g","label":"Privacy"}]'></glk-segmented>
|
|
1020
|
+
```
|
|
1021
|
+
|
|
1022
|
+
The attribute is `overflow`, not `scroll`: `scroll` is a method of every element, and a framework that sets a property whenever the element has one (hybrids, Vue, React 19) would overwrite that method instead of setting the attribute.
|
|
1023
|
+
|
|
1024
|
+
---
|
|
1025
|
+
|
|
1026
|
+
### 3.26 `<glk-steps>`
|
|
1027
|
+
|
|
1028
|
+
Progress through a short flow (since 1.15.0), purely presentational: the current step carries `aria-current="step"`, done steps show an SVG check. The narrowing in tight frames is the CSS block's container query (below 360 px only the current label stays) — the element knows nothing about it, so in a flex row give it a width.
|
|
1029
|
+
|
|
1030
|
+
```html
|
|
1031
|
+
<glk-steps steps="Day,Slot,Dog,Confirm" current="2" label="Progress"></glk-steps>
|
|
1032
|
+
```
|
|
1033
|
+
|
|
1034
|
+
| Attribute | Type | Description |
|
|
1035
|
+
|---|---|---|
|
|
1036
|
+
| `steps` | String | Comma-separated labels (property also accepts an array) |
|
|
1037
|
+
| `current` | Number | 0-based index of the current step (reflected property) |
|
|
1038
|
+
| `label` | String | `aria-label` of the list, optional |
|
|
1039
|
+
|
|
1040
|
+
Part: `steps`.
|
|
1041
|
+
|
|
1042
|
+
---
|
|
1043
|
+
|
|
1044
|
+
### 3.27 `<glk-sheet>`
|
|
1045
|
+
|
|
1046
|
+
Bottom sheet (since 1.15.0) — the mobile sibling of `<glk-modal>`, same API shape. Opening and closing are animated by GlassKit's `.glass-sheet` block: `.is-active` is added one frame after the overlay is unhidden, and on closing the overlay is hidden only after `transitionend` (safety net 400 ms), so no blurred layer idles behind the page; under `prefers-reduced-motion` it switches at once. The Escape listener sits on `document` and is removed in `teardownEvents()`.
|
|
1047
|
+
|
|
1048
|
+
```html
|
|
1049
|
+
<glk-sheet id="sheet" title="Rebook to …">
|
|
1050
|
+
<p>Content: a list, text, a form.</p>
|
|
1051
|
+
<glk-button slot="actions" variant="secondary" onclick="sheet.close()">Close</glk-button>
|
|
1052
|
+
</glk-sheet>
|
|
1053
|
+
<script>sheet.show();</script>
|
|
1054
|
+
```
|
|
1055
|
+
|
|
1056
|
+
| Attribute | Type | Description |
|
|
1057
|
+
|---|---|---|
|
|
1058
|
+
| `open` | Boolean | Shown as an overlay (reflected property) |
|
|
1059
|
+
| `inline` | Boolean | In the flow, no overlay, always visible — e.g. inside a phone frame or a card |
|
|
1060
|
+
| `title` | String | Heading and `aria-label` of the dialog |
|
|
1061
|
+
|
|
1062
|
+
Methods: `show()`, `close()`. Slots: default (content), `actions`. Events: `glk-close` — only when the user closes it (scrim click, Escape), like `<glk-modal>`; `close()` does not emit. Parts: `overlay`, `sheet`, `title`, `body`, `actions`.
|
|
1063
|
+
|
|
1064
|
+
---
|
|
1065
|
+
|
|
1066
|
+
### 3.28 `<glk-empty>`
|
|
1067
|
+
|
|
1068
|
+
Empty state for lists and result pages (since 1.15.0): icon plate, title, short muted text and room for one action. An empty `title` or `text` hides its element instead of leaving an empty paragraph.
|
|
1069
|
+
|
|
1070
|
+
```html
|
|
1071
|
+
<glk-empty title="No bookings yet" text="Pick a day and a dog — the team is looking forward to it.">
|
|
1072
|
+
<svg viewBox="0 0 24 24">…</svg>
|
|
1073
|
+
<glk-button slot="action" variant="primary" size="sm">Book</glk-button>
|
|
1074
|
+
</glk-empty>
|
|
1075
|
+
```
|
|
1076
|
+
|
|
1077
|
+
| Attribute | Type | Description |
|
|
1078
|
+
|---|---|---|
|
|
1079
|
+
| `title` | String | Heading; hidden when empty |
|
|
1080
|
+
| `text` | String | Muted text; hidden when empty |
|
|
1081
|
+
|
|
1082
|
+
Slots: default (an `<svg>` icon, 24 px stroked; a plain circle as fallback), `action`. Parts: `empty`, `icon`, `title`, `text`, `action`.
|
|
1083
|
+
|
|
1084
|
+
### 3.29 `<glk-date-strip>`
|
|
1085
|
+
|
|
1086
|
+
A row of day chips that scrolls sideways (since 1.16.0) — a booking horizon, the days around today. Chips are buttons in a `role="group"`, the chosen one `aria-pressed="true"`, each named with its full date from `Intl` for the `locale`. One tab stop: arrow keys move between chips, Home/End jump to the ends, Enter or Space picks. The chosen chip is centred in the strip after the first layout and on every value change; only the strip scrolls, never the page. A value change re-sets state; `start`, `days`, `today`, `marks` and `locale` rebuild the chips.
|
|
1087
|
+
|
|
1088
|
+
```html
|
|
1089
|
+
<glk-date-strip start="2026-09-22" days="29" value="2026-10-11" label="Pick a day"
|
|
1090
|
+
marks='{"2026-09-22":"success","2026-09-25":{"tone":"error","disabled":true}}'></glk-date-strip>
|
|
1091
|
+
```
|
|
1092
|
+
|
|
1093
|
+
| Attribute | Type | Description |
|
|
1094
|
+
|---|---|---|
|
|
1095
|
+
| `start` | String | First day `YYYY-MM-DD`; today when missing |
|
|
1096
|
+
| `days` | Number | Number of chips (default 7) |
|
|
1097
|
+
| `value` | String | Chosen day `YYYY-MM-DD` |
|
|
1098
|
+
| `today` | String | The underlined day; the browser's day when missing |
|
|
1099
|
+
| `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 |
|
|
1101
|
+
| `label` | String | `aria-label` of the group |
|
|
1102
|
+
|
|
1103
|
+
Event: `glk-change { value }` — only on a change made by the user. Properties: `value`, `marks` (object or JSON text), `locale`, `label`. Part: `strip`.
|
|
1104
|
+
|
|
1105
|
+
---
|
|
1106
|
+
|
|
1107
|
+
### 3.30 `<glk-calendar>`
|
|
1108
|
+
|
|
1109
|
+
One month with a day to pick (since 1.16.0). The days are buttons in a `role="group"`, the chosen one `aria-pressed="true"`, each named with its full date; month title, weekday names and the first day of the week come from `Intl` for the `locale` (`week-start` overrides; Monday where a browser has no week info). One tab stop: arrows move by a day or a week, Home/End to the ends of the week, PageUp/PageDown by a month — past the month's edge the calendar shows that month and emits `glk-month`. Arrows only move focus; Enter or Space picks. Days outside `min`/`max` carry `aria-disabled="true"`: in the arrow path, announced, never picked. A value set to another month flips the calendar there. Form-associated (`GlkFormElement`): a surrounding `<form>` receives `name=value`, reset restores the initial value.
|
|
1110
|
+
|
|
1111
|
+
```html
|
|
1112
|
+
<glk-calendar name="day" value="2026-09-22" min="2026-09-22" label="Pick a day"
|
|
1113
|
+
marks='{"2026-09-24":["error"],"2026-09-27":["success","warning"]}'></glk-calendar>
|
|
1114
|
+
```
|
|
1115
|
+
|
|
1116
|
+
| Attribute | Type | Description |
|
|
1117
|
+
|---|---|---|
|
|
1118
|
+
| `month` | String | Shown month `YYYY-MM`; without it the month of the value, else of today; reflected when the user navigates |
|
|
1119
|
+
| `value` | String | Chosen day `YYYY-MM-DD` |
|
|
1120
|
+
| `today` | String | The day with the warm border; the browser's day when missing |
|
|
1121
|
+
| `min`, `max` | String | Days outside cannot be picked |
|
|
1122
|
+
| `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 |
|
|
1124
|
+
| `week-start` | Number | 0 = Sunday … 6 = Saturday; from the locale when missing |
|
|
1125
|
+
| `label` | String | `aria-label` of the day group |
|
|
1126
|
+
| `prev-label`, `next-label` | String | Names of the nav buttons ("Previous month" / "Next month") |
|
|
1127
|
+
| `name` | String | Form field name |
|
|
1128
|
+
|
|
1129
|
+
Events: `glk-change { value }` on a pick by the user; `glk-month { month }` when the user moves to another month. Properties: `month`, `value`, `marks`, `locale`, `label`. Parts: `calendar`, `head`, `title`, `grid`.
|
|
1130
|
+
|
|
1131
|
+
---
|
|
1132
|
+
|
|
1133
|
+
### 3.31 `<glk-image-picker>`
|
|
1134
|
+
|
|
1135
|
+
One image with a preview, resized on the client before it goes anywhere (since 1.16.0). Choose opens the file dialog from a real button; the file is decoded with `createImageBitmap` (`imageOrientation: 'from-image'`, so a phone photo comes out upright), drawn onto a canvas no larger than `max` and handed out as a data URL in `glk-change`. The `src` property holds it and is not reflected, so a data URL of megabytes never lands in the DOM. A file the browser cannot decode — Chrome and HEIC, a corrupt file — emits `glk-error`. Not form-associated: listen to `glk-change` and upload, or copy the data URL into a hidden field.
|
|
1136
|
+
|
|
1137
|
+
```html
|
|
1138
|
+
<glk-image-picker id="photo" round max="512" label="Profile photo" hint="JPG or PNG"></glk-image-picker>
|
|
1139
|
+
<script>photo.addEventListener('glk-change', e => upload(e.detail.dataUrl));</script>
|
|
1140
|
+
```
|
|
1141
|
+
|
|
1142
|
+
| Attribute | Type | Description |
|
|
1143
|
+
|---|---|---|
|
|
1144
|
+
| `src` | String | Starting image: URL or data URL |
|
|
1145
|
+
| `label` | String | Visible label; names the group |
|
|
1146
|
+
| `hint` | String | Small muted line; none by default |
|
|
1147
|
+
| `round` | Boolean | Circular preview, for avatars |
|
|
1148
|
+
| `max` | Number | Longest edge in px after resizing (default 1024) |
|
|
1149
|
+
| `type`, `quality` | String, Number | Output MIME (default `image/jpeg`; `image/webp` where the browser encodes it, PNG otherwise), quality 0–1 (default 0.82) |
|
|
1150
|
+
| `accept` | String | File dialog filter (default `image/*`) |
|
|
1151
|
+
| `choose-label`, `change-label`, `remove-label` | String | Button texts ("Choose", "Change", "Remove") |
|
|
1152
|
+
|
|
1153
|
+
Events: `glk-change { dataUrl, width, height, size }` after a pick, on remove with an empty `dataUrl` and zeros; `glk-error { message, name }` when the file cannot be decoded. Property: `src` (not reflected). Parts: `picker`, `preview`, `meta`, `label`, `hint`, `actions`.
|
|
1154
|
+
|
|
1155
|
+
---
|
|
1156
|
+
|
|
1157
|
+
## 4. Composition Patterns
|
|
1158
|
+
|
|
1159
|
+
### Login Screen
|
|
1160
|
+
|
|
1161
|
+
```html
|
|
1162
|
+
<div class="glass-bg">
|
|
1163
|
+
<glk-nav>
|
|
1164
|
+
<glk-pill label="Back" onclick="history.back()">
|
|
1165
|
+
<svg viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg>
|
|
1166
|
+
</glk-pill>
|
|
1167
|
+
<glk-pill label="Toggle theme" onclick="toggleTheme()">
|
|
1168
|
+
<svg class="icon-moon" viewBox="0 0 24 24"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
|
|
1169
|
+
</glk-pill>
|
|
1170
|
+
</glk-nav>
|
|
1171
|
+
|
|
1172
|
+
<glk-title>Sign In</glk-title>
|
|
1173
|
+
|
|
1174
|
+
<form>
|
|
1175
|
+
<glk-input label="Email" type="email" name="email" placeholder="you@example.com" required></glk-input>
|
|
1176
|
+
<glk-input label="Password" type="password" name="password" required></glk-input>
|
|
1177
|
+
|
|
1178
|
+
<glk-button variant="primary" type="submit">Log In</glk-button>
|
|
1179
|
+
<glk-button variant="tertiary">Forgot password?</glk-button>
|
|
1180
|
+
</form>
|
|
1181
|
+
</div>
|
|
1182
|
+
```
|
|
1183
|
+
|
|
1184
|
+
### Dashboard with Cards + Tab Bar
|
|
1185
|
+
|
|
1186
|
+
```html
|
|
1187
|
+
<div class="glass-bg glass-bg--has-tab-bar">
|
|
1188
|
+
<glk-nav>
|
|
1189
|
+
<glk-title>Dashboard</glk-title>
|
|
1190
|
+
</glk-nav>
|
|
1191
|
+
|
|
1192
|
+
<glk-card glow>
|
|
1193
|
+
<p>Capture and upload documents.</p>
|
|
1194
|
+
</glk-card>
|
|
1195
|
+
<glk-card glow>
|
|
1196
|
+
<p>Manage and archive contracts.</p>
|
|
1197
|
+
</glk-card>
|
|
1198
|
+
|
|
1199
|
+
<glk-tab-bar>
|
|
1200
|
+
<glk-tab-item label="Home" active>
|
|
1201
|
+
<svg viewBox="0 0 24 24">...</svg>
|
|
1202
|
+
</glk-tab-item>
|
|
1203
|
+
<glk-tab-item label="Contracts" badge="3">
|
|
1204
|
+
<svg viewBox="0 0 24 24">...</svg>
|
|
1205
|
+
</glk-tab-item>
|
|
1206
|
+
<glk-tab-item label="Profile">
|
|
1207
|
+
<svg viewBox="0 0 24 24">...</svg>
|
|
1208
|
+
</glk-tab-item>
|
|
1209
|
+
</glk-tab-bar>
|
|
1210
|
+
</div>
|
|
1211
|
+
```
|
|
1212
|
+
|
|
1213
|
+
### Form Page with Validation
|
|
1214
|
+
|
|
1215
|
+
```html
|
|
1216
|
+
<form id="contract-form">
|
|
1217
|
+
<glk-title>New Contract</glk-title>
|
|
1218
|
+
|
|
1219
|
+
<glk-input label="Contract Name" name="name" required></glk-input>
|
|
1220
|
+
<glk-select label="Category" name="category">
|
|
1221
|
+
<option value="">Please select…</option>
|
|
1222
|
+
<option value="insurance">Insurance</option>
|
|
1223
|
+
<option value="rental">Rental</option>
|
|
1224
|
+
</glk-select>
|
|
1225
|
+
<glk-textarea label="Notes" name="notes" rows="4"></glk-textarea>
|
|
1226
|
+
<glk-toggle label="Enable reminder" name="reminder"></glk-toggle>
|
|
1227
|
+
|
|
1228
|
+
<glk-button variant="primary" type="submit">Save</glk-button>
|
|
1229
|
+
</form>
|
|
1230
|
+
|
|
1231
|
+
<script>
|
|
1232
|
+
document.getElementById('contract-form').addEventListener('submit', (e) => {
|
|
1233
|
+
e.preventDefault();
|
|
1234
|
+
const data = new FormData(e.target);
|
|
1235
|
+
console.log(Object.fromEntries(data)); // { name, category, notes, reminder? }
|
|
1236
|
+
});
|
|
1237
|
+
</script>
|
|
1238
|
+
```
|
|
1239
|
+
|
|
1240
|
+
### Delete Confirmation Modal
|
|
1241
|
+
|
|
1242
|
+
```html
|
|
1243
|
+
<glk-button variant="secondary" size="auto"
|
|
1244
|
+
onclick="document.getElementById('confirm').show()">
|
|
1245
|
+
Delete contract
|
|
1246
|
+
</glk-button>
|
|
1247
|
+
|
|
1248
|
+
<glk-modal id="confirm" title="Delete contract?">
|
|
1249
|
+
<p>This action cannot be undone.</p>
|
|
1250
|
+
<div slot="actions">
|
|
1251
|
+
<button class="glass-modal__action" onclick="document.getElementById('confirm').close()">Cancel</button>
|
|
1252
|
+
<button class="glass-modal__action glass-modal__action--danger"
|
|
1253
|
+
onclick="handleDelete(); document.getElementById('confirm').close()">Delete</button>
|
|
1254
|
+
</div>
|
|
1255
|
+
</glk-modal>
|
|
1256
|
+
```
|
|
1257
|
+
|
|
1258
|
+
### iOS-style Settings Screen (List + Popover)
|
|
1259
|
+
|
|
1260
|
+
Full example reproducing the native iOS Settings layout with two grouped lists and an inline action menu.
|
|
1261
|
+
|
|
1262
|
+
```html
|
|
1263
|
+
<div class="glass-bg">
|
|
1264
|
+
<glk-nav>
|
|
1265
|
+
<glk-pill label="Back" onclick="history.back()">
|
|
1266
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="15 18 9 12 15 6"/></svg>
|
|
1267
|
+
</glk-pill>
|
|
1268
|
+
<glk-title>Settings</glk-title>
|
|
1269
|
+
|
|
1270
|
+
<glk-popover placement="end">
|
|
1271
|
+
<glk-pill slot="trigger" label="More">
|
|
1272
|
+
<svg viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="5" r="1.5"/><circle cx="12" cy="12" r="1.5"/><circle cx="12" cy="19" r="1.5"/></svg>
|
|
1273
|
+
</glk-pill>
|
|
1274
|
+
<glk-list bare>
|
|
1275
|
+
<glk-list-item title="Edit profile" interactive></glk-list-item>
|
|
1276
|
+
<glk-list-item title="Sign out" interactive></glk-list-item>
|
|
1277
|
+
</glk-list>
|
|
1278
|
+
</glk-popover>
|
|
1279
|
+
</glk-nav>
|
|
1280
|
+
|
|
1281
|
+
<glk-list header="Account">
|
|
1282
|
+
<glk-list-item title="Marcel Jungherz" subtitle="marcel@jungherz.com" leading-lg interactive>
|
|
1283
|
+
<svg slot="leading" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
|
|
1284
|
+
<svg slot="trailing" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 18 15 12 9 6"/></svg>
|
|
1285
|
+
</glk-list-item>
|
|
1286
|
+
<glk-list-item title="Subscriptions" interactive detail="$34.94">
|
|
1287
|
+
<svg slot="leading" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="5" width="20" height="14" rx="2"/><line x1="2" y1="10" x2="22" y2="10"/></svg>
|
|
1288
|
+
<svg slot="trailing" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 18 15 12 9 6"/></svg>
|
|
1289
|
+
</glk-list-item>
|
|
1290
|
+
</glk-list>
|
|
1291
|
+
|
|
1292
|
+
<glk-list header="Notifications">
|
|
1293
|
+
<glk-list-item title="Push notifications" interactive>
|
|
1294
|
+
<svg slot="leading" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 8a6 6 0 1 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg>
|
|
1295
|
+
<span slot="trailing">On</span>
|
|
1296
|
+
</glk-list-item>
|
|
1297
|
+
<glk-list-item title="Email digest" interactive>
|
|
1298
|
+
<svg slot="leading" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22 6 12 13 2 6"/></svg>
|
|
1299
|
+
<span slot="trailing">Weekly</span>
|
|
1300
|
+
</glk-list-item>
|
|
1301
|
+
<glk-list-item title="Reset to defaults" variant="danger" center interactive></glk-list-item>
|
|
1302
|
+
</glk-list>
|
|
1303
|
+
</div>
|
|
1304
|
+
```
|
|
1305
|
+
|
|
1306
|
+
Key points:
|
|
1307
|
+
- Section headers via `header` attribute — no need for external `<p>` labels
|
|
1308
|
+
- Large leading icon via `leading-lg` for profile-style rows
|
|
1309
|
+
- Trailing value via `detail` attribute — muted metadata text before the chevron
|
|
1310
|
+
- Auto-dividers — no manual `<hr>` between rows; inset adapts for large icons automatically
|
|
1311
|
+
- `variant="danger"` on destructive "Reset" row — red text signals caution
|
|
1312
|
+
- Popover anchored in nav — `placement="end"` aligns the menu to the right edge of the trigger
|
|
1313
|
+
- Bare list inside popover — `<glk-list bare>` strips the inner glass surface so only the popover's glass shows
|
|
1314
|
+
|
|
1315
|
+
---
|
|
1316
|
+
|
|
1317
|
+
## 5. State & Event Overview
|
|
1318
|
+
|
|
1319
|
+
| Element | Key Property | HTML Attribute | Event(s) | Detail |
|
|
1320
|
+
|---|---|---|---|---|
|
|
1321
|
+
| `<glk-button>` | — | `disabled` | `glk-click` | — |
|
|
1322
|
+
| `<glk-pill>` | — | `disabled` | `glk-click` | — |
|
|
1323
|
+
| `<glk-tab-item>` | — | `active` | `glk-tab-select` | — |
|
|
1324
|
+
| `<glk-tab-accessory>` | — | `disabled` | `glk-click` | — |
|
|
1325
|
+
| `<glk-input>` | `.value` | `value` | `glk-input`, `glk-change` (+ native `input`, `change`) | `{ value }` |
|
|
1326
|
+
| `<glk-textarea>` | `.value` | `value` | `glk-input`, `glk-change` | `{ value }` |
|
|
1327
|
+
| `<glk-select>` | `.value` | `value` | `glk-change` | `{ value }` |
|
|
1328
|
+
| `<glk-search>` | `.value` | `value` | `glk-input`, `glk-change` | `{ value }` |
|
|
1329
|
+
| `<glk-toggle>` | `.checked` | `checked` | `glk-change` (+ native `change`) | `{ checked }` |
|
|
1330
|
+
| `<glk-checkbox>` | `.checked` | `checked` | `glk-change` | `{ checked }` |
|
|
1331
|
+
| `<glk-radio>` | `.checked` | `checked` | `glk-change` | `{ checked, value }` |
|
|
1332
|
+
| `<glk-range>` | `.value` | `value` | `glk-input`, `glk-change` | `{ value }` |
|
|
1333
|
+
| `<glk-modal>` | `.open` | `open` | `glk-close` | — |
|
|
1334
|
+
| `<glk-popover>` | `.open` | `open` | `glk-open`, `glk-close` | — |
|
|
1335
|
+
| `<glk-accordion-item>` | `.open` | `open` | `glk-toggle` | `{ open }` |
|
|
1336
|
+
| `<glk-list-item>` | — | `interactive` | `glk-click` (only when interactive) | — |
|
|
1337
|
+
| `<glk-segmented>` | `.value` | `value` | `glk-change` | `{ value }` |
|
|
1338
|
+
| `<glk-sheet>` | `.open` | `open` | `glk-close` (user close only) | — |
|
|
1339
|
+
| `<glk-date-strip>` | `.value` | `value` | `glk-change` (user only) | `{ value }` |
|
|
1340
|
+
| `<glk-calendar>` | `.value`, `.month` | `value`, `month` | `glk-change` (user only), `glk-month` (user only) | `{ value }`, `{ month }` |
|
|
1341
|
+
| `<glk-image-picker>` | `.src` (not reflected) | `src` | `glk-change`, `glk-error` | `{ dataUrl, width, height, size }`, `{ message, name }` |
|
|
1342
|
+
| `<glk-badge>` | `.selected` | `interactive`, `selected` | `glk-click` (only when interactive) | — |
|
|
1343
|
+
| `<glk-toast>` | `.show()` | `visible` | `glk-action`, `glk-close`, `glk-dismiss` | `{ action, label }`, —, — |
|
|
1344
|
+
|
|
1345
|
+
All `glk-*` events bubble and are `composed: true`, so they pierce shadow boundaries naturally.
|
|
1346
|
+
|
|
1347
|
+
---
|
|
1348
|
+
|
|
1349
|
+
## 6. Rules & Common Mistakes
|
|
1350
|
+
|
|
1351
|
+
### Always follow
|
|
1352
|
+
|
|
1353
|
+
1. **`data-theme` on `<html>`** — never on `<body>` or individual elements; the global MutationObserver only watches `<html>`.
|
|
1354
|
+
2. **Form elements belong inside a `<form>`** — otherwise `ElementInternals.setFormValue()` has no effect and `new FormData()` will be empty.
|
|
1355
|
+
3. **`<glk-list-item>` must be a direct child of `<glk-list>`** — nested deeper, it won't be observed for the auto-divider last-item detection.
|
|
1356
|
+
4. **Popover trigger via `slot="trigger"`** — don't reference external elements by ID. The popover's built-in click handler listens on the trigger slot.
|
|
1357
|
+
5. **Toggle popovers through the API** — `popover.open = true` or `popover.toggle()`, never `classList.add('is-open')` on internal nodes.
|
|
1358
|
+
6. **Use `<glk-list bare>` inside `<glk-popover>`** — otherwise you get a double glass surface.
|
|
1359
|
+
7. **Import the library once** — `import '@jungherz-de/glasskit-elements'` registers all elements via `customElements.define`. Multiple imports are idempotent but unnecessary.
|
|
1360
|
+
8. **SVG icons: `stroke: currentColor, stroke-width: 2`** — matches GlassKit's icon convention and inherits the token-driven text color.
|
|
1361
|
+
9. **`title` on `<glk-sheet>`, `<glk-modal>`, `<glk-list-item>`, `<glk-accordion-item>` and `<glk-empty>` is the heading, not a tooltip** (since 1.16.1) — the element reads it and takes the attribute off the host, so `el.getAttribute('title')` is `null` after upgrade; read and write `el.title` instead. Every other element keeps the native `title` tooltip.
|
|
1362
|
+
|
|
1363
|
+
### Common mistakes
|
|
1364
|
+
|
|
1365
|
+
| Mistake | Correction |
|
|
1366
|
+
|---|---|
|
|
1367
|
+
| `onclick="popover.togglePopover()"` | Collides with the native `HTMLElement.togglePopover()` method (HTML Popover API). Use `.toggle()` on the `<glk-popover>` element, or set the `open` attribute. |
|
|
1368
|
+
| `<hr>` or `<glk-divider>` manually between `<glk-list-item>`s | Remove it — dividers render automatically via `::after`. |
|
|
1369
|
+
| `<glk-list>` inside `<glk-popover>` without `bare` | Add `bare` to the inner list so only the popover surface has glass. |
|
|
1370
|
+
| `document.querySelector('.glass-modal').classList.add('is-active')` | Set `<glk-modal>.open = true` instead — the element manages `.is-active` internally. |
|
|
1371
|
+
| `data-theme` directly on a `<glk-*>` element | Set it on `<html>`; theme propagates automatically through the MutationObserver. |
|
|
1372
|
+
| Form element outside `<form>` | Wrap in a `<form>`; `ElementInternals` needs a form owner. |
|
|
1373
|
+
| Overriding shadow styles with external CSS | Use CSS custom properties (`--gl-color-primary`, etc.) — they pierce shadow roots. Hard-coded selectors inside shadow roots cannot be targeted externally. |
|
|
1374
|
+
| Forgetting `slot="trigger"` on the popover trigger element | Without it, the popover's internal click handler can't identify the trigger and never toggles. |
|
|
1375
|
+
| Using `open` as a method name (`mypopover.open()`) | `open` is a reflected property. Use `.show()` / `.close()` / `.toggle()` for imperative control. |
|
|
1376
|
+
| `<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
|
+
| `<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
|
+
| `<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. |
|
|
1379
|
+
|
|
1380
|
+
---
|
|
1381
|
+
|
|
1382
|
+
## 7. Quick Reference
|
|
1383
|
+
|
|
1384
|
+
| Tag | Category | Key attributes | Key slots | Key events |
|
|
1385
|
+
|---|---|---|---|---|
|
|
1386
|
+
| `<glk-nav>` | Navigation | — | default | — |
|
|
1387
|
+
| `<glk-pill>` | Navigation | `label`, `disabled` | default (icon) | `glk-click` |
|
|
1388
|
+
| `<glk-tab-bar>` | Navigation | `static`, `floating` | default | — |
|
|
1389
|
+
| `<glk-tab-item>` | Navigation | `label`, `active`, `badge` | default (icon) | `glk-tab-select` |
|
|
1390
|
+
| `<glk-tab-dock>` | Navigation | `accessory-left` | default | — |
|
|
1391
|
+
| `<glk-tab-accessory>` | Navigation | `label`, `variant`, `disabled` | default (icon) | `glk-click` |
|
|
1392
|
+
| `<glk-avatar>` | Content | `size`, `src` | default (initials) | — |
|
|
1393
|
+
| `<glk-badge>` | Content | `variant`, `interactive`, `selected` | default | `glk-click` (only when interactive) |
|
|
1394
|
+
| `<glk-empty>` | Content | `title`, `text` | default (icon), `action` | — |
|
|
1395
|
+
| `<glk-segmented>` | Forms | `options`, `value`, `full`, `overflow`, `label`, `name` | — | `glk-change` |
|
|
1396
|
+
| `<glk-steps>` | Navigation | `steps`, `current`, `label` | — | — |
|
|
1397
|
+
| `<glk-sheet>` | Feedback | `open`, `inline`, `title` | default, `actions` | `glk-close` |
|
|
1398
|
+
| `<glk-date-strip>` | Navigation | `start`, `days`, `value`, `today`, `marks`, `locale`, `label` | — | `glk-change` |
|
|
1399
|
+
| `<glk-calendar>` | Forms | `month`, `value`, `today`, `min`, `max`, `marks`, `locale`, `week-start`, `label`, `name` | — | `glk-change`, `glk-month` |
|
|
1400
|
+
| `<glk-image-picker>` | Forms | `src`, `label`, `hint`, `round`, `max`, `type`, `quality`, `accept` | — | `glk-change`, `glk-error` |
|
|
1401
|
+
| `<glk-card>` | Content | `glow` | default | — |
|
|
1402
|
+
| `<glk-divider>` | Content | — | — | — |
|
|
1403
|
+
| `<glk-status>` | Content | `message` | — | — |
|
|
1404
|
+
| `<glk-title>` | Content | — | default | — |
|
|
1405
|
+
| `<glk-button>` | Buttons | `variant`, `size`, `disabled`, `type` | default | `glk-click` |
|
|
1406
|
+
| `<glk-checkbox>` | Forms | `label`, `checked`, `disabled`, `name`, `value` | — | `glk-change` |
|
|
1407
|
+
| `<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` |
|
|
1409
|
+
| `<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` |
|
|
1412
|
+
| `<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` |
|
|
1414
|
+
| `<glk-modal>` | Feedback | `open`, `title` | default, `actions` | `glk-close` |
|
|
1415
|
+
| `<glk-popover>` | Feedback | `open`, `placement` | `trigger`, default | `glk-open`, `glk-close` |
|
|
1416
|
+
| `<glk-progress>` | Feedback | `value`, `label`, `variant`, `size` | — | — |
|
|
1417
|
+
| `<glk-toast>` | Feedback | `message`, `variant`, `duration`, `visible`, `action-label`, `action-value`, `dismissible`, `close-label` | `icon` | `glk-action`, `glk-close`, `glk-dismiss` |
|
|
1418
|
+
| `<glk-accordion>` | Containers | — | default | — |
|
|
1419
|
+
| `<glk-accordion-item>` | Containers | `title`, `open` | default | `glk-toggle` |
|
|
1420
|
+
| `<glk-list>` | Containers | `header`, `flush`, `bare` | default | — |
|
|
1421
|
+
| `<glk-list-item>` | Containers | `title`, `subtitle`, `interactive`, `center`, `leading-lg`, `wrap`, `detail`, `variant` | `leading`, `trailing` | `glk-click` (when interactive) |
|
|
1422
|
+
|
|
1423
|
+
---
|
|
1424
|
+
|
|
1425
|
+
## 8. Framework Integration
|
|
1426
|
+
|
|
1427
|
+
GlassKit Elements are standards-compliant Custom Elements. They work in every major framework:
|
|
1428
|
+
|
|
1429
|
+
### Vanilla HTML / JS
|
|
1430
|
+
|
|
1431
|
+
```html
|
|
1432
|
+
<script type="module" src="https://cdn.jsdelivr.net/npm/@jungherz-de/glasskit-elements/dist/glasskit-elements.esm.js"></script>
|
|
1433
|
+
<glk-button variant="primary" onclick="alert('clicked')">Click</glk-button>
|
|
1434
|
+
```
|
|
1435
|
+
|
|
1436
|
+
### React
|
|
1437
|
+
|
|
1438
|
+
```jsx
|
|
1439
|
+
import '@jungherz-de/glasskit-elements';
|
|
1440
|
+
|
|
1441
|
+
function App() {
|
|
1442
|
+
return (
|
|
1443
|
+
<glk-input
|
|
1444
|
+
label="Name"
|
|
1445
|
+
value={name}
|
|
1446
|
+
onGlkInput={(e) => setName(e.detail.value)}
|
|
1447
|
+
/>
|
|
1448
|
+
);
|
|
1449
|
+
}
|
|
1450
|
+
```
|
|
1451
|
+
|
|
1452
|
+
For React 18, custom-event listeners use the lowercase `onglkchange` form, or attach manually via `ref` + `addEventListener`. React 19 supports `onGlkChange`-style camelCase directly.
|
|
1453
|
+
|
|
1454
|
+
### Vue 3
|
|
1455
|
+
|
|
1456
|
+
```vue
|
|
1457
|
+
<template>
|
|
1458
|
+
<glk-input
|
|
1459
|
+
label="Name"
|
|
1460
|
+
:value="name"
|
|
1461
|
+
@glk-input="e => name = e.detail.value"
|
|
1462
|
+
/>
|
|
1463
|
+
</template>
|
|
1464
|
+
|
|
1465
|
+
<script setup>
|
|
1466
|
+
import '@jungherz-de/glasskit-elements';
|
|
1467
|
+
import { ref } from 'vue';
|
|
1468
|
+
const name = ref('');
|
|
1469
|
+
</script>
|
|
1470
|
+
```
|
|
1471
|
+
|
|
1472
|
+
### Svelte
|
|
1473
|
+
|
|
1474
|
+
```svelte
|
|
1475
|
+
<script>
|
|
1476
|
+
import '@jungherz-de/glasskit-elements';
|
|
1477
|
+
let name = '';
|
|
1478
|
+
</script>
|
|
1479
|
+
|
|
1480
|
+
<glk-input
|
|
1481
|
+
label="Name"
|
|
1482
|
+
value={name}
|
|
1483
|
+
on:glk-input={e => name = e.detail.value}
|
|
1484
|
+
/>
|
|
1485
|
+
```
|
|
1486
|
+
|
|
1487
|
+
### lit-html / Hybrids
|
|
1488
|
+
|
|
1489
|
+
```js
|
|
1490
|
+
import { html } from 'lit-html';
|
|
1491
|
+
import '@jungherz-de/glasskit-elements';
|
|
1492
|
+
|
|
1493
|
+
const template = (items) => html`
|
|
1494
|
+
<glk-list>
|
|
1495
|
+
${items.map(item => html`
|
|
1496
|
+
<glk-list-item title=${item.title} subtitle=${item.subtitle} ?interactive=${true}>
|
|
1497
|
+
${item.icon ? html`<svg slot="leading">...</svg>` : ''}
|
|
1498
|
+
</glk-list-item>
|
|
1499
|
+
`)}
|
|
1500
|
+
</glk-list>
|
|
1501
|
+
`;
|
|
1502
|
+
```
|
|
1503
|
+
|
|
1504
|
+
Framework-safe note: `<glk-list>` and `<glk-list-item>` use pure slot projection — no child cloning — so lit-html parts, Hybrids templates, and React virtual-DOM bindings stay intact across re-renders.
|
|
1505
|
+
|
|
1506
|
+
### SSR
|
|
1507
|
+
|
|
1508
|
+
GlassKit Elements are currently **client-side only** — they rely on `document.documentElement`, `adoptedStyleSheets`, and `ElementInternals` which are unavailable during server-side rendering. In SSR frameworks (Next.js, Nuxt, SvelteKit, Astro), use client-only directives or dynamic imports to defer hydration.
|
|
1509
|
+
|
|
1510
|
+
---
|
|
1511
|
+
|
|
1512
|
+
## 9. Custom Theming
|
|
1513
|
+
|
|
1514
|
+
Theming works identically to GlassKit CSS — define custom-property overrides in your global stylesheet; they pass through shadow boundaries automatically:
|
|
1515
|
+
|
|
1516
|
+
```css
|
|
1517
|
+
:root {
|
|
1518
|
+
--gl-color-primary: #007AFF;
|
|
1519
|
+
--gl-color-primary-dark: #0055CC;
|
|
1520
|
+
--gl-color-primary-mid: #0066E0;
|
|
1521
|
+
|
|
1522
|
+
--gl-border-warm: rgba(0, 122, 255, 0.35);
|
|
1523
|
+
--gl-border-focus: rgba(0, 122, 255, 0.60);
|
|
1524
|
+
|
|
1525
|
+
--gl-shadow-btn-primary: 0 6px 24px rgba(0, 100, 220, 0.35),
|
|
1526
|
+
0 2px 8px rgba(0, 0, 0, 0.15);
|
|
1527
|
+
--gl-shadow-focus: 0 0 0 3px rgba(0, 122, 255, 0.3);
|
|
1528
|
+
}
|
|
1529
|
+
```
|
|
1530
|
+
|
|
1531
|
+
See the class-based [GlassKit CSS `SKILL.md`](https://github.com/JUNGHERZ/GlassKit/blob/main/SKILL.md) Section 2 for the complete list of design tokens (colors, surfaces, borders, blur, radii, spacing, shadows, typography).
|
|
1532
|
+
|
|
1533
|
+
---
|
|
1534
|
+
|
|
1535
|
+
## 10. Architecture Notes
|
|
1536
|
+
|
|
1537
|
+
| Concept | Location |
|
|
1538
|
+
|---|---|
|
|
1539
|
+
| Base class | `src/base.js` → `GlkElement`, `GlkFormElement` |
|
|
1540
|
+
| Theme sync | Single module-level `MutationObserver` in `base.js` |
|
|
1541
|
+
| Adopted stylesheets | `glassSheet` (from `@jungherz-de/glasskit/glasskit-styles.js`) + module-level `hostSheet` / `inlineHostSheet` |
|
|
1542
|
+
| Per-component structure | One `.js` file per element in `src/components/{category}/glk-{name}.js` |
|
|
1543
|
+
| Barrel | `src/index.js` — exports and registers all 36 elements, and exports `GlkElement` / `GlkFormElement` (since 1.14.0) |
|
|
1544
|
+
| 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
|
+
|
|
1546
|
+
Each element is a subclass of `GlkElement` (or `GlkFormElement` for form controls) and follows a consistent lifecycle:
|
|
1547
|
+
|
|
1548
|
+
1. `constructor()` — attach open shadow, adopt GlassKit stylesheet + host display sheet
|
|
1549
|
+
2. `connectedCallback()` — create theme wrapper, call `render()`, call `setupEvents()`, register for global theme sync
|
|
1550
|
+
3. `render()` — build shadow DOM tree (override per component)
|
|
1551
|
+
4. `setupEvents()` — attach DOM listeners (override per component)
|
|
1552
|
+
5. `attributeChangedCallback()` — call `onAttributeChanged()` (override per component)
|
|
1553
|
+
6. `disconnectedCallback()` — unregister, call `teardownEvents()`
|
|
1554
|
+
|
|
1555
|
+
The `GlkFormElement` subclass adds `ElementInternals` for native form participation via `this.setFormValue(value)`, plus `formResetCallback` → `resetValue()` and `formStateRestoreCallback` → `restoreValue()`.
|