Pangram verdict · v3.3
We believe that this document is fully human-written
AI likelihood · overall
HumanArticle text · 1,557 words · 5 segments analyzed
Jun 4, 2026 An ersatz CSS tutorial for people who need to style a web page, but aren’t web developers. I am a wrong person to write this kind of thing, as I have neither the time, nor experience. I’d much rather read a book about this. Alas, I had to learn all this stuff from trawling MDN, so perhaps it is valuable to document what I have so far. CSS, HTML and Web APIs are truly vast, and it takes a career to become a professional. The good news is that modern web has a reasonably-sized, learnable subset which is enough for simple tasks like a programming blog or a simple GUI. I haven’t seen a resource that teaches just this subset, but it’s not too hard to figure this out. The bad news is that there’s also a nasty set of gotchas, which will mess up your page, which you won’t suspect to exist, and which will need days of debugging to figure out. Still, it’s not that bad. I am quite happy with the styling on this site, and it’s only about 200 of readable CSS. Good: HTML5 semantic tag names It’s worth looking through MDN Elements Reference. There aren’t that many elements, and things like main, article, nav, kbd make it much easier to structure your page. Less obvious: ul for any kind of list, like site’s sections in header > nav. details for table-of-contents (check the source of MDN). dl/dt for list of pairs. Bad: Wrappers If you “View Source” on any “real” website, you’ll notice that everything has layers and layers of wrapper elements, so you might be tricked into thinking that wrappers are how you solve layout problems.
I can’t really agree or disagree here, as I never wrote “production” CSS, but, in my experience, it’s much easier to understand if you do the opposite — restrict yourself to using only markup-meaningful semantic tags, and then figure out CSS which works with the markup you have. Bad: Layout This one is not an exclusively Web problem, layout is a struggle in every GUI framework I know. Imagine a fixed sized raster image, and a paragraph of text describing it. There are many ways to arrange these two elements on the screen’s rectangle. Generally, for every given width and height, you can do a decent job, as long as the total area is enough. A typical GUI is a hierarchy of such boxes, with a lot of “layout freedom”. The problem though is that layout of each box affects the layouts of all other boxes, as you generally want all boxes to meet exactly, without gaps and overlaps. An important negative realization is that the layout algorithm doesn’t exist. There isn’t a fully general solution to positioning and sizing GUI boxes. Rather, different systems use different sets of heuristics to do the job, from simple RectCut, to fully general constraint solvers, with everything in between. It is hard to get the mental model of how layout works, in general. So, don’t think “how can I do my layout in a given system”, think instead “what possible layouts are allowed by the system”. Bad: Browser defaults Let’s start with a bare (but still semantic) HTML markup of a blog article, without any CSS. If you open it in a browser, it will show something. The content isn’t unstyled — the text is of a certain color, font and size. Headers are bigger than the main text, links are underlined, etc. These are the default styles of your browser. They are helpful! The problem is that these styles differ between the browsers.
So, even when you add your own CSS, and the end result looks fine in your browser, I might see something different, because you might rely on a browser default, without knowing it. The last bit is the killer here — the problem is in something you didn’t write. The general solution here is a CSS reset, or normalization — starting your CSS with an explicit set of rules, overriding defaults. Not because defaults are inherently bad, because they are inconsistent. I don’t know which set of rules you need to override in practice, it’s a good idea to compare several existing CSS resets. This touches on the big question: should you style your web page? There are two competing views of the Web platform — some people treat it as a flexible, adaptive, primarily visual medium for expressing design, others would prefer if the Web focused on delivering the content, allowing each user to customize the presentation. My personal answer here is pragmatic — by default, an unstyled page is poorly usable and looks bad. I would have preferred the world where CSS-less pages were readable as is, but, in this world, I think it is helpful to style the content. At the same time, it’s a good idea to allow advanced users to bring their own CSS. Make sure that your HTML markup is reasonable, that you don’t overfit your HTML to CSS (vice-versa is fine), and that your page functions in reader mode. Good: Classless CSS You can’t reset styles to true neutral nothing: if you make the text invisible (white or transparent), it is still a style. So you might as well embrace it: after reset, style common HTML elements directly. For example, to set your favorite font for all code snippets:
code { font-family: "JetBrains Mono", monospace; } If you use main, header, footer, nav tags you can set the overall page layout without writing any CSS selectors.
This of course requires making assumptions, in CSS, about the structure of your HTML, but, like, this is your HTML and your CSS, you can do whatever, and, if you don’t like the result, you can always change it! Bad: CSS selectors In programming, we collectively came around to distrust inheritance and prefer composition. Default CSS is like supercharged inheritance, each design element on your web page is affected by multiple rules, and you can always “monkey patch” existing elements by appending to your CSS. There’s an unfortunate gap between CSS affordances, and what you actually want to do. The two reasonable approaches are: Conclude that CSS selectors add abstraction capability along the wrong axis, and stick to classless CSS and inline styles, using something like Tailwind to make writing inlines prettier, and something like JSX (or any other templating engine supporting composition) to avoid repetition in HTML. Use CSS nesting to avoid writing “far reaching” selectors and style component-per-component:
header { /* Site Header */ margin-bottom: 2rem; & nav { /* Styles, specific to nav in the Header. */ } } Bad: box-sizing UIs are recursive rectangles, layout is the process of figuring out where each rectangles goes, and it is determined by the sizes of rectangles themselves. So, understanding what is the size is quite fundamental. Sadly, by default the definition of size in HTML is very unintuitive: element’s width and height do not include element’s border and padding, which leads to surprising results: everything looks perfect at first, but increasing padding somewhere shifts the entire layout unexpectedly. For this reason, * { box-sizing: border-box; } deserves to be the first line in your CSS reset. It makes elements encapsulated, such that adding borders is a local-only change. Chaotic Good: margin collapsing Suppose you want to have a 8px gap around an element. You would think that you need to set the padding property.
But that would be wrong — if you have two such elements next to each other, the gap between them would be 16px. The paddings would add, creating a visual gap larger than intended. You want something more akin to social distancing, where if one person is more introverted, this person’s bigger radius of exclusion is what defines the distance. And that’s how the margin property works. Two neighboring margins are combined using max rather than sum. Margin collapsing is very useful, but it can surprise you. E.g. I think child margin can stick beyond parent’s? To be honest, I don’t have a good intuitive understanding of margins, but I know enough to at least identify when it is the problem. Margins are also one of the indirect inspirations for this post. In Moving away from Tailwind, and learning to structure my CSS Julia Evans writes that you generally don’t want to set margin on an element, and should rather let the parent control the inter-element margin of the children, using the so-called owl selector:
section > *+* { margin-top: 1rem; } That is, add margin to all section’s children exempting the first one. I didn’t know that! And, given all the pain that margin gave me so far, I actually get why you want to do this, and why this is a good idea. But it bugs me that you can’t learn that without becoming “professional” web developer, or reverse-engineering someone else’s CSS framework. Bad: Default (flow) layout Layout in general is tricky, because there’s no universal “layout algorithm”, just a bunch of special cases. But what does HTML actually do? The default layout algorithm I think goes back to the origin of HTML as a language for documents, and overfits a use-case of producing papers — mostly text content with some illustrations, where the text can flow around the pictures.