Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

One syntax for modifiers and states #37

Closed
AntonTrollback opened this issue Nov 13, 2013 · 9 comments
Closed

One syntax for modifiers and states #37

AntonTrollback opened this issue Nov 13, 2013 · 9 comments

Comments

@AntonTrollback
Copy link
Contributor

I keep returning to the idea that modifiers and states could share syntax, the state-of-component syntax.

<button class="Button is-primary is-larger is-disabled">
<button class="Button Button--primary Button--large is-disabled">

The main advantage would be greater simplicity. The distinction between states and modifiers becomes unimportant (something I've seen people new to SUIT have had problems with).

Another advantage, according to me, is that HTML attributes would be a bit more readable and shorter.

A drawback I can think of is that it becomes unclear which component the modifier/state belongs to if an element has more than one component base class added to it.

Thoughts? Is the distinction important?

@timkelty
Copy link
Member

I agree, this usage is tempting.

.Button.is-primary carries with it a greater specificity than .Button--primary, which may not be desirable. In most cases, though, I think it is pretty manageable.

@jorenvanhee
Copy link

I think it's easier to see what's related to each other if you use the Button namespace for instance. What if you have two objects with modifiers on an element. I know this is rare, but how will you know what's what.

@philipwalton
Copy link

I can see it both ways (and have wrestled with this question myself on occasion). But at the end of the day I think there is value in separating (via a naming convention) styles that modify components at load time verse styles that modify components at runtime via user interaction.

I wrote about this issue a few months back; here is an excerpt that summarizes my argument:

JavaScript can ... also add or remove classes to change the style of elements. But this can be a problem if those classes aren't identifiably different from the classes that appear on page load.

When JavaScript code knows too much about component styling it becomes very easy for a CSS developer to make changes to a stylesheet and not realize he's breaking critical functionality.

This is not to say that JavaScript shouldn't alter the look of visual components after the user interacts with them, but if it does it should do so through an agreed-upon interface. It should use classes that are identifiably different from the classes that define the default styling.

At the end of the day I think this is a question of maintainability verses learning curve, and for smaller teams you can probably get away without the distinction. For larger teams where the person writing the JavaScript isn't also the person writing the CSS, I think it's helpful to keep the codebase predictable.

If you end up choosing to just use one or the other, I'd strongly recommend sticking to the BEM-style modifiers over the SMACSS-style state classes.

@AntonTrollback
Copy link
Contributor Author

I tested this in a medium-size project.

It's nice, but the specificity changes is a deal breaker. Ended up using the modifier syntax in some cases and would not go down this road again.

@necolas
Copy link
Contributor

necolas commented Feb 6, 2014

I tested this in a medium-size project.

Nice! That's a good way to try things out. Thanks for the suggestion and everyone's feedback.

@simurai
Copy link

simurai commented Dec 20, 2014

@AntonTrollback Do you still remember the details about "specificity changes is a deal breaker"?

Because I've been toying around combining modifiers and states, like you suggested. Yes, it would increase specificity for modifiers, but on the other hand:

  1. No repeating of the component name. Button Button--primary is fine, but sometimes they can become pretty large and it gets harder to read the inspector.
  2. Peace of mind. You wouldn't have to think what is a state and what is a modifier. Or if a class exists on load or get changed/added at runtime. The line can also be blurry. For example if you click on a button and an error happens and the button should turn red, is that a modifier or a state? With the is- prefix it wouldn't matter. Button is-primary would be switched to Button is-error.
  3. Now that CSS variables use the double dash, it clashes a bit with the modifiers. I mean just visually .Button--error { color: var(--error-color) }

Well, I might give it a try and see how this specificity thing behaves.

@philipwalton
Copy link

@simurai I'm starting to think more and more about removing this distinction as well. However, I'm leaning the other way, toward a more traditional BEM implementation that only uses modifiers.

My concerns are about transitioning to a future with Web Components. With pure BEM there is a 1-to-1 relationship:

  • Block == Custom Element
  • Element == Shadow DOM subtree element
  • Modifier == Attribute on the shadow host

In this paradigm there isn't much of a distinction between state and modifier, so I think doing away with the distinction may be more future proof. That being said, this idea has definitely not been tested in a real app...

@necolas
Copy link
Contributor

necolas commented Dec 20, 2014

We're using state classes to control presentation when the state of a component changes. The extra specificity is useful for extension / customization contexts. Easier to think about this in React-like terms. Pseudo-code example:

I have a basic TweetAction definition:

<button class="{$props.className} TweetAction {$local.state}" aria-label="{$props.type}" type="button">
  <Icon class="TweetAction-icon" name="{$props.type}"></Icon>
</button>
.TweetAction {
  color: lightgray;
}

(A) if I use modifiers only:

.TweetAction--favorited {
  color: orange;
}

(B) if I use state:

.TweetAction.is-favorited {
  color: orange;
}

I want to make an action bar that I'm going to display on a dark background, so the actions needs to be white by default:

<div class="{$props.className} TweetActionsBar" role="group" aria-label="Tweet actions">
  <TweetAction class="TweetActionsBar-action" type="reply" tweet="{$tweet}"></TweetAction>
  <TweetAction class="TweetActionsBar-action" type="favorite" tweet="{$tweet}"></TweetAction>
  <TweetAction class="TweetActionsBar-action" type="retweet" tweet="{$tweet}"></TweetAction>
</div>

If I can only use modifiers (A), then the following CSS overrides the favorited state color too, even if I have no intention of doing so:

.TweetActionsBar-action {
  color: white;
}

But by using state classes (B), it is preserved, but you can still replace it if you intend to (notice how this is still a "valid" selector within the context of the TweetActionsBar CSS file):

.TweetActionsBar-action.is-favorited {
  color: yellow;
}

In this particular example there is a way to side-step the issue by letting the default color be inherited from the calling context. But a Button or other more complex components won't have that. If I want to extend a Button and make some style changes to the default styles, I might end up replacing the pressed state, disabled state, etc..

I'm still finding state useful for these reasons, basically extending components and making dynamic changes less likely to be unintentionally replaced. Having said all that, we use state presentation quite sparingly: open/closed dialogs, tweet action states, buttons / controls.

I don't think it will be incompatible with Web Components because IIRC they will supposedly provide a better, native way to define which nodes and styles can be altered from the outside context.

@philipwalton
Copy link

I don't think it will be incompatible with Web Components because IIRC they will supposedly provide a better, native way to define which nodes and styles can be altered from the outside context.

Yes, in Web Component land there is the :host-contenxt() pseudo-class, so you could do something like this:

:host-context(.is-favorited) {
  color: yellow;
}

mlnmln pushed a commit to mlnmln/suit that referenced this issue Dec 30, 2018
Use stylelint-config-suitcss within preprocessor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants