Contact template
BlockA contact page that routes the message first: pick the topic and it says who answers and how soon. The form keeps what you typed if sending fails and confirms where the reply will go.
pnpm dlx shadcn@latest add https://caliperui.site/r/contact-template.jsonContact
Talk to a person
Choose what it is about and we will say who answers and how soon. Type “fail” in the message to see a failed send.
- Pricing and plansThe sales team replies within one working day.sales@halden.app
- Help with my accountSupport replies within four working hours, 8:00–18:00 Lisbon time.help@halden.app
- Security or complianceOur security lead replies within two working days. For an urgent issue, write to security@halden.app.security@halden.app
- PressJoana Reis replies within two working days.press@halden.app
Offices
- LisbonRua do Ouro 120, 3º, 1100-063 LisboaMon–Fri 9:00–18:00 · WETDirections to the Lisbon office
- London71 Leonard Street, London EC2A 4QSMon–Thu 9:00–18:00 · GMTDirections to the London office
FAQ
Before you write
Straight answers, each linked to the page that explains it in full.
Not here? Ask us about it
- No. It prepares the close; your accountant reviews it and signs off, usually faster. How review works
- 11,000 banks through Plaid, plus Xero, QuickBooks and NetSuite in both directions. Connections
- Undo it in one click. Halden learns from the correction for that vendor. Corrections
- Never. Your data is processed in eu-central-1 and deleted 30 days after you leave. Data processing terms
When to use #
Use it for
- Sales, support and press enquiries on one page.
- Teams with offices people visit.
Skip it when
- Signed-in customers with an account; give them in-app support.
Installation #
pnpm dlx shadcn@latest add https://caliperui.site/r/contact-template.json
Adds its 16 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.
Usage #
This is the code behind the preview above.
import { ContactTemplate } from "@/components/ui/contact-template" const nav = (current: string) => [ { label: "Product", href: "#product" }, { label: "Customers", href: "#customers" }, { label: "Blog", href: "#blog" }, { label: "About", href: "#about" }, { label: "Contact", href: "#contact" }, ].map((link) => ({ ...link, current: link.label.toLowerCase() === current })); const signIn = { label: "Sign in", href: "#" }; const frame = "h-[720px] w-full overflow-auto rounded-[14px] shadow-[0_0_0_1px_var(--border)]"; const wait = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)); export function ContactDemo() { return ( <div className={frame}> <ContactTemplate brand="Halden" nav={nav("contact")} signIn={signIn} primary={heroes.primary!} intro={{ eyebrow: "Contact", title: "Talk to a person", lead: "Choose what it is about and we will say who answers and how soon. Type “fail” in the message to see a failed send." }} topics={[ { value: "sales", label: "Pricing and plans", response: "The sales team replies within one working day.", email: "sales@halden.app" }, { value: "support", label: "Help with my account", response: "Support replies within four working hours, 8:00–18:00 Lisbon time.", email: "help@halden.app" }, { value: "security", label: "Security or compliance", response: "Our security lead replies within two working days. For an urgent issue, write to security@halden.app.", email: "security@halden.app" }, { value: "press", label: "Press", response: "Joana Reis replies within two working days.", email: "press@halden.app" }, ]} offices={[ { city: "Lisbon", address: "Rua do Ouro 120, 3º, 1100-063 Lisboa", hours: "Mon–Fri 9:00–18:00", timezone: "WET", mapHref: "#lisbon" }, { city: "London", address: "71 Leonard Street, London EC2A 4QS", hours: "Mon–Thu 9:00–18:00", timezone: "GMT", mapHref: "#london" }, ]} faq={{ ...faqs, title: "Before you write", items: faqs.items.slice(0, 4) }} onSubmit={async (message) => { await wait(800); if (message.message.toLowerCase().includes("fail")) throw new Error("Our mail server did not answer."); }} footer={footers} /> </div> ); }
API reference #
ContactTemplate
component
Accessibility #
It exposes status semantics, with aria-label, aria-hidden, aria-labelledby and aria-live kept in step with state. Changes that matter are announced through a live region.
Keyboard and focus handling comes from Radix popover: arrow keys move through items, Enter and Space select, and Escape closes and returns focus.
Motion #
It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.