@masmarino/gabarit 2.0.0 → 2.1.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/README.md +131 -85
- package/accordion/README.md +56 -0
- package/alert/README.md +87 -0
- package/app-shell/README.md +133 -0
- package/auth/README.md +155 -0
- package/auth-activate/README.md +119 -0
- package/auth-login/README.md +113 -0
- package/auth-register/README.md +104 -0
- package/auth-reset-password/README.md +120 -0
- package/autocomplete/README.md +77 -0
- package/avatar/README.md +34 -0
- package/avatar-group/README.md +47 -0
- package/badge/README.md +177 -0
- package/bar-chart/README.md +44 -0
- package/breadcrumb/README.md +38 -0
- package/button/README.md +175 -0
- package/card/README.md +179 -0
- package/checkbox/README.md +50 -0
- package/checkbox-group/README.md +78 -0
- package/confirm-danger-modal/README.md +77 -0
- package/copy-button/README.md +90 -0
- package/copy-field/README.md +54 -0
- package/date-picker/README.md +84 -0
- package/description-list/README.md +63 -0
- package/dimension-card/README.md +39 -0
- package/disclosure/README.md +48 -0
- package/divider/README.md +31 -0
- package/drawer/README.md +49 -0
- package/empty-state/README.md +84 -0
- package/fesm2022/masmarino-gabarit-accordion.mjs +243 -0
- package/fesm2022/masmarino-gabarit-accordion.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-alert.mjs +208 -0
- package/fesm2022/masmarino-gabarit-alert.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-app-shell.mjs +378 -0
- package/fesm2022/masmarino-gabarit-app-shell.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-auth-activate.mjs +415 -0
- package/fesm2022/masmarino-gabarit-auth-activate.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-auth-login.mjs +467 -0
- package/fesm2022/masmarino-gabarit-auth-login.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-auth-register.mjs +457 -0
- package/fesm2022/masmarino-gabarit-auth-register.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-auth-reset-password.mjs +364 -0
- package/fesm2022/masmarino-gabarit-auth-reset-password.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-auth.mjs +844 -0
- package/fesm2022/masmarino-gabarit-auth.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-autocomplete.mjs +475 -0
- package/fesm2022/masmarino-gabarit-autocomplete.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-avatar-group.mjs +202 -0
- package/fesm2022/masmarino-gabarit-avatar-group.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-avatar.mjs +107 -0
- package/fesm2022/masmarino-gabarit-avatar.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-badge.mjs +449 -0
- package/fesm2022/masmarino-gabarit-badge.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-bar-chart.mjs +408 -0
- package/fesm2022/masmarino-gabarit-bar-chart.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-breadcrumb.mjs +59 -0
- package/fesm2022/masmarino-gabarit-breadcrumb.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-button.mjs +374 -0
- package/fesm2022/masmarino-gabarit-button.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-card.mjs +400 -0
- package/fesm2022/masmarino-gabarit-card.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-chart.mjs +1289 -0
- package/fesm2022/masmarino-gabarit-chart.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-checkbox-group.mjs +335 -0
- package/fesm2022/masmarino-gabarit-checkbox-group.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-checkbox.mjs +170 -0
- package/fesm2022/masmarino-gabarit-checkbox.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-confirm-danger-modal.mjs +387 -0
- package/fesm2022/masmarino-gabarit-confirm-danger-modal.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-copy-button.mjs +407 -0
- package/fesm2022/masmarino-gabarit-copy-button.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-copy-field.mjs +229 -0
- package/fesm2022/masmarino-gabarit-copy-field.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-date-picker.mjs +1242 -0
- package/fesm2022/masmarino-gabarit-date-picker.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-description-list.mjs +127 -0
- package/fesm2022/masmarino-gabarit-description-list.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-dimension-card.mjs +162 -0
- package/fesm2022/masmarino-gabarit-dimension-card.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-disclosure.mjs +154 -0
- package/fesm2022/masmarino-gabarit-disclosure.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-divider.mjs +90 -0
- package/fesm2022/masmarino-gabarit-divider.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-docs.mjs +16 -2
- package/fesm2022/masmarino-gabarit-docs.mjs.map +1 -1
- package/fesm2022/masmarino-gabarit-drawer.mjs +209 -0
- package/fesm2022/masmarino-gabarit-drawer.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-empty-state.mjs +226 -0
- package/fesm2022/masmarino-gabarit-empty-state.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-file-upload.mjs +335 -0
- package/fesm2022/masmarino-gabarit-file-upload.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-floating-panel.mjs +24 -0
- package/fesm2022/masmarino-gabarit-floating-panel.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-format.mjs +345 -0
- package/fesm2022/masmarino-gabarit-format.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-funnel-chart.mjs +247 -0
- package/fesm2022/masmarino-gabarit-funnel-chart.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-gauge-bar.mjs +180 -0
- package/fesm2022/masmarino-gabarit-gauge-bar.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-git-field.mjs +127 -0
- package/fesm2022/masmarino-gabarit-git-field.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-icon-marker.mjs +150 -0
- package/fesm2022/masmarino-gabarit-icon-marker.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-icon.mjs +308 -0
- package/fesm2022/masmarino-gabarit-icon.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-input.mjs +493 -0
- package/fesm2022/masmarino-gabarit-input.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-job-graph.mjs +216 -0
- package/fesm2022/masmarino-gabarit-job-graph.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-job-status.mjs +104 -0
- package/fesm2022/masmarino-gabarit-job-status.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-line-chart.mjs +574 -0
- package/fesm2022/masmarino-gabarit-line-chart.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-list-card.mjs +320 -0
- package/fesm2022/masmarino-gabarit-list-card.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-list-row.mjs +58 -0
- package/fesm2022/masmarino-gabarit-list-row.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-list-toolbar.mjs +369 -0
- package/fesm2022/masmarino-gabarit-list-toolbar.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-menu.mjs +464 -0
- package/fesm2022/masmarino-gabarit-menu.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-mfa-enrollment.mjs +897 -0
- package/fesm2022/masmarino-gabarit-mfa-enrollment.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-mfa-settings.mjs +629 -0
- package/fesm2022/masmarino-gabarit-mfa-settings.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-modal.mjs +260 -0
- package/fesm2022/masmarino-gabarit-modal.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-nav-tabs.mjs +332 -0
- package/fesm2022/masmarino-gabarit-nav-tabs.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-notification-dot.mjs +119 -0
- package/fesm2022/masmarino-gabarit-notification-dot.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-page-header.mjs +78 -0
- package/fesm2022/masmarino-gabarit-page-header.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-page-layout.mjs +157 -0
- package/fesm2022/masmarino-gabarit-page-layout.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-pagination.mjs +415 -0
- package/fesm2022/masmarino-gabarit-pagination.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-panel.mjs +80 -0
- package/fesm2022/masmarino-gabarit-panel.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-passkey-settings.mjs +606 -0
- package/fesm2022/masmarino-gabarit-passkey-settings.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-pie-chart.mjs +317 -0
- package/fesm2022/masmarino-gabarit-pie-chart.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-popover.mjs +142 -0
- package/fesm2022/masmarino-gabarit-popover.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-radio-group.mjs +202 -0
- package/fesm2022/masmarino-gabarit-radio-group.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-save-status.mjs +142 -0
- package/fesm2022/masmarino-gabarit-save-status.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-search-bar.mjs +576 -0
- package/fesm2022/masmarino-gabarit-search-bar.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-secret-reveal.mjs +348 -0
- package/fesm2022/masmarino-gabarit-secret-reveal.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-segmented-control.mjs +272 -0
- package/fesm2022/masmarino-gabarit-segmented-control.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-select.mjs +512 -0
- package/fesm2022/masmarino-gabarit-select.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-skeleton-list.mjs +189 -0
- package/fesm2022/masmarino-gabarit-skeleton-list.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-skeleton.mjs +109 -0
- package/fesm2022/masmarino-gabarit-skeleton.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-slider.mjs +239 -0
- package/fesm2022/masmarino-gabarit-slider.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-sparkline.mjs +276 -0
- package/fesm2022/masmarino-gabarit-sparkline.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-spinner.mjs +86 -0
- package/fesm2022/masmarino-gabarit-spinner.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-stat-grid.mjs +139 -0
- package/fesm2022/masmarino-gabarit-stat-grid.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-stat-tile.mjs +312 -0
- package/fesm2022/masmarino-gabarit-stat-tile.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-stepper.mjs +219 -0
- package/fesm2022/masmarino-gabarit-stepper.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-switch.mjs +151 -0
- package/fesm2022/masmarino-gabarit-switch.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-table.mjs +206 -0
- package/fesm2022/masmarino-gabarit-table.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-tabs.mjs +210 -0
- package/fesm2022/masmarino-gabarit-tabs.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-tag-input.mjs +288 -0
- package/fesm2022/masmarino-gabarit-tag-input.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-tag.mjs +155 -0
- package/fesm2022/masmarino-gabarit-tag.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-textarea.mjs +369 -0
- package/fesm2022/masmarino-gabarit-textarea.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-timeline-chart.mjs +521 -0
- package/fesm2022/masmarino-gabarit-timeline-chart.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-toaster.mjs +220 -0
- package/fesm2022/masmarino-gabarit-toaster.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-tooltip.mjs +185 -0
- package/fesm2022/masmarino-gabarit-tooltip.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-tree.mjs +258 -0
- package/fesm2022/masmarino-gabarit-tree.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit-user-chip.mjs +106 -0
- package/fesm2022/masmarino-gabarit-user-chip.mjs.map +1 -0
- package/fesm2022/masmarino-gabarit.mjs +83 -24785
- package/fesm2022/masmarino-gabarit.mjs.map +1 -1
- package/file-upload/README.md +47 -0
- package/funnel-chart/README.md +39 -0
- package/gauge-bar/README.md +35 -0
- package/git-field/README.md +34 -0
- package/icon/README.md +52 -0
- package/icon-marker/README.md +37 -0
- package/input/README.md +120 -0
- package/job-graph/README.md +76 -0
- package/job-status/README.md +34 -0
- package/line-chart/README.md +52 -0
- package/list-card/README.md +144 -0
- package/list-row/README.md +72 -0
- package/list-toolbar/README.md +52 -0
- package/menu/README.md +90 -0
- package/mfa-enrollment/README.md +189 -0
- package/mfa-settings/README.md +154 -0
- package/modal/README.md +72 -0
- package/nav-tabs/README.md +106 -0
- package/notification-dot/README.md +43 -0
- package/package.json +329 -1
- package/page-header/README.md +56 -0
- package/page-layout/README.md +76 -0
- package/pagination/README.md +73 -0
- package/panel/README.md +56 -0
- package/passkey-settings/README.md +120 -0
- package/pie-chart/README.md +85 -0
- package/popover/README.md +62 -0
- package/radio-group/README.md +47 -0
- package/save-status/README.md +44 -0
- package/search-bar/README.md +84 -0
- package/secret-reveal/README.md +60 -0
- package/segmented-control/README.md +79 -0
- package/select/README.md +61 -0
- package/skeleton/README.md +49 -0
- package/skeleton-list/README.md +40 -0
- package/slider/README.md +44 -0
- package/sparkline/README.md +37 -0
- package/spinner/README.md +41 -0
- package/stat-grid/README.md +47 -0
- package/stat-tile/README.md +75 -0
- package/stepper/README.md +67 -0
- package/switch/README.md +51 -0
- package/table/README.md +75 -0
- package/tabs/README.md +34 -0
- package/tag/README.md +34 -0
- package/tag-input/README.md +45 -0
- package/textarea/README.md +76 -0
- package/timeline-chart/README.md +49 -0
- package/toaster/README.md +92 -0
- package/tooltip/README.md +50 -0
- package/tree/README.md +79 -0
- package/types/masmarino-gabarit-accordion.d.ts +54 -0
- package/types/masmarino-gabarit-alert.d.ts +65 -0
- package/types/masmarino-gabarit-app-shell.d.ts +102 -0
- package/types/masmarino-gabarit-auth-activate.d.ts +70 -0
- package/types/masmarino-gabarit-auth-login.d.ts +63 -0
- package/types/masmarino-gabarit-auth-register.d.ts +66 -0
- package/types/masmarino-gabarit-auth-reset-password.d.ts +53 -0
- package/types/masmarino-gabarit-auth.d.ts +543 -0
- package/types/masmarino-gabarit-autocomplete.d.ts +140 -0
- package/types/masmarino-gabarit-avatar-group.d.ts +60 -0
- package/types/masmarino-gabarit-avatar.d.ts +31 -0
- package/types/masmarino-gabarit-badge.d.ts +139 -0
- package/types/masmarino-gabarit-bar-chart.d.ts +111 -0
- package/types/masmarino-gabarit-breadcrumb.d.ts +12 -0
- package/types/masmarino-gabarit-button.d.ts +108 -0
- package/types/masmarino-gabarit-card.d.ts +102 -0
- package/types/masmarino-gabarit-chart.d.ts +361 -0
- package/types/masmarino-gabarit-checkbox-group.d.ts +113 -0
- package/types/masmarino-gabarit-checkbox.d.ts +49 -0
- package/types/masmarino-gabarit-confirm-danger-modal.d.ts +94 -0
- package/types/masmarino-gabarit-copy-button.d.ts +111 -0
- package/types/masmarino-gabarit-copy-field.d.ts +64 -0
- package/types/masmarino-gabarit-date-picker.d.ts +399 -0
- package/types/masmarino-gabarit-description-list.d.ts +40 -0
- package/types/masmarino-gabarit-dimension-card.d.ts +55 -0
- package/types/masmarino-gabarit-disclosure.d.ts +44 -0
- package/types/masmarino-gabarit-divider.d.ts +20 -0
- package/types/masmarino-gabarit-docs.d.ts +2 -1
- package/types/masmarino-gabarit-drawer.d.ts +51 -0
- package/types/masmarino-gabarit-empty-state.d.ts +67 -0
- package/types/masmarino-gabarit-file-upload.d.ts +95 -0
- package/types/masmarino-gabarit-floating-panel.d.ts +14 -0
- package/types/masmarino-gabarit-format.d.ts +54 -0
- package/types/masmarino-gabarit-funnel-chart.d.ts +78 -0
- package/types/masmarino-gabarit-gauge-bar.d.ts +54 -0
- package/types/masmarino-gabarit-git-field.d.ts +39 -0
- package/types/masmarino-gabarit-icon-marker.d.ts +47 -0
- package/types/masmarino-gabarit-icon.d.ts +23 -0
- package/types/masmarino-gabarit-input.d.ts +181 -0
- package/types/masmarino-gabarit-job-graph.d.ts +63 -0
- package/types/masmarino-gabarit-job-status.d.ts +20 -0
- package/types/masmarino-gabarit-line-chart.d.ts +153 -0
- package/types/masmarino-gabarit-list-card.d.ts +87 -0
- package/types/masmarino-gabarit-list-row.d.ts +14 -0
- package/types/masmarino-gabarit-list-toolbar.d.ts +100 -0
- package/types/masmarino-gabarit-menu.d.ts +93 -0
- package/types/masmarino-gabarit-mfa-enrollment.d.ts +142 -0
- package/types/masmarino-gabarit-mfa-settings.d.ts +89 -0
- package/types/masmarino-gabarit-modal.d.ts +60 -0
- package/types/masmarino-gabarit-nav-tabs.d.ts +65 -0
- package/types/masmarino-gabarit-notification-dot.d.ts +34 -0
- package/types/masmarino-gabarit-page-header.d.ts +20 -0
- package/types/masmarino-gabarit-page-layout.d.ts +46 -0
- package/types/masmarino-gabarit-pagination.d.ts +109 -0
- package/types/masmarino-gabarit-panel.d.ts +21 -0
- package/types/masmarino-gabarit-passkey-settings.d.ts +94 -0
- package/types/masmarino-gabarit-pie-chart.d.ts +94 -0
- package/types/masmarino-gabarit-popover.d.ts +33 -0
- package/types/masmarino-gabarit-radio-group.d.ts +66 -0
- package/types/masmarino-gabarit-save-status.d.ts +39 -0
- package/types/masmarino-gabarit-search-bar.d.ts +183 -0
- package/types/masmarino-gabarit-secret-reveal.d.ts +95 -0
- package/types/masmarino-gabarit-segmented-control.d.ts +76 -0
- package/types/masmarino-gabarit-select.d.ts +146 -0
- package/types/masmarino-gabarit-skeleton-list.d.ts +54 -0
- package/types/masmarino-gabarit-skeleton.d.ts +26 -0
- package/types/masmarino-gabarit-slider.d.ts +73 -0
- package/types/masmarino-gabarit-sparkline.d.ts +70 -0
- package/types/masmarino-gabarit-spinner.d.ts +20 -0
- package/types/masmarino-gabarit-stat-grid.d.ts +39 -0
- package/types/masmarino-gabarit-stat-tile.d.ts +79 -0
- package/types/masmarino-gabarit-stepper.d.ts +67 -0
- package/types/masmarino-gabarit-switch.d.ts +42 -0
- package/types/masmarino-gabarit-table.d.ts +60 -0
- package/types/masmarino-gabarit-tabs.d.ts +40 -0
- package/types/masmarino-gabarit-tag-input.d.ts +78 -0
- package/types/masmarino-gabarit-tag.d.ts +35 -0
- package/types/masmarino-gabarit-textarea.d.ts +109 -0
- package/types/masmarino-gabarit-timeline-chart.d.ts +145 -0
- package/types/masmarino-gabarit-toaster.d.ts +62 -0
- package/types/masmarino-gabarit-tooltip.d.ts +39 -0
- package/types/masmarino-gabarit-tree.d.ts +71 -0
- package/types/masmarino-gabarit-user-chip.d.ts +26 -0
- package/types/masmarino-gabarit.d.ts +83 -6599
- package/user-chip/README.md +42 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# MfaSettings
|
|
2
|
+
|
|
3
|
+
The account settings card of the signed-in user's **authenticator app** (TOTP) and **backup codes**.
|
|
4
|
+
It sits next to [`gbt-passkey-settings`](../passkey-settings/passkey-settings.ts): a second factor is
|
|
5
|
+
an app OR a passkey, so this card reads both (the app is only "Optional" once a passkey exists). It
|
|
6
|
+
talks to the application's backend through the `MFA_PORT` it injects, and signals the one thing the
|
|
7
|
+
application must act on, a revoked session, through an output.
|
|
8
|
+
|
|
9
|
+
**Selector**: `gbt-mfa-settings`
|
|
10
|
+
|
|
11
|
+
## States
|
|
12
|
+
|
|
13
|
+
| State | What shows |
|
|
14
|
+
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
15
|
+
| loading | Skeleton rows (`aria-busy`) and, beside them (never inside the busy block, which would keep it quiet), a visually hidden `role="status"` ("Loading two-factor authentication…"), while `status()` runs. |
|
|
16
|
+
| failed | An error alert (`role="alert"`) with a **Retry** button that reads the status again. |
|
|
17
|
+
| app on | "App configured" with an **On** badge; the backup codes left (warning-toned, with an icon and a sentence, from `lowCodesThreshold` down) and **Regenerate backup codes**; **Reset** (the app is the only factor) or **Remove** (a passkey remains). Each action opens an inline current-password prompt. |
|
|
18
|
+
| passkey only | "No app configured" with an **Optional** badge and **Add an app**; the backup codes row as above; no reset. |
|
|
19
|
+
| no factor | Defensive (a live session without a factor should not exist: removing the last one signs the user out): a static warning that MFA is required and a primary **Set up now**. |
|
|
20
|
+
| enrolling | After the password: the QR ([`gbt-totp-qr`](../mfa-enrollment/totp-qr/totp-qr.ts)), the secret as text, a 6-digit code field, **Activate** and **Cancel**. |
|
|
21
|
+
| codes | After an enrolment or a regeneration: the ten backup codes, shown **once** ([`gbt-backup-codes`](../mfa-enrollment/backup-codes/backup-codes.ts): copy, download, acknowledgement), a static "Do not leave this page" warning until acknowledged, and **Done** (disabled until acknowledged; it returns to the ready state with the new count, without a reload). |
|
|
22
|
+
| revoked | After a successful reset/removal (the server revoked this session): an inert card, only "You have been signed out" (`signedOut`), no action left. `sessionRevoked` fires too: the card cannot clear the application's session itself. |
|
|
23
|
+
|
|
24
|
+
## Inputs
|
|
25
|
+
|
|
26
|
+
| Input | Type | Default | Role |
|
|
27
|
+
| ------------------- | ---------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
28
|
+
| `labels` | `Partial<MfaSettingsLabels>` | `{}` | The strings to change, string by string, over `provideAuthLabels({ mfaSettings })` and the English defaults (`DEFAULT_MFA_SETTINGS_LABELS`). |
|
|
29
|
+
| `lowCodesThreshold` | `number` | `3` | From this many backup codes left (inclusive), the count turns to a warning asking the user to regenerate them. |
|
|
30
|
+
|
|
31
|
+
## Outputs
|
|
32
|
+
|
|
33
|
+
| Output | Payload | Role |
|
|
34
|
+
| ---------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
35
|
+
| `sessionRevoked` | `void` | The app was reset/removed (`MfaPort.disable` succeeded) and the server revoked this very session with it. Sign out locally and go to the sign-in page (a new enrolment is forced there unless a passkey remains). Bind it: the card only turns inert (`revoked`). |
|
|
36
|
+
|
|
37
|
+
## Slots
|
|
38
|
+
|
|
39
|
+
None.
|
|
40
|
+
|
|
41
|
+
## Dependencies (DI)
|
|
42
|
+
|
|
43
|
+
| Token | Required | Role |
|
|
44
|
+
| ------------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
45
|
+
| `MFA_PORT` | yes | The application's `MfaPort`: `status`, `enroll`, `confirm`, `regenerate`, `disable` are used here. A wrong current password must be a **400** `{ "error": "current password is incorrect" }` (see `mfa.port.ts`). |
|
|
46
|
+
| `TOTP_QR_RENDERER` | no | Draws the QR. Without one, `gbt-totp-qr` shows its fallback text and the secret to type by hand. |
|
|
47
|
+
| `AUTH_LABELS` | no | The kit's strings for the whole application (`provideAuthLabels`). The nested backup codes and QR take theirs from here (`backupCodes`, `totpQr`), not from this card's `labels`. |
|
|
48
|
+
| `MfaSettingsState` | (root) | What this card and `gbt-passkey-settings` share: the factors as last read or changed, which card owns the open form, whether the app is mid-flow. Root-provided; provide it on the page to scope it. |
|
|
49
|
+
|
|
50
|
+
## Behaviour
|
|
51
|
+
|
|
52
|
+
- **One password prompt at a time**, in place of the button that opened it: opening another drops the
|
|
53
|
+
first one's password. Opening one claims the form in `MfaSettingsState`; when the passkeys card
|
|
54
|
+
claims it, this card closes its prompt (and its dialog) quietly. The enrolment and the codes step
|
|
55
|
+
are never interrupted (the passkeys card holds back instead, through `appFlowActive`).
|
|
56
|
+
- **At most one primary button** per state. The reset/remove prompt submits with a quiet
|
|
57
|
+
**Continue**; the one destructive action is in the confirmation dialog
|
|
58
|
+
(`gbt-confirm-danger-modal`), which sends the request. A second confirm while it runs is ignored.
|
|
59
|
+
- **Failures**: an empty field or a wrong password/code is said under the field
|
|
60
|
+
(`Enter your password`, `Incorrect password`, `Enter the 6-digit code from your app`,
|
|
61
|
+
`Incorrect code`, the refused code being emptied); a 429 or any other failure is an alert above
|
|
62
|
+
the form, which stays. The backend's own text is never shown.
|
|
63
|
+
- **Secrets**: the password lives only until its request answers (or the prompt closes); the secret,
|
|
64
|
+
the otpauth URL, the code and the backup codes are dropped as soon as they did their job, and
|
|
65
|
+
everything is emptied when the component is destroyed. Spaces in the typed code are removed.
|
|
66
|
+
- **Codes shown once**: while they are on screen and not acknowledged, closing or reloading the tab
|
|
67
|
+
asks the browser to confirm (`beforeunload` on the host document's window); the listener is
|
|
68
|
+
removed once they are acknowledged or the card is destroyed.
|
|
69
|
+
- **Focus** follows the state: the password or code field when it opens or after a refusal, the
|
|
70
|
+
instruction line (`tabindex="-1"`) of the scan and codes steps and of the ready state after
|
|
71
|
+
**Done**, the opener after **Cancel**.
|
|
72
|
+
- After a confirmed enrolment the card tells `MfaSettingsState` (`totpConfirmed`), so the passkeys card
|
|
73
|
+
knows deleting a key no longer removes the last factor; a passkey added by the passkeys card makes
|
|
74
|
+
the mandatory warning give way to "No app configured" at once.
|
|
75
|
+
|
|
76
|
+
## Example
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
// app.config.ts
|
|
80
|
+
import { HttpClient, provideHttpClient } from '@angular/common/http'
|
|
81
|
+
import { type ApplicationConfig, Injectable, inject } from '@angular/core'
|
|
82
|
+
import {
|
|
83
|
+
type BackupCodesResult,
|
|
84
|
+
MFA_PORT,
|
|
85
|
+
type MfaPort,
|
|
86
|
+
type MfaStatus,
|
|
87
|
+
TOTP_QR_RENDERER,
|
|
88
|
+
type TotpEnrollment,
|
|
89
|
+
type TotpQrRenderOptions,
|
|
90
|
+
provideAuthLabels,
|
|
91
|
+
} from '@masmarino/gabarit'
|
|
92
|
+
|
|
93
|
+
@Injectable({ providedIn: 'root' })
|
|
94
|
+
export class HttpMfaPort implements MfaPort {
|
|
95
|
+
private http = inject(HttpClient)
|
|
96
|
+
status = () => this.http.get<MfaStatus>('/api/me/mfa')
|
|
97
|
+
enroll = (currentPassword: string) =>
|
|
98
|
+
this.http.post<TotpEnrollment>('/api/me/mfa/totp/enroll', { currentPassword })
|
|
99
|
+
confirm = (code: string) =>
|
|
100
|
+
this.http.post<BackupCodesResult>('/api/me/mfa/totp/confirm', { code })
|
|
101
|
+
regenerate = (currentPassword: string) =>
|
|
102
|
+
this.http.post<BackupCodesResult>('/api/me/mfa/backup-codes/regenerate', { currentPassword })
|
|
103
|
+
disable = (currentPassword: string) =>
|
|
104
|
+
this.http.post<void>('/api/me/mfa/totp/disable', { currentPassword })
|
|
105
|
+
// … and the passkey calls used by gbt-passkey-settings.
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export const appConfig: ApplicationConfig = {
|
|
109
|
+
providers: [
|
|
110
|
+
provideHttpClient(),
|
|
111
|
+
{ provide: MFA_PORT, useExisting: HttpMfaPort },
|
|
112
|
+
{
|
|
113
|
+
provide: TOTP_QR_RENDERER,
|
|
114
|
+
useValue: (text: string, options: TotpQrRenderOptions) =>
|
|
115
|
+
import('qrcode').then((qr) => qr.toDataURL(text, options)),
|
|
116
|
+
},
|
|
117
|
+
// Optional: localise the whole kit once.
|
|
118
|
+
provideAuthLabels({
|
|
119
|
+
mfaSettings: { heading: "Application d'authentification", enabled: 'Activée' /* … */ },
|
|
120
|
+
backupCodes: { copy: 'Copier les codes' /* … */ },
|
|
121
|
+
}),
|
|
122
|
+
],
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
// security-page.ts
|
|
128
|
+
import { Component, inject } from '@angular/core'
|
|
129
|
+
import { Router } from '@angular/router'
|
|
130
|
+
import { MfaSettings, PasskeySettings } from '@masmarino/gabarit'
|
|
131
|
+
import { AuthService } from './auth.service' // the application's own session holder
|
|
132
|
+
|
|
133
|
+
@Component({
|
|
134
|
+
imports: [MfaSettings, PasskeySettings],
|
|
135
|
+
template: `
|
|
136
|
+
<gbt-mfa-settings [lowCodesThreshold]="3" (sessionRevoked)="signedOut()" />
|
|
137
|
+
<gbt-passkey-settings (sessionRevoked)="signedOut()" />
|
|
138
|
+
`,
|
|
139
|
+
})
|
|
140
|
+
export class SecurityPage {
|
|
141
|
+
private auth = inject(AuthService)
|
|
142
|
+
private router = inject(Router)
|
|
143
|
+
|
|
144
|
+
/** The server revoked the session with the factor: forget it locally and sign in again. */
|
|
145
|
+
signedOut() {
|
|
146
|
+
this.auth.logout()
|
|
147
|
+
this.router.navigateByUrl('/login')
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Accessibility
|
|
153
|
+
|
|
154
|
+
See [AUDIT.md](AUDIT.md).
|
package/modal/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Modal
|
|
2
|
+
|
|
3
|
+
Modal dialog box — traps focus, closes on Escape or a click on the
|
|
4
|
+
backdrop, and stays shut while a request is running (`busy`).
|
|
5
|
+
|
|
6
|
+
**Selector**: `gbt-modal`
|
|
7
|
+
|
|
8
|
+
## Inputs
|
|
9
|
+
|
|
10
|
+
| Input | Type | Default | Role |
|
|
11
|
+
| -------------- | ---------------------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
12
|
+
| `isOpen` | `boolean` | required | Open/closed state, driven by the application. |
|
|
13
|
+
| `heading` | `string` | `''` | Dialog title. |
|
|
14
|
+
| `headingLevel` | `1 \| 2 \| 3 \| 4 \| 5 \| 6` | `2` | Level of the rendered heading. |
|
|
15
|
+
| `closeLabel` | `string` | `'Close'` | Accessible name of the close button. |
|
|
16
|
+
| `busy` | `boolean` | `false` | A request is in flight: Escape, a click on the backdrop and the close button are ignored (no `closed`). The body reports `aria-busy="true"` (the status region below is deliberately outside it: a busy ancestor can suppress live announcements), the close button `aria-disabled="true"` (it stays focusable), and `busyLabel` is read out. |
|
|
17
|
+
| `busyLabel` | `string` | `'Please wait'` | Text read out by a visually hidden `role="status"` region while `busy`. |
|
|
18
|
+
| `returnFocus` | `boolean` | `true` | Gives focus back to the element that had it when the dialog opened, once it closes. Turn it off when the opener is gone or you move focus yourself. |
|
|
19
|
+
|
|
20
|
+
## Slots
|
|
21
|
+
|
|
22
|
+
| Slot | Role |
|
|
23
|
+
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
24
|
+
| default | The body. |
|
|
25
|
+
| `[modal-footer]` | Actions under a hairline, kept in view while a long body scrolls. Nothing to import: add the attribute `modal-footer` to the element. A `div`, `footer` or `section` carrying it is the actions row itself (right aligned, wrapping, spaced); any other element (a button, a link) is one item of that row. Nothing projected, no footer. |
|
|
26
|
+
|
|
27
|
+
## Outputs
|
|
28
|
+
|
|
29
|
+
| Output | Type | Role |
|
|
30
|
+
| -------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
|
31
|
+
| `closed` | `void` | Emitted on Escape, a click outside the box, or a click on the close button. The application must respond by setting `isOpen` to `false`. |
|
|
32
|
+
|
|
33
|
+
## Busy
|
|
34
|
+
|
|
35
|
+
While `busy` is `true`, pass it on from the request's state and disable your own cancel button; the
|
|
36
|
+
dialog keeps focus (it comes back to the dialog if the button you pressed was disabled) and closes
|
|
37
|
+
again as soon as `busy` is `false`. Setting `isOpen` to `false` yourself still closes it.
|
|
38
|
+
|
|
39
|
+
## Touch
|
|
40
|
+
|
|
41
|
+
On a coarse pointer (`pointer: coarse`) the close button is 44 × 44 px; the header keeps its height.
|
|
42
|
+
|
|
43
|
+
## Example
|
|
44
|
+
|
|
45
|
+
```html
|
|
46
|
+
<gbt-modal [isOpen]="ouvert()" heading="Confirmer" closeLabel="Fermer" (closed)="ouvert.set(false)">
|
|
47
|
+
Contenu de la boîte de dialogue.
|
|
48
|
+
</gbt-modal>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
With a footer and a running request:
|
|
52
|
+
|
|
53
|
+
```html
|
|
54
|
+
<gbt-modal
|
|
55
|
+
[isOpen]="open()"
|
|
56
|
+
[busy]="saving()"
|
|
57
|
+
busyLabel="Saving"
|
|
58
|
+
heading="Rename"
|
|
59
|
+
(closed)="open.set(false)"
|
|
60
|
+
>
|
|
61
|
+
<gbt-input label="Name" [(ngModel)]="name" />
|
|
62
|
+
<div modal-footer>
|
|
63
|
+
<gbt-button
|
|
64
|
+
variant="secondary"
|
|
65
|
+
text="Cancel"
|
|
66
|
+
[disabled]="saving()"
|
|
67
|
+
(clicked)="open.set(false)"
|
|
68
|
+
/>
|
|
69
|
+
<gbt-button text="Save" [loading]="saving()" (clicked)="save()" />
|
|
70
|
+
</div>
|
|
71
|
+
</gbt-modal>
|
|
72
|
+
```
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# NavTabs (`gbt-nav-tabs` and `a[gbtNavTab]`)
|
|
2
|
+
|
|
3
|
+
A navigation between the sections of a page (repository settings, account settings…) as **real links**: the
|
|
4
|
+
application projects `a[gbtNavTab]` items into `gbt-nav-tabs`. It is the link counterpart of
|
|
5
|
+
[`gbt-tabs`](../tabs/README.md), which is for panels that switch in place.
|
|
6
|
+
|
|
7
|
+
It is **router-agnostic**: no `@angular/router` anywhere in the library. The application decides which item is
|
|
8
|
+
active (`[active]`) and puts `routerLink` or a plain `href` on each anchor, so sections stay deep-linkable, the
|
|
9
|
+
back button walks through them, and middle-click / "open in a new tab" keep working. Keyboard behaviour is that of
|
|
10
|
+
native links: Tab, Shift+Tab and Enter (no roving tabindex, no arrow-key handling — it is a list of links, not a
|
|
11
|
+
`tablist`).
|
|
12
|
+
|
|
13
|
+
**Selectors**: `gbt-nav-tabs` (class `NavTabs`) and `a[gbtNavTab]` (class `NavTab`), both standalone.
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { NavTab, NavTabs } from '@masmarino/gabarit'
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## `gbt-nav-tabs` inputs
|
|
20
|
+
|
|
21
|
+
| Input | Type | Default | Role |
|
|
22
|
+
| ------------- | ---------------------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
23
|
+
| `ariaLabel` | `string` | `''` | Accessible name of the `<nav>` landmark. Give one (it is what a screen-reader user hears in the landmark list); ignored when `landmark` is `false`. |
|
|
24
|
+
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | `horizontal`: underline tabs above a section. `vertical`: a list of pill links for a side column. |
|
|
25
|
+
| `landmark` | `boolean` | `true` | Render the links in a `<nav>` of their own. Pass `false` inside a navigation that already exists (a `gbt-page-layout` `[page-nav]`): no nested landmarks. |
|
|
26
|
+
|
|
27
|
+
## `a[gbtNavTab]` inputs
|
|
28
|
+
|
|
29
|
+
| Input | Type | Default | Role |
|
|
30
|
+
| -------- | -------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
31
|
+
| `active` | `boolean` | `false` | This item is the current page or section: sets `aria-current="page"` and the active look. At most one item should be active. |
|
|
32
|
+
| `icon` | `string \| null` | `null` | Registered icon name before the label. Decorative (`aria-hidden`): the label names the link. |
|
|
33
|
+
| `badge` | `number \| string \| null` | `null` | A counter pill after the label — `0` and `'20+'` included, `null` and `''` show nothing. Read as part of the link's name. |
|
|
34
|
+
|
|
35
|
+
The label is the projected content.
|
|
36
|
+
|
|
37
|
+
## Behaviour
|
|
38
|
+
|
|
39
|
+
- **Horizontal** is a row of underline tabs with a hairline under it. When the tabs overflow, the row scrolls to
|
|
40
|
+
the active one (keeping a glimpse of the previous tab) — the list scrolls, never the page — and fades at the edge
|
|
41
|
+
that hides tabs. A tab keeps the width of its bold state, so nothing shifts when the active tab changes.
|
|
42
|
+
- **Vertical** is a list of pills, the active one with an accent bar on its leading edge (not the tint alone). While
|
|
43
|
+
a [`gbt-page-layout`](../page-layout/README.md) stacks its columns (its container is ≤ 768 px wide),
|
|
44
|
+
a vertical nav folds into that same row of tabs above the section, instead of pushing it far down. Outside a page
|
|
45
|
+
layout it stays a list.
|
|
46
|
+
- Both look at the active tab through `aria-current="page"`, which is what assistive technology reads.
|
|
47
|
+
|
|
48
|
+
## Examples
|
|
49
|
+
|
|
50
|
+
Router-less (plain hrefs; the application owns the active section):
|
|
51
|
+
|
|
52
|
+
```html
|
|
53
|
+
<gbt-nav-tabs ariaLabel="Repository settings">
|
|
54
|
+
<a
|
|
55
|
+
gbtNavTab
|
|
56
|
+
href="#general"
|
|
57
|
+
icon="settings"
|
|
58
|
+
[active]="section() === 'general'"
|
|
59
|
+
(click)="section.set('general')"
|
|
60
|
+
>General</a
|
|
61
|
+
>
|
|
62
|
+
<a
|
|
63
|
+
gbtNavTab
|
|
64
|
+
href="#webhooks"
|
|
65
|
+
icon="bell"
|
|
66
|
+
[badge]="3"
|
|
67
|
+
[active]="section() === 'webhooks'"
|
|
68
|
+
(click)="section.set('webhooks')"
|
|
69
|
+
>Webhooks</a
|
|
70
|
+
>
|
|
71
|
+
</gbt-nav-tabs>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
With the Angular router, in the application (the library never imports it):
|
|
75
|
+
|
|
76
|
+
```html
|
|
77
|
+
<gbt-nav-tabs ariaLabel="Account settings">
|
|
78
|
+
@for (section of sections; track section.path) {
|
|
79
|
+
<a
|
|
80
|
+
gbtNavTab
|
|
81
|
+
[routerLink]="section.path"
|
|
82
|
+
routerLinkActive
|
|
83
|
+
#rla="routerLinkActive"
|
|
84
|
+
[active]="rla.isActive"
|
|
85
|
+
[icon]="section.icon"
|
|
86
|
+
>{{ section.label }}</a
|
|
87
|
+
>
|
|
88
|
+
}
|
|
89
|
+
</gbt-nav-tabs>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Do not also set `ariaCurrentWhenActive` on the same element: `active` already sets `aria-current`. A matcher of your
|
|
93
|
+
own just computes the boolean you feed to `[active]`.
|
|
94
|
+
|
|
95
|
+
Inside a page layout's nav column, without a second landmark:
|
|
96
|
+
|
|
97
|
+
```html
|
|
98
|
+
<gbt-page-layout navLabel="Repository settings">
|
|
99
|
+
<gbt-nav-tabs page-nav orientation="vertical" [landmark]="false">…</gbt-nav-tabs>
|
|
100
|
+
…the section…
|
|
101
|
+
</gbt-page-layout>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Accessibility
|
|
105
|
+
|
|
106
|
+
See [AUDIT.md](AUDIT.md).
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# NotificationDot
|
|
2
|
+
|
|
3
|
+
A small dot or count badge overlaid on the corner of whatever it
|
|
4
|
+
wraps — an icon, an avatar, a button — for an "unread"/"new" signal
|
|
5
|
+
(e.g. a bell icon with an unread-notifications count). Distinct from
|
|
6
|
+
`Badge`, which is a standalone inline label, not an overlay on another
|
|
7
|
+
element.
|
|
8
|
+
|
|
9
|
+
**Selector**: `gbt-notification-dot` (`exportAs="gbtNotificationDot"`)
|
|
10
|
+
|
|
11
|
+
## Inputs
|
|
12
|
+
|
|
13
|
+
| Input | Type | Default | Role |
|
|
14
|
+
| --------- | ------------------------------------------------ | ----------- | --------------------------------------------------------- |
|
|
15
|
+
| `count` | `number \| null` | `null` | `null` shows a plain dot; a number shows it as text. |
|
|
16
|
+
| `max` | `number` | `99` | A count above this shows as `"{max}+"`. |
|
|
17
|
+
| `variant` | `'error' \| 'warning' \| 'success' \| 'info'` | `'error'` | Reuses `Badge`'s own semantic color tokens. |
|
|
18
|
+
| `hidden` | `boolean` | `false` | Force-hides the dot regardless of `count`. |
|
|
19
|
+
|
|
20
|
+
A `count` of exactly `0` also hides the dot — there's rarely a reason
|
|
21
|
+
to show "0" as a notification badge.
|
|
22
|
+
|
|
23
|
+
## Example
|
|
24
|
+
|
|
25
|
+
```html
|
|
26
|
+
<gbt-notification-dot #dot="gbtNotificationDot" [count]="unreadCount()">
|
|
27
|
+
<button type="button" [attr.aria-label]="'Notifications, ' + dot.count() + ' unread'">
|
|
28
|
+
<gbt-icon name="bell" />
|
|
29
|
+
</button>
|
|
30
|
+
</gbt-notification-dot>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Accessibility
|
|
34
|
+
|
|
35
|
+
Like `Tooltip` and `Popover`, `gbt-notification-dot` wraps arbitrary
|
|
36
|
+
projected content and cannot inject an accessible name onto it — the
|
|
37
|
+
visual dot itself is `aria-hidden="true"`, since a screen reader
|
|
38
|
+
reading "3" out of context (disconnected from what it's a count of)
|
|
39
|
+
isn't useful. Instead, `count` is exposed publicly via
|
|
40
|
+
`exportAs="gbtNotificationDot"` so the consumer can fold it into
|
|
41
|
+
*their own* trigger's accessible name (as in the example above),
|
|
42
|
+
producing one coherent announcement ("Notifications, 3 unread") rather
|
|
43
|
+
than two disconnected ones.
|