Skip to content

Components / Backgrounds

Fluted Glass

Reeded glass over whatever is behind it: the box's backdrop blurred and lightly brightened, cut into vertical flutes (or flutes at an angle), each with a light edge and a shade, as a pane of fluted glass bends what is behind it into strips. Put it in a positioned box over a picture, a video or a gradient, and give it the flute's width and how far it blurs. Without backdrop-filter the flutes are drawn over the content as they are, unblurred. Decoration, hidden from a screen reader; it never takes a click. Zero JavaScript.

Live demo

Live, from the library itself: scroll, hover, navigate away and back. The demo is never gated.

Measured

JavaScript of its own

0 B

CSS 4.6 kB raw including the base tokens. Measured from a production build, and again in CI for every change that can move it.

Browser support

Baseline · widely available

Chrome 76 · Firefox 103 · Safari 18; needs backdrop-filter. Elsewhere: without backdrop-filter the flutes are drawn unblurred.

Install

One command adds the integration, the base tokens and every component. Then import what you use.

npx astro add moonarc
pnpm astro add moonarc
bunx astro add moonarc
src/pages/index.astro
---
import FlutedGlass from '@moonarc/core/FlutedGlass';
---
<figure style="position: relative">
  <img src="/studio.webp" alt="The studio at dusk" width="1600" height="1000" />
  <FlutedGlass width="28px" blur="8px" angle={8} />
</figure>
Copy it into your project instead (shadcn registry)

Owns the file, no dependency. The registry item also installs the base tokens.

terminal
npx shadcn@latest add https://moonarc.dev/r/fluted-glass.json

The CLI needs a components.json and the @/* alias, which Setup has. The file lands in src/components/moonarc/.

Source

The whole component. Self-contained styles in a cascade layer so your classes always win; it imports nothing.

FlutedGlass.astro
---
/**
 * FlutedGlass — reeded glass over whatever is behind it: the box's backdrop blurred and lightly brightened, cut into
 * vertical flutes (or flutes at an angle), each with a light edge and a shade, as a pane of fluted glass bends what is
 * behind it into strips. Put it in a positioned box over a picture, a video or a gradient; give it the flute's width and
 * how much it blurs. Without backdrop-filter the flutes are drawn over the content as they are, unblurred.
 * Decoration, hidden from a screen reader; it never takes a click. Zero JavaScript.
 */
import type { HTMLAttributes } from 'astro/types';

interface Props extends HTMLAttributes<'div'> {
  /** The flutes' width, a CSS length. */
  width?: string;
  /** How far the backdrop blurs, a CSS length. */
  blur?: string;
  /** The flutes' angle, in degrees: 0 for upright. */
  angle?: number;
  /** How strong the flutes' light and shade are, 0 to 1. */
  strength?: number;
}

const { width = '22px', blur = '6px', angle = 0, strength = 0.5, class: className, style, ...rest } = Astro.props;
const vars = [`--ma-flute-width:${width}`, `--ma-flute-blur:${blur}`, `--ma-flute-angle:${angle}deg`, `--ma-flute-strength:${strength}`, typeof style === 'string' ? style : ''].filter(Boolean).join(';');
---

<div class:list={['ma-flute', className]} aria-hidden="true" style={vars} {...rest}></div>

<style is:global>
  @layer components {
    :where(.ma-flute) {
      --ma-flute-width: 22px;
      --ma-flute-angle: 0deg;
      --ma-flute-strength: 0.5;
      position: absolute;
      inset: 0;
      z-index: 1;
      pointer-events: none;
      -webkit-backdrop-filter: blur(var(--ma-flute-blur, 6px)) brightness(1.04) saturate(1.1);
      backdrop-filter: blur(var(--ma-flute-blur, 6px)) brightness(1.04) saturate(1.1);
      /* each flute: a light edge, the clear middle, a shade before the next */
      background: repeating-linear-gradient(
        calc(90deg + var(--ma-flute-angle, 0deg)),
        rgb(255 255 255 / calc(0.32 * var(--ma-flute-strength))) 0,
        rgb(255 255 255 / calc(0.06 * var(--ma-flute-strength))) calc(var(--ma-flute-width) * 0.18),
        transparent calc(var(--ma-flute-width) * 0.45),
        rgb(0 0 0 / calc(0.1 * var(--ma-flute-strength))) calc(var(--ma-flute-width) * 0.82),
        rgb(0 0 0 / calc(0.22 * var(--ma-flute-strength))) var(--ma-flute-width)
      );
    }
    /* prefers-reduced-motion: nothing here moves */
  }
</style>

Props

PropTypeDefaultDescription
widthstring'22px'The flutes' width.
blurstring'6px'How far the backdrop blurs.
anglenumber0The flutes' angle in degrees; 0 is upright.
strengthnumber0.5How strong the light edges and the shades are, 0 to 1.

Reduced motion

Nothing in it moves.

With ClientRouter

No script: it works on any page however it was reached.

Why it is built this way

Replaces: Fluted Glass (Cult UI) · Fluted glass shader (shaderscn). See the migration table.

A glass pane that blurs and tints whatever is behind it.

Chrome 76 · not Firefox · not Safarihover

Noise

Backgrounds

Film grain over a gradient or image, blended and flickering at 12 fps.

every browseralways

Progressive Blur

Backgrounds

An edge that blurs what scrolls under it, more the closer it gets: a few bands, each with a backdrop blur twice the last and a mask that keeps it to its own slice, so the blur ramps instead of stepping.

every browseralways

Also: view transitions · how costs are measured · browser support · accessibility policy · five-minute setup · MCP for agents · this page as Markdown