@deriv-com/dds-marketing 1.1.2 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.llm/components/accordion.md +4 -0
- package/.llm/components/accordionitem.md +4 -0
- package/.llm/components/aspectratio.md +4 -0
- package/.llm/components/awards.md +4 -0
- package/.llm/components/background.md +4 -0
- package/.llm/components/badge.md +7 -3
- package/.llm/components/bottomsheet.md +7 -3
- package/.llm/components/breadcrumb.md +4 -0
- package/.llm/components/breadcrumbitem.md +4 -0
- package/.llm/components/button.md +4 -0
- package/.llm/components/buttonicon.md +10 -6
- package/.llm/components/card.md +5 -1
- package/.llm/components/cardbody.md +5 -1
- package/.llm/components/cardbutton.md +4 -0
- package/.llm/components/checkbox.md +1 -1
- package/.llm/components/comparisoncards.md +4 -0
- package/.llm/components/comparisontable.md +4 -0
- package/.llm/components/ctabanner.md +4 -0
- package/.llm/components/ctabannerslider.md +4 -0
- package/.llm/components/datatable.md +4 -0
- package/.llm/components/divider.md +4 -0
- package/.llm/components/downloadbanner.md +4 -0
- package/.llm/components/dropdownmenu.md +9 -5
- package/.llm/components/estimator.md +336 -0
- package/.llm/components/faqaccordion.md +4 -0
- package/.llm/components/featurebullets.md +4 -0
- package/.llm/components/featurecards.md +5 -1
- package/.llm/components/featurecardscarousel.md +4 -0
- package/.llm/components/featurescroll.md +4 -0
- package/.llm/components/featuresplit.md +4 -0
- package/.llm/components/featurestack.md +4 -0
- package/.llm/components/filterchip.md +5 -1
- package/.llm/components/footer.md +4 -0
- package/.llm/components/footergroup.md +4 -0
- package/.llm/components/grid.md +4 -0
- package/.llm/components/icon.md +9 -5
- package/.llm/components/illustrationcard.md +4 -0
- package/.llm/components/illustrationscene.md +4 -0
- package/.llm/components/imagebanner.md +5 -1
- package/.llm/components/landinghero.md +4 -0
- package/.llm/components/link.md +5 -1
- package/.llm/components/logocolumns.md +4 -0
- package/.llm/components/logorows.md +4 -0
- package/.llm/components/menuitem.md +6 -2
- package/.llm/components/navbar.md +5 -1
- package/.llm/components/navbaritem.md +4 -0
- package/.llm/components/navbarmenu.md +4 -0
- package/.llm/components/navbarmenugroup.md +4 -0
- package/.llm/components/navbarpanel.md +4 -0
- package/.llm/components/navbarswitcher.md +10 -6
- package/.llm/components/navbarswitcheroption.md +4 -0
- package/.llm/components/option.md +5 -1
- package/.llm/components/pageheader.md +4 -0
- package/.llm/components/pagination.md +4 -0
- package/.llm/components/paginationdots.md +4 -0
- package/.llm/components/producthero.md +4 -0
- package/.llm/components/removablechip.md +5 -1
- package/.llm/components/resourcebanner.md +4 -0
- package/.llm/components/row.md +4 -0
- package/.llm/components/scenescroll.md +4 -0
- package/.llm/components/scroller.md +4 -0
- package/.llm/components/searchinput.md +4 -0
- package/.llm/components/segmentedcontrol.md +5 -1
- package/.llm/components/segmentedcontrolitem.md +6 -2
- package/.llm/components/select.md +4 -0
- package/.llm/components/selectchip.md +4 -0
- package/.llm/components/spinner.md +4 -0
- package/.llm/components/stack.md +4 -0
- package/.llm/components/stats.md +4 -0
- package/.llm/components/statusdot.md +4 -0
- package/.llm/components/steps.md +4 -0
- package/.llm/components/stickycards.md +4 -0
- package/.llm/components/subdomainnavbar.md +5 -1
- package/.llm/components/tab.md +6 -2
- package/.llm/components/table.md +4 -0
- package/.llm/components/tablebody.md +4 -0
- package/.llm/components/tablecell.md +4 -0
- package/.llm/components/tablehead.md +4 -0
- package/.llm/components/tableheadercell.md +4 -0
- package/.llm/components/tablerow.md +4 -0
- package/.llm/components/tablist.md +4 -0
- package/.llm/components/teamgrid.md +4 -0
- package/.llm/components/testimonialrow.md +4 -0
- package/.llm/components/testimonialslider.md +4 -0
- package/.llm/components/textarea.md +4 -0
- package/.llm/components/textinput.md +5 -1
- package/.llm/components/textscroll.md +4 -0
- package/.llm/components/tiers.md +4 -0
- package/.llm/components/typography.md +4 -0
- package/.llm/digest/accordion.md +1 -1
- package/.llm/digest/accordionitem.md +1 -1
- package/.llm/digest/aspectratio.md +1 -1
- package/.llm/digest/awards.md +1 -1
- package/.llm/digest/background.md +1 -1
- package/.llm/digest/badge.md +1 -1
- package/.llm/digest/bottomsheet.md +1 -1
- package/.llm/digest/breadcrumb.md +1 -1
- package/.llm/digest/breadcrumbitem.md +1 -1
- package/.llm/digest/button.md +1 -1
- package/.llm/digest/buttonicon.md +1 -1
- package/.llm/digest/card.md +1 -1
- package/.llm/digest/cardbody.md +1 -1
- package/.llm/digest/cardbutton.md +1 -1
- package/.llm/digest/checkbox.md +1 -1
- package/.llm/digest/comparisoncards.md +1 -1
- package/.llm/digest/comparisontable.md +1 -1
- package/.llm/digest/ctabanner.md +1 -1
- package/.llm/digest/ctabannerslider.md +1 -1
- package/.llm/digest/datatable.md +1 -1
- package/.llm/digest/divider.md +1 -1
- package/.llm/digest/downloadbanner.md +1 -1
- package/.llm/digest/dropdownmenu.md +1 -1
- package/.llm/digest/estimator.md +66 -0
- package/.llm/digest/faqaccordion.md +1 -1
- package/.llm/digest/featurebullets.md +1 -1
- package/.llm/digest/featurecards.md +1 -1
- package/.llm/digest/featurecardscarousel.md +1 -1
- package/.llm/digest/featurescroll.md +1 -1
- package/.llm/digest/featuresplit.md +1 -1
- package/.llm/digest/featurestack.md +1 -1
- package/.llm/digest/filterchip.md +1 -1
- package/.llm/digest/footer.md +1 -1
- package/.llm/digest/footergroup.md +1 -1
- package/.llm/digest/grid.md +1 -1
- package/.llm/digest/icon.md +1 -1
- package/.llm/digest/illustrationcard.md +1 -1
- package/.llm/digest/illustrationscene.md +1 -1
- package/.llm/digest/imagebanner.md +2 -2
- package/.llm/digest/landinghero.md +1 -1
- package/.llm/digest/link.md +1 -1
- package/.llm/digest/logocolumns.md +1 -1
- package/.llm/digest/logorows.md +1 -1
- package/.llm/digest/menuitem.md +1 -1
- package/.llm/digest/navbar.md +1 -1
- package/.llm/digest/navbaritem.md +1 -1
- package/.llm/digest/navbarmenu.md +1 -1
- package/.llm/digest/navbarmenugroup.md +1 -1
- package/.llm/digest/navbarpanel.md +1 -1
- package/.llm/digest/navbarswitcher.md +1 -1
- package/.llm/digest/navbarswitcheroption.md +1 -1
- package/.llm/digest/option.md +1 -1
- package/.llm/digest/pageheader.md +1 -1
- package/.llm/digest/pagination.md +1 -1
- package/.llm/digest/paginationdots.md +1 -1
- package/.llm/digest/producthero.md +1 -1
- package/.llm/digest/removablechip.md +1 -1
- package/.llm/digest/resourcebanner.md +1 -1
- package/.llm/digest/row.md +1 -1
- package/.llm/digest/scenescroll.md +1 -1
- package/.llm/digest/scroller.md +1 -1
- package/.llm/digest/searchinput.md +1 -1
- package/.llm/digest/segmentedcontrol.md +1 -1
- package/.llm/digest/segmentedcontrolitem.md +2 -2
- package/.llm/digest/select.md +1 -1
- package/.llm/digest/selectchip.md +1 -1
- package/.llm/digest/spinner.md +1 -1
- package/.llm/digest/stack.md +1 -1
- package/.llm/digest/stats.md +1 -1
- package/.llm/digest/statusdot.md +1 -1
- package/.llm/digest/steps.md +1 -1
- package/.llm/digest/stickycards.md +1 -1
- package/.llm/digest/subdomainnavbar.md +1 -1
- package/.llm/digest/tab.md +1 -1
- package/.llm/digest/table.md +1 -1
- package/.llm/digest/tablebody.md +1 -1
- package/.llm/digest/tablecell.md +1 -1
- package/.llm/digest/tablehead.md +1 -1
- package/.llm/digest/tableheadercell.md +1 -1
- package/.llm/digest/tablerow.md +1 -1
- package/.llm/digest/tablist.md +1 -1
- package/.llm/digest/teamgrid.md +1 -1
- package/.llm/digest/testimonialrow.md +1 -1
- package/.llm/digest/testimonialslider.md +1 -1
- package/.llm/digest/textarea.md +1 -1
- package/.llm/digest/textinput.md +1 -1
- package/.llm/digest/textscroll.md +1 -1
- package/.llm/digest/tiers.md +1 -1
- package/.llm/digest/typography.md +1 -1
- package/.llm/index.md +2 -1
- package/.llm/tokens.md +1 -0
- package/AGENTS.md +1 -0
- package/README.md +28 -3
- package/dist/components/blocks/comparison-cards/comparison-cards-impl.css +0 -12
- package/dist/components/blocks/estimator/Estimator.css +206 -0
- package/dist/components/blocks/estimator/Estimator.d.ts +27 -0
- package/dist/components/blocks/estimator/Estimator.js +61 -0
- package/dist/components/blocks/estimator/Estimator.types.d.ts +63 -0
- package/dist/components/blocks/estimator/Estimator.types.js +25 -0
- package/dist/components/blocks/estimator/estimator-impl.css +159 -0
- package/dist/components/blocks/image-banner/ImageBanner.d.ts +1 -0
- package/dist/components/blocks/image-banner/ImageBanner.js +2 -1
- package/dist/components/blocks/image-banner/ImageBanner.types.d.ts +1 -1
- package/dist/components/blocks/image-banner/image-banner-impl.css +7 -51
- package/dist/components/core/segmented-control-item/SegmentedControlItem.types.d.ts +1 -1
- package/dist/components/core/select/Select.css +2 -0
- package/dist/components/core/select/select-impl.css +58 -0
- package/dist/components/core/text-area/TextArea.css +2 -0
- package/dist/components/core/text-area/text-area-impl.css +54 -0
- package/dist/components/core/text-input/TextInput.css +2 -0
- package/dist/components/core/text-input/text-input-impl.css +63 -0
- package/dist/components/estimator-data.d.ts +29 -0
- package/dist/components/estimator-data.js +12 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.js +2 -0
- package/dist/icons/accounts.d.ts +1 -0
- package/dist/icons/accounts.js +1 -0
- package/dist/icons/app-stores.d.ts +1 -0
- package/dist/icons/app-stores.js +1 -0
- package/dist/icons/currency.d.ts +1 -0
- package/dist/icons/currency.js +1 -0
- package/dist/icons/flag.d.ts +1 -0
- package/dist/icons/flag.js +1 -0
- package/dist/icons/illustrative.d.ts +1 -0
- package/dist/icons/illustrative.js +1 -0
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +4 -0
- package/dist/icons/markets.d.ts +1 -0
- package/dist/icons/markets.js +1 -0
- package/dist/icons/social.d.ts +1 -0
- package/dist/icons/social.js +1 -0
- package/dist/icons/system.d.ts +1 -0
- package/dist/icons/system.js +1 -0
- package/dist/icons/trade.d.ts +1 -0
- package/dist/icons/trade.js +1 -0
- package/dist/marketing.css +2 -0
- package/package.json +44 -2
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/* GENERATED from estimator.contract.json. Do not edit. */
|
|
2
|
+
|
|
3
|
+
:where([data-dds-context="marketing"]) .dds-estimator {
|
|
4
|
+
box-sizing: border-box;
|
|
5
|
+
container-type: inline-size;
|
|
6
|
+
display: flex;
|
|
7
|
+
inline-size: 100%;
|
|
8
|
+
justify-content: center;
|
|
9
|
+
overflow-wrap: anywhere;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__inner {
|
|
13
|
+
box-sizing: border-box;
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: column;
|
|
16
|
+
inline-size: 100%;
|
|
17
|
+
min-inline-size: 0;
|
|
18
|
+
column-gap: var(--space-gap-3xl);
|
|
19
|
+
max-inline-size: var(--size-content-max);
|
|
20
|
+
padding-block: var(--space-section-default);
|
|
21
|
+
padding-inline: var(--space-inset-page);
|
|
22
|
+
row-gap: var(--space-heading-content);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__header {
|
|
26
|
+
display: flex;
|
|
27
|
+
flex-direction: column;
|
|
28
|
+
min-inline-size: 0;
|
|
29
|
+
row-gap: var(--space-gap-none);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__title {
|
|
33
|
+
margin-block: 0;
|
|
34
|
+
text-wrap: balance;
|
|
35
|
+
font-family: var(--font-h2-family);
|
|
36
|
+
font-size: var(--font-h2-size);
|
|
37
|
+
font-weight: var(--font-h2-weight);
|
|
38
|
+
letter-spacing: var(--font-h2-letter-spacing);
|
|
39
|
+
line-height: var(--font-h2-line-height);
|
|
40
|
+
margin-block-end: var(--space-heading-title-description);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__description {
|
|
44
|
+
margin-block: 0;
|
|
45
|
+
font-family: var(--font-body-md-family);
|
|
46
|
+
font-size: var(--font-body-md-size);
|
|
47
|
+
font-weight: var(--font-body-md-weight);
|
|
48
|
+
letter-spacing: var(--font-body-md-letter-spacing);
|
|
49
|
+
line-height: var(--font-body-md-line-height);
|
|
50
|
+
max-inline-size: var(--size-measure-description);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__tool {
|
|
54
|
+
display: flex;
|
|
55
|
+
flex-direction: column;
|
|
56
|
+
min-inline-size: 0;
|
|
57
|
+
row-gap: var(--space-gap-none);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__panels {
|
|
61
|
+
box-sizing: border-box;
|
|
62
|
+
display: flex;
|
|
63
|
+
flex-direction: column;
|
|
64
|
+
min-inline-size: 0;
|
|
65
|
+
column-gap: var(--space-gap-xl);
|
|
66
|
+
padding-block: var(--space-inset-card);
|
|
67
|
+
padding-inline: var(--space-inset-card);
|
|
68
|
+
row-gap: var(--space-gap-2xl);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__inputs {
|
|
72
|
+
display: flex;
|
|
73
|
+
flex-direction: column;
|
|
74
|
+
min-inline-size: 0;
|
|
75
|
+
row-gap: var(--space-gap-lg);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__inputs-label {
|
|
79
|
+
margin-block: 0;
|
|
80
|
+
font-family: var(--font-h5-family);
|
|
81
|
+
font-size: var(--font-h5-size);
|
|
82
|
+
font-weight: var(--font-h5-weight);
|
|
83
|
+
letter-spacing: var(--font-h5-letter-spacing);
|
|
84
|
+
line-height: var(--font-h5-line-height);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__results {
|
|
88
|
+
display: flex;
|
|
89
|
+
flex-direction: column;
|
|
90
|
+
min-inline-size: 0;
|
|
91
|
+
background-color: var(--color-bg-surface-card-raised);
|
|
92
|
+
border-radius: var(--radius-surface-compact);
|
|
93
|
+
box-shadow: var(--elevation-surface-floating);
|
|
94
|
+
padding-block: var(--space-inset-card);
|
|
95
|
+
padding-inline: var(--space-inset-card);
|
|
96
|
+
row-gap: var(--space-gap-lg);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__results-label {
|
|
100
|
+
margin-block: 0;
|
|
101
|
+
font-family: var(--font-h5-family);
|
|
102
|
+
font-size: var(--font-h5-size);
|
|
103
|
+
font-weight: var(--font-h5-weight);
|
|
104
|
+
letter-spacing: var(--font-h5-letter-spacing);
|
|
105
|
+
line-height: var(--font-h5-line-height);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__list {
|
|
109
|
+
display: flex;
|
|
110
|
+
flex-direction: column;
|
|
111
|
+
margin-block: 0;
|
|
112
|
+
margin-inline: 0;
|
|
113
|
+
padding-block: 0;
|
|
114
|
+
padding-inline: 0;
|
|
115
|
+
column-gap: var(--space-gap-2xl);
|
|
116
|
+
row-gap: var(--space-gap-none);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__result {
|
|
120
|
+
border-block-end-style: solid;
|
|
121
|
+
display: flex;
|
|
122
|
+
flex-direction: column;
|
|
123
|
+
min-inline-size: 0;
|
|
124
|
+
border-block-end-width: var(--border-width-default);
|
|
125
|
+
border-color: var(--color-border-subtle);
|
|
126
|
+
padding-block: var(--space-inset-md);
|
|
127
|
+
row-gap: var(--space-gap-xs);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__result-value {
|
|
131
|
+
margin-inline: 0;
|
|
132
|
+
font-family: var(--font-h4-family);
|
|
133
|
+
font-size: var(--font-h4-size);
|
|
134
|
+
font-weight: var(--font-h4-weight);
|
|
135
|
+
letter-spacing: var(--font-h4-letter-spacing);
|
|
136
|
+
line-height: var(--font-h4-line-height);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__result-note {
|
|
140
|
+
margin-inline: 0;
|
|
141
|
+
color: var(--color-fg-muted);
|
|
142
|
+
font-family: var(--font-body-sm-family);
|
|
143
|
+
font-size: var(--font-body-sm-size);
|
|
144
|
+
font-weight: var(--font-body-sm-weight);
|
|
145
|
+
letter-spacing: var(--font-body-sm-letter-spacing);
|
|
146
|
+
line-height: var(--font-body-sm-line-height);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__empty {
|
|
150
|
+
margin-block: 0;
|
|
151
|
+
color: var(--color-fg-muted);
|
|
152
|
+
font-family: var(--font-body-md-family);
|
|
153
|
+
font-size: var(--font-body-md-size);
|
|
154
|
+
font-weight: var(--font-body-md-weight);
|
|
155
|
+
letter-spacing: var(--font-body-md-letter-spacing);
|
|
156
|
+
line-height: var(--font-body-md-line-height);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__action {
|
|
160
|
+
display: flex;
|
|
161
|
+
margin-block-start: var(--space-gap-sm);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__input-actions {
|
|
165
|
+
display: flex;
|
|
166
|
+
flex-wrap: wrap;
|
|
167
|
+
column-gap: var(--space-actions-gap);
|
|
168
|
+
margin-block-start: var(--space-gap-sm);
|
|
169
|
+
row-gap: var(--space-actions-gap);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
:where([data-dds-context="marketing"]) .dds-estimator[data-layout="split"] .dds-estimator__header {
|
|
173
|
+
align-items: flex-start;
|
|
174
|
+
text-align: start;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
:where([data-dds-context="marketing"]) .dds-estimator[data-layout="stacked"] .dds-estimator__header {
|
|
178
|
+
align-items: center;
|
|
179
|
+
text-align: center;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__result-label {
|
|
183
|
+
color: var(--color-fg-muted);
|
|
184
|
+
font-family: var(--font-body-sm-family);
|
|
185
|
+
font-size: var(--font-body-sm-size);
|
|
186
|
+
font-weight: var(--font-body-sm-weight);
|
|
187
|
+
letter-spacing: var(--font-body-sm-letter-spacing);
|
|
188
|
+
line-height: var(--font-body-sm-line-height);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
:where([data-dds-context="marketing"]) .dds-estimator[data-ground="page"] {
|
|
192
|
+
background-color: var(--color-bg-page);
|
|
193
|
+
color: var(--color-fg-default);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
:where([data-dds-context="marketing"]) .dds-estimator[data-ground="strong"] {
|
|
197
|
+
background-color: var(--color-bg-brand-strong);
|
|
198
|
+
color: var(--color-fg-on-brand-strong);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
:where([data-dds-context="marketing"]) .dds-estimator[data-ground="subtle"] {
|
|
202
|
+
background-color: var(--color-bg-page);
|
|
203
|
+
background-image: linear-gradient(var(--color-bg-band-subtle), var(--color-bg-band-subtle));
|
|
204
|
+
color: var(--color-fg-default);
|
|
205
|
+
}
|
|
206
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import './Estimator.css';
|
|
2
|
+
import './estimator-impl.css';
|
|
3
|
+
import '../block-reveal.css';
|
|
4
|
+
import { type EstimatorProps } from './Estimator.types.js';
|
|
5
|
+
export type { EstimatorProps };
|
|
6
|
+
/**
|
|
7
|
+
* A section that turns a reader's inputs into figures — the trading calculators, the crypto
|
|
8
|
+
* converter and the affiliate income estimate of the reference design, as one block.
|
|
9
|
+
*
|
|
10
|
+
* It lays the tool out and works nothing out. The page passes its own fields as `inputs` and the
|
|
11
|
+
* figures it has computed from them as `results`; a live feed, a formula, a currency conversion are
|
|
12
|
+
* all the page's, so the block is the same on every page that uses it and in every test, and none
|
|
13
|
+
* of it reaches the network. What the block owns is the part every such tool gets wrong once:
|
|
14
|
+
*
|
|
15
|
+
* - The figures are a description list, so a value is announced with the label it answers.
|
|
16
|
+
* - The results panel is a polite live region from the first paint, so a reader using a screen
|
|
17
|
+
* reader hears each new set of figures once, after they stop typing, without being interrupted —
|
|
18
|
+
* and it is there before the first change, which is the only way a live region is listened to.
|
|
19
|
+
* - Each value is isolated (`<bdi>`), so '-12.40 USD' or '0.0214 BTC' keeps its order in a
|
|
20
|
+
* right-to-left line, where a sign or a currency is neutral and moves to the far end.
|
|
21
|
+
* - Before there are figures the panel says what it is waiting for, rather than showing a row of
|
|
22
|
+
* dashes or a zero that reads as an answer.
|
|
23
|
+
*
|
|
24
|
+
* The tool is a Card at its `surface` variant, the same object as every other card on the page; the
|
|
25
|
+
* ground it stands on decides its fill, in the stylesheet, as ComparisonCards' cards do.
|
|
26
|
+
*/
|
|
27
|
+
export declare function Estimator({ ground, layout, title, description, inputsLabel, inputs, inputActions, onCalculate, resultsLabel, results, emptyMessage, action, }: EstimatorProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useId } from 'react';
|
|
4
|
+
import './Estimator.css';
|
|
5
|
+
import './estimator-impl.css';
|
|
6
|
+
import '../block-reveal.css';
|
|
7
|
+
import { arriving, useReveal } from '../use-reveal.js';
|
|
8
|
+
import { given } from '../../given.js';
|
|
9
|
+
import { Card } from '../../core/card/Card.js';
|
|
10
|
+
import { estimatorDefaults, estimatorParts } from './Estimator.types.js';
|
|
11
|
+
/**
|
|
12
|
+
* A section that turns a reader's inputs into figures — the trading calculators, the crypto
|
|
13
|
+
* converter and the affiliate income estimate of the reference design, as one block.
|
|
14
|
+
*
|
|
15
|
+
* It lays the tool out and works nothing out. The page passes its own fields as `inputs` and the
|
|
16
|
+
* figures it has computed from them as `results`; a live feed, a formula, a currency conversion are
|
|
17
|
+
* all the page's, so the block is the same on every page that uses it and in every test, and none
|
|
18
|
+
* of it reaches the network. What the block owns is the part every such tool gets wrong once:
|
|
19
|
+
*
|
|
20
|
+
* - The figures are a description list, so a value is announced with the label it answers.
|
|
21
|
+
* - The results panel is a polite live region from the first paint, so a reader using a screen
|
|
22
|
+
* reader hears each new set of figures once, after they stop typing, without being interrupted —
|
|
23
|
+
* and it is there before the first change, which is the only way a live region is listened to.
|
|
24
|
+
* - Each value is isolated (`<bdi>`), so '-12.40 USD' or '0.0214 BTC' keeps its order in a
|
|
25
|
+
* right-to-left line, where a sign or a currency is neutral and moves to the far end.
|
|
26
|
+
* - Before there are figures the panel says what it is waiting for, rather than showing a row of
|
|
27
|
+
* dashes or a zero that reads as an answer.
|
|
28
|
+
*
|
|
29
|
+
* The tool is a Card at its `surface` variant, the same object as every other card on the page; the
|
|
30
|
+
* ground it stands on decides its fill, in the stylesheet, as ComparisonCards' cards do.
|
|
31
|
+
*/
|
|
32
|
+
export function Estimator({ ground = estimatorDefaults.ground, layout = estimatorDefaults.layout, title, description, inputsLabel, inputs, inputActions, onCalculate, resultsLabel, results, emptyMessage, action, }) {
|
|
33
|
+
const titleId = useId();
|
|
34
|
+
const [band, revealed] = useReveal();
|
|
35
|
+
const figures = Array.isArray(results) ? results : [];
|
|
36
|
+
/*
|
|
37
|
+
Enter in a field asks for the figures, which is what a reader expects of a calculator — the
|
|
38
|
+
page's Calculate button calls the same `onCalculate` from its own press. Only a plain field:
|
|
39
|
+
Enter in a picker's search chooses an answer, and in a text area it starts a line, and both
|
|
40
|
+
are theirs to keep.
|
|
41
|
+
*/
|
|
42
|
+
const calculateOnEnter = (event) => {
|
|
43
|
+
if (event.key !== 'Enter' || onCalculate === undefined)
|
|
44
|
+
return;
|
|
45
|
+
const field = event.target;
|
|
46
|
+
if (!(field instanceof HTMLInputElement))
|
|
47
|
+
return;
|
|
48
|
+
if (/^(checkbox|radio|button|submit|reset|file)$/.test(field.type))
|
|
49
|
+
return;
|
|
50
|
+
if (field.closest('[role="listbox"], [role="dialog"], [aria-haspopup], [aria-autocomplete]') !== null)
|
|
51
|
+
return;
|
|
52
|
+
event.preventDefault();
|
|
53
|
+
onCalculate();
|
|
54
|
+
};
|
|
55
|
+
// The fields are a form, and its submit never leaves the page. A form with one field submits on
|
|
56
|
+
// Enter by itself, and without this a reader pressing it would reload the page from under the tool.
|
|
57
|
+
const stay = (event) => event.preventDefault();
|
|
58
|
+
return (_jsx("section", { className: estimatorParts.root, "data-ground": ground, "data-layout": layout, "aria-labelledby": titleId, children: _jsxs("div", { className: estimatorParts.inner, ref: band, "data-revealed": revealed, children: [_jsxs("div", { className: estimatorParts.header, children: [_jsx("h2", { className: estimatorParts.title, ...arriving(0), id: titleId, children: title }), given(description) ? (_jsx("p", { className: estimatorParts.description, ...arriving(1), children: description })) : null] }), _jsx("div", { className: estimatorParts.tool, "data-theme": ground === 'strong' ? 'dark' : undefined, ...arriving(2), children: _jsx(Card, { variant: "surface", children: _jsxs("div", { className: estimatorParts.panels, children: [_jsxs("form", { className: estimatorParts.inputs, onSubmit: stay, onKeyDown: calculateOnEnter, children: [given(inputsLabel) ? _jsx("h3", { className: estimatorParts['inputs-label'], children: inputsLabel }) : null, inputs, inputActions ? _jsx("div", { className: estimatorParts['input-actions'], children: inputActions }) : null] }), _jsxs("div", { className: estimatorParts.results, "aria-live": "polite", children: [given(resultsLabel) ? _jsx("h3", { className: estimatorParts['results-label'], children: resultsLabel }) : null, figures.length > 0 ? (_jsx("dl", { className: estimatorParts.list, children: figures.map((figure) => (
|
|
59
|
+
// A figure's label is its identity: a tool never shows the same figure twice.
|
|
60
|
+
_jsxs("div", { className: estimatorParts.result, children: [_jsx("dt", { className: estimatorParts['result-label'], children: figure.label }), _jsx("dd", { className: estimatorParts['result-value'], children: _jsx("bdi", { children: figure.value }) }), given(figure.note) ? (_jsx("dd", { className: estimatorParts['result-note'], children: figure.note })) : null] }, figure.label))) })) : given(emptyMessage) ? (_jsx("p", { className: estimatorParts.empty, children: emptyMessage })) : null, action ? _jsx("div", { className: estimatorParts.action, children: action }) : null] })] }) }) })] }) }));
|
|
61
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { EstimatorResult } from '../../estimator-data.js';
|
|
3
|
+
export interface EstimatorOwnProps {
|
|
4
|
+
/**
|
|
5
|
+
* `page` — The default, and the page's own white. A section that has nothing to separate itself from what is above it — most of them — and the ground a reader is already on
|
|
6
|
+
* `subtle` — A quiet step off the page, for a band that wants to read as its own section without changing the page's voice. The one to reach for first when two sections need telling apart. Painted as the band's tint laid over the page's own ground, so it is the same grey whatever is behind the section — in dark mode the tint alone took on whatever it was set against, and on a light surface it was light ink on white.
|
|
7
|
+
* `strong` — `color.bg.brand-strong`, the dark band. It stops the page rather than dividing it, so it is for a section a reader should arrive at — and one per page is usually one more than enough
|
|
8
|
+
*/
|
|
9
|
+
ground?: 'page' | 'subtle' | 'strong';
|
|
10
|
+
/**
|
|
11
|
+
* `stacked` — The default. The heading centred above a wide card, the inputs beside the results — a calculator with several fields and several figures, where the two panels each need room
|
|
12
|
+
* `split` — The heading and a few lines beside the card, the inputs above the results inside it — a compact tool a page introduces in its own words, a converter or a single estimate
|
|
13
|
+
*/
|
|
14
|
+
layout?: 'stacked' | 'split';
|
|
15
|
+
/** Always. What the tool works out, in a few words — 'Trading calculator', 'Buy crypto in seconds', 'Estimate your earnings'. */
|
|
16
|
+
title: string;
|
|
17
|
+
/** A line or two under the title saying what the figures are for or where they come from — 'Live prices, updated every few seconds'. */
|
|
18
|
+
description?: string;
|
|
19
|
+
/** A heading over the inputs panel, where the two panels need naming — 'Calculator' over the fields and 'Results' over the figures. Leave both out for a tool whose fields name themselves. */
|
|
20
|
+
inputsLabel?: string;
|
|
21
|
+
/** Always. The fields the reader fills in — Select, TextInput, SearchInput, SegmentedControl, a slider — in the order they are filled. The page owns their values and works the results out from them. */
|
|
22
|
+
inputs: ReactNode;
|
|
23
|
+
/** A tool that works its figures out when asked rather than as the reader types — a calculation too slow or too costly to run on every keystroke, or one where half-filled fields would show figures that mislead. 'Calculate', whose press calls the page's own handler, and 'Reset' beside it to clear the fields. They share the row; on a phone they stack and each fills the width. */
|
|
24
|
+
inputActions?: ReactNode;
|
|
25
|
+
/** Whenever inputActions holds a Calculate button: Enter in any of the fields calls it, as the button's own press does — wire the button's onClick to the same function. The page works the figures out and passes them as `results`; the results panel announces them. */
|
|
26
|
+
onCalculate?: () => void;
|
|
27
|
+
/** A heading over the results panel, with inputsLabel — 'Results', 'You receive'. */
|
|
28
|
+
resultsLabel?: string;
|
|
29
|
+
/** The figures the inputs produce, in reading order — a `label`, a `value` written exactly as it is to be read, and an optional `note`. Pass an empty list until the inputs are complete, and the panel shows `emptyMessage` instead. Each change is announced politely, so a reader using a screen reader hears the new figures without being interrupted. */
|
|
30
|
+
results: EstimatorResult[];
|
|
31
|
+
/** What the results panel says before there are figures to show — 'Choose an instrument to see the results'. Translate it with the page. */
|
|
32
|
+
emptyMessage?: string;
|
|
33
|
+
/** The one thing a reader does once they have their figures — 'Open an account', 'Trade now', 'Become a partner'. Under the results, where the reader's eye already is. */
|
|
34
|
+
action?: ReactNode;
|
|
35
|
+
}
|
|
36
|
+
export type EstimatorProps = EstimatorOwnProps;
|
|
37
|
+
/** The class each part carries. Apply these — do not retype them. */
|
|
38
|
+
export declare const estimatorParts: {
|
|
39
|
+
readonly root: "dds-estimator";
|
|
40
|
+
readonly inner: "dds-estimator__inner";
|
|
41
|
+
readonly header: "dds-estimator__header";
|
|
42
|
+
readonly title: "dds-estimator__title";
|
|
43
|
+
readonly description: "dds-estimator__description";
|
|
44
|
+
readonly tool: "dds-estimator__tool";
|
|
45
|
+
readonly panels: "dds-estimator__panels";
|
|
46
|
+
readonly inputs: "dds-estimator__inputs";
|
|
47
|
+
readonly 'inputs-label': "dds-estimator__inputs-label";
|
|
48
|
+
readonly results: "dds-estimator__results";
|
|
49
|
+
readonly 'results-label': "dds-estimator__results-label";
|
|
50
|
+
readonly list: "dds-estimator__list";
|
|
51
|
+
readonly result: "dds-estimator__result";
|
|
52
|
+
readonly 'result-label': "dds-estimator__result-label";
|
|
53
|
+
readonly 'result-value': "dds-estimator__result-value";
|
|
54
|
+
readonly 'result-note': "dds-estimator__result-note";
|
|
55
|
+
readonly empty: "dds-estimator__empty";
|
|
56
|
+
readonly action: "dds-estimator__action";
|
|
57
|
+
readonly 'input-actions': "dds-estimator__input-actions";
|
|
58
|
+
};
|
|
59
|
+
/** Generated so defaults cannot drift from the contract. */
|
|
60
|
+
export declare const estimatorDefaults: {
|
|
61
|
+
readonly ground: "page";
|
|
62
|
+
readonly layout: "stacked";
|
|
63
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// GENERATED from estimator.contract.json. Do not edit.
|
|
2
|
+
/** The class each part carries. Apply these — do not retype them. */
|
|
3
|
+
export const estimatorParts = {
|
|
4
|
+
root: 'dds-estimator',
|
|
5
|
+
inner: 'dds-estimator__inner',
|
|
6
|
+
header: 'dds-estimator__header',
|
|
7
|
+
title: 'dds-estimator__title',
|
|
8
|
+
description: 'dds-estimator__description',
|
|
9
|
+
tool: 'dds-estimator__tool',
|
|
10
|
+
panels: 'dds-estimator__panels',
|
|
11
|
+
inputs: 'dds-estimator__inputs',
|
|
12
|
+
'inputs-label': 'dds-estimator__inputs-label',
|
|
13
|
+
results: 'dds-estimator__results',
|
|
14
|
+
'results-label': 'dds-estimator__results-label',
|
|
15
|
+
list: 'dds-estimator__list',
|
|
16
|
+
result: 'dds-estimator__result',
|
|
17
|
+
'result-label': 'dds-estimator__result-label',
|
|
18
|
+
'result-value': 'dds-estimator__result-value',
|
|
19
|
+
'result-note': 'dds-estimator__result-note',
|
|
20
|
+
empty: 'dds-estimator__empty',
|
|
21
|
+
action: 'dds-estimator__action',
|
|
22
|
+
'input-actions': 'dds-estimator__input-actions',
|
|
23
|
+
};
|
|
24
|
+
/** Generated so defaults cannot drift from the contract. */
|
|
25
|
+
export const estimatorDefaults = { ground: 'page', layout: 'stacked' };
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Scoped to this package, like the generated sheet beside it.
|
|
3
|
+
*
|
|
4
|
+
* `:where([data-dds-context="marketing"])` confines these rules without contributing specificity,
|
|
5
|
+
* so each is decided against the generated rule on its own terms.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
* The Card fills the tool. The tool part is the box in the section's layout; the Card inside it is
|
|
10
|
+
* the surface, and it takes the whole of the tool's width rather than its content's.
|
|
11
|
+
*/
|
|
12
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__tool > .dds-card {
|
|
13
|
+
flex: auto;
|
|
14
|
+
/*
|
|
15
|
+
* Card clips so a picture meets its corner; this card holds no picture, and the clip cut the
|
|
16
|
+
* results panel's shadow off where the panel sits close to the card's edge, which left it flat.
|
|
17
|
+
*/
|
|
18
|
+
overflow: visible;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/*
|
|
22
|
+
* The page's fields wrap their words only when a word does not fit. The section breaks anywhere so a
|
|
23
|
+
* long title cannot push the page wide, and that is inherited — but in a field it let the suffix
|
|
24
|
+
* and the panel heading give up their width a letter at a time whenever the panel was tight, "lots"
|
|
25
|
+
* stood on its end beside the box. `break-word` still wraps a word too long for the line, and keeps
|
|
26
|
+
* every word whole while there is room for it.
|
|
27
|
+
*/
|
|
28
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__inputs {
|
|
29
|
+
overflow-wrap: break-word;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* A centred heading centres its measure too, so the line under it sits under the title. */
|
|
33
|
+
:where([data-dds-context="marketing"]) .dds-estimator[data-layout="stacked"] .dds-estimator__description {
|
|
34
|
+
margin-inline: auto;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/*
|
|
38
|
+
* The figures read along a row of digits that change as a reader types, so the digits keep one
|
|
39
|
+
* width: a value going from 1,111 to 8,888 does not shuffle the currency beside it.
|
|
40
|
+
*/
|
|
41
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__result-value {
|
|
42
|
+
font-variant-numeric: tabular-nums;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/*
|
|
46
|
+
* The figures in two columns from the fold, each ruled off beneath — four read as a block of answers
|
|
47
|
+
* beside the fields that made them, and the figures at h4 keep a column each rather than a line
|
|
48
|
+
* each. One column on a phone, where half the panel is too narrow for a figure at that size.
|
|
49
|
+
*/
|
|
50
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__list {
|
|
51
|
+
display: grid;
|
|
52
|
+
grid-template-columns: minmax(0, 1fr);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@container (min-width: 48rem) {
|
|
56
|
+
/*
|
|
57
|
+
* Calculate and Reset share the row under the fields, a half each: neither is the smaller one, and
|
|
58
|
+
* two buttons of different widths under a column of full-width fields read as unfinished. Only
|
|
59
|
+
* from the fold, where they sit side by side — in the phone's column a zero basis is a zero
|
|
60
|
+
* height, and the buttons collapsed to a sliver.
|
|
61
|
+
*/
|
|
62
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__input-actions > * {
|
|
63
|
+
flex: 1 1 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__list {
|
|
67
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* A figure left on its own — the one figure of a converter, the last of an odd set — takes the
|
|
71
|
+
whole row rather than half of it beside an empty cell. */
|
|
72
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__result:last-child:nth-child(odd) {
|
|
73
|
+
grid-column: 1 / -1;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/*
|
|
78
|
+
* The white panel sits close to the card's edge — space.inset.xs from it, where the fields keep the
|
|
79
|
+
* card's full inset. The panel is a surface of its own, and a wide grey margin round it read as a
|
|
80
|
+
* card inside a card; close to the edge it reads as the card's other half.
|
|
81
|
+
*
|
|
82
|
+
* Pulled out by the difference between the two insets. In a column — a phone, and `split` — that is
|
|
83
|
+
* both sides and the foot; beside the fields it is the top, the foot and the far side.
|
|
84
|
+
*/
|
|
85
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__results {
|
|
86
|
+
margin-block-end: calc(var(--space-inset-xs) - var(--space-inset-card));
|
|
87
|
+
margin-inline: calc(var(--space-inset-xs) - var(--space-inset-card));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@container (min-width: 48rem) {
|
|
91
|
+
:where([data-dds-context="marketing"]) .dds-estimator[data-layout="stacked"] .dds-estimator__results {
|
|
92
|
+
margin-block: calc(var(--space-inset-xs) - var(--space-inset-card));
|
|
93
|
+
margin-inline-start: 0;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/*
|
|
98
|
+
* From the fold, under `stacked`: the inputs beside the results, two equal panels. A calculator's
|
|
99
|
+
* fields and its figures are read side by side — change one, watch the other.
|
|
100
|
+
*
|
|
101
|
+
* `48rem` longhand because `var()` is not permitted in a container query condition, and it is the
|
|
102
|
+
* width the rest of this package folds at.
|
|
103
|
+
*/
|
|
104
|
+
@container (min-width: 48rem) {
|
|
105
|
+
:where([data-dds-context="marketing"]) .dds-estimator[data-layout="stacked"] .dds-estimator__panels {
|
|
106
|
+
flex-direction: row;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
:where([data-dds-context="marketing"]) .dds-estimator[data-layout="stacked"] :is(.dds-estimator__inputs, .dds-estimator__results) {
|
|
110
|
+
flex: 1 1 0;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/*
|
|
115
|
+
* At full width, under `split`: the heading beside the card, two equal halves — the reference
|
|
116
|
+
* converter's columns, so the tool reads as wide as the words that introduce it. The panels stay one
|
|
117
|
+
* column inside the card, the inputs above the figures.
|
|
118
|
+
*
|
|
119
|
+
* `64rem`, the width a split of words and a tool can both breathe in; below it the heading goes back
|
|
120
|
+
* above the card, at the start.
|
|
121
|
+
*/
|
|
122
|
+
@container (min-width: 64rem) {
|
|
123
|
+
:where([data-dds-context="marketing"]) .dds-estimator[data-layout="split"] .dds-estimator__inner {
|
|
124
|
+
align-items: center;
|
|
125
|
+
flex-direction: row;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
:where([data-dds-context="marketing"]) .dds-estimator[data-layout="split"] :is(.dds-estimator__header, .dds-estimator__tool) {
|
|
129
|
+
flex: 1 1 0;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/*
|
|
134
|
+
* On a phone the way onward fills the panel, as every section's actions do — the package's rule:
|
|
135
|
+
* a thumb reaches a full-width button, and a short one stranded at the start reads as an aside.
|
|
136
|
+
*/
|
|
137
|
+
@container (max-width: 47.9375rem) {
|
|
138
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__action > * {
|
|
139
|
+
inline-size: 100%;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* And the inputs' own buttons stack, each the full width. */
|
|
143
|
+
:where([data-dds-context="marketing"]) .dds-estimator .dds-estimator__input-actions {
|
|
144
|
+
flex-direction: column;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/*
|
|
149
|
+
* The card on the grey band — ComparisonCards' rule for the same card. Card's recessed fill is the
|
|
150
|
+
* band's grey and vanishes, so it takes the raised fill. The block knows its ground and the card does
|
|
151
|
+
* not, so the answer is here rather than in Card.
|
|
152
|
+
*
|
|
153
|
+
* Nothing for the dark band: there the tool is drawn in the dark theme (see Estimator.tsx), which
|
|
154
|
+
* turns the card, the rules between figures, the muted ink and the page's own fields round together.
|
|
155
|
+
*/
|
|
156
|
+
:where([data-dds-context="marketing"]) .dds-estimator[data-ground="subtle"] .dds-estimator__tool > .dds-card {
|
|
157
|
+
background-color: var(--color-bg-surface-card-raised);
|
|
158
|
+
}
|
|
159
|
+
|
|
@@ -4,6 +4,7 @@ import { useId } from 'react';
|
|
|
4
4
|
import './ImageBanner.css';
|
|
5
5
|
import './image-banner-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 { imageBannerDefaults, imageBannerParts } from './ImageBanner.types.js';
|
|
@@ -37,5 +38,5 @@ export function ImageBanner({ ground = imageBannerDefaults.ground, variant = ima
|
|
|
37
38
|
const titleId = useId();
|
|
38
39
|
const Title = headingLevel;
|
|
39
40
|
const [band, revealed] = useReveal();
|
|
40
|
-
return (_jsx("section", { className: imageBannerParts.root, "data-ground": ground, "data-variant": variant, "data-media-position": mediaPosition, "data-parallax": parallax, "aria-labelledby": titleId, children: _jsx("div", { className: imageBannerParts.inner, ref: band, "data-revealed": revealed, children: _jsxs("div", { className: imageBannerParts.card, children: [_jsxs("div", { className: imageBannerParts.content, children: [_jsx(Title, { className: imageBannerParts.title, id: titleId, ...arriving(0), children: title }), given(description) ? (_jsx("p", { className: imageBannerParts.description, ...arriving(1), children: description })) : null, action ? (_jsx("div", { className: imageBannerParts.action, ...arriving(2), children: action })) : null] }), _jsx("div", { className: imageBannerParts.media, ...(variant === 'cover' ? {} : arriving(3)), children: media })] }) }) }));
|
|
41
|
+
return (_jsx("section", { className: imageBannerParts.root, "data-ground": ground, "data-variant": variant, "data-media-position": mediaPosition, "data-parallax": parallax, "aria-labelledby": titleId, children: _jsx("div", { className: imageBannerParts.inner, ref: band, "data-revealed": revealed, children: _jsxs("div", { className: imageBannerParts.card, children: [_jsxs("div", { className: imageBannerParts.content, children: [_jsx(Title, { className: imageBannerParts.title, id: titleId, ...arriving(0), children: title }), given(description) ? (_jsx("p", { className: imageBannerParts.description, ...arriving(1), children: description })) : null, action ? (_jsx("div", { className: imageBannerParts.action, ...arriving(2), children: action })) : null] }), _jsx("div", { className: imageBannerParts.media, "data-dds-parallax": parallax ? '' : undefined, ...(variant === 'cover' ? {} : arriving(3)), children: media })] }) }) }));
|
|
41
42
|
}
|
|
@@ -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,
|
|
32
|
+
/** The photograph drifts against the scroll as the card passes through the view, the drift every block's picture shares — drawn at motion.scale.parallax and moved from motion.translate.parallax-from to motion.translate.parallax-to 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;
|
|
@@ -226,56 +226,12 @@
|
|
|
226
226
|
|
|
227
227
|
/*
|
|
228
228
|
* `parallax`: the photograph drifts against the scroll as the card passes through the view — the
|
|
229
|
-
* reference design's ParallaxBanner, folded in
|
|
229
|
+
* reference design's ParallaxBanner, folded in as an option. The drift is the one every block's
|
|
230
|
+
* picture shares, in block-parallax.css: the component marks the frame and the picture is scaled
|
|
231
|
+
* and moved rather than resized.
|
|
230
232
|
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
* that
|
|
234
|
-
*
|
|
235
|
-
* Scroll-driven, the way LogoColumns moves its wall: the frame names a view timeline and the image
|
|
236
|
-
* runs down it from half the drift above centre to half below, over the whole pass (`cover`), so as
|
|
237
|
-
* the page carries the card up the photograph falls behind a little and reads as further away.
|
|
238
|
-
* Linear, because the scroll is the easing. The card and the frame clip with `overflow: clip`, not
|
|
239
|
-
* `hidden`: `hidden` makes a box a scroll container, and a view timeline measured against one that
|
|
240
|
-
* never scrolls stands still.
|
|
241
|
-
*
|
|
242
|
-
* A browser with no scroll timeline, and a reader who asked for less motion, get the photograph
|
|
243
|
-
* still and centred, as with `parallax` off.
|
|
233
|
+
* This card once drew its own, by making the photograph taller than the frame with `block-size`.
|
|
234
|
+
* A picture that sets its height inline — Next.js's Image with `fill` writes `height: 100%` on the
|
|
235
|
+
* element — beat that, so the photograph stayed the frame's height and the drift pulled its lower
|
|
236
|
+
* edge up off the card. `scale` is a property nothing sets inline, so the shared drift holds.
|
|
244
237
|
*/
|
|
245
|
-
:where([data-dds-context="marketing"]) .dds-imagebanner[data-parallax="true"]:is([data-variant="split"], [data-variant="cover"]) .dds-imagebanner__media img {
|
|
246
|
-
block-size: calc(100% + var(--motion-distance-drift));
|
|
247
|
-
margin-block-start: calc(var(--motion-distance-drift) / -2);
|
|
248
|
-
object-position: center top;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
@keyframes dds-imagebanner-drift {
|
|
252
|
-
from {
|
|
253
|
-
translate: 0 calc(var(--motion-distance-drift) / -2);
|
|
254
|
-
}
|
|
255
|
-
to {
|
|
256
|
-
translate: 0 calc(var(--motion-distance-drift) / 2);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
@supports (animation-timeline: view()) {
|
|
261
|
-
:where([data-dds-context="marketing"]) .dds-imagebanner .dds-imagebanner__media {
|
|
262
|
-
view-timeline: --dds-imagebanner-frame block;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/* Only for a reader who has not asked for less motion — asked here, around the rule, rather than
|
|
266
|
-
undone after it: the drift's selector outweighs any plain reset, and a reset that loses to it
|
|
267
|
-
leaves the photograph moving for exactly the reader who asked it not to. */
|
|
268
|
-
@media (prefers-reduced-motion: no-preference) {
|
|
269
|
-
:where([data-dds-context="marketing"]) .dds-imagebanner[data-parallax="true"]:is([data-variant="split"], [data-variant="cover"]) .dds-imagebanner__media img {
|
|
270
|
-
animation: dds-imagebanner-drift linear both;
|
|
271
|
-
animation-range: cover;
|
|
272
|
-
animation-timeline: --dds-imagebanner-frame;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
@media (prefers-reduced-motion: reduce) {
|
|
278
|
-
:where([data-dds-context="marketing"]) .dds-imagebanner[data-parallax="true"]:is([data-variant="split"], [data-variant="cover"]) .dds-imagebanner__media img {
|
|
279
|
-
animation: none;
|
|
280
|
-
}
|
|
281
|
-
}
|