Vignette

Updated: Sep 24, 2026
Use Vignette to soften selected edges of full-bleed imagery, custom media, or scroll-adjacent content that has no existing edge treatment. The gradients let content dissolve into the surrounding background instead of ending abruptly. Prefer a component’s built-in scrim or fading edge when available, because that component already coordinates the effect with its content and behavior.
Vignette draws one independent gradient on each side. The caller controls which edges are visible and whether each visibility change animates.

Usage

The vignette fills its nearest positioned ancestor. Give the owning region a positioning context and render Vignette as a sibling of the visual content so it covers the intended area:
import { Vignette, VignetteEdge } from '@wearables-ui-toolkit/mrbd';

export function MediaFrame({ photo }: { photo: Photo }) {
  return (
    <div className="media-frame">
      <img className="media-frame-image" src={photo.src} alt={photo.description} />
      <Vignette
        enabledEdges={{
          [VignetteEdge.TOP]: false,
          [VignetteEdge.BOTTOM]: true,
          [VignetteEdge.LEFT]: true,
          [VignetteEdge.RIGHT]: true,
        }}
      />
    </div>
  );
}
.media-frame {
  position: relative;
  overflow: hidden;
}

.media-frame-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
Edges can follow application state independently. Consider disabling animation for edges that should change immediately:
import { Vignette, VignetteEdge } from '@wearables-ui-toolkit/mrbd';
import type { VignetteEdgeVisibility } from '@wearables-ui-toolkit/mrbd';

interface ScrollFadeProps {
  hasScrolledDown: boolean;
  canScrollFurther: boolean;
}

export function ScrollFade({ hasScrolledDown, canScrollFurther }: ScrollFadeProps) {
  const scrolledEdges: VignetteEdgeVisibility = {
    [VignetteEdge.TOP]: hasScrolledDown,
    [VignetteEdge.BOTTOM]: canScrollFurther,
    [VignetteEdge.LEFT]: false,
    [VignetteEdge.RIGHT]: false,
  };

  return (
    <Vignette
      enabledEdges={scrolledEdges}
      animatedEdges={{ [VignetteEdge.LEFT]: false, [VignetteEdge.RIGHT]: false }}
    />
  );
}

Props

The complete prop type is VignetteProps. It extends the standard div attributes and spreads every prop not listed below onto the root element. The component forwards its ref to that root.
PropTypeDefaultDescription
enabledEdges
VignetteEdgeVisibility
all edges enabled
Controls which edge gradients are visible. Omitted edges remain enabled.
animate
boolean
true
Controls whether visibility changes fade for each edge without an override.
animatedEdges
Partial<Record<VignetteEdge, boolean>>
—
Per-edge override for animate. Omitted edges use the animate value.
className
string
''
Class added to the root.
style
CSSProperties
—
Inline styles applied to the root after the component’s custom properties.

Types and enums

VignetteEdge defines the four supported edge values:
  • TOP — 'top'
  • BOTTOM — 'bottom'
  • LEFT — 'left'
  • RIGHT — 'right'
These member values are the keys for enabledEdges and animatedEdges. Index both maps with VignetteEdge rather than bare strings so the keys remain aligned with the public type.
VignetteEdgeVisibility is Partial<Record<VignetteEdge, boolean>>. Each entry controls whether that edge is shown, and every omitted edge is shown.
animatedEdges has the same shape. Its entries control whether an edge animates, and every omitted entry falls back to animate.

Behavior

Layout. The root is absolutely positioned across all four insets of its nearest positioned ancestor and clips its own overflow. It does not contribute to layout. The owning region must establish a positioning context, typically with position: relative; otherwise, the vignette resolves against a more distant ancestor and covers the wrong area.
Edges. The component always renders all four gradients in top, bottom, left, right order. Disabling an edge hides it rather than removing it, allowing the edge to fade back in without changing layout. Each gradient is a fixed-depth band from var(--uit-color-background-window) at the outer edge to var(--uit-color-background-flat), which is transparent, at the inner edge.
Color. The gradient resolves to the window background semantic token, which is unlit by default. On the additive-light display, an unlit pixel emits no light, so the vignette removes light from covered content rather than tinting it. A Scrim uses a fixed black gradient; Vignette instead follows --uit-color-background-window. Repointing that role therefore keeps the fade aligned with the surface it meets.
Motion. When animation is enabled for an edge, a visibility change crossfades its opacity and visibility over a short fixed duration. animate sets the default for all edges, and animatedEdges overrides individual edges. This supports animated scroll-facing edges and immediate static edges. Consider animate={false} when edges never change or when the owning region already animates, avoiding competing motion.
Reduced motion.Vignette does not read the user’s motion preference. The application owns that preference and should call usePrefersReducedMotion, then pass animate={false} when reduced motion is requested. Edges then change immediately instead of crossfading.
Interaction. The root and each gradient ignore pointer events, allowing clicks, presses, and hover to reach the underlying content. The vignette is not a focus target and does not intercept directional navigation.
Stacking. The gradients paint above ordinary in-flow children of the parent. The application must position foreground text and controls with a higher z-index so they remain visible, readable, and interactive above the vignette.

Accessibility

  • The root has aria-hidden, keeping this decorative treatment out of the accessibility tree.
  • Meaningful content must remain in an accessible sibling above the vignette; the component renders no accessible information of its own.
  • Meaningful underlying media must provide appropriate alternative text so users receive the media’s meaning rather than a description of the decorative fade.
  • Pointer events pass through the vignette, so it does not interfere with hit testing or focus-based directional navigation between controls.
  • An edge fade must never be the only indication of a state such as more content below. Pair it with an announced signal so the same information reaches users who cannot perceive the fade.
  • Establish a positioning context on the owning region so the vignette covers only that region.
  • Enable only the edges that need contrast or a visual transition into the window background.
  • Keep foreground text and controls positioned above the vignette so they remain readable and interactive.
  • Disable animation on edges that remain permanently visible or hidden.

Avoid

  • Avoid duplicating an edge effect already owned by a card, carousel, or scroll view. Prefer the component-owned effect for consistent state and motion.
  • Prefer the appropriate page surface or material instead of using Vignette to darken an entire page or simulate a material.
  • Do not pass content as children. Vignette renders only its four edge gradients, so children are discarded and never appear in the DOM.
  • Do not rely on the vignette as the only state indication; provide an accessible signal with the same meaning.