21 examples Responsive advanced

21 CSS Circular Menu Designs

A CSS circular menu is a navigation pattern where items are arranged around a circle, ring, dome, or orbit instead of a flat list — useful for radial action menus, compass-style nav, and skeumorphic widgets. These 21 hand-coded designs are 100% pure CSS with custom-property trigonometry. Continuous animations honour prefers-reduced-motion.

21 unique menus 21 Pure CSS WCAG-friendly Mobile-first MIT licensed Updated May 4, 2026
Updated · 4 new demos added — Mission Hub, Service Orbit, Tactile Dial and Fan Reveal · May 2, 2026
01 / 21
Mission Hub
NEW Pure CSS
A 5-segment half-wheel command surface with curved SVG labels riding the outer rim, hairline wedge dividers, and a context-aware sub-toolbar that swaps icons based on the selected wedge. Pure CSS via :checked + :has(), teal-on-navy cockpit palette.
Try it
.ccm-sun { --n: 5; --ba: calc(180deg / var(--n)); --r: 110px; position: relative; width: 240px; height: 130px; overflow: visible; font-family: system-ui, sans-serif; }

.ccm-sun-wheel { position: absolute; bottom: 0; left: 50%; width: calc(var(--r) * 2); height: var(--r); margin-left: calc(var(--r) * -1); border-radius: var(--r) var(--r) 0 0; background: radial-gradient(farthest-side at 50% 100%, #14b8a6 35%, #1e293b 36%, #1e293b calc(100% - 1px), transparent 100%); filter: drop-shadow(0 4px 10px rgba(0,0,0,0.4)); }

.ccm-sun-rim { position: absolute; bottom: 0; left: 50%; width: 240px; height: 130px; margin-left: -120px; pointer-events: none; z-index: 1; }

.ccm-sun-dividers line { stroke: rgba(94,234,212,0.25); stroke-width: 1; }

.ccm-sun-curve { font: 700 9px/1 ui-monospace, monospace; letter-spacing: 0.18em; fill: #cbd5e1; }

.ccm-sun-pivot { position: absolute; bottom: 0; left: 50%; width: 70px; height: 35px; margin-left: -35px; border-radius: 35px 35px 0 0; background: radial-gradient(farthest-side at 50% 100%, #5eead4 0%, #14b8a6 70%, #0f766e 100%); box-shadow: 0 -2px 8px rgba(94,234,212,0.35); z-index: 3; }

.ccm-sun-arc { position: absolute; bottom: 0; left: 50%; width: 50px; height: var(--r); margin-left: -25px; display: flex; align-items: flex-start; justify-content: center; padding-top: 28px; cursor: pointer; transform-origin: 50% 100%; transform: rotate(calc((var(--i) + 0.5) * var(--ba) - 90deg)); transition: color 0.25s; color: #94a3b8; z-index: 2; }

.ccm-sun-ico { display: inline-block; font-size: 22px; line-height: 1; transform: rotate(calc((var(--i) + 0.5) * var(--ba) * -1 + 90deg)); transition: transform 0.3s cubic-bezier(0.34,1.56,0.64,1); }

.ccm-sun-arc:hover, .ccm-sun-arc:focus-visible { color: #5eead4; }

.ccm-sun-arc:hover .ccm-sun-ico, .ccm-sun-arc:focus-visible .ccm-sun-ico { transform: rotate(calc((var(--i) + 0.5) * var(--ba) * -1 + 90deg)) translateY(-3px) scale(1.1); }

.ccm-sun:has(#ccm-sun-0:checked) [for="ccm-sun-0"],
.ccm-sun:has(#ccm-sun-1:checked) [for="ccm-sun-1"],
.ccm-sun:has(#ccm-sun-2:checked) [for="ccm-sun-2"],
.ccm-sun:has(#ccm-sun-3:checked) [for="ccm-sun-3"],
.ccm-sun:has(#ccm-sun-4:checked) [for="ccm-sun-4"] { color: #5eead4; }

.ccm-sun:has(#ccm-sun-0:checked) [for="ccm-sun-0"] .ccm-sun-ico,
.ccm-sun:has(#ccm-sun-1:checked) [for="ccm-sun-1"] .ccm-sun-ico,
.ccm-sun:has(#ccm-sun-2:checked) [for="ccm-sun-2"] .ccm-sun-ico,
.ccm-sun:has(#ccm-sun-3:checked) [for="ccm-sun-3"] .ccm-sun-ico,
.ccm-sun:has(#ccm-sun-4:checked) [for="ccm-sun-4"] .ccm-sun-ico { filter: drop-shadow(0 0 6px rgba(94,234,212,0.7)); }

.ccm-sun-bar { position: absolute; left: 50%; bottom: calc(var(--r) + 14px); display: flex; gap: 6px; padding: 8px 10px; background: #1e293b; border: 1px solid #334155; border-radius: 12px; transform: translateX(-50%) scale(0); transform-origin: 50% calc(100% + 10px); transition: transform 0.35s cubic-bezier(0.34,1.56,0.64,1); z-index: 4; box-shadow: 0 6px 18px rgba(0,0,0,0.4); }

.ccm-sun-bar::after { content: ''; position: absolute; left: 50%; bottom: -7px; margin-left: -6px; border: 6px solid transparent; border-top-color: #1e293b; }

.ccm-sun:has(#ccm-sun-0:checked) .ccm-sun-bar-0, .ccm-sun:has(#ccm-sun-1:checked) .ccm-sun-bar-1, .ccm-sun:has(#ccm-sun-2:checked) .ccm-sun-bar-2, .ccm-sun:has(#ccm-sun-3:checked) .ccm-sun-bar-3, .ccm-sun:has(#ccm-sun-4:checked) .ccm-sun-bar-4 { transform: translateX(-50%) scale(1); }

.ccm-sun-sub { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: 50%; background: rgba(94,234,212,0.12); color: #5eead4; font: 700 13px/1 ui-monospace, monospace; text-decoration: none; border: 1px solid rgba(94,234,212,0.3); transition: background 0.2s, color 0.2s, transform 0.2s; }

.ccm-sun-sub:hover, .ccm-sun-sub:focus-visible { background: #5eead4; color: #0f172a; transform: scale(1.1); }
<div class="ccm-sun" role="toolbar" aria-label="Mission Hub">
  <input type="radio" name="ccm-sun" id="ccm-sun-0" class="ccm-sun-r" hidden>
  <input type="radio" name="ccm-sun" id="ccm-sun-1" class="ccm-sun-r" hidden>
  <input type="radio" name="ccm-sun" id="ccm-sun-2" class="ccm-sun-r" hidden checked>
  <input type="radio" name="ccm-sun" id="ccm-sun-3" class="ccm-sun-r" hidden>
  <input type="radio" name="ccm-sun" id="ccm-sun-4" class="ccm-sun-r" hidden>
  <div class="ccm-sun-wheel" aria-hidden="true"></div>
  <svg class="ccm-sun-rim" viewBox="0 0 240 130" aria-hidden="true">
    <defs>
      <path id="ccm-sun-arc-path" d="M 30 130 A 90 90 0 0 1 210 130" fill="none"/>
    </defs>
    <g class="ccm-sun-dividers">
      <line x1="91.7" y1="109.4" x2="56.9" y2="84.1"/>
      <line x1="109.2" y1="96.7" x2="95.9" y2="55.8"/>
      <line x1="130.8" y1="96.7" x2="144.1" y2="55.8"/>
      <line x1="148.3" y1="109.4" x2="183.1" y2="84.1"/>
    </g>
    <text class="ccm-sun-curve">
      <textPath href="#ccm-sun-arc-path" startOffset="10%" text-anchor="middle">MUSIC</textPath>
      <textPath href="#ccm-sun-arc-path" startOffset="30%" text-anchor="middle">MAPS</textPath>
      <textPath href="#ccm-sun-arc-path" startOffset="50%" text-anchor="middle">COMPASS</textPath>
      <textPath href="#ccm-sun-arc-path" startOffset="70%" text-anchor="middle">CAMERA</textPath>
      <textPath href="#ccm-sun-arc-path" startOffset="90%" text-anchor="middle">SETTINGS</textPath>
    </text>
  </svg>
  <div class="ccm-sun-pivot" aria-hidden="true"></div>
  <label for="ccm-sun-0" class="ccm-sun-arc" style="--i:0" aria-label="Music"><span class="ccm-sun-ico">♪</span></label>
  <label for="ccm-sun-1" class="ccm-sun-arc" style="--i:1" aria-label="Maps"><span class="ccm-sun-ico">◈</span></label>
  <label for="ccm-sun-2" class="ccm-sun-arc" style="--i:2" aria-label="Compass"><span class="ccm-sun-ico">⌖</span></label>
  <label for="ccm-sun-3" class="ccm-sun-arc" style="--i:3" aria-label="Camera"><span class="ccm-sun-ico">◉</span></label>
  <label for="ccm-sun-4" class="ccm-sun-arc" style="--i:4" aria-label="Settings"><span class="ccm-sun-ico">⚙</span></label>
  <div class="ccm-sun-bar ccm-sun-bar-0" role="toolbar" aria-label="Music actions">
    <a href="#" class="ccm-sun-sub" aria-label="Play">▶</a>
    <a href="#" class="ccm-sun-sub" aria-label="Pause">❚❚</a>
    <a href="#" class="ccm-sun-sub" aria-label="Skip">⏭</a>
  </div>
  <div class="ccm-sun-bar ccm-sun-bar-1" role="toolbar" aria-label="Maps actions">
    <a href="#" class="ccm-sun-sub" aria-label="Search">⌕</a>
    <a href="#" class="ccm-sun-sub" aria-label="Pin">◉</a>
    <a href="#" class="ccm-sun-sub" aria-label="Route">➤</a>
  </div>
  <div class="ccm-sun-bar ccm-sun-bar-2" role="toolbar" aria-label="Compass actions">
    <a href="#" class="ccm-sun-sub" aria-label="Calibrate">⊙</a>
    <a href="#" class="ccm-sun-sub" aria-label="North">▲</a>
    <a href="#" class="ccm-sun-sub" aria-label="Lock">⏻</a>
  </div>
  <div class="ccm-sun-bar ccm-sun-bar-3" role="toolbar" aria-label="Camera actions">
    <a href="#" class="ccm-sun-sub" aria-label="Shutter">●</a>
    <a href="#" class="ccm-sun-sub" aria-label="Flash">⚡</a>
    <a href="#" class="ccm-sun-sub" aria-label="Flip">⤾</a>
  </div>
  <div class="ccm-sun-bar ccm-sun-bar-4" role="toolbar" aria-label="Settings actions">
    <a href="#" class="ccm-sun-sub" aria-label="Profile">☺</a>
    <a href="#" class="ccm-sun-sub" aria-label="Theme">◐</a>
    <a href="#" class="ccm-sun-sub" aria-label="Help">?</a>
  </div>
</div>
02 / 21
Service Orbit
NEW Pure CSS
Industries Validation Engineering Project Mgmt Manufacturing Automation Learn more →
Six service satellites orbit a central disc with an animated pointer that snaps to the active selection. Click any satellite to swap the centre title and rotate the pointer with a spring overshoot. Pure CSS via :checked + :has() — adapted from a service-business radial nav, simplified to remove its 600+ lines of icon paths and JS pointer animation.
Try it
.ccm-svc { --n: 6; --ba: calc(360deg / var(--n)); --r: 105px; --pa: 0deg; position: relative; width: 260px; height: 260px; display: flex; align-items: center; justify-content: center; font-family: system-ui, sans-serif; }

.ccm-svc-orbit { position: absolute; width: calc(var(--r) * 2); height: calc(var(--r) * 2); border-radius: 50%; border: 1px dashed rgba(124,108,255,0.18); background: radial-gradient(circle at center, rgba(124,108,255,0.08), transparent 70%); }

.ccm-svc-disc { position: relative; width: 110px; height: 110px; border-radius: 50%; background: linear-gradient(135deg, #7c6cff, #a78bfa); color: #fff; display: flex; flex-direction: column; align-items: center; justify-content: center; box-shadow: 0 8px 24px rgba(124,108,255,0.4), inset 0 1px 0 rgba(255,255,255,0.2); z-index: 2; }

.ccm-svc-title { display: none; font: 700 14px/1.2 system-ui, sans-serif; text-align: center; padding: 0 12px; }

.ccm-svc-cta { display: block; margin-top: 8px; padding: 4px 10px; font: 600 10px/1 ui-monospace, monospace; letter-spacing: 0.06em; color: rgba(255,255,255,0.85); border: 1px solid rgba(255,255,255,0.4); border-radius: 999px; cursor: pointer; transition: background 0.2s, color 0.2s; }

.ccm-svc-cta:hover { background: #fff; color: #7c6cff; }

.ccm-svc:has(#ccm-svc-0:checked) .ccm-svc-t-0,
.ccm-svc:has(#ccm-svc-1:checked) .ccm-svc-t-1,
.ccm-svc:has(#ccm-svc-2:checked) .ccm-svc-t-2,
.ccm-svc:has(#ccm-svc-3:checked) .ccm-svc-t-3,
.ccm-svc:has(#ccm-svc-4:checked) .ccm-svc-t-4,
.ccm-svc:has(#ccm-svc-5:checked) .ccm-svc-t-5 { display: block; }

.ccm-svc-pointer { position: absolute; width: 12px; height: 12px; border-radius: 50%; background: #fff; border: 2px solid #7c6cff; box-shadow: 0 0 0 4px rgba(124,108,255,0.18); transform: rotate(var(--pa)) translateY(calc(var(--r) * -0.55)); transform-origin: 50% 50%; transition: transform 0.5s cubic-bezier(0.34,1.56,0.64,1); z-index: 3; }

.ccm-svc:has(#ccm-svc-0:checked) { --pa: 0deg; }

.ccm-svc:has(#ccm-svc-1:checked) { --pa: 60deg; }

.ccm-svc:has(#ccm-svc-2:checked) { --pa: 120deg; }

.ccm-svc:has(#ccm-svc-3:checked) { --pa: 180deg; }

.ccm-svc:has(#ccm-svc-4:checked) { --pa: 240deg; }

.ccm-svc:has(#ccm-svc-5:checked) { --pa: 300deg; }

.ccm-svc-sat { position: absolute; width: 44px; height: 44px; border-radius: 50%; background: #1f1f2e; border: 1px solid rgba(124,108,255,0.4); color: #c4b5fd; display: inline-flex; align-items: center; justify-content: center; cursor: pointer; transform: rotate(calc(var(--i) * var(--ba))) translateY(calc(var(--r) * -1)) rotate(calc(var(--i) * var(--ba) * -1)); transition: background 0.25s, color 0.25s, transform 0.3s cubic-bezier(0.34,1.56,0.64,1), box-shadow 0.25s; z-index: 4; }

.ccm-svc-sat span { font-size: 18px; line-height: 1; }

.ccm-svc-sat:hover, .ccm-svc-sat:focus-visible { background: #2a2a3e; color: #fff; transform: rotate(calc(var(--i) * var(--ba))) translateY(calc(var(--r) * -1)) rotate(calc(var(--i) * var(--ba) * -1)) scale(1.15); }

.ccm-svc:has(#ccm-svc-0:checked) [for="ccm-svc-0"],
.ccm-svc:has(#ccm-svc-1:checked) [for="ccm-svc-1"],
.ccm-svc:has(#ccm-svc-2:checked) [for="ccm-svc-2"],
.ccm-svc:has(#ccm-svc-3:checked) [for="ccm-svc-3"],
.ccm-svc:has(#ccm-svc-4:checked) [for="ccm-svc-4"],
.ccm-svc:has(#ccm-svc-5:checked) [for="ccm-svc-5"] { background: #7c6cff; color: #fff; border-color: #fff; box-shadow: 0 0 0 4px rgba(124,108,255,0.3); transform: rotate(calc(var(--i) * var(--ba))) translateY(calc(var(--r) * -1)) rotate(calc(var(--i) * var(--ba) * -1)) scale(1.15); }
<div class="ccm-svc">
  <input type="radio" name="ccm-svc" id="ccm-svc-0" class="ccm-svc-r" hidden checked>
  <input type="radio" name="ccm-svc" id="ccm-svc-1" class="ccm-svc-r" hidden>
  <input type="radio" name="ccm-svc" id="ccm-svc-2" class="ccm-svc-r" hidden>
  <input type="radio" name="ccm-svc" id="ccm-svc-3" class="ccm-svc-r" hidden>
  <input type="radio" name="ccm-svc" id="ccm-svc-4" class="ccm-svc-r" hidden>
  <input type="radio" name="ccm-svc" id="ccm-svc-5" class="ccm-svc-r" hidden>
  <div class="ccm-svc-orbit" aria-hidden="true"></div>
  <div class="ccm-svc-disc">
    <span class="ccm-svc-title ccm-svc-t-0">Industries</span>
    <span class="ccm-svc-title ccm-svc-t-1">Validation</span>
    <span class="ccm-svc-title ccm-svc-t-2">Engineering</span>
    <span class="ccm-svc-title ccm-svc-t-3">Project Mgmt</span>
    <span class="ccm-svc-title ccm-svc-t-4">Manufacturing</span>
    <span class="ccm-svc-title ccm-svc-t-5">Automation</span>
    <span class="ccm-svc-cta">Learn more →</span>
  </div>
  <div class="ccm-svc-pointer" aria-hidden="true"></div>
  <label for="ccm-svc-0" class="ccm-svc-sat" style="--i:0" aria-label="Industries"><span>♨</span></label>
  <label for="ccm-svc-1" class="ccm-svc-sat" style="--i:1" aria-label="Validation"><span>✓</span></label>
  <label for="ccm-svc-2" class="ccm-svc-sat" style="--i:2" aria-label="Engineering"><span>⚙</span></label>
  <label for="ccm-svc-3" class="ccm-svc-sat" style="--i:3" aria-label="Project Management"><span>◈</span></label>
  <label for="ccm-svc-4" class="ccm-svc-sat" style="--i:4" aria-label="Manufacturing"><span>⛭</span></label>
  <label for="ccm-svc-5" class="ccm-svc-sat" style="--i:5" aria-label="Automation"><span>⌬</span></label>
</div>
03 / 21
Tactile Dial
NEW Pure CSS
A brushed-steel rotary dial flanked by 5 icons arranged on a downward arc. Click any icon and the whole dial rotates left or right (-90°/-45°/0°/+45°/+90°) to "point" at the active selection, with a soft white halo around the chosen icon. Pure CSS via :checked + :has() + a CSS custom property for the rotation angle — adapted from a hardware-knob nav, simplified to drop ~10 PNG dependencies and ~100 lines of jQuery.
Try it
.ccm-tac { --rot: 0deg; position: relative; width: 280px; height: 240px; display: flex; align-items: flex-end; justify-content: center; font-family: system-ui, sans-serif; }

.ccm-tac-dial { position: absolute; bottom: 20px; left: 50%; width: 130px; height: 130px; margin-left: -65px; border-radius: 50%; background: repeating-conic-gradient(from 0deg, #c9cfd7 0deg, #8b929c 1deg, #c9cfd7 2deg), linear-gradient(180deg, #c9cfd7 0%, #2b2f3e 46%, #2b2f3e 54%, #b0b7c1 100%); background-blend-mode: overlay, normal; box-shadow: 0 12px 24px rgba(0,0,0,0.4), 0 4px 8px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.3), inset 0 -2px 4px rgba(0,0,0,0.4); transform: rotate(var(--rot)); transform-origin: center; transition: transform 0.7s cubic-bezier(0.65,0,0.35,1); z-index: 2; }

.ccm-tac-bevel { position: absolute; inset: 12px; border-radius: 50%; background: radial-gradient(circle at 30% 30%, #e8ecf0, #6b7280 70%, #2b2f3e); box-shadow: inset 0 1px 2px rgba(255,255,255,0.4), inset 0 -2px 4px rgba(0,0,0,0.5); }

.ccm-tac-mark { position: absolute; top: 8px; left: 50%; width: 4px; height: 16px; margin-left: -2px; background: linear-gradient(180deg, #fff, #94a3b8); border-radius: 2px; box-shadow: 0 1px 2px rgba(0,0,0,0.6); z-index: 3; }

.ccm-tac:has(#ccm-tac-0:checked) { --rot: -50deg; }

.ccm-tac:has(#ccm-tac-1:checked) { --rot: -25deg; }

.ccm-tac:has(#ccm-tac-2:checked) { --rot: 0deg; }

.ccm-tac:has(#ccm-tac-3:checked) { --rot: 25deg; }

.ccm-tac:has(#ccm-tac-4:checked) { --rot: 50deg; }

.ccm-tac-arc { position: absolute; bottom: -40px; left: 50%; width: 250px; height: 250px; margin-left: -125px; border-radius: 50%; border: 1px dashed rgba(255,255,255,0.08); pointer-events: none; -webkit-mask: linear-gradient(180deg, #000 0%, #000 50%, transparent 50%); mask: linear-gradient(180deg, #000 0%, #000 50%, transparent 50%); }

.ccm-tac-i { position: absolute; bottom: calc(20px + 65px); left: 50%; width: 38px; height: 38px; margin: -19px; border-radius: 50%; background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.1); color: #94a3b8; display: inline-flex; align-items: center; justify-content: center; cursor: pointer; transform: rotate(calc((var(--p) - 2) * 25deg)) translateY(-125px); transform-origin: 50% 50%; transition: background 0.25s, color 0.25s, box-shadow 0.3s, border-color 0.25s, transform 0.25s; z-index: 4; }

.ccm-tac-i span { font-size: 17px; line-height: 1; display: inline-block; transform: rotate(calc((var(--p) - 2) * -25deg)); }

.ccm-tac-i:hover, .ccm-tac-i:focus-visible { background: rgba(255,255,255,0.08); color: #fff; border-color: rgba(255,255,255,0.25); transform: rotate(calc((var(--p) - 2) * 25deg)) translateY(-130px); }

.ccm-tac:has(#ccm-tac-0:checked) [for="ccm-tac-0"],
.ccm-tac:has(#ccm-tac-1:checked) [for="ccm-tac-1"],
.ccm-tac:has(#ccm-tac-2:checked) [for="ccm-tac-2"],
.ccm-tac:has(#ccm-tac-3:checked) [for="ccm-tac-3"],
.ccm-tac:has(#ccm-tac-4:checked) [for="ccm-tac-4"] { background: rgba(255,255,255,0.16); color: #fff; border-color: rgba(255,255,255,0.5); box-shadow: 0 0 24px 6px rgba(255,255,255,0.18); transform: rotate(calc((var(--p) - 2) * 25deg)) translateY(-132px); }
<div class="ccm-tac" role="toolbar" aria-label="Tactile Dial">
  <input type="radio" name="ccm-tac" id="ccm-tac-0" class="ccm-tac-r" hidden>
  <input type="radio" name="ccm-tac" id="ccm-tac-1" class="ccm-tac-r" hidden>
  <input type="radio" name="ccm-tac" id="ccm-tac-2" class="ccm-tac-r" hidden checked>
  <input type="radio" name="ccm-tac" id="ccm-tac-3" class="ccm-tac-r" hidden>
  <input type="radio" name="ccm-tac" id="ccm-tac-4" class="ccm-tac-r" hidden>
  <div class="ccm-tac-dial" aria-hidden="true">
    <div class="ccm-tac-mark"></div>
    <div class="ccm-tac-bevel"></div>
  </div>
  <div class="ccm-tac-arc" aria-hidden="true"></div>
  <label for="ccm-tac-0" class="ccm-tac-i" style="--p:0" aria-label="Email"><span>✉</span></label>
  <label for="ccm-tac-1" class="ccm-tac-i" style="--p:1" aria-label="Photos"><span>◇</span></label>
  <label for="ccm-tac-2" class="ccm-tac-i" style="--p:2" aria-label="Cloud"><span>☁</span></label>
  <label for="ccm-tac-3" class="ccm-tac-i" style="--p:3" aria-label="Portfolio"><span>⊞</span></label>
  <label for="ccm-tac-4" class="ccm-tac-i" style="--p:4" aria-label="Settings"><span>⚙</span></label>
</div>
04 / 21
Fan Reveal
NEW Pure CSS
Six wedges of a 6-color pie that fan open from a closed stack on first paint with a staggered "deal the cards" reveal — each wedge rotates from 0° to its 60° slot with a per-wedge delay. Hover lifts the wedge outward, click locks the selection. Pure CSS keyframe animation, zero JS — adapted from a jQuery-driven menu and rebuilt with proper 60° clip-path geometry.
Try it
.ccm-fan { position: relative; width: 220px; height: 220px; display: flex; align-items: center; justify-content: center; font-family: 'Lato', system-ui, sans-serif; filter: drop-shadow(0 0 18px rgba(255,255,255,0.08)); }

.ccm-fan-w { position: absolute; inset: 0; background: radial-gradient(circle at 50% 5%, rgba(255,255,255,0.35), transparent 55%), radial-gradient(circle at 50% 30%, var(--c) 0%, color-mix(in srgb, var(--c), #000 35%) 100%); cursor: pointer; clip-path: polygon(50% 50%, 25% 6.7%, 37.06% 1.92%, 50% 0%, 62.94% 1.92%, 75% 6.7%); transform-origin: 50% 50%; transform: rotate(0deg); animation: ccm-fan-deal 0.9s cubic-bezier(0.49,0.24,0.32,0.96) forwards; animation-delay: calc(var(--i) * 0.08s); transition: filter 0.3s, transform 0.3s; }

.ccm-fan-w span { position: absolute; top: 24px; left: 50%; display: inline-block; color: #fff; font-size: 12px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; transform: translateX(-50%); pointer-events: none; text-shadow: 0 1px 2px rgba(0,0,0,0.6), 0 0 8px rgba(255,255,255,0.25); white-space: nowrap; }

.ccm-fan-w:hover, .ccm-fan-w:focus-visible { filter: brightness(1.18) saturate(1.2) drop-shadow(0 0 12px color-mix(in srgb, var(--c), white 20%)); z-index: 2; }

.ccm-fan:has(#ccm-fan-0:checked) [for="ccm-fan-0"],
.ccm-fan:has(#ccm-fan-1:checked) [for="ccm-fan-1"],
.ccm-fan:has(#ccm-fan-2:checked) [for="ccm-fan-2"],
.ccm-fan:has(#ccm-fan-3:checked) [for="ccm-fan-3"],
.ccm-fan:has(#ccm-fan-4:checked) [for="ccm-fan-4"],
.ccm-fan:has(#ccm-fan-5:checked) [for="ccm-fan-5"] { filter: brightness(1.45) saturate(1.5) drop-shadow(0 0 22px color-mix(in srgb, var(--c), white 40%)) drop-shadow(0 0 6px color-mix(in srgb, var(--c), white 60%)); z-index: 3; transform: rotate(calc(var(--i) * 60deg)) scale(1.06); }

.ccm-fan:has(#ccm-fan-0:checked) [for="ccm-fan-0"] span,
.ccm-fan:has(#ccm-fan-1:checked) [for="ccm-fan-1"] span,
.ccm-fan:has(#ccm-fan-2:checked) [for="ccm-fan-2"] span,
.ccm-fan:has(#ccm-fan-3:checked) [for="ccm-fan-3"] span,
.ccm-fan:has(#ccm-fan-4:checked) [for="ccm-fan-4"] span,
.ccm-fan:has(#ccm-fan-5:checked) [for="ccm-fan-5"] span { font-size: 13px; letter-spacing: 0.1em; text-shadow: 0 1px 2px rgba(0,0,0,0.7), 0 0 12px rgba(255,255,255,0.6); }

.ccm-fan-hub { position: absolute; width: 60px; height: 60px; border-radius: 50%; background: radial-gradient(circle at 35% 30%, rgba(255,255,255,0.18), transparent 55%), radial-gradient(circle at center, #1f1f2e 0%, #0a0a14 100%); border: 1px solid rgba(255,255,255,0.08); box-shadow: inset 0 1px 0 rgba(255,255,255,0.12), inset 0 -2px 6px rgba(0,0,0,0.5), 0 0 24px rgba(124,108,255,0.25), 0 4px 12px rgba(0,0,0,0.5); z-index: 4; pointer-events: none; }

@media (prefers-reduced-motion: reduce) {
  .ccm-fan-w { animation: none; transform: rotate(calc(var(--i) * 60deg)); }
}
<div class="ccm-fan" role="toolbar" aria-label="Fan Reveal">
  <input type="radio" name="ccm-fan" id="ccm-fan-0" class="ccm-fan-r" hidden>
  <input type="radio" name="ccm-fan" id="ccm-fan-1" class="ccm-fan-r" hidden>
  <input type="radio" name="ccm-fan" id="ccm-fan-2" class="ccm-fan-r" hidden>
  <input type="radio" name="ccm-fan" id="ccm-fan-3" class="ccm-fan-r" hidden>
  <input type="radio" name="ccm-fan" id="ccm-fan-4" class="ccm-fan-r" hidden>
  <input type="radio" name="ccm-fan" id="ccm-fan-5" class="ccm-fan-r" hidden>
  <label for="ccm-fan-0" class="ccm-fan-w" style="--i:0; --c:#255D74" aria-label="Home"><span>home</span></label>
  <label for="ccm-fan-1" class="ccm-fan-w" style="--i:1; --c:#DA353B" aria-label="About"><span>about</span></label>
  <label for="ccm-fan-2" class="ccm-fan-w" style="--i:2; --c:#F89F44" aria-label="Contact"><span>contact</span></label>
  <label for="ccm-fan-3" class="ccm-fan-w" style="--i:3; --c:#78302C" aria-label="Links"><span>links</span></label>
  <label for="ccm-fan-4" class="ccm-fan-w" style="--i:4; --c:#002B55" aria-label="Bio"><span>bio</span></label>
  <label for="ccm-fan-5" class="ccm-fan-w" style="--i:5; --c:#207C6B" aria-label="Store"><span>store</span></label>
  <span class="ccm-fan-hub" aria-hidden="true"></span>
</div>
05 / 21
Petal Fan
Pure CSS
Six action buttons fan out across a 180° upper arc from a central FAB on hover, each with a staggered scale-in. Pure CSS via :hover + transition-delay per child.
Try it
.ccm-petal { position: relative; width: 220px; height: 140px; }

.ccm-petal-fab { position: absolute; left: 50%; bottom: 0; width: 52px; height: 52px; border-radius: 50%; background: linear-gradient(135deg, #7c6cff, #a78bfa); color: #fff; font-size: 26px; line-height: 52px; text-align: center; cursor: pointer; transform: translateX(-50%); transition: transform 0.4s; box-shadow: 0 6px 18px rgba(124,108,255,0.4); z-index: 2; }

.ccm-petal-i { position: absolute; left: 50%; bottom: 12px; width: 36px; height: 36px; border-radius: 50%; background: #1f1f2e; color: #c4b5fd; font-size: 14px; display: flex; align-items: center; justify-content: center; text-decoration: none; border: 1px solid rgba(255,255,255,0.08); transform: translate(-50%, 0) rotate(0) translateY(0) rotate(0); opacity: 0; transition: transform 0.45s cubic-bezier(0.34,1.56,0.64,1), opacity 0.3s; }

.ccm-petal:hover .ccm-petal-fab, .ccm-petal-t:checked + .ccm-petal-fab { transform: translateX(-50%) rotate(45deg); }

.ccm-petal:hover .ccm-petal-i, .ccm-petal-t:checked ~ .ccm-petal-i { transform: translate(-50%, 0) rotate(var(--a)) translateY(-72px) rotate(calc(var(--a) * -1)); opacity: 1; }

.ccm-petal-i:nth-of-type(1) { transition-delay: 0.05s; }

.ccm-petal-i:nth-of-type(2) { transition-delay: 0.10s; }

.ccm-petal-i:nth-of-type(3) { transition-delay: 0.15s; }

.ccm-petal-i:nth-of-type(4) { transition-delay: 0.20s; }

.ccm-petal-i:nth-of-type(5) { transition-delay: 0.25s; }

.ccm-petal-i:nth-of-type(6) { transition-delay: 0.30s; }
<div class="ccm-petal">
  <input type="checkbox" id="ccm-petal-t" class="ccm-petal-t" hidden>
  <label for="ccm-petal-t" class="ccm-petal-fab" aria-label="Open menu">+</label>
  <a href="#" class="ccm-petal-i" style="--a:-90deg" aria-label="Home">⌂</a>
  <a href="#" class="ccm-petal-i" style="--a:-126deg" aria-label="Search">⌕</a>
  <a href="#" class="ccm-petal-i" style="--a:-162deg" aria-label="Star">★</a>
  <a href="#" class="ccm-petal-i" style="--a:-54deg" aria-label="Like">♥</a>
  <a href="#" class="ccm-petal-i" style="--a:-18deg" aria-label="Mail">✉</a>
  <a href="#" class="ccm-petal-i" style="--a:-198deg" aria-label="Settings">⚙</a>
</div>
06 / 21
Full Circle Wheel
Pure CSS
A B C D E F G H
Eight items evenly distributed around a 360° ring, all visible at rest. Hover any item to scale it up; the central icon stays anchored. Pure trig via CSS variables.
Try it
.ccm-wheel { position: relative; width: 200px; height: 200px; display: flex; align-items: center; justify-content: center; }

.ccm-wheel-c { width: 44px; height: 44px; border-radius: 50%; background: linear-gradient(135deg, #7c6cff, #ff6c8a); color: #fff; font-size: 18px; display: inline-flex; align-items: center; justify-content: center; box-shadow: 0 0 0 4px rgba(124,108,255,0.18); }

.ccm-wheel-i { position: absolute; top: 50%; left: 50%; width: 32px; height: 32px; margin: -16px; border-radius: 50%; background: #1f1f2e; color: #c4b5fd; font: 700 12px/32px ui-monospace, monospace; text-align: center; text-decoration: none; border: 1px solid rgba(124,108,255,0.3); transform: rotate(var(--a)) translate(78px) rotate(calc(var(--a) * -1)); transition: transform 0.25s, background 0.2s, color 0.2s; }

.ccm-wheel-i:hover, .ccm-wheel-i:focus-visible { background: #7c6cff; color: #fff; transform: rotate(var(--a)) translate(78px) rotate(calc(var(--a) * -1)) scale(1.4); z-index: 2; }
<div class="ccm-wheel">
  <span class="ccm-wheel-c" aria-hidden="true">◉</span>
  <a href="#" class="ccm-wheel-i" style="--a:0deg">A</a>
  <a href="#" class="ccm-wheel-i" style="--a:45deg">B</a>
  <a href="#" class="ccm-wheel-i" style="--a:90deg">C</a>
  <a href="#" class="ccm-wheel-i" style="--a:135deg">D</a>
  <a href="#" class="ccm-wheel-i" style="--a:180deg">E</a>
  <a href="#" class="ccm-wheel-i" style="--a:225deg">F</a>
  <a href="#" class="ccm-wheel-i" style="--a:270deg">G</a>
  <a href="#" class="ccm-wheel-i" style="--a:315deg">H</a>
</div>
07 / 21
Pie Slice Selector
Pure CSS
Four pie slices, each a labelled clickable wedge. Real per-slice hit area via border-radius corners (no overlapping squares) — only the visible pie wedge receives hover and click.
Try it
.ccm-pie { position: relative; width: 170px; height: 170px; }

.ccm-pie-s { position: absolute; width: 50%; height: 50%; background: var(--c); text-decoration: none; cursor: pointer; transition: filter 0.2s, transform 0.2s; display: flex; }

.ccm-pie-tr { top: 0; right: 0;    border-top-right-radius: 100% 100%;    transform-origin: 0% 100%; }

.ccm-pie-br { bottom: 0; right: 0; border-bottom-right-radius: 100% 100%; transform-origin: 0% 0%; }

.ccm-pie-bl { bottom: 0; left: 0;  border-bottom-left-radius: 100% 100%;  transform-origin: 100% 0%; }

.ccm-pie-tl { top: 0; left: 0;     border-top-left-radius: 100% 100%;     transform-origin: 100% 100%; }

.ccm-pie-s span { margin: auto; padding: 8px 18px; font: 700 12px/1 system-ui, sans-serif; color: #fff; text-shadow: 0 1px 2px rgba(0,0,0,0.3); }

.ccm-pie-s:hover, .ccm-pie-s:focus-visible { filter: brightness(1.18) saturate(1.15); transform: scale(1.04); z-index: 2; }

.ccm-pie-h { position: absolute; top: 50%; left: 50%; width: 56px; height: 56px; margin: -28px; border-radius: 50%; background: #15151d; border: 3px solid #17171f; pointer-events: none; z-index: 3; }
<div class="ccm-pie">
  <a href="#" class="ccm-pie-s ccm-pie-tr" style="--c:#7c6cff" aria-label="Plan"><span>Plan</span></a>
  <a href="#" class="ccm-pie-s ccm-pie-br" style="--c:#ff6c8a" aria-label="Build"><span>Build</span></a>
  <a href="#" class="ccm-pie-s ccm-pie-bl" style="--c:#2ecc8a" aria-label="Test"><span>Test</span></a>
  <a href="#" class="ccm-pie-s ccm-pie-tl" style="--c:#f5a84a" aria-label="Ship"><span>Ship</span></a>
  <span class="ccm-pie-h" aria-hidden="true"></span>
</div>
08 / 21
Donut Sectors
Pure CSS
A 6-sector SVG donut with curved labels following each arc via textPath. On hover the sector pulls outward (split effect), the label brightens, and a subtle scale animation runs. Real hit-tested wedges.
Try it
.ccm-donut { width: 200px; height: 200px; display: block; overflow: visible; font-family: ui-monospace, 'SF Mono', monospace; }

.ccm-donut-s { cursor: pointer; transform-origin: 100px 100px; transform: scale(1) translate(0, 0); transition: transform 0.35s cubic-bezier(0.34,1.56,0.64,1), filter 0.2s; text-decoration: none; animation: ccm-donut-pop 0.5s cubic-bezier(0.34,1.56,0.64,1) backwards; }

.ccm-donut-s path { stroke: #17171f; stroke-width: 2; transition: filter 0.2s; }

.ccm-donut-l { font: 700 10px/1 ui-monospace, monospace; fill: #fff; letter-spacing: 0.18em; pointer-events: none; opacity: 0.92; text-shadow: 0 1px 2px rgba(0,0,0,0.4); }

.ccm-donut-c { font: 700 9px/1 ui-monospace, monospace; fill: #c4b5fd; letter-spacing: 0.16em; pointer-events: none; }

.ccm-donut-s:nth-of-type(1):hover, .ccm-donut-s:nth-of-type(1):focus-visible { transform: translate(4px, -4px); }

.ccm-donut-s:nth-of-type(2):hover, .ccm-donut-s:nth-of-type(2):focus-visible { transform: translate(6px, 0px); }

.ccm-donut-s:nth-of-type(3):hover, .ccm-donut-s:nth-of-type(3):focus-visible { transform: translate(4px, 4px); }

.ccm-donut-s:nth-of-type(4):hover, .ccm-donut-s:nth-of-type(4):focus-visible { transform: translate(-4px, 4px); }

.ccm-donut-s:nth-of-type(5):hover, .ccm-donut-s:nth-of-type(5):focus-visible { transform: translate(-6px, 0px); }

.ccm-donut-s:nth-of-type(6):hover, .ccm-donut-s:nth-of-type(6):focus-visible { transform: translate(-4px, -4px); }

.ccm-donut-s:hover path, .ccm-donut-s:focus-visible path { filter: brightness(1.18) saturate(1.1); }

.ccm-donut-s:nth-of-type(1) { animation-delay: 0.05s; }

.ccm-donut-s:nth-of-type(2) { animation-delay: 0.10s; }

.ccm-donut-s:nth-of-type(3) { animation-delay: 0.15s; }

.ccm-donut-s:nth-of-type(4) { animation-delay: 0.20s; }

.ccm-donut-s:nth-of-type(5) { animation-delay: 0.25s; }

.ccm-donut-s:nth-of-type(6) { animation-delay: 0.30s; }
<svg class="ccm-donut" viewBox="0 0 200 200" role="navigation" aria-label="Donut menu">
  <defs>
    <path id="ccm-donut-arc-1" d="M 100 25 A 75 75 0 0 1 164.95 62.5"/>
    <path id="ccm-donut-arc-2" d="M 164.95 62.5 A 75 75 0 0 1 164.95 137.5"/>
    <path id="ccm-donut-arc-3" d="M 164.95 137.5 A 75 75 0 0 1 100 175"/>
    <path id="ccm-donut-arc-4" d="M 35.05 137.5 A 75 75 0 0 1 35.05 62.5"/>
    <path id="ccm-donut-arc-5" d="M 100 175 A 75 75 0 0 1 35.05 137.5"/>
    <path id="ccm-donut-arc-6" d="M 35.05 62.5 A 75 75 0 0 1 100 25"/>
  </defs>
  <a href="#" class="ccm-donut-s" style="--c:#7c6cff" aria-label="Plan">
    <path d="M 100 100 L 100 10 A 90 90 0 0 1 177.94 55 Z" fill="var(--c)"/>
    <text class="ccm-donut-l"><textPath href="#ccm-donut-arc-1" startOffset="50%" text-anchor="middle">PLAN</textPath></text>
  </a>
  <a href="#" class="ccm-donut-s" style="--c:#a78bfa" aria-label="Design">
    <path d="M 100 100 L 177.94 55 A 90 90 0 0 1 177.94 145 Z" fill="var(--c)"/>
    <text class="ccm-donut-l"><textPath href="#ccm-donut-arc-2" startOffset="50%" text-anchor="middle">DESIGN</textPath></text>
  </a>
  <a href="#" class="ccm-donut-s" style="--c:#ff6c8a" aria-label="Build">
    <path d="M 100 100 L 177.94 145 A 90 90 0 0 1 100 190 Z" fill="var(--c)"/>
    <text class="ccm-donut-l"><textPath href="#ccm-donut-arc-3" startOffset="50%" text-anchor="middle">BUILD</textPath></text>
  </a>
  <a href="#" class="ccm-donut-s" style="--c:#f5a84a" aria-label="Test">
    <path d="M 100 100 L 100 190 A 90 90 0 0 1 22.06 145 Z" fill="var(--c)"/>
    <text class="ccm-donut-l"><textPath href="#ccm-donut-arc-5" startOffset="50%" text-anchor="middle">TEST</textPath></text>
  </a>
  <a href="#" class="ccm-donut-s" style="--c:#2ecc8a" aria-label="Ship">
    <path d="M 100 100 L 22.06 145 A 90 90 0 0 1 22.06 55 Z" fill="var(--c)"/>
    <text class="ccm-donut-l"><textPath href="#ccm-donut-arc-4" startOffset="50%" text-anchor="middle">SHIP</textPath></text>
  </a>
  <a href="#" class="ccm-donut-s" style="--c:#00e5ff" aria-label="Learn">
    <path d="M 100 100 L 22.06 55 A 90 90 0 0 1 100 10 Z" fill="var(--c)"/>
    <text class="ccm-donut-l"><textPath href="#ccm-donut-arc-6" startOffset="50%" text-anchor="middle">LEARN</textPath></text>
  </a>
  <circle cx="100" cy="100" r="32" fill="#15151d" stroke="#17171f" stroke-width="3"/>
  <text x="100" y="104" class="ccm-donut-c" text-anchor="middle">MENU</text>
</svg>
09 / 21
Half-Donut Speed Dial
Pure CSS
A B C D E
A bottom-anchored half-arc FAB that fans 5 actions upward in a 180° spread. Each item lands on the arc at an even angle. Pure CSS via :checked.
Try it
.ccm-half { position: relative; width: 220px; height: 130px; display: flex; align-items: flex-end; justify-content: center; }

.ccm-half-fab { width: 56px; height: 56px; border-radius: 50%; background: linear-gradient(135deg, #2ecc8a, #00e5ff); color: #0a0f0c; font-weight: 700; font-size: 24px; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: transform 0.4s; z-index: 2; box-shadow: 0 6px 22px rgba(46,204,138,0.4); }

.ccm-half input:checked + .ccm-half-fab { transform: rotate(180deg); }

.ccm-half-i { position: absolute; bottom: 28px; left: 50%; width: 36px; height: 36px; margin: -18px; border-radius: 50%; background: #1f1f2e; border: 1.5px solid #2ecc8a; color: #2ecc8a; font: 700 12px/36px ui-monospace, monospace; text-align: center; text-decoration: none; transform: translate(0, 0) rotate(var(--a)) translate(0) rotate(calc(var(--a) * -1)); opacity: 0; transition: transform 0.5s cubic-bezier(0.34,1.56,0.64,1), opacity 0.3s; }

.ccm-half input:checked ~ .ccm-half-i { transform: translate(0, 0) rotate(var(--a)) translate(80px) rotate(calc(var(--a) * -1)); opacity: 1; }

.ccm-half-i:nth-of-type(1) { transition-delay: 0.05s; }

.ccm-half-i:nth-of-type(2) { transition-delay: 0.10s; }

.ccm-half-i:nth-of-type(3) { transition-delay: 0.15s; }

.ccm-half-i:nth-of-type(4) { transition-delay: 0.20s; }

.ccm-half-i:nth-of-type(5) { transition-delay: 0.25s; }
<div class="ccm-half">
  <input type="checkbox" id="ccm-half-t" hidden>
  <label for="ccm-half-t" class="ccm-half-fab" aria-label="Toggle">⊕</label>
  <a href="#" class="ccm-half-i" style="--a:180deg">A</a>
  <a href="#" class="ccm-half-i" style="--a:225deg">B</a>
  <a href="#" class="ccm-half-i" style="--a:270deg">C</a>
  <a href="#" class="ccm-half-i" style="--a:315deg">D</a>
  <a href="#" class="ccm-half-i" style="--a:0deg">E</a>
</div>
10 / 21
Glass Dome
Pure CSS
A glass hemisphere with backdrop-blur and an inner highlight; menu items orbit along the dome edge. Items lift on hover. The hero of the dome family.
Try it
.ccm-glass-bg { padding: 30px; border-radius: 14px; background: linear-gradient(135deg, #7c6cff 0%, #ff6c8a 100%); }

.ccm-glass { position: relative; width: 200px; height: 110px; }

.ccm-glass-dome { position: absolute; bottom: 0; left: 0; right: 0; height: 110px; border-radius: 100px 100px 0 0; background: rgba(255,255,255,0.18); border: 1px solid rgba(255,255,255,0.45); border-bottom: none; backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px); box-shadow: inset 0 1px 0 rgba(255,255,255,0.6), inset 0 -10px 30px rgba(255,255,255,0.1); }

.ccm-glass-dome::before { content: ''; position: absolute; top: 8px; left: 20%; right: 50%; height: 24px; border-radius: 50%; background: linear-gradient(180deg, rgba(255,255,255,0.55), transparent); filter: blur(2px); }

.ccm-glass-i { position: absolute; bottom: 0; left: 50%; width: 32px; height: 32px; margin-left: -16px; border-radius: 50%; background: rgba(255,255,255,0.25); border: 1px solid rgba(255,255,255,0.5); color: #fff; display: flex; align-items: center; justify-content: center; text-decoration: none; transform: rotate(var(--a)) translate(95px) rotate(calc(var(--a) * -1)); transition: transform 0.3s, background 0.2s; backdrop-filter: blur(6px); }

.ccm-glass-i:hover { background: rgba(255,255,255,0.45); transform: rotate(var(--a)) translate(95px) rotate(calc(var(--a) * -1)) scale(1.18) translateY(-6px); }
<div class="ccm-glass-bg">
  <div class="ccm-glass">
    <div class="ccm-glass-dome"></div>
    <a href="#" class="ccm-glass-i" style="--a:-150deg">⌂</a>
    <a href="#" class="ccm-glass-i" style="--a:-120deg">⌕</a>
    <a href="#" class="ccm-glass-i" style="--a:-90deg">★</a>
    <a href="#" class="ccm-glass-i" style="--a:-60deg">♥</a>
    <a href="#" class="ccm-glass-i" style="--a:-30deg">⚙</a>
  </div>
</div>
11 / 21
Holographic Dome
Pure CSS
A translucent dome with an iridescent rim that rotates continuously via @property-animated conic gradient. The dome itself stays still; only the rim shimmers.
Try it
.ccm-holo { position: relative; width: 200px; height: 110px; }

.ccm-holo-rim { position: absolute; bottom: -2px; left: -2px; right: -2px; height: 114px; border-radius: 100px 100px 0 0; background: conic-gradient(from var(--ccm-holo-a), #7c6cff, #ff6c8a, #2ecc8a, #00e5ff, #7c6cff); animation: ccm-holo-spin 4s linear infinite; filter: blur(0.5px); }

.ccm-holo-dome { position: absolute; bottom: 0; left: 0; right: 0; height: 110px; border-radius: 100px 100px 0 0; background: rgba(15,15,25,0.7); backdrop-filter: blur(8px); border-bottom: none; }

.ccm-holo-i { position: absolute; bottom: 0; left: 50%; width: 32px; height: 32px; margin-left: -16px; border-radius: 50%; background: #15151d; border: 1px solid rgba(255,255,255,0.18); color: #c4b5fd; display: flex; align-items: center; justify-content: center; text-decoration: none; transform: rotate(var(--a)) translate(95px) rotate(calc(var(--a) * -1)); transition: transform 0.3s, color 0.2s; }

.ccm-holo-i:hover { color: #fff; transform: rotate(var(--a)) translate(95px) rotate(calc(var(--a) * -1)) scale(1.2); }

@media (prefers-reduced-motion: reduce) {
  .ccm-holo-rim,
  .ccm-snow-flakes,
  .ccm-orbit-sat, .ccm-orbit-i,
  .ccm-solar-o, .ccm-solar-o a,
  .ccm-tri-grp,
  .ccm-vinyl-disc,
  .ccm-bh-disk,
  .ccm-neb-cloud, .ccm-neb-i { animation: none !important; }
}
<div class="ccm-holo">
  <div class="ccm-holo-rim"></div>
  <div class="ccm-holo-dome"></div>
  <a href="#" class="ccm-holo-i" style="--a:-150deg">⌂</a>
  <a href="#" class="ccm-holo-i" style="--a:-120deg">⌕</a>
  <a href="#" class="ccm-holo-i" style="--a:-90deg">★</a>
  <a href="#" class="ccm-holo-i" style="--a:-60deg">♥</a>
  <a href="#" class="ccm-holo-i" style="--a:-30deg">⚙</a>
</div>
12 / 21
Single Satellite Orbit
Pure CSS
One satellite continuously orbits the central FAB. Hover the FAB to pause the orbit, click to "lock in" the satellite at its current angle. Pure CSS via :hover paused state.
Try it
.ccm-orbit { position: relative; width: 180px; height: 180px; display: flex; align-items: center; justify-content: center; }

.ccm-orbit-track { position: absolute; inset: 20px; border-radius: 50%; border: 1px dashed rgba(124,108,255,0.3); }

.ccm-orbit-c { width: 48px; height: 48px; border-radius: 50%; background: linear-gradient(135deg, #7c6cff, #a78bfa); color: #fff; display: inline-flex; align-items: center; justify-content: center; text-decoration: none; font-size: 18px; z-index: 2; box-shadow: 0 0 0 4px rgba(124,108,255,0.18); }

.ccm-orbit-sat { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; animation: ccm-orbit-spin 5s linear infinite; }

.ccm-orbit-i { display: inline-flex; align-items: center; justify-content: center; width: 36px; height: 36px; border-radius: 50%; background: #1f1f2e; color: #c4b5fd; text-decoration: none; border: 1px solid rgba(124,108,255,0.4); transform: translate(70px); animation: ccm-orbit-counter 5s linear infinite; }

.ccm-orbit:hover .ccm-orbit-sat, .ccm-orbit:hover .ccm-orbit-i { animation-play-state: paused; }

@media (prefers-reduced-motion: reduce) {
  .ccm-holo-rim,
  .ccm-snow-flakes,
  .ccm-orbit-sat, .ccm-orbit-i,
  .ccm-solar-o, .ccm-solar-o a,
  .ccm-tri-grp,
  .ccm-vinyl-disc,
  .ccm-bh-disk,
  .ccm-neb-cloud, .ccm-neb-i { animation: none !important; }
}
<div class="ccm-orbit">
  <div class="ccm-orbit-track"></div>
  <div class="ccm-orbit-sat"><a href="#" class="ccm-orbit-i" aria-label="Item">◆</a></div>
  <a href="#" class="ccm-orbit-c" aria-label="Center">◉</a>
</div>
13 / 21
Solar System
Pure CSS
A central "sun" + 4 "planets" at decreasing orbit radii, each labelled, each orbiting at a different speed (Kepler-style — closer = faster). Real CSS animation, no JS.
Try it
.ccm-solar { position: relative; width: 240px; height: 240px; display: flex; align-items: center; justify-content: center; }

.ccm-solar-c { width: 36px; height: 36px; border-radius: 50%; background: radial-gradient(circle at 30% 30%, #ffd479, #f5a84a); display: inline-flex; align-items: center; justify-content: center; color: rgba(255,255,255,0.85); font-size: 20px; text-decoration: none; box-shadow: 0 0 24px #f5a84a; z-index: 5; }

.ccm-solar-o { position: absolute; border-radius: 50%; border: 1px dashed rgba(255,255,255,0.08); animation: ccm-solar-spin linear infinite; }

.ccm-solar-o1 { inset: 20px;  animation-duration: 11s; }

.ccm-solar-o2 { inset: 50px;  animation-duration: 8s; }

.ccm-solar-o3 { inset: 80px;  animation-duration: 6s; }

.ccm-solar-o4 { inset: 105px; animation-duration: 4s; }

.ccm-solar-pos { position: absolute; top: 0; left: 50%; width: 0; height: 0; }

.ccm-solar-p { position: absolute; top: 0; left: 0; width: 24px; height: 24px; margin: -12px; border-radius: 50%; background: #1f1f2e; color: #c4b5fd; font-size: 13px; display: inline-flex; align-items: center; justify-content: center; text-decoration: none; border: 1px solid rgba(124,108,255,0.4); animation: ccm-solar-counter linear infinite; z-index: 6; }

.ccm-solar-o1 .ccm-solar-p { animation-duration: 11s; }

.ccm-solar-o2 .ccm-solar-p { animation-duration: 8s; }

.ccm-solar-o3 .ccm-solar-p { animation-duration: 6s; }

.ccm-solar-o4 .ccm-solar-p { animation-duration: 4s; }

.ccm-solar-p:hover { background: #7c6cff; color: #fff; }

.ccm-solar:hover .ccm-solar-o, .ccm-solar:hover .ccm-solar-p { animation-play-state: paused; }

@media (prefers-reduced-motion: reduce) {
  .ccm-holo-rim,
  .ccm-snow-flakes,
  .ccm-orbit-sat, .ccm-orbit-i,
  .ccm-solar-o, .ccm-solar-o a,
  .ccm-tri-grp,
  .ccm-vinyl-disc,
  .ccm-bh-disk,
  .ccm-neb-cloud, .ccm-neb-i { animation: none !important; }
}
<div class="ccm-solar">
  <a href="#" class="ccm-solar-c" aria-label="Sun">☀</a>
  <div class="ccm-solar-o ccm-solar-o1"><span class="ccm-solar-pos"><a href="#" class="ccm-solar-p" aria-label="Mercury">☿</a></span></div>
  <div class="ccm-solar-o ccm-solar-o2"><span class="ccm-solar-pos"><a href="#" class="ccm-solar-p" aria-label="Venus">♀</a></span></div>
  <div class="ccm-solar-o ccm-solar-o3"><span class="ccm-solar-pos"><a href="#" class="ccm-solar-p" aria-label="Earth">♁</a></span></div>
  <div class="ccm-solar-o ccm-solar-o4"><span class="ccm-solar-pos"><a href="#" class="ccm-solar-p" aria-label="Mars">♂</a></span></div>
</div>
14 / 21
Hexagonal Honeycomb
Pure CSS
Six hexagonal items arranged around a central hex via real geometry (clip-path polygon). Each cell highlights independently. Honeycomb pattern with mathematical precision.
Try it
.ccm-hex { position: relative; width: 200px; height: 200px; display: flex; align-items: center; justify-content: center; }

.ccm-hex-c, .ccm-hex-i { width: 50px; height: 58px; clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%); display: inline-flex; align-items: center; justify-content: center; font: 700 11px/1 system-ui, sans-serif; text-decoration: none; transition: transform 0.2s, background 0.2s; }

.ccm-hex-c { background: linear-gradient(135deg, #7c6cff, #a78bfa); color: #fff; position: relative; z-index: 2; }

.ccm-hex-i { position: absolute; top: 50%; left: 50%; margin: -29px -25px; background: #1f1f2e; color: #c4b5fd; transform: rotate(var(--a)) translate(64px) rotate(calc(var(--a) * -1)); }

.ccm-hex-i:hover, .ccm-hex-i:focus-visible { background: #2a2a3e; color: #fff; transform: rotate(var(--a)) translate(64px) rotate(calc(var(--a) * -1)) scale(1.1); }
<div class="ccm-hex">
  <a href="#" class="ccm-hex-c" aria-label="Center">◆</a>
  <a href="#" class="ccm-hex-i" style="--a:-90deg" aria-label="N">N</a>
  <a href="#" class="ccm-hex-i" style="--a:-30deg" aria-label="NE">NE</a>
  <a href="#" class="ccm-hex-i" style="--a:30deg" aria-label="SE">SE</a>
  <a href="#" class="ccm-hex-i" style="--a:90deg" aria-label="S">S</a>
  <a href="#" class="ccm-hex-i" style="--a:150deg" aria-label="SW">SW</a>
  <a href="#" class="ccm-hex-i" style="--a:210deg" aria-label="NW">NW</a>
</div>
15 / 21
Rotating Triangle Trio
Pure CSS
A B C
Three triangle items rotate together in a trio formation. Hover any triangle to lift it forward. The whole group rotates slowly on idle for visual interest.
Try it
.ccm-tri { position: relative; width: 180px; height: 180px; display: flex; align-items: center; justify-content: center; }

.ccm-tri-grp { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; animation: ccm-tri-spin 16s linear infinite; }

.ccm-tri-i { position: absolute; width: 48px; height: 48px; display: inline-flex; align-items: center; justify-content: center; background: linear-gradient(135deg, #ff6c8a, #f5a84a); color: #fff; font: 700 14px/1 system-ui, sans-serif; text-decoration: none; clip-path: polygon(50% 5%, 95% 95%, 5% 95%); transform: rotate(var(--a)) translate(60px); transition: transform 0.3s; }

.ccm-tri-i:hover { transform: rotate(var(--a)) translate(60px) scale(1.2); filter: drop-shadow(0 4px 12px rgba(255,108,138,0.5)); z-index: 2; }

.ccm-tri-c { width: 14px; height: 14px; border-radius: 50%; background: #1f1f2e; border: 2px solid #7c6cff; position: relative; z-index: 1; }

@media (prefers-reduced-motion: reduce) {
  .ccm-holo-rim,
  .ccm-snow-flakes,
  .ccm-orbit-sat, .ccm-orbit-i,
  .ccm-solar-o, .ccm-solar-o a,
  .ccm-tri-grp,
  .ccm-vinyl-disc,
  .ccm-bh-disk,
  .ccm-neb-cloud, .ccm-neb-i { animation: none !important; }
}
<div class="ccm-tri">
  <div class="ccm-tri-grp">
    <a href="#" class="ccm-tri-i" style="--a:-90deg">A</a>
    <a href="#" class="ccm-tri-i" style="--a:30deg">B</a>
    <a href="#" class="ccm-tri-i" style="--a:150deg">C</a>
  </div>
  <span class="ccm-tri-c" aria-hidden="true">◯</span>
</div>
16 / 21
Vinyl Record
Pure CSS
A spinning vinyl record menu. The disc rotates continuously; menu items sit on the label area. Pause on hover via animation-play-state. Classic music-app metaphor.
Try it
.ccm-vinyl { position: relative; width: 200px; height: 200px; display: flex; align-items: center; justify-content: center; }

.ccm-vinyl-disc { width: 200px; height: 200px; border-radius: 50%; background: radial-gradient(circle at center, #1a1a1a 30%, transparent 31%), repeating-radial-gradient(circle at center, #0a0a0a 0, #0a0a0a 1px, #1a1a1a 1px, #1a1a1a 3px); position: relative; animation: ccm-vinyl-spin 8s linear infinite; box-shadow: 0 0 0 1px rgba(255,255,255,0.05), 0 8px 24px rgba(0,0,0,0.5); }

.ccm-vinyl-disc::after { content: ''; position: absolute; top: 50%; left: 50%; width: 8px; height: 8px; margin: -4px; border-radius: 50%; background: #f5a84a; }

.ccm-vinyl-label { position: absolute; top: 50%; left: 50%; width: 100px; height: 100px; margin: -50px; border-radius: 50%; background: linear-gradient(135deg, #ff6c8a, #f5a84a); }

.ccm-vinyl-i { position: absolute; top: 50%; left: 50%; width: 22px; height: 22px; margin: -11px; border-radius: 50%; background: rgba(0,0,0,0.45); color: #fff; display: inline-flex; align-items: center; justify-content: center; font-size: 11px; text-decoration: none; transform: rotate(var(--a)) translate(28px) rotate(calc(var(--a) * -1)); }

.ccm-vinyl-i:hover { background: rgba(0,0,0,0.7); }

.ccm-vinyl:hover .ccm-vinyl-disc { animation-play-state: paused; }

@media (prefers-reduced-motion: reduce) {
  .ccm-holo-rim,
  .ccm-snow-flakes,
  .ccm-orbit-sat, .ccm-orbit-i,
  .ccm-solar-o, .ccm-solar-o a,
  .ccm-tri-grp,
  .ccm-vinyl-disc,
  .ccm-bh-disk,
  .ccm-neb-cloud, .ccm-neb-i { animation: none !important; }
}
<div class="ccm-vinyl">
  <div class="ccm-vinyl-disc">
    <div class="ccm-vinyl-label">
      <a href="#" class="ccm-vinyl-i" style="--a:-90deg">▶</a>
      <a href="#" class="ccm-vinyl-i" style="--a:0deg">♫</a>
      <a href="#" class="ccm-vinyl-i" style="--a:90deg">♪</a>
      <a href="#" class="ccm-vinyl-i" style="--a:180deg">⏏</a>
    </div>
  </div>
</div>
17 / 21
Compass Rose
Pure CSS
A nautical compass rose with N/E/S/W labels and a needle that points at the active direction. Hover any direction to rotate the needle smoothly via :has().
Try it
.ccm-compass { position: relative; width: 190px; height: 190px; display: flex; align-items: center; justify-content: center; }

.ccm-compass-face { width: 160px; height: 160px; border-radius: 50%; background: radial-gradient(circle at center, #2a2a3e, #15151d); border: 2px solid rgba(212,175,55,0.5); box-shadow: inset 0 0 24px rgba(212,175,55,0.2), 0 4px 16px rgba(0,0,0,0.4); }

.ccm-compass-needle { position: absolute; top: 50%; left: 50%; width: 4px; height: 70px; margin: -35px -2px; background: linear-gradient(180deg, #ff3d6e 0 50%, #d4af37 50% 100%); transform-origin: center; transform: rotate(0deg); transition: transform 0.6s cubic-bezier(0.34,1.56,0.64,1); border-radius: 2px; }

.ccm-compass:has(#ccm-compass-n:checked) .ccm-compass-needle { transform: rotate(0deg); }

.ccm-compass:has(#ccm-compass-e:checked) .ccm-compass-needle { transform: rotate(90deg); }

.ccm-compass:has(#ccm-compass-s:checked) .ccm-compass-needle { transform: rotate(180deg); }

.ccm-compass:has(#ccm-compass-w:checked) .ccm-compass-needle { transform: rotate(270deg); }

.ccm-compass-i { position: absolute; top: 50%; left: 50%; width: 28px; height: 28px; margin: -14px; border-radius: 50%; background: rgba(212,175,55,0.18); color: #ffd479; font: 700 12px/28px ui-monospace, monospace; text-align: center; cursor: pointer; border: 1px solid rgba(212,175,55,0.4); transform: rotate(var(--a)) translate(80px) rotate(calc(var(--a) * -1)); transition: background 0.2s, color 0.2s, transform 0.2s; }

.ccm-compass-i:hover { background: rgba(212,175,55,0.4); color: #fff; }
<div class="ccm-compass">
  <input type="radio" name="ccm-compass" id="ccm-compass-n" hidden checked>
  <input type="radio" name="ccm-compass" id="ccm-compass-e" hidden>
  <input type="radio" name="ccm-compass" id="ccm-compass-s" hidden>
  <input type="radio" name="ccm-compass" id="ccm-compass-w" hidden>
  <div class="ccm-compass-face"></div>
  <div class="ccm-compass-needle" aria-hidden="true"></div>
  <label for="ccm-compass-n" class="ccm-compass-i" style="--a:0deg">N</label>
  <label for="ccm-compass-e" class="ccm-compass-i" style="--a:90deg">E</label>
  <label for="ccm-compass-s" class="ccm-compass-i" style="--a:180deg">S</label>
  <label for="ccm-compass-w" class="ccm-compass-i" style="--a:270deg">W</label>
</div>
18 / 21
Iris Aperture
Pure CSS
A camera-lens iris with 6 blades. On hover the blades "stop down" via clip-path + rotation, revealing menu items around the exposed diaphragm.
Try it
.ccm-iris { position: relative; width: 200px; height: 200px; display: flex; align-items: center; justify-content: center; border-radius: 50%; background: radial-gradient(circle at center, #0a0a14 30%, #15151d); border: 4px solid #2a2a3e; }

.ccm-iris-blade { position: absolute; top: 50%; left: 50%; width: 100px; height: 100px; margin: -50px; background: linear-gradient(135deg, #2a2a3e, #15151d); clip-path: polygon(50% 0%, 100% 50%, 50% 50%); transform: rotate(var(--a)) translateY(-30px); transform-origin: center; transition: transform 0.5s cubic-bezier(0.65,0,0.35,1); }

.ccm-iris:hover .ccm-iris-blade { transform: rotate(calc(var(--a) + 30deg)) translateY(-50px); }

.ccm-iris-i { position: absolute; top: 50%; left: 50%; width: 28px; height: 28px; margin: -14px; border-radius: 50%; background: #1f1f2e; color: #00e5ff; display: inline-flex; align-items: center; justify-content: center; text-decoration: none; border: 1px solid rgba(0,229,255,0.4); transform: rotate(var(--a)) translate(70px) rotate(calc(var(--a) * -1)); opacity: 0; transition: opacity 0.4s ease 0.25s; z-index: 2; }

.ccm-iris:hover .ccm-iris-i { opacity: 1; }

.ccm-iris-i:hover { background: #00e5ff; color: #0a0a14; }
<div class="ccm-iris">
  <div class="ccm-iris-blade" style="--a:0deg"></div>
  <div class="ccm-iris-blade" style="--a:60deg"></div>
  <div class="ccm-iris-blade" style="--a:120deg"></div>
  <div class="ccm-iris-blade" style="--a:180deg"></div>
  <div class="ccm-iris-blade" style="--a:240deg"></div>
  <div class="ccm-iris-blade" style="--a:300deg"></div>
  <a href="#" class="ccm-iris-i" style="--a:-90deg">◐</a>
  <a href="#" class="ccm-iris-i" style="--a:-30deg">◑</a>
  <a href="#" class="ccm-iris-i" style="--a:30deg">◒</a>
  <a href="#" class="ccm-iris-i" style="--a:90deg">◓</a>
  <a href="#" class="ccm-iris-i" style="--a:150deg">◉</a>
  <a href="#" class="ccm-iris-i" style="--a:210deg">○</a>
</div>
19 / 21
Black Hole
Pure CSS
A B C D E F
Items orbit a glowing accretion-disk singularity at all times. On hover the disk pulses brighter and items lift outward slightly — a gravitational "release". Items stay clickable throughout.
Try it
.ccm-bh { position: relative; width: 200px; height: 200px; display: flex; align-items: center; justify-content: center; }

.ccm-bh-disk { position: absolute; top: 50%; left: 50%; width: 110px; height: 110px; margin: -55px; border-radius: 50%; background: conic-gradient(from 0deg, #d4af37, #ff6c8a, #7c6cff, #d4af37); filter: blur(6px); animation: ccm-bh-spin 8s linear infinite; transition: filter 0.4s; z-index: 1; }

.ccm-bh:hover .ccm-bh-disk { filter: blur(4px) brightness(1.3); }

.ccm-bh-core { position: absolute; top: 50%; left: 50%; width: 56px; height: 56px; margin: -28px; border-radius: 50%; background: #000; box-shadow: 0 0 30px 8px rgba(0,0,0,0.7); z-index: 2; }

.ccm-bh-i { position: absolute; top: 50%; left: 50%; width: 30px; height: 30px; margin: -15px; border-radius: 50%; background: #1f1f2e; color: #ffd479; display: inline-flex; align-items: center; justify-content: center; font: 700 12px/1 ui-monospace, monospace; text-decoration: none; border: 1px solid rgba(212,175,55,0.5); transform: rotate(var(--a)) translate(80px) rotate(calc(var(--a) * -1)); transition: transform 0.5s cubic-bezier(0.34,1.56,0.64,1), background 0.2s, color 0.2s, box-shadow 0.3s; z-index: 3; }

.ccm-bh:hover .ccm-bh-i { transform: rotate(var(--a)) translate(92px) rotate(calc(var(--a) * -1)); box-shadow: 0 0 12px rgba(212,175,55,0.45); }

.ccm-bh-i:hover, .ccm-bh-i:focus-visible { background: #ffd479; color: #0a0a14; transform: rotate(var(--a)) translate(96px) rotate(calc(var(--a) * -1)) scale(1.2); z-index: 4; }

@media (prefers-reduced-motion: reduce) {
  .ccm-holo-rim,
  .ccm-snow-flakes,
  .ccm-orbit-sat, .ccm-orbit-i,
  .ccm-solar-o, .ccm-solar-o a,
  .ccm-tri-grp,
  .ccm-vinyl-disc,
  .ccm-bh-disk,
  .ccm-neb-cloud, .ccm-neb-i { animation: none !important; }
}
<div class="ccm-bh">
  <div class="ccm-bh-disk" aria-hidden="true"></div>
  <div class="ccm-bh-core" aria-hidden="true"></div>
  <a href="#" class="ccm-bh-i" style="--a:-90deg" aria-label="Item A">A</a>
  <a href="#" class="ccm-bh-i" style="--a:-30deg" aria-label="Item B">B</a>
  <a href="#" class="ccm-bh-i" style="--a:30deg" aria-label="Item C">C</a>
  <a href="#" class="ccm-bh-i" style="--a:90deg" aria-label="Item D">D</a>
  <a href="#" class="ccm-bh-i" style="--a:150deg" aria-label="Item E">E</a>
  <a href="#" class="ccm-bh-i" style="--a:210deg" aria-label="Item F">F</a>
</div>
20 / 21
Chronometer
Pure CSS
An analog watch face with menu items at hour positions. The second-hand sweeps continuously; click any hour position to "set" the menu (hand snaps to that hour).
Try it
.ccm-chrono { position: relative; width: 190px; height: 190px; display: flex; align-items: center; justify-content: center; --hand: 90deg; }

.ccm-chrono-face { width: 170px; height: 170px; border-radius: 50%; background: radial-gradient(circle at 30% 30%, #f5f5f5, #d8d8d8); border: 3px solid #2a2a3e; box-shadow: inset 0 2px 4px rgba(0,0,0,0.15), 0 4px 12px rgba(0,0,0,0.4); }

.ccm-chrono-hand { position: absolute; top: 50%; left: 50%; width: 3px; height: 60px; margin-left: -1.5px; background: #2a2a3e; border-radius: 1.5px; transform-origin: top center; transform: rotate(var(--hand)); transition: transform 0.6s cubic-bezier(0.34,1.56,0.64,1); }

.ccm-chrono-hand::after { content: ''; position: absolute; bottom: -3px; left: -2px; width: 7px; height: 7px; border-radius: 50%; background: #ff3d6e; }

.ccm-chrono:has(#ccm-chrono-3:checked)  { --hand: -90deg; }

.ccm-chrono:has(#ccm-chrono-6:checked)  { --hand: 0deg; }

.ccm-chrono:has(#ccm-chrono-9:checked)  { --hand: 90deg; }

.ccm-chrono:has(#ccm-chrono-12:checked) { --hand: 180deg; }

.ccm-chrono-i { position: absolute; top: 50%; left: 50%; width: 28px; height: 28px; margin: -14px; border-radius: 50%; background: rgba(0,0,0,0.05); color: #2a2a3e; font: 700 13px/28px Georgia, serif; text-align: center; cursor: pointer; transform: rotate(var(--a)) translate(70px) rotate(calc(var(--a) * -1)); transition: background 0.2s, color 0.2s; }

.ccm-chrono-i:hover { background: rgba(255,61,110,0.15); color: #ff3d6e; }
<div class="ccm-chrono">
  <input type="radio" name="ccm-chrono" id="ccm-chrono-3" hidden checked>
  <input type="radio" name="ccm-chrono" id="ccm-chrono-6" hidden>
  <input type="radio" name="ccm-chrono" id="ccm-chrono-9" hidden>
  <input type="radio" name="ccm-chrono" id="ccm-chrono-12" hidden>
  <div class="ccm-chrono-face"></div>
  <div class="ccm-chrono-hand" aria-hidden="true"></div>
  <label for="ccm-chrono-12" class="ccm-chrono-i" style="--a:0deg" aria-label="12">12</label>
  <label for="ccm-chrono-3"  class="ccm-chrono-i" style="--a:90deg" aria-label="3">3</label>
  <label for="ccm-chrono-6"  class="ccm-chrono-i" style="--a:180deg" aria-label="6">6</label>
  <label for="ccm-chrono-9"  class="ccm-chrono-i" style="--a:270deg" aria-label="9">9</label>
</div>
21 / 21
Nebula Cloud
Pure CSS
Items float in a soft glowing nebula cloud with no fixed grid positions. Each item has a subtle drift animation; hovering pulls it forward and brightens the surrounding glow.
Try it
.ccm-neb { position: relative; width: 220px; height: 180px; display: flex; align-items: center; justify-content: center; }

.ccm-neb-cloud { position: absolute; inset: 0; background: radial-gradient(60% 50% at 30% 40%, rgba(124,108,255,0.45), transparent 70%), radial-gradient(50% 40% at 70% 60%, rgba(255,108,138,0.4), transparent 70%), radial-gradient(45% 35% at 50% 30%, rgba(46,184,138,0.3), transparent 70%); filter: blur(10px); animation: ccm-neb-drift 10s ease-in-out infinite alternate; }

.ccm-neb-i { position: absolute; top: 50%; left: 50%; width: 36px; height: 36px; margin: -18px; border-radius: 50%; background: rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.18); color: #fff; font-size: 14px; display: inline-flex; align-items: center; justify-content: center; text-decoration: none; transform: translate(var(--x), var(--y)); animation: ccm-neb-float 4s ease-in-out var(--d) infinite alternate; transition: transform 0.3s, background 0.2s, box-shadow 0.3s; z-index: 2; }

.ccm-neb-i:hover { background: rgba(255,255,255,0.25); box-shadow: 0 0 20px rgba(255,255,255,0.4); transform: translate(var(--x), var(--y)) scale(1.3); animation-play-state: paused; }

@media (prefers-reduced-motion: reduce) {
  .ccm-holo-rim,
  .ccm-snow-flakes,
  .ccm-orbit-sat, .ccm-orbit-i,
  .ccm-solar-o, .ccm-solar-o a,
  .ccm-tri-grp,
  .ccm-vinyl-disc,
  .ccm-bh-disk,
  .ccm-neb-cloud, .ccm-neb-i { animation: none !important; }
}
<div class="ccm-neb">
  <div class="ccm-neb-cloud"></div>
  <a href="#" class="ccm-neb-i" style="--x:-50px;--y:-30px;--d:0s">★</a>
  <a href="#" class="ccm-neb-i" style="--x:30px;--y:-40px;--d:1s">✦</a>
  <a href="#" class="ccm-neb-i" style="--x:60px;--y:10px;--d:2s">✧</a>
  <a href="#" class="ccm-neb-i" style="--x:0px;--y:30px;--d:1.5s">◆</a>
  <a href="#" class="ccm-neb-i" style="--x:-60px;--y:20px;--d:0.5s">⬢</a>
</div>

Build your own

Tweak the exact look in our visual generators — no signup, instant copy-paste.

FAQ

Frequently asked questions

What is a CSS circular menu?
A circular menu (also called radial menu or pie menu) is a navigation pattern where items are arranged around a circle, dome, ring, or orbit instead of a flat horizontal or vertical list. Common variants include the floating-action-button speed-dial, full pie/donut wheels, satellite-orbit menus, and dome-shaped containers. They're particularly effective for mobile interfaces and any UI where space is limited.
How do you position items in a circle with pure CSS?
Use trigonometry baked into transforms. Set a CSS custom property --a (the angle) on each child and apply transform: rotate(var(--a)) translate(RADIUS) rotate(calc(var(--a) * -1)). The first rotate orients the item around the center, translate pushes it outward by the radius, and the second counter-rotate keeps the item upright. No JavaScript needed — every demo in this gallery uses this pattern.
How do you add curved text along a donut sector?
Use SVG with a hidden <path> inside <defs> that traces the arc you want the text to follow, then a <text><textPath href="#path-id">YOUR LABEL</textPath></text>. The text-anchor="middle" and startOffset="50%" attributes center the label along the arc. The Donut Sectors demo (#04) uses exactly this pattern with one path per sector.
Are these circular menus accessible?
Yes. Each demo uses real semantic <a> elements with aria-label on icon-only items, visible focus states, and keyboard tab order that follows the visual arrangement. Continuous animations (orbital spins, conic rotations, vinyl spin) honour the prefers-reduced-motion media query — animations stop and items show in their static position for users who request reduced motion.
Do circular menus work on touch devices?
Yes, with a caveat: hover-triggered fan-outs (Petal Fan, Iris Aperture) need a tap-to-toggle pattern on touch, which is handled by the :checked checkbox state in demos that include one (Petal Fan, Half-Donut Speed Dial). The always-visible variants (Full Circle Wheel, Pie Selector, Donut Sectors, Compass, Chronometer, Solar System, Black Hole) work identically on touch and desktop.
Can I use these circular menus in any framework?
Yes. Each demo is plain HTML and CSS (no JavaScript) with no dependencies. Drop the markup into React (with className), Vue, Svelte, Astro or static HTML — the styles work as-is. The CSS-variable-driven angles mean adding more items is just changing the --a values. MIT licensed.

Related collections