# Thread Lines

> A wide ribbon of fine threads behind a section's content, like silk: dozens of hair-thin strands flowing across the region at a slight angle, close together near one end, almost a single cord, and parting as they go toward the other, each taking a path of its own. The bundle rides slow waves that travel along it, so it shimmers, and the pointer parts the threads around itself, like fingers drawn through silk.

Source: https://ngwr.dev/animations/thread-lines  
Kind: Animation, Component, WebGL  
Added in v15.7

## Installation

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

```typescript
import { Component } from '@angular/core';
import { WrThreadLines } from 'ngwr/animations/thread-lines';

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

```scss
@use 'ngwr/animations/thread-lines';
```

## Basic

Put `<wr-thread-lines />` first inside the region it should fill. The drawing is one WebGL pass: every pixel measures its distance to every strand, so a strand is a crisp anti-aliased hairline with a faint glow, never a stroke. The context is made when the host is first on screen and sized, and the loop runs while it stays on screen and the document is visible: thirty frames a second while only the waves move, the screen's rate while the parting is live. The parent takes the pointer, passively; a finger is ignored, so a scroll over the section is never fought. Under RTL the ribbon is mirrored and gathers at the inline start.

```angular-html
<section class="opt-in">
  <wr-thread-lines />
  <h2>Opt in to what you need.</h2>
</section>
```

## The parent

The host is absolute, `inset: 0`, at `z-index: -1`, under no pointer. The parent provides two things: it is positioned, so the host fills it, and isolated, so the host's negative z-index lands at the floor of the parent's own stacking context, under the content and over the page background.

```scss
.opt-in {
  position: relative;
  isolation: isolate;
}
```

## Calmer

`count` is how many strands the bundle has, `amplitude` how high the wave lifts it, in px, `speed` how fast the wave travels and `fray` how far the strands part by the far end; none of it keeps them a cord the whole way. Fewer strands, a lower and slower wave and a narrower fray is a ribbon that barely stirs.

```angular-html
<wr-thread-lines count="48" amplitude="8" speed="0.6" fray="0.6" />
```

## Dense and frayed

More strands, a wider fray, a steeper `angle` (degrees; negative rises toward the far end) and a finer `thickness` (device px) is a frayed sheet of silk rather than a cord. The count is capped at 128: the shader walks every strand for every pixel, and the canvas renders under a pixel budget of its own, so a wide section is never a wide texture.

```angular-html
<wr-thread-lines count="110" fray="1.6" angle="-10" thickness="0.8" />
```

## Colours

The middle of the bundle is `--wr-thread-lines-color`, the body's ink by default, and the outer strands blend toward `--wr-thread-lines-accent`, the primary by default; set either on the host, where the defaults are declared. Both are read through `resolveCssColorChannels` and re-read when the scheme changes. The surface is read too: on a light one the strands are laid as ink, darker than the surface, with next to no glow; on a dark one they are lit, with a soft glow round each.

```angular-html
<wr-thread-lines style="--wr-thread-lines-color: var(--wr-color-info); --wr-thread-lines-accent: var(--wr-color-warning)" />
```

## Parting

`parting` is how strongly the pointer parts the strands: the gap is a lens drawn out along the ribbon, whole at the pointer and gone about 110px across from it, and it opens and closes on a slightly under-damped spring, so the threads swing a little past and back when the pointer leaves. None turns it off, and the loop then holds at thirty frames a second throughout.

```angular-html
<wr-thread-lines parting="0" />
```

## API

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| `count` | How many strands the bundle has, 128 at most. | `number` | `72` |
| `thickness` | How thick a strand is, in device px. | `number` | `1` |
| `amplitude` | How high the travelling wave lifts the bundle, in CSS px. | `number` | `16` |
| `speed` | How fast the wave travels, a multiplier: one is a wave every nine seconds. | `number` | `1` |
| `fray` | How far the strands part by the far end, a multiplier: none keeps them a cord the whole way. | `number` | `1` |
| `angle` | The ribbon's tilt, in degrees: positive falls toward the far end, negative rises. | `number` | `-6` |
| `parting` | How strongly the pointer parts the strands, a multiplier: none turns the parting off. | `number` | `1` |

## Accessibility

| Part | Behaviour |
| --- | --- |
| Screen readers | Decorative: the host is `aria-hidden`, holds no text, takes no pointer and is never focusable. The listeners on the parent are passive and stop nothing, so every control over the ribbon behaves as before. |
| Touch | A finger is ignored: it parts nothing, nothing is prevented and `touch-action` is left alone, so a scroll that starts over the region scrolls. |
| Reduced motion | Under `prefers-reduced-motion: reduce` one still frame of the closed ribbon is drawn and nothing moves; a preference that flips mid-flight stops the loop at once. Under `forced-colors: active` nothing is drawn. |
| Contrast | A strand is a hairline laid in the body's ink at well under full coverage, brightest in the middle of the bundle and faded at its edges and ends, so the text over it reads as it does on the plain surface. |
| No WebGL | Without WebGL nothing is drawn; a lost context is let go and rebuilt when the browser restores it. |
| Server | The empty canvas is rendered, absolutely positioned, so the browser's first draw shifts no layout. |

## 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/thread-lines` declares each default on its own element.

| Variable | Default | Declared on |
| --- | --- | --- |
| `--wr-thread-lines-accent` | `var(--wr-color-primary)` | `.wr-thread-lines` |
| `--wr-thread-lines-color` | `var(--wr-color-on-surface)` | `.wr-thread-lines` |

## See also

- [Flow Lines](https://ngwr.dev/animations/flow-lines) — A dense field of thin parallel lines behind a section's content that flows like silk: a slow, smooth noise field bends every line in long gentle waves, neighbours moving together, so the field reads like a surface or a contour map in a breeze.
- [Aurora](https://ngwr.dev/animations/aurora) — A curtain of light hanging from one edge of a section and reaching part of the way in, like an aurora: its free edge a slow broad wave, fine rays shimmering inside it, brightest just short of the free edge and fading toward the one it hangs from, its colour flowing across the width through three stops from the theme.
- [Motion](https://ngwr.dev/guides/tokens/motion) — Easing and duration tokens for your transitions and animations.
- [Alert](https://ngwr.dev/reference/components/alert) — An inline status message.
