visit
As part of my continuous work to see how much I can do with just CSS (see other work such as the CSS-only Minecraft Chicken), I decided to try and recreate Windows 98 using nothing else apart from CSS and HTML. Did anyone ask for this? Not really? Is it fun to try and see what you can accomplish with just CSS? Yes, sort of. Was it time-consuming? Unfortunately yes.
Minesweeper with just CSS - although no scorekeeping.
Login and logout with the memory of who is logged in using the brand new CSS parent selector.
Animated update process.
Minimizing, maximizing, and closing windows.
.windows-box-shadow, .minesweeper .content > label {
box-shadow: -2px -2px #e0dede, -2px 0 #e0dede, 0 -2px #e0dede, -4px -4px white, -4px 0 white, 0 -4px white, 2px 2px #818181, 0 2px #818181, 2px 0 #818181, 2px -2px #e0dede, -2px 2px #818181, -4px 2px white, -4px 4px black, 4px 4px black, 4px 0 black, 0 4px black, 2px -4px white, 4px -4px black;
}
.inverse-windows-box-shadow, .minesweeper .content > label:active {
box-shadow: -2px -2px #818181, -2px 0 #818181, 0 -2px #818181, -4px -4px black, -4px 0 black, 0 -4px black, 2px 2px #e0dede, 0 2px #e0dede, 2px 0 #e0dede, 2px -2px #818181, -2px 2px #e0dede, -4px 2px black, -4px 4px white, 4px 4px white, 4px 0 white, 0 4px white, 2px -4px black, 4px -4px white;
}
For things where there is only one option allowed to be active at a time (for example, which window should be on top) - we can use radio buttons. Both follow the same syntax in CSS, where we use the :checked
selector:
#windows-11:checked ~ .windows-11 .text {
/* -- CSS here -- */
}
Here, when the input #windows-11
is checked, it will affect its sibling's child .text
- so we can apply some custom CSS. Importantly, since we can't easily style an HTML input, we use label
s to model the different features of Windows 98.
<form id="windows">
<!-- Login and Shutdown -->
<input type="checkbox" id="login-screen-input" name="login-screen-input" />
<!-- Later on.. -->
<label for="login-screen-input">Log Off</label>
</form>
Here, the label
shown is associated with the checkbox #login-screen-input
. That means when you click the label, it will check the checkbox. This basically gives us free reign to track a user's clicks, and then use the checkbox :checked
status to show certain windows, in certain forms. The difficulty is you can only have one label associated with one input.
That means in a scenario where a button is supposed to open the window and place it on top of all other windows, you'd have to use Javascript since this will require tracking two states - the z-index
of the window, and whether it is open or closed. This is a major blocker in implementing CSS-only versions of complex UIs.
Since we have a login screen in a div
for when a user logs out, we can't use sibling selectors to easily track who is logged in. We can still use :checked
statuses to track this, but the inputs are too deep in our DOM to affect their parent's sibling's CSS. Fortunately, we can use the new CSS parent selector for just this task:
#login-screen:has(#login-window .select-box #zark-muckerberg:checked) ~ #start-bar .zark-muckerberg,
#login-screen:has(#login-window .select-box #donald-trump:checked) ~ #start-bar .donald-trump,
#login-screen:has(#login-window .select-box #spiderman:checked) ~ #start-bar .spiderman {
display: inline;
padding-left: 0.5rem;
}
Here, if #login-screen
has a :checked
div, we can use it to display the user name in the start bar, despite these checkboxes being deep within the DOM. This is pretty neat, and a useful way to use parent selectors if you ever wish to accomplish recreating a CSS-only version of a Windows operating system.
Much to my dismay, there was no way for me to create a CSS AND
selector using chained checked boxes. For example, consider this situation where we apply some CSS based on a :checked
state:
#minesweeper-box-1-1:checked ~ .content > .minesweeper-box-1-1 {
}
#minesweeper-box-1-1:checked + #minesweeper-box-2-1:checked ~ .content > .minesweeper-box-1-1 {
}
But unfortunately, that doesn't work. So while we have a way to track states in CSS, it's quite hard to track multi-conditioned checkbox states to create logical statements and styles based on that. That's disappointing, but it doesn't limit us too much for our Windows 98 implementation.
Windows 98 text is not anti-aliased. To remove anti-aliasing (at least for some browsers), and achieve that classic, crisp, Windows 98 finish, I used the following CSS:
body {
-webkit-font-smoothing: none;
-moz-osx-font-smoothing: grayscale;
}
So one of the major undertakings in this project was recreating Minesweeper. I kept the grid relatively small (to maintain my sanity) - but I had to make my own Minesweeper map created out of labels. Each of these labels mapped to an input, which tracked if a cell had been clicked or not. If a mine is clicked, it's game over and you can't interact with the board anymore. As there were ~56 Minesweeper cells, we needed an equivalent of ~56 Minesweeper inputs. Tracking that all in CSS required a lot of CSS, but the overall result is pretty cool looking.