Contact section
A validated contact form that morphs into a confirmation, support channels, and office cards with local times.
pnpm dlx shadcn@latest add @uiarc/contact-section- Contact pages and sections on marketing sites.
- Sales or support pages that list several channels with response times.
- Companies with offices in several time zones.
- Use support-widget for an in-app help launcher.
- Use signup-form or login blocks for account flows.
Installation
Add Contact section with the shadcn CLI, or copy the source by hand.
pnpm dlx shadcn@latest add @uiarc/contact-sectionAdds the block and its local dependencies, and installs motion, lucide-react. First time? Add the @uiarc registry to components.json, or use the full URL:
Usage
Use this as a starting point and replace the sample data with your own.
import { ContactSection } from "@/registry/blocks/contact-section/contact-section"; export function Contact() { return ( <ContactSection title="Talk to us" topics={["Sales", "Support"]} onSubmit={async message => { const response = await fetch("/api/contact", { method: "POST", body: JSON.stringify(message) }); if (!response.ok) throw new Error("Could not send"); }} /> );}API reference
2 parts. The first is the root.
ContactSection
A contact section in three layouts: a validated form that springs into a confirmation, a list of channels with detail, or offices with live local times and open status.
variant"form" | "channels" | "offices""form"Layout.titlestring–Section heading.descriptionstring–One line under the heading.topicsstring[]["Sales", "Support", "Partnerships", "Press"]Topics offered in the form as a radio group of chips.onSubmit(message: { name; email; topic; message }) => void | Promise<void>–Called with a valid message. Reject to keep the form and show an error; resolve to show the confirmation.channels{ value; label; meta; icon?; detail: ReactNode }[]contactExampleChannelsWays to reach you for the channels variant.channelstring–Selected channel (controlled).defaultChannelstring–Initial channel when uncontrolled.onChannelChange(value: string) => void–Called when the channel changes.offices{ city; timeZone; address: string[]; email?; hours?: [number, number] }[]contactExampleOfficesOffices. timeZone is an IANA zone; hours default to 9 to 18 local time.classNamestring–Extra class on the section.ContactSectionBlock
Default export: a preview with a layout switcher. Messages are simulated and never sent.
variantContactSectionVariant"form"Initial variant.- Arrow keys
- Move between topic chips, which form a radio group.
- Fields use labelled Arc Input and Textarea; errors appear after the first submit and update as you type.
- A failed send shows a role="alert" message and keeps the form.
- Office open status is written out, not shown by color alone.
- The form card springs into the confirmation; faces slide along one axis in the direction of travel out of a soft blur.
- The topic highlight glides between chips.
- Reduced motion swaps faces without travel.
- Container queries: below 820px the form layout stacks and the channel list becomes a horizontal scrolling row; below 520px field rows stack and the submit button fills the width.
- The offices variant re-renders every 15 seconds to keep local times live; other variants have no timers.
Notes for AI
Give your coding assistant the Markdown reference instead of screenshots.
- Without onSubmit the form only simulates sending; always wire it in production.
- Offices compute local time and open status with Intl.DateTimeFormat in each timeZone.
- Composes Arc Input, Textarea, Button, CopyButton, SegmentedControl, and Avatar.
The full library index for assistants is at /llms.txt.