Component · Hero sections
Hero Split
Split Vue 3 hero section with copy and two CTAs on the left and a Tailwind-drawn app window mockup on the right. Dark mode, typed props, no images.
Centered Vue 3 hero with an announcement pill, gradient headline, grid background, two CTAs and a row of wordmarks. Tailwind CSS with dark mode.
by Vueplayv1.0.0Updated 2 hours ago
A centered hero for product launches and homepages: an announcement pill linking to your changelog, a big headline with a gradient accent, one short paragraph, two calls to action and a quiet row of customer wordmarks for social proof.
The background is a CSS grid pattern masked with a radial gradient plus a blurred colour glow, so there are no images to load and it adapts to dark mode automatically. The wordmarks are plain text with varied type treatments; pass your own names or replace the whole row.
Set announcement or secondaryAction to null to hide them, and use the default slot to drop in extra content such as a product screenshot under the buttons.
<script setup lang="ts">
export interface HeroLink {
label: string
href: string
}
export interface Announcement {
/** Short badge text, e.g. "New". */
badge?: string
text: string
href: string
}
withDefaults(defineProps<{
announcement?: Announcement | null
heading?: string
highlight?: string
description?: string
primaryAction?: HeroLink
secondaryAction?: HeroLink | null
/** Label above the wordmark row. Empty string hides the row. */
logosLabel?: string
/** Fictional brand names rendered as text wordmarks. */
logos?: string[]
}>(), {
announcement: () => ({ badge: 'New', text: 'Branch previews for every pull request', href: '#changelog' }),
heading: 'The deploy platform for',
highlight: 'teams that ship daily',
description: 'Stackwise builds, previews and rolls out your frontend on every push. Instant rollbacks, zero-config edge caching and logs you can actually read.',
primaryAction: () => ({ label: 'Deploy your first app', href: '#start' }),
secondaryAction: () => ({ label: 'Read the docs', href: '#docs' }),
logosLabel: 'Powering releases at fast-moving teams',
logos: () => ['Quillstack', 'Norhaven', 'Parcelo', 'Brightloop', 'Ferrous', 'Oakmint'],
})
const emit = defineEmits<{
action: [kind: 'primary' | 'secondary' | 'announcement', event: MouseEvent]
}>()
// Each fictional wordmark gets its own typographic treatment so the row reads like real logos.
const wordmarkStyles = [
'font-black tracking-tight',
'font-serif italic font-semibold',
'font-mono font-bold uppercase tracking-widest text-sm',
'font-extrabold lowercase tracking-tighter',
'font-semibold uppercase tracking-[0.2em] text-sm',
'font-bold italic tracking-tight',
]
</script>
<template>
<section class="relative isolate overflow-hidden">
<!-- Background: faint grid fading out, plus a soft colour glow at the top. -->
<div aria-hidden="true" class="absolute inset-0 -z-10 bg-[linear-gradient(to_right,rgb(0_0_0/0.05)_1px,transparent_1px),linear-gradient(to_bottom,rgb(0_0_0/0.05)_1px,transparent_1px)] mask-[radial-gradient(ellipse_at_top,black_30%,transparent_75%)] bg-size-[48px_48px] dark:bg-[linear-gradient(to_right,rgb(255_255_255/0.06)_1px,transparent_1px),linear-gradient(to_bottom,rgb(255_255_255/0.06)_1px,transparent_1px)]" />
<div aria-hidden="true" class="absolute top-0 left-1/2 -z-10 h-[28rem] w-[56rem] max-w-none -translate-x-1/2 -translate-y-1/2 rounded-full bg-gradient-to-r from-violet-400/40 via-fuchsia-300/30 to-sky-300/30 blur-3xl dark:from-violet-600/30 dark:via-fuchsia-600/20 dark:to-sky-600/20" />
<div class="mx-auto max-w-4xl px-6 pt-24 pb-20 text-center sm:pt-32 sm:pb-24 lg:px-8">
<a
v-if="announcement"
:href="announcement.href"
class="group inline-flex max-w-full items-center gap-2 rounded-full bg-white/70 py-1 pr-3 pl-1 text-sm text-gray-700 shadow-sm ring-1 ring-gray-900/10 backdrop-blur transition hover:ring-gray-900/20 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-violet-600 dark:bg-white/5 dark:text-gray-300 dark:ring-white/10 dark:hover:ring-white/20"
@click="emit('action', 'announcement', $event)"
>
<span v-if="announcement.badge" class="rounded-full bg-violet-600 px-2.5 py-0.5 text-xs font-semibold text-white dark:bg-violet-500">{{ announcement.badge }}</span>
<span class="truncate">{{ announcement.text }}</span>
<svg viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" class="size-4 shrink-0 text-gray-400 transition group-hover:translate-x-0.5"><path fill-rule="evenodd" d="M7.2 14.8a.75.75 0 0 1 0-1.06L10.94 10 7.2 6.26a.75.75 0 1 1 1.06-1.06l4.27 4.27a.75.75 0 0 1 0 1.06L8.26 14.8a.75.75 0 0 1-1.06 0Z" clip-rule="evenodd" /></svg>
</a>
<h1 class="mt-8 text-4xl font-bold tracking-tight text-balance text-gray-900 sm:text-6xl lg:text-7xl dark:text-white">
<slot name="heading">
{{ heading }}
<span v-if="highlight" class="bg-gradient-to-r from-violet-600 to-fuchsia-500 bg-clip-text text-transparent dark:from-violet-400 dark:to-fuchsia-400">{{ highlight }}</span>
</slot>
</h1>
<p class="mx-auto mt-6 max-w-2xl text-lg leading-8 text-pretty text-gray-600 sm:text-xl dark:text-gray-300">{{ description }}</p>
<div class="mt-10 flex flex-col items-stretch justify-center gap-3 sm:flex-row sm:items-center">
<a
:href="primaryAction.href"
class="inline-flex items-center justify-center rounded-full bg-gray-900 px-6 py-3 text-sm font-semibold text-white shadow-lg shadow-violet-600/20 transition hover:bg-gray-700 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-violet-600 dark:bg-white dark:text-gray-900 dark:hover:bg-gray-200"
@click="emit('action', 'primary', $event)"
>
{{ primaryAction.label }}
</a>
<a
v-if="secondaryAction"
:href="secondaryAction.href"
class="group inline-flex items-center justify-center gap-1.5 rounded-full px-6 py-3 text-sm font-semibold text-gray-900 transition hover:bg-gray-900/5 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-violet-600 dark:text-white dark:hover:bg-white/10"
@click="emit('action', 'secondary', $event)"
>
{{ secondaryAction.label }}
<span aria-hidden="true" class="transition group-hover:translate-x-0.5">→</span>
</a>
</div>
<slot />
<div v-if="logosLabel && logos.length" class="mt-20">
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">{{ logosLabel }}</p>
<ul class="mt-6 grid grid-cols-2 items-center gap-x-8 gap-y-6 sm:grid-cols-3 lg:grid-cols-6">
<li
v-for="(logo, i) in logos"
:key="logo"
class="text-lg text-gray-400 transition hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300"
:class="wordmarkStyles[i % wordmarkStyles.length]"
>
{{ logo }}
</li>
</ul>
</div>
</div>
</section>
</template>
| Prop | Type | Default | Description |
|---|---|---|---|
| announcement | Announcement | null | { badge: 'New', text: 'Branch previews for every pull request', href: '#changelog' } | Pill above the heading. Pass null to hide it. |
| heading | string | 'The deploy platform for' | Main headline. |
| highlight | string | 'teams that ship daily' | Gradient-coloured phrase appended to the heading. |
| description | string | 'Stackwise builds, previews and rolls out…' | Supporting paragraph. |
| primaryAction | HeroLink | { label: 'Deploy your first app', href: '#start' } | Primary button. |
| secondaryAction | HeroLink | null | { label: 'Read the docs', href: '#docs' } | Secondary text button. Pass null to hide it. |
| logosLabel | string | 'Powering releases at fast-moving teams' | Caption above the wordmark row. An empty string hides the row. |
| logos | string[] | ['Quillstack', 'Norhaven', 'Parcelo', 'Brightloop', 'Ferrous', 'Oakmint'] | Brand names rendered as text wordmarks. |
| Event | Payload | Description |
|---|---|---|
| action | (kind: 'primary' | 'secondary' | 'announcement', event: MouseEvent) | Fired when a CTA or the announcement pill is clicked. |
| Slot | Description |
|---|---|
| heading | Replaces the heading and highlight inside the h1. |
| default | Extra content rendered under the buttons, e.g. a screenshot. |
Component · Hero sections
Split Vue 3 hero section with copy and two CTAs on the left and a Tailwind-drawn app window mockup on the right. Dark mode, typed props, no images.
Component · Hero sections
Vue 3 waitlist hero with an accessible email form: inline validation, loading and success states, and a typed submit event. Tailwind CSS, dark mode.
Component · Feature sections
"Trusted by" logo cloud for Vue 3 with a bordered grid or an endless marquee, using text wordmarks and SVG marks. Tailwind CSS, dark mode, reduced motion.
Component · Call to action
Vue 3 call-to-action banner in three layouts (glowing panel, split card and slim band) with primary and secondary buttons. Tailwind CSS, dark mode.