@hyvor/design 2.1.8-beta.5 → 2.1.9
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/dist/cloud/HyvorBar/BarNotice/BarNotice.svelte +1 -1
- package/dist/cloud/HyvorBar/BarSupport.svelte +1 -1
- package/dist/cloud/HyvorBar/Organization/BarOrganization.svelte +1 -1
- package/dist/components/Avatar/Avatar.svelte +50 -4
- package/dist/components/Avatar/Avatar.svelte.d.ts +4 -1
- package/dist/components/Avatar/AvatarStack.svelte +6 -3
- package/dist/components/ButtonGroup/ButtonGroup.svelte +82 -0
- package/dist/components/{Button → ButtonGroup}/ButtonGroup.svelte.d.ts +1 -0
- package/dist/components/Modal/ConfirmModalProvider.svelte +6 -9
- package/dist/components/Modal/ModalFooter.svelte +24 -27
- package/dist/components/Toast/ToastMessage.svelte +3 -6
- package/dist/components/Toast/ToastProvider.svelte +8 -4
- package/dist/components/Tooltip/Tooltip.svelte +54 -28
- package/dist/components/Tooltip/Tooltip.svelte.d.ts +15 -35
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.js +1 -1
- package/dist/index.css +4 -1
- package/dist/marketing/Docs/Docs.svelte +2 -2
- package/dist/marketing/Footer/Footer.svelte +98 -402
- package/dist/marketing/Footer/Footer.svelte.d.ts +2 -18
- package/dist/marketing/Footer/FooterLinkList.svelte +3 -10
- package/dist/marketing/Header/Header.svelte +28 -72
- package/dist/marketing/Header/Header.svelte.d.ts +2 -5
- package/dist/marketing/index.d.ts +0 -18
- package/dist/marketing/index.js +0 -17
- package/dist/marketing/social.d.ts +1 -1
- package/dist/marketing/social.js +1 -1
- package/package.json +1 -1
- package/dist/components/Button/ButtonGroup.svelte +0 -18
- package/dist/marketing/AllFeaturesAccordion/AllFeaturesAccordion.svelte +0 -251
- package/dist/marketing/AllFeaturesAccordion/AllFeaturesAccordion.svelte.d.ts +0 -23
- package/dist/marketing/AllFeaturesAccordion/AllFeaturesAccordionFeature.svelte +0 -75
- package/dist/marketing/AllFeaturesAccordion/AllFeaturesAccordionFeature.svelte.d.ts +0 -10
- package/dist/marketing/FAQ/FAQ.svelte +0 -233
- package/dist/marketing/FAQ/FAQ.svelte.d.ts +0 -13
- package/dist/marketing/FeatureSplit/FeatureSplit.svelte +0 -325
- package/dist/marketing/FeatureSplit/FeatureSplit.svelte.d.ts +0 -24
- package/dist/marketing/FullTrialSignup/FullTrialSignup.svelte +0 -164
- package/dist/marketing/FullTrialSignup/FullTrialSignup.svelte.d.ts +0 -18
- package/dist/marketing/Header/HeaderLanguageToggle.svelte +0 -121
- package/dist/marketing/Header/HeaderLanguageToggle.svelte.d.ts +0 -14
- package/dist/marketing/Header/HeaderNavLink.svelte +0 -148
- package/dist/marketing/Header/HeaderNavLink.svelte.d.ts +0 -17
- package/dist/marketing/Header/language.d.ts +0 -18
- package/dist/marketing/Header/language.js +0 -35
- package/dist/marketing/Hero/Hero.svelte +0 -353
- package/dist/marketing/Hero/Hero.svelte.d.ts +0 -16
- package/dist/marketing/LogoStrip/LogoStrip.svelte +0 -204
- package/dist/marketing/LogoStrip/LogoStrip.svelte.d.ts +0 -19
- package/dist/marketing/Seal/CcpaSeal.svelte +0 -29
- package/dist/marketing/Seal/CcpaSeal.svelte.d.ts +0 -6
- package/dist/marketing/Seal/GdprSeal.svelte +0 -60
- package/dist/marketing/Seal/GdprSeal.svelte.d.ts +0 -6
- package/dist/marketing/Seal/IsoSeal.svelte +0 -44
- package/dist/marketing/Seal/IsoSeal.svelte.d.ts +0 -6
- package/dist/marketing/Seal/Seal.svelte +0 -62
- package/dist/marketing/Seal/Seal.svelte.d.ts +0 -8
- package/dist/marketing/Seal/SsoSeal.svelte +0 -31
- package/dist/marketing/Seal/SsoSeal.svelte.d.ts +0 -6
- package/dist/marketing/Seal/ccpa.svg +0 -131
- package/dist/marketing/SpotlightSplit/SpotlightSplit.svelte +0 -256
- package/dist/marketing/SpotlightSplit/SpotlightSplit.svelte.d.ts +0 -19
- package/dist/marketing/Testimonials/Testimonials.svelte +0 -382
- package/dist/marketing/Testimonials/Testimonials.svelte.d.ts +0 -23
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import Button from '../../components/Button/Button.svelte';
|
|
3
|
-
import IconBoxArrowUpRight from '@hyvor/icons/IconBoxArrowUpRight';
|
|
4
|
-
import type { Snippet } from 'svelte';
|
|
5
|
-
|
|
6
|
-
type ButtonConfig = { href: string; label: string; external?: boolean };
|
|
7
|
-
|
|
8
|
-
interface Props {
|
|
9
|
-
title: string | Snippet;
|
|
10
|
-
description?: string;
|
|
11
|
-
stats?: string[];
|
|
12
|
-
button?: ButtonConfig | ButtonConfig[] | null;
|
|
13
|
-
content: Snippet;
|
|
14
|
-
reverse?: boolean;
|
|
15
|
-
background?: string;
|
|
16
|
-
highlightColor?: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
let {
|
|
20
|
-
title,
|
|
21
|
-
description = '',
|
|
22
|
-
stats = [],
|
|
23
|
-
button = null,
|
|
24
|
-
content,
|
|
25
|
-
reverse = false,
|
|
26
|
-
background = 'var(--accent)',
|
|
27
|
-
highlightColor = 'rgba(255, 238, 217, 0.9)'
|
|
28
|
-
}: Props = $props();
|
|
29
|
-
|
|
30
|
-
const buttons = $derived(button ? (Array.isArray(button) ? button : [button]) : []);
|
|
31
|
-
|
|
32
|
-
let mouseX = $state(0);
|
|
33
|
-
let mouseY = $state(0);
|
|
34
|
-
let hovering = $state(false);
|
|
35
|
-
|
|
36
|
-
function handlePointerMove(event: PointerEvent) {
|
|
37
|
-
const bounds = (event.currentTarget as HTMLElement).getBoundingClientRect();
|
|
38
|
-
mouseX = event.clientX - bounds.left;
|
|
39
|
-
mouseY = event.clientY - bounds.top;
|
|
40
|
-
}
|
|
41
|
-
</script>
|
|
42
|
-
|
|
43
|
-
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
44
|
-
<section
|
|
45
|
-
class="hds-spotlight-split"
|
|
46
|
-
onpointermove={handlePointerMove}
|
|
47
|
-
onpointerenter={() => (hovering = true)}
|
|
48
|
-
onpointerleave={() => (hovering = false)}
|
|
49
|
-
style:--mx="{mouseX}px"
|
|
50
|
-
style:--my="{mouseY}px"
|
|
51
|
-
style:--sps-bg={background}
|
|
52
|
-
style:--sps-highlight={highlightColor}
|
|
53
|
-
>
|
|
54
|
-
<svg class="bg-pattern" width="100%" height="100%" aria-hidden="true">
|
|
55
|
-
<defs>
|
|
56
|
-
<pattern id="spotlight-split-grid" width="56" height="56" patternUnits="userSpaceOnUse">
|
|
57
|
-
<path d="M 56 0 L 0 0 0 56" fill="none" stroke="white" stroke-width="1" />
|
|
58
|
-
</pattern>
|
|
59
|
-
<radialGradient id="spotlight-split-fade" cx="50%" cy="40%" r="75%">
|
|
60
|
-
<stop offset="0%" stop-color="white" stop-opacity="1" />
|
|
61
|
-
<stop offset="100%" stop-color="white" stop-opacity="0" />
|
|
62
|
-
</radialGradient>
|
|
63
|
-
<mask id="spotlight-split-mask">
|
|
64
|
-
<rect width="100%" height="100%" fill="url(#spotlight-split-fade)" />
|
|
65
|
-
</mask>
|
|
66
|
-
</defs>
|
|
67
|
-
<rect
|
|
68
|
-
width="100%"
|
|
69
|
-
height="100%"
|
|
70
|
-
fill="url(#spotlight-split-grid)"
|
|
71
|
-
mask="url(#spotlight-split-mask)"
|
|
72
|
-
/>
|
|
73
|
-
</svg>
|
|
74
|
-
|
|
75
|
-
<div class="grid-highlight" class:visible={hovering} aria-hidden="true"></div>
|
|
76
|
-
|
|
77
|
-
<div class="hds-container inner" class:reverse>
|
|
78
|
-
<div class="text-side">
|
|
79
|
-
<h2>
|
|
80
|
-
{#if typeof title === 'string'}
|
|
81
|
-
{@html title}
|
|
82
|
-
{:else}
|
|
83
|
-
{@render title()}
|
|
84
|
-
{/if}
|
|
85
|
-
</h2>
|
|
86
|
-
|
|
87
|
-
{#if description}
|
|
88
|
-
<p class="description">{description}</p>
|
|
89
|
-
{/if}
|
|
90
|
-
|
|
91
|
-
{#if stats.length}
|
|
92
|
-
<div class="stat-row">
|
|
93
|
-
{#each stats as stat}
|
|
94
|
-
<span class="stat">{stat}</span>
|
|
95
|
-
{/each}
|
|
96
|
-
</div>
|
|
97
|
-
{/if}
|
|
98
|
-
|
|
99
|
-
{#if buttons.length}
|
|
100
|
-
<div class="buttons">
|
|
101
|
-
{#each buttons as b}
|
|
102
|
-
<Button
|
|
103
|
-
as="a"
|
|
104
|
-
href={b.href}
|
|
105
|
-
target={b.external ? '_blank' : undefined}
|
|
106
|
-
rel={b.external ? 'noopener' : undefined}
|
|
107
|
-
variant="outline"
|
|
108
|
-
color="input"
|
|
109
|
-
>
|
|
110
|
-
{b.label}
|
|
111
|
-
{#if b.external}
|
|
112
|
-
{#snippet end()}<IconBoxArrowUpRight size={11} />{/snippet}
|
|
113
|
-
{/if}
|
|
114
|
-
</Button>
|
|
115
|
-
{/each}
|
|
116
|
-
</div>
|
|
117
|
-
{/if}
|
|
118
|
-
</div>
|
|
119
|
-
|
|
120
|
-
<div class="content-side">
|
|
121
|
-
{@render content()}
|
|
122
|
-
</div>
|
|
123
|
-
</div>
|
|
124
|
-
</section>
|
|
125
|
-
|
|
126
|
-
<style>
|
|
127
|
-
.hds-spotlight-split {
|
|
128
|
-
position: relative;
|
|
129
|
-
background: var(--sps-bg);
|
|
130
|
-
padding: 96px 0;
|
|
131
|
-
overflow: hidden;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
.bg-pattern {
|
|
135
|
-
position: absolute;
|
|
136
|
-
inset: 0;
|
|
137
|
-
opacity: 0.08;
|
|
138
|
-
pointer-events: none;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
.grid-highlight {
|
|
142
|
-
position: absolute;
|
|
143
|
-
inset: 0;
|
|
144
|
-
pointer-events: none;
|
|
145
|
-
background-image:
|
|
146
|
-
linear-gradient(to right, var(--sps-highlight) 1px, transparent 1px),
|
|
147
|
-
linear-gradient(to bottom, var(--sps-highlight) 1px, transparent 1px);
|
|
148
|
-
background-size: 56px 56px;
|
|
149
|
-
-webkit-mask-image: radial-gradient(
|
|
150
|
-
180px circle at var(--mx, 50%) var(--my, 50%),
|
|
151
|
-
black 0%,
|
|
152
|
-
black 35%,
|
|
153
|
-
transparent 75%
|
|
154
|
-
);
|
|
155
|
-
mask-image: radial-gradient(
|
|
156
|
-
180px circle at var(--mx, 50%) var(--my, 50%),
|
|
157
|
-
black 0%,
|
|
158
|
-
black 35%,
|
|
159
|
-
transparent 75%
|
|
160
|
-
);
|
|
161
|
-
opacity: 0;
|
|
162
|
-
transition: opacity 0.35s ease;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
.grid-highlight.visible {
|
|
166
|
-
opacity: 0.2;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
.inner {
|
|
170
|
-
position: relative;
|
|
171
|
-
display: flex;
|
|
172
|
-
align-items: center;
|
|
173
|
-
justify-content: space-between;
|
|
174
|
-
gap: 48px;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
.inner.reverse {
|
|
178
|
-
flex-direction: row-reverse;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
.text-side {
|
|
182
|
-
flex: 1;
|
|
183
|
-
min-width: 0;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
h2 {
|
|
187
|
-
font-size: clamp(35px, 4vw, 50px);
|
|
188
|
-
font-weight: 800;
|
|
189
|
-
letter-spacing: -0.02em;
|
|
190
|
-
line-height: 1.1;
|
|
191
|
-
color: #fff;
|
|
192
|
-
margin: 0 0 18px;
|
|
193
|
-
font-family: var(--font-serif);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
.description {
|
|
197
|
-
font-size: 16px;
|
|
198
|
-
line-height: 1.7;
|
|
199
|
-
color: rgba(255, 255, 255, 0.7);
|
|
200
|
-
max-width: 460px;
|
|
201
|
-
margin: 0 0 28px;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
.stat-row {
|
|
205
|
-
display: flex;
|
|
206
|
-
flex-wrap: wrap;
|
|
207
|
-
gap: 10px 20px;
|
|
208
|
-
margin-bottom: 28px;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
.stat {
|
|
212
|
-
font-size: 14px;
|
|
213
|
-
font-weight: 600;
|
|
214
|
-
color: rgba(255, 255, 255, 0.85);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
.buttons {
|
|
218
|
-
display: flex;
|
|
219
|
-
flex-wrap: wrap;
|
|
220
|
-
gap: 12px;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
.buttons :global(.button) {
|
|
224
|
-
border-color: rgba(255, 255, 255, 0.3) !important;
|
|
225
|
-
color: rgba(255, 255, 255, 0.85) !important;
|
|
226
|
-
background: transparent !important;
|
|
227
|
-
text-decoration: none !important;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
.buttons :global(.button):hover {
|
|
231
|
-
border-color: rgba(255, 255, 255, 0.55) !important;
|
|
232
|
-
color: #fff !important;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
.content-side {
|
|
236
|
-
flex-shrink: 0;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
@media (max-width: 768px) {
|
|
240
|
-
.inner,
|
|
241
|
-
.inner.reverse {
|
|
242
|
-
flex-direction: column;
|
|
243
|
-
text-align: center;
|
|
244
|
-
gap: 36px;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
.description {
|
|
248
|
-
max-width: 100%;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
.stat-row,
|
|
252
|
-
.buttons {
|
|
253
|
-
justify-content: center;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
</style>
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { Snippet } from 'svelte';
|
|
2
|
-
type ButtonConfig = {
|
|
3
|
-
href: string;
|
|
4
|
-
label: string;
|
|
5
|
-
external?: boolean;
|
|
6
|
-
};
|
|
7
|
-
interface Props {
|
|
8
|
-
title: string | Snippet;
|
|
9
|
-
description?: string;
|
|
10
|
-
stats?: string[];
|
|
11
|
-
button?: ButtonConfig | ButtonConfig[] | null;
|
|
12
|
-
content: Snippet;
|
|
13
|
-
reverse?: boolean;
|
|
14
|
-
background?: string;
|
|
15
|
-
highlightColor?: string;
|
|
16
|
-
}
|
|
17
|
-
declare const SpotlightSplit: import("svelte").Component<Props, {}, "">;
|
|
18
|
-
type SpotlightSplit = ReturnType<typeof SpotlightSplit>;
|
|
19
|
-
export default SpotlightSplit;
|
|
@@ -1,382 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
interface TextReview {
|
|
3
|
-
type: 'text';
|
|
4
|
-
name: string;
|
|
5
|
-
role: string;
|
|
6
|
-
quote: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
interface VideoReview {
|
|
10
|
-
type: 'video';
|
|
11
|
-
name: string;
|
|
12
|
-
role: string;
|
|
13
|
-
videoUrl?: string;
|
|
14
|
-
posterUrl?: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
type Review = TextReview | VideoReview;
|
|
18
|
-
|
|
19
|
-
interface Props {
|
|
20
|
-
label?: string;
|
|
21
|
-
title?: string;
|
|
22
|
-
reviews: Review[];
|
|
23
|
-
// the reviewer name is set in a handwritten-style font (Caveat, loaded
|
|
24
|
-
// from Bunny Fonts) by default — turn off to use the regular typeface,
|
|
25
|
-
// e.g. if the host page can't reach an external font host
|
|
26
|
-
handwrittenNames?: boolean;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
let {
|
|
30
|
-
label = 'Testimonials',
|
|
31
|
-
title = 'Loved by our customers.',
|
|
32
|
-
reviews,
|
|
33
|
-
handwrittenNames = true
|
|
34
|
-
}: Props = $props();
|
|
35
|
-
|
|
36
|
-
// deterministic, non-photographic "identicon" per reviewer name — used as a
|
|
37
|
-
// placeholder avatar when no photo is available for a text review
|
|
38
|
-
function identicon(seed: string) {
|
|
39
|
-
let hash = 0;
|
|
40
|
-
for (let i = 0; i < seed.length; i++) {
|
|
41
|
-
hash = (hash * 31 + seed.charCodeAt(i)) | 0;
|
|
42
|
-
}
|
|
43
|
-
const hue = Math.abs(hash) % 360;
|
|
44
|
-
const cells: { x: number; y: number }[] = [];
|
|
45
|
-
for (let col = 0; col < 3; col++) {
|
|
46
|
-
for (let row = 0; row < 5; row++) {
|
|
47
|
-
const bit = (hash >> (col * 5 + row)) & 1;
|
|
48
|
-
if (!bit) continue;
|
|
49
|
-
cells.push({ x: col, y: row });
|
|
50
|
-
if (col < 2) cells.push({ x: 4 - col, y: row });
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return {
|
|
54
|
-
bg: `hsl(${hue} 45% 92%)`,
|
|
55
|
-
fg: `hsl(${hue} 50% 40%)`,
|
|
56
|
-
cells
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
let scrollEl: HTMLDivElement | undefined = $state();
|
|
61
|
-
let dragging = $state(false);
|
|
62
|
-
let dragStartX = 0;
|
|
63
|
-
let dragStartScroll = 0;
|
|
64
|
-
|
|
65
|
-
function onPointerDown(e: PointerEvent) {
|
|
66
|
-
if (!scrollEl) return;
|
|
67
|
-
dragging = true;
|
|
68
|
-
dragStartX = e.clientX;
|
|
69
|
-
dragStartScroll = scrollEl.scrollLeft;
|
|
70
|
-
scrollEl.setPointerCapture(e.pointerId);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function onPointerMove(e: PointerEvent) {
|
|
74
|
-
if (!dragging || !scrollEl) return;
|
|
75
|
-
scrollEl.scrollLeft = dragStartScroll - (e.clientX - dragStartX);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function onPointerUp() {
|
|
79
|
-
dragging = false;
|
|
80
|
-
}
|
|
81
|
-
</script>
|
|
82
|
-
|
|
83
|
-
<svelte:head>
|
|
84
|
-
{#if handwrittenNames}
|
|
85
|
-
<link href="https://fonts.bunny.net/css?family=caveat:600,700" rel="stylesheet" />
|
|
86
|
-
{/if}
|
|
87
|
-
</svelte:head>
|
|
88
|
-
|
|
89
|
-
<section class="hds-testimonials" class:handwritten={handwrittenNames}>
|
|
90
|
-
<div class="hds-container head">
|
|
91
|
-
<p class="label">{label}</p>
|
|
92
|
-
<h2>{@html title}</h2>
|
|
93
|
-
</div>
|
|
94
|
-
|
|
95
|
-
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
96
|
-
<div
|
|
97
|
-
class="scroll-row"
|
|
98
|
-
class:dragging
|
|
99
|
-
bind:this={scrollEl}
|
|
100
|
-
onpointerdown={onPointerDown}
|
|
101
|
-
onpointermove={onPointerMove}
|
|
102
|
-
onpointerup={onPointerUp}
|
|
103
|
-
onpointerleave={onPointerUp}
|
|
104
|
-
role="region"
|
|
105
|
-
aria-label={label}
|
|
106
|
-
>
|
|
107
|
-
{#each reviews as review}
|
|
108
|
-
{#if review.type === 'text'}
|
|
109
|
-
{@const av = identicon(review.name)}
|
|
110
|
-
<figure class="card text-card hds-box">
|
|
111
|
-
<svg class="avatar" viewBox="0 0 5 5" style="background: {av.bg}" aria-hidden="true">
|
|
112
|
-
{#each av.cells as cell}
|
|
113
|
-
<rect x={cell.x} y={cell.y} width="1" height="1" fill={av.fg} />
|
|
114
|
-
{/each}
|
|
115
|
-
</svg>
|
|
116
|
-
<blockquote>“{review.quote}”</blockquote>
|
|
117
|
-
<figcaption>
|
|
118
|
-
<span class="name">{review.name}</span>
|
|
119
|
-
<span class="role">{review.role}</span>
|
|
120
|
-
</figcaption>
|
|
121
|
-
</figure>
|
|
122
|
-
{:else}
|
|
123
|
-
<figure class="card video-card hds-box">
|
|
124
|
-
<div class="poster">
|
|
125
|
-
<img src={review.posterUrl} alt="" />
|
|
126
|
-
<div class="poster-scrim"></div>
|
|
127
|
-
</div>
|
|
128
|
-
|
|
129
|
-
<div class="video-avatar">
|
|
130
|
-
<img src={review.posterUrl} alt="" />
|
|
131
|
-
</div>
|
|
132
|
-
|
|
133
|
-
<button class="play-btn" aria-label="Play video testimonial" disabled>
|
|
134
|
-
<svg width="20" height="20" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
|
|
135
|
-
<path d="M5 3.5v9l8-4.5-8-4.5z" />
|
|
136
|
-
</svg>
|
|
137
|
-
</button>
|
|
138
|
-
|
|
139
|
-
<figcaption>
|
|
140
|
-
<span class="name">{review.name}</span>
|
|
141
|
-
<span class="role">{review.role}</span>
|
|
142
|
-
</figcaption>
|
|
143
|
-
</figure>
|
|
144
|
-
{/if}
|
|
145
|
-
{/each}
|
|
146
|
-
</div>
|
|
147
|
-
</section>
|
|
148
|
-
|
|
149
|
-
<style>
|
|
150
|
-
.hds-testimonials {
|
|
151
|
-
background: var(--accent-lightest);
|
|
152
|
-
padding: 100px 0 96px;
|
|
153
|
-
overflow: hidden;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
.head {
|
|
157
|
-
margin-bottom: 44px;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
.label {
|
|
161
|
-
font-size: 14px;
|
|
162
|
-
font-weight: 600;
|
|
163
|
-
letter-spacing: 0.09em;
|
|
164
|
-
text-transform: uppercase;
|
|
165
|
-
color: var(--accent);
|
|
166
|
-
margin: 0 0 16px;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
h2 {
|
|
170
|
-
font-size: clamp(30px, 4vw, 44px);
|
|
171
|
-
font-weight: 800;
|
|
172
|
-
letter-spacing: -0.02em;
|
|
173
|
-
line-height: 1.15;
|
|
174
|
-
color: var(--text);
|
|
175
|
-
margin: 0;
|
|
176
|
-
font-family: var(--font-serif);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
.scroll-row {
|
|
180
|
-
display: flex;
|
|
181
|
-
align-items: stretch;
|
|
182
|
-
gap: 20px;
|
|
183
|
-
overflow-x: auto;
|
|
184
|
-
/* explicit, not left to default — a lone overflow-x: auto silently
|
|
185
|
-
forces overflow-y to auto too (per spec, when only one axis is
|
|
186
|
-
'visible'), which was clipping each card's box-shadow into a hard
|
|
187
|
-
line at the top/bottom of the row instead of letting it fade out */
|
|
188
|
-
overflow-y: visible;
|
|
189
|
-
scroll-snap-type: x proximity;
|
|
190
|
-
cursor: grab;
|
|
191
|
-
/* top/bottom padding must clear --box-shadow's blur radius, or the
|
|
192
|
-
shadow still gets clipped by the row's own padding-box edge */
|
|
193
|
-
padding: 32px 15px 36px max(15px, calc((100vw - 1000px) / 2));
|
|
194
|
-
scrollbar-width: none;
|
|
195
|
-
-webkit-overflow-scrolling: touch;
|
|
196
|
-
touch-action: pan-y;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
.scroll-row.dragging {
|
|
200
|
-
cursor: grabbing;
|
|
201
|
-
scroll-snap-type: none;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
.scroll-row::-webkit-scrollbar {
|
|
205
|
-
display: none;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
.card {
|
|
209
|
-
position: relative;
|
|
210
|
-
flex: 0 0 auto;
|
|
211
|
-
width: 320px;
|
|
212
|
-
height: 440px;
|
|
213
|
-
margin: 0;
|
|
214
|
-
box-sizing: border-box;
|
|
215
|
-
scroll-snap-align: start;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
.text-card {
|
|
219
|
-
padding: 28px;
|
|
220
|
-
display: flex;
|
|
221
|
-
flex-direction: column;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
.avatar {
|
|
225
|
-
width: 46px;
|
|
226
|
-
height: 46px;
|
|
227
|
-
border-radius: 50%;
|
|
228
|
-
flex-shrink: 0;
|
|
229
|
-
display: block;
|
|
230
|
-
shape-rendering: crispEdges;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
blockquote {
|
|
234
|
-
font-size: 16px;
|
|
235
|
-
line-height: 1.7;
|
|
236
|
-
color: var(--text);
|
|
237
|
-
margin: 20px 0 0;
|
|
238
|
-
flex: 1;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
.text-card figcaption {
|
|
242
|
-
display: flex;
|
|
243
|
-
flex-direction: column;
|
|
244
|
-
gap: 0;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
.name {
|
|
248
|
-
font-size: 16px;
|
|
249
|
-
font-weight: 600;
|
|
250
|
-
color: var(--text);
|
|
251
|
-
line-height: 1.2;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
.handwritten .name {
|
|
255
|
-
font-family: 'Caveat', cursive;
|
|
256
|
-
font-size: 28px;
|
|
257
|
-
font-weight: 600;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
.role {
|
|
261
|
-
font-size: 13px;
|
|
262
|
-
color: var(--text-light);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
.video-card {
|
|
266
|
-
width: 280px;
|
|
267
|
-
overflow: hidden;
|
|
268
|
-
padding: 0;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
.poster {
|
|
272
|
-
position: absolute;
|
|
273
|
-
inset: 0;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
.poster img {
|
|
277
|
-
width: 100%;
|
|
278
|
-
height: 100%;
|
|
279
|
-
object-fit: cover;
|
|
280
|
-
display: block;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/* even out contrast across whatever poster photo lands here, so the
|
|
284
|
-
play button and captions stay legible */
|
|
285
|
-
.poster-scrim {
|
|
286
|
-
position: absolute;
|
|
287
|
-
inset: 0;
|
|
288
|
-
background: linear-gradient(160deg, rgba(20, 14, 12, 0.45), rgba(20, 14, 12, 0.65));
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
.video-avatar {
|
|
292
|
-
position: absolute;
|
|
293
|
-
top: 16px;
|
|
294
|
-
left: 16px;
|
|
295
|
-
width: 40px;
|
|
296
|
-
height: 40px;
|
|
297
|
-
border-radius: 50%;
|
|
298
|
-
overflow: hidden;
|
|
299
|
-
box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.85);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
.video-avatar img {
|
|
303
|
-
width: 100%;
|
|
304
|
-
height: 100%;
|
|
305
|
-
object-fit: cover;
|
|
306
|
-
display: block;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
.play-btn {
|
|
310
|
-
position: absolute;
|
|
311
|
-
top: 50%;
|
|
312
|
-
left: 50%;
|
|
313
|
-
transform: translate(-50%, -50%);
|
|
314
|
-
width: 56px;
|
|
315
|
-
height: 56px;
|
|
316
|
-
border-radius: 50%;
|
|
317
|
-
background: rgba(255, 255, 255, 0.18);
|
|
318
|
-
backdrop-filter: blur(6px);
|
|
319
|
-
-webkit-backdrop-filter: blur(6px);
|
|
320
|
-
border: 1px solid rgba(255, 255, 255, 0.4);
|
|
321
|
-
color: #fff;
|
|
322
|
-
display: flex;
|
|
323
|
-
align-items: center;
|
|
324
|
-
justify-content: center;
|
|
325
|
-
cursor: default;
|
|
326
|
-
transition:
|
|
327
|
-
transform 0.2s,
|
|
328
|
-
background 0.2s;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
.play-btn:not(:disabled):hover {
|
|
332
|
-
transform: translate(-50%, -50%) scale(1.08);
|
|
333
|
-
background: rgba(255, 255, 255, 0.28);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
.play-btn svg {
|
|
337
|
-
margin-left: 3px;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
.video-card figcaption {
|
|
341
|
-
position: absolute;
|
|
342
|
-
left: 0;
|
|
343
|
-
right: 0;
|
|
344
|
-
bottom: 0;
|
|
345
|
-
padding: 40px 20px 20px;
|
|
346
|
-
background: linear-gradient(to top, rgba(0, 0, 0, 0.75), transparent);
|
|
347
|
-
display: flex;
|
|
348
|
-
flex-direction: column;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
.video-card .name {
|
|
352
|
-
color: #fff;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
.video-card .role {
|
|
356
|
-
color: rgba(255, 255, 255, 0.75);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
@media (max-width: 600px) {
|
|
360
|
-
/* keep in step with the wider .hds-container gutter set globally for
|
|
361
|
-
the host page at this same breakpoint */
|
|
362
|
-
.scroll-row {
|
|
363
|
-
padding: 32px 20px 36px max(20px, calc((100vw - 1000px) / 2));
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
@media (max-width: 768px) {
|
|
368
|
-
.head {
|
|
369
|
-
text-align: center;
|
|
370
|
-
margin-bottom: 32px;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
.card {
|
|
374
|
-
width: 280px;
|
|
375
|
-
height: 400px;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
.video-card {
|
|
379
|
-
width: 240px;
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
</style>
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
interface TextReview {
|
|
2
|
-
type: 'text';
|
|
3
|
-
name: string;
|
|
4
|
-
role: string;
|
|
5
|
-
quote: string;
|
|
6
|
-
}
|
|
7
|
-
interface VideoReview {
|
|
8
|
-
type: 'video';
|
|
9
|
-
name: string;
|
|
10
|
-
role: string;
|
|
11
|
-
videoUrl?: string;
|
|
12
|
-
posterUrl?: string;
|
|
13
|
-
}
|
|
14
|
-
type Review = TextReview | VideoReview;
|
|
15
|
-
interface Props {
|
|
16
|
-
label?: string;
|
|
17
|
-
title?: string;
|
|
18
|
-
reviews: Review[];
|
|
19
|
-
handwrittenNames?: boolean;
|
|
20
|
-
}
|
|
21
|
-
declare const Testimonials: import("svelte").Component<Props, {}, "">;
|
|
22
|
-
type Testimonials = ReturnType<typeof Testimonials>;
|
|
23
|
-
export default Testimonials;
|