Out of Style: !important Edition
A beginner-friendly browser-based puzzle platformer where the level is real CSS. Freeze the world, open the inspector and rewrite the rules to reach the exit.
Context
Every platformer teaches you a movement vocabulary and then asks you to master it. I wanted to build one that taught a different vocabulary, the one I actually use at work. If a platform is a div and its width is a CSS declaration, then the honest version of “make the gap crossable” is not a power-up. It is editing the declaration.
It is deliberately a beginner-friendly puzzle game rather than a hard one. There is no way to lose, no timer on the main levels and no penalty for a wrong value. Falling off simply puts you back at the start of the level, and an edit that does not work can be reverted instantly. The difficulty comes from noticing which property matters, not from execution or punishment, so the game stays approachable even if you have never written a line of CSS.
That idea only works if the game refuses to cheat. The browser had to be the engine.
Problem
The obvious way to build this is to fake it. Draw the level on a canvas, keep a JavaScript model of every object, and show the player a decorative code panel that maps typed values onto that model. It looks identical in a screenshot and it is far easier to control.
It is also a lie, and the lie leaks. The moment a player inspects the page with real DevTools and finds that the “CSS” on screen has nothing to do with the elements underneath, the entire premise collapses. The game stops being about CSS and becomes a game about guessing what the developer accepted as input.
The harder constraint was making a real DOM behave like a platformer. Real CSS animations and transitions run on the browser’s clock, while the physics loop runs on its own accumulated time. Any moment those two disagree, the player sees one thing and collides with another.
Solution
I built the game as a standalone TypeScript and Vite app with no runtime framework and no rendering engine. Every solid, ladder, laser and exit is a real DOM element carrying real CSS. The editor panel writes real declarations to those elements, and the physics measures the resulting boxes with getBoundingClientRect. Nothing in the panel is decoration.
The freeze is the core mechanic. Pressing I freezes the world. Physics pauses, keyframe animations pause, and any in-flight CSS transition is pinned in place by reading its computed value, writing it inline and removing the transition. You edit inside that frozen moment, watch the preview update live, then close the panel to execute. The world resumes from exactly where it stopped, because nothing was ever faked to get there.
The panel is a DevTools pastiche that tells the truth. Early levels give you a labelled slider. Later levels hand you an empty declaration line, a property dropdown and a text input, so choosing which property to change becomes the puzzle. Locked declarations are shown greyed with a padlock and are genuinely unwritable. When several objects share a class, the panel switches to per-instance #id selectors, because four different .laser {} rules with four different values would be dishonest CSS.
Each level introduces one idea and then complicates it. Widening a plank across a gap, extending a ladder, an invisible bridge that only becomes solid above an opacity threshold, an elevator whose edits are deferred until the panel closes, and a rotating barricade anchored so its pivot stays put while you resize it.
A hidden sixth level, Mission: CSSible, turns the vocabulary into a heist and is the one place the game gets demanding. Four lasers pulse on independent, randomly-phased cycles down a tight corridor. Two crates slide to block any of them, random power dips knock one laser out entirely for two seconds, and a terminal at the far end must be hacked by holding position against it. The hack sets --mainframe: running, which is exactly what the lift’s locked rule reads: animation-play-state: var(--mainframe, paused). The whole run is on a sixty second clock that pauses while the panel is open, so thinking is free and moving is not.
Controls
- Arrow keys / WASD - move, jump and climb
I- open the inspector and freeze the worldEsc- revert the current editR- restart the level
Outcome
The result is a game where the solution and the explanation are the same sentence. You do not learn that a wider plank crosses the gap. You learn that width: 240px crosses the gap, and you learn it by typing it.
Building it honestly also meant the bugs were honest. The most instructive one: a CSS animation restarts from scratch whenever its animation-name returns, anchoring its phase to that moment while the JavaScript danger clock still anchors to level start. A laser that looked safe would trip the alarm. The fix was not to special-case the collision, but to re-anchor the animation’s negative delay to the simulation’s current phase, so the pixels and the maths agree again.
The game runs entirely client side with no backend, keeps progress in localStorage, and ships as a static bundle under 40 kB of JavaScript.
Future goals
The current build is a complete short campaign, but the architecture was always pointed somewhere larger. The levels are hand-written TypeScript today, which is fine for six of them and clearly the wrong shape for sixty.
The next step is a proper level editor. Moving level definitions into a serialisable format lets the same runtime that plays the campaign also play anything the editor produces, which turns level design into a drag, drop and configure task instead of a code change. From there the interesting part is choosing which properties each object exposes for editing and which stay locked, because that choice is what makes a level a puzzle rather than a walk.
Once levels are data rather than code, sharing them becomes mostly a question of encoding. A community layer where players publish their own maps, browse what others have built and race each other for the fastest clean solve would let the game keep growing without me authoring every puzzle. Longer term I would like to add a proper soundtrack and sound effects, an accessibility pass, and a touch-friendly control scheme so the game is not restricted to desktop.
Play it here: Out of Style: !important Edition - desktop and a keyboard required.