Siteplane UI

Glass

A translucent variant of Button, Tooltip, Popover, Menu, Context Menu and Card for surfaces over a picture.

Markdown

Glass is a variant of the components on this page, not a component of its own. Set variant="glass" and the surface shows a darkened, blurred view of what lies behind it, with light text. It looks the same in Light and Dark. Shape, spacing, states and motion stay those of the component.

Glass needs something behind it. Use it over a photo, a video, a map or a color surface; on a plain page it shows almost nothing, so keep the default surface there. The gradient in these examples only makes the glass visible. It is not part of any component.

Installation

Glass comes with each component. To install every component on this page together with what its example uses:

pnpm dlx shadcn@4.18.0 add @siteplane/button @siteplane/tooltip @siteplane/popover @siteplane/menu @siteplane/context-menu @siteplane/card @siteplane/label @siteplane/switch

Button

Install and API: Button.

The gradient only makes the glass visible. It is not part of the component.

"use client";

import { PlayIcon, ShareIcon } from "lucide-react";
import { Button, IconButton } from "@/siteplane/components/button";

export default function Example() {
  return (
    <div className="flex flex-wrap items-center justify-center gap-3">
      <Button variant="glass">
        <PlayIcon />
        Watch trailer
      </Button>
      <Button variant="glass">Add to list</Button>
      <IconButton icon={<ShareIcon />} label="Share" variant="glass" />
    </div>
  );
}

Tooltip

Install and API: Tooltip.

The gradient only makes the glass visible. It is not part of the component.

"use client";

import { CopyIcon } from "lucide-react";
import { Button } from "@/siteplane/components/button";
import {
  Tooltip,
  TooltipPopup,
  TooltipProvider,
  TooltipTrigger,
} from "@/siteplane/components/tooltip";

export default function Example() {
  return (
    <TooltipProvider>
      <Tooltip>
        <TooltipTrigger
          render={
            <Button aria-label="Copy link" shape="square" variant="glass" />
          }
        >
          <CopyIcon />
        </TooltipTrigger>
        <TooltipPopup variant="glass">Copy link</TooltipPopup>
      </Tooltip>
    </TooltipProvider>
  );
}

Popover

Install and API: Popover.

The gradient only makes the glass visible. It is not part of the component.

"use client";

import { Button } from "@/siteplane/components/button";
import {
  Popover,
  PopoverDescription,
  PopoverPopup,
  PopoverTitle,
  PopoverTrigger,
} from "@/siteplane/components/popover";

export default function Example() {
  return (
    <Popover>
      <PopoverTrigger render={<Button variant="glass" />}>Share</PopoverTrigger>
      <PopoverPopup className="w-72" variant="glass">
        <PopoverTitle>Share link</PopoverTitle>
        <PopoverDescription>
          Anyone with the link can view this page.
        </PopoverDescription>
      </PopoverPopup>
    </Popover>
  );
}

Install and API: Menu. Dropdown Menu takes the same variant on DropdownMenuContent.

The gradient only makes the glass visible. It is not part of the component.

"use client";

import { CopyIcon, PencilIcon, ShareIcon, Trash2Icon } from "lucide-react";
import { Button } from "@/siteplane/components/button";
import {
  Menu,
  MenuItem,
  MenuPopup,
  MenuSeparator,
  MenuTrigger,
} from "@/siteplane/components/menu";

export default function Example() {
  return (
    <Menu>
      <MenuTrigger render={<Button variant="glass" />}>Options</MenuTrigger>
      <MenuPopup variant="glass">
        <MenuItem>
          <PencilIcon aria-hidden="true" />
          Edit
        </MenuItem>
        <MenuItem>
          <CopyIcon aria-hidden="true" />
          Duplicate
        </MenuItem>
        <MenuItem>
          <ShareIcon aria-hidden="true" />
          Share
        </MenuItem>
        <MenuSeparator />
        <MenuItem variant="destructive">
          <Trash2Icon aria-hidden="true" />
          Delete
        </MenuItem>
      </MenuPopup>
    </Menu>
  );
}

Context Menu

Install and API: Context Menu.

The gradient only makes the glass visible. It is not part of the component.

Right-click or long-press here
"use client";

import { CopyIcon, PencilIcon, ShareIcon, Trash2Icon } from "lucide-react";
import { Card } from "@/siteplane/components/card";
import {
  ContextMenu,
  ContextMenuItem,
  ContextMenuPopup,
  ContextMenuSeparator,
  ContextMenuTrigger,
} from "@/siteplane/components/context-menu";

export default function Example() {
  return (
    <ContextMenu>
      <ContextMenuTrigger
        className="h-28 w-full max-w-sm items-center justify-center text-sm"
        render={<Card variant="glass" />}
        tabIndex={0}
      >
        Right-click or long-press here
      </ContextMenuTrigger>
      <ContextMenuPopup variant="glass">
        <ContextMenuItem>
          <PencilIcon aria-hidden="true" />
          Edit
        </ContextMenuItem>
        <ContextMenuItem>
          <CopyIcon aria-hidden="true" />
          Copy
        </ContextMenuItem>
        <ContextMenuItem>
          <ShareIcon aria-hidden="true" />
          Share
        </ContextMenuItem>
        <ContextMenuSeparator />
        <ContextMenuItem variant="destructive">
          <Trash2Icon aria-hidden="true" />
          Delete
        </ContextMenuItem>
      </ContextMenuPopup>
    </ContextMenu>
  );
}

Card

Install and API: Card.

The gradient only makes the glass visible. It is not part of the component.

Notifications
Choose what reaches you.
"use client";

import {
  Card,
  CardDescription,
  CardHeader,
  CardPanel,
  CardTitle,
} from "@/siteplane/components/card";
import { Label } from "@/siteplane/components/label";
import { Switch } from "@/siteplane/components/switch";

export default function Example() {
  return (
    <Card className="w-full max-w-md" variant="glass">
      <CardHeader>
        <CardTitle>Notifications</CardTitle>
        <CardDescription>Choose what reaches you.</CardDescription>
      </CardHeader>
      <CardPanel className="grid gap-3">
        <Label className="w-full cursor-pointer justify-between">
          Live presence
          <Switch defaultChecked />
        </Label>
        <Label className="w-full cursor-pointer justify-between">
          Weekly digest
          <Switch />
        </Label>
      </CardPanel>
    </Card>
  );
}

Card and text

Glass is a dark card with light text by default. It reads on dark and colorful backgrounds and on most light ones. Secondary text and icons take shares of the text color, and destructive items use the lighter red of Dark.

For a lighter card, set data-glass-tone="light". For dark text, set data-glass-ink="dark". Set them on a glass component, or on any element to change every glass component inside it. A popup renders outside its trigger, so set them on the popup itself.

<section data-glass-tone="light" data-glass-ink="dark">
  <Card variant="glass">…</Card>
</section>

<MenuPopup data-glass-tone="light" data-glass-ink="dark" variant="glass">
  …
</MenuPopup>

How well text reads still depends on what lies behind the glass. Check the contrast on your own content, especially on very light pictures, and keep longer text on the default surface.