# Seat Limit Help

> Billing help card with seat usage, invoice details, a focusable disabled invite action, and collapsible answers explaining the seat limit and pending invites.

A shadcn/ui block from the [@01orbital](https://01orbital.dev/?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/01orbital/seat-limit-help).

- Page, with a live preview: https://shadcn.exchange/r/01orbital/seat-limit-help
- Screenshot: https://shadcn.exchange/shot/01orbital/seat-limit-help/light.png
- Registry item: https://01orbital.dev/r/seat-limit-help.json
- Categories: faq
- Listed: 2026-11-01, source last changed 2026-11-01

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/01orbital/seat-limit-help.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `@phosphor-icons/react`, `cn`
- Registry items: `@01orbital/button`, `card`, `@01orbital/collapsible`, `@01orbital/description-list`, `@01orbital/progress`

## Files

### `blocks/faq/seat-limit-help/index.tsx`

```tsx
import {
  CaretDownIcon,
  UserPlusIcon,
  WarningCircleIcon,
} from "@phosphor-icons/react/dist/ssr"
import { cn } from "cn"

import {
  DescriptionList,
  DescriptionListDetails,
  DescriptionListItem,
  DescriptionListTerm,
} from "@/components/editorial/description-list"
import { Button, buttonVariants } from "@/components/ui/button"
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"
import {
  Collapsible,
  CollapsibleContent,
  CollapsibleTrigger,
} from "@/components/ui/collapsible"
import { Progress, ProgressLabel } from "@/components/ui/progress"

// The copy: change text here, not in the markup.
const COPY = {
  title: "Seats",
  plan: "Team plan, 10 seats",
  usage: { label: "Seats used", value: "10 of 10" },
  limit: "Seat limit reached",
  invite: "Invite member",
} as const

const DETAILS = [
  { term: "Members", details: "8" },
  { term: "Pending invites", details: "2" },
  { term: "Next invoice", details: "1 November 2026" },
  { term: "10 seats at €14", details: "€140.00" },
] as const

const QUESTIONS = [
  {
    id: "limit",
    question: "Why can’t I invite anyone?",
    answer:
      "All 10 seats are taken: 8 by members and 2 by pending invites. Add a seat, or withdraw an invite, and the invite button works straight away. Each extra seat is €14 a month, and this month you pay only for the days left.",
    action: { label: "Add seats", href: "#" },
    open: true,
  },
  {
    id: "pending",
    question: "What happens to pending invites?",
    answer:
      "Each one holds a seat until the person accepts it. Invites nobody accepts are cancelled after 14 days, which frees the seat, or you can withdraw one yourself under Members.",
    open: false,
  },
] as const

export default function SeatLimitHelp() {
  return (
    <Card
      size="sm"
      className="@container/seats mx-auto w-full max-w-md min-w-0 pb-2 wrap-anywhere"
    >
      <CardHeader>
        <CardTitle>
          <h2>{COPY.title}</h2>
        </CardTitle>
        <CardDescription>{COPY.plan}</CardDescription>
      </CardHeader>
      <CardContent className="grid min-w-0 gap-6">
        {/* aria-valuetext reads the count the way it is written, not as 100%. */}
        <Progress
          value={10}
          max={10}
          aria-valuetext={COPY.usage.value}
          className="min-w-0"
        >
          <ProgressLabel>{COPY.usage.label}</ProgressLabel>
          <span
            aria-hidden="true"
            className="ml-auto min-w-0 text-sm text-muted-foreground tabular-nums"
          >
            {COPY.usage.value}
          </span>
        </Progress>
        <DescriptionList>
          {DETAILS.map((row) => (
            <DescriptionListItem key={row.term}>
              <DescriptionListTerm>{row.term}</DescriptionListTerm>
              <DescriptionListDetails>{row.details}</DescriptionListDetails>
            </DescriptionListItem>
          ))}
        </DescriptionList>
        <div className="flex flex-wrap items-center justify-between gap-3">
          <p className="flex min-w-0 items-center gap-2 text-sm font-medium text-destructive">
            <WarningCircleIcon aria-hidden className="size-4 shrink-0" />
            {COPY.limit}
          </p>
          {/* Focusable while disabled, so a keyboard or screen reader still
              reaches it and hears why. */}
          <Button
            disabled
            focusableWhenDisabled
            aria-description={COPY.limit}
            className="h-auto min-h-9 max-w-full min-w-0 py-1.5 whitespace-normal data-disabled:pointer-events-none data-disabled:opacity-50"
          >
            <UserPlusIcon
              data-icon="inline-start"
              aria-hidden
              className="@max-[16rem]/seats:hidden"
            />
            {COPY.invite}
          </Button>
        </div>
      </CardContent>
      <div className="divide-y border-t">
        {QUESTIONS.map((item) => (
          <Collapsible key={item.id} defaultOpen={item.open}>
            <h3>
              <CollapsibleTrigger className="group/question flex w-full items-center justify-between gap-3 px-(--card-spacing) py-4 text-left text-sm font-medium hover:underline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring">
                {item.question}
                <CaretDownIcon
                  aria-hidden
                  className="size-4 shrink-0 text-muted-foreground transition-transform group-data-panel-open/question:rotate-180 motion-reduce:transition-none"
                />
              </CollapsibleTrigger>
            </h3>
            <CollapsibleContent className="h-(--collapsible-panel-height) overflow-hidden transition-[height] duration-200 data-ending-style:h-0 data-starting-style:h-0 motion-reduce:transition-none">
              <div className="grid justify-items-start gap-3 px-(--card-spacing) pb-4 text-sm/6 text-muted-foreground">
                <p>{item.answer}</p>
                {"action" in item && (
                  <a
                    href={item.action.href}
                    data-slot="button"
                    className={cn(
                      buttonVariants({
                        variant: "outline",
                        size: "sm",
                      }),
                      "h-auto min-h-8 max-w-full py-1.5 text-center whitespace-normal text-foreground"
                    )}
                  >
                    {item.action.label}
                  </a>
                )}
              </div>
            </CollapsibleContent>
          </Collapsible>
        ))}
      </div>
    </Card>
  )
}
```
