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

推荐订阅源

月光博客
月光博客
人人都是产品经理
人人都是产品经理
C
Check Point Blog
大猫的无限游戏
大猫的无限游戏
小众软件
小众软件
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
博客园 - 聂微东
Help Net Security
Help Net Security
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
L
LINUX DO - 最新话题
Cloudbric
Cloudbric
博客园 - Franky
阮一峰的网络日志
阮一峰的网络日志
O
OpenAI News
V2EX - 技术
V2EX - 技术
H
Hacker News: Front Page
Hacker News: Ask HN
Hacker News: Ask HN
Google DeepMind News
Google DeepMind News
PCI Perspectives
PCI Perspectives
爱范儿
爱范儿
Hacker News - Newest:
Hacker News - Newest: "LLM"
Martin Fowler
Martin Fowler
S
Schneier on Security
Webroot Blog
Webroot Blog
Know Your Adversary
Know Your Adversary
云风的 BLOG
云风的 BLOG
H
Help Net Security
I
InfoQ
Simon Willison's Weblog
Simon Willison's Weblog
雷峰网
雷峰网
J
Java Code Geeks
V
Visual Studio Blog
MyScale Blog
MyScale Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
C
CERT Recently Published Vulnerability Notes
Google Online Security Blog
Google Online Security Blog
Spread Privacy
Spread Privacy
有赞技术团队
有赞技术团队
The GitHub Blog
The GitHub Blog
Engineering at Meta
Engineering at Meta
V
V2EX
Apple Machine Learning Research
Apple Machine Learning Research
www.infosecurity-magazine.com
www.infosecurity-magazine.com
A
About on SuperTechFans
S
SegmentFault 最新的问题
H
Hackread – Cybersecurity News, Data Breaches, AI and More
TaoSecurity Blog
TaoSecurity Blog
WordPress大学
WordPress大学
S
Securelist
P
Proofpoint News Feed

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? Code Smells in CSS Revisited 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
Grouping related classes in your markup
Harry Roberts · 2014-05-22 · via CSS Wizardry

Written by on CSS Wizardry.

Table of Contents

Independent writing is brought to you via my wonderful Supporters.

  1. How it works
  2. Scanning your code
    1. Featured case study: NHS
  3. Vim
  4. Validity
  5. Errors
  6. Extra bytes
  7. Teams
  8. JavaScript
  9. To use, or not to use?

N.B. 2016-02-23 I no longer advise this technique—it proved far too problematic on teams where people we unfamiliar with the syntax. If you do wish to clearly delimit classes in your markup, perhaps opt for something a little less alien:

<div class="media  media--large
            testimonial  testimonial--main"> 

A few weeks back, I put out a Tweet to a jsFiddle describing an idea I’d had in which I grouped two or more related class attributes to make them easier to notice when scanning an HTML file, and—coincidentally—trivial to manipulate very effectively with Vim, my text editor of choice.

I was grouping them by enclosing them in square brackets, and that looks something like this:

<div class="[ foo  foo--bar ]  baz">

Since sharing the demo, I’ve used this method on a few builds, and people on Twitter have asked me about the presence of the brackets, so I thought I’d write it up into a full article. Before we get into things too far though, there are a couple of things I’d like to say up front.

Firstly, after I shared the link to the jsFiddle, I got a few replies about a much earlier method along similar lines from Ben Everard, so the idea of visually separating classes isn’t all that new. Where this method differs, however, is the concept of grouping them, as opposed to just delimiting them. More on that later.

Secondly, and more importantly, this article is not a direct endorsement of, or recommendation for, this method, this is just a writeup of an idea. It has benefits and drawbacks—as do most things—so your mileage may vary. I will discuss relative merits and pitfalls later in the article so that you can decide whether this is a good thing for you, rather than it being a good or bad thing in and of itself.

How it works

There is no hard and fast rule as to how and when to begin grouping your classes, but the guidelines I’ve set for myself are:

  • There must be more than one ‘set’ of classes.
  • One ‘set’ must contain more than one class.

This basically just ringfences any groups that need it, for example:

<!-- Only one set. Nothing needs grouping. -->
<div class="foo  foo--bar">

<!-- Two sets, but only one class in each. Nothing needs grouping. -->
<div class="foo  bar">

<!-- Two sets, one of which contains more than one class. This set needs grouping. -->
<div class="[ foo  foo--bar ]  baz">

<!-- Two sets, both of which contain more than one class. These sets needs grouping. -->
<div class="[ foo  foo--bar ]  [ baz  baz--foo ]">

If these rules seem a little convoluted—and they might—feel free to experiment with your own. You could simplify it right down to ‘any groups always need enclosing’, like so:

<div class="[ foo  foo--bar ]">

<div class="[ foo ]  [ bar ]">

<div class="[ foo  foo--bar ]  [ baz ]">

<div class="[ foo  foo--bar ]  [ baz  baz--foo ]">

How you group them can be entirely your choice, the concept here just deals with the fact that we’re grouping things at all.

It’s important to note here that I’m using BEM-style naming, which already starts to group classes anyway. If you’re not using BEM, your HTML might look like this:

<div class="[ box  promo-box ]  sign-up">

We can see here that the classes .box and .promo-box are related, and .sign-up is an orphan, for want of a better word.

Scanning your code

Featured case study: NHS

How I helped the NHS rapidly build a brand new product.

Read case study…

I’ve long been a vocal advocate of using more classes in your markup: it just makes for more pragmatic, manageable, scalable development. It does, however, make your markup a little heavier. This is a small price to pay for the advantages that a nicely decoupled, single responsibility UI, built on a well-abstracted, highly composable codebase brings, but it’s always nice to try and mitigate the effects of more verbose HTML. I feel that it would be nice to have the best of both worlds (hence my proposal for Naming UI components in OOCSS), so I’m constantly looking for ways to try and keep an even balance.

BEM is already an incredibly solid start in making your HTML a lot more transparent: having namespaces in your classes paints a really clear picture of which classes are related to one another, if at all. It leaves you with highly descriptive markup that is simple to work with and debug.

One of BEM’s key strengths is its scannability, and, to my mind, there are two levels of reading code: the first is actually reading it, taking it in, learning what it is doing; the second is scanning it, which is a really high-level look over some code, perhaps to find something, perhaps to just give it a once-over to check its general sanity, or just poking around. I often feel we put a lot of weight on the former—reading code—and nowhere near enough on the latter—scanning it.

What I find the square brackets offer me is a very visual way of absorbing (i.e. scanning) an HTML page. The square brackets become very apparent—very noticeable—when giving some code a very high-level read, so I can very easily spot composed patterns without having to get too involved in the code itself.

Vim

Another absolutely huge benefit of grouping classes inside something (rather than just delimiting them with something like slashes or pipes) is that you can begin to manipulate them with certain text editors (my editor of choice being Vim).

Using commands like ya[ (yank around square-brackets), ci[ (change inside square brackets), da[ (delete around square brackets), we can, respectively: copy everything inside the square brackets, including the brackets themselves; change everything inside of the square brackets, leaving the square brackets intact; delete everything inside of the square brackets, and delete the square brackets as well. Once we’ve done this, we can then go and put (paste) the copied groups elsewhere.

This means that I can modify, duplicate, move, delete, and generally manipulate entire groups of classes very rapidly. Powerful stuff, particularly if—like me—you like to work by composing UIs and patterns in your HTML (think grid systems, OOCSS, etc.).

I made a very crude screencast of this in action—all commands are done without needing to reach for a mouse, or nudging around text-selections whilst holding the Shift key.

Working with grouped classes inside of Vim.

However, this feature is a combination of Vim’s power and us leaving handy markers for Vim to work with. If you aren’t using Vim, or a similarly powerful text editor, then these benefits will not be quite so apparent. If you fancy learning Vim—and I really would recommend it—simply open your terminal and run:

This will give you a really easy-to-follow, well-paced introduction to Vim.

Validity

Using square brackets in HTML’s class attribute is completely valid. In fact, where HTML is concerned, class attributes can legally contain any character at all. From the HTML5 spec:

There are no […] restrictions on the tokens authors can use in the class attribute…

In CSS, however, the [ and ] characters are invalid in a class, unless it is escaped. That is to say, this will not work:

However, this will:

Using square brackets in your class attributes is valid, and won’t trip browsers up (I’ve tested this personally in browsers as far back as IE7–8).

As we shall see next, though, this being valid does not free us from the chance of errors occurring.

Errors

As I mentioned previously, however, this method is not without its drawbacks, so please take these—and your own context—into account when considering adopting it.

The first possible pitfall with this method of using square brackets to group your classes is that it becomes a lot more easy to introduce errors. Traditional classes are pretty difficult to get wrong. In fact, nine-times-out-of-ten, the only way you could get a non-matching class would be a simple typo.

When introducing these square brackets, however, we are also introducing a huge potential for error. Whilst the following is perfectly valid, and will work just fine:

<div class="[ foo  foo--bar ]  baz">

Making a mistake by writing the following will invalidate things completely:

<div class="[ foo  foo--bar]  baz">

This is something to be wary of, because .foo--bar and .foo--bar] are two very different things. This becomes even more of a problem in situations where:

  • You’re writing classes to the view dynamically. If you have templating logic in your markup, it can often be hard to see where these errors might occur, and they also become harder to debug.
  • You have team members unfamiliar with the convention. Seeing something like this may well be confusing to a developer who’s never used it before, so it is possible that they won’t understand the caveats required for it to work.

One response I’ve had more than once is the concern with more bytes over the wire. This is something of a non-discussion, but I’ll address it anyway: if you really care about class-attribute bytes over the wire, you should be using a CSS obfuscater like Google’s Closure Stylesheets.

Crunching raw HTML bytes is a job for a tool, not a developer.

Teams

One of the main drawbacks of this method is its use in team environments, which is quite ironic considering my work centres a lot around managing codebases in teams.

The first and foremost—and most obvious—issue is the fact that it needs explaining at all. The fact I’ve managed to get a few-hundred-word article out of this may be testament to its unusual and potentially confusing nature.

This will always the case when adopting new methodologies—like BEM, for instance—but it is an overhead that cannot be ignored. The existing team will need to learn and be on board with this grouping methodology, and any new hires will also need teaching. It’s another hurdle; a small one, but definitely another.

JavaScript

As Todd Motto pointed out over Twitter, this method may have some impact on JavaScript interacting with your classes. I only ever bind onto .js-* classes, so these would typically never get grouped, but you may hit problems elsewhere. Again, your mileage may vary.

Todd did follow up with:

Adding classes is okay, even with ‘nested’ grouping […] if you’re using .js-* then you’ll be fine.

To use, or not to use?

This is entirely up to you. I’ve been using it on all builds recently, and I’m really liking it. It has been alien to other developers I’ve been working with, but I’ve been able to explain it, and I have had one incident of dynamically-generated classes breaking.

Because I use Vim, I really do get a lot of use out of this, and because I’m a vocal advocate of using more classes, it helps me spot and manage them in my markup.