I am having a discussion with a my technical director on the best way to style some forms on a website. The forms already have the data-ui=“form” attribute in place on each form tag.
I would like to use this:
<form data-ui=“form” class=“c-form”>
and apply styling thus:
.c-form {
background: red;
}
The advantages are:
The disadvantages are:
He would prefer to use:
<form data-ui=“form”>
and apply styling thus:
[data-ui=“form”] {
background: red;
}
The advantages are:
The disadvantages are:
What are your thoughts?
Hi everyone,
I am said technical director and firstly I'd like to thank Joe for garnering external opinion, it's a big guy who puts himself at risk of potentially being pointed out to be wrong! I also appreciate I can be a bit of a hard task-master, so fair play :P
So Joe's question misses out some critical context which I'll outline before adding my own thoughts about data-attributes vs / and / or classes.
First of all, we're building a web app using VueJS, and as for any SPA, it is extremely component heavy. The <form> element in question is actually one of about 20 core components, such as inputs, typeaheads, checkbox arrays, datepickers; you name it, we're building it (wrapping the excellent Semantic UI as it happens).
To distinguish core components from application components, we're prefixing them with ui- in the component markup, and rendering data-ui="type" to the output, which has various uses as you'll see in a moment.
To illustrate...
The form component definition looks as follows:
<template>
<form data-ui="form" :data-name="name" class="ui-form ui form" @submit.prevent="submit">
<div class="ui segment">
<slot>
<!-- content goes here -->
</slot>
<div data-name="buttons" class="ui unit">
<slot name="buttons">
<!-- buttons go here -->
</slot>
</div>
</div>
</form>
</template>
It's declared in the application like so:
<ui-form name="employment">
<ui-input ... />
<ui-dropdown ... />
<ui-typeahead ... />
</ui-form>
And its final rendered HTML looks like so:
<form data-ui="form" data-name="..." class="ui-form ui form" name="..."> ... </form>
The form, along with all the other components render into the browser as follows (along with automatically-injected comments; nice):

So first things first, Joe loves BEM.
I get what problems BEM is trying to solve, but to be honest, you don't need BEM if you understand CSS specificity, and you design your app properly.
Of course you don't get collisions if you write ridiculouslyLongClassNames and then nest &__sillyElementIdentifiers along with &--statesLikeThis.
However, for the single spurious problem which BEM is supposed to solve (classname collision, and possibly portability) it brings to the table multiple other problems which you'd rather not have:
class="foo__bar--baz" .foo { &__bar { &--baz Without going into all the other BS arguments for BEM, with modern build tools, you just don't need it. The foo__bar stuff becomes completely unnecessary when Webpack prefixes your component classnames for you.
Web apps are complex, and even more than a regular site, you want to keep things as light as possible. Unfortunately, BEM is the antithesis of this as it's trying to solve an entirely different problem (TL;DR BEM has no place in web apps).
Secondly, if all you have is a hammer, everything looks like a nail (see above point, BEM!)
Classes are just one of multiple selectors available; #id , .class, element , [attribute] and the :pseudo classes, and along with combinators such as + , > etc are used to select elements for styling.
Each of the selectors has their plusses and minuses. Classes:
However, they have no such significance above that, and arguing to use them "because they are classes" is showing either a limited understanding of CSS or possibly worse, ingrained / habitual thinking.
Attributes have different qualities:
[ui-data] "form" [foo^="starts-with"] [foo*="contains"] The fact that an attribute is prefixed with data- is neither here nor there.
"But the Internet says they are for JavaScript data only!"
I disagree. And so does Christian Heilmann:
HTML is supposed to validate; it's not XHTML after all, so in HTML5 data-* attributes were added to allow developers to add meaning where none was allowed:
HTML5 is designed with extensibility in mind for data that should be associated with a particular element but need not have any defined meaning.
data-*attributes allow us to store extra information on standard, semantic HTML elements without other hacks such as non-standard attributes, extra properties on DOM, orNode.setUserData().
Note the words but need not have any defined meaning. I believe this extends intrinsicly to CSS, and in our case the data-* attribute provides various advantages:
data-ui or data-layout and value i.e. ="typeahead" in HTML that does not yet allow custom components i.e. <typeahead> or <ui-typeahead> class="typeahead small left" .c-typeahead <div data-ui="..." Interestingly, this brings us back to the other main BEM argument, portability.
It's pretty unlikely that you'll be using two sets of [data-ui] attributes in your application, therefore, an attribute prefix is at least, if not more, effective than a class name:
div[data-ui="typeahead"] { ... }
div.c-typeahead { ... }
div.typeahead { ... }
So time to stop typing now, but I'll tackle a few of the other points that have come up here, or have come up in general:
As for "other forms" question which came up... as the tech director for the product I don't see us straying from the look, feel and functionality we need so don't see that this will be an issue. But if it is, guess what? We just make a new component!
<template>
<form data-ui="special-form" :data-name="name" class="ui-form ui form" @submit.prevent="submit">
<h1>I'm special!</h1>
...
I'm not sure if anyone's seen Jim Jeffries sketch "Gun Control" but if you watch it and swap the words "gun" for "BEM" and you'll get an idea of how I feel about BEM:
(And the OP is about BEM, no matter how much you think it's about data attributes!)
For what it's worth, I compromised and now all component names are also rendered automatically as class names, so Joe can do what he likes, but the thing I didn't want was non-DRY repetition of classes on both component instances and definitions:
<ui-form class="c-form"> <!-- nooooooooo! -->
className (...rest) {
const attrs = {
required: this.isRequired,
error: !!this.error
}
return classify(this.$vnode.componentOptions.tag, 'ui field component', this.classes, attrs, ...rest)
},
<div data-ui="input" class="ui-input ui field component required">
Now let's get on and build this damn app, Joe!
:D
Wow, there are a lot of strong opinions in here! :D
Personally I get a lot of use out of custom data attributes for styling purposes! Not only are you able to use the values held by attributes in CSS with attr(),like if you have data-tooltip="Tip here" on an element you can quite easily say: [data-tooltip]:after { content: attr(data-tooltip); } — but that's not even the main reason I do it.
The biggest reason I use custom data attributes for styling is that it's like creating your own namespace for the values you're about to use. If you're adding buttons to a website, there's a decent chance a class name like .button or .btn already exist and are defined as something, but the odds are a lot smaller that a website you need to add buttons to will already have a [data-button] defined. Same with .button.blue versus [data-button=blue] too.
Over time we ended up using custom data attributes at the 'component' level to identify components, then when we use classes we're almost always qualifying it with the name of the component its in. So on our sites you might have a rule for [data-widget] .headline and [data-article] headline, but you wouldn't have a rule for .headline sitting around (does that make sense?).
Here's an example of a set of buttons I've used on literally hundreds of different pages, I've gotten a lot of use out of them, and part of the reason why is because you can quite literally drop these into any website and they Just Work™. You style any element like a button by adding [data-button] and there are colours to choose from: green, red, blue, grey, outline. But there's also a semiflat style as well that can be combined with any color, for combinations like [data-button="red semiflat"] .
Using data attributes for styling is an excellent idea - and the fact that data attributes can be set and have values added or altered by JavaScript only makes them _more versatile. _Classes are great, but why limit yourself to picking names in just the class="" attribute alone, when you can have unlimited names in unlimited attributes :D
I have dozens more examples, but I wonder what any of the people saying not to use data attributes would give you as an example that they aren't useful :P
(EDIT: I'll just add that we didn't set out to use data attributes, nor did we especially want to use them, but when you have the same snippets of code to add to multiple websites and you have to keep changing the classes and selectors over and over, well eventually we used data attributes somewhere and it stuck. And then for our other snippets after changing from class to class to class…if we switched them to a data attribute instead…that's the form they're still in today. It was a few years of 'ending up as' data attributes before we started intentionally naming things this way from the outset. I think before we settled on [data-button] the original orange button started life as .button.start-it, as well as .button.send-it, became .cta-button, .RFIbutton, .orangebutton, and even more, before eventually becoming [data-button])
Based on the name of the thing "data attributes", I'm not sure it's good idea to use data to style things.
Those html5 data-* attributes are semantic attributes that meant to describe your data within the html and designed to be read and written by javascript (mainly). Note that they are also much slower to be accessed using css selectors than ids or classes.
Although, if it implies a modification on too much places (if you're maintaining 50 themes), the solution to use a data attribute could be ok as a quick fix. I'll still try to find a parent html element with any class / id and match that instead:
#parent form {}
.parent-div form {}
All that to give you time to add a class on your form in all the places it appears without pressure.
If there's really only one style of form for the whole site, you don't need either, just style form ... but of course it never is just one form style ;)
The strongest argument to me would be to remain consistent with the rest of the site. Having BEM for everything except this one bit is just crappy practice.
I would first be worried that you felt the need to create a data- attribute called "form" for a bloody form tag. Seems a bit redundant.
I would then be worried that you felt the need to create a data- attribute called "form" for a bloody form tag. Seems a bit redundant.
I realize this is in fact only one thing, but it is such a colossal sticking point I felt it warranted being repeated.
"c-form" is also painfully vague... We already know it's a form, hence it being a form TAG, WHAT is it a form OF?!?
For ME, I can't use attribute selectors yet, I'm still supporting too many users at places like healthcare and government offices who are on browsers that don't support them yet -- so it's not even a choice anyways.
Though since you said you're using BEM, that probably means endless pointless classes for everything and failing to leverage selectors properly; at which point why are you worried about one more class?
In all likelihood that alone probably means the whole codebase needs a good toss in the bin and a complete restart from scratch.
Hmm the question to me is -> does the "form-ui" always remain ? because data-attributes are custom attributes classes are default attributes.
that's the actual big question. We're all lazy to some extent so using "what's there" is always a good approach, but is it possible that this will break by a version update ? that's the maintainability perspective .
Personally I'm not a big fan of DOM-attr-coupling. classes are good enough to me. but if you want to just check if things can/will break in the future or if you have to keep dead nonsensical tags in the code just because you made this decision.
Maybe someone else has a better opinion Tommy Hodgins ? I am in the end not specialized on frontend.
Martin Muzatko
Crazy and passionate Front-end Developer. Always eager to bring ❤️ to the world in the shape of software and knowledge
We use flexproperties (npmjs.com/package/flexproperties), which uses attributes to define layouts. e.g.
<div layout="row" layout-align="space-between"></div>I used this on many projects so far and it works better with responsive design, than cluttering the classes with modifiers. I used this approach in k15t.com, help.k15t.com and my personal project happy-css.com with great success.