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