@deriv-com/dds-marketing 0.2.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.llm/components/accordion.md +8 -0
- package/.llm/components/accordionitem.md +8 -0
- package/.llm/components/aspectratio.md +8 -0
- package/.llm/components/badge.md +8 -0
- package/.llm/components/bottomsheet.md +8 -0
- package/.llm/components/breadcrumb.md +8 -0
- package/.llm/components/breadcrumbitem.md +8 -0
- package/.llm/components/button.md +46 -22
- package/.llm/components/buttonicon.md +14 -6
- package/.llm/components/card.md +15 -1
- package/.llm/components/cardbody.md +28 -2
- package/.llm/components/cardbutton.md +15 -1
- package/.llm/components/checkbox.md +8 -0
- package/.llm/components/ctabanner.md +186 -0
- package/.llm/components/ctabannerslider.md +204 -0
- package/.llm/components/divider.md +8 -0
- package/.llm/components/dropdownmenu.md +13 -5
- 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 +8 -0
- package/.llm/components/footer.md +8 -0
- package/.llm/components/footergroup.md +8 -0
- package/.llm/components/grid.md +8 -0
- package/.llm/components/icon.md +8 -0
- package/.llm/components/landinghero.md +254 -0
- package/.llm/components/link.md +9 -1
- package/.llm/components/logocolumns.md +235 -0
- package/.llm/components/logorows.md +212 -0
- package/.llm/components/menuitem.md +8 -0
- package/.llm/components/navbar.md +79 -15
- package/.llm/components/navbaritem.md +9 -1
- package/.llm/components/navbarmenu.md +8 -0
- package/.llm/components/navbarmenugroup.md +8 -0
- package/.llm/components/navbarpanel.md +9 -1
- package/.llm/components/navbarswitcher.md +9 -1
- package/.llm/components/navbarswitcheroption.md +8 -0
- package/.llm/components/option.md +8 -0
- package/.llm/components/pageheader.md +231 -0
- package/.llm/components/pagination.md +26 -89
- package/.llm/components/paginationdots.md +215 -0
- package/.llm/components/producthero.md +248 -0
- package/.llm/components/removablechip.md +8 -0
- package/.llm/components/row.md +8 -0
- package/.llm/components/scroller.md +8 -0
- package/.llm/components/searchinput.md +8 -0
- package/.llm/components/segmentedcontrol.md +8 -0
- package/.llm/components/segmentedcontrolitem.md +8 -0
- package/.llm/components/select.md +8 -0
- package/.llm/components/selectchip.md +56 -15
- package/.llm/components/spinner.md +8 -0
- package/.llm/components/stack.md +8 -0
- package/.llm/components/statusdot.md +8 -0
- package/.llm/components/subdomainnavbar.md +8 -0
- package/.llm/components/tab.md +8 -0
- package/.llm/components/table.md +8 -0
- package/.llm/components/tablebody.md +8 -0
- package/.llm/components/tablecell.md +8 -0
- package/.llm/components/tablehead.md +8 -0
- package/.llm/components/tableheadercell.md +8 -0
- package/.llm/components/tablerow.md +8 -0
- package/.llm/components/tablist.md +8 -0
- package/.llm/components/testimonialrow.md +235 -0
- package/.llm/components/testimonialslider.md +254 -0
- package/.llm/components/textarea.md +8 -0
- package/.llm/components/textinput.md +8 -0
- package/.llm/components/tooltip.md +8 -0
- package/.llm/components/typography.md +8 -0
- 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 +24 -0
- package/.llm/tokens.md +56 -12
- package/AGENTS.md +28 -3
- 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/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-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,40 @@
|
|
|
1
|
+
import type { FeatureScrollStep } from '../../scroll-data.js';
|
|
2
|
+
export interface FeatureScrollOwnProps {
|
|
3
|
+
/**
|
|
4
|
+
* `page` — The default, and the page's own white. A section that has nothing to separate itself from what is above it — most of them — and the ground a reader is already on
|
|
5
|
+
* `subtle` — A quiet step off the page, for a band that wants to read as its own section without changing the page's voice. The one to reach for first when two sections need telling apart. Painted as the band's tint laid over the page's own ground, so it is the same grey whatever is behind the section — in dark mode the tint alone took on whatever it was set against, and on a light surface it was light ink on white.
|
|
6
|
+
* `strong` — `color.bg.brand-strong`, the dark band. It stops the page rather than dividing it, so it is for a section a reader should arrive at — and one per page is usually one more than enough
|
|
7
|
+
*/
|
|
8
|
+
ground?: 'page' | 'subtle' | 'strong';
|
|
9
|
+
/**
|
|
10
|
+
* `end` — The default. The words are read first, which is the order a reader’s eye takes a two-up row in
|
|
11
|
+
* `start` — The pictures lead, for a section following one that put its own the other way — two scrolls facing the same way read as one very long column
|
|
12
|
+
*/
|
|
13
|
+
mediaPosition?: 'end' | 'start';
|
|
14
|
+
/** Always. What the set is a set of, in the fewest words that say it — 'All your markets in one place'. It is the only thing a reader sees before committing to several screens of scrolling, so it has to say what is coming */
|
|
15
|
+
title: string;
|
|
16
|
+
/** Always. The stops, in the order a reader meets them. Three at least — the section is built on each one having a screen, and a pair is worth one screen with both on it.
|
|
17
|
+
|
|
18
|
+
Every stop is the same shape, which is what makes the set a set: a name, a line, a picture, and a way onward where there is one. */
|
|
19
|
+
steps: FeatureScrollStep[];
|
|
20
|
+
}
|
|
21
|
+
export type FeatureScrollProps = FeatureScrollOwnProps;
|
|
22
|
+
/** The class each part carries. Apply these — do not retype them. */
|
|
23
|
+
export declare const featureScrollParts: {
|
|
24
|
+
readonly root: "dds-featurescroll";
|
|
25
|
+
readonly inner: "dds-featurescroll__inner";
|
|
26
|
+
readonly heading: "dds-featurescroll__heading";
|
|
27
|
+
readonly steps: "dds-featurescroll__steps";
|
|
28
|
+
readonly step: "dds-featurescroll__step";
|
|
29
|
+
readonly content: "dds-featurescroll__content";
|
|
30
|
+
readonly title: "dds-featurescroll__title";
|
|
31
|
+
readonly description: "dds-featurescroll__description";
|
|
32
|
+
readonly link: "dds-featurescroll__link";
|
|
33
|
+
readonly media: "dds-featurescroll__media";
|
|
34
|
+
readonly picture: "dds-featurescroll__picture";
|
|
35
|
+
};
|
|
36
|
+
/** Generated so defaults cannot drift from the contract. */
|
|
37
|
+
export declare const featureScrollDefaults: {
|
|
38
|
+
readonly ground: "page";
|
|
39
|
+
readonly mediaPosition: "end";
|
|
40
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// GENERATED from feature-scroll.contract.json. Do not edit.
|
|
2
|
+
/** The class each part carries. Apply these — do not retype them. */
|
|
3
|
+
export const featureScrollParts = {
|
|
4
|
+
root: 'dds-featurescroll',
|
|
5
|
+
inner: 'dds-featurescroll__inner',
|
|
6
|
+
heading: 'dds-featurescroll__heading',
|
|
7
|
+
steps: 'dds-featurescroll__steps',
|
|
8
|
+
step: 'dds-featurescroll__step',
|
|
9
|
+
content: 'dds-featurescroll__content',
|
|
10
|
+
title: 'dds-featurescroll__title',
|
|
11
|
+
description: 'dds-featurescroll__description',
|
|
12
|
+
link: 'dds-featurescroll__link',
|
|
13
|
+
media: 'dds-featurescroll__media',
|
|
14
|
+
picture: 'dds-featurescroll__picture',
|
|
15
|
+
};
|
|
16
|
+
/** Generated so defaults cannot drift from the contract. */
|
|
17
|
+
export const featureScrollDefaults = { ground: 'page', mediaPosition: 'end' };
|
|
@@ -0,0 +1,292 @@
|
|
|
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
|
+
* Every query here is `@container` rather than `@media`. The section declares `container-type`,
|
|
8
|
+
* so the set folds on the band's own width — which is what the rest of this package does, and
|
|
9
|
+
* what makes the workbench's viewport control mean something: it resizes the stage rather than
|
|
10
|
+
* the browser, and a section asking the window would hold its desktop layout at every setting.
|
|
11
|
+
*
|
|
12
|
+
* `48rem` longhand throughout because `var()` is not permitted in a container query condition,
|
|
13
|
+
* and it is the width the rest of this package folds at.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/*
|
|
17
|
+
* One column below the fold, and the picture between the name and the line about it.
|
|
18
|
+
*
|
|
19
|
+
* The row direction is the contract's to say — it varies by a styling axis, which is what
|
|
20
|
+
* `structure[]` is for, and the generated sheet carries both values. What is left here is the
|
|
21
|
+
* fold, which a binding cannot express: `structure` has no width to attach a rule to.
|
|
22
|
+
*
|
|
23
|
+
* Written with the `data-media-position` attribute so it carries the weight of the generated rules
|
|
24
|
+
* keyed on it — `.dds-featurescroll[data-media-position="end"] .dds-featurescroll__content` and its
|
|
25
|
+
* kin, a class, an attribute and a class — and wins over anything at that weight by loading later.
|
|
26
|
+
*/
|
|
27
|
+
@container (max-width: 47.9375rem) {
|
|
28
|
+
:where([data-dds-context="marketing"])
|
|
29
|
+
.dds-featurescroll[data-media-position]
|
|
30
|
+
.dds-featurescroll__step {
|
|
31
|
+
flex-direction: column;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/*
|
|
35
|
+
* The text column stops being a box so the picture can sit inside its order.
|
|
36
|
+
*
|
|
37
|
+
* A phone has one column, and the picture is what tells a reader which of six stops they have
|
|
38
|
+
* reached — put after the words it arrives once they have already decided whether to read them.
|
|
39
|
+
* The name, the picture, then the line and the way onward is the reference design's own order,
|
|
40
|
+
* and the only other way to get it is a second copy of the markup for the second layout.
|
|
41
|
+
*
|
|
42
|
+
* `display: contents` takes the box out and leaves its children to the row above, so the four
|
|
43
|
+
* parts become four flex items and `order` can interleave them. The children keep their own
|
|
44
|
+
* styling; what is lost is anything the box itself drew, which here is one `row-gap` that is
|
|
45
|
+
* `space.gap.none` at every width anyway.
|
|
46
|
+
*/
|
|
47
|
+
:where([data-dds-context="marketing"]) .dds-featurescroll .dds-featurescroll__content {
|
|
48
|
+
display: contents;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
:where([data-dds-context="marketing"]) .dds-featurescroll .dds-featurescroll__title {
|
|
52
|
+
order: 1;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/*
|
|
56
|
+
* The band hugs its picture and the picture runs the column.
|
|
57
|
+
*
|
|
58
|
+
* There is one column below the fold, nothing to pin against, and no band to be the height of —
|
|
59
|
+
* so the air the band holds the picture off its edges by would be a hole in the stack, where the
|
|
60
|
+
* gap between the parts already does that job.
|
|
61
|
+
*/
|
|
62
|
+
:where([data-dds-context="marketing"]) .dds-featurescroll .dds-featurescroll__media {
|
|
63
|
+
order: 2;
|
|
64
|
+
padding-block: 0;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
:where([data-dds-context="marketing"]) .dds-featurescroll .dds-featurescroll__picture {
|
|
68
|
+
inline-size: 100%;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
:where([data-dds-context="marketing"]) .dds-featurescroll .dds-featurescroll__description {
|
|
72
|
+
order: 3;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
:where([data-dds-context="marketing"]) .dds-featurescroll .dds-featurescroll__link {
|
|
76
|
+
order: 4;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/*
|
|
80
|
+
* The steps between the parts come off the parts and onto the stack.
|
|
81
|
+
*
|
|
82
|
+
* At full width the column carries them on each part's own `margin-block-end`, because
|
|
83
|
+
* title-to-description and description-to-what-follows are two different roles. Below the fold
|
|
84
|
+
* there is a fifth thing in the order — the picture — and a margin belonging to the title would
|
|
85
|
+
* land under the picture instead of over it. So the row's own gap governs here, and the margins
|
|
86
|
+
* that would add to it are cleared.
|
|
87
|
+
*/
|
|
88
|
+
:where([data-dds-context="marketing"]) .dds-featurescroll .dds-featurescroll__title,
|
|
89
|
+
:where([data-dds-context="marketing"]) .dds-featurescroll .dds-featurescroll__description {
|
|
90
|
+
margin-block-end: 0;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/*
|
|
95
|
+
* A screen each at full width, the picture pinned for the length of it, and the set closed up.
|
|
96
|
+
*
|
|
97
|
+
* The height is the whole idea rather than a decoration on it: a reader arrives at one stop at a
|
|
98
|
+
* time and scrolls on to the next, which is what a set too long to compare needs and what a grid
|
|
99
|
+
* of six cards cannot give. `100cqh` rather than `100svh` for the reason LandingHero gives — with
|
|
100
|
+
* no ancestor declaring size containment the two are the same number, and the container unit is
|
|
101
|
+
* what lets a frame standing in for a window say otherwise. The section's own `container-type` is
|
|
102
|
+
* `inline-size`, which answers no question about height, so the unit passes straight through it.
|
|
103
|
+
*
|
|
104
|
+
* `position: sticky` and nothing else. The reference design measures scroll position in React,
|
|
105
|
+
* keeps an active index and crossfades between a stack of absolutely-placed pictures; the dissolve
|
|
106
|
+
* is what that buys and three listeners on a marketing page is what it costs. The browser already
|
|
107
|
+
* knows where the box is.
|
|
108
|
+
*
|
|
109
|
+
* It pins at `size.bar-height`, which is what a collapsed navigation bar takes — so the picture
|
|
110
|
+
* comes to rest just under the bar rather than behind it. On a page with no bar the offset is a
|
|
111
|
+
* margin, which is what a picture at the very top of the window wants anyway.
|
|
112
|
+
*
|
|
113
|
+
* The square is capped at the band it has to fit inside: the bar's height above it and the same
|
|
114
|
+
* again below, so a short window compacts the picture instead of running it past the fold. Both
|
|
115
|
+
* axes take the same cap because the shape is the point — capping the height alone would clamp a
|
|
116
|
+
* square into a letterbox, and the picture inside is cropped to fill whatever it is given.
|
|
117
|
+
*
|
|
118
|
+
* The gap between stops goes to nothing here. Below the fold they are ordinary blocks and want air
|
|
119
|
+
* between them, which is the gap the contract binds; at full width a picture is released at the
|
|
120
|
+
* moment the next arrives, and a gap there opens a stretch of scrolling with no picture pinned at
|
|
121
|
+
* all — which reads as the section having ended and started again.
|
|
122
|
+
*/
|
|
123
|
+
@container (min-width: 48rem) {
|
|
124
|
+
:where([data-dds-context="marketing"]) .dds-featurescroll .dds-featurescroll__heading {
|
|
125
|
+
/* `margin-inline: auto` as well as the alignment, because the heading is held to
|
|
126
|
+
`size.measure.heading` — centred text inside a box still pinned to the start edge is a
|
|
127
|
+
heading that looks centred only while it is short enough not to reach the cap. */
|
|
128
|
+
margin-inline: auto;
|
|
129
|
+
text-align: center;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/*
|
|
133
|
+
* Two columns, and every picture in one of them.
|
|
134
|
+
*
|
|
135
|
+
* The rows are named rather than numbered so the contract can place the two halves: `structure`
|
|
136
|
+
* may hold a keyword and may not hold a 2, on the argument that a ratio between siblings is a
|
|
137
|
+
* layout decision — and the names say which half is which where a number would only say where.
|
|
138
|
+
*
|
|
139
|
+
* The row count arrives as a custom property because `grid-row: 1 / -1` cannot reach the end of
|
|
140
|
+
* an implicit grid: with no explicit rows, `-1` is the end of the explicit one, which is the
|
|
141
|
+
* first row. Stated, the pictures span every row and land on top of one another.
|
|
142
|
+
*/
|
|
143
|
+
:where([data-dds-context="marketing"]) .dds-featurescroll .dds-featurescroll__steps {
|
|
144
|
+
display: grid;
|
|
145
|
+
grid-template-columns: [words] 1fr [picture] 1fr;
|
|
146
|
+
grid-template-rows: repeat(var(--dds-featurescroll-stops, 1), auto);
|
|
147
|
+
row-gap: 0;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/*
|
|
151
|
+
* A stop gives up its box, so the grid can place what is inside it.
|
|
152
|
+
*
|
|
153
|
+
* This is what lets one tree serve both layouts. A picture inside its own stop can only be
|
|
154
|
+
* pinned within it — released a screen later, arriving from below, travelling rather than
|
|
155
|
+
* changing. Placed by the grid instead, every picture is in one column across every row, so
|
|
156
|
+
* they overlap and one can be exchanged for another in the same square.
|
|
157
|
+
*
|
|
158
|
+
* The stop stays in the markup and keeps its four parts together; what it gives up is a box
|
|
159
|
+
* nothing needed at this width.
|
|
160
|
+
*/
|
|
161
|
+
:where([data-dds-context="marketing"]) .dds-featurescroll .dds-featurescroll__step {
|
|
162
|
+
display: contents;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/*
|
|
166
|
+
* Nothing arrives on its own up here, whatever it was marked.
|
|
167
|
+
*
|
|
168
|
+
* Below the fold the words and then the picture come up as a reader reaches them, because there
|
|
169
|
+
* is nothing else moving in a column of stacked blocks. Above it the picture is pinned beside
|
|
170
|
+
* the words and exchanged as each title is passed — that is the section's motion, and a stop
|
|
171
|
+
* sliding in under it would be two arrivals for one thing.
|
|
172
|
+
*
|
|
173
|
+
* There is a harder reason for the picture. It sits inside a `position: sticky` band, and a
|
|
174
|
+
* transform on it is a transform the pinning has to survive. `none` rather than a zero value, so
|
|
175
|
+
* no transform exists at all.
|
|
176
|
+
*
|
|
177
|
+
* The attribute stays on both elements and is simply not read here, which is what keeps the
|
|
178
|
+
* component free of a width it cannot measure: the fold is a container query, and no observer
|
|
179
|
+
* can see one.
|
|
180
|
+
*/
|
|
181
|
+
:where([data-dds-context="marketing"]) .dds-featurescroll [data-revealed] [data-reveal-at] {
|
|
182
|
+
opacity: 1;
|
|
183
|
+
translate: none;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/*
|
|
187
|
+
* The words take a screen each, which is the rhythm the whole section is built on: a reader
|
|
188
|
+
* arrives at one stop at a time and scrolls on to the next. Centred in it so they sit level with
|
|
189
|
+
* the picture rather than at the head of a screen-tall row.
|
|
190
|
+
*/
|
|
191
|
+
:where([data-dds-context="marketing"]) .dds-featurescroll .dds-featurescroll__content {
|
|
192
|
+
justify-content: center;
|
|
193
|
+
min-block-size: 100cqh;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/*
|
|
197
|
+
* Every band pinned at one line, and all of them stacked.
|
|
198
|
+
*
|
|
199
|
+
* `grid-row` spans the whole set, so the containing block a sticky box measures its room against
|
|
200
|
+
* is the entire list rather than one stop — which is what keeps a picture pinned from the first
|
|
201
|
+
* stop to the last instead of being released at each boundary.
|
|
202
|
+
*
|
|
203
|
+
* `align-self: start` because the band is a screen tall and its area is the whole list; stretched
|
|
204
|
+
* it would be as tall as the section and there would be nothing to pin.
|
|
205
|
+
*
|
|
206
|
+
* `100cqh` rather than `100svh` for the reason LandingHero gives: with no ancestor declaring size
|
|
207
|
+
* containment the two are the same number, and the container unit is what lets a frame standing
|
|
208
|
+
* in for a window say otherwise.
|
|
209
|
+
*/
|
|
210
|
+
:where([data-dds-context="marketing"]) .dds-featurescroll .dds-featurescroll__media {
|
|
211
|
+
align-self: start;
|
|
212
|
+
block-size: calc(100cqh - var(--size-bar-height));
|
|
213
|
+
grid-row: 1 / -1;
|
|
214
|
+
inset-block-start: var(--size-bar-height);
|
|
215
|
+
opacity: 0;
|
|
216
|
+
position: sticky;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/*
|
|
220
|
+
* And one of them is shown.
|
|
221
|
+
*
|
|
222
|
+
* The only thing the component's own listener writes. Which stop's picture belongs to the words
|
|
223
|
+
* a reader has reached is a question about where each title has got to, and no query asks it —
|
|
224
|
+
* everything else here, the pinning and the dissolve, is this stylesheet's.
|
|
225
|
+
*
|
|
226
|
+
* The duration and the curve are bound on the part, so the generated sheet writes the
|
|
227
|
+
* reduced-motion block that turns this into a cut.
|
|
228
|
+
*/
|
|
229
|
+
:where([data-dds-context="marketing"])
|
|
230
|
+
.dds-featurescroll
|
|
231
|
+
.dds-featurescroll__media[data-showing="true"] {
|
|
232
|
+
opacity: 1;
|
|
233
|
+
/*
|
|
234
|
+
* On top, and that is what makes this a dissolve rather than a dip.
|
|
235
|
+
*
|
|
236
|
+
* The arriving picture fades in over whatever is beneath it, so at every moment of the change
|
|
237
|
+
* the square is covered by something opaque. Fading both at once instead leaves them near half
|
|
238
|
+
* way together, the section's own ground shows through the pair, and the square brightens and
|
|
239
|
+
* darkens — a pulse, from nothing but two opacities crossing.
|
|
240
|
+
*
|
|
241
|
+
* It has to be the shown one rather than the later one in the document, because a reader
|
|
242
|
+
* scrolls back as well as forward. Going up, the arriving picture is earlier in the markup and
|
|
243
|
+
* would fade in underneath the one it is replacing: invisible until that one left, which is a
|
|
244
|
+
* cut with a pause in front of it.
|
|
245
|
+
*
|
|
246
|
+
* A bare integer, which is the one kind of value this system keeps out of its tokens: a
|
|
247
|
+
* stacking order inside a component is not a decision any brand or mode has an opinion about.
|
|
248
|
+
*/
|
|
249
|
+
z-index: 1;
|
|
250
|
+
/* No wait on the way in. The pair of delays is the whole mechanism — see the rule below. */
|
|
251
|
+
transition-delay: 0s;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/*
|
|
255
|
+
* The one leaving holds until it has been covered, then goes.
|
|
256
|
+
*
|
|
257
|
+
* This is the other half of the dissolve. The arriving picture takes the whole duration to reach
|
|
258
|
+
* full opacity, and for all of it the one underneath stays exactly as it was — so there is never
|
|
259
|
+
* a frame where the square is anything less than a complete picture. Only once it is hidden
|
|
260
|
+
* behind the new one does it drop, which costs nothing to look at because nobody can see it.
|
|
261
|
+
*
|
|
262
|
+
* The delay is the arriving picture's own duration, read from the same role, so the two cannot
|
|
263
|
+
* drift apart into a gap or an overlap.
|
|
264
|
+
*
|
|
265
|
+
* There is no scale here any more. It read as a pulse, which is what it was: a square this large
|
|
266
|
+
* carries a change of picture on its own, and a sixth of a second of zoom on top of it is the
|
|
267
|
+
* section drawing attention to its own machinery.
|
|
268
|
+
*/
|
|
269
|
+
:where([data-dds-context="marketing"]) .dds-featurescroll .dds-featurescroll__media {
|
|
270
|
+
transition-delay: var(--motion-duration-media-hover);
|
|
271
|
+
/* Named here rather than in the contract because `staticCss` takes keywords, and this is the
|
|
272
|
+
one property the pair of rules is about. The duration and the curve stay bound there, which
|
|
273
|
+
is what the generated reduced-motion block is written from — so a reader who asked for less
|
|
274
|
+
still gets a cut. */
|
|
275
|
+
transition-property: opacity;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/*
|
|
279
|
+
* The largest square the band can hold.
|
|
280
|
+
*
|
|
281
|
+
* Both bounds in one measure, because a square capped on one axis alone is not a square: giving
|
|
282
|
+
* it a `max-block-size` clamps the height and leaves the width, and the browser hands back a
|
|
283
|
+
* flattened picture. `min()` picks whichever runs out first — the half it sits in, or the band's
|
|
284
|
+
* own inner height, which is the band less the air it holds the picture off its edges by.
|
|
285
|
+
*/
|
|
286
|
+
:where([data-dds-context="marketing"]) .dds-featurescroll .dds-featurescroll__picture {
|
|
287
|
+
inline-size: min(
|
|
288
|
+
100%,
|
|
289
|
+
calc(100cqh - var(--size-bar-height) - var(--space-gap-lg) * 2)
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/* GENERATED from featuresplit.contract.json. Do not edit. */
|
|
2
|
+
|
|
3
|
+
:where([data-dds-context="marketing"]) .dds-featuresplit {
|
|
4
|
+
box-sizing: border-box;
|
|
5
|
+
container-type: inline-size;
|
|
6
|
+
display: flex;
|
|
7
|
+
inline-size: 100%;
|
|
8
|
+
justify-content: center;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
:where([data-dds-context="marketing"]) .dds-featuresplit .dds-featuresplit__inner {
|
|
12
|
+
box-sizing: border-box;
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-direction: column;
|
|
15
|
+
inline-size: 100%;
|
|
16
|
+
min-inline-size: 0;
|
|
17
|
+
max-inline-size: var(--size-content-max);
|
|
18
|
+
padding-block: var(--space-section-default);
|
|
19
|
+
padding-inline: var(--space-inset-page);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
:where([data-dds-context="marketing"]) .dds-featuresplit .dds-featuresplit__split {
|
|
23
|
+
align-items: stretch;
|
|
24
|
+
display: flex;
|
|
25
|
+
min-inline-size: 0;
|
|
26
|
+
column-gap: var(--space-gap-3xl);
|
|
27
|
+
row-gap: var(--space-gap-2xl);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
:where([data-dds-context="marketing"]) .dds-featuresplit .dds-featuresplit__content {
|
|
31
|
+
display: flex;
|
|
32
|
+
flex-direction: column;
|
|
33
|
+
min-inline-size: 0;
|
|
34
|
+
row-gap: var(--space-gap-none);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
:where([data-dds-context="marketing"]) .dds-featuresplit .dds-featuresplit__title {
|
|
38
|
+
margin-block: 0;
|
|
39
|
+
text-wrap: balance;
|
|
40
|
+
font-family: var(--font-h2-family);
|
|
41
|
+
font-size: var(--font-h2-size);
|
|
42
|
+
font-weight: var(--font-h2-weight);
|
|
43
|
+
letter-spacing: var(--font-h2-letter-spacing);
|
|
44
|
+
line-height: var(--font-h2-line-height);
|
|
45
|
+
margin-block-end: var(--space-heading-title-description);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
:where([data-dds-context="marketing"]) .dds-featuresplit .dds-featuresplit__description {
|
|
49
|
+
margin-block: 0;
|
|
50
|
+
font-family: var(--font-body-md-family);
|
|
51
|
+
font-size: var(--font-body-md-size);
|
|
52
|
+
font-weight: var(--font-body-md-weight);
|
|
53
|
+
letter-spacing: var(--font-body-md-letter-spacing);
|
|
54
|
+
line-height: var(--font-body-md-line-height);
|
|
55
|
+
margin-block-end: var(--space-heading-description-actions);
|
|
56
|
+
max-inline-size: var(--size-measure-description);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
:where([data-dds-context="marketing"]) .dds-featuresplit .dds-featuresplit__link {
|
|
60
|
+
display: flex;
|
|
61
|
+
margin-block-start: var(--space-gap-none);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
:where([data-dds-context="marketing"]) .dds-featuresplit .dds-featuresplit__media {
|
|
65
|
+
min-inline-size: 0;
|
|
66
|
+
overflow: hidden;
|
|
67
|
+
position: relative;
|
|
68
|
+
aspect-ratio: var(--aspect-square);
|
|
69
|
+
border-radius: var(--radius-container);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
:where([data-dds-context="marketing"]) .dds-featuresplit[data-media-position="end"] .dds-featuresplit__split {
|
|
73
|
+
flex-direction: row;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
:where([data-dds-context="marketing"]) .dds-featuresplit[data-media-position="start"] .dds-featuresplit__split {
|
|
77
|
+
flex-direction: row-reverse;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
:where([data-dds-context="marketing"]) .dds-featuresplit[data-ground="page"] {
|
|
81
|
+
background-color: var(--color-bg-page);
|
|
82
|
+
color: var(--color-fg-default);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
:where([data-dds-context="marketing"]) .dds-featuresplit[data-ground="strong"] {
|
|
86
|
+
background-color: var(--color-bg-brand-strong);
|
|
87
|
+
color: var(--color-fg-on-brand-strong);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
:where([data-dds-context="marketing"]) .dds-featuresplit[data-ground="subtle"] {
|
|
91
|
+
background-color: var(--color-bg-page);
|
|
92
|
+
background-image: linear-gradient(var(--color-bg-band-subtle), var(--color-bg-band-subtle));
|
|
93
|
+
color: var(--color-fg-default);
|
|
94
|
+
}
|
|
95
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import './FeatureSplit.css';
|
|
2
|
+
import './feature-split-impl.css';
|
|
3
|
+
import '../block-reveal.css';
|
|
4
|
+
import { type FeatureSplitProps } from './FeatureSplit.types.js';
|
|
5
|
+
export type { FeatureSplitProps };
|
|
6
|
+
/**
|
|
7
|
+
* A section putting one claim, a line explaining it and a way onward beside a single picture.
|
|
8
|
+
*
|
|
9
|
+
* The plainest block this package has, and that is what it is for: a page that has said what
|
|
10
|
+
* something is needs somewhere to say one more thing about it without opening a list, a grid or a
|
|
11
|
+
* set of cards.
|
|
12
|
+
*
|
|
13
|
+
* A split rather than a stack, because the picture is the other half of the argument — the same
|
|
14
|
+
* words over a full-width photograph read as a caption, and beside it they read as a claim with
|
|
15
|
+
* evidence.
|
|
16
|
+
*
|
|
17
|
+
* The steps inside the column are carried by each part's own `margin-block-end` rather than by a
|
|
18
|
+
* gap on the column, because title-to-description and description-to-what-follows are two
|
|
19
|
+
* different roles that a single gap could only ever be one of. `content` binds `space.gap.none`
|
|
20
|
+
* to say the absence is deliberate rather than forgotten.
|
|
21
|
+
*
|
|
22
|
+
* The source template carried `showDescription` and `showLink` beside the values themselves. Both
|
|
23
|
+
* are gone: absence is the switch, which is the rule Footer set and `given` exists for. A flag
|
|
24
|
+
* saying whether to draw a value the caller already decided not to pass is two ways to say one
|
|
25
|
+
* thing, and they disagree the first time somebody sets only one of them.
|
|
26
|
+
*
|
|
27
|
+
* The title carries the section's accessible name through `aria-labelledby`. A `<section>` is only
|
|
28
|
+
* a region landmark once it has a name.
|
|
29
|
+
*/
|
|
30
|
+
export declare function FeatureSplit({ ground, mediaPosition, title, description, link, media, }: FeatureSplitProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useId } from 'react';
|
|
4
|
+
import './FeatureSplit.css';
|
|
5
|
+
import './feature-split-impl.css';
|
|
6
|
+
import '../block-reveal.css';
|
|
7
|
+
import { arriving, useReveal } from '../use-reveal.js';
|
|
8
|
+
import { given } from '../../given.js';
|
|
9
|
+
import { featureSplitDefaults, featureSplitParts, } from './FeatureSplit.types.js';
|
|
10
|
+
/**
|
|
11
|
+
* A section putting one claim, a line explaining it and a way onward beside a single picture.
|
|
12
|
+
*
|
|
13
|
+
* The plainest block this package has, and that is what it is for: a page that has said what
|
|
14
|
+
* something is needs somewhere to say one more thing about it without opening a list, a grid or a
|
|
15
|
+
* set of cards.
|
|
16
|
+
*
|
|
17
|
+
* A split rather than a stack, because the picture is the other half of the argument — the same
|
|
18
|
+
* words over a full-width photograph read as a caption, and beside it they read as a claim with
|
|
19
|
+
* evidence.
|
|
20
|
+
*
|
|
21
|
+
* The steps inside the column are carried by each part's own `margin-block-end` rather than by a
|
|
22
|
+
* gap on the column, because title-to-description and description-to-what-follows are two
|
|
23
|
+
* different roles that a single gap could only ever be one of. `content` binds `space.gap.none`
|
|
24
|
+
* to say the absence is deliberate rather than forgotten.
|
|
25
|
+
*
|
|
26
|
+
* The source template carried `showDescription` and `showLink` beside the values themselves. Both
|
|
27
|
+
* are gone: absence is the switch, which is the rule Footer set and `given` exists for. A flag
|
|
28
|
+
* saying whether to draw a value the caller already decided not to pass is two ways to say one
|
|
29
|
+
* thing, and they disagree the first time somebody sets only one of them.
|
|
30
|
+
*
|
|
31
|
+
* The title carries the section's accessible name through `aria-labelledby`. A `<section>` is only
|
|
32
|
+
* a region landmark once it has a name.
|
|
33
|
+
*/
|
|
34
|
+
export function FeatureSplit({ ground = featureSplitDefaults.ground, mediaPosition = featureSplitDefaults.mediaPosition, title, description, link, media, }) {
|
|
35
|
+
const titleId = useId();
|
|
36
|
+
const [band, revealed] = useReveal();
|
|
37
|
+
return (_jsx("section", { className: featureSplitParts.root, "data-ground": ground, "data-media-position": mediaPosition, "aria-labelledby": titleId, children: _jsx("div", { className: featureSplitParts.inner, ref: band, "data-revealed": revealed, children: _jsxs("div", { className: featureSplitParts.split, children: [_jsxs("div", { className: featureSplitParts.content, children: [_jsx("h2", { className: featureSplitParts.title, ...arriving(0), id: titleId, children: title }), given(description) ? (_jsx("p", { className: featureSplitParts.description, ...arriving(1), children: description })) : null, link ? (_jsx("div", { className: featureSplitParts.link, ...arriving(2), children: link })) : null] }), _jsx("div", { className: featureSplitParts.media, ...arriving(3), "aria-hidden": "true", children: media })] }) }) }));
|
|
38
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export interface FeatureSplitOwnProps {
|
|
3
|
+
/**
|
|
4
|
+
* `page` — The default, and the page's own white. A section that has nothing to separate itself from what is above it — most of them — and the ground a reader is already on
|
|
5
|
+
* `subtle` — A quiet step off the page, for a band that wants to read as its own section without changing the page's voice. The one to reach for first when two sections need telling apart. Painted as the band's tint laid over the page's own ground, so it is the same grey whatever is behind the section — in dark mode the tint alone took on whatever it was set against, and on a light surface it was light ink on white.
|
|
6
|
+
* `strong` — `color.bg.brand-strong`, the dark band. It stops the page rather than dividing it, so it is for a section a reader should arrive at — and one per page is usually one more than enough
|
|
7
|
+
*/
|
|
8
|
+
ground?: 'page' | 'subtle' | 'strong';
|
|
9
|
+
/**
|
|
10
|
+
* `end` — The default. The claim is read first, which is the order the section argues in
|
|
11
|
+
* `start` — The second of two on a page, so the two do not read as one column with a picture stuck to one side
|
|
12
|
+
*/
|
|
13
|
+
mediaPosition?: 'end' | 'start';
|
|
14
|
+
/** Always. The one claim the section makes, in the fewest words that say it */
|
|
15
|
+
title: string;
|
|
16
|
+
/** The lines under the title that say what the claim above means, for a reader not convinced by the claim alone. Two sentences is the normal length here and what the section is sized around — one is usually the title restated, and the column is wide enough to carry more than that. */
|
|
17
|
+
description?: string;
|
|
18
|
+
/** The section has somewhere to send a reader who wants more than it says — the page this one is a summary of. "Learn more" is this site's wording for it, and using the same three words everywhere is what lets a reader stop reading them: a link that renames itself per section is a link somebody has to read before they know it is the way onward. */
|
|
19
|
+
link?: ReactNode;
|
|
20
|
+
/** Always. The picture that sits beside the claim — a screenshot, a photograph, an illustration. The section is a split and the picture is one half of it: without one the claim runs to a measure it was never sized for. */
|
|
21
|
+
media: ReactNode;
|
|
22
|
+
}
|
|
23
|
+
export type FeatureSplitProps = FeatureSplitOwnProps;
|
|
24
|
+
/** The class each part carries. Apply these — do not retype them. */
|
|
25
|
+
export declare const featureSplitParts: {
|
|
26
|
+
readonly root: "dds-featuresplit";
|
|
27
|
+
readonly inner: "dds-featuresplit__inner";
|
|
28
|
+
readonly split: "dds-featuresplit__split";
|
|
29
|
+
readonly content: "dds-featuresplit__content";
|
|
30
|
+
readonly title: "dds-featuresplit__title";
|
|
31
|
+
readonly description: "dds-featuresplit__description";
|
|
32
|
+
readonly link: "dds-featuresplit__link";
|
|
33
|
+
readonly media: "dds-featuresplit__media";
|
|
34
|
+
};
|
|
35
|
+
/** Generated so defaults cannot drift from the contract. */
|
|
36
|
+
export declare const featureSplitDefaults: {
|
|
37
|
+
readonly ground: "page";
|
|
38
|
+
readonly mediaPosition: "end";
|
|
39
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// GENERATED from feature-split.contract.json. Do not edit.
|
|
2
|
+
/** The class each part carries. Apply these — do not retype them. */
|
|
3
|
+
export const featureSplitParts = {
|
|
4
|
+
root: 'dds-featuresplit',
|
|
5
|
+
inner: 'dds-featuresplit__inner',
|
|
6
|
+
split: 'dds-featuresplit__split',
|
|
7
|
+
content: 'dds-featuresplit__content',
|
|
8
|
+
title: 'dds-featuresplit__title',
|
|
9
|
+
description: 'dds-featuresplit__description',
|
|
10
|
+
link: 'dds-featuresplit__link',
|
|
11
|
+
media: 'dds-featuresplit__media',
|
|
12
|
+
};
|
|
13
|
+
/** Generated so defaults cannot drift from the contract. */
|
|
14
|
+
export const featureSplitDefaults = { ground: 'page', mediaPosition: 'end' };
|
|
@@ -0,0 +1,83 @@
|
|
|
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
|
+
* A title with nothing under it carries no step under it.
|
|
10
|
+
*
|
|
11
|
+
* The steps inside the column sit on each part's own `margin-block-end`, because title-to-
|
|
12
|
+
* description and description-to-what-follows are two different roles. The cost is a trailing
|
|
13
|
+
* margin: with both the description and the link absent, the title is the last child and its step
|
|
14
|
+
* is measured against nothing.
|
|
15
|
+
*
|
|
16
|
+
* `:last-child` is the condition itself rather than a prop standing in for it — two parts can
|
|
17
|
+
* follow the title, and asking the DOM which is last is the question with one answer.
|
|
18
|
+
*/
|
|
19
|
+
:where([data-dds-context="marketing"]) .dds-featuresplit .dds-featuresplit__title:last-child,
|
|
20
|
+
:where([data-dds-context="marketing"]) .dds-featuresplit .dds-featuresplit__description:last-child {
|
|
21
|
+
margin-block-end: 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/*
|
|
25
|
+
* One column below the fold, and the claim leads it.
|
|
26
|
+
*
|
|
27
|
+
* The row direction is the contract's to say — it varies by a styling axis, which is what
|
|
28
|
+
* `structure[]` is for, and the generated sheet carries both values. What is left here is the
|
|
29
|
+
* fold, which a binding cannot express: `structure` has no width to attach a rule to.
|
|
30
|
+
*
|
|
31
|
+
* Written a class deeper than it looks so it wins. The generated rule is
|
|
32
|
+
* `.dds-featuresplit[data-media-position="end"] .dds-featuresplit__split` — a class, an attribute
|
|
33
|
+
* and a class — and a plainer selector here would lose to it however late it is loaded. That is
|
|
34
|
+
* the fault FeatureBullets shipped with for an afternoon: the split stayed a column at every
|
|
35
|
+
* width, and a picture given `flex: 1 1 0` on the block axis collapsed to nothing.
|
|
36
|
+
*
|
|
37
|
+
* `48rem` longhand because `var()` is not permitted in a container query condition, and it is the
|
|
38
|
+
* width the rest of this package folds at.
|
|
39
|
+
*/
|
|
40
|
+
@container (max-width: 47.9375rem) {
|
|
41
|
+
:where([data-dds-context="marketing"])
|
|
42
|
+
.dds-featuresplit[data-media-position]
|
|
43
|
+
.dds-featuresplit__split {
|
|
44
|
+
flex-direction: column;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/*
|
|
49
|
+
* At full width the claim takes a measure and the picture takes the rest.
|
|
50
|
+
*
|
|
51
|
+
* The reference design writes 540px against its 1280, which is a number only right at one width.
|
|
52
|
+
* A share holds everywhere, and the picture growing into whatever is left is what keeps the square
|
|
53
|
+
* square without either half naming a height.
|
|
54
|
+
*/
|
|
55
|
+
@container (min-width: 48rem) {
|
|
56
|
+
:where([data-dds-context="marketing"]) .dds-featuresplit .dds-featuresplit__split {
|
|
57
|
+
align-items: center;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
:where([data-dds-context="marketing"]) .dds-featuresplit .dds-featuresplit__content {
|
|
61
|
+
flex: 0 0 45%;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
:where([data-dds-context="marketing"]) .dds-featuresplit .dds-featuresplit__media {
|
|
65
|
+
flex: 1 1 0;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/*
|
|
70
|
+
* Whatever the page put in the picture fills the square.
|
|
71
|
+
*
|
|
72
|
+
* The slot takes a node rather than a source, so what arrives is the page's element and this
|
|
73
|
+
* cannot know whether it carries dimensions. `cover` rather than `contain`, because the slot is a
|
|
74
|
+
* square the section decided on and a picture letterboxed inside it would show the ground through
|
|
75
|
+
* a corner the section rounded.
|
|
76
|
+
*/
|
|
77
|
+
:where([data-dds-context="marketing"]) .dds-featuresplit .dds-featuresplit__media > *,
|
|
78
|
+
:where([data-dds-context="marketing"]) .dds-featuresplit .dds-featuresplit__media img {
|
|
79
|
+
block-size: 100%;
|
|
80
|
+
display: block;
|
|
81
|
+
inline-size: 100%;
|
|
82
|
+
object-fit: cover;
|
|
83
|
+
}
|