DEV Community

stereobooster
stereobooster

Posted on • Originally published at stereobooster.com on

Styling components

style prop

<div style={{ color: "red" }} />
Enter fullscreen mode Exit fullscreen mode

style prop it the most basic CSS-in-JS solution. But it doesn't support:

styled function

const Box = styled.div`
  color: red;
`;
<Box />;
Enter fullscreen mode Exit fullscreen mode

or

const Box = styled('div', {
 color: "red"
})
<Box />
Enter fullscreen mode Exit fullscreen mode

There are a lot of implementation. Some examples:

Styles-as-props

aka: Style Props. Styles are exposed as props:

<Box display="flex" position="absolute" top={0} />
Enter fullscreen mode Exit fullscreen mode

There are a lot of implementation. Some examples:

Often combined with polymorphic as (is, component) prop and data prop.

<Box as="span" data={{ testid: "customIdentifier" }} />
Enter fullscreen mode Exit fullscreen mode

Responsive styles-as-props

Variation of styles-as-props to support size media-queries:

<Box padding={{ mobile: "small", tablet: "medium" }} />
Enter fullscreen mode Exit fullscreen mode

There are a lot of implementation. Some examples:

sx prop

aka: css-prop. Can be considered as a variation of style prop:

<Box sx={{ border: 1 }} />
Enter fullscreen mode Exit fullscreen mode

But it resolves issues (depending on implementation) with:

  • mediaqueries
  • pseudo-classes
  • pseudo-elements
  • theming
<Box
  sx={[
    // theme prop
    color: 'primary.main',
    // or
    (theme) => ({ color: theme.palette.primary.main }),
    // responsive prop
    width: { xs: 100, sm: 200 },
    // or
    width: [100, 200, 300],
    // pseudo-classes
    '&:hover': { color: 'red' }
  ]}
/>
Enter fullscreen mode Exit fullscreen mode

There are a lot of implementation. Some examples:

Variants

aka: recipes. I call it "variants" for the lack of better name. Maybe "design system driven props"? It can look, for example, like this:

const Button = styled("button", {
  defaultVariants: {
    color: "accent",
    size: "medium",
  },
  variants: {
    color: {
      neutral: { background: "whitesmoke" },
      brand: { background: "blueviolet" },
      accent: { background: "slateblue" },
    },
    size: {
      small: { padding: 12 },
      medium: { padding: 16 },
      large: { padding: 24 },
    },
  },
});
Enter fullscreen mode Exit fullscreen mode

There are a lot of implementation. Some examples:

Variants are often go hand in hand with theme (or design tokens):

export const { styled, css } = createStitches({
  theme: {
    colors: {
      gray500: "hsl(206,10%,76%)",
      blue500: "hsl(206,100%,50%)",
      purple500: "hsl(252,78%,60%)",
      green500: "hsl(148,60%,60%)",
      red500: "hsl(352,100%,62%)",
    },
    space: {
      1: "5px",
      2: "10px",
      3: "15px",
    },
  },
});
Enter fullscreen mode Exit fullscreen mode

You can define aliases for tokens (to use semantic names):

colors: {
  background: "$gray500";
}
Enter fullscreen mode Exit fullscreen mode

You can define a different theme, for example to support dark mode.

Tokens can be "smartly" mapped, e.g. system knows where (in which section of the theme) to lookup token depending on the property:

const Button = styled("button", {
  color: "$purple500",
});
Enter fullscreen mode Exit fullscreen mode

You can define aliases for styles-as-props:

export const { styled, css } = createStitches({
  utils: {
    // Abbreviated margin properties
    m: (value) => ({
      margin: value,
    }),
    mx: (value) => ({
      marginLeft: value,
      marginRight: value,
    }),
    linearGradient: (value) => ({
      backgroundImage: `linear-gradient(${value})`,
    }),
  },
});
Enter fullscreen mode Exit fullscreen mode

See:

Bonus: comparison to Tailwind

background

styles-as-props:

// with theme
<Box backGround={(theme) => theme.colors.slate100} />
// with smart mapping
<Box backGround="$slate100" />
// with alias and smart mapping
<Box bg="$slate100" />
// with dark mode mediaqueries
<Box bg={{ light: "$slate100", dark: "$slate600" }} />
Enter fullscreen mode Exit fullscreen mode

And dark mode can be implemented at theme level.

sx prop:

<Box sx={(theme) => ({ backGround: theme.colors.slate100 })} />
Enter fullscreen mode Exit fullscreen mode

Tailwind:

<Box className="bg-slate-100 dark:bg-slate-800" />
Enter fullscreen mode Exit fullscreen mode

padding

styles-as-props:

<Box p={{ sm: 8, md: 0 }} />
Enter fullscreen mode Exit fullscreen mode

sx prop:

<Box sx={{ padding: { sm: 8, md: 0 } }} />
Enter fullscreen mode Exit fullscreen mode

Tailwind:

<Box className="p-8 md:p-0" />
Enter fullscreen mode Exit fullscreen mode

flex

styles-as-props:

<Box flex={{ md: true }} />
Enter fullscreen mode Exit fullscreen mode

sx prop:

<Box sx={{ flex: { md: true } }} />
Enter fullscreen mode Exit fullscreen mode

Tailwind:

<Box className="md:flex" />
Enter fullscreen mode Exit fullscreen mode

Summary

To me they look the same, except that Tailwind doesn't require runtime and works with server components ¯\_(ツ)_/¯. Only variants look different.

Related:

CSSModules-vibe

const styles = StyleSheet.create({ root: { flex: 1, opacity: 0 } });
const Component = () => <View style={styles.root} />;
Enter fullscreen mode Exit fullscreen mode
export default () => (
  <div>
    <p>only this paragraph will get the style :)</p>
    <style jsx>{`
      p {
        color: red;
      }
    `}</style>
  </div>
);
Enter fullscreen mode Exit fullscreen mode
const useStyles = createUseStyles({
  myButton: {
    color: "green",
  },
  myLabel: {
    fontStyle: "italic",
  },
});

const Button = () => {
  const classes = useStyles();
  return (
    <button className={classes.myButton}>
      <span className={classes.myLabel}></span>
    </button>
  );
};
Enter fullscreen mode Exit fullscreen mode

Top comments (0)