vercel / styled-jsx

Full CSS support for JSX without compromises
http://npmjs.com/styled-jsx
MIT License
7.71k stars 261 forks source link

Support auto generation of RTL styles #586

Open HosseinAgha opened 5 years ago

HosseinAgha commented 5 years ago

Do you want to request a feature or report a bug?

feature

What is the current behavior?

There is no official way to auto generate right-to-left css.
What I mean by auto generation is the following:

// before generating rtl styles
.container {
   margin-right: 6px;
   padding-right: 6px;
   background: red;
   /*rtl:ignore*/
   text-align: left;
}

// after generating rtl styles
[dir=rtl] .container {
  margin-left: 6px;
  padding-left: 6px;
} 

[dir=ltr] .container {
  margin-right: 6px;
  padding-right: 6px;
} 

.container {
  background: red;
  text-align: left;
}

PostCSS has postcss-rtl plugin which uses very famous, super mature rtlcss library.

I tried to use it with styled-jsx-plugin-postcss and got some interesting results.

  1. Global Static Styles: Works fine (changing the html[dir] instantly changes the direction)

  2. Scoped Static Styles: Works fine using the following trick using the postcss-rtl's addPrefixToSelector function I changed the [dir=x] prefixes to :global([dir=x])

  3. Global and Scoped Dynamic Styles: Duplicates the styles for RTL direction

the code:

{/* props.border is '12px solid red' */}
<style jsx global>{`
    .container {
      border-left: ${props.border};
    }
`}</style>

the generated dynamic style:

[dir=ltr] .container {
   border-left: 12px solid red;
}

// duplicate `12px solid red`
[dir=rtl] .container {
   border-right: 12px solid red12px solid red;
}
  1. /*rtl:ignore*/ comments does not work I guess we remove comments before passing the strings to babel plugins.

Solution

I think with a little effort we can add one of the best auto RTL css-in-js solutions using the precompiled nature of styled-jsx.
The styled-components RTL support is mostly based on stylis-rtl which is not as mature as rtlcss and definitely has a long way to cover all RTL css generation edge cases.

I'd be happy to contribute if you guide me on what do you think the issue is.

giuseppeg commented 5 years ago

The problem with styled-jsx plugins is that the run on pre-evaluated styles and at compile time. Interpolations are replaced with placeholders and then the string is passed to the plugins, this is why the result is broken in the 3rd case.

I doubt there is a good solution to be honest, but I am happy to explore any idea you might have to solve this issue!

As for 4. probably comments are stripped out. I need to take a look.

HosseinAgha commented 5 years ago

My idea is to generate the same placeholder for [dir=rtl] styles so the runtime replaces the placeholder with the correct style. My main question is why it works with the generated [dir=ltr] (prefix) style? Shouldn't they both not work? I guess you look for border-left: _placeholder_ to replace the Interpolations instead of only replacing all _placeholder_s? But if it is true why we get duplicate properties for [dir=rtl] (something is getting replaced here 😄 ).

It would be awesome if you had an option to disable comments removal.

Thanks :)

giuseppeg commented 5 years ago

Maybe the issue is with how we restore the interpolations after styles have been through the plugins and Stylis (the css preprocessor that scopes selectors). https://github.com/zeit/styled-jsx/blob/d920a289aed3b17acae07bc09dc071be83d99b3e/src/_utils.js#L392

Maybe you can add a failing test and try to fix the function (if it is buggy)?

That being said I will leave to user land to generate RTL styles as not everybody needs them.

J-env commented 3 years ago

image

@HosseinAgha @giuseppeg

Is this demo feasible? Only handle Arabic