Skip to content

FreeIn reviewv1.0.0

Glass Controls

A control-centre panel: glass toggle tiles and sliders that are real range inputs.
npx shadcn@latest add @vibebox/glass-controls

Controls

Tone

About

Settings that feel like a device. Use it for smart-home demos, dashboards and product tours.

Use it when

  • Quick settings, smart-home and device demos.

Skip it when

  • Forms that need validation or long labels.

Install

  1. 1Add the VibeBox registry to components.json:
    {
      "registries": {
        "@vibebox": "https://motion.vibeboxph.com/r/{name}.json"
      }
    }
  2. 2Add the component with the shadcn CLI:
    npx shadcn@latest add @vibebox/glass-controls

AI prompt

Everything an AI coding tool needs to rebuild this component the way it looks here: colours, fonts, the art it draws, props and rules.

Read the prompt
Build the VibeBox Motion component "Glass Controls" (Liquid glass) in a Next.js App Router project with Tailwind CSS v4.

What it is
- A control-centre panel: glass toggle tiles and sliders that are real range inputs.
- A control panel on glass: toggle tiles that switch to a bright face, and sliders built on native range inputs, so keyboard and screen readers work.

Install
- `npx shadcn@latest add @vibebox/glass-controls`
- Registry: add `"@vibebox": "https://motion.vibeboxph.com/r/{name}.json"` to components.json, then run the command. The source lands in `components/vibebox/glass-controls` and is yours to edit.

Visual spec
- Colours (the VibeBox spectrum, the same in light and dark):
    - --spectrum-1: #00c5fc (cyan)
    - --spectrum-2: #1d6ef0 (blue)
    - --spectrum-3: #5225ed (indigo)
    - --spectrum-4: #e434dc (magenta)
    - --spectrum-5: #fd3aa9 (pink)
    - --spectrum-6: #f98472 (coral)
    - --spectrum-7: #fcbe72 (amber)
- Ink surface (dark): background #05070c, card #0a0d15, text #f5f6fa, muted text #a3abbd, border rgba(255,255,255,0.08).
- Paper surface (light): background #f3f0ea, card #fbfaf7, text #0c0e14, muted text #4a4f5c, border rgba(12,14,20,0.12).
- Fonts:
    - Display and body: Figtree (400, 500, 600, 700), falling back to ui-sans-serif, system-ui.
    - Emphasis: Fraunces italic, for one or two words of a heading.
    - Labels and code: JetBrains Mono (400, 500), uppercase labels with 0.14em tracking.
- Shape: radius 8 to 12px on cards and panels; pills may be fully round. Hairline borders, soft glow instead of heavy shadow.
- Motion: animate transform and opacity only; fast ease-out (cubic-bezier(0.16, 1, 0.3, 1)), 140ms for micro-interactions, 320 to 520ms for reveals.

Art and media
- Built from HTML, CSS and inline SVG only; no image files or video.

Props
- `toggles`: `{ id: string; label: string; glyph: GlassGlyph; on?: boolean }[]`. The toggle tiles.
- `sliders`: `{ id: string; label: string; glyph: GlassGlyph; value: number }[]`. The sliders (0 to 100).
- `onToggle`: `(id: string, on: boolean) => void`. Called when a tile flips.
- `tone`: `"auto" | "dark" | "light"` (default "auto"). Tints the glass and sets the text colour for the page behind it.
- `className`: `string`. Merged with cn(); sets the width and margins.

Behaviour and access
- Reduced motion: The pointer sheen and sliding are off; state changes are instant.
- Keyboard: Tiles are buttons with aria-pressed; sliders are range inputs (arrow keys, Home, End).
- Screen readers: Toggles expose pressed state; sliders carry their label and value.
- Touch: Tiles are 68 px tall; sliders 44 px.
- Budget: about 3 KB of JavaScript, WebGL none, GPU low. It pauses while off screen.

Theming
The surface reads `--g-tint`, `--g-edge`, `--g-ink`, `--g-blur` and `--g-radius` from its root; the demo backdrop uses `--spectrum-*`.

Notes
Add the VibeBox Motion Glass Controls with `npx shadcn@latest add @vibebox/glass-controls`. Pass `toggles` and `sliders` with a glyph name each, listen to `onToggle`, and keep sliders as native range inputs so keyboard and assistive tech keep working.

Rules
- Keep the licence header on every installed file.
- Original code only: do not copy components from other libraries, and do not add GSAP.
- Use tokens for colour, type and radius; no hard-coded brand values.
- Server Components by default; `"use client"` only for the island.

Code

2 files
components/vibebox/glass-controls/glass-controls.client.tsx
/**
 * VibeBox Motion · Glass Controls
 * Copyright (c) 2026 VibeBox. Licensed under the VibeBox Motion Free License:
 * https://motion.vibeboxph.com/license
 * Use it in unlimited personal, commercial and client projects.
 * Do not sell or redistribute it as a component, kit, template or builder.
 */
"use client";

import { useState } from "react";
import { cn } from "@/lib/utils";
import { Glass, GlassGlyphIcon, type GlassGlyph } from "@/lib/vibebox/glass";

export type GlassToggle = { id: string; label: string; glyph: GlassGlyph; on?: boolean };

export type GlassControlsProps = {
  /** The toggle tiles, two per row. */
  toggles?: GlassToggle[];
  /** Slider names and their starting values (0 to 100). */
  sliders?: { id: string; label: string; glyph: GlassGlyph; value: number }[];
  onToggle?: (id: string, on: boolean) => void;
  tone?: "auto" | "dark" | "light";
  className?: string;
};

const TOGGLES: GlassToggle[] = [
  { id: "wifi", label: "Wi-Fi", glyph: "wifi", on: true },
  { id: "bluetooth", label: "Bluetooth", glyph: "bluetooth", on: true },
  { id: "focus", label: "Focus", glyph: "moon" },
  { id: "alerts", label: "Alerts", glyph: "bell", on: true },
];
const SLIDERS = [
  { id: "brightness", label: "Brightness", glyph: "sun" as GlassGlyph, value: 72 },
  { id: "volume", label: "Volume", glyph: "volume" as GlassGlyph, value: 40 },
];

/**
 * A control centre: glass tiles that switch on to a bright face, and sliders that are real range
 * inputs (so keyboard and screen readers work) with a glass track.
 */
export function GlassControls({ toggles = TOGGLES, sliders = SLIDERS, onToggle, tone, className }: GlassControlsProps) {
  const [state, setState] = useState<Record<string, boolean>>(() => Object.fromEntries(toggles.map((toggle) => [toggle.id, Boolean(toggle.on)])));
  const [levels, setLevels] = useState<Record<string, number>>(() => Object.fromEntries(sliders.map((slider) => [slider.id, slider.value])));

  return (
    <Glass as="section" data-vb="glass-controls" aria-label="Controls" tone={tone} still className={cn("w-full max-w-[20rem] p-3", className)}>
      <ul className="grid grid-cols-2 gap-2">
        {toggles.map((toggle) => {
          const on = Boolean(state[toggle.id]);
          return (
            <li key={toggle.id}>
              <button
                type="button"
                aria-pressed={on}
                onClick={() => {
                  setState((value) => ({ ...value, [toggle.id]: !on }));
                  onToggle?.(toggle.id, !on);
                }}
                className={cn(
                  "flex h-[4.25rem] w-full flex-col justify-between rounded-lg border p-2.5 text-left transition-colors duration-200 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring",
                  on ? "border-white/60 bg-white/85 text-[#101521] shadow-[0_8px_20px_-10px_rgb(255_255_255/0.6)]" : "border-white/20 bg-white/10 hover:bg-white/15",
                )}
              >
                <GlassGlyphIcon name={toggle.glyph} className="size-[1.15rem]" />
                <span className="flex items-center justify-between text-[0.8rem] font-medium">
                  {toggle.label}
                  <span className="font-mono text-[0.62rem] opacity-60">{on ? "On" : "Off"}</span>
                </span>
              </button>
            </li>
          );
        })}
      </ul>
      <ul className="mt-2 flex flex-col gap-2">
        {sliders.map((slider) => (
          <li key={slider.id} className="relative h-11 overflow-hidden rounded-lg border border-white/20 bg-white/10">
            <span aria-hidden="true" className="absolute inset-0 origin-left bg-white/70" style={{ transform: `scaleX(${(levels[slider.id] ?? 0) / 100})` }} />
            <GlassGlyphIcon name={slider.glyph} className="pointer-events-none absolute top-1/2 left-3 size-[1.1rem] -translate-y-1/2 mix-blend-difference" />
            <input
              type="range"
              min={0}
              max={100}
              value={levels[slider.id] ?? 0}
              aria-label={slider.label}
              onChange={(event) => setLevels((value) => ({ ...value, [slider.id]: Number(event.target.value) }))}
              className="absolute inset-0 size-full cursor-pointer appearance-none bg-transparent opacity-0 focus-visible:opacity-100 focus-visible:outline-2 focus-visible:outline-ring"
            />
          </li>
        ))}
      </ul>
    </Glass>
  );
}
components/vibebox/glass-controls/index.tsx
/**
 * VibeBox Motion · Glass Controls
 * Copyright (c) 2026 VibeBox. Licensed under the VibeBox Motion Free License:
 * https://motion.vibeboxph.com/license
 * Use it in unlimited personal, commercial and client projects.
 * Do not sell or redistribute it as a component, kit, template or builder.
 */
export * from "./glass-controls.client";

Props

PropTypeDefaultDescription
toggles{ id: string; label: string; glyph: GlassGlyph; on?: boolean }[]The toggle tiles.
sliders{ id: string; label: string; glyph: GlassGlyph; value: number }[]The sliders (0 to 100).
onToggle(id: string, on: boolean) => voidCalled when a tile flips.
tone"auto" | "dark" | "light""auto"Tints the glass and sets the text colour for the page behind it.
classNamestring—Merged with cn(); sets the width and margins.

Accessibility and motion

Keyboard
Tiles are buttons with aria-pressed; sliders are range inputs (arrow keys, Home, End).
Screen readers
Toggles expose pressed state; sliders carry their label and value.
Touch
Tiles are 68 px tall; sliders 44 px.
Reduced motion
The pointer sheen and sliding are off; state changes are instant.
  • A glass confirmation dialog with two buttons over a dimmed colourful page

    A confirmation dialog on glass over a dimmed scrim, with focus kept inside and Escape to cancel.

    Liquid glass · CSS

  • A row of glass app icons, the middle one enlarged, over a gradient

    A floating dock of glass icons that swell toward the pointer, with labels and badges.

    Liquid glass · CSS

  • A glass pill header with a wordmark, four links and a white call to action

    A floating pill header on glass with a highlight that glides between links.

    Liquid glass · CSS

↑ ↓ to move↵ to openesc to close