react-usestyles icon indicating copy to clipboard operation
react-usestyles copied to clipboard

Composing styles

Open andywer opened this issue 7 years ago • 5 comments

How will composablity be handled? Seems there are different APIs for combining styles: JSS, emotion, styled-system (used in Rebass), & Vudu (newer, but interesting!).

(Raised by @tomByrer)

andywer avatar Nov 13 '18 05:11 andywer

@tomByrer Btw, it is already possible to compose styles easily in a naive way, but it will lead to some duplicated CSS (not a huge deal, but not ideal either).

Like this:

const basePadding = (props) => ({
  paddingLeft: theme => theme.spacing.unit * (props.padding || 1),
  paddingRight: theme => theme.spacing.unit * (props.padding || 1)
})

function ComponentOne (props) {
  const className = useStyle({
    ...basePadding(props),
    color: "white"
  }, [props.padding])
  return <div className={className}>{props.children}</div>
}

function ComponentTwo (props) {
  const className = useStyle({
    ...basePadding(props),
    color: "black"
  }, [props.padding])
  return <div className={className}>{props.children}</div>
}

Edit: Duplicated CSS in this context = Classes with duplicated style rules, instead of extracting those rules into a new class and making both components use their own class plus the shared one

andywer avatar Nov 13 '18 06:11 andywer

it is already possible to compose styles easily in a naive way

Ah, I didn't think of destructuring, cheers. Could use a macro to rewrite a cleaner syntax into this, then post process use a CSS cleaner.

tomByrer avatar Nov 13 '18 07:11 tomByrer

I am looking into simple ways to provide a CSS template literal tag. If that becomes a thing, that might already be the nicer syntax you are looking for?

The example above would look something like that:

const basePadding = (props) => css`
  paddingLeft: ${theme => theme.spacing.unit * (props.padding || 1)};
  paddingRight: ${theme => theme.spacing.unit * (props.padding || 1)};
`

function ComponentOne (props) {
  const className = useStyle(
    css`
      &: ${basePadding(props)};
      color: "white";
    `,
    [props.padding]
  )
  return <div className={className}>{props.children}</div>
}

I reformatted the code a little bit, so the original example might already look a bit friendlier when done like this:

function ComponentOne (props) {
  const className = useStyle(
    {
      "&": basePadding(props),
      color: "white"
    },
    [props.padding]
  )
  return <div className={className}>{props.children}</div>
}

andywer avatar Nov 13 '18 07:11 andywer

I was imagining even simpler:

paddingLeft: theme.spacing.unit * (props.padding || 1);

(or maybe use CSS's calc(), but this would be less typing) Yes it is not real JS at this point; that's what the AST will output. Use case: designers trying to learn ReactJS, so the code they're most likely to touch is simple as possible. I see the poor folks' minds being blown at the FramerX forums

tomByrer avatar Nov 13 '18 16:11 tomByrer

😄

Fair enough. The reason why the API demands you to use a function value for dynamic props is just for performance reasons: This way we can split static from dynamic styles in the hook which allows for a bunch of different optimizations.

If you would put the value in there directly, we would need to treat all style rules as potentially dynamic and would always need to check for changed values. Would be interesting to see what the actual performance impact would look like, though 🙂

andywer avatar Nov 13 '18 16:11 andywer