CSS Lift: A Pure CSS status machine with floor navigation | CSS tricks

CSS Lift: A Pure CSS status machine with floor navigation | CSS tricks

5 minutes, 3 seconds Read

As a developer with a passion for state machines, I have often inspired myself by articles such as “a complete state machine made with HTML selection boxes and CSS.” The power of pure CSS-driven state machines intrigued me and I started to wonder: could I make a little easier, more interactive and without using macros? This led to a project where I built a lift simulation in CSS, complete with direction indicators, animated transitions, counters and even accessibility functions.

In this article I will go through you how I used modern CSS functions – such as adapted properties, counters, the :has() pseudo-class, and @property – To build a fully functional, interactive lift that knows where it is, where it goes and how long it takes to get there. No JavaScript required.

Define the status with CSS variables

The backbone of this lift system is the use of CSS Custom properties to follow the status. Below I define different @property Rules for allowing transitions and typed values:

@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;
}

With these variables I can compare the current floor of the lift with its previous, calculate the speeding speed and drive animations and transitions accordingly.

A regular CSS adapted feature (--current-floor) Is great to give 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.

That’s true @property comes in. By “registering” the variable, I can tell the browser exactly what it is (etc.), give it a starting value and have it processed the smooth intermediate frames. Without that my lift would just click from floor to floor, and that is not the ride experience I went for.

A simple user interface: Radio buttons for floors

Radio buttons offer stattriggers. Each floor corresponds to a radio entrance and I use :has() To detect which has been selected:




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

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

With this combination, the lift system can become a status machine, whereby selecting a radio button activates transitions and calculations.

Motion via dynamic variables

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

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

The travel time is proportional to how many floors the lift must cross:

--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 they move over more floors.

So if the lift jumps from floor 1 to 4, the animation takes longer than from floor 2 to 3. All this is derived with only mathematical expressions in the CSS calc() function.

Determine direction and arrow behavior

The arrow of the lift points up or down based on the change in floor:

--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;
}

This is what happens:

  • The clamp() function limits the result between -1 And 1.
  • 1 means upward movement, -1 is down, and 0 means stationary.
  • This result is used to scale the arrow, turn around and adjust its opacity accordingly.

It is a lightweight way to convey management logic with the help of mathematics and visual instructions without scripting.

Simulating memory with --delay

CSS does not save previous status native. I simulate this by postponing updates from the --previous property:

.elevator-system {
  transition: --previous calc(var(--delay) * 1s);
  --delay: 1;
}

While the delay is, the --previous value lags behind the --current-floor. That lets me calculate the direction and speed during the animation. As soon as the delay ends, --previous catches up. With this delay -based memory trick, CSS enables to approach the status transitions that are normally done with Javascript.

Floor counters and Unicode -Styling

Showing floor numbers was elegant a pleasure thanks to CSS counters:

#floor-display:before {
  counter-reset: display var(--current-floor);
  content: counter(display, top-display);
}

I have defined a modified counter -style with the help of Unicode -Cirkelde numbers:

@counter-style top-display {
  system: cyclic;
  symbols: "\278A" "\2781" "\2782" "\2783";
  suffix: "";
}

The \278A Unpleasant \2783 Signs correspond to the ➊, ➋, ➌, ➃ Symbols and give a unique, visual charm to the display. The lift not only says “3”, but shows it with typographical flair. This approach is useful if you want to go further than rough figures and symbolic or visual meaning with the help of nothing but CSS.

Accessibility with aria-live

Accessibility is important. Although CSS cannot change the Dom text, the ScreenReader-visible content can still update ::before And counter().

#floor-announcer::before {
  counter-reset: floor var(--current-floor);
  content: "Now on floor " counter(floor);
}

Add one .sr-only Class to visually hide it, but to expose it to supporting technology:

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
}

This keeps the experience included and tailored to accessibility standards.

Practical applications of these techniques

This lift is more than toys. It is a blueprint. Consider these real applications:

  • Interactive prototypes without JavaScript
  • Progress indicators in forms with the help of live condition
  • Game onion with inventory or status mechanics
  • Logic puzzles or educational tools (CSS tracking from CSS!)
  • Reduced JavaScript -depends on performance or sandbox environments

These techniques are especially useful in static apps or limited scripting environments (eg e -mails, certain content management system widgets).

Last thoughts

What started when a small experiment turned into a functional CSS status machine that animates, gives direction and announces changes, completely without JavaScript. Modern CSs can do more than we often give it credit. Of :has()" @propertyCounters and a little smart math, you can build systems that are reactive, beautiful and even accessible.

If you try this technique, I would like to see your opinion. And if you remix the lift (maybe add more floors or challenges?) Send it my way!

#CSS #Lift #Pure #CSS #status #machine #floor #navigation #CSS #tricks

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *