bottega

backgrounds

Scallop Wave

A server component that renders a seamless scallop / fish-scale wave pattern using two overlapping radial gradients tiled at a configurable size. Full-bleed absolute layer with pointer-events-none. Pattern colors derive from --background, --primary, and --foreground tokens; reads correctly on both light and dark themes.

Ported from marcelodolza (MIT)

scallop-wave-bg

Install

1. Register the namespace (once per project):

json
// components.json — register the @bottega namespace once
{
  "registries": {
    "@bottega": { "url": "https://bottega.ariacode.ca/r/{name}.json" }
  }
}

2. Add the component:

bash
npx shadcn add @bottega/scallop-wave-bg

Usage

Usagetsx
<ScallopWaveBg />

Props

PropTypeDefaultDescription
sizenumber100Tile size in pixels — controls the scale of each scallop cell. Default: 100.

Source

scallop-wave-bg.tsxtsx
import type React from "react";

export interface ScallopWaveBgProps extends React.HTMLAttributes<HTMLDivElement> {
  /** Tile size in pixels — controls the scale of each scallop cell. Default: 100. */
  size?: number;
}

/**
 * Scallop / fish-scale wave background layer (server component, zero JS).
 * Two overlapping radial gradients produce seamless concentric-arc tiles.
 * Meant to be placed in a `relative` parent — fills it absolutely.
 * Colors derive from --background and --primary; reads on light and dark themes.
 */
export function ScallopWaveBg({
  size = 100,
  className = "",
  style,
  ...props
}: ScallopWaveBgProps) {
  const c1 = "var(--background)";
  const c2 = "color-mix(in oklch, var(--primary) 14%, transparent)";
  // Token-only relief tone (flips with theme — dark scales on light, light scales on dark; both read).
  const shadow = "color-mix(in oklch, var(--foreground) 18%, transparent)";

  // Alternating arc stops (mirrors original --_g, inlined since React can't hoist CSS vars)
  const _g = [
    `${c2} 4% 14%`, `${c1} 14% 24%`, `${c2} 22% 34%`,
    `${c1} 34% 44%`, `${c2} 44% 56%`, `${c1} 56% 66%`, `${c2} 66% 76%`,
    `${c1} 76% 86%`, `${c2} 86% 96%`,
  ].join(", ");

  const backgroundImage = [
    `radial-gradient(100% 100% at 100% 0, ${c1} 4%, ${_g}, ${shadow} 96%, transparent)`,
    `radial-gradient(100% 100% at 0 100%, transparent, ${shadow} 4%, ${_g}, ${c1} 96%)`,
  ].join(", ");

  return (
    <div
      aria-hidden="true"
      className={["absolute inset-0 pointer-events-none", className]
        .filter(Boolean)
        .join(" ")}
      style={{
        backgroundColor: c1,
        backgroundImage,
        backgroundSize: `${size}px ${size}px`,
        ...style,
      }}
      {...props}
    />
  );
}

Dependencies

  • @bottega/tokens