← каталог
Tracing Beam
Луч вдоль контента, прорисовываемый прокруткой: pathLength=1 и stroke-dashoffset 1→0 от view-timeline. Ни строки JS, откат прокрутки отматывает луч.
cssscrollintermediate0reduced-motion ✓
Пропсы
Разобрано из кода записи: обязательные — без «?», у остальных показано значение по умолчанию.
| Проп | Тип | По умолчанию | Что это |
|---|---|---|---|
| hue? | number | 300 | Тон луча, градусы oklch. |
| children? | ReactNode | — | Контент, вдоль которого рисуется луч. |
Установка
npx shadcn@latest add https://motiva.pages.dev/r/tracing-beam.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";
export interface TracingBeamProps {
/** Тон луча, градусы oklch. */
hue?: number;
/** Контент, вдоль которого рисуется луч. */
children?: ReactNode;
}
/**
* Луч вдоль контента, прорисовываемый прокруткой: у линии pathLength = 1, поэтому
* dash-паттерн задан долями, и прогресс — это просто stroke-dashoffset 1 → 0.
* Прогресс отдаёт браузер (именованный view-timeline на внешней коробке — она и
* проходит через экран, AGENTS), скрипта нет вовсе, откат прокрутки отматывает луч.
* Без поддержки animation-timeline и в reduced-motion луч дорисован полностью.
*/
export function TracingBeam({ hue = 300, children }: TracingBeamProps = {}) {
const bright = `oklch(0.72 0.14 ${hue})`;
return (
<div className="tb" style={{ display: "grid", gridTemplateColumns: "22px minmax(0, 1fr)", gap: 22, width: "100%" }}>
<style>{`
.tb { view-timeline: --tb-view block; }
.tb-rail { position: relative; container-type: size; }
/* база (вне gvarda supports): луч дорисован — так же выглядит reduced-motion */
.tb-draw { stroke-dashoffset: 0; }
.tb-dot {
position: absolute; left: 50%; top: 4px;
width: 12px; height: 12px; margin-left: -6px;
border-radius: 999px;
background: var(--tb-bright);
box-shadow: 0 0 12px var(--tb-bright);
}
@supports (animation-timeline: view()) {
/* contain, не cover: у колонки выше экрана это ровно «прогресс чтения» —
0 когда её верх коснулся верха экрана, 1 когда низ дошёл до низа.
cover требовал бы, чтобы коробка целиком ПОКИНУЛА экран, — на странице,
которая кончается вместе с колонкой, луч не дорисовывался никогда. */
.tb-draw {
stroke-dashoffset: 1;
animation: tb-draw linear both;
animation-timeline: --tb-view;
animation-range: contain;
}
.tb-dot {
animation: tb-move linear both;
animation-timeline: --tb-view;
animation-range: contain;
}
@keyframes tb-draw { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
@keyframes tb-move { from { transform: translateY(0); } to { transform: translateY(calc(100cqh - 16px)); } }
}
@media (prefers-reduced-motion: reduce) {
.tb-draw { animation: none; stroke-dashoffset: 0; }
.tb-dot { display: none; }
}
`}</style>
<div className="tb-rail" style={{ "--tb-bright": bright } as CSSProperties}>
<svg width="22" height="100%" style={{ position: "absolute", inset: 0, display: "block" }} aria-hidden="true">
<line x1="11" y1="8" x2="11" y2="100%" stroke="oklch(0.32 0.02 270 / 0.8)" strokeWidth="2" />
<line
className="tb-draw"
x1="11"
y1="8"
x2="11"
y2="100%"
pathLength={1}
strokeDasharray="1"
stroke={bright}
strokeWidth="2"
strokeLinecap="round"
/>
</svg>
<span className="tb-dot" />
</div>
<div style={{ minWidth: 0 }}>{children}</div>
</div>
);
}
Похожие эффекты
лицензия: MITавтор: Motivaисточник: оригинал (Motiva); идея-референс — Aceternity Tracing Beam (только идея: terms запрещают заимствование кода)v1.0.0