← каталог
Retro Grid
Ретро-сетка: клетчатая плоскость уходит к горизонту и едет на камеру. Одна CSS-перспектива вместо WebGL, бесшовный цикл — сдвиг ровно на клетку.
cssautobasic0reduced-motion ✓
🎛 Настройки — крути и копируй
Пропсы
Разобрано из кода записи: обязательные — без «?», у остальных показано значение по умолчанию.
| Проп | Тип | По умолчанию | Что это |
|---|---|---|---|
| backdrop? | string | false | BACKDROP | Подложка (база под сеткой): CSS-значение `background` или `false`. `false` нужен, когда запись стоит фоном СЕКЦИИ и подложку задаёт она. |
| hue? | number | 300 | Тон линий сетки, градусы oklch (0–360). |
| angle? | number | 64 | Наклон плоскости, градусы (50–78: меньше — площе, больше — «пол под ногами»). |
| cell? | number | 48 | Шаг сетки, px. |
| speed? | number | 1.6 | Скорость: секунд на одну клетку. |
| children? | ReactNode | — | Содержимое поверх. В витрине — подпись, в секции обычно пусто. |
Установка
npx shadcn@latest add https://motiva.pages.dev/r/retro-grid.jsonЧто нужно в проекте до установки
components.jsonв корне — с алиасами в форме"components": "@/components". Нет файла — CLI не поймёт, куда класть.- Алиас
@объявлен дважды:"paths": {"@/*": ["./src/*"]}в корневомtsconfig.json(в Vite он часто содержит толькоreferences— тогда CLI создаёт папку@) иresolve.aliasв конфиге сборщика. - Токены подключены один раз на приложение:
import "@/styles/motiva-tokens.css"— они приезжают вместе с записью. Без них секция потеряет цвета, ритм и тему. - Своя типографика не должна спорить: если у вас на обёртке
text-align: center, он протечёт внутрь блоков. - Проект без TypeScript? Всё равно нужен минимальный
tsconfig.jsonсpathsи"tsx": trueвcomponents.json: CLI грузит tsconfig безусловно, а с"tsx": falseидёт заjsconfig.jsonи падает так же. Приехавший рядом.tsxможно не трогать — вам нужен.vanilla.js.
Экспорт
Код
import type { CSSProperties, ReactNode } from "react";
/** Подложка по умолчанию — как в витрине. */
const BACKDROP = "linear-gradient(180deg, oklch(0.16 0.02 285), oklch(0.13 0.02 285))";
export interface RetroGridProps {
/**
* Подложка (база под сеткой): CSS-значение `background` или `false`.
* `false` нужен, когда запись стоит фоном СЕКЦИИ и подложку задаёт она.
*/
backdrop?: string | false;
/** Тон линий сетки, градусы oklch (0–360). */
hue?: number;
/** Наклон плоскости, градусы (50–78: меньше — площе, больше — «пол под ногами»). */
angle?: number;
/** Шаг сетки, px. */
cell?: number;
/** Скорость: секунд на одну клетку. */
speed?: number;
/** Содержимое поверх. В витрине — подпись, в секции обычно пусто. */
children?: ReactNode;
}
/**
* Ретро-сетка: плоскость с клеткой уходит к горизонту и едет на камеру.
* Вся «трёхмерность» — одна CSS-перспектива, движение — сдвиг РОВНО на клетку
* (кадр в конце цикла совпадает с первым, стыка не существует). Чистый CSS, 0 КБ.
* Reduced-motion / data-shot — сетка стоит, картинка остаётся.
*/
export function RetroGrid({
backdrop = BACKDROP,
hue = 300,
angle = 64,
cell = 48,
speed = 1.6,
children,
}: RetroGridProps = {}) {
const line = `oklch(0.65 0.14 ${hue} / 0.55)`;
return (
<div
className="rg"
style={{
position: "absolute",
inset: 0,
overflow: "hidden",
...(backdrop === false ? null : { background: backdrop }),
}}
>
<style>{`
/* размерный контейнер: плоскость меряется в РОДИТЕЛЕ (cqw/cqh), не во вьюпорте —
контракт слоя BUILDER 3.5 */
.rg { container-type: size; }
/* камера: линия горизонта на 35% высоты; растворение у горизонта — маской,
а не заливкой поверх, чтобы фон секции просвечивал */
.rg-cam {
position: absolute; inset: 0;
perspective: 200px;
perspective-origin: 50% 35%;
mask-image: linear-gradient(180deg, transparent 32%, #000 46%);
-webkit-mask-image: linear-gradient(180deg, transparent 32%, #000 46%);
}
.rg-plane {
position: absolute; left: 50%; top: 35%;
width: 300cqw; height: 180cqh;
transform: translateX(-50%) rotateX(var(--rg-angle));
transform-origin: top center;
}
/* едет ВНУТРЕННИЙ бегунок трансформом (композитор), а не background-position
(перекраска каждый кадр); запас сверху в одну клетку закрывает исходное положение */
.rg-run {
position: absolute; inset: calc(-1 * var(--rg-cell)) 0 0 0;
background:
repeating-linear-gradient(180deg, var(--rg-line) 0 1px, transparent 1px var(--rg-cell)),
repeating-linear-gradient(90deg, var(--rg-line) 0 1px, transparent 1px var(--rg-cell));
animation: rg-move var(--rg-speed) linear infinite;
}
@keyframes rg-move { to { transform: translate3d(0, var(--rg-cell), 0); } }
@media (prefers-reduced-motion: reduce) { .rg-run { animation: none; } }
`}</style>
<div
className="rg-cam"
style={
{
"--rg-angle": `${angle}deg`,
"--rg-cell": `${cell}px`,
"--rg-speed": `${speed}s`,
"--rg-line": line,
} as CSSProperties
}
>
<div className="rg-plane">
<div className="rg-run" />
</div>
</div>
{children && (
<div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center" }}>{children}</div>
)}
</div>
);
}
Похожие эффекты
лицензия: MITавтор: Motivaисточник: оригинал (Motiva); идея-референс — Magic UI Retro Grid (MIT)v1.0.0