andywer / react-usestyles

🖍 Style components using React hooks. Abstracts the styling library away.
MIT License
87 stars 2 forks source link

Composing styles #4

Open andywer opened 5 years ago

andywer commented 5 years ago

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 commented 5 years ago

@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

tomByrer commented 5 years ago

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.

andywer commented 5 years ago

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>
}
tomByrer commented 5 years ago

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

andywer commented 5 years ago

😄

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 🙂