Skip to content
buttery

v1.0.0

Source on GitHub

Version 1.0.0

Faq

Questions and answers in a titled section: groups to switch between, a search that filters as you type, a link to each question and a way to ask another.

import { Faq } from "@butteryui/react";

Demo

Questions, answered

What people ask the roastery most.

Still have a question?

Write to hello@halfmoon.example and Rosa or Theo will answer within a day.

When to use it

Use it for

  • A store's or an app's questions and answers, as a page of their own or a section of a longer one
  • A help center's questions in groups people switch between, with a search across all of them
  • A few questions under a product, a plan or a form

Not for

  • Guides or articles that run longer than an answer: give them pages of their own
  • Parts of a record or a form that aren't questions, such as an account's settings: use Accordion
  • Searching a whole site or app: use CommandSearch

Examples

Coffee page

A coffee's own page ends with three short questions about it: no groups, no search.

Good to know

Help center

A help center whose page keeps the open questions, so its popular links open one in whichever topic it's in.

Popular

Help with Deckhand

Answers about making, presenting and sharing decks.

Still have a question?

Write to help@deckhand.example and we'll answer within a working day.

Layouts

One set of questions in each layout and each look of its rows.

Wholesale

For cafés and offices that pour our coffee.

Nothing found

A search that finds nothing shows what to try and a way back to every question.

Store questions

The shop's questions beside their intro: topics in tabs, a search across them, a link to one answer and a way to write in.

Questions, answered

What people ask us most. New here? Start with when your coffee arrives.

Still wondering about something?

Write to hello@halfmoon.example and Rosa or Theo will answer within a day.

Variants and states

layout="stacked"

One column at most 720px wide: the intro, the questions, then the contact.

Questions, answered

What people ask the roastery most.

Still have a question?

Write to hello@halfmoon.example and Rosa or Theo will answer within a day.

layout="split"

The intro and the contact beside the questions from 640px of room, stacked under that.

Questions, answered

What people ask the roastery most.

Still have a question?

Write to hello@halfmoon.example and Rosa or Theo will answer within a day.

layout="two-column"

The questions in two columns under the intro from 640px of room, one under that.

Questions, answered

What people ask the roastery most.

Still have a question?

Write to hello@halfmoon.example and Rosa or Theo will answer within a day.

The search looks through every group's questions and answers as you type, marking what matches; when nothing does, a way to clear it shows in their place.

Props

Faq

PropTypeDefaultDescription
titleReactNodeThe heading over the questions, such as "Questions, answered".
introReactNodeA line or two under the heading.
titleAs"h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p""h2"What the heading renders as. Defaults to "h2"; the questions are headings a level below it.
questions

required

readonly FaqQuestion[]The questions, in the order they show.
groupsreadonly FaqGroup[]The groups the questions are in, in tab order. With two or more, tabs switch between them; each question names its own.
valuereadonly string[]The open questions' ids. Pair with onValueChange to control them.
defaultValuereadonly string[]The questions open at first when uncontrolled. Defaults to none.
onValueChange((value: readonly string[]) => void)Called with the open questions' ids each time one opens or closes.
groupstringThe group shown. Pair with onGroupChange to control it.
defaultGroupstringThe group shown at first when uncontrolled. Defaults to the first.
onGroupChange((group: string) => void)Called with the group a person shows: by its tab, or by following a link to a question in it.
searchbooleanfalseShows a search field over the questions that filters every group's as you type. Defaults to false.
layout"stacked" | "split" | "two-column""stacked"How the parts are arranged: "stacked" (the default), one column at most 720px wide; "split", the intro and contact beside the questions; or "two-column", the questions in two columns. Split and two columns take 640px of room and stack under that.
variant"divided" | "contained" | "cards" | "plain""divided"How the questions are drawn, as Accordion's variant. Defaults to "divided".
size"sm" | "md" | "lg""md"How big the questions are, as Accordion's size. Defaults to "md".
contactReactNodeWhat closes the FAQ, such as a Faq.Contact with a way to ask a question it doesn't answer.
labelsPartial<FaqLabels>Its words, for another language or your own.

It also takes Omit<ComponentPropsWithRef<"div">, "title" | "defaultValue" | "children">.

Faq.Contact

PropTypeDefaultDescription
titleReactNode"Still have a question?"The heading. Defaults to "Still have a question?".
descriptionReactNodeA line under it, such as who answers and how soon.
actionsReactNodeThe ways to ask, such as a Button with asChild around a link to an email address.
titleAs"h1" | "h2" | "h3" | "h4" | "h5" | "h6" | "p""h3"What the heading renders as. Defaults to "h3".

It also takes Omit<ComponentPropsWithRef<"div">, "title" | "children">.

FaqQuestion

One question and its answer.

FieldTypeDefaultDescription
id

required

stringIdentifies the question: the anchor that opens it and scrolls to it from a #id link, and what value holds while it's open. Unique on the page, as any element id is.
question

required

stringThe question, as its trigger's words.
answer

required

ReactNodeThe answer: words, or anything else, such as a paragraph with a link in it.
groupstringThe value of the group it's in, when the FAQ has groups.
keywordsstringMore words the search matches, such as the words people use for it: "refund" for a returns question.

FaqGroup

A group of questions, such as a topic, that a tab shows.

FieldTypeDefaultDescription
value

required

stringIdentifies the group: what each of its questions names as its group.
label

required

stringThe tab's words, such as "Orders and shipping".

FaqLabels

The words an FAQ shows, for another language or your own.

FieldTypeDefaultDescription
search

required

stringNames the search field and shows as its placeholder, such as "Search questions".
groups

required

stringNames the tabs for screen readers, such as "Topics".
results

required

(count: number) => stringHow many questions match a search, read out as the results change, such as "3 questions match".
noResults

required

(search: string) => stringThe title of what shows when nothing matches, such as No questions match "decaf".
noResultsHint

required

stringThe line under it.
showAll

required

stringThe button under it that clears the search, such as "Show all questions".

Accessibility

Its title is a heading (an h2 unless titleAs says otherwise) and each question is a button in a heading a level below it, named by the question (and in search results by its group after it), as in Accordion. The search field is a searchbox named by its label; as you type, a polite status says how many questions match, and the matching words are marked. The groups are tabs in a tablist named by labels.groups. A link to a question's #id opens it and moves focus to it, as following an anchor does, without a ring; the no-results message's "Show all questions" clears the search and puts focus back in the field.

KeysWhat they do
TabMoves through the search field, the chosen tab, each question and the contact, in that order
EscapeClears the search from the search field, keeping focus there
Arrow Right or Arrow LeftMoves between the tabs and shows that group's questions
Arrow Down or Arrow UpMoves to the next or previous question, wrapping at the ends of a column
Home or EndMoves to the first or last question
Enter or SpaceOpens or closes the question whose trigger has focus

Motion

It changes shape as one surface, on `spring.smooth`, or `spring.morph` for a big change, rather than closing and reopening.

With reduced motion: A question opens and closes at once and its chevron turns over at once; its answer fades in and out in 100ms.

Layout

It's its own size container and fills its container's width. Stacked, it's one column at most 720px wide: the intro, the questions 48px under it (64 on a page from 1024px wide), then the contact 32px under them. From 640px of room, split puts the intro and the contact in a column a third wide (240px at least) beside the questions, 48px apart, and two-column lays the questions out in two columns; under 640 both stack as stacked does. The search field fills the questions' column (at most 600px wide in two columns), the tabs fold the groups that don't fit into a More menu, and questions wrap instead of truncating. Faq.Contact puts its actions beside its words from 480px wide and under them when it's narrower.

See it in the Halfmoon shop demo.