Landing Page Components

Stack these inside <Base>, between <Header> and <Footer>, in whatever order tells your product's story. See them all assembled on a real page at /test, which is also where to check how a component actually looks, since the examples below are code-only.

Hero

The first section of a marketing homepage: a large title, an optional badge and subtitle, one or more call-to-action buttons, and a dotted background pattern. Place it as the first child inside <Base>, right after <Header>.

Properties

Name
Default
Description
title
The main heading. A plain string, or a Snippet if you need inline markup (e.g. a smaller second line). See Highlighting translatable text below for how to emphasize part of it.
subtitle
Supporting text under the title. Same string | Snippet shape as title.
badge
A small pill shown above the title, e.g. "Open-source & free to try". Also string | Snippet.
buttons
null
One button config, or an array for more than one. Each item is { href, label, external?, size?, variant?, ... }. Any other prop <Button> accepts is passed through.
accentColor
var(--accent)
Overrides the accent used for the badge, the .hds-hl highlight color, and the scroll dot, in light mode.
accentColorDark
accentColor
Same as accentColor, used in dark mode. Falls back to accentColor, then the theme accent.
fullHeight
false
When true, the hero fills the viewport height below the header instead of sizing to its content.
after
Snippet rendered below the buttons, e.g. a product screenshot or a row of customer logos.

Example

<Hero
	badge="Open-source & free to try"
	buttons={{ href: '/console?signup', label: 'Start your blog' }}
>
	{#snippet title()}
		You Publish.<br /><span class="heading-small">(We'll handle the rest)</span>
	{/snippet}
	{#snippet subtitle()}
		Hyvor Blogs is our <span class="hds-marker">blogging platform</span>. It's
		<span class="hds-hl">fast</span>, <span class="hds-hl">simple</span>, and
		<span class="hds-hl">open-source</span>!
	{/snippet}
</Hero>

Highlighting translatable text

Two helper classes are styled globally inside the title/subtitle: .hds-hl (accent-colored text) and .hds-marker (a highlighter-style background). Wrap the part you want emphasized in one of these, inside a {#snippet} as shown above.

If the text is translated, keep the <span> wrapping in code and let only the plain text be translated: with Intl.T's element param, translators never write markup:

{#snippet title()}
	<Intl.T key="hero.title" params={{ hl: { element: 'span', props: { class: 'hds-hl' } } }} />
{/snippet}

with the locale file holding only plain text and a bare tag name:

{
	"hero.title": "You <hl>Publish</hl>."
}

LogoStrip

A strip of customer/partner logos, usually placed right under Hero. By default it scrolls as an infinite marquee (pauses on hover, and falls back to a static wrapped row for prefers-reduced-motion); set marquee=false for a plain wrapped row instead. Logos are dimmed and forced to a single color by default (invert), so a mixed set of brand marks reads as one consistent row instead of competing with each other; opt individual opaque/full-color logos out with color.

Properties

Name
Default
Description
logos
Required. An array of { name, src, href?, width?, height?, color? }. width/height default to 120/28 per logo, matching each logo's own aspect ratio. Set color on a logo to keep it in its real colors regardless of invert (e.g. an opaque, full-color app icon that would otherwise crush into a blank box under the white silhouette treatment).
label
''
Small uppercase label above the logos, e.g. "Trusted by 100+ businesses".
background
var(--accent)
The section's background color. Any CSS color works: a hex value, another theme variable, rgb()/hsl(), etc.
invert
true
Forces every logo (except ones marked color) to solid white via filter, for a colored/dark background. Turn off on a light/neutral background to show logos in their original colors.
marquee
true
Scrolls the logos as an infinite, edge-faded, hover-to-pause marquee. Set to false for a plain centered row that wraps onto multiple lines instead.
speed
34
Seconds for one full marquee loop. Only used when marquee is on.

Example

<LogoStrip
	label="Trusted by 10,000+ websites & businesses"
	logos={[
		{ name: 'Elementor', src: '/customers/elementor.png' },
		{ name: 'Stanford University', src: '/customers/stanford.png', width: 125, height: 40 },
		{ name: 'ACCA', src: '/customers/acca.png', width: 44, height: 44, color: true },
		{ name: 'El Debate', src: '/customers/eldebate.png', width: 140, height: 28 }
	]}
/>

Feature Split

A two-column section: text (eyebrow, title, description, bullets, a button) on one side and a visual on the other. Stack a few of these to walk through a product's main features, alternating flip to keep the page from feeling one-sided.

Properties

Name
Default
Description
eyebrow
Small label above the title, e.g. "Post Editor". Required unless left is used.
title
The section heading. string | Snippet, same as Hero's title.
description
A paragraph under the title.
bullets
[]
A list of short feature bullets, each shown with a check icon, or a Snippet to render something else in that spot entirely (e.g. a stat row or a mini form). interactiveBullets only applies to the string[] form.
button
null
One or more { href, label, external? } configs, rendered as outline buttons.
visual
Required. Snippet for the other column: an image, a code block, a mock UI, etc. Receives activeBullet: number when interactiveBullets is on, so the visual can switch state to match the selected bullet.
interactiveBullets
false
Makes the bullets clickable tabs that auto-advance every 5s once the section scrolls into view.
flip
false
Swaps the text and visual columns (visual on the left).
altBg
false
Gives the section an alternate background, to visually separate it from the ones around it.
overlap
false
Lets the visual bleed under the text column (e.g. a visual with a negative margin), while keeping the text readable on top.
max
false
Uses the wider hds-container-max instead of the default hds-container.
left
Snippet that replaces the entire text column, when eyebrow/title/description/bullets/button aren't flexible enough.
after
Snippet rendered full-width, below the split.

Example

<FeatureSplit
	eyebrow="Post Editor"
	title="Write without friction"
	description="A clean, distraction-free editor with everything you need to write and format posts."
	bullets={[
		'Markdown shortcuts for all the formatting you need',
		'Paste, drag and drop, or pick images from Unsplash',
		'Syntax highlighting for 100+ languages'
	]}
	button={{ href: '/console?signup', label: 'Start writing' }}
	interactiveBullets
>
	{#snippet visual(activeBullet)}
		<div class="visual-placeholder hds-box">
			<IconImage size={40} />
			<span>Active bullet: {activeBullet + 1}</span>
		</div>
	{/snippet}
</FeatureSplit>

Custom content instead of bullets

Pass a Snippet as bullets to render something other than a checklist in that spot, here, a small stat row.

<FeatureSplit
	eyebrow="Optimizations"
	title="Built for speed"
	description="All official themes are optimized for speed."
>
	{#snippet bullets()}
		<div class="stats">
			<div><strong>99</strong><span>Lighthouse score</span></div>
			<div><strong>&lt;200ms</strong><span>Time to first byte</span></div>
		</div>
	{/snippet}
	{#snippet visual()}
		<div class="visual-placeholder hds-box">
			<IconImage size={40} />
		</div>
	{/snippet}
</FeatureSplit>

Testimonials

A horizontally-scrolling row of customer quotes and (optionally) video reviews. There's no limit on how many reviews you pass in: once they don't all fit on screen, the row becomes swipeable: touch/trackpad swipe and mouse drag both work, and it snaps to each card as you scroll.

Properties

Name
Default
Description
reviews
Required. An array of text or video reviews, see the shape below. Any number of reviews can be passed; the row scrolls once they overflow the screen.
label
"Testimonials"
Small label above the title.
title
"Loved by our customers."
The section heading.
handwrittenNames
true
Sets the reviewer name in a handwritten-style font (Caveat, loaded from Bunny Fonts). Turn off to use the regular typeface instead, e.g. if the host page can't reach an external font host.

The reviews shape

Field
Applies to
Description
type
both
'text' or 'video'.
name
both
Reviewer name. When no photo is set on a text review, a deterministic, non-photographic identicon is generated from it.
role
both
Reviewer role/title shown under the name.
quote
'text'
The quote text.
videoUrl
'video'
Optional. URL of the video to play.
posterUrl
'video'
Optional. Poster image shown before the video plays.

Example

<Testimonials
	reviews={[
		{
			type: 'text',
			name: 'Lionel S.',
			role: 'Blogger',
			quote:
				'I need a simple, easy-to-use, fast, beautiful and mature blogging tool that resolves the WordPress bloat. Hyvor Blogs handles this beautifully.'
		},
		{
			type: 'video',
			name: 'Video testimonial',
			role: 'Coming soon'
		},
		{
			type: 'text',
			name: 'Manoj P.',
			role: 'Senior Application Engineer',
			quote:
				'The platform offers a seamless and user-friendly experience for both bloggers and readers. The customization options are extensive.'
		}
	]}
/>

All Features Accordion

A catch-all "and there's more" section: categories of features, each expandable into a grid of icon + title + description cards. Good for listing everything that didn't get its own Feature Split section.

Properties

Name
Default
Description
categories
Required. An array of feature categories, see the shape below.
title
"And There's More..."
The section heading.
description
''
Optional paragraph under the title.
defaultOpenIndex
0
Index of the category open by default. Use -1 to start with all of them collapsed.
accentColor
var(--accent)
Overrides the accent used to tint the open category's background, in light mode. This is separate from each category's own color, which only tints that category's icon badge and its features' watermarks.
accentColorDark
accentColor
Same as accentColor, used in dark mode. Falls back to accentColor, then the theme accent.

The categories shape

Field
Description
label
Category name, shown as the accordion trigger.
icon
A @hyvor/icons component for the trigger.
color
Optional. Any CSS color, e.g. 'var(--green)' or '#4b874b', defaults to the theme accent.
features
An array of { icon, title, description }, one card per feature.

Example

<AllFeaturesAccordion
	categories={[
		{
			label: 'Post Editor',
			icon: IconPencil,
			color: 'var(--green)',
			features: [
				{
					icon: IconPencil,
					title: 'All the basics',
					description: 'Bold, italic, headings, lists, quotes, links, and more.'
				},
				{
					icon: IconImage,
					title: 'Images',
					description: 'Upload, paste, drag and drop, and more ways to add images.'
				}
			]
		},
		{
			label: 'Your Blog',
			icon: IconSignpost2,
			color: 'var(--blue)',
			features: [
				{ icon: IconTag, title: 'Tags', description: 'Organize your posts with tags.' },
				{
					icon: IconRss,
					title: 'Atom (RSS) Feed',
					description: 'Atom feeds are generated automatically.'
				}
			]
		}
	]}
/>

FAQ

A two-column accordion of frequently asked questions. Unlike the other homepage sections, this one isn't full-bleed, so wrap it in your own heading and a .hds-container, as shown in the example.

Properties

Name
Default
Description
items
Required. An array of { q, a }. a is rendered as HTML, and used verbatim as the rich schema's answer text.
columns
2
1 or 2. Items are split left-to-right, top-to-bottom within each column. Regardless of this setting, the layout always collapses to a single column below 768px, so mobile visitors never see a cramped two-column grid.
richSchema
true
Emits a FAQPage JSON-LD rich schema into <svelte:head>, generated straight from items, for Google's FAQ rich results.
toggleIconBg
var(--accent-light)
Overrides the background color of the round expand/collapse icon.

Example

<div class="hds-container">
	<h2>Frequently asked questions</h2>
	<FAQ
		items={[
			{ q: 'Is there a free trial?', a: 'Yes, every plan starts with a 14-day free trial.' },
			{ q: 'Can I cancel anytime?', a: 'Yes, there are no long-term contracts.' },
			{
				q: 'Do you offer discounts?',
				a: 'Yes, see our <a href="/pricing">pricing page</a> for details.'
			}
		]}
	/>
</div>

Full Trial Signup

A closing call-to-action section: a title, a description, a button, and a row of reassurance checks (e.g. "No credit card required"). Use it as the last section before the footer.

Properties

Name
Default
Description
title
"Start your trial today"
The section heading.
description
''
Optional paragraph under the title.
badge
null
Small pill shown above the title.
button
null
One or more { href, label, external? } configs, rendered as large buttons.
checks
['14-day free trial', 'No credit card required', 'Cancel anytime']
A row of short reassurance points, each shown with a check icon.
accentColor
var(--accent)
Overrides the accent used by the background gradient, the badge, and the check icons, in light mode.
accentColorDark
accentColor
Same as accentColor, used in dark mode. Falls back to accentColor, then the theme accent.

Any other prop passed in (e.g. id or class) is forwarded to the root <section>.

Example

<FullTrialSignup
	badge="Open-source & free to try"
	title="Start your blog today"
	description="Join hundreds of bloggers and businesses building with Hyvor Blogs."
	button={{ href: '/console?signup', label: 'Start your blog' }}
/>

Custom accent color

<FullTrialSignup
	accentColor="var(--red)"
	title="Start your blog today"
	button={{ href: '/console?signup', label: 'Start your blog' }}
/>

Spotlight Split

A dark, full-width section with an animated grid background that lights up around the cursor: a text column (title, description, a small stat row, a button) on one side, and a free-form content snippet on the other. It isn't tied to any one topic: the content side can hold compliance seals, a logo wall, a quote, stat cards, or anything else that fits a dark panel.

Properties

Name
Default
Description
title
Required. The section heading. string | Snippet.
description
''
Optional paragraph under the title.
stats
[]
A row of short strings shown as small highlights, e.g. "99.9% Uptime".
button
null
One or more { href, label, external? } configs, rendered as outline buttons.
content
Required. Snippet for the other column. Not limited to any particular shape; lay out whatever fits (a grid of icons, a logo wall, a single quote, stat cards, ...).
reverse
false
Swaps the text and content columns (content on the left).
background
var(--accent)
The section's background color. Any CSS color works: a hex value ("#574443"), another theme variable ("var(--red)"), rgb()/hsl(), etc.
highlightColor
rgba(255, 238, 217, 0.9)
Color of the grid lines that light up around the cursor. Pair it with background.

Example: compliance seals

The use case this section was first built for: an enterprise/security callout. The seal badges (GdprSeal, CcpaSeal, SsoSeal, IsoSeal) ship as ready-made presets built on the generic Seal component, which takes a size, an optional pending state (dashed border, muted colors, e.g. for a certification that's still in progress), and any content as children.

<SpotlightSplit
	title="Enterprise-ready."
	description="Enterprise-grade security and compliance, without adding complexity to your publishing workflow."
	stats={['> 99.9% Uptime', 'Priority Support']}
	button={{ href: 'https://hyvor.com/enterprise', label: 'Contact Sales', external: true }}
	background="#574443"
>
	{#snippet content()}
		<div class="seal-grid">
			<GdprSeal />
			<CcpaSeal />
			<SsoSeal />
			<IsoSeal />
		</div>
	{/snippet}
</SpotlightSplit>

Example: a quote, reversed

The same section works for unrelated content too, here a single pull quote, with reverse putting it on the left.

<SpotlightSplit
	title="Built by writers, for writers."
	description="Every part of the editor was shaped by feedback from people who publish daily."
	button={{ href: '/console?signup', label: 'Start writing' }}
	background="#2b2440"
	highlightColor="rgba(216, 200, 255, 0.9)"
	reverse
>
	{#snippet content()}
		<div class="quote hds-box">
			<IconQuote size={22} />
			<p>"The fastest editor I've used for long-form posts, by a wide margin."</p>
			<span>A Hyvor Blogs user</span>
		</div>
	{/snippet}
</SpotlightSplit>