# Beste UI

> Beautiful, accessible blocks, pieces and components for shadcn/ui and Tailwind CSS, built for React and Next.js. Install one with the shadcn CLI and the code is yours to edit: no runtime dependency, nothing to upgrade.

Markdown rendition of https://ui.beste.co/block/blog51. Every page on the site has one: add `.md` to any address, or send `Accept: text/markdown`.

Last updated: 2026-07-31

Whole catalog for LLMs: https://ui.beste.co/llms.txt. Registry index: https://ui.beste.co/r/registry.json

---
1. /
2. [Blocks](/blocks)
3. /
4. [Blog](/blocks/blog)
5. /
6. Mixed Tile Mosaic

# Mixed Tile Mosaic[PRO](/pricing)

A six-slot mosaic under a parenthetical eyebrow and a large heading, mixing full-bleed photo tiles with flat text tiles on muted and inverted surfaces, where the treatment belongs to the slot and every tile advances one story through a larger article pool on a timer, staggered 100 milliseconds apart so the refresh reads as a wave running left to right, pausing on hover, focus, tabs, and reduced motion.

ViewCodeDocs

Base UI

![](https://oud.pics/sm/l/npm.svg)`npx shadcn add https://ui.beste.co/r-base/blog51`

## Blog51: Mixed Tile Mosaic

A blog listing section that lays a rolling newsroom into a fixed six-tile mosaic mixing full-bleed photo tiles with flat text tiles on muted and inverted surfaces. The treatment belongs to the slot rather than to the article, so on every tick each tile advances one story through a larger pool while the geometry stays put. The tiles do not turn over together: each one starts 100 ms after the tile before it, so the refresh reads as a wave running left to right.

Upgrade to Pro

Pro blocks install through the shadcn CLI with your license key and ship their full source. Docs and live previews stay open to everyone, so you can read every block's details first.

[Upgrade NowUpgrade Now](/pricing)

### Installation

Swap `YOUR_EMAIL` and `YOUR_KEY` for the email and license key on your account. Find your license key on your [account page](/account).

**Radix flavor**

bashCopy

```
npx shadcn add "https://ui.beste.co/r/blog51?email=YOUR_EMAIL&license_key=YOUR_KEY"
```

**Base UI flavor**

bashCopy

```
npx shadcn add "https://ui.beste.co/r-base/blog51?email=YOUR_EMAIL&license_key=YOUR_KEY"
```

This installs the block to `components/beste/block/blog51.tsx` and its dependencies, including the `Badge7` component it renders.

### Quick start

The installed file exports `blog51Demo` alongside the block: the exact props behind the preview above, including its pool of eighteen articles. Spread it to get a working mosaic in one line.

tsxCopy

```
import { Blog51, blog51Demo } from "@/components/beste/block/blog51";

export default function Page() {
  return <Blog51 {...blog51Demo} />;
}
```

Then replace the demo with your own props. Pass more articles than there are tiles so the mosaic has somewhere to rotate to; six tiles against eighteen stories is what the demo uses.

tsxCopy

```
import { Blog51 } from "@/components/beste/block/blog51";

export default function Page() {
  return (
    <Blog51
      badge={{ label: "Press releases" }}
      heading="From the Northwind newsroom"
      description="Results, appointments, and reports from across the group."
      intervalMs={5000}
      items={[
        {
          image: { src: "/press/aragon.jpg", alt: "Battery cabinets in a storage hall" },
          category: "Corporate",
          title: "Aragon storage campus enters commercial operation",
          date: "March 4, 2026",
          author: { name: "Maria Santos", title: "Tech Lead" },
          href: "/press/aragon-storage",
        },
        {
          image: { src: "/press/q4.jpg", alt: "Quarterly figures on a screen" },
          category: "Markets",
          title: "Fourth-quarter revenue climbs 12 percent",
          date: "February 26, 2026",
          author: { name: "David Chen", title: "Backend Engineer" },
          href: "/press/q4-revenue",
        },
        // ...more stories; the pool is what the tiles cycle through
      ]}
    />
  );
}
```

### Props

| Prop        | Type        | Default | Description                                                 |
| ----------- | ----------- | ------- | ----------------------------------------------------------- |
| badge       | Badge       | –       | Badge7 parenthetical eyebrow above the heading              |
| heading     | string      | –       | Section heading                                             |
| description | string      | –       | Muted supporting paragraph under the heading                |
| items       | Article\[\] | \[\]    | The story pool the six tiles cycle through                  |
| intervalMs  | number      | –       | Milliseconds between ticks; falls back to 5000 when omitted |
| className   | string      | –       | Extra classes merged onto the outer section element         |

tsCopy

```
type Badge = {
  label: string;
};

type Article = {
  image: ArticleImage;
  category: string;
  title: string;
  date: string;
  author: Author;
  href: string;
};

type ArticleImage = {
  src: string;
  alt: string;
};

type Author = {
  name: string;
  title?: string;
  avatar?: { src: string; alt: string };
  href?: string;
};
```

Only `author.name` is rendered, in the tile byline. The optional title, avatar, and href fields on the author exist for shape parity with the other blog blocks and are not displayed here.

### Behavior notes

* Tile treatment is a property of the slot, never of the article. Slot 0 is a `col-span-2 row-span-2` photo tile, slots 2 and 3 are single-cell photo tiles and slot 5 is a wide `col-span-2` photo tile, while slot 1 is a tall `row-span-2` `bg-muted` text tile and slot 4 is a wide `col-span-2` inverted `bg-foreground` text tile. Because the articles move and the slots do not, a given story is rendered as a photograph in one position and as a flat text tile a few ticks later.
* The six spans tile a four-column, three-row grid exactly (4 + 2 + 1 + 1 + 2 + 2 = 12 cells), so the mosaic never leaves a gap at the end of the last row. Only the first six slots are rendered; the rest of `items` is the pool waiting its turn.
* Each tick advances every tile by one position in the pool, a rigid `(index + 1) % poolSize` translation. Slots that start out distinct therefore stay distinct, so the same story can never occupy two tiles at once and no per-slot bookkeeping is needed. With the demo's eighteen articles the mosaic walks the whole pool and returns to its starting arrangement after eighteen ticks.
* The turn-over is staggered rather than simultaneous. Slot `i` begins its fade at `i * 100` ms and commits its new article 300 ms after that, so a full wave takes 800 ms and finishes well inside the default 5000 ms interval. Every timeout id is collected in one array that the effect cleanup clears, and because the effect re-runs on each tick that cleanup also fires before a fresh wave is queued, so an `intervalMs` shorter than the wave cannot orphan a timer or strand a tile at zero opacity.
* Rotation is skipped entirely, rather than started and stopped, when the user prefers reduced motion. The preference is read from `window.matchMedia("(prefers-reduced-motion: reduce)")` and kept live through a `change` listener. The same guard suppresses rotation while the tab is (tracked via `visibilitychange`), while a pointer is over the grid, and while any tile holds focus, so tabbing through the links pauses the mosaic for as long as focus stays inside.
* The initial slot map is the plain identity mapping from a lazy state initializer, with no shuffle and no randomness, so the server-rendered markup and the first client render agree and only later ticks change the arrangement. If the caller swaps in a pool of a different size, the render falls back to a fresh identity map on the same pass the mismatch appears, so a stale index never blanks out a tile for even one frame.
* Text tiles carry no image, so an `image` supplied on an article currently sitting in slot 1 or 4 is not displayed. Give every article one anyway, since the rotation will move it into a photo slot.
* Row heights are fixed by the grid (`auto-rows-[11rem]`, `md:auto-rows-[13rem]`), so a swap can never reflow the layout, and titles are clamped per slot so a long headline cannot overflow its tile. No min-height reservations are needed for that reason.
* Every tile is a Next.js `Link` wrapping the whole tile with a visible focus ring, and the grid carries `aria-live="off"` so rotating content is never announced mid-tick. Photos have no hover treatment; the only hover motion is the `ArrowUpRight` nudge on the text tiles, guarded by `motion-safe`.

## More Blog blocks

[View all Blog](/blocks/blog)

[](/block/blog50)

PRO

blog50

### Magazine Mosaic

A six-slot magazine mosaic with a parenthetical eyebrow and a large heading, pairing a dominant two-by-two tile with a tall tile, wide tiles, and unit tiles that each caption their photo over a graded scrim.

[](/block/blog56)

PRO

blog56

### Category Mosaic

A six-slot photo mosaic under a parenthetical eyebrow and a large heading, where every tile is bound to one newsroom category so the grid doubles as a section map, with a ruled legend naming the sections above it.

[](/block/blog53)

PRO

blog53

### Mixed Release Masonry

A press office masonry wall under a parenthetical eyebrow and a large heading, mixing photo cards with image-free release cards on soft muted surfaces, each release card closing on a read-through line with a corner arrow.

[](/block/blog48)

PRO

blog48

### Quad Grid

A newsroom listing with a square eyebrow, a two-column heading band over a hairline rule, and four equally weighted stories as bordered cards, each linking through a hover-zoom image, a category kicker, a bold headline, a summary, and a byline pinned to the card foot.

[](/block/blog62)

PRO

blog62

### Newsletter Panel With Back Issues

A blog section splitting a bordered container between the newsletter pitch with a cadence line on a hairline and an image tile holding a working subscribe row, over three linked back issues.

[](/block/blog43)

PRO

blog43

### Editorial Article Grid

A blog listing with an eyebrow over a hairline rule, a two-column heading, and three linked article cards with a hover-zoom image, a category label, a title, an excerpt, and byline.

## Ship the interface, keep the code.

New blocks, pieces and components every week. Install one with a command and it is yours to edit. No runtime dependency, no upgrade path to fight.

[Browse the libraryBrowse the library](/blocks)[See pricingSee pricing](/pricing)

### Library

* [Blocks](/blocks)
* [Pages](/pages)
* [Pieces](/pieces)
* [Components](/components)
* [Search](/search)

### Learn

* [Docs](/docs)
* [AI & MCP](/docs/mcp)
* [Blog](/blog)
* [Free tools](/tools)
* [What's new?](/changelog)
* [Website Builder](https://beste.co)

### More

* [Pricing](/pricing)
* [Referrals](/referrals)
* [License](/license)
* [GitHub](https://github.com/beste-co/beste-ui)

### Shadcn Blocks

* [Shadcn Hero Blocks](/blocks/hero)
* [Shadcn Feature Blocks](/blocks/feature)
* [Shadcn Pricing Blocks](/blocks/pricing)
* [Shadcn CTA Blocks](/blocks/cta)
* [Shadcn FAQ Blocks](/blocks/faq)
* [Shadcn About Blocks](/blocks/about)
* [Shadcn Stats Blocks](/blocks/stats)
* [Shadcn Footer Blocks](/blocks/footer)
* [Shadcn Navigation Blocks](/blocks/navigation)
* [Shadcn Auth Blocks](/blocks/auth)
* [Shadcn Ecommerce Blocks](/blocks/ecommerce)
* [Shadcn Portfolio Blocks](/blocks/portfolio)
* [Shadcn Showcase Blocks](/blocks/showcase)
* [Shadcn Careers Blocks](/blocks/careers)
* [Shadcn Onboarding Blocks](/blocks/onboarding)
* [Shadcn Coming Soon Blocks](/blocks/coming-soon)
* [Shadcn Post Blocks](/blocks/post)
* [Shadcn Legal Blocks](/blocks/legal)
* [Shadcn Workflow Blocks](/blocks/workflow)
* [Shadcn News Blocks](/blocks/news)

© 2026, [Beste](https://beste.co). All rights reserved.