DEV Community 👩‍💻👨‍💻

Cover image for Using Only CSS to Recreate Windows 98
Johnny Simpson
Johnny Simpson

Posted on • Originally published at fjolt.com

Using Only CSS to Recreate Windows 98

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.

Here is the demo - and a quick note - this is a desktop Windows recreation, so it is of course optimised for desktop viewing. Ironically, though, it probably won't work on Windows 98, since you would have to use a very old version of Internet Explorer to view it.

The link to the demo can be found here, since it is better viewed in full screen mode. In this demo the things which I thought were cool included:

  • Minesweeper with just CSS - although no score keeping.
  • Login and logout with memory of who is logged in using the brand new CSS parent selector.
  • Animated update process.
  • Minimising, maximising and closing windows.

It is however worth noting that the things that are hard or just down right impossible to do with only CSS, such as:

  • Drag and drop - not possible at all with CSS.
  • Multiple conditions - for example, hard to say that a window should be both maximised and put on top at the same time.
  • Click anywhere to close - you have to click the start button to close and open it. You can't just click anywhere else to close it.
  • Multiple conditions in general - CSS doesn't really have an AND operator.

How to build Windows 98 in CSS

So, the first thing I wanted to get right about this version of Windows 98 was the look and feel. I'm using some pretty cool Windows 98 icons (which I do think should make a comeback), as well as the standard Windows 98 colour scheme. To get the indented and out-dented feel I used quite a complicated box shadow, as you can see here:

.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;
}
Enter fullscreen mode Exit fullscreen mode

Everything else was relatively straightforward look and feel wise. The key to making all of this work is checkboxes and radio buttons.

Using Checkboxes and Radio buttons as a store of information in CSS

Checkboxes and radios are the only way to store information in CSS. We can then use them to implement style changes. Checkboxes, when checked, allow us to enable or disable a single feature (like show a window, maximise a window, or click a minesweeper square). 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 -- */
}
Enter fullscreen mode Exit fullscreen mode

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 labels to model the different features of Windows 98. For example:

<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>
Enter fullscreen mode Exit fullscreen mode

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.

Using the parent selector to track who is logged in

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;
}
Enter fullscreen mode Exit fullscreen mode

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.

There is no CSS AND Selector

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 {

}
Enter fullscreen mode Exit fullscreen mode

This works fine, but what if we want to check if two minesweeper boxes next to each other are checked, before applying CSS? I thought, logically, that the selector would only continue if both were checked - so tried this:

#minesweeper-box-1-1:checked + #minesweeper-box-2-1:checked ~ .content > .minesweeper-box-1-1 {

}
Enter fullscreen mode Exit fullscreen mode

But unfortunately, that doesn't work. So while we have a way to track state 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.

Achieving Windows 98 Text

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;
}
Enter fullscreen mode Exit fullscreen mode

Recreating Minesweeper

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 ~56 Minesweeper inputs. Tracking that all in CSS required a lot of CSS, but the overall result is pretty cool looking.

Overall, this follows the same logic as the previously mentioned checkbox and radio trick - so conceptually it's not any more complicated than anything else we've done.

Minesweeper with just CSS

Conclusion

I hope you've enjoyed this guide. Doing this reminded me of how web development used to be, when things were a lot harder to accomplish and required a lot of manual creation of DOM elements. It's fun to see what can be achieved in CSS all these years on (including the parent selector). Is this a realistic way to create web applications? Not really in terms of speed, and not yet in terms of functionality, but CSS did a lot more than I thought it would be able to, and I'm pretty happy with the results.

If you enjoyed this, please consider following me on twitter.

Top comments (9)

Collapse
 
kaspera profile image
Kasper Andreassen

Image description

Only thing lacking, and properly not possible with CSS-only, is the progress bar in Windows update switching between "2 minutes left" and "138.193 years left" 😂

Great job!

Collapse
 
smpnjn profile image
Johnny Simpson Author

I actually thought about implementing that, not gonna lie haha. It is possible, maybe will require a CSS Windows 98 SP1 for that new feature 🤠

Collapse
 
mooktar profile image
mooktar

Great job 👍

Collapse
 
septiandjamari profile image
septian djamari

wow...it's so beautifull😍😍😍

Collapse
 
igorbifano profile image
Igor Bifano

Great Job!

Collapse
 
mmiglioranza22 profile image
Manuel Agustín Miglioranza

Beautiful!

Collapse
 
wiseai profile image
Mahmoud Harmouch

Thanks for sharing! It reminds me of BTC2(break the code 2). I was only able to reach the G4 drive(DotGang), didn't solve it. It was a good time.

Collapse
 
ackvf profile image
Vítězslav Ackermann Ferko

Are you mad 🤣👏👏👏

Collapse
 
arunran30008245 profile image
arun rana

Nice css skills; can you do a gui client for github gists?

🌚 Life is too short to browse without dark mode