sndrs / postcss-atomised

Atomise standard CSS
https://sndrs.github.io/atomised-css-repl
86 stars 2 forks source link

Order of precedence #67

Closed kaelig closed 7 years ago

kaelig commented 7 years ago

Here's a use case where the order of precedence gets overwritten by atomising class names: http://www.sassmeister.com/gist/3d1a7d19c08ebfdca383b16b33b7347a

What are the patterns/guidelines that can prevent getting into these kinds of situations?

sndrs commented 7 years ago

hi kaelig!

I'm not sure I understand correctly - baz should be a, not a b?

so:

.foo {
  color: red;
}
.bar {
  color: blue;
}
.baz {
  color: red;
}

becomes

.a {
  color: red;
}
.b {
  color: blue;
}
{
  "foo": "a",
  "bar": "b",
  "baz": "a"
}

(see here) – is this not what you'd expect?

kaelig commented 7 years ago

Hey!

What I was trying to say is that if you apply two class names to an element you can run into that kind of side effect. Does this make sense?

sndrs commented 7 years ago

ah yeah - it only works with one class per element. the idea is that you can @include away to your heart's content, without worrying about bloating your final CSS and without knowing about what else also @includes the things you @include.

so in this example, you can do

.foo {
  @include red;
  @include big;
}
.bar {
  @include blue;
  @include big;
}
.baz {
  @include red;
  @include big;
}

etc without the need for .big, .red. all the duplication and bloat should be atomised away. it passes the responsibility for knowing what else uses the same styles to the machine, and you just think about the thing your styling.

that's the idea, anyway...

kaelig commented 7 years ago

Thanks, that makes sense. I wonder how this works with things such as states (or modifiers) added via JS. e.g. if a gets toggled via JS over b, it will not have any effect whereas a change is expected.

sndrs commented 7 years ago

yeah JS with this is tricky and not solved. it's one reason we've kind of left this on the shelf a bit – it def feels better dealt with by things like CSS modules (if you have a full-stack js app at least).

that said, it shouldn't be impossible. you should be able to extrapolate the classes you'd need to apply for the modifier class in the same the you would with HTML, so if you compiled your templates for your client side js you should be able to 'know' what classes your modifier class equated to. that or you'd need to ship the map, which may or may not be a problem, depending on how big it gets. as long as you approach each class in isolation, you should be ok.

if it ever got to production, i'd always imagined component-based sass file structure, much like CSS modules, in which you could @extend local classes, to make modifiers simple, but only @include if the styles you needed came from elsewhere. so you could have this quite safely:

// mixins.scss
@mixin font($size: 12px) {
  font-size: $size;
  font-weight: normal;
  font-family: sans-serif;
}

// component-1.scss
@import 'mixins.scss';
.component-1 {
  @include font;
  color: red;
}

.component-1--blue {
  @extend .component-1;
  color: blue;
}

// component-2.scss
@import 'mixins.scss';

// not allowed to @import and @extend - duplicate instead
.component-2 {
  @include font;
  color: red;
}

.component-2--big {
  @extend .component-2;
  @include font(15px);
}

in each instance you're able to discretely style the component by state (in fact you have to). you know there will be zero side affects beyond you those can reasonably see (limited to that same file by convention – and probably linting), and the CSS is tiny.

so in your js src, i guess you'd need something like

myElement.classList.remove('component-2').add('component-2--big')

and then either compile the templates in a build step using the map to something like

myElement.classList.remove('a', 'b', 'c', 'd').add('f', 'b', 'c', 'd')

or send the map out for your own function to use do something similar like

function applyAtomisedClasses(el, className, map) {
  el.classList = '';
  DOMTokenList.prototype.add.apply(el, map[className]);
}
applyAtomisedClasses(myElement, 'component-2', map) ;

but yeah, never actually got that far, so far, in practise 😄

kaelig commented 7 years ago

Thanks, I guess that makes sense!

sndrs commented 7 years ago

oop, just updated :)