Components
A theme-aware image component that automatically swaps between light and dark screenshots based on the active color scheme.

import Screenshot from "@/components/ui/screenshot";
type Palette = {
bg: string;
chrome: string;
sidebar: string;
card: string;
cardBorder: string;
heading: string;
muted: string;
track: string;
accent: string;
accentSoft: string;
dot: string;
label: string;
};
const light: Palette = {
bg: "#ffffff",
chrome: "#f4f4f5",
sidebar: "#fafafa",
card: "#ffffff",
cardBorder: "#e4e4e7",
heading: "#18181b",
muted: "#a1a1aa",
track: "#ececee",
accent: "#f97316",
accentSoft: "#fdE7d6",
dot: "#d4d4d8",
label: "Analytics",
};
const dark: Palette = {
bg: "#09090b",
chrome: "#18181b",
sidebar: "#0f0f11",
card: "#141416",
cardBorder: "#27272a",
heading: "#fafafa",
muted: "#71717a",
track: "#27272a",
accent: "#fb923c",
accentSoft: "#3a2410",
dot: "#3f3f46",
label: "Analytics",
};
const dashboard = (p: Palette) => {
const bars = [140, 210, 175, 260, 300, 230, 320, 275, 360, 300, 390, 330];
const baseY = 690;
const barW = 40;
const gap = 20;
const startX = 300;
const barSvg = bars
.map((h, i) => {
const x = startX + i * (barW + gap);
const isPeak = i >= bars.length - 2;
return `<rect x='${x}' y='${baseY - h}' width='${barW}' height='${h}' rx='6' fill='${
isPeak ? p.accent : p.track
}'/>`;
})
.join("");
const nav = [0, 1, 2, 3, 4]
.map((i) => {
const y = 128 + i * 46;
const active = i === 0;
return (
`<rect x='20' y='${y}' width='170' height='34' rx='9' fill='${
active ? p.accentSoft : "transparent"
}'/>` +
`<circle cx='42' cy='${y + 17}' r='7' fill='${active ? p.accent : p.dot}'/>` +
`<rect x='60' y='${y + 11}' width='${active ? 96 : 110}' height='12' rx='6' fill='${
active ? p.accent : p.dot
}'/>`
);
})
.join("");
const cards = [0, 1, 2]
.map((i) => {
const x = 246 + i * 322;
return (
`<rect x='${x}' y='120' width='290' height='120' rx='16' fill='${p.card}' stroke='${p.cardBorder}' stroke-width='1.5'/>` +
`<rect x='${x + 24}' y='148' width='90' height='12' rx='6' fill='${p.muted}'/>` +
`<rect x='${x + 24}' y='176' width='128' height='24' rx='7' fill='${p.heading}'/>` +
`<rect x='${x + 24}' y='214' width='60' height='12' rx='6' fill='${p.accent}'/>`
);
})
.join("");
return (
"data:image/svg+xml," +
encodeURIComponent(
`<svg xmlns='http://www.w3.org/2000/svg' width='1248' height='765' viewBox='0 0 1248 765'>` +
`<rect width='1248' height='765' fill='${p.bg}'/>` +
// browser chrome
`<rect width='1248' height='44' fill='${p.chrome}'/>` +
`<circle cx='34' cy='22' r='6' fill='${p.dot}'/><circle cx='58' cy='22' r='6' fill='${p.dot}'/><circle cx='82' cy='22' r='6' fill='${p.dot}'/>` +
`<rect x='430' y='12' width='388' height='20' rx='10' fill='${p.bg}'/>` +
// sidebar
`<rect x='0' y='44' width='210' height='721' fill='${p.sidebar}'/>` +
`<line x1='210' y1='44' x2='210' y2='765' stroke='${p.cardBorder}' stroke-width='1.5'/>` +
`<rect x='20' y='72' width='30' height='30' rx='9' fill='${p.accent}'/>` +
`<rect x='60' y='82' width='84' height='12' rx='6' fill='${p.heading}'/>` +
nav +
// header
`<rect x='246' y='68' width='190' height='22' rx='7' fill='${p.heading}'/>` +
`<circle cx='1200' cy='79' r='16' fill='${p.card}' stroke='${p.cardBorder}' stroke-width='1.5'/>` +
// stat cards
cards +
// chart panel
`<rect x='246' y='268' width='966' height='458' rx='16' fill='${p.card}' stroke='${p.cardBorder}' stroke-width='1.5'/>` +
`<rect x='278' y='300' width='140' height='16' rx='8' fill='${p.heading}'/>` +
`<rect x='278' y='326' width='96' height='12' rx='6' fill='${p.muted}'/>` +
`<text x='1180' y='318' font-family='sans-serif' font-size='16' font-weight='600' fill='${p.accent}' text-anchor='end'>${p.label}</text>` +
barSvg +
`<line x1='278' y1='691' x2='1180' y2='691' stroke='${p.cardBorder}' stroke-width='1.5'/>` +
`</svg>`,
)
);
};
export default function ScreenshotDemo() {
return (
<div className="flex w-full items-center justify-center bg-background p-8">
<div className="w-full max-w-3xl overflow-hidden rounded-xl border border-border shadow-lg">
<Screenshot
srcLight={dashboard(light)}
srcDark={dashboard(dark)}
alt="Theme-aware analytics dashboard screenshot"
width={1248}
height={765}
loading="eager"
className="w-full"
/>
</div>
</div>
);
}
Part of Launch UI — browse the full library on 21st.dev.