@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,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The messages a CtaBannerSlider rotates between.
|
|
3
|
+
*
|
|
4
|
+
* Exported from the package for the reason `scroll-data.ts` is: a block whose content is a list is
|
|
5
|
+
* one whose consumer builds a value before passing it, and building it without the type means
|
|
6
|
+
* writing the shape out twice.
|
|
7
|
+
*
|
|
8
|
+
* A record rather than a component per slide, by decision on 2026-10-01. A slide was CtaBannerSlide,
|
|
9
|
+
* a child that took its actions as elements; it existed only so a slide could carry a Button, and it
|
|
10
|
+
* was a second component to learn for what is one block. As data, the set can come from a CMS, and
|
|
11
|
+
* the slider draws the buttons itself — the primary as Button variant="inverse", the second as the
|
|
12
|
+
* white-tinted secondary, both at tone="brand" — so no slide can put a coral button on the coral
|
|
13
|
+
* ground. What it costs is the slot: an action is a label and a href, not a Button the page composed.
|
|
14
|
+
*/
|
|
15
|
+
/** A way onward from a slide: the words on the button and where it goes. */
|
|
16
|
+
export interface CtaBannerAction {
|
|
17
|
+
/** What the button says, as a verb — 'Open account', 'Try TradersView'. */
|
|
18
|
+
label: string;
|
|
19
|
+
/** Where it goes. A slide's action is always a link; a banner that runs code on press is not a banner. */
|
|
20
|
+
href: string;
|
|
21
|
+
}
|
|
22
|
+
/** One message in a CtaBannerSlider: the headline, the line under it, and what it asks for. */
|
|
23
|
+
export interface CtaBannerSlide {
|
|
24
|
+
/**
|
|
25
|
+
* The message, in a few words. Short enough to take in as the slide arrives; drawn as a heading
|
|
26
|
+
* at font.h1, so a reader moving by heading finds the message in view.
|
|
27
|
+
*/
|
|
28
|
+
title: string;
|
|
29
|
+
/** A line saying why the action is worth taking. A sentence, not a paragraph. */
|
|
30
|
+
description?: string;
|
|
31
|
+
/** The action the message asks for, drawn as Button variant="inverse" tone="brand" — near-black in both themes. */
|
|
32
|
+
primaryAction?: CtaBannerAction;
|
|
33
|
+
/**
|
|
34
|
+
* A second, lesser action, drawn as the white-tinted secondary, Button tone="brand". Drawn only beside a
|
|
35
|
+
* primary: a slide with one action makes it the primary, the rule Navbar holds its pair to.
|
|
36
|
+
*/
|
|
37
|
+
secondaryAction?: CtaBannerAction;
|
|
38
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The messages a CtaBannerSlider rotates between.
|
|
3
|
+
*
|
|
4
|
+
* Exported from the package for the reason `scroll-data.ts` is: a block whose content is a list is
|
|
5
|
+
* one whose consumer builds a value before passing it, and building it without the type means
|
|
6
|
+
* writing the shape out twice.
|
|
7
|
+
*
|
|
8
|
+
* A record rather than a component per slide, by decision on 2026-10-01. A slide was CtaBannerSlide,
|
|
9
|
+
* a child that took its actions as elements; it existed only so a slide could carry a Button, and it
|
|
10
|
+
* was a second component to learn for what is one block. As data, the set can come from a CMS, and
|
|
11
|
+
* the slider draws the buttons itself — the primary as Button variant="inverse", the second as the
|
|
12
|
+
* white-tinted secondary, both at tone="brand" — so no slide can put a coral button on the coral
|
|
13
|
+
* ground. What it costs is the slot: an action is a label and a href, not a Button the page composed.
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The arrival every block shares, defined once.
|
|
3
|
+
*
|
|
4
|
+
* A section's parts wait a short way below where they land and come up in reading order as a
|
|
5
|
+
* reader reaches the block. It is the only motion these blocks have in common, and a copy per
|
|
6
|
+
* block is how six sections come to disagree about what arriving looks like — one tuned, five not,
|
|
7
|
+
* and nothing to say which is the house answer. So the movement is stated here and each block's
|
|
8
|
+
* own stylesheet says which of its parts make it.
|
|
9
|
+
*
|
|
10
|
+
* Imported by each block's component, which is what puts it in the bundle once however many
|
|
11
|
+
* blocks a page uses.
|
|
12
|
+
*
|
|
13
|
+
* `data-revealed` goes on the box that is watched — see `use-reveal.ts`, which explains why
|
|
14
|
+
* neither trigger CSS can offer on its own is enough. `data-reveal-at` marks each part inside it
|
|
15
|
+
* that arrives, and the same number is set as `--dds-reveal-at` for the arithmetic: an attribute
|
|
16
|
+
* is what CSS can select on, and a custom property is what it can multiply. Two spellings of one
|
|
17
|
+
* index, each doing the job the other cannot.
|
|
18
|
+
*
|
|
19
|
+
* The waiting state is `[data-revealed="false"]` rather than the absence of a value, so a block
|
|
20
|
+
* rendered before any script has run — or where none ever will — is visible rather than hidden.
|
|
21
|
+
* Content behind a script is the one failure this could have that is worse than no motion.
|
|
22
|
+
*
|
|
23
|
+
* Every value is a token, and three of them are the set this system already had for the job:
|
|
24
|
+
* `motion.duration.reveal` is described as "content appearing on scroll, section reveals, feature
|
|
25
|
+
* items fading in", `motion.easing.reveal` is its curve, and `motion.translate.reveal` is the
|
|
26
|
+
* distance. `opacity.hidden` is what a part waits at and `motion.duration.stagger` is the step
|
|
27
|
+
* between one part and the next.
|
|
28
|
+
*
|
|
29
|
+
* The duration was `motion.duration.dramatic` for a while, which is the role for an editorial hero
|
|
30
|
+
* entrance — right while the opening bands were on a timer and wrong once every block arrived on a
|
|
31
|
+
* reader reaching it. Two roles were named for these two triggers and the work had drifted onto
|
|
32
|
+
* the wrong one.
|
|
33
|
+
*/
|
|
34
|
+
/*
|
|
35
|
+
* Waiting is instant; only arriving is animated.
|
|
36
|
+
*
|
|
37
|
+
* A transition is decided by the state a thing is moving *to*, so declaring it on both states
|
|
38
|
+
* animates the way in and the way out alike — and the way out is not a thing anybody should see.
|
|
39
|
+
* A block is marked as waiting once, in a layout effect, before the browser has painted; if that
|
|
40
|
+
* ever lands a frame late the block has already been drawn, and an animated hide turns one dropped
|
|
41
|
+
* frame into three-quarters of a second of the section fading away in front of a reader. That is
|
|
42
|
+
* exactly what it looked like.
|
|
43
|
+
*
|
|
44
|
+
* `transition: none` here rather than nothing, because the arrived rule below is what carries the
|
|
45
|
+
* timings and a part moving back to waiting must not inherit them.
|
|
46
|
+
*
|
|
47
|
+
* Both spellings on every rule: a part is usually inside the box that is watched, and sometimes it
|
|
48
|
+
* is that box. FeatureScroll's heading is the case — the section watches the heading itself, and
|
|
49
|
+
* wrapping it in something whose only job was to be a container would put a box in the markup to
|
|
50
|
+
* satisfy a stylesheet.
|
|
51
|
+
*/
|
|
52
|
+
:where([data-dds-context="marketing"]) [data-revealed='false'] [data-reveal-at],
|
|
53
|
+
:where([data-dds-context="marketing"]) [data-revealed='false'][data-reveal-at] {
|
|
54
|
+
opacity: var(--opacity-hidden);
|
|
55
|
+
transition: none;
|
|
56
|
+
translate: var(--motion-translate-reveal);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/*
|
|
60
|
+
* Each part a step behind the one before it.
|
|
61
|
+
*
|
|
62
|
+
* On the way in only. The delay is declared on the arrived state, which is the state a part is
|
|
63
|
+
* transitioning *to* — so a block that is somehow put back to waiting drops without a queue.
|
|
64
|
+
*
|
|
65
|
+
* A block arrives as one thing that happens to be made of parts, which is what the step size is
|
|
66
|
+
* for: at a tenth of a second, five parts trail the first by less than half a second, well inside
|
|
67
|
+
* the movement's own duration. A longer step would be the section assembling itself while a reader
|
|
68
|
+
* waits for the sentence they came to read.
|
|
69
|
+
*/
|
|
70
|
+
:where([data-dds-context="marketing"]) [data-revealed='true'] [data-reveal-at],
|
|
71
|
+
:where([data-dds-context="marketing"]) [data-revealed='true'][data-reveal-at] {
|
|
72
|
+
transition-delay: calc(var(--dds-reveal-at, 0) * var(--motion-duration-stagger));
|
|
73
|
+
transition-duration: var(--motion-duration-reveal);
|
|
74
|
+
transition-property: opacity, translate;
|
|
75
|
+
transition-timing-function: var(--motion-easing-reveal);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/*
|
|
79
|
+
* A reader who asked for less movement gets none of it.
|
|
80
|
+
*
|
|
81
|
+
* The hook declines to hide anything under this preference, so the parts are never in the waiting
|
|
82
|
+
* state to begin with. This is the second half of the same answer: if a part is somehow marked as
|
|
83
|
+
* waiting, it still arrives without travelling and without queueing behind its neighbours.
|
|
84
|
+
*/
|
|
85
|
+
@media (prefers-reduced-motion: reduce) {
|
|
86
|
+
:where([data-dds-context="marketing"]) [data-revealed] [data-reveal-at],
|
|
87
|
+
:where([data-dds-context="marketing"]) [data-revealed][data-reveal-at] {
|
|
88
|
+
transition: none;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/* GENERATED from ctabanner.contract.json. Do not edit. */
|
|
2
|
+
|
|
3
|
+
:where([data-dds-context="marketing"]) .dds-ctabanner {
|
|
4
|
+
box-sizing: border-box;
|
|
5
|
+
container-type: inline-size;
|
|
6
|
+
display: flex;
|
|
7
|
+
inline-size: 100%;
|
|
8
|
+
justify-content: center;
|
|
9
|
+
position: relative;
|
|
10
|
+
background-color: var(--color-bg-brand);
|
|
11
|
+
color: var(--color-fg-on-brand);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
:where([data-dds-context="marketing"]) .dds-ctabanner .dds-ctabanner__inner {
|
|
15
|
+
box-sizing: border-box;
|
|
16
|
+
display: flex;
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
inline-size: 100%;
|
|
19
|
+
min-inline-size: 0;
|
|
20
|
+
max-inline-size: var(--size-content-max);
|
|
21
|
+
padding-block: var(--space-section-default);
|
|
22
|
+
padding-inline: var(--space-inset-page);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
:where([data-dds-context="marketing"]) .dds-ctabanner .dds-ctabanner__content {
|
|
26
|
+
display: flex;
|
|
27
|
+
flex-direction: column;
|
|
28
|
+
min-inline-size: 0;
|
|
29
|
+
row-gap: var(--space-heading-description-actions);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
:where([data-dds-context="marketing"]) .dds-ctabanner .dds-ctabanner__text {
|
|
33
|
+
display: flex;
|
|
34
|
+
flex-direction: column;
|
|
35
|
+
min-inline-size: 0;
|
|
36
|
+
max-inline-size: var(--size-measure-heading);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
:where([data-dds-context="marketing"]) .dds-ctabanner .dds-ctabanner__title {
|
|
40
|
+
margin-block: 0;
|
|
41
|
+
text-wrap: balance;
|
|
42
|
+
font-family: var(--font-h1-family);
|
|
43
|
+
font-size: var(--font-h1-size);
|
|
44
|
+
font-weight: var(--font-h1-weight);
|
|
45
|
+
letter-spacing: var(--font-h1-letter-spacing);
|
|
46
|
+
line-height: var(--font-h1-line-height);
|
|
47
|
+
margin-block-end: var(--space-heading-title-description);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
:where([data-dds-context="marketing"]) .dds-ctabanner .dds-ctabanner__description {
|
|
51
|
+
margin-block: 0;
|
|
52
|
+
font-family: var(--font-body-lg-family);
|
|
53
|
+
font-size: var(--font-body-lg-size);
|
|
54
|
+
font-weight: var(--font-body-lg-weight);
|
|
55
|
+
letter-spacing: var(--font-body-lg-letter-spacing);
|
|
56
|
+
line-height: var(--font-body-lg-line-height);
|
|
57
|
+
max-inline-size: var(--size-measure-description);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
:where([data-dds-context="marketing"]) .dds-ctabanner .dds-ctabanner__actions {
|
|
61
|
+
display: flex;
|
|
62
|
+
flex-wrap: wrap;
|
|
63
|
+
column-gap: var(--space-actions-gap);
|
|
64
|
+
row-gap: var(--space-actions-gap);
|
|
65
|
+
}
|
|
66
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import './CtaBanner.css';
|
|
2
|
+
import './cta-banner-impl.css';
|
|
3
|
+
import '../block-reveal.css';
|
|
4
|
+
import { type CtaBannerProps } from './CtaBanner.types.js';
|
|
5
|
+
export type { CtaBannerProps };
|
|
6
|
+
/**
|
|
7
|
+
* A full-bleed coral band carrying one promotion and the action it asks for.
|
|
8
|
+
*
|
|
9
|
+
* The reference site's CoralBanner in its single form, named for what it is for. The ground is
|
|
10
|
+
* color.bg.brand and the ink color.fg.on-brand, both the same in light and dark, so the banner is
|
|
11
|
+
* the same coral whatever theme the reader is in. Nothing moves: two or three promotions to rotate
|
|
12
|
+
* between are CtaBannerSlider, which is this band with a slider in it — the same column, the same
|
|
13
|
+
* headline and line, so a page can carry either and read the same.
|
|
14
|
+
*
|
|
15
|
+
* The region is named by its headline, the way PageHeader's is, so a reader can jump to the band
|
|
16
|
+
* and past it. `'use client'` only for `useId`, which ties the region to its headline — the same
|
|
17
|
+
* reason PageHeader carries it.
|
|
18
|
+
*
|
|
19
|
+
* It arrives the way every block does, and the way CtaBannerSlider's slides do: block-reveal.css
|
|
20
|
+
* brings the headline, the line and the actions up a step apart once the band is in view.
|
|
21
|
+
*/
|
|
22
|
+
export declare function CtaBanner({ title, headingLevel, description, actions, }: CtaBannerProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useId } from 'react';
|
|
4
|
+
import './CtaBanner.css';
|
|
5
|
+
import './cta-banner-impl.css';
|
|
6
|
+
import '../block-reveal.css';
|
|
7
|
+
import { arriving, useReveal } from '../use-reveal.js';
|
|
8
|
+
import { given } from '../../given.js';
|
|
9
|
+
import { ctaBannerDefaults, ctaBannerParts } from './CtaBanner.types.js';
|
|
10
|
+
/**
|
|
11
|
+
* A full-bleed coral band carrying one promotion and the action it asks for.
|
|
12
|
+
*
|
|
13
|
+
* The reference site's CoralBanner in its single form, named for what it is for. The ground is
|
|
14
|
+
* color.bg.brand and the ink color.fg.on-brand, both the same in light and dark, so the banner is
|
|
15
|
+
* the same coral whatever theme the reader is in. Nothing moves: two or three promotions to rotate
|
|
16
|
+
* between are CtaBannerSlider, which is this band with a slider in it — the same column, the same
|
|
17
|
+
* headline and line, so a page can carry either and read the same.
|
|
18
|
+
*
|
|
19
|
+
* The region is named by its headline, the way PageHeader's is, so a reader can jump to the band
|
|
20
|
+
* and past it. `'use client'` only for `useId`, which ties the region to its headline — the same
|
|
21
|
+
* reason PageHeader carries it.
|
|
22
|
+
*
|
|
23
|
+
* It arrives the way every block does, and the way CtaBannerSlider's slides do: block-reveal.css
|
|
24
|
+
* brings the headline, the line and the actions up a step apart once the band is in view.
|
|
25
|
+
*/
|
|
26
|
+
export function CtaBanner({ title, headingLevel = ctaBannerDefaults.headingLevel, description, actions, }) {
|
|
27
|
+
const titleId = useId();
|
|
28
|
+
const [band, revealed] = useReveal();
|
|
29
|
+
const Title = headingLevel === 'h3' ? 'h3' : 'h2';
|
|
30
|
+
return (_jsx("section", { className: ctaBannerParts.root, "aria-labelledby": titleId, children: _jsx("div", { className: ctaBannerParts.inner, ref: band, "data-revealed": revealed, children: _jsxs("div", { className: ctaBannerParts.content, children: [_jsxs("div", { className: ctaBannerParts.text, children: [_jsx(Title, { className: ctaBannerParts.title, id: titleId, ...arriving(0), children: title }), given(description) ? (_jsx("p", { className: ctaBannerParts.description, ...arriving(1), children: description })) : null] }), actions ? (_jsx("div", { className: ctaBannerParts.actions, ...arriving(2), children: actions })) : null] }) }) }));
|
|
31
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export interface CtaBannerOwnProps {
|
|
3
|
+
/** Always — the message, in a few words. Short enough to take in at a glance; a line break goes where the words pause, not where the box runs out. */
|
|
4
|
+
title: string;
|
|
5
|
+
/**
|
|
6
|
+
* `h2` — The default: a banner is a section of the page, under the page's own h1
|
|
7
|
+
* `h3` — A banner nested inside a section that already carries the h2 it belongs to
|
|
8
|
+
*/
|
|
9
|
+
headingLevel?: 'h2' | 'h3';
|
|
10
|
+
/** A line saying why the action is worth taking — what the reader gets, or how little it costs them. */
|
|
11
|
+
description?: string;
|
|
12
|
+
/** The one action the message asks for, or two at most. The coral is the banner's ground, so the primary is Button variant="inverse" tone="brand" — a primary Button is coral on coral, and the tone keeps it near-black in both themes — and a second is Button variant="secondary" tone="brand" — a white tint with a white label, fixed in both themes like the band. */
|
|
13
|
+
actions?: ReactNode;
|
|
14
|
+
}
|
|
15
|
+
export type CtaBannerProps = CtaBannerOwnProps;
|
|
16
|
+
/** The class each part carries. Apply these — do not retype them. */
|
|
17
|
+
export declare const ctaBannerParts: {
|
|
18
|
+
readonly root: "dds-ctabanner";
|
|
19
|
+
readonly inner: "dds-ctabanner__inner";
|
|
20
|
+
readonly content: "dds-ctabanner__content";
|
|
21
|
+
readonly text: "dds-ctabanner__text";
|
|
22
|
+
readonly title: "dds-ctabanner__title";
|
|
23
|
+
readonly description: "dds-ctabanner__description";
|
|
24
|
+
readonly actions: "dds-ctabanner__actions";
|
|
25
|
+
};
|
|
26
|
+
/** Generated so defaults cannot drift from the contract. */
|
|
27
|
+
export declare const ctaBannerDefaults: {
|
|
28
|
+
readonly headingLevel: "h2";
|
|
29
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// GENERATED from cta-banner.contract.json. Do not edit.
|
|
2
|
+
/** The class each part carries. Apply these — do not retype them. */
|
|
3
|
+
export const ctaBannerParts = {
|
|
4
|
+
root: 'dds-ctabanner',
|
|
5
|
+
inner: 'dds-ctabanner__inner',
|
|
6
|
+
content: 'dds-ctabanner__content',
|
|
7
|
+
text: 'dds-ctabanner__text',
|
|
8
|
+
title: 'dds-ctabanner__title',
|
|
9
|
+
description: 'dds-ctabanner__description',
|
|
10
|
+
actions: 'dds-ctabanner__actions',
|
|
11
|
+
};
|
|
12
|
+
/** Generated so defaults cannot drift from the contract. */
|
|
13
|
+
export const ctaBannerDefaults = { headingLevel: 'h2' };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Scoped to this package, like the generated sheet beside it.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
* Starts on a phone, centred once the band is wide enough to hold a line.
|
|
7
|
+
*
|
|
8
|
+
* PageHeader's rule and its reason: centred text starts every line at a different edge, which costs
|
|
9
|
+
* little across a wide measure and a great deal across a narrow one. CtaBannerSlider centres at the
|
|
10
|
+
* same width, so a page can carry either band and read the same.
|
|
11
|
+
*
|
|
12
|
+
* The text and the line under it are capped narrower than the column, so `margin-inline: auto` is
|
|
13
|
+
* what centres those boxes rather than their words inside a box left at the start edge.
|
|
14
|
+
*/
|
|
15
|
+
@container (min-width: 48rem) {
|
|
16
|
+
:where([data-dds-context="marketing"]) .dds-ctabanner .dds-ctabanner__content {
|
|
17
|
+
align-items: center;
|
|
18
|
+
text-align: center;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
:where([data-dds-context="marketing"]) .dds-ctabanner .dds-ctabanner__text,
|
|
22
|
+
:where([data-dds-context="marketing"]) .dds-ctabanner .dds-ctabanner__description {
|
|
23
|
+
margin-inline: auto;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
:where([data-dds-context="marketing"]) .dds-ctabanner .dds-ctabanner__actions {
|
|
27
|
+
justify-content: center;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/* GENERATED from ctabannerslider.contract.json. Do not edit. */
|
|
2
|
+
|
|
3
|
+
:where([data-dds-context="marketing"]) .dds-ctabannerslider {
|
|
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);
|
|
12
|
+
color: var(--color-fg-on-brand);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
:where([data-dds-context="marketing"]) .dds-ctabannerslider .dds-ctabannerslider__inner {
|
|
16
|
+
box-sizing: border-box;
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
inline-size: 100%;
|
|
20
|
+
min-inline-size: 0;
|
|
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-ctabannerslider .dds-ctabannerslider__track {
|
|
27
|
+
display: grid;
|
|
28
|
+
transition-property: none;
|
|
29
|
+
transition-duration: var(--motion-duration-slow);
|
|
30
|
+
transition-timing-function: var(--motion-easing-shift);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
:where([data-dds-context="marketing"]) .dds-ctabannerslider .dds-ctabannerslider__slide {
|
|
34
|
+
box-sizing: border-box;
|
|
35
|
+
display: flex;
|
|
36
|
+
flex: none;
|
|
37
|
+
flex-direction: column;
|
|
38
|
+
inline-size: 100%;
|
|
39
|
+
min-inline-size: 0;
|
|
40
|
+
row-gap: var(--space-heading-description-actions);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
:where([data-dds-context="marketing"]) .dds-ctabannerslider .dds-ctabannerslider__text {
|
|
44
|
+
display: flex;
|
|
45
|
+
flex-direction: column;
|
|
46
|
+
min-inline-size: 0;
|
|
47
|
+
max-inline-size: var(--size-measure-heading);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
:where([data-dds-context="marketing"]) .dds-ctabannerslider .dds-ctabannerslider__title {
|
|
51
|
+
margin-block: 0;
|
|
52
|
+
text-wrap: balance;
|
|
53
|
+
font-family: var(--font-h1-family);
|
|
54
|
+
font-size: var(--font-h1-size);
|
|
55
|
+
font-weight: var(--font-h1-weight);
|
|
56
|
+
letter-spacing: var(--font-h1-letter-spacing);
|
|
57
|
+
line-height: var(--font-h1-line-height);
|
|
58
|
+
margin-block-end: var(--space-heading-title-description);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
:where([data-dds-context="marketing"]) .dds-ctabannerslider .dds-ctabannerslider__description {
|
|
62
|
+
margin-block: 0;
|
|
63
|
+
font-family: var(--font-body-lg-family);
|
|
64
|
+
font-size: var(--font-body-lg-size);
|
|
65
|
+
font-weight: var(--font-body-lg-weight);
|
|
66
|
+
letter-spacing: var(--font-body-lg-letter-spacing);
|
|
67
|
+
line-height: var(--font-body-lg-line-height);
|
|
68
|
+
max-inline-size: var(--size-measure-description);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
:where([data-dds-context="marketing"]) .dds-ctabannerslider .dds-ctabannerslider__actions {
|
|
72
|
+
display: flex;
|
|
73
|
+
flex-wrap: wrap;
|
|
74
|
+
column-gap: var(--space-actions-gap);
|
|
75
|
+
row-gap: var(--space-actions-gap);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
:where([data-dds-context="marketing"]) .dds-ctabannerslider .dds-ctabannerslider__controls {
|
|
79
|
+
align-items: center;
|
|
80
|
+
display: flex;
|
|
81
|
+
flex-wrap: wrap;
|
|
82
|
+
column-gap: var(--space-actions-gap);
|
|
83
|
+
margin-block-start: var(--space-heading-description-actions);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@media (prefers-reduced-motion: reduce) {
|
|
87
|
+
:where([data-dds-context="marketing"]) .dds-ctabannerslider,
|
|
88
|
+
:where([data-dds-context="marketing"]) .dds-ctabannerslider * { transition: none !important; }
|
|
89
|
+
}
|
|
90
|
+
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import './CtaBannerSlider.css';
|
|
2
|
+
import './cta-banner-slider-impl.css';
|
|
3
|
+
import '../block-reveal.css';
|
|
4
|
+
import { type CtaBannerSliderProps } from './CtaBannerSlider.types.js';
|
|
5
|
+
export type { CtaBannerSliderProps };
|
|
6
|
+
/**
|
|
7
|
+
* CtaBanner's layout with a slider in it: the same coral band, rotating between a few promotions.
|
|
8
|
+
*
|
|
9
|
+
* The reference site's CoralBanner in its carousel form. The ground is color.bg.brand and the ink
|
|
10
|
+
* color.fg.on-brand, both the same in light and dark, exactly as CtaBanner paints them. A single
|
|
11
|
+
* promotion is CtaBanner; this is for two or three, and handed only one it draws it still rather
|
|
12
|
+
* than as a carousel with nowhere to go.
|
|
13
|
+
*
|
|
14
|
+
* One slide is in view. The slides are stacked in one place, and a change is the new one fading in
|
|
15
|
+
* where the old one stood — no travel, the old one's words gone at once. PaginationDots says
|
|
16
|
+
* where the reader is and moves to any slide pressed; with the swipe, it is the whole of how a reader
|
|
17
|
+
* moves the slider — there are no previous and next steps, by decision.
|
|
18
|
+
* Autoplay is off unless asked for. When it is on, rotation holds while the pointer is over the
|
|
19
|
+
* band or focus is inside it, never runs for a reader who asked for less motion, and stops for good
|
|
20
|
+
* once the reader presses anything in it. There is no pause control, by decision, so that press is
|
|
21
|
+
* the reader's only way to stop it, and nothing on screen says so — the contract records what that
|
|
22
|
+
* does and does not give WCAG 2.2.2. The default keeps a slider nobody configured still.
|
|
23
|
+
*
|
|
24
|
+
* Every slide out of view is inert and hidden from assistive technology, so Tab never reaches a
|
|
25
|
+
* button the reader cannot see, and the one in view is named by its position.
|
|
26
|
+
*
|
|
27
|
+
* The slides arrive as data and the banner draws them, buttons included: the primary as Button
|
|
28
|
+
* variant="inverse" and the second as the white-tinted secondary, both at tone="brand" so neither
|
|
29
|
+
* switches with the theme on a band that does not. A slide was once a
|
|
30
|
+
* child component of its own, which existed only to carry its Button; as a record it is one
|
|
31
|
+
* component to learn, and no slide can put a coral button on the coral ground.
|
|
32
|
+
*
|
|
33
|
+
* A slide's words arrive the way every block's do — block-reveal.css, the house answer — and they do
|
|
34
|
+
* it each time a slide comes into view, not only when the band first scrolls in: the slide in view
|
|
35
|
+
* is the one marked arrived, so a change puts the new slide's headline, line and actions through
|
|
36
|
+
* the same rise, a step apart. The slide leaving drops to waiting at once, as that sheet has every
|
|
37
|
+
* part do, so the outgoing words never travel.
|
|
38
|
+
*/
|
|
39
|
+
export declare function CtaBannerSlider({ slides, headingLevel, autoplay, label, controlsLabel, slideLabel, }: CtaBannerSliderProps): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import './CtaBannerSlider.css';
|
|
4
|
+
import './cta-banner-slider-impl.css';
|
|
5
|
+
import '../block-reveal.css';
|
|
6
|
+
import { arriving, useReveal } from '../use-reveal.js';
|
|
7
|
+
import { useRotation } from '../use-rotation.js';
|
|
8
|
+
import { given } from '../../given.js';
|
|
9
|
+
import { Button } from '../../core/button/Button.js';
|
|
10
|
+
import { PaginationDots } from '../../core/pagination-dots/PaginationDots.js';
|
|
11
|
+
import { ctaBannerSliderDefaults, ctaBannerSliderParts, } from './CtaBannerSlider.types.js';
|
|
12
|
+
/**
|
|
13
|
+
* CtaBanner's layout with a slider in it: the same coral band, rotating between a few promotions.
|
|
14
|
+
*
|
|
15
|
+
* The reference site's CoralBanner in its carousel form. The ground is color.bg.brand and the ink
|
|
16
|
+
* color.fg.on-brand, both the same in light and dark, exactly as CtaBanner paints them. A single
|
|
17
|
+
* promotion is CtaBanner; this is for two or three, and handed only one it draws it still rather
|
|
18
|
+
* than as a carousel with nowhere to go.
|
|
19
|
+
*
|
|
20
|
+
* One slide is in view. The slides are stacked in one place, and a change is the new one fading in
|
|
21
|
+
* where the old one stood — no travel, the old one's words gone at once. PaginationDots says
|
|
22
|
+
* where the reader is and moves to any slide pressed; with the swipe, it is the whole of how a reader
|
|
23
|
+
* moves the slider — there are no previous and next steps, by decision.
|
|
24
|
+
* Autoplay is off unless asked for. When it is on, rotation holds while the pointer is over the
|
|
25
|
+
* band or focus is inside it, never runs for a reader who asked for less motion, and stops for good
|
|
26
|
+
* once the reader presses anything in it. There is no pause control, by decision, so that press is
|
|
27
|
+
* the reader's only way to stop it, and nothing on screen says so — the contract records what that
|
|
28
|
+
* does and does not give WCAG 2.2.2. The default keeps a slider nobody configured still.
|
|
29
|
+
*
|
|
30
|
+
* Every slide out of view is inert and hidden from assistive technology, so Tab never reaches a
|
|
31
|
+
* button the reader cannot see, and the one in view is named by its position.
|
|
32
|
+
*
|
|
33
|
+
* The slides arrive as data and the banner draws them, buttons included: the primary as Button
|
|
34
|
+
* variant="inverse" and the second as the white-tinted secondary, both at tone="brand" so neither
|
|
35
|
+
* switches with the theme on a band that does not. A slide was once a
|
|
36
|
+
* child component of its own, which existed only to carry its Button; as a record it is one
|
|
37
|
+
* component to learn, and no slide can put a coral button on the coral ground.
|
|
38
|
+
*
|
|
39
|
+
* A slide's words arrive the way every block's do — block-reveal.css, the house answer — and they do
|
|
40
|
+
* it each time a slide comes into view, not only when the band first scrolls in: the slide in view
|
|
41
|
+
* is the one marked arrived, so a change puts the new slide's headline, line and actions through
|
|
42
|
+
* the same rise, a step apart. The slide leaving drops to waiting at once, as that sheet has every
|
|
43
|
+
* part do, so the outgoing words never travel.
|
|
44
|
+
*/
|
|
45
|
+
export function CtaBannerSlider({ slides, headingLevel = ctaBannerSliderDefaults.headingLevel, autoplay = ctaBannerSliderDefaults.autoplay, label = ctaBannerSliderDefaults.label, controlsLabel = ctaBannerSliderDefaults.controlsLabel, slideLabel = ctaBannerSliderDefaults.slideLabel, }) {
|
|
46
|
+
const Title = headingLevel === 'h3' ? 'h3' : 'h2';
|
|
47
|
+
const count = slides.length;
|
|
48
|
+
// The band arriving on screen, as every block does; until then no slide has arrived.
|
|
49
|
+
const [band, revealed] = useReveal();
|
|
50
|
+
// Which slide is in view, and every way a reader moves, holds or stops the set — shared with
|
|
51
|
+
// TestimonialSlider, so the two rotate by one set of rules. See use-rotation.ts.
|
|
52
|
+
const { current, rotating, root, region, track, item, away, dots } = useRotation(count, autoplay);
|
|
53
|
+
// Nothing to show is nothing drawn: a named region with no message in it is a landmark that
|
|
54
|
+
// leads a screen reader to an empty band.
|
|
55
|
+
if (count === 0)
|
|
56
|
+
return null;
|
|
57
|
+
return (_jsx("section", { ref: root, className: ctaBannerSliderParts.root, "aria-label": label, ...region, children: _jsxs("div", { className: ctaBannerSliderParts.inner, ref: band, children: [_jsx("div", { className: ctaBannerSliderParts.track, ...track, children: slides.map((slide, at) => (_jsxs("div", { className: ctaBannerSliderParts.slide, ...item(at), "data-revealed": revealed && !away(at), children: [_jsxs("div", { className: ctaBannerSliderParts.text, children: [_jsx(Title, { className: ctaBannerSliderParts.title, ...arriving(0), children: slide.title }), given(slide.description) ? (_jsx("p", { className: ctaBannerSliderParts.description, ...arriving(1), children: slide.description })) : null] }), slide.primaryAction ? (_jsxs("div", { className: ctaBannerSliderParts.actions, ...arriving(2), children: [_jsx(Button, { variant: "inverse", tone: "brand", href: slide.primaryAction.href, children: slide.primaryAction.label }), slide.secondaryAction ? (_jsx(Button, { variant: "secondary", tone: "brand", href: slide.secondaryAction.href, children: slide.secondaryAction.label })) : null] })) : null] }, at))) }), rotating ? (_jsx("div", { className: ctaBannerSliderParts.controls, children: _jsx(PaginationDots, { ...dots,
|
|
58
|
+
/* A dot pressed stops the dots' own rotation; the band's press handler stops this. */
|
|
59
|
+
label: controlsLabel, itemLabel: slideLabel }) })) : null] }) }));
|
|
60
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { CtaBannerSlide } from '../../banner-data.js';
|
|
2
|
+
export interface CtaBannerSliderOwnProps {
|
|
3
|
+
/** Always. The messages, in the order they rotate — two or three. Each is a headline, a line under it, and up to two actions as a label and a href: the banner draws the primary as Button variant="inverse" tone="brand", near-black in both themes, and the second as the white-tinted secondary, Button tone="brand", so no slide can put a coral button on the coral ground. A secondary is drawn only beside a primary — one action is the primary.
|
|
4
|
+
|
|
5
|
+
An empty list draws nothing — no region with nothing in it. A list of records rather than a child per slide, by decision: the set can come from a CMS, and there is one component to learn rather than two. */
|
|
6
|
+
slides: CtaBannerSlide[];
|
|
7
|
+
/**
|
|
8
|
+
* `h2` — The default: a banner is a section of the page, under the page's own h1
|
|
9
|
+
* `h3` — A banner nested inside a section that already carries the h2 it belongs to
|
|
10
|
+
*/
|
|
11
|
+
headingLevel?: 'h2' | 'h3';
|
|
12
|
+
/** Whether it advances on its own, holding each slide for motion.duration.dwell. Off by default, so a slider nobody configured stays still. On, it holds while the pointer is over it or focus is inside it, never moves for a reader who asked for less motion, and stops for good once the reader presses anything in it — a slide, its action, a dot — or swipes it. A press stops it for good — the WAI carousel pattern's rule that a reader who has interacted is reading, not watching. That gives a reader a way to stop the rotation, which WCAG 2.2.2 asks for, but not a visible one: nothing says a press will stop it, so a page that needs 2.2.2 met plainly should still offer its own pause, or leave autoplay off. */
|
|
13
|
+
autoplay?: boolean;
|
|
14
|
+
/** The name the banner is announced by — a region a screen reader can jump to and skip. Name what it holds, not that it is a banner: 'Promotions', 'Offers'. Translate it with the page. */
|
|
15
|
+
label?: string;
|
|
16
|
+
/** The name the dots are announced by, as a group inside the banner. Its own name rather than the banner's, so a screen reader moving into the dots hears what they are rather than 'Promotions' a second time. Translate it with the page, like label. */
|
|
17
|
+
controlsLabel?: string;
|
|
18
|
+
/** The word each dot is announced by, before its number — 'Slide 2'. Translate it with the page, as controlsLabel is; a page that translates only the group's name still has every dot announced in English. */
|
|
19
|
+
slideLabel?: string;
|
|
20
|
+
}
|
|
21
|
+
export type CtaBannerSliderProps = CtaBannerSliderOwnProps;
|
|
22
|
+
/** The class each part carries. Apply these — do not retype them. */
|
|
23
|
+
export declare const ctaBannerSliderParts: {
|
|
24
|
+
readonly root: "dds-ctabannerslider";
|
|
25
|
+
readonly inner: "dds-ctabannerslider__inner";
|
|
26
|
+
readonly track: "dds-ctabannerslider__track";
|
|
27
|
+
readonly slide: "dds-ctabannerslider__slide";
|
|
28
|
+
readonly text: "dds-ctabannerslider__text";
|
|
29
|
+
readonly title: "dds-ctabannerslider__title";
|
|
30
|
+
readonly description: "dds-ctabannerslider__description";
|
|
31
|
+
readonly actions: "dds-ctabannerslider__actions";
|
|
32
|
+
readonly controls: "dds-ctabannerslider__controls";
|
|
33
|
+
};
|
|
34
|
+
/** Generated so defaults cannot drift from the contract. */
|
|
35
|
+
export declare const ctaBannerSliderDefaults: {
|
|
36
|
+
readonly headingLevel: "h2";
|
|
37
|
+
readonly autoplay: false;
|
|
38
|
+
readonly label: "Promotions";
|
|
39
|
+
readonly controlsLabel: "Slides";
|
|
40
|
+
readonly slideLabel: "Slide";
|
|
41
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// GENERATED from cta-banner-slider.contract.json. Do not edit.
|
|
2
|
+
/** The class each part carries. Apply these — do not retype them. */
|
|
3
|
+
export const ctaBannerSliderParts = {
|
|
4
|
+
root: 'dds-ctabannerslider',
|
|
5
|
+
inner: 'dds-ctabannerslider__inner',
|
|
6
|
+
track: 'dds-ctabannerslider__track',
|
|
7
|
+
slide: 'dds-ctabannerslider__slide',
|
|
8
|
+
text: 'dds-ctabannerslider__text',
|
|
9
|
+
title: 'dds-ctabannerslider__title',
|
|
10
|
+
description: 'dds-ctabannerslider__description',
|
|
11
|
+
actions: 'dds-ctabannerslider__actions',
|
|
12
|
+
controls: 'dds-ctabannerslider__controls',
|
|
13
|
+
};
|
|
14
|
+
/** Generated so defaults cannot drift from the contract. */
|
|
15
|
+
export const ctaBannerSliderDefaults = { headingLevel: 'h2', autoplay: false, label: 'Promotions', controlsLabel: 'Slides', slideLabel: 'Slide' };
|