Vue componentFree

Hero Centered

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

Install
Full screen ↗

About this component

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.

  • Announcement pill with badge
  • Gradient headline accent
  • CSS grid + glow background, no images
  • Wordmark social-proof row
  • Light and dark mode

Code

11 files in the repo
<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>

Props

PropTypeDefaultDescription
announcementAnnouncement | null{ badge: 'New', text: 'Branch previews for every pull request', href: '#changelog' }Pill above the heading. Pass null to hide it.
headingstring'The deploy platform for'Main headline.
highlightstring'teams that ship daily'Gradient-coloured phrase appended to the heading.
descriptionstring'Stackwise builds, previews and rolls out…'Supporting paragraph.
primaryActionHeroLink{ label: 'Deploy your first app', href: '#start' }Primary button.
secondaryActionHeroLink | null{ label: 'Read the docs', href: '#docs' }Secondary text button. Pass null to hide it.
logosLabelstring'Powering releases at fast-moving teams'Caption above the wordmark row. An empty string hides the row.
logosstring[]['Quillstack', 'Norhaven', 'Parcelo', 'Brightloop', 'Ferrous', 'Oakmint']Brand names rendered as text wordmarks.

Events

EventPayloadDescription
action(kind: 'primary' | 'secondary' | 'announcement', event: MouseEvent)Fired when a CTA or the announcement pill is clicked.

Slots

SlotDescription
headingReplaces the heading and highlight inside the h1.
defaultExtra content rendered under the buttons, e.g. a screenshot.
All hero sections →
Preview of Hero SplitFree

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.

by Vueplay1 installs
Preview of Hero Email CaptureFree

Component · Hero sections

Hero Email Capture

Vue 3 waitlist hero with an accessible email form: inline validation, loading and success states, and a typed submit event. Tailwind CSS, dark mode.

by Vueplay
Preview of Logo CloudFree

Component · Feature sections

Logo Cloud

"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.

by Vueplay
Preview of CTA BannerFree

Component · Call to action

CTA Banner

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.

by Vueplay

Pro feature

Upgrade to Pro

Upgrade to unlock more of Vueplay. Cancel any time.

  • Custom domains with HTTPS
  • Private projects
  • No "Made with Vueplay" badge
  • GitHub sync

$120/year

Compare plansBilling settings