A two-column contact section: email, live chat and office hours cards on the left, a validated form with a topic select and a character counter inside a rotating border beam on the right.
Contact
No ticket mazes and no bots. Every message lands with someone on the Northwind team who can actually fix things.
npx shadcn@latest add https://hyperblur.dev/r/contact-split-form.jsonWorks in any React project set up with the shadcn CLI. The file lands in components/hyperblur/contact-split-form.tsx.
import { ContactSplitForm } from "@/components/hyperblur/contact-split-form"
<ContactSplitForm onSubmit={async (values) => fetch("/api/contact", { method: "POST", body: JSON.stringify(values) })} />| Prop | Type | Default | Description |
|---|---|---|---|
| eyebrow | string | "Contact" | Small label above the title. |
| title | string | "Talk to the people who build it" | Section heading. |
| subtitle | string | "No ticket mazes and no bots..." | Supporting paragraph under the heading. |
| methods | { icon: "email" | "chat" | "calendar" | "phone"; title: string; description: string; status?: string; action: { label: string; href: string } }[] | Email, live chat, office hours | Contact method cards. A status adds a pulsing online chip. |
| topics | string[] | 6 topics | Options of the topic select. |
| maxLength | number | 1000 | Character limit of the message. The counter turns amber at 90% and red past the limit. |
| responseNote | string | "We reply within one business day" | Note next to the submit button. |
| onSubmit | (values: { name: string; email: string; company: string; topic: string; message: string }) => void | Promise<unknown> | Called with the values after validation. A returned promise keeps the loading state until it settles; a rejection shows its message. | |
| accent | string | "#ff6b4a" | Border beam, icons, focus ring and button color. |