@hyvor/design 2.1.8-beta.1 → 2.1.8-beta.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/dist/marketing/AllFeaturesAccordion/AllFeaturesAccordion.svelte +251 -0
- package/dist/marketing/AllFeaturesAccordion/AllFeaturesAccordion.svelte.d.ts +23 -0
- package/dist/marketing/AllFeaturesAccordion/AllFeaturesAccordionFeature.svelte +75 -0
- package/dist/marketing/AllFeaturesAccordion/AllFeaturesAccordionFeature.svelte.d.ts +10 -0
- package/dist/marketing/FAQ/FAQ.svelte +233 -0
- package/dist/marketing/FAQ/FAQ.svelte.d.ts +13 -0
- package/dist/marketing/FeatureSplit/FeatureSplit.svelte +325 -0
- package/dist/marketing/FeatureSplit/FeatureSplit.svelte.d.ts +24 -0
- package/dist/marketing/Footer/Footer.svelte +15 -10
- package/dist/marketing/Footer/Footer.svelte.d.ts +5 -0
- package/dist/marketing/FullTrialSignup/FullTrialSignup.svelte +164 -0
- package/dist/marketing/FullTrialSignup/FullTrialSignup.svelte.d.ts +18 -0
- package/dist/marketing/Header/Header.svelte +7 -3
- package/dist/marketing/Header/Header.svelte.d.ts +2 -0
- package/dist/marketing/Header/HeaderLanguageToggle.svelte +121 -0
- package/dist/marketing/Header/HeaderLanguageToggle.svelte.d.ts +14 -0
- package/dist/marketing/Header/language.d.ts +18 -0
- package/dist/marketing/Header/language.js +35 -0
- package/dist/marketing/Hero/Hero.svelte +353 -0
- package/dist/marketing/Hero/Hero.svelte.d.ts +16 -0
- package/dist/marketing/Testimonials/Testimonials.svelte +382 -0
- package/dist/marketing/Testimonials/Testimonials.svelte.d.ts +23 -0
- package/dist/marketing/index.d.ts +10 -0
- package/dist/marketing/index.js +9 -0
- package/package.json +1 -1
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Button from '../../components/Button/Button.svelte';
|
|
3
|
+
import IconBoxArrowUpRight from '@hyvor/icons/IconBoxArrowUpRight';
|
|
4
|
+
import IconCheckCircleFill from '@hyvor/icons/IconCheckCircleFill';
|
|
5
|
+
import type { Snippet } from 'svelte';
|
|
6
|
+
|
|
7
|
+
type ButtonConfig = { href: string; label: string; external?: boolean };
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
eyebrow: string;
|
|
11
|
+
title: string | Snippet;
|
|
12
|
+
description: string;
|
|
13
|
+
bullets?: string[] | Snippet;
|
|
14
|
+
button?: ButtonConfig | ButtonConfig[] | null;
|
|
15
|
+
flip?: boolean;
|
|
16
|
+
altBg?: boolean;
|
|
17
|
+
overlap?: boolean;
|
|
18
|
+
interactiveBullets?: boolean;
|
|
19
|
+
visual: Snippet<[activeBullet: number]>;
|
|
20
|
+
after?: Snippet;
|
|
21
|
+
left?: Snippet;
|
|
22
|
+
max?: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let {
|
|
26
|
+
eyebrow,
|
|
27
|
+
title,
|
|
28
|
+
description,
|
|
29
|
+
bullets = [],
|
|
30
|
+
button = null,
|
|
31
|
+
flip = false,
|
|
32
|
+
altBg = false,
|
|
33
|
+
overlap = false,
|
|
34
|
+
interactiveBullets = false,
|
|
35
|
+
visual,
|
|
36
|
+
after,
|
|
37
|
+
left,
|
|
38
|
+
max = false
|
|
39
|
+
}: Props = $props();
|
|
40
|
+
|
|
41
|
+
let inView = $state(false);
|
|
42
|
+
let activeBullet = $state(0);
|
|
43
|
+
|
|
44
|
+
// how long the active bullet's progress bar takes to fill before auto-advancing
|
|
45
|
+
const AUTO_ADVANCE_MS = 5000;
|
|
46
|
+
|
|
47
|
+
$effect(() => {
|
|
48
|
+
if (!interactiveBullets || !inView) return;
|
|
49
|
+
if (!Array.isArray(bullets) || bullets.length <= 1) return;
|
|
50
|
+
activeBullet;
|
|
51
|
+
const count = bullets.length;
|
|
52
|
+
const timer = setTimeout(() => {
|
|
53
|
+
activeBullet = (activeBullet + 1) % count;
|
|
54
|
+
}, AUTO_ADVANCE_MS);
|
|
55
|
+
return () => clearTimeout(timer);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
function onView(node: HTMLElement, callback: () => void) {
|
|
59
|
+
const observer = new IntersectionObserver(
|
|
60
|
+
(entries) => {
|
|
61
|
+
if (entries[0]?.isIntersecting) {
|
|
62
|
+
callback();
|
|
63
|
+
observer.disconnect();
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
{ threshold: 0.25 }
|
|
67
|
+
);
|
|
68
|
+
observer.observe(node);
|
|
69
|
+
return {
|
|
70
|
+
destroy() {
|
|
71
|
+
observer.disconnect();
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
</script>
|
|
76
|
+
|
|
77
|
+
<section class="hds-feature-split" class:alt-bg={altBg}>
|
|
78
|
+
<div class={max ? 'hds-container-max split' : 'hds-container split'} class:flip class:overlap>
|
|
79
|
+
<div class="text-col" class:in-view={inView} use:onView={() => (inView = true)}>
|
|
80
|
+
{#if left}
|
|
81
|
+
{@render left()}
|
|
82
|
+
{:else}
|
|
83
|
+
<span class="eyebrow">{eyebrow}</span>
|
|
84
|
+
<h2>
|
|
85
|
+
{#if typeof title === 'string'}
|
|
86
|
+
{@html title}
|
|
87
|
+
{:else}
|
|
88
|
+
{@render title()}
|
|
89
|
+
{/if}
|
|
90
|
+
</h2>
|
|
91
|
+
<p>{description}</p>
|
|
92
|
+
|
|
93
|
+
{#if Array.isArray(bullets)}
|
|
94
|
+
{#if bullets.length}
|
|
95
|
+
<ul class="bullets" class:interactive={interactiveBullets}>
|
|
96
|
+
{#each bullets as b, i}
|
|
97
|
+
<li>
|
|
98
|
+
{#if interactiveBullets}
|
|
99
|
+
<button
|
|
100
|
+
type="button"
|
|
101
|
+
class="bullet-btn"
|
|
102
|
+
class:active={activeBullet === i}
|
|
103
|
+
style="animation-duration: {AUTO_ADVANCE_MS}ms"
|
|
104
|
+
onclick={() => (activeBullet = i)}
|
|
105
|
+
>
|
|
106
|
+
<IconCheckCircleFill size={14} />{b}
|
|
107
|
+
</button>
|
|
108
|
+
{:else}
|
|
109
|
+
<IconCheckCircleFill size={14} />{b}
|
|
110
|
+
{/if}
|
|
111
|
+
</li>
|
|
112
|
+
{/each}
|
|
113
|
+
</ul>
|
|
114
|
+
{/if}
|
|
115
|
+
{:else if bullets}
|
|
116
|
+
{@render bullets()}
|
|
117
|
+
{/if}
|
|
118
|
+
|
|
119
|
+
{#if button}
|
|
120
|
+
{@const buttons = Array.isArray(button) ? button : [button]}
|
|
121
|
+
<div class="buttons">
|
|
122
|
+
{#each buttons as b}
|
|
123
|
+
<Button
|
|
124
|
+
as="a"
|
|
125
|
+
href={b.href}
|
|
126
|
+
target={b.external ? '_blank' : undefined}
|
|
127
|
+
rel={b.external ? 'noopener' : undefined}
|
|
128
|
+
variant="outline"
|
|
129
|
+
size="small"
|
|
130
|
+
>
|
|
131
|
+
{b.label}
|
|
132
|
+
{#snippet end()}<IconBoxArrowUpRight size={11} />{/snippet}
|
|
133
|
+
</Button>
|
|
134
|
+
{/each}
|
|
135
|
+
</div>
|
|
136
|
+
{/if}
|
|
137
|
+
{/if}
|
|
138
|
+
</div>
|
|
139
|
+
|
|
140
|
+
<div class="visual-col">
|
|
141
|
+
{@render visual(activeBullet)}
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
{#if after}
|
|
146
|
+
<div class="hds-container-max">
|
|
147
|
+
{@render after()}
|
|
148
|
+
</div>
|
|
149
|
+
{/if}
|
|
150
|
+
</section>
|
|
151
|
+
|
|
152
|
+
<style>
|
|
153
|
+
.hds-feature-split {
|
|
154
|
+
padding: 100px 0;
|
|
155
|
+
overflow-x: hidden;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.alt-bg {
|
|
159
|
+
background: color-mix(in srgb, var(--accent) 10%, var(--background));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.split {
|
|
163
|
+
display: flex;
|
|
164
|
+
align-items: center;
|
|
165
|
+
gap: 72px;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.split.flip {
|
|
169
|
+
flex-direction: row-reverse;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.text-col,
|
|
173
|
+
.visual-col {
|
|
174
|
+
flex: 1;
|
|
175
|
+
min-width: 0;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.text-col {
|
|
179
|
+
opacity: 0;
|
|
180
|
+
transform: translateY(18px);
|
|
181
|
+
transition:
|
|
182
|
+
opacity 0.7s cubic-bezier(0.22, 1, 0.36, 1),
|
|
183
|
+
transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.text-col.in-view {
|
|
187
|
+
opacity: 1;
|
|
188
|
+
transform: none;
|
|
189
|
+
}
|
|
190
|
+
.split.overlap .text-col {
|
|
191
|
+
z-index: 2;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.eyebrow {
|
|
195
|
+
display: inline-block;
|
|
196
|
+
font-size: 12px;
|
|
197
|
+
font-weight: 700;
|
|
198
|
+
letter-spacing: 0.1em;
|
|
199
|
+
text-transform: uppercase;
|
|
200
|
+
color: var(--accent);
|
|
201
|
+
margin-bottom: 16px;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
h2 {
|
|
205
|
+
font-size: clamp(26px, 3.5vw, 38px);
|
|
206
|
+
font-weight: 800;
|
|
207
|
+
letter-spacing: -0.02em;
|
|
208
|
+
line-height: 1.15;
|
|
209
|
+
margin: 0 0 16px;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
p {
|
|
213
|
+
font-size: 16px;
|
|
214
|
+
line-height: 1.7;
|
|
215
|
+
color: var(--text-light);
|
|
216
|
+
margin: 0 0 28px;
|
|
217
|
+
max-width: 440px;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.bullets {
|
|
221
|
+
list-style: none;
|
|
222
|
+
margin: 0 0 32px;
|
|
223
|
+
padding: 0;
|
|
224
|
+
display: flex;
|
|
225
|
+
flex-direction: column;
|
|
226
|
+
gap: 10px;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.bullets li {
|
|
230
|
+
display: flex;
|
|
231
|
+
align-items: flex-start;
|
|
232
|
+
gap: 10px;
|
|
233
|
+
font-size: 1rem;
|
|
234
|
+
text-align: left;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.bullets li :global(svg) {
|
|
238
|
+
color: var(--accent);
|
|
239
|
+
flex-shrink: 0;
|
|
240
|
+
margin-top: 0.3em;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.bullet-btn {
|
|
244
|
+
position: relative;
|
|
245
|
+
display: flex;
|
|
246
|
+
align-items: flex-start;
|
|
247
|
+
gap: 10px;
|
|
248
|
+
width: 100%;
|
|
249
|
+
padding: 8px 10px;
|
|
250
|
+
border: none;
|
|
251
|
+
border-radius: 20px;
|
|
252
|
+
background-color: transparent;
|
|
253
|
+
background-image: linear-gradient(
|
|
254
|
+
color-mix(in srgb, var(--accent) 14%, transparent),
|
|
255
|
+
color-mix(in srgb, var(--accent) 14%, transparent)
|
|
256
|
+
);
|
|
257
|
+
background-repeat: no-repeat;
|
|
258
|
+
background-position: left center;
|
|
259
|
+
background-size: 0% 100%;
|
|
260
|
+
color: var(--text-light);
|
|
261
|
+
font: inherit;
|
|
262
|
+
font-size: 1rem;
|
|
263
|
+
text-align: left;
|
|
264
|
+
cursor: pointer;
|
|
265
|
+
transition:
|
|
266
|
+
background-color 0.2s,
|
|
267
|
+
color 0.2s;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.bullet-btn:hover {
|
|
271
|
+
background-color: color-mix(in srgb, var(--text) 4%, transparent);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.bullet-btn.active {
|
|
275
|
+
color: var(--text);
|
|
276
|
+
animation-name: bullet-fill;
|
|
277
|
+
animation-timing-function: linear;
|
|
278
|
+
animation-fill-mode: forwards;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
@keyframes bullet-fill {
|
|
282
|
+
from {
|
|
283
|
+
background-size: 0% 100%;
|
|
284
|
+
}
|
|
285
|
+
to {
|
|
286
|
+
background-size: 100% 100%;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.bullets.interactive li :global(svg) {
|
|
291
|
+
color: var(--text-light);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.bullets.interactive li:has(.bullet-btn.active) :global(svg) {
|
|
295
|
+
color: var(--accent);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.buttons {
|
|
299
|
+
display: flex;
|
|
300
|
+
flex-wrap: wrap;
|
|
301
|
+
gap: 12px;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
@media (max-width: 900px) {
|
|
305
|
+
.split,
|
|
306
|
+
.split.flip {
|
|
307
|
+
flex-direction: column;
|
|
308
|
+
align-items: stretch;
|
|
309
|
+
gap: 48px;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.text-col {
|
|
313
|
+
width: 100%;
|
|
314
|
+
text-align: left;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.visual-col {
|
|
318
|
+
width: 100%;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
p {
|
|
322
|
+
max-width: 100%;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
</style>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type ButtonConfig = {
|
|
3
|
+
href: string;
|
|
4
|
+
label: string;
|
|
5
|
+
external?: boolean;
|
|
6
|
+
};
|
|
7
|
+
interface Props {
|
|
8
|
+
eyebrow: string;
|
|
9
|
+
title: string | Snippet;
|
|
10
|
+
description: string;
|
|
11
|
+
bullets?: string[] | Snippet;
|
|
12
|
+
button?: ButtonConfig | ButtonConfig[] | null;
|
|
13
|
+
flip?: boolean;
|
|
14
|
+
altBg?: boolean;
|
|
15
|
+
overlap?: boolean;
|
|
16
|
+
interactiveBullets?: boolean;
|
|
17
|
+
visual: Snippet<[activeBullet: number]>;
|
|
18
|
+
after?: Snippet;
|
|
19
|
+
left?: Snippet;
|
|
20
|
+
max?: boolean;
|
|
21
|
+
}
|
|
22
|
+
declare const FeatureSplit: import("svelte").Component<Props, {}, "">;
|
|
23
|
+
type FeatureSplit = ReturnType<typeof FeatureSplit>;
|
|
24
|
+
export default FeatureSplit;
|
|
@@ -36,6 +36,11 @@
|
|
|
36
36
|
recordVisit?: boolean;
|
|
37
37
|
center?: Snippet;
|
|
38
38
|
max?: boolean;
|
|
39
|
+
logoAltText?: string;
|
|
40
|
+
copyEmailLabel?: string;
|
|
41
|
+
copiedLabel?: string;
|
|
42
|
+
gdprText?: string;
|
|
43
|
+
fromFranceText?: string;
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
let {
|
|
@@ -54,13 +59,13 @@
|
|
|
54
59
|
affiliate = true,
|
|
55
60
|
recordVisit = true,
|
|
56
61
|
center,
|
|
57
|
-
max = false
|
|
62
|
+
max = false,
|
|
63
|
+
logoAltText = 'Logo',
|
|
64
|
+
copyEmailLabel = 'Copy email',
|
|
65
|
+
copiedLabel = 'Copied!',
|
|
66
|
+
gdprText = 'GDPR Compliant',
|
|
67
|
+
fromFranceText = 'From France'
|
|
58
68
|
}: Props = $props();
|
|
59
|
-
|
|
60
|
-
// same "explicit logo, else derive from product" pattern as Header — pass
|
|
61
|
-
// `product` (e.g. "blogs") for the mascot to follow that product's logo
|
|
62
|
-
// automatically, or `logo` to override it directly. Neither given means
|
|
63
|
-
// no mascot.
|
|
64
69
|
const mascotLogo = $derived(
|
|
65
70
|
logo || (product ? `${instance}/api/public/logo/${product}.svg` : undefined)
|
|
66
71
|
);
|
|
@@ -134,7 +139,7 @@
|
|
|
134
139
|
<div class="mascot-wrap" use:onView={() => (mascotInView = true)}>
|
|
135
140
|
<img
|
|
136
141
|
src={mascotLogo}
|
|
137
|
-
alt="{name}
|
|
142
|
+
alt="{name} {logoAltText}"
|
|
138
143
|
width="100"
|
|
139
144
|
height="100"
|
|
140
145
|
class:in-view={mascotInView}
|
|
@@ -159,7 +164,7 @@
|
|
|
159
164
|
<IconEnvelope size={14} />
|
|
160
165
|
{email}
|
|
161
166
|
</a>
|
|
162
|
-
<Tooltip text={emailCopied ?
|
|
167
|
+
<Tooltip text={emailCopied ? copiedLabel : copyEmailLabel} position="top">
|
|
163
168
|
<IconButton
|
|
164
169
|
size="small"
|
|
165
170
|
variant="invisible"
|
|
@@ -210,13 +215,13 @@
|
|
|
210
215
|
</svg>
|
|
211
216
|
<span class="lock"><IconLockFill size={10} /></span>
|
|
212
217
|
</span>
|
|
213
|
-
<span class="gdpr-chip-text">
|
|
218
|
+
<span class="gdpr-chip-text">{gdprText}</span>
|
|
214
219
|
</a>
|
|
215
220
|
</div>
|
|
216
221
|
{/if}
|
|
217
222
|
|
|
218
223
|
<div class="bottom-right">
|
|
219
|
-
<div class="france">
|
|
224
|
+
<div class="france">{fromFranceText} <span class="flag">🇫🇷</span></div>
|
|
220
225
|
</div>
|
|
221
226
|
</div>
|
|
222
227
|
</Container>
|
|
@@ -17,6 +17,11 @@ interface Props {
|
|
|
17
17
|
recordVisit?: boolean;
|
|
18
18
|
center?: Snippet;
|
|
19
19
|
max?: boolean;
|
|
20
|
+
logoAltText?: string;
|
|
21
|
+
copyEmailLabel?: string;
|
|
22
|
+
copiedLabel?: string;
|
|
23
|
+
gdprText?: string;
|
|
24
|
+
fromFranceText?: string;
|
|
20
25
|
}
|
|
21
26
|
declare const Footer: import("svelte").Component<Props, {}, "">;
|
|
22
27
|
type Footer = ReturnType<typeof Footer>;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Button from '../../components/Button/Button.svelte';
|
|
3
|
+
import IconCheckCircleFill from '@hyvor/icons/IconCheckCircleFill';
|
|
4
|
+
|
|
5
|
+
type ButtonConfig = { href: string; label: string; external?: boolean };
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
badge?: string | null;
|
|
9
|
+
title?: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
button?: ButtonConfig | ButtonConfig[] | null;
|
|
12
|
+
checks?: string[];
|
|
13
|
+
accentColor?: string;
|
|
14
|
+
accentColorDark?: string;
|
|
15
|
+
[key: string]: any;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let {
|
|
19
|
+
badge = null,
|
|
20
|
+
title = 'Start your trial today',
|
|
21
|
+
description = '',
|
|
22
|
+
button = null,
|
|
23
|
+
checks = ['14-day free trial', 'No credit card required', 'Cancel anytime'],
|
|
24
|
+
accentColor,
|
|
25
|
+
accentColorDark,
|
|
26
|
+
...rest
|
|
27
|
+
}: Props = $props();
|
|
28
|
+
|
|
29
|
+
const buttons = $derived(button ? (Array.isArray(button) ? button : [button]) : []);
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<section
|
|
33
|
+
class="hds-full-trial-signup"
|
|
34
|
+
style:--fts-accent={accentColor}
|
|
35
|
+
style:--fts-accent-dark={accentColorDark}
|
|
36
|
+
{...rest}
|
|
37
|
+
>
|
|
38
|
+
<div class="hds-container inner">
|
|
39
|
+
{#if badge}
|
|
40
|
+
<div class="badge">{badge}</div>
|
|
41
|
+
{/if}
|
|
42
|
+
|
|
43
|
+
<h2>{title}</h2>
|
|
44
|
+
|
|
45
|
+
{#if description}
|
|
46
|
+
<p>{description}</p>
|
|
47
|
+
{/if}
|
|
48
|
+
|
|
49
|
+
{#if buttons.length}
|
|
50
|
+
<div class="cta-actions">
|
|
51
|
+
{#each buttons as b}
|
|
52
|
+
<Button
|
|
53
|
+
as="a"
|
|
54
|
+
size="large"
|
|
55
|
+
href={b.href}
|
|
56
|
+
target={b.external ? '_blank' : undefined}
|
|
57
|
+
rel={b.external ? 'noopener' : undefined}
|
|
58
|
+
>
|
|
59
|
+
{b.label}
|
|
60
|
+
</Button>
|
|
61
|
+
{/each}
|
|
62
|
+
</div>
|
|
63
|
+
{/if}
|
|
64
|
+
|
|
65
|
+
{#if checks.length}
|
|
66
|
+
<div class="checks">
|
|
67
|
+
{#each checks as check}
|
|
68
|
+
<div class="check">
|
|
69
|
+
<IconCheckCircleFill size={15} />
|
|
70
|
+
{check}
|
|
71
|
+
</div>
|
|
72
|
+
{/each}
|
|
73
|
+
</div>
|
|
74
|
+
{/if}
|
|
75
|
+
</div>
|
|
76
|
+
</section>
|
|
77
|
+
|
|
78
|
+
<style>
|
|
79
|
+
.hds-full-trial-signup {
|
|
80
|
+
--fts-accent-resolved: var(--fts-accent, var(--accent));
|
|
81
|
+
padding: 96px 0 138px;
|
|
82
|
+
background: linear-gradient(
|
|
83
|
+
to bottom,
|
|
84
|
+
var(--background, var(--box-background)),
|
|
85
|
+
color-mix(in srgb, var(--fts-accent-resolved) 40%, var(--background, var(--box-background)))
|
|
86
|
+
);
|
|
87
|
+
text-align: center;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
:global(:root.dark) .hds-full-trial-signup {
|
|
91
|
+
--fts-accent-resolved: var(--fts-accent-dark, var(--fts-accent, var(--accent)));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.inner {
|
|
95
|
+
width: 600px;
|
|
96
|
+
max-width: 100%;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.badge {
|
|
100
|
+
display: inline-flex;
|
|
101
|
+
padding: 4px 14px;
|
|
102
|
+
border-radius: 100px;
|
|
103
|
+
font-size: 13px;
|
|
104
|
+
font-weight: 600;
|
|
105
|
+
background: color-mix(in srgb, var(--fts-accent-resolved) 12%, transparent);
|
|
106
|
+
color: var(--fts-accent-resolved);
|
|
107
|
+
border: 1px solid color-mix(in srgb, var(--fts-accent-resolved) 25%, transparent);
|
|
108
|
+
margin-bottom: 24px;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
h2 {
|
|
112
|
+
font-size: clamp(28px, 4vw, 40px);
|
|
113
|
+
font-weight: 800;
|
|
114
|
+
margin: 0 0 12px;
|
|
115
|
+
letter-spacing: -0.02em;
|
|
116
|
+
font-family: var(--font-serif);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
p {
|
|
120
|
+
font-size: 1rem;
|
|
121
|
+
color: var(--text-light);
|
|
122
|
+
margin: 0 0 32px;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.cta-actions {
|
|
126
|
+
display: flex;
|
|
127
|
+
justify-content: center;
|
|
128
|
+
flex-wrap: wrap;
|
|
129
|
+
gap: 12px;
|
|
130
|
+
margin: 0 0 24px;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.checks {
|
|
134
|
+
display: flex;
|
|
135
|
+
align-items: center;
|
|
136
|
+
justify-content: center;
|
|
137
|
+
gap: 20px;
|
|
138
|
+
flex-wrap: wrap;
|
|
139
|
+
margin-top: 24px;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.check {
|
|
143
|
+
display: flex;
|
|
144
|
+
align-items: center;
|
|
145
|
+
gap: 6px;
|
|
146
|
+
font-size: 14px;
|
|
147
|
+
color: var(--text-light);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.check :global(svg) {
|
|
151
|
+
color: var(--fts-accent-resolved);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
@media (max-width: 600px) {
|
|
155
|
+
.checks {
|
|
156
|
+
flex-direction: column;
|
|
157
|
+
align-items: flex-start;
|
|
158
|
+
width: fit-content;
|
|
159
|
+
max-width: 100%;
|
|
160
|
+
margin: 24px auto 0;
|
|
161
|
+
gap: 10px;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
</style>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
type ButtonConfig = {
|
|
2
|
+
href: string;
|
|
3
|
+
label: string;
|
|
4
|
+
external?: boolean;
|
|
5
|
+
};
|
|
6
|
+
interface Props {
|
|
7
|
+
badge?: string | null;
|
|
8
|
+
title?: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
button?: ButtonConfig | ButtonConfig[] | null;
|
|
11
|
+
checks?: string[];
|
|
12
|
+
accentColor?: string;
|
|
13
|
+
accentColorDark?: string;
|
|
14
|
+
[key: string]: any;
|
|
15
|
+
}
|
|
16
|
+
declare const FullTrialSignup: import("svelte").Component<Props, {}, "">;
|
|
17
|
+
type FullTrialSignup = ReturnType<typeof FullTrialSignup>;
|
|
18
|
+
export default FullTrialSignup;
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
center?: Snippet;
|
|
21
21
|
end?: Snippet;
|
|
22
22
|
max?: boolean;
|
|
23
|
+
logoAltText?: string;
|
|
24
|
+
menuLabel?: string;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
let {
|
|
@@ -32,7 +34,9 @@
|
|
|
32
34
|
darkToggle = true,
|
|
33
35
|
center,
|
|
34
36
|
end,
|
|
35
|
-
max = false
|
|
37
|
+
max = false,
|
|
38
|
+
logoAltText = 'Logo',
|
|
39
|
+
menuLabel = 'Menu'
|
|
36
40
|
}: Props = $props();
|
|
37
41
|
|
|
38
42
|
let scrolled = $state(false);
|
|
@@ -61,7 +65,7 @@
|
|
|
61
65
|
<a class="nav-brand" {href}>
|
|
62
66
|
<img
|
|
63
67
|
src={logo || `${instance}/api/public/logo/${product}.svg`}
|
|
64
|
-
alt="{name + (subName ? ' ' + subName : '')}
|
|
68
|
+
alt="{name + (subName ? ' ' + subName : '')} {logoAltText}"
|
|
65
69
|
width="26"
|
|
66
70
|
height="26"
|
|
67
71
|
/>
|
|
@@ -93,7 +97,7 @@
|
|
|
93
97
|
<span class="mobile-nav-wrap">
|
|
94
98
|
<Dropdown bind:show={mobileOpen} align="end" width={300} contentPadding={8}>
|
|
95
99
|
{#snippet trigger()}
|
|
96
|
-
<IconButton variant="invisible" aria-label=
|
|
100
|
+
<IconButton variant="invisible" aria-label={menuLabel} aria-expanded={mobileOpen}>
|
|
97
101
|
{#if mobileOpen}
|
|
98
102
|
<IconX size={18} />
|
|
99
103
|
{:else}
|