Blocks · Marketing
Plain tiles
A heading and two lines of text on a quiet surface, nothing else; three across, stacked on narrow widths.
74% of the 2,073 sites in The UI Index have a card (1,540 sites).
What you get
Fast deploys
Push to main and the site is live before you have switched tabs. Every branch gets its own preview link.
One-click rollback
Pick any earlier deploy and make it live again. Nothing is rebuilt, so nothing can fail on the way back.
Quiet by default
No dashboards to learn and no config to babysit. Read the log if you want to; most weeks you will not.
What you get
Fast deploys
Push to main and the site is live before you have switched tabs. Every branch gets its own preview link.
One-click rollback
Pick any earlier deploy and make it live again. Nothing is rebuilt, so nothing can fail on the way back.
Quiet by default
No dashboards to learn and no config to babysit. Read the log if you want to; most weeks you will not.
Both previews render the same markup; the HTML tab below is that markup without a framework.
Install
- React
- npx shadcn@
latest add https:// www.themes.dev/ ui/ r/ plain-tiles.json - Astro
- npx shadcn@
latest add https:// www.themes.dev/ ui/ r/ astro/ plain-tiles.json
Or copy the files below into your project. They need clsx, tailwind-merge and the components they import, and the themes.dev UI theme. The HTML needs only the compiled CSS.
/** Plain tiles: a heading and two lines of text on a quiet surface, nothing else (no picture, no link, no icon).
* Three across from 48rem of the block's own width, one under the other below it; tiles of a row share a height.
* The tiles are not interactive, so they have no hover or pressed treatment. */
export const plainTiles = {
root: '@container bg-background px-6 py-12 text-foreground @sm:py-16',
inner: 'mx-auto max-w-6xl',
heading: 'mb-8 text-2xl font-semibold tracking-[-0.02em] text-balance',
list: 'm-0 grid list-none gap-4 p-0 @3xl:grid-cols-3 @3xl:gap-6',
item: 'flex',
tile: 'flex w-full min-w-0 flex-col gap-3 rounded-xl border bg-card p-6 text-card-foreground',
title: 'text-lg leading-snug font-semibold tracking-[-0.01em] text-balance',
text: 'text-base text-pretty text-muted-foreground',
};
export type Tile = { title: string; text: string };import { cn } from '@/lib/utils';
import { plainTiles as c, type Tile } from './plain-tiles.classes';
export type PlainTilesProps = { label?: string; title?: string; tiles: Tile[]; id?: string; className?: string };
/** Plain tiles: a heading and two lines of text each, three across, stacked on narrow widths. */
export function PlainTiles({ label = 'Highlights', title, tiles, id = 'plain-tiles', className }: PlainTilesProps) {
return (
<section id={id} aria-label={title ? undefined : label} aria-labelledby={title ? `${id}-title` : undefined} className={cn(c.root, className)}>
<div className={c.inner}>
{title && <h2 className={c.heading} id={`${id}-title`}>{title}</h2>}
<ul className={c.list}>
{tiles.map((t) => (
<li key={t.title} className={c.item}>
<div className={c.tile}>
<h3 className={c.title}>{t.title}</h3>
<p className={c.text}>{t.text}</p>
</div>
</li>
))}
</ul>
</div>
</section>
);
}---
import { cn } from '@/lib/utils';
import { plainTiles as c, type Tile } from './plain-tiles.classes';
interface Props {
/** The accessible name of the group of tiles. */
label?: string;
/** A visible heading (h2) over the tiles; when set it names the group instead of `label`. */
title?: string;
tiles: Tile[];
id?: string;
class?: string;
}
const { label = 'Highlights', title, tiles, id = 'plain-tiles', class: className } = Astro.props;
---
<section id={id} aria-label={title ? undefined : label} aria-labelledby={title ? `${id}-title` : undefined} class={cn(c.root, className)}>
<div class={c.inner}>
{title && <h2 class={c.heading} id={`${id}-title`}>{title}</h2>}
<ul class={c.list}>
{tiles.map((t) => (
<li class={c.item}>
<div class={c.tile}>
<h3 class={c.title}>{t.title}</h3>
<p class={c.text}>{t.text}</p>
</div>
</li>
))}
</ul>
</div>
</section>/** Plain tiles: a heading and two lines of text on a quiet surface, nothing else (no picture, no link, no icon).
* Three across from 48rem of the block's own width, one under the other below it; tiles of a row share a height.
* The tiles are not interactive, so they have no hover or pressed treatment. */
export const plainTiles = {
root: '@container bg-background px-6 py-12 text-foreground @sm:py-16',
inner: 'mx-auto max-w-6xl',
heading: 'mb-8 text-2xl font-semibold tracking-[-0.02em] text-balance',
list: 'm-0 grid list-none gap-4 p-0 @3xl:grid-cols-3 @3xl:gap-6',
item: 'flex',
tile: 'flex w-full min-w-0 flex-col gap-3 rounded-xl border bg-card p-6 text-card-foreground',
title: 'text-lg leading-snug font-semibold tracking-[-0.01em] text-balance',
text: 'text-base text-pretty text-muted-foreground',
};
export type Tile = { title: string; text: string };<section id="ex-tiles" aria-labelledby="ex-tiles-title" class="@container bg-background px-6 py-12 text-foreground @sm:py-16">
<div class="mx-auto max-w-6xl">
<h2 class="mb-8 text-2xl font-semibold tracking-[-0.02em] text-balance" id="ex-tiles-title">What you get</h2>
<ul class="m-0 grid list-none gap-4 p-0 @3xl:grid-cols-3 @3xl:gap-6">
<li class="flex">
<div class="flex w-full min-w-0 flex-col gap-3 rounded-xl border bg-card p-6 text-card-foreground">
<h3 class="text-lg leading-snug font-semibold tracking-[-0.01em] text-balance">Fast deploys</h3>
<p class="text-base text-pretty text-muted-foreground">
Push to main and the site is live before you have switched tabs. Every branch gets its own preview link.
</p>
</div>
</li>
<li class="flex">
<div class="flex w-full min-w-0 flex-col gap-3 rounded-xl border bg-card p-6 text-card-foreground">
<h3 class="text-lg leading-snug font-semibold tracking-[-0.01em] text-balance">One-click rollback</h3>
<p class="text-base text-pretty text-muted-foreground">
Pick any earlier deploy and make it live again. Nothing is rebuilt, so nothing can fail on the way back.
</p>
</div>
</li>
<li class="flex">
<div class="flex w-full min-w-0 flex-col gap-3 rounded-xl border bg-card p-6 text-card-foreground">
<h3 class="text-lg leading-snug font-semibold tracking-[-0.01em] text-balance">Quiet by default</h3>
<p class="text-base text-pretty text-muted-foreground">
No dashboards to learn and no config to babysit. Read the log if you want to; most weeks you will not.
</p>
</div>
</li>
</ul>
</div>
</section>