

AM – Attribute Modules for CSS - geelen
http://glenmaddern.com/articles/introducing-am-css

======
gioele
Why should "rounded" and "large" be classes?

Isn't

    
    
        <div class="button buy">...</div>
    

plus a bit of SASS

    
    
        .buy {
            @include big;
            @include rounded;
        }
    

enough and more correct?

If you embed "big" in the HTML, how do you go from mobile to desktop? Maybe
that button should be "big" on desktop, but only slightly bigger than normal
on mobile. If you specify its _role_ rather than its presentation, it becomes
very easy to override the style in CSS with @media selectors.

PS: BTW, <button class="buy">...</button>, no <div> needed.

~~~
jdcantrell
In my experience the lower level classes increase flexibility,
maintainability, and sanity of your css. It's what allows bootstrap to be such
a powerful tool for so many products.

Using a conceptually high-level naming scheme won't scale very well as your
team and site grows. Eventually someone will say, I need a large rounded
button, I can use the .buy class for this contact form. Or perhaps your team
is very disciplined but ends up with 10-15 different ways of specifying large
rounded buttons.

You'll potentially end up where .buy makes sense in two different contexts
which will then increase the complexity of your css:

button.buy {...} h1.buy {...}

Having .button .button-rounded .button-large helps you keep your css simple
allows it to be remixed and reused in ways you haven't planned for yet.

Not to say semantic classes are wrong, I like them, but I have yet to see a
system for it that doesn't break down after a certain size for a project/team.

~~~
Smudge
I've started combining both approaches, with help from SASS's '@extend'
directive.

What I mean is, I have a section of my CSS for defining all of those simple,
modular classes like 'button' and 'button-rounded' (I start with bootstrap for
most of these and then add more as I need them), with the idea that I'll reuse
these over and over.

BUT, these never get used in the HTML directly. Instead, I have a second
section of my CSS which contains definitions for those high-level, one-time-
use rules. So, for instance:

    
    
      // Low-level CSS (don't nest anything or it breaks @extend)
      .button { // basic button... }
      .button-large { // large button... }
      .button-rounded { // rounded button }
      .button-primary { // blue button }
      .button-warning { // red button }
    
      // High-level CSS (avoid nesting, but sometimes it is okay)
      #buy_page .buy_button {
        @extend .button;
        @extend .button-large;
        @extend .button-primary;
      }
      #settings_page .deactivate_button {
        @extend .button;
        @extend .button-rounded;
        @extend .button-warning;
      }
    

The big rule for the HTML is that I can only use IDs and classes from the
high-level CSS, and should really only have one class/id per element. For the
most part the high-level selectors are 1:1 with the markup, but things like
widgets/components it's okay to reuse them on multiple pages.

The major exception here is the CSS for actually laying out pages. I don't use
a class-based grid system. Instead, I map <section> tags to rows and
<header>/<article>/<aside> tags to columns. The grid behavior is defined using
Susy[1], and for specific pages I can modify the standard layout pattern if
necessary.

The result of all of this is CSS that is easy to navigate. The low-level CSS
is highly reusable, and the high-level CSS is structurally similar to page
content, yet keeps the presentation information entirely out of the HTML. It
also sometimes results in very long selectors, but that's actually not an
issue performance-wise and has yet to cause me any issues.

[1]: [http://susy.oddbird.net/](http://susy.oddbird.net/)

------
huskyr
This looks a promising approach to the chaos that is writing reusable and
maintainable CSS. I wonder about performance though, especially on mobile.
Will this perform as good as regular class-written CSS, even if you have >2000
loc CSS files?

~~~
goldenkey
Attribute selectors have the worst performance of any selector. Coupling that
with the =~ operator is even more cause for concern. This is why classes and
id exist.

~~~
ratbeard
No, :nth-last-child(n) has the worst performance of any selector.

~~~
bGriz
span:nth-last-of-type(2n+1):not(div) > span ?

Edit: I reversed my edits to maintain proper history.

~~~
ratbeard
Actually, I spoke to soon. Since selector engines work right to left, this
would first narrow down the list of elements to all the spans in the document.
Assuming thats a fairly small percentage of the overall tags in the document,
this selector runs at .0023 ms on my MBP w/ 8 GB or RAM.

Sa'll good.

Edit: For the record, bGriz edited his comment, making my comment invalid.

Edit (2): Thanks!

~~~
bGriz
Definitely fact.

------
shadowmint
Looks like tal:attributes to me. That was a terrible idea before, and its a
terrible idea now.

Honestly, what problem is this _actually solving_?

You have .foo, .foo--bar, .foo--extra, and you're concerned the style tags are
what... not pretty enough? Too hard to visually parse? Too complex when you're
building a large css framework?

I really don't understand the problem.

...but having worked with tal, I can hands down say I hate the custom
attribute syntax; it scatters the style into multiple locations and makes it
unclear what parts are data and what parts are presentation.

I thought we all agreed that:

    
    
        <font color="red"> Its 1998! Hi! </font>
    

Was a bad thing. (and that was fifteen years ago... lessons from the past or
whatever...)

~~~
grumblestumble
This is a terrible misinterpretation of the article. It's not even
tangentially related to hard-coding styles into HTML. Attribute models retain
the ability to abstract style away from the HTML. Take the example used in the
article, am-Button. Using the AM approach, you retain the ability to update
the font-color, size, base styling in one place in a separate block of code
(css, scss, whatevs). The styles are by no means scattered into multiple
locations.

~~~
shadowmint
Certainly having a custom attribute that bundles multiple styles is novel.

...but having single attributes for color, font size and base styling is what
attribute styles in early html _were_.

I think its disingenuous to suggest this is fundamentally different because
these are _custom_ style attributes (am-font) instead of hard coded style
attributes (color).

The point is that presentation and data should be _distinct_ in markup, and
this use of custom attributes muddles things.

I can easily imagine some css framework providing css attribute classes that
get used just like the old html style attributes, with all the same markup
clutter and maintenance issues.

oh, want to change a style? now you have to change the markup attribute
instead of the style sheet <\--- this is why multiple display classes (btn--
big) and custom attributes are fundamentally an anti-pattern.

Markup should be tagged with meaning attributes (btn--buy) and the styling
done via stylesheets.

------
acjohnson55
I've gone full BEM on a recent project of mine, and it's been a godsend. Name
our block elements by function, take advantage of SASS's abbreviated BEM
syntax, reuse actual presentation styles with mixins, and use helpers within
our templates to vastly minimize BEM's noisiness in the markup we work with.
The modifiers are also great for capturing variations.

AM strikes me as clever, but I can't see it replacing class-based styling. At
best, with some extensions, it might be a slightly more abbreviated way to
write BEM, but decent tooling can do that for you anyway.

------
wildpeaks
You may prefer the regular "data-" prefix ([https://developer.mozilla.org/en-
US/docs/Web/Guide/HTML/Usin...](https://developer.mozilla.org/en-
US/docs/Web/Guide/HTML/Using_data_attributes)) to "am-", especially if you
want to be compatible with React.

~~~
geelen
You can definitely do this, though I think "data-am-" is better to avoid
conflicts. React ignores unexpected attributes at the moment, but can't
forever with Custom Elements (since it can't know what valid attributes for
new elements are).

We've got an open discussion on this point, if you want to contribute there:
[https://github.com/amcss/attribute-module-
specification/issu...](https://github.com/amcss/attribute-module-
specification/issues/4)

~~~
wildpeaks
"data-am-" would be a good choice indeed :)

~~~
germanforblack
Absolutely! The prefix is optional (but recommended). If you're comfortable
with data attributes (or, they're required for some reason), thats cool too.

------
o_____________o
This will be horribly slow for DOM-heavy projects.

------
u124556
What's the problem with `class="btn large rounded"` and then in css:

    
    
        .btn {}
        .btn.large {}
        .btn.rounded {}
        .btn.large.rounded {}

~~~
pault
There's nothing technically wrong with this, but one could make the argument
that once a project expands beyond a single developer, you have a greater
chance of namespace collisions with vague class names like `.large`.
Eventually someone will come along and make a `.large` class for headlines
that will make your buttons have 36pt labels.

Edit: I have a suspicion that most attempts at "semantic" CSS are a symptom of
a slightly OCD designer wanting to write pretty markup, which is, in my
opinion, probably the last thing you should be optimizing for productivity.

~~~
platz
Except here .large doesn't exist on it's own, it's only used inside the .btn
selector.

the .large class for headlines would similarly only be used inside the .h1
selector.

So there would be no collision in this case.

~~~
ryanbrunner
You can't guarantee that someone won't add a 'top-level' class name in the
future that matches one of your 'modifier' class names. It's essentially the
same argument as saying that global variables are OK because no one will ever
have a global variable with the same name as yours.

------
demircancelebi
I agree that this approach solves a problem. But using custom data attributes
for styling is just not as easy as using classes and I do not think I'll ever
use such approach.

------
egeozcan
`class="btn btn-large"` was sufficient for all the projects that I've seen so
far. I'm honestly curious about the scale of the applications requiring such
measures (there were some other attempts IIRC) and whether they "work".

On a side note, the name sounds funny in Turkish but I guess that's a minor
concern.

------
grumblestumble
I really like this approach, but I think the thing that is currently missing
is preprocessor/build support. The primary use case for this would be large-
scale web applications -- it's probably overkill for anything else.
Unfortunately, for large apps, the performance hit of using ~= is
unacceptable. However, most large apps use some kind of build system - grunt,
gulp, make, etc, and writing a task to convert these into old-fashioned CSS
class names as part of the build seems entirely doable.

------
taylorlapeyre
Very interesting approach. One small peeve: this method will not allow you to
use Sass/LESS nesting.

~~~
masklinn
why wouldn't it? Attribute selectors are just selectors.

    
    
        [foo] {
          color: blue;
          background-color: pink;
          .bar .baz {
            [qux~="ok"] {
              color: red;
            }
          }
        }
    

compiles to

    
    
        [foo] {
          color: blue;
          background-color: pink;
        }
        [foo] .bar .baz [qux~="ok"] {
          color: red;
        }
    

as I'd expect.

~~~
taylorlapeyre
Oh, lovely! I must have been mistaken then.

------
amccloud
What is the performance of an attribute selector?

~~~
EdSharkey
These selectors are evaluated globally, checked against to EVERY NODE in the
DOM. You might as well write

    
    
      *[am-Button]
    

just to hammer home to the reader that performance is going to be a problem
with this selector approach on any complex DOM.

If I recall, CSS performance can roughly be gauged by the key selector (right
most thingy on the selector) like this:

    
    
      * inline styles = fastest (ok, this doesn't have a selector, but it evaluates fast!)
      * id = fastest-ish
      * class = fast
      * tagname = slower
      * any/everything else evaluated like global = super slowest

------
eridal
interesting approach! any real web/app using this technique?

~~~
CatsoCatsoCatso
I hacked together a markdown editor with previews. To allow the user to change
how an image was displayed they could put space separated options in the alt
text values of the syntax.

eg. ![Cute-Cat aside max500px](/path/to/img.jpg)

Aside & max500px were then picked up the CSS using the images alt attribute as
the selectors.

It was a terrible hack but it worked.

