# ASCII Dissolve

> Text that dissolves into shade noise and resolves back.

Category: Type. Docs: https://asciicn.sumak.dev/docs/ascii-dissolve

## Installation

```bash
npx shadcn@latest add @asciicn/ascii-dissolve
```

Needs the `@asciicn` registry in components.json: `"registries": { "@asciicn": "https://asciicn.sumak.dev/r/{name}.json" }`.

## Usage

```tsx
import { AsciiDissolve } from "@/components/ascii/ascii-dissolve"

<AsciiDissolve text="Made of characters" />
<AsciiDissolve text="hover me" trigger="hover" />
```

## Examples

### Links on hover

With `trigger="hover"` each pointer enter crumbles the label to dust and draws it back. Short durations keep it a flicker rather than a wait.

```tsx
<nav className="flex gap-6">
  {["index", "archive", "colophon"].map((l) => (
    <a key={l} href={`#${l}`}>
      <AsciiDissolve text={l} trigger="hover" duration={350} />
    </a>
  ))}
</nav>
```

### Presentation mode

Pass `dissolved` to drive it yourself, here from a toggle that hides personal details before screen sharing. Reversing mid-way retraces the same grains.

```tsx
const [hidden, setHidden] = useState(false)

<Button onClick={() => setHidden((h) => !h)}>
  {hidden ? "Show details" : "Hide details"}
</Button>
<dl>
  <dt>Name</dt>
  <dd><AsciiDissolve text="Ada Lovelace" dissolved={hidden} /></dd>
  <dt>Email</dt>
  <dd><AsciiDissolve text="ada@analytical.engine" dissolved={hidden} /></dd>
</dl>
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `text` | `string` |  | Final text. Screen readers get it immediately. |
| `trigger` | `"view" | "mount" | "hover"` | `"view"` | View resolves once the text scrolls into view, mount resolves on mount, hover dissolves and resolves on every pointer enter. |
| `dissolved` | `boolean` |  | Controls the state yourself and ignores `trigger`. True dissolves, false resolves. |
| `duration` | `number` | `900` | Milliseconds for one full dissolve or resolve. A hover does both, so it takes twice as long. |
| `glyphs` | `string` | `"#%&@*+=:;"` | Noise a character crumbles into before it thins out through ▓▒░. |

## Notes

- **Accessibility.** The real text sits in an `sr-only` span from the first render and the noise is `aria-hidden`, so dissolving is visual only. Don't use it to hide secrets: screen readers and the DOM still have the text.
- **Motion.** Under `prefers-reduced-motion` the text always shows resolved, whatever the trigger or `dissolved`.
- **Layout.** Each noise glyph is drawn over its character and clipped to that character's width, so it works in any font, proportional or not, and the text never reflows.
- **Dependencies.** None. One `requestAnimationFrame` loop while it moves, repainting at about 30 fps, plus an IntersectionObserver for `trigger="view"`.
