@deriv-com/dds-marketing 1.1.1 → 1.1.2
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/featurescroll.md +1 -1
- package/.llm/components/imagebanner.md +2 -2
- package/.llm/components/steps.md +14 -6
- package/.llm/digest/imagebanner.md +1 -1
- package/.llm/digest/steps.md +2 -2
- package/.llm/tokens.md +4 -0
- package/dist/components/blocks/block-parallax.css +62 -0
- package/dist/components/blocks/download-banner/DownloadBanner.d.ts +1 -0
- package/dist/components/blocks/download-banner/DownloadBanner.js +2 -1
- package/dist/components/blocks/feature-bullets/FeatureBullets.d.ts +1 -0
- package/dist/components/blocks/feature-bullets/FeatureBullets.js +2 -1
- package/dist/components/blocks/feature-cards/FeatureCards.d.ts +1 -0
- package/dist/components/blocks/feature-cards/FeatureCards.js +1 -0
- package/dist/components/blocks/feature-scroll/FeatureScroll.js +9 -3
- package/dist/components/blocks/feature-scroll/feature-scroll-impl.css +63 -40
- package/dist/components/blocks/feature-split/FeatureSplit.d.ts +1 -0
- package/dist/components/blocks/feature-split/FeatureSplit.js +2 -1
- package/dist/components/blocks/feature-stack/FeatureStack.d.ts +1 -0
- package/dist/components/blocks/feature-stack/FeatureStack.js +2 -1
- package/dist/components/blocks/image-banner/ImageBanner.types.d.ts +2 -2
- package/dist/components/blocks/image-banner/ImageBanner.types.js +1 -1
- package/dist/components/blocks/landing-hero/LandingHero.css +1 -1
- package/dist/components/blocks/landing-hero/LandingHero.d.ts +1 -0
- package/dist/components/blocks/landing-hero/LandingHero.js +2 -1
- package/dist/components/blocks/product-hero/ProductHero.css +1 -1
- package/dist/components/blocks/product-hero/ProductHero.d.ts +1 -0
- package/dist/components/blocks/product-hero/ProductHero.js +2 -1
- package/dist/components/blocks/steps/Steps.css +2 -2
- package/dist/components/blocks/steps/Steps.d.ts +1 -0
- package/dist/components/blocks/steps/Steps.js +3 -2
- package/dist/components/blocks/steps/Steps.types.d.ts +1 -1
- package/dist/components/blocks/steps/steps-impl.css +76 -4
- package/dist/components/blocks/steps/use-turn.d.ts +8 -8
- package/dist/components/blocks/steps/use-turn.js +19 -16
- package/dist/components/blocks/team-grid/TeamGrid.d.ts +1 -0
- package/dist/components/blocks/team-grid/TeamGrid.js +2 -1
- package/dist/components/core/card/Card.css +1 -1
- package/dist/marketing.css +8 -0
- package/package.json +1 -1
|
@@ -64,7 +64,7 @@ FeatureScroll renders 11 elements. A token binding names the part it applies to.
|
|
|
64
64
|
|
|
65
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
66
|
|
|
67
|
-
One picture gives way to the next by
|
|
67
|
+
One picture gives way to the next by being uncovered over it from an edge — up from the foot as a reader scrolls on, down from the head as they scroll back — rather than by fading in. The arriving one is brought to the front and uncovered over the bound duration and curve, and the one beneath holds whole until it is covered, then goes. A fade put both pictures on screen at half strength at once, a double exposure that read as the two overlapping; a wipe never mixes them, so every point of the square is one complete picture or the other.
|
|
68
68
|
|
|
69
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
70
|
|
|
@@ -98,9 +98,9 @@ Accepts: *
|
|
|
98
98
|
Use when: Always. The page's photograph, through AspectRatio or as an image — people or product. Split and cover crop it to fill the space they give it. Alt text is the page's to give, empty where the photograph is decoration.
|
|
99
99
|
Never for: Artwork with words in it. The cover photograph sits under a scrim and the text, and the split crops to fill
|
|
100
100
|
|
|
101
|
-
### parallax — `boolean` (default `
|
|
101
|
+
### parallax — `boolean` (default `true`)
|
|
102
102
|
|
|
103
|
-
Use when: The photograph drifts against the scroll as the card passes through the view, by motion.distance.drift over the whole pass, so it reads as further away than the card — the reference design's ParallaxBanner, which this replaces.
|
|
103
|
+
Use when: The photograph drifts against the scroll as the card passes through the view, by motion.distance.drift over the whole pass, so it reads as further away than the card — the reference design's ParallaxBanner, which this replaces. On by default, as every block's picture drifts: pass false for a card whose photograph must hold still under its button.
|
|
104
104
|
Never for: Holding still for a reader who asked for less motion — it already holds still for them, and in a browser that cannot drive it from the scroll.
|
|
105
105
|
|
|
106
106
|
## Accessibility
|
package/.llm/components/steps.md
CHANGED
|
@@ -33,7 +33,7 @@ Steps renders 15 elements. A token binding names the part it applies to.
|
|
|
33
33
|
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.
|
|
34
34
|
- `inner` — `<div>`, class `dds-steps__inner`. The measure and the gutter, holding the section's content in the same column as every other section on the page.
|
|
35
35
|
- `split` — `<div>`, class `dds-steps__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 picture first — the order the reference design takes and the one a sequence reads in.
|
|
36
|
-
- `media` — `<div>`, class `dds-steps__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 the section is the same shape whatever the page passes. With pictures on the steps it holds all of them in one cell, and the current step's is the one shown: the first while the list is still, each in turn under `autoplay`,
|
|
36
|
+
- `media` — `<div>`, class `dds-steps__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 the section is the same shape whatever the page passes. With pictures on the steps it holds all of them in one cell, and the current step's is the one shown: the first while the list is still, each in turn under `autoplay`, uncovered over one another in motion.duration.turn.
|
|
37
37
|
- `image` — `<img>`, class `dds-steps__image`. One step's picture, from its `image`, stacked in the media cell with the others and shown while its step is current. Decoration, as the media cell is: what the step says is its title and its line.
|
|
38
38
|
- `content` — `<div>`, class `dds-steps__content`. The title and the steps under it.
|
|
39
39
|
|
|
@@ -56,7 +56,7 @@ Steps renders 15 elements. A token binding names the part it applies to.
|
|
|
56
56
|
- `step-title` — `<h3>`, class `dds-steps__step-title`. What the reader does at this step. `h3` under the section's `h2`, so a reader moving by heading walks the steps one at a time.
|
|
57
57
|
- `trigger` — `<button>`, class `dds-steps__trigger`. Under `autoplay`, the step's title as a button: pressing it makes the step current and stops the turn for good. Inside the heading, so a reader moving by heading still walks the steps; `aria-current="step"` on the current one says which step is shown. Rendered only when `autoplay` is set.
|
|
58
58
|
- `step-description` — `<p>`, class `dds-steps__step-description`. What the step involves, in a sentence.
|
|
59
|
-
- `progress` — `<span>`, class `dds-steps__progress`. Under `autoplay`, the current step's rule filling from the start edge over motion.duration.
|
|
59
|
+
- `progress` — `<span>`, class `dds-steps__progress`. Under `autoplay`, the current step's rule filling from the start edge over motion.duration.step — how long until the next step — in the brand's coral. Held where the turn holds, full and still once it stops or for a reader who asked for less motion. Hidden from assistive technology: the current step is said by `aria-current`.
|
|
60
60
|
|
|
61
61
|
Apply these classes from the generated `stepsParts` constant rather than writing the strings by hand.
|
|
62
62
|
|
|
@@ -90,8 +90,8 @@ Never for: Moving the picture on a phone. There is one column there and the pict
|
|
|
90
90
|
|
|
91
91
|
### autoplay — `boolean` (default `false`)
|
|
92
92
|
|
|
93
|
-
Use when: Whether the steps take their turn on their own: one step is current, its rule fills over motion.duration.
|
|
94
|
-
Never for: Steps whose descriptions take longer to read than motion.duration.
|
|
93
|
+
Use when: Whether the steps take their turn on their own: one step is current, its rule fills over motion.duration.step, then the next takes over, and back to the first after the last. The current step's number is drawn at full strength and the rest recede; with an `image` on each step the picture changes to the current one's. Each step's title becomes a button that makes it current. Off by default, so a list nobody configured stays still. On, it holds while focus is inside the section — not under the pointer, so it keeps turning while a reader watches it — waits until the section is on screen, never moves for a reader who asked for less motion, and stops for good once the reader presses a step — the WAI carousel rule CtaBannerSlider follows: a reader who has chosen a step is reading it. That gives a reader a way to stop it, which WCAG 2.2.2 asks for, but not a visible one, so a page that needs 2.2.2 met plainly should leave it off.
|
|
94
|
+
Never for: Steps whose descriptions take longer to read than motion.duration.step — leave it off rather than cut the copy to the timer. And a list with no picture per step, where the only thing that moves is a rule: the turn is worth it when the picture changes with it.
|
|
95
95
|
|
|
96
96
|
### title — `string` **Required.**
|
|
97
97
|
|
|
@@ -122,7 +122,7 @@ Guaranteed by the component. Write none of it by hand: the ids these attributes
|
|
|
122
122
|
- The number drawn beside each step is decoration and is hidden from assistive technology. The ordered list already announces each step's place, and reading the drawn number as well says it twice
|
|
123
123
|
- Each step's title is a heading one level under the section's, so a reader moving by heading walks the steps one at a time
|
|
124
124
|
- The picture is decoration and is hidden from assistive technology — the one picture and every step's alike. What the section says is the title and the steps; a picture carrying something they do not is a section that reads as half empty to anyone who cannot see it
|
|
125
|
-
- Under autoplay, a hold while
|
|
125
|
+
- Under autoplay, a hold while focus is inside the section, no turn until the section is on screen, no movement for a reader who asked for less motion, and a stop for good once a step is pressed — the way to stop a list that would otherwise turn for as long as it is shown, which WCAG 2.2.2 asks for. The current step's title carries aria-current="step", so the step a reader is shown is the step a screen reader names
|
|
126
126
|
|
|
127
127
|
## Rules
|
|
128
128
|
|
|
@@ -270,7 +270,7 @@ import { AspectRatio, Steps } from '@deriv-com/dds-marketing'
|
|
|
270
270
|
|
|
271
271
|
### Taking turns
|
|
272
272
|
|
|
273
|
-
With `autoplay` each step takes its turn: its rule fills, its picture shows, then the next.
|
|
273
|
+
With `autoplay` each step takes its turn: its rule fills, its picture shows, then the next. Focus holds it, hovering does not; pressing a step stops it.
|
|
274
274
|
|
|
275
275
|
```tsx
|
|
276
276
|
import { Steps } from '@deriv-com/dds-marketing'
|
|
@@ -280,6 +280,14 @@ import { Steps } from '@deriv-com/dds-marketing'
|
|
|
280
280
|
|
|
281
281
|
// autoplay on the dark band
|
|
282
282
|
<Steps title="Get started in three steps" steps={[{"title":"Open an account","description":"Sign up in minutes with your email and a few details.","image":{"src":"/sample-stop-brand.svg","alt":"","width":800,"height":600}},{"title":"Fund it","description":"Deposit with the local payment method you already use.","image":{"src":"/sample-stop-teal.svg","alt":"","width":800,"height":600}},{"title":"Start trading","description":"Pick a market and place your first trade.","image":{"src":"/sample-stop-amber.svg","alt":"","width":800,"height":600}}]} autoplay ground="strong" mediaPosition="end" />
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### A picture on each step
|
|
286
|
+
|
|
287
|
+
Pictures on the steps without autoplay: the list stays still, the first step's picture is shown, and each title is a button that shows its own.
|
|
288
|
+
|
|
289
|
+
```tsx
|
|
290
|
+
import { Steps } from '@deriv-com/dds-marketing'
|
|
283
291
|
|
|
284
292
|
// still, a picture on each step — the first is shown
|
|
285
293
|
<Steps title="Get started in three steps" steps={[{"title":"Open an account","description":"Sign up in minutes with your email and a few details.","image":{"src":"/sample-stop-brand.svg","alt":"","width":800,"height":600}},{"title":"Fund it","description":"Deposit with the local payment method you already use.","image":{"src":"/sample-stop-teal.svg","alt":"","width":800,"height":600}},{"title":"Start trading","description":"Pick a market and place your first trade.","image":{"src":"/sample-stop-amber.svg","alt":"","width":800,"height":600}}]} />
|
|
@@ -51,7 +51,7 @@ A dark card in the middle of a page, pairing a headline and a line under it with
|
|
|
51
51
|
- never: A second action. The card asks for one thing
|
|
52
52
|
- **media** `node` · required — Always. The page's photograph, through AspectRatio or as an image — people or product. Split and cover crop it to fill the space they give it. Alt text is the page's to give, empty where the photograph is decoration
|
|
53
53
|
- never: Artwork with words in it. The cover photograph sits under a scrim and the text, and the split crops to fill
|
|
54
|
-
- **parallax** `boolean` · default
|
|
54
|
+
- **parallax** `boolean` · default true — The photograph drifts against the scroll as the card passes through the view, by motion.distance.drift over the whole pass, so it reads as further away than the card — the reference design's ParallaxBanner, which this replaces. …
|
|
55
55
|
- never: Holding still for a reader who asked for less motion — it already holds still for them, and in a browser that cannot drive it from the scroll
|
|
56
56
|
|
|
57
57
|
## Inside it
|
package/.llm/digest/steps.md
CHANGED
|
@@ -36,8 +36,8 @@ A two-column how-it-works section pairing a supporting picture with a numbered,
|
|
|
36
36
|
- `start` — The default, and the reference design's: the picture first, then the steps it introduces
|
|
37
37
|
- `end` — The steps first, for a section sitting under a split whose picture is already at the start
|
|
38
38
|
- never: Moving the picture on a phone. There is one column there and the picture leads it
|
|
39
|
-
- **autoplay** `boolean` · default false — Whether the steps take their turn on their own: one step is current, its rule fills over motion.duration.
|
|
40
|
-
- never: Steps whose descriptions take longer to read than motion.duration.
|
|
39
|
+
- **autoplay** `boolean` · default false — Whether the steps take their turn on their own: one step is current, its rule fills over motion.duration.step, then the next takes over, and back to the first after the last. …
|
|
40
|
+
- never: Steps whose descriptions take longer to read than motion.duration.step — leave it off rather than cut the copy to the timer. And a list with no picture per step, where the only thing that moves is a rule: the turn is worth it when the picture changes with it
|
|
41
41
|
- **title** `string` · required — Always. What the steps get a reader to, in the fewest words that say it — 'Get started in 3 steps'
|
|
42
42
|
- never: A sentence. The steps underneath are the argument; this only has to name where they lead
|
|
43
43
|
- **steps** `data` · required — Always. The steps, in the order a reader takes them — three or four is right, six the most. Each is a short imperative and a sentence under about two hundred characters. …
|
package/.llm/tokens.md
CHANGED
|
@@ -392,6 +392,7 @@ one value at every width.
|
|
|
392
392
|
- `--motion-duration-slow` — Large UI transitions, e.g. accordions expanding, page transitions, interactive demos.
|
|
393
393
|
- `--motion-duration-spinner` — One full rotation of a spinner. Fast enough to read as working, slow enough that the stroke does not blur into a solid ring.
|
|
394
394
|
- `--motion-duration-stagger` — The step between one part of a section arriving and the next. A gap rather than a length: five parts at this step trail the first by less than half a second, so a block still arrives as one thing rather than assembling itself while a reader waits.
|
|
395
|
+
- `--motion-duration-step` — How long one step holds before the next takes its turn, in a Steps list under autoplay. Longer than dwell: a step is a title, a sentence and a picture to take in, not a headline to glance at, and at dwell's pace the list moved on before a reader had finished the line.
|
|
395
396
|
- `--motion-duration-ticker` — How long a running row takes to carry one item past — Awards' row on a phone, multiplied by the number of awards for one whole loop. The reference site's pace: thirty-two seconds for eight awards, slow enough to read an award as it goes by.
|
|
396
397
|
- `--motion-duration-trail` — How long something takes to catch up with a pointer it trails — a background's glow following the pointer across a band. Half a second, so the light drifts after the hand rather than sticking to it: a trail is felt, a cursor is tracked.
|
|
397
398
|
- `--motion-duration-turn` — One turn of a wheel of figures: the one in place rising away as the next rises into it. The reference site's 700 — long enough that the eye follows the number in rather than seeing it swap.
|
|
@@ -406,9 +407,12 @@ one value at every width.
|
|
|
406
407
|
- `--motion-scale-enter` — What a component scales from when something reveals it — a tooltip appearing. Needs a transform origin on the edge nearest its anchor, or it grows out of its own middle. The counterpart to scale.hover, which is a response rather than an entrance.
|
|
407
408
|
- `--motion-scale-hover` — How much a control grows under the cursor on hover. Deliberately small — a control that grows past the pointer fires its own mouse-out.
|
|
408
409
|
- `--motion-scale-media-hover` — How much art grows inside a frame that holds still — an editorial card's picture under the cursor. Twice motion.scale.hover, which stays smaller because a control can outrun its own pointer and a framed picture cannot.
|
|
410
|
+
- `--motion-scale-parallax` — How much larger a block's picture is drawn inside its frame, so it can drift with the scroll without its edge showing. A tenth: room for the parallax travel either way with a margin to spare, and too little to feel like a crop.
|
|
409
411
|
- `--motion-scale-pile-covered` — How far a card in a StickyCards pile draws back once the next card has slid over it, from its top edge, so the pile reads as depth. Not motion.scale.enter, which is for something arriving.
|
|
410
412
|
- `--motion-translate-enter` — Where a surface waits before something reveals it: level on the inline axis, its own full height down the block one. A share of the element rather than of the page, so a sheet travels exactly its own height whatever that turns out to be.
|
|
411
413
|
- `--motion-translate-hover` — How far a pressable surface rises under the cursor. A fixed distance rather than a share of the element, so a tall card and a short one lift by the same amount and a row of them stays level. Small enough that nothing it sits beside looks disturbed.
|
|
414
|
+
- `--motion-translate-parallax-from` — Where a block's picture stands in its frame as the frame enters the view from below: a little above centre, as a share of its own height. It runs to parallax-to as the frame crosses the view, so the picture moves slower than the page and reads as further away.
|
|
415
|
+
- `--motion-translate-parallax-to` — Where a block's picture stands as its frame leaves the view at the top: a little below centre. The pair stays inside the room motion.scale.parallax gives, so no edge ever shows.
|
|
412
416
|
- `--motion-translate-reveal` — How far a section waits below where it lands, before the scroll brings it up. A fixed distance rather than a share of the block, so a hero a screen tall and a split half that arrive by the same movement. Pairs with motion.easing.reveal.
|
|
413
417
|
|
|
414
418
|
## opacity
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The drift every block's picture shares, defined once.
|
|
3
|
+
*
|
|
4
|
+
* As a block passes through the view its picture moves a little slower than the page, so it reads
|
|
5
|
+
* as further away than the words beside it — the reference design's parallax, on every block that
|
|
6
|
+
* holds a picture rather than as an option one banner had. A copy per block is how ten sections
|
|
7
|
+
* come to disagree about how far a picture moves, so the movement is stated here and each block
|
|
8
|
+
* only marks its frame: `data-dds-parallax` on the box that holds the picture and clips it.
|
|
9
|
+
*
|
|
10
|
+
* Imported by each block's component, which is what puts it in the bundle once however many blocks
|
|
11
|
+
* a page uses.
|
|
12
|
+
*
|
|
13
|
+
* Driven by the scroll and nothing else. The frame names a view timeline and the picture runs down
|
|
14
|
+
* it over the frame's whole pass across the view (`cover`), from motion.translate.parallax-from to
|
|
15
|
+
* motion.translate.parallax-to. No script measures anything and no listener runs on scroll: the
|
|
16
|
+
* browser advances the timeline itself, and the only properties it animates are `translate` and
|
|
17
|
+
* `scale`, which it composites without laying the page out or painting it again — so a page full
|
|
18
|
+
* of pictures scrolls as smoothly as a page with none. Linear, because the scroll is the easing.
|
|
19
|
+
*
|
|
20
|
+
* The picture is drawn at motion.scale.parallax inside a frame that clips it, which leaves room
|
|
21
|
+
* above and below for the travel, so no edge of the picture is ever seen. The frame keeps its size,
|
|
22
|
+
* so nothing around it moves.
|
|
23
|
+
*
|
|
24
|
+
* The frame clips with `overflow: clip`, not `hidden`. `hidden` makes a box a scroll container, and
|
|
25
|
+
* a view timeline is measured against its nearest one: under a box that never scrolls it stands
|
|
26
|
+
* still at its first frame. The same holds for every box above the frame, which is why the blocks
|
|
27
|
+
* that carry one clip their roots rather than hiding their overflow.
|
|
28
|
+
*
|
|
29
|
+
* Every picture inside the frame drifts, except one inside an IllustrationCard: the card is a
|
|
30
|
+
* picture of the product laid over the photograph, and the photograph moving behind it is the
|
|
31
|
+
* effect; the product's own icons sliding inside the card would be the card coming apart.
|
|
32
|
+
*
|
|
33
|
+
* Only where the browser can drive it from the scroll, and only for a reader who has not asked for
|
|
34
|
+
* less motion. Everywhere else the picture stands still, centred and at its own size.
|
|
35
|
+
*/
|
|
36
|
+
/* FeatureCards' pictures are drawn by CardBody, a core component that marks nothing, so its frame is
|
|
37
|
+
named here for the one block that wants it moving. */
|
|
38
|
+
:where([data-dds-context="marketing"]) :is([data-dds-parallax], .dds-featurecards .dds-cardbody__media-frame) {
|
|
39
|
+
overflow: clip;
|
|
40
|
+
view-timeline-axis: block;
|
|
41
|
+
view-timeline-name: --dds-parallax;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@supports (animation-timeline: view()) {
|
|
45
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
46
|
+
:where([data-dds-context="marketing"]) :is([data-dds-parallax], .dds-featurecards .dds-cardbody__media-frame) img:not(.dds-illustrationcard img) {
|
|
47
|
+
animation: dds-parallax linear both;
|
|
48
|
+
animation-range: cover;
|
|
49
|
+
animation-timeline: --dds-parallax;
|
|
50
|
+
scale: var(--motion-scale-parallax);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@keyframes dds-parallax {
|
|
56
|
+
from {
|
|
57
|
+
translate: var(--motion-translate-parallax-from);
|
|
58
|
+
}
|
|
59
|
+
to {
|
|
60
|
+
translate: var(--motion-translate-parallax-to);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -5,6 +5,7 @@ import { AndroidApkFillColoredIcon, AppleAppStoreFillColoredIcon, AppleDmgFillCo
|
|
|
5
5
|
import './DownloadBanner.css';
|
|
6
6
|
import './download-banner-impl.css';
|
|
7
7
|
import '../block-reveal.css';
|
|
8
|
+
import '../block-parallax.css';
|
|
8
9
|
import { Background } from '../../core/background/Background.js';
|
|
9
10
|
import { given } from '../../given.js';
|
|
10
11
|
import { arriving, useReveal } from '../use-reveal.js';
|
|
@@ -71,5 +72,5 @@ export function DownloadBanner({ ground = downloadBannerDefaults.ground, variant
|
|
|
71
72
|
builds of the same desktop app — and a key repeated is a badge React confuses
|
|
72
73
|
with the other. */
|
|
73
74
|
_jsx("li", { className: downloadBannerParts.badge, children: _jsx("a", { className: downloadBannerParts['badge-link'], href: href, "aria-label": given(label) ? label : name, children: _jsx(Mark, { className: downloadBannerParts.mark, width: undefined, height: undefined, "aria-hidden": "true", focusable: "false" }) }) }, `${platform}-${at}`));
|
|
74
|
-
}) })) : null, variant === 'featured' && media !== undefined && media !== null ? (_jsx("div", { className: downloadBannerParts.media, ...arriving(3), children: media })) : null] }) }) }));
|
|
75
|
+
}) })) : null, variant === 'featured' && media !== undefined && media !== null ? (_jsx("div", { "data-dds-parallax": "", className: downloadBannerParts.media, ...arriving(3), children: media })) : null] }) }) }));
|
|
75
76
|
}
|
|
@@ -5,6 +5,7 @@ import { CircleCheckSolidIcon } from '@deriv-com/quill-icons-web';
|
|
|
5
5
|
import './FeatureBullets.css';
|
|
6
6
|
import './feature-bullets-impl.css';
|
|
7
7
|
import '../block-reveal.css';
|
|
8
|
+
import '../block-parallax.css';
|
|
8
9
|
import { arriving, useReveal } from '../use-reveal.js';
|
|
9
10
|
import { featureBulletsDefaults, featureBulletsParts, } from './FeatureBullets.types.js';
|
|
10
11
|
/**
|
|
@@ -51,5 +52,5 @@ export function FeatureBullets({ ground = featureBulletsDefaults.ground, mediaPo
|
|
|
51
52
|
|
|
52
53
|
Two behind the claim, which the picture beside them takes — the two halves of
|
|
53
54
|
the split arrive first and the list follows them. */
|
|
54
|
-
_jsxs("li", { className: featureBulletsParts.item, ...arriving(2 + at), children: [_jsx("span", { className: featureBulletsParts.mark, "aria-hidden": "true", children: _jsx(CircleCheckSolidIcon, {}) }), _jsx("span", { className: featureBulletsParts.text, children: bullet })] }, bullet))) })] }), _jsx("div", { className: featureBulletsParts.media, ...arriving(1), "aria-hidden": "true", children: media })] }) }) }));
|
|
55
|
+
_jsxs("li", { className: featureBulletsParts.item, ...arriving(2 + at), children: [_jsx("span", { className: featureBulletsParts.mark, "aria-hidden": "true", children: _jsx(CircleCheckSolidIcon, {}) }), _jsx("span", { className: featureBulletsParts.text, children: bullet })] }, bullet))) })] }), _jsx("div", { "data-dds-parallax": "", className: featureBulletsParts.media, ...arriving(1), "aria-hidden": "true", children: media })] }) }) }));
|
|
55
56
|
}
|
|
@@ -4,6 +4,7 @@ import { useId } from 'react';
|
|
|
4
4
|
import './FeatureCards.css';
|
|
5
5
|
import './feature-cards-impl.css';
|
|
6
6
|
import '../block-reveal.css';
|
|
7
|
+
import '../block-parallax.css';
|
|
7
8
|
import { given } from '../../given.js';
|
|
8
9
|
import { cardsOf } from '../cards-of.js';
|
|
9
10
|
import { arriving, useReveal } from '../use-reveal.js';
|
|
@@ -52,7 +52,13 @@ export function FeatureScroll({ ground = featureScrollDefaults.ground, title, st
|
|
|
52
52
|
const root = useRef(null);
|
|
53
53
|
const titles = useRef([]);
|
|
54
54
|
const band = useRef(null);
|
|
55
|
-
|
|
55
|
+
/*
|
|
56
|
+
Which stop's picture is showing, and which way the reader went to reach it. The way is held with
|
|
57
|
+
the stop rather than worked out after it, so the picture arriving and the edge it is uncovered
|
|
58
|
+
from change in the same render — a step behind, it would open from the wrong side for a frame.
|
|
59
|
+
*/
|
|
60
|
+
// `going` is absent until the first change, so the picture a reader opens on is simply there.
|
|
61
|
+
const [{ at: showing, going }, setShowing] = useState({ at: 0 });
|
|
56
62
|
const count = steps.length;
|
|
57
63
|
useLayoutEffect(() => {
|
|
58
64
|
if (count === 0)
|
|
@@ -92,7 +98,7 @@ export function FeatureScroll({ ground = featureScrollDefaults.ground, title, st
|
|
|
92
98
|
reached = i;
|
|
93
99
|
}
|
|
94
100
|
const next = Math.min(count - 1, reached + 1);
|
|
95
|
-
setShowing((held) => (held === next ? held : next));
|
|
101
|
+
setShowing((held) => (held.at === next ? held : { at: next, going: next > held.at ? 'down' : 'up' }));
|
|
96
102
|
};
|
|
97
103
|
const schedule = () => {
|
|
98
104
|
if (frame !== 0)
|
|
@@ -127,7 +133,7 @@ export function FeatureScroll({ ground = featureScrollDefaults.ground, title, st
|
|
|
127
133
|
window.cancelAnimationFrame(frame);
|
|
128
134
|
};
|
|
129
135
|
}, [count]);
|
|
130
|
-
return (_jsx("section", { className: featureScrollParts.root, "data-ground": ground, "data-media-position": mediaPosition, "aria-labelledby": titleId, ref: root, children: _jsxs("div", { className: featureScrollParts.inner, children: [_jsx("h2", { className: featureScrollParts.heading, ref: heading, "data-revealed": revealed, ...arriving(0), id: titleId, children: title }), _jsx("div", { className: featureScrollParts.steps, style: { '--dds-featurescroll-stops': count }, children: steps.map((step, index) => (
|
|
136
|
+
return (_jsx("section", { className: featureScrollParts.root, "data-ground": ground, "data-media-position": mediaPosition, "data-going": going, "aria-labelledby": titleId, ref: root, children: _jsxs("div", { className: featureScrollParts.inner, children: [_jsx("h2", { className: featureScrollParts.heading, ref: heading, "data-revealed": revealed, ...arriving(0), id: titleId, children: title }), _jsx("div", { className: featureScrollParts.steps, style: { '--dds-featurescroll-stops': count }, children: steps.map((step, index) => (
|
|
131
137
|
/* Keyed by the title. An index would key the set by position, so filtering a stop out
|
|
132
138
|
would renumber every stop under it and React would reconcile the wrong ones — and
|
|
133
139
|
these are the content, so two the same would be a duplicate stop rather than a key
|
|
@@ -226,53 +226,50 @@
|
|
|
226
226
|
* The duration and the curve are bound on the part, so the generated sheet writes the
|
|
227
227
|
* reduced-motion block that turns this into a cut.
|
|
228
228
|
*/
|
|
229
|
+
/*
|
|
230
|
+
* One of them is shown, and the next is uncovered over it rather than faded in.
|
|
231
|
+
*
|
|
232
|
+
* The only thing the component's own listener writes is which stop's picture belongs to the words
|
|
233
|
+
* a reader has reached, and which way they went to reach it — a question about where each title
|
|
234
|
+
* has got to, which no query asks. The pinning and the change are this stylesheet's.
|
|
235
|
+
*
|
|
236
|
+
* A wipe rather than a dissolve. Fading the arriving picture in over the last one put both on
|
|
237
|
+
* screen at once for the whole of the change — at its middle the square was two photographs at
|
|
238
|
+
* half strength each, a double exposure that read as the pictures overlapping. Uncovered from an
|
|
239
|
+
* edge instead, every point of the square is one picture or the other at full strength, and the
|
|
240
|
+
* line between them moves the way the page does: up from the foot as a reader scrolls on, down
|
|
241
|
+
* from the head as they scroll back. Nothing is mixed, so nothing overlaps.
|
|
242
|
+
*
|
|
243
|
+
* An animation rather than a transition, because an animation starts from its first frame. A
|
|
244
|
+
* transition starts from wherever the picture last rested, so a reader who turned back mid-section
|
|
245
|
+
* had the next picture open from the edge they were no longer moving towards.
|
|
246
|
+
*
|
|
247
|
+
* The shown picture is brought to the front and uncovered; the one it replaces stays beneath,
|
|
248
|
+
* whole, for the same duration and then goes — a step at the end of a zero-length transition,
|
|
249
|
+
* delayed by the uncovering's own duration, read from the same role so the two cannot drift
|
|
250
|
+
* apart into a gap. Every other picture is already gone.
|
|
251
|
+
*
|
|
252
|
+
* `z-index` is a bare integer, the one kind of value this system keeps out of its tokens: a
|
|
253
|
+
* stacking order inside a component is not a decision any brand or mode has an opinion about.
|
|
254
|
+
*/
|
|
255
|
+
:where([data-dds-context="marketing"]) .dds-featurescroll .dds-featurescroll__media {
|
|
256
|
+
transition: opacity 0s linear var(--motion-duration-media-hover);
|
|
257
|
+
}
|
|
258
|
+
|
|
229
259
|
:where([data-dds-context="marketing"])
|
|
230
260
|
.dds-featurescroll
|
|
231
261
|
.dds-featurescroll__media[data-showing="true"] {
|
|
232
262
|
opacity: 1;
|
|
233
|
-
/*
|
|
234
|
-
* On top, and that is what makes this a dissolve rather than a dip.
|
|
235
|
-
*
|
|
236
|
-
* The arriving picture fades in over whatever is beneath it, so at every moment of the change
|
|
237
|
-
* the square is covered by something opaque. Fading both at once instead leaves them near half
|
|
238
|
-
* way together, the section's own ground shows through the pair, and the square brightens and
|
|
239
|
-
* darkens — a pulse, from nothing but two opacities crossing.
|
|
240
|
-
*
|
|
241
|
-
* It has to be the shown one rather than the later one in the document, because a reader
|
|
242
|
-
* scrolls back as well as forward. Going up, the arriving picture is earlier in the markup and
|
|
243
|
-
* would fade in underneath the one it is replacing: invisible until that one left, which is a
|
|
244
|
-
* cut with a pause in front of it.
|
|
245
|
-
*
|
|
246
|
-
* A bare integer, which is the one kind of value this system keeps out of its tokens: a
|
|
247
|
-
* stacking order inside a component is not a decision any brand or mode has an opinion about.
|
|
248
|
-
*/
|
|
249
|
-
z-index: 1;
|
|
250
|
-
/* No wait on the way in. The pair of delays is the whole mechanism — see the rule below. */
|
|
251
263
|
transition-delay: 0s;
|
|
264
|
+
z-index: 1;
|
|
252
265
|
}
|
|
253
266
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
* behind the new one does it drop, which costs nothing to look at because nobody can see it.
|
|
261
|
-
*
|
|
262
|
-
* The delay is the arriving picture's own duration, read from the same role, so the two cannot
|
|
263
|
-
* drift apart into a gap or an overlap.
|
|
264
|
-
*
|
|
265
|
-
* There is no scale here any more. It read as a pulse, which is what it was: a square this large
|
|
266
|
-
* carries a change of picture on its own, and a sixth of a second of zoom on top of it is the
|
|
267
|
-
* section drawing attention to its own machinery.
|
|
268
|
-
*/
|
|
269
|
-
:where([data-dds-context="marketing"]) .dds-featurescroll .dds-featurescroll__media {
|
|
270
|
-
transition-delay: var(--motion-duration-media-hover);
|
|
271
|
-
/* Named here rather than in the contract because `staticCss` takes keywords, and this is the
|
|
272
|
-
one property the pair of rules is about. The duration and the curve stay bound there, which
|
|
273
|
-
is what the generated reduced-motion block is written from — so a reader who asked for less
|
|
274
|
-
still gets a cut. */
|
|
275
|
-
transition-property: opacity;
|
|
267
|
+
:where([data-dds-context="marketing"]) .dds-featurescroll[data-going="down"] .dds-featurescroll__media[data-showing="true"] {
|
|
268
|
+
animation: dds-featurescroll-from-foot var(--motion-duration-media-hover) var(--motion-easing-media-hover);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
:where([data-dds-context="marketing"]) .dds-featurescroll[data-going="up"] .dds-featurescroll__media[data-showing="true"] {
|
|
272
|
+
animation: dds-featurescroll-from-head var(--motion-duration-media-hover) var(--motion-easing-media-hover);
|
|
276
273
|
}
|
|
277
274
|
|
|
278
275
|
/*
|
|
@@ -290,3 +287,29 @@
|
|
|
290
287
|
);
|
|
291
288
|
}
|
|
292
289
|
}
|
|
290
|
+
|
|
291
|
+
@keyframes dds-featurescroll-from-foot {
|
|
292
|
+
from {
|
|
293
|
+
clip-path: inset(100% 0 0 0);
|
|
294
|
+
}
|
|
295
|
+
to {
|
|
296
|
+
clip-path: inset(0);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
@keyframes dds-featurescroll-from-head {
|
|
301
|
+
from {
|
|
302
|
+
clip-path: inset(0 0 100% 0);
|
|
303
|
+
}
|
|
304
|
+
to {
|
|
305
|
+
clip-path: inset(0);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/* Less movement is a cut: no uncovering, and the last picture gone at once. The generated sheet
|
|
310
|
+
already drops every transition here; the animation is this sheet's to drop. */
|
|
311
|
+
@media (prefers-reduced-motion: reduce) {
|
|
312
|
+
:where([data-dds-context="marketing"]) .dds-featurescroll .dds-featurescroll__media[data-showing="true"] {
|
|
313
|
+
animation: none;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
@@ -4,6 +4,7 @@ import { useId } from 'react';
|
|
|
4
4
|
import './FeatureSplit.css';
|
|
5
5
|
import './feature-split-impl.css';
|
|
6
6
|
import '../block-reveal.css';
|
|
7
|
+
import '../block-parallax.css';
|
|
7
8
|
import { arriving, useReveal } from '../use-reveal.js';
|
|
8
9
|
import { given } from '../../given.js';
|
|
9
10
|
import { featureSplitDefaults, featureSplitParts, } from './FeatureSplit.types.js';
|
|
@@ -34,5 +35,5 @@ import { featureSplitDefaults, featureSplitParts, } from './FeatureSplit.types.j
|
|
|
34
35
|
export function FeatureSplit({ ground = featureSplitDefaults.ground, mediaPosition = featureSplitDefaults.mediaPosition, title, description, link, media, }) {
|
|
35
36
|
const titleId = useId();
|
|
36
37
|
const [band, revealed] = useReveal();
|
|
37
|
-
return (_jsx("section", { className: featureSplitParts.root, "data-ground": ground, "data-media-position": mediaPosition, "aria-labelledby": titleId, children: _jsx("div", { className: featureSplitParts.inner, ref: band, "data-revealed": revealed, children: _jsxs("div", { className: featureSplitParts.split, children: [_jsxs("div", { className: featureSplitParts.content, children: [_jsx("h2", { className: featureSplitParts.title, ...arriving(0), id: titleId, children: title }), given(description) ? (_jsx("p", { className: featureSplitParts.description, ...arriving(1), children: description })) : null, link ? (_jsx("div", { className: featureSplitParts.link, ...arriving(2), children: link })) : null] }), _jsx("div", { className: featureSplitParts.media, ...arriving(3), "aria-hidden": "true", children: media })] }) }) }));
|
|
38
|
+
return (_jsx("section", { className: featureSplitParts.root, "data-ground": ground, "data-media-position": mediaPosition, "aria-labelledby": titleId, children: _jsx("div", { className: featureSplitParts.inner, ref: band, "data-revealed": revealed, children: _jsxs("div", { className: featureSplitParts.split, children: [_jsxs("div", { className: featureSplitParts.content, children: [_jsx("h2", { className: featureSplitParts.title, ...arriving(0), id: titleId, children: title }), given(description) ? (_jsx("p", { className: featureSplitParts.description, ...arriving(1), children: description })) : null, link ? (_jsx("div", { className: featureSplitParts.link, ...arriving(2), children: link })) : null] }), _jsx("div", { "data-dds-parallax": "", className: featureSplitParts.media, ...arriving(3), "aria-hidden": "true", children: media })] }) }) }));
|
|
38
39
|
}
|
|
@@ -4,6 +4,7 @@ import { useId } from 'react';
|
|
|
4
4
|
import './FeatureStack.css';
|
|
5
5
|
import './feature-stack-impl.css';
|
|
6
6
|
import '../block-reveal.css';
|
|
7
|
+
import '../block-parallax.css';
|
|
7
8
|
import { given } from '../../given.js';
|
|
8
9
|
import { arriving, useReveal } from '../use-reveal.js';
|
|
9
10
|
import { featureStackDefaults, featureStackParts, } from './FeatureStack.types.js';
|
|
@@ -23,5 +24,5 @@ import { featureStackDefaults, featureStackParts, } from './FeatureStack.types.j
|
|
|
23
24
|
export function FeatureStack({ ground = featureStackDefaults.ground, title, description, link, image, }) {
|
|
24
25
|
const titleId = useId();
|
|
25
26
|
const [band, revealed] = useReveal();
|
|
26
|
-
return (_jsx("section", { className: featureStackParts.root, "data-ground": ground, "aria-labelledby": titleId, children: _jsxs("div", { className: featureStackParts.inner, ref: band, "data-revealed": revealed, children: [_jsxs("div", { className: featureStackParts.header, children: [_jsx("h2", { className: featureStackParts.title, id: titleId, ...arriving(0), children: title }), given(description) ? (_jsx("p", { className: featureStackParts.description, ...arriving(1), children: description })) : null, link ? (_jsx("div", { className: featureStackParts.link, ...arriving(2), children: link })) : null] }), _jsx("div", { className: featureStackParts.media, ...arriving(3), children: image ? (_jsx("img", { className: featureStackParts.image, src: image.src, alt: image.alt, width: image.width, height: image.height, loading: "lazy", decoding: "async" })) : null })] }) }));
|
|
27
|
+
return (_jsx("section", { className: featureStackParts.root, "data-ground": ground, "aria-labelledby": titleId, children: _jsxs("div", { className: featureStackParts.inner, ref: band, "data-revealed": revealed, children: [_jsxs("div", { className: featureStackParts.header, children: [_jsx("h2", { className: featureStackParts.title, id: titleId, ...arriving(0), children: title }), given(description) ? (_jsx("p", { className: featureStackParts.description, ...arriving(1), children: description })) : null, link ? (_jsx("div", { className: featureStackParts.link, ...arriving(2), children: link })) : null] }), _jsx("div", { "data-dds-parallax": "", className: featureStackParts.media, ...arriving(3), children: image ? (_jsx("img", { className: featureStackParts.image, src: image.src, alt: image.alt, width: image.width, height: image.height, loading: "lazy", decoding: "async" })) : null })] }) }));
|
|
27
28
|
}
|
|
@@ -29,7 +29,7 @@ export interface ImageBannerOwnProps {
|
|
|
29
29
|
action?: ReactNode;
|
|
30
30
|
/** Always. The page's photograph, through AspectRatio or as an image — people or product. Split and cover crop it to fill the space they give it. Alt text is the page's to give, empty where the photograph is decoration. */
|
|
31
31
|
media: ReactNode;
|
|
32
|
-
/** The photograph drifts against the scroll as the card passes through the view, by motion.distance.drift over the whole pass, so it reads as further away than the card — the reference design's ParallaxBanner, which this replaces.
|
|
32
|
+
/** The photograph drifts against the scroll as the card passes through the view, by motion.distance.drift over the whole pass, so it reads as further away than the card — the reference design's ParallaxBanner, which this replaces. On by default, as every block's picture drifts: pass false for a card whose photograph must hold still under its button. */
|
|
33
33
|
parallax?: boolean;
|
|
34
34
|
}
|
|
35
35
|
export type ImageBannerProps = ImageBannerOwnProps;
|
|
@@ -50,5 +50,5 @@ export declare const imageBannerDefaults: {
|
|
|
50
50
|
readonly variant: "split";
|
|
51
51
|
readonly mediaPosition: "end";
|
|
52
52
|
readonly headingLevel: "h2";
|
|
53
|
-
readonly parallax:
|
|
53
|
+
readonly parallax: true;
|
|
54
54
|
};
|
|
@@ -11,4 +11,4 @@ export const imageBannerParts = {
|
|
|
11
11
|
media: 'dds-imagebanner__media',
|
|
12
12
|
};
|
|
13
13
|
/** Generated so defaults cannot drift from the contract. */
|
|
14
|
-
export const imageBannerDefaults = { ground: 'page', variant: 'split', mediaPosition: 'end', headingLevel: 'h2', parallax:
|
|
14
|
+
export const imageBannerDefaults = { ground: 'page', variant: 'split', mediaPosition: 'end', headingLevel: 'h2', parallax: true };
|
|
@@ -4,6 +4,7 @@ import { useId } from 'react';
|
|
|
4
4
|
import './LandingHero.css';
|
|
5
5
|
import './landing-hero-impl.css';
|
|
6
6
|
import '../block-reveal.css';
|
|
7
|
+
import '../block-parallax.css';
|
|
7
8
|
import { arriving, useReveal } from '../use-reveal.js';
|
|
8
9
|
import { given } from '../../given.js';
|
|
9
10
|
import { Background } from '../../core/background/Background.js';
|
|
@@ -51,5 +52,5 @@ export function LandingHero({ eyebrow, title, headingLevel = landingHeroDefaults
|
|
|
51
52
|
/* The one inline style here, and it is a value rather than a decision: which photograph,
|
|
52
53
|
supplied by the page. Everything about how it is drawn — the cover fit and the position — is in the stylesheet, where it can be reasoned about. A background shorthand
|
|
53
54
|
would have reset those four properties every time the page passed an image. */
|
|
54
|
-
style: given(backgroundImage) ? { backgroundImage: `url(${backgroundImage})` } : undefined, children: [background === 'halftone' ? _jsx(Background, { tone: "dark", children: null }) : null, media ? (_jsx("div", { className: landingHeroParts.media, "aria-hidden": "true", children: media })) : null, _jsx("div", { className: landingHeroParts.inner, ref: band, "data-revealed": revealed, children: _jsxs("div", { className: landingHeroParts.content, children: [_jsxs("div", { className: landingHeroParts.text, children: [given(eyebrow) ? (_jsx("p", { className: landingHeroParts.eyebrow, ...arriving(0), children: eyebrow })) : null, _jsx(Title, { className: landingHeroParts.title, ...arriving(1), id: titleId, children: title }), given(description) ? (_jsx("p", { className: landingHeroParts.description, ...arriving(2), children: description })) : null] }), actions ? (_jsx("div", { className: landingHeroParts.actions, ...arriving(3), children: actions })) : null] }) })] }));
|
|
55
|
+
style: given(backgroundImage) ? { backgroundImage: `url(${backgroundImage})` } : undefined, children: [background === 'halftone' ? _jsx(Background, { tone: "dark", children: null }) : null, media ? (_jsx("div", { "data-dds-parallax": "", className: landingHeroParts.media, "aria-hidden": "true", children: media })) : null, _jsx("div", { className: landingHeroParts.inner, ref: band, "data-revealed": revealed, children: _jsxs("div", { className: landingHeroParts.content, children: [_jsxs("div", { className: landingHeroParts.text, children: [given(eyebrow) ? (_jsx("p", { className: landingHeroParts.eyebrow, ...arriving(0), children: eyebrow })) : null, _jsx(Title, { className: landingHeroParts.title, ...arriving(1), id: titleId, children: title }), given(description) ? (_jsx("p", { className: landingHeroParts.description, ...arriving(2), children: description })) : null] }), actions ? (_jsx("div", { className: landingHeroParts.actions, ...arriving(3), children: actions })) : null] }) })] }));
|
|
55
56
|
}
|
|
@@ -4,6 +4,7 @@ import { useId } from 'react';
|
|
|
4
4
|
import './ProductHero.css';
|
|
5
5
|
import './product-hero-impl.css';
|
|
6
6
|
import '../block-reveal.css';
|
|
7
|
+
import '../block-parallax.css';
|
|
7
8
|
import { arriving, useReveal } from '../use-reveal.js';
|
|
8
9
|
import { given } from '../../given.js';
|
|
9
10
|
import { productHeroDefaults, productHeroParts, } from './ProductHero.types.js';
|
|
@@ -47,5 +48,5 @@ export function ProductHero({ eyebrow, title, headingLevel = productHeroDefaults
|
|
|
47
48
|
style: given(backgroundImage) ? { backgroundImage: `url(${backgroundImage})` } : undefined, children: _jsx("div", { className: productHeroParts.inner, ref: band, "data-revealed": revealed, children: _jsxs("div", { className: productHeroParts.content, children: [_jsxs("div", { className: productHeroParts.text, children: [given(eyebrow) ? (_jsx("p", { className: productHeroParts.eyebrow, ...arriving(0), children: eyebrow })) : null, _jsx(Title, { className: productHeroParts.title, ...arriving(1), id: titleId, children: title }), given(description) ? (_jsx("p", { className: productHeroParts.description, ...arriving(2), children: description })) : null] }), actions ? (_jsx("div", { className: productHeroParts.actions, ...arriving(3), children: actions })) : null, image ? (
|
|
48
49
|
// The picture at its own shape: drawn here so the band can size it, and read for its
|
|
49
50
|
// `alt`, which the page leaves empty where the claim already names it.
|
|
50
|
-
_jsx("div", { className: productHeroParts.media, ...arriving(4), children: _jsx("img", { className: productHeroParts.image, src: image.src, alt: image.alt, width: image.width, height: image.height, decoding: "async" }) })) : media ? (_jsx("div", { className: productHeroParts.media, ...arriving(4), "aria-hidden": "true", children: media })) : null] }) }) }));
|
|
51
|
+
_jsx("div", { "data-dds-parallax": "", className: productHeroParts.media, ...arriving(4), children: _jsx("img", { className: productHeroParts.image, src: image.src, alt: image.alt, width: image.width, height: image.height, decoding: "async" }) })) : media ? (_jsx("div", { "data-dds-parallax": "", className: productHeroParts.media, ...arriving(4), "aria-hidden": "true", children: media })) : null] }) }) }));
|
|
51
52
|
}
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
|
|
39
39
|
:where([data-dds-context="marketing"]) .dds-steps .dds-steps__image {
|
|
40
40
|
display: block;
|
|
41
|
-
transition-duration: var(--motion-duration-
|
|
42
|
-
transition-timing-function: var(--motion-easing-
|
|
41
|
+
transition-duration: var(--motion-duration-turn);
|
|
42
|
+
transition-timing-function: var(--motion-easing-reveal);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
:where([data-dds-context="marketing"]) .dds-steps .dds-steps__content {
|
|
@@ -4,6 +4,7 @@ import { useId } from 'react';
|
|
|
4
4
|
import './Steps.css';
|
|
5
5
|
import './steps-impl.css';
|
|
6
6
|
import '../block-reveal.css';
|
|
7
|
+
import '../block-parallax.css';
|
|
7
8
|
import { arriving, useReveal } from '../use-reveal.js';
|
|
8
9
|
import { given } from '../../given.js';
|
|
9
10
|
import { useTurn } from './use-turn.js';
|
|
@@ -41,11 +42,11 @@ export function Steps({ ground = stepsDefaults.ground, mediaPosition = stepsDefa
|
|
|
41
42
|
// records as they are typed, and a number against nothing is a step the page did not write — so
|
|
42
43
|
// it is dropped before the count is taken, and the numbers close up rather than skipping one.
|
|
43
44
|
const shown = steps.filter((step) => given(step.title));
|
|
44
|
-
const { current, turning, playing, next, choose, section, hold } = useTurn(shown.length, autoplay);
|
|
45
|
+
const { current, going, turning, playing, next, choose, section, hold } = useTurn(shown.length, autoplay);
|
|
45
46
|
// The steps' own pictures replace the one picture only where every step has one: a turn that
|
|
46
47
|
// reached a step without a picture would show nothing beside it.
|
|
47
48
|
const pictured = shown.length > 0 && shown.every((step) => given(step.image?.src));
|
|
48
|
-
return (_jsx("section", { className: stepsParts.root, ref: section, "data-ground": ground, "data-media-position": mediaPosition, "data-autoplay": autoplay || undefined, "aria-labelledby": titleId, ...(autoplay ? hold : {}), children: _jsx("div", { className: stepsParts.inner, ref: band, "data-revealed": revealed, children: _jsxs("div", { className: stepsParts.split, children: [_jsx("div", { className: stepsParts.media, ...arriving(0), "aria-hidden": "true", children: pictured
|
|
49
|
+
return (_jsx("section", { className: stepsParts.root, ref: section, "data-ground": ground, "data-media-position": mediaPosition, "data-autoplay": autoplay || undefined, "data-going": going, "aria-labelledby": titleId, ...(autoplay ? hold : {}), children: _jsx("div", { className: stepsParts.inner, ref: band, "data-revealed": revealed, children: _jsxs("div", { className: stepsParts.split, children: [_jsx("div", { "data-dds-parallax": "", className: stepsParts.media, ...arriving(0), "aria-hidden": "true", children: pictured
|
|
49
50
|
? shown.map((step, at) => (_jsx("img", { className: stepsParts.image, src: step.image?.src, alt: "", width: step.image?.width, height: step.image?.height, "data-current": at === current, loading: at === 0 ? undefined : 'lazy' }, `${at}:${step.title}`)))
|
|
50
51
|
: media }), _jsxs("div", { className: stepsParts.content, children: [_jsx("h2", { className: stepsParts.title, ...arriving(0), id: titleId, children: title }), _jsx("ol", { className: stepsParts.list, role: "list", "data-turning": autoplay ? turning : undefined, "data-playing": autoplay ? playing : undefined, children: shown.map((step, at) => (
|
|
51
52
|
/* Keyed by the step's own name. An index would key the list by position, so
|
|
@@ -12,7 +12,7 @@ export interface StepsOwnProps {
|
|
|
12
12
|
* `end` — The steps first, for a section sitting under a split whose picture is already at the start
|
|
13
13
|
*/
|
|
14
14
|
mediaPosition?: 'start' | 'end';
|
|
15
|
-
/** Whether the steps take their turn on their own: one step is current, its rule fills over motion.duration.
|
|
15
|
+
/** Whether the steps take their turn on their own: one step is current, its rule fills over motion.duration.step, then the next takes over, and back to the first after the last. The current step's number is drawn at full strength and the rest recede; with an `image` on each step the picture changes to the current one's. Each step's title becomes a button that makes it current. Off by default, so a list nobody configured stays still. On, it holds while focus is inside the section — not under the pointer, so it keeps turning while a reader watches it — waits until the section is on screen, never moves for a reader who asked for less motion, and stops for good once the reader presses a step — the WAI carousel rule CtaBannerSlider follows: a reader who has chosen a step is reading it. That gives a reader a way to stop it, which WCAG 2.2.2 asks for, but not a visible one, so a page that needs 2.2.2 met plainly should leave it off. */
|
|
16
16
|
autoplay?: boolean;
|
|
17
17
|
/** Always. What the steps get a reader to, in the fewest words that say it — 'Get started in 3 steps' */
|
|
18
18
|
title: string;
|
|
@@ -152,7 +152,7 @@
|
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
/*
|
|
155
|
-
* The fill: the current step's rule, drawn from the start edge over motion.duration.
|
|
155
|
+
* The fill: the current step's rule, drawn from the start edge over motion.duration.step. When it
|
|
156
156
|
* ends the next step takes over — the component listens for the end, so the bar and the turn are
|
|
157
157
|
* one clock. Paused wherever the turn is held; full and still where the turn has stopped or never
|
|
158
158
|
* runs, so the current step is still marked.
|
|
@@ -175,7 +175,7 @@
|
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
:where([data-dds-context="marketing"]) .dds-steps .dds-steps__list[data-turning="true"] > .dds-steps__step[data-current="true"] > .dds-steps__progress {
|
|
178
|
-
animation: dds-steps-fill var(--motion-duration-
|
|
178
|
+
animation: dds-steps-fill var(--motion-duration-step) linear;
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
:where([data-dds-context="marketing"]) .dds-steps .dds-steps__list[data-playing="false"] > .dds-steps__step[data-current="true"] > .dds-steps__progress {
|
|
@@ -184,7 +184,20 @@
|
|
|
184
184
|
|
|
185
185
|
/*
|
|
186
186
|
* The steps' own pictures share the media cell, and the current step's is the one shown — the
|
|
187
|
-
* first while the list is still.
|
|
187
|
+
* first while the list is still.
|
|
188
|
+
*
|
|
189
|
+
* The next is uncovered over the last rather than faded into it, FeatureScroll's change. A fade put
|
|
190
|
+
* both pictures on screen at half strength at once — a double exposure that read as the two
|
|
191
|
+
* overlapping. Uncovered from an edge, every point of the square is one picture or the other at full
|
|
192
|
+
* strength: up from the foot as the turn moves on, the wrap to the first step included, and down
|
|
193
|
+
* from the head when a reader presses an earlier step.
|
|
194
|
+
*
|
|
195
|
+
* An animation rather than a transition, because an animation starts from its first frame and a
|
|
196
|
+
* transition from wherever the picture last rested. The current picture is brought to the front and
|
|
197
|
+
* uncovered over motion.duration.turn — the figure-to-figure step a turning set already takes — and
|
|
198
|
+
* the one it replaces stays beneath, whole, for that long, then goes: a zero-length transition
|
|
199
|
+
* delayed by the same role, so the two cannot drift into a gap. Every other picture is already gone.
|
|
200
|
+
* No `data-going` before the first change, so the picture a reader opens on is simply there.
|
|
188
201
|
*/
|
|
189
202
|
:where([data-dds-context="marketing"]) .dds-steps .dds-steps__media:has(> .dds-steps__image) {
|
|
190
203
|
display: grid;
|
|
@@ -193,11 +206,66 @@
|
|
|
193
206
|
:where([data-dds-context="marketing"]) .dds-steps .dds-steps__media > .dds-steps__image {
|
|
194
207
|
grid-area: 1 / 1;
|
|
195
208
|
opacity: 0;
|
|
196
|
-
transition
|
|
209
|
+
transition: opacity 0s linear var(--motion-duration-turn);
|
|
197
210
|
}
|
|
198
211
|
|
|
199
212
|
:where([data-dds-context="marketing"]) .dds-steps .dds-steps__media > .dds-steps__image[data-current="true"] {
|
|
200
213
|
opacity: 1;
|
|
214
|
+
transition-delay: 0s;
|
|
215
|
+
z-index: 1;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
:where([data-dds-context="marketing"]) .dds-steps[data-going="on"] .dds-steps__media > .dds-steps__image[data-current="true"] {
|
|
219
|
+
animation: dds-steps-from-foot var(--motion-duration-turn) var(--motion-easing-reveal);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
:where([data-dds-context="marketing"]) .dds-steps[data-going="back"] .dds-steps__media > .dds-steps__image[data-current="true"] {
|
|
223
|
+
animation: dds-steps-from-head var(--motion-duration-turn) var(--motion-easing-reveal);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/*
|
|
227
|
+
* The wipe and the parallax together. The current picture's wipe is an `animation`, and so is the
|
|
228
|
+
* drift block-parallax.css gives every picture in the frame — one shorthand would replace the other,
|
|
229
|
+
* and the current picture, the only one a reader sees, would stop drifting the moment it arrived. So
|
|
230
|
+
* both are listed here: the wipe on the document's clock, the drift on the frame's view timeline,
|
|
231
|
+
* where the browser can drive one and the reader has not asked for less motion.
|
|
232
|
+
*/
|
|
233
|
+
@supports (animation-timeline: view()) {
|
|
234
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
235
|
+
:where([data-dds-context="marketing"]) .dds-steps[data-going="on"] .dds-steps__media > .dds-steps__image[data-current="true"] {
|
|
236
|
+
animation:
|
|
237
|
+
dds-steps-from-foot var(--motion-duration-turn) var(--motion-easing-reveal),
|
|
238
|
+
dds-parallax linear both;
|
|
239
|
+
animation-range: normal, cover;
|
|
240
|
+
animation-timeline: auto, --dds-parallax;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
:where([data-dds-context="marketing"]) .dds-steps[data-going="back"] .dds-steps__media > .dds-steps__image[data-current="true"] {
|
|
244
|
+
animation:
|
|
245
|
+
dds-steps-from-head var(--motion-duration-turn) var(--motion-easing-reveal),
|
|
246
|
+
dds-parallax linear both;
|
|
247
|
+
animation-range: normal, cover;
|
|
248
|
+
animation-timeline: auto, --dds-parallax;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
@keyframes dds-steps-from-foot {
|
|
254
|
+
from {
|
|
255
|
+
clip-path: inset(100% 0 0 0);
|
|
256
|
+
}
|
|
257
|
+
to {
|
|
258
|
+
clip-path: inset(0);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
@keyframes dds-steps-from-head {
|
|
263
|
+
from {
|
|
264
|
+
clip-path: inset(0 0 100% 0);
|
|
265
|
+
}
|
|
266
|
+
to {
|
|
267
|
+
clip-path: inset(0);
|
|
268
|
+
}
|
|
201
269
|
}
|
|
202
270
|
|
|
203
271
|
/*
|
|
@@ -213,4 +281,8 @@
|
|
|
213
281
|
:where([data-dds-context="marketing"]) .dds-steps[data-autoplay] .dds-steps__number {
|
|
214
282
|
transition: none;
|
|
215
283
|
}
|
|
284
|
+
|
|
285
|
+
:where([data-dds-context="marketing"]) .dds-steps .dds-steps__media > .dds-steps__image[data-current="true"] {
|
|
286
|
+
animation: none;
|
|
287
|
+
}
|
|
216
288
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { type FocusEvent
|
|
1
|
+
import { type FocusEvent } from 'react';
|
|
2
2
|
/**
|
|
3
3
|
* The steps taking their turn under `autoplay`: which step is current, and whether the turn is
|
|
4
4
|
* running.
|
|
5
5
|
*
|
|
6
|
-
* The timing is not here. The current step's rule fills over motion.duration.
|
|
6
|
+
* The timing is not here. The current step's rule fills over motion.duration.step as a CSS
|
|
7
7
|
* animation, and the turn moves on when that animation ends (`next`), so the bar a reader watches
|
|
8
8
|
* and the step changing are one clock rather than two that drift. Holding the turn is pausing that
|
|
9
9
|
* animation, which is what `playing` is for: the stylesheet pauses the fill wherever it is false.
|
|
@@ -12,21 +12,21 @@ import { type FocusEvent, type PointerEvent } from 'react';
|
|
|
12
12
|
* - `autoplay` is on and there are two steps or more;
|
|
13
13
|
* - the reader has not asked for less motion — asked live, so turning it on mid-visit stops it;
|
|
14
14
|
* - the section is on screen. A turn nobody can see does not run, and starts again where it was;
|
|
15
|
-
* - nothing holds it:
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
15
|
+
* - nothing holds it: focus inside the section holds it for as long as it lasts, and pressing a
|
|
16
|
+
* step stops it for good — a reader who has chosen a step is reading it. The keyboard's way and
|
|
17
|
+
* the pointer's and a touch screen's, which WCAG 2.2.2 asks for of anything that moves on its own.
|
|
18
|
+
* A pointer merely resting over the section does not hold it: the list is meant to be watched as
|
|
19
|
+
* it turns, and pausing whenever the pointer crossed it read as the list having stopped.
|
|
19
20
|
*/
|
|
20
21
|
export declare function useTurn(count: number, autoplay: boolean): {
|
|
21
22
|
current: number;
|
|
23
|
+
going: "on" | "back" | undefined;
|
|
22
24
|
turning: boolean;
|
|
23
25
|
playing: boolean;
|
|
24
26
|
next: () => void;
|
|
25
27
|
choose: (at: number) => void;
|
|
26
28
|
section: import("react").RefObject<HTMLElement | null>;
|
|
27
29
|
hold: {
|
|
28
|
-
onPointerEnter: (event: PointerEvent<HTMLElement>) => void;
|
|
29
|
-
onPointerLeave: (event: PointerEvent<HTMLElement>) => void;
|
|
30
30
|
onFocus: () => void;
|
|
31
31
|
onBlur: (event: FocusEvent<HTMLElement>) => void;
|
|
32
32
|
};
|
|
@@ -4,7 +4,7 @@ import { useCallback, useEffect, useRef, useState } from 'react';
|
|
|
4
4
|
* The steps taking their turn under `autoplay`: which step is current, and whether the turn is
|
|
5
5
|
* running.
|
|
6
6
|
*
|
|
7
|
-
* The timing is not here. The current step's rule fills over motion.duration.
|
|
7
|
+
* The timing is not here. The current step's rule fills over motion.duration.step as a CSS
|
|
8
8
|
* animation, and the turn moves on when that animation ends (`next`), so the bar a reader watches
|
|
9
9
|
* and the step changing are one clock rather than two that drift. Holding the turn is pausing that
|
|
10
10
|
* animation, which is what `playing` is for: the stylesheet pauses the fill wherever it is false.
|
|
@@ -13,13 +13,21 @@ import { useCallback, useEffect, useRef, useState } from 'react';
|
|
|
13
13
|
* - `autoplay` is on and there are two steps or more;
|
|
14
14
|
* - the reader has not asked for less motion — asked live, so turning it on mid-visit stops it;
|
|
15
15
|
* - the section is on screen. A turn nobody can see does not run, and starts again where it was;
|
|
16
|
-
* - nothing holds it:
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
16
|
+
* - nothing holds it: focus inside the section holds it for as long as it lasts, and pressing a
|
|
17
|
+
* step stops it for good — a reader who has chosen a step is reading it. The keyboard's way and
|
|
18
|
+
* the pointer's and a touch screen's, which WCAG 2.2.2 asks for of anything that moves on its own.
|
|
19
|
+
* A pointer merely resting over the section does not hold it: the list is meant to be watched as
|
|
20
|
+
* it turns, and pausing whenever the pointer crossed it read as the list having stopped.
|
|
20
21
|
*/
|
|
21
22
|
export function useTurn(count, autoplay) {
|
|
22
23
|
const [current, setCurrent] = useState(0);
|
|
24
|
+
/*
|
|
25
|
+
Which way the last change went, for the picture to be uncovered from the matching edge: on, from
|
|
26
|
+
the foot, for every turn — the wrap from the last step to the first included, because the turn
|
|
27
|
+
still moves on — and back, from the head, when a reader presses an earlier step. Absent until the
|
|
28
|
+
first change, so the picture a reader opens on is simply there.
|
|
29
|
+
*/
|
|
30
|
+
const [going, setGoing] = useState(undefined);
|
|
23
31
|
const [seen, setSeen] = useState(false);
|
|
24
32
|
const [still, setStill] = useState(false);
|
|
25
33
|
const [held, setHeld] = useState(false);
|
|
@@ -46,24 +54,19 @@ export function useTurn(count, autoplay) {
|
|
|
46
54
|
}, [autoplay]);
|
|
47
55
|
/** The current step's fill has run: the next step takes over, and the first after the last. */
|
|
48
56
|
const next = useCallback(() => {
|
|
49
|
-
if (count > 0)
|
|
57
|
+
if (count > 0) {
|
|
58
|
+
setGoing('on');
|
|
50
59
|
setCurrent((at) => (at + 1) % count);
|
|
60
|
+
}
|
|
51
61
|
}, [count]);
|
|
52
62
|
/** A step pressed: it is current, and the turn stops for good. */
|
|
53
63
|
const choose = useCallback((at) => {
|
|
64
|
+
setGoing(at >= current ? 'on' : 'back');
|
|
54
65
|
setCurrent(at);
|
|
55
66
|
setStopped(true);
|
|
56
|
-
}, []);
|
|
67
|
+
}, [current]);
|
|
57
68
|
/** Spread on the section: what holds the turn. */
|
|
58
69
|
const hold = {
|
|
59
|
-
onPointerEnter: (event) => {
|
|
60
|
-
if (event.pointerType === 'mouse')
|
|
61
|
-
setHeld(true);
|
|
62
|
-
},
|
|
63
|
-
onPointerLeave: (event) => {
|
|
64
|
-
if (event.pointerType === 'mouse')
|
|
65
|
-
setHeld(false);
|
|
66
|
-
},
|
|
67
70
|
onFocus: () => setHeld(true),
|
|
68
71
|
onBlur: (event) => {
|
|
69
72
|
if (!event.currentTarget.contains(event.relatedTarget))
|
|
@@ -71,5 +74,5 @@ export function useTurn(count, autoplay) {
|
|
|
71
74
|
},
|
|
72
75
|
};
|
|
73
76
|
// A list that shrank under the current step falls back to its first.
|
|
74
|
-
return { current: current < count ? current : 0, turning, playing, next, choose, section, hold };
|
|
77
|
+
return { current: current < count ? current : 0, going, turning, playing, next, choose, section, hold };
|
|
75
78
|
}
|
|
@@ -4,6 +4,7 @@ import { useId, useState } from 'react';
|
|
|
4
4
|
import './TeamGrid.css';
|
|
5
5
|
import './team-grid-impl.css';
|
|
6
6
|
import '../block-reveal.css';
|
|
7
|
+
import '../block-parallax.css';
|
|
7
8
|
import { given } from '../../given.js';
|
|
8
9
|
import { Button } from '../../core/button/Button.js';
|
|
9
10
|
import { Link } from '../../core/link/Link.js';
|
|
@@ -69,6 +70,6 @@ export function TeamGrid({ ground = teamGridDefaults.ground, title, description,
|
|
|
69
70
|
style: { '--dds-teamgrid-extra-at': at - shown },
|
|
70
71
|
}
|
|
71
72
|
: arriving(Math.min(FIRST_PERSON + at, LAST_STEP));
|
|
72
|
-
return (_jsxs("li", { className: teamGridParts.item, hidden: extra && !expanded, ...motion, children: [_jsx("div", { className: teamGridParts.media, children: _jsx(AspectRatio, { ratio: "square", children: _jsx("img", { src: person.image.src, alt: person.image.alt, loading: "lazy", decoding: "async" }) }) }), _jsxs("div", { className: teamGridParts.text, children: [_jsx("h3", { className: teamGridParts.name, children: person.name }), _jsx("p", { className: teamGridParts.role, children: person.role }), person.profile !== undefined ? (_jsx("div", { className: teamGridParts.profile, children: _jsx(Link, { href: person.profile.href, external: true, tone: inverse ? 'inherit' : 'brand', "aria-label": `${profileName(person)} ${externalLabel}`, children: person.profile.label }) })) : null] })] }, `${person.name}-${at}`));
|
|
73
|
+
return (_jsxs("li", { className: teamGridParts.item, hidden: extra && !expanded, ...motion, children: [_jsx("div", { "data-dds-parallax": "", className: teamGridParts.media, children: _jsx(AspectRatio, { ratio: "square", children: _jsx("img", { src: person.image.src, alt: person.image.alt, loading: "lazy", decoding: "async" }) }) }), _jsxs("div", { className: teamGridParts.text, children: [_jsx("h3", { className: teamGridParts.name, children: person.name }), _jsx("p", { className: teamGridParts.role, children: person.role }), person.profile !== undefined ? (_jsx("div", { className: teamGridParts.profile, children: _jsx(Link, { href: person.profile.href, external: true, tone: inverse ? 'inherit' : 'brand', "aria-label": `${profileName(person)} ${externalLabel}`, children: person.profile.label }) })) : null] })] }, `${person.name}-${at}`));
|
|
73
74
|
}) }), longer ? (_jsx("div", { className: teamGridParts.actions, ...arriving(Math.min(FIRST_PERSON + shown, LAST_STEP + 1)), children: _jsx(Button, { variant: "secondary", size: "md", tone: inverse ? 'inverse' : 'default', "aria-expanded": expanded, "aria-controls": listId, onClick: () => setExpanded((open) => !open), children: expanded ? collapseLabel : expandLabel }) })) : null] })] }) }));
|
|
74
75
|
}
|
package/dist/marketing.css
CHANGED
|
@@ -238,6 +238,7 @@
|
|
|
238
238
|
--motion-duration-slow: 300ms;
|
|
239
239
|
--motion-duration-spinner: 500ms;
|
|
240
240
|
--motion-duration-stagger: 100ms;
|
|
241
|
+
--motion-duration-step: 8000ms;
|
|
241
242
|
--motion-duration-ticker: 4000ms;
|
|
242
243
|
--motion-duration-trail: 500ms;
|
|
243
244
|
--motion-duration-turn: 700ms;
|
|
@@ -252,9 +253,12 @@
|
|
|
252
253
|
--motion-scale-enter: 0.96;
|
|
253
254
|
--motion-scale-hover: 1.02;
|
|
254
255
|
--motion-scale-media-hover: 1.04;
|
|
256
|
+
--motion-scale-parallax: 1.1;
|
|
255
257
|
--motion-scale-pile-covered: 0.96;
|
|
256
258
|
--motion-translate-enter: 0 100%;
|
|
257
259
|
--motion-translate-hover: 0 -4px;
|
|
260
|
+
--motion-translate-parallax-from: 0 -4%;
|
|
261
|
+
--motion-translate-parallax-to: 0 4%;
|
|
258
262
|
--motion-translate-reveal: 0 24px;
|
|
259
263
|
--opacity-disabled: 0.22;
|
|
260
264
|
--opacity-dot-current: 1;
|
|
@@ -591,6 +595,7 @@
|
|
|
591
595
|
--motion-duration-slow: 300ms;
|
|
592
596
|
--motion-duration-spinner: 500ms;
|
|
593
597
|
--motion-duration-stagger: 100ms;
|
|
598
|
+
--motion-duration-step: 8000ms;
|
|
594
599
|
--motion-duration-ticker: 4000ms;
|
|
595
600
|
--motion-duration-trail: 500ms;
|
|
596
601
|
--motion-duration-turn: 700ms;
|
|
@@ -605,9 +610,12 @@
|
|
|
605
610
|
--motion-scale-enter: 0.96;
|
|
606
611
|
--motion-scale-hover: 1.02;
|
|
607
612
|
--motion-scale-media-hover: 1.04;
|
|
613
|
+
--motion-scale-parallax: 1.1;
|
|
608
614
|
--motion-scale-pile-covered: 0.96;
|
|
609
615
|
--motion-translate-enter: 0 100%;
|
|
610
616
|
--motion-translate-hover: 0 -4px;
|
|
617
|
+
--motion-translate-parallax-from: 0 -4%;
|
|
618
|
+
--motion-translate-parallax-to: 0 4%;
|
|
611
619
|
--motion-translate-reveal: 0 24px;
|
|
612
620
|
--opacity-disabled: 0.22;
|
|
613
621
|
--opacity-dot-current: 1;
|