惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
雷峰网
雷峰网
Recent Announcements
Recent Announcements
月光博客
月光博客
G
Google Developers Blog
腾讯CDC
S
Secure Thoughts
大猫的无限游戏
大猫的无限游戏
T
Tenable Blog
云风的 BLOG
云风的 BLOG
W
WeLiveSecurity
博客园 - 【当耐特】
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
博客园 - 聂微东
The Cloudflare Blog
阮一峰的网络日志
阮一峰的网络日志
人人都是产品经理
人人都是产品经理
P
Privacy International News Feed
MyScale Blog
MyScale Blog
K
Kaspersky official blog
T
The Blog of Author Tim Ferriss
Attack and Defense Labs
Attack and Defense Labs
Spread Privacy
Spread Privacy
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
aimingoo的专栏
aimingoo的专栏
I
Intezer
Vercel News
Vercel News
小众软件
小众软件
Simon Willison's Weblog
Simon Willison's Weblog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
N
Netflix TechBlog - Medium
P
Proofpoint News Feed
Latest news
Latest news
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
T
Tor Project blog
S
Security Affairs
P
Proofpoint News Feed
博客园 - 三生石上(FineUI控件)
博客园 - Franky
C
Cyber Attacks, Cyber Crime and Cyber Security
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
美团技术团队
Recent Commits to openclaw:main
Recent Commits to openclaw:main
S
Security @ Cisco Blogs
L
LINUX DO - 热门话题
Know Your Adversary
Know Your Adversary
Project Zero
Project Zero
D
Docker
L
Lohrmann on Cybersecurity
F
Full Disclosure

CSS Wizardry

Front-End’s Missing Metric: The TBT Window Meet Your Users Where They Are with Obs.js Better Browser Caching with No-Vary-Search font-family Doesn’t Fall Back the Way You Think What Is CSS Containment and How Can I Use It? When All You Can Do Is All or Nothing, Do Nothing Obs.js: Context-Aware Web Performance for Everyone Low- and Mid-Tier Mobile for the Real World (2025) The Fastest Site in the Tour de France Making Sense of the Performance Extensibility API Why Do We Have a Cache-Control Request Header? HTML Is Not a Programming Language… Build for the Web, Build on the Web, Build with the Web Licensing Code on CSS Wizardry A Layered Approach to Speculation Rules Designing (and Evolving) a New Web Performance Score Core Web Vitals Colours The Ultimate Contract Templates for Tech Consultants: Protect Your Business and Get Paid Optimising for High Latency Environments Cache Grab: How Much Are You Leaving on the Table? blocking=render: Why would you do that?! Correctly Configure (Pre) Connections The Three Cs: 🤝 Concatenate, 🗜️ Compress, 🗳️ Cache What Is the Maximum max-age? How to Clear Cache and Cookies on a Customer’s Device The Ultimate Low-Quality Image Placeholder Technique Core Web Vitals for Search Engine Optimisation: What Do We Need to Know? The HTTP/1-liness of HTTP/2 In Defence of DOM­Content­Loaded Site-Speed Topography Remapped Why Not document.write()? Speeding Up Async Snippets Critical CSS? Not So Fast! Measure What You Impact, Not What You Influence Optimising Largest Contentful Paint Measuring Web Performance in Mobile Safari Site-Speed Topography Speed Up Google Fonts Real-World Effectiveness of Brotli Performance Budgets, Pragmatically Lazy Pre-Browsing with Prefetch Making Cloud.typography Fast(er) Time to First Byte: What It Is and How to Improve It Self-Host Your Static Assets Tips for Technical Interviews Cache-Control for Civilians Bandwidth or Latency: When to Optimise for Which ITCSS × Skillshare What If? CSS and Network Performance The Three Types of Performance Testing Getting to Know a Legacy Codebase Image Inconsistencies: How and When Browsers Download Images Identifying, Auditing, and Discussing Third Parties My Digital Music Setup Measuring the Hard-to-Measure Finding Dead CSS The Fallacies of Distributed Computing (Applied to Front-End Performance) Ten Years Old Relative Requirements Airplanes and Ashtrays Performance and Resilience: Stress-Testing Third Parties Refactoring Tunnels Little Things I Like to Do with Git Writing Tidy Code Configuring Git and Vim Base64 Encoding & Performance, Part 2: Gathering Data Base64 Encoding & Performance, Part 1: What’s Up with Base64? Typography for Developers Moving CSS Wizardry onto HTTPS and HTTP/2 Ack for CSS Developers A New Year, a New Focus Preparing Vim for Apple’s Touch Bar Choosing the Correct Average CSS Shorthand Syntax Considered an Anti-Pattern CSS Wizardry Newsletter Nesting Your BEM? Improving Perceived Performance with Multiple Background Images Continue Normalising Your CSS Pure CSS Content Filter Pragmatic, Practical, and Progressive Theming with Custom Properties Refactoring CSS: The Three I’s Speaker’s Checklist: Before and After Your Talk Improving Your CSS with Parker The Importance of !important: Forcing Immutability in CSS Mixins Better for Performance Managing Typography on Large Apps White October Events Workshop Partnership BEMIT: Taking the BEM Naming Convention a Step Further Travelling Like You Want to, When You Have To Contextual Styling: UI Components, Nesting, and Implementation Detail Subtleties with Self-Chained Classes Cyclomatic Complexity: Logic in CSS Immutable CSS Can CSS Be Too Modular? More Transparent UI Code with Namespaces When to use @extend; when to use a mixin The Specificity Graph CSS Wizardry Ltd.: Year 1 in review CSS Guidelines 2.0.0
Code Smells in CSS Revisited
Harry Roberts · 2017-02-08 · via CSS Wizardry

Written by on CSS Wizardry.

Table of Contents

Independent writing is brought to you via my wonderful Supporters.

  1. @extend
  2. String Concatenation for Classes
  3. Background Shorthand
  4. Key Selector Appearing More Than Once
  5. A Class Appearing in Another Component’s File
    1. BEM Mixes
  6. CSS @import

Way back in 2012, I wrote a post about potential CSS anti-patterns called Code Smells in CSS. Looking back on that piece, I still agree with all of it even four years later, but I do have some new things to add to the list. Again, these aren’t necessarily always bad things, hence referring to them as code smells: they might be perfectly acceptable in your use case, but they still smell kinda funny.

Before we start, then, let’s remind ourselves what a Code Smell actually is. From Wikipedia (emphasis mine):

Code smell, also known as bad smell, in computer programming code, refers to any symptom in the source code of a program that possibly indicates a deeper problem. According to Martin Fowler, ‘a code smell is a surface indication that usually corresponds to a deeper problem in the system’. Another way to look at smells is with respect to principles and quality: ‘smells are certain structures in the code that indicate violation of fundamental design principles and negatively impact design quality’. Code smells are usually not bugs—they are not technically incorrect and do not currently prevent the program from functioning. Instead, they indicate weaknesses in design that may be slowing down development or increasing the risk of bugs or failures in the future. Bad code smells can be an indicator of factors that contribute to technical debt. Robert C. Martin calls a list of code smells a ‘value system’ for software craftsmanship.

So they’re not technically, always wrong, they’re just a good litmus test.

@extend

Hopefully I can keep this first one nice and brief: I have long been vocal about the side effects and pitfalls of @extend, and now I would actively consider it a code smell. It’s not absolutely, always, definitely bad, but it usually is. Treat it with suspicion.

The problems with @extend are manifold, but to summarise:

  • It’s actually worse for performance than mixins are. Gzip favours repetition, so CSS files with greater repetition (i.e. mixins) achieve a greater compression delta.
  • It’s greedy. Sass’ @extend will @extend every instance of a class that it finds, giving us crazy-long selector chains that look like this.
  • It moves things around your codebase. Source order is vital in CSS, so moving selectors around your project should always be avoided.
  • It obscures the paper-trail. @extend hides a lot of complexity in your Sass that you need to unpick more gradually, whereas the multiple class approach puts all of the information front-and-center in your markup.

For further reading:

String Concatenation for Classes

Another Sass-based gripe is the use of the & to concatenate strings in your classes, e.g.:

.foo {
  color: red;

  &-bar {
    font-weight: bold;
  }

}

Which yields:

.foo {
  color: red;
}

.foo-bar {
  font-weight: bold;
}

The obvious benefit of this is its terseness: the fact that we have to write our foo namespace only once is certainly very DRY.

One less obvious downside, however, is the fact that the string foo-bar now no longer exists in my source code. Searching my codebase for foo-bar will return only results in HTML (or compiled CSS if we’ve checked that into our project). It suddenly became a lot more difficult to locate the source of .foo-bar’s styles.

I am much more a fan of writing my CSS longhand: on balance, I am far more likely to look for a class than I am to rename it, so findability wins for me. If I join a project making heavy use of Sass’ string concatenation, I’m usually expecting to have a hard time tracking things down.

Of course you could argue that sourcemaps will help us out, or that if I’m looking for a class of .nav__item then I simply need to open the nav.scss file, but unfortunately that’s not always going to help. For a little more detail, I made a screencast about it.

Background Shorthand

Something else I discussed only recently is the use of background shorthand syntax. For full details, please refer to the relevant article, but the summary here is that using something like:

.btn {
  background: #f43059;
}

…when you probably meant:

.btn {
  background-color: #f43059;
}

…is another practice I would consider a code smell. When I see the former being used, it is seldom what the developer actually intended: nearly every time they really meant the latter. Where the latter only sets or modifies a background colour, the former will also reset/unset background images, positions, attachments, etc.

Seeing this in CSS projects immediately warns me that we could end up having problems with it.

Key Selector Appearing More Than Once

The key selector is the selector that actually gets targeted/styled. It is often, though not always, the selector just before your opening curly brace ({). In the following CSS:

.foo {}

nav li .bar {}

.promo a,
.promo .btn {}

…the key selectors are:

  • .foo,
  • .bar,
  • a, and
  • .btn respectively.

If I were to take a codebase and ack for .btn, I might see some output like this:

.btn {}

.header .btn,
.header .btn:hover {}

.sidebar .btn {}

.modal .btn {}

.page aside .btn {}

nav .btn {}

Aside from the fact that a lot of that is just generally pretty poor CSS, the problem I’m spotting here is that .btn is defined many times. This tells me that:

  1. there is no Single Source of Truth telling me what buttons look like;
  2. there has been a lot of mutation meaning that the class .btn has many different potential outcomes, all via mutable CSS.

As soon as I see CSS like this, I’m aware of the fact that doing any work on buttons will have a large surface area, tracking down exactly where buttons’ styles come from will be a lot more difficult, and that changing anything will likely have huge knock-on effects elsewhere. This is one of the key problems with mutable CSS.

Make use of something like BEM in order to create completely brand new classes that carry those changes, e.g.:

.btn {}

.btn--large {}

.btn--primary {}

.btn--ghost {}

Just one key selector each.

A Class Appearing in Another Component’s File

On a similar but subtly different theme as above, the appearance of classes in other components’ files is indicative of a code smell.

Where the previous code smell deals with the question of there being more than one instance of the same key selector, this code smell deals with where those selectors might live. Take this question from Dave Rupert:

.some-context .thing { /* special rules and overrides */ }
Does that go in thing.css or some-context.css?

— Dave Rupert (@davatron5000) 7 February, 2017

If we need to style something differently because of its context, where should we put that additional CSS?

  1. In the file that styles the thing?
  2. In the file that controls that context?

Let’s say we have the following CSS:

.btn {
  [styles]
}

.modal .btn {
  font-size: 0.75em;
}

Where should .modal .btn {} live?

It should live in the .btn file.

We should do our best to group our styles based on the subject (i.e. the key selector). In this example, the subject is .btn: that’s the thing we actually care about. .modal is purely a context for .btn, so we aren’t styling it at all. To this end, we shouldn’t move .btn styling out into another file.

The reason we shouldn’t do this is simply down to collocation: it’s much more convenient to have the context of all of our buttons in one place. If I want to get a good overview of all of the button styles in my project, I should expect to only have to open _components.buttons.scss, and not a dozen other files.

This makes it much easier to move all of the button styles onto a new project, but more importantly it eases cognitive overhead. I’m sure you’re all familiar with the feeling of having ten files open in your text editor whilst just trying to change one small piece of styling. This is something we can avoid.

Group your styles into files based on the subject: if it styles a button, no matter how it goes about it, we should see it in _components.buttons.scss.

As a simple rule of thumb, ask yourself the question am I styling x or am I styling y? If the answer is x, then your CSS should live in x.css; if the answer is y, it should live in y.css.

BEM Mixes

Actually, interestingly, I wouldn’t write this CSS at all—I’d use a BEM mix—but that’s an answer to a different question. Instead of this:

// _components.buttons.scss

.btn {
  [styles]
}

.modal .btn {
  [styles]
}


// _components.modal.scss

.modal {
  [styles]
}

We’d have this:

// _components.buttons.scss

.btn {
  [styles]
}


// _components.modal.scss

.modal {
  [styles]
}

  .modal__btn {
    [styles]
  }

This third, brand new class would get applied to the HTML like this:

<div class="modal">
  <button class="btn  modal__btn">Dismiss</button>
</div>

This is called a BEM mix, in which we introduce a third brand new class to refer to a button belonging to a modal. This avoids the question of where things live, it reduces the specificity by avoiding nesting, and also prevents mutation by avoiding repeating the .btn class again. Magical.

CSS @import

I would go as far as saying that CSS @import is not just a code smell, it’s an active bad practice. It poses a huge performance penalty in that it delays the downloading of CSS (which is a critical asset) until later than necessary. The (simplified) workflow involved in downloading @imported CSS looks a little like:

  1. Get the HTML file, which asks for a CSS file;
  2. Get the CSS file, which asks for another CSS file;
  3. Get the last CSS file;
  4. Begin rendering the page.

If we’d got that @import flattened into one single file, the workflow would look more like this:

  1. Get the HTML file, which asks for a CSS file;
  2. Get the CSS file;
  3. Begin rendering the page.

If we can’t manage to smush all of our CSS into one file (e.g. we’re linking to Google Fonts), then we should use two <link /> elements in our HTML instead of @import. While this might feel a little less encapsulated (it would be nicer to handle all of these dependencies in our CSS files), it’s still much better for performance:

  1. Get the HTML file, which asks for two CSS files;
  2. Get both CSS files;
  3. Begin rendering the page.

So there we have a few additions to my previous piece on Code Smells. These are usually all things I have seen and suffered in the wild: hopefully now you can avoid them as well.