# Hero Index

> Oversized left-aligned headline over a ruled row of facts. Type-led, no media.

- **Slug:** `hero-index`
- **Kind:** section · **Category:** Hero
- **Source:** original to Viewership UI
- **Docs page:** https://viewership.ai/ui/components/hero-index
- **All files in one bundle:** https://viewership.ai/ui/components/hero-index.bundle.txt
- **Structured JSON:** https://viewership.ai/ui/components/hero-index.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/hero-index/`:

   - `HeroIndex.astro` (raw: https://viewership.ai/ui/raw/hero-index/HeroIndex.astro.txt)

3. Copy its registry dependencies, each into its own sibling folder under `src/components/viewership/`:

   - `button`: `Button.astro` (docs: https://viewership.ai/ui/components/button.md)

npm packages: none.

## Usage

```astro
---
import HeroIndex from "../components/viewership/hero-index/HeroIndex.astro";
---

<HeroIndex />
```

## Example

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

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

<HeroIndex
  title="Twelve years, one"
  accent="repair bench."
  body="Tents, packs and layers cut in small batches, repaired for life and shipped from one warehouse in Duluth."
  primary={{ label: "Shop the range", href: "#" }}
  secondary={{ label: "Book a repair", href: "#" }}
  facts={[
    { value: "12 yr", label: "Repair promise on every pack" },
    { value: "40", label: "Small batches a year" },
    { value: "2 days", label: "Average repair turnaround" },
    { value: "0", label: "Single-use shells" },
  ]}
/>
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `title` | `string` |  | Headline. |
| `accent` | `string` |  | Closing words of the headline, drawn in the theme's accent style. |
| `body` | `string` |  | Supporting paragraph, set in the right half on wide screens. |
| `primary` | `{ label: string; href: string }` |  | Main call to action. |
| `secondary` | `{ label: string; href: string }` |  | Quieter second action. |
| `facts` | `{ value: string; label: string }[]` | `[]` | Figures under the headline. |
| `as` | `"h1" \| "h2"` | `"h1"` | Heading element. |

## Source

### `src/components/viewership/hero-index/HeroIndex.astro`

```astro
---
/**
 * HeroIndex: an oversized left-aligned headline over a ruled row of facts.
 * No media needed; the type carries it.
 *
 * @example
 * <HeroIndex title="Twelve years, one" accent="repair bench."
 *   facts={[{ value: "12 yr", label: "Repair promise" }]} />
 */
import Button from "../button/Button.astro";

interface Cta {
  label: string;
  href: string;
}
interface Fact {
  value: string;
  label: string;
}
interface Props {
  title: string;
  accent?: string;
  body?: string;
  primary?: Cta;
  secondary?: Cta;
  facts?: Fact[];
  as?: "h1" | "h2";
  class?: string;
}

const { title, accent, body, primary, secondary, facts = [], as: Heading = "h1", class: className } = Astro.props;
---

<section class:list={["vs-hero-index", className]}>
  <div class="vs-hero-index__inner">
    <div class="vs-hero-index__copy">
      <Heading class="vs-display vs-hero-index__title">{title}{accent && <> <em>{accent}</em></>}</Heading>
      {body && <p class="vs-hero-index__body">{body}</p>}
      {
        (primary || secondary) && (
          <div class="vs-hero-index__actions">
            {primary && <Button href={primary.href} variant="primary">{primary.label}</Button>}
            {secondary && <Button href={secondary.href} variant="secondary">{secondary.label}</Button>}
          </div>
        )
      }
    </div>
    {
      facts.length > 0 && (
        <dl class="vs-hero-index__facts">
          {facts.map((f) => (
            <div class="vs-hero-index__fact">
              <dt>{f.label}</dt>
              <dd class="vs-display">{f.value}</dd>
            </div>
          ))}
        </dl>
      )
    }
  </div>
</section>

<style>
  .vs-hero-index {
    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-hero-index__inner {
    max-width: var(--vs-container);
    margin-inline: auto;
    padding: var(--vs-section-y) var(--vs-gutter);
  }
  .vs-hero-index__copy {
    display: grid;
  }
  .vs-hero-index__title {
    font-size: clamp(2.75rem, 13cqi, 9rem);
    line-height: 0.95;
  }
  .vs-hero-index__body {
    max-width: 30rem;
    margin: 2.5rem 0 0;
    font-size: 1.0625rem;
    line-height: 1.55;
    color: var(--vs-ink-2);
  }
  .vs-hero-index__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-top: 2rem;
  }
  .vs-hero-index__facts {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 24px;
    margin: clamp(40px, 7cqi, 80px) 0 0;
  }
  .vs-hero-index__fact {
    display: flex;
    flex-direction: column-reverse;
    gap: 6px;
    padding-top: 14px;
    border-top: calc(var(--vs-border-w) + 1px) solid var(--vs-ink);
  }
  .vs-hero-index__fact dd {
    margin: 0;
    font-size: clamp(2rem, 5cqi, 3.5rem);
  }
  .vs-hero-index__fact dt {
    font-size: 0.875rem;
    color: var(--vs-ink-2);
  }
  @container (min-width: 760px) {
    .vs-hero-index__copy {
      grid-template-columns: repeat(12, minmax(0, 1fr));
      column-gap: 24px;
    }
    .vs-hero-index__title {
      grid-column: 1 / -1;
    }
    .vs-hero-index__body,
    .vs-hero-index__actions {
      grid-column: 7 / -1;
    }
  }
</style>
```
