@repobit/dex-system-design 0.25.12 → 0.26.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/CHANGELOG.md +187 -0
- package/README.md +131 -8
- package/package.json +95 -13
- package/schemas/component-manifest.json +3294 -405
- package/schemas/manifest-omissions.json +173 -0
- package/src/blog/src/breakpoints.js +6 -6
- package/src/blog/src/content/blog-atoms-showcase.js +1 -1
- package/src/blog/src/content/blog-page-layout.js +2 -2
- package/src/blog/src/lib/article-toc.js +7 -7
- package/src/blog/src/shell/blog-shell.js +1 -1
- package/src/blog/src/tokens/blog.js +2 -2
- package/src/blog/src/tokens/core.js +34 -35
- package/src/blog/src/tokens/legacy.js +0 -9
- package/src/blog/tokens/blog.js +2 -2
- package/src/blog/tokens/core.js +34 -35
- package/src/blog/tokens/legacy.js +0 -9
- package/src/blog/tokens.css +36 -46
- package/src/components/accordion/accordion-bg.css.js +83 -59
- package/src/components/accordion/accordion-bg.js +29 -10
- package/src/components/accordion/accordion-bg.stories.js +67 -67
- package/src/components/accordion/accordion-keyboard.js +80 -0
- package/src/components/accordion/accordion.css.js +142 -80
- package/src/components/accordion/accordion.js +37 -10
- package/src/components/accordion/accordion.stories.js +7 -7
- package/src/components/agent-endpoints/agent-endpoints.css.js +36 -39
- package/src/components/agent-endpoints/agent-endpoints.js +30 -20
- package/src/components/agent-endpoints/agent-endpoints.stories.js +1 -1
- package/src/components/ai-guardian-demo.stories.js +91 -91
- package/src/components/alert/alert.css.js +198 -0
- package/src/components/alert/alert.js +103 -0
- package/src/components/alert/alert.stories.js +206 -0
- package/src/components/anchor/anchor-nav.css.js +213 -101
- package/src/components/anchor/anchor-nav.js +26 -40
- package/src/components/anchor/anchor.stories.js +22 -12
- package/src/components/annotation/annotation.css.js +58 -0
- package/src/components/annotation/annotation.js +67 -0
- package/src/components/annotation/annotation.stories.js +63 -0
- package/src/components/architecture.stories.js +285 -0
- package/src/components/atoms.stories.js +342 -0
- package/src/components/avatar/avatar.css.js +67 -0
- package/src/components/avatar/avatar.js +42 -0
- package/src/components/avatar/avatar.stories.js +95 -0
- package/src/components/awards/awards-flex.css.js +209 -23
- package/src/components/awards/awards-flex.js +66 -7
- package/src/components/awards/awards-flex.stories.js +1 -1
- package/src/components/awards/{awards-light.stories.js → awards-summary.stories.js} +54 -64
- package/src/components/awards/awards.css.js +116 -58
- package/src/components/awards/awards.js +102 -28
- package/src/components/awards/awards.stories.js +1 -1
- package/src/components/back/back.css.js +51 -22
- package/src/components/back/back.js +28 -7
- package/src/components/back/back.stories.js +9 -3
- package/src/components/badge/badge.css.js +158 -46
- package/src/components/badge/badge.js +62 -74
- package/src/components/badge/badge.stories.js +176 -248
- package/src/components/bento-grid/bento-grid.css.js +21 -16
- package/src/components/bento-grid/bento-grid.js +16 -4
- package/src/components/bento-grid/bento-grid.stories.js +19 -18
- package/src/components/bento-grid/bento-item.css.js +52 -28
- package/src/components/bento-grid/bento-item.js +2 -2
- package/src/components/blockquote/blockquote.css.js +47 -0
- package/src/components/blockquote/blockquote.js +29 -0
- package/src/components/blockquote/blockquote.stories.js +62 -0
- package/src/components/breadcrumb/breadcrumb.css.js +92 -22
- package/src/components/breadcrumb/breadcrumb.js +56 -11
- package/src/components/button/button.css.js +531 -161
- package/src/components/button/button.js +206 -214
- package/src/components/button/button.stories.js +322 -174
- package/src/components/caption/caption.css.js +21 -0
- package/src/components/caption/caption.js +31 -0
- package/src/components/caption/caption.stories.js +62 -0
- package/src/components/card-features/card-features.css.js +14 -22
- package/src/components/card-features/card-features.js +68 -9
- package/src/components/card-features/card-features.stories.js +11 -11
- package/src/components/cards/card.css.js +60 -81
- package/src/components/cards/card.js +56 -25
- package/src/components/cards/card.stories.js +39 -39
- package/src/components/carousel/carousel.css.js +93 -99
- package/src/components/carousel/carousel.js +32 -4
- package/src/components/checkbox/checkbox.css.js +111 -93
- package/src/components/checkbox/checkbox.js +64 -90
- package/src/components/checkbox/checkbox.stories.js +96 -29
- package/src/components/code/code.css.js +70 -0
- package/src/components/code/code.js +40 -0
- package/src/components/code/code.stories.js +73 -0
- package/src/components/compare/compare.css.js +179 -43
- package/src/components/compare/compare.js +52 -25
- package/src/components/compare/compare.stories.js +61 -4
- package/src/components/comparison-panel/comparison-panel.css.js +38 -51
- package/src/components/comparison-panel/comparison-panel.js +5 -4
- package/src/components/comparison-panel/comparison-panel.stories.js +1 -1
- package/src/components/compatibility-table/compatibility-table.css.js +180 -80
- package/src/components/compatibility-table/compatibility-table.js +37 -9
- package/src/components/control-alignment.stories.js +393 -0
- package/src/components/cta-banner/cta-banner.css.js +41 -13
- package/src/components/cta-banner/cta-banner.js +6 -5
- package/src/components/cta-banner/cta-banner.stories.js +43 -98
- package/src/components/display/display.css.js +25 -1
- package/src/components/display/display.js +46 -13
- package/src/components/display/display.stories.js +270 -40
- package/src/components/divider/divider-horizontal.js +21 -5
- package/src/components/divider/divider-vertical.js +15 -7
- package/src/components/divider/divider.css.js +2 -2
- package/src/components/divider/divider.stories.js +38 -34
- package/src/components/dropdown/dropdown.css.js +93 -126
- package/src/components/dropdown/dropdown.js +114 -34
- package/src/components/dropdown/dropdown.stories.js +56 -24
- package/src/components/features/features.css.js +24 -11
- package/src/components/features/features.js +7 -3
- package/src/components/features/features.stories.js +37 -37
- package/src/components/features-tab/features-tab.css.js +19 -8
- package/src/components/features-tab/features-tab.js +6 -5
- package/src/components/features-tab/features-tab.stories.js +23 -23
- package/src/components/fieldset/fieldset.css.js +30 -0
- package/src/components/fieldset/fieldset.js +28 -0
- package/src/components/fieldset/fieldset.stories.js +84 -0
- package/src/components/footer/footer-links-group.css.js +10 -14
- package/src/components/footer/footer-links-group.js +4 -3
- package/src/components/footer/footer-lp.css.js +59 -30
- package/src/components/footer/footer-lp.js +17 -12
- package/src/components/footer/footer-lp.stories.js +63 -75
- package/src/components/footer/footer-nav-menu.css.js +26 -4
- package/src/components/footer/footer-nav-menu.js +3 -2
- package/src/components/footer/footer.css.js +146 -264
- package/src/components/footer/footer.js +176 -106
- package/src/components/footer/footer.stories.js +9 -9
- package/src/components/footer/localeMap.js +1 -1
- package/src/components/grid/grid.css.js +1 -1
- package/src/components/grid/grid.js +49 -7
- package/src/components/grid/grid.stories.js +111 -0
- package/src/components/header/header-code.css.js +61 -51
- package/src/components/header/header-code.js +28 -14
- package/src/components/header/header-code.stories.js +5 -5
- package/src/components/header/header.css.js +655 -176
- package/src/components/header/header.js +269 -36
- package/src/components/header/header.stories.js +841 -18
- package/src/components/heading/heading.css.js +14 -17
- package/src/components/heading/heading.js +48 -33
- package/src/components/heading/heading.stories.js +27 -25
- package/src/components/help-text/help-text.css.js +21 -0
- package/src/components/help-text/help-text.js +25 -0
- package/src/components/help-text/help-text.stories.js +66 -0
- package/src/components/highlight/highlight.css.js +86 -64
- package/src/components/highlight/highlight.js +18 -27
- package/src/components/highlight/highlight.stories.js +14 -3
- package/src/components/icons/analysis-icon.js +6 -16
- package/src/components/icons/arrow-down-icon.js +6 -19
- package/src/components/icons/arrow-icon.js +44 -0
- package/src/components/icons/arrow-right-icon.js +30 -23
- package/src/components/icons/arrow-up-icon.js +19 -18
- package/src/components/icons/bd-ai-icon.js +1 -1
- package/src/components/icons/brand-marks.stories.js +279 -0
- package/src/components/icons/brand-signature-icon.js +42 -0
- package/src/components/icons/check-icon.js +27 -0
- package/src/components/icons/circle-info-icon.js +33 -0
- package/src/components/icons/close-icon.js +6 -18
- package/src/components/icons/family-icon.js +8 -20
- package/src/components/icons/globe-icon.js +6 -18
- package/src/components/icons/guide-icon.js +6 -18
- package/src/components/icons/icon-element.js +99 -0
- package/src/components/icons/icons.js +36 -0
- package/src/components/icons/icons.stories.js +184 -52
- package/src/components/icons/individual-icon.js +6 -18
- package/src/components/icons/laurel-icon.js +9 -19
- package/src/components/icons/machine-learning-icon.js +6 -17
- package/src/components/icons/marks.js +41 -0
- package/src/components/icons/minus-icon.js +7 -21
- package/src/components/icons/missing-icon.js +52 -0
- package/src/components/icons/plus-icon.js +7 -21
- package/src/components/icons/quote-icon.js +19 -19
- package/src/components/image/image.css.js +72 -25
- package/src/components/image/image.js +11 -6
- package/src/components/image/image.stories.js +2 -2
- package/src/components/input/custom-form.stories.js +214 -62
- package/src/components/input/input-clipboard.css.js +51 -144
- package/src/components/input/input-clipboard.js +31 -15
- package/src/components/input/input.css.js +8 -146
- package/src/components/input/input.js +93 -38
- package/src/components/install-options/install-options.css.js +84 -77
- package/src/components/install-options/install-options.js +33 -30
- package/src/components/install-options/install-options.stories.js +3 -3
- package/src/components/label/label.css.js +35 -0
- package/src/components/label/label.js +38 -0
- package/src/components/label/label.stories.js +82 -0
- package/src/components/light-carousel/light-carousel.css.js +111 -63
- package/src/components/light-carousel/light-carousel.js +85 -11
- package/src/components/light-carousel/light-carousel.stories.js +126 -82
- package/src/components/link/link.css.js +150 -99
- package/src/components/link/link.js +86 -11
- package/src/components/link/link.stories.js +124 -30
- package/src/components/list/list-item/list-item.css.js +74 -22
- package/src/components/list/list-item/list-item.js +32 -10
- package/src/components/list/list-item/list-item.stories.js +23 -23
- package/src/components/list/list.css.js +8 -2
- package/src/components/list/list.js +3 -2
- package/src/components/list/list.stories.js +222 -0
- package/src/components/modal/modal.css.js +114 -24
- package/src/components/modal/modal.js +83 -7
- package/src/components/modal/modal.stories.js +9 -9
- package/src/components/overview/overview.css.js +19 -14
- package/src/components/overview/overview.js +6 -5
- package/src/components/overview/overview.stories.js +10 -10
- package/src/components/page-switcher/page-switcher.css.js +97 -40
- package/src/components/page-switcher/page-switcher.js +28 -5
- package/src/components/page-switcher/page-switcher.stories.js +95 -0
- package/src/components/paragraph/paragraph.css.js +15 -10
- package/src/components/paragraph/paragraph.js +41 -44
- package/src/components/paragraph/paragraph.stories.js +56 -21
- package/src/components/picture/picture.css.js +103 -44
- package/src/components/picture/picture.js +100 -30
- package/src/components/picture/picture.stories.js +1 -1
- package/src/components/pricing-cards/new-pricing-card.js +3 -3
- package/src/components/pricing-cards/new-pricing.css.js +24 -15
- package/src/components/pricing-cards/pricing-card-actions.css.js +1 -1
- package/src/components/pricing-cards/pricing-card-actions.js +3 -4
- package/src/components/pricing-cards/pricing-card-container.css.js +3 -3
- package/src/components/pricing-cards/pricing-card-container.js +3 -2
- package/src/components/pricing-cards/pricing-card-header.css.js +10 -8
- package/src/components/pricing-cards/pricing-card-header.js +6 -7
- package/src/components/pricing-cards/pricing-card-pricing.css.js +16 -9
- package/src/components/pricing-cards/pricing-card-pricing.js +8 -6
- package/src/components/pricing-cards/pricing-card-show-more.css.js +11 -3
- package/src/components/pricing-cards/pricing-card-show-more.js +8 -3
- package/src/components/pricing-cards/pricing-card.css.js +244 -190
- package/src/components/pricing-cards/pricing-card.js +54 -70
- package/src/components/pricing-cards/pricing-feature-item.css.js +25 -8
- package/src/components/pricing-cards/pricing-feature-item.js +8 -3
- package/src/components/process-steps/process-steps.css.js +26 -14
- package/src/components/process-steps/process-steps.js +23 -6
- package/src/components/process-steps/process-steps.stories.js +1 -1
- package/src/components/progress/progress.css.js +80 -0
- package/src/components/progress/progress.js +42 -0
- package/src/components/progress/progress.stories.js +79 -0
- package/src/components/quick-answer/quick-answer.css.js +75 -13
- package/src/components/quick-answer/quick-answer.js +8 -3
- package/src/components/radio/radio.css.js +92 -128
- package/src/components/radio/radio.js +106 -127
- package/src/components/radio/radio.stories.js +54 -19
- package/src/components/review/review-grid.css.js +5 -6
- package/src/components/review/review-grid.js +14 -11
- package/src/components/review/review.css.js +11 -6
- package/src/components/review/review.js +3 -8
- package/src/components/shared/ensemble.css.js +93 -0
- package/src/components/shared/form-field.css.js +206 -0
- package/src/components/shared/prop-vocabulary.js +48 -0
- package/src/components/shared/token-scales.js +306 -0
- package/src/components/skeleton/skeleton.css.js +34 -0
- package/src/components/skeleton/skeleton.js +32 -0
- package/src/components/skeleton/skeleton.stories.js +70 -0
- package/src/components/spinner/spinner.css.js +62 -0
- package/src/components/spinner/spinner.js +38 -0
- package/src/components/spinner/spinner.stories.js +92 -0
- package/src/components/spotlight-section/spotlight-section.css.js +14 -13
- package/src/components/spotlight-section/spotlight-section.js +42 -39
- package/src/components/switch/switch.css.js +103 -0
- package/src/components/switch/switch.js +44 -0
- package/src/components/switch/switch.stories.js +86 -0
- package/src/components/tab-list/content-switcher.stories.js +189 -0
- package/src/components/tab-list/tab-list.css.js +250 -0
- package/src/components/tab-list/tab-list.js +184 -0
- package/src/components/tab-list/tab-list.stories.js +221 -0
- package/src/components/tabs/tabs.css.js +14 -123
- package/src/components/tabs/tabs.js +16 -19
- package/src/components/tabs/tabs.stories.js +95 -95
- package/src/components/tag/tag.css.js +54 -0
- package/src/components/tag/tag.js +32 -0
- package/src/components/tag/tag.stories.js +126 -0
- package/src/components/terminal/terminal-message.css.js +164 -60
- package/src/components/terminal/terminal-message.js +5 -5
- package/src/components/terminal/terminal-message.stories.js +70 -1
- package/src/components/termsOfUse/terms.css.js +25 -20
- package/src/components/termsOfUse/terms.js +13 -9
- package/src/components/termsOfUse/terms.stories.js +1 -1
- package/src/components/textarea/textarea.css.js +51 -0
- package/src/components/textarea/textarea.js +136 -0
- package/src/components/textarea/textarea.stories.js +191 -0
- package/src/components/tooltip/tooltip.css.js +75 -0
- package/src/components/tooltip/tooltip.js +156 -0
- package/src/components/tooltip/tooltip.stories.js +161 -0
- package/src/components/trust-panel/trust-panel.css.js +85 -33
- package/src/components/trust-panel/trust-panel.js +18 -10
- package/src/components/trust-panel/trust-panel.stories.js +1 -1
- package/src/components/ultimate-security-demo.stories.js +431 -0
- package/src/components/video/video.css.js +17 -0
- package/src/components/video/video.js +40 -0
- package/src/components/video/video.stories.js +106 -0
- package/src/components/video-player/video-player.css.js +67 -11
- package/src/components/video-player/video-player.js +35 -4
- package/src/components/video-player/video-player.stories.js +1 -1
- package/src/components/visual-alignment.stories.js +411 -0
- package/src/components/visually-hidden/visually-hidden.css.js +24 -0
- package/src/components/visually-hidden/visually-hidden.js +16 -0
- package/src/components/visually-hidden/visually-hidden.stories.js +56 -0
- package/src/stories/Button.stories.js +17 -17
- package/src/stories/Header.stories.js +10 -10
- package/src/stories/Page.js +1 -1
- package/src/stories/Page.stories.js +6 -6
- package/src/tokens/borders.stories.js +339 -0
- package/src/tokens/color-responsibilities.stories.js +227 -0
- package/src/tokens/dark-derivation.js +309 -0
- package/src/tokens/dark-findings.js +197 -0
- package/src/tokens/dark-mode.stories.js +207 -0
- package/src/tokens/dimension.stories.js +251 -0
- package/src/tokens/elevation.stories.js +143 -0
- package/src/tokens/focus.stories.js +276 -0
- package/src/tokens/fonts.css +20 -20
- package/src/tokens/fonts.stories.js +354 -374
- package/src/tokens/layout.css +25 -11
- package/src/tokens/motion.stories.js +231 -0
- package/src/tokens/shape.stories.js +272 -0
- package/src/tokens/spacing-rhythm.stories.js +359 -0
- package/src/tokens/spacing.stories.js +208 -703
- package/src/tokens/story-chrome.js +503 -0
- package/src/tokens/surfaces.stories.js +224 -0
- package/src/tokens/text-on-surface.stories.js +164 -0
- package/src/tokens/token-map.js +220 -0
- package/src/tokens/token-map.stories.js +257 -0
- package/src/tokens/token-purposes.js +266 -0
- package/src/tokens/tokens-grid.stories.js +388 -900
- package/src/tokens/tokens.css +1633 -1045
- package/src/tokens/tokens.js +1130 -492
- package/src/tokens/tokens.stories.js +137 -47
- package/src/tokens/type-ceiling-words.js +299 -0
- package/src/tokens/typography.stories.js +1003 -965
- package/src/tokens/vertical-rhythm.stories.js +224 -0
- package/src/components/awards/awards-icon.js +0 -56
- package/src/components/awards/awards-light.js +0 -240
- package/src/components/banner/banner.js +0 -60
- package/src/components/banner/banner.stories.js +0 -51
- package/src/components/button/icons.js +0 -24
- package/src/components/input/index.js +0 -0
- package/src/components/light-carousel/light-carousel-simple.css.js +0 -183
- package/src/components/light-carousel/light-carousel-simple.js +0 -74
- package/src/tokens/colors.js +0 -121
- package/src/tokens/typography.css.js +0 -207
- package/src/tokens/typography.js +0 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { html } from 'lit';
|
|
2
|
+
import {
|
|
3
|
+
discoverConsumers,
|
|
4
|
+
docsPage,
|
|
5
|
+
primitiveTable,
|
|
6
|
+
readAllCssText,
|
|
7
|
+
refSection,
|
|
8
|
+
referenceNote,
|
|
9
|
+
resolve,
|
|
10
|
+
tokensFor
|
|
11
|
+
} from './story-chrome.js';
|
|
12
|
+
|
|
13
|
+
export default {
|
|
14
|
+
title: 'Foundations/Dimension',
|
|
15
|
+
tags : ['autodocs']
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const dimensionStyles = html`
|
|
19
|
+
<style>
|
|
20
|
+
.rule {
|
|
21
|
+
height: var(--spacing-12);
|
|
22
|
+
background: var(--color-blue-500);
|
|
23
|
+
border-radius: var(--radius-sm);
|
|
24
|
+
min-width: 1px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.multiple {
|
|
28
|
+
font-family: var(--typography-fontFamily-mono);
|
|
29
|
+
font-size: var(--typography-fontSize-xxs);
|
|
30
|
+
color: var(--color-neutral-500);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.multiple[data-of4='false'] {
|
|
34
|
+
color: var(--color-neutral-900);
|
|
35
|
+
font-weight: var(--typography-fontWeight-semibold);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.callout {
|
|
39
|
+
font-size: var(--typography-fontSize-xs);
|
|
40
|
+
line-height: var(--typography-lineHeight-normal);
|
|
41
|
+
color: var(--color-neutral-700);
|
|
42
|
+
background: var(--color-neutral-50);
|
|
43
|
+
border-radius: var(--radius-md);
|
|
44
|
+
padding: var(--spacing-12) var(--spacing-16);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.callout code {
|
|
48
|
+
font-family: var(--typography-fontFamily-mono);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* The guide line must cross the boxes at the target height, so the boxes
|
|
52
|
+
and the line share one positioned container and both sit on its floor. */
|
|
53
|
+
.target-zone {
|
|
54
|
+
position: relative;
|
|
55
|
+
padding-top: var(--spacing-24);
|
|
56
|
+
overflow-x: auto;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.target-demo {
|
|
60
|
+
display: flex;
|
|
61
|
+
align-items: flex-end;
|
|
62
|
+
gap: var(--spacing-16);
|
|
63
|
+
flex-wrap: nowrap;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.guide {
|
|
67
|
+
position: absolute;
|
|
68
|
+
left: 0;
|
|
69
|
+
right: 0;
|
|
70
|
+
border-top: var(--border-width-2) dashed var(--color-red-500);
|
|
71
|
+
pointer-events: none;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.guide span {
|
|
75
|
+
position: absolute;
|
|
76
|
+
right: 0;
|
|
77
|
+
top: calc(-1 * var(--spacing-18));
|
|
78
|
+
font-family: var(--typography-fontFamily-mono);
|
|
79
|
+
font-size: var(--typography-fontSize-xxs);
|
|
80
|
+
color: var(--color-red-800);
|
|
81
|
+
background: var(--color-neutral-25);
|
|
82
|
+
padding: 0 var(--spacing-4);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.target {
|
|
86
|
+
display: flex;
|
|
87
|
+
align-items: center;
|
|
88
|
+
justify-content: center;
|
|
89
|
+
background: var(--color-blue-100);
|
|
90
|
+
border: var(--border-width-1) solid var(--color-blue-500);
|
|
91
|
+
border-radius: var(--radius-md);
|
|
92
|
+
font-family: var(--typography-fontFamily-mono);
|
|
93
|
+
font-size: var(--typography-fontSize-xxs);
|
|
94
|
+
color: var(--color-neutral-900);
|
|
95
|
+
padding: 0 var(--spacing-8);
|
|
96
|
+
white-space: nowrap;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.target--under {
|
|
100
|
+
background: var(--color-red-50);
|
|
101
|
+
border-color: var(--color-red-500);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
</style>
|
|
105
|
+
`;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Purely factual: is the value a multiple of 4?
|
|
109
|
+
*
|
|
110
|
+
* The dimension scale never claims a 4px grid — that claim belonged to the
|
|
111
|
+
* --space-* scale, which is gone. So this column
|
|
112
|
+
* reports, it does not judge. What is worth noticing is which values are not
|
|
113
|
+
* multiples of 4, and that is called out under the table.
|
|
114
|
+
*/
|
|
115
|
+
function multipleOfFour(value) {
|
|
116
|
+
const px = parseFloat(value);
|
|
117
|
+
if (!Number.isFinite(px)) return html``;
|
|
118
|
+
const isMultiple = px % 4 === 0;
|
|
119
|
+
return html`<span class="multiple" data-of4=${isMultiple}>${isMultiple ? 'yes' : 'no'}</span>`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export const Dimensions = () => {
|
|
123
|
+
const cssText = readAllCssText();
|
|
124
|
+
const consumers = discoverConsumers(cssText);
|
|
125
|
+
const dimensions = tokensFor(cssText, 'dimension-');
|
|
126
|
+
const max = Math.max(...dimensions.map(token => parseFloat(resolve(token)) || 0), 1);
|
|
127
|
+
|
|
128
|
+
// any step that is not a multiple of 4 — should be empty now that the scale is aligned
|
|
129
|
+
const offGrid = dimensions
|
|
130
|
+
.map(token => ({ token, px: parseFloat(resolve(token)) || 0 }))
|
|
131
|
+
.filter(({ px }) => px % 4 !== 0);
|
|
132
|
+
|
|
133
|
+
return html`
|
|
134
|
+
${dimensionStyles}
|
|
135
|
+
${docsPage({
|
|
136
|
+
title: 'Dimension',
|
|
137
|
+
lede : html`Fixed sizes for control heights and icon boxes — button and input heights, icon
|
|
138
|
+
squares. Unlike spacing, these are not a rhythm scale; each value exists because a specific
|
|
139
|
+
control needs it.`,
|
|
140
|
+
note: html`The names embed the value (<code>--dimension-36</code>), so changing a value
|
|
141
|
+
means renaming the token or leaving the name lying. There is no semantic height alias
|
|
142
|
+
to prefer instead: the <code>--button-medium-*</code> family covers fontSize, padding,
|
|
143
|
+
gap and borderRadius and deliberately has no height, because buttons here are
|
|
144
|
+
padding-driven and have no height at all. Form controls are the ones with a height
|
|
145
|
+
contract, and <code>form-field.css.js</code> derives it — padding plus fontSize times
|
|
146
|
+
lineHeight — so the box and the text it holds cannot disagree. Copy that shape rather
|
|
147
|
+
than reaching for a height token. ${referenceNote}`,
|
|
148
|
+
children: html`
|
|
149
|
+
${refSection({
|
|
150
|
+
name : 'Scale',
|
|
151
|
+
meta : `${dimensions.length} steps · --dimension-*`,
|
|
152
|
+
children: primitiveTable({
|
|
153
|
+
caption : 'Dimension scale',
|
|
154
|
+
tokens : dimensions,
|
|
155
|
+
consumers,
|
|
156
|
+
visualHeader: 'Multiple of 4',
|
|
157
|
+
visual : (_token, value) => html`
|
|
158
|
+
<div style="display: flex; align-items: center; gap: var(--spacing-8)">
|
|
159
|
+
<div class="rule" style="width: ${((parseFloat(value) || 0) / max) * 120}px"></div>
|
|
160
|
+
${multipleOfFour(value)}
|
|
161
|
+
</div>
|
|
162
|
+
`
|
|
163
|
+
})
|
|
164
|
+
})}
|
|
165
|
+
${refSection({
|
|
166
|
+
name : 'Grid alignment',
|
|
167
|
+
children: html`
|
|
168
|
+
<div class="callout">
|
|
169
|
+
<p style="margin: 0">
|
|
170
|
+
${offGrid.length
|
|
171
|
+
? html`<b>${offGrid.map(({ px }) => `${px}px`).join(', ')}</b> ${offGrid.length === 1 ? 'is' : 'are'}
|
|
172
|
+
not ${offGrid.length === 1 ? 'a multiple' : 'multiples'} of 4. Every other step is.`
|
|
173
|
+
: html`Every step is a multiple of 4, and each token is named after its own
|
|
174
|
+
value. The control heights were <code>34px</code>, <code>45px</code> and
|
|
175
|
+
<code>55px</code> before this scale was aligned; they are now
|
|
176
|
+
<code>36px</code>, <code>44px</code> and <code>56px</code>, which also puts
|
|
177
|
+
the medium control exactly on the 44px WCAG 2.5.5 target.`}
|
|
178
|
+
</p>
|
|
179
|
+
</div>
|
|
180
|
+
`
|
|
181
|
+
})}
|
|
182
|
+
`
|
|
183
|
+
})}
|
|
184
|
+
`;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
export const TouchTargets = () => {
|
|
188
|
+
const cssText = readAllCssText();
|
|
189
|
+
const consumers = discoverConsumers(cssText);
|
|
190
|
+
const touch = tokensFor(cssText, 'touch-');
|
|
191
|
+
|
|
192
|
+
const minimum = parseFloat(resolve('--touch-target-size')) || 44;
|
|
193
|
+
const controls = [
|
|
194
|
+
['--dimension-36', 'button/input small'],
|
|
195
|
+
['--dimension-44', 'button/input medium'],
|
|
196
|
+
['--dimension-56', 'button/input large']
|
|
197
|
+
];
|
|
198
|
+
|
|
199
|
+
return html`
|
|
200
|
+
${dimensionStyles}
|
|
201
|
+
${docsPage({
|
|
202
|
+
title: 'Touch targets',
|
|
203
|
+
lede : html`<code>--touch-target-size</code> is the minimum hit area for a pointer or finger,
|
|
204
|
+
and <code>--touch-target-spacing</code> the minimum gap between two of them.`,
|
|
205
|
+
note: html`WCAG 2.2 has two thresholds: <b>2.5.8 Target Size (Minimum)</b> is level AA and
|
|
206
|
+
asks for <b>24×24</b> CSS px; <b>2.5.5 Target Size (Enhanced)</b> is level AAA and asks for
|
|
207
|
+
<b>44×44</b>. This token encodes the AAA figure. ${referenceNote}`,
|
|
208
|
+
children: html`
|
|
209
|
+
${refSection({
|
|
210
|
+
name : 'Tokens',
|
|
211
|
+
meta : `${touch.length} tokens · --touch-*`,
|
|
212
|
+
children: primitiveTable({ caption: 'Touch target tokens', tokens: touch, consumers })
|
|
213
|
+
})}
|
|
214
|
+
${refSection({
|
|
215
|
+
name : 'Control heights against the target',
|
|
216
|
+
meta : `dashed line = --touch-target-size (${minimum}px)`,
|
|
217
|
+
children: html`
|
|
218
|
+
<div class="specimen">
|
|
219
|
+
<div class="target-zone">
|
|
220
|
+
<div class="target-demo">
|
|
221
|
+
${controls.map(([token, label]) => {
|
|
222
|
+
const height = parseFloat(resolve(token)) || 0;
|
|
223
|
+
const under = height < minimum;
|
|
224
|
+
return html`
|
|
225
|
+
<div
|
|
226
|
+
class="target ${under ? 'target--under' : ''}"
|
|
227
|
+
style="height: ${height}px"
|
|
228
|
+
title="${label}"
|
|
229
|
+
>
|
|
230
|
+
${height}px
|
|
231
|
+
</div>
|
|
232
|
+
`;
|
|
233
|
+
})}
|
|
234
|
+
</div>
|
|
235
|
+
<div class="guide" style="bottom: ${minimum}px">
|
|
236
|
+
<span>${minimum}px — WCAG 2.5.5 (AAA)</span>
|
|
237
|
+
</div>
|
|
238
|
+
</div>
|
|
239
|
+
<p class="docs-lede" style="margin-top: var(--spacing-16)">
|
|
240
|
+
The small control height is
|
|
241
|
+
<b>${resolve('--dimension-36')}</b>, below the ${minimum}px AAA target but above
|
|
242
|
+
the 24px AA minimum. Where a small control is the only way to perform an action,
|
|
243
|
+
give it padding or spacing so the hit area still reaches ${minimum}px.
|
|
244
|
+
</p>
|
|
245
|
+
</div>
|
|
246
|
+
`
|
|
247
|
+
})}
|
|
248
|
+
`
|
|
249
|
+
})}
|
|
250
|
+
`;
|
|
251
|
+
};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { html } from 'lit';
|
|
2
|
+
import {
|
|
3
|
+
discoverConsumers,
|
|
4
|
+
docsPage,
|
|
5
|
+
primitiveTable,
|
|
6
|
+
readAllCssText,
|
|
7
|
+
refSection,
|
|
8
|
+
referenceNote,
|
|
9
|
+
tokensFor
|
|
10
|
+
} from './story-chrome.js';
|
|
11
|
+
|
|
12
|
+
export default {
|
|
13
|
+
title: 'Foundations/Elevation',
|
|
14
|
+
tags : ['autodocs']
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const elevationStyles = html`
|
|
18
|
+
<style>
|
|
19
|
+
.shadow-chip {
|
|
20
|
+
width: var(--spacing-64);
|
|
21
|
+
height: var(--spacing-40);
|
|
22
|
+
background: var(--color-neutral-0);
|
|
23
|
+
border-radius: var(--radius-md);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* shadows need room around them or the blur is clipped */
|
|
27
|
+
.shadow-cell {
|
|
28
|
+
padding: var(--spacing-12) var(--spacing-8);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.ladder {
|
|
32
|
+
display: flex;
|
|
33
|
+
flex-wrap: wrap;
|
|
34
|
+
gap: var(--spacing-32);
|
|
35
|
+
background: var(--color-neutral-50);
|
|
36
|
+
padding: var(--spacing-32);
|
|
37
|
+
border-radius: var(--radius-md);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.ladder figure {
|
|
41
|
+
margin: 0;
|
|
42
|
+
text-align: center;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.ladder .card {
|
|
46
|
+
width: var(--spacing-96);
|
|
47
|
+
height: var(--spacing-64);
|
|
48
|
+
background: var(--color-neutral-0);
|
|
49
|
+
border-radius: var(--radius-lg);
|
|
50
|
+
margin-bottom: var(--spacing-8);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.ladder figcaption {
|
|
54
|
+
font-family: var(--typography-fontFamily-mono);
|
|
55
|
+
font-size: var(--typography-fontSize-xxs);
|
|
56
|
+
color: var(--color-neutral-600);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.value {
|
|
60
|
+
font-family: var(--typography-fontFamily-mono);
|
|
61
|
+
font-size: var(--typography-fontSize-xxs);
|
|
62
|
+
color: var(--color-neutral-600);
|
|
63
|
+
white-space: normal;
|
|
64
|
+
max-width: 32ch;
|
|
65
|
+
line-height: var(--typography-lineHeight-normal);
|
|
66
|
+
}
|
|
67
|
+
</style>
|
|
68
|
+
`;
|
|
69
|
+
|
|
70
|
+
export const Shadows = () => {
|
|
71
|
+
const cssText = readAllCssText();
|
|
72
|
+
const consumers = discoverConsumers(cssText);
|
|
73
|
+
|
|
74
|
+
const all = tokensFor(cssText, 'shadow-');
|
|
75
|
+
const surface = all.filter(token => !token.includes('focus'));
|
|
76
|
+
const rings = all.filter(token => token.includes('focus'));
|
|
77
|
+
|
|
78
|
+
const ladder = ['--shadow-sm', '--shadow-md', '--shadow-lg', '--shadow-xl', '--shadow-2xl'];
|
|
79
|
+
|
|
80
|
+
return html`
|
|
81
|
+
${elevationStyles}
|
|
82
|
+
${docsPage({
|
|
83
|
+
title: 'Elevation',
|
|
84
|
+
lede : html`Shadows are the only elevation mechanism in the system — there is no z-index
|
|
85
|
+
scale. <code>sm</code> through <code>2xl</code> form a ladder;
|
|
86
|
+
<code>--shadow-inner</code> is an inset press state.`,
|
|
87
|
+
note : referenceNote,
|
|
88
|
+
children: html`
|
|
89
|
+
${refSection({
|
|
90
|
+
name : 'Surface shadows',
|
|
91
|
+
meta : `${surface.length} tokens · --shadow-*`,
|
|
92
|
+
children: primitiveTable({
|
|
93
|
+
caption : 'Surface shadow tokens',
|
|
94
|
+
tokens : surface,
|
|
95
|
+
consumers,
|
|
96
|
+
valueClass : 'value',
|
|
97
|
+
visualHeader: 'Preview',
|
|
98
|
+
visual : token =>
|
|
99
|
+
html`<div class="shadow-cell">
|
|
100
|
+
<div class="shadow-chip" style="box-shadow: var(${token})"></div>
|
|
101
|
+
</div>`
|
|
102
|
+
})
|
|
103
|
+
})}
|
|
104
|
+
${refSection({
|
|
105
|
+
name : 'The ladder on a tinted surface',
|
|
106
|
+
meta : 'sm → 2xl',
|
|
107
|
+
children: html`
|
|
108
|
+
<div class="ladder">
|
|
109
|
+
${ladder.map(
|
|
110
|
+
token => html`
|
|
111
|
+
<figure>
|
|
112
|
+
<div class="card" style="box-shadow: var(${token})"></div>
|
|
113
|
+
<figcaption>${token.replace('--shadow-', '')}</figcaption>
|
|
114
|
+
</figure>
|
|
115
|
+
`
|
|
116
|
+
)}
|
|
117
|
+
</div>
|
|
118
|
+
`
|
|
119
|
+
})}
|
|
120
|
+
${refSection({
|
|
121
|
+
name : 'Focus ring shadows',
|
|
122
|
+
meta : `${rings.length} tokens · --shadow-focus-outline-*`,
|
|
123
|
+
children: html`
|
|
124
|
+
<p class="docs-lede">
|
|
125
|
+
Soft 3px rings at 15% alpha. These are decorative glows, not accessible focus
|
|
126
|
+
indicators — see the <b>Focus</b> page for the rings that meet contrast requirements.
|
|
127
|
+
</p>
|
|
128
|
+
${primitiveTable({
|
|
129
|
+
caption : 'Focus ring shadow tokens',
|
|
130
|
+
tokens : rings,
|
|
131
|
+
consumers,
|
|
132
|
+
valueClass: 'value',
|
|
133
|
+
visual : token =>
|
|
134
|
+
html`<div class="shadow-cell">
|
|
135
|
+
<div class="shadow-chip" style="box-shadow: var(${token})"></div>
|
|
136
|
+
</div>`
|
|
137
|
+
})}
|
|
138
|
+
`
|
|
139
|
+
})}
|
|
140
|
+
`
|
|
141
|
+
})}
|
|
142
|
+
`;
|
|
143
|
+
};
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { html } from 'lit';
|
|
2
|
+
import {
|
|
3
|
+
INK,
|
|
4
|
+
PAPER,
|
|
5
|
+
contrast,
|
|
6
|
+
discoverConsumers,
|
|
7
|
+
docsPage,
|
|
8
|
+
levelBadge,
|
|
9
|
+
primitiveTable,
|
|
10
|
+
readAllCssText,
|
|
11
|
+
refSection,
|
|
12
|
+
referenceNote,
|
|
13
|
+
resolve,
|
|
14
|
+
tokensFor
|
|
15
|
+
} from './story-chrome.js';
|
|
16
|
+
|
|
17
|
+
export default {
|
|
18
|
+
title: 'Foundations/Focus',
|
|
19
|
+
tags : ['autodocs']
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const focusStyles = html`
|
|
23
|
+
<style>
|
|
24
|
+
.rule-note {
|
|
25
|
+
margin: 0 0 var(--spacing-12);
|
|
26
|
+
font-size: var(--typography-fontSize-sm);
|
|
27
|
+
line-height: var(--typography-lineHeight-normal);
|
|
28
|
+
color: var(--color-neutral-900);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.rule-note:last-child { margin-bottom: 0; }
|
|
32
|
+
|
|
33
|
+
.rule-code {
|
|
34
|
+
margin: 0 0 var(--spacing-16);
|
|
35
|
+
padding: var(--spacing-12) var(--spacing-16);
|
|
36
|
+
background: var(--color-neutral-50);
|
|
37
|
+
border-radius: var(--radius-md);
|
|
38
|
+
font-family: var(--typography-fontFamily-mono);
|
|
39
|
+
font-size: var(--typography-fontSize-xs);
|
|
40
|
+
line-height: var(--typography-lineHeight-relaxed);
|
|
41
|
+
overflow-x: auto;
|
|
42
|
+
}
|
|
43
|
+
.focus-demo {
|
|
44
|
+
display: flex;
|
|
45
|
+
flex-wrap: wrap;
|
|
46
|
+
gap: var(--spacing-24);
|
|
47
|
+
align-items: center;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.focus-demo--dark {
|
|
51
|
+
background: var(--color-neutral-900);
|
|
52
|
+
padding: var(--spacing-24);
|
|
53
|
+
border-radius: var(--radius-md);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* Rings are shown permanently so they can be compared without tabbing. */
|
|
57
|
+
.ring {
|
|
58
|
+
font-family: var(--typography-fontFamily-sans);
|
|
59
|
+
font-size: var(--typography-fontSize-sm);
|
|
60
|
+
font-weight: var(--typography-fontWeight-medium);
|
|
61
|
+
padding: var(--spacing-8) var(--spacing-16);
|
|
62
|
+
border: var(--border-width-1) solid var(--color-neutral-300);
|
|
63
|
+
border-radius: var(--radius-md);
|
|
64
|
+
background: var(--color-neutral-0);
|
|
65
|
+
color: var(--color-neutral-900);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.ring-label {
|
|
69
|
+
font-family: var(--typography-fontFamily-mono);
|
|
70
|
+
font-size: var(--typography-fontSize-xxs);
|
|
71
|
+
color: var(--color-neutral-600);
|
|
72
|
+
display: block;
|
|
73
|
+
margin-bottom: var(--spacing-6);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.focus-demo--dark .ring-label {
|
|
77
|
+
color: var(--color-neutral-300);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* A genuinely interactive control, so the real :focus-visible can be tested. */
|
|
81
|
+
.try-me {
|
|
82
|
+
font: inherit;
|
|
83
|
+
font-size: var(--typography-fontSize-sm);
|
|
84
|
+
padding: var(--spacing-8) var(--spacing-16);
|
|
85
|
+
border: var(--border-width-1) solid var(--color-neutral-300);
|
|
86
|
+
border-radius: var(--radius-md);
|
|
87
|
+
background: var(--color-neutral-0);
|
|
88
|
+
cursor: pointer;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.try-me:focus-visible {
|
|
92
|
+
outline: var(--focus-outline-traditional);
|
|
93
|
+
outline-offset: var(--focus-outline-offset);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.try-me--shadow:focus-visible {
|
|
97
|
+
outline: none;
|
|
98
|
+
box-shadow: var(--focus-outline-primary);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.value {
|
|
102
|
+
font-family: var(--typography-fontFamily-mono);
|
|
103
|
+
font-size: var(--typography-fontSize-xxs);
|
|
104
|
+
color: var(--color-neutral-600);
|
|
105
|
+
white-space: normal;
|
|
106
|
+
max-width: 34ch;
|
|
107
|
+
line-height: var(--typography-lineHeight-normal);
|
|
108
|
+
}
|
|
109
|
+
</style>
|
|
110
|
+
`;
|
|
111
|
+
|
|
112
|
+
export const FocusRings = () => {
|
|
113
|
+
const cssText = readAllCssText();
|
|
114
|
+
const consumers = discoverConsumers(cssText);
|
|
115
|
+
const tokens = tokensFor(cssText, 'focus-');
|
|
116
|
+
|
|
117
|
+
// -offset and -radius are lengths, not rings; they would render as empty swatches
|
|
118
|
+
const NOT_RINGS = ['--focus-outline-offset', '--focus-outline-radius'];
|
|
119
|
+
const rings = tokens.filter(token => token.startsWith('--focus-outline-') && !NOT_RINGS.includes(token));
|
|
120
|
+
const ringColour = resolve('--color-blue-500');
|
|
121
|
+
const onLight = contrast(ringColour, PAPER);
|
|
122
|
+
const onDark = contrast(ringColour, INK);
|
|
123
|
+
|
|
124
|
+
return html`
|
|
125
|
+
${focusStyles}
|
|
126
|
+
${docsPage({
|
|
127
|
+
title: 'Focus',
|
|
128
|
+
lede : html`<code>--focus-outline-primary</code> is <b>the</b> focus ring — every interactive
|
|
129
|
+
component uses it and none define their own. It is a two-colour ring: an
|
|
130
|
+
inner band of <code>--color-neutral-0</code> and an outer band of the state colour. The
|
|
131
|
+
inner band is what makes the ring visible on both light and dark backgrounds.
|
|
132
|
+
<br /><br />
|
|
133
|
+
The ring is a <code>box-shadow</code>, so it already follows the radius of whatever it
|
|
134
|
+
sits on — a component with its own radius should set nothing and let the ring take its
|
|
135
|
+
shape. <code>--focus-outline-radius</code> is the default for the ones that have no
|
|
136
|
+
radius of their own, so the ring is not a hard rectangle around a link or a tab.`,
|
|
137
|
+
note: html`WCAG 2.2 <b>1.4.11 Non-text Contrast</b> asks for <b>3:1</b> between a focus
|
|
138
|
+
indicator and its adjacent background. The blue ring is
|
|
139
|
+
${onLight.toFixed(2)}${levelBadge(onLight)} on white and
|
|
140
|
+
${onDark.toFixed(2)}${levelBadge(onDark)} on <code>--color-neutral-900</code> — which is
|
|
141
|
+
exactly why the white inner band matters on dark surfaces. ${referenceNote}`,
|
|
142
|
+
children: html`
|
|
143
|
+
${refSection({
|
|
144
|
+
name : 'The rule',
|
|
145
|
+
meta : 'one ring, every interactive component',
|
|
146
|
+
children: html`
|
|
147
|
+
<div class="specimen">
|
|
148
|
+
<p class="rule-note">
|
|
149
|
+
Every interactive component shows the same ring on keyboard focus,
|
|
150
|
+
and it is two lines:
|
|
151
|
+
</p>
|
|
152
|
+
<pre class="rule-code"><code>.control:focus-visible {
|
|
153
|
+
outline: none;
|
|
154
|
+
box-shadow: var(--focus-outline-primary);
|
|
155
|
+
}</code></pre>
|
|
156
|
+
<p class="rule-note">
|
|
157
|
+
<b>Always <code>:focus-visible</code>, never <code>:focus</code>.</b>
|
|
158
|
+
<code>:focus</code> fires on a mouse click too, which rings
|
|
159
|
+
something the user just clicked. The browser already decides
|
|
160
|
+
keyboard modality — do not reimplement it. bd-button used to,
|
|
161
|
+
with two requestAnimationFrame hops and a data attribute.
|
|
162
|
+
</p>
|
|
163
|
+
<p class="rule-note">
|
|
164
|
+
<b>Never suppress it without replacing it.</b>
|
|
165
|
+
<code>outline: none</code> on its own removes the only cue a
|
|
166
|
+
keyboard user has for where they are, which fails WCAG 2.4.7. Two
|
|
167
|
+
accordion triggers did exactly that.
|
|
168
|
+
</p>
|
|
169
|
+
<p class="rule-note">
|
|
170
|
+
<b>Text fields are the one exception.</b> Inputs, textareas and
|
|
171
|
+
selects keep the field border and add
|
|
172
|
+
<code>--shadow-focus-outline-blue</code> as a glow, because an
|
|
173
|
+
offset ring around a full-width field reads as a second border.
|
|
174
|
+
Everything else you can operate — buttons, links, checkboxes,
|
|
175
|
+
radios, switches, tabs, accordion triggers, the scrollable code
|
|
176
|
+
block — uses the ring.
|
|
177
|
+
</p>
|
|
178
|
+
</div>
|
|
179
|
+
`
|
|
180
|
+
})}
|
|
181
|
+
${refSection({
|
|
182
|
+
name : 'Tokens',
|
|
183
|
+
meta : `${tokens.length} tokens · --focus-*`,
|
|
184
|
+
children: primitiveTable({
|
|
185
|
+
caption : 'Focus tokens',
|
|
186
|
+
tokens,
|
|
187
|
+
consumers,
|
|
188
|
+
valueClass: 'value'
|
|
189
|
+
})
|
|
190
|
+
})}
|
|
191
|
+
${refSection({
|
|
192
|
+
name : 'On a light surface',
|
|
193
|
+
meta : 'rings shown permanently for comparison',
|
|
194
|
+
children: html`
|
|
195
|
+
<div class="specimen">
|
|
196
|
+
<div class="focus-demo">
|
|
197
|
+
${rings.map(
|
|
198
|
+
token => html`
|
|
199
|
+
<div>
|
|
200
|
+
<span class="ring-label">${token.replace('--focus-outline-', '')}</span>
|
|
201
|
+
<div class="ring" style="box-shadow: var(${token})">Button</div>
|
|
202
|
+
</div>
|
|
203
|
+
`
|
|
204
|
+
)}
|
|
205
|
+
</div>
|
|
206
|
+
</div>
|
|
207
|
+
`
|
|
208
|
+
})}
|
|
209
|
+
${refSection({
|
|
210
|
+
name : 'On a dark surface',
|
|
211
|
+
meta : 'the white inner band keeps the ring readable',
|
|
212
|
+
children: html`
|
|
213
|
+
<div class="focus-demo focus-demo--dark">
|
|
214
|
+
${rings.map(
|
|
215
|
+
token => html`
|
|
216
|
+
<div>
|
|
217
|
+
<span class="ring-label">${token.replace('--focus-outline-', '')}</span>
|
|
218
|
+
<div class="ring" style="box-shadow: var(${token})">Button</div>
|
|
219
|
+
</div>
|
|
220
|
+
`
|
|
221
|
+
)}
|
|
222
|
+
</div>
|
|
223
|
+
`
|
|
224
|
+
})}
|
|
225
|
+
${refSection({
|
|
226
|
+
name : 'Try it',
|
|
227
|
+
meta : 'tab to these — real :focus-visible, not a simulation',
|
|
228
|
+
children: html`
|
|
229
|
+
<div class="specimen">
|
|
230
|
+
<div class="focus-demo">
|
|
231
|
+
<button class="try-me">outline (traditional)</button>
|
|
232
|
+
<button class="try-me try-me--shadow">box-shadow (two-colour)</button>
|
|
233
|
+
</div>
|
|
234
|
+
</div>
|
|
235
|
+
`
|
|
236
|
+
})}
|
|
237
|
+
${refSection({
|
|
238
|
+
name : 'The ring owns its corner',
|
|
239
|
+
meta : 'one place to change it',
|
|
240
|
+
children: html`
|
|
241
|
+
<p>
|
|
242
|
+
The ring is a <code>box-shadow</code>, so it already follows the corner of whatever
|
|
243
|
+
it is on. <code>--focus-outline-radius</code> is for the elements that have no
|
|
244
|
+
corner of their own, and it is the only place a standard ring should get one.
|
|
245
|
+
Five components used to set <code>--radius-lg</code> here instead, so the same
|
|
246
|
+
indicator drew an 8px corner in five places and a 4px corner in fifteen. They
|
|
247
|
+
resolve through the role now, which makes the radius one decision in one place
|
|
248
|
+
rather than five — <code>focus-radius-bypass</code> in
|
|
249
|
+
<code>npm run lint:tokens</code> fails on a new one.
|
|
250
|
+
</p>
|
|
251
|
+
<p>
|
|
252
|
+
<b>The exception is shape, not preference.</b> A circular control needs a circular
|
|
253
|
+
ring, so <code>--radius-full</code> and <code>50%</code> are allowed and the
|
|
254
|
+
AwardsFlex dot uses one. The rule knows about that case; a new kind of exception
|
|
255
|
+
belongs in the rule with a self-test, not in a comment.
|
|
256
|
+
</p>
|
|
257
|
+
<p>
|
|
258
|
+
<b>A ring that is clipped is not an indicator.</b> It is drawn outside the box, so
|
|
259
|
+
an ancestor with <code>overflow: hidden</code> can cut it off entirely while every
|
|
260
|
+
static check still passes. Leave at least 4px of room inside any clipping ancestor,
|
|
261
|
+
or move the clipping.
|
|
262
|
+
</p>
|
|
263
|
+
<p>
|
|
264
|
+
<b>Verify with the keyboard, not with code.</b> Calling
|
|
265
|
+
<code>element.focus()</code> does not reliably match
|
|
266
|
+
<code>:focus-visible</code>, so a programmatic check can report a ring that a
|
|
267
|
+
reader never sees — and can equally miss one that is broken. Press Tab. Two real
|
|
268
|
+
defects in this package were invisible to programmatic focus and obvious to a
|
|
269
|
+
keyboard: the header's ring never reached its slotted links, and one accordion
|
|
270
|
+
variant drew a different colour from the other.
|
|
271
|
+
</p>`
|
|
272
|
+
})}
|
|
273
|
+
`
|
|
274
|
+
})}
|
|
275
|
+
`;
|
|
276
|
+
};
|