Raster mit Verlauf
Ein Linien- oder Kachelraster, das ausblendet, mit Feldern, die in der Akzentfarbe aufleuchten.
Eine von 6 Components in Backgrounds
Variants
Details
Was Raster mit Verlauf mitbringt, aus Registry und Quellcode gelesen.
- Kategorie
- Backgrounds
- Bereich
- Elements
- Variants
- 3
- Version
- 1.0.0 · seit 1.0.0
- Datei
- components/ui/grid-fade.tsx
- Bringt mit
- utilskit
- npm-Pakete
- Keine
- Sprachen
- Texte kommen über Props
- Studio
- Im Code anpassen
- Mit KI bauen
- Per MCP in Claude, Cursor, Copilot und Codex
Props
Die Schnittstelle GridFadeProps aus components/ui/grid-fade.tsx. Beschreibungen stammen aus den Kommentaren im Code und sind auf Englisch.
variantoptionalGridFadeVariantlinessquaresdashed
lines: hairline grid. squares: rounded tiles with gaps. dashed: a quiet dashed grid.
celloptionalnumberCell size in px, default 48
fadeoptionalGridFadeFaderadialtopbottomnone
Where the grid stays visible: radial around the middle, toward the top or the bottom
highlightsoptionalnumberHow many cells light up in the accent color, default 0
pulseoptionalbooleanLet the highlighted cells pulse slowly, default true
seedoptionalnumberSame seed, same cells
scrollRevealoptionalbooleanThe grid settles from a little larger and fainter into place while the stage scrolls up to the middle of the window. Default false.
Barrierefreiheit
Aus dem Quellcode der Component abgeleitet.
- Der Server rendert den Startzustand vollständig, JavaScript ergänzt nur die Interaktion.
Bewegung
- Bewegt sich mit dem Scrollen, der Fortschritt folgt der Position auf der Seite.
- Bringt eigene Animationen mit, ruhig getaktet und ohne Wackeln.
- Bei reduzierter Bewegung (prefers-reduced-motion) bleibt sie ruhig.
Quellcode
Die Datei, die aiw add in dein Projekt legt. Sie gehört danach dir.
Datei anzeigen, 152 Zeilen
'use client';
import { useEffect, useId, useRef } from 'react';
import { cn, prefersReducedMotion, round } from '@/lib/kit';
export type GridFadeVariant = 'lines' | 'squares' | 'dashed';
export type GridFadeFade = 'radial' | 'top' | 'bottom' | 'none';
export interface GridFadeProps {
/** lines: hairline grid. squares: rounded tiles with gaps. dashed: a quiet dashed grid. */
variant?: GridFadeVariant;
/** Cell size in px, default 48 */
cell?: number;
/** Where the grid stays visible: radial around the middle, toward the top or the bottom */
fade?: GridFadeFade;
/** How many cells light up in the accent color, default 0 */
highlights?: number;
/** Let the highlighted cells pulse slowly, default true */
pulse?: boolean;
/** Same seed, same cells */
seed?: number;
/** The grid settles from a little larger and fainter into place while the stage scrolls up to the middle of the window. Default false. */
scrollReveal?: boolean;
className?: string;
}
const CSS = `
@keyframes kitfx-grid-pulse { 0%, 100% { opacity: 0.3; } 50% { opacity: 0.9; } }
.kitfx-grid * { animation-play-state: var(--kitfx-play, running); }
.kitfx-grid[data-scroll] > svg { --e: min(1, var(--kitfx-p, 0.5) * 2); opacity: calc(0.3 + 0.7 * var(--e)); transform: scale(calc(1.06 - 0.06 * var(--e))); transform-origin: 50% 45%; transition: opacity .3s linear, transform .3s linear; }
@media (prefers-reduced-motion: reduce) { .kitfx-grid * { animation: none !important; } .kitfx-grid[data-scroll] > svg { opacity: 1; transform: none; } }
`;
const FADE: Record<GridFadeFade, string | undefined> = {
radial: 'radial-gradient(ellipse 70% 65% at 50% 45%, #000 25%, transparent 78%)',
top: 'linear-gradient(to bottom, #000 10%, transparent 75%)',
bottom: 'linear-gradient(to top, #000 10%, transparent 75%)',
none: undefined,
};
function seeded(seed: number) {
let a = seed >>> 0 || 1;
return () => {
a = (a + 0x6d2b79f5) | 0;
let t = Math.imul(a ^ (a >>> 15), 1 | a);
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}
/**
* Scroll pass. While `on`, writes --kitfx-p on the element: 0 when its top
* meets the bottom of the window, 1 when its bottom leaves at the top. Never
* with reduced motion, the CSS then falls back to the resting value.
*/
function useScrollPass(ref: { current: HTMLElement | null }, on: boolean) {
useEffect(() => {
const el = ref.current;
if (!el || !on || prefersReducedMotion()) return;
let frame = 0;
const update = () => {
frame = 0;
const r = el.getBoundingClientRect();
const p = Math.min(1, Math.max(0, (window.innerHeight - r.top) / (r.height + window.innerHeight)));
el.style.setProperty('--kitfx-p', p.toFixed(4));
};
const onScroll = () => {
if (!frame) frame = requestAnimationFrame(update);
};
update();
window.addEventListener('scroll', onScroll, { passive: true });
window.addEventListener('resize', onScroll);
return () => {
cancelAnimationFrame(frame);
window.removeEventListener('scroll', onScroll);
window.removeEventListener('resize', onScroll);
el.style.removeProperty('--kitfx-p');
};
}, [ref, on]);
}
/**
* A line or tile grid that fades out, with optional cells that light up in
* the accent color and pulse. Lines run through the middle of the parent, so
* the grid sits symmetric at every width. Fills its parent, decorative,
* static with reduced motion.
*/
export function GridFade({ variant = 'lines', cell = 48, fade = 'radial', highlights = 0, pulse = true, seed = 11, scrollReveal = false, className }: GridFadeProps) {
const ref = useRef<HTMLDivElement>(null);
useScrollPass(ref, scrollReveal);
const id = `kitfx-grid-${useId().replace(/[^a-zA-Z0-9_-]/g, '')}`;
useEffect(() => {
const el = ref.current;
if (!el || !pulse || !highlights || typeof IntersectionObserver === 'undefined') return;
const io = new IntersectionObserver(([e]) => el.style.setProperty('--kitfx-play', e.isIntersecting ? 'running' : 'paused'));
io.observe(el);
return () => io.disconnect();
}, [pulse, highlights]);
// Cells around the middle, never twice the same
const rnd = seeded(seed);
const used = new Set<string>();
const cells = Array.from({ length: highlights }, () => {
let i = 0, j = 0;
for (let n = 0; n < 20; n++) {
i = Math.floor((rnd() - 0.5) * 16);
j = Math.floor((rnd() - 0.5) * 9);
// keep the very middle free for the headline
if (!used.has(`${i},${j}`) && !(Math.abs(i + 0.5) < 5 && Math.abs(j + 0.5) < 3)) break;
}
used.add(`${i},${j}`);
return { i, j, dur: round(5 + rnd() * 4, 1), delay: round(-rnd() * 9, 1), still: round(0.4 + rnd() * 0.45) };
});
const mask = FADE[fade];
const gap = variant === 'squares' ? Math.max(3, Math.round(cell / 12)) : 0;
const inset = gap / 2;
return (
<div ref={ref} aria-hidden data-clip-ok data-scroll={scrollReveal ? '' : undefined} className={cn('kitfx-grid pointer-events-none absolute inset-0 overflow-hidden', className)} style={mask ? { maskImage: mask, WebkitMaskImage: mask } : undefined}>
{(highlights > 0 || scrollReveal) && <style dangerouslySetInnerHTML={{ __html: CSS }} />}
<svg className="absolute inset-0 size-full text-[color-mix(in_oklch,var(--foreground)_9%,transparent)] dark:text-[color-mix(in_oklch,var(--foreground)_10%,transparent)]">
<defs>
<pattern id={id} width={cell} height={cell} patternUnits="userSpaceOnUse" x="50%" y="50%">
{variant === 'squares' ? (
<rect x={inset} y={inset} width={cell - gap} height={cell - gap} rx={Math.round(cell / 9)} fill="currentColor" fillOpacity={0.45} stroke="currentColor" />
) : (
<path d={`M ${cell} 0.5 H 0.5 V ${cell}`} fill="none" stroke="currentColor" strokeWidth={1} strokeDasharray={variant === 'dashed' ? '3 5' : undefined} />
)}
</pattern>
</defs>
<rect width="100%" height="100%" fill={`url(#${id})`} />
{cells.length > 0 && (
<svg x="50%" y="50%" overflow="visible">
{cells.map((c, k) => (
<rect
key={k}
x={c.i * cell + (variant === 'squares' ? inset : 1)}
y={c.j * cell + (variant === 'squares' ? inset : 1)}
width={variant === 'squares' ? cell - gap : cell - 1}
height={variant === 'squares' ? cell - gap : cell - 1}
rx={variant === 'squares' ? Math.round(cell / 9) : 0}
className="fill-[color-mix(in_oklab,var(--primary)_14%,transparent)] stroke-[color-mix(in_oklab,var(--primary)_40%,transparent)] dark:fill-[color-mix(in_oklab,var(--primary)_22%,transparent)] dark:stroke-[color-mix(in_oklab,var(--primary)_55%,transparent)]"
style={{ opacity: c.still, animation: pulse ? `kitfx-grid-pulse ${c.dur}s ease-in-out ${c.delay}s infinite` : undefined }}
/>
))}
</svg>
)}
</svg>
</div>
);
}Häufige Fragen
Wie installiere ich Raster mit Verlauf?
Mit aiw add @ai-website/grid-fade. Der Befehl legt die Datei components/ui/grid-fade.tsx in dein Projekt, der Code gehört danach dir. Weitere npm-Pakete braucht sie nicht.
Welche Varianten hat Raster mit Verlauf?
3 Varianten, Linien mit Puls, Kacheln und Gestrichelt von oben. Du wählst sie mit der Prop variant, zum Beispiel variant="squares".
Kann ein KI-Assistent Raster mit Verlauf einbauen?
Ja. Über den MCP-Server der aiw CLI finden Claude, Cursor, GitHub Copilot, Windsurf, Codex und Gemini Raster mit Verlauf mit aiw_components und installieren sie mit aiw_add @ai-website/grid-fade. Nenn im Prompt die Variante, etwa variant="lines", dann schreibt der Assistent deine Inhalte direkt in die Props.