Detent web components
By Shane Logsdon

Media & effects

Text Scramble Decoder

Implemented

Controlled technical typography reveal

detent-text-scramble-decoder

Port status
Catalog status
implemented
Origin
upstream
Phase
4
Wave
phase-4-canvas
Group
Media & effects

4.2 KB gzip for the class module

Variants

The readable text never changes: only the decorative glyph layer is scrambled, and it is aria-hidden, so the copy is read once. Each run resolves position by position; a second hover, a property write or a replayed decode cancels the run in flight instead of stacking. With prefers-reduced-motion: reduce the final text is written immediately and no frame is scheduled.

trigger="hover" — move the pointer over the line

trigger="manual" — replay button, disabled while decoding

custom glyph alphabet and a 500 ms run

Documented parts: root, text, glyph, replay, replay-icon. Documented custom properties: --detent-scramble-text, --detent-scramble-glyph, --detent-scramble-surface, --detent-scramble-radius, --detent-scramble-font-size, --detent-scramble-replay-bg, --detent-scramble-replay-border, --detent-scramble-replay-icon.

Recorded states

  • reduced motion recorded state; no setter

Usage

Import

import '/components/text-scramble-decoder/define.js';

// or register it yourself
import { DetentTextScrambleDecoder } from '/components/text-scramble-decoder/text-scramble-decoder.js';

Attributes

  • characters
  • duration
  • replay-label
  • text
  • trigger

Properties

No properties are recorded for this component.

Slots

No slots are recorded for this component.

Events

No events are recorded for this component.

Example

Runnable example: site/examples/text-scramble-decoder.ts. The snippet below is generated from the catalogued attributes.

<detent-text-scramble-decoder
  characters
  duration="900"
  replay-label
  text
  trigger="mount"
></detent-text-scramble-decoder>

Source

The pinned upstream source for this row:

  • src/components/ui/TextScrambleDecoder.tsx sha256 5e7cb5af556d…

Native files

The native module and its artifacts:

  • src/components/text-scramble-decoder/text-scramble-decoder.ts
  • src/components/text-scramble-decoder/styles.css
  • src/components/text-scramble-decoder/define.ts
  • docs/components/text-scramble-decoder.md
  • site/examples/text-scramble-decoder.ts
  • tests/browser/text-scramble-decoder.spec.ts
  • evidence/components/text-scramble-decoder.md

API

Boolean attributes follow HTML presence semantics: present means true, absent means false, and disabled="false" still means disabled.

Attributes

Attributes
AttributePropertyTypeDefaultNotes
characterscharactersstringA-Z, 0-9, symbolsKebab-case attribute; camelCase property.
durationdurationnumber900Kebab-case attribute; camelCase property.
replay-label———Recorded in the catalog as an attribute.
texttextstring—Kebab-case attribute; camelCase property.
triggertrigger'mount' | 'hover' | 'manual''mount'Kebab-case attribute; camelCase property.

Properties

No property-only values are recorded for this component.

Events

No custom events are recorded for this component.

Slots

No slots are recorded for this component.

CSS customisation

  • `lucide-react` icon components replaced with local SVG assets (ISC notice retained).
  • React state/hooks replaced by element-owned state with pre-upgrade property preservation.
  • duration clamps to 0..20000 ms and blank characters fall back to the pinned alphabet; a negative duration made the source loop forever.
  • Progress and cadence are measured from the run first frame instead of performance.now().
  • Progress 0 is painted synchronously at run start, so there is no flash of the final text.
  • lucide-react RefreshCw becomes a local SVG (the ISC notice is already in THIRD_PARTY_NOTICES.md).
  • Colours are theme tokens with light/dark hex fallbacks, and the replay border and icon are raised above the pinned #1F1F1F/#525252 (2.4:1) to at least 4.5:1.
  • The accessible reading is real text in a visually hidden span as well as the pinned aria-label, because a generic span aria-label alone is not exposed by any of the three engines.
  • The catalog records duration 900 while the pinned source initialises 800; the row is the API authority, so the native default is 900. Owner confirmed 900 on 2026-10-29.

What it does

A controlled technical typography reveal: scrambled glyphs resolve position by position into readable text. The readable text never changes — only the painted glyph layer does — so the copy stays one reading for assistive technology.

Upstream behaviour
  • Position-staged character resolution with a stable final state
  • Mount, hover, and manual replay trigger modes
  • Configurable glyph set and duration
Differences from upstream
  • `lucide-react` icon components replaced with local SVG assets (ISC notice retained).
  • React state/hooks replaced by element-owned state with pre-upgrade property preservation.
  • duration clamps to 0..20000 ms and blank characters fall back to the pinned alphabet; a negative duration made the source loop forever.
  • Progress and cadence are measured from the run first frame instead of performance.now().
  • Progress 0 is painted synchronously at run start, so there is no flash of the final text.
  • lucide-react RefreshCw becomes a local SVG (the ISC notice is already in THIRD_PARTY_NOTICES.md).
  • Colours are theme tokens with light/dark hex fallbacks, and the replay border and icon are raised above the pinned #1F1F1F/#525252 (2.4:1) to at least 4.5:1.
  • The accessible reading is real text in a visually hidden span as well as the pinned aria-label, because a generic span aria-label alone is not exposed by any of the three engines.
  • The catalog records duration 900 while the pinned source initialises 800; the row is the API authority, so the native default is 900. Owner confirmed 900 on 2026-10-29.
Previous: StretchSwitch Next: Thinking Orb

Docs

Components

Project