Motiva
Мои коллекции
← каталог

Glitch Text

Глитч-надпись: слои-каналы в псевдоэлементах порезаны clip-path и дёргаются step-end всплесками. Дискретные прыжки, между всплесками — чистый текст.

cssautobasic0reduced-motion ✓
🎛 Настройки — крути и копируй

Пропсы

Разобрано из кода записи: обязательные — без «?», у остальных показано значение по умолчанию.

ПропТипПо умолчаниюЧто это
text?string"ПОМЕХИ В КАНАЛЕ"Текст надписи (он же уходит в data-атрибут для слоёв-каналов).
period?number3.2Секунд на цикл (в цикле два коротких всплеска).
color?string"oklch(0.93 0.01 265)"Цвет базовой надписи.

Установка

npx shadcn@latest add https://motiva.pages.dev/r/glitch-text.json
Что нужно в проекте до установки
  1. components.json в корне — с алиасами в форме"components": "@/components". Нет файла — CLI не поймёт, куда класть.
  2. Алиас @ объявлен дважды: "paths": {"@/*": ["./src/*"]} в корневом tsconfig.json (в Vite он часто содержит только references — тогда CLI создаёт папку@) и resolve.alias в конфиге сборщика.
  3. Токены подключены один раз на приложение:import "@/styles/motiva-tokens.css" — они приезжают вместе с записью. Без них секция потеряет цвета, ритм и тему.
  4. Своя типографика не должна спорить: если у вас на обёрткеtext-align: center, он протечёт внутрь блоков.
  5. Проект без TypeScript? Всё равно нужен минимальныйtsconfig.json с pathsи "tsx": true в components.json: CLI грузит tsconfig безусловно, а с "tsx": false идёт за jsconfig.json и падает так же. Приехавший рядом.tsx можно не трогать — вам нужен.vanilla.js.

Экспорт

↓ Скачать .zip

Код

import type { CSSProperties } from "react";

export interface GlitchTextProps {
  /** Текст надписи (он же уходит в data-атрибут для слоёв-каналов). */
  text?: string;
  /** Секунд на цикл (в цикле два коротких всплеска). */
  period?: number;
  /** Цвет базовой надписи. */
  color?: string;
}

/**
 * Глитч-надпись: два слоя-канала (::before/::after с content: attr(data-text))
 * сдвинуты в стороны и порезаны полосами clip-path. Весь «шум» — окна в процентах
 * ОДНОГО цикла с step-end: значения прыгают, а не плывут, — глитч дискретен по природе.
 * Вне всплесков слои полностью скрыты, видна чистая надпись.
 * Reduced-motion / data-shot — слои спрятаны, остаётся обычный текст.
 */
export function GlitchText({ text = "ПОМЕХИ В КАНАЛЕ", period = 3.2, color = "oklch(0.93 0.01 265)" }: GlitchTextProps = {}) {
  return (
    <span
      className="gt"
      data-text={text}
      style={
        {
          position: "relative",
          display: "inline-block",
          color,
          fontWeight: 800,
          letterSpacing: "0.02em",
          whiteSpace: "nowrap",
          "--gt-period": `${period}s`,
        } as CSSProperties
      }
    >
      <style>{`
        .gt::before, .gt::after {
          content: attr(data-text);
          position: absolute; inset: 0;
          opacity: 0;
          animation-duration: var(--gt-period);
          animation-iteration-count: infinite;
          /* прыжки, а не переходы: глитч дискретен по природе */
          animation-timing-function: step-end;
        }
        .gt::before { color: oklch(0.65 0.25 25); animation-name: gt-a; }
        .gt::after  { color: oklch(0.75 0.15 195); animation-name: gt-b; }
        /* два всплеска на цикл; между ними слои скрыты целиком */
        @keyframes gt-a {
          0%, 34% { opacity: 0; }
          35% { opacity: 0.85; clip-path: inset(8% -2% 62% -2%); transform: translate(-3px, -1px); }
          38% { clip-path: inset(48% -2% 22% -2%); transform: translate(3px, 1px); }
          41% { clip-path: inset(72% -2% 4% -2%); transform: translate(-2px, 0); }
          44%, 79% { opacity: 0; }
          80% { opacity: 0.85; clip-path: inset(28% -2% 52% -2%); transform: translate(2px, 1px); }
          83% { clip-path: inset(60% -2% 12% -2%); transform: translate(-3px, -1px); }
          86%, 100% { opacity: 0; }
        }
        @keyframes gt-b {
          0%, 35% { opacity: 0; }
          36% { opacity: 0.85; clip-path: inset(55% -2% 18% -2%); transform: translate(3px, 1px); }
          39% { clip-path: inset(12% -2% 70% -2%); transform: translate(-3px, -1px); }
          42% { clip-path: inset(38% -2% 40% -2%); transform: translate(2px, 0); }
          45%, 80% { opacity: 0; }
          81% { opacity: 0.85; clip-path: inset(66% -2% 8% -2%); transform: translate(-2px, 1px); }
          84% { clip-path: inset(18% -2% 58% -2%); transform: translate(3px, -1px); }
          87%, 100% { opacity: 0; }
        }
        @media (prefers-reduced-motion: reduce) { .gt::before, .gt::after { display: none; } }
      `}</style>
      {text}
    </span>
  );
}
лицензия: MITавтор: Motivaисточник: оригинал (Motiva); идея-референс — ReactBits Glitch Text (MIT + Commons Clause: только идея)v1.0.0