A company handbook assistant with follow-up questions under the card. Pick one and the answer folds away, the card eases to its new height and the next answer reads and writes itself. Its status line says "Checked 7 handbook pages".
registry/demos/footnote-follow-ups-demo.tsx
"use client";
import * as React from "react";
import { Footnote, type FootnoteClaim, type FootnoteSource } from "@/components/ui/footnote";
const sources: Record<string, FootnoteSource> = {
abroad: {
id: "abroad",
site: "handbook.kiln.team",
title: "Working from abroad",
href: "#working-from-abroad",
snippet:
"Everyone can work from another country for up to 30 working days per calendar year, taken in blocks of no more than 10 consecutive working days.",
quote: "up to 30 working days per calendar year",
},
requests: {
id: "requests",
site: "hr.kiln.team",
title: "Requesting time abroad",
href: "#requesting-time-abroad",
snippet:
"Submit the request in Kiln HR at least 14 days before you travel. Your lead approves it, and payroll and People Ops are told automatically.",
quote: "at least 14 days before you travel",
},
offices: {
id: "offices",
site: "kiln.team/offices",
title: "Offices and hubs",
href: "#offices",
snippet: "We have offices in Berlin, Lisbon and Toronto. Days spent working from any Kiln office count as office days.",
quote: "count as office days",
},
excluded: {
id: "excluded",
site: "legal.kiln.team",
title: "Countries we can't support",
href: "#excluded-countries",
snippet:
"For tax and export reasons we can't approve work from sanctioned countries or from countries without a tax treaty with your employing entity.",
quote: "without a tax treaty",
},
payroll: {
id: "payroll",
site: "payroll.kiln.team",
title: "Payroll FAQ: working abroad",
href: "#payroll-faq",
snippet:
"If a block runs past 10 days or crosses into a new tax year, we'll get in touch within two working days to check your withholding.",
quote: "within two working days",
},
travel: {
id: "travel",
site: "handbook.kiln.team/travel",
title: "Travel and expenses",
href: "#travel-policy",
snippet: "Per diems apply to trips your team asks you to make. Time abroad you choose follows the normal expense rules.",
quote: "Per diems apply to trips your team asks you to make",
},
spaces: {
id: "spaces",
site: "spaces.kiln.team",
title: "Booking a desk in Lisbon",
href: "#lisbon-desks",
snippet: "Lisbon has 24 bookable desks. Book at least a day ahead; Tuesdays and Wednesdays fill first.",
quote: "Tuesdays and Wednesdays fill first",
},
};
type Turn = { question: string; answer: FootnoteClaim[]; sources: FootnoteSource[]; read: number };
const turns: Turn[] = [
{
question: "How many days can I work from abroad each year?",
answer: [
{ text: "Up to 30 working days a year, in blocks of at most 10 days in a row.", cite: "abroad" },
{ text: "Each block needs your lead's approval in Kiln HR two weeks before you go.", cite: "requests" },
{ text: "Days at a Kiln office in another country don't count toward the 30.", cite: ["abroad", "offices"] },
{ text: "Sanctioned countries, and any without a tax treaty, can't be approved.", cite: "excluded" },
],
sources: [sources.abroad, sources.requests, sources.offices, sources.excluded],
read: 7,
},
{
question: "Do I need to tell payroll first?",
answer: [
{ text: "No. Once your lead approves the request, payroll gets the dates automatically.", cite: "requests" },
{
text: "If a block runs past 10 days or into a new tax year, payroll will check your withholding within two working days.",
cite: "payroll",
},
{ text: "Per diems don't apply, since it's time abroad you chose rather than a work trip.", cite: "travel" },
],
sources: [sources.requests, sources.payroll, sources.travel],
read: 5,
},
{
question: "Does the Lisbon office count as abroad?",
answer: [
{ text: "No. Days working from the Lisbon office count as office days, not days abroad.", cite: "offices" },
{ text: "Book a desk at least a day ahead: there are 24, and Tuesdays fill first.", cite: "spaces" },
{
text: "For stays longer than a week, file a request anyway so the visit shows on your team's calendar.",
cite: "requests",
},
],
sources: [sources.offices, sources.spaces, sources.requests],
read: 4,
},
];
export function FootnoteFollowUpsDemo() {
const [current, setCurrent] = React.useState(0);
const turn = turns[current];
const next = turns.map((t, i) => ({ ...t, i })).filter((t) => t.i !== current);
return (
<div className="flex w-full max-w-lg flex-col gap-3">
<Footnote
question={turn.question}
answer={turn.answer}
sources={turn.sources}
read={turn.read}
avatar={<KilnMark className="size-3.5" />}
status={(read, done) => (done ? `Checked ${read} handbook pages` : `Checking the handbook · ${read}`)}
className="[--footnote-accent:#c2410c] dark:[--footnote-accent:#fb923c]"
/>
<div className="flex flex-wrap items-center gap-1.5 pl-1">
<span className="mr-1 text-xs text-zinc-500 dark:text-zinc-400">Ask next</span>
{next.map((t) => (
<button
key={t.i}
type="button"
onClick={() => setCurrent(t.i)}
className="inline-flex h-8 cursor-pointer items-center gap-1.5 rounded-full bg-white px-3 text-xs text-zinc-700 shadow-sm ring-1 ring-zinc-950/10 transition-[background-color,color,scale] duration-150 ease-out hover:bg-zinc-50 hover:text-zinc-950 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-zinc-950 active:scale-[0.97] dark:bg-zinc-900 dark:text-zinc-300 dark:ring-white/10 dark:hover:bg-zinc-800 dark:hover:text-white dark:focus-visible:outline-white"
>
<ReplyIcon className="size-3 text-zinc-400" />
{t.question}
</button>
))}
</div>
</div>
);
}
function KilnMark({ className }: { className?: string }) {
return (
<svg viewBox="0 0 16 16" className={className} aria-hidden>
<path d="M3 14V7.5a5 5 0 0 1 10 0V14h-3V9.5a2 2 0 0 0-4 0V14Z" fill="currentColor" />
</svg>
);
}
function ReplyIcon({ className }: { className?: string }) {
return (
<svg viewBox="0 0 12 12" className={className} fill="none" aria-hidden>
<path
d="M2.5 2.5v3a2 2 0 0 0 2 2h5m0 0L7.5 5.5m2 2-2 2"
stroke="currentColor"
strokeWidth={1.3}
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
Live preview, examples and props: https://keyframeui.com/components/footnote#footnote-follow-ups-demo