Overlay

A contextual scrim anchored to a media element, for presenting info/actions specific to what's behind it (a caption on a photo, a "Play" button on a video thumbnail, hover actions on a gallery grid item).

Real use · caption scrim, always visible

Mountain sunrise

Banff National Park

Placement · top / bottom / full

top
bottom
full

reveal="hover" · gallery grid, scrim fades in on hover/focus

Using Overlay

Silica ships one design system through three paths. They share a class vocabulary but are not interchangeable — pick the one that matches what you are building.

Plain HTML — CSS classes

The Tailwind v4 plugin. Works in any framework or none — Rails, Django, PHP, Go, a static file. This path is complete for Overlay — it needs no JavaScript, so the classes below are the whole story.

/* in your CSS */
@import "tailwindcss";
@plugin "@wizeworks/silicaui";

Root class overlay, with 1 modifier:

  • .overlay
  • .overlay-scrim

React

Typed components over those same classes, with behavior and accessibility from Base UI.

npm i @wizeworks/silicaui-react
import { Overlay } from "@wizeworks/silicaui-react";

Props

PropTypeRequiredDescription
placementOverlayPlacementnoWhere the scrim sits over the media. Default "bottom".
revealOverlayRevealno"always" (default) shows the scrim outright; "hover" fades it in on hover/focus.
overlayReact.ReactNodeyesThe scrim's content — a caption, badges, action buttons, ….
childrenReact.ReactNodeyesThe underlying media — an <img>, <video>, or any element to scrim over.

Source of the demo above

The exact file that renders the examples above, so it can never drift from them. It imports a few of this site’s own layout helpers (../lib/…), so read it for the component calls rather than copying it whole.

Show source
import { Overlay, Badge, Button } from "@wizeworks/silicaui-react";
import { Section, Row } from "../lib/Section";

function placeholder(label: string, from: string, to: string): string {
    return `data:image/svg+xml,${encodeURIComponent(
        `<svg xmlns='http://www.w3.org/2000/svg' width='400' height='260'>` +
            `<defs><linearGradient id='g' x1='0' y1='0' x2='1' y2='1'>` +
            `<stop offset='0' stop-color='${from}'/><stop offset='1' stop-color='${to}'/>` +
            `</linearGradient></defs>` +
            `<rect width='400' height='260' fill='url(#g)'/>` +
            `</svg>`,
    )}`;
}

export function OverlayDemo() {
    return (
        <>
            <Section title="Real use · caption scrim, always visible">
                <div className="max-w-sm">
                    <Overlay
                        className="rounded-box"
                        overlay={
                            <div>
                                <h3 className="text-base font-semibold">Mountain sunrise</h3>
                                <p className="text-sm opacity-80">Banff National Park</p>
                            </div>
                        }
                    >
                        <img src={placeholder("1", "#0ea5e9", "#1e3a8a")} alt="" />
                    </Overlay>
                </div>
            </Section>

            <Section title="Placement · top / bottom / full">
                <Row>
                    {(["top", "bottom", "full"] as const).map((placement) => (
                        <div key={placement} className="w-48">
                            <Overlay
                                className="rounded-box"
                                placement={placement}
                                overlay={<Badge color="primary">{placement}</Badge>}
                            >
                                <img src={placeholder(placement, "#f97316", "#7c2d12")} alt="" />
                            </Overlay>
                        </div>
                    ))}
                </Row>
            </Section>

            <Section title="reveal=&quot;hover&quot; · gallery grid, scrim fades in on hover/focus">
                <div className="grid max-w-md grid-cols-3 gap-2">
                    {["10b981-06b6d4", "8b5cf6-ec4899", "f59e0b-ef4444"].map((pair, i) => {
                        const [from, to] = pair.split("-").map((c) => `#${c}`);
                        return (
                            <Overlay
                                key={pair}
                                className="rounded-field"
                                reveal="hover"
                                placement="full"
                                overlay={
                                    <Button size="sm" color="primary">
                                        View
                                    </Button>
                                }
                            >
                                <img src={placeholder(`g${i}`, from!, to!)} alt="" />
                            </Overlay>
                        );
                    })}
                </div>
            </Section>
        </>
    );
}

Vanilla — node tree to HTML

Overlay — pure CSS ([data-reveal="hover"]:hover .overlay-scrim); no behavior at all (confirmed this pass). Build a node tree, project it with toHtml(), and let the zero-dependency runtime hydrate it. This is the path for generated or user-authored documents — a site builder, CMS output, a static export.

npm i @wizeworks/silicaui-html @wizeworks/silicaui-behaviors
import { atom, toHtml } from "@wizeworks/silicaui-html";

const node = atom("Overlay", "overlay", { /* props */ }, [/* children */]);
const markup = toHtml(node);

Styling on this path is the class string in the second argument — the same classes as the Plain HTML section above. There is no color prop here; write "overlay overlay-primary" rather than passing a colour as a prop.

Takes children
yes
Hydrated by
nothing — this one is static markup

The full API for Overlay, on every path, is available through the Silica MCP server.