@guildofgleks/ui 21.6.0 → 21.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +326 -82
- package/CHANGELOG.md +269 -2
- package/README.md +98 -20
- package/TOKENS.md +10 -7
- package/fesm2022/guildofgleks-ui.mjs +1067 -1383
- package/fesm2022/guildofgleks-ui.mjs.map +1 -1
- package/package.json +1 -1
- package/styles/button.css +11 -32
- package/styles/index.css +5 -0
- package/styles/presets/bevel.css +82 -0
- package/styles/presets/ledger.css +66 -0
- package/styles/presets/material.css +111 -0
- package/styles/presets/one-dark.css +22 -5
- package/styles/presets/one-light.css +17 -4
- package/styles/presets/parchment.css +63 -0
- package/styles/presets/parchment.fonts.css +29 -0
- package/styles/presets/primeng.css +121 -0
- package/styles/presets/slate.css +26 -8
- package/styles/presets/terminal.css +67 -0
- package/styles/presets/terminal.fonts.css +26 -0
- package/styles/ripple.css +94 -0
- package/styles/surfaces.css +151 -0
- package/styles/theme.css +620 -516
- package/types/guildofgleks-ui.d.ts +359 -36
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,273 @@ All notable changes to `@guildofgleks/ui` are documented here. Format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.0.0/); this project has not yet
|
|
5
5
|
reached 1.0, so breaking changes may land in minor versions.
|
|
6
6
|
|
|
7
|
+
## [21.7.0] - 29.08.2026
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **Every shipped theme now meets WCAG AA, and the check runs in CI.** Nine gated failures across
|
|
12
|
+
five palettes were fixed: muted text in `slate`, `one-dark` and `one-light`; the filled button's
|
|
13
|
+
label, at rest and on hover, in `light`, `primeng`, `one-light` and `slate`. `check:contrast` is
|
|
14
|
+
now a CI step — it was deliberately kept out while any finding was open, because a permanently
|
|
15
|
+
red step over a known condition teaches everyone to ignore CI.
|
|
16
|
+
|
|
17
|
+
**`one-dark` and `one-light` changed colour, and that is a trade you may notice.** They
|
|
18
|
+
reproduce a named editor palette; `#5c6370` is One Dark's own comment colour and reads at 2.32:1
|
|
19
|
+
on its own background — fine for code, well under AA for UI text. Muted text is now `#9099a8`
|
|
20
|
+
(dark) and `#6e6f77` (light). If you depended on the exact original hues, set
|
|
21
|
+
`--gog-muted-text-color` back in your own theme block.
|
|
22
|
+
|
|
23
|
+
- **Tokens read from TypeScript now resolve `calc()` instead of silently falling back.**
|
|
24
|
+
`--gog-scroll-thumb-min-size`, `--gog-tooltip-gap`, `--gog-tooltip-z` and `--gog-dropdown-z` were
|
|
25
|
+
read with `parseFloat`, which returns `NaN` for `calc(...)` — and every caller turned that into
|
|
26
|
+
its default without a word. **This is a consumer-facing fix:** writing
|
|
27
|
+
`--gog-scroll-thumb-min-size: calc(2rem + 4px)` in your own theme now works, where before it
|
|
28
|
+
quietly became 32px. `rem`, `em` and `%` work too.
|
|
29
|
+
|
|
30
|
+
- **Two WCAG AA contrast fixes, both on a filled button's own label.** The library's own `light`
|
|
31
|
+
theme: `--gog-accent-color` `#9e6f00` → `#926600` (white on the old value was 4.44:1, under
|
|
32
|
+
AA's 4.5), and its hover fill `#c88e00` → `#7a5500` — the hover is now _darker_ than the rest
|
|
33
|
+
state rather than brighter, because gold light enough to read as "a brighter gleam" cannot
|
|
34
|
+
carry white text at all (the old hover was 2.87:1, worse than the rest state everyone had
|
|
35
|
+
noticed). `primeng`: moved one step down Aura's own blue ramp, `#3b82f6` → `#2563eb`
|
|
36
|
+
(500 → 600), rather than inventing a colour outside the palette it reproduces; 3.68:1 → 5.17:1.
|
|
37
|
+
|
|
38
|
+
**If you were overriding either token, nothing breaks** — but a theme built on top of `light`'s
|
|
39
|
+
gold may want to re-check its own derived shades. `check:contrast` now also tests the hover
|
|
40
|
+
fill, which was never checked before; `one-dark`, `one-light` and `slate` keep their known
|
|
41
|
+
findings on purpose (see `docs/backlog.md`).
|
|
42
|
+
|
|
43
|
+
- **Three new presets complete the catalogue: `terminal`, `bevel` and `parchment`.** Retro and
|
|
44
|
+
Historical, the two families `docs/themes.md` had left unbuilt. `terminal` is green phosphor on
|
|
45
|
+
an unlit screen, monospaced throughout, square, no motion. `bevel` is the early-web desktop —
|
|
46
|
+
grey panels, raised buttons, sunken fields, navy — and is the first theme to use
|
|
47
|
+
`--gog-border-style: outset`/`inset`, which is what the character layer carries a border _style_
|
|
48
|
+
for. `parchment` is ink on laid paper: old-style serif, oxblood accent, roomy margins. Each is
|
|
49
|
+
`@import '@guildofgleks/ui/styles/presets/<name>.css'`, then `data-theme="<name>"`. All three
|
|
50
|
+
pass `check:contrast` on every gated pair.
|
|
51
|
+
|
|
52
|
+
- **Optional webfont files, and the rule they exist to keep: a preset never makes a network
|
|
53
|
+
request.** Each preset sets a font stack that resolves to a real system face. Where a webfont
|
|
54
|
+
is worth offering, it is a separate file the consumer imports _after_ the preset —
|
|
55
|
+
`presets/terminal.fonts.css` (IBM Plex Mono) and `presets/parchment.fonts.css` (EB Garamond).
|
|
56
|
+
Import the preset alone and nothing is downloaded.
|
|
57
|
+
|
|
58
|
+
- **`slate`, `one-dark` and `one-light` now set a character, not just a palette** — so every
|
|
59
|
+
shipped preset does. They were palette-only, which made them recoloured defaults: they wore another product's
|
|
60
|
+
colours on this library's shape. Each now sets the character layer and a density —
|
|
61
|
+
`slate` becomes the catalogue's _soft modern_ entry (12px corners, hairline borders, roomier at
|
|
62
|
+
`1.05`), and `one-dark`/`one-light` become editor chrome (4px corners, compact at `0.9`,
|
|
63
|
+
sentence-case labels), deliberately identical to each other so a toggle between them changes
|
|
64
|
+
tone and nothing else. **Palettes are unchanged**; `check:contrast` reports the same eight
|
|
65
|
+
findings as before. See `docs/themes.md` iteration 4.
|
|
66
|
+
|
|
67
|
+
- **`--gog-density`: one number that sets the spacing of the whole library.** A theme makes
|
|
68
|
+
every component tighter or roomier with `--gog-density: 0.9` — no component tokens named, no
|
|
69
|
+
list to keep up to date as components are added. It multiplies a new fourteen-step spacing
|
|
70
|
+
scale (`--gog-space-2` … `--gog-space-48`, named for their pixel value at density 1), and
|
|
71
|
+
every padding and gap in `theme.css` now derives from that scale. The three shipped presets
|
|
72
|
+
that carry a character layer use it: `ledger` at `0.9` (administrative software packed a
|
|
73
|
+
screen), `primeng` at `0.95`, `material` at `1.1` (M3's posture is generous by design).
|
|
74
|
+
|
|
75
|
+
The five existing `--gog-space-xs|sm|md|lg|2xl` names are kept as aliases with their exact
|
|
76
|
+
previous values, so no existing theme or consumer stylesheet changes. **Nothing moves at
|
|
77
|
+
density 1** except thirteen paddings that were never on a grid to begin with — five of them
|
|
78
|
+
fractional `rem` values computing to 3.6px, 4.4px, 4.8px, 7.2px and 9.6px — which now round
|
|
79
|
+
to the nearest 2px step. The largest single change is 4px, on `--gog-panel-slg-padding-x`.
|
|
80
|
+
See `docs/themes.md` iteration 6.
|
|
81
|
+
|
|
82
|
+
- **A new shipped preset: `ledger`** — the square-cornered, hard-shadowed, system-font
|
|
83
|
+
administrative-software identity. Beige and grey, a muted navy accent, uniform 1px borders,
|
|
84
|
+
zero corner radius, a hard offset shadow with no blur, and every transition duration set to
|
|
85
|
+
`0s`. `@import '@guildofgleks/ui/styles/presets/ledger.css'`, then `data-theme="ledger"`.
|
|
86
|
+
Palette-plus-character-layer only, like `material`/`primeng` below but with no per-component
|
|
87
|
+
overrides needed at all — see `docs/themes.md` iteration 4. Named `ledger`, not the plan's own
|
|
88
|
+
"Classic" (that names the catalogue _family_, not this preset) — `ui-showcase` already uses
|
|
89
|
+
"Classic" as the display label for `data-theme="light"`.
|
|
90
|
+
|
|
91
|
+
- **Two new shipped presets: `material` and `primeng`** — full visual identities (Material
|
|
92
|
+
Design 3 baseline and PrimeNG's Aura), not just palettes, ported from `gleks-ui-lab`'s
|
|
93
|
+
compare page. `@import '@guildofgleks/ui/styles/presets/material.css'` /
|
|
94
|
+
`.../presets/primeng.css'`, then `data-theme="material"` / `"primeng"`. Unlike `slate`/
|
|
95
|
+
`one-dark`/`one-light`, both set the character layer (corner rounding, border weight, casing,
|
|
96
|
+
tracking) plus a handful of settings the character layer has no vocabulary for yet (a pill
|
|
97
|
+
button, a table's header font). See each file's own header comment for what's per-component
|
|
98
|
+
and why.
|
|
99
|
+
|
|
100
|
+
- **Four new foundation tokens give a theme somewhere to set corner rounding, border weight and
|
|
101
|
+
emphasis casing/tracking once, instead of per component:** `--gog-text-transform` (default
|
|
102
|
+
`uppercase`), `--gog-letter-spacing` (default `1px`), `--gog-border-width` and `--gog-border-style`
|
|
103
|
+
(default `1px solid` — a third tier alongside the existing `--gog-control-border-*` for form
|
|
104
|
+
fields and `--gog-panel-border-*` for raised surfaces, covering everything smaller and inline:
|
|
105
|
+
chips, tags, badges, toggles, table rows, tabs, calendar cells). 40 component tokens across
|
|
106
|
+
`theme.css` now derive from these plus the pre-existing `--gog-radius` — no default changed;
|
|
107
|
+
every conversion was checked to render the identical pixel value before and after. Not
|
|
108
|
+
converted, on purpose: pill/circle radii and deliberately-flat corners (a rounding axis
|
|
109
|
+
shouldn't reshape a shape choice), and per-component values with no shared pattern to extract.
|
|
110
|
+
`docs/themes.md` has the full audit. Setting any of the four in a `[data-theme]` block now
|
|
111
|
+
restyles every component that reads it, with no per-component overrides to list.
|
|
112
|
+
|
|
113
|
+
- **`DialogService.open()` and `DialogConfig` gain an optional `TData` generic**, checked against
|
|
114
|
+
`data` at the call site: `dialogService.open<TResult, TData>({ data: /* checked against TData */ })`.
|
|
115
|
+
Supplying only `TResult` — the existing, common form — leaves `TData` as `unknown`, exactly as
|
|
116
|
+
before; nothing about an existing call changes. This closes only the sending half of the round
|
|
117
|
+
trip: the component you open still reads its data via `inject(DIALOG_DATA)`, one
|
|
118
|
+
`InjectionToken<unknown>` shared by every dialog, so it still needs its own cast
|
|
119
|
+
(`inject<TData>(DIALOG_DATA)`) — Angular's DI has no way to carry a per-call-site type through a
|
|
120
|
+
single shared token. See `AGENTS.md`'s `DialogService` section for the full example.
|
|
121
|
+
|
|
122
|
+
### Removed
|
|
123
|
+
|
|
124
|
+
- **The three abbreviated token prefixes deprecated in 21.5.0 (21.3.0 for `--gog-ms-*`) are gone.**
|
|
125
|
+
Each had been declared nowhere, honoured only as a fallback the spelled-out token wrapped:
|
|
126
|
+
`--gog-button-x: var(--gog-btn-x, value)`. The fallback layer is deleted; the spelled-out name is
|
|
127
|
+
now the plain declaration, with the same final value as before. Rename any of the following you
|
|
128
|
+
still set, in your own stylesheet — a `var()` reference to a name nothing declares does not fail
|
|
129
|
+
a build, it just silently stops matching anything:
|
|
130
|
+
|
|
131
|
+
| Old | New |
|
|
132
|
+
| ----------------- | ----------------------------- |
|
|
133
|
+
| `--gog-btn-*` | `--gog-button-*` |
|
|
134
|
+
| `--gog-ms-*` | `--gog-multiselect-*` |
|
|
135
|
+
| `--gog-confirm-*` | `--gog-confirmation-dialog-*` |
|
|
136
|
+
|
|
137
|
+
`GogTokenName` (the exported type listing every `--gog-*` custom property) drops the 20 old
|
|
138
|
+
spellings it used to include — a compile error on a variable annotated with one of them is the
|
|
139
|
+
one part of this change your build will actually catch.
|
|
140
|
+
|
|
141
|
+
### Fixed
|
|
142
|
+
|
|
143
|
+
- **Four tokens that resolved to nothing, found while surveying this release's removal below.**
|
|
144
|
+
Each was a `var(--gog-…)` with no fallback, naming a custom property nothing declares — which
|
|
145
|
+
makes the token holding it guaranteed-invalid, so the declaration reading it silently computes to
|
|
146
|
+
nothing. No build error, nothing to see except the missing style:
|
|
147
|
+
|
|
148
|
+
- **`gog-multiselect`'s focus glow never rendered.** Its default read the deprecated
|
|
149
|
+
`--gog-ms-focus-ring`, declared nowhere — now reads its own `--gog-multiselect-focus-ring`,
|
|
150
|
+
matching `gog-input` and `gog-select`.
|
|
151
|
+
- **The filter box inside `gog-select` and `gog-multiselect` had no border and the wrong text
|
|
152
|
+
colour.** Both read a `--gog-{select,multiselect}-control-*` pair that was never declared
|
|
153
|
+
anywhere in the library — now read `--gog-{select,multiselect}-field-*`, the tokens that were
|
|
154
|
+
actually meant. The border loss was total (`border` is a shorthand, so one invalid part drops
|
|
155
|
+
the whole declaration); the colour loss was invisible, because `color` just inherited instead.
|
|
156
|
+
- **`gog-multiselect`'s JS-computed panel height silently used hardcoded defaults instead of the
|
|
157
|
+
themed metrics.** Its `GogDropdownBase` token overrides named the deprecated `--gog-ms-*`
|
|
158
|
+
prefix, which `theme.css` has never declared as a real property — so
|
|
159
|
+
`getComputedStyle().getPropertyValue()` always read `''`. Now reads `--gog-multiselect-*`,
|
|
160
|
+
matching the pattern `gog-select` already used correctly.
|
|
161
|
+
|
|
162
|
+
A consumer who set `--gog-multiselect-focus-glow`, `--gog-select-filter-input-color/-border` or
|
|
163
|
+
their `gog-multiselect` counterparts directly, to work around any of the first two being missing,
|
|
164
|
+
keeps working exactly as before — an explicit override still wins over the (now real) default.
|
|
165
|
+
|
|
166
|
+
## [21.6.1] - 26.08.2026
|
|
167
|
+
|
|
168
|
+
### Added
|
|
169
|
+
|
|
170
|
+
- **`gog-card` — a surface for one self-contained thing.** A product tile, a summary, a search
|
|
171
|
+
result. It paints a background, a border and a radius, which a CSS class of your own could also
|
|
172
|
+
do; what it adds is what a class cannot:
|
|
173
|
+
|
|
174
|
+
- **It names itself.** The heading you project as `gogCardHeader` becomes the card's accessible
|
|
175
|
+
name — the card takes that element's id (minting one if it has none), points its own
|
|
176
|
+
`aria-labelledby` at it and announces itself as a group. `role="group"` rather than `region`
|
|
177
|
+
on purpose: a grid of twenty cards would put twenty landmarks in a screen reader's landmark
|
|
178
|
+
list, which is worse than none.
|
|
179
|
+
- **The whole surface can activate a link, without the card inventing a control.** There is no
|
|
180
|
+
`interactive` input and no `gogClick` output. Put `gogCardLink` on the `<a>` the card is about
|
|
181
|
+
— usually the one in its heading — and its hit area stretches over the card, with the focus
|
|
182
|
+
ring drawn around the surface. The link stays yours, so `routerLink`, `href`, `target`,
|
|
183
|
+
middle-click, "open in new tab" and Enter all keep working, and anything else focusable in the
|
|
184
|
+
card still receives its own clicks. A card rendering its own `<button>` instead could hold no
|
|
185
|
+
other control (a button may not contain one), could not navigate the way an app navigates, and
|
|
186
|
+
would announce the card's entire text as its name.
|
|
187
|
+
- **`loading` and `disabled` are folded in**, including `aria-busy`, the placeholder shaped like
|
|
188
|
+
a title and body copy, and taking the card's link out of the tab order.
|
|
189
|
+
|
|
190
|
+
Slots: `gogCardHeader`, `gogCardMedia` (full-bleed, rounds into the top corners), `gogCardFooter`.
|
|
191
|
+
Tokens: `--gog-card-*`, with `--gog-card-bg`, `--gog-card-border-color`, `--gog-card-shadow`,
|
|
192
|
+
`--gog-card-padding-y`, `--gog-card-padding-x` and `--gog-card-gap` left undeclared as the
|
|
193
|
+
per-instance escape hatch.
|
|
194
|
+
|
|
195
|
+
- **`gog-panel` — a titled region of a page.** A settings section, a dashboard area, a form group.
|
|
196
|
+
It differs from `gog-card` in behaviour, not only in size: it is a real landmark
|
|
197
|
+
(`role="region"` named by its `gogPanelHeader` heading), it can **collapse**, and its surface is
|
|
198
|
+
never itself a link, because controls live inside a panel.
|
|
199
|
+
|
|
200
|
+
Collapsing **composes `gog-collapsible`** rather than repeating it, so the open/close state, the
|
|
201
|
+
id wiring and the animation are the ones the rest of the library already uses. The heading stays
|
|
202
|
+
a heading: the toggle is a separate `<button>` named by it through `aria-labelledby`, with its
|
|
203
|
+
hit area stretched across the header row — so the pointer still gets "click the title to
|
|
204
|
+
collapse", while a screen reader gets a heading _and_ a named expandable button instead of a
|
|
205
|
+
heading swallowed by `role="button"`. A panel that cannot collapse undoes the collapse geometry
|
|
206
|
+
it inherits, `overflow` included, so a dropdown opened inside one is not clipped.
|
|
207
|
+
|
|
208
|
+
`loading` keeps the heading and the footer and replaces only the body — a page section is titled
|
|
209
|
+
before its content arrives. Slots: `gogPanelHeader`, `gogPanelFooter`. New label:
|
|
210
|
+
`GOG_CONFIG.labels.togglePanel`, used only by a collapsible panel with no heading to be named by.
|
|
211
|
+
|
|
212
|
+
**`--gog-panel-*` now means two things, deliberately.** `--gog-panel-radius`,
|
|
213
|
+
`--gog-panel-shadow`, `--gog-panel-border-width` and `--gog-panel-border-style` were already the
|
|
214
|
+
foundation surface tier that dialogs, dropdown panels and tooltips read; the component adopts
|
|
215
|
+
them instead of owning a fourth definition of "raised surface", so a theme's house radius and
|
|
216
|
+
shadow reach it for free. The rest of `--gog-panel-*` is the component's own. Nothing was
|
|
217
|
+
renamed and no existing override changes meaning.
|
|
218
|
+
|
|
219
|
+
- **`gogRipple` — a press wash for any element.** `<button gogRipple>`, `<div gogRipple>`, a
|
|
220
|
+
`gog-*` host tag: a directive, so it adds no wrapper and changes no layout. Built from scratch —
|
|
221
|
+
there is no `@angular/cdk` in this package and there will not be.
|
|
222
|
+
|
|
223
|
+
The interesting part is not the animation. A ripple normally clips its host, and `gogBadge`
|
|
224
|
+
pins its badge **outside** the host's box on purpose — so the first ordinary pairing anyone
|
|
225
|
+
tries, a badged button, loses its badge the moment the ripple is installed. This one never
|
|
226
|
+
clips the host: the wash lives in its own layer that clips itself, and that layer takes the
|
|
227
|
+
host's corner radius through `border-radius: inherit`, per instance and per theme, with no
|
|
228
|
+
token to set. The one thing to know follows from it — **put the directive on the element that
|
|
229
|
+
paints the surface**, since a wrapper whose child paints the rounded background passes on the
|
|
230
|
+
wrapper's radius (usually `0`) and the wash squares off.
|
|
231
|
+
|
|
232
|
+
Inputs: `rippleDisabled`, `rippleCentred`. Suppressed with no wiring on a host that is
|
|
233
|
+
`disabled` or `aria-disabled="true"`, and under `prefers-reduced-motion: reduce` — suppressed
|
|
234
|
+
outright there, not merely shortened. Keyboard activation is centred, since `Enter` and `Space`
|
|
235
|
+
carry no coordinates. Tokens: `--gog-ripple-color` (defaults to `currentColor`, so one value
|
|
236
|
+
works on a filled surface and a ghost one), `--gog-ripple-opacity`,
|
|
237
|
+
`--gog-ripple-enter-duration`, `--gog-ripple-exit-duration`, `--gog-ripple-easing`.
|
|
238
|
+
|
|
239
|
+
- **Every interactive surface in the library can ripple, from one switch.**
|
|
240
|
+
`provideGogConfig({ ripple: { enabled: true } })` turns it on for `gog-button`, `[gogButton]`,
|
|
241
|
+
`gog-button-toggle-group`, `gog-chip`, `gog-tabs` headers, `gog-accordion` headers,
|
|
242
|
+
`gogCollapsibleTrigger`, `gogMenuItem` and the options inside `gog-select`, `gog-multiselect`
|
|
243
|
+
and `gog-autocomplete` — `gog-paginator` follows, because its page buttons are `gog-button`s.
|
|
244
|
+
|
|
245
|
+
**Off by default**, so this release changes the appearance of nothing. Each of those components
|
|
246
|
+
also takes a **`ripple`** input that beats the app-wide setting in both directions:
|
|
247
|
+
`[ripple]="false"` opts one control out of an app that turned it on, `[ripple]="true"` opts one
|
|
248
|
+
in without switching the app over.
|
|
249
|
+
|
|
250
|
+
`ripple` is the first visual default in `GOG_CONFIG` rather than in `theme.css`, and the
|
|
251
|
+
exception is deliberate: `--gog-ripple-opacity: 0` hides the wash but still pays for the DOM
|
|
252
|
+
node, the pointer listeners and the animation frames. A real off has to reach the TypeScript —
|
|
253
|
+
and it does: a disabled ripple attaches no listeners and adds no class to its host.
|
|
254
|
+
|
|
255
|
+
Two surfaces are deliberately left out, with `gogRipple` available if you disagree: **`gog-table`
|
|
256
|
+
rows** and **`gogCardLink`**. Both are hundreds of pixels wide, so the wave has to cross the
|
|
257
|
+
whole surface and reads as a flash rather than as feedback where you pressed — and a table
|
|
258
|
+
installs one directive per row, with no virtualization in the library yet. A `gog-chip` that is
|
|
259
|
+
not `clickable`, or is `disabled`, never ripples whatever the config says.
|
|
260
|
+
|
|
261
|
+
- **`GogSurfaceVariant`** (`'outlined' | 'elevated' | 'filled'`), shared by both, so the two agree
|
|
262
|
+
on what each word looks like.
|
|
263
|
+
|
|
264
|
+
### Fixed
|
|
265
|
+
|
|
266
|
+
- **A loading `gog-table` and `gog-autocomplete` announce themselves.** Both now set
|
|
267
|
+
`aria-busy="true"` while `loading` is on, which every component with a `loading` input is
|
|
268
|
+
supposed to do. A loading table replaces its rows with a single spinner cell and dims its
|
|
269
|
+
header; the autocomplete's spinner sits in its actions slot and is `aria-hidden`. Neither had
|
|
270
|
+
anything to tell a screen reader that a wait was in progress — the table read as a table with
|
|
271
|
+
no rows, and the field as idle. The autocomplete is the worse of the two, because unlike a
|
|
272
|
+
button the user pressed, that wait is not something they started.
|
|
273
|
+
|
|
7
274
|
## [21.6.0] - 23.08.2026
|
|
8
275
|
|
|
9
276
|
### Fixed
|
|
@@ -40,7 +307,7 @@ reached 1.0, so breaking changes may land in minor versions.
|
|
|
40
307
|
rendering under it. The rule is a single class in specificity, so your own styles still win.
|
|
41
308
|
|
|
42
309
|
- **Disabled and focus styling can no longer be lost to an ordinary app stylesheet.** `[gogButton]`
|
|
43
|
-
and `gogMenuItem` are applied to
|
|
310
|
+
and `gogMenuItem` are applied to _your_ element, which is the element you style — and a plain
|
|
44
311
|
`.my-button { cursor: pointer }` in an Angular component stylesheet is the same specificity as
|
|
45
312
|
the library's `.gog-btn:disabled` once `[_ngcontent-…]` is stamped on it, so it won on source
|
|
46
313
|
order. Measured: a disabled button reading `cursor: pointer` at full opacity — enabled-looking
|
|
@@ -189,7 +456,7 @@ a version bump with nothing to migrate.
|
|
|
189
456
|
**`npm run check:logical-properties` now fails the build** on a `padding`, `margin`,
|
|
190
457
|
`border-width` or `border-radius` shorthand that sets the two horizontal sides differently.
|
|
191
458
|
21.5.0 converted 16 stylesheets to logical properties and still missed these four, because the
|
|
192
|
-
sidedness lives in a value's
|
|
459
|
+
sidedness lives in a value's _position inside a shorthand_ rather than in a `left`/`right`
|
|
193
460
|
keyword — invisible to a grep, and invisible to a unit test with no style engine.
|
|
194
461
|
|
|
195
462
|
- **Documented the `position: fixed` containing-block caveat**, in one place plus a line on each
|
package/README.md
CHANGED
|
@@ -81,6 +81,13 @@ export class ExampleComponent {}
|
|
|
81
81
|
Outputs are prefixed `gog` (`gogClick`, `gogToggle`) so they never collide with native DOM
|
|
82
82
|
events. Inputs keep their natural names.
|
|
83
83
|
|
|
84
|
+
> **Don't write `(click)` on `gog-button`.** Its click handler is bound on the `<button>` inside
|
|
85
|
+
> its own template, not on the host — a native click still bubbles up through the host element, so
|
|
86
|
+
> a `(click)` listener there fires on every press, silently bypassing `debounce`'s throttling.
|
|
87
|
+
> `(gogClick)` is the one that only emits once the debounce window has passed; use it instead. This
|
|
88
|
+
> is specific to the `gog-button` component — `[gogButton]` on your own `<a>`/`<button>` has no
|
|
89
|
+
> debounce to bypass, so your own `(click)` on it works exactly as written.
|
|
90
|
+
|
|
84
91
|
**3. If you use dialogs or toasts, place their hosts once.** `DialogService.open()` and
|
|
85
92
|
`ToastService.show()` update state but render nothing without them:
|
|
86
93
|
|
|
@@ -116,8 +123,12 @@ want for a toast.
|
|
|
116
123
|
|
|
117
124
|
Every value the components paint with lives in `styles/theme.css`, in three layers:
|
|
118
125
|
|
|
119
|
-
**Foundation** — palette, type scale, spacing, motion
|
|
120
|
-
|
|
126
|
+
**Foundation** — palette, type scale, spacing, motion, and a small _character_ layer: corner
|
|
127
|
+
rounding (`--gog-radius`), border weight (`--gog-control-border-*` for form fields,
|
|
128
|
+
`--gog-panel-border-*` for raised surfaces, `--gog-border-*` for everything smaller and inline),
|
|
129
|
+
and emphasis casing/tracking (`--gog-text-transform`, `--gog-letter-spacing`). Override these to
|
|
130
|
+
restyle everything at once; component tokens derive from them, so a palette or character change
|
|
131
|
+
carries through on its own, with nothing to re-list per component.
|
|
121
132
|
|
|
122
133
|
**Component** — `--gog-<component>-*`, one block per component, named after the component you
|
|
123
134
|
write in markup (`gog-button` → `--gog-button-*`), to restyle a single component app-wide:
|
|
@@ -139,16 +150,23 @@ variant and size classes:
|
|
|
139
150
|
}
|
|
140
151
|
```
|
|
141
152
|
|
|
142
|
-
> **Renamed in 21.5.0.** Three prefixes were abbreviated and are now spelled
|
|
143
|
-
> `--gog-btn-*` → `--gog-button-*`, `--gog-confirm-*` → `--gog-confirmation-dialog-*`, and
|
|
144
|
-
> `--gog-ms-*` → `--gog-multiselect-*` (that one since 21.3.0). **The old spellings
|
|
145
|
-
>
|
|
146
|
-
>
|
|
153
|
+
> **Renamed in 21.5.0, removed in 21.7.0.** Three prefixes were abbreviated and are now spelled
|
|
154
|
+
> out: `--gog-btn-*` → `--gog-button-*`, `--gog-confirm-*` → `--gog-confirmation-dialog-*`, and
|
|
155
|
+
> `--gog-ms-*` → `--gog-multiselect-*` (that one since 21.3.0). **The old spellings no longer
|
|
156
|
+
> resolve.** If you set one of them, rename it — a `var()` reference to a name nothing declares
|
|
157
|
+
> doesn't fail your build, it just silently stops matching anything.
|
|
147
158
|
>
|
|
148
159
|
> One prefix that looks abbreviated and is not: **`--gog-input-*`**. It names the shared
|
|
149
160
|
> text-field block that both `gog-inputfield` and `gog-textarea` render (`.gog-input__field`), not
|
|
150
161
|
> the `gog-inputfield` component — the two are meant to restyle together from one token set, so
|
|
151
162
|
> there is no `--gog-inputfield-*` and there will not be one.
|
|
163
|
+
>
|
|
164
|
+
> And one prefix that means two things on purpose: **`--gog-panel-*`**. Four of them —
|
|
165
|
+
> `--gog-panel-radius`, `--gog-panel-shadow`, `--gog-panel-border-width`,
|
|
166
|
+
> `--gog-panel-border-style` — are the _foundation_ surface tier that dialogs, dropdown panels and
|
|
167
|
+
> tooltips read, and the `gog-panel` component reads them too rather than owning a fourth copy of
|
|
168
|
+
> "what a raised surface looks like here". Change one and every raised surface follows, which is
|
|
169
|
+
> the intent; the rest of `--gog-panel-*` belongs to the component alone.
|
|
152
170
|
|
|
153
171
|
Every group and token name is in **[`TOKENS.md`](./TOKENS.md)**, generated from `theme.css` so it
|
|
154
172
|
cannot drift, and available at runtime as `GOG_TOKEN_GROUPS`.
|
|
@@ -160,7 +178,7 @@ The active theme is a `data-theme` attribute on `:root`, managed by `ThemeServic
|
|
|
160
178
|
```ts
|
|
161
179
|
private readonly theme = inject(ThemeService);
|
|
162
180
|
this.theme.toggleTheme(); // light ⇄ dark
|
|
163
|
-
this.theme.setTheme('
|
|
181
|
+
this.theme.setTheme('one-dark'); // any preset you imported, or any name you declared in CSS
|
|
164
182
|
```
|
|
165
183
|
|
|
166
184
|
Out of the box it adopts whatever `data-theme` is already on the document, or `light`.
|
|
@@ -168,19 +186,64 @@ Persisting the choice and following the OS setting are opt-in:
|
|
|
168
186
|
|
|
169
187
|
```ts
|
|
170
188
|
provideGogConfig({
|
|
171
|
-
theme: { storageKey: 'app-theme', followSystem: true, darkTheme: '
|
|
189
|
+
theme: { storageKey: 'app-theme', followSystem: true, darkTheme: 'one-dark' },
|
|
172
190
|
});
|
|
173
191
|
```
|
|
174
192
|
|
|
175
|
-
A theme only declares what it changes — the derived layer re-resolves against whatever
|
|
176
|
-
|
|
177
|
-
|
|
193
|
+
A theme only declares what it changes — the derived layer re-resolves against whatever is in
|
|
194
|
+
scope, so a theme restyles every component without listing any of them. **Eleven declarations
|
|
195
|
+
are a whole visual identity**, not a recolour:
|
|
178
196
|
|
|
179
197
|
```css
|
|
180
|
-
|
|
181
|
-
|
|
198
|
+
[data-theme='compact'] {
|
|
199
|
+
/* Palette: every colour in the library re-derives from these. */
|
|
200
|
+
--gog-background-color: #f4f6f8;
|
|
201
|
+
--gog-surface-color: #ffffff;
|
|
202
|
+
--gog-text-color: #1e293b;
|
|
203
|
+
--gog-accent-color: #4f46e5;
|
|
204
|
+
|
|
205
|
+
/* Character: every corner, border, label and gap in the library re-derives from these. */
|
|
206
|
+
--gog-radius: 2px;
|
|
207
|
+
--gog-density: 0.85; /* one number = every padding and gap at once */
|
|
208
|
+
--gog-control-border-width: 1px;
|
|
209
|
+
--gog-text-transform: none;
|
|
210
|
+
--gog-letter-spacing: normal;
|
|
211
|
+
}
|
|
182
212
|
```
|
|
183
213
|
|
|
214
|
+
The palette half has always worked this way. The **character layer** (`--gog-radius`,
|
|
215
|
+
`--gog-density`, the border and casing tokens — all since 21.7.0) is the other half: it is what
|
|
216
|
+
lets a theme change the library's _shape_ — square or generous corners, thin borders,
|
|
217
|
+
sentence-case labels, tight or roomy spacing — still without naming a single component.
|
|
218
|
+
|
|
219
|
+
Nine presets ship alongside the built-in `light` and `dark`, each at `styles/presets/<name>.css`
|
|
220
|
+
and activated by `data-theme="<name>"`. All nine set palette **and** character:
|
|
221
|
+
|
|
222
|
+
| Preset | The identity |
|
|
223
|
+
| ----------------------- | ----------------------------------------------------------------------- |
|
|
224
|
+
| `slate` | soft modern — 12px corners, hairline borders, roomier than the default |
|
|
225
|
+
| `one-dark`, `one-light` | editor chrome — 4px corners, compact, sentence case; one UI, two tones |
|
|
226
|
+
| `material`, `primeng` | Material Design 3 and PrimeNG Aura, including their shape and density |
|
|
227
|
+
| `ledger` | administrative software — square corners, hard offset shadow, no motion |
|
|
228
|
+
| `terminal` | green phosphor — monospaced throughout, square, no motion |
|
|
229
|
+
| `bevel` | the early-web desktop — raised buttons, sunken fields, grey and navy |
|
|
230
|
+
| `parchment` | ink on laid paper — old-style serif, oxblood accent, roomy |
|
|
231
|
+
|
|
232
|
+
**No preset downloads a font.** Each sets a stack that resolves to a real system face — the
|
|
233
|
+
platform's own monospace for `terminal`, Tahoma/Verdana for `bevel`, Iowan Old Style/Palatino for
|
|
234
|
+
`parchment` — so importing a preset never adds a network request. Where a webfont makes a visible
|
|
235
|
+
difference, it lives in a separate opt-in file you import _after_ the preset:
|
|
236
|
+
|
|
237
|
+
```css
|
|
238
|
+
@import '@guildofgleks/ui/styles/presets/parchment.css';
|
|
239
|
+
@import '@guildofgleks/ui/styles/presets/parchment.fonts.css'; /* optional: EB Garamond */
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
`terminal.fonts.css` (IBM Plex Mono) is the other one. `material` and `primeng` additionally set
|
|
243
|
+
a few things the character layer has no vocabulary for (a pill button, a table's header font), and
|
|
244
|
+
`bevel` sets one (a button's bevel must disagree with a field's). `AGENTS.md` has the per-preset
|
|
245
|
+
detail and the full token list.
|
|
246
|
+
|
|
184
247
|
Fonts are left alone on purpose (system stacks, no webfont download). Add
|
|
185
248
|
`@guildofgleks/ui/styles/fonts.css` for the showcase's typography.
|
|
186
249
|
|
|
@@ -194,14 +257,20 @@ provideGogConfig({
|
|
|
194
257
|
control: { size: 'sm', errorDisplay: 'auto', clearable: true },
|
|
195
258
|
dropdown: { appendToBody: true },
|
|
196
259
|
datepicker: { locale: 'de-DE', format: 'dd.MM.yyyy' },
|
|
197
|
-
|
|
260
|
+
ripple: { enabled: true }, // press feedback on every interactive surface at once
|
|
261
|
+
labels: { clear: 'Löschen', selectAll: 'Alle auswählen' }, // translate the library once
|
|
198
262
|
});
|
|
199
263
|
```
|
|
200
264
|
|
|
201
265
|
Keys: `control`, `dropdown`, `floatLabel`, `datepicker`, `autocomplete`, `inputfield`, `textarea`,
|
|
202
|
-
`tooltip`, `scroll`, `button`, `paginator`, `toast`, `theme`, `labels`. An instance's own
|
|
203
|
-
always wins, and providing the config again lower in the injector tree layers onto the
|
|
204
|
-
rather than replacing it.
|
|
266
|
+
`tooltip`, `scroll`, `button`, `ripple`, `paginator`, `toast`, `theme`, `labels`. An instance's own
|
|
267
|
+
input always wins, and providing the config again lower in the injector tree layers onto the
|
|
268
|
+
parent rather than replacing it.
|
|
269
|
+
|
|
270
|
+
`ripple` is the one visual default that is not a token, and the exception is deliberate:
|
|
271
|
+
`--gog-ripple-opacity: 0` would hide the wash but still pay for the DOM node, the listeners and
|
|
272
|
+
the animation frames, so a real off has to reach the TypeScript. It is **off by default**, and
|
|
273
|
+
every rippling component takes a `ripple` input that beats it in both directions.
|
|
205
274
|
|
|
206
275
|
Icons work the same way — 41 Lucide glyphs ship with the package, and your own register by name:
|
|
207
276
|
|
|
@@ -243,13 +312,14 @@ dropdowns, always for the menu.
|
|
|
243
312
|
| Form controls | `gog-inputfield`, `gog-textarea`, `gog-select`, `gog-multiselect`, `gog-autocomplete`, `gog-checkbox`, `gog-radio-group`, `gog-toggle`, `gog-slider`, `gog-datepicker`, `gog-calendar`, `gog-button-toggle-group` |
|
|
244
313
|
| Actions | `gog-button`, `gog-chip` |
|
|
245
314
|
| Data | `gog-table` (+ `gog-column`), `gog-paginator`, `gog-tag` |
|
|
246
|
-
| Layout & disclosure | `gog-accordion`, `gog-tabs` (+ `gog-tab`), `gog-collapsible`, `gog-divider`, `gog-scroll`
|
|
315
|
+
| Layout & disclosure | `gog-accordion`, `gog-tabs` (+ `gog-tab`), `gog-collapsible`, `gog-card`, `gog-panel`, `gog-divider`, `gog-scroll` |
|
|
247
316
|
| Overlays | `gog-dialog`, `gog-confirmation-dialog`, `gog-toast` (+ `gog-toast-container`), `gog-menu` (+ `gogMenuTrigger` / `gogMenuItem`) |
|
|
248
317
|
| Feedback | `gog-spinner`, `gog-spinner-overlay`, `gog-progressbar`, `gog-skeleton` |
|
|
249
318
|
| Content | `gog-icon` |
|
|
250
319
|
|
|
251
320
|
**Directives:** `gogButton` (a link that looks like a button), `gogTooltip`, `gogBadge`,
|
|
252
|
-
`gogCollapsibleTrigger`, `gogCollapsibleContent
|
|
321
|
+
`gogRipple` (a press wash on any element), `gogCollapsibleTrigger`, `gogCollapsibleContent`,
|
|
322
|
+
`gogCardLink` (a link the whole card activates).
|
|
253
323
|
**Services:** `DialogService`, `ToastService`, `ThemeService`.
|
|
254
324
|
|
|
255
325
|
Seventeen more directives go on markup you own rather than configuring a component through an
|
|
@@ -270,6 +340,14 @@ A few things worth knowing before you reach for a workaround:
|
|
|
270
340
|
They also generate their own `id`, so labels and error messages are wired up without `inputId`.
|
|
271
341
|
- **`gog-collapsible` is headless** — no markup of its own. Project any element as the trigger and
|
|
272
342
|
any element as the panel.
|
|
343
|
+
- **A clickable `gog-card` has no `interactive` input.** Put `gogCardLink` on the `<a>` the card is
|
|
344
|
+
about — usually the one in its heading — and the whole surface activates that link, keyboard,
|
|
345
|
+
middle-click and `routerLink` included. Same reasoning as `gog-button` above: the element stays
|
|
346
|
+
yours. Anything else focusable in the card keeps receiving its own clicks.
|
|
347
|
+
- **`gog-panel` shares the `--gog-panel-*` prefix with the foundation surface tier.**
|
|
348
|
+
`--gog-panel-radius`, `--gog-panel-shadow` and the border pair are the tokens dialogs and
|
|
349
|
+
dropdown panels already read, so a theme's idea of a raised surface reaches the component for
|
|
350
|
+
free. Its own family (background, padding, heading, toggle, footer) sits alongside them.
|
|
273
351
|
- **`[(ngModel)]` is untested.** The library never imports `FormsModule`; use Reactive Forms.
|
|
274
352
|
|
|
275
353
|
## Documentation
|