@hyvor/design 2.1.8-beta.2 → 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/FullTrialSignup/FullTrialSignup.svelte +164 -0
- package/dist/marketing/FullTrialSignup/FullTrialSignup.svelte.d.ts +18 -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 +7 -0
- package/dist/marketing/index.js +7 -0
- package/package.json +1 -1
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import IconCaretDown from '@hyvor/icons/IconCaretDown';
|
|
3
|
+
import type { Component } from 'svelte';
|
|
4
|
+
import Feature from './AllFeaturesAccordionFeature.svelte';
|
|
5
|
+
|
|
6
|
+
interface FeatureItem {
|
|
7
|
+
icon: Component;
|
|
8
|
+
title: string;
|
|
9
|
+
description: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface Category {
|
|
13
|
+
label: string;
|
|
14
|
+
icon: Component;
|
|
15
|
+
color?: string;
|
|
16
|
+
features: FeatureItem[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface Props {
|
|
20
|
+
title?: string;
|
|
21
|
+
description?: string;
|
|
22
|
+
categories: Category[];
|
|
23
|
+
defaultOpenIndex?: number;
|
|
24
|
+
accentColor?: string;
|
|
25
|
+
accentColorDark?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let {
|
|
29
|
+
title = "And There's More...",
|
|
30
|
+
description = '',
|
|
31
|
+
categories,
|
|
32
|
+
defaultOpenIndex = 0,
|
|
33
|
+
accentColor,
|
|
34
|
+
accentColorDark
|
|
35
|
+
}: Props = $props();
|
|
36
|
+
|
|
37
|
+
let openIndex = $state(defaultOpenIndex);
|
|
38
|
+
|
|
39
|
+
function toggle(i: number) {
|
|
40
|
+
openIndex = openIndex === i ? -1 : i;
|
|
41
|
+
}
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<section
|
|
45
|
+
class="hds-all-features-accordion hds-container"
|
|
46
|
+
style:--afa-accent={accentColor}
|
|
47
|
+
style:--afa-accent-dark={accentColorDark}
|
|
48
|
+
>
|
|
49
|
+
<div class="section-header">
|
|
50
|
+
<h2>{title}</h2>
|
|
51
|
+
{#if description}
|
|
52
|
+
<p>{description}</p>
|
|
53
|
+
{/if}
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<div class="accordion">
|
|
57
|
+
{#each categories as cat, i}
|
|
58
|
+
{@const CatIcon = cat.icon}
|
|
59
|
+
{@const catColor = cat.color || 'var(--accent)'}
|
|
60
|
+
<div class="accordion-item" class:open={openIndex === i}>
|
|
61
|
+
<h3 class="accordion-heading">
|
|
62
|
+
<button
|
|
63
|
+
class="accordion-trigger"
|
|
64
|
+
id="hds-accordion-trigger-{i}"
|
|
65
|
+
onclick={() => toggle(i)}
|
|
66
|
+
aria-expanded={openIndex === i}
|
|
67
|
+
aria-controls="hds-accordion-panel-{i}"
|
|
68
|
+
style="--cat-color: {catColor}"
|
|
69
|
+
>
|
|
70
|
+
<span class="cat-icon" aria-hidden="true">
|
|
71
|
+
<CatIcon size={18} />
|
|
72
|
+
</span>
|
|
73
|
+
<span class="cat-label">{cat.label}</span>
|
|
74
|
+
<span class="cat-count">{cat.features.length} features</span>
|
|
75
|
+
<span class="chevron" class:rotated={openIndex === i} aria-hidden="true">
|
|
76
|
+
<IconCaretDown size={16} />
|
|
77
|
+
</span>
|
|
78
|
+
</button>
|
|
79
|
+
</h3>
|
|
80
|
+
|
|
81
|
+
<div
|
|
82
|
+
class="accordion-body"
|
|
83
|
+
class:open={openIndex === i}
|
|
84
|
+
role="region"
|
|
85
|
+
id="hds-accordion-panel-{i}"
|
|
86
|
+
aria-labelledby="hds-accordion-trigger-{i}"
|
|
87
|
+
>
|
|
88
|
+
<div class="accordion-body-inner">
|
|
89
|
+
<div class="accordion-content">
|
|
90
|
+
<ul class="features-grid">
|
|
91
|
+
{#each cat.features as feat}
|
|
92
|
+
<Feature
|
|
93
|
+
icon={feat.icon}
|
|
94
|
+
title={feat.title}
|
|
95
|
+
description={feat.description}
|
|
96
|
+
color={catColor}
|
|
97
|
+
/>
|
|
98
|
+
{/each}
|
|
99
|
+
</ul>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
{/each}
|
|
105
|
+
</div>
|
|
106
|
+
</section>
|
|
107
|
+
|
|
108
|
+
<style>
|
|
109
|
+
.hds-all-features-accordion {
|
|
110
|
+
padding: 80px 0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.section-header {
|
|
114
|
+
text-align: center;
|
|
115
|
+
margin-bottom: 48px;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.section-header h2 {
|
|
119
|
+
font-size: 32px;
|
|
120
|
+
font-weight: 700;
|
|
121
|
+
margin: 0 0 6px;
|
|
122
|
+
font-family: var(--font-serif);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.section-header p {
|
|
126
|
+
font-size: 1rem;
|
|
127
|
+
color: var(--text-light);
|
|
128
|
+
margin: 0;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.accordion {
|
|
132
|
+
display: flex;
|
|
133
|
+
flex-direction: column;
|
|
134
|
+
gap: 4px;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.accordion-item {
|
|
138
|
+
border-radius: 16px;
|
|
139
|
+
transition: background 0.2s ease;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.accordion-item.open {
|
|
143
|
+
background: color-mix(in srgb, var(--afa-accent, var(--accent)) 6%, transparent);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
:global(:root.dark) .accordion-item.open {
|
|
147
|
+
background: color-mix(
|
|
148
|
+
in srgb,
|
|
149
|
+
var(--afa-accent-dark, var(--afa-accent, var(--accent))) 6%,
|
|
150
|
+
transparent
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.accordion-heading {
|
|
155
|
+
margin: 0;
|
|
156
|
+
font-size: inherit;
|
|
157
|
+
font-weight: inherit;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.accordion-trigger {
|
|
161
|
+
width: 100%;
|
|
162
|
+
display: flex;
|
|
163
|
+
align-items: center;
|
|
164
|
+
gap: 14px;
|
|
165
|
+
padding: 18px 20px;
|
|
166
|
+
background: none;
|
|
167
|
+
border: none;
|
|
168
|
+
border-radius: inherit;
|
|
169
|
+
cursor: pointer;
|
|
170
|
+
text-align: left;
|
|
171
|
+
transition: background 0.15s;
|
|
172
|
+
color: var(--text);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.accordion-item:not(.open) .accordion-trigger:hover {
|
|
176
|
+
background: var(--hover);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.cat-icon {
|
|
180
|
+
display: flex;
|
|
181
|
+
align-items: center;
|
|
182
|
+
justify-content: center;
|
|
183
|
+
width: 34px;
|
|
184
|
+
height: 34px;
|
|
185
|
+
border-radius: 10px;
|
|
186
|
+
color: var(--cat-color);
|
|
187
|
+
background: color-mix(in srgb, var(--cat-color) 16%, transparent);
|
|
188
|
+
flex-shrink: 0;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.cat-label {
|
|
192
|
+
font-size: 16px;
|
|
193
|
+
font-weight: 600;
|
|
194
|
+
flex: 1;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.cat-count {
|
|
198
|
+
font-size: 13px;
|
|
199
|
+
color: var(--text-light);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.chevron {
|
|
203
|
+
display: flex;
|
|
204
|
+
align-items: center;
|
|
205
|
+
color: var(--text-light);
|
|
206
|
+
transition: transform 0.2s;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.chevron.rotated {
|
|
210
|
+
transform: rotate(180deg);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/* CSS grid row trick: content stays in DOM for SEO, height animates via grid */
|
|
214
|
+
.accordion-body {
|
|
215
|
+
display: grid;
|
|
216
|
+
grid-template-rows: 0fr;
|
|
217
|
+
transition: grid-template-rows 0.22s ease;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.accordion-body.open {
|
|
221
|
+
grid-template-rows: 1fr;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.accordion-body-inner {
|
|
225
|
+
overflow: hidden;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.accordion-content {
|
|
229
|
+
padding: 8px 20px 28px;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.features-grid {
|
|
233
|
+
display: grid;
|
|
234
|
+
grid-template-columns: repeat(3, 1fr);
|
|
235
|
+
gap: 28px 20px;
|
|
236
|
+
margin: 0;
|
|
237
|
+
padding: 0;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/* Override AllFeaturesAccordionFeature's width since we're using grid now */
|
|
241
|
+
/* TODO */
|
|
242
|
+
.features-grid :global(.feature) {
|
|
243
|
+
width: auto;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
@media (max-width: 768px) {
|
|
247
|
+
.features-grid {
|
|
248
|
+
grid-template-columns: 1fr;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
</style>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Component } from 'svelte';
|
|
2
|
+
interface FeatureItem {
|
|
3
|
+
icon: Component;
|
|
4
|
+
title: string;
|
|
5
|
+
description: string;
|
|
6
|
+
}
|
|
7
|
+
interface Category {
|
|
8
|
+
label: string;
|
|
9
|
+
icon: Component;
|
|
10
|
+
color?: string;
|
|
11
|
+
features: FeatureItem[];
|
|
12
|
+
}
|
|
13
|
+
interface Props {
|
|
14
|
+
title?: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
categories: Category[];
|
|
17
|
+
defaultOpenIndex?: number;
|
|
18
|
+
accentColor?: string;
|
|
19
|
+
accentColorDark?: string;
|
|
20
|
+
}
|
|
21
|
+
declare const AllFeaturesAccordion: Component<Props, {}, "">;
|
|
22
|
+
type AllFeaturesAccordion = ReturnType<typeof AllFeaturesAccordion>;
|
|
23
|
+
export default AllFeaturesAccordion;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Component } from 'svelte';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
icon: Component;
|
|
6
|
+
title: string;
|
|
7
|
+
description: string;
|
|
8
|
+
// any CSS color, e.g. 'var(--green)' or '#4b874b' — defaults to the
|
|
9
|
+
// theme accent so this works out of the box on any product
|
|
10
|
+
color?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let { icon, title, description, color = 'var(--accent)' }: Props = $props();
|
|
14
|
+
|
|
15
|
+
const SvelteComponent = $derived(icon);
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<li class="feature">
|
|
19
|
+
<div class="icon-bg" style:color aria-hidden="true">
|
|
20
|
+
<SvelteComponent size={52} />
|
|
21
|
+
</div>
|
|
22
|
+
<div class="content">
|
|
23
|
+
<h4 class="title">{title}</h4>
|
|
24
|
+
<p class="description">{description}</p>
|
|
25
|
+
</div>
|
|
26
|
+
</li>
|
|
27
|
+
|
|
28
|
+
<style>
|
|
29
|
+
.feature {
|
|
30
|
+
position: relative;
|
|
31
|
+
width: calc(33.33% - 15px);
|
|
32
|
+
list-style: none;
|
|
33
|
+
overflow: hidden;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.content {
|
|
37
|
+
position: relative;
|
|
38
|
+
z-index: 1;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/* a large, faint watermark of the feature's own icon, sitting behind the
|
|
42
|
+
title/description rather than as a small marker above them */
|
|
43
|
+
.icon-bg {
|
|
44
|
+
position: absolute;
|
|
45
|
+
top: 50%;
|
|
46
|
+
right: -8px;
|
|
47
|
+
transform: translateY(-50%);
|
|
48
|
+
z-index: 0;
|
|
49
|
+
opacity: 0.14;
|
|
50
|
+
pointer-events: none;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.title {
|
|
54
|
+
font-weight: 600;
|
|
55
|
+
font-size: 20px;
|
|
56
|
+
margin: 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.description {
|
|
60
|
+
font-size: 16px;
|
|
61
|
+
margin: 10px 0 0;
|
|
62
|
+
color: var(--text-light);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@media (max-width: 992px) {
|
|
66
|
+
.feature {
|
|
67
|
+
width: 100%;
|
|
68
|
+
text-align: center;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.icon-bg {
|
|
72
|
+
display: none;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
</style>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Component } from 'svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
icon: Component;
|
|
4
|
+
title: string;
|
|
5
|
+
description: string;
|
|
6
|
+
color?: string;
|
|
7
|
+
}
|
|
8
|
+
declare const AllFeaturesAccordionFeature: Component<Props, {}, "">;
|
|
9
|
+
type AllFeaturesAccordionFeature = ReturnType<typeof AllFeaturesAccordionFeature>;
|
|
10
|
+
export default AllFeaturesAccordionFeature;
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface FaqItem {
|
|
3
|
+
q: string;
|
|
4
|
+
/** answer content as HTML — also used verbatim as the rich schema's answer text */
|
|
5
|
+
a: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
items: FaqItem[];
|
|
10
|
+
columns?: 1 | 2;
|
|
11
|
+
// emit the FAQPage JSON-LD rich schema into <svelte:head>
|
|
12
|
+
richSchema?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let { items, columns = 2, richSchema = true }: Props = $props();
|
|
16
|
+
|
|
17
|
+
const uid = $props.id();
|
|
18
|
+
|
|
19
|
+
// which items go in which column (left-to-right, top-to-bottom within each column),
|
|
20
|
+
// carrying each item's original index along so column splitting doesn't affect it
|
|
21
|
+
const cols = $derived.by(() => {
|
|
22
|
+
const indexed = items.map((item, index) => ({ item, index }));
|
|
23
|
+
if (columns === 1) return [indexed];
|
|
24
|
+
const mid = Math.ceil(indexed.length / 2);
|
|
25
|
+
return [indexed.slice(0, mid), indexed.slice(mid)];
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// independent open/close state per item, first one open by default
|
|
29
|
+
let openStates = $state(items.map((_, i) => i === 0));
|
|
30
|
+
|
|
31
|
+
function toggle(i: number) {
|
|
32
|
+
openStates[i] = !openStates[i];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// FAQPage rich schema, generated straight from the same array that renders the accordion
|
|
36
|
+
// (Google's FAQPage guidelines allow a limited set of formatting HTML in the answer text)
|
|
37
|
+
const richSchemaObj = $derived({
|
|
38
|
+
'@context': 'https://schema.org',
|
|
39
|
+
'@type': 'FAQPage',
|
|
40
|
+
mainEntity: items.map((item) => ({
|
|
41
|
+
'@type': 'Question',
|
|
42
|
+
name: item.q,
|
|
43
|
+
acceptedAnswer: {
|
|
44
|
+
'@type': 'Answer',
|
|
45
|
+
text: item.a
|
|
46
|
+
}
|
|
47
|
+
}))
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const richSchemaScript = $derived(
|
|
51
|
+
'<' + 'script type="application/ld+json">' + JSON.stringify(richSchemaObj) + '<' + '/script>'
|
|
52
|
+
);
|
|
53
|
+
</script>
|
|
54
|
+
|
|
55
|
+
<svelte:head>
|
|
56
|
+
{#if richSchema}
|
|
57
|
+
{@html richSchemaScript}
|
|
58
|
+
{/if}
|
|
59
|
+
</svelte:head>
|
|
60
|
+
|
|
61
|
+
<div class="hds-faq" class:single={columns === 1}>
|
|
62
|
+
{#each cols as col, colIndex (colIndex)}
|
|
63
|
+
<div class="faq-col">
|
|
64
|
+
{#each col as { item, index } (item.q)}
|
|
65
|
+
<div class="faq" class:open={openStates[index]}>
|
|
66
|
+
<h3 class="heading">
|
|
67
|
+
<button
|
|
68
|
+
class="trigger"
|
|
69
|
+
id="hds-faq-trigger-{uid}-{index}"
|
|
70
|
+
onclick={() => toggle(index)}
|
|
71
|
+
aria-expanded={openStates[index]}
|
|
72
|
+
aria-controls="hds-faq-panel-{uid}-{index}"
|
|
73
|
+
>
|
|
74
|
+
<span class="q">{item.q}</span>
|
|
75
|
+
<span class="toggle-icon" aria-hidden="true">
|
|
76
|
+
<span class="line horizontal"></span>
|
|
77
|
+
<span class="line vertical"></span>
|
|
78
|
+
</span>
|
|
79
|
+
</button>
|
|
80
|
+
</h3>
|
|
81
|
+
|
|
82
|
+
<div
|
|
83
|
+
class="body"
|
|
84
|
+
class:open={openStates[index]}
|
|
85
|
+
role="region"
|
|
86
|
+
id="hds-faq-panel-{uid}-{index}"
|
|
87
|
+
aria-labelledby="hds-faq-trigger-{uid}-{index}"
|
|
88
|
+
>
|
|
89
|
+
<div class="body-inner">
|
|
90
|
+
<div class="a">{@html item.a}</div>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
{/each}
|
|
95
|
+
</div>
|
|
96
|
+
{/each}
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<style>
|
|
100
|
+
.hds-faq {
|
|
101
|
+
display: flex;
|
|
102
|
+
gap: 0 60px;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.hds-faq.single {
|
|
106
|
+
display: block;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.faq-col {
|
|
110
|
+
flex: 1;
|
|
111
|
+
min-width: 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.faq {
|
|
115
|
+
border-bottom: 1px solid var(--border);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.heading {
|
|
119
|
+
margin: 0;
|
|
120
|
+
font-size: inherit;
|
|
121
|
+
font-weight: inherit;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.trigger {
|
|
125
|
+
display: flex;
|
|
126
|
+
align-items: center;
|
|
127
|
+
justify-content: space-between;
|
|
128
|
+
gap: 20px;
|
|
129
|
+
width: 100%;
|
|
130
|
+
padding: 20px 0;
|
|
131
|
+
background: none;
|
|
132
|
+
border: none;
|
|
133
|
+
cursor: pointer;
|
|
134
|
+
text-align: left;
|
|
135
|
+
color: inherit;
|
|
136
|
+
font: inherit;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.q {
|
|
140
|
+
font-size: 16px;
|
|
141
|
+
font-weight: 600;
|
|
142
|
+
letter-spacing: -0.01em;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.toggle-icon {
|
|
146
|
+
position: relative;
|
|
147
|
+
flex-shrink: 0;
|
|
148
|
+
width: 24px;
|
|
149
|
+
height: 24px;
|
|
150
|
+
border-radius: 50%;
|
|
151
|
+
background: var(--accent-light-mid);
|
|
152
|
+
color: var(--accent);
|
|
153
|
+
transition: transform 0.3s cubic-bezier(0.65, 0, 0.35, 1);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.faq.open .toggle-icon {
|
|
157
|
+
transform: rotate(180deg);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.line {
|
|
161
|
+
position: absolute;
|
|
162
|
+
top: 50%;
|
|
163
|
+
left: 50%;
|
|
164
|
+
background: currentColor;
|
|
165
|
+
border-radius: 2px;
|
|
166
|
+
transform: translate(-50%, -50%);
|
|
167
|
+
transition:
|
|
168
|
+
transform 0.3s cubic-bezier(0.65, 0, 0.35, 1),
|
|
169
|
+
opacity 0.2s ease;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.line.horizontal {
|
|
173
|
+
width: 12px;
|
|
174
|
+
height: 2px;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.line.vertical {
|
|
178
|
+
width: 2px;
|
|
179
|
+
height: 12px;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.faq.open .line.vertical {
|
|
183
|
+
transform: translate(-50%, -50%) rotate(90deg);
|
|
184
|
+
opacity: 0;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* CSS grid row trick: content stays in DOM for SEO, height animates via grid */
|
|
188
|
+
.body {
|
|
189
|
+
display: grid;
|
|
190
|
+
grid-template-rows: 0fr;
|
|
191
|
+
transition: grid-template-rows 0.3s cubic-bezier(0.65, 0, 0.35, 1);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.body.open {
|
|
195
|
+
grid-template-rows: 1fr;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.body-inner {
|
|
199
|
+
overflow: hidden;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.a {
|
|
203
|
+
padding: 0 30px 22px 0;
|
|
204
|
+
font-size: 15px;
|
|
205
|
+
line-height: 1.6;
|
|
206
|
+
color: var(--text-light);
|
|
207
|
+
opacity: 0;
|
|
208
|
+
transform: translateY(-6px);
|
|
209
|
+
transition:
|
|
210
|
+
opacity 0.25s ease,
|
|
211
|
+
transform 0.25s ease;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.body.open .a {
|
|
215
|
+
opacity: 1;
|
|
216
|
+
transform: translateY(0);
|
|
217
|
+
transition-delay: 0.08s;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/* answers are raw HTML (@html) — style plain <a> tags to match the design
|
|
221
|
+
system's Link component, which snippet-based answers would use */
|
|
222
|
+
.a :global(a) {
|
|
223
|
+
color: var(--link);
|
|
224
|
+
text-decoration: underline;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
@media (max-width: 768px) {
|
|
228
|
+
.hds-faq {
|
|
229
|
+
flex-direction: column;
|
|
230
|
+
gap: 0;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
</style>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
interface FaqItem {
|
|
2
|
+
q: string;
|
|
3
|
+
/** answer content as HTML — also used verbatim as the rich schema's answer text */
|
|
4
|
+
a: string;
|
|
5
|
+
}
|
|
6
|
+
interface Props {
|
|
7
|
+
items: FaqItem[];
|
|
8
|
+
columns?: 1 | 2;
|
|
9
|
+
richSchema?: boolean;
|
|
10
|
+
}
|
|
11
|
+
declare const FAQ: import("svelte").Component<Props, {}, "">;
|
|
12
|
+
type FAQ = ReturnType<typeof FAQ>;
|
|
13
|
+
export default FAQ;
|