WebberUI

Control Center Panel

A grid of glass control modules; long-press any tile and it springs open in place into a fine-grained control card (slider, segmented toggle) while the background blurs and recedes, springing back on release.

Loading preview…
npx shadcn@latest add https://webberui.com/r/control-center-panel.json

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

2
<ControlCenterPanel />

Installation

npx shadcn@latest add https://webberui.com/r/control-center-panel.json

Or, once registries are configured in components.json, install it as @webberui/control-center-panel.

Usage

import {
  ControlCenterPanel,
  ControlTile,
} from "@/components/ui/control-center-panel";

const [brightness, setBrightness] = React.useState(72);
const [appearance, setAppearance] = React.useState("auto");

<ControlCenterPanel columns={2}>
  <ControlTile
    id="brightness"
    label="Brightness"
    icon={<Sun />}
    value={brightness}
    onValueChange={setBrightness}
    formatValue={(v) => `${v}%`}
  />
  <ControlTile
    id="appearance"
    label="Appearance"
    icon={<Palette />}
    control="segmented"
    value={appearance}
    onValueChange={setAppearance}
    options={[
      { label: "Light", value: "light" },
      { label: "Dark", value: "dark" },
      { label: "Auto", value: "auto" },
    ]}
  />
</ControlCenterPanel>;

Props

ControlCenterPanel

PropTypeDefaultDescription
columnsnumber2Number of grid columns (2–4)
valuestring | nullControlled mode: the id of the currently expanded tile (null means all collapsed)
defaultValuestring | nullnullInitially expanded id in uncontrolled mode
onValueChange(id: string | null) => voidFires when a tile expands or collapses
classNamestringOverrides the panel container's styles
childrenReactNodePut ControlTile children here

ControlTile (shared)

PropTypeDefaultDescription
idstringUnique tile identifier; the panel uses it to coordinate "only one expanded at a time"
labelstringTitle (such as "Brightness")
iconReactNodeIcon in the top-left corner; a lucide-react icon is recommended
control'slider' | 'segmented''slider'Control type
onValueChange(value) => voidFires when the value or selection changes
classNamestringOverrides the tile's styles

ControlTile — control="slider"

PropTypeDefaultDescription
valuenumberCurrent value (controlled)
minnumber0Minimum
maxnumber100Maximum
stepnumber1Step
formatValue(v: number) => string(v) => `${v}`Formatting for the compact readout and the expanded card

ControlTile — control="segmented"

PropTypeDefaultDescription
valuestringCurrently selected value (controlled)
options{ label: ReactNode; value: string }[]Segment options, laid out at equal widths

How it works

  • Long-press to expand: the tile only expands after the pointer has been held for about 380ms; while held, the tile shrinks slightly to build tension, and releasing early springs it back into place (without expanding)
  • Morphing in place: the tile and the expanded card share a layoutId, so a decelerating spring grows the tile seamlessly from its grid cell into the fine-grained control card; only one tile is expanded at a time
  • Background recedes: while expanded, the whole grid is covered by a translucent overlay with backdrop-blur, creating a layer where the background steps back and focus moves forward
  • Fine-grained slider: the slider inside the expanded card supports pointer dragging and the keyboard (arrow keys ±step, PageUp/Down ±10%, Home/End to the ends), with the fill and thumb following on springs
  • Segmented toggle: equal-width options, with the highlight sliding to a position computed from the index (avoiding nested layout animations), and arrow keys cycling through them
  • Collapse: click the background overlay or press Esc to collapse; the expanded card morphs back to its grid cell on the same spring

Accessibility

  • Tiles are role="button" with aria-haspopup="dialog" and aria-expanded; Enter / Space expand directly (no long press needed for keyboard users)
  • The expanded card is a role="dialog" with aria-modal; on open, focus moves into the card and the grid gets inert to leave the tab order, and on collapse focus returns to the original tile
  • The slider is a role="slider" with aria-valuemin/max/now and aria-valuetext; the segmented control is a role="radiogroup" / role="radio" marked with aria-checked
  • When the user has "reduce motion" enabled at the system level: the long-press expansion and every control feature are kept exactly as they are, and only the morph offsets and the tension scaling are removed, replaced by a brief fade in / fade out

On this page