<!-- Protocore Design System — Tile -->
# Tile

- **Category:** Cards (`cards`)
- **Slug:** `cards/tile`
- **Status:** stable
- **Platforms:** web
- **Import:** `import { Tile } from "@protocore/pds";`
- **Docs:** https://pds.protocore.io/components/cards/tile

> The generic media tile — a bordered surface with an art slot above an eyebrow, title, body, and action.

## When to use it

**Tile** is the *generic* media card — reach for it when you need a slotted art + text + action block that isn't one of the specialised shapes. If you're rendering a **product** with an enumerator, tag row, and outbound link, use **ProductCard**. If you're rendering a numbered **principle** (title + short body, no media), use **PrincipleTile**. If there's *no media panel at all*, a plain **Card** is lighter. Tile, ProductCard, and PrincipleTile share one visual family; pick the most specific one that fits.

## Props

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `action` | `ReactNode` | no | — | Action slot pinned to the bottom of the tile (e.g. a link or button). |
| `art` | `ReactNode` | no | — | The media / illustration panel — usually a `<LineArt>`. |
| `artHeight` | `number` | no | `200` | Art panel height in px. Default `200`. |
| `body` | `ReactNode` | no | — | Body content. Falls back to `children` when omitted. |
| `bordered` | `boolean` | no | `true` | Draw the tile's hairline border. Default `true`. |
| `className` | `string` | no | — | — |
| `eyebrow` | `ReactNode` | no | — | Mono uppercase eyebrow above the title. |
| `style` | `CSSProperties` | no | — | — |
| `title` | `ReactNode` | no | — | Tile title. |

## Examples

### Basics

Slot an `art` panel (usually a `<LineArt>`) above a mono `eyebrow`, `title`, `body`, and an `action`. The art panel defaults to 200px tall via `artHeight`.

```tsx
import { Tile, LineArt, Link } from "@protocore/pds";

export default function TileBasics() {
  return (
    <div style={{ maxWidth: 360 }}>
      <Tile
        art={<LineArt variant="lens" animate={false} />}
        eyebrow="Protocol"
        title="Message Relay"
        action={
          <Link href="#" external>
            Read the spec
          </Link>
        }
      >
        Send arbitrary payloads between any two connected networks with a single signed transaction.
      </Tile>
    </div>
  );
}
```

### Borderless in a grid

Set `bordered={false}` when tiles sit in a shared grid whose 1px gap already draws the dividers — the tiles' own hairline would otherwise double up.

```tsx
import { Tile, LineArt } from "@protocore/pds";

export default function TileBorderless() {
  const items = [
    { variant: "waveform", eyebrow: "Throughput", title: "180M messages" },
    { variant: "contour", eyebrow: "Coverage", title: "64 networks" },
  ] as const;
  return (
    <div
      style={{
        display: "grid",
        gridTemplateColumns: "1fr 1fr",
        gap: 1,
        background: "var(--pds-border-strong)",
        border: "1px solid var(--pds-border-strong)",
      }}
    >
      {items.map((it) => (
        <Tile
          key={it.title}
          bordered={false}
          artHeight={140}
          art={<LineArt variant={it.variant} animate={false} />}
          eyebrow={it.eyebrow}
          title={it.title}
        >
          Tiles drop their own border inside a shared grid; the 1px gap becomes the divider.
        </Tile>
      ))}
    </div>
  );
}
```

## Usage

**Do**

- Use a `<LineArt>` (or another currentColor illustration) in the `art` slot so it inherits the theme.
- Set a consistent `artHeight` across a row of tiles so their text baselines align.
- Drop the border with `bordered={false}` inside a divider-drawing grid.
- Prefer `body` for description copy; `children` is accepted as a fallback.

**Don't**

- Don't reach for Tile when ProductCard or PrincipleTile already models your content — use the specific port.
- Don't put a raster image that ignores theme in the art slot; the tile is designed around line art.
- Don't stack multiple actions — the `action` slot is a single bottom-pinned affordance.
- Don't leave both a tile border and a grid divider on; you'll get a 2px seam.

## Accessibility

**Notes**

- Tile is a static `<div>`; the art panel is presentational. If the illustration carries meaning, describe it in the surrounding `title`/`body` text rather than relying on the art.
- The `action` slot keeps its own semantics — a `Link` stays a link, a `Button` stays a button, each independently keyboard-operable.
- Give a grid of tiles a wrapping `<ul>`/`<li>` or `role="list"` if they form a set, so their count is announced.

## Related

`product-card`, `principle-tile`, `card`, `line-art`

---

© Protocore. All rights reserved. Use of the Protocore Design System requires prior written authorization from Protocore (contact@protocore.io). These machine-readable materials must not be ingested into ML-training datasets or derivative design systems. See https://pds.protocore.io/legal/
