I built pure CSS 3D dice cube module: FSCSS

I built pure CSS 3D dice cube module: FSCSS

โ—1 โ—12
calendar_today ago โ€ข schedule2 min read

cube.fscss is a lightweight, pure CSS 3D cube system built on FSCSS. It renders a spinnable 6-face cube using nothing but perspective, transform-style: preserve-3d, and translateZ/rotateX/rotateY โ€” no JavaScript, no 3D library, no canvas.

Here's example whole thing rendered live, nothing but <div>s and CSS:

<script src="https://cdn.jsdelivr.net/npm/fscss@1.1.24/exec.min.js" defer></script>

<style>
@import((*) from cube)

@cube-root()
@cube-wrapper(.my-cube)
@cube-face(.my-cube div)
@cube-face-front(.my-cube .f1)
@cube-face-back(.my-cube .f2)
@cube-face-left(.my-cube .f3)
@cube-face-right(.my-cube .f4)
@cube-face-top(.my-cube .f5)
@cube-face-bottom(.my-cube .f6)
@cube-micro-dice(.my-cube .cube)

.my-cube {
  @cube-size(80px)
  transform: rotateY(50deg) rotateX(30deg);
  animation: spin 5s linear infinite;
}

@keyframes spin {
  0%   { transform: rotateX(20deg) rotateY(40deg); }
  100% { transform: rotateX(380deg) rotateY(400deg); }
}
</style>

<div class="my-cube">
  <div class="cube f1 dice-1"></div>
  <div class="cube f2 dice-2"></div>
  <div class="cube f3 dice-3"></div>
  <div class="cube f4 dice-4"></div>
  <div class="cube f5 dice-5"></div>
  <div class="cube f6 dice-6"></div>
</div>

No dice PNGs. No SVG paths. No canvas draw calls. Every pip is a radial-gradient.

Why build this

Already had a chart/dashboard module (st-core.fscss) built on clip-path polygons โ€” no JS charting library, just CSS custom properties driving the shape. I wanted to see how far that "CSS does the visual math" philosophy could stretch into actual 3D space. A cube felt like the right next test: real perspective, real preserve-3d, real face geometry โ€” not a flat illusion.

How the geometry works

A cube is 6 absolutely-positioned faces sharing one parent set to transform-style: preserve-3d. Each face gets pushed out from the center by half the cube's edge length, then rotated to face outward:

front  โ†’ translateZ(half)
back   โ†’ rotateY(180deg) translateZ(half)
top    โ†’ rotateX(90deg)  translateZ(half)
bottom โ†’ rotateX(-90deg) translateZ(half)
right  โ†’ rotateY(90deg)  translateZ(half)
left   โ†’ rotateY(-90deg) translateZ(half)

The entire cube's dimensions live in two CSS variables: --cube-size and --cube-half (always size รท 2). Resize the whole thing with one call:

.my-cube {
  @cube-size(150px)
}

Faces, offsets, and dice dot size all scale together โ€” no manually recalculating six translateZ values by hand.

Dice pips, no font glyphs

I originally reached for Unicode dice characters (โš€โšโš‚โšƒโš„โš…) for the pips, but font rendering for those glyphs is wildly inconsistent across platforms โ€” some Android/Chrome stacks show them thin, outlined, or as tofu boxes. Stacked radial-gradients solved it instead:

.dice-3 {
  background-image:
    radial-gradient(circle var(--cube-dot-size) at 25% 25%, var(--cube-dot-color) 99%, transparent 100%),
    radial-gradient(circle var(--cube-dot-size) at 50% 50%, var(--cube-dot-color) 99%, transparent 100%),
    radial-gradient(circle var(--cube-dot-size) at 75% 75%, var(--cube-dot-color) 99%, transparent 100%);
}

Font-independent, crisp at any size, and the dot radius is itself a variable โ€” so it scales automatically with @cube-size() instead of overflowing on a big cube or vanishing on a small one.

It's not just dice

Faces and face content are decoupled on purpose. The face mixins only handle position โ€” what renders inside is a separate "micro" you apply on top. Right now there are eight:

  • cube-micro-fire โ€” pulsing gradient-text fire
  • cube-micro-matrix โ€” scrolling digital rain
  • cube-micro-neon โ€” pulsing cyan/magenta glow border
  • cube-micro-float โ€” subtle bounce, stacks with anything
  • cube-micro-glass โ€” frosted backdrop-filter panel
  • cube-micro-holo โ€” holographic shifting gradient text
  • cube-micro-glitch โ€” RGB-split glitch shift
  • cube-micro-dice โ€” the pips above

Mix and match per face on the same cube:

@cube-micro-fire(.my-cube .f1)
@cube-micro-matrix(.my-cube .f2)
@cube-micro-neon(.my-cube .f3)
@cube-micro-glass(.my-cube .f5)
@cube-micro-holo(.my-cube .f6)

Try it

CDN, drop-in:

<script src="https://cdn.jsdelivr.net/npm/fscss@1.1.24/exec.min.js" defer></script>
<style> 
  @import((*) from cube)
  /* your mixins here */
</style>

Or compile ahead of time with the CLI for a plain .css file, zero runtime:

npm install -g fscss
fscss style.fscss style.css

Full docs, token reference, and more examples: github.com/fscss-ttr/cube.fscss

If you build something with it โ€” I'd love to see. And if you hit issue, open an issue, it's open-source.


help us star the repo so more dev can discover it. ๐Ÿ™

๐Ÿ”ฅ Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

More Posts

The Sovereign Vault โ€” A Comprehensive Guide to Protocol-Driven AI

Ken W. Algerverified - Jun 4

How I Built a React Portfolio in 7 Days That Landed โ‚น1.2L in Freelance Work

Dharanidharan - Feb 9

Tailwind CSS for Beginners: From Zero to Your First Component

muhammadfarhan.dev - Jul 21

I Wrote a Script to Fix Audible's Unreadable PDF Filenames

snapsynapseverified - Apr 20

Designing Stats UIs with FSCSS and st-core

Figsh - Apr 4
chevron_left
826 Points โ€ข 13 Badges
5Posts
2Comments
4Connections
A full-stack web developer: Building smart solutions, designing meaningful experiences, and continuo... Show more

Related Jobs

View all jobs โ†’

Commenters (This Week)

3 comments
1 comment
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!