@deriv-com/dds-marketing 0.2.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.llm/components/accordion.md +9 -1
- package/.llm/components/accordionitem.md +9 -1
- package/.llm/components/aspectratio.md +9 -1
- package/.llm/components/badge.md +9 -1
- package/.llm/components/bottomsheet.md +9 -1
- package/.llm/components/breadcrumb.md +9 -1
- package/.llm/components/breadcrumbitem.md +9 -1
- package/.llm/components/button.md +55 -27
- package/.llm/components/buttonicon.md +16 -8
- package/.llm/components/card.md +16 -2
- package/.llm/components/cardbody.md +29 -3
- package/.llm/components/cardbutton.md +16 -2
- package/.llm/components/checkbox.md +9 -1
- package/.llm/components/ctabanner.md +186 -0
- package/.llm/components/ctabannerslider.md +204 -0
- package/.llm/components/divider.md +9 -1
- package/.llm/components/dropdownmenu.md +15 -7
- package/.llm/components/faqaccordion.md +217 -0
- package/.llm/components/featurebullets.md +254 -0
- package/.llm/components/featurecards.md +313 -0
- package/.llm/components/featurescroll.md +284 -0
- package/.llm/components/featuresplit.md +249 -0
- package/.llm/components/filterchip.md +9 -1
- package/.llm/components/footer.md +9 -1
- package/.llm/components/footergroup.md +9 -1
- package/.llm/components/grid.md +9 -1
- package/.llm/components/icon.md +21 -3
- package/.llm/components/landinghero.md +254 -0
- package/.llm/components/link.md +13 -5
- package/.llm/components/logocolumns.md +235 -0
- package/.llm/components/logorows.md +212 -0
- package/.llm/components/menuitem.md +9 -1
- package/.llm/components/navbar.md +80 -16
- package/.llm/components/navbaritem.md +10 -2
- package/.llm/components/navbarmenu.md +9 -1
- package/.llm/components/navbarmenugroup.md +9 -1
- package/.llm/components/navbarpanel.md +10 -2
- package/.llm/components/navbarswitcher.md +10 -2
- package/.llm/components/navbarswitcheroption.md +9 -1
- package/.llm/components/option.md +9 -1
- package/.llm/components/pageheader.md +231 -0
- package/.llm/components/pagination.md +27 -90
- package/.llm/components/paginationdots.md +215 -0
- package/.llm/components/producthero.md +248 -0
- package/.llm/components/removablechip.md +9 -1
- package/.llm/components/row.md +9 -1
- package/.llm/components/scroller.md +9 -1
- package/.llm/components/searchinput.md +9 -1
- package/.llm/components/segmentedcontrol.md +9 -1
- package/.llm/components/segmentedcontrolitem.md +9 -1
- package/.llm/components/select.md +9 -1
- package/.llm/components/selectchip.md +57 -16
- package/.llm/components/spinner.md +9 -1
- package/.llm/components/stack.md +9 -1
- package/.llm/components/statusdot.md +9 -1
- package/.llm/components/subdomainnavbar.md +9 -1
- package/.llm/components/tab.md +11 -3
- package/.llm/components/table.md +9 -1
- package/.llm/components/tablebody.md +9 -1
- package/.llm/components/tablecell.md +9 -1
- package/.llm/components/tablehead.md +9 -1
- package/.llm/components/tableheadercell.md +9 -1
- package/.llm/components/tablerow.md +9 -1
- package/.llm/components/tablist.md +9 -1
- package/.llm/components/testimonialrow.md +235 -0
- package/.llm/components/testimonialslider.md +254 -0
- package/.llm/components/textarea.md +9 -1
- package/.llm/components/textinput.md +9 -1
- package/.llm/components/tooltip.md +9 -1
- package/.llm/components/typography.md +9 -1
- package/.llm/digest/button.md +11 -6
- package/.llm/digest/buttonicon.md +2 -2
- package/.llm/digest/card.md +1 -0
- package/.llm/digest/cardbody.md +3 -1
- package/.llm/digest/cardbutton.md +1 -0
- package/.llm/digest/ctabanner.md +44 -0
- package/.llm/digest/ctabannerslider.md +45 -0
- package/.llm/digest/dropdownmenu.md +1 -1
- package/.llm/digest/faqaccordion.md +43 -0
- package/.llm/digest/featurebullets.md +52 -0
- package/.llm/digest/featurecards.md +49 -0
- package/.llm/digest/featurescroll.md +52 -0
- package/.llm/digest/featuresplit.md +54 -0
- package/.llm/digest/landinghero.md +53 -0
- package/.llm/digest/logocolumns.md +55 -0
- package/.llm/digest/logorows.md +48 -0
- package/.llm/digest/navbar.md +18 -3
- package/.llm/digest/pageheader.md +49 -0
- package/.llm/digest/pagination.md +6 -17
- package/.llm/digest/paginationdots.md +43 -0
- package/.llm/digest/producthero.md +53 -0
- package/.llm/digest/selectchip.md +6 -1
- package/.llm/digest/testimonialrow.md +51 -0
- package/.llm/digest/testimonialslider.md +51 -0
- package/.llm/index.md +24 -3
- package/.llm/rules.md +43 -9
- package/.llm/tokens.md +56 -12
- package/AGENTS.md +32 -8
- package/README.md +41 -21
- package/dist/components/banner-data.d.ts +38 -0
- package/dist/components/banner-data.js +15 -0
- package/dist/components/blocks/block-reveal.css +90 -0
- package/dist/components/blocks/cta-banner/CtaBanner.css +66 -0
- package/dist/components/blocks/cta-banner/CtaBanner.d.ts +22 -0
- package/dist/components/blocks/cta-banner/CtaBanner.js +31 -0
- package/dist/components/blocks/cta-banner/CtaBanner.types.d.ts +29 -0
- package/dist/components/blocks/cta-banner/CtaBanner.types.js +13 -0
- package/dist/components/blocks/cta-banner/cta-banner-impl.css +29 -0
- package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.css +90 -0
- package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.d.ts +39 -0
- package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.js +60 -0
- package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.types.d.ts +41 -0
- package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.types.js +15 -0
- package/dist/components/blocks/cta-banner-slider/cta-banner-slider-impl.css +73 -0
- package/dist/components/blocks/faq-accordion/FaqAccordion.css +54 -0
- package/dist/components/blocks/faq-accordion/FaqAccordion.d.ts +20 -0
- package/dist/components/blocks/faq-accordion/FaqAccordion.js +28 -0
- package/dist/components/blocks/faq-accordion/FaqAccordion.types.d.ts +25 -0
- package/dist/components/blocks/faq-accordion/FaqAccordion.types.js +10 -0
- package/dist/components/blocks/faq-accordion/faq-accordion-impl.css +54 -0
- package/dist/components/blocks/feature-bullets/FeatureBullets.css +139 -0
- package/dist/components/blocks/feature-bullets/FeatureBullets.d.ts +31 -0
- package/dist/components/blocks/feature-bullets/FeatureBullets.js +55 -0
- package/dist/components/blocks/feature-bullets/FeatureBullets.types.d.ts +39 -0
- package/dist/components/blocks/feature-bullets/FeatureBullets.types.js +16 -0
- package/dist/components/blocks/feature-bullets/feature-bullets-impl.css +104 -0
- package/dist/components/blocks/feature-cards/FeatureCards.css +84 -0
- package/dist/components/blocks/feature-cards/FeatureCards.d.ts +24 -0
- package/dist/components/blocks/feature-cards/FeatureCards.js +66 -0
- package/dist/components/blocks/feature-cards/FeatureCards.types.d.ts +33 -0
- package/dist/components/blocks/feature-cards/FeatureCards.types.js +14 -0
- package/dist/components/blocks/feature-cards/feature-cards-impl.css +245 -0
- package/dist/components/blocks/feature-scroll/FeatureScroll.css +137 -0
- package/dist/components/blocks/feature-scroll/FeatureScroll.d.ts +39 -0
- package/dist/components/blocks/feature-scroll/FeatureScroll.js +167 -0
- package/dist/components/blocks/feature-scroll/FeatureScroll.types.d.ts +40 -0
- package/dist/components/blocks/feature-scroll/FeatureScroll.types.js +17 -0
- package/dist/components/blocks/feature-scroll/feature-scroll-impl.css +292 -0
- package/dist/components/blocks/feature-split/FeatureSplit.css +95 -0
- package/dist/components/blocks/feature-split/FeatureSplit.d.ts +30 -0
- package/dist/components/blocks/feature-split/FeatureSplit.js +38 -0
- package/dist/components/blocks/feature-split/FeatureSplit.types.d.ts +39 -0
- package/dist/components/blocks/feature-split/FeatureSplit.types.js +14 -0
- package/dist/components/blocks/feature-split/feature-split-impl.css +83 -0
- package/dist/components/blocks/landing-hero/LandingHero.css +85 -0
- package/dist/components/blocks/landing-hero/LandingHero.d.ts +41 -0
- package/dist/components/blocks/landing-hero/LandingHero.js +54 -0
- package/dist/components/blocks/landing-hero/LandingHero.types.d.ts +37 -0
- package/dist/components/blocks/landing-hero/LandingHero.types.js +15 -0
- package/dist/components/blocks/landing-hero/landing-hero-impl.css +355 -0
- package/dist/components/blocks/logo-columns/LogoColumns.css +161 -0
- package/dist/components/blocks/logo-columns/LogoColumns.d.ts +23 -0
- package/dist/components/blocks/logo-columns/LogoColumns.js +46 -0
- package/dist/components/blocks/logo-columns/LogoColumns.types.d.ts +46 -0
- package/dist/components/blocks/logo-columns/LogoColumns.types.js +18 -0
- package/dist/components/blocks/logo-columns/logo-columns-impl.css +188 -0
- package/dist/components/blocks/logo-rows/LogoRows.css +122 -0
- package/dist/components/blocks/logo-rows/LogoRows.d.ts +20 -0
- package/dist/components/blocks/logo-rows/LogoRows.js +43 -0
- package/dist/components/blocks/logo-rows/LogoRows.types.d.ts +36 -0
- package/dist/components/blocks/logo-rows/LogoRows.types.js +16 -0
- package/dist/components/blocks/logo-rows/logo-rows-impl.css +179 -0
- package/dist/components/blocks/page-header/PageHeader.css +79 -0
- package/dist/components/blocks/page-header/PageHeader.d.ts +34 -0
- package/dist/components/blocks/page-header/PageHeader.js +43 -0
- package/dist/components/blocks/page-header/PageHeader.types.d.ts +32 -0
- package/dist/components/blocks/page-header/PageHeader.types.js +14 -0
- package/dist/components/blocks/page-header/page-header-impl.css +176 -0
- package/dist/components/blocks/product-hero/ProductHero.css +86 -0
- package/dist/components/blocks/product-hero/ProductHero.d.ts +35 -0
- package/dist/components/blocks/product-hero/ProductHero.js +48 -0
- package/dist/components/blocks/product-hero/ProductHero.types.d.ts +37 -0
- package/dist/components/blocks/product-hero/ProductHero.types.js +15 -0
- package/dist/components/blocks/product-hero/product-hero-impl.css +354 -0
- package/dist/components/blocks/testimonial-row/TestimonialRow.css +245 -0
- package/dist/components/blocks/testimonial-row/TestimonialRow.d.ts +20 -0
- package/dist/components/blocks/testimonial-row/TestimonialRow.js +128 -0
- package/dist/components/blocks/testimonial-row/TestimonialRow.types.d.ts +54 -0
- package/dist/components/blocks/testimonial-row/TestimonialRow.types.js +26 -0
- package/dist/components/blocks/testimonial-row/testimonial-row-impl.css +195 -0
- package/dist/components/blocks/testimonial-slider/TestimonialSlider.css +181 -0
- package/dist/components/blocks/testimonial-slider/TestimonialSlider.d.ts +22 -0
- package/dist/components/blocks/testimonial-slider/TestimonialSlider.js +36 -0
- package/dist/components/blocks/testimonial-slider/TestimonialSlider.types.d.ts +49 -0
- package/dist/components/blocks/testimonial-slider/TestimonialSlider.types.js +21 -0
- package/dist/components/blocks/testimonial-slider/testimonial-slider-impl.css +202 -0
- package/dist/components/blocks/use-reveal.d.ts +46 -0
- package/dist/components/blocks/use-reveal.js +120 -0
- package/dist/components/blocks/use-rotation.d.ts +62 -0
- package/dist/components/blocks/use-rotation.js +176 -0
- package/dist/components/core/accordion-item/AccordionItem.css +6 -5
- package/dist/components/core/bottom-sheet/BottomSheet.css +1 -1
- package/dist/components/core/button/Button.css +63 -16
- package/dist/components/core/button/Button.d.ts +14 -12
- package/dist/components/core/button/Button.js +43 -14
- package/dist/components/core/button/Button.types.d.ts +9 -14
- package/dist/components/core/button/button-impl.css +25 -0
- package/dist/components/core/button-icon/ButtonIcon.css +31 -15
- package/dist/components/core/button-icon/ButtonIcon.d.ts +2 -2
- package/dist/components/core/button-icon/ButtonIcon.js +2 -2
- package/dist/components/core/button-icon/ButtonIcon.types.d.ts +4 -4
- package/dist/components/core/card/Card.css +13 -4
- package/dist/components/core/card/Card.types.d.ts +2 -1
- package/dist/components/core/card-body/CardBody.css +11 -6
- package/dist/components/core/card-body/CardBody.d.ts +1 -1
- package/dist/components/core/card-body/CardBody.js +2 -2
- package/dist/components/core/card-body/CardBody.types.d.ts +4 -1
- package/dist/components/core/card-body/CardBody.types.js +1 -0
- package/dist/components/core/card-button/CardButton.css +13 -4
- package/dist/components/core/card-button/CardButton.types.d.ts +2 -1
- package/dist/components/core/checkbox/Checkbox.css +8 -1
- package/dist/components/core/dropdown-menu/DropdownMenu.css +13 -12
- package/dist/components/core/dropdown-menu/DropdownMenu.d.ts +5 -3
- package/dist/components/core/dropdown-menu/DropdownMenu.js +5 -3
- package/dist/components/core/filter-chip/FilterChip.css +2 -1
- package/dist/components/core/footer/footer-impl.css +1 -1
- package/dist/components/core/footer-group/FooterGroup.css +8 -7
- package/dist/components/core/icon/Icon.css +1 -1
- package/dist/components/core/link/Link.css +1 -1
- package/dist/components/core/link/Link.js +5 -5
- package/dist/components/core/menu-item/MenuItem.css +1 -1
- package/dist/components/core/navbar/Navbar.css +7 -8
- package/dist/components/core/navbar/Navbar.d.ts +17 -1
- package/dist/components/core/navbar/Navbar.js +139 -12
- package/dist/components/core/navbar/Navbar.types.d.ts +23 -4
- package/dist/components/core/navbar/Navbar.types.js +1 -1
- package/dist/components/core/navbar/navbar-impl.css +10 -3
- package/dist/components/core/navbar/navbar-material.css +96 -0
- package/dist/components/core/navbar-item/NavbarItem.css +4 -4
- package/dist/components/core/navbar-menu/NavbarMenu.css +7 -5
- package/dist/components/core/navbar-menu/NavbarMenu.d.ts +1 -0
- package/dist/components/core/navbar-menu/NavbarMenu.js +1 -0
- package/dist/components/core/navbar-menu/navbar-menu-impl.css +7 -7
- package/dist/components/core/navbar-switcher/NavbarSwitcher.css +6 -5
- package/dist/components/core/navbar-switcher/NavbarSwitcher.d.ts +1 -0
- package/dist/components/core/navbar-switcher/NavbarSwitcher.js +2 -1
- package/dist/components/core/option/Option.css +1 -1
- package/dist/components/core/pagination/Pagination.css +1 -75
- package/dist/components/core/pagination/Pagination.d.ts +4 -4
- package/dist/components/core/pagination/Pagination.js +11 -51
- package/dist/components/core/pagination/Pagination.types.d.ts +5 -17
- package/dist/components/core/pagination/Pagination.types.js +1 -3
- package/dist/components/core/pagination-dots/PaginationDots.css +60 -0
- package/dist/components/core/pagination-dots/PaginationDots.d.ts +25 -0
- package/dist/components/core/pagination-dots/PaginationDots.js +104 -0
- package/dist/components/core/pagination-dots/PaginationDots.types.d.ts +38 -0
- package/dist/components/core/pagination-dots/PaginationDots.types.js +9 -0
- package/dist/components/core/removable-chip/RemovableChip.css +3 -3
- package/dist/components/core/search-input/SearchInput.css +1 -1
- package/dist/components/core/segmented-control/SegmentedControl.css +1 -1
- package/dist/components/core/segmented-control-item/SegmentedControlItem.css +1 -1
- package/dist/components/core/select/Select.css +2 -1
- package/dist/components/core/select-chip/SelectChip.css +14 -7
- package/dist/components/core/select-chip/SelectChip.d.ts +1 -1
- package/dist/components/core/select-chip/SelectChip.js +7 -4
- package/dist/components/core/select-chip/SelectChip.types.d.ts +7 -1
- package/dist/components/core/select-chip/SelectChip.types.js +1 -1
- package/dist/components/core/select-chip/select-chip-impl.css +14 -3
- package/dist/components/core/status-dot/StatusDot.css +1 -1
- package/dist/components/core/subdomain-navbar/SubdomainNavbar.css +3 -3
- package/dist/components/core/tab/Tab.css +1 -1
- package/dist/components/core/tab/Tab.types.d.ts +1 -1
- package/dist/components/core/tab-list/TabList.css +1 -1
- package/dist/components/core/table/table-impl.css +36 -14
- package/dist/components/core/table-header-cell/TableHeaderCell.css +5 -5
- package/dist/components/core/text-area/TextArea.css +1 -1
- package/dist/components/core/text-input/TextInput.css +1 -1
- package/dist/components/core/tooltip/Tooltip.css +1 -1
- package/dist/components/core/typography/Typography.css +5 -5
- package/dist/components/index.d.ts +35 -0
- package/dist/components/index.js +30 -0
- package/dist/components/logo-data.d.ts +24 -0
- package/dist/components/logo-data.js +11 -0
- package/dist/components/scroll-data.d.ts +64 -0
- package/dist/components/scroll-data.js +17 -0
- package/dist/components/testimonial-row-data.d.ts +51 -0
- package/dist/components/testimonial-row-data.js +11 -0
- package/dist/components/testimonial-slider-data.d.ts +26 -0
- package/dist/components/testimonial-slider-data.js +10 -0
- package/dist/marketing.css +385 -14
- package/package.json +1 -6
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/* GENERATED from pageheader.contract.json. Do not edit. */
|
|
2
|
+
|
|
3
|
+
:where([data-dds-context="marketing"]) .dds-pageheader {
|
|
4
|
+
box-sizing: border-box;
|
|
5
|
+
container-type: inline-size;
|
|
6
|
+
display: flex;
|
|
7
|
+
inline-size: 100%;
|
|
8
|
+
justify-content: center;
|
|
9
|
+
overflow: hidden;
|
|
10
|
+
position: relative;
|
|
11
|
+
background-color: var(--color-bg-brand-strong);
|
|
12
|
+
color: var(--color-fg-on-brand-strong);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
:where([data-dds-context="marketing"]) .dds-pageheader .dds-pageheader__inner {
|
|
16
|
+
box-sizing: border-box;
|
|
17
|
+
display: flex;
|
|
18
|
+
inline-size: 100%;
|
|
19
|
+
min-inline-size: 0;
|
|
20
|
+
position: relative;
|
|
21
|
+
max-inline-size: var(--size-content-max);
|
|
22
|
+
padding-block: var(--space-section-default);
|
|
23
|
+
padding-inline: var(--space-inset-page);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
:where([data-dds-context="marketing"]) .dds-pageheader .dds-pageheader__content {
|
|
27
|
+
display: flex;
|
|
28
|
+
flex-direction: column;
|
|
29
|
+
inline-size: 100%;
|
|
30
|
+
min-inline-size: 0;
|
|
31
|
+
row-gap: var(--space-heading-description-actions);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
:where([data-dds-context="marketing"]) .dds-pageheader .dds-pageheader__text {
|
|
35
|
+
display: flex;
|
|
36
|
+
flex-direction: column;
|
|
37
|
+
min-inline-size: 0;
|
|
38
|
+
max-inline-size: var(--size-measure-hero-heading);
|
|
39
|
+
row-gap: var(--space-gap-none);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
:where([data-dds-context="marketing"]) .dds-pageheader .dds-pageheader__eyebrow {
|
|
43
|
+
margin-block: 0;
|
|
44
|
+
font-family: var(--font-eyebrow-lg-family);
|
|
45
|
+
font-size: var(--font-eyebrow-lg-size);
|
|
46
|
+
font-weight: var(--font-eyebrow-lg-weight);
|
|
47
|
+
letter-spacing: var(--font-eyebrow-lg-letter-spacing);
|
|
48
|
+
line-height: var(--font-eyebrow-lg-line-height);
|
|
49
|
+
margin-block-end: var(--space-heading-eyebrow-title);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
:where([data-dds-context="marketing"]) .dds-pageheader .dds-pageheader__title {
|
|
53
|
+
margin-block: 0;
|
|
54
|
+
text-wrap: balance;
|
|
55
|
+
font-family: var(--font-h1-family);
|
|
56
|
+
font-size: var(--font-h1-size);
|
|
57
|
+
font-weight: var(--font-h1-weight);
|
|
58
|
+
letter-spacing: var(--font-h1-letter-spacing);
|
|
59
|
+
line-height: var(--font-h1-line-height);
|
|
60
|
+
margin-block-end: var(--space-heading-title-description);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
:where([data-dds-context="marketing"]) .dds-pageheader .dds-pageheader__description {
|
|
64
|
+
margin-block: 0;
|
|
65
|
+
font-family: var(--font-body-md-family);
|
|
66
|
+
font-size: var(--font-body-md-size);
|
|
67
|
+
font-weight: var(--font-body-md-weight);
|
|
68
|
+
letter-spacing: var(--font-body-md-letter-spacing);
|
|
69
|
+
line-height: var(--font-body-md-line-height);
|
|
70
|
+
max-inline-size: var(--size-measure-hero-description);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
:where([data-dds-context="marketing"]) .dds-pageheader .dds-pageheader__actions {
|
|
74
|
+
display: flex;
|
|
75
|
+
flex-wrap: wrap;
|
|
76
|
+
column-gap: var(--space-actions-gap);
|
|
77
|
+
row-gap: var(--space-actions-gap);
|
|
78
|
+
}
|
|
79
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import './PageHeader.css';
|
|
2
|
+
import './page-header-impl.css';
|
|
3
|
+
import '../block-reveal.css';
|
|
4
|
+
import { type PageHeaderProps } from './PageHeader.types.js';
|
|
5
|
+
export type { PageHeaderProps };
|
|
6
|
+
/**
|
|
7
|
+
* The band that opens a company or a legal page.
|
|
8
|
+
*
|
|
9
|
+
* The third of three opening bands, and the only one that is not a hero. Which band a page opens
|
|
10
|
+
* with follows from where the page sits rather than from a prop: a landing page takes LandingHero
|
|
11
|
+
* and a product page ProductHero. Separate components rather than one with a switch, so a page
|
|
12
|
+
* cannot be given a tier it is not.
|
|
13
|
+
*
|
|
14
|
+
* Capped rather than a screen tall, which is the whole difference. A reader arrives at Contact or
|
|
15
|
+
* Terms for the content, so the band names the page and gets out of the way — `size.band.compact`
|
|
16
|
+
* is the floor it takes, and the page begins immediately under it.
|
|
17
|
+
*
|
|
18
|
+
* No photograph and no visual slot, which is a decision rather than an omission: there is nothing
|
|
19
|
+
* to show on a page of terms, and a slot for one is a slot somebody eventually fills. What the band
|
|
20
|
+
* paints instead is a faint rule grid over its own ground, drawn in the stylesheet out of a border
|
|
21
|
+
* role — enough to say the band is deliberate rather than a gap.
|
|
22
|
+
*
|
|
23
|
+
* Which also means it has no scrim and needs none. The other two darken a photograph so a title
|
|
24
|
+
* reads over it; here the ground is the component's own and its contrast is already known.
|
|
25
|
+
*
|
|
26
|
+
* The steps inside the text block are carried by each part's own `margin-block-end` rather than by
|
|
27
|
+
* a gap on the block, because eyebrow-to-title and title-to-description are two different roles
|
|
28
|
+
* that agree at full width and part company on a phone.
|
|
29
|
+
*
|
|
30
|
+
* The title carries the band's accessible name through `aria-labelledby`. A `<section>` is only a
|
|
31
|
+
* region landmark once it has a name, and a band that announced itself as an unnamed section is one
|
|
32
|
+
* more thing a screen reader user walks through rather than a place they can skip to.
|
|
33
|
+
*/
|
|
34
|
+
export declare function PageHeader({ eyebrow, title, headingLevel, description, actions, }: PageHeaderProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useId } from 'react';
|
|
4
|
+
import './PageHeader.css';
|
|
5
|
+
import './page-header-impl.css';
|
|
6
|
+
import '../block-reveal.css';
|
|
7
|
+
import { arriving, useReveal } from '../use-reveal.js';
|
|
8
|
+
import { given } from '../../given.js';
|
|
9
|
+
import { pageHeaderDefaults, pageHeaderParts } from './PageHeader.types.js';
|
|
10
|
+
/**
|
|
11
|
+
* The band that opens a company or a legal page.
|
|
12
|
+
*
|
|
13
|
+
* The third of three opening bands, and the only one that is not a hero. Which band a page opens
|
|
14
|
+
* with follows from where the page sits rather than from a prop: a landing page takes LandingHero
|
|
15
|
+
* and a product page ProductHero. Separate components rather than one with a switch, so a page
|
|
16
|
+
* cannot be given a tier it is not.
|
|
17
|
+
*
|
|
18
|
+
* Capped rather than a screen tall, which is the whole difference. A reader arrives at Contact or
|
|
19
|
+
* Terms for the content, so the band names the page and gets out of the way — `size.band.compact`
|
|
20
|
+
* is the floor it takes, and the page begins immediately under it.
|
|
21
|
+
*
|
|
22
|
+
* No photograph and no visual slot, which is a decision rather than an omission: there is nothing
|
|
23
|
+
* to show on a page of terms, and a slot for one is a slot somebody eventually fills. What the band
|
|
24
|
+
* paints instead is a faint rule grid over its own ground, drawn in the stylesheet out of a border
|
|
25
|
+
* role — enough to say the band is deliberate rather than a gap.
|
|
26
|
+
*
|
|
27
|
+
* Which also means it has no scrim and needs none. The other two darken a photograph so a title
|
|
28
|
+
* reads over it; here the ground is the component's own and its contrast is already known.
|
|
29
|
+
*
|
|
30
|
+
* The steps inside the text block are carried by each part's own `margin-block-end` rather than by
|
|
31
|
+
* a gap on the block, because eyebrow-to-title and title-to-description are two different roles
|
|
32
|
+
* that agree at full width and part company on a phone.
|
|
33
|
+
*
|
|
34
|
+
* The title carries the band's accessible name through `aria-labelledby`. A `<section>` is only a
|
|
35
|
+
* region landmark once it has a name, and a band that announced itself as an unnamed section is one
|
|
36
|
+
* more thing a screen reader user walks through rather than a place they can skip to.
|
|
37
|
+
*/
|
|
38
|
+
export function PageHeader({ eyebrow, title, headingLevel = pageHeaderDefaults.headingLevel, description, actions, }) {
|
|
39
|
+
const titleId = useId();
|
|
40
|
+
const [band, revealed] = useReveal();
|
|
41
|
+
const Title = headingLevel === 'h2' ? 'h2' : 'h1';
|
|
42
|
+
return (_jsx("section", { className: pageHeaderParts.root, "aria-labelledby": titleId, children: _jsx("div", { className: pageHeaderParts.inner, ref: band, "data-revealed": revealed, children: _jsxs("div", { className: pageHeaderParts.content, children: [_jsxs("div", { className: pageHeaderParts.text, children: [given(eyebrow) ? (_jsx("p", { className: pageHeaderParts.eyebrow, ...arriving(0), children: eyebrow })) : null, _jsx(Title, { className: pageHeaderParts.title, ...arriving(1), id: titleId, children: title }), given(description) ? (_jsx("p", { className: pageHeaderParts.description, ...arriving(2), children: description })) : null] }), actions ? (_jsx("div", { className: pageHeaderParts.actions, ...arriving(3), children: actions })) : null] }) }) }));
|
|
43
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export interface PageHeaderOwnProps {
|
|
3
|
+
/** A word or two placing the page before its title names it — a product family, a market, a campaign */
|
|
4
|
+
eyebrow?: string;
|
|
5
|
+
/** Always. The one thing the page is about, in the fewest words that still say it. It is the page's leading heading and the band's accessible name */
|
|
6
|
+
title: string;
|
|
7
|
+
/**
|
|
8
|
+
* `h1` — The default, and correct whenever the hero opens the page — which is what a hero is for
|
|
9
|
+
* `h2` — The rare page that already carries its `h1` elsewhere, and would otherwise have two
|
|
10
|
+
*/
|
|
11
|
+
headingLevel?: 'h1' | 'h2';
|
|
12
|
+
/** The sentence under the title that says what the claim above it means, for the reader who is not yet convinced by the claim alone */
|
|
13
|
+
description?: string;
|
|
14
|
+
/** The one or two things the page wants a reader to do. Composed by the page rather than described to this component, so a call to action is the same Button here as anywhere else on the site */
|
|
15
|
+
actions?: ReactNode;
|
|
16
|
+
}
|
|
17
|
+
export type PageHeaderProps = PageHeaderOwnProps;
|
|
18
|
+
/** The class each part carries. Apply these — do not retype them. */
|
|
19
|
+
export declare const pageHeaderParts: {
|
|
20
|
+
readonly root: "dds-pageheader";
|
|
21
|
+
readonly inner: "dds-pageheader__inner";
|
|
22
|
+
readonly content: "dds-pageheader__content";
|
|
23
|
+
readonly text: "dds-pageheader__text";
|
|
24
|
+
readonly eyebrow: "dds-pageheader__eyebrow";
|
|
25
|
+
readonly title: "dds-pageheader__title";
|
|
26
|
+
readonly description: "dds-pageheader__description";
|
|
27
|
+
readonly actions: "dds-pageheader__actions";
|
|
28
|
+
};
|
|
29
|
+
/** Generated so defaults cannot drift from the contract. */
|
|
30
|
+
export declare const pageHeaderDefaults: {
|
|
31
|
+
readonly headingLevel: "h1";
|
|
32
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// GENERATED from page-header.contract.json. Do not edit.
|
|
2
|
+
/** The class each part carries. Apply these — do not retype them. */
|
|
3
|
+
export const pageHeaderParts = {
|
|
4
|
+
root: 'dds-pageheader',
|
|
5
|
+
inner: 'dds-pageheader__inner',
|
|
6
|
+
content: 'dds-pageheader__content',
|
|
7
|
+
text: 'dds-pageheader__text',
|
|
8
|
+
eyebrow: 'dds-pageheader__eyebrow',
|
|
9
|
+
title: 'dds-pageheader__title',
|
|
10
|
+
description: 'dds-pageheader__description',
|
|
11
|
+
actions: 'dds-pageheader__actions',
|
|
12
|
+
};
|
|
13
|
+
/** Generated so defaults cannot drift from the contract. */
|
|
14
|
+
export const pageHeaderDefaults = { headingLevel: 'h1' };
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Scoped to this package, like the generated sheet beside it.
|
|
3
|
+
*
|
|
4
|
+
* `:where([data-dds-context="marketing"])` confines these rules without contributing specificity,
|
|
5
|
+
* so each is decided against the generated rule on its own terms.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
* Capped, not a screen.
|
|
10
|
+
*
|
|
11
|
+
* `size.band.compact` is the floor, and it is the one thing that separates this tier from the two
|
|
12
|
+
* above it — both take `100cqh`. A page opened for its content wants its name and then the
|
|
13
|
+
* content, so the band is held to size.band.compact rather than a screen and the first sentence
|
|
14
|
+
* is on screen with it.
|
|
15
|
+
*
|
|
16
|
+
* `min-block-size` rather than `block-size`, so a long title or a third line of description grows
|
|
17
|
+
* the band instead of being clipped by it.
|
|
18
|
+
*/
|
|
19
|
+
:where([data-dds-context="marketing"]) .dds-pageheader {
|
|
20
|
+
align-items: center;
|
|
21
|
+
min-block-size: var(--size-band-compact);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/*
|
|
25
|
+
* The rule grid, drawn rather than fetched.
|
|
26
|
+
*
|
|
27
|
+
* The reference design paints this with an inline SVG carrying its own brand hex — a 3KB data URI
|
|
28
|
+
* with `#FF7E88` written into it, which is a colour decision hidden where no token can reach and
|
|
29
|
+
* no check can read. Two repeating gradients say the same thing out of `color.border.subtle-inverse`,
|
|
30
|
+
* the role this package already has for a hairline on a dark ground.
|
|
31
|
+
*
|
|
32
|
+
* `background-image` rather than a part of its own: it is a texture on the band's ground, not an
|
|
33
|
+
* element, and a part that rendered an empty div to carry a pattern would be markup standing in
|
|
34
|
+
* for a paint.
|
|
35
|
+
*
|
|
36
|
+
* The line is `border.width.hairline` and the step is `size.rule-grid.pitch`, a role added with this
|
|
37
|
+
* band. A pitch is not a gap — it is how far apart a decorative line recurs, not the space between
|
|
38
|
+
* two things a reader is comparing — so it earns a name of its own rather than borrowing one.
|
|
39
|
+
*/
|
|
40
|
+
:where([data-dds-context="marketing"]) .dds-pageheader {
|
|
41
|
+
background-image:
|
|
42
|
+
/*
|
|
43
|
+
* The fade, painted over the grid rather than into it.
|
|
44
|
+
*
|
|
45
|
+
* A background layer list paints first-listed on top, so this sits above both sets of rules
|
|
46
|
+
* and below everything the band holds — the heading is a child element and children paint
|
|
47
|
+
* above every background their box carries, so the fade reaches the grid and nothing else.
|
|
48
|
+
*
|
|
49
|
+
* It fades *from* the band's own ground, which is how a paint covers something without a mask:
|
|
50
|
+
* opaque at the top, where the rules are covered, and transparent at the foot, where they come
|
|
51
|
+
* through. `color.bg.brand-strong` is the same role the band paints beneath, so the fade and
|
|
52
|
+
* the ground are one colour and the grid is the only thing that changes.
|
|
53
|
+
*
|
|
54
|
+
* That way round because the band opens under a navigation bar and closes against the page:
|
|
55
|
+
* the rules gather towards the edge the content carries on over, so the grid reads as the
|
|
56
|
+
* ground the page is built on rather than as a lid over the heading.
|
|
57
|
+
*
|
|
58
|
+
* Masking the element instead would have faded the title with it — the grid is a background
|
|
59
|
+
* and the words are not, and a mask cannot tell them apart.
|
|
60
|
+
*/
|
|
61
|
+
linear-gradient(to bottom, var(--color-bg-brand-strong), transparent),
|
|
62
|
+
/*
|
|
63
|
+
* `to right` is physical, and it is the one place in these blocks that is.
|
|
64
|
+
*
|
|
65
|
+
* A CSS gradient has no logical direction — there is no `to inline-end` — so a pattern that
|
|
66
|
+
* must mirror has to be written twice under a direction query. This one does not: the rules
|
|
67
|
+
* repeat on a fixed pitch and the whole field is centred, so the same pixels land in the same
|
|
68
|
+
* places whichever way the page is read. A grid of evenly spaced lines has no leading edge to
|
|
69
|
+
* get wrong.
|
|
70
|
+
*
|
|
71
|
+
* Said out loud because the next pattern here may not be periodic, and a reader who copies
|
|
72
|
+
* this line into one that is would mirror nothing. See the `written-along-the-reading-axis`
|
|
73
|
+
* rule.
|
|
74
|
+
*/
|
|
75
|
+
repeating-linear-gradient(
|
|
76
|
+
to right,
|
|
77
|
+
var(--color-border-subtle-inverse) 0 var(--border-width-hairline),
|
|
78
|
+
transparent var(--border-width-hairline) var(--size-rule-grid-pitch)
|
|
79
|
+
),
|
|
80
|
+
/*
|
|
81
|
+
* The horizontal rules run up from the foot, not down from the top.
|
|
82
|
+
*
|
|
83
|
+
* Drawn `to bottom` and centred, the last rule landed wherever the band's height left it — a
|
|
84
|
+
* line short of the edge on one page, a line past it on the next. `to top` starts the pattern at
|
|
85
|
+
* the bottom edge, so the first rule is the band's own lower edge and every other one is a
|
|
86
|
+
* whole pitch above it, whatever the title and description make the height. The fade covers
|
|
87
|
+
* the top, which is the only end that can now fall between two rules.
|
|
88
|
+
*/
|
|
89
|
+
repeating-linear-gradient(
|
|
90
|
+
to top,
|
|
91
|
+
var(--color-border-subtle-inverse) 0 var(--border-width-hairline),
|
|
92
|
+
transparent var(--border-width-hairline) var(--size-rule-grid-pitch)
|
|
93
|
+
);
|
|
94
|
+
background-position: center;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/*
|
|
98
|
+
* A title with nothing under it carries no step under it.
|
|
99
|
+
*
|
|
100
|
+
* The steps inside the text block sit on each part's own `margin-block-end`, because eyebrow-to-
|
|
101
|
+
* title and title-to-description are two roles that agree at full width and part company on a
|
|
102
|
+
* phone. The cost is a trailing margin: with `description` absent the title is the last child and
|
|
103
|
+
* its step is measured against nothing.
|
|
104
|
+
*/
|
|
105
|
+
:where([data-dds-context="marketing"]) .dds-pageheader .dds-pageheader__title:last-child {
|
|
106
|
+
margin-block-end: 0;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/*
|
|
110
|
+
* Starts on a phone, centred once the band is wide enough to hold a line.
|
|
111
|
+
*
|
|
112
|
+
* The same rule the product tier follows, and the reference design applies it to both: centred
|
|
113
|
+
* text is read from a different starting edge on every line, which costs little across a wide
|
|
114
|
+
* measure and a great deal across a narrow one.
|
|
115
|
+
*
|
|
116
|
+
* `size.measure.hero-heading` caps the block at either width; `margin-inline: auto` is what centres
|
|
117
|
+
* that capped block rather than leaving it at the column's leading edge with its text centred
|
|
118
|
+
* inside it.
|
|
119
|
+
*/
|
|
120
|
+
@container (min-width: 48rem) {
|
|
121
|
+
:where([data-dds-context="marketing"]) .dds-pageheader .dds-pageheader__content {
|
|
122
|
+
align-items: center;
|
|
123
|
+
text-align: center;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
:where([data-dds-context="marketing"]) .dds-pageheader .dds-pageheader__text {
|
|
127
|
+
margin-inline: auto;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/*
|
|
131
|
+
* And the line under the title, which is held to a narrower measure than the block it sits in.
|
|
132
|
+
*
|
|
133
|
+
* `text` runs to a heading's measure and the description to a reading one, so the description's
|
|
134
|
+
* box is the narrower of the two and was sitting against the start edge of a centred block. Its
|
|
135
|
+
* words were centred within it, so what a reader saw was a centred title over a paragraph that
|
|
136
|
+
* had slipped left — the kind of misalignment that looks like a mistake in the copy rather than
|
|
137
|
+
* in a box nobody can see.
|
|
138
|
+
*
|
|
139
|
+
* Only where the block is centred. Below the fold everything ranges to the start and an auto margin
|
|
140
|
+
* would centre the one part that should not be.
|
|
141
|
+
*/
|
|
142
|
+
:where([data-dds-context="marketing"]) .dds-pageheader .dds-pageheader__description {
|
|
143
|
+
margin-inline: auto;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
:where([data-dds-context="marketing"]) .dds-pageheader .dds-pageheader__actions {
|
|
147
|
+
justify-content: center;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/*
|
|
152
|
+
* Two calls to action stack on a phone and stand side by side once there is room.
|
|
153
|
+
*
|
|
154
|
+
* The reference design makes each one full width at the narrow end — a button that hugs its label
|
|
155
|
+
* on a phone is a tap target sized by how long the word happens to be — and lets them hug once the
|
|
156
|
+
* band is wide enough to hold both on a line.
|
|
157
|
+
*/
|
|
158
|
+
:where([data-dds-context="marketing"]) .dds-pageheader .dds-pageheader__actions {
|
|
159
|
+
align-items: stretch;
|
|
160
|
+
flex-direction: column;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
:where([data-dds-context="marketing"]) .dds-pageheader .dds-pageheader__actions > * {
|
|
164
|
+
inline-size: 100%;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
@container (min-width: 48rem) {
|
|
168
|
+
:where([data-dds-context="marketing"]) .dds-pageheader .dds-pageheader__actions {
|
|
169
|
+
align-items: center;
|
|
170
|
+
flex-direction: row;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
:where([data-dds-context="marketing"]) .dds-pageheader .dds-pageheader__actions > * {
|
|
174
|
+
inline-size: auto;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/* GENERATED from producthero.contract.json. Do not edit. */
|
|
2
|
+
|
|
3
|
+
:where([data-dds-context="marketing"]) .dds-producthero {
|
|
4
|
+
box-sizing: border-box;
|
|
5
|
+
container-type: inline-size;
|
|
6
|
+
display: flex;
|
|
7
|
+
inline-size: 100%;
|
|
8
|
+
justify-content: center;
|
|
9
|
+
overflow: hidden;
|
|
10
|
+
position: relative;
|
|
11
|
+
background-color: var(--color-bg-brand-strong);
|
|
12
|
+
color: var(--color-fg-on-brand-strong);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
:where([data-dds-context="marketing"]) .dds-producthero .dds-producthero__inner {
|
|
16
|
+
box-sizing: border-box;
|
|
17
|
+
display: flex;
|
|
18
|
+
inline-size: 100%;
|
|
19
|
+
min-inline-size: 0;
|
|
20
|
+
position: relative;
|
|
21
|
+
max-inline-size: var(--size-content-max);
|
|
22
|
+
padding-block: var(--space-section-default);
|
|
23
|
+
padding-inline: var(--space-inset-page);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
:where([data-dds-context="marketing"]) .dds-producthero .dds-producthero__content {
|
|
27
|
+
display: flex;
|
|
28
|
+
flex-direction: column;
|
|
29
|
+
inline-size: 100%;
|
|
30
|
+
min-inline-size: 0;
|
|
31
|
+
row-gap: var(--space-heading-description-actions);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
:where([data-dds-context="marketing"]) .dds-producthero .dds-producthero__text {
|
|
35
|
+
display: flex;
|
|
36
|
+
flex-direction: column;
|
|
37
|
+
min-inline-size: 0;
|
|
38
|
+
row-gap: var(--space-gap-none);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
:where([data-dds-context="marketing"]) .dds-producthero .dds-producthero__eyebrow {
|
|
42
|
+
margin-block: 0;
|
|
43
|
+
font-family: var(--font-eyebrow-lg-family);
|
|
44
|
+
font-size: var(--font-eyebrow-lg-size);
|
|
45
|
+
font-weight: var(--font-eyebrow-lg-weight);
|
|
46
|
+
letter-spacing: var(--font-eyebrow-lg-letter-spacing);
|
|
47
|
+
line-height: var(--font-eyebrow-lg-line-height);
|
|
48
|
+
margin-block-end: var(--space-heading-eyebrow-title);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
:where([data-dds-context="marketing"]) .dds-producthero .dds-producthero__title {
|
|
52
|
+
margin-block: 0;
|
|
53
|
+
text-wrap: balance;
|
|
54
|
+
font-family: var(--font-h1-family);
|
|
55
|
+
font-size: var(--font-h1-size);
|
|
56
|
+
font-weight: var(--font-h1-weight);
|
|
57
|
+
letter-spacing: var(--font-h1-letter-spacing);
|
|
58
|
+
line-height: var(--font-h1-line-height);
|
|
59
|
+
margin-block-end: var(--space-heading-title-description);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
:where([data-dds-context="marketing"]) .dds-producthero .dds-producthero__description {
|
|
63
|
+
margin-block: 0;
|
|
64
|
+
font-family: var(--font-body-md-family);
|
|
65
|
+
font-size: var(--font-body-md-size);
|
|
66
|
+
font-weight: var(--font-body-md-weight);
|
|
67
|
+
letter-spacing: var(--font-body-md-letter-spacing);
|
|
68
|
+
line-height: var(--font-body-md-line-height);
|
|
69
|
+
max-inline-size: var(--size-measure-hero-description);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
:where([data-dds-context="marketing"]) .dds-producthero .dds-producthero__actions {
|
|
73
|
+
display: flex;
|
|
74
|
+
flex-wrap: wrap;
|
|
75
|
+
column-gap: var(--space-actions-gap);
|
|
76
|
+
row-gap: var(--space-actions-gap);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
:where([data-dds-context="marketing"]) .dds-producthero .dds-producthero__media {
|
|
80
|
+
min-inline-size: 0;
|
|
81
|
+
overflow: hidden;
|
|
82
|
+
position: relative;
|
|
83
|
+
aspect-ratio: var(--aspect-square);
|
|
84
|
+
border-radius: var(--radius-none);
|
|
85
|
+
}
|
|
86
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import './ProductHero.css';
|
|
2
|
+
import './product-hero-impl.css';
|
|
3
|
+
import '../block-reveal.css';
|
|
4
|
+
import { type ProductHeroProps } from './ProductHero.types.js';
|
|
5
|
+
export type { ProductHeroProps };
|
|
6
|
+
/**
|
|
7
|
+
* The band that opens a product or a market page.
|
|
8
|
+
*
|
|
9
|
+
* The product tier's band, and one of three. Which band a page opens with follows from where the
|
|
10
|
+
* page sits rather than from a prop: a landing page takes LandingHero and a legal page PageHeader.
|
|
11
|
+
* Separate components rather than one with a switch, so a page cannot be given a tier it is not.
|
|
12
|
+
*
|
|
13
|
+
* One column, centred, read down: what the page is about, what a reader can do about it, and then
|
|
14
|
+
* the thing itself. That last part is the whole difference from the tier above. LandingHero ranges
|
|
15
|
+
* its claim to the start and places a subject against the band's own edge, cropped by it; here the claim
|
|
16
|
+
* goes over the middle and the product sits under it, in the column, whole and capped to the same
|
|
17
|
+
* measure as the text. A product page is about one thing, and nothing should cut into it.
|
|
18
|
+
*
|
|
19
|
+
* Which is why `media` is a child of `content` here and a child of the band there. The same slot, in two
|
|
20
|
+
* places, because the two tiers answer different questions about what a visual is for.
|
|
21
|
+
*
|
|
22
|
+
* `root` spans the full width and `inner` holds the measure — the arrangement Footer uses, and for
|
|
23
|
+
* the same reason: a ground stopping at the measure would read as a wide card at the top of the
|
|
24
|
+
* page rather than as the page beginning. The ground is dark and the ink is set once on the root,
|
|
25
|
+
* inherited by everything inside it.
|
|
26
|
+
*
|
|
27
|
+
* The steps inside the text block are carried by each part's own `margin-block-end` rather than by
|
|
28
|
+
* a gap on the block, because eyebrow-to-title and title-to-description are two different roles
|
|
29
|
+
* that agree at full width and part company on a phone.
|
|
30
|
+
*
|
|
31
|
+
* The title carries the band's accessible name through `aria-labelledby`. A `<section>` is only a
|
|
32
|
+
* region landmark once it has a name, and a band that announced itself as an unnamed section is one
|
|
33
|
+
* more thing a screen reader user walks through rather than a place they can skip to.
|
|
34
|
+
*/
|
|
35
|
+
export declare function ProductHero({ eyebrow, title, headingLevel, description, actions, media, backgroundImage, }: ProductHeroProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useId } from 'react';
|
|
4
|
+
import './ProductHero.css';
|
|
5
|
+
import './product-hero-impl.css';
|
|
6
|
+
import '../block-reveal.css';
|
|
7
|
+
import { arriving, useReveal } from '../use-reveal.js';
|
|
8
|
+
import { given } from '../../given.js';
|
|
9
|
+
import { productHeroDefaults, productHeroParts, } from './ProductHero.types.js';
|
|
10
|
+
/**
|
|
11
|
+
* The band that opens a product or a market page.
|
|
12
|
+
*
|
|
13
|
+
* The product tier's band, and one of three. Which band a page opens with follows from where the
|
|
14
|
+
* page sits rather than from a prop: a landing page takes LandingHero and a legal page PageHeader.
|
|
15
|
+
* Separate components rather than one with a switch, so a page cannot be given a tier it is not.
|
|
16
|
+
*
|
|
17
|
+
* One column, centred, read down: what the page is about, what a reader can do about it, and then
|
|
18
|
+
* the thing itself. That last part is the whole difference from the tier above. LandingHero ranges
|
|
19
|
+
* its claim to the start and places a subject against the band's own edge, cropped by it; here the claim
|
|
20
|
+
* goes over the middle and the product sits under it, in the column, whole and capped to the same
|
|
21
|
+
* measure as the text. A product page is about one thing, and nothing should cut into it.
|
|
22
|
+
*
|
|
23
|
+
* Which is why `media` is a child of `content` here and a child of the band there. The same slot, in two
|
|
24
|
+
* places, because the two tiers answer different questions about what a visual is for.
|
|
25
|
+
*
|
|
26
|
+
* `root` spans the full width and `inner` holds the measure — the arrangement Footer uses, and for
|
|
27
|
+
* the same reason: a ground stopping at the measure would read as a wide card at the top of the
|
|
28
|
+
* page rather than as the page beginning. The ground is dark and the ink is set once on the root,
|
|
29
|
+
* inherited by everything inside it.
|
|
30
|
+
*
|
|
31
|
+
* The steps inside the text block are carried by each part's own `margin-block-end` rather than by
|
|
32
|
+
* a gap on the block, because eyebrow-to-title and title-to-description are two different roles
|
|
33
|
+
* that agree at full width and part company on a phone.
|
|
34
|
+
*
|
|
35
|
+
* The title carries the band's accessible name through `aria-labelledby`. A `<section>` is only a
|
|
36
|
+
* region landmark once it has a name, and a band that announced itself as an unnamed section is one
|
|
37
|
+
* more thing a screen reader user walks through rather than a place they can skip to.
|
|
38
|
+
*/
|
|
39
|
+
export function ProductHero({ eyebrow, title, headingLevel = productHeroDefaults.headingLevel, description, actions, media, backgroundImage, }) {
|
|
40
|
+
const titleId = useId();
|
|
41
|
+
const [band, revealed] = useReveal();
|
|
42
|
+
const Title = headingLevel === 'h2' ? 'h2' : 'h1';
|
|
43
|
+
return (_jsx("section", { className: productHeroParts.root, "aria-labelledby": titleId,
|
|
44
|
+
/* The one inline style here, and it is a value rather than a decision: which photograph,
|
|
45
|
+
supplied by the page. How it is drawn — the cover fit and the position —
|
|
46
|
+
is in the stylesheet, where it can be reasoned about. */
|
|
47
|
+
style: given(backgroundImage) ? { backgroundImage: `url(${backgroundImage})` } : undefined, children: _jsx("div", { className: productHeroParts.inner, ref: band, "data-revealed": revealed, children: _jsxs("div", { className: productHeroParts.content, children: [_jsxs("div", { className: productHeroParts.text, children: [given(eyebrow) ? (_jsx("p", { className: productHeroParts.eyebrow, ...arriving(0), children: eyebrow })) : null, _jsx(Title, { className: productHeroParts.title, ...arriving(1), id: titleId, children: title }), given(description) ? (_jsx("p", { className: productHeroParts.description, ...arriving(2), children: description })) : null] }), actions ? (_jsx("div", { className: productHeroParts.actions, ...arriving(3), children: actions })) : null, media ? (_jsx("div", { className: productHeroParts.media, ...arriving(4), "aria-hidden": "true", children: media })) : null] }) }) }));
|
|
48
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export interface ProductHeroOwnProps {
|
|
3
|
+
/** A word or two placing the page before its title names it — a product family, a market, a campaign */
|
|
4
|
+
eyebrow?: string;
|
|
5
|
+
/** Always. The one thing the page is about, in the fewest words that still say it. It is the page's leading heading and the band's accessible name */
|
|
6
|
+
title: string;
|
|
7
|
+
/**
|
|
8
|
+
* `h1` — The default, and correct whenever the hero opens the page — which is what a hero is for
|
|
9
|
+
* `h2` — The rare page that already carries its `h1` elsewhere, and would otherwise have two
|
|
10
|
+
*/
|
|
11
|
+
headingLevel?: 'h1' | 'h2';
|
|
12
|
+
/** The sentence under the title that says what the claim above it means, for the reader who is not yet convinced by the claim alone */
|
|
13
|
+
description?: string;
|
|
14
|
+
/** The one or two things the page wants a reader to do. Composed by the page rather than described to this component, so a call to action is the same Button here as anywhere else on the site */
|
|
15
|
+
actions?: ReactNode;
|
|
16
|
+
/** The product itself, under the claim and the actions — in the column, capped to the measure, and whole */
|
|
17
|
+
media?: ReactNode;
|
|
18
|
+
/** A photograph filling the whole band. It is shown as it is — nothing is painted over it, so choose one already dark enough that the title reads on it */
|
|
19
|
+
backgroundImage?: string;
|
|
20
|
+
}
|
|
21
|
+
export type ProductHeroProps = ProductHeroOwnProps;
|
|
22
|
+
/** The class each part carries. Apply these — do not retype them. */
|
|
23
|
+
export declare const productHeroParts: {
|
|
24
|
+
readonly root: "dds-producthero";
|
|
25
|
+
readonly inner: "dds-producthero__inner";
|
|
26
|
+
readonly content: "dds-producthero__content";
|
|
27
|
+
readonly text: "dds-producthero__text";
|
|
28
|
+
readonly eyebrow: "dds-producthero__eyebrow";
|
|
29
|
+
readonly title: "dds-producthero__title";
|
|
30
|
+
readonly description: "dds-producthero__description";
|
|
31
|
+
readonly actions: "dds-producthero__actions";
|
|
32
|
+
readonly media: "dds-producthero__media";
|
|
33
|
+
};
|
|
34
|
+
/** Generated so defaults cannot drift from the contract. */
|
|
35
|
+
export declare const productHeroDefaults: {
|
|
36
|
+
readonly headingLevel: "h1";
|
|
37
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// GENERATED from product-hero.contract.json. Do not edit.
|
|
2
|
+
/** The class each part carries. Apply these — do not retype them. */
|
|
3
|
+
export const productHeroParts = {
|
|
4
|
+
root: 'dds-producthero',
|
|
5
|
+
inner: 'dds-producthero__inner',
|
|
6
|
+
content: 'dds-producthero__content',
|
|
7
|
+
text: 'dds-producthero__text',
|
|
8
|
+
eyebrow: 'dds-producthero__eyebrow',
|
|
9
|
+
title: 'dds-producthero__title',
|
|
10
|
+
description: 'dds-producthero__description',
|
|
11
|
+
actions: 'dds-producthero__actions',
|
|
12
|
+
media: 'dds-producthero__media',
|
|
13
|
+
};
|
|
14
|
+
/** Generated so defaults cannot drift from the contract. */
|
|
15
|
+
export const productHeroDefaults = { headingLevel: 'h1' };
|