3D Computer

A retro computer workstation built entirely with CSS 3D transforms — no WebGL, just perspective and transform-style: preserve-3d.

CSS 3D3D computertransformsno-WebGLretro3D CSS · 3 files · 754 lines
CSS 3D

<Computer> + 4 zones · rotate(-5.4°, 5.4°, 0°) · scale 1.00

man computer-3d

A line ref opens it in the source below

Name

computer-3d — a laptop made of two CSS boxes, with the home's four widgets live on its screen.

How it works

  1. Two boxes, twelve faces. The lid and the chassis are each a preserve-3d frame of six faces, 10 px deep: the back sits at translateZ(-10px), the four edges are 10 px strips turned 90°.

  2. One camera. The container holds perspective: 2000px from its top left corner; the lid leans at rotateY(-45deg) rotateX(15deg), the chassis lies flat at rotateX(90deg).

  3. A real screen inside. The lid's front face is the only one left in the accessibility tree: it hosts the four widgets on a 3 × 3 grid, each one its own Lab. Every other face is aria-hidden and inert.

  4. Keys sized by their face. The chassis face is a size container (container-type: inline-size): the keyboard and the trackpad are measured in cqi, so they scale with the laptop instead of the page.

  5. Exploded, not rebuilt. The xray appends one translateZ(±n px) to each face's own transform. In a turned face's frame local Z is its normal, so the same step pushes every face outward.

Cost

  1. No canvas for the machine. The laptop is DOM faces and CSS. Only the four widgets on its screen draw, and they stop when the stage leaves the viewport or the screen is paused.

  2. Transforms only. Rotation, zoom and the explode change transform alone: no layout runs while a slider moves.

See also

import { assignInlineVars } from "@vanilla-extract/dynamic";
import clsx from "clsx";
import type { ComponentProps } from "react";
import { ComputerKeyboard } from "./components/computer-keyboard/computer-keyboard.component";
import * as styles from "./computer.css";
import type { ComputerFace } from "./computer.css";

export type ComputerFrame = "lid" | "chassis";
export type ComputerFaceId = `${ComputerFrame}-${ComputerFace}`;

/** How far a face travels at `explode={1}`: nearly five times the box's 10 px depth. */
export const COMPUTER_EXPLODE_PX = 48;

interface ComputerProps extends ComponentProps<"div"> {
  /** The Lab's xray: faces outlined, named and pulled apart. */
  xray?: boolean;
  /** With `xray`: 0 keeps the box assembled, 1 pushes each face out by COMPUTER_EXPLODE_PX. */
  explode?: number;
  /** With `xray`: the face lit gold. */
  litFace?: ComputerFaceId | null;
}

export function Computer({
  children,
  className,
  style,
  xray = false,
  explode = 0,
  litFace = null,
  ...props
}: ComputerProps) {
  // Only the xray writes these attributes: the home's markup stays as it was.
  const face = (frame: ComputerFrame, name: ComputerFace, label?: string) =>
    xray
      ? {
          "data-face": name,
          "data-lit": litFace === `${frame}-${name}` || undefined,
          "data-label": label,
        }
      : {};
  const frame = (name: ComputerFrame) => (xray ? { "data-frame": name } : {});

  return (
    <div
      className={clsx(styles.computerStyle, className)}
      data-xray={xray || undefined}
      style={
        xray
          ? {
              ...style,
              ...assignInlineVars({
                [styles.computerExplode]: `${explode * COMPUTER_EXPLODE_PX}px`,
              }),
            }
          : style
      }
      {...props}
    >
      <div id="computer-frame-lid" className={styles.computerFrameLidStyle} {...frame("lid")}>
        {/* Front face stays in the a11y tree so screen children can remain accessible. */}
        <div
          id="computer-frame-lid-front"
          className={styles.computerFrameLidFrontStyle}
          {...face("lid", "front", "lid · front")}
        >
          <div id="computer-screen" className={styles.computerScreenStyle}>
            {children}
          </div>
        </div>
        {/* Decorative lid faces — hidden from assistive tech and non-interactive. */}
        <div
          id="computer-frame-lid-back"
          className={styles.computerFrameLidBackStyle}
          aria-hidden="true"
          inert
          {...face("lid", "back", "lid · back")}
        />
        <div
          id="computer-frame-lid-bottom"
          className={styles.computerFrameLidBottomStyle}
          aria-hidden="true"
          inert
          {...face("lid", "bottom")}
        />
        <div
          id="computer-frame-lid-left"
          className={styles.computerFrameLidLeftStyle}
          aria-hidden="true"
          inert
          {...face("lid", "left")}
        />
        <div
          id="computer-frame-lid-right"
          className={styles.computerFrameLidRightStyle}
          aria-hidden="true"
          inert
          {...face("lid", "right")}
        />
        <div
          id="computer-frame-lid-top"
          className={styles.computerFrameLidTopStyle}
          aria-hidden="true"
          inert
          {...face("lid", "top")}
        />
      </div>
      {/* Entire chassis (faces + keyboard) is decorative chrome. */}
      <div
        id="computer-frame-chassis"
        className={styles.computerFrameChassisStyle}
        aria-hidden="true"
        inert
        {...frame("chassis")}
      >
        <div
          id="computer-frame-chassis-front"
          className={styles.computerFrameChassisFrontStyle}
          {...face("chassis", "front", "chassis · front")}
        >
          <ComputerKeyboard />
          <div className={styles.computerTrackpadStyle} />
        </div>
        <div
          id="computer-frame-chassis-back"
          className={styles.computerFrameChassisBackStyle}
          {...face("chassis", "back", "chassis · back")}
        />
        <div
          id="computer-frame-chassis-bottom"
          className={styles.computerFrameChassisBottomStyle}
          {...face("chassis", "bottom")}
        />
        <div
          id="computer-frame-chassis-left"
          className={styles.computerFrameChassisLeftStyle}
          {...face("chassis", "left")}
        />
        <div
          id="computer-frame-chassis-right"
          className={styles.computerFrameChassisRightStyle}
          {...face("chassis", "right")}
        />
        <div
          id="computer-frame-chassis-top"
          className={styles.computerFrameChassisTopStyle}
          {...face("chassis", "top")}
        />
      </div>
    </div>
  );
}
computer.component.tsx · 151 lines · 4.7K · tsx_Source_