Zum Inhalt springen
UI Kit
Accent

3D-Wellen als Hintergrund

Eine wellige Fläche in Perspektive, mit WebGL als Linien oder Punkte gezeichnet. Gleitet auf dich zu, Kämme fangen den Akzent, der Zeiger hebt die Wellen unter sich.

4 VariantsEine von 6 Components in 3D-Hintergründe

Eine Fläche, die auf dich zugleitet

Ein Wellenfeld zeichnet eine wellige Fläche in Perspektive, aus Linien oder Punkten, die auf dich zugleitet. Es gibt einer Seite Bewegung und Tiefe zugleich und lässt die obere Hälfte für den Text frei.

„Hero über den Wellen“ setzt den Text über ein Linienfeld, das auf dich zugleitet. Das „Gepunktete Band“ legt ein flaches Punktfeld unter ein Band mit Kennzahlen über die ganze Breite. Die „Hervorgehobene Karte“ füllt die Karte mit hohen Wellen wie eine Schallwelle. „Hinter einem Call to Action“ lässt ein ruhiges Meer aus Punkten bis zur Mitte der Abschlussfläche reichen.

Linien wirken präzise und technisch, Punkte leichter, wähle einen Stil pro Seite. Die Karte passt zu Produkten rund um Audio, Sprache und Daten, dort erzählt die Wellenform die Geschichte von selbst.

Variants

Hero über den WellenNeu in 1.2

Der Text steht über einem Linienfeld, das auf dich zugleitet.

Gepunktetes BandNeu in 1.2

Ein flaches Punktfeld unter einem Band mit Kennzahlen über die ganze Breite.

Hervorgehobene KarteNeu in 1.2

Hohe Wellen füllen die hervorgehobene Karte wie eine Schallwelle.

Hinter einem Call to ActionNeu in 1.2

Ein ruhiges Meer aus Punkten reicht bis zur Mitte der Abschlussfläche.

Details

Was Wellenfeld mitbringt, aus Registry und Quellcode gelesen.

Bereich
Visuals
Variants
4
Version
1.0.0 · seit 1.2.0
Datei
components/ui/wave-field.tsx
Bringt mit
utilskitwebgl
npm-Pakete
Keine
Sprachen
Texte kommen über Props
Studio
Im Code anpassen

Props

Die Schnittstelle WaveFieldProps aus components/ui/wave-field.tsx. Beschreibungen stammen aus den Kommentaren im Code und sind auf Englisch. Nimmt außerdem alle Props von ShaderLayerProps an.

  • patternoptional
    WaveFieldPattern

    linesdots

    lines draws each row as a line, dots as a grid of points

  • rowsoptional
    number

    Rows on desktop, 12 to 64, default 44. Each pixel only looks at the rows near its own depth, so more rows cost nothing extra.

  • horizonoptional
    number

    Height of the horizon as share of the box from the bottom, default 0.55

  • amplitudeoptional
    number

    Wave height, 1 is the designed height

Barrierefreiheit

Aus dem Quellcode der Component abgeleitet.

  • Kommt ohne Client-State aus. Der Server schreibt sie vollständig, sie ist auch ohne JavaScript lesbar.

Bewegung

  • Ohne Animationen, sie steht sofort ruhig da.

Quellcode

Die Datei, die aiw add in dein Projekt legt. Sie gehört danach dir.

Datei anzeigen, 128 Zeilen
components/ui/wave-field.tsx
'use client';

import { cn } from '@/lib/kit';
import { ShaderBackdrop, ShaderCanvas, type ShaderLayerProps } from '@/components/ui/webgl';

/*
 * A wavy surface seen in perspective, drawn as lines or dots. Rows sit at
 * depths that grow geometrically toward the horizon, every row is lifted by
 * the waves at its depth and projected onto the screen. The field glides
 * toward the viewer, crests catch the accent, far rows fade into haze. The
 * pointer raises the waves under it. Phones draw fewer rows.
 */

export type WaveFieldPattern = 'lines' | 'dots';

export interface WaveFieldProps extends ShaderLayerProps {
  /** lines draws each row as a line, dots as a grid of points */
  pattern?: WaveFieldPattern;
  /** Rows on desktop, 12 to 64, default 44. Each pixel only looks at the rows near its own depth, so more rows cost nothing extra. */
  rows?: number;
  /** Height of the horizon as share of the box from the bottom, default 0.55 */
  horizon?: number;
  /** Wave height, 1 is the designed height */
  amplitude?: number;
}

const FRAG = `
uniform float u_rows;uniform float u_horizon;uniform float u_amp;uniform float u_dots;
float wave(float x,float z,float t){
return (sin(x*1.15+t*.9+z*.55)*.55+sin(x*.53-t*.6+z*1.35)*.35+sin(x*2.1+z*.8-t*1.3)*.12)*u_amp;
}
void main(){
float m=min(u_res.x,u_res.y);
vec2 p=vec2(gl_FragCoord.x-.5*u_res.x,gl_FragCoord.y)/m;
float yh=u_res.y/m*u_horizon;
float px=1./m;
float f=yh*.55;
float t=u_time*.5;
float mx=(u_mouse.x-.5)*u_res.x/m;
float n=floor(u_rows*mix(.6,1.,u_quality));
float ph=fract(u_time*.08+u_scroll*.6);
vec3 col=vec3(0.);float al=0.;
float zf=f/max(yh-p.y,.002);
float ic=floor(log(max(zf/.45,1.))/log(30.)*n-1.+ph);
for(int j=0;j<15;j++){
float fi=ic-7.+float(j);if(fi<0.||fi>=n)continue;
float k=(fi+1.-ph)/n;
float z=.45*pow(30.,k);
float xw=p.x*z/f;
float bump=u_hover*.55*exp(-(p.x-mx)*(p.x-mx)*3.)*u_amp;
float h=wave(xw,z,t)*.32+bump*.3;
float y=yh-(1.-h)*f/z;
float dy=abs(p.y-y);
if(dy>.08)continue;
float far=k;
float w=px*mix(1.5,.75,far);
float v;
if(u_dots>.5){
float sp=.55;
float dx=(fract(xw/sp+.5)-.5)*sp*f/z;
float r=px*mix(2.6,.9,far);
v=smoothstep(r+px,r-px*.5,length(vec2(dx,p.y-y)));
}else{
v=smoothstep(w+px,w-px*.4,dy);
}
float fade=smoothstep(.88,.35,far)*smoothstep(0.,.08,k);
float crest=smoothstep(.0,.3,h);
vec3 c=mix(u_c3,u_c0,clamp(crest*.85+bump*.4+(1.-u_dark)*.35,0.,1.));
float a=v*fade*(.42+.58*crest);
col+=c*a;al+=a;
}
float glow=exp(-abs(p.y-yh)*7.)*.2*mix(.5,1.,u_dark)*smoothstep(yh+.18,yh-.02,p.y);
col+=u_c0*glow;al+=glow;
float kk=mix(.62,1.,u_dark)*u_intensity;
al=clamp(al*kk,0.,1.);
gl_FragColor=vec4(min(col*kk,vec3(1.)),al);
}`;

/* Still picture: a floor of lines tilted back with CSS, fading into the horizon */
function Still({ pattern, horizon }: { pattern: WaveFieldPattern; horizon: number }) {
  const ink = 'color-mix(in oklab, var(--primary) 55%, var(--foreground))';
  const bg =
    pattern === 'dots'
      ? `radial-gradient(circle, ${ink} 0 1.5px, transparent 2.2px) 0 0 / 26px 26px`
      : `repeating-linear-gradient(0deg, ${ink} 0 2px, transparent 2px 22px)`;
  return (
    <div className="absolute inset-x-0 bottom-0 overflow-hidden [perspective:420px]" style={{ height: `${Math.round(horizon * 100)}%` }}>
      <div
        className={cn('absolute inset-x-[-60%] bottom-[-10%] top-0 origin-bottom')}
        style={{ background: bg, transform: 'rotateX(64deg)', maskImage: 'linear-gradient(to top, #000 10%, transparent 92%)', WebkitMaskImage: 'linear-gradient(to top, #000 10%, transparent 92%)', opacity: 0.9 }}
      />
      <div className="absolute inset-x-0 top-0 h-px" style={{ boxShadow: '0 0 60px 14px color-mix(in oklab, var(--primary) 26%, transparent)' }} />
    </div>
  );
}

/**
 * A gliding wave surface in perspective behind any content. With children it
 * renders a box, without children only the layer for a `relative` parent.
 */
export function WaveField({ intensity = 1, speed = 1, colors, interactive = true, pattern = 'lines', rows = 44, horizon = 0.55, amplitude = 1, fallback, scrim = 'none', scrimStrength, scrimColor, children, className, contentClassName, layerClassName }: WaveFieldProps) {
  const h = Math.min(0.95, Math.max(0.15, horizon));
  return (
    <ShaderBackdrop
      scrim={scrim}
      scrimStrength={scrimStrength}
      scrimColor={scrimColor}
      className={className}
      contentClassName={contentClassName}
      layer={
        <ShaderCanvas
          fragment={FRAG}
          colors={colors ?? ['var(--primary)', 'var(--primary)', 'var(--primary)', 'color-mix(in oklab, var(--primary) 30%, var(--foreground))']}
          uniforms={{ u_rows: Math.max(12, Math.min(64, Math.round(rows))), u_horizon: h, u_amp: amplitude, u_dots: pattern === 'dots' ? 1 : 0 }}
          speed={speed}
          intensity={intensity}
          maxDpr={2}
          phoneDpr={1.5}
          pointer={interactive}
          fallback={fallback ?? <Still pattern={pattern} horizon={h} />}
          className={layerClassName}
        />
      }
    >
      {children}
    </ShaderBackdrop>
  );
}

Häufige Fragen

Wie installiere ich Wellenfeld?

Mit aiw add @ai-website/wave-field. Der Befehl legt die Datei components/ui/wave-field.tsx in dein Projekt, der Code gehört danach dir. Mit dabei sind webgl. Weitere npm-Pakete braucht sie nicht.

Welche Varianten hat Wellenfeld?

4 Varianten, Hero über den Wellen, Gepunktetes Band, Hervorgehobene Karte und Hinter einem Call to Action.

Kann ein KI-Assistent Wellenfeld einbauen?

Ja. Über den MCP-Server der aiw CLI finden Claude, Cursor, GitHub Copilot, Windsurf, Codex und Gemini Wellenfeld mit aiw_components und installieren sie mit aiw_add @ai-website/wave-field.

Oft gesucht

Direkt zu

In die Zwischenablage kopiert