All Taste components
Features
Features — tabs switching panels
Four large tabs across the top; picking one swaps the panel underneath with its words and picture.
4 tabs with icon, title and hint · underline on the active tab · text + picture panel · 2 × 2 tabs on small screens
How Fernbill fits your week
One place for every step of getting paid
Quotes that turn into work
Send a clear quote with options. When the client accepts, it becomes an invoice — no retyping.
- Optional extras clients can tick
- Accept and sign on any phone
Invoices with your name on them
Your logo, colours and terms on every invoice. Numbers go up on their own; taxes add themselves.
- Your branding, set once
- Tax and discounts per line
Reminders that sound like you
Pick the days and the tone. Fernbill sends the follow-ups and stops the second the money lands.
- Friendly, firm or final tone
- Skips weekends and holidays
Reports in plain numbers
See what came in, what's owed and what you'll owe in tax — by month, client or currency.
- Export CSV or PDF
- Share read-only with your accountant
"use client";
import { useState } from "react";
import { BarChart3, Check, FileText, Mail, Receipt, type LucideIcon } from "lucide-react";
import { cn } from "@/lib/utils";
type Feature = {
id: string; icon: LucideIcon; tab: string; hint: string;
title: string; text: string; points: string[];
card: { title: string; tag: string; lines: [string, string][] };
};
const features: Feature[] = [
{ id: "quote", icon: FileText, tab: "Quote", hint: "Turn a quote into an invoice in one click.",
title: "Quotes that turn into work", text: "Send a clear quote with options. When the client accepts, it becomes an invoice — no retyping.",
points: ["Optional extras clients can tick", "Accept and sign on any phone"],
card: { title: "Quote Q-208", tag: "Draft", lines: [["Website redesign", "$4,800"], ["Extra page (optional)", "$350"], ["Hosting, 12 months", "$180"]] } },
{ id: "invoice", icon: Receipt, tab: "Invoice", hint: "Branded, numbered and sent in a minute.",
title: "Invoices with your name on them", text: "Your logo, colours and terms on every invoice. Numbers go up on their own; taxes add themselves.",
points: ["Your branding, set once", "Tax and discounts per line"],
card: { title: "Invoice INV-1043", tag: "Sent", lines: [["Design sprint", "$2,400"], ["Copywriting, 6 pages", "$900"], ["Tax (18%)", "$594"]] } },
{ id: "remind", icon: Mail, tab: "Remind", hint: "Nudges that stop the moment you’re paid.",
title: "Reminders that sound like you", text: "Pick the days and the tone. Fernbill sends the follow-ups and stops the second the money lands.",
points: ["Friendly, firm or final tone", "Skips weekends and holidays"],
card: { title: "Reminder plan", tag: "On", lines: [["3 days before due", "Friendly"], ["On the due date", "Friendly"], ["7 days late", "Firm"]] } },
{ id: "report", icon: BarChart3, tab: "Report", hint: "Totals your accountant will thank you for.",
title: "Reports in plain numbers", text: "See what came in, what’s owed and what you’ll owe in tax — by month, client or currency.",
points: ["Export CSV or PDF", "Share read-only with your accountant"],
card: { title: "Q3 summary", tag: "Jul – Sep", lines: [["Paid this quarter", "$48,210"], ["Outstanding", "$8,420"], ["Tax set aside", "$7,230"]] } },
];
export function FeaturesTabs() {
const [active, setActive] = useState(features[0].id);
const f = features.find((x) => x.id === active) ?? features[0];
return (
<section aria-labelledby="features-title" className="bg-bg font-sans">
<div className="mx-auto max-w-6xl px-4 py-16 sm:px-6 lg:py-24">
<p className="text-[13px] font-medium text-primary">How Fernbill fits your week</p>
<h2 id="features-title" className="mt-3.5 max-w-2xl text-balance text-3xl font-semibold tracking-tight text-fg sm:text-4xl">
One place for every step of getting paid
</h2>
<div role="tablist" aria-label="Features" className="mb-9 mt-12 grid grid-cols-2 gap-y-2 border-b border-border md:grid-cols-4">
{features.map(({ id, icon: Icon, tab, hint }) => (
<button
key={id}
type="button"
role="tab"
id={"tab-" + id}
aria-selected={active === id}
aria-controls={"panel-" + id}
onClick={() => setActive(id)}
className="group -mb-px grid justify-items-start gap-1.5 border-b-2 border-transparent pb-4 pr-4 text-left text-fg-muted transition hover:text-fg aria-selected:border-primary aria-selected:text-fg"
>
<Icon className="size-5 group-aria-selected:text-primary" aria-hidden />
<span className="text-[15px] font-semibold tracking-tight">{tab}</span>
<span className="hidden text-[13px] leading-snug text-fg-subtle md:block">{hint}</span>
</button>
))}
</div>
<div role="tabpanel" id={"panel-" + f.id} aria-labelledby={"tab-" + f.id} className="grid items-center gap-6 lg:grid-cols-[1fr_1.3fr] lg:gap-12">
<div className="flex flex-col items-start gap-4">
<h3 className="text-balance text-2xl font-semibold tracking-tight text-fg">{f.title}</h3>
<p className="text-pretty text-[14.5px] leading-relaxed text-fg-muted">{f.text}</p>
<ul className="grid gap-2.5 text-sm text-fg">
{f.points.map((p) => (
<li key={p} className="flex gap-2.5"><Check className="mt-0.5 size-4 shrink-0 text-primary" aria-hidden />{p}</li>
))}
</ul>
</div>
<div aria-hidden className="grid min-h-[260px] place-items-center rounded-xl bg-surface bg-gradient-to-br from-primary-soft to-surface p-5 sm:p-8">
<div className="grid w-full max-w-[340px] gap-2.5 rounded-lg border border-border bg-bg p-4 text-[12.5px] text-fg shadow-xl">
<div className="flex items-center justify-between">
<span className="font-semibold">{f.card.title}</span>
<span className="rounded-full bg-surface-2 px-2 py-0.5 text-xs text-fg-muted">{f.card.tag}</span>
</div>
<hr className="border-border" />
{f.card.lines.map(([a, b]) => (
<div key={a} className={cn("flex justify-between gap-3")}><span className="truncate">{a}</span><span className="tabular-nums">{b}</span></div>
))}
</div>
</div>
</div>
</div>
</section>
);
}
The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.