@repobit/dex-system-design 0.25.11 → 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 +195 -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,206 @@
|
|
|
1
|
+
import { css } from "lit";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The shared chrome for a form field: its label, the control itself, its three
|
|
5
|
+
* sizes, its states and its help text.
|
|
6
|
+
*
|
|
7
|
+
* bd-custom-input, bd-custom-dropdown and bd-custom-input-clipboard are one
|
|
8
|
+
* field with three kinds of control in the middle. They used to be three
|
|
9
|
+
* stylesheets of about 160 lines each, roughly 130 of them identical, and they
|
|
10
|
+
* had drifted: the select set its labels and help text at line-height normal
|
|
11
|
+
* where the other two used tight, and its `md` size declared 8px of vertical
|
|
12
|
+
* padding while the height formula on the line below assumed 12. Every fix had
|
|
13
|
+
* to be applied three times and demonstrably had not been.
|
|
14
|
+
*
|
|
15
|
+
* This is the same arrangement radio.css.js already uses across bd-radio and
|
|
16
|
+
* bd-toggle-radio. A component imports this first and then its own sheet:
|
|
17
|
+
*
|
|
18
|
+
* static styles = [formFieldCSS, dropdownCSS];
|
|
19
|
+
*
|
|
20
|
+
* Values come from the --form-input-* and --form-label-* component tokens
|
|
21
|
+
* rather than from the palette, which is what bd-textarea was already doing
|
|
22
|
+
* and what lets a single token change reach all four controls.
|
|
23
|
+
*/
|
|
24
|
+
export const formFieldCSS = css`
|
|
25
|
+
/* No width. This used to be 90%, an arbitrary fraction that made every field
|
|
26
|
+
narrower than whatever contained it and left the last tenth unexplained.
|
|
27
|
+
A field is display:block and fills its container; deciding how wide that
|
|
28
|
+
container is belongs to the page. */
|
|
29
|
+
:host {
|
|
30
|
+
display: block;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.input-container {
|
|
34
|
+
display: flex;
|
|
35
|
+
flex-direction: column;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* ---- label ------------------------------------------------------------
|
|
39
|
+
Driven by --form-label-*, the same tokens bd-label reads, so the field's
|
|
40
|
+
built-in label and the standalone atom render identically. They did not
|
|
41
|
+
before: these rules hardcoded bold where the token says medium, which is
|
|
42
|
+
200 units apart on the same line of the same form. */
|
|
43
|
+
.form-label {
|
|
44
|
+
display: block;
|
|
45
|
+
font-family: var(--form-label-default-fontFamily);
|
|
46
|
+
color: var(--form-label-default-color);
|
|
47
|
+
letter-spacing: var(--form-label-default-letterSpacing);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.label-sm {
|
|
51
|
+
font-size: var(--form-label-small-fontSize);
|
|
52
|
+
font-weight: var(--form-label-small-fontWeight);
|
|
53
|
+
line-height: var(--form-label-small-lineHeight);
|
|
54
|
+
letter-spacing: var(--form-label-small-letterSpacing);
|
|
55
|
+
margin-bottom: var(--form-label-small-marginBottom);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.label-md {
|
|
59
|
+
font-size: var(--form-label-default-fontSize);
|
|
60
|
+
font-weight: var(--form-label-default-fontWeight);
|
|
61
|
+
line-height: var(--form-label-default-lineHeight);
|
|
62
|
+
margin-bottom: var(--form-label-default-marginBottom);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.label-lg {
|
|
66
|
+
font-size: var(--form-label-large-fontSize);
|
|
67
|
+
font-weight: var(--form-label-large-fontWeight);
|
|
68
|
+
line-height: var(--form-label-large-lineHeight);
|
|
69
|
+
margin-bottom: var(--form-label-large-marginBottom);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.form-label__required {
|
|
73
|
+
color: var(--form-label-required-color);
|
|
74
|
+
margin-left: var(--form-label-required-marginLeft);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* ---- the control ------------------------------------------------------
|
|
78
|
+
line-height is tight here and only here. The height below is derived from
|
|
79
|
+
padding, type step and line-height, and bd-button derives its own from the
|
|
80
|
+
same three, which is what keeps a field and a button of the same size the
|
|
81
|
+
same height. At normal (1.5) the line box also stood taller than the
|
|
82
|
+
content box the fixed height leaves, and the select clipped the descenders
|
|
83
|
+
of its own option text. */
|
|
84
|
+
.form-input-bd {
|
|
85
|
+
width: 100%;
|
|
86
|
+
box-sizing: border-box;
|
|
87
|
+
font-family: var(--form-input-default-fontFamily);
|
|
88
|
+
/* no role: tight is load-bearing here rather than chosen -- the comment
|
|
89
|
+
above this rule has the argument. The height is DERIVED from padding,
|
|
90
|
+
type step and this leading, so a role's leading would change the
|
|
91
|
+
control's height; and an <input> renders one line, so CLAUDE.md's
|
|
92
|
+
"anything that can wrap needs at least tight" floor is met and its
|
|
93
|
+
concern does not arise. Overridden for the textarea, which does wrap:
|
|
94
|
+
see textarea.css.js. */
|
|
95
|
+
line-height: var(--typography-lineHeight-tight);
|
|
96
|
+
border: var(--form-input-default-borderWidth) solid var(--form-input-default-borderColor);
|
|
97
|
+
background-color: var(--form-input-default-backgroundColor);
|
|
98
|
+
color: var(--form-input-default-color);
|
|
99
|
+
outline: none;
|
|
100
|
+
transition: border-color var(--transition-duration-slow) var(--transition-easing-smooth),
|
|
101
|
+
background-color var(--transition-duration-slow) var(--transition-easing-smooth),
|
|
102
|
+
box-shadow var(--transition-duration-slow) var(--transition-easing-smooth);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* :focus-visible, not :focus. On a text field the two agree — a browser
|
|
106
|
+
shows the indicator for a pointer click there too, because typing is
|
|
107
|
+
expected — but the select is a control you click and walk away from, and
|
|
108
|
+
it was drawing the full ring on every mouse press where bd-button drew
|
|
109
|
+
nothing. .focused is the component's own prop, not a user interaction, so
|
|
110
|
+
it stays unconditional. */
|
|
111
|
+
.form-input-bd:focus-visible,
|
|
112
|
+
.form-input-bd.focused {
|
|
113
|
+
border-color: var(--form-input-focus-borderColor);
|
|
114
|
+
box-shadow: var(--focus-outline-primary);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/* ---- sizes ------------------------------------------------------------
|
|
118
|
+
Four reads of the size's own token, no arithmetic. The height calc used to
|
|
119
|
+
live here in triplicate; it lives in --form-input-*-height now, where the
|
|
120
|
+
value it produces is also the value the package publishes. */
|
|
121
|
+
.input-sm {
|
|
122
|
+
padding: var(--form-input-small-padding);
|
|
123
|
+
font-size: var(--form-input-small-fontSize);
|
|
124
|
+
height: var(--form-input-small-height);
|
|
125
|
+
border-radius: var(--form-input-small-borderRadius);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.input-md {
|
|
129
|
+
padding: var(--form-input-medium-padding);
|
|
130
|
+
font-size: var(--form-input-medium-fontSize);
|
|
131
|
+
height: var(--form-input-medium-height);
|
|
132
|
+
border-radius: var(--form-input-medium-borderRadius);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.input-lg {
|
|
136
|
+
padding: var(--form-input-large-padding);
|
|
137
|
+
font-size: var(--form-input-large-fontSize);
|
|
138
|
+
height: var(--form-input-large-height);
|
|
139
|
+
border-radius: var(--form-input-large-borderRadius);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
::placeholder {
|
|
143
|
+
color: var(--color-text-subtle);
|
|
144
|
+
opacity: 1;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* On a tinted field the placeholder takes the next step up. text-subtle on
|
|
148
|
+
the error tint measured 4.22:1 and on the success tint 4.43 -- under 4.5,
|
|
149
|
+
and an empty required field in error is exactly when the placeholder is
|
|
150
|
+
showing. text-muted gives 5.95 and 6.25. The plain field keeps
|
|
151
|
+
text-subtle (4.81). */
|
|
152
|
+
.form-input--error::placeholder,
|
|
153
|
+
.form-input--success::placeholder {
|
|
154
|
+
color: var(--color-text-muted);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/* ---- states ----------------------------------------------------------- */
|
|
158
|
+
.form-input--error {
|
|
159
|
+
background-color: var(--form-input-error-backgroundColor);
|
|
160
|
+
border-color: var(--form-input-error-borderColor);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.form-input--success {
|
|
164
|
+
background-color: var(--form-input-success-backgroundColor);
|
|
165
|
+
border-color: var(--form-input-success-borderColor);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.form-input--disabled {
|
|
169
|
+
background-color: var(--form-input-disabled-backgroundColor);
|
|
170
|
+
border-color: var(--form-input-disabled-borderColor);
|
|
171
|
+
color: var(--form-input-disabled-color);
|
|
172
|
+
cursor: not-allowed;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/* ---- help text --------------------------------------------------------
|
|
176
|
+
The same roles bd-help-text renders, so a field's built-in message and the
|
|
177
|
+
standalone atom agree on colour and measure. */
|
|
178
|
+
.form-help-text {
|
|
179
|
+
font-family: var(--form-help-text-default-fontFamily);
|
|
180
|
+
font-size: var(--form-help-text-default-fontSize);
|
|
181
|
+
font-weight: var(--form-help-text-default-fontWeight);
|
|
182
|
+
line-height: var(--form-help-text-default-lineHeight);
|
|
183
|
+
letter-spacing: var(--form-help-text-default-letterSpacing);
|
|
184
|
+
margin-top: var(--form-help-text-default-marginTop);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.form-help-text--default {
|
|
188
|
+
color: var(--form-help-text-default-color);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.form-help-text--error {
|
|
192
|
+
color: var(--form-help-text-error-color);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.form-help-text--success {
|
|
196
|
+
color: var(--form-help-text-success-color);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
@media (prefers-reduced-motion: reduce) {
|
|
200
|
+
.form-input-bd {
|
|
201
|
+
transition: none;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
`;
|
|
205
|
+
|
|
206
|
+
export default formFieldCSS;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The words a component prop is allowed to use, and the ones it used to.
|
|
3
|
+
*
|
|
4
|
+
* Three axes, three names, and each name means one thing everywhere:
|
|
5
|
+
*
|
|
6
|
+
* size how big sm · md · lg
|
|
7
|
+
* variant how it looks primary, bullet, shade — per component
|
|
8
|
+
* tone what it means neutral · brand · success · warning · error · info
|
|
9
|
+
*
|
|
10
|
+
* Before this there were three size vocabularies and `kind` meant four
|
|
11
|
+
* unrelated things: the visual variant on bd-button and bd-link, the size on
|
|
12
|
+
* the three form fields, the type step on bd-p and the marker on bd-li. An
|
|
13
|
+
* author could not carry anything learned on one atom to the next.
|
|
14
|
+
*
|
|
15
|
+
* Every old name still resolves. bd-button already did this for the kind
|
|
16
|
+
* values it retired, and that is the pattern the rest now follow — renaming a
|
|
17
|
+
* prop is not worth breaking a consumer's markup over.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/** The one size scale. */
|
|
21
|
+
export const SIZES = ['sm', 'md', 'lg'];
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Size names from before there was one vocabulary. `default` is the
|
|
25
|
+
* interesting one: it named a position rather than a size, so a reader had to
|
|
26
|
+
* know the set to know whether it was smaller or larger than the alternative.
|
|
27
|
+
*/
|
|
28
|
+
export const LEGACY_SIZES = {
|
|
29
|
+
small : 'sm',
|
|
30
|
+
medium : 'md',
|
|
31
|
+
default: 'md',
|
|
32
|
+
large : 'lg'
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Resolves a prop value against its closed set, mapping any legacy spelling
|
|
37
|
+
* first and falling back when the value is not in the set at all. Returning
|
|
38
|
+
* the fallback rather than the raw value is what stops a typo rendering an
|
|
39
|
+
* unstyled component.
|
|
40
|
+
*/
|
|
41
|
+
export function resolveEnum(value, { allowed, legacy = {}, fallback }) {
|
|
42
|
+
const mapped = Object.hasOwn(legacy, value) ? legacy[value] : value;
|
|
43
|
+
return allowed.includes(mapped) ? mapped : fallback;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** The common case: a size prop, with the old spellings still accepted. */
|
|
47
|
+
export const resolveSize = (value, fallback = 'md') =>
|
|
48
|
+
resolveEnum(value, { allowed: SIZES, legacy: LEGACY_SIZES, fallback });
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The closed sets a component prop may draw from, and the only place that
|
|
3
|
+
* turns a prop value into CSS.
|
|
4
|
+
*
|
|
5
|
+
* See CLAUDE.md, "The foundation rule". Every prop-facing function here
|
|
6
|
+
* returns either a token reference or the empty string, so a literal cannot
|
|
7
|
+
* come out of a component prop no matter what a caller passes. An
|
|
8
|
+
* unrecognised value is dropped and the component keeps its own styling.
|
|
9
|
+
*
|
|
10
|
+
* The breakpoint helpers at the foot of this file are the one exception, and
|
|
11
|
+
* they throw rather than returning empty. The reason is who calls them: every
|
|
12
|
+
* scale above is fed a value a *consumer* passed as an attribute, where
|
|
13
|
+
* dropping a bad value degrades gracefully. A breakpoint name is written by
|
|
14
|
+
* the component author inside a stylesheet, and an empty string there yields
|
|
15
|
+
* `@media { ... }` — a malformed query the browser discards silently, taking
|
|
16
|
+
* the whole block with it. A typo has to fail loudly at the point it is made.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export const SPACING_STEPS = [
|
|
20
|
+
'0', '1', '2', '4', '6', '8', '10', '12', '14', '16', '18', '20', '22', '24',
|
|
21
|
+
'28', '32', '36', '40', '44', '48', '52', '56', '64', '80', '96', '112', '128'
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
export const FONT_SIZE_STEPS = [
|
|
25
|
+
'xxs', 'xs', 'sm', 'base', 'lg', 'xl',
|
|
26
|
+
'2xl', '3xl', '4xl', '5xl', '6xl', '7xl', '8xl', '9xl'
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
export const FONT_WEIGHT_STEPS = ['light', 'normal', 'medium', 'semibold', 'bold'];
|
|
30
|
+
|
|
31
|
+
/** The two faces the system ships. There is no third, and no escape hatch. */
|
|
32
|
+
export const FONT_FAMILY_STEPS = ['sans', 'mono'];
|
|
33
|
+
|
|
34
|
+
export const LETTER_SPACING_STEPS = ['tighter', 'tight', 'normal', 'wide', 'wider', 'widest'];
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The t-shirt vocabulary bd-grid's gap and padding props take, mapped onto the
|
|
38
|
+
* live pixel scale. It used to be a second CSS scale, --space-*, which existed
|
|
39
|
+
* mainly so the component could build a token name from a prop — thirteen
|
|
40
|
+
* tokens duplicating --spacing-* under other names. A prop vocabulary belongs
|
|
41
|
+
* here, with the other closed sets, rather than in the token file.
|
|
42
|
+
*/
|
|
43
|
+
export const SPACE_STEPS = {
|
|
44
|
+
none : '0', '4xs': '2', '3xs': '4', '2xs': '8', xs : '12', sm : '16',
|
|
45
|
+
md : '24', lg : '32', xl : '40', '2xl': '48', '3xl': '64', '4xl': '96', '5xl': '128'
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/** Icon sizes. The scale stops at 3xl (64px); there is no step above it. */
|
|
49
|
+
export const ICON_SIZE_STEPS = ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'];
|
|
50
|
+
|
|
51
|
+
export const RADIUS_STEPS = ['none', 'sm', 'md', 'lg', 'xl', '2xl', '3xl', 'full'];
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Semantic colour roles a component's `color` prop may name.
|
|
55
|
+
*
|
|
56
|
+
* These resolve through the --color-text-* roles rather than reaching past
|
|
57
|
+
* them into the palette, which is what the semantic colour block in
|
|
58
|
+
* src/tokens/tokens.js says they should do. Spelled as palette steps, this
|
|
59
|
+
* map was the reason a page could retint --color-text-muted and watch every
|
|
60
|
+
* bd-h with color="muted" ignore it: the values matched, so nothing looked
|
|
61
|
+
* wrong until someone tried to change one.
|
|
62
|
+
*
|
|
63
|
+
* The prop vocabulary and the token vocabulary are deliberately not the same
|
|
64
|
+
* words. `strong` and `primary` read better on a component than
|
|
65
|
+
* `text-primary` and `text-brand` do, and renaming the prop would break
|
|
66
|
+
* consumers' markup for no gain. The mapping is the seam, and it lives here.
|
|
67
|
+
*
|
|
68
|
+
* Every role clears 4.5:1 on white, which is what the docstring here always
|
|
69
|
+
* claimed and what `subtle` did not do: it named neutral-400, which measures
|
|
70
|
+
* 3.23:1 and fails 1.4.3 for text. It now resolves to --color-text-subtle
|
|
71
|
+
* (neutral-500, 4.67:1). Nothing in this repository passed color="subtle",
|
|
72
|
+
* so the fix costs nothing today — note that neutral-400 is the step the
|
|
73
|
+
* token layer calls `disabled`, and disabled text is exempt under 1.4.3,
|
|
74
|
+
* which is presumably how the two got crossed.
|
|
75
|
+
*/
|
|
76
|
+
export const COLOR_ROLES = {
|
|
77
|
+
primary: '--color-text-brand',
|
|
78
|
+
strong : '--color-text-primary',
|
|
79
|
+
muted : '--color-text-muted',
|
|
80
|
+
subtle : '--color-text-subtle',
|
|
81
|
+
inverse: '--color-text-inverse',
|
|
82
|
+
success: '--color-text-success',
|
|
83
|
+
warning: '--color-text-warning',
|
|
84
|
+
error : '--color-text-error'
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const COLOR_ROLE_NAMES = Object.keys(COLOR_ROLES);
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The hairline vocabulary.
|
|
91
|
+
*
|
|
92
|
+
* A divider is a border, and until now the only colour prop it had resolved
|
|
93
|
+
* through COLOR_ROLES above -- the *ink* vocabulary. That is the category
|
|
94
|
+
* error CLAUDE.md names ("Never use a token from the wrong category"), and
|
|
95
|
+
* this file already carried a milder version of it: bd-divider-horizontal's
|
|
96
|
+
* constructor records that its default used to say `subtle`, a text role that
|
|
97
|
+
* matched the right value by coincidence.
|
|
98
|
+
*
|
|
99
|
+
* The cost was not theoretical. bd-quick-answer needs a hairline in
|
|
100
|
+
* --status-brand-border, because its rule sits on a brand tint and a neutral
|
|
101
|
+
* line reads as dirt there. No text role expresses that, so the component drew
|
|
102
|
+
* its own `border-top` instead of composing the atom -- which is exactly the
|
|
103
|
+
* "dividers are not borders" confusion the same document warns about, arrived
|
|
104
|
+
* at from the other direction: the divider could not say what the component
|
|
105
|
+
* meant, so the component stopped using the divider.
|
|
106
|
+
*
|
|
107
|
+
* `brand-subtle` is the one entry that reaches outside the --color-border-*
|
|
108
|
+
* family, and it is here because it has a consumer today. The rest of the
|
|
109
|
+
* status hairlines are deliberately absent until one does.
|
|
110
|
+
*
|
|
111
|
+
* `inverse` is deliberately NOT here. It would resolve to
|
|
112
|
+
* --color-border-container-inverse, which is white at 12%, where the text
|
|
113
|
+
* role it resolves to today is solid white -- and each divider then multiplies
|
|
114
|
+
* that by its own `opacity` prop (0.25 by default on the horizontal, 1.00 on
|
|
115
|
+
* the vertical). The dividers that pass color="inverse" would drop from solid
|
|
116
|
+
* white to 12%, or from 25% to 3%, and effectively vanish. It keeps falling
|
|
117
|
+
* through to the legacy text role below, and that is recorded rather than
|
|
118
|
+
* fixed: whether a hairline on a dark surface should be a 12% border role at
|
|
119
|
+
* full opacity, or a solid one dimmed by the prop, is a real design question
|
|
120
|
+
* and not this change's to answer.
|
|
121
|
+
*/
|
|
122
|
+
export const BORDER_ROLES = {
|
|
123
|
+
default : '--color-border-default',
|
|
124
|
+
subtle : '--color-border-subtle',
|
|
125
|
+
strong : '--color-border-strong',
|
|
126
|
+
container : '--color-border-container',
|
|
127
|
+
brand : '--color-border-brand',
|
|
128
|
+
disabled : '--color-border-disabled',
|
|
129
|
+
'brand-subtle': '--status-brand-border'
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export const BORDER_ROLE_NAMES = Object.keys(BORDER_ROLES);
|
|
133
|
+
|
|
134
|
+
const onScale = (steps, prefix) => value =>
|
|
135
|
+
steps.includes(String(value)) ? `var(${prefix}${value})` : '';
|
|
136
|
+
|
|
137
|
+
export const spacing = onScale(SPACING_STEPS, '--spacing-');
|
|
138
|
+
export const fontSize = onScale(FONT_SIZE_STEPS, '--typography-fontSize-');
|
|
139
|
+
export const fontWeight = onScale(FONT_WEIGHT_STEPS, '--typography-fontWeight-');
|
|
140
|
+
export const fontFamily = onScale(FONT_FAMILY_STEPS, '--typography-fontFamily-');
|
|
141
|
+
export const letterSpacing = onScale(LETTER_SPACING_STEPS, '--typography-letterSpacing-');
|
|
142
|
+
export const radius = onScale(RADIUS_STEPS, '--radius-');
|
|
143
|
+
|
|
144
|
+
export const space = name =>
|
|
145
|
+
(Object.hasOwn(SPACE_STEPS, name) ? `var(--spacing-${SPACE_STEPS[name]})` : '');
|
|
146
|
+
|
|
147
|
+
/** --icon-*-size wraps its step, so it needs a suffix onScale cannot express. */
|
|
148
|
+
export const iconSize = value =>
|
|
149
|
+
(ICON_SIZE_STEPS.includes(String(value)) ? `var(--icon-${value}-size)` : '');
|
|
150
|
+
|
|
151
|
+
export const color = role =>
|
|
152
|
+
Object.hasOwn(COLOR_ROLES, role) ? `var(${COLOR_ROLES[role]})` : '';
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* A hairline's colour: border roles first, then the ink vocabulary as legacy.
|
|
156
|
+
*
|
|
157
|
+
* Border-first is the point -- `subtle` on a divider means
|
|
158
|
+
* --color-border-subtle, not --color-text-subtle. The fallback exists so the
|
|
159
|
+
* names already in use keep working (`inverse`, and `strong`, which resolves
|
|
160
|
+
* to neutral-900 either way), and it is a migration path, not a second
|
|
161
|
+
* vocabulary: a new divider should be spelled with a border role.
|
|
162
|
+
*/
|
|
163
|
+
export const borderColor = role =>
|
|
164
|
+
(Object.hasOwn(BORDER_ROLES, role)
|
|
165
|
+
? `var(${BORDER_ROLES[role]})`
|
|
166
|
+
: color(role));
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* A one-to-four-value spacing shorthand written as steps, e.g. "10 0" gives
|
|
170
|
+
* `var(--spacing-10) var(--spacing-0)`. All parts must be on the scale, or
|
|
171
|
+
* the whole value is dropped — a half-valid shorthand is worse than none.
|
|
172
|
+
*/
|
|
173
|
+
export const spacingShorthand = value => {
|
|
174
|
+
const parts = String(value ?? '').trim()
|
|
175
|
+
.split(/\s+/)
|
|
176
|
+
.filter(Boolean);
|
|
177
|
+
if (parts.length < 1 || parts.length > 4) return '';
|
|
178
|
+
const refs = parts.map(spacing);
|
|
179
|
+
return refs.every(Boolean) ? refs.join(' ') : '';
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
/* ================================================================
|
|
183
|
+
BREAKPOINTS
|
|
184
|
+
================================================================
|
|
185
|
+
The six viewport widths, and the only place a media condition is built
|
|
186
|
+
from them.
|
|
187
|
+
|
|
188
|
+
This lives in JS rather than in the token file because a media condition
|
|
189
|
+
cannot read a custom property — `@media (max-width: var(--breakpoint-md))`
|
|
190
|
+
is not valid CSS — so the six values have to be interpolated into the
|
|
191
|
+
stylesheet at author time. `--breakpoint-*` in src/tokens/tokens.js stays
|
|
192
|
+
the published record of the scale; this is how a component consumes it.
|
|
193
|
+
|
|
194
|
+
`above` and `below` return a media *condition*, not a whole `@media` rule,
|
|
195
|
+
so they compose:
|
|
196
|
+
|
|
197
|
+
@media ${below('md')} { ... }
|
|
198
|
+
@media ${above('sm')} and ${below('lg')} { ... }
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
WHY THE HELPER OWNS THE COMPLEMENT.
|
|
203
|
+
|
|
204
|
+
A breakpoint is one number, but it has two spellings, and writing the
|
|
205
|
+
second one by hand is how the boundary drifts. `min-width: 768px` and
|
|
206
|
+
`max-width: 768px` are both true at exactly 768px, so a component that
|
|
207
|
+
spells its mobile rule the obvious way applies both its mobile and its
|
|
208
|
+
desktop styling at that one width. pricing-card.css.js has the collision
|
|
209
|
+
today — `@media (max-width: 768px)` at one rule and
|
|
210
|
+
`@media (min-width: 768px) and (max-width: 1024px)` at another — and
|
|
211
|
+
thirteen more files use the same `max-width: 768px` form.
|
|
212
|
+
|
|
213
|
+
The complement is derived here so that number is never typed. Do not write
|
|
214
|
+
767px, 991px or 1199px anywhere; write `below('md')`, `below('lg')`,
|
|
215
|
+
`below('xl')`.
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
WHY 0.02 AND NOT 1.
|
|
220
|
+
|
|
221
|
+
A viewport is not always a whole number of pixels: browser zoom, some
|
|
222
|
+
Android densities and fractional device-pixel-ratios all produce widths
|
|
223
|
+
like 767.5px. Measured in Chrome at that width, `min-width: 768px` is
|
|
224
|
+
false and `max-width: 767px` is *also* false — an element between the two
|
|
225
|
+
rules falls back to its unqueried state, briefly neither mobile nor
|
|
226
|
+
desktop. Subtracting 0.02 rather than 1 removes that band: across
|
|
227
|
+
575.98, 576, 767, 767.5, 767.98, 767.99, 768 and 768.5 every width matches
|
|
228
|
+
exactly one of `above('md')` and `below('md')`, and none matches both.
|
|
229
|
+
|
|
230
|
+
0.02 rather than 0.01 is the long-standing convention, from Safari having
|
|
231
|
+
rounded a hundredth away.
|
|
232
|
+
|
|
233
|
+
The five subtractions are exact in IEEE 754 — 575.98, 767.98, 991.98,
|
|
234
|
+
1199.98 and 1399.98 all round-trip — so no rounding is needed here. If a
|
|
235
|
+
value is ever added to the scale whose complement does not, this is where
|
|
236
|
+
the rounding belongs. */
|
|
237
|
+
|
|
238
|
+
export const BREAKPOINTS = {
|
|
239
|
+
xs : 0,
|
|
240
|
+
sm : 576,
|
|
241
|
+
md : 768,
|
|
242
|
+
lg : 992,
|
|
243
|
+
xl : 1200,
|
|
244
|
+
xxl: 1400
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
export const BREAKPOINT_NAMES = Object.keys(BREAKPOINTS);
|
|
248
|
+
|
|
249
|
+
/** Throws rather than returning empty — see the note in the file header. */
|
|
250
|
+
const step = name => {
|
|
251
|
+
if (!Object.hasOwn(BREAKPOINTS, name)) {
|
|
252
|
+
throw new Error(`Unknown breakpoint: ${name}. Expected one of ${BREAKPOINT_NAMES.join(', ')}.`);
|
|
253
|
+
}
|
|
254
|
+
return BREAKPOINTS[name];
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* "This breakpoint and wider."
|
|
259
|
+
*
|
|
260
|
+
* above('md') → (min-width: 768px)
|
|
261
|
+
*
|
|
262
|
+
* `above('xs')` is `(min-width: 0px)`, which is always true. That is a
|
|
263
|
+
* harmless no-op rather than an error, because it is the honest answer for
|
|
264
|
+
* the bottom of the scale — but a rule that needs no condition should have
|
|
265
|
+
* no media query.
|
|
266
|
+
*/
|
|
267
|
+
export const above = name => `(min-width: ${step(name)}px)`;
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* "Narrower than this breakpoint" — the exact complement of `above`.
|
|
271
|
+
*
|
|
272
|
+
* below('md') → (max-width: 767.98px)
|
|
273
|
+
*
|
|
274
|
+
* `below('xs')` throws: xs is 0, and no viewport is narrower than that, so
|
|
275
|
+
* the query could only ever be dead CSS.
|
|
276
|
+
*/
|
|
277
|
+
export const below = name => {
|
|
278
|
+
const value = step(name);
|
|
279
|
+
if (value === 0) {
|
|
280
|
+
throw new Error(`below('${name}') has no meaning: no viewport is narrower than ${value}px. Did you mean above('${name}')?`);
|
|
281
|
+
}
|
|
282
|
+
return `(max-width: ${value - 0.02}px)`;
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* The JS side of `below`, for the viewport logic CSS genuinely cannot express
|
|
287
|
+
* — choosing which markup to render rather than how to style it.
|
|
288
|
+
*
|
|
289
|
+
* matchesBelow('md') → true when the viewport is narrower than md
|
|
290
|
+
*
|
|
291
|
+
* It reads the same scale as the stylesheet, so the two cannot disagree. That
|
|
292
|
+
* is the failure this replaces: bd-footer switched at 767 in CSS and at 767
|
|
293
|
+
* in JS while bd-footer-lp, sharing the same stylesheet, switched at 768.
|
|
294
|
+
*
|
|
295
|
+
* Prefer CSS. Reach for this only when the component's *behaviour* changes,
|
|
296
|
+
* and re-read it on `resize` — the value is a snapshot, not a subscription.
|
|
297
|
+
*
|
|
298
|
+
* Outside a browser (SSR, a bare Node import) there is no viewport to be
|
|
299
|
+
* narrower than, so this reports false. The name is still validated first, so
|
|
300
|
+
* a typo throws in every environment rather than only in the browser.
|
|
301
|
+
*/
|
|
302
|
+
export const matchesBelow = name => {
|
|
303
|
+
const condition = below(name);
|
|
304
|
+
if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return false;
|
|
305
|
+
return window.matchMedia(condition).matches;
|
|
306
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { css } from 'lit';
|
|
2
|
+
|
|
3
|
+
/** Styled entirely from design tokens — see npm run lint:tokens. */
|
|
4
|
+
export const skeletonCSS = css`
|
|
5
|
+
:host { display: block; }
|
|
6
|
+
|
|
7
|
+
.bone {
|
|
8
|
+
background: var(--color-surface-muted);
|
|
9
|
+
border-radius: var(--radius-sm);
|
|
10
|
+
height: var(--spacing-16);
|
|
11
|
+
animation: bd-shimmer var(--transition-duration-slower) var(--transition-easing-smooth) infinite alternate;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.bone + .bone { margin-top: var(--spacing-8); }
|
|
15
|
+
|
|
16
|
+
:host([shape='circle']) .bone {
|
|
17
|
+
width: var(--dimension-40);
|
|
18
|
+
height: var(--dimension-40);
|
|
19
|
+
border-radius: var(--radius-full);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
:host([shape='title']) .bone { height: var(--spacing-24); }
|
|
23
|
+
|
|
24
|
+
/* --opacity-pulse, not 0.45. The dim end of a "this is busy" pulse is the
|
|
25
|
+
same intent here, in bd-button and in bd-spinner; it was spelled three
|
|
26
|
+
times and two of the three said 0.4. The role now says it once. */
|
|
27
|
+
@keyframes bd-shimmer { to { opacity: var(--opacity-pulse); } }
|
|
28
|
+
|
|
29
|
+
@media (prefers-reduced-motion: reduce) {
|
|
30
|
+
.bone { animation: none; }
|
|
31
|
+
}
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
export default skeletonCSS;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { LitElement, html, nothing } from 'lit';
|
|
2
|
+
// Installs the design tokens into document scope; see src/tokens/tokens.js
|
|
3
|
+
import '../../tokens/tokens.js';
|
|
4
|
+
import { skeletonCSS } from './skeleton.css.js';
|
|
5
|
+
|
|
6
|
+
export class BdSkeleton extends LitElement {
|
|
7
|
+
static styles = [skeletonCSS];
|
|
8
|
+
|
|
9
|
+
static properties = {
|
|
10
|
+
lines: { type: Number },
|
|
11
|
+
shape: { type: String, reflect: true }
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
constructor() {
|
|
15
|
+
super();
|
|
16
|
+
this.lines = 1;
|
|
17
|
+
this.shape = 'line';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
render() {
|
|
21
|
+
return html`
|
|
22
|
+
<span aria-hidden="true">
|
|
23
|
+
${Array.from({ length: this.lines }, (_unused, i) => html`<div
|
|
24
|
+
class="bone"
|
|
25
|
+
style=${i === this.lines - 1 && this.lines > 1 ? 'width: 60%' : nothing}
|
|
26
|
+
></div>`)}
|
|
27
|
+
</span>
|
|
28
|
+
`;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
customElements.define('bd-skeleton', BdSkeleton);
|