CSS Elevator: Pure CSS State Machine for Floor Navigation

As a developer passionate about state machines, I’ve been inspired by articles like “A Complete State Machine Made with HTML Checkboxes and CSS.” The potential of pure CSS-driven state machines intrigued me, leading me to wonder: could I create something simpler and more interactive without macros? This curiosity sparked a project where I built an elevator simulation in CSS, featuring direction indicators, animated transitions, counters, and accessibility features.

In this article, I’ll guide you through how I utilized modern CSS features — like custom properties, counters, the :has() pseudo-class, and @property — to construct a fully functional, interactive elevator that knows its location, destination, and travel time. No JavaScript needed.

Defining the State with CSS Variables

The core of this elevator system is CSS custom properties to track its state. Below, I define several @property rules to enable transitions and typed values:

“`css
@property –current-floor {
syntax: ““;
initial-value: 1;
inherits: true;
}

@property –previous {
syntax: ““;
initial-value: 1;
inherits: true;
}

@property –relative-speed {
syntax: ““;
initial-value: 4;
inherits: true;
}

@property –direction {
syntax: ““;
initial-value: 0;
inherits: true;
}
“`

These variables allow me to compare the elevator’s current floor to its previous one, calculate movement speed, and drive animations and transitions accordingly.

A regular CSS custom property (–current-floor) is great for passing values around, but the browser treats everything like a string: it doesn’t know if 5 is a number, a color, or the name of your cat. And if it doesn’t know, it can’t animate it.

That’s where @property comes in. By “registering” the variable, I can tell the browser exactly what it is (, , etc.), give it a starting value, and let it handle the smooth in-between frames. Without it, my elevator would just snap from floor to floor, and that’s not the ride experience I was going for.

A Simple UI: Radio Buttons for Floors

Radio buttons provide the state triggers. Each floor corresponds to a radio input, and I use :has() to detect which one is selected:




“`css
.elevator-system:has(#floor1:checked) {
–current-floor: 1;
–previous: var(–current-floor);
}

.elevator-system:has(#floor2:checked) {
–current-floor: 2;
–previous: var(–current-floor);
}
“`

This combination lets the elevator system become a state machine, where selecting a radio button triggers transitions and calculations.

Motion via Dynamic Variables

To simulate elevator movement, I use transform: translateY(…) and calculate it with the –current-floor value:

“`css
.elevator {
transform: translateY(calc((1 – var(–current-floor)) * var(–floor-height)));
transition: transform calc(var(–relative-speed) * 1s);
}
“`

The travel duration is proportional to how many floors the elevator must traverse:

“`css
–abs: calc(abs(var(–current-floor) – var(–previous)));
–relative-speed: calc(1 + var(–abs));
“`

Let’s break that down:

– –abs gives the absolute number of floors to move.
– –relative-speed makes the animation slower when moving across more floors.

So, if the elevator jumps from floor 1 to 4, the animation lasts longer than it does going from floor 2 to 3. All of this is derived using just math expressions in the CSS calc() function.

Determining Direction and Arrow Behavior

The elevator’s arrow points up or down based on the change in floor:

“`css
–direction: clamp(-1, calc(var(–current-floor) – var(–previous)), 1);

.arrow {
scale: calc(var(–direction) * 2);
opacity: abs(var(–direction));
transition: all 0.15s ease-in-out;
}
“`

Here’s what’s happening:

– The clamp() function limits the result between -1 and 1.
– 1 means upward movement, -1 is downward, and 0 means stationary.
– This result is used to scale the arrow, flipping it and adjusting its opacity accordingly.

It’s a lightweight way to convey directional logic using math and visual cues with no scripting.

Simulating Memory with Delay

CSS doesn


Discover more from WIREDGORILLA

Subscribe to get the latest posts sent to your email.

Similar Posts