Skip to content
This repository has been archived by the owner on Sep 21, 2022. It is now read-only.

Server-side: Media queries and pseudo classes for inline styles #8

Open
sophiebits opened this issue May 26, 2014 · 28 comments
Open

Server-side: Media queries and pseudo classes for inline styles #8

sophiebits opened this issue May 26, 2014 · 28 comments

Comments

@sophiebits
Copy link
Member

https://github.com/reactjs/react-future/blob/f8808dd4cd275dcc2a81b41989a71cd23ad98308/04%20-%20Layout/04%20-%20Inline%20Styles.md provides a proposed API for defining inline styles on a component. In some cases, it's necessary to use media queries or pseudo classes in a selector, which isn't directly possible with inline styles. Any ideas here?

cc @vjeux

@vjeux
Copy link
Contributor

vjeux commented May 26, 2014

For media queries, I would suggest to just read window dimensions in JS and re-render the entire page.

For pseudo-class like :hover, my thought is to do hoverStyle={...}. This way if your component also implements hover in pure JS, it can use the same API.

For :before and :after, just insert a new element in the DOM.

Do you see anything else I missed?

@sophiebits
Copy link
Member Author

It would be nice to be able to do actual media queries so that server-rendered pages can be laid out correctly.

:visited is probably the trickiest because it can't be faked in JS.

@yocontra
Copy link

window.matchMedia would work well here

@guillermo
Copy link

I am having problems with the :focus pseudo classes.
Is the typical TagList, where you have an input as the end of the tags. This input is small, but when :focus it gets a css outline. The element have an inline style of outline:none, but is ignore on :focus.

@ainscore
Copy link

@spicyj Would the intention be that server-rendered pages would display correctly sans-JS?

@sophiebits
Copy link
Member Author

@ainscore Yes, that's what I meant.

@ainscore
Copy link

That sounds interesting, I've thought previously about a method where the styles are written into a style tag for server side rendering and parsed by code on the client similar to how the DOM is with current server-side rendering. This would leverage the css engine even for js-enabled browsers, but I don't know what kind of performance impact there would be from reading out the styles client side. And for inaccessible styles rules like :visited, the client side code could also write into a style tag.

@yocontra
Copy link

To solve media queries I ended up doing this https://github.com/wearefractal/react-responsive which I think makes way more sense

@ainscore
Copy link

@contra i like that solution for media queries applied client side, I was thinking more toward spicyj's point for sites that use react exclusively on the server side. And I think a large part of the reasoning behind the server-side rendering of react in general is that you get the initial static content pop, which may require media queries.

@yocontra
Copy link

@ainscore Server-side media queries could specify a default that is executed, or possibly parse user agents to guess the device specs and do your own matching. I'm planning on playing around with those ideas soon

@matthewwithanm
Copy link

The "specifying a default" approach is the one I took with react-mediaswitch. The UA stuff is separate IMO, though it could be used to decide which media case is the default. We're also considering sending a separate XHR request for the initial view which passes along device dimensions (or a string that indicates which of the server-defined device dimensions is appropriate).

@Cethy
Copy link

Cethy commented Oct 23, 2014

Another take on react & media-queries : https://github.com/Cethy/react-mixin-media-query

I used a "mixin approach" instead of the "classical" component one. It might seems less flexible, but it makes a lot more sens to me.

The nice side-effects I wanted to attain were :

  • no more html tag than semantically needed ;
  • Having a nice clean DOM for the final user (you can "destroy" the html parts that must not be shown on a particular screen size)

Feel free to try it and send me some feedback. :)

@sebmarkbage sebmarkbage changed the title Media queries and pseudo classes for inline styles Server-side: Media queries and pseudo classes for inline styles Nov 5, 2014
@sebmarkbage
Copy link
Contributor

I clarified in the title, that this issue is concerned with server-side rendering.

@yocontra
Copy link

yocontra commented Nov 5, 2014

What do you think about having media information become a new object that updates a view (similar to this.state and this.props)?

render: function(){
  if (this.media.screenWidth >= 480) {
    // desktop stuff
  } else {
    // mobile stuff
  }
}

@RReverser
Copy link

@contra You can already do this via if (screen.width >= 480) + onresize.

@Cethy
Copy link

Cethy commented Nov 6, 2014

@sebmarkbage I fail to see why side is important here, server-side you could have default value or parameter passed from the client ; Am I missing something ?

@contra That's pretty much what I'm trying to accomplish with my mixin.

@parshap
Copy link

parshap commented Nov 6, 2014

@sebmarkbage I fail to see why side is important here, server-side you could have default value or parameter passed from the client ; Am I missing something ?

A "default" value, or event a guess from the user-agent, will sometimes be wrong.

@Cethy
Copy link

Cethy commented Nov 6, 2014

@parshap And how would you fix that ?

"Isomorphically", you need to be able to handle media-queries the same way on both side. So you need a fallback value for when you don't have "screen size".
But I don't see why we would need to have a specific way to handle it server-side. It seems overkill to me.

@yocontra
Copy link

yocontra commented Nov 6, 2014

@Cethy The mixin seems too complicated. I think something as simple as re-render on window.onresize and a sugar this.media object (with info like this.media.screenWidth, this.media.pixelRatio, etc.) as a mixin would suffice just fine.

@parshap
Copy link

parshap commented Nov 6, 2014

@Cethy: There are two separate things being discussed here:

  1. Conditionally returning different DOM trees from render() calls based on JavaScript values (such as those that come from matchMedia, component state, a React mixin, etc.

  2. Having a way to use CSS media queries for the a React element's CSS styles.

These two things are different and have non-interchangeable behaviors. Your mixin is a way of doing (1). This issue is about finding a solution for (2).

@Cethy
Copy link

Cethy commented Nov 6, 2014

@contra you've got a point, I'll try to split my work to have something more simple.

@parshap Thx for the clarification, i hadn't read the inline styles proposal (since the link from spicyj is broken, aha) ; now I understand, so forget my last intervention :)

@sophiebits
Copy link
Member Author

Updated the original link so that it works again.

@fdecampredon
Copy link

Perhaps it's a stupid proposition but with that kind of thing : https://gist.github.com/fdecampredon/86ccbba3863bccaec7dd
We could obtain client media information while we stream server response and still have the same result from rendering on client and server.

@ainscore
Copy link

If we're sticking to a very strict application of server-side rendering in which no javascript needs to run in order for the media queries and pseudo-classes to work correctly, I see two approaches.

One would require compiling a stylesheet from the Javascript logic and sending that down with the html. The dom and stylesheet would then be parsed by React on the client side and any further application of styles would happen on the DOM.

This would not necessarily render the exact same DOM tree on the server side as the client side(in this example, both bigContent and smallContent would be rendered on the server side and only one displayed on the client, whereas on the client side only one would be inserted in the DOM), thus it could result in rendering an unnecessary amount of HTML. However, the extra content would be removed after a render pass on the client side, so functionally this shouldn't be an issue(at least in a pure react environment).

I'm not entirely sure if the javascript logic can be parsed into correct media queries in all cases but this code is how I imagine it happening in a simple case.

(Showing examples for media queries but pseudo classes could be applied in a similar way)

render: function(){
  if (screen.width >= 480) {
    // desktop stuff
    return React.DOM.div({}, bigContent);
  } else {
    // mobile stuff
    return React.DOM.div({}, smallContent);
  }
}

Rendered html:

<div class="disp_0" >
<!-- big content -->
</div>
<div class="disp_1" >
<!-- small content -->
</div>

Rendered stylesheet:

@media (min-width: 480px) {
    .disp_1 {
        display:none;
    }
}

@media (max-width: 479px) {
    .disp_0 {
        display:none;
    }
}

A second, simpler approach would allow specifying the media queries in React StyleSheet objects, so that only those objects would need to be processed by the stylesheet compiler.

render: function(){
    return React.DOM.div({},
        bigContent({
            style:StyleSheet.create({
                base: {
                    //set prop as "none" if width < 480
                    display: React.Media.maxWidth(480)("none")
                    width: 1024,
                    height:40
                }
            })
        }),
        smallContent({
            style:{
                base: {
                    display: React.Media.minWidth(480)("none")
                    width: 480,
                    height:40
                }
            }
        })
    );
}

Rendered html:

<div class="disp_0" >
<!-- big content -->
</div>
<div class="disp_1" >
<!-- small content -->
</div>

Rendered css:

@media (min-width: 480px) {
    .disp_1 {
        display:none;
    }
}

@media (max-width: 479px) {
    .disp_0 {
        display:none;
    }
}

.disp_0 {
    width:1024px;
    height:40px;
}

.disp_1 {
    width:480px;
    height:40px;
}

@mtford90
Copy link

+1 for a simple way to apply inline pseudo styles

@koistya
Copy link
Contributor

koistya commented Dec 16, 2014

@ainscore 👍

var styles = StyleSheet.create({
  styleName: {
    '@media': React.Media.minWidth(700).andHandheld().andOrientation('landscape').style({
      fontSize: 20
    }),
    ':active': {
      textDecoration: 'underline'
    }
  }
});
var options = {renderHidden: false, renderStyles: true, screen: { width: 480 }};
var { markup, css } = React.renderToString(<App />, options);

@jviereck
Copy link

Thinking about this problem I describe my solution in the following blog post, which introduces a new abstraction called VirtualCSS which is similar to CSS like VirtualDOM is to the DOM in react terms:

https://medium.com/@jviereck/modularise-css-the-react-way-1e817b317b04

@minaseem
Copy link

minaseem commented Apr 5, 2016

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

No branches or pull requests