Motion & effects · Original
Mesh gradient
A full-bleed, layered CSS mesh with readable type and one explicit play/pause control.
Live preview
Try it in context
Customize preview
Source delivery
v0.3.0Usage & source
"use client";
import type { CSSProperties } from "react";
import { MeshGradient } from "./components/stepkit/MeshGradient";
import "./components/stepkit/styles.css";
import "./components/stepkit/showcase.css";
const themeStyle = {"--stepkit-bg":"#111217","--stepkit-surface":"#1b1c24","--stepkit-surface-muted":"#22232d","--stepkit-fg":"#f6f5fb","--stepkit-muted":"#a4a6b4","--stepkit-border":"#343641","--stepkit-border-strong":"#565866","--stepkit-code":"#20212a","--stepkit-shadow":"0 24px 70px rgba(0, 0, 0, .34)","--stepkit-accent":"#6950df","--stepkit-accent-strong":"#5137c7","--stepkit-accent-soft":"#302760","--stepkit-accent-on-soft":"#c8bcff","--stepkit-accent-text":"#ffffff","--stepkit-error":"#ff9ba9","--stepkit-placeholder":"#b8bac6","--stepkit-radius":"28px","--stepkit-space":"16px","--stepkit-control-height":"44px","--stepkit-font-sans":"Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif"} as CSSProperties;
export default function Example() {
return (
<div className="stepkit-theme" data-stepkit-preset="modern" style={themeStyle}>
<MeshGradient
mode={"dark"}
defaultPlaying={false}
/>
</div>
);
}Dependencies
[email protected] [email protected]
/*
* Stepkit 0.3.0
* Copyright (c) 2026 Vladislav Stepanov
*
* Original Stepkit portions in this release are licensed under the MIT License. Adapted portions identify their upstream licence and attribution in the copied source and in the entry-specific upstream notice files.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
"use client";
import { useEffect, useId, useState, type CSSProperties } from "react";
import "./styles.css";
import "./showcase.css";
export type MeshGradientMode = "light" | "dark";
export interface MeshGradientProps {
palette?: [string, string, string, string] | string[];
mode?: MeshGradientMode;
title?: string;
body?: string;
defaultPlaying?: boolean;
playing?: boolean;
onPlayingChange?: (playing: boolean) => void;
className?: string;
}
const defaultPalette: [string, string, string, string] = ["#ff5c78", "#ffb347", "#50dbc2", "#5751ff"];
function PlayIcon({ playing }: { playing: boolean }) {
return playing ? <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M8 5v14M16 5v14" fill="none" stroke="currentColor" strokeLinecap="round" strokeWidth="2" /></svg> : <svg viewBox="0 0 24 24" aria-hidden="true"><path d="m8 5 11 7-11 7V5Z" fill="none" stroke="currentColor" strokeLinejoin="round" strokeWidth="1.8" /></svg>;
}
export function MeshGradient({ palette = defaultPalette, mode = "dark", title = "A field of colour, held in motion.", body = "A saturated field that gives the next screen a point of view.", defaultPlaying = false, playing, onPlayingChange, className = "" }: MeshGradientProps) {
const grainId = useId().replace(/:/g, "");
const [internalPlaying, setInternalPlaying] = useState(defaultPlaying);
const [reduced, setReduced] = useState(false);
const isPlaying = playing ?? internalPlaying;
const effectivePlaying = reduced ? false : isPlaying;
const colours = [palette[0] ?? defaultPalette[0], palette[1] ?? defaultPalette[1], palette[2] ?? defaultPalette[2], palette[3] ?? defaultPalette[3]];
const style = { "--stepkit-mesh-a": colours[0], "--stepkit-mesh-b": colours[1], "--stepkit-mesh-c": colours[2], "--stepkit-mesh-d": colours[3] } as CSSProperties;
useEffect(() => {
const query = window.matchMedia("(prefers-reduced-motion: reduce)");
const update = () => setReduced(query.matches);
update();
query.addEventListener?.("change", update);
return () => query.removeEventListener?.("change", update);
}, []);
const toggle = () => { if (reduced) return; const next = !isPlaying; if (playing === undefined) setInternalPlaying(next); onPlayingChange?.(next); };
return (
<section className={`stepkit stepkit-showcase stepkit-mesh-gradient${className ? ` ${className}` : ""}`} data-mode={mode} data-playing={effectivePlaying} style={style} aria-label="Mesh gradient showcase">
<div className="stepkit-mesh-gradient__field" aria-hidden="true" />
<svg className="stepkit-mesh-gradient__grain" aria-hidden="true"><filter id={grainId}><feTurbulence baseFrequency=".72" numOctaves="2" seed="13" type="fractalNoise" /><feColorMatrix type="saturate" values="0" /></filter><rect filter={`url(#${grainId})`} height="100%" opacity=".38" width="100%" /></svg>
<div className="stepkit-mesh-gradient__veil" aria-hidden="true" />
<div className="stepkit-mesh-gradient__content"><div><span className="stepkit-mesh-gradient__eyebrow">Gradient / CSS composition</span><h2 className="stepkit-mesh-gradient__title">{title}</h2><p className="stepkit-mesh-gradient__body">{body}</p></div><div><button className="stepkit-mesh-gradient__toggle" type="button" aria-pressed={effectivePlaying} onClick={toggle}><PlayIcon playing={effectivePlaying} />{effectivePlaying ? "Pause movement" : "Play movement"}</button></div></div>
</section>
);
}
export default MeshGradient;
components/MeshGradient.tsxcomponents/showcase.csscomponents/styles.cssInstall
Use the registry command or download the complete source archive.
npx [email protected] add https://vstepanov.com/stepkit/r/mesh-gradient.jsonIntegration prompt
Install Stepkit Mesh gradient 0.3.0 from the registry and preserve the included MIT notice. npx [email protected] add https://vstepanov.com/stepkit/v0.3.0/r/mesh-gradient.json Install the tested runtime dependencies exactly: npm install [email protected] [email protected] Keep the component source, shared styles, NOTICE.md, and provenance files together. Apply the theme tokens on a wrapper rather than passing theme keys as component props. Usage: ```tsx "use client"; import type { CSSProperties } from "react"; import { MeshGradient } from "./components/stepkit/MeshGradient"; import "./components/stepkit/styles.css"; import "./components/stepkit/showcase.css"; const themeStyle = {"--stepkit-bg":"#f7f7f8","--stepkit-surface":"#ffffff","--stepkit-surface-muted":"#f0f0f3","--stepkit-fg":"#17181c","--stepkit-muted":"#626572","--stepkit-border":"#e3e4e9","--stepkit-border-strong":"#c8cad3","--stepkit-code":"#f1f1f4","--stepkit-shadow":"0 20px 60px rgba(31, 26, 64, .10)","--stepkit-accent":"#6950df","--stepkit-accent-strong":"#5137c7","--stepkit-accent-soft":"#eeeafd","--stepkit-accent-on-soft":"#5137c7","--stepkit-accent-text":"#ffffff","--stepkit-error":"#b43145","--stepkit-placeholder":"#626572","--stepkit-radius":"16px","--stepkit-space":"16px","--stepkit-control-height":"44px","--stepkit-font-sans":"Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif"} as CSSProperties; export default function Example() { return ( <div className="stepkit-theme" data-stepkit-preset="modern" style={themeStyle}> <MeshGradient mode={"dark"} defaultPlaying={false} /> </div> ); } ``` Selected bounded settings: {"mode":"dark","defaultPlaying":false}. Selected theme: {"mode":"light","accent":"violet","radius":"soft","density":"comfortable","preset":"modern"}.
Props
| Name | Type | Default | Description |
|---|---|---|---|
palette | [string, string, string, string] | default palette | Four CSS colours for the layered field. |
mode | "light" | "dark" | "dark" | Chooses readable foreground treatment. |
playing | boolean | undefined | Controlled motion state. |
onPlayingChange | (playing: boolean) => void | undefined | Called after the motion handle changes. |
defaultPlaying | boolean | false | Initial motion state. |
Accessibility & limits
- The gradient is explicitly decorative and paired with readable heading and body copy.
- Motion is opt-in through one visible handle and stops under reduced-motion preferences.
- The play state is exposed with aria-pressed.
Limitations
- The visual is a CSS gradient composition rather than a shader or image.
Provenance
Original source under the MIT licence.
Original Stepkit implementation.
Required notices: NOTICE.md, PROVENANCE-stepkit-mesh-gradient.md