flex-layout: Breakpoint fallback broken for CSS @media queries with Universal server rendering

Bug Report

I’m trying to migrate to Universal SSR. Flex Layout @media query breakpoints seem to be broken. Is there a workaround for now?

What is the expected behavior?

Universal SSR generates a <style> block that should mimic the browser-side JS runtime behavior, but it’s consistently not.

Example A: xs shows 1, 2, 3 in a column, sm shows 1,2 on one line and 3 on the next, and md/lg/xl shows 1, 2, 3 in a row.

<div fxLayout="row wrap" fxLayoutAlign="center start">
  <div fxFlex="100%" fxFlex.gt-xs="50%" fxFlex.gt-sm="33.33%"> 1 </div>
  <div fxFlex="100%" fxFlex.gt-xs="50%" fxFlex.gt-sm="33.33%"> 2 </div>
  <div fxFlex="100%" fxFlex.gt-xs="50%" fxFlex.gt-sm="33.33%"> 3 </div>
</div>

Example B: xs/sm shows 1, 2, 3 in a column, md/lg shows 1,2 on one line and 3 on the next, and xl shows 1, 2, 3 in a row.

<div fxLayout="row wrap" fxLayoutAlign="center start">
  <div fxFlex="100%" fxFlex.gt-sm="50%" fxFlex.gt-lg="33.33%"> 1 </div>
  <div fxFlex="100%" fxFlex.gt-sm="50%" fxFlex.gt-lg="33.33%"> 2 </div>
  <div fxFlex="100%" fxFlex.gt-sm="50%" fxFlex.gt-lg="33.33%"> 3 </div>
</div>

What is the current behavior?

Breakpoints aren’t properly calculated, showing the wrong layout on initial page render, which then jarringly corrects itself once JS has loaded.

Example A: xs shows 1, 2, 3 in a column, sm/md/lg/xl shows in a row.

Example B: all screen sizes shows 1, 2, 3 in a row.

It seems the @media queries are not generated correctly. Here’s the CSS that Example A generates for one of the divs. The gt-xs generates a media query at the top ((min-width: 600px)) that is then overridden by the sm below it ((min-width: 600px) and (max-width: 959px)):

<style class="flex-layout-ssr">
@media all { .flex-layout-355 { max-width:100%; } }
@media screen and (min-width: 600px) { .flex-layout-355 { max-width:50%; } }
@media screen and (min-width: 960px) { .flex-layout-355 { max-width:33.33%; } }
@media screen and (min-width: 1280px) { .flex-layout-355 { max-width:33.33%; } }
@media screen and (min-width: 1920px) { .flex-layout-355 { max-width:33.33%; } }
@media screen and (min-width: 1920px) and (max-width: 5000px) { .flex-layout-355 { max-width:33.33%; } }
@media screen and (max-width: 1919px) { .flex-layout-355 { max-width:33.33%; } }
@media screen and (min-width: 1280px) and (max-width: 1919px) { .flex-layout-355 { max-width:33.33%; } }
@media screen and (max-width: 1279px) { .flex-layout-355 { max-width:33.33%; } }
@media screen and (min-width: 960px) and (max-width: 1279px) { .flex-layout-355 { max-width:33.33%; } }
@media screen and (max-width: 959px) { .flex-layout-355 { max-width:33.33%; } }
@media screen and (min-width: 600px) and (max-width: 959px) { .flex-layout-355 { max-width:33.33%; } }
@media screen and (max-width: 599px) { .flex-layout-355 { max-width:100%; } }
@media screen and (min-width: 0px) and (max-width: 599px) { .flex-layout-355 { max-width:100%; } }
</style>

What are the steps to reproduce?

Serve app via Angular Universal (FlexLayoutServerModule in app.server.module.ts). Add this to a page:

<div fxLayout="row wrap" fxLayoutAlign="center start">
  <div fxFlex="100%" fxFlex.gt-xs="50%" fxFlex.gt-sm="33.33%"> 1 </div>
  <div fxFlex="100%" fxFlex.gt-xs="50%" fxFlex.gt-sm="33.33%"> 2 </div>
  <div fxFlex="100%" fxFlex.gt-xs="50%" fxFlex.gt-sm="33.33%"> 3 </div>
</div>

Disable JS in browser. Load page.

What is the use-case or motivation for changing an existing behavior?

To render page accurately. Without this, Flex Layout with Angular Universal displays layouts improperly.

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

FlexLayout 7.0.0-beta.24. Angular 7.2.15. Material 7.3.7. macOS 10.14.3. TypeScript 3.2.4. Firefox 66 and Chrome 74.

Is there anything else we should know?

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 6
  • Comments: 25 (3 by maintainers)

Commits related to this issue

Most upvoted comments

mine’s generating 100kb of styles. It starts at flex-layout-551 (for some reason) and they’re each repeated 10x.

Will the fix (https://github.com/angular/flex-layout/issues/1065#issuecomment-498759579) be applied to the new releases anytime soon? Or is there a quick fix temporary?

That fixes a lot of the layout issues with fxFlex, etc! But it seems like responsive ngClass’s are broken still, which makes for lots of inconsistencies on my site 😦

E.g. <div class="foo" ngClass.gt-xs="foo-wide"> renders as <div class="foo foo-wide"> with no correcting inline CSS from SSR. Thus shows the wide-screen version in mobile until the browser app kicks in.

This is on 7.0.0-beta.24…do ngClass’s work with your 8.0.0.beta26 by chance?

Wow, that looks promising, wsfriday-sfd! Thanks for sharing.

I feel silly asking this, but I’ve been searching for half an hour and can’t find anything. Is there a way to apply this patch to flex-layout besides forking the entire flex-layout project and installing that repo?

I’ve used patch-package before, but npm doesn’t install the flex-layout source files to ./node_modules/@angular/flex-layout, it installs the built umd, esm2015, esm5 bundles, etc. I think it’d be pretty bad to try to hand-code patches for the multiple bundles this code compiles into.

I have a possible fix for the issues we are experiencing.

When the Server starts the generateStaticFlexLayoutStyles (server-provider.ts) function is setup to go through each breakpoint, activate it, generate the CSS, deactivate, and move to the next one.

During this process the MediaMarshaller service will setup the styling for each HTML element for the current breakpoint. The issue is that the Server will always have every breakpoint active, and MediaMarshaller adds the style to more breakpoints than it should.

To fix this I have a new Server only function in MediaMarshaller that will grab the current breakpoint and look to see if there are any styles that should be applied to the element. If there isn’t any styles that are needed, it will move on instead of going through all of the possible breakpoints.

I have a patch file for 7.0.0-beta.24 and I have also applied the same fixes to 8.0.0-beta.26 and it worked there as well.

flex-layout-beta-24_media-marshaller.zip