CircularGallery
circular-galleryBrowses image cards around a draggable circular track.
Usage
Basic usage
Just pass in items, scroll wheel/drag to pan, start and end seamlessly cycle; use the built-in placeholder card if you don't pass in items.
<div
className="relative h-72 overflow-hidden rounded-xl"
style={{ background: "oklch(0.14 0.02 255)" }}
>
<CircularGallery
items={[
{ text: "Hanshu" },
{ text: "HanDuo" },
{ text: "Hanship" },
{ text: "Hanfu" },
]}
/>
</div>Arc strength bend
bend Control bending: 0 straight in a row, positive values are concave downwards, negative values are convex upwards; the larger the absolute value, the deeper the arc.
<CircularGallery bend={0} items={items} /> {/* Straight */}
<CircularGallery bend={-4} items={items} /> {/* upward convex arc */}Card rounded corners
borderRadius Normalized to half the length of the card (0–0.5), 0 is a right angle, and the larger it is, the rounder it is.
<CircularGallery borderRadius={0.2} items={items} />Customize title color
textColor accepts any CSS color, and also accepts var(--color-*) token (runtime parsing feeds canvas).
<CircularGallery
textColor="var(--color-chart-2)"
items={items}
/>When to use
Use CircularGallery when you need a WebGL image track that bends along an arc, responds to wheel and drag gestures with inertia, and loops seamlessly—for example, in portfolio browsers or brand showcases. For a draggable 3D gallery mapped to the inside of a hemisphere, use DomeGallery. For a card wall revealed by a cursor spotlight, use ChromaGrid. For structured tabular data, use Table. This client component is built on ogl. When an item has no image, it generates an offline placeholder from chart tokens.
Import
import { CircularGallery } from "@hulianui/ui"Props
| Name | Type | Default | Description |
|---|---|---|---|
| items | CircularGalleryItem[] | Built-in placeholder cards | Gallery items containing an image and title. An omitted or empty array uses programmatic gradient placeholders based on chart tokens, with no remote assets. |
| bend | number | 3 | Arc curvature. 0 is flat, positive values curve downward, and negative values curve upward. A range of -6 to 6 is recommended. |
| textColor | string | var(--color-foreground) | Title color. Accepts any CSS color or var(--color-*) token, which is resolved to a concrete color before being passed to the canvas. |
| borderRadius | number | 0.05 | Normalized card radius from 0 to 0.5. 0 is square and 0.5 is pill-shaped or circular. |
| scrollSpeed | number | 2 | Wheel and drag sensitivity. Larger values move farther per gesture. |
| scrollEase | number | 0.05 | Inertial lerp factor from 0 to 1. Smaller values feel heavier and smoother. |
| font | string | bold 30px ui-sans-serif, system-ui, sans-serif | Canvas font shorthand for titles. The default system font stack is offline- and RSC-safe. |
| className | string | — | Class name forwarded to the root container. |
CircularGalleryItem is { image?; text }. When image is omitted, the component generates a gradient placeholder from chart tokens.
Slots
| Slot | Type | Description |
|---|---|---|
| fallback | React.ReactNode | Fallback rendered above the static placeholder layer when reduced motion is enabled or WebGL is unavailable. |
Usage notes
- CircularGallery uses ogl and WebGL, so it is a client component. Place it in a host with an explicit height and
overflow-hidden, and render it only on the client in SSR or RSC applications. - Use a
var(--color-*)token for a theme-awaretextColor. The component resolves the token before passing the color to the canvas; bare token names cannot be resolved there. - A static track is the expected fallback when reduced motion is enabled or WebGL is unavailable. Use
fallbackto add custom fallback content. - Headless screenshots can look empty when the WebGL animation has not advanced past its first frame. Use a real browser or allow a few frames to render before visual capture.
Related
Table · Book3D · ProTable · PricingTable · JsonViewer · EditableTable
Playground
<div className="relative h-72 overflow-hidden rounded-xl"
style={{ background: "oklch(0.14 0.02 255)" }}>
<CircularGallery
bend={3}
borderRadius={0.05}
scrollSpeed={2}
scrollEase={0.05}
items={[{ text: "Hanshu" }, { text: "Hanruo" }]}
/>
</div>