Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a way to query whether there is a UA-provided back button #693

Closed
mgiuca opened this issue Jun 14, 2018 · 33 comments
Closed

Add a way to query whether there is a UA-provided back button #693

mgiuca opened this issue Jun 14, 2018 · 33 comments
Assignees
Labels

Comments

@mgiuca
Copy link
Collaborator

mgiuca commented Jun 14, 2018

As discussed in #673, developers of "display: standalone" (or "minimal-ui") apps cannot be sure of whether a back button will be provided by the user agent (meaning, the browser or operating system or hardware). For some apps, this is fine, but for others, not having a back button makes it hard to navigate, so they need to add an in-app back button. The problem is, if there is already one provided by the system, this can be redundant, wasting space and looking silly.

At the extreme, this can lead to the "double back button" problem, seen here in the Twitter PWA currently installable from the Microsoft Store on Windows 10 (this internally uses https://mobile.twitter.com/):

image

We can discuss ways to let developers control whether a back button is shown in #673; here I want to propose that we give developers a way to query whether a back button is shown. Thus Twitter and other apps can conditionally show an in-app back button, depending on whether there is already a system one. Giving a standard way to check this means we can avoid problematic user-agent detection, or viewport-size detection (which is just as bad for compatibility --- that's what Twitter does today).

Proposal

I propose a CSS media feature which says whether a back button is currently provided by the user agent. The precedent for this is the display-mode media feature already in the Manifest spec.

The proposed feature is named navigation-controls with possible values {'none' | 'back-button'}, (for example, allowing it to be extended later with 'back-and-forward'), but it is designed to be used in a Boolean context so all future values would imply the presence of the back button. Thus, you could use it like this to show a back button if-and-only-if there isn't already a system-provided one:

@media navigation-controls {
  #go-back {
    display: none;
  }
}

(Note: I use an enum instead of a Boolean due to this note in Media Queries: "The <mq-boolean> type exists only for legacy purposes. If this feature were being designed today, it would instead use proper named keywords for its values.")

navigation-controls would always be available (even when not in a PWA's standalone window), generally having the back-button value when in a normal browser, and an appropriate value in a stand-alone window. It could change dynamically, for example if the UA shows and hides the back button depending on whether there is navigation history.

This would live in the Manifest spec, for now (as with display-mode), but probably eventually belongs in the Media Queries spec.

The Android quirk

There's a bit of a quirk with Android, which is that native apps are generally expected to show a back button even though the system has its own back button along the bottom. I believe Twitter is leaning into this pattern with their PWA.

This pattern is quite unique on Android. All other platforms either a) have a system back button, and don't expect apps to provide their own (e.g., Windows 10), or b) don't have a system back button, and expect apps to provide their own (e.g., iOS).

Since this is unique on Android, I think it's appropriate to ask developers to use User-Agent detection if they want to carry forward this pattern. I usually don't like recommending UA detection, but in this case, it is literally a developer saying "I want to show my back button if and only if the user agent does not provide a back button, or on Android where there is a convention to do so despite the system back button already existing." Because the developer is explicitly thinking about Android, it makes sense to explicitly mention Android in their code.

I wouldn't want to have Android browsers show navigation-controls: none on Android (a lie) based on the assumption that apps will want to show a redundant back button.

@kenchris
Copy link
Collaborator

I would also point out that most people who use Chrome OS with external monitor and keyboard, won't have the back key on their keyboard, so the availability of a hardware/system wide key probably shouldn't indicate it is there UI wise.

@mgiuca
Copy link
Collaborator Author

mgiuca commented Jun 14, 2018

That's a good point @kenchris about systems with a hardware back button that may or may not be accessible. Perhaps we should explicitly leave hardware back buttons out of this (so you return "none" if there is only a hardware back button, thus showing an on-screen button).

@kenchris
Copy link
Collaborator

Yes, that is what I was thinking. The back button on Android it basically also a hardware button though it is always shown on screen when it is not an actual hardware button. Maybe we can come up with a term for describing these

@fchristant
Copy link

I think this is a great idea. Two thoughts/questions to add:

  • Would it be extensible to allow for checking of a share button? Or do you imply that a back button always means a full browser navbar is available, including share? Could be a risky assumption.

  • A bit unrelated, but would it also be possible to check navigation-control position or orientation? (top, bottom, etc). Example is a non-fullscreen PWA with nav docked to bottom yet the browser has its navbar also in the bottom. This can lead to serious usability issues, and is a fairly common issue on iOS, not sure about Android.

@mgiuca
Copy link
Collaborator Author

mgiuca commented Jun 28, 2018

Would it be extensible to allow for checking of a share button?

It could, but we would start with the obviously-needed property (back button) and go from there.

Most browsers don't have a very prominent share button, so it's generally acceptable to show one within the page. It doesn't really trigger the "silly double back button" syndrome that an in-page back button does.

Or do you imply that a back button always means a full browser navbar is available, including share?

You shouldn't assume this. A solid counter-example would be Android, which would have a back button but no other nav bar.

A bit unrelated, but would it also be possible to check navigation-control position or orientation? (top, bottom, etc). Example is a non-fullscreen PWA with nav docked to bottom yet the browser has its navbar also in the bottom. This can lead to serious usability issues, and is a fairly common issue on iOS, not sure about Android.

I'd rather not do this. We don't want to expose too much logic about the browser UI to the site, since sites should generally be agnostic as to the browser UI, and we end up baking too much into the spec. That's why I'm carving a very small and explicit space here: just knowing whether there is a back button or not. I don't really want to get into the business of differentiating top, bottom, hardware, software, in-window, system-UI, etc.

@fchristant
Copy link

@mgiuca Thank you for the response.

Fair point on the share button, but I do have some follow-up remarks. On iOS, the share button IS prominent and this is a serious piece of market share in light of PWAs. And since iOS Safari does not (yet) support the Web Share API, there really is no comparison between an in-app share functionality and the real thing from the browser.

So whilst you can safely mimic "back" functionality from inside a PWA, you can't for "share" functionality. Not in a cross browser way. For me enough reason to not use full screen at all, but that's a separate discussion.

Either way, good to hear this may be potentially be extended with extra buttons, that would solve it.

Regarding this...

"I'd rather not do this. We don't want to expose too much logic about the browser UI to the site, since sites should generally be agnostic as to the browser UI"

From a purely pragmatic spec point of view, I get it. Still, playing the devil's advocate here. The origin of your request is to solve a problem with a specific situation of browser UI (therefore not agnostic) interfering with a PWA UI. Your proposal tries to solve this by doing browser UI detection.

Double back navigation and interfering bottom navigation are two problems of the very same kind. Why solve one problem, but not the other? They are both usability problems of multiple navigation systems interfering with each other.

Do you have a conceptual reason for this distinction? Or is it purely pragmatic to keep a spec small?

@tomayac
Copy link
Contributor

tomayac commented Jun 29, 2018

To provide the context again, the present issue came out of #673.

On iOS, the share button IS prominent and this is a serious piece of market share in light of PWAs.

We're talking standalone/fullscreen/minimal-ui PWAs here, so there is no prominent share button on iOS.

you can't for "share" functionality

I would politely disagree. Right now, you can treat the Share API as a progressive enhancement and fall back to in-app share buttons or libraries seamlessly (without sharing to apps, of course).

Double back navigation and interfering bottom navigation are two problems of the very same kind.

Again this proposal is about standalone/fullscreen/minimal-ui, so I don't think they are comparable problems. Apart from that, moving the navigation bar could on some UAs even be a configurable option.

@warpdesign
Copy link

warpdesign commented Jun 29, 2018

Sounds good to me!

Just one thing: what about cases where there is a back button in window mode, and no back button in fullscreen mode.

For example, a normal browser window would have the back-button but what happens if the user switches to fullscreen: in this case the back button isn't there anymore. Would the value be dynamically updated in this case?

Likewise, most recent OS allow to switch almost any app to fullscreen in which case the back button that was there previously isn't visible anymore. Sometimes you can make it appear (like moving your mouse on the top of the screen), but sometimes you have to first exit fullscreen to make it appear again.

I guess it's not that simple..

@aarongustafson
Copy link
Collaborator

Not to throw a wrench in the works, but why have this in CSS? This sort of a control (back button, but also forward, etc.) requires JavaScript to work, so wouldn’t it make more sense to have the feature detection happen in JavaScript? That way we’re steering developers toward the best practice of only adding the control to the DOM when it’s useful rather than having it in the DOM for everyone and hiding it when it’s redundant.

@fchristant
Copy link

@tomayac Fair point on progressively enhancing sharing. Although there's a world of difference between the power in both share modes, I can see how its less essential than back navigation.

"Again this proposal is about standalone/fullscreen/minimal-ui, so I don't think they are comparable problems."

And here I will politely disagree at a conceptual level. They may not be comparable as to in which spec they belong, from a user perspective they are very similar problems, and I'd consider users over implementers (based on the web mantra). Double back navigation is browser UI interfering with app UI. Double positioned browser UI and app UI (so in same orientation) is also browser UI interfering with app UI. Conceptually a similar problem, reasoned from a user.

Don't disagree though at a practical level.

@tomayac
Copy link
Contributor

tomayac commented Jul 3, 2018

@warpdesign I envision the media query to update its state dynamically based on the use cases you describe. Like this the app can adapt based on the window state.

@aarongustafson You can match media queries in JavaScript with matchMedia. Note that you can also be notified through a MediaQueryList listener when changes happen.

@mgiuca
Copy link
Collaborator Author

mgiuca commented Jul 3, 2018

@fchristant What is the specific problem you're trying to solve with your proposal to let the site read the orientation or position of the system back button? Can you describe a concrete situation where it would be useful to distinguish between a system back button at the top versus bottom? What is "interfering bottom navigation"?

Generally, apps should show a back button if there is no system back button, and not show one if there is a system back button.

@tomayac I agree.

This sort of a control (back button, but also forward, etc.) requires JavaScript to work, so wouldn’t it make more sense to have the feature detection happen in JavaScript?

No, it can be controlled through CSS just by setting the display property of the buttom.

That way we’re steering developers toward the best practice of only adding the control to the DOM when it’s useful rather than having it in the DOM for everyone and hiding it when it’s redundant.

I think the best practice is to control it through CSS; that way you don't need to write complex and possibly error-prone event handler logic to update the state. Having a simple hidden DOM element is not a big overhead, though I'm willing to be proven wrong with data. Note that this mirrors the display-mode property which is also a CSS media query. I'd rather make it possible to do it in CSS or JS, whereas the JS-only approach forces you to use JS.

@aarongustafson
Copy link
Collaborator

You can match media queries in JavaScript with matchMedia. Note that you can also be notified through a MediaQueryList listener when changes happen.

@tomayac I know; it’s totally possible to control the display from CSS and to read that info from JavaScript. What I am arguing is that I don’t think CSS is the right place to define this. Looping in @mgiuca here too because I’ve not done a good job explaining the issue. I’ll try again.

If we implement the media query, designers/developers are encouraged to add the button into the markup and then toggle its visibility using CSS. The button itself requires JavaScript to function. In other words, without JavaScript, the HTML-based button will not be able navigate the user backwards (onclick="history.back(-1)", etc.). JavaScript is a prerequisite here; there’s no way to avoid using it.

There are a number of reasons JavaScript may fail to run or why CSS may fail to load. If either of those things happen, the presented document contains a button that either a) appears when it shouldn’t (CSS not loaded or this syntax is unsupported) or b) appears when it should but is non-functional (JavaScript not loaded or error experienced).

This thread began with a simple use case: Let’s find a way to make it easier for developers to determine whether the OS or hardware is presenting a back button so we can decide whether we should show one ourselves. Given that use case, this is a programatic decision, the application logic—whether that’s in some front end framework or just plain old JavaScript—seems like the most appropriate place for it. And, if it were a JavaScript API, we can eliminate the dependency issues that can cause a breakdown in the interface and a poor user experience.

Consider this hypothetical example:

if ( "features" in navigator &&
     ! navigator.features.backButton ) {
  // add your back button to the DOM
}

With something like this in place, you can add whatever buttons you need to the interface based on querying for the existence of the hardware/os/browser feature first. That makes it possible to avoid:

  1. Adding a button when it’s not needed
  2. Having a button displayed when it shouldn’t be (and there’s a CSS issue)
  3. Having a non-functional button displayed (and there’s a JS issue)

This also keeps the logic neatly in one place and could even lead to our ability to look at other potentially interesting things:

  • navigator.features.forwardButton
  • navigator.features.shareButton
  • navigator.features.urlBar
  • navigator.features.tooltips

@fchristant
Copy link

@mgiuca I'm talking about browser chrome interfering with docked navigation. For example, having sticky navigation in the bottom whilst browser chrome is also in the bottom. Or worse, sticky navigation in the bottom with OS gestures interfering (as seen on iPhone X).

But anyway, never mind, I think this side tracks the main issue, so I'll drop it.

@fchristant
Copy link

@aarongustafson Just because a button requires JS to run doesn't mean detection or rendering needs to happen in JS? CSS-based rendering is pretty much superior in every way, every time. No delay, no screen jumps.

@aarongustafson
Copy link
Collaborator

Just because a button requires JS to run doesn't mean detection or rendering needs to happen in JS?

You’re correct; it doesn’t necessitate that you inject it via DOM APIs, but it is a best practice to not include JS-dependent buttons (or other interactive elements) before you know they can actually be used.

When dependencies are not met, you introduce both UX and accessibility issues. Enabling this feature in CSS is not, in and of itself, an issue. People could still code defensively and avoid these issues. But it does make it far easier to do the wrong thing, encouraging designers to include the button in their markup by default, relying on CSS to sort out whether it should be displayed, ignorant of the fact that the JavaScript dependencies the button relies on may not be met and that some user agents visiting the page without support for the CSS feature would display the button by default. Sure, you could set the default display state to "none" in CSS and then show it only when you know there is no native back implementation shown, but a quick survey of what developers actually do (and tell others to do on Stack Overflow) shows this more defensive, user-friendly approach is more the exception than the rule.

So why not approach this in such a way that encourages developers to do the right thing™ from the start rather than crossing our fingers and hoping for the best?

FWIW, I gotta imagine the CSS-in-JS folks would probably dig this approach too.

@fchristant
Copy link

I guess we disagree on what the right thing is. JS-based rendering in my view is never a best or better practice over solving the same problem in CSS. Don't want to side track the original proposal though with a long and hairy theoretical discussion, so I'd say agree to disagree :)

@mgiuca
Copy link
Collaborator Author

mgiuca commented Jul 5, 2018

@aarongustafson:

There are a number of reasons JavaScript may fail to run or why CSS may fail to load. If either of those things happen, the presented document contains a button that either a) appears when it shouldn’t (CSS not loaded or this syntax is unsupported) or b) appears when it should but is non-functional (JavaScript not loaded or error experienced).

Won't this be true for just about every element on the page? A modern webpage is bound to have dozens to hundreds of buttons on it that don't do anything if JavaScript is disabled or broken. In practice, it's essentially a requirement of the modern web that JS is working properly, so having an entire page of non-functional UI is a big problem, but it's rare or indicative of a bigger issue.

Now you could follow a strict practice where every UI element that needs JavaScript to work is not included in the base page's HTML, but is instead inserted by JavaScript at load time, to ensure an "all or nothing" experience, but there are major downsides to this (UI loading in sporadically, not part of the initial page load). I'd say this is a fairly niche/extreme philosophy in an era where JavaScript is expected to always work. Most sites are just going to include UI elements in the base HTML. We should give developers this affordance for conditional UI like the back button.

Basically, your point of view is valid, but it is one of a handful of valid approaches. Providing this as a CSS query supports all approaches, whereas providing it as a JavaScript method supports only your approach.

@comp615
Copy link

comp615 commented Jul 5, 2018

Hi all! Wanted to jump in and try to provide a related use cases and problems we ran into with the Twitter PWA, in the hopes it informs the current discussion:

  1. I would like to go one step further in this proposal and point out it's important to us to differentiate the TYPE of back button provided. For instance, the back windows provides in the nav bar is not useful. But if Android indicates it provides back as a hardware button, that may be more noteworthy.

For instance, right now in the Twitter PWA, we actually go to great lengths to make back behavior sensible. Only "pages" have "routes", things like dropdowns or sheets do not. This is a small distinction, but consider the UI patterns across devices. On Android, a user expects the back button to close an open sheet, and NOT to navigate back a literal page. On a desktop browser, when I hit back, I expect the last page to come up, not just some random dropdown to close.

This is a small distinction, but you can see that the actual differentiator in behavior is if the back came from a hardware vs software button/shortcut. This is something Android natively does, but is not exposed in websites.

  1. For CSS vs JS, I get that it can be exposed in either. I don't think doing feature detection via CSS is as common place as has been argued yet, even display: standalone CSS detection is tedious at best. We use it only to try and figure out if the user currently has Twitter "installed". I'd much prefer to have navigator.features.installed or something like that which is direct and to the point. Roundabout way of saying, ultimately we'll follow whatever, but JS for long-term consistency with other feature detections?

Long, but hopefully that's all helpful. I'll try and think more about it and circle back with more.

@mgiuca
Copy link
Collaborator Author

mgiuca commented Jul 5, 2018

Hi Charlie, thanks for stepping in.

I would like to go one step further in this proposal and point out it's important to us to differentiate the TYPE of back button provided. For instance, the back windows provides in the nav bar is not useful. But if Android indicates it provides back as a hardware button, that may be more noteworthy.

Thanks for clarifying the difference between what you expect out of back buttons on Android vs other systems.

It sounds like, though, you don't really want to know the "type" of back button (i.e., "hardware" vs "system" vs "browser") or something like that. What you want to know is: "is this the Android back button, which has special conventions attached to it, or is it like a normal browser back button?"

The scenario I'm afraid of is: We provide additional details like Chrome-on-Android returns "system" while Edge-on-Windows returns "browser", and developers start attaching Android-specific semantics to "system" (because it's currently the only OS that returns "system"). Then, in the future, "system" basically means "has the semantics of the Android back button". This is what I meant in the section "The Android Quirk" at the top of this page.

From the cross-platform perspective, there is no meaningful difference between a "hardware", "system" and "browser" back button, even though those things might have specific meanings on specific platforms. So it isn't right for us to expose those differences to the site. What you really want to know is not "is this a system back button?", but "is this Android?" And you can do that by explicit user-agent detection.

For CSS vs JS, I get that it can be exposed in either. I don't think doing feature detection via CSS is as common place as has been argued yet, even display: standalone CSS detection is tedious at best. We use it only to try and figure out if the user currently has Twitter "installed". I'd much prefer to have navigator.features.installed or something like that which is direct and to the point. Roundabout way of saying, ultimately we'll follow whatever, but JS for long-term consistency with other feature detections?

This isn't a feature detection (which implies a static feature that a browser either supports or doesn't). It's a dynamic property that can change, e.g., as the user installs the app and we pop out the browser tab into a window, as the user moves from normal to fullscreen, as the user pushes or pops history, etc. The same applies to the display CSS media query.

So this can't be as simple as if (navigator.features.installed) or if (navigator.features.backbutton). You'll need a) a method you can call to check whether it is currently visible (navigator.features.backbutton.isVisible()), and b) an event you can register to listen for updates to that state (navigator.features.backbutton.addEventListener(...)), then you'll need to implement an event listener to show/hide the button when the state changes.

Basically, CSS media query is perfect for this because firstly, all of the above machinery is already implemented and standardized (no need to invent new conventions for notifying when the state changes), and secondly, if all you want is a simple show/hide or enable/disable of a button, you can do it declaratively in CSS without writing any JavaScript (and maybe forgetting the event handler if the state changes).

@kenchris
Copy link
Collaborator

kenchris commented Jul 5, 2018

It sounds like, though, you don't really want to know the "type" of back button (i.e., "hardware" vs "system" vs "browser") or something like that. What you want to know is: "is this the Android back button, which has special conventions attached to it, or is it like a normal browser back button?"

I think this difference exists, because on Android a popup etc basically takes up most of the screen estate, much as what a new view or page would do, but that is not the case on desktop etc.

It is kind of a gray zone. If I use a Chrome OS tablet, say 10", that dialog won't take up most of my screen and I thus would also be confused if the hardware back button would just close it.

@aarongustafson
Copy link
Collaborator

Won't this be true for just about every element on the page? A modern webpage is bound to have dozens to hundreds of buttons on it that don't do anything if JavaScript is disabled or broken. In practice, it's essentially a requirement of the modern web that JS is working properly, so having an entire page of non-functional UI is a big problem, but it's rare or indicative of a bigger issue.

@mgiuca Sadly, many sites do assume JavaScript is a given. It’s not. There have been numerous instances where a 100% reliance on/assumption of JavaScript has caused disastrous consequences for companies and their customers. As developers, we often live in a bubble of high cost, high-speed, constantly-connected devices and we are often lulled into a false sense of security, thinking everyone is so privileged. But that’s not the reality on the web. And so it’s important to consider how our API design choices shape the way people build things for the web.

but there are major downsides to this (UI loading in sporadically, not part of the initial page load). I'd say this is a fairly niche/extreme philosophy in an era where JavaScript is expected to always work. Most sites are just going to include UI elements in the base HTML. We should give developers this affordance for conditional UI like the back button.

I feel like you’re arguing two sides here, but maybe I’m misunderstanding. You’re saying that for many sites JavaScript is a requirement. To me, that seems to imply reliance on a front-end framework of some kind (without which the UI does not work). If that’s the case, most (or all) of the UI is already being rendered by JavaScript on the client side. Which means the JS API makes a lot of sense because the framework (keeper of the application logic) can make the determination as to whether or not the UI control is needed.

If, on the other hand, you are using a framework like Vue (which takes a more progressive enhancement-style approach), the markup does exist in the DOM, but the philosophy of Vue is to supplement the core, non-Vue experience with enhancements (which the Back button would be, since it is not needed on a normal website, only in an installed PWA context).

That last bit bears repeating: the button(s) in question is only necessary in an installed PWA context.

Regardless, even in situations where you aren’t relying on a frontend framework to generate (and maintain) the DOM, there are absolutely ways to render new controls into the page very quickly without causing weird experiences for our users. It also stands to reason that if you were planning to possibly render a back button (or similar) into your website’s UI, you would probably have already reserved the space for it in your design, meaning a late injection/render would not affect layout, only paint. It could even be faded in to further reduce the "pop in" effect.

CSS is great for doing a lot of things (including showing and hiding things), but this is one instance where I think JavaScript is the more appropriate venue.

This isn't a feature detection (which implies a static feature that a browser either supports or doesn't). It's a dynamic property that can change, e.g., as the user installs the app and we pop out the browser tab into a window, as the user moves from normal to fullscreen, as the user pushes or pops history, etc. The same applies to the display CSS media query.

So this can't be as simple as if (navigator.features.installed) or if (navigator.features.backbutton). You'll need a) a method you can call to check whether it is currently visible (navigator.features.backbutton.isVisible()), and b) an event you can register to listen for updates to that state (navigator.features.backbutton.addEventListener(...)), then you'll need to implement an event listener to show/hide the button when the state changes.

Basically, CSS media query is perfect for this because firstly, all of the above machinery is already implemented and standardized (no need to invent new conventions for notifying when the state changes), and secondly, if all you want is a simple show/hide or enable/disable of a button, you can do it declaratively in CSS without writing any JavaScript (and maybe forgetting the event handler if the state changes).

Is it a dynamic property though? For @comp615’s scenario (navigator.features.installed), you either exist in an installed state or you don’t. That’s not going to change over the course of a browsing session. It’s also not going to change when you return to the PWA the next time.

As for the existence of a back button (or multiple kinds, to @comp615’s point), unless I’m missing something, that is also likely to be a static feature of the browsing context. If you’re in a browser or in an installed PWA rendered as "browser" or "minimal-ui," there’s going to be a back button (though it may be disabled on the start_url). In "standalone" and "fullscreen" it would not exist. In the PWA context, those rendering properties are statically-defined in the Manifest and are not dynamic. In most Android devices (and others), there’s also a hardware back button. That’s not dynamic either.

as the user installs the app and we pop out the browser tab into a window

Two different browsing contexts. The JS would be re-intepreted. No event necessary. Also worth noting: the web app manifest (which defines the display behavior of your app) currently only comes into play when the app is installed (the latter scenario).

as the user moves from normal to fullscreen

As in watching a fullscreen video or something? Based on my experience, it’s incumbent on the developer to enable exiting fullscreen mode, usually by checking for the Escape key or supplying a "close" button (though I have occasionally seen a "window blinds" style control from the browser).

as the user pushes or pops history, etc.

The back button either exists or it doesn’t. It may shift from disabled to enabled, but only if it’s in the "browser" or "minimal-ui" context.

Can you provide any other examples where information like this might be dynamic? I’m having a hard time imagining a scenario.

@dominickng
Copy link
Collaborator

@aarongustafson there are several examples where this may be a dynamic property, depending on the user-agent implementation of installation. For example:

Is it a dynamic property though? For @comp615’s scenario (navigator.features.installed), you either exist in an installed state or you don’t. That’s not going to change over the course of a browsing session. It’s also not going to change when you return to the PWA the next time.

On Chrome OS, users have the ability to "pop-out" and "pop-in" from a PWA window to the browser window, and vice-versa. This doesn't reload the page, it merely changes the window frame. You don't just exist in an installed state or not: users have the choice to move between the two.

As for the existence of a back button (or multiple kinds, to @comp615’s point), unless I’m missing something, that is also likely to be a static feature of the browsing context. If you’re in a browser or in an installed PWA rendered as "browser" or "minimal-ui," there’s going to be a back button (though it may be disabled on the start_url). In "standalone" and "fullscreen" it would not exist. In the PWA context, those rendering properties are statically-defined in the Manifest and are not dynamic. In most Android devices (and others), there’s also a hardware back button. That’s not dynamic either.

Two different browsing contexts. The JS would be re-intepreted. No event necessary. Also worth noting: the web app manifest (which defines the display behavior of your app) currently only comes into play when the app is installed (the latter scenario).

Again, in Chrome's implementation, installation can pop-out the PWA into its own window without reloading the browser context, which is why this can be a dynamic property (and the existence of a back button is also dynamic).

As in watching a fullscreen video or something? Based on my experience, it’s incumbent on the developer to enable exiting fullscreen mode, usually by checking for the Escape key or supplying a "close" button (though I have occasionally seen a "window blinds" style control from the browser).

Users can choose to go to fullscreen (F11), it's not just developers who control that.

@mgiuca mgiuca self-assigned this Jul 6, 2018
@mgiuca
Copy link
Collaborator Author

mgiuca commented Jul 6, 2018

Hi Aaron,

I feel like you’re arguing two sides here, but maybe I’m misunderstanding.

I'm not arguing two sides. I'm acknowledging that there is merit to your philosophy of ensuring that everything works without JS, and always using JS to set up JS-dependent features so if JS is disabled, the site still falls back gracefully. However, this is one philosophy; there are merits to both. We, as the web platform, have no right to force that philosophy on web designers.

I do understand that JS doesn't always load when a website loads. However, to be realistic, modern "web apps" (as opposed to web pages) are basically going to be non-functional without JS. It's up to each individual developer to decide whether to "noscript-proof" their site if they think they can get sufficient functionality when JS is disabled, or just assume JS as a basic requirement. The former approach will require more resources, more careful design, more testing, etc, so this is not a trivial decision. We should not be forcing this decision on developers by deliberately designing the platform to require JavaScript for things that don't strictly need JavaScript.

To add to what @dominickn is saying about the situations where this can change dynamically (without a page reload):

Also worth noting: the web app manifest (which defines the display behavior of your app) currently only comes into play when the app is installed (the latter scenario).

This isn't true. The Web App Manifest is only applied when the app is installed and running in a stand-alone window, but the Web App Manifest spec is still relevant in a normal browsing context. The beforeinstallprompt event and the display-mode media feature are two such examples.

As in watching a fullscreen video or something? Based on my experience, it’s incumbent on the developer ...

That's not what I mean. I mean that shifting into and out of full screen mode may change whether the user agent or operating system is showing a back button. The developer may accordingly wish to have their in-client back button appear at the same time as the external back button disappears due to full screen. (Of course, you can't just query for full screen mode changing, because that doesn't guarantee the OS hides the back button.)

The back button either exists or it doesn’t. It may shift from disabled to enabled, but only if it’s in the "browser" or "minimal-ui" context.

That's an assumption about user-agent-specific UI. Edge PWAs on Windows physically show and hide the back button as the history stack becomes empty/non-empty.

A non-comprehensive list of actual cases that exist in some browser today where the non-client back button visibility can change without a page load:

  • Entering and exiting full screen (all browsers).
  • Installing an app (on Chrome OS — automatically reparents the web contents into an app window without a back button without reloading).
  • "Pop out" / "Pop in" UI (on Chrome OS — a menu item allows the user to manually move the web contents into an app window and back into a browser tab without reloading).
  • History stack changing (on Edge — the back button is shown/hidden depending on the contents of history).

Some hypothetical other cases:

  • An OS like Android might show a back button in portrait but not landscape mode, so changing the device orientation may affect this flag.
  • A desktop browser might show a back button unless the window is too small, so resizing the window may affect this flag.

User agents are allowed to dynamically show or hide the back button, so we (the web platform, and web developers) need to allow for that.

Now I want to point out that you made an assumption (that the back button state doesn't change without a page load) that's provably false. Many web developers far less thoughtful than you will make this same assumption, which, if they just query using JS on page load, would result in buggy sites that don't dynamically change their back button visibility as the non-client button visibility changes. The JS approach requires site developers to think about this scenario and write correct logic to deal with it. The CSS approach does not — it's declarative and will always be correct without complex logic.

Aaron, I see you have an impressive track record in arguing to developers that they should build "noscript-proof" websites. I'm all for you continuing that fight, but this is not the right arena. It's good advice for developers, but it isn't a "one size fits all" solution that should be baked into the web platform, and it just doesn't make sense to me to start by carving out a little exception in the ability to detect a back button.

@aarongustafson
Copy link
Collaborator

@dominickng & @mgiuca Thank you for those additional scenarios! I have not gone back to play with Chrome OS since PWA support was added there, so I was not aware of that behavior. I’ll see about getting a VM up and running when I return from vacation.

I’ll also ruminate on the other stuff.

@comp615
Copy link

comp615 commented Jul 17, 2018

Ignoring the CSS vs JS debate, I think what I was getting at is maybe tangental / off-topic to detecting if the button exists, so we can skip it for this debate, but I wanted to clarify anyways in case it's of interest later.

Rather, I'd like to know if it was used. When a user is cruising around the site and goes back (via browser, chrome, hardware, swipe*, whatever)...essentially all we get is a popState event. We don't know how came to be popping. So while we can have per-system functionality, we cannot have per-trigger functionality. As pointed out, there's ways we can work around this (e.g. assuming any back on android closes), and the dropdown / modal is a bad example as it's screen cover based instead of input based. But I bring it up, as I remember that being a difference to android development with stack management, you could detect how the user was navigating.

@tomayac
Copy link
Contributor

tomayac commented Jul 18, 2018

@comp615 Thanks for replying! I fear like this is going deeply into Android specifics and I am not sure if having a richer PopStateEvent interface would help here either. I guess right now the best bet would be to roll your own state management, maybe similar to how history.scrollRestoration is being polyfilled.

@tomayac
Copy link
Contributor

tomayac commented Oct 22, 2018

@firtman did some research on installed PWAs in Chrome 70 on Windos7/8/10, according to which no back button is being shown.

In general, what is the status with the media query proposal?

@Malvoz
Copy link

Malvoz commented Oct 23, 2018

@fchristant

...would it also be possible to check navigation-control position or orientation? (top, bottom, etc). Example is a non-fullscreen PWA with nav docked to bottom yet the browser has its navbar also in the bottom. This can lead to serious usability issues, and is a fairly common issue on iOS, not sure about Android.

I'm talking about browser chrome interfering with docked navigation. For example, having sticky navigation in the bottom whilst browser chrome is also in the bottom. Or worse, sticky navigation in the bottom with OS gestures interfering (as seen on iPhone X).

But anyway, never mind, I think this side tracks the main issue, so I'll drop it.

The browsers navigation UI could perhaps be a predefined environment variable for web authors to access, see: w3c/csswg-drafts#2630 (comment)

@fallaciousreasoning
Copy link
Collaborator

I've put together an explainer at https://github.com/fallaciousreasoning/backbutton-mediaquery (I don't know where to put it yet, as it's meant to be part of the manifest spec. Hopefully we'll migrate it somewhere sensible in the not-too-distant future).

It's pretty much a longer version of Matt's proposal at the top of this thread. Feedback would be awesome :)

@tomayac
Copy link
Contributor

tomayac commented Feb 1, 2019

@fallaciousreasoning I have just reviewed this and like the proposal. Thanks for writing this!

@frivoal
Copy link
Contributor

frivoal commented Oct 27, 2021

FYI, This has been accepted by the CSS working group, and just landed into mediaqueries 5 via w3c/csswg-drafts#4187. See https://drafts.csswg.org/mediaqueries-5/#nav-controls (as soon as the server has had enough time to rebuild the spec)

@mgiuca
Copy link
Collaborator Author

mgiuca commented Nov 1, 2021

Fantastic! Since this has now landed in at least a draft of CSS, I will close this issue on the Manifest side.

@mgiuca mgiuca closed this as completed Nov 1, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests