react-future icon indicating copy to clipboard operation
react-future copied to clipboard

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

Open sophiebits opened this issue 10 years ago • 28 comments

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

sophiebits avatar May 26 '14 22:05 sophiebits

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?

vjeux avatar May 26 '14 23:05 vjeux

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.

sophiebits avatar May 27 '14 02:05 sophiebits

window.matchMedia would work well here

yocontra avatar Jun 10 '14 03:06 yocontra

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.

guillermo avatar Jul 08 '14 20:07 guillermo

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

ainscore avatar Aug 25 '14 21:08 ainscore

@ainscore Yes, that's what I meant.

sophiebits avatar Aug 25 '14 21:08 sophiebits

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.

ainscore avatar Aug 25 '14 21:08 ainscore

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

yocontra avatar Aug 25 '14 22:08 yocontra

@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.

ainscore avatar Aug 25 '14 23:08 ainscore

@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

yocontra avatar Aug 25 '14 23:08 yocontra

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).

matthewwithanm avatar Aug 26 '14 00:08 matthewwithanm

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. :)

Cethy avatar Oct 23 '14 17:10 Cethy

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

sebmarkbage avatar Nov 05 '14 19:11 sebmarkbage

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
  }
}

yocontra avatar Nov 05 '14 23:11 yocontra

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

RReverser avatar Nov 06 '14 00:11 RReverser

@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.

Cethy avatar Nov 06 '14 09:11 Cethy

@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.

parshap avatar Nov 06 '14 10:11 parshap

@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.

Cethy avatar Nov 06 '14 11:11 Cethy

@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.

yocontra avatar Nov 06 '14 21:11 yocontra

@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).

parshap avatar Nov 06 '14 21:11 parshap

@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 :)

Cethy avatar Nov 06 '14 22:11 Cethy

Updated the original link so that it works again.

sophiebits avatar Nov 06 '14 22:11 sophiebits

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.

fdecampredon avatar Nov 09 '14 10:11 fdecampredon

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;
}

ainscore avatar Nov 10 '14 03:11 ainscore

+1 for a simple way to apply inline pseudo styles

mtford90 avatar Dec 13 '14 00:12 mtford90

@ainscore :+1:

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);

koistya avatar Dec 16 '14 18:12 koistya

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

jviereck avatar Mar 31 '15 17:03 jviereck

use radium https://github.com/formidablelabs/radium

minaseem avatar Apr 05 '16 13:04 minaseem