Motion & effects · Adapted
Background paths
A flowing path field adapted from KokonutUI with a readable editorial layer and bounded motion.
Live preview
Try it in context
Customize preview
Source delivery
v0.3.0Usage & source
"use client";
import type { CSSProperties } from "react";
import { BackgroundPaths } from "./components/stepkit/BackgroundPaths";
import "./components/stepkit/styles.css";
import "./components/stepkit/adapted-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":"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}>
<BackgroundPaths
title={"Make room for a moving idea"}
description={"Flowing paths add depth without taking attention away from the work."}
paused={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.
*
*
* MIT License
*
* Copyright (c) 2025 kokonutUI
*
* 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 { memo, useId, useMemo, type CSSProperties } from "react";
import "./styles.css";
import "./adapted-showcase.css";
/**
* Adapted from KokonutUI Background Paths (MIT).
* Source: https://raw.githubusercontent.com/kokonut-labs/kokonutui/b1ac155138b089ae7825022182b431da89de89be/components/kokonutui/background-paths.tsx
* License snapshot: provenance/KOKONUTUI-LICENSE-b1ac155.md
* Stepkit replaces Motion and Tailwind with scoped SVG/CSS while retaining the flowing path field.
*/
export interface BackgroundPathsProps {
title?: string;
description?: string;
paused?: boolean;
className?: string;
}
type PathKind = "primary" | "secondary" | "accent";
interface Point { x: number; y: number; }
interface PathData { id: string; d: string; opacity: number; width: number; duration: number; delay: number; }
type ShowcaseStyle = CSSProperties & Record<`--stepkit-${string}`, string | number>;
function generateAestheticPath(index: number, position: number, type: PathKind) {
const amplitude = type === "primary" ? 150 : type === "secondary" ? 100 : 60;
const phase = index * 0.2;
const segments = type === "primary" ? 10 : type === "secondary" ? 8 : 6;
const points: Point[] = [];
const startX = 2400;
const startY = 800;
const endX = -2400;
const endY = -800 + index * 25;
for (let i = 0; i <= segments; i += 1) {
const progress = i / segments;
const eased = 1 - (1 - progress) ** 2;
const baseX = startX + (endX - startX) * eased;
const baseY = startY + (endY - startY) * eased;
const amplitudeFactor = 1 - eased * 0.3;
const wave =
Math.sin(progress * Math.PI * 3 + phase) * amplitude * 0.7 * amplitudeFactor +
Math.cos(progress * Math.PI * 4 + phase) * amplitude * 0.3 * amplitudeFactor +
Math.sin(progress * Math.PI * 2 + phase) * amplitude * 0.2 * amplitudeFactor;
points.push({ x: baseX * position, y: baseY + wave });
}
return points.map((point, pointIndex) => {
if (pointIndex === 0) return `M ${point.x} ${point.y}`;
const previous = points[pointIndex - 1];
const tension = 0.4;
const cp1x = previous.x + (point.x - previous.x) * tension;
const cp2x = previous.x + (point.x - previous.x) * (1 - tension);
return `C ${cp1x} ${previous.y}, ${cp2x} ${point.y}, ${point.x} ${point.y}`;
}).join(" ");
}
const FloatingPaths = memo(function FloatingPaths({ id, position, paused }: { id: string; position: number; paused: boolean }) {
const groups = useMemo(() => ({
primary: Array.from({ length: 12 }, (_, index): PathData => ({ id: `primary-${position}-${index}`, d: generateAestheticPath(index, position, "primary"), opacity: 0.15 + index * 0.02, width: 4 + index * 0.3, duration: 8, delay: 0 })),
secondary: Array.from({ length: 15 }, (_, index): PathData => ({ id: `secondary-${position}-${index}`, d: generateAestheticPath(index, position, "secondary"), opacity: 0.12 + index * 0.015, width: 3 + index * 0.25, duration: 6, delay: 0 })),
accent: Array.from({ length: 10 }, (_, index): PathData => ({ id: `accent-${position}-${index}`, d: generateAestheticPath(index, position, "accent"), opacity: 0.08 + index * 0.12, width: 2 + index * 0.2, duration: 5, delay: index * 0.12 })),
}), [position]);
const pathMarkup = (paths: PathData[], group: PathKind) => paths.map((path) => {
const style: ShowcaseStyle = {
opacity: path.opacity,
"--stepkit-path-duration": `${path.duration}s`,
"--stepkit-path-delay": `${path.delay}s`,
};
return <path className={`stepkit-background-paths__path stepkit-background-paths__path--${group}`} d={path.d} key={path.id} strokeWidth={path.width} style={style} />;
});
return (
<svg aria-hidden="true" className="stepkit-background-paths__svg" fill="none" preserveAspectRatio="xMidYMid slice" viewBox="-2400 -800 4800 1600">
<defs>
<linearGradient id={id} x1="0%" x2="100%" y1="0%" y2="0%">
<stop offset="0%" stopColor="var(--stepkit-accent, #6950df)" stopOpacity=".22" />
<stop offset="50%" stopColor="var(--stepkit-accent-strong, #5137c7)" stopOpacity=".4" />
<stop offset="100%" stopColor="var(--stepkit-accent, #6950df)" stopOpacity=".16" />
</linearGradient>
</defs>
<g className="stepkit-background-paths__group" data-paused={paused} stroke={`url(#${id})`} strokeLinecap="round">
{pathMarkup(groups.primary, "primary")}
{pathMarkup(groups.secondary, "secondary")}
{pathMarkup(groups.accent, "accent")}
</g>
</svg>
);
});
export function BackgroundPaths({ title = "Make room for a moving idea", description = "Flowing paths add depth without taking attention away from the work.", paused = false, className = "" }: BackgroundPathsProps) {
const gradientId = `stepkit-background-gradient-${useId().replace(/:/g, "")}`;
return (
<section aria-label={title} className={`stepkit stepkit-background-paths${className ? ` ${className}` : ""}`} data-paused={paused}>
<div className="stepkit-background-paths__field"><FloatingPaths id={gradientId} paused={paused} position={1} /><FloatingPaths id={`${gradientId}-reverse`} paused={paused} position={-1} /></div>
<div className="stepkit-background-paths__content">
<span className="stepkit-background-paths__eyebrow">Background paths</span>
<h2 className="stepkit-background-paths__title">{title}</h2>
<p className="stepkit-background-paths__description">{description}</p>
</div>
</section>
);
}
export default BackgroundPaths;
components/BackgroundPaths.tsxcomponents/adapted-showcase.csscomponents/styles.cssInstall
Use the registry command or download the complete source archive.
npx [email protected] add https://vstepanov.com/stepkit/r/background-paths.jsonIntegration prompt
Install Stepkit Background paths 0.3.0 from the registry and preserve the included MIT notice. npx [email protected] add https://vstepanov.com/stepkit/v0.3.0/r/background-paths.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 { BackgroundPaths } from "./components/stepkit/BackgroundPaths"; import "./components/stepkit/styles.css"; import "./components/stepkit/adapted-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}> <BackgroundPaths title={"Make room for a moving idea"} description={"Flowing paths add depth without taking attention away from the work."} paused={false} /> </div> ); } ``` Selected bounded settings: {"title":"Make room for a moving idea","description":"Flowing paths add depth without taking attention away from the work.","paused":false}. Selected theme: {"mode":"light","accent":"violet","radius":"soft","density":"comfortable","preset":"modern"}.
Props
| Name | Type | Default | Description |
|---|---|---|---|
title | string | "Make room for a moving idea" | Accessible section heading. |
description | string | "Flowing paths add depth without taking attention away from the work." | Supporting copy. |
paused | boolean | false | Freezes path motion. |
className | string | "" | Additional class name. |
Accessibility & limits
- The SVG path field is decorative and paired with a labelled section.
- Paused and reduced-motion states remove path animation.
- The content remains readable over the complete field.
Limitations
- The component owns its decorative SVG field; replace it with a product-specific background when needed.
Provenance
Adapted source under the MIT licence.
Upstream: KokonutUI Background Paths
Required notices: NOTICE.md, PROVENANCE-stepkit-background-paths.md, NOTICE-KOKONUTUI.md