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-bgUsage
Usagetsx
<ScallopWaveBg />Props
| Prop | Type | Default | Description |
|---|---|---|---|
| size | number | 100 | Tile 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