Skip to content

PlAnimateLighting

A light travelling around the outside of something. It draws attention with light rather than by moving anything, which is the only way this library has of saying "here" without also saying "and it moved".

React
tsx
import { PlAnimateLighting } from 'plass-ui';

<PlAnimateLighting size="lg" color="primary">
  <PlCard size="lg" title="Recommended">

  </PlCard>
</PlAnimateLighting>;
dart
import 'package:plass_ui/plass_ui.dart';

const PlAnimateLighting(
  size: PlassSize.lg,
  child: PlCard(size: PlassSize.lg, title: Text('Recommended'), child: Text('…')),
);

Props

PropTypeDefaultDescription
colorshared'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info''primary'Which family the light is drawn in. The arc turns between that family two ends as it travels, exactly as a solid fill does
glowstringA CSS colour, when a semantic family is not what is wanted. Overrides color, and the arc stops turning — one colour has nowhere to turn to
sizeshared'xs' | 'sm' | 'md' | 'lg' | 'xl''md'The radius the light follows. It has to match what is inside, or the glow will cut a corner the content has rounded off
spreadnumber3How far past the content the light reaches, in pixels
arcnumber50How much of the outline is lit at once, in degrees. Small is a travelling spark; large is a sweep
blurnumber5How soft the light is, in pixels. At 0 it is a hard-edged wedge, which reads as a graphic rather than as light
reversebooleanfalseRuns the light the other way round
durationsharednumber3000How long one run takes, in milliseconds. A number, never a CSS string
delaysharednumber0How long before it starts, in milliseconds
easingsharedstringthe house curveThe easing curve, written the way CSS writes it
repeatsharednumber | 'infinite''infinite'How many times it runs. 'infinite' rather than Infinity, because that word is what reaches CSS
alternatesharedbooleanfalseRuns every other pass backwards, so a repeat returns instead of jumping
pausedsharedbooleanfalseHolds the animation where it is
triggershared'mount' | 'visible' | 'hover' | 'manual''mount'What starts it: mount as soon as it is on the page, visible when it is scrolled into view, hover while the pointer or focus is on it, manual only when play says so
playsharedbooleanRuns it when trigger is manual. Each false → true starts it over
oncesharedbooleantrueWith trigger="visible", whether it runs only the first time. Off, it runs again every time the element comes back into view
thresholdsharednumber0.2With trigger="visible", how much of the element has to be on screen before it counts as visible, from 0 to 1
renderReactElement | (props, state) => ReactElementRenders something other than a <div>
PropTypeDefaultDescription
colorsharedPlassColorPlassColor.primaryWhich family the light is drawn in. The arc turns between that family two ends as it travels, exactly as a solid fill does
glowColor?A CSS colour, when a semantic family is not what is wanted. Overrides color, and the arc stops turning — one colour has nowhere to turn to
sizesharedPlassSizePlassSize.mdThe radius the light follows. It has to match what is inside, or the glow will cut a corner the content has rounded off
spreaddouble3How far past the content the light reaches, in pixels
arcdouble50How much of the outline is lit at once, in degrees. Small is a travelling spark; large is a sweep
blurdouble5How soft the light is, in pixels. At 0 it is a hard-edged wedge, which reads as a graphic rather than as light
reverseboolfalseRuns the light the other way round
durationsharedDurationDuration(milliseconds: 3000)How long one run takes, in milliseconds. A number, never a CSS string
delaysharedDurationDuration.zeroHow long before it starts, in milliseconds
curvesharedCurve?the house curveThe easing curve, written the way CSS writes it
repeatsharedint?nullHow many times it runs. null is what never stops: there is no 'infinite' to write, and -1 would be a sentinel a caller has to look up
alternatesharedboolfalseRuns every other pass backwards, so a repeat returns instead of jumping
pausedsharedboolfalseHolds the animation where it is
triggersharedPlassAnimateTriggerPlassAnimateTrigger.mountWhat starts it: mount as soon as it is on the page, visible when it is scrolled into view, hover while the pointer or focus is on it, manual only when play says so
playsharedboolfalseRuns it when trigger is manual. Each false → true starts it over
oncesharedbooltrueWith trigger="visible", whether it runs only the first time. Off, it runs again every time the element comes back into view
thresholdshareddouble0.2With trigger="visible", how much of the element has to be on screen before it counts as visible, from 0 to 1
child * WidgetWhat is lit

Every native <div> attribute passes straight through. color is excluded from the pass-through because it is a Plass prop here, and render swaps the element for another one.

glow is a Color? rather than a CSS colour string. spread and blur are doubles in logical pixels, and arc is degrees.

size has to agree with the radius of what is inside it. The glow follows the wrapper's own corners, so an lg card in an xs Lighting shows light poking out of four corners the card has already rounded away.

The light sits behind the content rather than on it, in a stacking context of its own, so nothing inside is altered or overlaid and the content stays exactly as legible as it was.

The ten shared settings — duration, delay, easing, repeat, alternate, paused, trigger, play, once, threshold — are the same on every PlAnimate* component, except that repeat defaults to 'infinite' here.

Examples

color

The arc turns between the two ends of the family as it travels, which is the same rule every filled surface in the library follows: a flat coloured arc would be paint, and nothing here is paint. glow takes one CSS colour instead when a semantic family is not what is wanted, and then there is nothing for the arc to turn to.

React

arc, blur and spread

How much of the outline is lit at once, how soft the light is, and how far past the content it reaches. A small arc is a spark running round an edge; a large one is a sweep. At blur={0} it stops being light and becomes a graphic.

React

Accessibility

  • Under prefers-reduced-motion the arc stops travelling and becomes an even glow. The decoration survives; the motion does not.
  • The light says nothing to a screen reader, and it should not have to. Whatever it is marking — the row that is processing, the plan being recommended — needs to be stated in the content as well.
  • One per screen. A page with three things glowing has no one thing that is live.
  • When the platform has animations turned off (MediaQuery.disableAnimations) the arc stops travelling and becomes an even glow. The decoration survives; the motion does not.
  • The light says nothing to a screen reader, and it should not have to. Whatever it is marking needs to be stated in the content as well.
  • One per screen. A screen with three things glowing has no one thing that is live.

Differences from the React build

ReactFlutterWhy
glow as a CSS colour stringColor?The framework already has the type.
a conic gradient on a ::beforea SweepGradient on a Positioned layer behind the childThere are no pseudo-elements. The layer is first in a Stack with clipBehavior: Clip.none, so the glow reaches past the content and still sits under it.
@property on the angle so the from is animatableGradientRotation on the sweepWhat moves is the gradient's own rotation, not the layer's — rotating the layer would swing its corners out past the content on every quarter turn, which is the same reason the CSS animates the angle rather than the element.
filter: blur()ImageFilteredThe framework's own name for the same filter.
renderFlutter has no polymorphic element.
duration, delay in millisecondsDurationThe framework already has the type.
easing as a CSS stringcurve, a CurveDart's own name for the same thing.
repeat: number | 'infinite'int?, null never stopsThere is no 'infinite' to write, and -1 would be a sentinel a caller has to look up.
trigger="visible" via IntersectionObserverwatches the nearest ScrollableThere is no observer here; with no scrollable above it there is nothing to watch, so it runs.
prefers-reduced-motionMediaQuery.disableAnimationsThe platform's own signal.
className, styleThere is no class list and no style attribute to pass through.

Released under the MIT License