callstack / linaria

Zero-runtime CSS in JS library
https://linaria.dev
MIT License
11.34k stars 414 forks source link

Styled System support #465

Open anzorb opened 4 years ago

anzorb commented 4 years ago

Describe the feature

https://github.com/styled-system/styled-system

Styled System is getting a lot of attention, and currently works with Emotion and Styled Components. Something to keep in mind for future dev.

import { styled } from "linaria/react";
import space from "@styled-system/space";

export const Container = styled.div`
  ${space}
  display: flex;
`;

<Container p={2}></Container>

This currently results in --c155nh7h-0: [object Object]; on the element (I am guessing ${space} isn't being executed as a function, like it does in Emotion/SC

brandonkal commented 4 years ago

The reason this doesn't work is because styled system interpolates both the declaration name and the value. With linaria, you should only interpolate the value.

Styled System adds a lot of complexity to your components. If that is something that you want, you are better off pairing it with emotion, where everything is handled at runtime.

okonet commented 4 years ago

@brandonkal genuinely wondering how hard would it be to implement support for it in Linaria. I've been toying with this idea for quite some time and wondering if Linaria could be extended to support it. Since styled-system has a spec, I guess that change could only accommodate the spec?

brandonkal commented 4 years ago

@okonet The Styled System approach encourages all properties to be interpolated at which point the benefits of something like linaria start to become less attractive. If you manage to get it working, I'll accept a PR on my fork.

okonet commented 4 years ago

So the idea I had in mind was to get the permutations of all tokens from the theme since it's a finite number so I think it should work as long as it's just styled-system props without some custom interpolations, right?

jayu commented 4 years ago

Supporting this in linaria would be tricky. It would end up with support for runtime assigned styles since we cannot statically generate the output for each possible value of props. It's somehow related to #409, #234 and #244

mauleb commented 4 years ago

@jayu

Would it be possible to support the responsive props however?

in the example from your readme:

import { styled } from 'linaria/react';
import { families, sizes } from './fonts';

// Write your styles in `styled` tag
const Title = styled.h1`
  font-family: ${families.serif};
`;

const Container = styled.div`
  font-size: ${sizes.medium}px;
  color: ${props => props.color};
  border: 1px solid red;

  &:hover {
    border-color: blue;
  }

  ${Title} {
    margin-bottom: 24px;
  }
`;

// Then use the resulting component
<Container color="#333">
  <Title>Hello world</Title>
</Container>;

It is explained that behind the scenes color is handled as a CSS variable. If breakpoints were declared in some manner for linaria, would it be possible to support something like this:

const Container = styled.div`
   padding: ${props => props.padding};
`;

<Container padding={[ '4px', '10px' ]}>
  <Title>Hello world</Title>
</Container>;

which still relies on CSS variables behind the scenes?

lunelson commented 4 years ago

I think something effectively similar to styled-system could be built on linaria, but styled-system (whether using styled-components or emotion) depends on passing the theme via React Context, so that the style-prop functions have access to it. To do this in linaria would require fundamentally retooling the way the style-prop functions are generated AFAIK

mauleb commented 4 years ago

@lunelson Yea, the theme system would require context like you said, but supporting the responsive props themeless seems theoretically possible no?

mauleb commented 4 years ago

I think the one implicit "themeing" required would be the breakpoints. This could be declared globally in some manner for linaria, but it could also instead be handled by deviating from the shorthand and requiring the breakpoints to be included

<Container padding={{ '767px': '4px', '1023px': '10px' }} />

In either case, behind the scenes a css var could be used normally with the css var itself being changed for the different screen sizes.

lunelson commented 4 years ago

@mauleb yes but you could also make your theme available in a different way, if you generated the style prop functions within the same closure where you process the theme, and returned them together. But this is of course rebuilding styled-system in a different way

function setupTheme(userTheme) {
  const baseTheme = {
    breakpoints: {
      s: 20,
      m: 40,
      l: 60,
    }
  }
  const theme = _.mergeDeep({}, userTheme, baseTheme);
  return {
    theme,
    padding(arg) {
      /* set padding based on arg relative to theme.breakpoints */
    }
  }
}

const { theme, padding } = setupTheme({});

const Box = styled.div`
  ${props => props.p && padding(props.p)}
`;
uptonking commented 3 years ago

styled-system provides a future direction for react components api design,
because it allows developers to pass styling property and value directly to the Component instead of passing traditional css properties inline to Component via style object.

I think it's on the right way to standardize components api, because google flutter ui widgets and apple swiftui view is providing their ui apis this way.

I really want linaria to support styled-system like api for react components, if it's difficult to integrate with styled-system directly.

One more thing, styled-system is based on a predefined theme object with finite and similar number of props according to theme specification, just like

// simple theme.js
export default {
  colors: {
    black: '#000e1a',
    white: '#fff',
    blue: '#007ce0',
    navy: '#004175',
  },
}

// complex theme.js
export const bulmaTheme = {
  colors,
  fonts,
  fontSizes,
  fontWeights,
  space,
  styles,
}

some examples for theme.js: styled-system site theme.js, theme-ui bulma theme.js.

I have used styled-components and styled-system, but I'm unfamiliar with the source code.
Again, I really want u to integrate styled-system with linaria, or reimplement a styled-system like api for linaria.

turadg commented 3 years ago

reimplement a styled-system like api for linaria.

@uptonking what are your requirements for such a system? We've been theming with Linaria at my company using CSS custom props. It looks like everything in https://github.com/styled-system/styled-system/blob/master/docs/src/gatsby-plugin-theme-ui/index.js could be defined that way. Perhaps with some sugar for namespaces. (E.g. flattening nested JSON props like JSON {styles {h5: {a { textDecoration: 'none' --> prop --styles.h5.a.textDecoration: 'none')

uptonking commented 3 years ago

@turadg
The current react component api and usage built with linaria is like this:

const Container = styled.div`
  font-size: ${sizes.medium}px;
  color: ${props => props.color};
  border: 1px solid red;
`;

// Then use the resulting component
// here color can only be valid css values
<Container color="#333"></Container>;

But I want the api and usage like this:

// ${space}, such style functions may be reimplemented in linaria-core or linaria-extension
// here is just copied from styled-system, but can be implemented in some other form
const Container = styled.div`
   border: 1px solid red;
   ${color}
   ${space}
   ${layout} 
`

// Then use the component
// here we can directly pick values from colors props in theme.js, even use alias
<Container color="primary" p="10px"></Container>;

// this structure is similar to flutter/swiftui api:
React.createElement(
  Container,
  {color: 'blue', padding: '10px'},
  children:{
    //...
    }
)

I think it's on the way to standardize components api. It saves my repeated work to define the Color,Padding type for ContainerType again and again.

Of course I can write many props.styles.h5.a.textDecoration to pick values, but it's boring and repeated.

I hope linaria can handle the repeated work for me, just like styled-components+styled-system, but styled-components has runtime cost.

I really want linaria to take in these two benefits.

turadg commented 3 years ago

I see that you want to indicate a color by name instead of by literal. Would this work for you?

const Container = styled.div`
  font-size: ${sizes.medium}px;
  color: var(--color-${props => props.colorName});
  border: 1px solid red;
`;

<Container colorName="primary" p="10px"></Container>;

Then instead of storing your colors in themes.js you'd just have to define a CSS custom property, --color-primary. (I'm not sure the above works but I'm exploring what might.)

FrancisVega commented 3 years ago

I'm trying to achieve a similar API of styled-system, the responsive props, and especially the <Box/> primitive component.

I know the build time libs like linaria or treat are not in the same league of run time ones.

Concerning treat, the people who develop it also developed a design system using treat (who is similar to linaria) and it has responsive props, Box, etc..

I found in the source code several files related to styled props, Box and so, but I admit, I get lost in the typescript thing :S

Could we get some cues from that? Is treat so different than linaria and we don't have anything to do?

Box: https://github.com/seek-oss/braid-design-system/tree/master/lib/components/Box

Utils for responsive props: https://github.com/seek-oss/braid-design-system/tree/master/lib/utils