@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.
Files changed (62) hide show
  1. package/.llm/components/background.md +371 -0
  2. package/.llm/components/ctabanner.md +25 -0
  3. package/.llm/components/ctabannerslider.md +21 -0
  4. package/.llm/components/illustrationcard.md +236 -0
  5. package/.llm/components/illustrationscene.md +233 -0
  6. package/.llm/components/landinghero.md +24 -0
  7. package/.llm/components/pageheader.md +21 -0
  8. package/.llm/digest/background.md +52 -0
  9. package/.llm/digest/ctabanner.md +4 -0
  10. package/.llm/digest/ctabannerslider.md +3 -0
  11. package/.llm/digest/illustrationcard.md +46 -0
  12. package/.llm/digest/illustrationscene.md +51 -0
  13. package/.llm/digest/landinghero.md +7 -0
  14. package/.llm/digest/pageheader.md +3 -0
  15. package/.llm/index.md +7 -0
  16. package/.llm/tokens.md +11 -0
  17. package/AGENTS.md +3 -0
  18. package/dist/components/blocks/cta-banner/CtaBanner.d.ts +1 -1
  19. package/dist/components/blocks/cta-banner/CtaBanner.js +3 -2
  20. package/dist/components/blocks/cta-banner/CtaBanner.types.d.ts +5 -0
  21. package/dist/components/blocks/cta-banner/cta-banner-impl.css +8 -0
  22. package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.d.ts +1 -1
  23. package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.js +5 -4
  24. package/dist/components/blocks/cta-banner-slider/CtaBannerSlider.types.d.ts +4 -0
  25. package/dist/components/blocks/cta-banner-slider/cta-banner-slider-impl.css +8 -0
  26. package/dist/components/blocks/landing-hero/LandingHero.d.ts +1 -1
  27. package/dist/components/blocks/landing-hero/LandingHero.js +4 -3
  28. package/dist/components/blocks/landing-hero/LandingHero.types.d.ts +7 -3
  29. package/dist/components/blocks/page-header/PageHeader.d.ts +1 -1
  30. package/dist/components/blocks/page-header/PageHeader.js +3 -2
  31. package/dist/components/blocks/page-header/PageHeader.types.d.ts +4 -0
  32. package/dist/components/blocks/page-header/page-header-impl.css +8 -0
  33. package/dist/components/core/background/Background.css +69 -0
  34. package/dist/components/core/background/Background.d.ts +21 -0
  35. package/dist/components/core/background/Background.js +36 -0
  36. package/dist/components/core/background/Background.types.d.ts +36 -0
  37. package/dist/components/core/background/Background.types.js +9 -0
  38. package/dist/components/core/background/background-impl.css +427 -0
  39. package/dist/components/core/background/halftone.d.ts +123 -0
  40. package/dist/components/core/background/halftone.js +682 -0
  41. package/dist/components/core/background/use-halftone.d.ts +15 -0
  42. package/dist/components/core/background/use-halftone.js +27 -0
  43. package/dist/components/core/background/use-offscreen.d.ts +10 -0
  44. package/dist/components/core/background/use-offscreen.js +30 -0
  45. package/dist/components/core/background/use-spotlight.d.ts +13 -0
  46. package/dist/components/core/background/use-spotlight.js +65 -0
  47. package/dist/components/core/illustration-card/IllustrationCard.css +40 -0
  48. package/dist/components/core/illustration-card/IllustrationCard.d.ts +22 -0
  49. package/dist/components/core/illustration-card/IllustrationCard.js +82 -0
  50. package/dist/components/core/illustration-card/IllustrationCard.types.d.ts +23 -0
  51. package/dist/components/core/illustration-card/IllustrationCard.types.js +8 -0
  52. package/dist/components/core/illustration-card/illustration-card-impl.css +27 -0
  53. package/dist/components/core/illustration-scene/IllustrationScene.css +44 -0
  54. package/dist/components/core/illustration-scene/IllustrationScene.d.ts +14 -0
  55. package/dist/components/core/illustration-scene/IllustrationScene.js +16 -0
  56. package/dist/components/core/illustration-scene/IllustrationScene.types.d.ts +33 -0
  57. package/dist/components/core/illustration-scene/IllustrationScene.types.js +8 -0
  58. package/dist/components/core/illustration-scene/illustration-scene-impl.css +40 -0
  59. package/dist/components/index.d.ts +6 -0
  60. package/dist/components/index.js +6 -0
  61. package/dist/marketing.css +24 -0
  62. 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';
@@ -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';