@guildofgleks/ui 21.7.2 → 21.9.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 CHANGED
@@ -5,7 +5,8 @@ an app that **consumes** the published `@guildofgleks/ui` npm package. It is not
5
5
  authoring the library — if you are working inside the `gleks_web_ui` monorepo itself, read
6
6
  `.github/instructions/*.md` instead.
7
7
 
8
- Everything below reflects the library's actual source as of **`21.7.0`**. 21.7.0 removed the three
8
+ Everything below reflects the library's actual source as of **`21.9.0`** (in progress the
9
+ released version is 21.8.0; see `CHANGELOG.md` for what 21.9.0 adds). 21.7.0 removed the three
9
10
  abbreviated token prefixes and 21.5.0 removed a batch of deprecated API — see **Removed in 21.7.0**
10
11
  and **Removed in 21.5.0** near the end of this file, which exist so code written against an older
11
12
  version can be migrated — and `CHANGELOG.md` has the rest. `README.md` covers the same ground at a
@@ -180,13 +181,30 @@ Full model is in `README.md`'s Theming section; short version:
180
181
  `--gog-text-transform`/`--gog-letter-spacing` (emphasis casing/tracking). Component tokens in
181
182
  the categories these cover derive from them by default; setting one in a `[data-theme]` block
182
183
  restyles every component that reads it, with nothing to re-list per component.
184
+ - **The type scale is `--gog-text-xs | sm | md | lg | slg | xl | 2xl | 3xl`.** `slg` (1.25rem)
185
+ fills the gap between `lg` and `xl` and is named for the control size that needed it. Every
186
+ component font size that is one of these reads the token, so retuning the scale retunes the
187
+ library; the handful that do not are off-scale on purpose (an 11px chip, the accordion
188
+ chevron's px ramp, the toggle's own micro-ramp).
189
+
190
+ - **Weight is `--gog-font-weight-medium | semibold | bold | heavy`** (500/600/700/900). Every
191
+ component weight reads one of them, so a lighter or heavier house style is four declarations.
192
+
193
+ - **`--gog-z-base` moves the whole stacking order.** Badge `+1`, toast `+100`, dropdowns, dialogs
194
+ and menus `+300`, tooltip `+400`, the blocking spinner overlay `+8000`. Set the base to lift
195
+ the library above your own chrome without disturbing its internal order.
196
+
183
197
  - **`--gog-density` is the character layer for spacing** (since 21.7.0, `docs/themes.md`
184
198
  iteration 6). It multiplies the fourteen-step scale `--gog-space-2` … `--gog-space-48`, named
185
199
  for their pixel value at density 1, and every padding and gap in the library derives from a
186
200
  step. `--gog-density: 0.9` in a `[data-theme]` block makes the whole library tighter; nothing
187
201
  else needs to be named. `--gog-space-xs|sm|md|lg|2xl` are aliases for steps 4/8/16/24/48 and
188
- still work. Icon offsets follow density; the glyph box, focus-ring offset and float-label
189
- reserve deliberately do not they are legibility, not spacing.
202
+ still work. Icon offsets, dropdown panel gaps, error-line offsets and the badge's overhang
203
+ follow density; the glyph box, the focus-ring offset, the float-label reserve and the
204
+ scrollbar/toggle thumb insets deliberately do not — those are legibility or geometry fitted to
205
+ a fixed-width track, not spacing. Since 21.9.0 the split is enforced rather than trusted:
206
+ `check-tokens` rule H fails the build on a length token that restates a scale step's value as
207
+ a bare literal, with the three exceptions named in the script.
190
208
  - **Component prefixes are spelled out** since 21.5.0: `--gog-button-*`, `--gog-multiselect-*`,
191
209
  `--gog-confirmation-dialog-*`. The abbreviated `--gog-btn-*`, `--gog-ms-*` and `--gog-confirm-*`
192
210
  were removed in 21.7.0 — if you're reading a codebase or an example that still uses one, rename
@@ -264,13 +282,14 @@ parent's config**, one level deep per key — it does not replace it.
264
282
 
265
283
  | Key | Fields | Applies to |
266
284
  | -------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
267
- | `control` | `size`, `errorDisplay`, `clearable` | `size`: button, inputfield, textarea, select, multiselect, checkbox, radio-group, button-toggle-group, datepicker. `errorDisplay`: inputfield, textarea, select, multiselect, autocomplete, radio-group, slider, datepicker. `clearable`: inputfield, textarea, select, multiselect, autocomplete, datepicker. Not table/accordion/paginator (density, not form size), not spinner/skeleton/tag/chip/toggle. |
285
+ | `control` | `size`, `errorDisplay`, `clearable` | `size`: button, `[gogButton]`, button-toggle-group, checkbox, toggle, radio-group, inputfield, textarea, select, multiselect, autocomplete, datepicker. `errorDisplay`: inputfield, textarea, select, multiselect, autocomplete, datepicker, radio-group, slider. `clearable`: inputfield, textarea, select, multiselect, autocomplete, datepicker. Not table/accordion/paginator (density, not form size), not spinner/skeleton/tag/chip. |
268
286
  | `dropdown` | `appendToBody`, `direction`, `filter`, `filterPosition` | `gog-select`, `gog-multiselect`. `gog-datepicker`/`gog-autocomplete` honour `appendToBody`/`direction` too (autocomplete has no `filter` box — it filters via the trigger's own text). |
269
287
  | `floatLabel` | `variant`, `showPlaceholder` | inputfield, textarea, select, multiselect, autocomplete, datepicker. |
270
288
  | `datepicker` | `locale`, `firstDayOfWeek`, `format` | `gog-datepicker`, `gog-calendar`. |
271
289
  | `autocomplete` | `searchDebounce`, `minLength`, `openOnFocus` | `gog-autocomplete`. |
272
290
  | `tooltip` | `position`, `showDelay`, `hideDelay` | the `gogTooltip` directive. |
273
- | `scroll` | `autoHide`, `hideDelay`, `size`, `overscrollBehavior`, `showTrack` | `gog-scroll` (and every component that uses one internally). |
291
+ | `spinner` | `component`, `variant` | every spinner the library draws — `gog-spinner`, `gog-spinner-overlay`, and the ones inside `gog-button` and `gog-autocomplete`, which have no input of their own. `component` takes **your** component and renders it in place of the built-in look. |
292
+ | `scroll` | `autoHide`, `hideDelay`, `size`, `overscrollBehavior`, `showTrack`, `horizontalWheel` | `gog-scroll` (and every component that uses one internally). |
274
293
  | `button` | `debounce` | `gog-button`. |
275
294
  | `ripple` | `enabled` | the press ripple on `gog-button`, `[gogButton]`, `gog-button-toggle-group`, `gog-chip`, `gog-tabs`, `gog-accordion`, `gogCollapsibleTrigger`, `gogMenuItem` and the `gog-select`/`gog-multiselect`/`gog-autocomplete` options. **Off by default.** Each of those takes a `ripple` input that wins over it. Not the `gogRipple` directive — writing that attribute is already the per-element decision. |
276
295
  | `inputfield` | `showSpinButtons` | `gog-inputfield`. |
@@ -467,6 +486,7 @@ Every component below is exported from `@guildofgleks/ui`'s root — `import { X
467
486
  | Input | Type | Default | Notes |
468
487
  | ----------- | --------------------------------- | ----------- | ----------------------------------------------------- |
469
488
  | `variant` | `GogVariant` | `'primary'` | |
489
+ | `severity` | `GogSeverity` | `'accent'` | what the action means; orthogonal to `variant` — see below |
470
490
  | `size` | `GogSize \| undefined` | `'md'` | via `GOG_CONFIG.control.size` |
471
491
  | `disabled` | `boolean` | `false` | |
472
492
  | `fullWidth` | `boolean` | `false` | |
@@ -474,10 +494,80 @@ Every component below is exported from `@guildofgleks/ui`'s root — `import { X
474
494
  | `loading` | `boolean` | `false` | shows an inline `gog-spinner`, blocks clicks |
475
495
  | `debounce` | `number \| undefined` | `300` | ms; via `GOG_CONFIG.button.debounce` — see note below |
476
496
  | `ariaLabel` | `string \| null` | `null` | **use this, not a raw `aria-label` attribute** |
497
+ | `ariaPressed` | `boolean \| 'mixed' \| null` | `null` | toggle button; `false` renders `aria-pressed="false"` |
498
+ | `ariaExpanded` | `boolean \| null` | `null` | disclosure / popup trigger |
499
+ | `ariaControls` | `string \| null` | `null` | id of the controlled element; pairs with `ariaExpanded` |
500
+ | `ariaHasPopup` | `GogAriaHasPopup \| null` | `null` | `boolean \| 'menu' \| 'listbox' \| 'tree' \| 'grid' \| 'dialog'` |
477
501
  | `ripple` | `boolean \| undefined` | `false` | press ripple; via `GOG_CONFIG.ripple.enabled` |
478
502
 
479
503
  Outputs: `gogClick: MouseEvent`.
480
504
 
505
+ **`severity` says what the action means; `variant` says how loudly it is drawn** (21.9.0). The
506
+ two are orthogonal, so this is not a fifth variant — it re-points the colours all four are built
507
+ from, and every combination is real: `variant="ghost" severity="danger"` is a quiet delete,
508
+ `variant="primary" severity="danger"` a loud one. `'accent'` is the default and the absence of a
509
+ claim, so nothing has to opt out of a severity it does not have. `GogSeverity` is shared with
510
+ `gog-progressbar`, whose `GogProgressbarVariant` is now an alias of it.
511
+
512
+ ```html
513
+ <gog-button severity="danger" (gogClick)="deleteAccount()">Delete account</gog-button>
514
+ <gog-button variant="outline" severity="warning">Discard draft</gog-button>
515
+ <a gogButton severity="success" routerLink="/done">Finish</a>
516
+ ```
517
+
518
+ Two colour rules are worth knowing before you override anything. A **filled** severity button's
519
+ label is `--gog-<status>-text-color`, which each theme states for its own hue — `material` and
520
+ `primeng` put near-black on their bright ones, the rest white — and hover and press deepen the
521
+ fill *away* from that label (`--gog-<status>-shade`), so a state always makes the label easier to
522
+ read rather than harder. A **transparent** one's label is `--gog-button-<status>-ink`: the status
523
+ hue mixed halfway toward the page's ink, because the raw hue is legible body text in only five of
524
+ the eleven shipped themes. Override `--gog-button-<status>-ink` if your own theme wants more
525
+ colour there, and check it: all four severities across all four variants and all their states are
526
+ gated by `npm run check:contrast`.
527
+
528
+ **Every ARIA attribute this button needs has an input, and a raw attribute is not a
529
+ substitute.** `<gog-button [attr.aria-pressed]="on()">` compiles, throws nothing, and does
530
+ nothing: the attribute lands on the `<gog-button>` custom element, which has no role, while the
531
+ real `<button>` inside stays unmarked. The failure is invisible — the control looks right and is
532
+ simply not a toggle to a screen reader. Use `[ariaPressed]`, `[ariaExpanded]`, `[ariaControls]`,
533
+ `[ariaHasPopup]` and `ariaLabel`.
534
+
535
+ `false` is not the same as unset. `null` omits the attribute; `false` renders
536
+ `aria-pressed="false"` / `aria-expanded="false"`, which is what an off toggle or a closed
537
+ disclosure has to say — a button with no `aria-pressed` at all is not a toggle button.
538
+
539
+ **A toggle button now looks toggled** (21.9.0). `aria-pressed="true"` (or `"mixed"`) draws an
540
+ inset ring — `--gog-button-<variant>-toggled-shadow`, overridable per instance with
541
+ `--gog-button-toggled-shadow`. A ring rather than a fill because hover and press already own the
542
+ background: the state has to survive both, and until 21.9.0 it did not exist at all, so a button
543
+ could announce itself as on to a screen reader and look identical to an off one. `[gogButton]`
544
+ gets the same look from the attribute you write on your own element.
545
+
546
+ **`[gogButton]` needs none of these inputs.** It styles an element you own, so write the ARIA
547
+ attributes on your own `<button>`/`<a>` directly. Same for `[gogMenuTrigger]`, which sets
548
+ `aria-haspopup`/`aria-expanded`/`aria-controls` on its host — put it on your own `<button
549
+ gogButton>`, as its own example shows, not on a `<gog-button>`.
550
+
551
+ ```html
552
+ <gog-button [ariaPressed]="mirrored()" (gogClick)="toggleMirror()">Mirror</gog-button>
553
+
554
+ <gog-button [ariaExpanded]="open()" ariaControls="filters" ariaHasPopup="dialog"
555
+ (gogClick)="open.set(!open())">Filters</gog-button>
556
+ ```
557
+
558
+ **The press is a colour, not only a movement.** `:active` deepens the button's background (and
559
+ the label where the fill demands it) as well as scaling it by `--gog-button-active-scale`. Under
560
+ `prefers-reduced-motion: reduce` the scale is dropped and the colour stays, so the press is still
561
+ visible to a reader who has switched animations off — before 21.9.0 that reader got no feedback at
562
+ all, since the ripple is off by default and is itself suppressed under reduced motion. Override
563
+ per instance with `--gog-button-press-bg` / `--gog-button-press-color`, or per theme with
564
+ `--gog-button-<variant>-active-bg`.
565
+
566
+ Every other pressable surface in the library does the same thing since 21.9.0 — menu items,
567
+ chips, tab and accordion headers, button-toggle options and the three dropdowns' option rows —
568
+ each through its own `--gog-<block>-press-bg`. `gogCollapsibleTrigger` is the exception: the
569
+ library paints nothing on that element in any state, because it is yours.
570
+
481
571
  **`debounce` is a spam guard, not a delay before the first click.** The first click in a window
482
572
  fires immediately (leading edge); further clicks within `debounce` ms are silently dropped.
483
573
 
@@ -707,12 +797,12 @@ icon.
707
797
  An on/off switch (`role="switch"`) — semantically different from a checkbox ("is this setting
708
798
  on", not "is this one of the things you selected").
709
799
 
710
- | Input | Type | Default |
711
- | ----------------------- | ---------------------- | ------- |
712
- | `label`, `ariaLabel` | `string` | `''` |
713
- | `size` | `GogSize \| undefined` | `'md'` |
714
- | `disabled`, `fullWidth` | `boolean` | `false` |
715
- | `labelPosition` | `'start' \| 'end'` | `'end'` |
800
+ | Input | Type | Default | Notes |
801
+ | ----------------------- | ---------------------- | ------- | ------------------------------------- |
802
+ | `label`, `ariaLabel` | `string` | `''` | |
803
+ | `size` | `GogSize \| undefined` | `'md'` | via `GOG_CONFIG.control.size` |
804
+ | `disabled`, `fullWidth` | `boolean` | `false` | |
805
+ | `labelPosition` | `'start' \| 'end'` | `'end'` | |
716
806
  | `onLabel`, `offLabel` | `string` | `''` | text rendered inside the track itself |
717
807
 
718
808
  Model: `checked: boolean`. CVA: yes.
@@ -928,6 +1018,7 @@ the element stays yours and the directive only gives it the look.
928
1018
  | Input | Type | Default |
929
1019
  | ----------- | ------------------------ | ---------------------------------------- |
930
1020
  | `variant` | `GogVariant` | `'primary'` |
1021
+ | `severity` | `GogSeverity` | `'accent'`; same as `gog-button` |
931
1022
  | `size` | `GogSize \| undefined` | `'md'`; via `GOG_CONFIG.control.size` |
932
1023
  | `fullWidth` | `boolean` (bare attr ok) | `false` |
933
1024
  | `ripple` | `boolean \| undefined` | `false`; via `GOG_CONFIG.ripple.enabled` |
@@ -976,6 +1067,7 @@ are impossible by design.
976
1067
  | `size` | `GogSize` | `'md'` |
977
1068
  | `shape` | `GogTagShape` (`'rounded'\|'pill'`) | `'rounded'` |
978
1069
  | `disabled`, `clickable` | `boolean` | `false`, `true` |
1070
+ | `selected` | `boolean \| null` (two-way) | `null` — see below |
979
1071
  | `removable` | `boolean` | `false` |
980
1072
  | `fullWidth` | `boolean` | `false` |
981
1073
  | `ariaLabel`, `removeAriaLabel` | `string` | `''`, `'Remove chip'` |
@@ -991,6 +1083,29 @@ Outputs: `gogClick: MouseEvent | KeyboardEvent`, `gogRemove: void`.
991
1083
  >
992
1084
  ```
993
1085
 
1086
+ **`selected` makes it a filter chip** (21.9.0) — a chip you toggle on and off rather than press.
1087
+ It is tri-state, and `null` is the default so nothing about an existing chip changes: no
1088
+ `aria-pressed`, no selected look, activation only emits `gogClick`. Set it to `false` and the chip
1089
+ is a toggle that is off (`aria-pressed="false"` — a chip with no `aria-pressed` at all is not a
1090
+ toggle to a screen reader, so "off" has to be stated); `true` and it is on, which draws an inset
1091
+ ring from `--gog-chip-selected-shadow`. A ring rather than a fill because `:hover` and `:active`
1092
+ already own the chip's background and the selection has to survive both.
1093
+
1094
+ It is a two-way `model`, so the chip flips it on click, Enter and Space — a row of filters needs
1095
+ no click handler:
1096
+
1097
+ ```html
1098
+ @for (f of filters; track f.label) {
1099
+ <gog-chip [(selected)]="f.on">{{ f.label }}</gog-chip>
1100
+ }
1101
+ ```
1102
+
1103
+ `gogClick` still fires, **after** the flip, so a handler reading `selected()` sees the new value.
1104
+ Drive the state from that handler instead and you want a one-way `[selected]`, or the two writes
1105
+ cancel out. A `disabled` chip keeps the ring but drops `aria-pressed`, which needs the
1106
+ `role="button"` a disabled chip does not carry — "selected, and currently unavailable" is a real
1107
+ state and hiding it would leave it announced and invisible.
1108
+
994
1109
  #### `gog-tag`
995
1110
 
996
1111
  | Input | Type | Default |
@@ -1020,6 +1135,19 @@ Slot: `<ng-template gogTagIcon>` for custom icon markup.
1020
1135
  `variant="custom"` renders your own projected markup, still inheriting the size wrapper and
1021
1136
  `--gog-spinner-color` theming.
1022
1137
 
1138
+ **To replace the spinner everywhere at once, pass a component to `GOG_CONFIG`** — including the
1139
+ two places you cannot reach with an input, `gog-button`'s and `gog-autocomplete`'s loading
1140
+ states:
1141
+
1142
+ ```ts
1143
+ provideGogConfig({ spinner: { component: HouseLoaderComponent } });
1144
+ ```
1145
+
1146
+ It renders inside the same size wrapper as the built-ins, so it keeps the sizing, the overlay
1147
+ behaviour, `role="status"` and the accessible name — only the visual is yours. An instance's own
1148
+ `variant` still wins over it, so `<gog-spinner variant="ring">` is a ring in an app that has set
1149
+ a component: a default does not overrule something asked for explicitly.
1150
+
1023
1151
  ```html
1024
1152
  <gog-spinner-overlay [loading]="isLoading()">
1025
1153
  <app-content-that-loads />
@@ -1560,6 +1688,21 @@ region — the library's official recommendation over a raw `overflow-x`/`overfl
1560
1688
  | `ariaLabel` | `string` | `''` |
1561
1689
  | `overscrollBehavior` | `GogScrollOverscrollBehavior \| undefined` (`'auto'\|'contain'\|'none'`) | `'auto'`; via `GOG_CONFIG.scroll.overscrollBehavior` |
1562
1690
  | `showTrack` | `boolean \| undefined` | `true`; via `GOG_CONFIG.scroll.showTrack` |
1691
+ | `horizontalWheel` | `boolean \| undefined` | `false`; via `GOG_CONFIG.scroll.horizontalWheel` |
1692
+
1693
+ **`horizontalWheel` turns a vertical wheel into horizontal scrolling** (21.9.0), for the case a
1694
+ consumer hits first: hover a horizontal-only row, turn the wheel, and the *page* moves. That is
1695
+ the browser's own behaviour and the component deliberately did nothing about it until now.
1696
+
1697
+ It is off by default because it changes what an existing instance does with a gesture it
1698
+ currently passes on; `provideGogConfig({ scroll: { horizontalWheel: true } })` turns it on
1699
+ app-wide. It only acts when the viewport cannot scroll vertically (checked against live
1700
+ geometry, so `axis="both"` scrolls down while there is down to go), the event carries no
1701
+ horizontal delta of its own (a trackpad swipe and `Shift`+wheel already work), `ctrlKey` is
1702
+ clear (pinch-zoom), and there is room left in the direction of the turn. **That last condition is
1703
+ the point:** at the content's end the event is left alone and the page picks it up, so the wheel
1704
+ never goes dead over a scrolled-to-the-end region. `overscrollBehavior: 'contain'` still
1705
+ contains — that boundary is the browser's and this never reaches past it.
1563
1706
 
1564
1707
  Outputs: `gogScroll: GogScrollMetrics`, `gogReachStart`/`gogReachEnd: 'vertical'|'horizontal'`.
1565
1708
  Methods (via template ref): `scrollTo(options)`, `scrollToTop()`, `scrollToBottom()`,
@@ -1752,6 +1895,7 @@ Shared enum-like types (`import type { ... } from '@guildofgleks/ui'`):
1752
1895
  | `GogSize` | `'xsm' \| 'sm' \| 'md' \| 'lg' \| 'slg'` |
1753
1896
  | `GogVariant` | `'primary' \| 'secondary' \| 'outline' \| 'ghost'` |
1754
1897
  | `GogSurfaceVariant` | `'outlined' \| 'elevated' \| 'filled'` — `gog-card` and `gog-panel` |
1898
+ | `GogAriaHasPopup` | `boolean \| 'menu' \| 'listbox' \| 'tree' \| 'grid' \| 'dialog'` — `gog-button`'s `ariaHasPopup` |
1755
1899
  | `GogTagVariant` | `'success' \| 'danger' \| 'warning' \| 'info'` |
1756
1900
  | `GogOrientation` | `'horizontal' \| 'vertical'` |
1757
1901
  | `GogTagShape` | `'rounded' \| 'pill'` |