Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dynamic styles mode #81

Closed
giuseppeg opened this issue Jan 18, 2017 · 12 comments
Closed

dynamic styles mode #81

giuseppeg opened this issue Jan 18, 2017 · 12 comments

Comments

@giuseppeg
Copy link
Collaborator

@rauchg and I discussed about possible ways to support dynamic styles i.e. allow props and other dynamic values which #80 doesn't.

From slack:

@g and I are contemplating a "dynamic" mode for styled-jsx where you pay a small penalty in performance but you can write stuff like

<style jsx dynamic>{`
 p {
   width: 100%;
   color: ${someProp ? 'red' : 'blue'}
 }
`}</style>

Basically this mean separate static css from dynamic one, render static css with the current system and inline the dynamic one i.e. each instance will get its own styles.

In order to do so we would need a CSS parser to split things up so that the example above becomes:

[
  {
    dynamic: false,
    css: 'p { width: 100% }'
  },
  {
    dynamic: true,
    css: 'p { color: ___styled-jsx-expression_placeholder_n_1___ }'
  }
]

which eventually compiles down to

<_JSXStyle styleId={woot} css={"p[data-jsx=\"woot\"] {width: 100%;}"} />
<_JSXStyle dynamic styleId={woot2} css={`p[data-jsx=\"woot2\"] {color: ${someProp ? 'red' : 'blue'}}`} />

When dynamic is set our component renders a regular style tag instead of returning null.

To parse the CSS we could write a tiny parser or use something like CSSTree since it seems to be fairly fast https://csstree.github.io/docs/validator.html (we could reuse this for the css transformation as well).

@giuseppeg
Copy link
Collaborator Author

@lahmatiy showed me how this could be done with CSSTree

https://gist.github.com/giuseppeg/2699094fc7f9f355e4289f6432266a83

@thysultan would it be as easy to do so with a new Stylis middleware?

@thysultan
Copy link
Contributor

thysultan commented Feb 20, 2017

@giuseppeg Could add to the existing middleware

var selector = '', collection = '', dynamic = {};

function middlware (context, value) {
	// selector
	if (context === 1) selector = value;

	// property, and some rule to only pull actual dynamic props
	if (context === 2) {
		if (dynamic[selector] === void 0) dynamic[selector] = [];

		dynamic[selector].push(value);

		// removes dynamic property
		return '';
	}

	// complete
	if (context === 6) {
		selector = '';
		collection = '';
                dynamic = {};

		for (var key in dynamic) collection += key + dynamic[key].join('') + '}';

		// now you have all dynamic rule sets stored in collection
		// selector { ...dynamic props } ...etc
	}
}

@giuseppeg
Copy link
Collaborator Author

@thysultan 👍 that's indeed really easy, thank you!

@giuseppeg
Copy link
Collaborator Author

render static css with the current system and inline the dynamic one i.e. each instance will get its own styles

@rauchg does this make sense? or can you think of a better way to optimize and avoid instance based styles?

Instead of allowing expressions like this ${ someProp ? 'red' : 'blue' } should we add support for functions only? ${ ({someProp}) => (someProp ? red : blue) }?

Also I think that we could auto detect dynamic styles if we want, do you still want to make it explicit?

@ianstormtaylor
Copy link

I would love to see support for the styled-components-like function interpolation. Ideally using (props, context) => to match React in other places. It feels like it enables some powerful things.

@corysimmons
Copy link

corysimmons commented Apr 28, 2017

Yeah, this is the only thing that makes me even slightly hesitant to commit to styled-jsx. Praying styled-jsx-postcss sticks around too.

Dynamic styles via props + PostCSS = 😍😍😍

@ianstormtaylor Thumb for the issue? Might bump it up in priority. :X

@giuseppeg
Copy link
Collaborator Author

Dynamic styles via props + PostCSS

This is going to be hard-ish to do since dynamic parts and in general expressions are replaced with placeholders. Also PostCSS runs at compile time not runtime.

@ianstormtaylor yup we are going to adopt a similar api (props, context) => {}. Are you using styled-jsx at Segment by any chance? If so maybe you guys can contribute? I could use some help.

About the priority this is going to happen after #148 (blocking) and a bunch of bug fixes and other minor features.

@corysimmons
Copy link

corysimmons commented Apr 29, 2017

This is going to be hard-ish to do since dynamic parts and in general expressions are replaced with placeholders.

Do you have any ideas for how to approach this? I experimented a bit with a similar concept (template literals in CSS). My approach ran the file in a VM, somehow ignored the CSS (I forget exactly), captured via regex/parsed the JS and inserted it back where it was expressed.

I'm sure it's possible and could help out with this in the future (swamped for the next week at least) if you're interested in a proof of concept.

There's actually a lot of interesting stuff revolving around template literals + VMs. I feel like I was circling something really cool but never finished it.

Also PostCSS runs at compile time not runtime.

styled-jsx doesn't compile? How can it SSR? I thought SSR meant it just outputted a bunch of static (compiled) stuff. 🤔

@giuseppeg
Copy link
Collaborator Author

Sure why not, I would take a look at what you've got.

To be clear the problem is that in order for Stylis1 to work we need to replace expressions with placeholders so that the parser gets a simple (valid) CSS string.

This means that any expression (including dynamic styles in the future) is not processed by Stylis – not a big deal with plain styled-jsx probably since Stylis only adds scope (and autoprefixes) but in the case of PostCSS it could be worse.

#182 could solve this problem I think.

[1] (the css processor which adds scoping by rewriting selectors to .foo[data-jsx="id"])

@rstacruz
Copy link
Contributor

rstacruz commented Sep 18, 2017

👍, the style attribute can't always account for things, like hover states and such.

/* doesn't work at the moment */
function ColorSwatch ({ color }) {
  return <div className='root'>
    <style jsx>{`
      .root { background: ${color}; }
      .root:hover { background: ${lighten(0.10, color)}; }
    `}</style>
  </div>
}

<ColorSwatch color='#aaddff' />

...defining a dynamic color for a hover state isn't possible in any way, at the moment.

@giuseppeg
Copy link
Collaborator Author

@rstacruz I've got a branch which adds support for dynamic styles almost ready :) I might publish a beta this week.

@giuseppeg
Copy link
Collaborator Author

Fixed by #288

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants