故障文字

悬停时文字瞬间撕裂成错位的彩色切片,像信号中断一样,随后又干净地复原。

  • CSS
  • 文字
  • 悬停
  • 收录于
信号丢失

AI 提示词

用纯 CSS 制作故障风格的文字悬停效果。把文字放在一个元素中,并把 data-text 设为同样的字符串,再用 ::before 和 ::after 配合 content: attr(data-text) 精确叠在上面。一层使用主色,另一层使用偏冷的浅色,静止时两层都不可见。在 :hover 和 :focus-visible 时显示这两层,并各自运行一个 600ms、steps(1) 的 keyframe,在四五个水平切片之间跳变 clip-path: inset(top% 0 bottom% 0),同时左右偏移几个像素,两层的节奏错开。底下的真实文字保持可读。给屏幕阅读器放一个视觉隐藏的 span,故障元素本身设为 aria-hidden。在 prefers-reduced-motion 下改为静态的双色错位。文字为“信号丢失”。

源代码

Demo.tsx
'use client'

import s from './demo.module.css'

export default function GlitchText({ labels }: { labels: Record<string, string> }) {
  return (
    <span className={s.wrap} tabIndex={0}>
      <span className={s.sr}>{labels.text}</span>
      <span className={s.glitch} data-text={labels.text} aria-hidden="true">
        {labels.text}
      </span>
    </span>
  )
}
demo.module.css
.wrap {
  border-radius: 6px;
  outline: none;
  cursor: default;
}

.wrap:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 6px; }

.sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.glitch {
  position: relative;
  display: inline-block;
  font: 800 clamp(2rem, 7vw, 2.6rem) / 1 var(--font-display);
  letter-spacing: 0.04em;
  color: var(--stage-ink);
}

.glitch::before,
.glitch::after {
  content: attr(data-text);
  position: absolute;
  inset: 0;
  opacity: 0;
}

.glitch::before { color: var(--stage-accent); }
.glitch::after { color: oklch(80% 0.1 220); }

.wrap:hover .glitch::before,
.wrap:focus-visible .glitch::before { opacity: 1; animation: slice-a 600ms steps(1) infinite; }

.wrap:hover .glitch::after,
.wrap:focus-visible .glitch::after { opacity: 1; animation: slice-b 600ms steps(1) infinite; }

@keyframes slice-a {
  0% { clip-path: inset(10% 0 70% 0); translate: -4px 0; }
  20% { clip-path: inset(55% 0 25% 0); translate: 3px 0; }
  40% { clip-path: inset(30% 0 50% 0); translate: -2px 0; }
  60% { clip-path: inset(80% 0 5% 0); translate: 4px 0; }
  80% { clip-path: inset(0 0 85% 0); translate: -3px 0; }
}

@keyframes slice-b {
  0% { clip-path: inset(65% 0 15% 0); translate: 3px 0; }
  20% { clip-path: inset(5% 0 80% 0); translate: -4px 0; }
  40% { clip-path: inset(40% 0 40% 0); translate: 2px 0; }
  60% { clip-path: inset(20% 0 65% 0); translate: -3px 0; }
  80% { clip-path: inset(75% 0 10% 0); translate: 4px 0; }
}

@media (prefers-reduced-motion: reduce) {
  .wrap:hover .glitch::before,
  .wrap:hover .glitch::after,
  .wrap:focus-visible .glitch::before,
  .wrap:focus-visible .glitch::after { animation: none; clip-path: inset(0 0 50% 0); }
  .wrap:hover .glitch::before, .wrap:focus-visible .glitch::before { translate: -2px 0; }
  .wrap:hover .glitch::after, .wrap:focus-visible .glitch::after { clip-path: inset(50% 0 0 0); translate: 2px 0; }
}

投稿创意

看到想收录的动效或主题?简单描述一下,会打开一个预先填好的 GitHub Issue,确认后即可提交。

你想投稿的是?

需要 GitHub 账号,投稿内容会公开。