# FAQ Split

> Headline and a contact link pinned left, numbered ruled questions on the right. FAQPage data included.

- **Slug:** `faq-split`
- **Kind:** section · **Category:** Conversion
- **Source:** original to Viewership UI
- **Docs page:** https://viewership.ai/ui/components/faq-split
- **All files in one bundle:** https://viewership.ai/ui/components/faq-split.bundle.txt
- **Structured JSON:** https://viewership.ai/ui/components/faq-split.json

## Install

1. Import the foundation tokens once (download: https://viewership.ai/ui/raw/foundation.css.txt):

```astro
---
// src/layouts/Layout.astro
import "../styles/foundation.css";
---
```

2. Copy this file into `src/components/viewership/faq-split/`:

   - `FaqSplit.astro` (raw: https://viewership.ai/ui/raw/faq-split/FaqSplit.astro.txt)

npm packages: none.

## Usage

```astro
---
import FaqSplit from "../components/viewership/faq-split/FaqSplit.astro";
---

<FaqSplit />
```

## Example

The gallery demo, verbatim. Use the import path from Usage instead of `./`.

```astro
---
import FaqSplit from "./FaqSplit.astro";
---

<FaqSplit
  title="Before you"
  accent="order."
  intro="The short version of how we work."
  contact={{ label: "Ask the bench a question", href: "#" }}
  schema={false}
  items={[
    { q: "What does the repair promise cover?", a: "Stitching, zips, seam tape and fabric patches on anything we made, for as long as you own it. Damage from a fire or a bear is on you." },
    { q: "How long does a repair take?", a: "Two working days on average once the pack reaches the bench, plus shipping both ways. Rush slots open each Friday." },
    { q: "Where are your fabrics made?", a: "Shells are woven in Portugal and finished in Wisconsin. Every product page lists the mill and the batch." },
    { q: "Do you ship outside the United States?", a: "Yes, to Canada and the UK today. Duties are shown at checkout so there are no surprises." },
    { q: "Can I return something I have used?", a: "Within 60 days, yes. Tell us what did not work and we will take it back, no form to fill in." },
  ]}
/>
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `title` | `string` |  | Section headline. |
| `accent` | `string` |  | Closing words of the headline. |
| `intro` | `string` |  | Supporting paragraph. |
| `contact` | `{ label: string; href: string }` |  | Link for questions not on the list. |
| `items` | `{ q: string; a: string }[]` |  | Questions and plain-text answers. |
| `openFirst` | `boolean` | `false` | Open the first question on load. |
| `schema` | `boolean` | `true` | Emit FAQPage JSON-LD. |
| `as` | `"h2" \| "h3"` | `"h2"` | Heading element. |

## Source

### `src/components/viewership/faq-split/FaqSplit.astro`

```astro
---
/**
 * FaqSplit: headline and a contact prompt pinned on the left, ruled
 * questions on the right. Native <details>, FAQPage JSON-LD included.
 *
 * @example
 * <FaqSplit title="Before you" accent="order." contact={{ label: "Write to us", href: "/contact" }} items={[...]} />
 */
interface Item {
  q: string;
  a: string;
}
interface Link {
  label: string;
  href: string;
}
interface Props {
  title: string;
  accent?: string;
  intro?: string;
  /** A link under the intro for questions not on the list. */
  contact?: Link;
  items: Item[];
  openFirst?: boolean;
  schema?: boolean;
  as?: "h2" | "h3";
  class?: string;
}

const { title, accent, intro, contact, items, openFirst = false, schema = true, as: Heading = "h2", class: className } = Astro.props;
const jsonLd = JSON.stringify({
  "@context": "https://schema.org",
  "@type": "FAQPage",
  mainEntity: items.map((i) => ({ "@type": "Question", name: i.q, acceptedAnswer: { "@type": "Answer", text: i.a } })),
}).replace(/</g, "\\u003c");
---

<section class:list={["vs-faq-split", className]}>
  <div class="vs-faq-split__inner">
    <header class="vs-faq-split__head">
      <Heading class="vs-display vs-faq-split__title">{title}{accent && <> <em>{accent}</em></>}</Heading>
      {intro && <p class="vs-faq-split__intro">{intro}</p>}
      {
        contact && (
          <a class="vs-faq-split__contact" href={contact.href}>
            {contact.label}
          </a>
        )
      }
    </header>
    <div class="vs-faq-split__list">
      {
        items.map((item, i) => (
          <details class="vs-faq-split__row" open={openFirst && i === 0}>
            <summary>
              <span class="vs-faq-split__num" aria-hidden="true">{String(i + 1).padStart(2, "0")}</span>
              <span class="vs-faq-split__q">{item.q}</span>
              <span class="vs-faq-split__mark" aria-hidden="true" />
            </summary>
            <p class="vs-faq-split__a">{item.a}</p>
          </details>
        ))
      }
    </div>
  </div>
  {schema && <script is:inline type="application/ld+json" set:html={jsonLd} />}
</section>

<style>
  .vs-faq-split {
    container-type: inline-size;
    background: var(--vs-page);
    color: var(--vs-ink);
    font-family: var(--vs-font-sans);
    font-stretch: var(--vs-font-stretch);
  }
  .vs-faq-split__inner {
    display: grid;
    gap: clamp(28px, 5cqi, 56px);
    max-width: var(--vs-container);
    margin-inline: auto;
    padding: var(--vs-section-y) var(--vs-gutter);
  }
  .vs-faq-split__title {
    font-size: clamp(2rem, 6.5cqi, 4.25rem);
  }
  .vs-faq-split__intro {
    max-width: 24rem;
    margin: 1.25rem 0 0;
    font-size: 1rem;
    line-height: 1.55;
    color: var(--vs-ink-2);
  }
  .vs-faq-split__contact {
    display: inline-block;
    margin-top: 1.25rem;
    padding-bottom: 2px;
    border-bottom: calc(var(--vs-border-w) + 1px) solid var(--vs-accent);
    font-size: 0.9375rem;
    font-weight: 600;
    color: var(--vs-ink);
    text-decoration: none;
  }
  .vs-faq-split__contact:focus-visible {
    outline: none;
    box-shadow: var(--vs-focus-ring);
  }
  .vs-faq-split__list {
    border-top: calc(var(--vs-border-w) + 1px) solid var(--vs-ink);
  }
  .vs-faq-split__row {
    border-bottom: var(--vs-border-w) solid var(--vs-line-strong);
  }
  .vs-faq-split__row summary {
    display: grid;
    grid-template-columns: 2.25rem minmax(0, 1fr) auto;
    align-items: center;
    gap: 12px;
    padding: 20px 4px;
    cursor: pointer;
    list-style: none;
  }
  .vs-faq-split__row summary::-webkit-details-marker {
    display: none;
  }
  .vs-faq-split__row summary:focus-visible {
    outline: none;
    box-shadow: var(--vs-focus-ring);
  }
  .vs-faq-split__num {
    font-size: 0.8125rem;
    font-variant-numeric: tabular-nums;
    color: var(--vs-ink-3);
  }
  .vs-faq-split__q {
    font-size: 1.0625rem;
    font-weight: 600;
    line-height: 1.3;
  }
  .vs-faq-split__a {
    margin: 0;
    padding: 0 4px 22px calc(2.25rem + 16px);
    max-width: 40rem;
    font-size: 0.9375rem;
    line-height: 1.6;
    color: var(--vs-ink-2);
  }
  .vs-faq-split__mark {
    position: relative;
    width: 14px;
    height: 14px;
  }
  .vs-faq-split__mark::before,
  .vs-faq-split__mark::after {
    content: "";
    position: absolute;
    left: 0;
    top: 6px;
    width: 14px;
    height: 2px;
    background: var(--vs-ink);
    transition: transform var(--vs-duration) var(--vs-ease-out);
  }
  .vs-faq-split__mark::after {
    transform: rotate(90deg);
  }
  .vs-faq-split__row[open] .vs-faq-split__mark::after {
    transform: rotate(0deg);
  }
  .vs-faq-split__row[open] .vs-faq-split__num {
    color: var(--vs-accent-ink);
  }
  @container (min-width: 820px) {
    .vs-faq-split__inner {
      grid-template-columns: minmax(0, 4fr) minmax(0, 7fr);
      gap: clamp(32px, 6cqi, 88px);
      align-items: start;
    }
    .vs-faq-split__head {
      position: sticky;
      top: 24px;
    }
  }
</style>
```
