@deriv-com/dds-marketing 1.2.0 → 1.2.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/illustrationcard.md +1 -1
- package/.llm/components/illustrationchoice.md +1 -1
- package/.llm/components/producthero.md +4 -23
- package/.llm/components/scenescroll.md +4 -4
- package/.llm/digest/producthero.md +2 -8
- package/.llm/digest/scenescroll.md +1 -1
- package/dist/components/blocks/image-banner/image-banner-impl.css +33 -6
- package/dist/components/blocks/product-hero/ProductHero.d.ts +1 -1
- package/dist/components/blocks/product-hero/ProductHero.js +2 -2
- package/dist/components/blocks/product-hero/ProductHero.types.d.ts +3 -8
- package/dist/components/blocks/product-hero/ProductHero.types.js +1 -1
- package/dist/components/blocks/product-hero/product-hero-impl.css +0 -54
- package/dist/components/blocks/scene-scroll/SceneScroll.d.ts +4 -3
- package/dist/components/blocks/scene-scroll/SceneScroll.js +4 -3
- package/dist/components/blocks/scene-scroll/scene-scroll-impl.css +87 -6
- package/dist/components/blocks/stats/stats-impl.css +27 -1
- package/dist/components/core/illustration-card/illustration-card-impl.css +4 -3
- package/dist/components/core/illustration-choice/IllustrationChoice.css +1 -1
- package/package.json +1 -1
|
@@ -33,7 +33,7 @@ Everything inside is illustrative: built from the system's real components so it
|
|
|
33
33
|
|
|
34
34
|
IllustrationCard renders 3 elements. A token binding names the part it applies to.
|
|
35
35
|
|
|
36
|
-
- `root` — `<div>`, class `dds-illustrationcard`. The pane: one of three proportions whatever is put in it — the aspect tokens — and the glass. It is drawn in the dark theme whatever the page's, so the product it shows looks the same on every page. Its fill is color.bg.surface-glass — a faint frosted white — laid over the dark page's ground when it stands alone and over the photograph in a scene, over a blur of size.backdrop-blur in illustration-card-impl.css, because a blur is a function no binding can name. It holds radius.surface and space.inset.card like a Card, a hairline edge so its outline survives on a light page, and the default ink, color.fg.default — light on the frosted pane. It is one image to assistive technology — `role="img"`, named by `label` — so a screen reader hears what the picture shows in a sentence rather than a list of controls that are not there. It is the host the arrivals inside it follow (`data-arrival-host`): it plays once when it has landed — well into view, or brought in by its block — and every part with a motion of its own waits for it, so the photograph lands, then the pane, then what moves on it.
|
|
36
|
+
- `root` — `<div>`, class `dds-illustrationcard`. The pane: one of three proportions whatever is put in it — the aspect tokens — and the glass. It is drawn in the dark theme whatever the page's, so the product it shows looks the same on every page. Its fill is color.bg.surface-glass — a faint frosted white — laid over the dark page's ground when it stands alone and over the photograph under a scrim of color.bg.overlay in a scene, so the light ink reads over a light photograph too, over a blur of size.backdrop-blur in illustration-card-impl.css, because a blur is a function no binding can name. It holds radius.surface and space.inset.card like a Card, a hairline edge so its outline survives on a light page, and the default ink, color.fg.default — light on the frosted pane. It is one image to assistive technology — `role="img"`, named by `label` — so a screen reader hears what the picture shows in a sentence rather than a list of controls that are not there. It is the host the arrivals inside it follow (`data-arrival-host`): it plays once when it has landed — well into view, or brought in by its block — and every part with a motion of its own waits for it, so the photograph lands, then the pane, then what moves on it.
|
|
37
37
|
- `content` — `<div>`, class `dds-illustrationcard__content`. What the pane holds, made a picture. It is `inert`: whatever components draw it — a Button, a SegmentedControl, a TextInput — look exactly as they do anywhere, and none of them can be focused, tabbed to, pressed or typed into, so a reader never meets a control that does nothing. Laid out down the pane from the start edge at its own size, and scaled down as a whole where the pane is too small to hold it — the way a picture shrinks — so a pane on a phone shows the same UI smaller rather than overlapped or cut off. The shape is fixed: the UI never stretches the pane.
|
|
38
38
|
- `stage` — `<div>`, class `dds-illustrationcard__stage`. Under `then`, one of the pane's two states: what it holds first, and `then`, laid in the same cell so the second takes the first one's place. Still, the first is not drawn and the second is; arriving, the first holds for motion.duration.hold and gives way to the second. Both in the DOM, and the pane is one image to assistive technology, so only `label` is read — which describes the second. Rendered only when `then` is set.
|
|
39
39
|
|
|
@@ -33,7 +33,7 @@ IllustrationChoice renders 7 elements. A token binding names the part it applies
|
|
|
33
33
|
- `dot` — `<span>`, class `dds-illustrationchoice__dot`. The fill inside the picked option's circle, in the option's ink. Picking, it pops in once the outline has arrived.
|
|
34
34
|
- `label` — `<span>`, class `dds-illustrationchoice__label`. The option's name, at font.label.md.
|
|
35
35
|
- `description` — `<span>`, class `dds-illustrationchoice__description`. The line under the name, at font.body.sm in color.fg.muted. Drawn only for an option that has one.
|
|
36
|
-
- `outline` — `<span>`, class `dds-illustrationchoice__outline`. The selection outline, border.width.emphasis in color.
|
|
36
|
+
- `outline` — `<span>`, class `dds-illustrationchoice__outline`. The selection outline, border.width.emphasis in the option's ink (color.fg.default: white on a dark or glass pane, dark on a light one) at the options' radius, laid in the picked option's row. Picking, it starts on a neighbouring option, holds for motion.duration.hold, and slides to the picked one over motion.duration.shift.
|
|
37
37
|
|
|
38
38
|
Apply these classes from the generated `illustrationChoiceParts` constant rather than writing the strings by hand.
|
|
39
39
|
|
|
@@ -31,7 +31,7 @@ The claim is centred and the product sits under it rather than beside it, whole
|
|
|
31
31
|
|
|
32
32
|
ProductHero renders 10 elements. A token binding names the part it applies to.
|
|
33
33
|
|
|
34
|
-
- `root` — `<section>`, class `dds-producthero`. The band. It paints the ground, carries the background image where there is one, and clips everything to its own edge so a visual that overruns cannot widen the page. `container-type` is what its own rules ask their width against, so a band placed in a narrow region of a wide page folds on its own measure rather than the window's.
|
|
34
|
+
- `root` — `<section>`, class `dds-producthero`. The band. It paints the ground, carries the background image where there is one, and clips everything to its own edge so a visual that overruns cannot widen the page. `container-type` is what its own rules ask their width against, so a band placed in a narrow region of a wide page folds on its own measure rather than the window's.
|
|
35
35
|
- `inner` — `<div>`, class `dds-producthero__inner`. The measure, and the gutter. It caps the content at the page's own maximum and holds the page inset, which is what keeps a band's text in the same column as the section under it. Positioned so it sits above the photograph without either of them naming a stacking order.
|
|
36
36
|
- `content` — `<div>`, class `dds-producthero__content`. The column the band is read down: the text, then what a reader can do about it, then the visual in the arrangement that puts one there. One step between them, because the move from reading to acting is the same move on every page. It takes the whole measure rather than hugging its longest line: `inner` is a flex row, so a column that declared no width would shrink to its text and sit at the leading edge — which centring inside it cannot correct, because the box being centred in is already the wrong shape.
|
|
37
37
|
- `text` — `<div>`, class `dds-producthero__text`. The eyebrow, the title and the description, in that order. It sets no gap of its own: the step from an eyebrow to a title and the step from a title to a description are two different roles, and a single gap could only ever be one of them. Each part carries its own.
|
|
@@ -97,15 +97,10 @@ Accepts: AspectRatio
|
|
|
97
97
|
Use when: A product picture in a shape the page fixes on purpose — an AspectRatio box, which keeps its ratio across the column. Prefer `image`, which keeps the picture's own shape and adapts to it.
|
|
98
98
|
Never for: The background. A photograph filling the band is `backgroundImage`; this sits in front of it, in the column, and is never cropped
|
|
99
99
|
|
|
100
|
-
### backgroundImage — `string`
|
|
100
|
+
### backgroundImage — `string`
|
|
101
101
|
|
|
102
|
-
Use when: A photograph filling the whole band. It is shown as it is — nothing is painted over it
|
|
103
|
-
Never for: A flat colour, or a bright photograph
|
|
104
|
-
|
|
105
|
-
### shade — `boolean` (default `false`)
|
|
106
|
-
|
|
107
|
-
Use when: Under `backgroundImage`, when the photograph is bright where the words sit — a daylight sky — and the description or the actions would fall below AA on it. A soft shade of color.bg.overlay behind the words and the actions only, fading out around them, so the rest of the photograph keeps its light.
|
|
108
|
-
Never for: A band with no photograph, or darkening the whole photograph. The band's own ground already carries the words, and a shade over all of it is the gloom a bright photograph was chosen to avoid.
|
|
102
|
+
Use when: A photograph filling the whole band. It is shown as it is — nothing is painted over it, so choose one already dark enough that the title reads on it
|
|
103
|
+
Never for: A flat colour, or a bright photograph. The band already paints its ground, and nothing here darkens an image on the page's behalf — a pale one leaves the claim unreadable
|
|
109
104
|
|
|
110
105
|
## Accessibility
|
|
111
106
|
|
|
@@ -211,9 +206,6 @@ Two things genuinely have no logical form and are not covered by this. A CSS gra
|
|
|
211
206
|
❌ ProductHero for: The band closes the page rather than opening it
|
|
212
207
|
→ use `Footer` instead.
|
|
213
208
|
|
|
214
|
-
❌ shade=true + backgroundImage missing or empty
|
|
215
|
-
A shade is drawn between a photograph and the words; with no photograph it darkens the band's own ground, which the words were already set to read on.
|
|
216
|
-
|
|
217
209
|
## Example
|
|
218
210
|
|
|
219
211
|
```tsx
|
|
@@ -288,14 +280,3 @@ import { ProductHero } from '@deriv-com/dds-marketing'
|
|
|
288
280
|
// h2 — the page already carries its h1 elsewhere
|
|
289
281
|
<ProductHero headingLevel="h2" title="Trade global currencies" description="Speculate on over 50 major, minor and exotic currency pairs, with competitive spreads and 0% commission." />
|
|
290
282
|
```
|
|
291
|
-
|
|
292
|
-
### A shade behind the words
|
|
293
|
-
|
|
294
|
-
backgroundImage with shade: a bright photograph keeps its light, and a soft shade behind the words and actions holds them at AA.
|
|
295
|
-
|
|
296
|
-
```tsx
|
|
297
|
-
import { Button, ProductHero } from '@deriv-com/dds-marketing'
|
|
298
|
-
|
|
299
|
-
// Bright photograph, shade on
|
|
300
|
-
<ProductHero backgroundImage="/sample-banner-art.svg" eyebrow="Forex" title="Trade global currencies" description="Speculate on over 50 major, minor and exotic currency pairs, with competitive spreads and 0% commission." actions={<Button variant="primary">Try free demo</Button>} shade />
|
|
301
|
-
```
|
|
@@ -26,7 +26,7 @@ The reference design's DayNightTransition, made generic: nothing in it is about
|
|
|
26
26
|
- Words a reader must not miss — a price, a disclaimer, a legal line. Each scene's words are on screen for only part of the pass
|
|
27
27
|
- Two of these on one page, or one beside another pinned section. Each holds the screen for a screen and a half of scrolling, and two make the page feel stuck
|
|
28
28
|
- Photographs with words in them. The words are the headings and the lines, where a screen reader and a translation reach them; a photograph saying them is cropped to fill and has the scene's words set over it
|
|
29
|
-
- Words over a busy
|
|
29
|
+
- Words over a busy stretch of photograph. White words lay a shade under themselves, but near-black words sit straight on the photograph, so a light photograph has to leave them a calm stretch to sit on, and the scene's ink has to name which
|
|
30
30
|
|
|
31
31
|
## Anatomy
|
|
32
32
|
|
|
@@ -35,9 +35,9 @@ SceneScroll renders 9 elements. A token binding names the part it applies to.
|
|
|
35
35
|
- `root` — `<section>`, class `dds-scenescroll`. The section, a region named by the first scene's title. Where the scroll drives the change-over it is the track: two and a half screens tall, and the view timeline the dissolve runs down. Elsewhere it is as tall as the two scenes it holds. It paints color.bg.brand-strong under the photographs, which shows only before they load, and sets color.fg.on-brand-strong, white in both modes, as the ink a scene takes unless it names another. It clips with `overflow: clip` if it ever clips, never `hidden`, which would make it a scroll container and freeze the timeline and the pin.
|
|
36
36
|
- `stage` — `<div>`, class `dds-scenescroll__stage`. What the reader sees. Where the scroll drives the change-over it is a screen tall and pinned to the top of the window for the length of the track, with both scenes stacked in its one cell. Elsewhere it is a column of the two scenes, one after the other with nothing between them.
|
|
37
37
|
- `scene` — `<div>`, class `dds-scenescroll__scene`. One scene, drawn twice — `data-scene` says which. A screen tall, the photograph behind and the words over it, inset by the section's air and the page's gutter. Isolated, so the second scene's photograph covers the first scene's words as well as its photograph as it dissolves in. Its ink is the one the scene names — color.fg.on-media-light, near-black in both modes, over a light photograph; color.fg.on-brand-strong, white, over a dark one.
|
|
38
|
-
- `media` — `<div>`, class `dds-scenescroll__media`. The scene's photograph, filling the scene edge to edge and square-cornered,
|
|
38
|
+
- `media` — `<div>`, class `dds-scenescroll__media`. The scene's photograph, filling the scene edge to edge and square-cornered, anchored at its foot and, from 62rem, at the far side from the words, so a crop takes from the words' side and the subject keeps its own. The second scene's photograph dissolves in over the first's.
|
|
39
39
|
- `image` — `<img>`, class `dds-scenescroll__image`. The photograph, drawn from the scene's `image` and cropped to fill the scene. Its `alt` is the scene's own: empty for a photograph that is decoration, which most are here.
|
|
40
|
-
- `content` — `<div>`, class `dds-scenescroll__content`. The scene's words — its title and the line under it — over the photograph, held to the heading group's measure. At the start on every screen, held to the heading group's measure — and from 62rem to half the stage, whichever is narrower — and to the page's content column, so the far side of the photograph is left to its subject
|
|
40
|
+
- `content` — `<div>`, class `dds-scenescroll__content`. The scene's words — its title and the line under it — over the photograph, held to the heading group's measure. At the start on every screen, held to the heading group's measure — and from 62rem to half the stage, whichever is narrower — and to the page's content column, so the far side of the photograph is left to its subject. Under white ink it lays a shade of color.bg.overlay under itself, full strength to its own edge and eased out past it — from 62rem a band down its half, fading across three times space.section.default toward the subject; below 62rem down from the top of the stage and out space.section.default under the words — so it fades with the words through the change-over. It sits at the top of the stage below 62rem, under the room a navigation bar takes, and centred down the stage from 62rem — the reference design's arrangement, at its 992px. Where the scroll drives the change-over the first scene's words fade out and then the second scene's fade in, in the same place; both are in the DOM and read in order the whole time, and neither is ever hidden from assistive technology.
|
|
41
41
|
- `title` — `<h2>`, class `dds-scenescroll__title`. The scene's title. The first scene's is at `headingLevel` and names the section; the second's is one level under it. Both drawn at the size `headingLevel` names, font.h2 or font.h1.
|
|
42
42
|
- `description` — `<p>`, class `dds-scenescroll__description`. The line under a scene's title, at the large body step and the description measure. Drawn only for a scene that has one.
|
|
43
43
|
- `action` — `<div>`, class `dds-scenescroll__action`. The section's one thing to do, right under the words. While the scenes change over, both scenes' words share one area of the stage, as tall as the taller of them, and the action is the row under it — under the text on every screen, and still while the text changes. At the start with the words on every screen: at the top of the stage below 62rem, centred down it with them from 62rem. In the plain fallback, where the scenes stack, it follows the second. Rendered only when `action` is set.
|
|
@@ -83,7 +83,7 @@ Guaranteed by the component. Write none of it by hand: the ids these attributes
|
|
|
83
83
|
- `Tab` — Moves past the section. Nothing in it is operable — the scenes are read, not used — so there is nothing here to land on.
|
|
84
84
|
- The section is a region named by the first scene's title, through aria-labelledby, so a reader can jump to it and past it. Named by a heading already on the page rather than a hidden label of its own, so what is announced is what is shown
|
|
85
85
|
- Both scenes' words are in the DOM, in order — the first scene's title and line, then the second's — whatever the scroll has faded. The visual change-over never hides either scene's words from assistive technology, never removes them, and never re-orders them
|
|
86
|
-
- The words over each photograph are drawn in the ink its scene names, near-black over a light photograph and white over a dark one, and the author answers for
|
|
86
|
+
- The words over each photograph are drawn in the ink its scene names, near-black over a light photograph and white over a dark one. White words lay a shade of color.bg.overlay under themselves, which holds AA under every word as ImageBanner's cover shade does. Near-black words sit straight on the photograph, and the author answers for their contrast against the part of it they sit on: no check here can see that pair, so the scene's ink is what a reviewer reads
|
|
87
87
|
- Without a scroll timeline, for a reader who asked for less motion, and before any script, nothing is faded and nothing is pinned: the two scenes are drawn one after the other, each with its words at full strength
|
|
88
88
|
- Each photograph's alt is the one its scene was given — empty for a photograph that is decoration
|
|
89
89
|
|
|
@@ -42,14 +42,8 @@ Opens a product or market page with a centred headline over a dark background
|
|
|
42
42
|
- never: The background — that is `backgroundImage`. And beside `media`: one product, one picture. Given both, `image` is drawn and `media` ignored
|
|
43
43
|
- **media** `node` · optional — A product picture in a shape the page fixes on purpose — an AspectRatio box, which keeps its ratio across the column. Prefer `image`, which keeps the picture's own shape and adapts to it
|
|
44
44
|
- never: The background. A photograph filling the band is `backgroundImage`; this sits in front of it, in the column, and is never cropped
|
|
45
|
-
- **backgroundImage** `string` · optional — A photograph filling the whole band. It is shown as it is — nothing is painted over it
|
|
46
|
-
- never: A flat colour, or a bright photograph
|
|
47
|
-
- **shade** `boolean` · default false — Under `backgroundImage`, when the photograph is bright where the words sit — a daylight sky — and the description or the actions would fall below AA on it. …
|
|
48
|
-
- never: A band with no photograph, or darkening the whole photograph. The band's own ground already carries the words, and a shade over all of it is the gloom a bright photograph was chosen to avoid
|
|
49
|
-
|
|
50
|
-
## The type refuses
|
|
51
|
-
|
|
52
|
-
- shade=true with backgroundImage=null — A shade is drawn between a photograph and the words; with no photograph it darkens the band's own ground, which the words were already set to read on
|
|
45
|
+
- **backgroundImage** `string` · optional — A photograph filling the whole band. It is shown as it is — nothing is painted over it, so choose one already dark enough that the title reads on it
|
|
46
|
+
- never: A flat colour, or a bright photograph. The band already paints its ground, and nothing here darkens an image on the page's behalf — a pale one leaves the claim unreadable
|
|
53
47
|
|
|
54
48
|
## Inside it
|
|
55
49
|
|
|
@@ -25,7 +25,7 @@ Two full-screen photo scenes that cross-fade as the reader scrolls
|
|
|
25
25
|
- Words a reader must not miss — a price, a disclaimer, a legal line. Each scene's words are on screen for only part of the pass
|
|
26
26
|
- Two of these on one page, or one beside another pinned section. Each holds the screen for a screen and a half of scrolling, and two make the page feel stuck
|
|
27
27
|
- Photographs with words in them. The words are the headings and the lines, where a screen reader and a translation reach them; a photograph saying them is cropped to fill and has the scene's words set over it
|
|
28
|
-
- Words over a busy
|
|
28
|
+
- Words over a busy stretch of photograph. White words lay a shade under themselves, but near-black words sit straight on the photograph, so a light photograph has to leave them a calm stretch to sit on, and the scene's ink has to name which
|
|
29
29
|
|
|
30
30
|
## Props
|
|
31
31
|
|
|
@@ -99,11 +99,13 @@
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
/*
|
|
102
|
-
* The cover card's words hold
|
|
103
|
-
*
|
|
102
|
+
* The cover card's words hold half of it, on the side away from where the photograph shows
|
|
103
|
+
* clear, and the photograph's subject has the other half. Three fifths left the subject two, and
|
|
104
|
+
* the shade's fade past the words took much of those. Written as a share rather than `1 1 0`: the
|
|
105
|
+
* photograph is out of flow behind, so the words would have the whole row to grow into.
|
|
104
106
|
*/
|
|
105
107
|
:where([data-dds-context="marketing"]) .dds-imagebanner[data-variant="cover"] .dds-imagebanner__content {
|
|
106
|
-
flex: 0 1
|
|
108
|
+
flex: 0 1 50%;
|
|
107
109
|
}
|
|
108
110
|
|
|
109
111
|
:where([data-dds-context="marketing"]) .dds-imagebanner[data-variant] .dds-imagebanner__action {
|
|
@@ -178,8 +180,8 @@
|
|
|
178
180
|
* White body copy has to keep AA over even a pale photograph, so the words' column is
|
|
179
181
|
* color.bg.overlay at full strength — darkest only at the words' edge was tried and left the end of a
|
|
180
182
|
* line at about 2:1. But the shade is the words', not the photograph's: it is drawn by the column the
|
|
181
|
-
* words take and fades to clear across the column's own margin and
|
|
182
|
-
*
|
|
183
|
+
* words take and fades to clear across the column's own margin and past its edge — a long fade, so
|
|
184
|
+
* it never reads as an edge on the photograph — and the rest of the
|
|
183
185
|
* photograph is shown as it was taken. It once held across three fifths of the card and then thinned
|
|
184
186
|
* out to the far edge, which turned a bright photograph slate grey nearly all the way across; on a
|
|
185
187
|
* phone it ran down three fifths of the card's height however short the words were.
|
|
@@ -202,17 +204,42 @@
|
|
|
202
204
|
position: relative;
|
|
203
205
|
}
|
|
204
206
|
|
|
207
|
+
/* Across from the fold, three times space.section.default. Eased over the shorter fade, the step
|
|
208
|
+
from full strength to clear still read as a band on the photograph, beside the words where the
|
|
209
|
+
eye lands; spread over three it reads as light falling off. Down on a phone it stays one, where a
|
|
210
|
+
longer tail would grey most of a short card. */
|
|
211
|
+
@container (min-width: 48rem) {
|
|
212
|
+
:where([data-dds-context="marketing"]) .dds-imagebanner[data-variant="cover"] .dds-imagebanner__content {
|
|
213
|
+
--dds-imagebanner-shade-fade: calc(var(--space-section-default) * 3);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
205
217
|
@container (min-width: 64rem) {
|
|
206
218
|
:where([data-dds-context="marketing"]) .dds-imagebanner[data-variant="cover"] .dds-imagebanner__content {
|
|
207
219
|
--dds-imagebanner-words-inset: var(--space-inset-2xl);
|
|
208
220
|
}
|
|
209
221
|
}
|
|
210
222
|
|
|
223
|
+
/*
|
|
224
|
+
* The fade is eased: its strength follows a smoothstep curve rather than a straight line, which is
|
|
225
|
+
* what made the first one read as a seam on the photograph — a linear fade has its sharpest change at
|
|
226
|
+
* both of its ends, where it leaves full strength and where it lands on clear. The stops are the
|
|
227
|
+
* curve sampled across the fade, which runs the words' inset and the fade's length past it; a
|
|
228
|
+
* gradient between two stops is still linear, so the curve is in how many there are.
|
|
229
|
+
*/
|
|
211
230
|
:where([data-dds-context="marketing"]) .dds-imagebanner[data-variant="cover"] .dds-imagebanner__content::before {
|
|
231
|
+
--dds-imagebanner-shade-ramp: calc(var(--dds-imagebanner-shade-fade) + var(--dds-imagebanner-words-inset));
|
|
212
232
|
background-image: linear-gradient(
|
|
213
233
|
var(--dds-imagebanner-shade-toward, to bottom),
|
|
214
234
|
var(--color-bg-overlay),
|
|
215
|
-
var(--color-bg-overlay) calc(100% - var(--dds-imagebanner-shade-
|
|
235
|
+
var(--color-bg-overlay) calc(100% - var(--dds-imagebanner-shade-ramp) * 1.0),
|
|
236
|
+
color-mix(in srgb, var(--color-bg-overlay) 95.7%, transparent) calc(100% - var(--dds-imagebanner-shade-ramp) * 0.875),
|
|
237
|
+
color-mix(in srgb, var(--color-bg-overlay) 84.4%, transparent) calc(100% - var(--dds-imagebanner-shade-ramp) * 0.75),
|
|
238
|
+
color-mix(in srgb, var(--color-bg-overlay) 68.4%, transparent) calc(100% - var(--dds-imagebanner-shade-ramp) * 0.625),
|
|
239
|
+
color-mix(in srgb, var(--color-bg-overlay) 50.0%, transparent) calc(100% - var(--dds-imagebanner-shade-ramp) * 0.5),
|
|
240
|
+
color-mix(in srgb, var(--color-bg-overlay) 31.6%, transparent) calc(100% - var(--dds-imagebanner-shade-ramp) * 0.375),
|
|
241
|
+
color-mix(in srgb, var(--color-bg-overlay) 15.6%, transparent) calc(100% - var(--dds-imagebanner-shade-ramp) * 0.25),
|
|
242
|
+
color-mix(in srgb, var(--color-bg-overlay) 4.3%, transparent) calc(100% - var(--dds-imagebanner-shade-ramp) * 0.125),
|
|
216
243
|
transparent
|
|
217
244
|
);
|
|
218
245
|
content: '';
|
|
@@ -33,4 +33,4 @@ export type { ProductHeroProps };
|
|
|
33
33
|
* region landmark once it has a name, and a band that announced itself as an unnamed section is one
|
|
34
34
|
* more thing a screen reader user walks through rather than a place they can skip to.
|
|
35
35
|
*/
|
|
36
|
-
export declare function ProductHero({ eyebrow, title, headingLevel, description, actions, image, media, backgroundImage,
|
|
36
|
+
export declare function ProductHero({ eyebrow, title, headingLevel, description, actions, image, media, backgroundImage, }: ProductHeroProps): import("react").JSX.Element;
|
|
@@ -37,11 +37,11 @@ import { productHeroDefaults, productHeroParts, } from './ProductHero.types.js';
|
|
|
37
37
|
* region landmark once it has a name, and a band that announced itself as an unnamed section is one
|
|
38
38
|
* more thing a screen reader user walks through rather than a place they can skip to.
|
|
39
39
|
*/
|
|
40
|
-
export function ProductHero({ eyebrow, title, headingLevel = productHeroDefaults.headingLevel, description, actions, image, media, backgroundImage,
|
|
40
|
+
export function ProductHero({ eyebrow, title, headingLevel = productHeroDefaults.headingLevel, description, actions, image, media, backgroundImage, }) {
|
|
41
41
|
const titleId = useId();
|
|
42
42
|
const [band, revealed] = useReveal();
|
|
43
43
|
const Title = headingLevel === 'h2' ? 'h2' : 'h1';
|
|
44
|
-
return (_jsx("section", { className: productHeroParts.root, "aria-labelledby": titleId,
|
|
44
|
+
return (_jsx("section", { className: productHeroParts.root, "aria-labelledby": titleId,
|
|
45
45
|
/* The one inline style here, and it is a value rather than a decision: which photograph,
|
|
46
46
|
supplied by the page. How it is drawn — the cover fit and the position —
|
|
47
47
|
is in the stylesheet, where it can be reasoned about. */
|
|
@@ -18,14 +18,10 @@ export interface ProductHeroOwnProps {
|
|
|
18
18
|
image?: BlockImage;
|
|
19
19
|
/** A product picture in a shape the page fixes on purpose — an AspectRatio box, which keeps its ratio across the column. Prefer `image`, which keeps the picture's own shape and adapts to it. */
|
|
20
20
|
media?: ReactNode;
|
|
21
|
-
|
|
22
|
-
export type ProductHeroProps = ProductHeroOwnProps & ({
|
|
23
|
-
shade?: boolean;
|
|
24
|
-
backgroundImage: string;
|
|
25
|
-
} | {
|
|
26
|
-
shade?: false;
|
|
21
|
+
/** A photograph filling the whole band. It is shown as it is — nothing is painted over it, so choose one already dark enough that the title reads on it */
|
|
27
22
|
backgroundImage?: string;
|
|
28
|
-
}
|
|
23
|
+
}
|
|
24
|
+
export type ProductHeroProps = ProductHeroOwnProps;
|
|
29
25
|
/** The class each part carries. Apply these — do not retype them. */
|
|
30
26
|
export declare const productHeroParts: {
|
|
31
27
|
readonly root: "dds-producthero";
|
|
@@ -42,5 +38,4 @@ export declare const productHeroParts: {
|
|
|
42
38
|
/** Generated so defaults cannot drift from the contract. */
|
|
43
39
|
export declare const productHeroDefaults: {
|
|
44
40
|
readonly headingLevel: "h1";
|
|
45
|
-
readonly shade: false;
|
|
46
41
|
};
|
|
@@ -13,4 +13,4 @@ export const productHeroParts = {
|
|
|
13
13
|
image: 'dds-producthero__image',
|
|
14
14
|
};
|
|
15
15
|
/** Generated so defaults cannot drift from the contract. */
|
|
16
|
-
export const productHeroDefaults = { headingLevel: 'h1'
|
|
16
|
+
export const productHeroDefaults = { headingLevel: 'h1' };
|
|
@@ -383,57 +383,3 @@
|
|
|
383
383
|
padding-block: var(--space-section-compact);
|
|
384
384
|
}
|
|
385
385
|
}
|
|
386
|
-
|
|
387
|
-
/*
|
|
388
|
-
* `shade`: a shade behind the words, for a bright photograph.
|
|
389
|
-
*
|
|
390
|
-
* The photograph is shown as it is, and a bright one — the daylight sky the brand's heroes ask for —
|
|
391
|
-
* leaves the description and the actions below AA. Darkening the whole photograph fixes the number
|
|
392
|
-
* and loses the day. So the shade is drawn from the words themselves: a box of color.bg.overlay under
|
|
393
|
-
* the text and another under the actions, each reaching space.inset.2xl past the words and blurred by
|
|
394
|
-
* space.inset.xl, so it is full strength under every word and fades to clear in the margin round them.
|
|
395
|
-
* It follows the words wherever they sit — at the start on a phone, centred from the fold — and leaves
|
|
396
|
-
* the rest of the photograph its light.
|
|
397
|
-
*
|
|
398
|
-
* The two boxes meet rather than overlap: the text's runs down across the step to the actions, the
|
|
399
|
-
* actions' starts at their top edge. Overlapping, the two layers of shade doubled into a darker band
|
|
400
|
-
* between the words and the buttons; meeting, their blurred edges add back up to the one strength.
|
|
401
|
-
* A blur rather than a gradient because a gradient fills a box to its edge, and the edge then shows.
|
|
402
|
-
*
|
|
403
|
-
* Under the words, inside their own stacking context, so it can never be drawn over a word or a
|
|
404
|
-
* button.
|
|
405
|
-
*/
|
|
406
|
-
:where([data-dds-context="marketing"]) .dds-producthero[data-shade] :is(.dds-producthero__text, .dds-producthero__actions) {
|
|
407
|
-
isolation: isolate;
|
|
408
|
-
position: relative;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
:where([data-dds-context="marketing"]) .dds-producthero[data-shade] :is(.dds-producthero__text, .dds-producthero__actions)::before {
|
|
412
|
-
background-color: var(--color-bg-overlay);
|
|
413
|
-
content: '';
|
|
414
|
-
filter: blur(var(--space-inset-xl));
|
|
415
|
-
inset: calc(-1 * var(--space-inset-2xl));
|
|
416
|
-
pointer-events: none;
|
|
417
|
-
position: absolute;
|
|
418
|
-
z-index: -1;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
:where([data-dds-context="marketing"]) .dds-producthero[data-shade] .dds-producthero__text:has(+ .dds-producthero__actions)::before {
|
|
422
|
-
inset-block-end: calc(-1 * var(--space-heading-description-actions));
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
:where([data-dds-context="marketing"]) .dds-producthero[data-shade] .dds-producthero__actions::before {
|
|
426
|
-
inset-block-start: 0;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
/*
|
|
430
|
-
* From the fold, where the words are centred, the text's box is the band's whole width while its
|
|
431
|
-
* balanced lines take much less of it, so a shade the box's width was a veil across the band. It is
|
|
432
|
-
* held to size.heading-group.max — the width a centred title and its line may run as a block — and
|
|
433
|
-
* the margin round that, centred with the words.
|
|
434
|
-
*/
|
|
435
|
-
@container (min-width: 48rem) {
|
|
436
|
-
:where([data-dds-context="marketing"]) .dds-producthero[data-shade] .dds-producthero__text::before {
|
|
437
|
-
inset-inline: max(calc(-1 * var(--space-inset-2xl)), calc(50% - var(--size-heading-group-max) / 2 - var(--space-inset-2xl)));
|
|
438
|
-
}
|
|
439
|
-
}
|
|
@@ -24,9 +24,10 @@ export type { SceneScrollProps };
|
|
|
24
24
|
* translate and keep in step. The second scene's title sits one level under it, as the second half
|
|
25
25
|
* of the section the first one names.
|
|
26
26
|
*
|
|
27
|
-
*
|
|
28
|
-
* scene
|
|
29
|
-
*
|
|
27
|
+
* Each scene names its ink — near-black over a daylight photograph, white over a night one — and the
|
|
28
|
+
* scene carries it as `data-ink`, so the stylesheet sets it. White words lay a shade under
|
|
29
|
+
* themselves, eased out toward the photograph's subject; near-black words sit straight on the
|
|
30
|
+
* photograph, as the reference's do, there being no light shade. Each scene's words fade as a whole, so
|
|
30
31
|
* the two inks are never drawn over each other; what the reference ran as one colour interpolating
|
|
31
32
|
* with the scroll is here two sets of words, each in its own ink, one giving way to the other.
|
|
32
33
|
*
|
|
@@ -29,9 +29,10 @@ const under = { h1: 'h2', h2: 'h3' };
|
|
|
29
29
|
* translate and keep in step. The second scene's title sits one level under it, as the second half
|
|
30
30
|
* of the section the first one names.
|
|
31
31
|
*
|
|
32
|
-
*
|
|
33
|
-
* scene
|
|
34
|
-
*
|
|
32
|
+
* Each scene names its ink — near-black over a daylight photograph, white over a night one — and the
|
|
33
|
+
* scene carries it as `data-ink`, so the stylesheet sets it. White words lay a shade under
|
|
34
|
+
* themselves, eased out toward the photograph's subject; near-black words sit straight on the
|
|
35
|
+
* photograph, as the reference's do, there being no light shade. Each scene's words fade as a whole, so
|
|
35
36
|
* the two inks are never drawn over each other; what the reference ran as one colour interpolating
|
|
36
37
|
* with the scroll is here two sets of words, each in its own ink, one giving way to the other.
|
|
37
38
|
*
|
|
@@ -50,13 +50,13 @@
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/*
|
|
53
|
-
* The words
|
|
53
|
+
* The words are drawn in the ink the scene names.
|
|
54
54
|
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
55
|
+
* Whichever ink the photograph leaves room for — color.fg.on-media-light, near-black in both modes,
|
|
56
|
+
* over a light photograph such as a daylight sky, and the section's own color.fg.on-brand-strong,
|
|
57
|
+
* white, over a dark one. The photograph does not change with the theme, so neither ink does. The
|
|
58
|
+
* block cannot see the photograph, so the scene says which. White words have a shade behind them,
|
|
59
|
+
* below; near-black words sit straight on the photograph, and the author answers for that contrast.
|
|
60
60
|
*
|
|
61
61
|
* On the scene, so its title and its line take it together, and inherited through the scene even
|
|
62
62
|
* where the scroll lays the scenes out as `display: contents`.
|
|
@@ -65,6 +65,71 @@
|
|
|
65
65
|
color: var(--color-fg-on-media-light);
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
/*
|
|
69
|
+
* The shade under a white-ink scene's words, ImageBanner's cover shade on a full-screen scene.
|
|
70
|
+
*
|
|
71
|
+
* White words were left to the photograph, and a photograph bright anywhere behind them — a sky, a
|
|
72
|
+
* lit wall — took the line under the title below AA. So the words lay color.bg.overlay under
|
|
73
|
+
* themselves: full strength to the edge of their own column and then eased out past it, a
|
|
74
|
+
* smoothstep sampled as stops for the reason ImageBanner gives, so it never reads as a seam on the
|
|
75
|
+
* photograph. From 62rem it is a band the stage's height down the words' half, fading across
|
|
76
|
+
* three times space.section.default toward the subject; below 62rem the words run the width at the
|
|
77
|
+
* top, and it runs down from the top of the stage and out space.section.default under them.
|
|
78
|
+
*
|
|
79
|
+
* Drawn by the words rather than the photograph, so it fades with them through the change-over and
|
|
80
|
+
* is never over a scene whose words are not there. Only under white ink: there is no light shade
|
|
81
|
+
* for near-black words, which sit straight on a light photograph as they always did. It reaches
|
|
82
|
+
* past the stage's edges on purpose and is clipped there — by the stage while the scroll drives the
|
|
83
|
+
* change-over, and by its own scene in the plain fallback, with `clip` rather than `hidden` for the
|
|
84
|
+
* timeline's sake. A gradient takes a physical direction, so the direction is a custom property,
|
|
85
|
+
* mirrored under `:dir(rtl)`.
|
|
86
|
+
*/
|
|
87
|
+
:where([data-dds-context="marketing"]) .dds-scenescroll {
|
|
88
|
+
--dds-scenescroll-shade-fade: var(--space-section-default);
|
|
89
|
+
--dds-scenescroll-shade-toward: to bottom;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
:where([data-dds-context="marketing"]) .dds-scenescroll .dds-scenescroll__scene {
|
|
93
|
+
overflow: clip;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
:where([data-dds-context="marketing"]) .dds-scenescroll .dds-scenescroll__scene[data-ink="light"] .dds-scenescroll__content::before {
|
|
97
|
+
background-image: linear-gradient(
|
|
98
|
+
var(--dds-scenescroll-shade-toward),
|
|
99
|
+
var(--color-bg-overlay),
|
|
100
|
+
var(--color-bg-overlay) calc(100% - var(--dds-scenescroll-shade-fade) * 1.0),
|
|
101
|
+
color-mix(in srgb, var(--color-bg-overlay) 95.7%, transparent) calc(100% - var(--dds-scenescroll-shade-fade) * 0.875),
|
|
102
|
+
color-mix(in srgb, var(--color-bg-overlay) 84.4%, transparent) calc(100% - var(--dds-scenescroll-shade-fade) * 0.75),
|
|
103
|
+
color-mix(in srgb, var(--color-bg-overlay) 68.4%, transparent) calc(100% - var(--dds-scenescroll-shade-fade) * 0.625),
|
|
104
|
+
color-mix(in srgb, var(--color-bg-overlay) 50.0%, transparent) calc(100% - var(--dds-scenescroll-shade-fade) * 0.5),
|
|
105
|
+
color-mix(in srgb, var(--color-bg-overlay) 31.6%, transparent) calc(100% - var(--dds-scenescroll-shade-fade) * 0.375),
|
|
106
|
+
color-mix(in srgb, var(--color-bg-overlay) 15.6%, transparent) calc(100% - var(--dds-scenescroll-shade-fade) * 0.25),
|
|
107
|
+
color-mix(in srgb, var(--color-bg-overlay) 4.3%, transparent) calc(100% - var(--dds-scenescroll-shade-fade) * 0.125),
|
|
108
|
+
transparent
|
|
109
|
+
);
|
|
110
|
+
content: '';
|
|
111
|
+
inset: calc(-1 * 100cqh) calc(-1 * 100cqw) calc(-1 * var(--dds-scenescroll-shade-fade));
|
|
112
|
+
pointer-events: none;
|
|
113
|
+
position: absolute;
|
|
114
|
+
z-index: -1;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
@container (min-width: 62rem) {
|
|
118
|
+
:where([data-dds-context="marketing"]) .dds-scenescroll {
|
|
119
|
+
--dds-scenescroll-shade-fade: calc(var(--space-section-default) * 3);
|
|
120
|
+
--dds-scenescroll-shade-toward: to right;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
:where([data-dds-context="marketing"]) .dds-scenescroll:dir(rtl) {
|
|
124
|
+
--dds-scenescroll-shade-toward: to left;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
:where([data-dds-context="marketing"]) .dds-scenescroll .dds-scenescroll__scene[data-ink="light"] .dds-scenescroll__content::before {
|
|
128
|
+
inset-block: calc(-1 * 100cqh);
|
|
129
|
+
inset-inline: calc(-1 * 100cqw) calc(-1 * var(--dds-scenescroll-shade-fade));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
68
133
|
/*
|
|
69
134
|
* A title with nothing under it carries no step under it.
|
|
70
135
|
*/
|
|
@@ -124,6 +189,22 @@
|
|
|
124
189
|
object-position: bottom;
|
|
125
190
|
}
|
|
126
191
|
|
|
192
|
+
/*
|
|
193
|
+
* From 62rem the subject is on the far side from the words, so a crop takes from the words' side
|
|
194
|
+
* rather than the middle: on a window narrower than the photograph's shape, a centred crop cut into
|
|
195
|
+
* the subject at the edge and pushed it toward the words. Physical, since `object-position` has no
|
|
196
|
+
* logical form, and mirrored under `:dir(rtl)`, where the words are on the right.
|
|
197
|
+
*/
|
|
198
|
+
@container (min-width: 62rem) {
|
|
199
|
+
:where([data-dds-context="marketing"]) .dds-scenescroll .dds-scenescroll__image {
|
|
200
|
+
object-position: right bottom;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
:where([data-dds-context="marketing"]) .dds-scenescroll:dir(rtl) .dds-scenescroll__image {
|
|
204
|
+
object-position: left bottom;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
127
208
|
/*
|
|
128
209
|
* The change-over, scrubbed by the scroll.
|
|
129
210
|
*
|
|
@@ -259,7 +259,7 @@
|
|
|
259
259
|
* How many figures share a row, read from the figures — FeatureCards' way, `:has()` asking the
|
|
260
260
|
* markup for the count it already holds rather than a prop restating it.
|
|
261
261
|
*
|
|
262
|
-
* Three to a row by default, which is five as three and two, and six as two rows of three. Three
|
|
262
|
+
* Three to a row by default, which is five as three and two centred under them, and six as two rows of three. Three
|
|
263
263
|
* or fewer take one row of their own. Four are two rows of two here and one row at full width:
|
|
264
264
|
* four figures across a tablet's column are each narrower than a figure is wide at this size.
|
|
265
265
|
*
|
|
@@ -270,6 +270,32 @@
|
|
|
270
270
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
+
/*
|
|
274
|
+
* A last row short of three is centred under the rows above it, as the header is over them, rather
|
|
275
|
+
* than left at the start edge.
|
|
276
|
+
*
|
|
277
|
+
* Five or more is the only set that can leave one: three or fewer is a single row, and four is two
|
|
278
|
+
* rows of two. Its three columns are laid as six half-columns, each figure across two, so a figure
|
|
279
|
+
* is exactly as wide as before — two half-columns and the gutter between them is one column — and
|
|
280
|
+
* a short last row can start half a column in. One left over starts at the third half-column, the
|
|
281
|
+
* middle; the first of two left over at the second, and the other follows it.
|
|
282
|
+
*/
|
|
283
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__list:has(> :nth-child(5)) {
|
|
284
|
+
grid-template-columns: repeat(6, minmax(0, 1fr));
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__list:has(> :nth-child(5)) > .dds-stats__item {
|
|
288
|
+
grid-column: span 2;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__list:has(> :nth-child(5)) > .dds-stats__item:nth-child(3n + 1):last-child {
|
|
292
|
+
grid-column: 3 / span 2;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__list:has(> :nth-child(5)) > .dds-stats__item:nth-child(3n + 1):nth-last-child(2) {
|
|
296
|
+
grid-column: 2 / span 2;
|
|
297
|
+
}
|
|
298
|
+
|
|
273
299
|
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__list:has(> :nth-child(4):last-child),
|
|
274
300
|
:where([data-dds-context="marketing"]) .dds-stats .dds-stats__list:has(> :nth-child(2):last-child) {
|
|
275
301
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
* Over a light page the same glass came out near-white, behind the dark theme's white ink. So the
|
|
25
25
|
* dark page's ground goes under it, and the card is the same charcoal on every page.
|
|
26
26
|
*
|
|
27
|
-
*
|
|
27
|
+
* In a scene the photograph shows through instead, but under a scrim of color.bg.overlay: the
|
|
28
|
+
* glass alone over a light photograph came out near-white behind the same white ink, and the
|
|
29
|
+
* pane's UI could not be read. Darkened, the picture is still there, as colour and shape.
|
|
28
30
|
*/
|
|
29
31
|
:where([data-dds-context="marketing"]) .dds-illustrationcard {
|
|
30
32
|
background-color: var(--color-bg-page);
|
|
@@ -32,8 +34,7 @@
|
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
:where([data-dds-context="marketing"]) .dds-illustrationscene .dds-illustrationcard {
|
|
35
|
-
background-color: var(--color-bg-
|
|
36
|
-
background-image: none;
|
|
37
|
+
background-color: var(--color-bg-overlay);
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
/*
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
:where([data-dds-context="marketing"]) .dds-illustrationchoice .dds-illustrationchoice__outline {
|
|
39
39
|
border-style: solid;
|
|
40
40
|
pointer-events: none;
|
|
41
|
-
border-color: var(--color-
|
|
41
|
+
border-color: var(--color-fg-default);
|
|
42
42
|
border-radius: var(--radius-control-md);
|
|
43
43
|
border-width: var(--border-width-emphasis);
|
|
44
44
|
}
|