26 CSS Circular Menus16 / 26

CSS + JSMIT licensed

Glowing Cyberpunk Radial UI

A sci-fi HUD ring: a scanning sweep rotates behind six neon nodes, dashed orbit rails counter-rotate at different speeds, and the whole thing hums with layered box-shadow glow. Every glow is pure CSS — no images, no canvas — making this the reference for game UIs, dashboards that want drama, and portfolio pieces.

Published Updated

Live Demo
Try it

The code

<section class="ccm-16" aria-label="Cyberpunk radial HUD demo">
  <div class="ccm-16__stage">
    <nav class="ccm-16__hud" style="--n:6" aria-label="Ship systems">
      <span class="ccm-16__scan" aria-hidden="true"></span>
      <span class="ccm-16__rail ccm-16__rail--a" aria-hidden="true"></span>
      <span class="ccm-16__rail ccm-16__rail--b" aria-hidden="true"></span>
      <ul class="ccm-16__nodes">
        <li style="--i:0"><button type="button" class="is-active" aria-pressed="true" data-sys="NAV — plotting course">NAV</button></li>
        <li style="--i:1"><button type="button" aria-pressed="false" data-sys="SHIELDS — 94% integrity">SHD</button></li>
        <li style="--i:2"><button type="button" aria-pressed="false" data-sys="WEAPONS — safeties on">WPN</button></li>
        <li style="--i:3"><button type="button" aria-pressed="false" data-sys="COMMS — channel open">COM</button></li>
        <li style="--i:4"><button type="button" aria-pressed="false" data-sys="ENGINES — 62% thrust">ENG</button></li>
        <li style="--i:5"><button type="button" aria-pressed="false" data-sys="SENSORS — sweep active">SNS</button></li>
      </ul>
      <div class="ccm-16__core"><em>SYSTEM</em><output class="ccm-16__read" aria-live="polite">NAV — plotting course</output></div>
    </nav>
  </div>
</section>
.ccm-16,
.ccm-16 *,
.ccm-16 *::before,
.ccm-16 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.ccm-16 {
  width: 100%;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: radial-gradient(circle at 50% 45%,oklch(0.22 0.03 240),var(--bg) 70%);
  --neon: oklch(0.85 0.15 190);
  --neon-dim: oklch(0.85 0.15 190 / .25);
  --bg: oklch(0.16 0.02 250);
  font-family: ui-monospace,'Cascadia Code',Menlo,monospace;
}

.ccm-16__stage {
  width: 100%;
  height: 100vh;
  position: relative;
  display: grid;
  place-items: center;
  border-radius: 20px;
  overflow: hidden;
  background: radial-gradient(circle at 50% 45%,oklch(0.22 0.03 240),var(--bg) 70%);
}

.ccm-16__stage::before {
  content: "";
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(0deg,transparent 0 3px,oklch(0 0 0 / .12) 3px 4px);
  pointer-events: none;
}

.ccm-16__hud {
  position: relative;
  width: 360px;
  height: 360px;
  display: grid;
  place-items: center;
}

.ccm-16__scan {
  position: absolute;
  inset: 14px;
  border-radius: 50%;
  background: conic-gradient(from 0deg,transparent 78%,var(--neon-dim) 98%,oklch(0.85 0.15 190 / .5));
  animation: ccm16-rot 6s linear infinite;
  mask: radial-gradient(circle,transparent 34%,black 35%);
}

.ccm-16__rail {
  position: absolute;
  border-radius: 50%;
}

.ccm-16__rail--a {
  inset: 0;
  border: 1px dashed oklch(0.7 0.1 190 / .4);
  animation: ccm16-rot 60s linear infinite;
}

.ccm-16__rail--b {
  inset: 38px;
  border: 1px dashed oklch(0.7 0.1 190 / .25);
  animation: ccm16-rot 45s linear infinite reverse;
}

.ccm-16__nodes {
  position: absolute;
  inset: 0;
  list-style: none;
}

.ccm-16__nodes li {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  pointer-events: none;
  --a: calc((360deg / var(--n)) * var(--i) - 90deg);
  translate: calc(cos(var(--a)) * 152px) calc(sin(var(--a)) * 152px);
}

.ccm-16__nodes button {
  pointer-events: auto;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  border: 1px solid oklch(0.7 0.12 190 / .6);
  background: oklch(0.2 0.03 230 / .9);
  color: var(--neon);
  font: 700 12px/1 ui-monospace,Menlo,monospace;
  letter-spacing: .08em;
  cursor: pointer;
  box-shadow: 0 0 6px oklch(0.85 0.15 190 / .25),inset 0 0 8px oklch(0.85 0.15 190 / .15);
  transition: box-shadow .25s,border-color .25s,color .25s,scale .25s;
}

.ccm-16__nodes button:hover,
.ccm-16__nodes button:focus-visible {
  border-color: var(--neon);
  box-shadow: 0 0 4px var(--neon),0 0 14px var(--neon-dim),inset 0 0 10px oklch(0.85 0.15 190 / .3);
  outline: none;
  scale: 1.08;
}

.ccm-16__nodes button:focus-visible {
  outline: 1px solid var(--neon);
  outline-offset: 5px;
}

.ccm-16__nodes button.is-active {
  background: var(--neon);
  color: oklch(0.18 0.03 230);
  box-shadow: 0 0 6px var(--neon),0 0 18px var(--neon),0 0 44px var(--neon-dim);
}

.ccm-16__core {
  position: relative;
  display: grid;
  justify-items: center;
  gap: 8px;
  width: 200px;
  height: 200px;
  border-radius: 50%;
  place-content: center;
  text-align: center;
  background: oklch(0.19 0.025 240 / .9);
  border: 1px solid oklch(0.7 0.12 190 / .5);
  box-shadow: inset 0 0 30px oklch(0.85 0.15 190 / .12),0 0 40px -10px oklch(0.85 0.15 190 / .3);
}

.ccm-16__core em {
  font-style: normal;
  font-size: 10px;
  letter-spacing: .4em;
  color: oklch(0.6 0.08 190);
}

.ccm-16__read {
  font-size: 13px;
  line-height: 1.5;
  color: var(--neon);
  text-shadow: 0 0 8px var(--neon-dim);
  max-width: 150px;
  animation: ccm16-flicker 4s linear infinite;
}

@keyframes ccm16-rot {
  to {
    rotate: 360deg;
  }
}

@keyframes ccm16-flicker {
  0%,
    91%,
    94%,
    98%,
    100% {
    opacity: 1;
  }

  92%,
    96% {
    opacity: .55;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ccm-16 * {
    animation: none !important;
    transition-duration: .01s !important;
  }
}
(() => {
  const hud = document.querySelector('.ccm-16__hud');
  const read = hud.querySelector('.ccm-16__read');
  hud.querySelectorAll('.ccm-16__nodes button').forEach((b) => {
    b.addEventListener('click', () => {
      hud.querySelectorAll('.ccm-16__nodes button').forEach((x) => { x.classList.remove('is-active'); x.setAttribute('aria-pressed', 'false'); });
      b.classList.add('is-active'); b.setAttribute('aria-pressed', 'true');
      read.textContent = b.dataset.sys;
    });
  });
})();
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Circular Menu from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Glowing Cyberpunk Radial UI
Source: https://codefronts.com/navigation/css-circular-menus/holographic-dome/

A sci-fi HUD ring: a scanning sweep rotates behind six neon nodes, dashed orbit rails counter-rotate at different speeds, and the whole thing hums with layered box-shadow glow. Every glow is pure CSS — no images, no canvas — making this the reference for game UIs, dashboards that want drama, and portfolio pieces.
## HTML
```html
<section class="ccm-16" aria-label="Cyberpunk radial HUD demo">
  <div class="ccm-16__stage">
    <nav class="ccm-16__hud" style="--n:6" aria-label="Ship systems">
      <span class="ccm-16__scan" aria-hidden="true"></span>
      <span class="ccm-16__rail ccm-16__rail--a" aria-hidden="true"></span>
      <span class="ccm-16__rail ccm-16__rail--b" aria-hidden="true"></span>
      <ul class="ccm-16__nodes">
        <li style="--i:0"><button type="button" class="is-active" aria-pressed="true" data-sys="NAV — plotting course">NAV</button></li>
        <li style="--i:1"><button type="button" aria-pressed="false" data-sys="SHIELDS — 94% integrity">SHD</button></li>
        <li style="--i:2"><button type="button" aria-pressed="false" data-sys="WEAPONS — safeties on">WPN</button></li>
        <li style="--i:3"><button type="button" aria-pressed="false" data-sys="COMMS — channel open">COM</button></li>
        <li style="--i:4"><button type="button" aria-pressed="false" data-sys="ENGINES — 62% thrust">ENG</button></li>
        <li style="--i:5"><button type="button" aria-pressed="false" data-sys="SENSORS — sweep active">SNS</button></li>
      </ul>
      <div class="ccm-16__core"><em>SYSTEM</em><output class="ccm-16__read" aria-live="polite">NAV — plotting course</output></div>
    </nav>
  </div>
</section>
```
## CSS
```css
.ccm-16,
.ccm-16 *,
.ccm-16 *::before,
.ccm-16 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.ccm-16 {
  width: 100%;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: radial-gradient(circle at 50% 45%,oklch(0.22 0.03 240),var(--bg) 70%);
  --neon: oklch(0.85 0.15 190);
  --neon-dim: oklch(0.85 0.15 190 / .25);
  --bg: oklch(0.16 0.02 250);
  font-family: ui-monospace,'Cascadia Code',Menlo,monospace;
}

.ccm-16__stage {
  width: 100%;
  height: 100vh;
  position: relative;
  display: grid;
  place-items: center;
  border-radius: 20px;
  overflow: hidden;
  background: radial-gradient(circle at 50% 45%,oklch(0.22 0.03 240),var(--bg) 70%);
}

.ccm-16__stage::before {
  content: "";
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(0deg,transparent 0 3px,oklch(0 0 0 / .12) 3px 4px);
  pointer-events: none;
}

.ccm-16__hud {
  position: relative;
  width: 360px;
  height: 360px;
  display: grid;
  place-items: center;
}

.ccm-16__scan {
  position: absolute;
  inset: 14px;
  border-radius: 50%;
  background: conic-gradient(from 0deg,transparent 78%,var(--neon-dim) 98%,oklch(0.85 0.15 190 / .5));
  animation: ccm16-rot 6s linear infinite;
  mask: radial-gradient(circle,transparent 34%,black 35%);
}

.ccm-16__rail {
  position: absolute;
  border-radius: 50%;
}

.ccm-16__rail--a {
  inset: 0;
  border: 1px dashed oklch(0.7 0.1 190 / .4);
  animation: ccm16-rot 60s linear infinite;
}

.ccm-16__rail--b {
  inset: 38px;
  border: 1px dashed oklch(0.7 0.1 190 / .25);
  animation: ccm16-rot 45s linear infinite reverse;
}

.ccm-16__nodes {
  position: absolute;
  inset: 0;
  list-style: none;
}

.ccm-16__nodes li {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  pointer-events: none;
  --a: calc((360deg / var(--n)) * var(--i) - 90deg);
  translate: calc(cos(var(--a)) * 152px) calc(sin(var(--a)) * 152px);
}

.ccm-16__nodes button {
  pointer-events: auto;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  border: 1px solid oklch(0.7 0.12 190 / .6);
  background: oklch(0.2 0.03 230 / .9);
  color: var(--neon);
  font: 700 12px/1 ui-monospace,Menlo,monospace;
  letter-spacing: .08em;
  cursor: pointer;
  box-shadow: 0 0 6px oklch(0.85 0.15 190 / .25),inset 0 0 8px oklch(0.85 0.15 190 / .15);
  transition: box-shadow .25s,border-color .25s,color .25s,scale .25s;
}

.ccm-16__nodes button:hover,
.ccm-16__nodes button:focus-visible {
  border-color: var(--neon);
  box-shadow: 0 0 4px var(--neon),0 0 14px var(--neon-dim),inset 0 0 10px oklch(0.85 0.15 190 / .3);
  outline: none;
  scale: 1.08;
}

.ccm-16__nodes button:focus-visible {
  outline: 1px solid var(--neon);
  outline-offset: 5px;
}

.ccm-16__nodes button.is-active {
  background: var(--neon);
  color: oklch(0.18 0.03 230);
  box-shadow: 0 0 6px var(--neon),0 0 18px var(--neon),0 0 44px var(--neon-dim);
}

.ccm-16__core {
  position: relative;
  display: grid;
  justify-items: center;
  gap: 8px;
  width: 200px;
  height: 200px;
  border-radius: 50%;
  place-content: center;
  text-align: center;
  background: oklch(0.19 0.025 240 / .9);
  border: 1px solid oklch(0.7 0.12 190 / .5);
  box-shadow: inset 0 0 30px oklch(0.85 0.15 190 / .12),0 0 40px -10px oklch(0.85 0.15 190 / .3);
}

.ccm-16__core em {
  font-style: normal;
  font-size: 10px;
  letter-spacing: .4em;
  color: oklch(0.6 0.08 190);
}

.ccm-16__read {
  font-size: 13px;
  line-height: 1.5;
  color: var(--neon);
  text-shadow: 0 0 8px var(--neon-dim);
  max-width: 150px;
  animation: ccm16-flicker 4s linear infinite;
}

@keyframes ccm16-rot {
  to {
    rotate: 360deg;
  }
}

@keyframes ccm16-flicker {
  0%,
    91%,
    94%,
    98%,
    100% {
    opacity: 1;
  }

  92%,
    96% {
    opacity: .55;
  }
}

@media (prefers-reduced-motion: reduce) {
  .ccm-16 * {
    animation: none !important;
    transition-duration: .01s !important;
  }
}
```

## JavaScript
```js
(() => {
  const hud = document.querySelector('.ccm-16__hud');
  const read = hud.querySelector('.ccm-16__read');
  hud.querySelectorAll('.ccm-16__nodes button').forEach((b) => {
    b.addEventListener('click', () => {
      hud.querySelectorAll('.ccm-16__nodes button').forEach((x) => { x.classList.remove('is-active'); x.setAttribute('aria-pressed', 'false'); });
      b.classList.add('is-active'); b.setAttribute('aria-pressed', 'true');
      read.textContent = b.dataset.sys;
    });
  });
})();
```

How this works

Neon = stacked shadows of the same hue at increasing blur:

box-shadow: 0 0 4px var(--neon), 0 0 12px var(--neon),
            0 0 32px oklch(0.8 0.16 190 / .5);

The scanner is a conic-gradient(from 0deg, transparent 82%, var(--neon-dim)) disc rotating on an infinite linear animation; because the gradient fades into its leading edge, it reads as a radar sweep. Two dashed rails are just bordered circles with different animation-durations and one reverse, giving cheap parallax depth.

Nodes brighten on hover/focus by swapping a longer shadow stack — never by scaling blur radii at 60fps, which janks. The active node writes its label to a flickering readout in the core.

Make it yours

  • Hue: one --neon token; try 330 (magenta) or 25 (red alert).
  • Scanner speed: the 6s rotation; slower reads calmer.
  • Flicker: the ccm16-flicker keyframes — delete for a stable readout.
  • Rails: add a third circle with a new duration for more depth.

Gotchas — read before shipping

  • Layer glow with multiple sharp-to-wide shadows; one huge blur looks like fog, not neon.
  • Rotating gradients repaint cheaply, but blurred box-shadows on moving elements don't — keep glows on static nodes and rotate simple gradients instead.
  • Neon-on-dark fails contrast fast: keep body text in a desaturated light tone and reserve the pure neon for accents.

Browser support

ChromeSafariFirefoxEdge
111+15.4+110+111+

conic-gradient, trig placement and oklch. All animation is transform/opacity only, so it stays smooth even on integrated GPUs.

Search CodeFronts

Loading…