VirtualCSS / planning

Repository for planing and brainstorming on the VirtualCSS system
MIT License
66 stars 0 forks source link

Onboarding people #1

Closed jviereck closed 7 years ago

jviereck commented 9 years ago

Use this issue to express your interest to this project. I am than happy to give admin rights to anyone that wants to help out with this idea.

jviereck commented 9 years ago

I am interested (okay - you might have guess that - just for demo purpose ;) ).

vjeux commented 9 years ago

I'm interested in following the discussions but i've got a LOT of word to do on React Native right now, so don't expect much from my end :)

alexlande commented 9 years ago

Hey y'all, very interested in contributing. I work on Radium (https://github.com/FormidableLabs/radium).

jlongster commented 9 years ago

I'm not really involved in the CSS aspect of React in any way, but I do have one comment: I think the name "VirtualCSS" is somewhat confusing honestly. Comparing it to the virtual DOM approach is somewhat misleading because the virtual DOM is tightly tied to the idea of optimizing performance by diffing and only touching the DOM when possible. "VirtualCSS" makes me expect a similar technique, but maybe that's just me.

I had not heard of JSS before but it seems like that might a better place to have a bunch of discussion because they already have a working tool that people use in production: https://github.com/jsstyles/react-jss

jviereck commented 9 years ago

@jlongster, you made good points there - thanks for them!

I'm not really involved in the CSS aspect of React in any way, but I do have one comment: I think the name "VirtualCSS"

Without getting a too long naming debate started, I think VirtualCSS might be right as it is also about performance and optimising the style definitions. But I agree a different name might make more sense and I am happy for suggestions :)

I had not heard of JSS before but it seems like that might a better place to have a bunch of discussion

From my first look at JSS I get the feeling it fits better as a possible frontend that uses a VirtualCSS implementation for the low-level parts (CSS optimisation, mounting, ...) and a good idea is to see what APIs needs to be supported by a core library to get JSS supported.

Maybe let's wait before making such a move until I have made up my mind and wrote the promised reply to @gaearon about the differences between jss and my outlined idea in the blog post.

jlongster commented 9 years ago

@jviereck Sounds great, absolutely don't take my words too seriously. Often the initial connotations with a new name wears off pretty quickly, so if others like it, I think it's fine. I look forward to seeing what you flesh this out into.

robertknight commented 9 years ago

I'm interested. @jlongster that's a fair point about the VDOM and performance. I think of the 'virtual' aspect though as being more about decoupling the way you write your app from the environment it will run in - with performance being one of the benefits. The React README was recently changed to emphasize the abstraction over the diffing, but I'd guess that most people still think of it as being primarily about performance.

Off the top of my head, some of the different aspects to think about are:

Other major concerns that I haven't listed above?

SanderSpies commented 9 years ago

So React Style started last year, before all the CSS-in-JS madness really took off - there have already been a ton of discussions, and I've tried really hard to push everything into the right direction. Obviously, I'm very happy with the current direction and where it's going:

Issues we do have:

I think we nailed it pretty good, but yes I might have some bias ;-). So I'm totally up for discussions and exchange of ideas! Hope to steal some good ideas - and hope you guys do the same.

ghost commented 9 years ago

I like this idea a lot. It should solve a lot of issues most developers have with global CSS. I'm definitely on board, as the project's goals coincide with some things I'm working on. Components with compartmentalization is the future of the web, so it only makes sense that their styles are confined just the same and without any real performance hit.

Any actual code written for this yet? I have a few ideas that should allow changes to be made as usual via browsers' native CSS tools while updating the respective JSX, although a separate file would probably be less hacky.

robertknight commented 9 years ago

@SanderSpies - Adding support for the 'styles' attribute so that clients the styling can switch between or mix inline CSS and classes is useful but currently requires monkey-patching React.createElement(). Can you see a way to avoid this?

gaearon commented 9 years ago

cc @kof

alexlande commented 9 years ago

/cc @ianobermiller

ianobermiller commented 9 years ago

I've been experimenting with styling react components for a while now, and the current state of that is reflected in Cesium (see Button example usage), the core ideas of which I'm working on incorporating into Radium.

I'm not sure I buy all of your premises, I'd love if you could expand on (or defend) these:

Finally, and @robertknight has already brought this up, I think the two most difficult issues to solve when using classes are:

  1. When you combine styles (say a base style and an override based on prop, e.g. button + special), how do you make sure the second (special, in this case) has higher precedence?
  2. How do you handle overrides? After you create a generic component, you'll need to tweak the styles depending on where it is used (size, margin, positioning, etc). While this could be solved with diligence, like always using a wrapper and making the component fill its container, that'd be unnatural and hard to get right.

React Style handles this by creating these giant selectors, like .foo,.foo.foo1,.foo.foo1.foo2. I'm not sure this is any less "hacky" than going wholesale on inline-styles.

I've personally given up generating classes and actual CSS, and have instead turned my focus to emulating all the nice things we then forgo, like states (:hover, :focus, :active), media queries (which I've always found to be painful in CSS, but that's just my opinion), animations (Cesium actually uses an injected stylesheet for these, which I don't mind so much). This way, you solve the overriding/specificity issues once and for all, and always have the full power of JavaScript at your disposal (no difference between "static" and "dynamic" styles, for example).

-- Thanks for the mention, @alexlande!

boopathi commented 9 years ago

Nice idea. I'm interested.

SanderSpies commented 9 years ago

Pure inline styles would be great, but not everyone is a fan of that. I'm also not convinced it's the right solution at the moment. I don't think that merging style objects every render, especially when animating, is desirable. CSS classes are ideal for immutable styles, inline styles are great for mutable styles. Doing as little as possible during render is important. React Style also has some work to do here, pseudo classes branch has already improved a bit on this. We could probably do better though.

ghost commented 9 years ago

I think we need to support preprocessers. People have a need to reduce or change syntax or add features like autoprefix or use features from upcoming css spec via 4to3 cssnext.

robertknight commented 9 years ago

Aside from @SanderSpies comments about making render() as cheap as possible, I can also see other problems with emulating pseudo-selectors, media queries etc. in JS. For example, a recent talk at London React described the way that for the Tesco mobile shopping side, they render the site isomorphically and turn off JavaScript for old devices. CSS media queries could still work in such an environment, roll-your-own implementations in JS won't.

Additionally, the logic for implementing some pseudo-selectors is not as trivial as some people think and attempts to do the same with event listeners are likely to omit at least some of the native behaviors. I suspect it would be like the CSS equivalent of using hashbangs for URLs that lots of sites tried a few years ago.

joeybaker commented 9 years ago

I've been both excited and disappointed by the react's inline style approach. I'd love to be involved with this.

felixakiragreen commented 9 years ago

I'm very interested! I'm a designer & developer, been working with React for nearly a year and have recently been very involved in researching and using as many CSS in JS options and I can find.

jviereck commented 9 years ago

Hi there, thanks a lot for all the great responses and the time you put into writing them. I don't know exactly what's the best way to write a proper response (especially as I am getting a bit sick at the moment) - let’s hope the response is helpful to move the brainstorming further. In this response I try to explain why I want a system to be able to create a CSS file at the end and outline that to achieve this the way styles can be defined in an JS-To-CSS must be restricted. I give an outline what these restrictions could look like and I would love to hear back from you what you think about it.

It might be important to note that my experience with building web apps is biased towards large scale apps. In such environments you have different constraints compared to when building a smaller website for a client. For instance, using only inline styles like in Radium and relying on JS is a possible and in many cases a good practical solution. However, for large scale apps where you also have to cover the case of no-js-users not feasible. Therefore, any kind of solution that I am interested in building is about emitting a “static” CSS file at the end. Also the emitted CSS file should be optimised for file size. The part that takes care about creating this static CSS file and optmising it is what I referred to in my blog post as "VirtualCSS". The blog post talks about VirtualCSS in the context of React but if possible I would love to see it as a general library/framework that can be used by other frameworks and therefore works as a standalone component.

Because there is already a lot of CSS tooling I would love to see a soultion that enables to reuse the tools as much as it makes sense. Also, if there is a way to support preprocessors like SASS in any way, this is something I find worth considering given the big community and knowledge around these tools. For now I will use JS in my examples to declare the styles but I don't think this is a real limiting factor as converting a CSS-definition-string to the equivalent JS-object is doable. As of the feature set I think VirtualCSS should allow developers to write any kind of CSS as long as the CSS does not use cascading selectors or element selectors (like div { ...}).

To generate CSS from JS style definitions, the requirement is to scan the JS code and be able to determine all the style definitions and usages statically (at build time) without actually running the JS code. (This was not so much clear to me before @robertknight asked me about it). At first it seems this cannot be done by any of the existing CSS-In-JS libraries. For instance (using the react-style library here)

var StyleSheet = require('react-style');

var res = (function() {
  var i = math.random() * 100;
  while (i— > 0) {
    var ButtonStyles = StyleSheet.create({
      primary: {
        backgroundColor: 'rgb(0, 120, ' + i + ')',
        color: '#fff'
      }
    });
  }
  return ButtonStyles;
})();

module.exports = ButtonStyles;

makes it impossible to figure out the style definitions statically. But this looks like a completely made up case (on purpose :P ) and I expect most style definitions to look more along the lines of this:

var StyleSheet = require('react-style');

var ButtonStyles = StyleSheet.create({
  basic: {
    backgroundColor: 'rgb(0, 120, 120)',
    color: '#fff'
  }
}

module.exports = ButtonStyles;

That is the definition of a style happens at the top level of the module and there are no variables involved in the object passed to StyleSheet.create. This restriction makes the last listing relatively easy to analysis statically. Assuming there is then a “FancyButton” component that we want to implement, the implementation might look like this:

var StyleSheet = require('react-style');
var ButtonStyles = require('button-styles');

// Create a new set of styles that inherit from the previous ButtonStyles.
var FancyButtonStyles = StyleSheet.cascade(ButtonStyles, {
  basic: {
    fontWeight: 'bold'
  },
  'basic:hover': {
    fontSize: '20px'
  }
});

class FancyButton extends React.Component {
  render() {
    var colorStyle = this.props.colorStyle;
    return <div className={FancyButtonStyles.basic} style={colorStyle}>
              Lore Ipsum
            </div>
  }
}

This should also be possible to be analyised statically as well. Note that where the first idea with VirtualCSS was to be able to combine statically and dynamic changing styles this solution opts for a simpler setup: If the developer want to use dynamic changing styles, they have to do this by specifing inline styles themselfs. That doesn't look like a too bad limitation to me.

The restriction to disallow any values on the object passed to StyleSheet.create is very strong and to make it more flexible I think it should be possible to allow the use of constant variables that are defined at the top level of modules and pure function (hand waving definition: pure as in the sense they don't touch any other variables beside the ones passed to them by arguments or locally defined ones). With this extension the following becomes possible to write:

// ---
// In file: 'css-utils.js'

var createSimpleButton = function(baseColor) {
  return {
    backgroundColor: 'rgb(0, 120, 120)',
    color: baseColor
  };
}

module.exports = { createSimpleButton: createSimpleButton};

// ---
// In file 'button-styles.js'

var StyleSheet = require('react-style');
var cssUtils = rquire('css-utils');

var ButtonStyles = StyleSheet.create({
  basic: cssUtils.createSimpleButton('#fff');
}

module.exports = ButtonStyles;

This becomes a bit more complicated to analyse statically and involves running the cssUtils.createSimpleButton when building the final CSS file statically, but looks still doable to me.

As you can see, there are certain restrictions on how to define the style sheets and what to use as their arguments. The build system generating the CSS files (aka VirtualCSS) has to check if the developer plays by the restricted rules and if there is a violation raise an error to the user. Although I use react-style here in the examples, I could also imagine the analysis to work similar for jss, ts-style and rcss.

So the question to ask might be this: Do you think to build such a tool, that checks if the developer follows certain restrictions and if they do get the ability to generate an optimised CSS output is a good idea or do you think this is not useful in reality?

Please let me know what you think. Also not, that I emphasized in what I am interested in building. That doesn't mean there are other options and if you have different opinions, feel free to express them - I am more than happy to reconsider my decision making :)

alexlande commented 9 years ago

Have only started reading, but wanted to say one thing:

For instance, using only inline styles like in Radium and relying on JS is a possible and in many cases a good practical solution. However, for large scale apps where you also have to cover the case of no-js-users not feasible.

Supporting no-js + server-side rendering completely with Radium is 100% a goal/very important to me, and for that case something like VirtualCSS seems like the correct solution (to cover media queries, animations, and browser state pseudo-selectors like :hover specifically).

Have discussed that a little bit here: https://github.com/FormidableLabs/radium/issues/53

I don't expect Radium to start using a style tag with generated classes for everything, but for those use cases I think it's the best solution.

ghost commented 9 years ago

@jviereck Regarding the optimization of the CSS file for size, it might be worth it (in advance, for testing) to put together something that generates large semi-random CSS files, both optimized versions and unoptimized versions, and then compare the size after GZIP compression. It may not even be necessary (or worth the time and effort) to add a bunch of complex optimization logic if GZIP is able to reduce it to a comparable size.

jviereck commented 9 years ago

if GZIP is able to reduce it to a comparable size.

@timbur good thought, glad you bring it up!!!

Are you interested in doing such an experiment and report back you findings?

For the architecture of VirtualCSS is made up of three more-or-less independent parts anyway:

1) Collecting the style definitions from the JS files, check invariants hold, generate the CSS by running all the embedded functions 2) Optimize the found CSS by merging selector rules 3) Minimize the resulting CSS using traditional CSS minimize techniques 4) Place the generated CSS class names at the appropriate points in the JS file (that is replace the style definitions with the computed CSS class names)

For a first version the step 2) can definitely be left out first and then later depending out the research finding added later if it makes sense.

jviereck commented 9 years ago

Supporting no-js + server-side rendering completely with Radium is 100% a goal/very important to me, and for that case something like VirtualCSS seems like the correct solution (to cover media queries, animations, and browser state pseudo-selectors like :hover specifically).

Have discussed that a little bit here: https://github.com/FormidableLabs/radium/issues/53

@alexlande, thanks a lot for the clarification. I was under the impression that Radium sees JS as the solution to support media queries and pseudo classes.

The Issue you link looks very interesting and from what I've read goes into the same direction as what proposed above.

ghost commented 9 years ago

Also, @jviereck everything you mentioned in your last (long) comment is spot on. :)

Although, maybe I misunderstood what you were saying, but I'm not sure how I feel about the practicality of trying to analyze JS to extract the CSS.

I'm also not sure when I might have time to try out the optimization comparisons. I will most likely be contributing soon in some other form, however.

ghost commented 9 years ago

What about the idea to use template strings with a annotation about what css dialect is used.

var x = `@css4
  html: {
    color: #aaa;
  }
`;
var y = `@stylus
  html 
    color #aaa
`;

It should be possible to find such code in AST when Espree or similar libraries are used.

kof commented 9 years ago

Hi everybody, I am a maintainer of jss.

  1. I was breaking my head by trying to allow static css out of js. Its a step backwards. Once you say you support this you loose all the beautiful runtime capabilities. Its even more important in large scale apps. Its good to have just one way styles are rendered. Basically I do support this indirectly in jss. You just need to use .toString method serverside. But you can't use most of plugins then.
  2. jss vs. linline styles They are not competitors. I use them both for different purposes. JSS for everything static, inline for dynamic parts. Sometimes I take declarations from jss, modify them and use inline.
  3. JSS tries to map 1:1 to css syntax but in json. I am not sure trying to support regular css but then to convert it to json during compilation will lead to an understandable code.
boopathi commented 9 years ago

The major trade-offs I experienced was between,

And

@fibric :+1: Relay is using template strings for specifying GraphQL queries as seen here - https://facebook.github.io/react/blog/2015/03/19/building-the-facebook-news-feed-with-relay.html#whats-the-ltstorygt . So I guess, if we have something like this,

class Component extends React.Component { /*...*/ }
Stylesheet.create(Component, {
    styles: scss`
       /* ... */
    `
});

with tag specifying the css dialect, it could either be seen as an obstruction to using with Relay, or an advantage only. I'm not able to speculate about it. Also, as @robertknight mentioned, it might require monkey-patching React.createElement or an abstraction over the component definition which might lead to StylesPlusReact.createClass.

SanderSpies commented 9 years ago

@boopathi - React Style styling is not connected to a component. Styles can be defined anywhere, but should preferably be done at the module level.

kof commented 9 years ago

On the other hand if there is a strong issue with writing css in json. There might be a less preprocessor which will parse from components (like in your example) and converts them to json syntax which can be then used by jss.

jviereck commented 9 years ago

@fibric, the idea to use template strings is a good one and supporting different preprocesors would be nice. However, take a look at this example:

var x = `@css4
  .button {
    color: #aaa;
  }`;

class FooComponent extends React.Component {
  render() {
    return <div className={x.button} style={colorStyle}>Lore Ipsum</div> // (1)
  }
}

If the script is extracted statically and a CSS is created this could work, but if the file is run without transformations, then it won't because the template string assigned to x will still be a string. A better way might be to write:

var x = StyleSheet.defineCSS4(`...`);
...

What do you think?


@kof, thanks a lot for sharing your expertise. Would you mind to comment more on this comment:

Once you say you support this you loose all the beautiful runtime capabilities. Its even more important in large scale apps. Its good to have just one way styles are rendered.

Especially, what do you mean by "beautiful runtime capabilities"? Can you give an example?

jviereck commented 9 years ago

PS: From Saturday till next Sunday (12. April) I will be on holidays and leaving my internet back home - so I won't be able to reply or watch the discussion during that time.

kof commented 9 years ago

@jviereck I mean all the things we can ONLY do at runtime. E.g. proper vendor detection results in less css (we don't need to generate all vendor prefixes). You can do env. specific calculations for e.g. screen sizes, densities etc in a very different way than what you can do with pure css. There can be layout systems done on top of jss ... possibilities are endless .... I don't see a lot of reasons to write more or less static css vs. js for applications.

kof commented 9 years ago

What I mean by loosing those capabilities - you just can't allow both, static and dynamic in one fluent way without to introduce different issues and limitations in different rendering modes ... and basically making your daly job 2x harder. (I am about isomorphic rendering here ...)

ghost commented 9 years ago

@jviereck maybe we can have 2 ways of dealing with css. Ahead of time mode and just in time mode. In AoT mode we can strip out and replace annotated template strings, using any css preprocessor. But can't be smart enough to produce optimized css. In JiT mode we need a runtime but it allows to dynamically auto prefix. But not sure if JiT mode can support any css flavors but css3.

I had the idea with template strings just because, that is what I've understood from this post.

jviereck commented 9 years ago

@kof you are right with the points you made and I agree with them. Especially I think it is important to enforce as much as possible in the dynamic setting the behavior to agree with the static one. E.g. defining StyleSheet functions inside of an extra function wrapper, such that checking if the function declaration is pure becomes possible at runtime (by doing fn.toString() and then check if it pure):

// ---
// In file: 'css-utils.js'
var StyleSheet = require('react-style');

// The wrapper StyleSheet.fn is new, such that the purity aspect of the function can also
// be checked at runtime.
var createSimpleButton = StyleSheet.fn(function(baseColor) {
  return {
    backgroundColor: 'rgb(0, 120, 120)',
    color: baseColor
  };
});

module.exports = { createSimpleButton: createSimpleButton};

// ---
// In file 'button-styles.js'

var StyleSheet = require('react-style');
var cssUtils = rquire('css-utils');

var ButtonStyles = StyleSheet.create({
  basic: cssUtils.createSimpleButton('#fff');
}

module.exports = ButtonStyles;

If these points are a problem in the end, I don't know. For now I think there is more value in building a stripped down version of VirtualCSS and see if it is practical useful instead of going into too detailed analysis what the pro and cons of the system might be, although not all details of the system are clear yet either :)

and basically making your daly job 2x harder. (I am about isomorphic rendering here ...)

Hopefully we can avoid this extra factor of overhead and think about a design which (when followed) results in 0x harder daily work :)

joeybaker commented 9 years ago

@jviereck any thoughts on where to go next?

jviereck commented 9 years ago

@jviereck any thoughts on where to go next?

Yes, the holidays were good time to get a clear picture here. As next step I would try to implement a very rough version of this idea using WebPack, that emits a static CSS file. The rough design is finished in my head but catching up after the holidays and training for my marathon on Sunday keeps me from writing the few lines of code this idea requires to become to life (I really hope "a few lines" will be enough to get the basic idea).

The other alternative would be to outline the pictures in my head but I feel it is better to create something concrete at this point and use it for further discussions.

How does this sound?

joeybaker commented 9 years ago

Yea… a proof of concept would be great! We can continue this debate via PRs :)

ghost commented 9 years ago

@jviereck I'm not 100% clear on what exactly the plan is here. Is the idea to build onto something like react-style? Or build something from the ground up?

jviereck commented 9 years ago

With commit e0a0d21428923b I've changed the license from BSD to MIT. The reason is that I want to import code from react-style into the repo and the library uses MIT license. Also, I've noticed many other css-in-js libraries use MIT license as well.

Does anyone see a problem with this change? Please let me know :)

jviereck commented 9 years ago

Just did a little bit of coding and created a first PR: https://github.com/VirtualCSS/planning/pull/4

The part that generates the static CSS is not in there yet but the README.md at 1 lists already a few changes compared to other css-in-js implementations (I use react-styles as reference, but I guess other implementations are similar).

This WIP is not completely finished yet - as mentioned in the README.md file, I don't see yet how to support class inheritance like:

<Button styles={[ButtonStyles.error, ApplicationStyles.childStyle]}>

where the user defines custom styles on a button yet.

Q: Is sharing such WIP useful for people following this issue?

jviereck commented 9 years ago

If you have any input to the current WIP in #4, please let me know about them :)

SanderSpies commented 9 years ago

I hope you didn't rewrite the React Style syntax example into the React Style simple example: https://github.com/js-next/react-style/tree/master/examples/0.13

jviereck commented 9 years ago

@jviereck I'm not 100% clear on what exactly the plan is here. Is the idea to build onto something like react-style? Or build something from the ground up?

From my perspective, the plan is to build onto something like react-style as much as possible. A "VirtualCSS" implementation (whatever that will be in the end) should provide the minimal required API to other libraries like react-styles, such that these can emit static css in the end.

What is your/other peoples plan?

jviereck commented 9 years ago

I hope you didn't rewrite the React Style syntax example into the React Style simple example:

No, I didn't - the link in the readme was just wrong :) Sorry for the confusion. (I fixed the link in PR readme.)

ghost commented 9 years ago

From my perspective, the plan is to build onto something like react-style as much as possible. A "VirtualCSS" implementation (whatever that will be in the end) should provide the minimal required API to other libraries like react-styles, such that these can emit static css in the end.

Thanks for clarifying. This sounds good. It's what I was hoping for. :)

What is your/other peoples plan?

Right now I'm personally learning as much as I can about the current state of things so that I'll know which areas need improvement and which areas are already pretty solid. I think I have a pretty firm grasp on everything now, so after I finish a couple of other things I'm working on (hopefully by the end of the day), putting together what I believe would be an optimal prototype for modular (virtual) CSS will be moved to the top of my todo list.

That said, I was checking out that list of related projects, and I think this is my favorite so far (albeit 11 days old): https://github.com/ericwooley/react-in-style

It makes the most sense to me, though I'd design things very slightly differently. I'll probably use it as the base of my prototype and combine it with certain aspects of react-style. Thoughts?

dmitry commented 9 years ago

@timbur I think react-in-style approach is nice, but I don't like that it use cascades. Why not to use BEM style classes? The reason it's faster and more predictable. Also as I understand it use custom tags, and it's not really a good idea to do so.

ghost commented 9 years ago

@dmitry I am actually planning on using BEM style classes, although I didn't know that's what it's called! See my questions at https://github.com/js-next/react-style/issues/108#issuecomment-92885761. They're probably pretty newbie questions so maybe someone here can answer them for me.

The main things I wanted to take from react-in-style was the use of certain SASS functionality and pseudo classes. I see react-style has pseudo classes planned for its next release, but perhaps someone (@SanderSpies?) could provide an example for how it's expected to work.

SanderSpies commented 9 years ago

@timbur - we're going for the following syntax:

StyleSheet.create({
   foo: {
      backgroundColor: 'blue',
      ':hover': {
         backgroundColor: 'orange'
      }
   }
});

Most people seem to prefer this above other ways of defining pseudo classes.

Note that by default we only will support a subset of pseudo classes - as we want to maintain optimal support for both inline styles and compile to CSS.

Also moving to JS has an advantage - removing the extra layer of SASS or LESS. It's now just JavaScript which you are using already anyway. Adding SASS or LESS like syntax seems to me like going into the wrong direction.

As for using certain functions of LESS, if you go here: https://github.com/less/less.js/tree/master/lib/less/functions there's a whole bunch of functions that you can extract and use with any CSS-in-JS solution today (might need some changes here and there though).