import { Component } from '@angular/core';
import { WrFlowLines } from 'ngwr/animations/flow-lines';
@Component({ imports: [WrFlowLines] })
export class MyComponent {}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. The pointer is a soft weight on it: nearby lines are pushed away, take on the accent where they bend the most and spring back with a small bounce when it moves on, so a moving pointer leaves a short living wake. A click sends a ring out across the field. The hero of this site runs it.
Installation
Import the class, and @use the entry point in your global styles. Nothing warns you when the @use is missing.
Runs on the GPU
Basic
Put <wr-flow-lines /> first inside the region it should fill. The drawing is one WebGL pass: every pixel measures its distance to the nearest line of the bent field, so a line is a crisp anti-aliased hairline and a curve is smooth whatever bends it, never a chain of segments. 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 drift moves, the screen's rate while a wake, a ripple or the intro is live. The parent takes the pointer, passively; a moving finger is ignored, so a scroll over the section is never fought, and a tap may ripple.
<section class="hero">
<wr-flow-lines />
<h1>Angular components that bind straight to Signal Forms.</h1>
</section>The parent
The host is absolute, inset: 0, at z-index: -1, under no pointer, and the field fades out softly at the top and the bottom; the sides run on to the edge. 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.
.hero {
position: relative;
isolation: isolate;
}Calmer
spacing is the distance between two lines, amplitude how far the drift bends a line at most, both in px, speed how fast the drift moves and push how hard the pointer pushes; none turns the push off. Wider lines, a lower and slower wave and a lighter touch is a field that barely stirs.
<wr-flow-lines spacing="14" amplitude="16" speed="0.5" push="0.6" />Vertical
orientation set to vertical runs the lines from top to bottom. Everything else, the drift, the pointer's push and the ripple, works the same way; twist tilts from whichever way they run.
<wr-flow-lines orientation="vertical" />Dense, with a twist
Closer lines, a finer lineWidth (px) and a twist (degrees; positive falls toward the inline end, mirrored under RTL) is a sheet of silk rather than a ruled page; a higher amplitude bends it further. The canvas renders under a pixel budget of its own, so a wide section is never a wide texture, and the cost is per pixel, not per line.
<wr-flow-lines spacing="7" lineWidth="0.75" twist="-8" amplitude="36" />Dashed
dashed cuts every line into dashes that stream along it, each line starting its dashes at a place of its own, so the field reads as a current rather than a grid. speed moves the dashes as it moves the drift.
<wr-flow-lines dashed speed="1.4" />The push and the ripple
reach is how far from the pointer a line is still pushed, in px, with a smooth falloff to nothing at the edge; push is how far it moves them, a seventh of reach at one, nothing right at the pointer and the most about halfway out, so the lines part round it like water round a stone; bounce is how far a released line swings past its place before it settles, none settling without a swing. A click sends a ring out across the field, lifting the lines as it passes and tinting its crest; ripple turns it off.
<wr-flow-lines ripple="false" />Intro
intro draws the lines in from the inline start the first time the host is on screen, each line a little behind the one before, over about a second and a half. Under reduced motion the field is whole from the first frame.
<wr-flow-lines intro />Colours
A line is --wr-flow-lines-color, the muted ink by default, and a pushed line and a ripple's crest blend toward --wr-flow-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 dark one the lines lay a little more light than they lay ink on a light one, so the field weighs the same under both schemes.
<wr-flow-lines style="--wr-flow-lines-color: var(--wr-color-info); --wr-flow-lines-accent: var(--wr-color-warning)" />API
| Name | Description | Type | Default |
|---|---|---|---|
spacing | Between two lines, in CSS px, four at least. | number | 10 |
lineWidth | How thick a line is, in CSS px. | number | 1 |
amplitude | How far the drift bends a line at most, in CSS px. | number | 28 |
orientation | Which way the lines run across the region. | WrFlowLinesOrientation | 'horizontal' |
twist | The lines' tilt from their orientation, in degrees: positive falls toward the inline end. | number | 0 |
speed | How fast the drift and the dashes move, a multiplier: none holds the field still between pushes. | number | 1 |
reach | How far from the pointer a line is still pushed, in CSS px. | number | 160 |
push | How hard the pointer pushes, a multiplier: one moves a line a seventh of reach at most; none turns the push off. | number | 1 |
bounce | How much a released line swings past its place, a multiplier: none settles without a swing. | number | 1 |
ripple | Whether a click, or a tap, sends a ring out across the field. | boolean | true |
intro | Whether the lines draw in from the inline start when first on screen. | boolean | false |
dashed | Whether the lines are dashed, with the dashes streaming along them. | boolean | false |
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 field behaves as before. |
| Touch | A moving finger pushes nothing, nothing is prevented and touch-action is left alone, so a scroll that starts over the region scrolls. A tap sends a ripple, as a click does. |
| Reduced motion | Under prefers-reduced-motion: reduce one still frame of the whole field is drawn, with no push, no ripple and no intro, and nothing moves; a preference that flips mid-flight stops the loop at once. Under forced-colors: active nothing is drawn. |
| Contrast | A line is a hairline laid in the muted ink at well under full coverage, so the text over it reads as it does on the plain surface; the accent shows only where a line is pushed. |
| 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/flow-lines declares each default on its own element.
| Variable | Default | Declared on |
|---|---|---|
--wr-flow-lines-accent | var(--wr-color-primary) | .wr-flow-lines |
--wr-flow-lines-color | var(--wr-color-on-surface-muted) | .wr-flow-lines |
See also
Animation Thread LinesA 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.Animation Dot GridAn even field of small, faint dots behind a section's content.Guide MotionEasing and duration tokens for your transitions and animations.Component AlertAn inline status message.