@repobit/dex-system-design 0.25.12 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +204 -0
- package/README.md +131 -8
- package/package.json +96 -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
|
@@ -1,23 +1,144 @@
|
|
|
1
1
|
import { LitElement, html } from "lit";
|
|
2
|
-
|
|
2
|
+
// Installs the design tokens into document scope; see src/tokens/tokens.js
|
|
3
|
+
import "../../tokens/tokens.js";
|
|
3
4
|
import "../button/button.js";
|
|
5
|
+
import "../badge/badge.js";
|
|
6
|
+
import "../icons/brand-signature-icon.js";
|
|
4
7
|
import headerCSS from "../header/header.css.js";
|
|
8
|
+
import { ensembleCSS } from "../shared/ensemble.css.js";
|
|
5
9
|
import "../heading/heading.js";
|
|
6
10
|
import "../paragraph/paragraph.js";
|
|
7
11
|
|
|
8
12
|
class Header extends LitElement {
|
|
9
13
|
static properties = {
|
|
10
|
-
title
|
|
11
|
-
subtitle
|
|
12
|
-
description
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
title : { type: String },
|
|
15
|
+
subtitle : { type: String },
|
|
16
|
+
description : { type: String },
|
|
17
|
+
/* The outline level of the headline. `h1` by default, because that is what
|
|
18
|
+
a product hero is on the page it leads, and every current consumer wants
|
|
19
|
+
it -- so nothing has to change to keep the behaviour this component
|
|
20
|
+
already had.
|
|
21
|
+
|
|
22
|
+
It exists because the level is not this component's call. CLAUDE.md is
|
|
23
|
+
unambiguous: `as=` is the document outline and the composing page owns
|
|
24
|
+
it, `visual-level=` is the type role and the component owns that. This
|
|
25
|
+
component used to hardcode `as="h1"` and expose no way to say otherwise,
|
|
26
|
+
which took a page-level decision away from the page. Decided 2026-09-17:
|
|
27
|
+
the consuming dev team and the SEO team set the heading levels on this
|
|
28
|
+
section, so the component has to let them.
|
|
29
|
+
|
|
30
|
+
`visual-level="h1"` is PINNED in the template, deliberately, and that is
|
|
31
|
+
the half worth understanding. Passing a different level moves the
|
|
32
|
+
heading in the outline and changes nothing about how it looks: the hero
|
|
33
|
+
stays 48/700/1.25 at `heading-h1`, because the size is the design's and
|
|
34
|
+
only the level is the page's. Without the pin, `bd-h` follows `as` for
|
|
35
|
+
both, so `heading-level="h3"` would have quietly rendered the hero at
|
|
36
|
+
the h3 role instead -- measured 32px at 1024 and 24px at 320, against
|
|
37
|
+
48 and 30 -- an SEO decision repainting the design.
|
|
38
|
+
|
|
39
|
+
Anything that is not h1-h6 (or a bare 1-6) falls through bd-h's own
|
|
40
|
+
normalisation to h2 rather than h1: still a heading, never the page's.
|
|
41
|
+
That fallback is heading.js's, not repeated here. */
|
|
42
|
+
headingLevel : { type: String, attribute: "heading-level" },
|
|
43
|
+
/* THE TWO CTA LABELS, and the secondary's destination. Props, not slots,
|
|
44
|
+
and they default to the strings this component has always rendered --
|
|
45
|
+
so no consumer changes and every consumer can now change them.
|
|
46
|
+
|
|
47
|
+
This is the half of the CTA question that was ours. The other half is
|
|
48
|
+
not: Header / v2 reads "Get Protection" and "Start your 30-day trial"
|
|
49
|
+
where these say "Buy now" and "View more plans", and which words sell a
|
|
50
|
+
subscription is purchase-funnel copy rather than a design-system value.
|
|
51
|
+
Until 2026-09-17 there was no way for the page to answer it even after
|
|
52
|
+
someone decided -- the strings were literals in this template. Now the
|
|
53
|
+
decision lands in markup, where copy belongs, and the default is only
|
|
54
|
+
what the component happened to ship.
|
|
55
|
+
|
|
56
|
+
PROPS RATHER THAN SLOTS, deliberately. A label is a plain string with no
|
|
57
|
+
markup in it, which is what `title` and `subtitle` already are; props
|
|
58
|
+
appear in schemas/component-manifest.json, which is the contract a page
|
|
59
|
+
generator is handed, and a slot does not. Adding both would give one
|
|
60
|
+
value two sources that can disagree -- the reason the three price props
|
|
61
|
+
were deleted rather than wired up.
|
|
62
|
+
|
|
63
|
+
THE SECONDARY CTA HAS NO DESTINATION UNTIL A PAGE GIVES IT ONE, and
|
|
64
|
+
that is not new: it never had one. bd-button-link drops the href
|
|
65
|
+
attribute entirely when href is empty, so the secondary has always
|
|
66
|
+
rendered an <a> with no href -- which is not focusable, is not a link in
|
|
67
|
+
the accessibility tree, and does nothing when clicked. The primary is
|
|
68
|
+
fine: `buyLink` makes the store inject the buy URL at runtime. So
|
|
69
|
+
cta-secondary-href is the fix for a dead control, not a nicety, and
|
|
70
|
+
there is no default because a URL is content this package cannot
|
|
71
|
+
invent. */
|
|
72
|
+
ctaLabel : { type: String, attribute: "cta-label" },
|
|
73
|
+
ctaSecondaryLabel: { type: String, attribute: "cta-secondary-label" },
|
|
74
|
+
ctaSecondaryHref : { type: String, attribute: "cta-secondary-href" },
|
|
75
|
+
/* fullPrice, discount and finalPrice USED TO BE DECLARED HERE and are gone.
|
|
76
|
+
Nothing read them -- not this template, not this stylesheet, not the
|
|
77
|
+
store. The three price zones below are store-driven
|
|
78
|
+
(data-store-render with data-store-price / data-store-discount), and the
|
|
79
|
+
store computes the numbers from product-id, devices and subscription; it
|
|
80
|
+
never looks at a host property. So setting full-price="$159.99" did
|
|
81
|
+
exactly nothing, in every consumer, since the props were added.
|
|
82
|
+
|
|
83
|
+
Removed rather than wired up, and that is the decision worth recording.
|
|
84
|
+
Wiring them would give the same value two sources that can disagree --
|
|
85
|
+
the store's computed price and a hardcoded string -- with no answer to
|
|
86
|
+
which wins when both are present. The price belongs to the store; the
|
|
87
|
+
API for it is product-id plus devices plus subscription.
|
|
88
|
+
|
|
89
|
+
It matters more than a dead prop usually would because the manifest is
|
|
90
|
+
the contract a page generator is handed, and check-manifest.mjs's own
|
|
91
|
+
header says a stale entry is worse than a missing one: the model emits
|
|
92
|
+
props that do not exist. All three were listed there as if they worked.
|
|
93
|
+
|
|
94
|
+
The one consumer was packages/dex-system-design/index.html, Vite's dev
|
|
95
|
+
scratch page, which sets all three, loads no store, and has therefore
|
|
96
|
+
never displayed a price. It demonstrated the bug rather than a need, and
|
|
97
|
+
it is updated in the same commit. */
|
|
98
|
+
/* `product-id`, the spelling this component documents and the only one
|
|
99
|
+
anything writes. Lit derives an observed attribute by lowercasing the
|
|
100
|
+
property name rather than dash-casing it, so `productId` was listening
|
|
101
|
+
for `productid` while header.stories.js writes `product-id` in seven
|
|
102
|
+
places -- including its own usage example and its argTypes-driven
|
|
103
|
+
playground. The attribute was inert and the property stayed on its
|
|
104
|
+
"vsb" default: measured on Components/Header "Different Product IDs",
|
|
105
|
+
all three headers reported productId "vsb" while their markup asked for
|
|
106
|
+
"av", "ts" and "ps", so the same product was handed to <bd-product> for
|
|
107
|
+
every price lookup on the page.
|
|
108
|
+
|
|
109
|
+
Zero consumers of the working lowercase spelling: grepped `productid`
|
|
110
|
+
and `productId=` as an attribute across src, schemas and docs -- none,
|
|
111
|
+
and no `.productId=` property binding either. So naming the attribute
|
|
112
|
+
breaks nothing. Same root cause and same one-line repair as bd-grid's
|
|
113
|
+
cols-sm and bd-picture's fallback-src in P7H. */
|
|
114
|
+
productId : { type: String, attribute: "product-id" },
|
|
115
|
+
devices : { type: String },
|
|
116
|
+
subscription : { type: String },
|
|
117
|
+
/* NOT styling, despite what this was documented as for a long time. It is
|
|
118
|
+
the store's opt-in for walking a shadow root.
|
|
119
|
+
@repobit/dex-store-elements' renders/index.ts selects shadow hosts with
|
|
120
|
+
SHADOW_HOST_SELECTOR = '[shadow], [data-bd-watch-shadow]', so an element
|
|
121
|
+
that does not reflect this attribute never has its shadow tree searched
|
|
122
|
+
for [data-store-render] or [data-store-hide] nodes -- and every price
|
|
123
|
+
zone in this component is inside the shadow root. Reflected and
|
|
124
|
+
defaulting to true for that reason, and load-bearing: drop it and the
|
|
125
|
+
prices silently stop rendering on a real page.
|
|
126
|
+
|
|
127
|
+
Nearly deleted as inert during the prop audit, because nothing in this
|
|
128
|
+
component or its stylesheet reads it. The consumer is another package. */
|
|
129
|
+
shadow : { type: Boolean, reflect: true },
|
|
130
|
+
dataLayerEvent : { type: String },
|
|
131
|
+
/* Whether the header-picture slot has anything in it. Internal state, not
|
|
132
|
+
an attribute: the consumer says this by slotting a picture or not, and a
|
|
133
|
+
second way to say it could only ever disagree with the first.
|
|
134
|
+
|
|
135
|
+
It exists because the scrim is only correct over an image. It is a
|
|
136
|
+
blue-950 band, and the NoImage story is a documented state -- rendering
|
|
137
|
+
the band there would paint a blue wash across the bottom of an empty
|
|
138
|
+
column, and the mark would sit on the page ground in text-inverse,
|
|
139
|
+
which is white on white. Nothing in CSS can ask whether a slot received
|
|
140
|
+
anything, so the slot is asked directly. */
|
|
141
|
+
_hasPicture : { state: true }
|
|
21
142
|
};
|
|
22
143
|
|
|
23
144
|
constructor() {
|
|
@@ -25,17 +146,30 @@ class Header extends LitElement {
|
|
|
25
146
|
this.title = "";
|
|
26
147
|
this.subtitle = "";
|
|
27
148
|
this.description = "";
|
|
149
|
+
this.headingLevel = "h1";
|
|
150
|
+
this.ctaLabel = "Buy now";
|
|
151
|
+
this.ctaSecondaryLabel = "View more plans";
|
|
152
|
+
this.ctaSecondaryHref = "";
|
|
28
153
|
this.productId = "vsb";
|
|
29
154
|
this.devices = "5";
|
|
30
155
|
this.subscription = "1";
|
|
31
156
|
this.shadow = true;
|
|
32
157
|
this.dataLayerEvent = "info";
|
|
158
|
+
this._hasPicture = false;
|
|
33
159
|
}
|
|
34
160
|
|
|
35
161
|
// attributeChangedCallback(name, oldVal, newVal) {
|
|
36
162
|
// super.attributeChangedCallback(name, oldVal, newVal);
|
|
37
163
|
// }
|
|
38
164
|
|
|
165
|
+
/* assignedElements rather than assignedNodes: a slot fed nothing but
|
|
166
|
+
whitespace from the consumer's markup still reports text nodes, and that
|
|
167
|
+
is not a picture. flatten so a picture arriving through another slot in a
|
|
168
|
+
composed tree still counts. */
|
|
169
|
+
_pictureSlotChanged(event) {
|
|
170
|
+
this._hasPicture = event.target.assignedElements({ flatten: true }).length > 0;
|
|
171
|
+
}
|
|
172
|
+
|
|
39
173
|
render() {
|
|
40
174
|
return html`
|
|
41
175
|
<bd-context ignore-events-parent>
|
|
@@ -45,49 +179,141 @@ class Header extends LitElement {
|
|
|
45
179
|
subscription="${this.subscription}"
|
|
46
180
|
data-layer-event="${this.dataLayerEvent}">
|
|
47
181
|
|
|
48
|
-
<div class="bd-container">
|
|
182
|
+
<div class="bd-ensemble bd-container">
|
|
49
183
|
<div class="bd-header-left">
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
184
|
+
<!-- ONE HEADING HERE, AND THE EYEBROW IS NOT IT.
|
|
185
|
+
|
|
186
|
+
What this replaces: the product name was as="h2" and the
|
|
187
|
+
headline as="h4", so the outline ran h2 then h4 in that DOM
|
|
188
|
+
order -- an eyebrow announced as a landmark, above the actual
|
|
189
|
+
promise announced two levels below it. Every page rendering
|
|
190
|
+
this header carried that.
|
|
191
|
+
|
|
192
|
+
The headline is the page's main heading, so it is h1 --
|
|
193
|
+
h1 by DEFAULT now rather than hardcoded, see headingLevel
|
|
194
|
+
above. This component is the product hero and there is one per
|
|
195
|
+
page, which is the condition an h1 needs, so h1 is the right
|
|
196
|
+
default; the page can still say otherwise. bd-h's h1 role is
|
|
197
|
+
5xl/bold/tight -- 48/700/1.25 -- which is Header / v2's
|
|
198
|
+
value-prop exactly, which is why visual-level is pinned to h1
|
|
199
|
+
and not left to follow as: the two agree at the default, and
|
|
200
|
+
when a page separates them the design keeps the size.
|
|
201
|
+
|
|
202
|
+
The product name is a kicker -- a label naming the product, and
|
|
203
|
+
in Figma a plain text layer called product-name rather than a
|
|
204
|
+
heading. So it is a <p>, out of the heading list entirely, and
|
|
205
|
+
it resolves through the heading-h4 ROLE in header.css.js, which
|
|
206
|
+
is where a role on a non-heading element belongs. A kicker in
|
|
207
|
+
the outline is worse than no kicker in the outline: it adds a
|
|
208
|
+
rung nobody navigates by and pushes the real heading down.
|
|
209
|
+
|
|
210
|
+
THE ALTERNATIVE, and why not. The other accessible pattern is
|
|
211
|
+
to put both inside the h1 -- <h1><span>Bitdefender Ultimate
|
|
212
|
+
Security</span><span>Your Ultimate Digital Protection</span>
|
|
213
|
+
</h1> -- which gives the heading an accessible name carrying
|
|
214
|
+
the product as well as the promise. It was rejected on two
|
|
215
|
+
counts: every heading list, skip link and rotor entry would
|
|
216
|
+
then read the whole run, and it would mean rendering the h1
|
|
217
|
+
here by hand instead of through bd-h, since the atom takes one
|
|
218
|
+
slot. The product is named by the document title, the price
|
|
219
|
+
block and the CTA; the heading does not have to carry it. -->
|
|
220
|
+
<p class="bd-product-name">${this.title}</p>
|
|
221
|
+
<bd-h
|
|
222
|
+
as="${this.headingLevel}"
|
|
223
|
+
visual-level="h1"
|
|
224
|
+
class="bd-value-prop">${this.subtitle}</bd-h>
|
|
225
|
+
<bd-p variant="regular" class="bd-description">${this.description}</bd-p>
|
|
53
226
|
|
|
54
227
|
<div class="bd-price-zone-top" data-store-hide="!it.option.price.discounted">
|
|
55
|
-
|
|
56
|
-
|
|
228
|
+
<!-- font-weight="medium", the atom's own attribute rather than a
|
|
229
|
+
declaration out in the stylesheet. Header / v2 sets the struck
|
|
230
|
+
price at 16 Medium, and a weight on a bd-p HOST is inert --
|
|
231
|
+
the atom sets all five type axes on its inner element -- so
|
|
232
|
+
the only way in from outside is the attribute, which
|
|
233
|
+
paragraph.js resolves through the weight scale and applies to
|
|
234
|
+
that inner element. Measured before adding it: 400. -->
|
|
235
|
+
<bd-p variant="regular" font-weight="medium" class="original-price">
|
|
236
|
+
<s data-store-render data-store-price="full"></s>
|
|
237
|
+
</bd-p>
|
|
238
|
+
<!-- bd-badge, not the span this used to be. Header / v2 places the
|
|
239
|
+
Badge component here at tone=success variant=fill size=lg, and
|
|
240
|
+
the atom's own lg step is that spec exactly: 8/12 padding, the
|
|
241
|
+
control-label size, a full radius. The span it replaces was a
|
|
242
|
+
hand-rolled badge and a DIFFERENT one -- green-100 behind
|
|
243
|
+
text/success, which is the atom's shade variant, at 16px
|
|
244
|
+
semibold in a --radius-xl box. Its two "no role:" markers went
|
|
245
|
+
with it: they existed to say "this should be a bd-badge", and
|
|
246
|
+
the answer to that is a bd-badge. -->
|
|
247
|
+
<bd-badge
|
|
248
|
+
tone="success"
|
|
249
|
+
variant="fill"
|
|
250
|
+
size="lg"
|
|
251
|
+
class="bd-discount"
|
|
252
|
+
data-store-render
|
|
253
|
+
data-store-discount="percentage"
|
|
254
|
+
></bd-badge>
|
|
57
255
|
</div>
|
|
58
256
|
|
|
59
257
|
<span class="bd-price-zone-final" data-store-render data-store-price="discounted||full"></span>
|
|
60
258
|
|
|
61
259
|
<div class="bd-cta">
|
|
260
|
+
<!-- size="lg", and no font-size override. Header / v2 draws both
|
|
261
|
+
CTAs at 16/32 padding with an 18px label in an 8px-radius box,
|
|
262
|
+
and --button-large-* is that set exactly: spacing-16/32,
|
|
263
|
+
fontSize-lg, radius-lg. These were size="md" (12/24, 16px,
|
|
264
|
+
radius-md) with font-size="lg" bolted on top, so the label was
|
|
265
|
+
the large step's and the box was the medium step's -- 18px type
|
|
266
|
+
in a box padded for 16px type. Dropping the override puts the
|
|
267
|
+
whole control back on one answer.
|
|
268
|
+
|
|
269
|
+
THE LABELS come from cta-label and cta-secondary-label as of
|
|
270
|
+
2026-09-17, defaulting to the strings this component always
|
|
271
|
+
rendered. Which words sell a subscription is still not ours
|
|
272
|
+
-- see the prop declarations above -- but the page can now
|
|
273
|
+
act on the answer without editing this file.
|
|
274
|
+
|
|
275
|
+
NO label ATTRIBUTE ON EITHER. On bd-button-link, label
|
|
276
|
+
is the accessible name and the slot is the visible text, and
|
|
277
|
+
these passed the same string to both -- so the anchor
|
|
278
|
+
carried an aria-label duplicating its own content. Dropping
|
|
279
|
+
it lets the accessible name come from the text, which is one
|
|
280
|
+
source instead of two saying the same thing.
|
|
281
|
+
|
|
282
|
+
The secondary's href is empty by default and the atom then
|
|
283
|
+
renders an anchor with no href at all. Measured on
|
|
284
|
+
Components/Header -> Design copy & a live secondary: it
|
|
285
|
+
refuses focus and matches neither a[href] nor :enabled, so
|
|
286
|
+
it is not focusable, not a link to a screen reader, and does
|
|
287
|
+
nothing on click. It has been that way since it was written.
|
|
288
|
+
Set cta-secondary-href.
|
|
289
|
+
|
|
290
|
+
In Storybook the PRIMARY is inert too, for the same reason
|
|
291
|
+
the price zones are empty there -- no store to inject an
|
|
292
|
+
href. On a real page the store fills the primary in and
|
|
293
|
+
never touches the secondary. -->
|
|
62
294
|
<bd-button-link
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
size="md"
|
|
295
|
+
variant="accent"
|
|
296
|
+
size="lg"
|
|
66
297
|
part="buy-button"
|
|
67
|
-
font-size="18px"
|
|
68
|
-
font-weight="600"
|
|
69
298
|
buyLink
|
|
70
|
-
|
|
299
|
+
>${this.ctaLabel}</bd-button-link
|
|
71
300
|
>
|
|
72
301
|
<bd-button-link
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
size="
|
|
76
|
-
|
|
77
|
-
font-weight="600"
|
|
302
|
+
href="${this.ctaSecondaryHref}"
|
|
303
|
+
variant="tertiary"
|
|
304
|
+
size="lg"
|
|
305
|
+
>${this.ctaSecondaryLabel}</bd-button-link
|
|
78
306
|
>
|
|
79
|
-
View more plans
|
|
80
|
-
</bd-button-link>
|
|
81
307
|
</div>
|
|
82
308
|
|
|
83
|
-
<bd-p
|
|
309
|
+
<bd-p variant="xsmall" class="bd-disclaimer-top">
|
|
84
310
|
<slot name="disclaimer-top">
|
|
85
311
|
Protection for 1 account & 5 PCs, Macs, tablets, or smartphones.
|
|
86
312
|
Windows® | macOS® | Android™ | iOS®
|
|
87
313
|
</slot>
|
|
88
314
|
</bd-p>
|
|
89
315
|
|
|
90
|
-
<bd-p
|
|
316
|
+
<bd-p variant="xsmall" class="bd-disclaimer-bottom">
|
|
91
317
|
<slot name="disclaimer-bottom-text-start"></slot>
|
|
92
318
|
<slot name="disclaimer-bottom-link"></slot>
|
|
93
319
|
<slot name="disclaimer-bottom-text-end"></slot>
|
|
@@ -96,8 +322,15 @@ class Header extends LitElement {
|
|
|
96
322
|
|
|
97
323
|
<div class="bd-header-right">
|
|
98
324
|
<div class="bd-header-image-wrapper">
|
|
99
|
-
<slot name="header-picture"></slot>
|
|
100
|
-
|
|
325
|
+
<slot name="header-picture" @slotchange=${this._pictureSlotChanged}></slot>
|
|
326
|
+
${this._hasPicture
|
|
327
|
+
? html`
|
|
328
|
+
<div class="bd-header-scrim" aria-hidden="true"></div>
|
|
329
|
+
<bd-brand-signature-icon
|
|
330
|
+
class="bd-header-signature"
|
|
331
|
+
></bd-brand-signature-icon>
|
|
332
|
+
`
|
|
333
|
+
: null}
|
|
101
334
|
</div>
|
|
102
335
|
</div>
|
|
103
336
|
</div>
|
|
@@ -108,6 +341,6 @@ class Header extends LitElement {
|
|
|
108
341
|
}
|
|
109
342
|
}
|
|
110
343
|
|
|
111
|
-
Header.styles = [
|
|
344
|
+
Header.styles = [ensembleCSS, headerCSS];
|
|
112
345
|
|
|
113
346
|
customElements.define("bd-header", Header);
|