---
name: inner-glow-panels
description: Use when you want "Futuristic, premium, dramatic" - Adds internal glow or light bleeding inside panels.
---

# Inner Glow Panels

> **Category:** Glass / Depth  -  **Personality:** Futuristic, premium, dramatic
>
> **Best use:** Dark UI, AI products, gaming
>
> **Ratings:** Professional 3/5 - Casual 3/5 - Exotic 5/5 - Visual impact 4/5 - Difficulty 3/5 - Performance cost 3/5

## What it is

Inner Glow Panels make a surface look **lit from within** — light bleeds inward from the panel's edges and pools near the top, as if the panel itself is a source of energy rather than a flat card. The entire effect is built from layered `inset` box-shadows plus a soft `radial-gradient`, so it stays pure CSS and GPU-cheap. It only reads on a **dark UI**, which is exactly where it belongs: AI consoles, telemetry dashboards and game HUDs, where it feels futuristic and premium. An optional pointer-tracked light makes the glow follow the cursor for a panel that "reacts to you".

## Dependencies / CDN

None for the effect itself — **pure CSS**. The interactive pointer-spotlight is ~15 lines of **vanilla JS** (no library). The demo loads display/body fonts, which are optional:

```html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Chakra+Petch:wght@500;600;700&family=JetBrains+Mono:wght@400;500&family=Sora:wght@400;500;600&display=swap" rel="stylesheet">
```

## HTML

A panel is just a box. `data-spot` opts it into the pointer-tracked light; the `--glow` custom property themes its colour.

```html
<div class="stage">
  <section class="panel" data-spot>
    <span class="label">Live throughput</span>
    <strong class="metric">12,480 <small>tok/s</small></strong>
  </section>

  <!-- re-theme any panel with one variable -->
  <section class="panel" data-spot style="--glow:139,108,255">
    <span class="label">Avg latency</span>
    <strong class="metric">214 ms</strong>
  </section>
</div>
```

## CSS

```css
/* Theme every panel through ONE custom property (an "R,G,B" triplet). */
.panel{
  --glow: 47,230,218;                              /* ion cyan */
  position: relative;
  border-radius: 18px;                             /* inset glow is clipped to this radius */
  background:
    radial-gradient(150% 120% at 50% -18%, rgba(var(--glow),.13), transparent 56%),
    linear-gradient(180deg, rgba(255,255,255,.035), rgba(255,255,255,.012));
  border: 1px solid rgba(var(--glow),.16);
  box-shadow:
    inset 0 1px 0   rgba(255,255,255,.06),          /* lit top edge             */
    inset 0 0 34px -8px  rgba(var(--glow),.38),      /* soft glow off every rim  */
    inset 0 30px 64px -46px rgba(var(--glow),.9),    /* light pooling near top   */
    0 26px 50px -32px rgba(0,0,0,.85);               /* float above the page     */
  transition: box-shadow .45s ease, border-color .45s ease;
}
.panel:hover{                                        /* glow intensifies on hover */
  border-color: rgba(var(--glow),.36);
  box-shadow:
    inset 0 1px 0   rgba(255,255,255,.09),
    inset 0 0 52px -6px  rgba(var(--glow),.6),
    inset 0 32px 74px -42px rgba(var(--glow),1),
    0 32px 60px -30px rgba(0,0,0,.92);
}

/* Optional: a light source that follows the pointer INSIDE the panel. */
.panel[data-spot]::before{
  content:""; position:absolute; inset:0; border-radius:inherit;
  pointer-events:none; z-index:0; mix-blend-mode:screen;
  background: radial-gradient(260px 260px at var(--mx,50%) var(--my,12%),
              rgba(var(--glow),.22), transparent 68%);
  opacity:0; transition:opacity .4s ease;
}
.panel[data-spot]:hover::before{ opacity:1 }
.panel > *{ position:relative; z-index:1 }          /* keep content ABOVE the glow */

/* The effect only reads on a dark surface. */
.stage{ background:#05060c; padding:40px; border-radius:24px; isolation:isolate }
```

## JavaScript

Only needed for the pointer-tracked spotlight — the static glow is 100% CSS. It writes two CSS variables (`--mx`/`--my`), is `requestAnimationFrame`-throttled, and bails out under reduced-motion.

```js
document.querySelectorAll('.panel[data-spot]').forEach(function(el){
  if (matchMedia('(prefers-reduced-motion: reduce)').matches) return;
  var raf = 0, mx = 50, my = 12;
  el.addEventListener('pointermove', function(e){
    var r = el.getBoundingClientRect();
    mx = ((e.clientX - r.left) / r.width)  * 100;
    my = ((e.clientY - r.top)  / r.height) * 100;
    if (!raf) raf = requestAnimationFrame(function(){
      el.style.setProperty('--mx', mx.toFixed(1) + '%');
      el.style.setProperty('--my', my.toFixed(1) + '%');
      raf = 0;
    });
  });
});
```

## How it works

- **`box-shadow: inset` is the whole effect.** An inset shadow paints *inside* the border-box; a soft, blurred one reads as light bleeding in from the rim.
- **Negative spread** (the `-8px` / `-46px` values) is the trick — it shrinks the shadow so it hugs the edges as a glowing rim instead of flooding the entire panel. Drop the negative spread and the panel just goes solid.
- **A top `radial-gradient`** in the `background` adds a brighter "pool" where the light gathers, layered over a faint glass gradient so the surface isn't flat.
- **The pointer spotlight** is a `::before` radial-gradient positioned at `--mx`/`--my`; `mix-blend-mode: screen` makes it *add* light to whatever is beneath rather than paint a flat disc over it.
- **One `--glow` triplet** (`R,G,B`) drives the border, all the shadows, and the spotlight — so a single inline `style="--glow:…"` re-themes a whole panel (cyan / violet / magenta in the demo).

## Customization

- **Glow colour:** change `--glow` to any `R,G,B` triplet — set it per-panel via inline style for a multi-colour grid.
- **Intensity:** raise the rgba alphas (`.38 → .6`) or pull the spread less negative for a hotter, fuller glow; lower them for a whisper.
- **Where the light sits:** move the `radial-gradient(... at 50% -18%)` position to pool light from a corner or the bottom.
- **Calm vs. dramatic:** remove the `:hover` block for a static panel, or drop `data-spot` to disable the cursor light entirely (pure CSS, zero JS).
- **Pair it:** add a `radial-gradient` "core" element (the demo's reactor orb) using the same inset-highlight + outer-glow recipe for a focal point.

## Accessibility & performance

- `box-shadow` is **paint-bound**, so animate it sparingly. Here it only transitions on hover (a one-off) and the live "breathing" is on a small element — never animate the blur radius across many panels at once.
- The pointer handler is **paint-only and rAF-throttled** (it just sets two CSS vars), so it never triggers layout/reflow.
- **Respect `prefers-reduced-motion`:** the JS returns early, and CSS freezes the equalizer/orb/ring animations to a tasteful static frame.
- **Contrast:** body copy sits in a higher stacking context (`z-index:1`) above the glow, and the glow itself is low-alpha, so text legibility holds. Verify your text colour against the brightest part of the pool.

## Gotchas

- **Needs a dark surface.** On white/light backgrounds the inner glow is invisible — this is a dark-UI effect by nature.
- **Inset shadows are clipped to `border-radius`.** Forget the radius and the glow squares off at the corners.
- **Positive spread floods the panel.** The soft inner rim depends on *negative* spread; `inset 0 0 34px 0` (no negative) fills the whole box instead of hugging the edges.
- **Keep content above the glow.** Without `.panel > *{position:relative;z-index:1}`, the `::before` spotlight paints over your text. The `mix-blend-mode:screen` spotlight also needs a not-fully-opaque-white backdrop to blend against.
- **Give the dark wrapper `isolation:isolate`** when it has `overflow:hidden`+`border-radius`, so blend modes and stacking stay contained to the stage.
