Brand
Panel
Rounded atmosphere surfaces in paper, sand, haze, dusk and night, with optional film grain.
paper
Panel
sand
Panel
haze
Panel
dusk
Panel
night
Panel
pnpm dlx shadcn@latest add @paragon/panelBrings in@base-ui/reactclass-variance-authoritycn
registry/paragon/components/panel.tsx
import * as React from "react"
import { mergeProps } from "@base-ui/react/merge-props"
import { useRender } from "@base-ui/react/use-render"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "cn"
const GRAIN =
"url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .5 0 0 0 0 .5 0 0 0 0 .5 0 0 0 1 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E\")"
const panelVariants = cva("relative isolate overflow-hidden rounded-panel text-foreground", {
variants: {
tone: {
paper: "bg-secondary",
// Surfaces carry their own paper and night values, so text on them stays legible in both themes.
sand: "bg-surface-sand",
// Dust haze: a flat warm stone with light breaking in from one corner. No hue shift.
haze: "bg-surface-haze bg-[radial-gradient(120%_90%_at_88%_-12%,rgb(255_255_255/0.4),transparent_58%)] dark:bg-[radial-gradient(120%_90%_at_88%_-12%,rgb(255_255_255/0.06),transparent_58%)]",
// Dark tones scope the night palette to the panel, so any shadcn
// component placed inside renders in its dark variant automatically.
dusk: "dark bg-dusk bg-[radial-gradient(120%_90%_at_88%_-12%,rgb(255_255_255/0.12),transparent_58%)]",
night:
"dark bg-[radial-gradient(120%_90%_at_85%_0%,var(--night-3)_0%,var(--night-2)_38%,var(--night)_78%)]",
},
padding: {
none: "",
sm: "p-6",
default: "p-8 md:p-12",
lg: "px-gutter py-16 md:py-24",
},
},
defaultVariants: {
tone: "paper",
padding: "default",
},
})
/**
* The rounded "atmosphere" surface Paragon pages are built from.
* `grain` adds the film-grain texture used on hero and closing panels.
*/
function Panel({
className,
tone = "paper",
padding = "default",
grain = false,
children,
render,
...props
}: useRender.ComponentProps<"div"> & VariantProps<typeof panelVariants> & { grain?: boolean }) {
return useRender({
defaultTagName: "div",
props: mergeProps<"div">(
{
className: cn(panelVariants({ tone, padding }), className),
children: (
<>
{grain ? (
<span
aria-hidden="true"
data-slot="panel-grain"
className="pointer-events-none absolute inset-0 -z-10 opacity-[0.22] mix-blend-overlay"
style={{ backgroundImage: GRAIN }}
/>
) : null}
{children}
</>
),
},
props
),
render,
state: { slot: "panel", tone },
})
}
export { Panel, panelVariants }