Workspace / component / shaders

Liquid Shader

The Pax liquid engine renders your HTML through a refractive fluid surface that ripples from the pointer. WebGL2 with a static fallback.

Workspacecomponentshaders

paxfx add blocks/component-shaders-liquidlocal only

Tier: high. Reduced motion: Surface holds still; content renders undistorted.

"use client";

import type { ReactNode } from "react";
import { cn, EffectSurface } from "@pax-sh/blocks";

export interface LiquidShaderProps {
  className?: string;
  /** Content rendered through the refractive fluid surface. Defaults to a demo panel. */
  children?: ReactNode;
}

function DemoContent() {
  return (
    <div className="flex min-h-[320px] flex-col justify-center gap-5 p-8 md:p-12">
      <p className="m-0 text-xs font-medium uppercase tracking-[0.22em] text-[var(--pax-acid,#c8ff00)]">
        Fluid pass / WebGL2
      </p>
      <h3 className="m-0 max-w-xl text-3xl font-semibold leading-tight tracking-tight [text-wrap:balance] md:text-4xl">
        Type you can pour.
      </h3>
      <p className="m-0 max-w-md text-sm leading-relaxed text-[var(--pax-paper,#f6f2ea)] opacity-70">
        Move the pointer across this panel. The liquid engine bends the whole layout through a
        refractive surface and lets the ripples settle on their own.
      </p>
      <dl className="mt-2 flex flex-wrap gap-x-10 gap-y-3">
        {[
          ["Renderer", "WebGL2"],
          ["Fallback", "Static HTML"],
          ["Input", "Pointer ripples"],
        ].map(([term, detail]) => (
          <div key={term}>
            <dt className="text-[11px] font-medium uppercase tracking-[0.14em] text-[var(--pax-paper,#f6f2ea)] opacity-50">
              {term}
            </dt>
            <dd className="m-0 mt-1 text-sm font-semibold">{detail}</dd>
          </div>
        ))}
      </dl>
    </div>
  );
}

/**
 * Mounts the Pax liquid engine over live HTML: the content renders through a
 * refractive fluid surface that ripples away from the pointer. Under reduced
 * motion the surface holds still and the content renders undistorted.
 */
export function LiquidShader({ className, children }: LiquidShaderProps) {
  return (
    <div
      className={cn(
        "relative isolate overflow-hidden rounded-2xl border border-[var(--pax-border,rgba(18,17,15,0.12))]",
        "bg-[var(--pax-night,#0b0d10)] text-[var(--pax-paper,#f6f2ea)]",
        className,
      )}
      data-pax-asset="component-shaders-liquid"
    >
      <EffectSurface effect="liquid" tier="high" className="h-full w-full">
        {children ?? <DemoContent />}
      </EffectSurface>
    </div>
  );
}