@property --pulse-mix { syntax: ""; inherits: true; initial-value: 0; } :root { --cube-size: 4.5rem; --iso-scale: calc(sqrt(3) / 2); --cube-empty-alpha: 0.4; --face-step: 0.033; } .cube { --cube-width: calc(var(--cube-size) * 2 * var(--iso-scale)); --cube-height: calc(var(--cube-size) * 2); --cube-face-base: var(--border-color); --face-top: light-dark( var(--cube-face-base), oklch(from var(--cube-face-base) calc(l + var(--face-step) * 2) c h) ); --face-right: light-dark( oklch(from var(--cube-face-base) calc(l - var(--face-step) * 2) c h), var(--cube-face-base) ); --face-left: light-dark( oklch(from var(--cube-face-base) calc(l - var(--face-step)) c h), oklch(from var(--cube-face-base) calc(l + var(--face-step)) c h) ); --face-bottom: oklch( from var(--cube-face-base) calc(l - var(--face-step) * 3) c h ); --is-full: round(down, var(--fill), 1); --is-empty: round(down, calc(1 - var(--fill)), 1); flex-shrink: 0; position: relative; width: var(--cube-width); height: var(--cube-height); /* .cube can be an ; reset anchor styles that would clip the iso silhouette or underline the empty link. */ overflow: visible; text-decoration: none; color: var(--color); user-select: none; pointer-events: none; /* Hover/active styling is gated on the anchor tag: only cubes (confirmed blocks) react to pointer state. Plain
projected previews stay visually inert. --face-color-base is resolved lazily, so the orange override below feeds the face declarations too. */ &:is(a):hover, &:is(a):active, &.selected { color: var(--background-color); --face-color-base: var(--inv-border-color); --face-top: var(--face-color-base); --face-right: oklch( from var(--face-color-base) calc(l - var(--face-step) * 2) c h ); --face-left: oklch( from var(--face-color-base) calc(l - var(--face-step)) c h ); --face-bottom: oklch( from var(--face-color-base) calc(l - var(--face-step) * 3) c h ); } &:is(a):active, &.selected { color: var(--black); --face-color-base: var(--orange); } &.projected { animation: cube-pulse 4s ease-in-out infinite; --cube-face-base: color-mix( in oklch, var(--border-color), var(--background-color) calc(var(--pulse-mix) * 100%) ); } /* visibility (not color:transparent) so child hides too */ &.skeleton .face-text { visibility: hidden; } .face { position: absolute; transform-origin: 0 0; box-sizing: border-box; width: var(--cube-size); height: var(--cube-size); transform: translateY(50%) var(--face-orient) translate( calc(var(--cube-size) * var(--face-x)), calc(var(--cube-size) * var(--face-y)) ) scale(var(--face-scale-x, 1), var(--face-scale-y)); pointer-events: auto; } /* will-change on painted roles only so each gets its own compositor layer for snappy hover/select repaints. */ .liquid, .glass { will-change: background-color; } .liquid { background: var(--face-color); opacity: calc(1 - var(--is-empty)); --face-scale-y: calc(var(--iso-scale) * var(--fill)); --face-stack-shift: calc(var(--iso-scale) * (1 - var(--fill))); } .glass { background: oklch(from var(--face-color) l c h / var(--cube-empty-alpha)); --face-scale-y: calc(var(--iso-scale) * (1 - var(--fill))); --face-stack-shift: 0; } .face-text { --face-scale-y: var(--iso-scale); --face-stack-shift: 0; pointer-events: none; padding: 0.1rem; font-family: var(--font-mono); font-size: var(--font-size-xs); font-weight: 450; display: flex; flex-direction: column; align-items: center; text-align: center; &.top { justify-content: center; text-transform: uppercase; } &.right { justify-content: space-between; } &.left { justify-content: center; } p { margin: 0; } } .top, .bottom { --face-orient: rotate(30deg) skewX(-30deg); --face-scale-y: var(--iso-scale); } .right, .rear-left { --face-orient: rotate(-30deg) skewX(-30deg); } .left, .rear-right { --face-orient: rotate(30deg) skewX(30deg); } .top, .rear-right { --face-y: calc(var(--face-stack-shift) - var(--iso-scale)); } .left, .rear-left { --face-y: var(--face-stack-shift); } .right { --face-y: calc(var(--face-stack-shift) + var(--iso-scale)); } .bottom { --face-y: 0; } .top { --face-color: var(--face-top); --face-x: 0; } .bottom { --face-color: var(--face-bottom); --face-x: 1; } .right { --face-color: var(--face-right); --face-x: 1; } .left { --face-color: var(--face-left); --face-x: 0; } .rear-right { --face-color: var(--face-left); --face-x: 1; } .rear-left { --face-color: var(--face-top); --face-x: 1; --face-scale-x: -1; } .liquid.top { --face-x: calc(1 - var(--fill)); } } @keyframes cube-pulse { 0%, 100% { --pulse-mix: 0.5; } 50% { --pulse-mix: 1; } }