Skip to content

Tiles

Edit on GitHub
@volvo-cars/css v2.3.0

Tiles are interactive navigation elements that link to content. Unlike Cards which are static content containers, tiles are clickable and use <a> or <button> elements. Use text-tile for icon-and-label layouts or media-tile for image-based navigation.

The text tile is a compact interactive element with an optional icon at the top and a body label anchored to the bottom. The icon is a direct child of the tile — not a slotted element.

When the icon is omitted, space is reserved at the top and the body label stays anchored to the bottom.

import { Icon } from '@volvo-cars/react-icons';
export function TextTile() {
return (
<div
className="grid-cols-2 gap-x-gutter gap-y-24"
style={{ height: '200px' }}
>
<a href="#top" className="text-tile">
<Icon icon="charging-battery" size={24} />
<span slot="body">Charging</span>
</a>
<a href="#top" className="text-tile">
<span slot="body">Without icon</span>
</a>
</div>
);
}

This component uses slots to give content built-in styling.

  • Icon – Direct child element (not slotted)
  • slot="body" – Label text
<a class="text-tile" href="#" style="height: 200px">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="none"
viewBox="0 0 24 24"
>
<path stroke="currentColor" stroke-width="1.5" d="M12 3v18M3 12h18" />
</svg>
<code slot="body">body</code>
</a>

Wrap tiles in <li> elements inside a <ul> for semantic grouping. Give the container a fixed height so tiles share consistent dimensions.

import { Icon } from '@volvo-cars/react-icons';
export function TextTileGrid() {
return (
<ul
className="grid-cols-3 gap-x-gutter gap-y-24"
style={{ height: '200px' }}
>
<li>
<a href="#top" className="text-tile">
<Icon icon="charging-battery" size={24} />
<span slot="body">Charging</span>
</a>
</li>
<li>
<a href="#top" className="text-tile">
<Icon icon="calendar" size={24} />
<span slot="body">Service booking</span>
</a>
</li>
<li>
<a href="#top" className="text-tile">
<Icon icon="plus-circled" size={24} />
<span slot="body">Accessories</span>
</a>
</li>
</ul>
);
}

The media tile pairs an image with a body label. The media slot defaults to a 4 / 3 aspect ratio. Images hover-zoom on interaction.

<a href="#" class="media-tile" style="max-width: 360px">
<div slot="media">
<img class="img" src="/images/cars/card/es90.avif" alt="" />
</div>
<span slot="body">Explore the Volvo ES90</span>
</a>

This component uses slots to give content built-in styling.

  • slot="media" – Image container
  • slot="body" – Label text
<a class="media-tile" href="#" style="max-width: 280px">
<div
slot="media"
class="bg-feedback-gray flex items-center justify-center h-full"
>
<code>media</code>
</div>
<code slot="body">body</code>
</a>

The media slot requires a wrapping <div> — images are absolutely positioned inside it for the hover zoom effect.

Add flex-row items-center gap-16 to arrange the media beside the label. Set a fixed width on the media slot to control its size — the aspect ratio determines the height.

<a
href="#"
class="media-tile flex-row items-center gap-16"
style="max-width: 360px"
>
<div slot="media" class="aspect-3/2" style="width: 125px">
<img class="img" src="/images/cars/card/es90.avif" alt="" />
</div>
<span slot="body">Explore the Volvo ES90</span>
</a>
<ul class="grid-cols-3 gap-x-gutter gap-y-24">
<li>
<a href="#" class="media-tile">
<div slot="media">
<img class="img" src="/images/cars/thumbnails/ex30-small.avif" alt="" />
</div>
<span slot="body">EX30</span>
</a>
</li>
<li>
<a href="#" class="media-tile">
<div slot="media">
<img class="img" src="/images/cars/thumbnails/ex60-small.avif" alt="" />
</div>
<span slot="body">EX60</span>
</a>
</li>
<li>
<a href="#" class="media-tile">
<div slot="media">
<img class="img" src="/images/cars/thumbnails/ex90-small.avif" alt="" />
</div>
<span slot="body">EX90</span>
</a>
</li>
</ul>
  • Always use <a> for navigation or <button> for actions — tiles must be interactive elements
  • Provide descriptive alt text for images, or use alt="" for decorative images when the body label provides sufficient context
  • Add aria-hidden="true" to decorative icons that repeat the body label’s meaning
  • In grid layouts, wrap tiles in <li> elements inside a <ul> for semantic list structure
  • The body label is styled with an underline to signal interactivity without relying on color alone
ClassDescription
text-tileInteractive tile with optional icon and bottom-anchored label. Secondary background.
media-tileInteractive tile with image (4:3 cover) and label. Image zooms on hover.