# Logo Grid

> Logos in bordered cells beside a short statement, like a spec sheet.

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

   - `LogoGrid.astro` (raw: https://viewership.ai/ui/raw/logo-grid/LogoGrid.astro.txt)

npm packages: none.

## Usage

```astro
---
import LogoGrid from "../components/viewership/logo-grid/LogoGrid.astro";
---

<LogoGrid />
```

## Example

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

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

<LogoGrid
  title="Trusted by outfitters and schools"
  body="Over two hundred shops, guide services and outdoor programmes run on Northwind gear."
  items={[
    { name: "Harbor & Pine" },
    { name: "Tallgrass" },
    { name: "Lumen Co." },
    { name: "Fieldnote" },
    { name: "Saltmarsh" },
    { name: "Bearing" },
    { name: "Kestrel" },
    { name: "Orchard Row" },
  ]}
/>
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `title` | `string` |  | Short headline beside the grid. |
| `body` | `string` |  | Supporting line. |
| `items` | `{ name: string; src?: string; href?: string }[]` |  | Logos. Multiples of four fill the grid. |
| `as` | `"h2" \| "h3"` | `"h2"` | Heading element. |

## Source

### `src/components/viewership/logo-grid/LogoGrid.astro`

```astro
---
/**
 * LogoGrid: logos in bordered cells beside a short statement, like a spec
 * sheet. Two columns when narrow, four when wide.
 *
 * @example
 * <LogoGrid title="Trusted by outfitters" body="..." items={[{ name: "Kestrel" }]} />
 */
interface Item {
  name: string;
  src?: string;
  href?: string;
}
interface Props {
  title?: string;
  body?: string;
  items: Item[];
  as?: "h2" | "h3";
  class?: string;
}

const { title, body, items, as: Heading = "h2", class: className } = Astro.props;
---

<section class:list={["vs-logo-grid", className]}>
  <div class="vs-logo-grid__inner">
    {
      (title || body) && (
        <header class="vs-logo-grid__head">
          {title && <Heading class="vs-display vs-logo-grid__title">{title}</Heading>}
          {body && <p class="vs-logo-grid__body">{body}</p>}
        </header>
      )
    }
    <ul class="vs-logo-grid__cells">
      {
        items.map((it) => {
          const mark = it.src ? <img src={it.src} alt={it.name} height="28" loading="lazy" /> : <span class="vs-display">{it.name}</span>;
          return <li>{it.href ? <a href={it.href}>{mark}</a> : mark}</li>;
        })
      }
    </ul>
  </div>
</section>

<style>
  .vs-logo-grid {
    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-logo-grid__inner {
    display: grid;
    gap: 32px;
    max-width: var(--vs-container);
    margin-inline: auto;
    padding: var(--vs-section-y) var(--vs-gutter);
  }
  .vs-logo-grid__title {
    font-size: clamp(1.75rem, 4.5cqi, 2.75rem);
  }
  .vs-logo-grid__body {
    max-width: 22rem;
    margin: 1rem 0 0;
    font-size: 0.9375rem;
    line-height: 1.55;
    color: var(--vs-ink-2);
  }
  .vs-logo-grid__cells {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    margin: 0;
    padding: 0;
    list-style: none;
    border-top: var(--vs-border-w) solid var(--vs-line-strong);
    border-left: var(--vs-border-w) solid var(--vs-line-strong);
  }
  .vs-logo-grid__cells li {
    display: flex;
    min-height: 96px;
    align-items: center;
    justify-content: center;
    padding: 20px;
    border-right: var(--vs-border-w) solid var(--vs-line-strong);
    border-bottom: var(--vs-border-w) solid var(--vs-line-strong);
    color: var(--vs-ink-2);
    transition: background-color var(--vs-duration) var(--vs-ease-out);
  }
  .vs-logo-grid__cells li:hover {
    background: var(--vs-surface);
    color: var(--vs-ink);
  }
  .vs-logo-grid__cells span {
    font-size: 1.25rem;
    text-align: center;
  }
  .vs-logo-grid__cells img {
    display: block;
    width: auto;
    height: 28px;
    filter: grayscale(1);
    opacity: 0.75;
  }
  .vs-logo-grid__cells a {
    display: inline-flex;
    color: inherit;
    text-decoration: none;
  }
  .vs-logo-grid__cells a:focus-visible {
    outline: none;
    box-shadow: var(--vs-focus-ring);
  }
  @container (min-width: 720px) {
    .vs-logo-grid__cells {
      grid-template-columns: repeat(4, minmax(0, 1fr));
    }
  }
  @container (min-width: 960px) {
    .vs-logo-grid__inner {
      grid-template-columns: minmax(0, 1fr) minmax(0, 2.4fr);
      gap: 56px;
      align-items: start;
    }
  }
</style>
```
