Let me start by saying that Cascading Style Sheets - CSS - is not a hard language to write. The problem is not the languageMostly. If you’re doing something complex, such as working with animations, the syntax can get a bit weird, but with how humans don’t think about style and art in a linear way and dealing with the complexities of different devices.

How we think about style #

Look around this website, there are things that are stylistically cohesive - the color theme, the way I indicate that something is a local link by putting it in a button with a color that matches where it goes. That makes sense to the part of our brains that wants to see the full overview and look at the completed picture. We like to think of art from the macroscopic scale.

In the vast majority of contexts, this line of thought works. Think about digital art - you put pixels on layers and stack those layers. The one thing you have to worry about is color on each pixel. Sure, if you’re stacking layers with weirder blend modes it can start to get more complicated but it’s almost always easy to think about what the change you’re making will look like before you make it. There is a convenient 1:1 relationship between you and the content on the screen.

We do not want to have to think about styling every. single. element. on our web page. You would not want to have to set the background color, font sizes, line height, etc. etc. each time you made a new element. You need to be able to set defaults for each type of thing on the page.

At the same time, it’s necessary to think about individual elements sometimes and give special attention to one thing on a page. For example on this website I have tables where things need color applied to make them easier to read.

For example, over on the “The Cloud” page I have this table:

Traditional: Infrastructure as a Service Platform as a Service Software as a Service
Applications Applications Applications Applications
Data Data Data Data
Runtime Runtime Runtime Runtime
Middleware Middleware Middleware Middleware
Virtualization Virtualization Virtualization Virtualization
Servers Servers Servers Servers
Storage Storage Storage Storage
Networking Networking Networking Networking

I wouldn’t want EVERY table to have that stair-cased highlight to it. It makes sense where used, but that alteration extends the style applied to all tables on the website. It doesn’t replace it.

This is the first problem: We have to think about microscopic changes as how they extend the behavior of the macroscopic. In most digital art, we can just zoom in, change a some pixels, and know that the sum of these small changes will result in a better picture as a whole - yes, even in normal art you’d have to keep the entire composition in mind - but In CSS, you need to think in both directions and be sure you understand what you want to applied on a macroscopic scale and what you want to be microscopic, and decide what you want to be “default”.

For example, lets say you have a bunch of images on your website,

and you decide they’d all look better with rounded corners, so you make a CSS rule to apply rounded corners to ALL images on the site:

	border-radius: 1rem;

That looks good, does what we want, great, right…

… Oops, we broke the logo.

Unintended consequences are the bane of your existence when working on CSS because you want to (and should) apply style to the most generic class that it applies to, but it’s easy to go one step too far and break other things. There are two ways to address this problem:

From the microscopic side, we can assign a class to the logo image:

<img class="logo" src="mylogo.svg">

and it should specify, in it’s style, that it should have no border radius:

.logo {
	border-radius: 0rem;

This is probably the wrong way to do this. Unless you know that the logo is going to be the one exception to this rounded corner rule, you’re going to be playing Whac-A-Mole to un-round corners on images from here on out if you do this.

The better solution, in most cases, is just to make a new class for rounded corners:

.rounded {
	border-radius: 1rem;

and then when you insert the images you set them as a member of the rounded class:

<img class="rounded" src="meme.jpeg">

Now, if we did decide to round the logo we could add the rounded class on, (Yes, you can use more than one class):

<img class="logo rounded" src="mylogo.svg">

but if we just left the logo alone and didn’t specify a class at all, it should be fine, because the defaults we apply to all img elements shouldn’t break it.

If you have something like our situation here, where you have two things which derive from the same class - here, “rounded images” and “logo images” are both of type img, you should never make a change to the parent of both (img in this case) for one child if it will require you undo that change in another - just make the change where it belongs in the first place!

The tough part about that is realizing when you need to make a new class and what those classes are. Above I made a rounded class, but that may not be sufficient. Maybe you need variants for setting the amounts of rounding, ranging from subtle to full-on-circle. What would you call those classes? You also don’t want to go to nuts with different classes, otherwise each element would have a whole pile of classes assigned to it. There is art in organization and picking the right amount of abstraction here. That’s an art that’s much harder to teach.

What can CSS do? #

.stage { height: 100px; width: 100%; margin: auto; position: relative; top: 0; right: 0; bottom: 0; left: 0; perspective: 9999px; transform-style: preserve-3d; } .animlayer { width: 100%; height: 100%; position: absolute; transform-style: preserve-3d; animation: effect 5s infinite alternate ease-in-out -7.5s; animation-fill-mode: forwards; transform: rotateY(40deg) rotateX(33deg) translateZ(0); } .animlayer:after { font: 50px/0.65 "Roboto", "Trebuchet MS", sans-serif; content: "some pretty crazy shit"; text-align: center; height: 100%; width: 100%; position: absolute; } .animlayer:nth-child(1):after { transform: translateZ(0px); } .animlayer:nth-child(2):after { transform: translateZ(-1.5px); } .animlayer:first-child:after { color: #fff; text-shadow: none; } @keyframes effect { 100% { transform: rotateY(-30deg) rotateX(-43deg); } }

That said, you should probably not do to crazy of shit. Not all browsers behave the same or support everything. While nearly everything should work, you will find weird edge cases. For example, this website used to have a nice gradient for the background, but it had awful banding on Firefox and was unusable on the default browser on Samsung phones.

In general though, if you have an idea for a simple animation in mind, you can probably make it work with just CSS… ish. The limitation is if you want to actually change the content, that’s a job better suited to JavaScript, which we’ll look at next. Here, as a teaser:

function getRandomUnicodeLetter() {
  // Generate a random number between 65 (A) and 122 (z)
  const randomNumber = Math.floor(Math.random() * (122 - 65 + 1)) + 65;
  // Convert the number to a Unicode character and return it
  return String.fromCharCode(randomNumber);

function updateUnicodeLetters() {
  // Generate 7 random Unicode letters
  const letters = [];
  for (let i = 0; i < 7; i++) {
  // Update the text content of the element with the ID "unicode-letters"
  document.getElementById('unicode-letters').textContent = letters.join('');

setInterval(updateUnicodeLetters, 70); // Update the letters every 70 milliseconds
<p> hello <span id="unicode-letters" class="conlang"></span>, how are you?</p>

hello , how are you?

Okay, so how do I actually learn it? #

Same way as everything else: RTFM, experiment, and get your hands dirty. You’ve seen enough examples on this page that you’ve probably gotten a grasp for some of the syntax.

To start, you’ll just need a basic HTML page with some elements on it, so make a file like mytest.html and fill it with some content:

<!DOCTYPE html>
<link rel="stylesheet" href="mystyle.css">
<p>this is <b>HTML</b></p>

then, in the same folder, make a mystyle.css file:

h1 {
	color: green;

and open mytest.html with your web browser and start experimenting - just refresh the page each time you change something.

You may want to follow along with some tutorials: https://web.dev/learn/css/ and https://www.w3schools.com/css/ are both decent starting places.

As some homework, I’d recommend trying to get

columns of text to work, as it it’s a good test of your ability to handle padding, grids, etc.
Plus, you should experiment with how having a smaller browser window breaks this.

To work smarter not harder, you may want to use the browser’s development window and try changing things live - nothing you do there will save after a refresh - just press F12, (You may need to press fn + F12 depending on your keyboard) or right click on the page and click “Inspect Element” to look at the HTML and CSS for a specific item on the page. On the Styles tab you should be able to change things and see what they do live. If you want, you can ever try it on this page!


Inline styles #

TODO why you shouldn’t use them, but why you might want to anyway.

Units. #

px vs vh/vw vs rem vs em vs %

Further Reading #





If you have literal hours to kill, start going though everything on https://css-tricks.com/almanac/


If you would like to support my development of OpGuides, please consider supporting me on GitHub Sponsors or dropping me some spare change on Venmo @vegadeftwing - every little bit helps ❤️