Dot Matrix Hero
A free animated hero section with a masked reveal on a lit dot grid.
Your whole fleet, checked in every thirty seconds
Northwind keeps a live picture of 14,209 edge devices across 61 sites, so a node that goes quiet raises a page before a customer notices.
Nodes reporting
99.98%
Check-ins per minute, last two hours
Dot Matrix Hero is a free animated hero section for Next.js and React that reveals your headline word by word, each word rising out of its own mask, on a dot grid lit from one side. It is built with Motion, styled with Tailwind, and composes the Dot Matrix background component through a registry dependency.
The composition is asymmetric: the copy runs hard left in a single measure, the grid brightens through the empty space opposite it, and an optional readout drawn on the grid's own dot pitch anchors the foot of the section. You pass a title, an optional subtitle, up to two calls to action and an optional readout, and the section handles the rest: the headline is split into words as it renders, the words rise into view when the section scrolls in, the supporting elements follow a beat later, and a static scrim keeps the text readable over the brightest lit dots. With prefers-reduced-motion enabled there is no splitting and no movement, everything renders immediately, and the background holds still.
Install
Add the @te registry to your components.json once:
{
"registries": {
"@te": "https://templateempire.io/r/{name}.json"
}
}Then install the component:
npx shadcn@latest add @te/hero-dot-matrixUsage
import { HeroDotMatrix } from "@/components/hero-dot-matrix";
export default function Page() {
return (
<HeroDotMatrix
title="Your whole fleet, checked in every thirty seconds"
subtitle="A live picture of every edge device you run."
cta={{ label: "Start monitoring", href: "/signup" }}
secondaryCta={{ label: "See a live fleet", href: "/demo" }}
readout={{
label: "Nodes reporting",
value: "99.98%",
caption: "Check-ins per minute, last two hours",
series: [0.35, 0.52, 0.41, 0.68, 0.57, 0.74, 0.46, 0.63],
seriesDescription:
"Check-ins stayed between a third and three quarters of the two-hour peak, ending just under two thirds.",
}}
/>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| title | string | required | Headline copy, rendered as the section's single h1. |
| subtitle | string | undefined | Supporting copy rendered below the headline. |
| cta | { label: string; href: string } | required | Primary call to action, rendered as a real anchor. |
| secondaryCta | { label: string; href: string } | undefined | Optional secondary call to action, rendered as a real anchor. |
| readout | { label: string; value: string; caption: string; series: number[]; seriesDescription: string } | undefined | Optional readout plotted on the grid at the foot of the section. series holds samples from 0 to 1, oldest first, drawn as lit dots on the grid pitch; a sample of 0 lights no dots. seriesDescription is the plot's text alternative, one sentence stating the shape of the series, and becomes the accessible name of the plot. |
| className | string | undefined | Additional classes merged onto the section root. |
Customisation
Dot Matrix Hero drives the grid itself, so it declares its own knobs rather than leaving the background at its bare defaults: a tighter cell and a stronger resting dot, so the field reads as a surface instead of an empty ground. Override --hero-dm-cell for the grid pitch, which the readout plot steps in time with, --hero-dm-plot-dot for the size of a plotted dot, --hero-dm-dot for the resting dots, --hero-dm-glow for the sweeping highlight and --hero-dm-lit for the plotted dots, from :root or any ancestor of the hero. Each plotted sample owns one cell, so centre to centre is exactly one cell; a plot dot larger than the cell is capped at it rather than pushing the plot off the field's pitch:
:root {
--hero-dm-cell: 24px;
--hero-dm-plot-dot: 4px;
--hero-dm-dot: color-mix(in oklab, var(--foreground) 46%, transparent);
--hero-dm-glow: var(--color-accent-400, var(--ring));
--hero-dm-lit: var(--color-accent-400, var(--ring));
}Frequently asked questions
- Does Dot Matrix Hero work with right-to-left languages?
- Yes. The headline takes the direction of the page or the element around it, exactly as plain text does, so for a Hebrew or Arabic title set dir="rtl" on the page or on an ancestor, with lang for the language itself (lang alone does not change the direction). Left-to-right text splits into words; text containing right-to-left characters, or placed in a right-to-left context, enters as one block, laid out by the browser's own bidirectional rules.
- Is Dot Matrix Hero free for commercial use?
- Yes. Dot Matrix Hero is MIT licensed, so you can use it in personal and commercial projects without attribution.
- Does Dot Matrix Hero affect performance or accessibility?
- The reveal animates transforms and opacity only, the words move inside space the layout has already reserved, so there is no layout shift, and the entrance runs once when the section enters the viewport, leaving no scroll listener or per-frame script behind once it finishes. The background sweep is a single CSS transform that pauses while offscreen. The headline stays a single accessible string for screen readers, both calls to action are real anchors with visible focus rings, the readout plot is exposed as one image carrying your written description of the series rather than being hidden as decoration, and with reduced motion enabled the full content renders immediately with a static background.
- How do I change the dot grid behind the hero?
- Override the CSS custom properties --hero-dm-cell, --hero-dm-plot-dot, --hero-dm-dot, --hero-dm-glow and --hero-dm-lit from :root or any ancestor element. The hero maps them onto the Dot Matrix background it installs automatically: --hero-dm-cell sets the grid pitch, and every plotted sample owns one cell of it, so a plotted dot sits exactly one cell from its neighbour and the two grids step together. --hero-dm-plot-dot sizes a plotted dot and is capped at the cell, because a dot wider than its cell would leave no gap and the plot would step on its own dot size instead. --hero-dm-dot sets the resting dots, --hero-dm-glow colours the sweeping highlight and --hero-dm-lit colours the plotted dots.
- What happens to a long readout series on a narrow screen?
- Every sample stays on screen. The plot asks for one grid cell per sample and takes it when there is room, so on a desktop the plot steps in time with the field. When the series is wider than the space the rail gives it, on a phone or with a long series, the columns share out what there is instead: the pitch drops below the field's, the whole window your caption describes is still drawn, and nothing is cropped. The series is a prop, so pass as many samples as the window you name in the caption.