@masmarino/gabarit 1.1.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/ACCESSIBILITY.md CHANGED
@@ -15,20 +15,25 @@ AAA level.
15
15
  is therefore not "RGAA-compliant" and cannot be: it's your application
16
16
  that is, or isn't. What Gabarit guarantees is that **the 30 criteria that
17
17
  depend on its components are met**, verified by `axe-core` in the unit
18
- tests, by a contrast test on the tokens, and by thirteen manual audit
18
+ tests, by a contrast test on the tokens, and by 69 manual audit
19
19
  checklists (`AUDIT.md`) — one alone covers seven chart-base components,
20
- another covers both tab components, and five components have none. The
21
- first two means run locally via `npm test`, not in continuous
22
- integration: this repository has no CI. The checklists are handwritten
23
- and reviewed by a human.
20
+ another covers both tab components, and eight components have none. The
21
+ first two means run with `npm test`, locally and in continuous
22
+ integration (`.github/workflows/ci.yml` runs lint, tests, the library
23
+ build and the Storybook build on every push and pull request to `main`,
24
+ plus `npm audit --audit-level=high`; the release workflow re-runs lint,
25
+ tests and build before publishing). The checklists are handwritten and
26
+ reviewed by a human.
24
27
 
25
28
  ## How it's actually verified, and where that stops
26
29
 
27
30
  **This describes what actually runs, not what would be desirable.**
28
- This repository has no continuous integration, and no `axe-core` pass
29
- runs on Storybook stories. What does exist:
31
+ CI runs the unit tests, but no `axe-core` pass runs on Storybook
32
+ stories: for 1.2.0 such a pass (colour contrast included, light, dark and
33
+ 375px) was run once by hand over the stories, and nothing repeats it
34
+ automatically. What does exist:
30
35
 
31
- - **`axe-core`, run in the unit tests (`npm test`, local).** Each
36
+ - **`axe-core`, run in the unit tests (`npm test`, locally and in CI).** Each
32
37
  component has its own `expectNoA11yViolations` test. It covers only
33
38
  around 30% of RGAA criteria: necessary, and very insufficient on its
34
39
  own.
@@ -71,16 +76,21 @@ runs on Storybook stories. What does exist:
71
76
  it will actually be painted. It's the only check in this repository
72
77
  that measures a pair under rendering conditions, rather than two
73
78
  tokens side by side.
74
- - **Thirteen manual audit checklists** (`AUDIT.md` in the folder of the
75
- components that have one — not all of them). One (`chart-frame/AUDIT.md`)
79
+ - **69 manual audit checklists** (`AUDIT.md` in the folder of the
80
+ components that have one — not all of them; 86 component folders in
81
+ all). One (`chart-frame/AUDIT.md`)
76
82
  alone covers seven chart-base components (`chart-frame`, `chart-axis`,
77
83
  `chart-tooltip`, `chart-legend`, `chart-empty`, `chart-table`,
78
84
  `chart-context`), another (`tabs/AUDIT.md`) covers both the `Tabs` and
79
85
  `Tab` components. Each checklist records what automation doesn't see —
80
86
  actual keyboard use, screen reader, measurements in Storybook, reading
81
- the `@storybook/addon-a11y` panel. Conversely, five components
82
- (`bar-chart`, `funnel-chart`, `icon`, `sparkline`, `timeline-chart`)
83
- have none.
87
+ the `@storybook/addon-a11y` panel. Conversely, eight components
88
+ (`app-shell`, `bar-chart`, `breadcrumb`, `confirm-danger-modal`,
89
+ `funnel-chart`, `icon`, `sparkline`, `timeline-chart`) have none.
90
+ Together the checklists record a verdict on 39 distinct RGAA criteria
91
+ (counted from their criterion column; some rows also cover WCAG 2.2
92
+ criteria 2.4.11 and 2.5.8) — more than the 30 below, see the note that
93
+ follows that table.
84
94
 
85
95
  Two measured limits of `axe-core` in this tooling, worth knowing before
86
96
  reproducing this approach:
@@ -126,11 +136,21 @@ rule.
126
136
  | 12 Navigation | 12.8, 12.9, 12.11 |
127
137
  | 13 Consultation | 13.8 |
128
138
 
129
- That's 30 out of the 106 criteria in RGAA 4.1.2. Two topics that might
130
- look like they belong here don't: **Topic 6 Links (6.1, 6.2)** isn't
131
- listed because no `<a>` element exists anywhere in the library — a
132
- library that never produces a link cannot guarantee a criterion about
133
- links. And **11.2, 11.10, and 11.13** sit under "What remains your
139
+ That's 30 out of the 106 criteria in RGAA 4.1.2. Of these 30, 26 appear
140
+ as rows in the `AUDIT.md` files; 10.5, 10.9, 10.10 and 10.14 do not.
141
+ Conversely, the checklists also record verdicts on 13 criteria that are
142
+ not in the table: 1.1, 1.2, 6.1, 6.2, 8.9, 9.1, 9.2, 10.4, 11.2, 11.5,
143
+ 11.10, 11.13 and 12.6. Those are observations on the components, each for
144
+ its documented usage, not a guarantee, because the text or the structure
145
+ that decides them is yours. Two topics that might look like they belong
146
+ in the table are worth explaining: **Topic 6 Links (6.1, 6.2)** isn't
147
+ listed because the wording and destination of a link are the
148
+ application's. Since 1.2.0 the library styles the consumer's own `<a>` elements
149
+ (`a[gbtButton]`, `a[gbtNavTab]`, `a[gbtMenuItem]`, `a[gbtCardLink]`,
150
+ `a[gbtStatTileLink]`) and renders a few links itself (the card's
151
+ link mode, the brand link of `gbt-app-shell`, the link of
152
+ `gbt-stat-tile`), but none of them writes the link text for you. And
153
+ **11.2, 11.10, and 11.13** sit under "What remains your
134
154
  responsibility" below rather than here, since the components only
135
155
  provide the mechanism (`label`/field association, `role="alert"`,
136
156
  `aria-describedby`, an `autocomplete` you can override) — never the
@@ -144,9 +164,9 @@ The other 76 criteria. The main ones:
144
164
  | Topic | What you need to do |
145
165
  | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
146
166
  | 1 Images | Text alternatives for your images. `gbt-icon` is unconditionally decorative — see "Points to watch" below — a meaningful icon must be accompanied by text on your side. |
147
- | 6 Links | Explicit (6.1) and relevant (6.2) wording for every link in your application. No Gabarit component renders an `<a>`: nothing to delegate. |
167
+ | 6 Links | Explicit (6.1) and relevant (6.2) wording for every link in your application. The link components style or render an `<a>`, the text and the destination stay yours: an icon-only link needs an `aria-label`. |
148
168
  | 8 Mandatory elements | Doctype, `lang`, page `<title>`, absence of validity errors. |
149
- | 9 Structure | Heading hierarchy of the page. `gbt-card` lets you choose its level via `headingLevel` — it's up to you to set it correctly. |
169
+ | 9 Structure | Heading hierarchy of the page. `gbt-card`, `gbt-page-header` and `gbt-empty-state` let you choose their level via `headingLevel` — it's up to you to set it correctly. |
150
170
  | 10 Presentation | 200% zoom, page-wide reflow. The components hold up their end; the layout is yours. |
151
171
  | 11 Forms | Field grouping (`fieldset` / `legend`), **relevance** of your labels (11.2 — the component associates `label`/field, the text is yours), **content** of your error messages (11.10 — the component announces them via `role="alert"`, you write the text in `errorMessage`), and a correct `autocomplete` value (11.13 — `gbt-input` sets it to `'off'` by default, to override field by field). |
152
172
  | 12 Navigation | Two navigation systems, sitemap, skip link, ARIA regions. The `.skip-link` utility is provided (`tokens/_utilities.scss`), using it is up to you. |
@@ -197,8 +217,12 @@ The other 76 criteria. The main ones:
197
217
  values worth reading if you inspect the attribute rather than letting
198
218
  assistive technology announce it.
199
219
  - **Default strings in English, to override in a non-English
200
- application** — these components carry visible or announced labels
201
- with no localized default:
220
+ application** — the components added in 1.2.0 follow the same rule
221
+ (every visible or announced string is an input, for instance
222
+ `closeLabel` on `gbt-search-bar` in compact mode, the labels of
223
+ `gbt-copy-button` and `gbt-secret-reveal`; see each README's inputs
224
+ table), and the pipes and formatters take a locale. These components
225
+ carry visible or announced labels with no localized default:
202
226
  - `gbt-button`: `loadingLabel` (`'Loading'`).
203
227
  - `gbt-input`: `showPasswordLabel` (`'Show password'`),
204
228
  `hidePasswordLabel` (`'Hide password'`).
@@ -216,6 +240,15 @@ The other 76 criteria. The main ones:
216
240
  `clearLabel` (`'Clear search'`), `resultsAnnouncement` (function,
217
241
  announces the result count — criterion 7.5), `navigateHint`
218
242
  (`'Navigate'`), `selectHint` (`'Select'`), `closeHint` (`'Close'`).
243
+ - **Known gaps in 1.2.0.** Not fixed, stated here so you can decide: the
244
+ flyout label of the collapsed rail of `gbt-app-shell` cannot be
245
+ dismissed with `Escape` (WCAG 1.4.13, "dismissible"); `gbt-toaster`
246
+ expires every toast after 5 s, errors included, and has no pause on
247
+ hover and no `live` input (warnings are announced assertively — see its
248
+ README); and controls that already existed keep their sizes: the close
249
+ buttons of the toaster, drawer and modal are 24px hit areas on a fine
250
+ pointer, and only the controls added or reworked in 1.2.0 reach 44px on
251
+ a coarse pointer.
219
252
  - **Token overrides** — if you redefine the semantic colors, you take on
220
253
  criteria 3.2 and 3.3 yourself. Gabarit's contrast test only checks its
221
254
  own values.
package/README.md CHANGED
@@ -20,49 +20,268 @@ outputs — the link is on its name.
20
20
 
21
21
  ### Atoms
22
22
 
23
- | Component | Selector | Role |
24
- | -------------------------------------------------------------------------- | --------------- | ------------------------------------- |
25
- | [Button](projects/gabarit/src/lib/components/atoms/button/README.md) | `gbt-button` | Action button. |
26
- | [Checkbox](projects/gabarit/src/lib/components/atoms/checkbox/README.md) | `gbt-checkbox` | Checkbox, integrated with forms. |
27
- | [GaugeBar](projects/gabarit/src/lib/components/atoms/gauge-bar/README.md) | `gbt-gauge-bar` | Progress gauge with alert thresholds. |
28
- | [Icon](projects/gabarit/src/lib/components/atoms/icon/README.md) | `gbt-icon` | Registered SVG icon. |
29
- | [Input](projects/gabarit/src/lib/components/atoms/input/README.md) | `gbt-input` | Text or password field. |
30
- | [Sparkline](projects/gabarit/src/lib/components/atoms/sparkline/README.md) | `gbt-sparkline` | Fixed-size trend mini-chart. |
23
+ | Component | Selector | Role |
24
+ | --------------------------------------------------------------------------------------- | ---------------------- | -------------------------------------------------------------------- |
25
+ | [Avatar](projects/gabarit/src/lib/components/atoms/avatar/README.md) | `gbt-avatar` | User picture with an initials fallback. |
26
+ | [Badge](projects/gabarit/src/lib/components/atoms/badge/README.md) | `gbt-badge` | Status/category label pill. |
27
+ | [Button](projects/gabarit/src/lib/components/atoms/button/README.md) | `gbt-button` | Action button. |
28
+ | [ButtonLink](projects/gabarit/src/lib/components/atoms/button-link/README.md) | `a[gbtButton]` | Anchor styled as a button (router-agnostic). |
29
+ | [Checkbox](projects/gabarit/src/lib/components/atoms/checkbox/README.md) | `gbt-checkbox` | Checkbox, integrated with forms. |
30
+ | [CodeChip](projects/gabarit/src/lib/components/atoms/code-chip/README.md) | `gbt-code-chip` | Monospace chip for a SHA, tag or branch; truncates, optional copy. |
31
+ | [CopyButton](projects/gabarit/src/lib/components/atoms/copy-button/README.md) | `gbt-copy-button` | Copies a value to the clipboard, with a live-region confirmation. |
32
+ | [Counter](projects/gabarit/src/lib/components/atoms/counter/README.md) | `gbt-counter` | Small number pill: cap (`99+`), neutral / primary. |
33
+ | [Divider](projects/gabarit/src/lib/components/atoms/divider/README.md) | `gbt-divider` | Separating line with an optional centred label. |
34
+ | [GaugeBar](projects/gabarit/src/lib/components/atoms/gauge-bar/README.md) | `gbt-gauge-bar` | Progress gauge with alert thresholds. |
35
+ | [Icon](projects/gabarit/src/lib/components/atoms/icon/README.md) | `gbt-icon` | Registered SVG icon, with generic built-in glyphs (`Gallery` story). |
36
+ | [IconMarker](projects/gabarit/src/lib/components/atoms/icon-marker/README.md) | `gbt-icon-marker` | Icon on a soft tinted disc or tile, six tones. |
37
+ | [Input](projects/gabarit/src/lib/components/atoms/input/README.md) | `gbt-input` | Text or password field. |
38
+ | [JobStatus](projects/gabarit/src/lib/components/atoms/job-status/README.md) | `gbt-job-status` | CI job status glyph (extracted from the job graph). |
39
+ | [NotificationDot](projects/gabarit/src/lib/components/atoms/notification-dot/README.md) | `gbt-notification-dot` | Dot or count overlaid on the corner of what it wraps. |
40
+ | [SaveStatus](projects/gabarit/src/lib/components/atoms/save-status/README.md) | `gbt-save-status` | Saving / saved / not saved status, live region. |
41
+ | [Skeleton](projects/gabarit/src/lib/components/atoms/skeleton/README.md) | `gbt-skeleton` | Loading placeholder: line, circle or rectangle. |
42
+ | [Slider](projects/gabarit/src/lib/components/atoms/slider/README.md) | `gbt-slider` | Native range slider, integrated with forms. |
43
+ | [Sparkline](projects/gabarit/src/lib/components/atoms/sparkline/README.md) | `gbt-sparkline` | Fixed-size trend mini-chart. |
44
+ | [Spinner](projects/gabarit/src/lib/components/atoms/spinner/README.md) | `gbt-spinner` | Standalone loading indicator. |
45
+ | [Switch](projects/gabarit/src/lib/components/atoms/switch/README.md) | `gbt-switch` | On/off toggle. |
46
+ | [Tag](projects/gabarit/src/lib/components/atoms/tag/README.md) | `gbt-tag` | Custom-colored, optionally removable label. |
47
+ | [Textarea](projects/gabarit/src/lib/components/atoms/textarea/README.md) | `gbt-textarea` | Multiline text field. |
31
48
 
32
49
  ### Molecules
33
50
 
34
- | Component | Selector | Role |
35
- | --------------------------------------------------------------------------------------- | ---------------------- | ------------------------------------------- |
36
- | [Breadcrumb](projects/gabarit/src/lib/components/molecules/breadcrumb/README.md) | `gbt-breadcrumb` | Trail of ancestor links plus current item. |
37
- | [Card](projects/gabarit/src/lib/components/molecules/card/README.md) | `gbt-card` | Titled container. |
38
- | [DimensionCard](projects/gabarit/src/lib/components/molecules/dimension-card/README.md) | `gbt-dimension-card` | Dimension table with an accented hover row. |
39
- | [FunnelChart](projects/gabarit/src/lib/components/molecules/funnel-chart/README.md) | `gbt-funnel-chart` | Step-by-step conversion funnel. |
40
- | [Menu](projects/gabarit/src/lib/components/molecules/menu/README.md) | `gbt-menu` | Generic dropdown menu. |
41
- | [Select](projects/gabarit/src/lib/components/molecules/select/README.md) | `gbt-select` | Dropdown list, single or multiple. |
42
- | [Table](projects/gabarit/src/lib/components/molecules/table/README.md) | `gbt-table` | Data table. |
43
- | [Tabs](projects/gabarit/src/lib/components/molecules/tabs/README.md) | `gbt-tabs` / `gbt-tab` | Tab navigation. |
51
+ | Component | Selector | Role |
52
+ | --------------------------------------------------------------------------------------------- | ---------------------------------------- | ---------------------------------------------------------------------- |
53
+ | [Accordion](projects/gabarit/src/lib/components/molecules/accordion/README.md) | `gbt-accordion` / `gbt-accordion-item` | Collapsible sections. |
54
+ | [Alert](projects/gabarit/src/lib/components/molecules/alert/README.md) | `gbt-alert` | Persistent inline banner message. |
55
+ | [Autocomplete](projects/gabarit/src/lib/components/molecules/autocomplete/README.md) | `gbt-autocomplete` | Text field with async suggestions. |
56
+ | [AvatarGroup](projects/gabarit/src/lib/components/molecules/avatar-group/README.md) | `gbt-avatar-group` | Overlapping avatars with a +N overflow menu. |
57
+ | [Breadcrumb](projects/gabarit/src/lib/components/molecules/breadcrumb/README.md) | `gbt-breadcrumb` | Ancestor trail plus current segment. |
58
+ | [Card](projects/gabarit/src/lib/components/molecules/card/README.md) | `gbt-card` | Titled container. |
59
+ | [CheckboxGroup](projects/gabarit/src/lib/components/molecules/checkbox-group/README.md) | `gbt-checkbox-group` | Group of checkboxes (fieldset + legend), value is an array. |
60
+ | [CopyField](projects/gabarit/src/lib/components/molecules/copy-field/README.md) | `gbt-copy-field` | Read-only monospace value with a copy button. |
61
+ | [DatePicker](projects/gabarit/src/lib/components/molecules/date-picker/README.md) | `gbt-date-picker` | Single date, calendar dropdown, integrated with forms. |
62
+ | [DateRangePicker](projects/gabarit/src/lib/components/molecules/date-range-picker/README.md) | `gbt-date-range-picker` | Start and end dates, calendar dropdown. |
63
+ | [DescriptionList](projects/gabarit/src/lib/components/molecules/description-list/README.md) | `gbt-description-list` | Label/value pairs on a `dl`, `valueAlign`, opt-in stacking. |
64
+ | [DimensionCard](projects/gabarit/src/lib/components/molecules/dimension-card/README.md) | `gbt-dimension-card` | Dimension table with an accented hover row. |
65
+ | [Disclosure](projects/gabarit/src/lib/components/molecules/disclosure/README.md) | `gbt-disclosure` | Single disclosure: toggle button and panel, `open` model. |
66
+ | [EmptyState](projects/gabarit/src/lib/components/molecules/empty-state/README.md) | `gbt-empty-state` | Placeholder for an empty list/grid. |
67
+ | [FileUpload](projects/gabarit/src/lib/components/molecules/file-upload/README.md) | `gbt-file-upload` | Drag-and-drop or click file selection, removable list. |
68
+ | [FunnelChart](projects/gabarit/src/lib/components/molecules/funnel-chart/README.md) | `gbt-funnel-chart` | Step-by-step conversion funnel. |
69
+ | [JobGraph](projects/gabarit/src/lib/components/molecules/job-graph/README.md) | `gbt-job-graph` | Pipeline jobs by stage, linked by their dependencies. |
70
+ | [ListCard](projects/gabarit/src/lib/components/molecules/list-card/README.md) | `gbt-list-card` | Card for a list: header band, loading / failed / empty / ready states. |
71
+ | [ListRow](projects/gabarit/src/lib/components/molecules/list-row/README.md) | `gbt-list-row` | List item: leading status, title, meta, trailing actions. |
72
+ | [ListToolbar](projects/gabarit/src/lib/components/molecules/list-toolbar/README.md) | `gbt-list-toolbar` | Search + sort controls for a list. |
73
+ | [Menu](projects/gabarit/src/lib/components/molecules/menu/README.md) | `gbt-menu` | Generic dropdown menu. |
74
+ | [MenuItem](projects/gabarit/src/lib/components/molecules/menu-item/README.md) | `button[gbtMenuItem]` / `a[gbtMenuItem]` | Menu item: icon, danger variant, disabled, link form. |
75
+ | [NavTabs](projects/gabarit/src/lib/components/molecules/nav-tabs/README.md) | `gbt-nav-tabs` / `a[gbtNavTab]` | Router-agnostic nav links: icon, badge, orientation, fades. |
76
+ | [PageHeader](projects/gabarit/src/lib/components/molecules/page-header/README.md) | `gbt-page-header` | Page title block: heading, badges, meta, actions. |
77
+ | [Pagination](projects/gabarit/src/lib/components/molecules/pagination/README.md) | `gbt-pagination` | Page navigation for a list/table. |
78
+ | [Panel](projects/gabarit/src/lib/components/molecules/panel/README.md) | `gbt-panel` | Flat side-panel section: heading, actions, content. |
79
+ | [Popover](projects/gabarit/src/lib/components/molecules/popover/README.md) | `gbt-popover` | Floating panel opened by a click on its trigger. |
80
+ | [RadioGroup](projects/gabarit/src/lib/components/molecules/radio-group/README.md) | `gbt-radio-group` | Radio button group, integrated with forms. |
81
+ | [SecretReveal](projects/gabarit/src/lib/components/molecules/secret-reveal/README.md) | `gbt-secret-reveal` | Masked secret: show / hide and copy. |
82
+ | [SegmentedControl](projects/gabarit/src/lib/components/molecules/segmented-control/README.md) | `gbt-segmented-control` | Exclusive-choice button group (`tinted`, `fullWidth`, `wrap`). |
83
+ | [Select](projects/gabarit/src/lib/components/molecules/select/README.md) | `gbt-select` | Dropdown list, single or multiple. |
84
+ | [SkeletonList](projects/gabarit/src/lib/components/molecules/skeleton-list/README.md) | `gbt-skeleton-list` | Placeholder rows for a loading list, with a polite status. |
85
+ | [StatGrid](projects/gabarit/src/lib/components/molecules/stat-grid/README.md) | `gbt-stat-grid` | Responsive grid of stat tiles (container query), loading state. |
86
+ | [StatTile](projects/gabarit/src/lib/components/molecules/stat-tile/README.md) | `gbt-stat-tile` | Figure with label, icon, hint, trend and optional link. |
87
+ | [Stepper](projects/gabarit/src/lib/components/molecules/stepper/README.md) | `gbt-stepper` | Informational progress through numbered steps. |
88
+ | [Table](projects/gabarit/src/lib/components/molecules/table/README.md) | `gbt-table` | Data table. |
89
+ | [Tabs](projects/gabarit/src/lib/components/molecules/tabs/README.md) | `gbt-tabs` / `gbt-tab` | Tab navigation. |
90
+ | [TagInput](projects/gabarit/src/lib/components/molecules/tag-input/README.md) | `gbt-tag-input` | Free-typed values shown as removable tags. |
91
+ | [Tooltip](projects/gabarit/src/lib/components/molecules/tooltip/README.md) | `gbt-tooltip` | Hover/focus info bubble for any content. |
92
+ | [Tree](projects/gabarit/src/lib/components/molecules/tree/README.md) | `gbt-tree` | Expandable hierarchical list (WAI-ARIA tree view). |
93
+ | [UserChip](projects/gabarit/src/lib/components/molecules/user-chip/README.md) | `gbt-user-chip` | Avatar + name on one line. |
44
94
 
45
95
  ### Organisms
46
96
 
47
- | Component | Selector | Role |
48
- | --------------------------------------------------------------------------------------- | -------------------- | ------------------------------------------ |
49
- | [BarChart](projects/gabarit/src/lib/components/organisms/bar-chart/README.md) | `gbt-bar-chart` | Bar chart on the dataviz base. |
50
- | [ChartAxis](projects/gabarit/src/lib/components/organisms/chart-axis/README.md) | `g[gbtChartAxis]` | Axis ticks — base building block. |
51
- | [ChartEmpty](projects/gabarit/src/lib/components/organisms/chart-empty/README.md) | `gbt-chart-empty` | Empty state — base building block. |
52
- | [ChartFrame](projects/gabarit/src/lib/components/organisms/chart-frame/README.md) | `gbt-chart-frame` | Low-level base, for a custom chart. |
53
- | [ChartLegend](projects/gabarit/src/lib/components/organisms/chart-legend/README.md) | `gbt-chart-legend` | Multi-series legend — base building block. |
54
- | [ChartTable](projects/gabarit/src/lib/components/organisms/chart-table/README.md) | `gbt-chart-table` | Non-visual table — base building block. |
55
- | [ChartTooltip](projects/gabarit/src/lib/components/organisms/chart-tooltip/README.md) | `gbt-chart-tooltip` | Tooltip — base building block. |
56
- | [LineChart](projects/gabarit/src/lib/components/organisms/line-chart/README.md) | `gbt-line-chart` | Line(s) on the dataviz base. |
57
- | [Modal](projects/gabarit/src/lib/components/organisms/modal/README.md) | `gbt-modal` | Modal dialog box. |
58
- | [SearchBar](projects/gabarit/src/lib/components/organisms/search-bar/README.md) | `gbt-search-bar` | Search with grouped results. |
59
- | [TimelineChart](projects/gabarit/src/lib/components/organisms/timeline-chart/README.md) | `gbt-timeline-chart` | Timeline on the dataviz base. |
97
+ | Component | Selector | Role |
98
+ | -------------------------------------------------------------------------------------------------- | -------------------------- | ------------------------------------------------------------------------ |
99
+ | [BarChart](projects/gabarit/src/lib/components/organisms/bar-chart/README.md) | `gbt-bar-chart` | Bar chart on the dataviz base. |
100
+ | [ChartAxis](projects/gabarit/src/lib/components/organisms/chart-axis/README.md) | `g[gbtChartAxis]` | Axis ticks — base building block. |
101
+ | [ChartEmpty](projects/gabarit/src/lib/components/organisms/chart-empty/README.md) | `gbt-chart-empty` | Empty state — base building block. |
102
+ | [ChartFrame](projects/gabarit/src/lib/components/organisms/chart-frame/README.md) | `gbt-chart-frame` | Low-level base, for a custom chart. |
103
+ | [ChartLegend](projects/gabarit/src/lib/components/organisms/chart-legend/README.md) | `gbt-chart-legend` | Multi-series legend — base building block. |
104
+ | [ChartTable](projects/gabarit/src/lib/components/organisms/chart-table/README.md) | `gbt-chart-table` | Non-visual table — base building block. |
105
+ | [ChartTooltip](projects/gabarit/src/lib/components/organisms/chart-tooltip/README.md) | `gbt-chart-tooltip` | Tooltip — base building block. |
106
+ | [ConfirmDangerModal](projects/gabarit/src/lib/components/organisms/confirm-danger-modal/README.md) | `gbt-confirm-danger-modal` | Type-to-confirm destructive-action dialog. |
107
+ | [Drawer](projects/gabarit/src/lib/components/organisms/drawer/README.md) | `gbt-drawer` | Side panel from a screen edge, focus trapped. |
108
+ | [GbtToastService](projects/gabarit/src/lib/components/organisms/toaster/README.md) | `GbtToastService` | Signal store of toasts; `gbt-toaster` shows it when `toasts` is omitted. |
109
+ | [LineChart](projects/gabarit/src/lib/components/organisms/line-chart/README.md) | `gbt-line-chart` | Line(s) on the dataviz base. |
110
+ | [Modal](projects/gabarit/src/lib/components/organisms/modal/README.md) | `gbt-modal` | Modal dialog box. |
111
+ | [PieChart](projects/gabarit/src/lib/components/organisms/pie-chart/README.md) | `gbt-pie-chart` | Pie or donut chart. |
112
+ | [SearchBar](projects/gabarit/src/lib/components/organisms/search-bar/README.md) | `gbt-search-bar` | Search with grouped results. |
113
+ | [TimelineChart](projects/gabarit/src/lib/components/organisms/timeline-chart/README.md) | `gbt-timeline-chart` | Timeline on the dataviz base. |
114
+ | [Toaster](projects/gabarit/src/lib/components/organisms/toaster/README.md) | `gbt-toaster` | Stack of temporary notifications. |
60
115
 
61
116
  ### Templates
62
117
 
63
- | Component | Selector | Role |
64
- | ----------------------------------------------------------------------------- | --------------- | -------------------------------------- |
65
- | [AppShell](projects/gabarit/src/lib/components/templates/app-shell/README.md) | `gbt-app-shell` | Page shell: side nav, header, content. |
118
+ | Component | Selector | Role |
119
+ | ----------------------------------------------------------------------------------------------- | ------------------------- | --------------------------------------------------------- |
120
+ | [AppShell](projects/gabarit/src/lib/components/templates/app-shell/README.md) | `gbt-app-shell` | Page shell: side nav, header, content. |
121
+ | [AppShellNavGroup](projects/gabarit/src/lib/components/templates/app-shell-nav-group/README.md) | `gbt-app-shell-nav-group` | Collapsible nav group, works in the collapsed rail. |
122
+ | [PageLayout](projects/gabarit/src/lib/components/templates/page-layout/README.md) | `gbt-page-layout` | Page grid: main, aside and nav columns (container query). |
123
+
124
+ ### Directives and helper components to import
125
+
126
+ These are not standalone widgets: they decorate a projected element of a
127
+ component above. **They must be imported next to the component** (`imports:
128
+ [Card, CardHeader]`); without the import the element is still rendered
129
+ but plain, without the layout the directive provides.
130
+
131
+ | Class | Selector | Used with |
132
+ | -------------- | -------------------- | -------------------------------------------------------------------- |
133
+ | `CardHeader` | `[card-header]` | `gbt-card`: your own header above the box, actions on the right. |
134
+ | `CardLink` | `a[gbtCardLink]` | `gbt-card`: link mode, the card's box is clickable (stretched link). |
135
+ | `MenuTrigger` | `[gbtMenuTrigger]` | `gbt-menu`: a custom trigger element instead of the default button. |
136
+ | `StatTileLink` | `a[gbtStatTileLink]` | `gbt-stat-tile`: the router-agnostic link, its text is the label. |
137
+
138
+ ## Primitives and pipes
139
+
140
+ Pure functions exported from `@masmarino/gabarit`, usable in any TypeScript
141
+ file (no component, no injection context, no browser API beyond `Intl`) and
142
+ tree-shakable. Every formatter takes the **locale first**, because Gabarit
143
+ has no i18n of its own; the pipes default it to Angular's `LOCALE_ID`.
144
+ A missing value (`null`/`undefined`) prints `''`; a value that is not a
145
+ finite number or a valid date prints `—`.
146
+
147
+ | Function | Role |
148
+ | ------------------------------------------------------------- | -------------------------------------------------------------------- |
149
+ | `formatBytes(bytes, locale, { base, decimals, binaryUnits })` | A file size: `1.5 KiB`, `1,5 Kio`, `1,5 ko` (base 1000). |
150
+ | `formatRelativeTime(date, locale, now?, options?)` | "5 minutes ago" through `Intl.RelativeTimeFormat`. |
151
+ | `formatDateTime(date, locale, options?)` | A date and time through `Intl.DateTimeFormat`. |
152
+ | `formatDuration(ms, locale, { days })` | `1 min 30 s`, `2 h 5 min`; `{ days: true }` adds `2 d 3 h`. |
153
+ | `formatNumber`, `formatCompact`, `formatPercent` | Locale-aware numbers (`1,234`, `1.2K`, `12.3%`). |
154
+ | `computeInitials(name)` | Up to two capitals for an avatar: `Ada Lovelace` gives `AL`. |
155
+ | `createListToolbarState(config)` | Search and sort signals for a `gbt-list-toolbar`. |
156
+ | `copyToClipboard`, `ClipboardFeedback`, `selectContents` | The clipboard helpers behind `gbt-copy-button` and `gbt-copy-field`. |
157
+
158
+ Numbers and units in `formatBytes` are joined by a no-break space (`U+00A0`).
159
+ The examples below write it as a plain space.
160
+
161
+ | Call | `en` | `fr` |
162
+ | --------------------------------------------------- | ----------------------- | ---------------------- |
163
+ | `formatBytes(1536, l)` | `1.5 KiB` | `1,5 Kio` |
164
+ | `formatBytes(1536, l, { binaryUnits: 'legacy' })` | `1.5 KB` | `1,5 Ko` |
165
+ | `formatBytes(1500, l, { base: 1000 })` | `1.5 kB` | `1,5 ko` |
166
+ | `formatBytes(512, l)`, `formatBytes(NaN, l)` | `512 B`, `—` | `512 o`, `—` |
167
+ | `formatRelativeTime(d, l, now)`, 5 minutes earlier | `5 minutes ago` | `il y a 5 minutes` |
168
+ | the same, 26 hours earlier | `yesterday` | `hier` |
169
+ | the same, 3 days earlier | `3 days ago` | `il y a 3 jours` |
170
+ | the same, 2 hours later | `in 2 hours` | `dans 2 heures` |
171
+ | the same, less than 10 s away | `now` | `maintenant` |
172
+ | `formatRelativeTime(d, l, now, { style: 'short' })` | `5 min. ago` | `il y a 5 min` |
173
+ | `formatDateTime(d, l)` | `Sep 26, 2026, 2:05 PM` | `26 sept. 2026, 14:05` |
174
+ | `formatDateTime(d, l, { dateStyle: 'long' })` | `September 26, 2026` | `26 septembre 2026` |
175
+ | `formatDuration(90_000_000, l, { days: true })` | `1 d 1 h` | `1 j 1 h` |
176
+
177
+ Details worth knowing:
178
+
179
+ - **`formatBytes`** counts in 1024 by default (as a file manager does) and
180
+ labels with the unambiguous binary units; `base: 1000` counts in thousands
181
+ and labels with the SI units of the locale (`kB`, `ko`). Bytes are never
182
+ fractional, the ranks above show at most `decimals` (default 1) digits and
183
+ roll over (`1023.96 KiB` is `1 MiB`), a negative size keeps its sign. The
184
+ binary label is the locale's SI label with an `i` (`Kio` in `fr`, `KiB` in
185
+ `en`, `de`, `ja`), except where the locale writes its byte in another script
186
+ (`ru`, `uk`, `ar`): those get the Latin `KiB`, `MiB`... so a label never mixes
187
+ two scripts. `binaryUnits: 'legacy'` prints `KB` / `Ko` instead.
188
+ - **`formatRelativeTime`** picks the unit from the gap, rounding toward zero:
189
+ under 10 s `now`, seconds under a minute, then minutes, hours, days (under
190
+ 7 days), weeks (under 30 days), months (30 days each, under a year), years.
191
+ Past and future are handled. `now` defaults to the current time; pass it for
192
+ a deterministic render. The dates are `Date`, ISO strings or epoch
193
+ milliseconds. Options:
194
+ - `style`: `'long'` (default, `5 minutes ago`), `'short'` (`5 min. ago`),
195
+ `'narrow'`.
196
+ - `numeric`: `'auto'` (default) says `yesterday` / `hier`; `'always'` says
197
+ `1 day ago` / `il y a 1 jour`.
198
+ - `maxUnit`: `'hour' | 'day' | 'week' | 'month'` caps the largest unit, so
199
+ `'day'` gives `il y a 12 jours` instead of `il y a 2 semaines` (and
200
+ `il y a 400 jours` instead of years). Without it weeks and months always
201
+ appear.
202
+ - `absoluteAfterDays: 30` switches to the numeric date (`26/08/2026`) from
203
+ that gap on; `timeZone` sets the zone of that date.
204
+ - **`formatDateTime`** uses the runtime's time zone unless `options.timeZone`
205
+ says otherwise. Options that select fields replace the default medium date
206
+ and short time, other options (a time zone) apply on top of it.
207
+ - **`formatDuration`** keeps its existing output unless `days: true`; with it
208
+ a duration of a day or more prints days and hours (minutes are dropped, as
209
+ with hours and seconds) using the locale's day letter (`d`, `j`).
210
+
211
+ ### Pipes
212
+
213
+ Standalone, **pure** pipes that call the primitives. The locale is
214
+ `options.locale`, else Angular's `LOCALE_ID` (`en-US` unless the application
215
+ provides another, for instance `{ provide: LOCALE_ID, useValue: 'fr' }`); the
216
+ document's `lang` is deliberately not read, so server and browser render
217
+ alike.
218
+
219
+ | Pipe | Example | Output (`fr`) |
220
+ | ----------------- | --------------------------------------------- | ---------------------- |
221
+ | `gbtRelativeTime` | `{{ event.at \| gbtRelativeTime }}` | `il y a 5 minutes` |
222
+ | `gbtBytes` | `{{ file.size \| gbtBytes: { base: 1000 } }}` | `1,5 ko` |
223
+ | `gbtDateTime` | `{{ event.at \| gbtDateTime }}` | `26 sept. 2026, 14:05` |
224
+
225
+ Relative time goes stale, and a pure pipe is not re-evaluated as time passes:
226
+ the text is right as of the last time the value or the options changed. That
227
+ is what most lists want (no timer per row). For a view that must stay live,
228
+ update one signal on a timer and pass it as `now`:
229
+
230
+ ```html
231
+ {{ event.at | gbtRelativeTime: { now: clock() } }}
232
+ ```
233
+
234
+ ### List toolbar state
235
+
236
+ `createListToolbarState(config)` holds the three signals behind a
237
+ `gbt-list-toolbar` (`search`, `sortValue`, `direction`) and the filter and sort
238
+ that go with them, so a list page does not re-declare them. It needs no
239
+ injection context.
240
+
241
+ ```ts
242
+ readonly toolbar = createListToolbarState({
243
+ sortOptions: [
244
+ { value: 'name', label: 'Name' },
245
+ { value: 'created', label: 'Created' },
246
+ ],
247
+ defaultSort: 'name', // defaults to the first option
248
+ })
249
+
250
+ readonly rows = this.toolbar.filtered(() => this.repos(), {
251
+ text: (repo) => [repo.name, repo.description], // searched, case-insensitive
252
+ sortBy: { name: (repo) => repo.name, created: (repo) => repo.createdAt },
253
+ })
254
+ ```
255
+
256
+ ```html
257
+ <gbt-list-toolbar
258
+ searchLabel="Search"
259
+ [sortOptions]="toolbar.sortOptions"
260
+ [searchValue]="toolbar.search()"
261
+ (searchValueChange)="toolbar.search.set($event)"
262
+ [sortValue]="toolbar.sortValue()"
263
+ (sortValueChange)="toolbar.sortValue.set($event)"
264
+ [sortDirection]="toolbar.direction()"
265
+ (sortDirectionChange)="toolbar.direction.set($event)"
266
+ />
267
+ ```
268
+
269
+ | Member | Role |
270
+ | ---------------------------------- | --------------------------------------------------------------------------------- |
271
+ | `search`, `sortValue`, `direction` | The writable signals. |
272
+ | `sortOptions` | The options given in the config, ready to bind. |
273
+ | `dirty()`, `reset()` | Whether anything differs from the start values, and back to them. |
274
+ | `apply(items, accessors)` | Filters then sorts a list now; call it inside a `computed` to follow the signals. |
275
+ | `filtered(source, accessors)` | A `computed` of `apply(source(), accessors)`; `source` is read lazily. |
276
+
277
+ The accessors are `text` (a string or an array of strings, matched as a
278
+ case-insensitive substring of the trimmed search) and `sortBy` (one accessor
279
+ per sort field, returning a string, number, boolean or `Date`; missing or
280
+ non-finite values sort last in both directions). Strings compare with `Intl.Collator` (case-insensitive, numeric: `runner
281
+ 2` before `runner 10`; pass `locale`). To take over, give `matches(item,
282
+ query)` and/or `compare(a, b, key)` instead. There is no debounce: the search
283
+ updates on every keystroke. With `sortBy`, `desc` sorts descending (equal items
284
+ keep their source order); with `compare`, `desc` reverses the ascending result.
66
285
 
67
286
  ## Styles
68
287
 
@@ -84,24 +303,26 @@ application should override to re-theme itself** — the raw palette
84
303
  (`--brand-*`, `--grey-*`, `--red-*`…) is an internal detail, never
85
304
  referenced outside `_semantic.scss` (enforced by `token-usage.spec.ts`).
86
305
 
87
- | Category | Token | Role |
88
- | ----------- | ------------------------------------------------------------------------ | --------------------------------------------------------------- |
89
- | Brand | `--primary` / `--primary-hover` | Action color (buttons, active links, focus) and its hover state |
90
- | Backgrounds | `--bg-principal` | Page and surface background (cards, panels) |
91
- | | `--bg-panel` | Background of persistent navigation areas (nav, header) |
92
- | | `--bg-hover` | Hover state of an interactive element on a neutral background |
93
- | Border | `--border-color` | All borders |
94
- | Text | `--text-primary` / `--text-secondary` / `--text-discret` | From most to least emphasized |
95
- | | `--text-on-primary` / `--text-on-error` / `--text-on-color` | Text set on a `--primary` fill, an error fill, or a solid color |
96
- | Success | `--color-success-base` / `-hover` / `-text` / `-bg` / `-bg-text` | Fill, hover, text, light background, text on that background |
97
- | Warning | `--color-warning-base` / `-hover` / `-text` / `-bg` / `-bg-text` | Same |
98
- | Error | `--color-error-base` / `-fill` / `-hover` / `-text` / `-bg` / `-bg-text` | Same (`-fill`: solid fill, e.g. an icon) |
99
- | Dataviz | `--chart-series-1-base`, `-2-base`, `-3-base` | The three chart series, in order |
100
- | | `--chart-grid` | Chart grid and axes |
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 |
101
322
 
102
323
  Other tokens live on `:root` without being colors — border radii
103
324
  (`--site-border-radius*`), shadows (`--site-shadow-*`), transition
104
- durations (`--site-transition-*`) — overridable the same way.
325
+ durations (`--site-transition-*`), the monospace stack `--gbt-font-mono` — overridable the same way.
105
326
 
106
327
  Overriding a token after the import:
107
328
 
@@ -136,8 +357,8 @@ component input, supplied by the application.
136
357
 
137
358
  [`ACCESSIBILITY.md`](./ACCESSIBILITY.md) documents what Gabarit
138
359
  guarantees with respect to RGAA (30 out of 106 criteria, checked on every
139
- push by CI — `axe-core` in the unit tests, a dedicated contrast test, and
140
- a manual audit per component), what remains the application's
360
+ push by CI — `axe-core` in the unit tests and a dedicated contrast test —
361
+ plus a manual audit checklist for 69 components), what remains the application's
141
362
  responsibility (76 criteria), and the points to watch when integrating
142
363
  each component. Read it before writing your application's accessibility
143
364
  statement — without it, that statement will be incomplete.
@@ -169,20 +390,34 @@ utilities in the same file do carry the `gbt-` prefix.
169
390
 
170
391
  ## Release
171
392
 
172
- The published version is the one in `projects/gabarit/package.json`. To
173
- cut a new release:
393
+ The version lives in **five files**, bumped together in one commit:
394
+ `package.json`, `package-lock.json` (the root package and its `projects/gabarit`
395
+ workspace entry), `projects/gabarit/package.json`,
396
+ `projects/gabarit/src/lib/version.ts` (`GABARIT_VERSION`, asserted equal to the
397
+ library's `package.json` by `version.spec.ts`) and the version string in
398
+ `projects/gabarit/src/lib/public-api.spec.ts`. To cut a release:
174
399
 
175
400
  ```bash
176
- npm version <patch|minor|major> --prefix projects/gabarit --no-git-tag-version
177
- git add projects/gabarit/package.json
178
- git commit -m "chore(release): vX.Y.Z"
401
+ # 1. bump the five files, then check the whole thing
402
+ npm run lint && npm test && npm run build && npm run build-storybook
403
+ git add package.json package-lock.json projects/gabarit/package.json \
404
+ projects/gabarit/src/lib/version.ts projects/gabarit/src/lib/public-api.spec.ts
405
+ git commit -m "bump(): version X.Y.Z"
406
+ # 2. publish: push main, then the tag
407
+ git push origin main
179
408
  git tag vX.Y.Z
180
- git push origin main --follow-tags
409
+ git push origin vX.Y.Z
181
410
  ```
182
411
 
183
- The tag triggers the `release.yml` workflow: full rebuild, verification
184
- that the tag matches the package version, then publishing to npm with
185
- provenance attestation.
412
+ Pushing the tag triggers the `release.yml` workflow (environment
413
+ `npm-release`): full lint, test and build, verification that the tag matches
414
+ the package version, then `npm publish --provenance --access public` from
415
+ `dist/gabarit`. There is no changelog file.
416
+
417
+ Before tagging, an unpublished build can be tried in an application with
418
+ `npm run build`, then `npm pack` in `dist/gabarit` and, in the application,
419
+ `npm install --no-save <path>/masmarino-gabarit-X.Y.Z.tgz` (revert with
420
+ `npm ci`).
186
421
 
187
422
  ## License
188
423