Shiny Text
Металлический блик по тексту: градиент шире строки едет по background-position, текст работает маской. Один слой заливки — эффект живёт на любом объёме текста.
Пропсы
Разобрано из кода записи: обязательные — без «?», у остальных показано значение по умолчанию.
| Проп | Тип | По умолчанию | Что это |
|---|---|---|---|
| children? | ReactNode | "премиум-каталог анимаций" | |
| color? | string | "#8b8b99" | Базовый цвет текста. |
| shine? | string | "#ffffff" | Цвет блика. |
| duration? | number | 3 | Длительность пробега, сек. |
Установка
npx shadcn@latest add https://motiva.pages.dev/r/shiny-text.jsonЧто нужно в проекте до установки
Файл
components.jsonв корнеИз него CLI узнаёт, куда класть файлы. Алиасы — в такой форме:
"aliases": { "components": "@/components", "lib": "@/lib" }Нет файла — установка не начнётся вовсе.
Алиас
@объявлен дваждыВ корневом
tsconfig.jsonи в конфиге сборщика. В Vite корневой tsconfig часто содержит толькоreferences— этого мало."paths": { "@/*": ["./src/*"] } // tsconfig.json resolve.alias = { "@": "/src" } // vite.config.tsБез
pathsCLI молча создаёт папку с именем@.Токены подключены один раз на приложение
Они приезжают вместе с записью — достаточно импортировать их в точке входа:
import "@/styles/motiva-tokens.css";Без них блок потеряет цвета, ритм и тему.
Своя типографика не спорит с блоком
Наследуемые свойства протекают внутрь:
text-align: centerна обёртке отцентрует содержимое секции. То же касаетсяfont-familyиline-height.Проект без TypeScript? всё равно нужен tsconfig
CLI грузит
tsconfig.jsonбезусловно, а при"tsx": falseидёт заjsconfig.jsonи падает так же. Минимум — файл сpathsи"tsx": trueвcomponents.json.Приехавший рядом
.tsxможно не трогать — вам нужен.vanilla.js.
Экспорт
Код
@keyframes sht-slide { to { background-position: -200% 0; } }
.sht {
display: inline-block;
background-image: linear-gradient(110deg,
var(--sht-color) 0%, var(--sht-color) 42%,
var(--sht-shine) 50%,
var(--sht-color) 58%, var(--sht-color) 100%);
background-size: 200% auto; background-position: 0 0;
-webkit-background-clip: text; background-clip: text; color: transparent;
animation: sht-slide var(--sht-dur) linear infinite;
}
@media (prefers-reduced-motion: reduce) { .sht { animation: none; } }
html[data-shot] .sht { animation: none; }
const css = `@keyframes sht-slide { to { background-position: -200% 0; } }
.sht {
display: inline-block;
background-image: linear-gradient(110deg,
var(--sht-color) 0%, var(--sht-color) 42%,
var(--sht-shine) 50%,
var(--sht-color) 58%, var(--sht-color) 100%);
background-size: 200% auto; background-position: 0 0;
-webkit-background-clip: text; background-clip: text; color: transparent;
animation: sht-slide var(--sht-dur) linear infinite;
}
@media (prefers-reduced-motion: reduce) { .sht { animation: none; } }
html[data-shot] .sht { animation: none; }
`;
import type { CSSProperties, ReactNode } from "react";
export interface ShinyTextProps {
children?: ReactNode;
/** Базовый цвет текста. */
color?: string;
/** Цвет блика. */
shine?: string;
/** Длительность пробега, сек. */
duration?: number;
}
/**
* Металлический блик по тексту: градиент шире строки едет по background-position,
* а текст работает маской (`background-clip: text`). Ни одного элемента-обёртки на букву —
* это один слой заливки, поэтому эффект живёт на любом объёме текста и не мешает выделению.
*/
export function ShinyText({
children = "премиум-каталог анимаций",
color = "#8b8b99",
shine = "#ffffff",
duration = 3,
}: ShinyTextProps) {
return (
<>
<style href="motiva-shiny-text" precedence="medium">{css}</style>
<span
className="sht"
style={
{ "--sht-color": color, "--sht-shine": shine, "--sht-dur": `${duration}s` } as CSSProperties
}
>
{children}
</span>
</>
);
}