threepointone/glamor

How would you declare a 'mixin' using glamor?

Closed this issue · 5 comments

Say I have a project with a lot of different (Sass) mixins. Each mixin serves one or more of these purposes:

  1. limit the options for certain css values
  2. use mappings to turn a 'simple parameter' into a set of css properties that belong together
  3. standardize a certain look and feel across components

Some of these mixins might look something like this:

@mixin mixinWithParameter ($size) {
  font-size: $size;
  line-height: $size * 2;  
}
@mixin mixinWithoutParameter () {
  font-family: sans-serif;  
}
some-element {
  @include mixinWithParameter(16px);
  @include mixinWithoutParameter();
  color: blue;
}

How would you use style() and/or merge() to declare and use these mixins as inline styles?

How would you ensure the least amount of duplicate rules are generated?

this should work.

let mixinWithParameter = size => ({ 
  fontSize: size, 
  lineHeight: size * 2 
})

let mixinWithoutParameter = {  // don't need function here!
  fontFamily: 'sans-serif' 
} 

let rule = merge(mixinWithParameter(16), mixinWithoutParameter, { color: 'blue' })

// merge accepts regular objects, so we can avoid allocating intermediate css rules. win!

the above will result in just one rule being added to the styleSheet.

Thanks for reacting so quickly. :)
Yeah that implementation seems very similar to what I've been experimenting with. But the problem arises when I would like to reuse those mixins between different components.

If I create a 'rule' like in your example for every component that uses those 'mixins', Glamor will add a separate rule to the styleSheet for each invocation, resulting in a lot of duplicate styles.

Especially for mixins like the 'mixinWithoutParameter', it would be very nice to have those properties 'cached' somehow to prevent duplicating them.
And for mixins with parameters, some sort of input memoization could also create a set of 'cached' rules minimizing duplicate styles as well. But that's probably trickier to implement and would require a 'mixin' method from Glamor so it can do the input memoization.
Or am I thinking in the wrong direction here?

I was kind of hoping something like this would prevent duplicated styles:

const mixinWithoutParameter = style({ fontFamily: sans-serif });

let ruleForComponentA = merge(mixinWithoutParameter, { color: blue });
let ruleForComponentB = merge(mixinWithoutParameter, { color: red });

Perhaps merge could return an array of classes / data properties, reusing existing classes when passed as arguments.
My assumption herein is that 'mixinWithoutParameter' above is a class name / data property [the return value of style()], which could be detected by merge() and reused instead of regenerating a new class which duplicates all of the css properties from the merged sources.

But maybe this kind of reuse functionality should just be implemented 'manually' using regular css classes instead of inline styles? And importing/bundling those classes using webpack?

Glamor consciously avoids this! I'd argue that trying to do that introduces way too much complexity and bugginess, by way of precedence order etc.

And before you ask, this turns or to be fine for performance. I test with 10s of 1000s of rules, but do point out if you see any problems. It gzips well, and gets better when combined with our SSR helpers.

Thanks for clarifying your/glamor's stance on the subject. I think you're probably right about the complexity and potential bugginess of making that work. I'm glad to hear you've done exhaustive performance testing.
And I agree this kind of repetition gzips well so the 'extra file size' shouldn't be a problem either.

I'll continue working on my project (a proprietary UI library in React for products my company makes) using the strategy of my last example.

Let me know how it goes, and I'm always open to feedback/requests!