General2026-07-21

CSS Selectors I Wish I Learned Sooner

I spent years writing bloated CSS with too many classes before I discovered a handful of selectors that changed everything. Here is what I wish someone had shown me sooner.

My early CSS looked like a fever dream of class names. \x60header-nav-item-active\x60. \x60sidebar-link-wrapper-inner\x60. I was creating a new class for every single styling variation because I simply did not know there was a better way. When I finally discovered proper CSS selectors, I deleted about forty percent of my stylesheets in a single afternoon. The selector that changed my life first was the child combinator — the humble \x60>\x60 symbol. Before I learned about it, if I wanted to style only the direct children of a container, I would either create a specific class or accidentally style everything nested inside. The child combinator let me target only the direct children, leaving grandchildren untouched. It sounds small, but it eliminated dozens of unnecessary classes overnight. Then came \x60:nth-child()\x60. I cannot tell you how many times I had written specific classes like \x60row-1\x60, \x60row-2\x60, \x60row-3\x60 just to alternate background colors on a table. \x60:nth-child(even)\x60 and \x60:nth-child(odd)\x60 would have saved me literal hours. And once you start using formulas like \x60:nth-child(3n+1)\x60, you realize you can target every third item, or the first four, or any pattern you can think of without touching your markup. The \x60:not()\x60 pseudo-class was another revelation. I used to write long chains of selectors to exclude one specific element. Now I just write something like \x60div:not(.excluded)\x60 and move on. It is so much cleaner, and it made me think about my CSS logic differently — more about what I want to exclude than what I want to include. Attribute selectors were the hidden gem I stumbled on last. \x60[type="text"]\x60 lets me target all text inputs without a class. \x60[href^="https"]\x60 targets only secure links. \x60[href$=".pdf"]\x60 targets download links. These are incredibly powerful for form styling and link styling, and they require zero extra markup. Adjacent sibling combinator — the \x60+\x60 sign — became my best friend for spacing. Need a margin between every list item except the first one? \x60li + li { margin-top: 10px; }\x60 does it in one line. No first-child overrides needed. No special class on the first element. The \x60:empty\x60 selector sounds niche until you realize how often frameworks leave empty divs in your markup. \x60:empty { display: none; }\x60 cleaned up my layouts instantly. What I realized going through all of this was that I had been treating CSS as a system of explicit labeling when it is really a system of pattern matching. The selectors are the language for describing the patterns. Once I learned to describe what I wanted in terms of relationships and positions instead of names, everything clicked. If you are still writing class names like \x60item-1\x60, \x60item-2\x60, \x60item-3\x60, stop. Open a reference on CSS selectors and spend an hour learning the combinators and pseudo-classes. It will be the most productive hour of your week.