@masmarino/gabarit 1.3.0 → 2.0.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/README.md CHANGED
@@ -135,6 +135,12 @@ but plain, without the layout the directive provides.
135
135
  | `MenuTrigger` | `[gbtMenuTrigger]` | `gbt-menu`: a custom trigger element instead of the default button. |
136
136
  | `StatTileLink` | `a[gbtStatTileLink]` | `gbt-stat-tile`: the router-agnostic link, its text is the label. |
137
137
 
138
+ ## Documentation reader
139
+
140
+ `@masmarino/gabarit/docs` is a reader for documentation written in Markdown and shipped with an app (navigation,
141
+ search, outline, neighbours), with `gbt-markdown-view` for Markdown anywhere else. It is a separate entry point
142
+ because it needs `@angular/router`, `marked` and `dompurify`; see [its README](docs/README.md).
143
+
138
144
  ## Primitives and pipes
139
145
 
140
146
  Pure functions exported from `@masmarino/gabarit`, usable in any TypeScript
@@ -285,92 +291,133 @@ keep their source order); with `compare`, `desc` reverses the ascending result.
285
291
 
286
292
  ## Styles
287
293
 
288
- Import the tokens in the application's `styles.scss`:
294
+ Gabarit carries the look of the Ferris family: white paper and near-black ink in the light theme, a
295
+ cool graphite in the dark one, IBM Plex throughout, hairlines and square-ish corners. Neutrals carry
296
+ the screens; the crab's rust is kept for the marks that are the brand's own (focus, the current page),
297
+ patina green says success. Every text token reaches 7:1 on the page and on a panel, in both themes
298
+ (`contrast.spec.ts`).
299
+
300
+ Import the tokens, and the fonts, in the application's `styles.scss`:
289
301
 
290
302
  ```scss
291
- @use 'gabarit/tokens' as *;
303
+ @use '@masmarino/gabarit/fonts' with ($path: '/fonts/ibm-plex/');
304
+ @use '@masmarino/gabarit/tokens';
305
+
306
+ // Optional: body, h1–h3, code and selection in the family's type.
307
+ @include tokens.document-base;
292
308
  ```
293
309
 
294
- Gabarit declares **no** `@font-face` rule. The tokens expose
295
- `--font-family: 'Inter', sans-serif`, but serving and declaring the font
296
- is the application's responsibility.
310
+ The fonts entry declares IBM Plex Sans (400, 500, 600), Sans Condensed (600) and Mono (400, 500),
311
+ subset to Latin-1 (OFL licence beside the files). The application serves the files at `$path`, for
312
+ instance with an Angular asset:
313
+
314
+ ```json
315
+ { "glob": "*.woff2", "input": "node_modules/@masmarino/gabarit/fonts", "output": "fonts/ibm-plex" }
316
+ ```
317
+
318
+ Without them the stacks fall back to the system's sans and monospace.
319
+
320
+ ### The graphite frame
321
+
322
+ `gbt-app-shell` draws its rail and its bar as one graphite frame, the same in both themes, with the
323
+ page set into it as a sheet whose corner is rounded. The auth pages (`gbt-auth-panel`) open on the
324
+ same graphite, their panel keeping the page's theme. An application can lay its own parts on the
325
+ frame with the `frame-tokens` mixin, which sets the dark tokens on a deeper ground:
326
+
327
+ ```scss
328
+ .public-header {
329
+ @include tokens.frame-tokens;
330
+ background: var(--bg-panel);
331
+ }
332
+ ```
297
333
 
298
334
  ### Color tokens
299
335
 
300
- Every component exclusively reads the custom properties below, set on
301
- `:root` by `_semantic.scss`. **These, and only these, are what an
302
- application should override to re-theme itself** — the raw palette
303
- (`--brand-*`, `--grey-*`, `--red-*`…) is an internal detail, never
304
- referenced outside `_semantic.scss` (enforced by `token-usage.spec.ts`).
305
-
306
- | Category | Token | Role |
307
- | ----------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------- |
308
- | Brand | `--primary` / `--primary-hover` | Action color (buttons, active links, focus) and its hover state |
309
- | Backgrounds | `--bg-principal` | Page and surface background (cards, panels) |
310
- | | `--bg-panel` | Background of persistent navigation areas (nav, header) |
311
- | | `--bg-hover` | Hover state of an interactive element on a neutral background |
312
- | | `--bg-track` | Track of a segmented control, visible on the page and on a panel |
313
- | Border | `--border-color` | All borders |
314
- | | `--gbt-hairline` / `--gbt-card-border` | Quiet separators and card edges, derived from `--border-color` |
315
- | Text | `--text-primary` / `--text-secondary` / `--text-discret` | From most to least emphasized |
316
- | | `--text-on-primary` / `--text-on-error` / `--text-on-color` | Text set on a `--primary` fill, an error fill, or a solid color |
317
- | Success | `--color-success-base` / `-hover` / `-text` / `-bg` / `-bg-text` | Fill, hover, text, light background, text on that background |
318
- | Warning | `--color-warning-base` / `-hover` / `-text` / `-bg` / `-bg-text` | Same |
319
- | Error | `--color-error-base` / `-fill` / `-hover` / `-text` / `-bg` / `-bg-text` | Same (`-fill`: solid fill, e.g. an icon) |
320
- | Dataviz | `--chart-series-1-base`, `-2-base`, `-3-base` | The three chart series, in order |
321
- | | `--chart-grid` | Chart grid and axes |
322
-
323
- Other tokens live on `:root` without being colors — border radii
324
- (`--site-border-radius*`), shadows (`--site-shadow-*`), transition
325
- durations (`--site-transition-*`), the monospace stack `--gbt-font-mono` — overridable the same way.
336
+ Every component reads only the custom properties below, set on `:root` from the `light-tokens` and
337
+ `dark-tokens` mixins (`_themes.scss`). **These are what an application overrides to re-theme
338
+ itself**; the raw palette (`--slate-*`, `--graphite-*`, `--rust-*`…) is never referenced by a
339
+ component (enforced by `token-usage.spec.ts`).
340
+
341
+ | Category | Token | Role |
342
+ | ----------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------- |
343
+ | Brand | `--primary` / `--primary-hover` | Action colour (buttons, selected states): the ink |
344
+ | | `--accent` / `--accent-text` | The rust, for marks; its text shade |
345
+ | | `--focus-ring` | Every focus outline |
346
+ | Backgrounds | `--bg-principal` | Surfaces: cards, panels, fields |
347
+ | | `--bg-panel` | The page's ground |
348
+ | | `--bg-hover` | Hover state of an interactive element on a neutral background |
349
+ | | `--bg-track` | Track of a segmented control, visible on the page and on a panel |
350
+ | | `--frame` | The graphite of the shell and the auth pages, in both themes |
351
+ | Border | `--border-color` | The edge of a field or a control (3:1) |
352
+ | | `--gbt-hairline` / `--gbt-card-border` | Quiet separators and card edges |
353
+ | Text | `--text-primary` / `--text-secondary` / `--text-discret` | From most to least emphasized |
354
+ | | `--text-on-primary` / `--text-on-error` / `--text-on-color` | Text set on a `--primary` fill, an error fill, or a solid colour |
355
+ | Success | `--color-success-base` / `-hover` / `-text` / `-bg` / `-bg-text` | Fill, hover, text, light background, text on that background |
356
+ | Warning | `--color-warning-base` / `-hover` / `-text` / `-bg` / `-bg-text` | Same |
357
+ | Error | `--color-error-base` / `-fill` / `-hover` / `-text` / `-bg` / `-bg-text` | Same (`-fill`: a solid fill, the danger button) |
358
+ | Info | `--color-info-bg` / `-bg-text` / `-vivid-base` | Light background, text on it, the mark |
359
+ | Dataviz | `--chart-series-1-base` … `-6-base` | The chart series, in order |
360
+ | | `--chart-grid` | Chart grid and axes |
361
+
362
+ Other tokens live on `:root` without being colours: border radii (`--site-border-radius*`, 3 to
363
+ 6px), shadows (`--site-shadow-*`, rings rather than drops), transitions (`--site-transition-*`) and
364
+ the type stacks (`--font-family`, `--font-display`, `--gbt-font-mono`).
326
365
 
327
366
  ### Theming hooks
328
367
 
329
- A few more tokens are not set on `:root`: each component falls back to the
330
- value in the last column, resolved where it is used, so an app that leaves
331
- them alone looks the same. Set one to take that part of the look in hand.
332
-
333
- | Token | Used by | Fallback |
334
- | ----------------------------------------------- | ------------------------------------------------------------------------- | --------------------------------------- |
335
- | `--gbt-focus-ring` | Every focus outline | `var(--primary)` |
336
- | `--gbt-font-display` | Page titles, stat tile values, empty state, auth panel and modal headings | `inherit` |
337
- | `--gbt-radius-badge` | Badges, card and tab counts | `999px` (a pill) |
338
- | `--gbt-radius-chip` | Mono badges, inline code in a list row, search bar keys | `6px` |
339
- | `--gbt-radius-menu` | Select and autocomplete panels, menu items, search results | `8px` |
340
- | `--gbt-radius-tile` | Icon marker tiles, job graph nodes, avatar group overflow, TOTP QR code | `8px` (`12px` for the QR) |
341
- | `--gbt-radius-search` | The search bar field | `16px` |
342
- | `--gbt-nav-active-bg` / `--gbt-nav-active-text` | The current page in the app shell navigation | `--primary` / its text |
343
- | `--gbt-nav-active-mark` | A 2px rule on the start edge of the current page link | `transparent` |
344
- | `--gbt-shell-border` | The app shell's navigation and header edges | `var(--border-color)` |
345
- | `--gbt-shell-header-bg` | The app shell's header | `transparent` |
346
- | `--gbt-shell-content-padding` | The app shell's main content area | `1rem` |
347
- | `--gbt-auth-panel-backdrop` | The page behind the auth panels (login, register…) | a soft `--primary` glow on `--bg-panel` |
348
- | `--gbt-auth-panel-radius` | The auth panel card | `12px` |
368
+ A few more tokens are not set on `:root`: each component falls back to the value in the last column,
369
+ so an app that leaves them alone gets the family's look. Set one to take that part in hand.
370
+
371
+ | Token | Used by | Fallback |
372
+ | ----------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------ |
373
+ | `--gbt-focus-ring` | Every focus outline | `var(--focus-ring)` |
374
+ | `--gbt-font-display` | Page, card, panel, modal and drawer titles, stat tile values, empty state | `var(--font-display)` |
375
+ | `--gbt-radius-badge` | Badges, card and tab counts | `3px` |
376
+ | `--gbt-radius-chip` | Mono badges, inline code in a list row, search bar keys | `2px` |
377
+ | `--gbt-radius-menu` | Select and autocomplete panels, menu items, search results | `4px` |
378
+ | `--gbt-radius-tile` | Icon marker tiles, job graph nodes, avatar group overflow, TOTP QR code | `4px` |
379
+ | `--gbt-radius-search` | The search bar field | `3px` |
380
+ | `--gbt-nav-active-bg` / `--gbt-nav-active-text` | The current page in the app shell navigation | a 9% ink tint / `--text-primary` |
381
+ | `--gbt-nav-active-mark` | A 2px rule on the start edge of the current page link | `var(--accent)` |
382
+ | `--gbt-shell-bar` | The height of the app shell's bar and of the logo's corner | `4rem` |
383
+ | `--gbt-shell-border` | The app shell's navigation and header edges | `transparent` |
384
+ | `--gbt-shell-header-bg` | The app shell's header | the frame |
385
+ | `--gbt-shell-content-padding` | The app shell's bar and main content area | `clamp(1rem, 0.25rem + 2vw, 2rem)` |
386
+ | `--gbt-auth-panel-backdrop` | The page behind the auth panels (login, register…) | `var(--frame)` |
387
+ | `--gbt-auth-panel-radius` | The auth panel card | `var(--site-border-radius-lg)` |
349
388
 
350
389
  Overriding a token after the import:
351
390
 
352
391
  ```scss
353
- @use 'gabarit/tokens' as *;
392
+ @use '@masmarino/gabarit/tokens';
354
393
 
355
394
  :root {
356
- --primary: #7c3aed;
395
+ --accent: #7c3aed;
357
396
  }
358
397
  ```
359
398
 
360
- **Dark mode activates via `prefers-color-scheme` or `[data-theme='dark']`
361
- on `<html>`, and reapplies to the same tokens.** An override set on a bare
362
- `:root` applies to both themes indifferently; for a value specific to
363
- dark mode, redeclare it under the same conditions as Gabarit, after its
364
- import:
399
+ **Dark mode activates via `prefers-color-scheme` or `[data-theme='dark']` on `<html>`, and
400
+ reapplies to the same tokens.** An override set on a bare `:root` applies to both themes; for a value
401
+ specific to dark mode, redeclare it under the same conditions as Gabarit, after its import:
365
402
 
366
403
  ```scss
367
404
  @media (prefers-color-scheme: dark) {
368
405
  :root {
369
- --primary: #a78bfa;
406
+ --accent: #a78bfa;
370
407
  }
371
408
  }
372
409
  ```
373
410
 
411
+ ### Upgrading from 1.x
412
+
413
+ - The default look is the Ferris family's (palette, IBM Plex, radii, the graphite shell and auth
414
+ pages). An application that themed 1.x by mapping its own tokens onto Gabarit's can drop that map.
415
+ - The raw palette is new (`--slate-*`, `--graphite-*`, `--rust-*`, `--patina-*`, `--amber-*`,
416
+ `--red-*`, `--indigo-*`); `--brand-*`, `--grey-*`, `--green-*` and `--emerald-*` are gone.
417
+ - New tokens: `--accent`, `--accent-text`, `--focus-ring`, `--frame`, `--font-display`; new mixins:
418
+ `light-tokens`, `dark-tokens`, `frame-tokens`, `document-base`, `visually-hidden`, `focus-ring`.
419
+ - `@masmarino/gabarit/fonts` replaces the font an application served for `'Inter'`.
420
+
374
421
  ## Internationalization
375
422
 
376
423
  Gabarit ships no translation mechanism. Every visible string is a
package/docs/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # @masmarino/gabarit/docs
2
+
3
+ A reader for documentation written in Markdown and shipped with an app: a navigation with search on the left, the page
4
+ with its breadcrumb and neighbours, and its outline on a wide screen. It is its own entry point because it needs
5
+ `@angular/router`, [marked](https://marked.js.org) and [DOMPurify](https://github.com/cure53/DOMPurify), which the rest
6
+ of Gabarit does not; install them next to Gabarit:
7
+
8
+ ```sh
9
+ npm install marked dompurify
10
+ ```
11
+
12
+ ## The pages
13
+
14
+ The app serves the documentation as static files under a root (`/docs` by default), for instance from its `public/`
15
+ folder:
16
+
17
+ ```
18
+ public/docs/
19
+ index.json # the sections and their pages, in reading order
20
+ demarrer/presentation.md # one Markdown file per page: <section>/<page>.md
21
+ demarrer/prise-en-main.md
22
+ ```
23
+
24
+ ```json
25
+ {
26
+ "sections": [
27
+ {
28
+ "slug": "demarrer",
29
+ "title": "Démarrer",
30
+ "pages": [
31
+ { "slug": "presentation", "title": "Présentation", "description": "What the app does." }
32
+ ]
33
+ }
34
+ ]
35
+ }
36
+ ```
37
+
38
+ The index and each page are fetched once and kept. A page links to another with a root-relative link
39
+ (`[Configuration](/docs/installation/configuration#variables)`): the reader follows it through the router. A quote
40
+ whose first word is a bold callout key becomes a callout: `> **Note** …` (and `> **Warning** …` by default).
41
+
42
+ ## Wiring
43
+
44
+ ```ts
45
+ // app.routes.ts
46
+ import { docsRoutes } from '@masmarino/gabarit/docs'
47
+
48
+ export const routes: Routes = [
49
+ {
50
+ path: 'docs',
51
+ // The app's own page around the reader (its layout, its header), or DocsPage itself.
52
+ children: docsRoutes(() => import('./docs/docs-route').then((m) => m.DocsRoute)),
53
+ },
54
+ ]
55
+ ```
56
+
57
+ ```ts
58
+ // docs-route.ts: the reader inside the app's layout
59
+ @Component({
60
+ imports: [AppLayout, DocsPage],
61
+ providers: [
62
+ provideDocs({ callouts: { note: 'note', attention: 'warning' } }),
63
+ provideDocsLabels(FRENCH_DOCS_LABELS),
64
+ { provide: DOCS_TITLE, useFactory: () => (title: string) => inject(Title).setTitle(title) },
65
+ ],
66
+ template: `<app-layout><gbt-docs-page /></app-layout>`,
67
+ })
68
+ export class DocsRoute {}
69
+ ```
70
+
71
+ `docsRoutes()` sends the root to the first page, mounts `<section>/<page>`, and gives any other address the reader's
72
+ own "not found". `gbt-docs-page` reads the section and the page from the route.
73
+
74
+ ## Providers
75
+
76
+ | Token | Role |
77
+ | --------------------- | ------------------------------------------------------------------------------------------------------ |
78
+ | `provideDocs(config)` | `root` (`/docs`) and the `callouts` keys (`note`, `warning`). |
79
+ | `provideDocsLabels()` | The reader's strings, over the English defaults (`DEFAULT_DOCS_LABELS`). |
80
+ | `DOCS_TITLE` | A function given the shown page's title (or "not found", or the reader's name while loading). Optional. |
81
+
82
+ ## Pieces
83
+
84
+ | Piece | Role |
85
+ | ---------------------- | -------------------------------------------------------------------------------------------------------- |
86
+ | `gbt-docs-page` | The whole reader for the routed page: navigation, page, neighbours, outline, loading, failure, not found. |
87
+ | `gbt-docs-nav` | The sections and their pages, with the search; folds behind a toggle on a narrow layout. |
88
+ | `gbt-docs-search` | A search over every page, in the browser, loaded on first focus. |
89
+ | `gbt-markdown-view` | Markdown to sanitised HTML, with the reading styles; usable on its own (a README, release notes). |
90
+ | `gbt-markdown-outline` | The "On this page" panel for the headings a `gbt-markdown-view` emits. |
91
+
92
+ ## Security
93
+
94
+ The Markdown is sanitised with a dedicated DOMPurify instance, stricter than its defaults: no `<style>`, no `style`
95
+ attribute, no forms or buttons, no IDREFs or `tabindex`, ids and names prefixed with `user-content-`, only the
96
+ `language-*` classes of code blocks kept, task-list checkboxes disabled, remote images lazy and without a referrer.
97
+
98
+ ## Accessibility
99
+
100
+ Every heading gets a stable id; the outline and in-page links move the focus to the heading they reach. Code blocks
101
+ and tables that scroll sideways take the focus and are named. Task-list checkboxes are named by their item. A new page
102
+ moves the focus to its title, except on the first visit.