suitcss / suit

Style tools for UI components
http://suitcss.github.io/
3.8k stars 229 forks source link

Styles for export - best way to override compoents and/or their modifiers #142

Closed dryoma closed 6 years ago

dryoma commented 7 years ago

Hey all,

Assume we have a widget to be used on other sites. Some styles from it:

// The default button; styles meant to be overridden on the importing side
.w-Button {
  background: red;
  ...
}

//  Some special button. Would like to have these styles on the importing side intact
.w-Button--special {
  background: blue;
  background-image: url("data:image/svg+xml;utf8, some huge svg here");
}

A site we want to import that widget to:

.w-Button {
  background: #chuckh; // Oops, we just messed the w-Button--special's fancy SVG as well
}

I.e., on all sites where we use that widget, we normally want to redefine the .w-Button, but we want to keep the .w-Button--special as-is. Not just because we're lazy, but redefining that SVG might be a pain. And if it's to be updated in some way in the widget, we'd have to update it everywhere.

Some options that come to mind:

  1. .w-Button--special.w-Button.w-Button--special in the widget. Pros: works. Cons: increases specificity, the styles get less clean.
  2. !important
  3. "Don't override the whole background, just the color; that way the SVG won't be lost" - but what if we'd like some more of that w-Button--special styles exported as-is? Including those that actually should be overriden for the default w-Button on the importing site?
  4. Separate component for the special button. This will help, but makes less sense than a modifier as far as the class structure is concerned.

And how do you normally fight this? And what would be the best SUIT-able way?

oleersoy commented 7 years ago

If you are modifying the style of the core button, then use CSS variables. If you want to change the border color of a button that has already been modified, then use utilities. For example: https://superflycss.github.io/utilities-colors/target/test/html/

dryoma commented 7 years ago

I'm afraid I don't follow. How would variables and utilities help?

oleersoy commented 7 years ago

Here's how I do it. 1) Define the variables that you can override for the component in the stylesheet. You can see my variable naming conventions here. An example of their usage here. These let you override all the 'configurable' parts of the component - including events using standard naming conventions that are easy for all other developers to understand.

So you would first import the component into your stylesheet and then override the variables that correspond to the component pieces you want customized.

Once you have done that if you still want to change the look of the component, but not override the core of it, use utilities. For example if the component has a blue border, and you want it to be red, then apply u-border-color-red. Make sense?

If you want to try this out you can check out test case for @superflycss/component-header and build the test and then play around with overriding the variables and applying utilities as needed. The test already has dev dependencies for a lot of the @superflycss utilities.

git checkout https://github.com/superflycss/component-header
npm i
npm run test
dryoma commented 7 years ago

Ah, that. Well, on the projects in question we are doing something similar, except we use SCSS and don't have so many elements set up as variables. I think I didn't make clear enough what I want to try to achieve.

There are. let's say, projects A and B. A is a js widget, that have elements with .w-Button. B is actually a bunch of sites that use that widget by calling it's JS. B all have styles for their elements different from what A's elements look like out of the box. So for most of A's elements B do styles overriding, the way I have shown in the 1st comment.

It worked well until a modifier to .w-Button, the .w-Button--special showed up that is meant to not be redefined by B's native styles, but kept as-is. That as-is is the key. Because, if we override the base component styles on B, we automatically apply those overriding to the modifier, .w-Button--special. Reapplying the original .w-Button--special's styles would work if B was just one site, but there are more than a dozen. And even then it would be quite fragile, as those reapplied styles would basically be the new styles, even though equal to them; so if the original styles would change, the duplicate styles would have to be edited too, on every single importing site.

Using variables, even the CSS variables that can be imported with the original A styles, - I think it's not the best solution here either. We'd have to have variables for all .w-Button's props we might want to redefine, and a separate set for all the .w-Button--special's props. Just looking at @superflycss/component-header, that's really an overkill. Util classes don't work here either by the same reason.

oleersoy commented 7 years ago

If I understand you correctly you are saying that overriding the properties of w-Button cascades to w-Button--special. If that's the case then you need to define a new button. Say x-Button. That button could extend w-Button, if you have the need to reuse a lot of the core features.

dryoma commented 7 years ago

Yep, the more I think about it, the better I like a separate component vs increasing specificity.

Thanks for the discussion!

oleersoy commented 7 years ago

Glad it helped!