@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,284 @@
|
|
|
1
|
+
<!-- GENERATED from feature-scroll.contract.json. Do not edit. -->
|
|
2
|
+
|
|
3
|
+
# FeatureScroll
|
|
4
|
+
|
|
5
|
+
A section that gives a set of things one screen each, with the picture pinned beside the words while a reader is on them.
|
|
6
|
+
|
|
7
|
+
For a set too long to compare and too important to list — six markets, five platforms. A grid of cards asks a reader which to read first and most read none; this arrives at one at a time, at the cost of a screen of scrolling each.
|
|
8
|
+
|
|
9
|
+
The stops are data rather than children, so a page can map them from a CMS. What that costs is slots: the picture is a path and the way onward two strings. `FeatureScrollStep` in `scroll-data` is the shape of one.
|
|
10
|
+
|
|
11
|
+
The picture is held by `position: sticky`. Which picture is showing is the one thing here that is measured rather than styled.
|
|
12
|
+
|
|
13
|
+
## Use when
|
|
14
|
+
|
|
15
|
+
- A page has three or more things of the same kind to explain, each with a picture worth a screen — the markets a broker offers, the platforms it runs, the steps of an account opening
|
|
16
|
+
- The set is meant to be walked through rather than compared, and a reader is expected to scroll it
|
|
17
|
+
- The set comes from somewhere — a CMS, a registry, a filtered list — rather than being written out by hand
|
|
18
|
+
|
|
19
|
+
## Do not use
|
|
20
|
+
|
|
21
|
+
- There are three or four items a reader is meant to compare → `FeatureCards`
|
|
22
|
+
- The set is lines rather than things — what an account includes → `FeatureBullets`
|
|
23
|
+
- There is one point to make → `FeatureSplit`
|
|
24
|
+
- The section opens the page → `LandingHero`
|
|
25
|
+
- Two items. A set given a screen each has to be worth the travel, and a pair is worth one screen with both on it
|
|
26
|
+
- A list of links. The stops are things with pictures and sentences; a set of destinations belongs in a navigation panel or a footer's columns
|
|
27
|
+
- Anything a reader has to act on. This is a walk through a set, and a form or a price in the middle of it is a decision asked at the point a reader is least ready to make one
|
|
28
|
+
- A set whose stops each need their own arrangement. Every stop here is the same shape by construction, which is what makes the set a set — a page wanting one stop to differ wants several FeatureSplits instead
|
|
29
|
+
|
|
30
|
+
## Anatomy
|
|
31
|
+
|
|
32
|
+
FeatureScroll renders 11 elements. A token binding names the part it applies to.
|
|
33
|
+
|
|
34
|
+
- `root` — `<section>`, class `dds-featurescroll`. The section. It paints the ground that separates it from the page above and below, and asks its own width — so everything inside it, including the fold each stop takes, answers to the section rather than to the window.
|
|
35
|
+
|
|
36
|
+
It paints one of the page's three grounds and the ink that goes with it, in one decision — see `ground`. The ink is set here and inherited by everything inside, which is the package's rule about a section that paints a ground: stated once, it cannot be half-applied.
|
|
37
|
+
- `inner` — `<div>`, class `dds-featurescroll__inner`. The measure and the gutter, holding the section's content in the same column as every other section on the page.
|
|
38
|
+
- `heading` — `<h2>`, class `dds-featurescroll__heading`. The line over the set. Held to a heading's measure and centred at full width, which is what says the set below it is the subject rather than a step in an argument.
|
|
39
|
+
|
|
40
|
+
Named `heading` and not `title` because each stop has a title of its own, and two parts called the same thing in one component is a stylesheet nobody can read.
|
|
41
|
+
- `steps` — `<div>`, class `dds-featurescroll__steps`. The set, one stop under the next below the fold, and a two-column grid at full width.
|
|
42
|
+
|
|
43
|
+
The grid is what puts every picture in one place. Each stop stops being a box there — `display: contents` — so the words and the pictures become the grid's own items, and every picture is placed in the same column across every row. They overlap, which is the whole point: one is shown and the rest are held at nothing, so moving between them is a change in the same square rather than one picture leaving and another arriving from below.
|
|
44
|
+
|
|
45
|
+
It carries a gap between the stops below the fold and none at full width, where the rows are a screen tall and the picture over them never moves.
|
|
46
|
+
- `step` — `<div>`, class `dds-featurescroll__step`. One stop: a name, a line, a way onward and the picture that belongs to them.
|
|
47
|
+
|
|
48
|
+
Below the fold it is a stack, and the only place these four are boxed together. At full width it is `display: contents` and has no box at all — the words and the picture are placed by the grid instead, into a column each, because a picture inside its own stop can only be pinned within it and the section needs one pinned across the whole set.
|
|
49
|
+
|
|
50
|
+
It is still what groups them in the markup, which is what a reader's assistive technology and anybody reading the DOM sees: four things about one stop, in the order a phone reads them.
|
|
51
|
+
- `content` — `<div>`, class `dds-featurescroll__content`. The words of one stop, centred in the stop's own height so they sit level with the picture beside them.
|
|
52
|
+
|
|
53
|
+
Centring is `justify-content` on a column, which only says anything once the column is as tall as the stop — that is what the row's `stretch` is for. A column the height of its own text has nothing to centre within, and reads as correct right up until the stop is taller than the words, which here it always is.
|
|
54
|
+
|
|
55
|
+
It sets no gap of its own — the steps inside it sit on each part's own margin, title-to-description and description-to-what-follows being two different roles that a single gap could only ever be one of.
|
|
56
|
+
|
|
57
|
+
Below the fold it is `display: contents`, which is what lets the picture sit between the name and the line about it without a second copy of the markup. The reference design ships two DOM trees for this and hides one at each width; two trees are two things to keep in step, and a screen reader offered the hidden one finds every stop twice.
|
|
58
|
+
|
|
59
|
+
Below the fold it also arrives on its own, a beat ahead of the picture under it. Above the fold neither moves: the pinned square being exchanged is the section's motion there, and a stop sliding in under it would be two arrivals for one thing.
|
|
60
|
+
- `title` — `<h3>`, class `dds-featurescroll__title`. What one stop is called. `h3` because the section carries the page's `h2`, and the stops are subsections of it — which is also what lets a screen reader move between them rather than scroll through six screens.
|
|
61
|
+
- `description` — `<p>`, class `dds-featurescroll__description`. The line under the name, held to a readable measure rather than the half it sits in — a column of body text as wide as a picture is a column a reader loses their place in.
|
|
62
|
+
- `link` — `<div>`, class `dds-featurescroll__link`. The way onward from one stop, drawn only where that stop gives both a href and a label. It holds a Link, which the section builds rather than the page: a list of records cannot carry an element, so the two strings are what arrive and this is what turns them into the same Link the rest of the site uses.
|
|
63
|
+
- `media` — `<div>`, class `dds-featurescroll__media`. The band one picture is pinned inside.
|
|
64
|
+
|
|
65
|
+
Every stop has one and at full width they are stacked in the same column of the grid, pinned at the same line under the bar. One is shown and the rest are held at nothing, so a reader who scrolls past a stop's title sees the square change rather than travel — which is the section's whole effect and the reason the stops give up their boxes for it.
|
|
66
|
+
|
|
67
|
+
One picture gives way to the next by fading in over it, rather than by the pair fading past each other. The arriving one is brought to the front and takes the full duration to become opaque, and the one beneath holds exactly as it was until it is covered — so at no moment is the square anything less than a complete picture. Fading both at once would leave them near half way together, the section's ground showing through the pair, and the square would brighten and dim: a pulse made of nothing but two opacities crossing.
|
|
68
|
+
|
|
69
|
+
Front is the shown one rather than the later one in the markup, because a reader scrolls back as well as forward — going up, the arriving picture is earlier in the document and would fade in underneath what it is replacing.
|
|
70
|
+
|
|
71
|
+
The duration and the easing are the roles this system names for art moving inside a frame that holds still, which is what the pinned square is. The easing is symmetric for the same reason it is on a hovered card: the change runs both ways as a reader scrolls up and down.
|
|
72
|
+
|
|
73
|
+
Which one is shown is the only thing here that is not CSS: the component watches where each title has reached and marks the band whose stop is next. See FeatureScroll's own notes.
|
|
74
|
+
|
|
75
|
+
It is a band rather than the picture itself because a sticky box holds only while its containing block has room left under it — a square pinned in a screen-tall column is released a third of the way down. The band is the height of a screen, so it never runs out.
|
|
76
|
+
|
|
77
|
+
Below the fold it is static, hugs its picture, and every one of them is shown: there is one column there, nothing to pin against, and each stop carries its own.
|
|
78
|
+
- `picture` — `<img>`, class `dds-featurescroll__picture`. The picture of one stop.
|
|
79
|
+
|
|
80
|
+
Held to a square and cropped to fill it, so a section of six stops is six identical frames whatever the page passes — six different crops down a scroll is a column whose edge moves on every screen. `cover` rather than `contain`, because the square is the section's decision and a picture letterboxed inside it would show the ground through a corner the section rounded.
|
|
81
|
+
|
|
82
|
+
The largest square the band can hold: as wide as the half it sits in, or as tall as the band, whichever runs out first. Both bounds are needed — a square capped on one axis alone is not a square, it is a picture the browser has flattened.
|
|
83
|
+
|
|
84
|
+
Decorative, and hidden from assistive technology with an empty alt. What a stop says is its name and the line under it.
|
|
85
|
+
|
|
86
|
+
The arrival below the fold is on the picture rather than on the band around it, because the band's own opacity belongs to the crossfade — two things writing one property is two things disagreeing. The gap between the words coming up and the picture following is the layout's rather than a delay: each watches for itself, and on a phone the picture reaches the viewport a beat later because it sits under the line it belongs to.
|
|
87
|
+
|
|
88
|
+
Apply these classes from the generated `featureScrollParts` constant rather than writing the strings by hand.
|
|
89
|
+
|
|
90
|
+
## Composition
|
|
91
|
+
|
|
92
|
+
- Takes no children. Everything it renders comes from props
|
|
93
|
+
- Never contains: `FeatureScroll`, `FeatureSplit`, `FeatureBullets`, `LandingHero`, `ProductHero`, `PageHeader`, `Footer`, `Navbar`, `Table`, `Accordion` — in children or in any prop that takes a node
|
|
94
|
+
|
|
95
|
+
## Props
|
|
96
|
+
|
|
97
|
+
### ground — `page` | `subtle` | `strong` (default `page`)
|
|
98
|
+
|
|
99
|
+
- `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
|
|
100
|
+
- `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.
|
|
101
|
+
- `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
|
|
102
|
+
|
|
103
|
+
Use when: Which of the page's three grounds this section paints. A marketing page is white, and a band that wants to be seen as a band takes one of the other two — so this is how a page builds its rhythm: white, white, grey, white, dark. Two in a row is a deliberate pair rather than an accident, and the section above knows nothing about the one below it, so the page is the only thing that can decide.
|
|
104
|
+
|
|
105
|
+
The ink follows the ground and is not a second decision: a dark band carries white type because this sets both halves in one value, which is what the painted-ground-carries-its-ink rule exists to stop a page getting wrong.
|
|
106
|
+
Never for: Marking importance. Every section on a page painted dark is a page with no emphasis left, and a reader stops reading the grounds as meaning anything. Alternating for its own sake is the same mistake more slowly.
|
|
107
|
+
|
|
108
|
+
And never the brand's own coral, `color.bg.brand`, which is not offered here. That ground belongs to the band that asks a reader to do something — the one place on a page where the brand colour is the point rather than decoration — and a feature section wearing it is a page with two calls to action, one of which does nothing. It is `color.bg.brand-strong` that this offers: the dark brand ground, which is a band breaking a page rather than a band asking for something.
|
|
109
|
+
|
|
110
|
+
### mediaPosition — `end` | `start` (default `end`)
|
|
111
|
+
|
|
112
|
+
- `end` — The default. The words are read first, which is the order a reader’s eye takes a two-up row in
|
|
113
|
+
- `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
|
|
114
|
+
|
|
115
|
+
Use when: Which side the pictures pin to, for every stop at once. It is one value rather than one per stop because the picture pins to a place and stays there for several screens — a reader who found it once looks for it in the same place on the next one, and a set that moved it would be a set they have to re-find.
|
|
116
|
+
Never for: Moving the pictures on a phone. There is one column there, and the picture sits under each name whichever side this names
|
|
117
|
+
|
|
118
|
+
### title — `string` **Required.**
|
|
119
|
+
|
|
120
|
+
Use when: 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
|
|
121
|
+
Never for: A claim about the first stop. The heading is over the whole set; an argument belongs in the stop it argues for
|
|
122
|
+
|
|
123
|
+
### steps — `data` `FeatureScrollStep[]` **Required.**
|
|
124
|
+
|
|
125
|
+
Use when: 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.
|
|
126
|
+
|
|
127
|
+
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.
|
|
128
|
+
Never for: A set assembled to fill the section. Each stop costs a reader a screen of scrolling, so a stop that is there for symmetry is a screen nobody wanted
|
|
129
|
+
|
|
130
|
+
## Accessibility
|
|
131
|
+
|
|
132
|
+
Guaranteed by the component. Write none of it by hand: the ids these attributes point at are generated per instance, so an attribute set from outside either says the same thing twice or points at nothing.
|
|
133
|
+
|
|
134
|
+
- Announced as: `region`
|
|
135
|
+
- `Tab` — Moves through the ways onward, one per stop. Nothing else here is operable, and nothing about the pinning is reachable at all — it is the browser keeping a box in place while the page scrolls.
|
|
136
|
+
- accessible name from title, so the section is a named region a screen reader can jump to and skip rather than several screens a reader has to walk
|
|
137
|
+
- Each stop's name is a heading, so a reader moving by heading passes through the set at the speed they choose rather than at the speed the scroll imposes
|
|
138
|
+
- The pictures are decoration and are hidden from assistive technology. What a stop says is its name and its line; a picture carrying something they do not is a stop that reads as half empty to anyone who cannot see it
|
|
139
|
+
- Nothing here depends on scrolling having happened. Every stop is in the document, fully rendered, at the moment the page loads — the pinning is a visual arrangement of content that is already there, so a reader who never scrolls and a reader using a screen reader meet the same set
|
|
140
|
+
|
|
141
|
+
## Rules
|
|
142
|
+
|
|
143
|
+
- **On a phone a section's actions stack and each fills the width, including when there is only one** — On a phone a page's actions stack, and each one fills the width. Not most of it, and not the width its label happens to need — the whole column.
|
|
144
|
+
|
|
145
|
+
Two side by side is what this replaces. On the narrowest screen each gets half a column minus the gap, which is rarely enough for a real label: the words wrap inside the pills, the two buttons come out different heights, and the pair reads as a broken row rather than as a choice. Stacked, each has the whole width and neither has to shorten its words to fit.
|
|
146
|
+
|
|
147
|
+
A single action fills too, and that is the part worth saying, because it is the one that looks unnecessary. A lone button has the room to sit at its natural width and still should not: a full-width control is the phone's convention for the thing the screen is asking you to do, and a pill hugging its label reads as one option among others that are not there. It also makes the target predictable — the whole width, wherever the thumb lands — which a centred pill does not.
|
|
148
|
+
|
|
149
|
+
Filling is the row's job rather than the button's. A button has no width of its own and is not given one; the row it sits in becomes a column and stretches its children, so nothing about the control changes and the same button is full width here and its own size on a desktop.
|
|
150
|
+
|
|
151
|
+
This is the phone's breakpoint, size.breakpoint.md, and not the wider one that decides whether a heading is centred. Those are different questions and they break at different widths: a heading stops being centred when the measure is the screen, while a button stops being full width much sooner than that — stretched across a tablet it is a long way from its own label, and a label centred in a metre of pill is harder to hit than one in a pill the size of the words.
|
|
152
|
+
|
|
153
|
+
What this is not about is every row of controls. A set of filter chips, a pagination strip, a toolbar, a link sitting after a sentence — none of these is the page asking for something, and stacking them to full width would turn a strip the eye skims into a list it has to read. The test is whether the row is the call to action the section was written for.
|
|
154
|
+
Guidance only — cannot be checked automatically.
|
|
155
|
+
- **Never nest one interactive element inside another** — Nested interactive elements produce ambiguous hit targets and an unreachable inner control for keyboard and screen-reader users. This is why the rule cannot live on any single component — it constrains Button, Link, CardButton and TextInput simultaneously.
|
|
156
|
+
Guidance only — cannot be checked automatically.
|
|
157
|
+
- **A section that paints a ground sets the ink to go with it** — A page sits on color.bg.page, and a section that separates itself from it takes one of three grounds. color.bg.subtle is the light one and needs nothing further — the page's own ink still reads on it. The other two are dark and do not: color.bg.brand-strong takes color.fg.on-brand-strong, and color.bg.brand takes color.fg.on-solid. Both are white, and both are written as the role rather than the white they resolve to. Set one of those backgrounds without its ink and the text inherits whatever the page was already using, which is near-black on a dark band — legible to nobody, and invisible to every check here, because each pair passes contrast on its own and nothing can tell whether a section used both.
|
|
158
|
+
|
|
159
|
+
color.bg.brand carries a second limit that is not about forgetting. Its ink measures 3.39:1, which clears the 3:1 a heading needs and misses the 4.5:1 running copy needs. A coral band may carry a headline and a call to action; a paragraph on one is text the reader has to work through at a ratio the waiver on that role does not cover — the waiver says so itself. A section with copy in it takes color.bg.brand-strong, which is near-black and carries white at full contrast.
|
|
160
|
+
|
|
161
|
+
color.bg.strong is not a section ground. It is the inverted fill for an action that has to outrank what surrounds it, and the components that paint it — Button and ButtonIcon in their inverse form — already set both halves in one rule, which is the pattern a hand-painted section has to copy.
|
|
162
|
+
Guidance only — cannot be checked automatically.
|
|
163
|
+
- **Every gap inside a section names the relationship it expresses, and the same relationship keeps its measure when it turns on its side** — A section's spacing is named by the relationship it expresses, not chosen by size. Every gap in a section has a role token, and reaching for a step off the generic scale because it happens to be the right number is how two sections written by two people stop lining up: the numbers agree today and drift the first time one of them is tuned.
|
|
164
|
+
|
|
165
|
+
The heading group is three steps, not one gap. An eyebrow sits nearer its title than the title sits to its description, and the actions sit further from the description than either — space.heading.eyebrow-title, space.heading.title-description, space.heading.description-actions, in that order. A single `gap` on the group cannot say this; it sets all three the same and the block reads as evenly spaced lines rather than as a mark, a name and a paragraph. The eyebrow has to identify itself for this to work, because a group that opens with a title must not give its description the eyebrow's tighter step.
|
|
166
|
+
|
|
167
|
+
The step between a title and its description follows the size of the title, which is why there are two of it. What a reader sees is not the gap but the gap plus the leading above it, and leading scales with the type: under a display title the leading is most of the space, under a card's title it is half as deep. One value for both makes a card look loose at the size that made a section look right. So space.heading.title-description is the display step and space.heading.title-description-compact is the step at a card's size, and the choice is made by how big the title is rather than by what kind of block it sits in.
|
|
168
|
+
|
|
169
|
+
Every one of those steps is smaller than the gap below the group, and that ordering is what holds the group together as one thing. space.heading.content is the step between a heading group and the content it introduces, and it is the widest measure inside a section: the one gap the eye reads as a change of subject rather than as a continuation.
|
|
170
|
+
|
|
171
|
+
That step is the same whether the content is under the heading or beside it. A split is the same relationship turned on its side — a heading group and the content it introduces — so the space between the two halves is space.heading.content, not a gutter. When the split collapses to one column on a phone the gap becomes vertical again, which is where it started, and nothing has to change for that to be right.
|
|
172
|
+
|
|
173
|
+
space.grid.gutter is between two things of the same kind. Two cards in a row are the same kind; the two halves of a split are not, which is why the gutter is the wrong measure there even when the number would have looked acceptable. The gutter runs on both axes, so a grid reads as an even field rather than as rows with a seam down them.
|
|
174
|
+
|
|
175
|
+
space.inset.card is the card's, not the page's. A row of cards padded differently is a row of different cards, and the fill is the only edge they have. A page composing a row does not get to choose it.
|
|
176
|
+
|
|
177
|
+
Column count is not a breakpoint decision. A row holds as many columns as fit before a column would be narrower than size.grid.column-min, and drops one when they would not. Written as a fixed count that collapses at a breakpoint, a four-up row keeps four columns at every width above the phone and sets its copy two or three words to a line through the whole of the tablet range — and on a narrow desktop, which no breakpoint sits on at all. The threshold is a property of the row rather than of a card: a card fills whatever cell it is given.
|
|
178
|
+
|
|
179
|
+
What this does not cover is the space above and below a section, which is space.section.default and space.section.compact, and the page's own side margin, which is space.inset.page. Those are the section's outside; everything here is its inside.
|
|
180
|
+
Guidance only — cannot be checked automatically.
|
|
181
|
+
- **A section's title is centred over stacked content, starts beside content in columns, and starts on a phone** — Two arrangements, and the title's alignment is decided by which one it is introducing rather than chosen per section.
|
|
182
|
+
|
|
183
|
+
Content stacked under the title — a row of cards, an accordion, a set of steps — takes a centred title. The heading group and the block below it share one axis, so a centred title is symmetric with the thing it names, and the eye drops from the middle of the words to the middle of the set. A title starting at the left over a centred or full-width block reads as belonging to the first column rather than to all of them.
|
|
184
|
+
|
|
185
|
+
Content beside the title — a split, a picture in the other half, anything laid out in columns — takes a title that starts where its content starts. The two share a left edge, and that edge is what says they are one thing read across rather than two stacked. Centring the title there breaks the only alignment the reader has to hold the halves together, and it drifts further from its content the wider the page gets.
|
|
186
|
+
|
|
187
|
+
The heading group is what carries this, not the heading alone. An eyebrow, a title and a description are read as one block and align together; aligning the title without them leaves a centred heading over start-aligned copy, which reads as a mistake rather than as a choice.
|
|
188
|
+
|
|
189
|
+
Centred means all of it, not just the words. A badge, a row of actions, a picture — these are flex children, and a flex child is placed by align-items rather than by text-align, so a block that only sets the text leaves everything that is not text at the start. A hero is the case that shows it: its title and copy centred while its badge and its buttons did not.
|
|
190
|
+
|
|
191
|
+
A capped block centres itself as well as its words. A heading group takes size.heading-group.max, and a cap that is not centred leaves the measure at the start of the column with the words centred inside it — composed, and a long way left of the middle it appears to be in.
|
|
192
|
+
|
|
193
|
+
Both arrangements go to the start on a phone. A centred block reverts below the breakpoint, and the reason is the measure: on a phone the measure is the screen, so a centred title wraps to three or four ragged lines and the eye loses the left edge it was tracking — the same symmetry that reads as composure on a desktop is what makes it hard to scan on a phone. Centring there has to be asked for twice, and a section title is not one of the things that asks: it is centred because there is room for it, and on a phone there is not. Going back to the start means all of it too, and a capped block is the one that hides a leftover: its words go to the start while its measure stays in the middle of the column, which reads as a title still centred over a badge and buttons that are not — the box in the middle and the line inside it at the edge.
|
|
194
|
+
|
|
195
|
+
A hero is a section like any other: its badge, title, copy and actions are one stacked block, so it is centred on a desktop and starts on a phone with everything else.
|
|
196
|
+
|
|
197
|
+
What this is not about is a page whose body is a form or a document. A title over a sign-up form or a guide is not introducing a set of content — it is the first line of the thing itself, and it keeps the alignment the body has, which for running text is the start. The distinction is whether the block below is content the title names or content the title is part of.
|
|
198
|
+
Guidance only — cannot be checked automatically.
|
|
199
|
+
- **Anything a pointer has to hit is at least 24px in both directions** — A control sized by its contents is as big as its contents, and a glyph is chosen for how it reads rather than for how easily it is hit. An icon-only button with no padding is a 16px target — smaller than the pad of a finger, and smaller than the tremor of a hand using a mouse.
|
|
200
|
+
|
|
201
|
+
WCAG 2.2 asks for 24 by 24 CSS pixels at AA. The exceptions are narrower than they look: inline targets inside a sentence are exempt, and so is a small target with 24px of clear space around it — but a clear button sits against the field it empties, and a field is itself a target, so the spacing exemption does not apply to the case that keeps arising.
|
|
202
|
+
|
|
203
|
+
The target and the mark are separate decisions, and conflating them is what produces both faults. Growing the glyph to 24px makes it shout; leaving the button at 16px makes it hard to hit. So the glyph keeps the size that reads correctly beside its text, and the box around it declares a minimum of its own — which is what size.target.min is for.
|
|
204
|
+
|
|
205
|
+
Declared rather than emergent. Padding plus contents does clear 24px in several components today, and it is the weaker statement: it is a size nobody wrote down, so it moves when the type scale or the glyph does, and nothing says it was meant to be a target at all.
|
|
206
|
+
Guidance only — cannot be checked automatically.
|
|
207
|
+
- **A component is written along the reading axis, not along the page's left and right** — Every measure, inset and placement in this package names an axis rather than a side: inline-size and block-size, padding-inline and padding-block, inset-inline-start and inset-block-end, margin-inline: auto, text-align: start. Written that way a component follows whichever direction the page is read in, and a band whose claim ranges to the leading edge with its subject on the trailing one is the same composition in Arabic as in English, mirrored, with nothing restated. Written as left and right it is the same pixels in both, which is a layout nobody designed for the second language.
|
|
208
|
+
|
|
209
|
+
The enum values follow the same rule and for the same reason. A prop that names where a picture sits takes start and end, never left and right — FeatureSplit, FeatureBullets and FeatureScroll all declare mediaPosition that way, and the flex directions they generate flip on their own because row already means the reading order. A value called left would have to be re-read as right by every author working in a mirrored page, which is a translation this system should be doing rather than asking for.
|
|
210
|
+
|
|
211
|
+
The guidance a contract carries is part of the component, so it follows too. Describing a claim as ranging left is describing one of the two languages, and it was written that way here for a while: three of the opening bands said so in their own intent. A reader in a mirrored page meets that sentence and has to decide whether the component is wrong or the sentence is, and neither answer is one documentation should force.
|
|
212
|
+
|
|
213
|
+
Two things genuinely have no logical form and are not covered by this. A CSS gradient takes a physical direction — there is no to inline-end — so a pattern that must mirror has to be stated twice under a direction query, and one that is periodic and centred, like PageHeader's rule grid, does not need to. A glyph that points the way onward is the other: an arrow is a picture of a direction, and mirroring it belongs to the icon set rather than to the component holding it.
|
|
214
|
+
Guidance only — cannot be checked automatically.
|
|
215
|
+
|
|
216
|
+
## Anti-examples
|
|
217
|
+
|
|
218
|
+
❌ FeatureScroll for: There are three or four items a reader is meant to compare
|
|
219
|
+
Comparing means seeing them at once, which is the one thing a screen-at-a-time set cannot offer
|
|
220
|
+
→ use `FeatureCards` instead.
|
|
221
|
+
|
|
222
|
+
❌ FeatureScroll for: The set is lines rather than things — what an account includes
|
|
223
|
+
A ruled list is countable at a glance where this is six screens of scrolling
|
|
224
|
+
→ use `FeatureBullets` instead.
|
|
225
|
+
|
|
226
|
+
❌ FeatureScroll for: There is one point to make
|
|
227
|
+
One claim beside one picture, at a section’s height rather than a screen’s, and the picture and the link are slots the page composes
|
|
228
|
+
→ use `FeatureSplit` instead.
|
|
229
|
+
|
|
230
|
+
❌ FeatureScroll for: The section opens the page
|
|
231
|
+
A page’s opening band is a claim, and a reader who meets a scroll-driven set first has not been told what the page is yet
|
|
232
|
+
→ use `LandingHero` instead.
|
|
233
|
+
|
|
234
|
+
## Example
|
|
235
|
+
|
|
236
|
+
```tsx
|
|
237
|
+
import { FeatureScroll } from '@deriv-com/dds-marketing'
|
|
238
|
+
|
|
239
|
+
<FeatureScroll title="All your markets in one place" steps={[{"title":"Forex","description":"Trade the most popular currency pairs with high leverage, tight spreads and execution fast enough to matter on a pair that moves in the time it takes to read this.","href":"/markets/forex","linkLabel":"Learn more","image":"/sample-stop-brand.svg"},{"title":"Derived indices","description":"Trade around the clock on indices derived from a random number generator rather than from a market — the volatility is chosen rather than waited for.","href":"/markets/derived-indices","linkLabel":"Learn more","image":"/sample-stop-teal.svg"},{"title":"Stocks","description":"Trade the global names a reader already knows, on the same platform and out of the same account as everything else here.","href":"/markets/stocks","linkLabel":"Learn more","image":"/sample-stop-amber.svg"},{"title":"Commodities","description":"Trade gold, silver, oil and natural gas — the instruments a portfolio reaches for when it wants something that is not a currency or a company.","href":"/markets/commodities","linkLabel":"Learn more","image":"/sample-stop-purple.svg"},{"title":"Cryptocurrencies","description":"Trade the volatility of Bitcoin and Ethereum without holding either, on a market that does not close at the weekend.","href":"/markets/cryptocurrencies","linkLabel":"Learn more","image":"/sample-stop-blue.svg"}]} />
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## In use
|
|
243
|
+
|
|
244
|
+
### The ground it sits on
|
|
245
|
+
|
|
246
|
+
The three grounds a page can put a section on. The page is white, so a band painting one of the other two is saying it is its own section — and the ink follows the ground.
|
|
247
|
+
|
|
248
|
+
```tsx
|
|
249
|
+
import { FeatureScroll } from '@deriv-com/dds-marketing'
|
|
250
|
+
|
|
251
|
+
// On the page itself
|
|
252
|
+
<FeatureScroll ground="page" title="All your markets in one place" steps={[{"title":"Forex","description":"Trade the most popular currency pairs with high leverage, tight spreads and execution fast enough to matter on a pair that moves in the time it takes to read this.","href":"/markets/forex","linkLabel":"Learn more","image":"/sample-stop-brand.svg"},{"title":"Derived indices","description":"Trade around the clock on indices derived from a random number generator rather than from a market — the volatility is chosen rather than waited for.","href":"/markets/derived-indices","linkLabel":"Learn more","image":"/sample-stop-teal.svg"},{"title":"Stocks","description":"Trade the global names a reader already knows, on the same platform and out of the same account as everything else here.","href":"/markets/stocks","linkLabel":"Learn more","image":"/sample-stop-amber.svg"},{"title":"Commodities","description":"Trade gold, silver, oil and natural gas — the instruments a portfolio reaches for when it wants something that is not a currency or a company.","href":"/markets/commodities","linkLabel":"Learn more","image":"/sample-stop-purple.svg"},{"title":"Cryptocurrencies","description":"Trade the volatility of Bitcoin and Ethereum without holding either, on a market that does not close at the weekend.","href":"/markets/cryptocurrencies","linkLabel":"Learn more","image":"/sample-stop-blue.svg"}]} />
|
|
253
|
+
|
|
254
|
+
// On the quiet ground
|
|
255
|
+
<FeatureScroll ground="subtle" title="All your markets in one place" steps={[{"title":"Forex","description":"Trade the most popular currency pairs with high leverage, tight spreads and execution fast enough to matter on a pair that moves in the time it takes to read this.","href":"/markets/forex","linkLabel":"Learn more","image":"/sample-stop-brand.svg"},{"title":"Derived indices","description":"Trade around the clock on indices derived from a random number generator rather than from a market — the volatility is chosen rather than waited for.","href":"/markets/derived-indices","linkLabel":"Learn more","image":"/sample-stop-teal.svg"},{"title":"Stocks","description":"Trade the global names a reader already knows, on the same platform and out of the same account as everything else here.","href":"/markets/stocks","linkLabel":"Learn more","image":"/sample-stop-amber.svg"},{"title":"Commodities","description":"Trade gold, silver, oil and natural gas — the instruments a portfolio reaches for when it wants something that is not a currency or a company.","href":"/markets/commodities","linkLabel":"Learn more","image":"/sample-stop-purple.svg"},{"title":"Cryptocurrencies","description":"Trade the volatility of Bitcoin and Ethereum without holding either, on a market that does not close at the weekend.","href":"/markets/cryptocurrencies","linkLabel":"Learn more","image":"/sample-stop-blue.svg"}]} />
|
|
256
|
+
|
|
257
|
+
// On the dark band
|
|
258
|
+
<FeatureScroll ground="strong" title="All your markets in one place" steps={[{"title":"Forex","description":"Trade the most popular currency pairs with high leverage, tight spreads and execution fast enough to matter on a pair that moves in the time it takes to read this.","href":"/markets/forex","linkLabel":"Learn more","image":"/sample-stop-brand.svg"},{"title":"Derived indices","description":"Trade around the clock on indices derived from a random number generator rather than from a market — the volatility is chosen rather than waited for.","href":"/markets/derived-indices","linkLabel":"Learn more","image":"/sample-stop-teal.svg"},{"title":"Stocks","description":"Trade the global names a reader already knows, on the same platform and out of the same account as everything else here.","href":"/markets/stocks","linkLabel":"Learn more","image":"/sample-stop-amber.svg"},{"title":"Commodities","description":"Trade gold, silver, oil and natural gas — the instruments a portfolio reaches for when it wants something that is not a currency or a company.","href":"/markets/commodities","linkLabel":"Learn more","image":"/sample-stop-purple.svg"},{"title":"Cryptocurrencies","description":"Trade the volatility of Bitcoin and Ethereum without holding either, on a market that does not close at the weekend.","href":"/markets/cryptocurrencies","linkLabel":"Learn more","image":"/sample-stop-blue.svg"}]} />
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Which side the pictures pin to
|
|
262
|
+
|
|
263
|
+
One switch for the whole set, because the picture pins to a place and stays there for several screens. Two of these on a page take opposite sides, or they read as one very long column.
|
|
264
|
+
|
|
265
|
+
```tsx
|
|
266
|
+
import { FeatureScroll } from '@deriv-com/dds-marketing'
|
|
267
|
+
|
|
268
|
+
// Pictures at the end
|
|
269
|
+
<FeatureScroll title="All your markets in one place" mediaPosition="end" steps={[{"title":"Forex","description":"Trade the most popular currency pairs with high leverage, tight spreads and execution fast enough to matter on a pair that moves in the time it takes to read this.","href":"/markets/forex","linkLabel":"Learn more","image":"/sample-stop-brand.svg"},{"title":"Derived indices","description":"Trade around the clock on indices derived from a random number generator rather than from a market — the volatility is chosen rather than waited for.","href":"/markets/derived-indices","linkLabel":"Learn more","image":"/sample-stop-teal.svg"},{"title":"Stocks","description":"Trade the global names a reader already knows, on the same platform and out of the same account as everything else here.","href":"/markets/stocks","linkLabel":"Learn more","image":"/sample-stop-amber.svg"}]} />
|
|
270
|
+
|
|
271
|
+
// Pictures at the start
|
|
272
|
+
<FeatureScroll title="And the platforms they run on" mediaPosition="start" steps={[{"title":"Forex","description":"Trade the most popular currency pairs with high leverage, tight spreads and execution fast enough to matter on a pair that moves in the time it takes to read this.","href":"/markets/forex","linkLabel":"Learn more","image":"/sample-stop-brand.svg"},{"title":"Derived indices","description":"Trade around the clock on indices derived from a random number generator rather than from a market — the volatility is chosen rather than waited for.","href":"/markets/derived-indices","linkLabel":"Learn more","image":"/sample-stop-teal.svg"},{"title":"Stocks","description":"Trade the global names a reader already knows, on the same platform and out of the same account as everything else here.","href":"/markets/stocks","linkLabel":"Learn more","image":"/sample-stop-amber.svg"}]} />
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### A stop with nowhere to go on to
|
|
276
|
+
|
|
277
|
+
The way onward is the one optional part of a stop, and it takes both a href and a label — one without the other draws nothing rather than half of it.
|
|
278
|
+
|
|
279
|
+
```tsx
|
|
280
|
+
import { FeatureScroll } from '@deriv-com/dds-marketing'
|
|
281
|
+
|
|
282
|
+
// No link on the last stop
|
|
283
|
+
<FeatureScroll title="Three ways to trade" steps={[{"title":"Forex","description":"Trade the most popular currency pairs with high leverage, tight spreads and execution fast enough to matter on a pair that moves in the time it takes to read this.","href":"/markets/forex","linkLabel":"Learn more","image":"/sample-stop-brand.svg"},{"title":"Derived indices","description":"Trade around the clock on indices derived from a random number generator rather than from a market — the volatility is chosen rather than waited for.","href":"/markets/derived-indices","linkLabel":"Learn more","image":"/sample-stop-teal.svg"},{"title":"Stocks","description":"Trade the global names a reader already knows, on the same platform and out of the same account as everything else here.","image":"/sample-stop-amber.svg"}]} />
|
|
284
|
+
```
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
<!-- GENERATED from feature-split.contract.json. Do not edit. -->
|
|
2
|
+
|
|
3
|
+
# FeatureSplit
|
|
4
|
+
|
|
5
|
+
A section putting one claim, a line explaining it and a way onward beside a single picture.
|
|
6
|
+
|
|
7
|
+
The plainest block here, and that is what it is for: somewhere to make one more point about something the page has already introduced, without opening a list or a set of cards. A split rather than a stack, because the same words over a full-width photograph read as a caption and beside one as a claim with evidence.
|
|
8
|
+
|
|
9
|
+
It sits on the page's own ground by default; `ground` paints the grey or the dark band where the section needs setting apart from the ones around it.
|
|
10
|
+
|
|
11
|
+
## Use when
|
|
12
|
+
|
|
13
|
+
- A page needs one more point about something it has already introduced, with a picture to carry it
|
|
14
|
+
- The point is a sentence rather than a list, and does not earn a set of cards
|
|
15
|
+
- A section needs a way onward that is not a call to action — a link to the page that says more
|
|
16
|
+
|
|
17
|
+
## Do not use
|
|
18
|
+
|
|
19
|
+
- The point is several things the reader gets, each one line long → `FeatureBullets`
|
|
20
|
+
- There are three or four points and each needs its own picture → `FeatureCards`
|
|
21
|
+
- The section opens the page → `ProductHero`
|
|
22
|
+
- A list. Two sentences separated by a full stop is not a list, and a reader scanning for several things will miss the second — that is what FeatureBullets is for
|
|
23
|
+
- The page's opening or closing band. This is a section in the middle of a page and carries no navigation of its own
|
|
24
|
+
- A call to action. The way onward here is a link to somewhere that says more, not the thing the page wants a reader to do
|
|
25
|
+
|
|
26
|
+
## Anatomy
|
|
27
|
+
|
|
28
|
+
FeatureSplit renders 8 elements. A token binding names the part it applies to.
|
|
29
|
+
|
|
30
|
+
- `root` — `<section>`, class `dds-featuresplit`. The section. It paints the ground that separates it from the page above and below, and asks its own width so the split folds on the section's measure rather than the window's.
|
|
31
|
+
|
|
32
|
+
It paints one of the page's three grounds and the ink that goes with it, in one decision — see `ground`. The ink is set here and inherited by everything inside, which is the package's rule about a section that paints a ground: stated once, it cannot be half-applied.
|
|
33
|
+
- `inner` — `<div>`, class `dds-featuresplit__inner`. The measure and the gutter, holding the section's content in the same column as every other section on the page.
|
|
34
|
+
- `split` — `<div>`, class `dds-featuresplit__split`. The two halves. A row at full width with the picture on whichever side `mediaPosition` names, and one column below the fold with the words first — because a reader on a phone came for the words, whichever side the picture takes on a desktop.
|
|
35
|
+
- `content` — `<div>`, class `dds-featuresplit__content`. The claim, the line under it and the way onward. It sets no gap of its own: the step from a title to its description and the step from a description to what follows are two different roles, and a single gap could only ever be one of them.
|
|
36
|
+
- `title` — `<h2>`, class `dds-featuresplit__title`. The claim. `h2` because a section in the middle of a page sits under the page's own heading.
|
|
37
|
+
- `description` — `<p>`, class `dds-featuresplit__description`. The line under the claim, held to a readable measure rather than the half it sits in — a column of body text as wide as a picture is a column a reader loses their place in. Rendered only when `description` is set.
|
|
38
|
+
- `link` — `<div>`, class `dds-featuresplit__link`. The way onward. A slot rather than a label and a handler, so a link here is the same Link as anywhere else on the site — and the step above it is carried by whatever it follows. Rendered only when `link` is set.
|
|
39
|
+
- `media` — `<div>`, class `dds-featuresplit__media`. The picture, and one of the section's two halves rather than an addition to it. Held to a square and cropped to fill it, so a section is the same shape whatever the page passes — three of these down a page with three different crops is three sections that do not line up.
|
|
40
|
+
|
|
41
|
+
Apply these classes from the generated `featureSplitParts` constant rather than writing the strings by hand.
|
|
42
|
+
|
|
43
|
+
## Composition
|
|
44
|
+
|
|
45
|
+
- Takes no children. Everything it renders comes from props
|
|
46
|
+
- Never contains: `FeatureSplit`, `FeatureBullets`, `LandingHero`, `ProductHero`, `PageHeader`, `Footer`, `Navbar`, `Table`, `Accordion` — in children or in any prop that takes a node
|
|
47
|
+
|
|
48
|
+
## Props
|
|
49
|
+
|
|
50
|
+
### ground — `page` | `subtle` | `strong` (default `page`)
|
|
51
|
+
|
|
52
|
+
- `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
|
|
53
|
+
- `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.
|
|
54
|
+
- `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
|
|
55
|
+
|
|
56
|
+
Use when: Which of the page's three grounds this section paints. A marketing page is white, and a band that wants to be seen as a band takes one of the other two — so this is how a page builds its rhythm: white, white, grey, white, dark. Two in a row is a deliberate pair rather than an accident, and the section above knows nothing about the one below it, so the page is the only thing that can decide.
|
|
57
|
+
|
|
58
|
+
The ink follows the ground and is not a second decision: a dark band carries white type because this sets both halves in one value, which is what the painted-ground-carries-its-ink rule exists to stop a page getting wrong.
|
|
59
|
+
Never for: Marking importance. Every section on a page painted dark is a page with no emphasis left, and a reader stops reading the grounds as meaning anything. Alternating for its own sake is the same mistake more slowly.
|
|
60
|
+
|
|
61
|
+
And never the brand's own coral, `color.bg.brand`, which is not offered here. That ground belongs to the band that asks a reader to do something — the one place on a page where the brand colour is the point rather than decoration — and a feature section wearing it is a page with two calls to action, one of which does nothing. It is `color.bg.brand-strong` that this offers: the dark brand ground, which is a band breaking a page rather than a band asking for something.
|
|
62
|
+
|
|
63
|
+
### mediaPosition — `end` | `start` (default `end`)
|
|
64
|
+
|
|
65
|
+
- `end` — The default. The claim is read first, which is the order the section argues in
|
|
66
|
+
- `start` — The second of two on a page, so the two do not read as one column with a picture stuck to one side
|
|
67
|
+
|
|
68
|
+
Use when: Which side the picture takes at full width. Alternating it down a page is what stops three of these in a row reading as one long column, and it is the page's decision rather than this section's — which is why it is a prop and not a rule.
|
|
69
|
+
|
|
70
|
+
It is the arrangement at full width and never a promise about a phone: below the fold the words are always first, because a reader on a phone came for them.
|
|
71
|
+
Never for: Moving the picture on a phone. There is one column there and the words lead it
|
|
72
|
+
|
|
73
|
+
### title — `string` **Required.**
|
|
74
|
+
|
|
75
|
+
Use when: Always. The one claim the section makes, in the fewest words that say it
|
|
76
|
+
Never for: A sentence. The description underneath carries the argument; this only has to name it
|
|
77
|
+
|
|
78
|
+
### description — `string`
|
|
79
|
+
|
|
80
|
+
Use when: 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.
|
|
81
|
+
Never for: A paragraph. Past about two hundred characters the text column runs longer than the square beside it and the two halves stop lining up, which is the one thing a split has to get right. The reference design caps it at exactly that.
|
|
82
|
+
|
|
83
|
+
### link — `node`
|
|
84
|
+
|
|
85
|
+
Accepts: Link, Button
|
|
86
|
+
Use when: 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.
|
|
87
|
+
Never for: The page's main call to action. This is a way onward from a supporting point, and a primary button here competes with the one the page actually wants pressed
|
|
88
|
+
|
|
89
|
+
### media — `node` **Required.**
|
|
90
|
+
|
|
91
|
+
Accepts: *
|
|
92
|
+
Use when: 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.
|
|
93
|
+
Never for: Anything a reader has to read or press. It is held to a square and cropped to fill it, so words in it are cropped too
|
|
94
|
+
|
|
95
|
+
## Accessibility
|
|
96
|
+
|
|
97
|
+
Guaranteed by the component. Write none of it by hand: the ids these attributes point at are generated per instance, so an attribute set from outside either says the same thing twice or points at nothing.
|
|
98
|
+
|
|
99
|
+
- Announced as: `region`
|
|
100
|
+
- `Tab` — Moves to the way onward, where there is one, and past the section otherwise. Nothing else here is operable.
|
|
101
|
+
- accessible name from title, so the section is a named region a screen reader can jump to and skip rather than an unlabelled one a reader has to walk
|
|
102
|
+
- The picture is decoration and is hidden from assistive technology. What the section says is the title and the description; a picture carrying something they do not is a section that reads as half empty to anyone who cannot see it
|
|
103
|
+
|
|
104
|
+
## Rules
|
|
105
|
+
|
|
106
|
+
- **On a phone a section's actions stack and each fills the width, including when there is only one** — On a phone a page's actions stack, and each one fills the width. Not most of it, and not the width its label happens to need — the whole column.
|
|
107
|
+
|
|
108
|
+
Two side by side is what this replaces. On the narrowest screen each gets half a column minus the gap, which is rarely enough for a real label: the words wrap inside the pills, the two buttons come out different heights, and the pair reads as a broken row rather than as a choice. Stacked, each has the whole width and neither has to shorten its words to fit.
|
|
109
|
+
|
|
110
|
+
A single action fills too, and that is the part worth saying, because it is the one that looks unnecessary. A lone button has the room to sit at its natural width and still should not: a full-width control is the phone's convention for the thing the screen is asking you to do, and a pill hugging its label reads as one option among others that are not there. It also makes the target predictable — the whole width, wherever the thumb lands — which a centred pill does not.
|
|
111
|
+
|
|
112
|
+
Filling is the row's job rather than the button's. A button has no width of its own and is not given one; the row it sits in becomes a column and stretches its children, so nothing about the control changes and the same button is full width here and its own size on a desktop.
|
|
113
|
+
|
|
114
|
+
This is the phone's breakpoint, size.breakpoint.md, and not the wider one that decides whether a heading is centred. Those are different questions and they break at different widths: a heading stops being centred when the measure is the screen, while a button stops being full width much sooner than that — stretched across a tablet it is a long way from its own label, and a label centred in a metre of pill is harder to hit than one in a pill the size of the words.
|
|
115
|
+
|
|
116
|
+
What this is not about is every row of controls. A set of filter chips, a pagination strip, a toolbar, a link sitting after a sentence — none of these is the page asking for something, and stacking them to full width would turn a strip the eye skims into a list it has to read. The test is whether the row is the call to action the section was written for.
|
|
117
|
+
Guidance only — cannot be checked automatically.
|
|
118
|
+
- **Never nest one interactive element inside another** — Nested interactive elements produce ambiguous hit targets and an unreachable inner control for keyboard and screen-reader users. This is why the rule cannot live on any single component — it constrains Button, Link, CardButton and TextInput simultaneously.
|
|
119
|
+
Guidance only — cannot be checked automatically.
|
|
120
|
+
- **A section that paints a ground sets the ink to go with it** — A page sits on color.bg.page, and a section that separates itself from it takes one of three grounds. color.bg.subtle is the light one and needs nothing further — the page's own ink still reads on it. The other two are dark and do not: color.bg.brand-strong takes color.fg.on-brand-strong, and color.bg.brand takes color.fg.on-solid. Both are white, and both are written as the role rather than the white they resolve to. Set one of those backgrounds without its ink and the text inherits whatever the page was already using, which is near-black on a dark band — legible to nobody, and invisible to every check here, because each pair passes contrast on its own and nothing can tell whether a section used both.
|
|
121
|
+
|
|
122
|
+
color.bg.brand carries a second limit that is not about forgetting. Its ink measures 3.39:1, which clears the 3:1 a heading needs and misses the 4.5:1 running copy needs. A coral band may carry a headline and a call to action; a paragraph on one is text the reader has to work through at a ratio the waiver on that role does not cover — the waiver says so itself. A section with copy in it takes color.bg.brand-strong, which is near-black and carries white at full contrast.
|
|
123
|
+
|
|
124
|
+
color.bg.strong is not a section ground. It is the inverted fill for an action that has to outrank what surrounds it, and the components that paint it — Button and ButtonIcon in their inverse form — already set both halves in one rule, which is the pattern a hand-painted section has to copy.
|
|
125
|
+
Guidance only — cannot be checked automatically.
|
|
126
|
+
- **Every gap inside a section names the relationship it expresses, and the same relationship keeps its measure when it turns on its side** — A section's spacing is named by the relationship it expresses, not chosen by size. Every gap in a section has a role token, and reaching for a step off the generic scale because it happens to be the right number is how two sections written by two people stop lining up: the numbers agree today and drift the first time one of them is tuned.
|
|
127
|
+
|
|
128
|
+
The heading group is three steps, not one gap. An eyebrow sits nearer its title than the title sits to its description, and the actions sit further from the description than either — space.heading.eyebrow-title, space.heading.title-description, space.heading.description-actions, in that order. A single `gap` on the group cannot say this; it sets all three the same and the block reads as evenly spaced lines rather than as a mark, a name and a paragraph. The eyebrow has to identify itself for this to work, because a group that opens with a title must not give its description the eyebrow's tighter step.
|
|
129
|
+
|
|
130
|
+
The step between a title and its description follows the size of the title, which is why there are two of it. What a reader sees is not the gap but the gap plus the leading above it, and leading scales with the type: under a display title the leading is most of the space, under a card's title it is half as deep. One value for both makes a card look loose at the size that made a section look right. So space.heading.title-description is the display step and space.heading.title-description-compact is the step at a card's size, and the choice is made by how big the title is rather than by what kind of block it sits in.
|
|
131
|
+
|
|
132
|
+
Every one of those steps is smaller than the gap below the group, and that ordering is what holds the group together as one thing. space.heading.content is the step between a heading group and the content it introduces, and it is the widest measure inside a section: the one gap the eye reads as a change of subject rather than as a continuation.
|
|
133
|
+
|
|
134
|
+
That step is the same whether the content is under the heading or beside it. A split is the same relationship turned on its side — a heading group and the content it introduces — so the space between the two halves is space.heading.content, not a gutter. When the split collapses to one column on a phone the gap becomes vertical again, which is where it started, and nothing has to change for that to be right.
|
|
135
|
+
|
|
136
|
+
space.grid.gutter is between two things of the same kind. Two cards in a row are the same kind; the two halves of a split are not, which is why the gutter is the wrong measure there even when the number would have looked acceptable. The gutter runs on both axes, so a grid reads as an even field rather than as rows with a seam down them.
|
|
137
|
+
|
|
138
|
+
space.inset.card is the card's, not the page's. A row of cards padded differently is a row of different cards, and the fill is the only edge they have. A page composing a row does not get to choose it.
|
|
139
|
+
|
|
140
|
+
Column count is not a breakpoint decision. A row holds as many columns as fit before a column would be narrower than size.grid.column-min, and drops one when they would not. Written as a fixed count that collapses at a breakpoint, a four-up row keeps four columns at every width above the phone and sets its copy two or three words to a line through the whole of the tablet range — and on a narrow desktop, which no breakpoint sits on at all. The threshold is a property of the row rather than of a card: a card fills whatever cell it is given.
|
|
141
|
+
|
|
142
|
+
What this does not cover is the space above and below a section, which is space.section.default and space.section.compact, and the page's own side margin, which is space.inset.page. Those are the section's outside; everything here is its inside.
|
|
143
|
+
Guidance only — cannot be checked automatically.
|
|
144
|
+
- **A section's title is centred over stacked content, starts beside content in columns, and starts on a phone** — Two arrangements, and the title's alignment is decided by which one it is introducing rather than chosen per section.
|
|
145
|
+
|
|
146
|
+
Content stacked under the title — a row of cards, an accordion, a set of steps — takes a centred title. The heading group and the block below it share one axis, so a centred title is symmetric with the thing it names, and the eye drops from the middle of the words to the middle of the set. A title starting at the left over a centred or full-width block reads as belonging to the first column rather than to all of them.
|
|
147
|
+
|
|
148
|
+
Content beside the title — a split, a picture in the other half, anything laid out in columns — takes a title that starts where its content starts. The two share a left edge, and that edge is what says they are one thing read across rather than two stacked. Centring the title there breaks the only alignment the reader has to hold the halves together, and it drifts further from its content the wider the page gets.
|
|
149
|
+
|
|
150
|
+
The heading group is what carries this, not the heading alone. An eyebrow, a title and a description are read as one block and align together; aligning the title without them leaves a centred heading over start-aligned copy, which reads as a mistake rather than as a choice.
|
|
151
|
+
|
|
152
|
+
Centred means all of it, not just the words. A badge, a row of actions, a picture — these are flex children, and a flex child is placed by align-items rather than by text-align, so a block that only sets the text leaves everything that is not text at the start. A hero is the case that shows it: its title and copy centred while its badge and its buttons did not.
|
|
153
|
+
|
|
154
|
+
A capped block centres itself as well as its words. A heading group takes size.heading-group.max, and a cap that is not centred leaves the measure at the start of the column with the words centred inside it — composed, and a long way left of the middle it appears to be in.
|
|
155
|
+
|
|
156
|
+
Both arrangements go to the start on a phone. A centred block reverts below the breakpoint, and the reason is the measure: on a phone the measure is the screen, so a centred title wraps to three or four ragged lines and the eye loses the left edge it was tracking — the same symmetry that reads as composure on a desktop is what makes it hard to scan on a phone. Centring there has to be asked for twice, and a section title is not one of the things that asks: it is centred because there is room for it, and on a phone there is not. Going back to the start means all of it too, and a capped block is the one that hides a leftover: its words go to the start while its measure stays in the middle of the column, which reads as a title still centred over a badge and buttons that are not — the box in the middle and the line inside it at the edge.
|
|
157
|
+
|
|
158
|
+
A hero is a section like any other: its badge, title, copy and actions are one stacked block, so it is centred on a desktop and starts on a phone with everything else.
|
|
159
|
+
|
|
160
|
+
What this is not about is a page whose body is a form or a document. A title over a sign-up form or a guide is not introducing a set of content — it is the first line of the thing itself, and it keeps the alignment the body has, which for running text is the start. The distinction is whether the block below is content the title names or content the title is part of.
|
|
161
|
+
Guidance only — cannot be checked automatically.
|
|
162
|
+
- **Anything a pointer has to hit is at least 24px in both directions** — A control sized by its contents is as big as its contents, and a glyph is chosen for how it reads rather than for how easily it is hit. An icon-only button with no padding is a 16px target — smaller than the pad of a finger, and smaller than the tremor of a hand using a mouse.
|
|
163
|
+
|
|
164
|
+
WCAG 2.2 asks for 24 by 24 CSS pixels at AA. The exceptions are narrower than they look: inline targets inside a sentence are exempt, and so is a small target with 24px of clear space around it — but a clear button sits against the field it empties, and a field is itself a target, so the spacing exemption does not apply to the case that keeps arising.
|
|
165
|
+
|
|
166
|
+
The target and the mark are separate decisions, and conflating them is what produces both faults. Growing the glyph to 24px makes it shout; leaving the button at 16px makes it hard to hit. So the glyph keeps the size that reads correctly beside its text, and the box around it declares a minimum of its own — which is what size.target.min is for.
|
|
167
|
+
|
|
168
|
+
Declared rather than emergent. Padding plus contents does clear 24px in several components today, and it is the weaker statement: it is a size nobody wrote down, so it moves when the type scale or the glyph does, and nothing says it was meant to be a target at all.
|
|
169
|
+
Guidance only — cannot be checked automatically.
|
|
170
|
+
- **A component is written along the reading axis, not along the page's left and right** — Every measure, inset and placement in this package names an axis rather than a side: inline-size and block-size, padding-inline and padding-block, inset-inline-start and inset-block-end, margin-inline: auto, text-align: start. Written that way a component follows whichever direction the page is read in, and a band whose claim ranges to the leading edge with its subject on the trailing one is the same composition in Arabic as in English, mirrored, with nothing restated. Written as left and right it is the same pixels in both, which is a layout nobody designed for the second language.
|
|
171
|
+
|
|
172
|
+
The enum values follow the same rule and for the same reason. A prop that names where a picture sits takes start and end, never left and right — FeatureSplit, FeatureBullets and FeatureScroll all declare mediaPosition that way, and the flex directions they generate flip on their own because row already means the reading order. A value called left would have to be re-read as right by every author working in a mirrored page, which is a translation this system should be doing rather than asking for.
|
|
173
|
+
|
|
174
|
+
The guidance a contract carries is part of the component, so it follows too. Describing a claim as ranging left is describing one of the two languages, and it was written that way here for a while: three of the opening bands said so in their own intent. A reader in a mirrored page meets that sentence and has to decide whether the component is wrong or the sentence is, and neither answer is one documentation should force.
|
|
175
|
+
|
|
176
|
+
Two things genuinely have no logical form and are not covered by this. A CSS gradient takes a physical direction — there is no to inline-end — so a pattern that must mirror has to be stated twice under a direction query, and one that is periodic and centred, like PageHeader's rule grid, does not need to. A glyph that points the way onward is the other: an arrow is a picture of a direction, and mirroring it belongs to the icon set rather than to the component holding it.
|
|
177
|
+
Guidance only — cannot be checked automatically.
|
|
178
|
+
|
|
179
|
+
## Anti-examples
|
|
180
|
+
|
|
181
|
+
❌ FeatureSplit for: The point is several things the reader gets, each one line long
|
|
182
|
+
The same split with a ruled list in it, which is what makes a set of inclusions countable
|
|
183
|
+
→ use `FeatureBullets` instead.
|
|
184
|
+
|
|
185
|
+
❌ FeatureSplit for: There are three or four points and each needs its own picture
|
|
186
|
+
A heading over a grid of cards, where a split repeated four times is four screens of scrolling
|
|
187
|
+
→ use `FeatureCards` instead.
|
|
188
|
+
|
|
189
|
+
❌ FeatureSplit for: The section opens the page
|
|
190
|
+
A page's opening band is a claim, not a supporting point
|
|
191
|
+
→ use `ProductHero` instead.
|
|
192
|
+
|
|
193
|
+
## Example
|
|
194
|
+
|
|
195
|
+
```tsx
|
|
196
|
+
import { FeatureSplit, Link, AspectRatio } from '@deriv-com/dds-marketing'
|
|
197
|
+
|
|
198
|
+
<FeatureSplit title="Trade on the platform professionals use" description="Deriv MT5 brings charting, order types and risk controls into one place, on desktop, web and mobile. Your positions, watchlists and alerts follow you between all three." link={<Link href="/platforms/deriv-mt5">Learn more</Link>} media={<AspectRatio ratio="square" />} />
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## In use
|
|
202
|
+
|
|
203
|
+
### The ground it sits on
|
|
204
|
+
|
|
205
|
+
The three grounds a page can put a section on. The page is white, so a band painting one of the other two is saying it is its own section — and the ink follows the ground.
|
|
206
|
+
|
|
207
|
+
```tsx
|
|
208
|
+
import { AspectRatio, FeatureSplit, Link } from '@deriv-com/dds-marketing'
|
|
209
|
+
|
|
210
|
+
// On the page itself
|
|
211
|
+
<FeatureSplit ground="page" title="Trade on the platform professionals use" description="Deriv MT5 brings charting, order types and risk controls into one place, on desktop, web and mobile. Your positions, watchlists and alerts follow you between all three." link={<Link href="/platforms/deriv-mt5">Learn more</Link>} media={<AspectRatio ratio="square" />} />
|
|
212
|
+
|
|
213
|
+
// On the quiet ground
|
|
214
|
+
<FeatureSplit ground="subtle" title="Trade on the platform professionals use" description="Deriv MT5 brings charting, order types and risk controls into one place, on desktop, web and mobile. Your positions, watchlists and alerts follow you between all three." link={<Link href="/platforms/deriv-mt5">Learn more</Link>} media={<AspectRatio ratio="square" />} />
|
|
215
|
+
|
|
216
|
+
// On the dark band
|
|
217
|
+
<FeatureSplit ground="strong" title="Trade on the platform professionals use" description="Deriv MT5 brings charting, order types and risk controls into one place, on desktop, web and mobile. Your positions, watchlists and alerts follow you between all three." link={<Link href="/platforms/deriv-mt5">Learn more</Link>} media={<AspectRatio ratio="square" />} />
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Which side the picture takes
|
|
221
|
+
|
|
222
|
+
Alternating it down a page is what stops three of these in a row reading as one long column with a ragged edge. Below the fold there is one column and the claim leads it either way.
|
|
223
|
+
|
|
224
|
+
```tsx
|
|
225
|
+
import { AspectRatio, FeatureSplit, Link } from '@deriv-com/dds-marketing'
|
|
226
|
+
|
|
227
|
+
// Picture last — the default
|
|
228
|
+
<FeatureSplit mediaPosition="end" title="Trade on the platform professionals use" description="Deriv MT5 brings charting, order types and risk controls into one place, on desktop, web and mobile. Your positions, watchlists and alerts follow you between all three." link={<Link href="/platforms/deriv-mt5">Learn more</Link>} media={<AspectRatio ratio="square" />} />
|
|
229
|
+
|
|
230
|
+
// Picture first — for the second of two
|
|
231
|
+
<FeatureSplit mediaPosition="start" title="And on the one built for beginners" description="Deriv Trader is the same markets with the controls a first trade needs and none of the ones it does not. Nothing to install, and a demo account to practise on first." link={<Link href="/platforms/deriv-mt5">Learn more</Link>} media={<AspectRatio ratio="square" />} />
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### How much the section carries
|
|
235
|
+
|
|
236
|
+
Everything but the claim and the picture is optional, and the steps close up rather than leaving a gap where a part would have been.
|
|
237
|
+
|
|
238
|
+
```tsx
|
|
239
|
+
import { AspectRatio, FeatureSplit, Link } from '@deriv-com/dds-marketing'
|
|
240
|
+
|
|
241
|
+
// A claim and a picture
|
|
242
|
+
<FeatureSplit title="Trade on the platform professionals use" media={<AspectRatio ratio="square" />} />
|
|
243
|
+
|
|
244
|
+
// With a line explaining it
|
|
245
|
+
<FeatureSplit title="Trade on the platform professionals use" description="Deriv MT5 brings charting, order types and risk controls into one place, on desktop, web and mobile. Your positions, watchlists and alerts follow you between all three." media={<AspectRatio ratio="square" />} />
|
|
246
|
+
|
|
247
|
+
// And a way onward
|
|
248
|
+
<FeatureSplit title="Trade on the platform professionals use" description="Deriv MT5 brings charting, order types and risk controls into one place, on desktop, web and mobile. Your positions, watchlists and alerts follow you between all three." link={<Link href="/platforms/deriv-mt5">Learn more</Link>} media={<AspectRatio ratio="square" />} />
|
|
249
|
+
```
|