Skip to content
All Taste components

Contact

Contact — form with contact channels

A contact form with topic chips on one side and separate cards for sales, support and press on the other.

topic chips · 2-column fields · channel cards · stacks on mobile

Contact

Talk to a person, not a ticket queue

Tell us what you need and the right team will write back within one working day.

What can we help with?
We reply within one working day.
"use client";
import { useState } from "react";
import { BarChart3, FileText, MessageCircle, type LucideIcon } from "lucide-react";
import { cn } from "@/lib/utils";

const channels: { icon: LucideIcon; title: string; note: string; email: string }[] = [
  { icon: BarChart3, title: "Sales", note: "Plans for teams of 10 or more.", email: "sales@tallybook.example" },
  { icon: MessageCircle, title: "Support", note: "Help with your account, any time.", email: "help@tallybook.example" },
  { icon: FileText, title: "Press", note: "Stories, interviews and assets.", email: "press@tallybook.example" },
];

const topics = ["A demo", "Pricing", "Moving from another tool", "Something else"];

const field = "h-9 w-full rounded border border-border-strong bg-bg px-3 text-sm text-fg outline-none placeholder:text-fg-subtle focus:border-primary focus:ring-[3px] focus:ring-primary/20";

export function ContactWithChannels({ onSubmit }: { onSubmit?: (data: FormData) => void }) {
  const [topic, setTopic] = useState(topics[0]);
  return (
    <section aria-labelledby="contact-channels-title" className="bg-surface px-5 py-16 font-sans sm:px-10 sm:py-20">
      <div className="mx-auto grid max-w-6xl gap-9 lg:grid-cols-[0.9fr_1.1fr] lg:gap-16">
        <div>
          <p className="text-[13px] font-medium text-primary">Contact</p>
          <h2 id="contact-channels-title" className="mt-3 text-3xl font-semibold tracking-tight text-fg sm:text-4xl">Talk to a person, not a ticket queue</h2>
          <p className="mt-4 text-base text-fg-muted sm:text-lg">Tell us what you need and the right team will write back within one working day.</p>
          <ul className="mt-7 grid gap-3">
            {channels.map(({ icon: Icon, title, note, email }) => (
              <li key={title} className="flex items-start gap-3.5 rounded-lg border border-border bg-bg p-[18px]">
                <span className="grid size-10 shrink-0 place-items-center rounded bg-primary-soft text-primary-soft-fg"><Icon className="size-[19px]" aria-hidden /></span>
                <div className="grid min-w-0 gap-0.5">
                  <p className="text-sm font-semibold text-fg">{title}</p>
                  <p className="text-[13px] text-fg-subtle">{note}</p>
                  <a href={"mailto:" + email} className="break-all text-sm font-medium text-primary hover:underline">{email}</a>
                </div>
              </li>
            ))}
          </ul>
        </div>

        <form
          onSubmit={(e) => { e.preventDefault(); const data = new FormData(e.currentTarget); data.set("topic", topic); onSubmit?.(data); }}
          className="grid gap-4 rounded-xl border border-border bg-bg p-7 shadow-xl"
        >
          <fieldset>
            <legend className="mb-2 text-[13px] font-medium text-fg">What can we help with?</legend>
            <div className="flex flex-wrap gap-2">
              {topics.map((t) => (
                <button key={t} type="button" aria-pressed={topic === t} onClick={() => setTopic(t)}
                  className={cn("h-[34px] rounded-full border px-3.5 text-[13px] font-medium transition",
                    topic === t ? "border-primary bg-primary-soft text-primary-soft-fg" : "border-border-strong bg-bg text-fg-muted hover:text-fg")}>
                  {t}
                </button>
              ))}
            </div>
          </fieldset>
          <div className="grid gap-4 sm:grid-cols-2">
            <label className="grid gap-1.5"><span className="text-[13px] font-medium text-fg">Full name</span><input name="name" required autoComplete="name" className={field} /></label>
            <label className="grid gap-1.5"><span className="text-[13px] font-medium text-fg">Work email</span><input name="email" type="email" required autoComplete="email" placeholder="you@company.com" className={field} /></label>
          </div>
          <div className="grid gap-4 sm:grid-cols-2">
            <label className="grid gap-1.5"><span className="text-[13px] font-medium text-fg">Company</span><input name="company" autoComplete="organization" className={field} /></label>
            <label className="grid gap-1.5">
              <span className="text-[13px] font-medium text-fg">Team size</span>
              <select name="size" defaultValue="2-10" className={field}>
                <option value="1">Just me</option><option value="2-10">2–10 people</option><option value="11-50">11–50 people</option><option value="51+">51 or more</option>
              </select>
            </label>
          </div>
          <label className="grid gap-1.5">
            <span className="text-[13px] font-medium text-fg">Message</span>
            <textarea name="message" rows={4} placeholder="A little about how you invoice today…" className={cn(field, "h-auto py-2.5")} />
          </label>
          <div className="flex flex-wrap items-center justify-between gap-3">
            <span className="text-[13px] text-fg-subtle">We reply within one working day.</span>
            <button type="submit" className="inline-flex h-9 items-center rounded bg-primary px-4 text-sm font-medium text-primary-fg transition hover:bg-primary-hover">Send message</button>
          </div>
        </form>
      </div>
    </section>
  );
}

The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.