Skip to content
This repository has been archived by the owner on Jul 6, 2021. It is now read-only.

Research: prototype MediaQueryPropertyMixin #36

Closed
web-padawan opened this issue Jan 17, 2020 · 3 comments · Fixed by #37
Closed

Research: prototype MediaQueryPropertyMixin #36

web-padawan opened this issue Jan 17, 2020 · 3 comments · Fixed by #37
Assignees

Comments

@web-padawan
Copy link
Member

web-padawan commented Jan 17, 2020

Problem

In certain components we apply CSS conditionally based on media queries.
We don't have a unified approach for it, so components handle it differently.

1. JS API

Example: vaadin-date-picker with _fullscreenMediaQuery set from JS which toggles the fullscreen state attribute on vaadin-date-picker-overlay.

When using this approach, we currently hardcode media query also in CSS (example) which makes overriding it more complex (as it needs to be done in multiple places).

2. CSS API

Example: vaadin-app-layout with following CSS custom properties:

Both of these are observed in window resize event handler (there is no platform way to observe changes to custom CSS properties so far, see also discussion about observedStyles).

Then those true / false are used to show / hide parts of the DOM and one of them is synced to overlay property (which reflects to attribute and is read-only).

While this approach lets the user to write their CSS and override the defaults, it still requires boilerplate work for every component and that doesn't sound like a good DX.

Solution proposal

Let's assume we have a mixin with the following API:

static get mediaQueries() {
  return {
    touchOptimized: '(max-width: 800px) and (min-height: 500px)'
  };
}

Then we declare property with noAccessor flag:

static get properties() {
  return {
    touchOptimized: {
      type: Boolean,
      reflect: true, // optional. set if we need to sync with an attribute
      attribute: 'touch-oprimized' // optional. set to have dash-case
    }
  };
}

For each of the properties declared like this, the mixin does the following:

  • create window.matchMedia and add listener to the mediaQueries[prop]
  • create custom accessors that make sure the property is read only
  • toggle property value between true and false depending on media query
  • when attribute is set manually, keep it and override media query based value
    • this is needed to allow forcing specific appearance e.g. touch-optimized
  • when attribute is removed manually, watch to media query changes again

This approach (only allow to force the value that is not the default one) is inspired by the way how we handle dir attribute in DirMixin when set by the user to a different value).

@web-padawan
Copy link
Member Author

web-padawan commented Jan 20, 2020

One use case that came up recently is vaadin/vaadin-app-layout#129

The problem

  • we can't have custom CSS properties for breakpoints (platform limitation)
  • currently user can write their own breakpoints but that needs CSS to override existing one, too

Static get mediaQueries()

The static approach would allow to override breakpoints only once.
That's ok for those who can extend a base class, but it's not an option for Java users.

Combine with custom CSS properties

What if we introduce a "one time custom CSS property" concept?
This would let us to use something like this:

static get mediaQueries() {
  return {
    touchOptimized: window.getComputedStyle(document.documentElement)
      .getPropertyValue('--vaadin-breakpoint-touch').trim() || 
      '(max-width: 800px) and (min-height: 500px)';
  };
}

@web-padawan
Copy link
Member Author

web-padawan commented Jan 21, 2020

I've gone through all the components and collected media queries we use.

component property query
app-layout overlay (max-width: 800px) and (max-height: 600px)
app-layout touch-optimized (max-width: 800px) and (min-height: 500px)
board small (max-width: 600px)
board medium (max-width: 960px)
board large (min-width: 961px)
login small (max-width: 500px)
login small landscape (max-height: 600px) and (min-width: 600px) and (orientation: landscape)
context-menu wide (min-device-width: 750px)
date-picker fullscreen (max-width: 420px) and (max-height: 420px)
date-picker desktop (min-width: 375px)
notification (not reflected) (max-width: 420px)
select phone (max-width: 420px) and (max-height: 420px)

Note: we also use (max-width: 420px) and (max-height: 420px) in menu-overlay mixin.

@web-padawan
Copy link
Member Author

web-padawan commented Jan 22, 2020

Alternative API suggestion by Yuriy:

static get properties() {
  return {
    touchOptimized: {
      type: Boolean,
      media: '(max-width: 800px) and (min-height: 500px)'
    }
  };
}

Need to check whether this would work with @property decorator and TypeScript.

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

Successfully merging a pull request may close this issue.

1 participant