Contact Bar Hero
A hero with a raised contact bar under the headline — one-click email copy, a phone link and a booking button — which can dock to the bottom of the viewport once the hero scrolls away.
Built for service homepages — studios, clinics, consultants — where the visitor's next step is getting in touch. Under the title and subtitle sits a rounded capsule contact bar in three parts: the email (with one-click copy; on success the icon turns into a check and the text reads "Copied" for 1.6 seconds), the phone (a tel: link) and an accent-coloured booking button. With dock on, once the visitor scrolls past the whole hero a compact contact bar springs in at the bottom of the viewport; close it with × and it stays hidden for the rest of the visit. On the right (or below, when centred) is a window-view placeholder you can swap for your own photo.
Bring this into your project
Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.
npx webberui@latest add contact-bar-hero.enPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<ContactBarHero/>
The complete example includes the preview layout and data. Install the component first, then paste this file into your project.
Installation
npx webberui@latest add https://webberui.com/r/contact-bar-hero.en.jsonOr, once registries are configured in components.json, install it as @webberui/contact-bar-hero.
Usage
import { ContactBarHero } from "@/components/ui/contact-bar-hero";
<ContactBarHero
eyebrow="栖木空間|住宅與小店設計"
title="把光留在家裡,從一通電話開始"
subtitle="專做 20 坪以下的老屋翻新,三個工作天內回覆初步規劃。"
email="hello@qimu-studio.example"
phone="02-2345-6789"
booking={{ label: "預約諮詢", href: "/booking" }}
images={[{ src: "/images/studio-window.jpg", alt: "工作室窗邊" }]}
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
title | ReactNode | — | Main title (required) |
eyebrow | ReactNode | — | Small label above the title |
subtitle | ReactNode | — | Subtitle |
primaryAction | ContactBarHeroAction | — | Primary CTA under the title (optional, sits alongside the contact bar) |
secondaryAction | ContactBarHeroAction | — | Secondary CTA under the title |
as | "h1" | "h2" | "h1" | Heading level used for the title |
minHeight | number | 560 | Minimum section height (px) |
images | ContactBarHeroImage[] | — | Media images; only the first is used, and a placeholder is drawn without src |
email | string | — | Contact email, copyable in one click (required) |
phone | string | — | Contact phone; shown as given, the dial link keeps only digits and + |
booking | ContactBarHeroAction | — | Primary booking button at the right end of the contact bar (required) |
dock | boolean | true | Whether a compact contact bar docks to the bottom of the viewport once the hero scrolls away |
dismissed | boolean | — | Controlled: whether the user has closed the dock |
defaultDismissed | boolean | false | Uncontrolled: initial closed state of the dock |
onDismissedChange | (dismissed: boolean) => void | — | Called when the dock's closed state changes |
onCopy | (email: string) => void | — | Called after the email is copied successfully |
align | "left" | "center" | "left" | Text alignment; with left the media sits on the right, with center it sits below |
showMedia | boolean | true | Whether to show the media image |
labels | ContactBarHeroLabels | — | Interface string overrides |
className | string | — | Passed through to the root section |
ContactBarHeroAction
| Field | Type | Default | Description |
|---|---|---|---|
label | string | — | Button text |
href | string | — | Link URL; renders a link when provided |
onClick | () => void | — | Click callback; without href it renders a type="button" button |
ContactBarHeroImage
| Field | Type | Default | Description |
|---|---|---|---|
src | string | — | Image URL; when omitted a CSS gradient + SVG placeholder is used |
alt | string | — | Alt text |
ContactBarHeroLabels
| Field | Type | Default | Description |
|---|---|---|---|
copy | string | "複製" (Copy) | Copy button text |
copied | string | "已複製" (Copied) | Text after a successful copy |
copyFailed | string | "無法複製" (Copy failed) | Text when copying fails |
close | string | "關閉快速聯絡列" (Close quick contact bar) | aria-label of the dock close button |
group | string | "聯絡方式" (Contact options) | aria-label of the contact bar |
dock | string | "快速聯絡" (Quick contact) | aria-label of the dock |
How it works
- Timing variable:
--wb-contact-bar-hero-durationdrives both the title group's entrance (600ms when unset) and the contact bar's hover lift (200ms when unset). The accent is--wb-contact-bar-hero-accent(default#0d9488in light,#2dd4bfin dark); the booking button, eyebrow and focus ring all follow it. - Entrance order: eyebrow, title, subtitle and CTAs fade in with y 16→0 in sequence (80ms stagger,
cubic-bezier(0.22,1,0.36,1)); 300ms after the title starts, the contact bar rises from y 24 on a spring (stiffness 260, damping 24). - Dock trigger: an IntersectionObserver watches the whole section, and the dock appears only once the hero has scrolled entirely above the viewport — a hero still below the fold on page load never triggers it. The closed state supports controlled (
dismissed+onDismissedChange) and uncontrolled use. - Copy uses
navigator.clipboard.writeText; on non-HTTPS origins or when permission is denied it shows the copy-failed text. The dial link strips spaces and hyphens, keeping only digits and+. - The component itself uses no vh units; its height comes from the content and
minHeight.
Accessibility
- The root is a
<section aria-labelledby>pointing at the title (aspicks h1 or h2) - The contact bar is
role="group"with anaria-label; the dock is an<aside>with its ownaria-label - The copy button's
aria-labelincludes the full email; the result is announced through arole="status",aria-live="polite"region - Icon-only buttons in the dock all carry an
aria-label; every button and link has a focus-visible ring, and buttons aretype="button" - With the system "reduce motion" setting on, the entrance becomes a 150ms fade, the contact bar no longer moves, the hover lift is off, and the dock fades in and out instead of sliding
Angled Gallery Hero
A hero with the headline on the left and a gallery of images set at a 3D angle drifting slowly sideways on the right, two rows in opposite directions, pausing on hover.
Count-Up Stats Hero
A dark hero React component: a left-aligned headline over 2–4 key figures. On entering the viewport the dividers draw in, each figure counts up from 0 in sequence, and an accent underline draws on completion.