Search

Jump to any page.

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/panel
Brings 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 }