backgrounds
Concentric Rings
A server component that renders a tileable repeating-radial-gradient ring pattern across the full surface. Zero JS — ring color derives from --foreground at 14% opacity (reads on both themes); field derives from --background. Place in a relative parent; the layer fills it absolutely with pointer-events-none.
Ported from Javierrocadev (MIT)
concentric-rings-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/concentric-rings-bgUsage
Usagetsx
<ConcentricRingsBg />Props
| Prop | Type | Default | Description |
|---|---|---|---|
| size | number | 50 | Cell size in pixels (diameter of each ring tile). Default: 50. |
| ringWidth | number | 2 | Ring stop width as a percentage of the cell radius (1–10). Default: 2. |
| fade | boolean | false | Apply a radial fade-out mask toward edges. Default: false. |
Source
concentric-rings-bg.tsxtsx
import type React from "react";
export interface ConcentricRingsBgProps extends React.HTMLAttributes<HTMLDivElement> {
/** Cell size in pixels (diameter of each ring tile). Default: 50. */
size?: number;
/** Ring stop width as a percentage of the cell radius (1–10). Default: 2. */
ringWidth?: number;
/** Apply a radial fade-out mask toward edges. Default: false. */
fade?: boolean;
}
/**
* Tileable concentric-rings background layer (server component, zero JS).
* Reproduces the repeating-radial-gradient ring pattern from uiverse.io/Javierrocadev.
* Ring color derives from --foreground at low opacity; field derives from --background.
* Place in a `relative` parent — fills the parent absolutely with pointer-events-none.
*/
export function ConcentricRingsBg({
size = 50,
ringWidth = 2,
fade = false,
className = "",
style,
...props
}: ConcentricRingsBgProps) {
// Ring appears at the very edge of each radial tile.
// ringStart% = 100 - ringWidth*2 keeps a thin band at ~90% of the radius (matching original 88%→90%).
const ringStart = Math.max(0, 100 - ringWidth * 2);
const ringEnd = Math.min(100, ringStart + ringWidth);
// Field = var(--background); ring ink = foreground at 14% opacity so it reads on both themes.
const bg = `repeating-radial-gradient(
var(--background) ${ringStart}%,
color-mix(in oklch, var(--foreground) 14%, transparent) ${ringEnd}%
)`;
return (
<div
aria-hidden="true"
className={["absolute inset-0 pointer-events-none", className]
.filter(Boolean)
.join(" ")}
style={{
backgroundImage: bg,
backgroundSize: `${size}px ${size}px`,
backgroundColor: "var(--background)",
...(fade
? {
/* mask alpha — not a UI color */
maskImage:
"radial-gradient(ellipse 80% 80% at 50% 50%, rgb(0 0 0 / 1) 40%, transparent 100%)",
WebkitMaskImage:
"radial-gradient(ellipse 80% 80% at 50% 50%, rgb(0 0 0 / 1) 40%, transparent 100%)",
}
: {}),
...style,
}}
{...props}
/>
);
}
Dependencies
- @bottega/tokens