@deriv-com/dds-marketing 1.0.1 → 1.0.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/background.md +371 -0
- package/.llm/components/ctabanner.md +25 -0
- package/.llm/components/ctabannerslider.md +21 -0
- package/.llm/components/illustrationcard.md +236 -0
- package/.llm/components/illustrationscene.md +233 -0
- package/.llm/components/landinghero.md +24 -0
- package/.llm/components/pageheader.md +21 -0
- package/.llm/digest/background.md +52 -0
- package/.llm/digest/ctabanner.md +4 -0
- package/.llm/digest/ctabannerslider.md +3 -0
- package/.llm/digest/illustrationcard.md +46 -0
- package/.llm/digest/illustrationscene.md +51 -0
- package/.llm/digest/landinghero.md +7 -0
- package/.llm/digest/pageheader.md +3 -0
- package/.llm/index.md +7 -0
- package/.llm/tokens.md +11 -0
- package/AGENTS.md +3 -0
- package/dist/components/blocks/cta-banner/CtaBanner.d.ts +1 -1
- package/dist/components/blocks/cta-banner/CtaBanner.js +3 -2
- package/dist/components/blocks/cta-banner/CtaBanner.types.d.ts +5 -0
- package/dist/components/blocks/cta-banner/cta-banner-impl.css +8 -0
- package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.d.ts +1 -1
- package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.js +5 -4
- package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.types.d.ts +4 -0
- package/dist/components/blocks/cta-banner-slider/cta-banner-slider-impl.css +8 -0
- package/dist/components/blocks/landing-hero/LandingHero.d.ts +1 -1
- package/dist/components/blocks/landing-hero/LandingHero.js +4 -3
- package/dist/components/blocks/landing-hero/LandingHero.types.d.ts +7 -3
- package/dist/components/blocks/page-header/PageHeader.d.ts +1 -1
- package/dist/components/blocks/page-header/PageHeader.js +3 -2
- package/dist/components/blocks/page-header/PageHeader.types.d.ts +4 -0
- package/dist/components/blocks/page-header/page-header-impl.css +8 -0
- package/dist/components/core/background/Background.css +69 -0
- package/dist/components/core/background/Background.d.ts +21 -0
- package/dist/components/core/background/Background.js +36 -0
- package/dist/components/core/background/Background.types.d.ts +36 -0
- package/dist/components/core/background/Background.types.js +9 -0
- package/dist/components/core/background/background-impl.css +427 -0
- package/dist/components/core/background/halftone.d.ts +123 -0
- package/dist/components/core/background/halftone.js +682 -0
- package/dist/components/core/background/use-halftone.d.ts +15 -0
- package/dist/components/core/background/use-halftone.js +27 -0
- package/dist/components/core/background/use-offscreen.d.ts +10 -0
- package/dist/components/core/background/use-offscreen.js +30 -0
- package/dist/components/core/background/use-spotlight.d.ts +13 -0
- package/dist/components/core/background/use-spotlight.js +65 -0
- package/dist/components/core/illustration-card/IllustrationCard.css +40 -0
- package/dist/components/core/illustration-card/IllustrationCard.d.ts +22 -0
- package/dist/components/core/illustration-card/IllustrationCard.js +82 -0
- package/dist/components/core/illustration-card/IllustrationCard.types.d.ts +23 -0
- package/dist/components/core/illustration-card/IllustrationCard.types.js +8 -0
- package/dist/components/core/illustration-card/illustration-card-impl.css +27 -0
- package/dist/components/core/illustration-scene/IllustrationScene.css +44 -0
- package/dist/components/core/illustration-scene/IllustrationScene.d.ts +14 -0
- package/dist/components/core/illustration-scene/IllustrationScene.js +16 -0
- package/dist/components/core/illustration-scene/IllustrationScene.types.d.ts +33 -0
- package/dist/components/core/illustration-scene/IllustrationScene.types.js +8 -0
- package/dist/components/core/illustration-scene/illustration-scene-impl.css +40 -0
- package/dist/components/index.d.ts +6 -0
- package/dist/components/index.js +6 -0
- package/dist/marketing.css +24 -0
- package/package.json +1 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* A halftone mounted on a band: the band to observe and listen on, and the canvas to draw in.
|
|
4
|
+
*
|
|
5
|
+
* Shared by Background and by the blocks that can stand on the same pattern, so there is one
|
|
6
|
+
* painter and one set of rules about when it draws. Returns the two refs to place; `enabled` false
|
|
7
|
+
* mounts nothing, which is a block without the option asked for.
|
|
8
|
+
*/
|
|
9
|
+
export declare function useHalftone<Band extends HTMLElement>(enabled: boolean, interactive: boolean,
|
|
10
|
+
/** Whether the glow drifts on its own while nothing is happening. */
|
|
11
|
+
animated: boolean,
|
|
12
|
+
/** Anything whose change should re-read the tokens — a tone that changes the ink. */
|
|
13
|
+
key?: string,
|
|
14
|
+
/** Listen for the pointer on the band's parent rather than the band: a Background used as a layer. */
|
|
15
|
+
onParent?: boolean): [RefObject<Band | null>, RefObject<HTMLCanvasElement | null>];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
|
+
import { halftone } from './halftone.js';
|
|
4
|
+
/**
|
|
5
|
+
* A halftone mounted on a band: the band to observe and listen on, and the canvas to draw in.
|
|
6
|
+
*
|
|
7
|
+
* Shared by Background and by the blocks that can stand on the same pattern, so there is one
|
|
8
|
+
* painter and one set of rules about when it draws. Returns the two refs to place; `enabled` false
|
|
9
|
+
* mounts nothing, which is a block without the option asked for.
|
|
10
|
+
*/
|
|
11
|
+
export function useHalftone(enabled, interactive,
|
|
12
|
+
/** Whether the glow drifts on its own while nothing is happening. */
|
|
13
|
+
animated,
|
|
14
|
+
/** Anything whose change should re-read the tokens — a tone that changes the ink. */
|
|
15
|
+
key,
|
|
16
|
+
/** Listen for the pointer on the band's parent rather than the band: a Background used as a layer. */
|
|
17
|
+
onParent = false) {
|
|
18
|
+
const band = useRef(null);
|
|
19
|
+
const canvas = useRef(null);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (!enabled || band.current === null || canvas.current === null)
|
|
22
|
+
return undefined;
|
|
23
|
+
const host = onParent ? (band.current.parentElement ?? undefined) : undefined;
|
|
24
|
+
return halftone(band.current, canvas.current, { interactive, animated, host }).destroy;
|
|
25
|
+
}, [enabled, interactive, animated, key, onParent]);
|
|
26
|
+
return [band, canvas];
|
|
27
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Whether a band is off screen, written onto it as `data-offscreen` so the stylesheet can pause
|
|
4
|
+
* the motion it runs at rest. A pattern the stylesheet animates repaints the band each frame, and
|
|
5
|
+
* nobody sees a band move that is not on screen; the halftone pauses itself the same way.
|
|
6
|
+
*
|
|
7
|
+
* `enabled` false observes nothing — a pattern that does not move at rest, or the halftone, whose
|
|
8
|
+
* painter has its own observer.
|
|
9
|
+
*/
|
|
10
|
+
export declare function useOffscreen(band: RefObject<HTMLElement | null>, enabled: boolean): void;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useEffect } from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* Whether a band is off screen, written onto it as `data-offscreen` so the stylesheet can pause
|
|
5
|
+
* the motion it runs at rest. A pattern the stylesheet animates repaints the band each frame, and
|
|
6
|
+
* nobody sees a band move that is not on screen; the halftone pauses itself the same way.
|
|
7
|
+
*
|
|
8
|
+
* `enabled` false observes nothing — a pattern that does not move at rest, or the halftone, whose
|
|
9
|
+
* painter has its own observer.
|
|
10
|
+
*/
|
|
11
|
+
export function useOffscreen(band, enabled) {
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const root = band.current;
|
|
14
|
+
if (!enabled || root === null || typeof IntersectionObserver === 'undefined')
|
|
15
|
+
return undefined;
|
|
16
|
+
const sight = new IntersectionObserver(([entry]) => {
|
|
17
|
+
if (entry === undefined)
|
|
18
|
+
return;
|
|
19
|
+
if (entry.isIntersecting)
|
|
20
|
+
delete root.dataset['offscreen'];
|
|
21
|
+
else
|
|
22
|
+
root.dataset['offscreen'] = 'true';
|
|
23
|
+
});
|
|
24
|
+
sight.observe(root);
|
|
25
|
+
return () => {
|
|
26
|
+
sight.disconnect();
|
|
27
|
+
delete root.dataset['offscreen'];
|
|
28
|
+
};
|
|
29
|
+
}, [band, enabled]);
|
|
30
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* The pointer's light on a pattern the stylesheet paints: where the pointer is, written onto the
|
|
4
|
+
* band as two custom properties the stylesheet centres its light on — a brighter copy of the grid
|
|
5
|
+
* or the rings, the aurora's trailing pool — and whether it is over the band.
|
|
6
|
+
*
|
|
7
|
+
* Events only record the pointer; the position is written once a frame, so a fast mouse costs one
|
|
8
|
+
* style write a frame rather than one per event. Nothing runs while the pointer is elsewhere. A
|
|
9
|
+
* touch has no hover to follow, so it lights nothing, and `enabled` false listens for nothing.
|
|
10
|
+
*/
|
|
11
|
+
export declare function useSpotlight(band: RefObject<HTMLElement | null>, enabled: boolean,
|
|
12
|
+
/** Listen on the band's parent: a Background used as a layer under a block's content. */
|
|
13
|
+
onParent?: boolean): void;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useEffect } from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* The pointer's light on a pattern the stylesheet paints: where the pointer is, written onto the
|
|
5
|
+
* band as two custom properties the stylesheet centres its light on — a brighter copy of the grid
|
|
6
|
+
* or the rings, the aurora's trailing pool — and whether it is over the band.
|
|
7
|
+
*
|
|
8
|
+
* Events only record the pointer; the position is written once a frame, so a fast mouse costs one
|
|
9
|
+
* style write a frame rather than one per event. Nothing runs while the pointer is elsewhere. A
|
|
10
|
+
* touch has no hover to follow, so it lights nothing, and `enabled` false listens for nothing.
|
|
11
|
+
*/
|
|
12
|
+
export function useSpotlight(band, enabled,
|
|
13
|
+
/** Listen on the band's parent: a Background used as a layer under a block's content. */
|
|
14
|
+
onParent = false) {
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
const root = band.current;
|
|
17
|
+
if (!enabled || root === null)
|
|
18
|
+
return undefined;
|
|
19
|
+
// A layer takes no pointer events and the content covers it, so the pointer is listened for on
|
|
20
|
+
// the band around it. The layer fills that band, so positions measured against it agree.
|
|
21
|
+
const host = onParent ? (root.parentElement ?? root) : root;
|
|
22
|
+
let frame = 0;
|
|
23
|
+
let clientX = 0;
|
|
24
|
+
let clientY = 0;
|
|
25
|
+
const write = () => {
|
|
26
|
+
frame = 0;
|
|
27
|
+
const box = root.getBoundingClientRect();
|
|
28
|
+
root.style.setProperty('--dds-background-spot-x', `${clientX - box.left}px`);
|
|
29
|
+
root.style.setProperty('--dds-background-spot-y', `${clientY - box.top}px`);
|
|
30
|
+
};
|
|
31
|
+
const move = (event) => {
|
|
32
|
+
if (event.pointerType === 'touch')
|
|
33
|
+
return;
|
|
34
|
+
clientX = event.clientX;
|
|
35
|
+
clientY = event.clientY;
|
|
36
|
+
if (root.dataset['spotlit'] !== 'true') {
|
|
37
|
+
// Arriving: place the light under the pointer before lighting it, and let the browser
|
|
38
|
+
// take that position in — reading the aurora pool's computed transform does — so what
|
|
39
|
+
// fades in is already where the pointer is, rather than easing over from where it last
|
|
40
|
+
// was. Unlit, the pool does not trail, so the move is instant.
|
|
41
|
+
if (frame !== 0)
|
|
42
|
+
cancelAnimationFrame(frame);
|
|
43
|
+
frame = 0;
|
|
44
|
+
write();
|
|
45
|
+
void getComputedStyle(root, '::after').transform;
|
|
46
|
+
root.dataset['spotlit'] = 'true';
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (frame === 0)
|
|
50
|
+
frame = requestAnimationFrame(write);
|
|
51
|
+
};
|
|
52
|
+
const leave = () => {
|
|
53
|
+
delete root.dataset['spotlit'];
|
|
54
|
+
};
|
|
55
|
+
host.addEventListener('pointermove', move, { passive: true });
|
|
56
|
+
host.addEventListener('pointerleave', leave, { passive: true });
|
|
57
|
+
return () => {
|
|
58
|
+
host.removeEventListener('pointermove', move);
|
|
59
|
+
host.removeEventListener('pointerleave', leave);
|
|
60
|
+
if (frame !== 0)
|
|
61
|
+
cancelAnimationFrame(frame);
|
|
62
|
+
delete root.dataset['spotlit'];
|
|
63
|
+
};
|
|
64
|
+
}, [band, enabled, onParent]);
|
|
65
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/* GENERATED from illustrationcard.contract.json. Do not edit. */
|
|
2
|
+
|
|
3
|
+
:where([data-dds-context="marketing"]) .dds-illustrationcard {
|
|
4
|
+
border-style: solid;
|
|
5
|
+
box-sizing: border-box;
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
inline-size: 100%;
|
|
9
|
+
overflow: hidden;
|
|
10
|
+
text-align: start;
|
|
11
|
+
background-color: var(--color-bg-surface-glass);
|
|
12
|
+
border-color: var(--color-border-subtle);
|
|
13
|
+
border-radius: var(--radius-surface);
|
|
14
|
+
border-width: var(--border-width-hairline);
|
|
15
|
+
color: var(--color-fg-default);
|
|
16
|
+
padding-block: var(--space-inset-card);
|
|
17
|
+
padding-inline: var(--space-inset-card);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
:where([data-dds-context="marketing"]) .dds-illustrationcard .dds-illustrationcard__content {
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
flex-shrink: 0;
|
|
24
|
+
min-block-size: 0;
|
|
25
|
+
user-select: none;
|
|
26
|
+
gap: var(--space-gap-sm);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
:where([data-dds-context="marketing"]) .dds-illustrationcard[data-ratio="landscape"] {
|
|
30
|
+
aspect-ratio: var(--aspect-landscape);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
:where([data-dds-context="marketing"]) .dds-illustrationcard[data-ratio="square"] {
|
|
34
|
+
aspect-ratio: var(--aspect-square);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
:where([data-dds-context="marketing"]) .dds-illustrationcard[data-ratio="upright"] {
|
|
38
|
+
aspect-ratio: var(--aspect-upright);
|
|
39
|
+
}
|
|
40
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import './IllustrationCard.css';
|
|
2
|
+
import './illustration-card-impl.css';
|
|
3
|
+
import { type IllustrationCardProps } from './IllustrationCard.types.js';
|
|
4
|
+
export type { IllustrationCardProps };
|
|
5
|
+
/**
|
|
6
|
+
* A pane of glass holding a picture of the product, in one of three shapes.
|
|
7
|
+
*
|
|
8
|
+
* The shape and the surface are this component's and nobody else's — that is the whole reason it
|
|
9
|
+
* exists. Every picture of this kind across a site is four by three, square or three by four, and is
|
|
10
|
+
* the same glass, because those are the two things each page would otherwise decide for itself and
|
|
11
|
+
* decide differently. What the pane shows is the page's.
|
|
12
|
+
*
|
|
13
|
+
* Everything inside is illustrative. The pane is one image to assistive technology, described by
|
|
14
|
+
* `label`, and its contents are `inert`: real components draw it, so it looks exactly like the
|
|
15
|
+
* product, and none of them can be focused, pressed or typed into.
|
|
16
|
+
*
|
|
17
|
+
* And it scales like a picture. The UI is laid out at its own size and, where the pane is too small
|
|
18
|
+
* to hold it, scaled down as a whole to fit — never up — so a pane on a phone shows the same picture
|
|
19
|
+
* smaller rather than text overlapping or cut off. The scale is worked out when the pane changes
|
|
20
|
+
* size and at no other time.
|
|
21
|
+
*/
|
|
22
|
+
export declare function IllustrationCard({ ratio, label, children, ...rest }: IllustrationCardProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useLayoutEffect, useRef } from 'react';
|
|
4
|
+
import './IllustrationCard.css';
|
|
5
|
+
import './illustration-card-impl.css';
|
|
6
|
+
import { illustrationCardDefaults, illustrationCardParts, } from './IllustrationCard.types.js';
|
|
7
|
+
/**
|
|
8
|
+
* A pane of glass holding a picture of the product, in one of three shapes.
|
|
9
|
+
*
|
|
10
|
+
* The shape and the surface are this component's and nobody else's — that is the whole reason it
|
|
11
|
+
* exists. Every picture of this kind across a site is four by three, square or three by four, and is
|
|
12
|
+
* the same glass, because those are the two things each page would otherwise decide for itself and
|
|
13
|
+
* decide differently. What the pane shows is the page's.
|
|
14
|
+
*
|
|
15
|
+
* Everything inside is illustrative. The pane is one image to assistive technology, described by
|
|
16
|
+
* `label`, and its contents are `inert`: real components draw it, so it looks exactly like the
|
|
17
|
+
* product, and none of them can be focused, pressed or typed into.
|
|
18
|
+
*
|
|
19
|
+
* And it scales like a picture. The UI is laid out at its own size and, where the pane is too small
|
|
20
|
+
* to hold it, scaled down as a whole to fit — never up — so a pane on a phone shows the same picture
|
|
21
|
+
* smaller rather than text overlapping or cut off. The scale is worked out when the pane changes
|
|
22
|
+
* size and at no other time.
|
|
23
|
+
*/
|
|
24
|
+
export function IllustrationCard({ ratio = illustrationCardDefaults.ratio, label, children, ...rest }) {
|
|
25
|
+
const pane = useRef(null);
|
|
26
|
+
const content = useRef(null);
|
|
27
|
+
useLayoutEffect(() => {
|
|
28
|
+
const root = pane.current;
|
|
29
|
+
const inner = content.current;
|
|
30
|
+
if (root === null || inner === null || typeof ResizeObserver === 'undefined')
|
|
31
|
+
return undefined;
|
|
32
|
+
const fit = () => {
|
|
33
|
+
const style = getComputedStyle(root);
|
|
34
|
+
const across = root.clientWidth - Number.parseFloat(style.paddingInlineStart) - Number.parseFloat(style.paddingInlineEnd);
|
|
35
|
+
const down = root.clientHeight - Number.parseFloat(style.paddingBlockStart) - Number.parseFloat(style.paddingBlockEnd);
|
|
36
|
+
if (across <= 0 || down <= 0)
|
|
37
|
+
return;
|
|
38
|
+
// Laid out unscaled at the pane's own width, to see how much room the UI wants there — down,
|
|
39
|
+
// and across, since a line that cannot wrap (a large figure) overflows sideways.
|
|
40
|
+
inner.style.transform = '';
|
|
41
|
+
inner.style.inlineSize = `${across}px`;
|
|
42
|
+
const tall = inner.scrollHeight;
|
|
43
|
+
const wide = inner.scrollWidth;
|
|
44
|
+
const scale = Math.min(1, down / tall, across / wide);
|
|
45
|
+
if (scale >= 1) {
|
|
46
|
+
inner.style.inlineSize = '';
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
// Scaled, the UI is narrower than the pane too; laying it out wider by the same factor gives
|
|
50
|
+
// the width back, so the picture fills the pane across as well as down. Wider, it can only
|
|
51
|
+
// wrap less and so be shorter, which keeps it inside the pane.
|
|
52
|
+
inner.style.inlineSize = `${across / scale}px`;
|
|
53
|
+
inner.style.transformOrigin = style.direction === 'rtl' ? 'top right' : 'top left';
|
|
54
|
+
inner.style.transform = `scale(${scale})`;
|
|
55
|
+
};
|
|
56
|
+
// Fitted again only when something can have changed: the pane's size, the size of what it
|
|
57
|
+
// holds, or the typeface arriving — before it does, the UI is measured in a fallback face of a
|
|
58
|
+
// different size. Batched to one fit a frame; scaling is a transform and does not resize what
|
|
59
|
+
// is observed, so a fit does not set off another.
|
|
60
|
+
let queued = 0;
|
|
61
|
+
const refit = () => {
|
|
62
|
+
if (queued !== 0)
|
|
63
|
+
return;
|
|
64
|
+
queued = requestAnimationFrame(() => {
|
|
65
|
+
queued = 0;
|
|
66
|
+
fit();
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
const watch = new ResizeObserver(refit);
|
|
70
|
+
watch.observe(root);
|
|
71
|
+
for (const child of Array.from(inner.children))
|
|
72
|
+
watch.observe(child);
|
|
73
|
+
void document.fonts?.ready.then(refit);
|
|
74
|
+
fit();
|
|
75
|
+
return () => {
|
|
76
|
+
watch.disconnect();
|
|
77
|
+
if (queued !== 0)
|
|
78
|
+
cancelAnimationFrame(queued);
|
|
79
|
+
};
|
|
80
|
+
}, []);
|
|
81
|
+
return (_jsx("div", { ...rest, ref: pane, className: illustrationCardParts.root, "data-ratio": ratio, role: "img", "aria-label": label, children: _jsx("div", { ref: content, className: illustrationCardParts.content, inert: true, children: children }) }));
|
|
82
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
export interface IllustrationCardOwnProps {
|
|
3
|
+
/**
|
|
4
|
+
* `landscape` — Four by three, and the default. For a panel beside words in a wide section
|
|
5
|
+
* `square` — One by one. For a grid of panels, or a column no wider than it is tall
|
|
6
|
+
* `upright` — Three by four. For a phone, or a narrow column beside a tall run of words
|
|
7
|
+
*/
|
|
8
|
+
ratio?: 'landscape' | 'square' | 'upright';
|
|
9
|
+
/** Always. What the picture shows, in a sentence — "Trade ticket: buy Gold to rise over 5 minutes, staking 10 USD". The pane is one image to assistive technology and this is its whole description; nothing inside it is read out on its own */
|
|
10
|
+
label: string;
|
|
11
|
+
/** Allowed: any content */
|
|
12
|
+
children: ReactNode;
|
|
13
|
+
}
|
|
14
|
+
export type IllustrationCardProps = IllustrationCardOwnProps & Omit<HTMLAttributes<HTMLDivElement>, 'className' | 'ratio' | 'label'>;
|
|
15
|
+
/** The class each part carries. Apply these — do not retype them. */
|
|
16
|
+
export declare const illustrationCardParts: {
|
|
17
|
+
readonly root: "dds-illustrationcard";
|
|
18
|
+
readonly content: "dds-illustrationcard__content";
|
|
19
|
+
};
|
|
20
|
+
/** Generated so defaults cannot drift from the contract. */
|
|
21
|
+
export declare const illustrationCardDefaults: {
|
|
22
|
+
readonly ratio: "landscape";
|
|
23
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// GENERATED from illustration-card.contract.json. Do not edit.
|
|
2
|
+
/** The class each part carries. Apply these — do not retype them. */
|
|
3
|
+
export const illustrationCardParts = {
|
|
4
|
+
root: 'dds-illustrationcard',
|
|
5
|
+
content: 'dds-illustrationcard__content',
|
|
6
|
+
};
|
|
7
|
+
/** Generated so defaults cannot drift from the contract. */
|
|
8
|
+
export const illustrationCardDefaults = { ratio: 'landscape' };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Scoped to this package, like the generated sheet beside it.
|
|
3
|
+
*
|
|
4
|
+
* `:where([data-dds-context="marketing"])` confines these rules without contributing
|
|
5
|
+
* specificity, so every override below still ties or beats exactly what it did before.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
* The glass: whatever is behind the pane, blurred. The fill is bound in the contract —
|
|
10
|
+
* color.bg.surface-glass — and the blur is here because `blur()` is a function no binding can
|
|
11
|
+
* name, the same division navbar-material.css makes. size.backdrop-blur is enough that what is
|
|
12
|
+
* behind stops reading as detail, so the UI on the pane stays legible.
|
|
13
|
+
*/
|
|
14
|
+
:where([data-dds-context="marketing"]) .dds-illustrationcard {
|
|
15
|
+
-webkit-backdrop-filter: blur(var(--size-backdrop-blur));
|
|
16
|
+
backdrop-filter: blur(var(--size-backdrop-blur));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/*
|
|
20
|
+
* What the pane holds keeps its own size: nothing in it shrinks to make room, because a shrunk item
|
|
21
|
+
* in a flex column overlaps the next one. Where the UI is taller than the pane, IllustrationCard
|
|
22
|
+
* scales the whole of it down to fit, as a picture would be; until that has run — on a server
|
|
23
|
+
* render — whatever does not fit is clipped at the pane's edge.
|
|
24
|
+
*/
|
|
25
|
+
:where([data-dds-context="marketing"]) .dds-illustrationcard .dds-illustrationcard__content > * {
|
|
26
|
+
flex-shrink: 0;
|
|
27
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/* GENERATED from illustrationscene.contract.json. Do not edit. */
|
|
2
|
+
|
|
3
|
+
:where([data-dds-context="marketing"]) .dds-illustrationscene {
|
|
4
|
+
align-items: center;
|
|
5
|
+
box-sizing: border-box;
|
|
6
|
+
display: grid;
|
|
7
|
+
inline-size: 100%;
|
|
8
|
+
isolation: isolate;
|
|
9
|
+
overflow: hidden;
|
|
10
|
+
position: relative;
|
|
11
|
+
border-radius: var(--radius-surface);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
:where([data-dds-context="marketing"]) .dds-illustrationscene .dds-illustrationscene__image {
|
|
15
|
+
block-size: 100%;
|
|
16
|
+
inline-size: 100%;
|
|
17
|
+
object-fit: cover;
|
|
18
|
+
background-color: var(--color-bg-surface);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
:where([data-dds-context="marketing"]) .dds-illustrationscene[data-placement="center"] {
|
|
22
|
+
justify-items: center;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
:where([data-dds-context="marketing"]) .dds-illustrationscene[data-placement="end"] {
|
|
26
|
+
justify-items: end;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
:where([data-dds-context="marketing"]) .dds-illustrationscene[data-placement="start"] {
|
|
30
|
+
justify-items: start;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
:where([data-dds-context="marketing"]) .dds-illustrationscene[data-ratio="landscape"] {
|
|
34
|
+
aspect-ratio: var(--aspect-landscape);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
:where([data-dds-context="marketing"]) .dds-illustrationscene[data-ratio="square"] {
|
|
38
|
+
aspect-ratio: var(--aspect-square);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
:where([data-dds-context="marketing"]) .dds-illustrationscene[data-ratio="upright"] {
|
|
42
|
+
aspect-ratio: var(--aspect-upright);
|
|
43
|
+
}
|
|
44
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import './IllustrationScene.css';
|
|
2
|
+
import './illustration-scene-impl.css';
|
|
3
|
+
import { type IllustrationSceneProps } from './IllustrationScene.types.js';
|
|
4
|
+
export type { IllustrationSceneProps };
|
|
5
|
+
/**
|
|
6
|
+
* A photograph with an IllustrationCard laid over it, in one of three shapes.
|
|
7
|
+
*
|
|
8
|
+
* The scene owns how the two meet — the shape, the card's share of the frame, where it sits — so
|
|
9
|
+
* every picture of the product in use is composed the same way across a site. The photograph is
|
|
10
|
+
* decoration unless `alt` says otherwise; the card's own label describes the whole picture.
|
|
11
|
+
*
|
|
12
|
+
* No state and no script, so it renders on a server as it is.
|
|
13
|
+
*/
|
|
14
|
+
export declare function IllustrationScene({ ratio, placement, src, alt, children, ...rest }: IllustrationSceneProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import './IllustrationScene.css';
|
|
3
|
+
import './illustration-scene-impl.css';
|
|
4
|
+
import { illustrationSceneDefaults, illustrationSceneParts, } from './IllustrationScene.types.js';
|
|
5
|
+
/**
|
|
6
|
+
* A photograph with an IllustrationCard laid over it, in one of three shapes.
|
|
7
|
+
*
|
|
8
|
+
* The scene owns how the two meet — the shape, the card's share of the frame, where it sits — so
|
|
9
|
+
* every picture of the product in use is composed the same way across a site. The photograph is
|
|
10
|
+
* decoration unless `alt` says otherwise; the card's own label describes the whole picture.
|
|
11
|
+
*
|
|
12
|
+
* No state and no script, so it renders on a server as it is.
|
|
13
|
+
*/
|
|
14
|
+
export function IllustrationScene({ ratio = illustrationSceneDefaults.ratio, placement = illustrationSceneDefaults.placement, src, alt = illustrationSceneDefaults.alt, children, ...rest }) {
|
|
15
|
+
return (_jsxs("div", { ...rest, className: illustrationSceneParts.root, "data-ratio": ratio, "data-placement": placement, children: [_jsx("img", { className: illustrationSceneParts.image, src: src, alt: alt }), children] }));
|
|
16
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
export interface IllustrationSceneOwnProps {
|
|
3
|
+
/**
|
|
4
|
+
* `landscape` — Four by three. A scene beside words in a wide section; the card takes a little under half the width
|
|
5
|
+
* `square` — One by one, and the default — the shape most such scenes are drawn in. The card takes a little over half the width
|
|
6
|
+
* `upright` — Three by four. A scene on a phone or in a narrow column; the card takes most of the width
|
|
7
|
+
*/
|
|
8
|
+
ratio?: 'landscape' | 'square' | 'upright';
|
|
9
|
+
/**
|
|
10
|
+
* `center` — The default: the card in the middle of the frame. For a photograph whose subject frames the middle — a person looking down at what the card shows
|
|
11
|
+
* `start` — The card towards the start edge, for a photograph whose subject is at the end
|
|
12
|
+
* `end` — The card towards the end edge, for a photograph whose subject is at the start
|
|
13
|
+
*/
|
|
14
|
+
placement?: 'center' | 'start' | 'end';
|
|
15
|
+
/** Always. The photograph the scene is set in */
|
|
16
|
+
src: string;
|
|
17
|
+
/** Empty by default, because the card's `label` describes the whole picture and the photograph is decoration. Write one only where the photograph says something the label does not */
|
|
18
|
+
alt?: string;
|
|
19
|
+
/** Allowed: IllustrationCard */
|
|
20
|
+
children: ReactNode;
|
|
21
|
+
}
|
|
22
|
+
export type IllustrationSceneProps = IllustrationSceneOwnProps & Omit<HTMLAttributes<HTMLDivElement>, 'className' | 'ratio' | 'placement' | 'src' | 'alt'>;
|
|
23
|
+
/** The class each part carries. Apply these — do not retype them. */
|
|
24
|
+
export declare const illustrationSceneParts: {
|
|
25
|
+
readonly root: "dds-illustrationscene";
|
|
26
|
+
readonly image: "dds-illustrationscene__image";
|
|
27
|
+
};
|
|
28
|
+
/** Generated so defaults cannot drift from the contract. */
|
|
29
|
+
export declare const illustrationSceneDefaults: {
|
|
30
|
+
readonly ratio: "square";
|
|
31
|
+
readonly placement: "center";
|
|
32
|
+
readonly alt: "";
|
|
33
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// GENERATED from illustration-scene.contract.json. Do not edit.
|
|
2
|
+
/** The class each part carries. Apply these — do not retype them. */
|
|
3
|
+
export const illustrationSceneParts = {
|
|
4
|
+
root: 'dds-illustrationscene',
|
|
5
|
+
image: 'dds-illustrationscene__image',
|
|
6
|
+
};
|
|
7
|
+
/** Generated so defaults cannot drift from the contract. */
|
|
8
|
+
export const illustrationSceneDefaults = { ratio: 'square', placement: 'center', alt: '' };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Scoped to this package, like the generated sheet beside it.
|
|
3
|
+
*
|
|
4
|
+
* `:where([data-dds-context="marketing"])` confines these rules without contributing
|
|
5
|
+
* specificity, so every override below still ties or beats exactly what it did before.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
* The photograph fills the frame behind everything; the card sits in the frame's one grid cell
|
|
10
|
+
* over it. Both share the cell, so the card is placed by the grid's own alignment — centred, or
|
|
11
|
+
* to one edge — and the photograph is laid underneath by coming first.
|
|
12
|
+
*/
|
|
13
|
+
:where([data-dds-context="marketing"]) .dds-illustrationscene > * {
|
|
14
|
+
grid-area: 1 / 1;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
:where([data-dds-context="marketing"]) .dds-illustrationscene .dds-illustrationscene__image {
|
|
18
|
+
align-self: stretch;
|
|
19
|
+
justify-self: stretch;
|
|
20
|
+
display: block;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/*
|
|
24
|
+
* The card's share of the frame, per shape: a little under half of a wide frame, a little over half
|
|
25
|
+
* of a square one, most of a tall one. Proportions of the frame rather than measures — a scene is
|
|
26
|
+
* drawn at whatever width its column gives it, and the composition has to hold at all of them.
|
|
27
|
+
* Held off the frame's edges by space.inset.card, so a card placed to one side never touches it.
|
|
28
|
+
*/
|
|
29
|
+
:where([data-dds-context="marketing"]) .dds-illustrationscene > .dds-illustrationcard {
|
|
30
|
+
inline-size: 46%;
|
|
31
|
+
margin-inline: var(--space-inset-card);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
:where([data-dds-context="marketing"]) .dds-illustrationscene[data-ratio='square'] > .dds-illustrationcard {
|
|
35
|
+
inline-size: 58%;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
:where([data-dds-context="marketing"]) .dds-illustrationscene[data-ratio='upright'] > .dds-illustrationcard {
|
|
39
|
+
inline-size: 78%;
|
|
40
|
+
}
|
|
@@ -4,6 +4,8 @@ export { AccordionItem, type AccordionItemProps } from './core/accordion-item/Ac
|
|
|
4
4
|
export { accordionItemParts, accordionItemDefaults } from './core/accordion-item/AccordionItem.types.js';
|
|
5
5
|
export { AspectRatio, type AspectRatioProps } from './layout/aspect-ratio/AspectRatio.js';
|
|
6
6
|
export { aspectRatioParts, aspectRatioDefaults } from './layout/aspect-ratio/AspectRatio.types.js';
|
|
7
|
+
export { Background, type BackgroundProps } from './core/background/Background.js';
|
|
8
|
+
export { backgroundParts, backgroundDefaults } from './core/background/Background.types.js';
|
|
7
9
|
export { Badge, type BadgeProps } from './core/badge/Badge.js';
|
|
8
10
|
export { badgeParts, badgeDefaults } from './core/badge/Badge.types.js';
|
|
9
11
|
export { BottomSheet, type BottomSheetProps } from './core/bottom-sheet/BottomSheet.js';
|
|
@@ -52,6 +54,10 @@ export { Grid, type GridProps } from './layout/grid/Grid.js';
|
|
|
52
54
|
export { gridParts, gridDefaults } from './layout/grid/Grid.types.js';
|
|
53
55
|
export { Icon, type IconProps } from './core/icon/Icon.js';
|
|
54
56
|
export { iconParts, iconDefaults } from './core/icon/Icon.types.js';
|
|
57
|
+
export { IllustrationCard, type IllustrationCardProps } from './core/illustration-card/IllustrationCard.js';
|
|
58
|
+
export { illustrationCardParts, illustrationCardDefaults } from './core/illustration-card/IllustrationCard.types.js';
|
|
59
|
+
export { IllustrationScene, type IllustrationSceneProps } from './core/illustration-scene/IllustrationScene.js';
|
|
60
|
+
export { illustrationSceneParts, illustrationSceneDefaults } from './core/illustration-scene/IllustrationScene.types.js';
|
|
55
61
|
export { LandingHero, type LandingHeroProps } from './blocks/landing-hero/LandingHero.js';
|
|
56
62
|
export { landingHeroParts, landingHeroDefaults } from './blocks/landing-hero/LandingHero.types.js';
|
|
57
63
|
export { Link, type LinkProps } from './core/link/Link.js';
|
package/dist/components/index.js
CHANGED
|
@@ -5,6 +5,8 @@ export { AccordionItem } from './core/accordion-item/AccordionItem.js';
|
|
|
5
5
|
export { accordionItemParts, accordionItemDefaults } from './core/accordion-item/AccordionItem.types.js';
|
|
6
6
|
export { AspectRatio } from './layout/aspect-ratio/AspectRatio.js';
|
|
7
7
|
export { aspectRatioParts, aspectRatioDefaults } from './layout/aspect-ratio/AspectRatio.types.js';
|
|
8
|
+
export { Background } from './core/background/Background.js';
|
|
9
|
+
export { backgroundParts, backgroundDefaults } from './core/background/Background.types.js';
|
|
8
10
|
export { Badge } from './core/badge/Badge.js';
|
|
9
11
|
export { badgeParts, badgeDefaults } from './core/badge/Badge.types.js';
|
|
10
12
|
export { BottomSheet } from './core/bottom-sheet/BottomSheet.js';
|
|
@@ -53,6 +55,10 @@ export { Grid } from './layout/grid/Grid.js';
|
|
|
53
55
|
export { gridParts, gridDefaults } from './layout/grid/Grid.types.js';
|
|
54
56
|
export { Icon } from './core/icon/Icon.js';
|
|
55
57
|
export { iconParts, iconDefaults } from './core/icon/Icon.types.js';
|
|
58
|
+
export { IllustrationCard } from './core/illustration-card/IllustrationCard.js';
|
|
59
|
+
export { illustrationCardParts, illustrationCardDefaults } from './core/illustration-card/IllustrationCard.types.js';
|
|
60
|
+
export { IllustrationScene } from './core/illustration-scene/IllustrationScene.js';
|
|
61
|
+
export { illustrationSceneParts, illustrationSceneDefaults } from './core/illustration-scene/IllustrationScene.types.js';
|
|
56
62
|
export { LandingHero } from './blocks/landing-hero/LandingHero.js';
|
|
57
63
|
export { landingHeroParts, landingHeroDefaults } from './blocks/landing-hero/LandingHero.types.js';
|
|
58
64
|
export { Link } from './core/link/Link.js';
|