Library / Buttons

Glass lens

Price: Free

A clear lens with a lit rim and a cast shadow that moves as it lifts and presses. It shows what's behind it, so it's made for sitting over a picture or a 3D scene, such as Gemstones or Ice Cubes. Add on-light on a pale page. Browsers without backdrop-filter get a solid tint. Round and a switch are included.

Get Started

Send to Studio

Copy the CSS once and the markup wherever you need it. Set --c to change the color and --ink for the label; all the shading follows from those. It uses system fonts, so it needs nothing from us; set --btn-font to use your own. Covered by the Atomic Socket License.

React
{/* a link or a button */}
<a className="glass" href="/start">
  <span className="glass-body">
    <span className="glass-label">Get Started</span>
  </span>
</a>

{/* round, for short words */}
<button type="button" className="glass round">
  <span className="glass-body">
    <span className="glass-label">Go</span>
  </span>
</button>

{/* a switch */}
<label className="glass-toggle">
  <input type="checkbox" role="switch" aria-label="Sound" defaultChecked />
  <span className="glass-body"></span>
  <span className="glass-knob-shadow"></span>
  <span className="glass-knob"></span>
</label>
HTML
<!-- a link or a button -->
<a class="glass" href="/start">
  <span class="glass-body">
    <span class="glass-label">Get Started</span>
  </span>
</a>

<!-- round, for short words -->
<button type="button" class="glass round">
  <span class="glass-body">
    <span class="glass-label">Go</span>
  </span>
</button>

<!-- a switch -->
<label class="glass-toggle">
  <input type="checkbox" role="switch" aria-label="Sound" checked>
  <span class="glass-body"></span>
  <span class="glass-knob-shadow"></span>
  <span class="glass-knob"></span>
</label>
CSS
/* Buttons by EELUMA: eeluma.com/library/buttons */
/* Glass: a clear lens with a cast shadow, for sitting over a picture or a 3D scene */
.glass, .glass *, .glass-toggle, .glass-toggle * { box-sizing: border-box; }
.glass, .glass-toggle {
  --c: #dff1ff; --ink: #fff;
  /* how colored the glass is, 0 (clear, tinted only at the edge) to 1 (deep colored glass) */
  --tint: 0;
  --rim: rgba(255, 255, 255, .92); --rim-dim: rgba(255, 255, 255, .16); --rim-w: 1.25px;
  --glow: rgba(255, 255, 255, .62);
  --shade: rgba(0, 0, 0, .55);
  /* the color as glass: darker and richer the more it's tinted */
  --deep: var(--c);
  /* the more tinted, the less the glass lightens what's behind it, so a deep color doesn't go milky */
  --back: saturate(calc(135% + var(--tint) * 105%)) brightness(calc(1.07 - var(--tint) * .02));
  position: relative; display: inline-block; padding: 0; border: 0; background: none;
  border-radius: 999px; cursor: pointer; font: inherit; text-decoration: none; -webkit-tap-highlight-color: transparent;
}
/* The cast shadow falls below and to the right, away from the window */
.glass::before, .glass-toggle::before, .glass-knob-shadow {
  content: ""; position: absolute; inset: 34% -3% -14px 8%; border-radius: inherit;
  background: color-mix(in oklab, var(--shade), color-mix(in oklab, var(--c), black 25%) calc(var(--tint) * 55%)); filter: blur(12px);
  transition: transform 220ms ease, opacity 220ms ease, filter 220ms ease;
}
/* The caustic: light through the glass, focused inside the shadow */
.glass::after, .glass-toggle::after {
  content: ""; position: absolute; left: 22%; right: 2%; bottom: -13px; height: 20px; border-radius: 50%;
  background: radial-gradient(closest-side, color-mix(in oklab, var(--c), white calc(30% - var(--tint) * 20%)), transparent);
  filter: blur(5px); opacity: calc(.34 + var(--tint) * .3);
  transition: transform 220ms ease, opacity 220ms ease;
}
/* The lens: clear in the middle, the tint gathering toward the edge where the glass is thickest. Tinted, it stays
   see-through (the shadow shows under it), in the pure color with no white in it */
.glass-body, .glass-knob {
  position: relative; display: flex; align-items: center; justify-content: center; gap: .6em;
  overflow: hidden; border-radius: inherit;
  background: radial-gradient(120% 130% at 50% 45%, color-mix(in oklch, var(--deep) calc(1% + var(--tint) * 66%), transparent) calc(55% - var(--tint) * 10%), color-mix(in oklch, var(--deep) calc(15% + var(--tint) * 82%), transparent));
  -webkit-backdrop-filter: var(--back);
  backdrop-filter: var(--back);
  box-shadow:
    inset -3px -4px 7px -4px color-mix(in srgb, var(--glow) calc(100% - var(--tint) * 75%), transparent),
    inset 0 0 10px -5px color-mix(in oklab, var(--c) 70%, transparent);
  transition: transform 180ms cubic-bezier(.3, 1.5, .5, 1);
}
.glass-body { min-height: 58px; padding: 0 2em; }
/* A glass capsule (after Cassie's reference, 2026-11-11): clear through the middle, the glass's thickness as a
   deeper band round the edge (heaviest along the bottom), a long soft highlight bar across the top and a fainter one
   lower left, and two small glints, top left and bottom right. Frost draws its own background, so it keeps its look. */
.glass:not(.frost) .glass-body {
  /* the soft shadow lines inside the glass: grey when clear, the glass's own deeper color when tinted, never black */
  --line: color-mix(in oklch, color-mix(in oklch, var(--c), #39414c calc(70% - var(--tint) * 40%)) calc(28% + var(--tint) * 40%), transparent);
  background:
    radial-gradient(closest-side, rgba(255, 255, 255, .9), transparent) 13% 24% / 18px 11px no-repeat,
    radial-gradient(closest-side, rgba(255, 255, 255, .8), transparent) 88% 80% / 14px 9px no-repeat,
    radial-gradient(closest-side, var(--line), transparent) 50% 9% / 86% 5px no-repeat,
    radial-gradient(closest-side, rgba(255, 255, 255, .62), rgba(255, 255, 255, .25) 70%, transparent) 52% 19% / 80% 6px no-repeat,
    radial-gradient(closest-side, var(--line), transparent) 50% 30% / 84% 6px no-repeat,
    radial-gradient(closest-side, rgba(255, 255, 255, .22), transparent) 34% 58% / 44% 6px no-repeat,
    radial-gradient(130% 160% at 50% 42%, color-mix(in oklch, var(--deep) calc(1% + var(--tint) * 64%), transparent) calc(50% - var(--tint) * 12%), color-mix(in oklch, var(--deep) calc(18% + var(--tint) * 82%), transparent));
  box-shadow:
    inset 0 -5px 9px -4px color-mix(in oklab, var(--line), transparent 20%),
    inset 0 0 0 1px color-mix(in oklab, var(--deep) calc(20% + var(--tint) * 50%), transparent),
    inset -3px -4px 7px -4px color-mix(in srgb, var(--glow) calc(100% - var(--tint) * 75%), transparent),
    inset 0 0 10px -5px color-mix(in oklab, var(--c) 70%, transparent);
}
/* The rim: bright where it faces the window and across from it, nearly gone in between */
.glass-body::before, .glass-knob::before {
  content: ""; position: absolute; inset: 0; border-radius: inherit; padding: var(--rim-w); pointer-events: none;
  background: conic-gradient(from 315deg, var(--rim), var(--rim-dim) 80deg, var(--rim-dim) 110deg, var(--rim) 180deg, var(--rim-dim) 260deg, var(--rim-dim) 290deg, var(--rim));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
}
/* The window: a crescent that follows the edge of any shape, cut into panes by thin bars, fading along the curve */
.glass-body::after, .glass-knob::after {
  content: ""; position: absolute; inset: 2px; border-radius: inherit; pointer-events: none;
  box-shadow: inset 4px 4px 4px -2.5px rgba(255, 255, 255, .95);
  -webkit-mask: repeating-linear-gradient(45deg, #000 0 11px, transparent 11px 12.5px), linear-gradient(135deg, #000 8%, transparent 46%);
  -webkit-mask-composite: source-in;
  mask: repeating-linear-gradient(45deg, #000 0 11px, transparent 11px 12.5px) intersect, linear-gradient(135deg, #000 8%, transparent 46%);
}
/* The lens's window is one smooth crescent, no pane bars (Cassie, 2026-11-11); frost keeps the panes */
.glass:not(.frost) .glass-body::after {
  -webkit-mask: linear-gradient(135deg, #000 8%, transparent 46%);
  mask: linear-gradient(135deg, #000 8%, transparent 46%);
}
/* The letters are a little see-through, so what's behind the glass shows in them */
.glass-label {
  position: relative; z-index: 1; color: var(--ink); opacity: .8; filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .4));
  white-space: nowrap; font: 500 19px/1 var(--btn-font, system-ui, -apple-system, "Segoe UI", sans-serif); letter-spacing: .01em;
}

/* Lifted: it rises, so its shadow moves away and softens */
.glass:hover .glass-body { transform: translateY(-3px); }
.glass:hover::before { transform: translate(2px, 5px); opacity: .8; filter: blur(15px); }
.glass:hover::after { transform: translate(2px, 6px); opacity: .46; }
/* Pressed: down onto its own shadow, which tightens under it */
.glass:active .glass-body { transform: translateY(3px) scale(.985); transition-duration: 45ms; }
.glass:active::before { transform: translate(-3px, -8px); filter: blur(6px); transition-duration: 45ms; }
.glass:active::after { transform: translate(-3px, -9px); opacity: .22; transition-duration: 45ms; }
.glass:focus-visible { outline: 2px solid var(--btn-focus, #7db4ff); outline-offset: 5px; }

/* Shapes */
.glass.round .glass-body { width: 66px; height: 66px; min-height: 0; padding: 0; }
.glass.round::before { inset: 36% -6% -14px 14%; }
.glass.round::after { left: 30%; right: 2%; }

/* The switch: a glass knob sliding in a glass track, its own shadow going with it */
.glass-toggle { display: inline-block; width: 96px; height: 54px; }
.glass-toggle input { position: absolute; inset: 0; z-index: 3; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.glass-toggle .glass-body { position: absolute; inset: 0; min-height: 0; padding: 0; transition: background 220ms ease; }
.glass-toggle input:checked ~ .glass-body { background: color-mix(in oklab, var(--c) 38%, transparent); }
.glass-knob, .glass-knob-shadow { position: absolute; left: 6px; top: 6px; width: 42px; height: 42px; border-radius: 50%; transition: transform 240ms cubic-bezier(.3, 1.4, .5, 1); }
.glass-knob { z-index: 2; }
.glass-knob-shadow { inset: auto; left: 10px; top: 13px; z-index: 1; filter: blur(5px); opacity: .9; }
.glass-toggle input:checked ~ .glass-knob, .glass-toggle input:checked ~ .glass-knob-shadow { transform: translateX(42px); }
.glass-toggle:has(input:focus-visible) { outline: 2px solid var(--btn-focus, #7db4ff); outline-offset: 5px; }

/* On a pale page the rim and label go dark, or the glass disappears */
.glass.on-light, .glass-toggle.on-light { --ink: #16202b; --rim: rgba(20, 40, 60, .55); --rim-dim: rgba(20, 40, 60, .16); --shade: rgba(20, 30, 45, .3); --glow: #fff; }
.glass.on-light .glass-label { filter: none; }
/* No backdrop-filter: more color, so the label still reads */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .glass-body, .glass-knob { background: color-mix(in oklab, var(--c) 45%, #223); }
}
@media (prefers-reduced-motion: reduce) {
  .glass::before, .glass::after, .glass-body, .glass-knob, .glass-knob-shadow, .glass-toggle::before, .glass-toggle::after { transition: none; }
}