Components / Blocks

ContactSection

A validated contact form that morphs into a confirmation, support channels, and office cards with local times.

Install

npx shadcn@latest add https://irisui.dev/r/contact-section.json
import { ContactSection } from "@/components/iris/blocks/contact-section/contact-section";
Registry item contact-sectionBrings in foundationRegistry JSON

Preview

ContactSection

A contact page section in three forms: a validated message form that morphs into a confirmation, a list of support channels with a detail pane, or office cards with live local times and open status.

When to use

  • A contact or support page where people choose how to reach you.
  • form to collect a message with a topic; channels to route people to chat, email, sales or community; offices to show where and when your team is reachable.
  • Not for a sign up or lead form with many fields: build it from Input and friends, or use SignupForm.
  • Not for in-app help; use a Popover or Drawer with your support widget.

Usage

import { ContactSection } from "@/components/iris/blocks/contact-section/contact-section";

h(ContactSection, {
  variant: "form",
  title: "Talk to the OpsisID team",
  description: "Pricing is by quote. Tell us your purposes, countries and monthly sessions.",
  topics: ["Sales", "Support", "Compliance", "Partnerships"],
  onSubmit: async ({ name, email, topic, message }) => {
    await api.contact({ name, email, topic, message });
  },
});

h(ContactSection, {
  variant: "offices",
  offices: [
    {
      city: "Athens",
      timeZone: "Europe/Athens",
      address: ["10 Sample Street, 4th floor", "106 00 Athens, Greece"],
      email: "athens@opsisid.example",
      hours: [9, 18],
    },
  ],
});

Props

Prop Type Default Description
variant "form" | "channels" | "offices" "form" Which layout to show.
title / description string per variant Heading and supporting text; each variant has its own sample copy for an identity verification team.
topics string[] Sales, Support, Compliance, Press Topic chips in the form.
onSubmit (message: { name, email, topic, message }) => void | Promise<void> none Called with a valid message. Reject to keep the form and show an error; resolve to show the confirmation.
channels ContactChannel[] 4 sample channels (chat, email, sales, a decision review) { value, label, meta, icon?, detail: ReactNode }; detail fills the pane when selected.
channel / defaultChannel / onChannelChange string first channel Selected channel (controlled / uncontrolled).
offices ContactOffice[] 3 sample offices (Athens, Amsterdam, Lisbon) { city, timeZone (IANA), address: string[], email?, hours?: [open, close] }; hours default to 9 to 18 local time, weekdays.
className string none Extra class on the section.

Notes

  • The message has a 500 character limit with a live counter; name, email and message are validated on submit and focus moves to the first problem.
  • Pass your own channels for real use: the defaults are sample copy, and the chat channel's agent shows as an initials avatar.
  • Office times refresh every 15 seconds, read in 24 hour time, and show Open or Closed from each office's own time zone.
  • Topic chips are a radio group and channels a tab list, both with roving focus and arrow keys.
  • Reduced motion removes the morph between the form and the confirmation.