@guildofgleks/ui 21.11.0 → 21.13.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 +2133 -1964
- package/CHANGELOG.md +3468 -2692
- package/README.md +523 -467
- package/TOKENS.md +52 -49
- package/fesm2022/guildofgleks-ui-datepicker.mjs +20 -0
- package/fesm2022/guildofgleks-ui-datepicker.mjs.map +1 -0
- package/fesm2022/guildofgleks-ui-dialog.mjs +20 -0
- package/fesm2022/guildofgleks-ui-dialog.mjs.map +1 -0
- package/fesm2022/guildofgleks-ui-shared.mjs +4692 -0
- package/fesm2022/guildofgleks-ui-shared.mjs.map +1 -0
- package/fesm2022/guildofgleks-ui-table.mjs +20 -0
- package/fesm2022/guildofgleks-ui-table.mjs.map +1 -0
- package/fesm2022/guildofgleks-ui.mjs +609 -3719
- package/fesm2022/guildofgleks-ui.mjs.map +1 -1
- package/package.json +17 -1
- package/styles/button.css +5 -5
- package/styles/presets/bevel.css +110 -87
- package/styles/presets/ledger.css +22 -2
- package/styles/presets/material.css +157 -122
- package/styles/presets/one-dark.css +101 -82
- package/styles/presets/one-light.css +99 -73
- package/styles/presets/parchment.css +89 -63
- package/styles/presets/primeng.css +175 -139
- package/styles/presets/slate.css +21 -1
- package/styles/presets/terminal.css +43 -8
- package/styles/theme.css +2905 -2591
- package/types/guildofgleks-ui-datepicker.d.ts +1 -0
- package/types/guildofgleks-ui-dialog.d.ts +1 -0
- package/types/guildofgleks-ui-shared.d.ts +2000 -0
- package/types/guildofgleks-ui-table.d.ts +1 -0
- package/types/guildofgleks-ui.d.ts +279 -1226
package/README.md
CHANGED
|
@@ -1,467 +1,523 @@
|
|
|
1
|
-

|
|
2
|
-

|
|
3
|
-

|
|
4
|
-

|
|
5
|
-

|
|
6
|
-
|
|
7
|
-
# @guildofgleks/ui
|
|
8
|
-
|
|
9
|
-
An Angular 21 and 22 component library with **no CDK and no Material**.
|
|
10
|
-
directives and 3 services, all standalone, all signal-based, themed entirely through CSS custom
|
|
11
|
-
properties.
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
npm install @guildofgleks/ui
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
## Why this one
|
|
18
|
-
|
|
19
|
-
- **Small dependency footprint.** Peers are `@angular/core`, `@angular/common`, `@angular/forms`
|
|
20
|
-
and `@angular/platform-browser`. No router, no CDK, no animations package. `tslib` is the only
|
|
21
|
-
runtime dependency.
|
|
22
|
-
- **Signals throughout.** `input()` / `output()` / `model()`, `OnPush` everywhere, no NgModules.
|
|
23
|
-
- **Themeable without a build step.** Every value a component paints with is a `--gog-*` custom
|
|
24
|
-
property. Swap a palette, restyle one component, or override a single instance — no Sass
|
|
25
|
-
variables, no JS theme object.
|
|
26
|
-
- **Reactive Forms native.** Every form control is a `ControlValueAccessor` built and tested
|
|
27
|
-
against `[formControl]` / `formControlName`.
|
|
28
|
-
- **Your data, your shapes.** Dropdowns take your objects with accessor paths
|
|
29
|
-
(`optionLabel="profile.fullName"`), not a mandated `{ id, name }` DTO.
|
|
30
|
-
- **Accessible by default.** Keyboard navigation, ARIA wiring and generated label associations
|
|
31
|
-
come with the components rather than with extra attributes.
|
|
32
|
-
- **Right-to-left included.** `dir="rtl"` on `<html>` or on any wrapper mirrors every component,
|
|
33
|
-
portaled overlays included. Nothing to configure per component, no second stylesheet.
|
|
34
|
-
|
|
35
|
-
## Setup
|
|
36
|
-
|
|
37
|
-
Install it with whichever package manager you use — or with `ng add`, which installs it and does
|
|
38
|
-
step 1 for you:
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
npm install @guildofgleks/ui
|
|
42
|
-
# or
|
|
43
|
-
yarn add @guildofgleks/ui
|
|
44
|
-
# or — also does step 1 below
|
|
45
|
-
ng add @guildofgleks/ui
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
Steps 2 and 3 are yours either way: a schematic can't know where in your app you want components
|
|
49
|
-
or dialog and toast hosts.
|
|
50
|
-
|
|
51
|
-
**1. Add the stylesheet.** It carries the baseline theme and the utility classes the components
|
|
52
|
-
use — without it they render unstyled.
|
|
53
|
-
|
|
54
|
-
```jsonc
|
|
55
|
-
// angular.json → projects.<app>.architect.build.options
|
|
56
|
-
"styles": [
|
|
57
|
-
"node_modules/@guildofgleks/ui/styles/index.css",
|
|
58
|
-
"src/styles.scss" // yours, after the baseline so it wins
|
|
59
|
-
]
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
It brings its own `box-sizing: border-box`, scoped to the elements the library renders, so the
|
|
63
|
-
components size correctly whether or not your app has a global reset — since 21.6.0. Your own
|
|
64
|
-
reset is untouched either way, and a single class of specificity means your own styles still win.
|
|
65
|
-
|
|
66
|
-
**2. Import components where you use them** — each is standalone:
|
|
67
|
-
|
|
68
|
-
```ts
|
|
69
|
-
import { ButtonComponent, SelectComponent } from '@guildofgleks/ui';
|
|
70
|
-
|
|
71
|
-
@
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
>
|
|
87
|
-
>
|
|
88
|
-
>
|
|
89
|
-
>
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
<
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
--gog-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
>
|
|
156
|
-
>
|
|
157
|
-
>
|
|
158
|
-
>
|
|
159
|
-
>
|
|
160
|
-
>
|
|
161
|
-
>
|
|
162
|
-
>
|
|
163
|
-
>
|
|
164
|
-
>
|
|
165
|
-
>
|
|
166
|
-
>
|
|
167
|
-
>
|
|
168
|
-
>
|
|
169
|
-
>
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
The
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
:
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
It
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
|
452
|
-
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
`
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
`
|
|
1
|
+

|
|
2
|
+

|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
# @guildofgleks/ui
|
|
8
|
+
|
|
9
|
+
An Angular 21 and 22 component library with **no CDK and no Material**. 30 components, 5
|
|
10
|
+
directives and 3 services, all standalone, all signal-based, themed entirely through CSS custom
|
|
11
|
+
properties.
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @guildofgleks/ui
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Why this one
|
|
18
|
+
|
|
19
|
+
- **Small dependency footprint.** Peers are `@angular/core`, `@angular/common`, `@angular/forms`
|
|
20
|
+
and `@angular/platform-browser`. No router, no CDK, no animations package. `tslib` is the only
|
|
21
|
+
runtime dependency.
|
|
22
|
+
- **Signals throughout.** `input()` / `output()` / `model()`, `OnPush` everywhere, no NgModules.
|
|
23
|
+
- **Themeable without a build step.** Every value a component paints with is a `--gog-*` custom
|
|
24
|
+
property. Swap a palette, restyle one component, or override a single instance — no Sass
|
|
25
|
+
variables, no JS theme object.
|
|
26
|
+
- **Reactive Forms native.** Every form control is a `ControlValueAccessor` built and tested
|
|
27
|
+
against `[formControl]` / `formControlName`.
|
|
28
|
+
- **Your data, your shapes.** Dropdowns take your objects with accessor paths
|
|
29
|
+
(`optionLabel="profile.fullName"`), not a mandated `{ id, name }` DTO.
|
|
30
|
+
- **Accessible by default.** Keyboard navigation, ARIA wiring and generated label associations
|
|
31
|
+
come with the components rather than with extra attributes.
|
|
32
|
+
- **Right-to-left included.** `dir="rtl"` on `<html>` or on any wrapper mirrors every component,
|
|
33
|
+
portaled overlays included. Nothing to configure per component, no second stylesheet.
|
|
34
|
+
|
|
35
|
+
## Setup
|
|
36
|
+
|
|
37
|
+
Install it with whichever package manager you use — or with `ng add`, which installs it and does
|
|
38
|
+
step 1 for you:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install @guildofgleks/ui
|
|
42
|
+
# or
|
|
43
|
+
yarn add @guildofgleks/ui
|
|
44
|
+
# or — also does step 1 below
|
|
45
|
+
ng add @guildofgleks/ui
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Steps 2 and 3 are yours either way: a schematic can't know where in your app you want components
|
|
49
|
+
or dialog and toast hosts.
|
|
50
|
+
|
|
51
|
+
**1. Add the stylesheet.** It carries the baseline theme and the utility classes the components
|
|
52
|
+
use — without it they render unstyled.
|
|
53
|
+
|
|
54
|
+
```jsonc
|
|
55
|
+
// angular.json → projects.<app>.architect.build.options
|
|
56
|
+
"styles": [
|
|
57
|
+
"node_modules/@guildofgleks/ui/styles/index.css",
|
|
58
|
+
"src/styles.scss" // yours, after the baseline so it wins
|
|
59
|
+
]
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
It brings its own `box-sizing: border-box`, scoped to the elements the library renders, so the
|
|
63
|
+
components size correctly whether or not your app has a global reset — since 21.6.0. Your own
|
|
64
|
+
reset is untouched either way, and a single class of specificity means your own styles still win.
|
|
65
|
+
|
|
66
|
+
**2. Import components where you use them** — each is standalone:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { ButtonComponent, SelectComponent } from '@guildofgleks/ui';
|
|
70
|
+
// Three components have their own entry point, so a lazy route can keep them out of your
|
|
71
|
+
// initial bundle: '@guildofgleks/ui/table', '/datepicker' and '/dialog'.
|
|
72
|
+
|
|
73
|
+
@Component({
|
|
74
|
+
imports: [ButtonComponent, SelectComponent],
|
|
75
|
+
template: `
|
|
76
|
+
<gog-select label="Region" [options]="regions" [(value)]="region" />
|
|
77
|
+
<gog-button (gogClick)="save()">Save</gog-button>
|
|
78
|
+
`,
|
|
79
|
+
})
|
|
80
|
+
export class ExampleComponent {}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Outputs are prefixed `gog` (`gogClick`, `gogToggle`) so they never collide with native DOM
|
|
84
|
+
events. Inputs keep their natural names.
|
|
85
|
+
|
|
86
|
+
> **Don't write `(click)` on `gog-button`.** Its click handler is bound on the `<button>` inside
|
|
87
|
+
> its own template, not on the host — a native click still bubbles up through the host element, so
|
|
88
|
+
> a `(click)` listener there fires on every press, silently bypassing `debounce`'s throttling.
|
|
89
|
+
> `(gogClick)` is the one that only emits once the debounce window has passed; use it instead. This
|
|
90
|
+
> is specific to the `gog-button` component — `[gogButton]` on your own `<a>`/`<button>` has no
|
|
91
|
+
> debounce to bypass, so your own `(click)` on it works exactly as written.
|
|
92
|
+
|
|
93
|
+
**3. If you use dialogs or toasts, place their hosts once.** `DialogService.open()` and
|
|
94
|
+
`ToastService.show()` update state but render nothing without them:
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
@Component({
|
|
98
|
+
selector: 'app-root',
|
|
99
|
+
imports: [DialogComponent, ToastContainerComponent],
|
|
100
|
+
template: `
|
|
101
|
+
<router-outlet />
|
|
102
|
+
<gog-dialog />
|
|
103
|
+
<gog-toast-container />
|
|
104
|
+
`,
|
|
105
|
+
})
|
|
106
|
+
export class App {}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
One `<gog-dialog />` hosts every dialog (they stack); one `<gog-toast-container />` hosts all
|
|
110
|
+
four toast corners.
|
|
111
|
+
|
|
112
|
+
## Right-to-left
|
|
113
|
+
|
|
114
|
+
**RTL is supported.** Set `dir="rtl"` on `<html>` (or on any subtree) and every component
|
|
115
|
+
mirrors: stylesheets use logical properties, portaled panels and tooltip bubbles copy a scoped
|
|
116
|
+
`dir` onto themselves, a tooltip's `position="auto"` prefers the mirrored horizontal side, and
|
|
117
|
+
the calendar's month arrows turn around.
|
|
118
|
+
|
|
119
|
+
Two things stay physical on purpose, because they are physical words in the API: a tooltip's
|
|
120
|
+
explicit `position="left"`/`"right"`, and a toast's `top-left`/`top-right`/`bottom-left`/
|
|
121
|
+
`bottom-right` corner. `"auto"` is the direction-aware tooltip placement; pick the corner you
|
|
122
|
+
want for a toast.
|
|
123
|
+
|
|
124
|
+
## Theming
|
|
125
|
+
|
|
126
|
+
Every value the components paint with lives in `styles/theme.css`, in three layers:
|
|
127
|
+
|
|
128
|
+
**Foundation** — palette, type scale, spacing, motion, and a small _character_ layer: corner
|
|
129
|
+
rounding (`--gog-radius`), border weight (`--gog-control-border-*` for form fields,
|
|
130
|
+
`--gog-panel-border-*` for raised surfaces, `--gog-border-*` for everything smaller and inline),
|
|
131
|
+
and emphasis casing/tracking (`--gog-text-transform`, `--gog-letter-spacing`). Override these to
|
|
132
|
+
restyle everything at once; component tokens derive from them, so a palette or character change
|
|
133
|
+
carries through on its own, with nothing to re-list per component.
|
|
134
|
+
|
|
135
|
+
**Component** — `--gog-<component>-*`, one block per component, named after the component you
|
|
136
|
+
write in markup (`gog-button` → `--gog-button-*`), to restyle a single component app-wide:
|
|
137
|
+
|
|
138
|
+
```css
|
|
139
|
+
:root[data-theme='mine'] {
|
|
140
|
+
--gog-button-font-family: var(--gog-font-body);
|
|
141
|
+
--gog-button-ghost-hover-bg: color-mix(in srgb, var(--gog-accent-color) 20%, transparent);
|
|
142
|
+
--gog-table-hover-bg: var(--gog-hover-color);
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Instance** — a small set left deliberately undeclared, so setting one anywhere beats the
|
|
147
|
+
variant and size classes:
|
|
148
|
+
|
|
149
|
+
```css
|
|
150
|
+
.my-form gog-button {
|
|
151
|
+
--gog-button-bg: rebeccapurple; /* wins over .gog-btn--primary */
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
> **Renamed in 21.5.0, removed in 21.7.0.** Three prefixes were abbreviated and are now spelled
|
|
156
|
+
> out: `--gog-btn-*` → `--gog-button-*`, `--gog-confirm-*` → `--gog-confirmation-dialog-*`, and
|
|
157
|
+
> `--gog-ms-*` → `--gog-multiselect-*` (that one since 21.3.0). **The old spellings no longer
|
|
158
|
+
> resolve.** If you set one of them, rename it — a `var()` reference to a name nothing declares
|
|
159
|
+
> doesn't fail your build, it just silently stops matching anything.
|
|
160
|
+
>
|
|
161
|
+
> One prefix that looks abbreviated and is not: **`--gog-input-*`**. It names the shared
|
|
162
|
+
> text-field block that both `gog-inputfield` and `gog-textarea` render (`.gog-input__field`), not
|
|
163
|
+
> the `gog-inputfield` component — the two are meant to restyle together from one token set, so
|
|
164
|
+
> there is no `--gog-inputfield-*` and there will not be one.
|
|
165
|
+
>
|
|
166
|
+
> And one prefix that means two things on purpose: **`--gog-panel-*`**. Four of them —
|
|
167
|
+
> `--gog-panel-radius`, `--gog-panel-shadow`, `--gog-panel-border-width`,
|
|
168
|
+
> `--gog-panel-border-style` — are the _foundation_ surface tier that dialogs, dropdown panels and
|
|
169
|
+
> tooltips read, and the `gog-panel` component reads them too rather than owning a fourth copy of
|
|
170
|
+
> "what a raised surface looks like here". Change one and every raised surface follows, which is
|
|
171
|
+
> the intent; the rest of `--gog-panel-*` belongs to the component alone.
|
|
172
|
+
|
|
173
|
+
**Shadows come off a ladder, not out of a stylesheet.** Since 21.12.0 every raised surface reads
|
|
174
|
+
one of six heights — `--gog-elevation-0` through `-5`, with Z doubling: 0, 1, 2, 4, 8, 16. Step 3
|
|
175
|
+
is anything anchored to a control (a dropdown panel, a tooltip, a menu), step 4 a toast, step 5 a
|
|
176
|
+
modal dialog. The steps are generated, so you never write one; a theme turns ten knobs and all six
|
|
177
|
+
follow:
|
|
178
|
+
|
|
179
|
+
```css
|
|
180
|
+
:root[data-theme='mine'] {
|
|
181
|
+
--gog-elevation-ink: 15 23 42; /* the shadow's colour, unpacked for rgb(… / α) */
|
|
182
|
+
--gog-elevation-key-alpha: 0.12; /* the light that moves with height */
|
|
183
|
+
--gog-elevation-ambient-alpha: 0.06; /* the contact shadow, which does not */
|
|
184
|
+
--gog-elevation-contact-blur: 3px;
|
|
185
|
+
--gog-elevation-key-x: 0; /* per unit of Z — the three that carry the style */
|
|
186
|
+
--gog-elevation-key-y: 1;
|
|
187
|
+
--gog-elevation-key-blur: 3;
|
|
188
|
+
--gog-elevation-ring-width: 1px; /* a hairline contour; 0px for none */
|
|
189
|
+
--gog-elevation-highlight-ink: 255 255 255; /* a top-edge catch light, for dark grounds */
|
|
190
|
+
--gog-elevation-highlight-alpha: 0;
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
The three per-Z multipliers are the whole style axis. Leave them alone for a soft drop shadow; set
|
|
195
|
+
`key-x` and `key-y` to a fraction and `key-blur` to `0` for a hard offset (that is what `bevel` and
|
|
196
|
+
`ledger` do); set `key-y` to `0` and keep the blur and the key light becomes a glow (`terminal`).
|
|
197
|
+
**Declare all ten or none** — a custom property inherits, so a theme that states six of them picks
|
|
198
|
+
the other four up from whatever encloses it, which is how a light subtree inside a dark page ends
|
|
199
|
+
up with dark-weight shadows. `--gog-panel-shadow`, `--gog-dialog-shadow` and the rest are still the
|
|
200
|
+
names you override for a single surface; what changed is that their default is a step.
|
|
201
|
+
|
|
202
|
+
**A control's edge and a divider are different tokens.** `--gog-border-color` is decoration —
|
|
203
|
+
dividers, table rules, panel outlines — and every theme keeps it faint on purpose.
|
|
204
|
+
`--gog-control-boundary-color` is the edge that says _this is a control_, and WCAG SC 1.4.11 wants
|
|
205
|
+
it at 3:1 against whatever it sits on. If you build a theme, set both: a palette that gives them
|
|
206
|
+
one value either shouts its dividers or hides its controls. `npm run suggest:color -- <ink>
|
|
207
|
+
<ground> 3` will tell you the nearest passing value for any colour you would rather keep.
|
|
208
|
+
|
|
209
|
+
**A status colour is three tokens, not one.** `--gog-danger-color` and its three siblings are
|
|
210
|
+
fills, and a fill needs a label that reads on it and a direction to deepen in — so each also has
|
|
211
|
+
`--gog-<status>-text-color` (the label; defaults to the accent's, state it only when your hue
|
|
212
|
+
disagrees) and `--gog-<status>-shade` (which way hover and press move; defaults to the page's ink,
|
|
213
|
+
and should be the opposite when your label _is_ the ink). Setting a status colour alone and
|
|
214
|
+
leaving those at their defaults is how a bright amber ends up under white text: it measured 1.97:1
|
|
215
|
+
in one of this package's own presets before 21.9.0. `gogBadge` and `gog-button`'s `severity` read
|
|
216
|
+
the label; the button also reads the shade. `gog-tag` derives its own pair by mixing and
|
|
217
|
+
`gog-progressbar` paints no label on its bar, so neither needs them.
|
|
218
|
+
|
|
219
|
+
Every group and token name is in **[`TOKENS.md`](./TOKENS.md)**, generated from `theme.css` so it
|
|
220
|
+
cannot drift, and available at runtime as `GOG_TOKEN_GROUPS`.
|
|
221
|
+
|
|
222
|
+
### Light, dark and your own
|
|
223
|
+
|
|
224
|
+
The active theme is a `data-theme` attribute on `:root`, managed by `ThemeService`:
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
private readonly theme = inject(ThemeService);
|
|
228
|
+
this.theme.toggleTheme(); // light ⇄ dark
|
|
229
|
+
this.theme.setTheme('one-dark'); // any preset you imported, or any name you declared in CSS
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Out of the box it adopts whatever `data-theme` is already on the document, or `light`.
|
|
233
|
+
Persisting the choice and following the OS setting are opt-in:
|
|
234
|
+
|
|
235
|
+
```ts
|
|
236
|
+
provideGogConfig({
|
|
237
|
+
theme: { storageKey: 'app-theme', followSystem: true, darkTheme: 'one-dark' },
|
|
238
|
+
});
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
A theme only declares what it changes — the derived layer re-resolves against whatever is in
|
|
242
|
+
scope, so a theme restyles every component without listing any of them. **Eleven declarations
|
|
243
|
+
are a whole visual identity**, not a recolour:
|
|
244
|
+
|
|
245
|
+
```css
|
|
246
|
+
[data-theme='compact'] {
|
|
247
|
+
/* Palette: every colour in the library re-derives from these. */
|
|
248
|
+
--gog-background-color: #f4f6f8;
|
|
249
|
+
--gog-surface-color: #ffffff;
|
|
250
|
+
--gog-text-color: #1e293b;
|
|
251
|
+
--gog-accent-color: #4f46e5;
|
|
252
|
+
|
|
253
|
+
/* Character: every corner, border, label and gap in the library re-derives from these. */
|
|
254
|
+
--gog-radius: 2px;
|
|
255
|
+
--gog-density: 0.85; /* one number = every padding and gap at once */
|
|
256
|
+
--gog-control-border-width: 1px;
|
|
257
|
+
--gog-text-transform: none;
|
|
258
|
+
--gog-letter-spacing: normal;
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
The palette half has always worked this way. The **character layer** (`--gog-radius`,
|
|
263
|
+
`--gog-density`, the border and casing tokens — all since 21.7.0) is the other half: it is what
|
|
264
|
+
lets a theme change the library's _shape_ — square or generous corners, thin borders,
|
|
265
|
+
sentence-case labels, tight or roomy spacing — still without naming a single component.
|
|
266
|
+
|
|
267
|
+
Nine presets ship alongside the built-in `light` and `dark`, each at `styles/presets/<name>.css`
|
|
268
|
+
and activated by `data-theme="<name>"`. All nine set palette **and** character:
|
|
269
|
+
|
|
270
|
+
| Preset | The identity |
|
|
271
|
+
| ----------------------- | ----------------------------------------------------------------------- |
|
|
272
|
+
| `slate` | soft modern — 12px corners, hairline borders, roomier than the default |
|
|
273
|
+
| `one-dark`, `one-light` | editor chrome — 4px corners, compact, sentence case; one UI, two tones |
|
|
274
|
+
| `material`, `primeng` | Material Design 3 and PrimeNG Aura, including their shape and density |
|
|
275
|
+
| `ledger` | administrative software — square corners, hard offset shadow, no motion |
|
|
276
|
+
| `terminal` | green phosphor — monospaced throughout, square, no motion |
|
|
277
|
+
| `bevel` | the early-web desktop — raised buttons, sunken fields, grey and navy |
|
|
278
|
+
| `parchment` | ink on laid paper — old-style serif, oxblood accent, roomy |
|
|
279
|
+
|
|
280
|
+
**No preset downloads a font.** Each sets a stack that resolves to a real system face — the
|
|
281
|
+
platform's own monospace for `terminal`, Tahoma/Verdana for `bevel`, Iowan Old Style/Palatino for
|
|
282
|
+
`parchment` — so importing a preset never adds a network request. Where a webfont makes a visible
|
|
283
|
+
difference, it lives in a separate opt-in file you import _after_ the preset:
|
|
284
|
+
|
|
285
|
+
```css
|
|
286
|
+
@import '@guildofgleks/ui/styles/presets/parchment.css';
|
|
287
|
+
@import '@guildofgleks/ui/styles/presets/parchment.fonts.css'; /* optional: EB Garamond */
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
`terminal.fonts.css` (IBM Plex Mono) is the other one. `material` and `primeng` additionally set
|
|
291
|
+
a few things the character layer has no vocabulary for (a pill button, a table's header font), and
|
|
292
|
+
`bevel` sets one (a button's bevel must disagree with a field's). `AGENTS.md` has the per-preset
|
|
293
|
+
detail and the full token list.
|
|
294
|
+
|
|
295
|
+
Fonts are left alone on purpose (system stacks, no webfont download). Add
|
|
296
|
+
`@guildofgleks/ui/styles/fonts.css` for the showcase's typography.
|
|
297
|
+
|
|
298
|
+
### Making it fluid — one `clamp()`, not thirty
|
|
299
|
+
|
|
300
|
+
**Component sizing ships no `clamp()`, no `vw` and no breakpoints, and that is a decision rather
|
|
301
|
+
than an omission.** A component does not know how wide the screen is; it knows how wide its
|
|
302
|
+
container is, and `size` is your input, not something a stylesheet should override at 400px. So
|
|
303
|
+
fluid sizing is the app's to declare — and because everything here derives from a few foundation
|
|
304
|
+
tokens, it is one declaration rather than one per component.
|
|
305
|
+
|
|
306
|
+
**Chrome that floats over the viewport is the deliberate exception, and it is a narrower thing than
|
|
307
|
+
fluid sizing.** `--gog-tooltip-max-width`, `--gog-menu-max-width` and `--gog-toast-max-width` each
|
|
308
|
+
read `min(<cap>, calc(100vw - <margin> * 2))` — an overlay positioned against the screen rather
|
|
309
|
+
than a container, where "no wider than the screen" is what the component is for, not a style choice
|
|
310
|
+
a consumer makes. It does not grow anything: it only ever narrows a cap that would otherwise
|
|
311
|
+
overflow a small screen. It is not the recipe below, and reading one as an example of the other is
|
|
312
|
+
the mistake to avoid.
|
|
313
|
+
|
|
314
|
+
Viewport units appear in four other places, all older than that rule and all the same shape — a
|
|
315
|
+
ceiling rather than a curve: a dialog panel defaults to `90vw` and `--gog-dialog-max-height` to
|
|
316
|
+
`90vh`, `gog-menu` falls back to `100vh` when it cannot measure the room below its trigger, and
|
|
317
|
+
`gog-table`'s `maxHeight` takes any CSS length you give it, `'60vh'` included. `gog-confirmation-dialog`
|
|
318
|
+
has **no** viewport clamp of its own precisely because the panel it renders inside already carries
|
|
319
|
+
one.
|
|
320
|
+
|
|
321
|
+
Interpolate as a straight line between two viewports. Between `(W_min, V_min)` and
|
|
322
|
+
`(W_max, V_max)`:
|
|
323
|
+
|
|
324
|
+
```
|
|
325
|
+
slope m = (V_max − V_min) / (W_max − W_min) × 100 → the vw coefficient
|
|
326
|
+
intercept b = (W_min·V_max − W_max·V_min) / (W_min − W_max) → the constant
|
|
327
|
+
size = clamp(V_min, b + m·vw, V_max)
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
**The type scale is in `rem`, so the root font size is the one knob that moves all of it.** For 15px
|
|
331
|
+
at a 360px viewport growing to 17px at 1440px — `m = 0.185`, `b = 14.33px` — write the constant in
|
|
332
|
+
`rem` rather than `px`:
|
|
333
|
+
|
|
334
|
+
```css
|
|
335
|
+
html {
|
|
336
|
+
/* 15px at 360px wide, 17px at 1440px. 0.8958rem is the 14.33px intercept. */
|
|
337
|
+
font-size: clamp(0.9375rem, 0.8958rem + 0.185vw, 1.0625rem);
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
**Keep the intercept in `rem`, not `px`.** A viewport-only font size ignores the reader's own
|
|
342
|
+
browser text-size setting, which fails WCAG 1.4.4; with a `rem` term in the expression, their
|
|
343
|
+
preference still scales the result. Every `--gog-text-*` follows, and so does `--gog-icon-size`,
|
|
344
|
+
which is `1.2em`.
|
|
345
|
+
|
|
346
|
+
Spacing does not follow, deliberately: `--gog-space-*` is authored in `px` times `--gog-density` so
|
|
347
|
+
that one number is the whole spacing system, and a unitless multiplier cannot carry a `vw` term
|
|
348
|
+
(`calc()` will not add a number to a length). If you want gaps to grow with the type too, restate
|
|
349
|
+
the ten steps against the root font size once — the derived layer re-resolves and every component
|
|
350
|
+
follows:
|
|
351
|
+
|
|
352
|
+
```css
|
|
353
|
+
:root {
|
|
354
|
+
--gog-space-4: calc(0.25rem * var(--gog-density));
|
|
355
|
+
--gog-space-8: calc(0.5rem * var(--gog-density));
|
|
356
|
+
/* …12, 16, 20, 24, 28, 32, 40, 48, each Npx as N/16 rem */
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
`--gog-density` on its own remains the simpler answer for "roomier" versus "compact", and it needs
|
|
361
|
+
no arithmetic at all.
|
|
362
|
+
|
|
363
|
+
## App-wide configuration
|
|
364
|
+
|
|
365
|
+
Anything visual is a token. Everything else — the settings you would otherwise repeat on every
|
|
366
|
+
instance — goes through one provider:
|
|
367
|
+
|
|
368
|
+
```ts
|
|
369
|
+
provideGogConfig({
|
|
370
|
+
control: { size: 'sm', errorDisplay: 'auto', clearable: true },
|
|
371
|
+
dropdown: { appendToBody: true },
|
|
372
|
+
datepicker: { locale: 'de-DE', format: 'dd.MM.yyyy' },
|
|
373
|
+
ripple: { enabled: true }, // press feedback on every interactive surface at once
|
|
374
|
+
labels: { clear: 'Löschen', selectAll: 'Alle auswählen' }, // translate the library once
|
|
375
|
+
});
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Keys: `control`, `dropdown`, `floatLabel`, `datepicker`, `autocomplete`, `inputfield`, `textarea`,
|
|
379
|
+
`tooltip`, `scroll`, `button`, `ripple`, `spinner`, `paginator`, `toast`, `theme`, `labels`. An
|
|
380
|
+
instance's own input always wins, and providing the config again lower in the injector tree layers
|
|
381
|
+
onto the parent rather than replacing it.
|
|
382
|
+
|
|
383
|
+
`spinner` is the one key that takes a **component** rather than a value:
|
|
384
|
+
|
|
385
|
+
```ts
|
|
386
|
+
provideGogConfig({ spinner: { component: HouseLoader } });
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
That renders your loader wherever the library draws a spinner — including the two places no input
|
|
390
|
+
could reach, since `gog-button` and `gog-autocomplete` render their own and expose nothing for it.
|
|
391
|
+
It keeps the library's sizing, overlay behaviour, `role="status"` and accessible name; only the
|
|
392
|
+
visual is yours.
|
|
393
|
+
|
|
394
|
+
`ripple` is the one visual default that is not a token, and the exception is deliberate:
|
|
395
|
+
`--gog-ripple-opacity: 0` would hide the wash but still pay for the DOM node, the listeners and
|
|
396
|
+
the animation frames, so a real off has to reach the TypeScript. It is **off by default**, and
|
|
397
|
+
every rippling component takes a `ripple` input that beats it in both directions.
|
|
398
|
+
|
|
399
|
+
**Under `prefers-reduced-motion: reduce` the ripple does not appear at all** — suppressed
|
|
400
|
+
outright rather than shortened, in CSS and in the controller, so no node is created and no
|
|
401
|
+
listener attached. If you turned it on and see nothing, check that setting before checking your
|
|
402
|
+
config.
|
|
403
|
+
|
|
404
|
+
That is the one place motion is removed entirely, and it is deliberate: a ripple is decoration,
|
|
405
|
+
so losing it costs a user nothing. Everywhere else the rule is the opposite — **reduced motion
|
|
406
|
+
drops the animation, never the information.** A chevron still turns to show a panel is open, a
|
|
407
|
+
toggle still moves; only the tween between the two states goes away.
|
|
408
|
+
|
|
409
|
+
Icons work the same way — 41 Lucide glyphs ship with the package, and your own register by name:
|
|
410
|
+
|
|
411
|
+
```ts
|
|
412
|
+
provideGogIcons({ cart: '<svg viewBox="0 0 24 24">…</svg>' });
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
```html
|
|
416
|
+
<gog-icon name="cart" /> <gog-tag iconName="cart">In basket</gog-tag>
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
## Overlays and the viewport
|
|
420
|
+
|
|
421
|
+
Three things this library renders cover the **viewport** with `position: fixed`:
|
|
422
|
+
`<gog-dialog />`'s backdrop, `<gog-toast-container />`, and `<gog-spinner [overlay]="true" />`.
|
|
423
|
+
|
|
424
|
+
That is true only while nothing above them establishes a containing block. `contain`,
|
|
425
|
+
`transform`, `filter`, `backdrop-filter` and `will-change` on **any** ancestor silently retarget
|
|
426
|
+
a fixed element to that ancestor's box — a CSS rule with no error and no warning, and the usual
|
|
427
|
+
first sighting is "my modal only dims half the page".
|
|
428
|
+
|
|
429
|
+
It is not hypothetical here: **`gog-scroll` sets `contain: layout style`**, so a dialog opened
|
|
430
|
+
from inside a scroller dims the scroller, and a toast container nested in one corners its toasts
|
|
431
|
+
against the scroller. Two rules keep it simple:
|
|
432
|
+
|
|
433
|
+
- **Place the dialog and toast outlets in your root component**, not inside the section that
|
|
434
|
+
happens to use them. They are singletons anyway — one of each renders everything.
|
|
435
|
+
- **A spinner overlay covers whatever contains it**, which is often what you want inside a card.
|
|
436
|
+
For a genuinely full-screen one, render it at the root too.
|
|
437
|
+
|
|
438
|
+
The dropdown panels (`gog-select`, `gog-multiselect`, `gog-autocomplete`, `gog-datepicker`) and
|
|
439
|
+
`gog-menu` avoid the whole question by rendering into `<body>` — `appendToBody` for the
|
|
440
|
+
dropdowns, always for the menu.
|
|
441
|
+
|
|
442
|
+
## Components
|
|
443
|
+
|
|
444
|
+
| Group | Components |
|
|
445
|
+
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
446
|
+
| 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` |
|
|
447
|
+
| Actions | `gog-button`, `gog-chip` |
|
|
448
|
+
| Data | `gog-table` (+ `gog-column`), `gog-paginator`, `gog-tag` |
|
|
449
|
+
| Layout & disclosure | `gog-accordion`, `gog-tabs` (+ `gog-tab`), `gog-collapsible`, `gog-card`, `gog-panel`, `gog-divider`, `gog-scroll` |
|
|
450
|
+
| Overlays | `gog-dialog`, `gog-confirmation-dialog`, `gog-toast` (+ `gog-toast-container`), `gog-menu` (+ `gogMenuTrigger` / `gogMenuItem`) |
|
|
451
|
+
| Feedback | `gog-spinner`, `gog-spinner-overlay`, `gog-progressbar`, `gog-skeleton`, `gog-alert` |
|
|
452
|
+
| Content | `gog-icon` |
|
|
453
|
+
|
|
454
|
+
**Directives:** `gogButton` (a link that looks like a button), `gogTooltip`, `gogBadge`,
|
|
455
|
+
`gogRipple` (a press wash on any element), `gogCollapsibleTrigger`, `gogCollapsibleContent`,
|
|
456
|
+
`gogCardLink` (a link the whole card activates).
|
|
457
|
+
**Services:** `DialogService`, `ToastService`, `ThemeService`.
|
|
458
|
+
|
|
459
|
+
Seventeen more directives go on markup you own rather than configuring a component through an
|
|
460
|
+
input — slots like `gogColumnBody`, `gogInputAddonStart` and `gogDropdownOption`, and the menu's
|
|
461
|
+
`gogMenuTrigger` / `gogMenuItem`.
|
|
462
|
+
|
|
463
|
+
A few things worth knowing before you reach for a workaround:
|
|
464
|
+
|
|
465
|
+
- **`gog-table` works two ways.** By default it owns the data and sorts and pages in memory. With
|
|
466
|
+
`[lazy]="true"` it hands both to the server: `value` is the current page, `totalRecords` drives
|
|
467
|
+
the paginator, and `gogSortChange` / `gogPageChange` are your refetch signals. Row selection is
|
|
468
|
+
`selectionMode` + `[(selection)]`; set `dataKey` or a refetch drops it.
|
|
469
|
+
|
|
470
|
+
**And here is where it stops, so you find out now rather than halfway in.** `gog-table` has no
|
|
471
|
+
**column resizing or reordering** by the reader (a column's `width`/`minWidth`/`maxWidth` are
|
|
472
|
+
yours to set, not theirs to drag), no **frozen columns**, no **expandable rows** and no **row
|
|
473
|
+
grouping**.
|
|
474
|
+
|
|
475
|
+
It **does** virtualize, with `virtualize` — which needs `maxHeight` and `fullWidth`, and says so
|
|
476
|
+
in a dev-mode warning if either is missing. That is the DOM half: `[lazy]="true"` keeps the fetch
|
|
477
|
+
small and still stamps every row it is handed. Neither substitutes for the other, and a long
|
|
478
|
+
table usually wants both.
|
|
479
|
+
|
|
480
|
+
`stickyHeader` is not the missing feature in disguise — it pins the header while rows scroll
|
|
481
|
+
under it, which is the vertical axis. Freezing a first column against horizontal scroll is the
|
|
482
|
+
one this list says no to.
|
|
483
|
+
|
|
484
|
+
If you need a data grid, use one. This is a table that sorts, pages, selects and lets you
|
|
485
|
+
template any cell, and it is meant to stay small enough to read.
|
|
486
|
+
|
|
487
|
+
- **`gog-button` cannot be a link** — it renders its own `<button>`. Use `[gogButton]` on your own
|
|
488
|
+
`<a>` instead; nothing is brokered through inputs, so `routerLink`, `href` and `target` keep
|
|
489
|
+
working. That is also why this package needs no `@angular/router`.
|
|
490
|
+
- **`gog-inputfield` and `gog-textarea` forward the native attribute space** they wrap —
|
|
491
|
+
`readonly`, `maxlength`, `pattern`, `inputMode`, `spellcheck` and the text-field `type` values.
|
|
492
|
+
They also generate their own `id`, so labels and error messages are wired up without `inputId`.
|
|
493
|
+
- **`gog-collapsible` is headless** — no markup of its own. Project any element as the trigger and
|
|
494
|
+
any element as the panel.
|
|
495
|
+
- **A clickable `gog-card` has no `interactive` input.** Put `gogCardLink` on the `<a>` the card is
|
|
496
|
+
about — usually the one in its heading — and the whole surface activates that link, keyboard,
|
|
497
|
+
middle-click and `routerLink` included. Same reasoning as `gog-button` above: the element stays
|
|
498
|
+
yours. Anything else focusable in the card keeps receiving its own clicks.
|
|
499
|
+
- **`gog-panel` shares the `--gog-panel-*` prefix with the foundation surface tier.**
|
|
500
|
+
`--gog-panel-radius`, `--gog-panel-shadow` and the border pair are the tokens dialogs and
|
|
501
|
+
dropdown panels already read, so a theme's idea of a raised surface reaches the component for
|
|
502
|
+
free. Its own family (background, padding, heading, toggle, footer) sits alongside them.
|
|
503
|
+
- **`[(ngModel)]` is untested.** The library never imports `FormsModule`; use Reactive Forms.
|
|
504
|
+
|
|
505
|
+
## Documentation
|
|
506
|
+
|
|
507
|
+
| | |
|
|
508
|
+
| ---------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
509
|
+
| **[`AGENTS.md`](./AGENTS.md)** | the full API reference — every input, output, slot, type and default, per component. Ships in this package. |
|
|
510
|
+
| **[`TOKENS.md`](./TOKENS.md)** | every `--gog-*` token, generated from `theme.css` |
|
|
511
|
+
| [CHANGELOG](https://github.com/GuildOfGleks/gleks_web_ui/blob/master/projects/gleks/ui/CHANGELOG.md) | release history |
|
|
512
|
+
|
|
513
|
+
`AGENTS.md` is written for an AI coding assistant working in your project, but it is the most
|
|
514
|
+
complete API reference either way — point your assistant at it and it will stop guessing.
|
|
515
|
+
|
|
516
|
+
## License
|
|
517
|
+
|
|
518
|
+
Apache-2.0 © Roman Malitskyi
|
|
519
|
+
|
|
520
|
+
Built-in icons are [Lucide](https://lucide.dev) glyphs, inlined so the package keeps zero runtime
|
|
521
|
+
dependencies. Lucide is ISC licensed; portions are held by Cole Bemis 2013–2022 as part of
|
|
522
|
+
Feather (MIT), all others by Lucide Contributors 2022 — full notice in
|
|
523
|
+
`shared/icons.ts`.
|