One of the challenges we face when implementing class-based atomic styling is that it often depends on a specific breakpoint for context.
<div class="span-12"></div> <!-- we want this for small screens -->
<div class="span-6"></div> <!-- we want this for medium screens -->
<div class="span-4"></div> <!-- we want this for large screens -->
It’s common to use a prefix to target each breakpoint:
<div class="sm-span-12 md-span-6 lg-span-4"></div>
This works well until we start adding multiple classes. That’s when it becomes difficult to keep a track what relates to what and where to add, remove. or change stuff.
<div class="
sm-span-12
md-span-6
lg-span-4
sm-font-size-xl
md-font-size-xl
lg-font-size-xl
md-font-weight-500
lg-font-weight-700">
</div>
We can try to make it more readable by re-grouping:
<div class="
sm-span-12
sm-font-size-xl
md-span-6
md-font-size-xl
md-font-weight-500
lg-span-4
lg-font-size-xl
lg-font-weight-700">
</div>
We can add funky separators (invalid class names will be ignored):
<div class="
[
sm-span-12
sm-font-size-xl
],[
md-span-6
md-font-size-xl
md-font-weight-500
],[
lg-span-4
lg-font-size-xl
lg-font-weight-700
]">
</div>
But this still feels messy and hard to grasp, at least to me.
We can get a better overview and avoid implementation prefixes by grouping attribute selectors instead of actual classes:
<div
data-sm="span-12 font-size-lg"
data-md="span-6 font-size-xl font-weight-500"
data-lg="span-4 font-size-xl font-weight-700"
>
</div>
These aren’t lost of classes but a whitespace-separated list of attributes we can select using [attribute~="value"]
, where ~=
requires the exact word to be found in the attribute value in order to match.
@media (min-width: 0) {
[data-sm~="span-1"] { /*...*/ }
[data-sm~="span-2"] { /*...*/ }
/* etc. */
}
@media (min-width: 30rem) {
[data-md~="span-1"] { /*...*/ }
[data-md~="span-2"] { /*...*/ }
/* etc. */
}
@media (min-width: 60rem) {
[data-lg~="span-1"] { /*...*/ }
[data-lg~="span-2"] { /*...*/ }
/* etc. */
}
It may be a bit odd-looking but I think translating atomic classes to attributes is fairly straightforward (e.g. .sm-span-1
becomes [data-sm~="span-1"]
). Plus, attribute selectors have the same specificity as classes, so we lose nothing there. And, unlike classes, attributes can be written without escaping special characters, like /+.:?
.
That’s all! Again, this is merely an idea that aims to make switching declarations in media queries easier to write, read and manage. It’s definitely not a proposal to do away with classes or anything like that.
Yes, it might be easier to read, but it’s not valid HTML. For a site like CSS Tricks that has a lot of readers and a huge influence, something like this should come with some sort of warning or disclaimer, so the audience doesn’t go and implement this without knowing the consequences.
Hm yeah those should be
data-*
attributes. Sorry about that, will fix.Why do you think it is invalid HTML?
The spec says data attributes are even valid XML:
https://www.w3.org/TR/2011/WD-html5-20110525/elements.html#attr-data
While I think the HTML is valid, I do not know if it is smart to use it like this. Are there any performance downsides by using find-in-attribute selectors instead of specific classes?
Also I find the attribute selector CSS less pleasant to read than regular old classes. Maybe that is just a matter of being more used to those.
@J.T.: When the article was first published they were not
data
-attributes — Chris was very quick to add those after my comment, so kudos to him :)HTML attributes like
myAttribute="value"
are ignored by browsers if not in the WC3 specifications but they will work as CSS selectors. Adding thedata-
prefix makes sure there are no conflicts ifmyAttribute
is introduced as an official attribute in the future.I hope this helps to bring back focus on what was the intention of the article :)
~ Jakob
That’s slick man, I like it!
Amazing idea of the day, Will use it in my projects too.
It is really a good way to apply dynamic templating within responsive design. Thanks for sharing.
From a specificty point of view this can definitely be replaced with default class based grids because attribute selectors are equal to class selectors.
But building webpages with the atomic styling approach in mind just has this side effect, that classes will get overwhelmingly big (in my opinion).
Thats why I myself much more preffer the BEM approach because there aren’t as many classes in the DOM as in atomic styling and the DOM is much more readable and semantic.
Just beware that ~=’span-1′ will also match span-10, span-11, and span-12. So if you’re using this, expect some overlap, and keep an eye out for strangeness.
Hi Shea,
This is only the case if you use
*=
. The~=
requires an exact value match (no spaces).Example:
~ Jakob
Very nice. Good to see other people also starting to use attribute selectors. I have been using them to prototype a lot recently, even without data- prefix. Here’s an example: https://codepen.io/mikemai2awesome/pen/58903b18084725268a30609ff6528cd3
I’m curious, do these kind of selectors incur any kind of notciable performance hit? I would’ve thought class name matching was heavily optimized.