Exclusivebeta

Backdrop .mv-backdrop

Hero and section backgrounds from one class and zero JavaScript: glow, mesh, aurora-still, grid-fade, dots-fade, rays, topo and grain presets, combinable (data-backdrop="mesh grain") and built from the brand tokens, so they follow light, dark, [data-theme] subtrees and the app's accent hue. What others miss: it paints behind the content and never takes pointer events, needs no network, canvas or blur filter, stays still unless asked, offers a scrim that fades the backdrop toward the section's own background behind the copy, and falls back to that plain background in print and forced colors.

CategoryBackgrounds
TypeCSS only (.mv-backdrop)
Statusbeta
Keywordsexclusive, light, background, hero, gradient, mesh, glow, grid, dots, rays, topographic, grain, aurora, css, theme-aware

When to use

  • A landing page hero needs a brand-tinted background that looks right in light and dark without extra assets
  • Feature, pricing or signup sections need distinct but related backgrounds that stay cheap on mobile
  • A docs or changelog header wants a quiet grid, dot or contour texture that fades out behind the title
  • Headline and CTA must stay readable over a colorful background, including in print and high contrast modes

Avoid when

  • The background itself should be a continuously moving, fluid centerpiece → use Mesh Gradient instead
  • Grid cells should react to the cursor with trails and click ripples → use Grid Pattern instead
  • Grain must sit above the content as an overlay texture rather than behind it → use Noise instead

Install

node scripts/add.mjs backdrop --out ./src/marvelous

AI agent with the Marvelous UI MCP server: install_components({ slugs: ["backdrop"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).

Files copied (dependencies included): tokens/tokens.css, core/base.css, components/backdrop/backdrop.css.

Usage

Quick start, the smallest working markup:

<section class="mv-backdrop" data-backdrop="mesh grain">…</section>

Canonical markup, to start from and customize with attributes, data-* and CSS variables:

<div id="bd-demo">
  <style>
    #bd-demo { display: grid; gap: 1.25rem; width: 100%; max-width: 72rem; margin: auto; }
    #bd-demo .bdd-bar { display: flex; flex-wrap: wrap; gap: .75rem 1.5rem; align-items: center; justify-content: center; }
    #bd-demo mv-toggle-group { flex-wrap: wrap; justify-content: center; max-width: 100%; }
    #bd-demo .bdd-field { display: inline-flex; gap: .6rem; align-items: center; font-size: var(--mv-text-sm); color: var(--mv-fg-muted); }
    #bd-demo .bdd-field input[type="range"] { width: 8rem; }
    #bd-demo .bdd-twins { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr)); gap: 1rem; }
    #bd-demo .bdd-hero { display: grid; align-content: center; justify-items: center; gap: .9rem; min-height: 21rem; padding: 2.5rem 1.5rem; text-align: center; border: 1px solid var(--mv-border); border-radius: var(--mv-radius-2xl); background: var(--mv-bg); color: var(--mv-fg); }
    #bd-demo .bdd-hero h2 { margin: 0; max-width: 17ch; font: var(--mv-weight-bold) var(--mv-text-4xl)/var(--mv-leading-tight) var(--mv-font-display); letter-spacing: var(--mv-tracking-tight); text-wrap: balance; }
    #bd-demo .bdd-hero p { margin: 0; max-width: 34ch; color: var(--mv-fg-muted); line-height: var(--mv-leading-normal); text-wrap: pretty; }
    #bd-demo .bdd-cta { display: flex; flex-wrap: wrap; gap: .6rem; justify-content: center; margin-top: .35rem; }
    #bd-demo .bdd-fine { font-size: var(--mv-text-xs); color: var(--mv-fg-subtle); }
    #bd-demo .bdd-tag { position: absolute; top: .85rem; left: 1rem; font: 500 var(--mv-text-xs)/1 var(--mv-font-mono); color: var(--mv-fg-subtle); }
    #bd-demo .bdd-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 16rem), 1fr)); gap: 1rem; }
    #bd-demo .bdd-tile { display: grid; align-content: end; gap: .4rem; min-height: 12.5rem; padding: 2.5rem 1.25rem 1.25rem; border: 1px solid var(--mv-border); border-radius: var(--mv-radius-xl); background: var(--mv-bg); }
    #bd-demo .bdd-tile h3 { margin: 0; font: var(--mv-weight-semibold) var(--mv-text-lg)/var(--mv-leading-snug) var(--mv-font-display); letter-spacing: -.01em; text-wrap: balance; }
    #bd-demo .bdd-tile p { margin: 0 0 .5rem; font-size: var(--mv-text-sm); color: var(--mv-fg-muted); }
    #bd-demo .bdd-tile .mv-button { justify-self: start; }
  </style>

  <div class="bdd-bar">
    <mv-toggle-group id="bd-demo-presets" type="multiple" value="mesh,grain" data-size="sm" aria-label="Backdrop presets">
      <button class="mv-toggle" value="glow">glow</button>
      <button class="mv-toggle" value="mesh">mesh</button>
      <button class="mv-toggle" value="aurora-still">aurora-still</button>
      <button class="mv-toggle" value="grid-fade">grid-fade</button>
      <button class="mv-toggle" value="dots-fade">dots-fade</button>
      <button class="mv-toggle" value="rays">rays</button>
      <button class="mv-toggle" value="topo">topo</button>
      <button class="mv-toggle" value="grain">grain</button>
    </mv-toggle-group>
    <label class="bdd-field"><input type="checkbox" role="switch" class="mv-switch" id="bd-demo-drift"> Drift</label>
    <label class="bdd-field">Scrim <input type="range" class="mv-slider" id="bd-demo-scrim" min="0" max="0.9" step="0.05" value="0"></label>
  </div>

  <div class="bdd-twins">
    <section class="mv-backdrop bdd-hero" data-theme="light" data-backdrop="mesh grain" aria-labelledby="bd-demo-h-light">
      <span class="bdd-tag" aria-hidden="true">light</span>
      <span class="mv-badge" data-variant="outline">New · Multi-currency ledgers</span>
      <h2 id="bd-demo-h-light">Close the books in minutes, not days</h2>
      <p>Match every invoice across 40+ currencies and hand your accountant a clean ledger each month.</p>
      <div class="bdd-cta">
        <a class="mv-button" data-size="lg" href="#backdrop">Start free trial</a>
        <a class="mv-button" data-size="lg" data-variant="outline" href="#backdrop">Book a demo</a>
      </div>
      <span class="bdd-fine">From $29/month · No credit card required</span>
    </section>
    <section class="mv-backdrop bdd-hero" data-theme="dark" data-backdrop="mesh grain" aria-labelledby="bd-demo-h-dark">
      <span class="bdd-tag" aria-hidden="true">dark</span>
      <span class="mv-badge" data-variant="outline">New · Multi-currency ledgers</span>
      <h2 id="bd-demo-h-dark">Close the books in minutes, not days</h2>
      <p>Match every invoice across 40+ currencies and hand your accountant a clean ledger each month.</p>
      <div class="bdd-cta">
        <a class="mv-button" data-size="lg" href="#backdrop">Start free trial</a>
        <a class="mv-button" data-size="lg" data-variant="outline" href="#backdrop">Book a demo</a>
      </div>
      <span class="bdd-fine">From $29/month · No credit card required</span>
    </section>
  </div>

  <div class="bdd-grid">
    <section class="mv-backdrop bdd-tile" data-backdrop="glow">
      <code class="bdd-tag">glow</code>
      <h3>Ship your side project this weekend</h3>
      <p>Hosting, auth and payments in one place.</p>
      <a class="mv-button" data-size="sm" href="#backdrop">Get started</a>
    </section>
    <section class="mv-backdrop bdd-tile" data-backdrop="mesh">
      <code class="bdd-tag">mesh</code>
      <h3>Payments that cross every border</h3>
      <p>Accept 135 currencies, settle in yours.</p>
      <a class="mv-button" data-size="sm" href="#backdrop">Open an account</a>
    </section>
    <section class="mv-backdrop bdd-tile" data-backdrop="aurora-still">
      <code class="bdd-tag">aurora-still</code>
      <h3>Sleep better, starting tonight</h3>
      <p>Guided wind-downs from Dr. Amara Okafor.</p>
      <a class="mv-button" data-size="sm" href="#backdrop">Try 7 nights free</a>
    </section>
    <section class="mv-backdrop bdd-tile" data-backdrop="grid-fade">
      <code class="bdd-tag">grid-fade</code>
      <h3>Infrastructure that scales with you</h3>
      <p>Deploy to 30 regions with one command.</p>
      <a class="mv-button" data-size="sm" href="#backdrop">Read the docs</a>
    </section>
    <section class="mv-backdrop bdd-tile" data-backdrop="dots-fade">
      <code class="bdd-tag">dots-fade</code>
      <h3>Notes that link themselves</h3>
      <p>Backlinks, daily pages and offline sync.</p>
      <a class="mv-button" data-size="sm" href="#backdrop">Download for Mac</a>
    </section>
    <section class="mv-backdrop bdd-tile" data-backdrop="rays">
      <code class="bdd-tag">rays</code>
      <h3>Version 4.0 is here</h3>
      <p>Faster builds, a new editor and 60+ fixes.</p>
      <a class="mv-button" data-size="sm" href="#backdrop">See what’s new</a>
    </section>
    <section class="mv-backdrop bdd-tile" data-backdrop="topo">
      <code class="bdd-tag">topo</code>
      <h3>Trails and offline maps for 90 parks</h3>
      <p>Elevation, water sources and campsites.</p>
      <a class="mv-button" data-size="sm" href="#backdrop">Plan a hike</a>
    </section>
    <section class="mv-backdrop bdd-tile" data-backdrop="glow grain">
      <code class="bdd-tag">glow grain</code>
      <h3>The design journal, issue 12</h3>
      <p>Essays by Sofia Lindqvist and Kenji Mori.</p>
      <a class="mv-button" data-size="sm" href="#backdrop">Read issue</a>
    </section>
  </div>

  <script type="module">
    const root = document.getElementById("bd-demo");
    const heroes = root.querySelectorAll(".bdd-hero");
    const set = (fn) => heroes.forEach(fn);
    root.querySelector("#bd-demo-presets").addEventListener("mv-change", (e) => {
      const value = e.detail.value.join(" ") || "none";
      set((h) => h.setAttribute("data-backdrop", value));
    });
    root.querySelector("#bd-demo-drift").addEventListener("change", (e) => {
      set((h) => e.target.checked ? h.setAttribute("data-backdrop-motion", "drift") : h.removeAttribute("data-backdrop-motion"));
    });
    root.querySelector("#bd-demo-scrim").addEventListener("input", (e) => {
      set((h) => h.style.setProperty("--mv-backdrop-scrim", e.target.value));
    });
  </script>
</div>

API

Attributes

NameTypeDefaultDescription
data-backdropglow | mesh | aurora-still | grain | grid-fade | dots-fade | rays | topo | none (space-separated)glowPresets, combinable: one or more color presets (glow, mesh, aurora-still), grain, and pattern presets (grid-fade, dots-fade, rays). topo owns the pattern layer and does not combine with the other patterns. none paints nothing.
data-backdrop-motiondriftVery slow drift of the color field (transform only, 36 s per cycle, alternating). Clips the element's overflow (overflow: clip). Stops under reduced motion.

Content structure

NameDescription
(content)Any content; it always paints above the backdrop.

CSS classes

NameDescription
mv-backdropPut it on the section itself (hero, card, header, body). It paints in ::before (color field) and ::after (pattern) behind the content; makes the element positioned (relative) and isolated. Alone, it shows the glow preset.

CSS variables

NameDefaultDescription
--mv-backdrop-intensity1Overall strength, 0 to 1 (opacity of both layers).
--mv-backdrop-hue0Hue shift from the brand accent, in degrees without a unit (e.g. 40 or -120).
--mv-backdrop-x / -yper presetFocus point: main light of glow, mesh and aurora-still, origin of rays, center of the grid, dots and topo fade (e.g. 50% 0%).
--mv-backdrop-scale1Pattern scale: grid cell (44px), dot spacing (22px), contour tile (640px).
--mv-backdrop-scrim0Text protection, 0 to 1: inside the scrim area the backdrop fades toward the element's own background by this much (a mask, so the scrim color is always the real section background, in either theme).
--mv-backdrop-scrim-area58% 56% at 50% 50%Ellipse radii and center of the scrim; move it under left-aligned copy (e.g. 55% 70% at 25% 50%).
--mv-backdrop-color-1 / -2 / -3accent, accent +50°, accent -100° (alpha included)Override the three colors of the color presets and rays.
--mv-backdrop-linefg tinted by accent, 13%Line and dot color of grid-fade and dots-fade.
--mv-backdrop-speed36sDuration of one drift cycle.

Accessibility

Purely decorative: pseudo-elements with no content, z-index -1 inside an isolated stacking context, pointer-events: none, so they never cover text, links or controls and never intercept the pointer; nothing is announced. The color presets are tuned and measured so fg-muted body text keeps about 4.5:1 or better in both themes, and --mv-backdrop-scrim fades the backdrop toward the element's real background behind the copy, in either theme, without guessing a scrim color. No motion by default; data-backdrop-motion="drift" animates transform only and stops under prefers-reduced-motion and [data-motion="reduce"]. prefers-contrast: more quiets the backdrop to 40%. Forced colors and print remove it, leaving the element's own plain background. Zero JavaScript and zero network: the grain and contour textures are inline SVG data URIs.