AnimationDirectiveCSSAdded in v15.7

Glitch

A short piece of text that looks like a broken signal while the pointer is over it: two tinted copies cut into thin slices jump sideways while the text leans a little, and the moment the pointer leaves everything snaps back clean.

Installation

Import the class, and @use the entry point in your global styles. Nothing warns you when the @use is missing.

import { Component } from '@angular/core';
import { WrGlitch } from 'ngwr/animations/glitch';

@Component({ imports: [WrGlitch] })
export class MyComponent {}

Basic

Put wrGlitch on the element that holds the text and move the pointer over it. It plays only under the pointer, never on its own, and stops the moment the pointer leaves. While it plays, the stylesheet loops six held steps per --wr-glitch-cycle, 1.2 s unset: each step a different thin slice of each copy, pushed a few pixels to one side.

<h2 wrGlitch>Hover me</h2>

Hover me

A descendant as the target

A selector as the directive's value points it at one descendant of the host instead, for a heading a library component draws: the 404 page of this site glitches the title of <wr-result-404> this way. The pointer is read on the target, not the host. The target is looked up once after the first render by its public BEM class; a selector that matches nothing turns the effect off.

<wr-result-404 wrGlitch=".wr-result__title" />

Page not found

Only the heading above glitches, and only under the pointer.

Keyboard focus

The pointer and focus are read on the target's closest focusable ancestor, which is what a keyboard reaches: in a button or a link, the text glitches while the control has focus as well as under the pointer, and stops when focus moves on.

<button wr-btn outlined type="button"><span wrGlitch>Hover or focus me</span></button>

Without a pointer

active plays the effect exactly as a pointer does, hover and focus still work on top. Turn it on in short bursts to make text glitch by itself, and keep each burst at one cycle or more with longer gaps, which keeps the flash budget below.

<h2 wrGlitch [active]="on()">Signal lost</h2>

Signal lost

Paint

--wr-glitch-cycle is one pass of the six steps, 1.2 s unset; keep it at a second or more, which is what holds the flash budget below. --wr-glitch-shift is how far a slice and the text jump, in em, and --wr-glitch-skew how far the text leans. The two copies are theme inks at --wr-glitch-strength over transparent, primary and danger by default; --wr-glitch-tint-a and --wr-glitch-tint-b set them outright.

.signal {
  --wr-glitch-cycle: 2s;
  --wr-glitch-shift: 0.2em;
  --wr-glitch-skew: 4deg;
  --wr-glitch-strength: 40%;
}

A wider, slower tear

Accessibility

PartBehaviour
Screen readers The text is never touched: its node stays where it is, with its bindings. The copies are aria-hidden spans appended while the pointer is over the text and removed when it leaves, and they sit outside the selection, so a copy of the page gives the text once.
Flashing The line is cut into twelve thin bands and each copy lights each of its six bands once per cycle, so no point of the text is lit more than once a cycle by either copy. With the cycle at a second or more, any one second shows a point at most two opposing pairs of luminance changes, where WCAG 2.3.1 allows three. The copies are faint tints over thin strips of glyphs, never a strobe, and the text reads in its own colour at every frame.
Reduced motion Under prefers-reduced-motion: reduce nothing plays and the stylesheet paints no copy. The demos on this page follow the same setting.
Forced coloursUnder forced-colors: active no copy is painted and the directive does not run.
Server The server renders the text as it is. The listeners are added in the browser, after the first render. Nothing plays while the document is hidden.

API

NameDescriptionTypeDefault
wrGlitchA selector for one descendant of the host to glitch instead of the host itself, written as the directive's own value: wrGlitch=".wr-result__title". Resolved once after the first render with querySelector; a selector that matches nothing turns the effect off. Unset or empty, the host.string | nullnull
activePlays the effect while true, exactly as under a pointer; hover and focus still start it when this is false. Drive it with short bursts to make a tile glitch on its own: keep each burst to one cycle or more and the gaps longer than the burst, so the flash budget above holds.booleanfalse

CSS variables

Set these on the element itself, with a rule more specific than its Declared on selector. In a global stylesheet, put a class of yours on an ancestor it really has: <body> for an overlay panel, which renders outside your markup. In a component stylesheet the selector alone works, but only for an element in that component's own template. A value on a wrapper or :root is shadowed, because ngwr/animations/glitch declares each default on its own element.

VariableDefaultDeclared on
--wr-glitch-cycle1.2s.wr-glitch
--wr-glitch-shift0.1em.wr-glitch
--wr-glitch-skew2deg.wr-glitch
--wr-glitch-strength55%.wr-glitch
--wr-glitch-tint-acolor-mix(in srgb, var(--wr-color-primary-ink) var(--wr-glitch-strength), transparent).wr-glitch
--wr-glitch-tint-bcolor-mix(in srgb, var(--wr-color-danger-ink) var(--wr-glitch-strength), transparent).wr-glitch

See also