Mantine Scene

Logo

@gfazioli/mantine-scene

A composable decorative background component for React applications built with Mantine. Layer gradients, glow blobs, dot grids, mesh gradients, and noise textures to create rich atmospheric backgrounds.

Installation

yarn add @gfazioli/mantine-scene

After installation import package styles at the root of your application:

import '@gfazioli/mantine-scene/styles.css';

You can import styles within a layer @layer mantine-scene by importing @gfazioli/mantine-scene/styles.layer.css file.

import '@gfazioli/mantine-scene/styles.layer.css';

Usage

Scene is a decorative background container that composes multiple visual layers: gradients, glow blobs, dot grids, mesh gradients, noise textures, star fields, shooting stars, snow, and auroras. Place it behind your content to create rich, atmospheric backgrounds.

The root element has aria-hidden="true" since it is purely decorative.

Fullscreen

Set fullscreen to cover the entire viewport with position: fixed. The scene sits behind all content at z-index: 0 by default (configurable via the zIndex prop).

See the fullscreen demo page →

Decorative background

Scene renders behind your content

Interactive easing
import { Box, Text } from '@mantine/core';
import { Scene } from '@gfazioli/mantine-scene';

function Demo() {
  return (
    <Box pos="relative" h={300} style={{ borderRadius: 'var(--mantine-radius-md)', overflow: 'hidden', background: 'var(--mantine-color-body)' }}>
      <Scene>
        <Scene.Gradient from="violet" fromOpacity={0.15} />
        <Scene.Glow color="violet" size={400} blur={120} opacity={0.3} top="20%" left="30%" />
        <Scene.DotGrid color="gray" opacity={0.3} spacing={24} />
        <Scene.Noise opacity={0.025} />
      </Scene>
      <Box pos="relative" style={{ zIndex: 1 }} p="xl">
        <Text size="xl" fw={700} c="white">Decorative background</Text>
        <Text c="dimmed" mt="sm">Scene renders behind your content</Text>
      </Box>
    </Box>
  );
}

Reduced Motion

Scene respects prefers-reduced-motion by default. Use the reducedMotion prop to control behavior:

  • 'auto' (default) — disables animations when the user has enabled reduced motion in their OS settings
  • 'always' — always disables all animations
  • 'never' — keeps animations regardless of user preference

Interactive

Set interactive on the root Scene to enable mouse tracking. Sub-components that support it will react to the cursor position automatically:

  • Scene.StarWarp — focal point follows the mouse
  • Scene.Glow — blob position follows the mouse
  • Scene.Gradient — radial/conic gradient center follows the mouse

When the mouse leaves the scene, components fall back to their default position props.

Responsive Props

Some props accept responsive objects following Mantine's breakpoint convention. This is useful for reducing particle counts on mobile or adjusting sizes across viewports.

Supported responsive props:

  • Scene.Glowsize, blur
  • Scene.DotGridspacing
  • Scene.StarFieldcount
  • Scene.StarWarpcount
  • Scene.Snowcount
import { Scene } from '@gfazioli/mantine-scene';

function Demo() {
  return (
    <Scene>
      {/* Fewer stars on small screens for performance */}
      <Scene.StarField count={{ base: 30, sm: 60, md: 100 }} />

      {/* Smaller glow on mobile */}
      <Scene.Glow
        color="violet"
        size={{ base: 200, md: 400 }}
        blur={{ base: 60, md: 120 }}
      />

      {/* Tighter dot grid on small screens */}
      <Scene.DotGrid color="gray" spacing={{ base: 16, md: 24 }} />
    </Scene>
  );
}

Plain number values continue to work as before — responsive objects are entirely opt-in.

Responsive props

base

Resize the browser to see stars, glow, and dot spacing adapt

import { Badge, Box, Group, Text } from '@mantine/core';
import { useMatches } from '@mantine/core';
import { Scene } from '@gfazioli/mantine-scene';

function Demo() {
  const breakpoint = useMatches({
    base: 'base',
    xs: 'xs',
    sm: 'sm',
    md: 'md',
    lg: 'lg',
    xl: 'xl',
  });

  return (
    <Box pos="relative" h={300} style={{ borderRadius: 'var(--mantine-radius-md)', overflow: 'hidden', background: 'var(--mantine-color-body)' }}>
      <Scene>
        <Scene.StarField
          count={{ base: 20, sm: 50, md: 100 }}
          twinkle
          opacity={0.7}
        />
        <Scene.Glow
          color="violet"
          size={{ base: 150, sm: 250, md: 400 }}
          blur={{ base: 40, sm: 80, md: 120 }}
          opacity={0.4}
          top="30%"
          left="40%"
        />
        <Scene.DotGrid
          color="gray"
          spacing={{ base: 14, sm: 20, md: 28 }}
          opacity={0.2}
          fade="radial"
        />
        <Scene.Noise opacity={0.02} />
      </Scene>
      <Box pos="relative" style={{ zIndex: 1 }} p="xl">
        <Group>
          <Text size="lg" fw={700} c="white">Responsive props</Text>
          <Badge variant="filled" color="violet" size="lg">{breakpoint}</Badge>
        </Group>
        <Text c="dimmed" mt="xs" size="sm">
          Resize the browser to see stars, glow, and dot spacing adapt
        </Text>
      </Box>
    </Box>
  );
}

Gradient

Scene.Gradient supports three types: radial (default), linear, and conic. Use from and to with Mantine theme colors, or pass raw CSS color stops via colors.

Set animate to enable rotate (hue-rotate) or pulse (opacity oscillation) animations.

From
To
From opacity
To opacity
Angle
Opacity
Duration
Pulse min opacity
Interactive easing
import { Scene } from '@gfazioli/mantine-scene';
import { Box } from '@mantine/core';

function Demo() {
  return (
    <Box pos="relative" h={300} style={{ borderRadius: 'var(--mantine-radius-md)', overflow: 'hidden', background: 'var(--mantine-color-body)' }}>
      <Scene>
        <Scene.Gradient from="violet" to="pink" />
      </Scene>
    </Box>
  );
}

Glow

Scene.Glow renders animated floating blobs. Each blob can be positioned, sized, and animated independently. Disable animation with animate={false}.

Choose between animation types: float (default), pulse (scale oscillation), and breathe (opacity oscillation). Use delay to stagger multiple blobs.

Color
Size
Blur
Opacity
Top
Left
Duration
Delay
Drift x
Drift y
Interactive easing
import { Scene } from '@gfazioli/mantine-scene';
import { Box } from '@mantine/core';

function Demo() {
  return (
    <Box pos="relative" h={350} style={{ borderRadius: 'var(--mantine-radius-md)', overflow: 'hidden', background: 'var(--mantine-color-body)' }}>
      <Scene>
        <Scene.Glow top={50} />
      </Scene>
    </Box>
  );
}

DotGrid

Scene.DotGrid creates a repeating dot pattern. Enable stagger for a honeycomb-like layout. Use fade to mask the edges (radial, top, bottom, or edges).

Color
Dot size
Spacing
Duration
Opacity
import { Scene } from '@gfazioli/mantine-scene';
import { Box } from '@mantine/core';

function Demo() {
  return (
    <Box pos="relative" h={300} style={{ borderRadius: 'var(--mantine-radius-md)', overflow: 'hidden', background: 'var(--mantine-color-body)' }}>
      <Scene>
        <Scene.DotGrid />
      </Scene>
    </Box>
  );
}

Mesh

Scene.Mesh overlays multiple radial gradients to simulate a mesh gradient effect. Define color stops with position and spread.

Set animate to enable a hue-rotate shift. Use blend to change the CSS mix-blend-mode.

Opacity
Duration
import { Scene } from '@gfazioli/mantine-scene';
import { Box } from '@mantine/core';

function Demo() {
  return (
    <Box pos="relative" h={300} style={{ borderRadius: 'var(--mantine-radius-md)', overflow: 'hidden', background: 'var(--mantine-color-body)' }}>
      <Scene>
        <Scene.Mesh
          stops={[
            { color: 'violet', position: '20% 20%', spread: 50 },
            { color: 'pink', position: '80% 30%', spread: 45 },
            { color: 'blue', position: '50% 80%', spread: 55 },
          ]}
        />
      </Scene>
    </Box>
  );
}

Noise

Scene.Noise adds a film grain texture via an inline SVG filter. Control the noise with seed, type, and octaves. Use tint with a Mantine theme color to overlay a color tint.

Opacity
Grain
Seed
Octaves
Tint
Tint opacity
import { Scene } from '@gfazioli/mantine-scene';
import { Box } from '@mantine/core';

function Demo() {
  return (
    <Box pos="relative" h={300} style={{ borderRadius: 'var(--mantine-radius-md)', overflow: 'hidden', background: 'var(--mantine-color-body)' }}>
      <Scene>
        <Scene.Noise opacity={0.15} tint="" />
      </Scene>
    </Box>
  );
}

StarField

Scene.StarField renders a CSS-only star field using box-shadow on a single element per layer. Stars are positioned deterministically via a PRNG seeded with the seed prop.

Enable twinkle for a staggered opacity animation across three layers.

Count
Color
Min size
Max size
Duration
Seed
Opacity
import { Scene } from '@gfazioli/mantine-scene';
import { Box } from '@mantine/core';

function Demo() {
  return (
    <Box pos="relative" h={300} style={{ borderRadius: 'var(--mantine-radius-md)', overflow: 'hidden', background: 'var(--mantine-color-body)' }}>
      <Scene>
        <Scene.StarField color="blue" />
      </Scene>
    </Box>
  );
}

StarWarp

Scene.StarWarp creates a classic hyperspace / warp speed effect where stars radiate from a configurable focal point. Stars scale up as they move outward, simulating depth and perspective.

Use direction to choose between flying away (out) or approaching (in). Move the focal point with focalX / focalY to shift the vanishing point. Enable trail for elongated streaks.

Count
Speed
Focal x
Focal y
Color
Min size
Max size
Opacity
Seed
Interactive easing
import { Scene } from '@gfazioli/mantine-scene';
import { Box } from '@mantine/core';

function Demo() {
  return (
    <Box pos="relative" h={400} style={{ borderRadius: 'var(--mantine-radius-md)', overflow: 'hidden', background: 'var(--mantine-color-body)' }}>
      <Scene>
        <Scene.StarWarp color="blue" />
      </Scene>
    </Box>
  );
}

ShootingStar

Scene.ShootingStar creates streaks of light that traverse the scene at a configurable angle. Each trail has a gradient from transparent to the chosen color.

Control count (number of lanes), trailLength, speed, and minInterval/maxInterval between appearances.

Count
Color
Trail length
Angle
Speed
Min interval
Max interval
Seed
Opacity
import { Scene } from '@gfazioli/mantine-scene';
import { Box } from '@mantine/core';

function Demo() {
  return (
    <Box pos="relative" h={300} style={{ borderRadius: 'var(--mantine-radius-md)', overflow: 'hidden', background: 'var(--mantine-color-body)' }}>
      <Scene>
        <Scene.ShootingStar count={50} color="blue" angle={150} minInterval={1} maxInterval={3} />
      </Scene>
    </Box>
  );
}

Snow

Scene.Snow renders falling snowflakes with horizontal drift. Each flake has a randomized size, speed, and position (deterministic via seed).

Use wind (-1 to 1) to bias the drift direction and speed as a multiplier on fall duration.

Count
Color
Min size
Max size
Speed
Drift
Wind
Opacity
import { Scene } from '@gfazioli/mantine-scene';
import { Box } from '@mantine/core';

function Demo() {
  return (
    <Box pos="relative" h={300} style={{ borderRadius: 'var(--mantine-radius-md)', overflow: 'hidden', background: 'var(--mantine-color-body)' }}>
      <Scene>
        <Scene.Snow color="blue" />
      </Scene>
    </Box>
  );
}

Aurora

Scene.Aurora creates shimmering aurora borealis bands. Each band is a blurred, colored rectangle with a wave animation.

Customize colors (Mantine theme colors), bands count, position (top/center/bottom), blur, and opacity.

Bands
Duration
Opacity
Blur
import { Scene } from '@gfazioli/mantine-scene';
import { Box } from '@mantine/core';

function Demo() {
  return (
    <Box pos="relative" h={300} style={{ borderRadius: 'var(--mantine-radius-md)', overflow: 'hidden', background: 'var(--mantine-color-body)' }}>
      <Scene>
        <Scene.Aurora />
      </Scene>
    </Box>
  );
}

Combined

All sub-components can be composed freely. Layer order follows DOM order.

All effects combined

StarField + Gradient + DotGrid + Mesh + Glow + ShootingStar + Noise

import { Box, Text } from '@mantine/core';
import { Scene } from '@gfazioli/mantine-scene';

function Demo() {
  return (
    <Box pos="relative" h={400} style={{ borderRadius: 'var(--mantine-radius-md)', overflow: 'hidden', background: 'var(--mantine-color-body)' }}>
      <Scene>
        <Scene.StarField count={80} twinkle duration={4} opacity={0.6} />
        <Scene.Gradient from="violet" fromOpacity={0.12} />
        <Scene.DotGrid color="gray" opacity={0.15} spacing={28} fade="edges" />
        <Scene.Mesh
          stops={[
            { color: 'blue', position: '15% 20%', spread: 45 },
            { color: 'teal', position: '85% 40%', spread: 40 },
            { color: 'violet', position: '50% 80%', spread: 50 },
          ]}
          opacity={0.3}
        />
        <Scene.Glow color="blue" size={500} opacity={0.2} top="20%" left="30%" duration={10} />
        <Scene.Glow color="teal" size={400} opacity={0.15} top="60%" left="70%" duration={14} />
        <Scene.ShootingStar count={2} opacity={0.4} />
        <Scene.Noise opacity={0.03} />
      </Scene>
      <Box pos="relative" style={{ zIndex: 1 }} p="xl">
        <Text size="xl" fw={700} c="white">All effects combined</Text>
        <Text c="dimmed" mt="sm">StarField + Gradient + DotGrid + Mesh + Glow + ShootingStar + Noise</Text>
      </Box>
    </Box>
  );
}