Download

Good morning, everyone.

Welcome to Session 203 Introducing Desktop-class Browsing on iPad.

My name is Charles Ying, and I'm joined today by Wenson Hsieh and Beth Dakin from the Safari WebKit teams.

We are so thrilled to tell you about desktop-class browsing, a major new advancement to the web platform on iPad.

As you heard in the keynote, Safari on iPadOS is now a desktop-class browser.

Now, what does that mean? It means getting more done with Safari.

You can now use Safari for all the great things you want to do on the web. Safari is the new download manager, so you can download and upload files while using other tabs and even other apps.

Safari's new controls for full-page zoom, hiding the toolbar when you need more space to work and per-site preferences to tailor Safari to your workflow.

And Safari now has all the keyboard shortcuts you expect from a desktop browser to get things done fast and stay focused on what you're doing. But at the heart of it all is browsing desktop websites.

We've been working on web browsers for a long time.

When the original iPhone was first introduced, all websites were big and made for mouse input.

iPhone made these websites work out of the box without requiring them to change to work with a small screen and Multi-Touch.

iPhone would scale websites to fit and boost text sizes so you could read them.

Then you used new web APIs to make your websites take full advantage of iPhone's capabilities. And the results were fantastic.

Some of you used responsive design techniques to make your websites flexible on screens of any size.

Those websites look great on iPad today.

But it was also common to have two different versions of the site, one for big screens and one for small screens. iPad used the mobile user agent which meant it got small screen websites.

So new in iPadOS, iPad will now present itself to websites as a Mac.

This is not just user agent change, but a set of deep fundamental changes to WebKit that add up to a great new experience.

Let me walk you through two of these changes.

Some desktop websites are designed to show lots of information on a large screen.

These websites were zoomed in on big iPads, and you couldn't see as much information as the desktop browser.

WebKit used a viewport to lay out websites that was the same on every iPad size, the iPad mini all the way up to the largest iPad Pro. So new in iPadOS, viewports will now match iPad screen size.

Now websites can use all the space available on bigger iPads.

With smaller iPads, we'll scale websites to fit everything on screen and boost text sizes for comfortable reading just like iPhone.

You can now see more of the website at once just like a desktop computer, make it easier to get things done.

Some desktop websites are designed for mouse input.

New in iOS 13, iPad optimizes how touch input adapts to mouse input.

So websites like this one using mouse hover now work out of the box.

And for web apps that want full control, you can now use Pointer Events.

This is just a taste of the dozens of web APIs and features new in iPadOS that you can take advantage of in your web apps.

You'll hear more about them from Beth later on.

All of this adds up to a powerful browsing experience that you expect from your iPad, and that's desktop-class browsing, new in iPadOS. Now -- Now, we want to show you how your apps and websites can take full advantage of these new capabilities.

For app developers, we'll show you how to use desktop-class browsing in your app.

And for web developers, we'll show you how to make your websites work even better on iPad.

We'll start with apps.

There are four common ways apps use web views; following links to web content and browsing within your app; web browsers, where browsing is the primary focus of your app; hybrid apps, where web technologies are a part of your app's user interface and authenticating users with a third-party services using OAuth.

First up, link following.

The best way to follow links and stay within your app is to use Safari View Controller.

Just like Safari, Safari View Controller has great features, including Autofill and Reader.

So I'm happy to say that if you're using Safari View Controller, you get desktop-class browsing for free.

There's nothing for you to do.

Safari and Safari View Controller automatically choose a browsing mode to give you the best experience for your situation.

Let me explain.

iPad is the perfect device for browsing the internet.

It's like holding the web in your hands.

And depending on what you're doing, iPad can be used in many different ways.

For example, some desktop websites are pretty small on iPad mini and are more comfortable to read with a mobile browsing experience. When you look at a desktop website in narrow Split View or Slide Over, the same thing is true.

In these cases for narrow windows, iPad mini mobile browsing can be better.

Safari and Safari View Controller will automatically choose a browsing mode to give you the best experience.

Safari View Controller takes care of everything you need to follow links within your app.

What if your app is a web browser? Here's what you do.

First, build your app with iOS 13 SDK to turn on desktop-class browsing.

Then look at how you're setting the user agent. If using the custom user agent property, we recommend instead to use WKWebView configurations applicationNameForUserAgent property.

When you provide an application name, WebKit does the right thing to fill in the rest of the user agent for you.

WKWebView also is the new WKWebpagePreferences API for setting a preferred content browsing mode.

There are three modes; recommended, which does what Safari does; mobile; and desktop modes.

In most cases, Safari's recommended mode is what you want.

Your web browser might also want to let users switch between mobile and desktop modes or have per-site preferences.

For these cases, WebKit has a new WKNavigationDelegate API to specify a content mode preference at navigation time.

You'll see a demo from Wenson of these new APIs in a bit. In addition to web browsers, web technologies can be used to build parts of your app's content or user interface.

If your app uses WKWebView this way, build your app with iOS 13 SDK to turn on desktop-class browsing.

Then test your app's use of WKWebView.

In most cases, you're done. But in the rare case that you need to opt out of viewport sizing, you can set the preferred content mode to mobile. Overall, it's pretty easy.

WKWebView does most of the work for you.

Oops. Finally, web views are used to authenticate users with a third-party service using OAuth.

If you're doing this, ASWebAuthenticationSession is the best way to authenticate.

New in iPadOS, ASWebAuthenticationSession now presents a form sheet on iPad to show the authentication interface while staying within the context of your app.

Given this new presentation, ASWebAuthenticationSession loads websites using a mobile content mode.

Just like Safari View Controller, there's nothing for you to do if you're using this API. To recap, Safari View Controller and ASWebAuthenticationSession do the heavy lifting for you.

And if using WKWebView, you have all the tools you need.

And now I'd like to invite Wenson to the stage to show you a demo of how this works.

That's it.

Thank you, Charles.

So when I'm not busy working on a web browser, I like to take my mind off of things every now and then by writing more web browsers.

So today, I'd like to introduce Shiny Browser.

It's a WKWebView-based browser that I've written, and here it is on Google Docs.

So sometimes I need to collaborate with my friends using Google Docs.

For that, I'd like to be able to use my browser, but as you can see here, it just tells me to download the app.

I'll bet that if I had requested a desktop version of this website, it would give me full access.

So as I just learned from Charles a moment ago, WKWebView in iOS 13 should request the desktop version by default on my iPad Pro.

So I'm going to recompile my browser against the iOS 13 SDK, and that should give me the desktop website.

So I'm going to switch over to Xcode here, and I'm just going to hit Command-R to run. That's going to compile my app, and we should see the desktop website.

Okay, well. Unfortunately, we're still getting the mobile version.

Let's see if we can try to debug this together.

Now, let's take a step back.

Google Docs thinks I'm a mobile browser.

It's probably using the user agent string to make that determination.

What I'm going to do is connect to the web view using Web Inspector and print out the user agent string.

Maybe that'll give me a hint as to what's going on.

So now I'm going to launch Safari, go to Develop menu and target Shiny Browser here.

So I'm going to switch over to Console tab, and I'm just going to type navigator.userAgent and hit Enter.

So let's zoom in on this user agent.

There's a couple of interesting things I want to point out here.

First of all, the word iPad appears in the user agent string, so clearly it's not desktop-class.

But what's more, there's this Version/1.0 ShinyBrowser/1.0 here. It's kind of mysterious.

Where is this coming from? Well, I like to know myself, so I'm going to copy and paste into the search field in Xcode. So I'm going to Command-F to find and just paste. Okay, well as it turns out, I had code to override the entire user agent string using the custom user agent property of WKWebView.

I must have copied and pasted this from the internet at some point and forgot about it since.

Maybe it was a reasonable solution at the time, but as I've just learned from Charles, there's an alternative.

What I can use instead is the application name for user agent property on WKWebView configuration.

So let's change this to use that instead.

First thing I'm going to do is remove this custom user agent code and go up here where I'm creating my web view.

So what I'm going to do here is first create a WKWebView configuration and then set the application name for user agent to what I want, Version/1.0 ShinyBrowser/1.0.

And finally, I'm going to create my web view using this configuration. So that was a very small tweak. I'm going to recompile my app and see what I get this time. All right, so as you can see, we're now getting the full version of Google Docs, and I can finally collaborate with my friends.

That was pretty easy.

But now let's take it one step further.

One of the features that my users have been requesting for a while is the ability to switch between the desktop and mobile versions of a web page.

With the new API in iOS 13, it's now fairly easy to implement, so let me show you how.

First of all, notice I have this button in the top right-hand corner that contains some extra options like Add To Favorites and Share.

What I'm going to do is I'm going to add a third option here to allow the user to change content modes.

So that's going to either say Request Mobile Website or Request Desktop Website.

Okay. So let's jump into Xcode and implement that. I'm going to go down here, add this helper function that presents a list of UIAlertActions.

So far, as you can see, I've have Add to Favorites and Share.

What I want to do is add a third action here.

But before I do that, I'm going to make a new helper function. It's going to help me create the UIAlertAction.

And of course, I'm just going to use it up here. Okay. So that should get my action.

And now I want to draw your attention over to this currentContentMode instance variable.

So what am I doing here? Well, I'm checking if the current content mode is equal to desktop.

And if it's equal to desktop, I want the string to say "request mobile website" and vice versa. Okay, so that should make sense, but the question is "How do we keep track of currentContentMode?" How we know what it is? Well, I'm going to scroll down here where I've implemented my WKNavigation delegate, namely the didCommit navigation method, and what I'm going to do here is use a new API in iOS 13.

So in iOS 13, the WKNavigation has a new property called effectiveContentMode.

This is only ever going to be mobile or desktop and is perfect for my use case.

What I can do is set currentContentMode equal to the navigation's effective content mode when I commit a navigation, and that should help me keep track of what content is currently being loaded.

So now, I should be showing the right string.

But I want to draw your attention back to this action handler.

We still need a way to say, "Let's prefer to load mobile or desktop content for given host name." To do this, what we're going to do is save a dictionary of host names to content modes, be it mobile or desktop, whatever the user is tapping.

So I'm going to implement the action handler now, and what I'm going to do here is simply get the host name and set that in the dictionary to either mobile or desktop.

Then I'm going to tell the web view to reload.

Okay, so this is where we properly are, contentModeToRequestForHost dictionary. But of course, we still have to use it somewhere.

So I'm going to scroll down here back where I have the navigation delegate, and I'm going to add a new method.

I'm going to implement, decide policy for navigationAction.

This is very similar to the version you might be familiar with, except, in iOS 13, this one now contains web page preferences as a parameter. So this preferences right here.

So what I can do with these preferences is set the preferred content mode equal to the content mode I would get by looking it up in my dictionary.

And just as a refresher, this contentModeToRequestForHost dictionary is the same one that we populated up here in the action handler just a moment ago.

Okay. Finally, last but not least, we have to remember to actually call our decisionHandler with these new preferences, and I should be good to go.

So I'm going to recompile my app, and let's see how our feature works.

I'm also going to switch it up this time.

Instead of Google Docs, let's go to an image gallery.

So this is the desktop version of Shiny Pics.

And what I'm going to do here is use the Extras menu to request the mobile website.

So this is what the mobile website looks like, the version of Shiny Pics.

And, I mean, I can tell it's kind of the mobile version of the website, because it really looks like a page that was optimized for a phone, not so much my iPad Pro.

I think on my iPad Pro, I actually prefer the desktop version of Shiny Pics.

So I'm going to just tap Request Desktop Website, and I'm back to the desktop version of Shiny Pics.

Okay, so to recap, I first ensured that my browser can load desktop content, and then I used new API to implement a Request Mobile or Desktop Website feature.

But so far, we've only shown some new tools for you app developers in the crowd.

Of course, we have news for web developers as well.

And to tell you more about it, I'd like to invite Beth to the stage.

Thank you, Wenson.

That was great.

So what does all of this mean for web developers? If you have a responsive website, probably very little is different now.

But we do have some new tools that you can use to maintain and improve your responsive website.

I also have some best practices to share for web development on iPad so that whether your site is currently responsive or a big-screen website designed for desktop computers, you can make it a truly great experience on iPad.

There are six new features I'd like to discuss.

Some of these are purely developer features like pointer events, and some are more like new end user features that have developer implications like accelerated scrolling.

Let's start with Pointer events.

One of the biggest problems of loading desktop content on an iPad is reconciling mouse and touch input.

Sometimes websites are written so that the small-screen version uses only touch events, and the big-screen version uses only mouse events.

Our goal is to keep websites using only mouse events working as much as possible on iPad even though of course there's no mouse.

So when a user taps, WebKit will send the mouse-down, mouse-up and click event for compatibility just like always.

Most hover events get dispatched at that time too, and I'll dig into that more later.

However, mouse move really doesn't make sense on iPad. The closest analogy is moving your finger on the screen, but that's how you scroll.

So we did try to send mouse-move events at the same time that we send touch move, but it actually caused many more problems than it solved, so we decided not to do that. It conflicted with scrolling.

If you need something like mouse move, then we have a great solution, and that's pointer events.

We added support for pointer events in WebKit in iOS 13 and macOS Catalina.

This is a web standard that provides a layer of abstraction between user input and your website, so the same API can be used for input that comes from a mouse, from touch, or from a pencil.

Pointer Events are easy to adopt. If you have existing code to support mouse events on a desktop that looks like this, the code to support Pointer Events is really similar.

Just the event name is different.

But let me expand the context to show how I'll really use this. I'll actually use feature detection to determine if pointer events are supported. I'll keep the mouse-event listener in order to accommodate older clients only when pointer events aren't available.

The PointerEvent object actually inherits from mouse event, so I literally don't have to change anything about my updateInteraction function to get this to work unless I want to take advantage of additional parameters on PointerEvent that are specific to mouse or pencil input, which is huge. It really is this easy to adopt pointer events.

Pointer events can coexist with mouse and touch events. But be careful when intermingling event types that map to the same user action like here.

There's no else clause, so both of these events will be registered, and my function will be called twice every time the pointer moves, which might mess up some of my state.

Also, you don't need to register all three types of events if you want to distinguish between mouse and touch interaction on devices that support both, because pointer events allow you to know the difference.

If you want to cancel default web browser behaviors like scrolling, then on Mac you'll use preventDefault just like you would for mouse events.

On iOS, preventDefault won't lock all browser behaviors, so you should also use the touch-action CSS property. Touch-action is actually really awesome.

It's easier than writing JavaScript and it allows you to make more granular decisions than preventDefault.

Here I am using it to block all browser behaviors with the none value, but I could use it to, for example, prevent scrolling but still allow zooming.

Touch-action also enables more efficient user interactions, because it's declarative, so using touch-action and pointer events on your website may actually be a performance improvement.

So we have compatibility mouse events to keep desktop sites working, but that's a stopgap on the iOS, and the solution is pointer events.

They're easy to adopt.

They let you do everything you can do today with mouse or touch events, and they ensure quick responsive user interactions.

This is a great solution to all of the problems that we face reconciling mouse and touch input.

As I mentioned, WebKit also sends hover events for the sake of compatibility.

Hover is tricky on touch screen since the hardware just doesn't support it. We've changed the way that mouse hover detection works in iOS 13 to keep desktop sites that rely on it working well.

If you tap on an element in a web page, the response to hover will perform that hover, and if a meaningful change has happened in a web page as a result of the hover, will stop there.

The user can tap a second time if they wanted to click. In iOS 13, WebKit detects many more changes as meaningful than ever before.

This adds critical functionality to many desktop websites that rely on hover for essential parts of the web page like this web page which uses it for menus.

We're finding that this is working really well, but fundamentally this is a heuristic where the browser engine is trying to interpret design intent, so it won't always be perfect.

We have some best practices to keep in mind when you're using hover.

First, you should always provide another way to access meaningful content just in case WebKit misses something.

This is important for accessibility too. Wenson will dig into this later with the demo.

You should also avoid forcing the user to tap twice for the most common interactions.

Remember, if WebKit detects that a meaningful change happened on hover, that means that the user has to tap a second time if they actually wanted to click.

So if you have an element on your page where you think users will far more often want to perform that click than they will want to see or interact with the hover content, then consider making some changes to avoid that two-tap speed bump that you get from using hover on clickable elements.

Speaking of efficiency, keep hover snappy. If you start timers in response to hover, WebKit will wait for those timers to fire in case meaningful content is added to the web page on a delay.

In a worst-case scenario, something starts the timer, but then nothing meaningful happens, so WebKit automatically performs the click.

But in the meantime, waited several 100 milliseconds longer than usual, and the tap feels slow to the user.

We believe that we've made mouse hover detection better than ever on iOS.

Still, we suggest that you use it only as progressive enhancement and make sure your site works great even without hover.

WebKit on iOS has always had hardware accelerated scrolling for the mainframe, but I'm delighted to say that WebKit now has hardware accelerated scrolling everywhere, meaning that subframes and overflow scroll regions now have buttery smooth fast scrolling out of the box just like the mainframe.

That's right. Even subframes. On older versions of iOS, WebKit would force subframes to be the full size of their contents, so they didn't actually scroll individually and could end up being much larger than you defined them to be in your code.

Now on iPad, frames will be the size that you specify, and they'll be able to scroll just like you expect from a desktop browser.

This has been such a commonly requested feature that there were two popular ways to work around the fact that WebKit did not have support for this by default.

First, we added a CSS property -webkit-overflow-scrolling: touch; that developers could opt into when fast scrolling made sense for them.

We never made this the default, because it creates a CSS stacking context which affects the front to back layering of elements on your page.

And second, some of you built or used JavaScript libraries that would use touch events to emulate fast scrolling.

Neither of those techniques is needed now. And in fact, WebKit overflow scrolling touch a no-op on iPad. So you should test how hardware accelerated scrolling everywhere affects your content, and if you were using a technique to work around the absence of this feature in the past, you may not need it anymore. Next, I want to tell you about our new automatic viewport and text sizing behaviors.

We developed our new automatic behaviors because web pages should fit with legible text on iPad.

To elaborate, websites that weren't necessarily built for iPad should display to fit on iPad, meaning that they shouldn't scroll horizontally unless they were designed to do so, and all text should be comfortably legible for most people without additional zooming.

We need automatic behaviors to achieve these goals, because some desktop websites are built in a fixed width that is wider than an iPad.

We found that a number of sites that are very wide like this one also declare incorrectly to be responsive in the viewport meta tag, which is kind of unfortunate. Let me step back and explain what I mean by that.

The viewport meta tag was originally created to address problems that can result from displaying content that was built for a desktop computer on a small screen.

So it's perfect for this, right? Well, this particular viewport value is meant as a promise to the browser engine that the website was designed responsively, meaning that it will adaptively reflow as the window size changes.

In previous versions of iOS, we would typically take this promise at face value, and we wouldn't apply any automatic viewport adjustments on websites with a meta tag of width equals device width and initial scale equals 1.

But that led to a bad experience on many desktop websites on iPad.

This site should be able to fit, but it didn't.

So new in iPadOS, WebKit will ignore the meta tag if it promises to be responsive but then actually lays out to the width that is greater than the device width.

We found that this results in a much better experience overall.

If your website is designed to scroll horizontally and is incorrectly being scaled down, you have an easy fix.

You just need to add one more value to your viewport meta tag, shrink-to-fit=no.

This is not a new value. We added this in iOS 9 because we ran into the same problem with websites in Split View or Slide Over.

So shrink-to-fit=no will now prevent automatic shrinking for websites in Split View, Slide Over and for wide desktop websites.

You may have noticed that even though the boxes on this website shrank down to fit in the viewport, the text in the header actually got bigger, and that's because we'll automatically adjust the text size on web pages that have been shrunk down in this manner in order to keep the text legible.

If you want to control the viewport and text size on your website, the very best thing you can do is to adopt responsive design and ensure that your content adaptively lays out to fit any window size.

Responsive design is a dense topic, so I won't attempt to give a tutorial here, but there are lots of great ones online.

So in summary, WebKit has new viewport and text sizing behaviors that make web pages fit with legible text.

The best way to control this from your end is to adopt responsive design, and if you have a website that is meant to scroll horizontally, then you can get everything right with the viewport meta tag. Next, I want to tell you about the visual viewport API.

To do that, I first want to make a distinction between the visual viewport and the layout viewport.

We were just talking about our automatic viewport sizing behaviors and the viewport meta tag.

Those are used to define the layout viewport along with the screen and window size.

The window size can change when the user enters or adjusts split screen.

Responsively designed websites will react to that change using media queries or perhaps by listening to the resize event and JavaScript.

Max and min with media queries will be assessed whenever the layout viewport changes, and the resize event will fire then as well.

So if the user rotates the device, the layout viewport changes again, and your content can react to it with one of these techniques.

But then there's the keyboard.

The keyboard presents us an overlay, so it doesn't change the layout viewport, and media queries and the resize event don't react. So if the user taps on the name field here, the keyboard comes up. And clearly, that's affected what's visually onscreen.

That's what defined by the visual viewport.

The layout viewport here is the normal window size, and the visual viewport is the section that is currently visible and unobscured.

We've heard for a long time that developers want to react to changes in the visual viewport.

In this sample web page, for example, the Donate button is now obscured, and the site would be more useable if it always stayed onscreen.

In iOS 13, we can finally address this problem with a W3C standard visual viewport API.

You can use this API to monitor the resize of the visual viewport.

This event will fire when the keyboard goes on or off-screen. And it will also fire in Safari as the smart search field collapses while scrolling. Now I can keep the Donate button visible. We think the visual viewport API is a great tool for taking advantage of the big iPad screen.

Let's talk about streaming video.

Those of you who offer streaming premium video content in web browsers probably already know that HTTP Live Streaming or HLS is the best way to do it. HLS is available on iPhone, iPad and Mac. It's an easy solution to a hard problem, because it does all of the heavy lifting for you.

It works well with CDNs, and you get things like AirPlay integration for free.

However, some desktop content uses Media Source Extensions or MSE instead.

MSE is an API that enables video providers to have explicit control of the data that is served to the user. For example, you can manually upgrade or downgrade video quality in response to bandwidth changes.

If you have existing content that uses MSE, I have great news for you.

MSE is available for desktop sites on iPad for the first time in iPadOS.

If you have an existing engine that uses MSE for your desktop site, it just works on iPad. And if you use a JavaScript library that implements an MSE engine, that will work too. With both HLS and MSE as options, streaming video is more powerful than ever in Safari on iPad.

These new features should help you make your web content sing on iPad. I talked about some best practices along the way, but I have a few more to share.

Best practices help us zoom out. They guide this platform transition, but they take us into the future as our platforms continue to grow and evolve.

First and most importantly, we believe that you should build one responsive website instead of building parallel desktop and mobile sites.

I know this is easier said than done, but we really believe in using this suite of responsive design techniques to build a single site.

And you should use feature detection instead of user agent sniffing. In the past, some developers have wanted to know the user agent to specifically identify iPad, but our new desktop user agent on iPad will prevent you from doing this. I want to convince you that you don't need to know your content is on an iPad. You just need to use feature detection.

And the reason is that iPad is a chameleon.

With all of the new content mode APIs that Charles told you about, your website could be in app that is in desktop mode or in mobile mode.

Your web content could be in a Split View on either the mobile side or the desktop side.

So knowing that your content is on an iPad specifically is not really that useful.

And the confusion with user agent doesn't end with iPad. Anything is possible. We now have UIKit apps running on the Mac. Really, if you step back, we have web content on Apple Watch, on iPhone, on iPad and all of the different content modes and configurations we already covered, web content and UIKit apps on Mac, web content on AppKit apps on Mac.

Targeting each one of these configurations and handing it big-screen site or small-screen site is going to be so much more limiting, more fragile and less future proof than using feature detection to see what each configuration is capable of.

We're going to stand up here and tell you that responsive design is best practices for whatever the hot new product is year after year after year until finally we don't have to because it's the norm.

Server-delivered content that's agnostic to who asked for it but adapted with responsive design is awesome on every device.

And this device landscape has not been shrinking.

We know this takes a lot of work, but we also know it's worth it.

When we say iPad has desktop-class browsing, we mean modern desktop-class browsing, and that means no plug-ins.

We've never had plug-ins on iOS, and we don't intend to add them now.

Even on Mac, if you go out of your way to install Flash, it's still off by default, and we're dropping support for it in Safari altogether in 2020.

So if you still haven't migrated your old video or games or restaurant menus to standard web technologies, now is the time to say goodbye to Flash. Safari on iPad is desktop-class, but iPad is still a mobile device, which means that we expect it to be used on the go, often in public places.

Therefore, WebKit will prevent audio from playing automatically.

We have found some desktop websites that assume that automatic playback will happen, but it's best not to assume.

The standard media API allows you to know the truths of what actually happened, because the play function has a promise.

You should catch that promise in case playback was rejected.

But you should also consider designing your website so that all users on all devices can decide for themselves when they want audio.

Next, think beyond the mouse and hardware keyboard when designing user interaction flows.

Specifically, consider using pointer events, and avoid using mouse hover for anything other than decorative auxiliary changes.

And finally, use built-in APIs.

This is a broad topic, but I do have a specific example.

We found many desktop websites that will use mouse events in order to customize text selection or text input.

But there's a better tool for the job.

Selection change events and input events are designed for this exact purpose.

You'll always get the better behavior on every platform if you utilize built-in APIs instead of reverse engineering them with basic interaction events.

So that's a lot.

To help you digest all of this information, I'd like to invite Wenson back to the stage so that he can show you some of these new features and best practices in action.

Wenson.

All right.

Thanks, Beth.

So you'll never be able to guess, but when I'm not working on web browsers in my spare time and not-so-spare time, I also like to work on web applications.

Today, I'd like to share one I've been working on recently called Shiny Sketch.

Now, I've only ever tested this against desktop browsers, so it'll be interesting to see how it behaves on my iPad.

But first, let me give you a quick tour on my Mac.

And as I do, I'll point out some potential areas that we may need to adjust in order to make it look and feel great on iPad.

Okay, so here's what it looks like on macOS.

And first, this website has a fixed four-column layout.

What that means is as I make my window smaller, I just get horizontal scrolling, okay? So that's one thing to keep in mind.

The next is that in order to access the Edit or Delete controls, I have to hover over each drawing.

Of course, the Edit and Delete controls are really critical part of my web application.

And lastly, I'm going to go in and try to scribble on one of these drawings.

So I'm able to use my trackpad to draw here, but the code that I'm currently using to implement this only listens for mouse events.

I'm probably going to need to adjust that for compatibility with iPad.

Okay, so that's Mac.

What does it look like on iPad? Let me show you.

I happen to have the same page open in Safari.

And the first thing I notice is that the four-column layout works great in landscape mode.

However, once I rotate into portrait mode, let's see what happens.

So I still get a four-column layout.

I bet if I made my website responsive, I could avoid this scaling.

What I'm going to do now is return to my Mac and see if I can figure out what's triggering this horizontal scrolling in my browser.

Okay, so first things first.

I'm going to resize the window so I do get horizontal scrolling like this. And then I'm going to go to the Develop menu. So I'm going to go down to Start Element Selection, and that allows me to see how big each element is.

As an example, right here, this Edit button is 72 by 48 pixels.

Okay, so I'm just going to start at the top of the page and try to find a really wide element.

So this Shiny Sketch page banner or title is about 760 pixels.

If I look at the window width itself using this ruler, it's also about 760 pixels, so this is pretty well proportioned.

I'm going to move my cursor down, and as you might have guessed, the gallery section is a whopping 1,300 pixels.

So I'm pretty sure this is what's triggering the horizontal scrolling in my web app. But I want to find out more it, so I'm just going to click. That's going to open Web Inspector where I can see more about the gallery.

So in the Styles sidebar, there is indeed a hard-coded width of 1,300 pixels.

Why is this here? Well, when I added this, I think I was trying to make my website look good on really large displays, really wide displays.

I didn't think that anyone would actually load my web page with a display of less than 1,300 pixels width.

Maybe it was a reasonable assumption to make at the time, but now I think I can do better.

So what I'm going to do now is switch over and try to fix it.

I'm going to make my website responsive.

By the way, the entire web application is just three files, an HTML file, CSS file, and a JavaScript file. So I'm going to start in the HTML file where I'm going to add a meta viewport tag.

So there's two important components to this width=device-width and initial-scale=1.0.

This lets the browser know that the page is responsive at any device width. So that's important, but now let's actually make it responsive at any device width.

So let's go to our CSS file.

And remember that 1,300 pixel rule we saw earlier? I'm going to search for that.

So here we go, width: 1300px. It is indeed on the gallery.

And what I'm going to do is just change this to a max-width.

What that means is that if my window is less than 1,300 pixels, the drawings under my gallery should be able to reflow.

They're able to reflow, because I conveniently set them as display: inline-block.

Okay. So those were a couple of small changes.

Let's see how it now behaves. All right, so it's now a three-column layout in portrait mode, but if I rotate back into landscape mode, it becomes four columns. That's because landscape mode is wide enough to accommodate for the four-column layout.

Either way, there's no more scaling, and that's great.

Okay, so next let's talk about drawing.

As Beth mentioned, there's no mouse move on iPad, but luckily, I can adopt pointer events, and the touch-action CSS property to make it work.

So let's return to the CSS file. So over here, I'm going to add touch-action none, or specifically, I'm going to add it to the .drawable-canvas which, as its name might suggest, is the element that I'm going to be dragging my finger over as I draw.

Of course, it's important that as I drag my finger over, it doesn't scroll.

That's why touch-action is important.

Okay, next I'm going to go over to the JavaScript file where I'm going to look for mousemove.

Okay, so this is the code that I'm currently using to listen for mouse events so I can draw on my Mac.

I'm going to adjust this a little bit.

So instead, what I'm going to do is check if pointer events are supported.

If they are, I'm going to register pointer event listeners instead.

Otherwise, I'll fall back to the mouse events that I'm currently using.

Okay, so let's see these changes in action though.

Let's give our canine friend Sona here a little hat.

Well, it's not best drawn, but as you can see, I can clearly able to draw now. Okay, so one last minor detail.

As Beth mentioned, iPad is compatible with mouse hover, but it's not immediately clear to me that the way to edit or delete, or drawings rather, is to first tap on each drawing in order to show the controls and then tap on the controls themselves. It means that any meaningful interaction with my app is going to require two taps on iPad. Let's make it easy to edit and delete with a single tap.

What I'm going to do is add a Delete button that's always visible and just make tapping on each drawing immediately go into editing mode.

So let's do that.

First, I'm going to go over to the HTML file where I'm going to add a bit of markup.

It's a very simple button that I'm going to add here.

But note that I'm putting the class static-control here.

It's going to become important very soon, because I'm going to head over to the CSS file where I'm going to add a media query. So what I'm doing here is I'm checking if hover is supported by using this media query, and if it is supported, we don't actually need that static-control, that static Delete button that I just added.

So I can just set display: none on that, and it should hide it. Lastly, there's a small change we have to make to our JavaScript file.

So now I'm going to look for mouseenter, and this is a code I'm using to show or hide the hover controls when the user's mouse enters or leaves each drawing.

We don't need to do this if hover is not supported.

So what I'm going to do is add a bit of logic up here.

It's going to use the same media query as I did in the CSS file to check if hover exists, and if hover is not supported, I'm going to instead add a click event listener to the drawing itself and allow it to immediately start drawing mode.

Then this early return ensures that I don't add the event listeners I don't need in the case where hover is not supported.

Okay, so that was a lot of changes.

But let's see it in action now. All right, first thing I notice is that Delete buttons are now always visible and ready to be tapped.

But if I tap on each of the drawings, I'm able to edit them immediately.

So it's one tap to do anything in my app.

Okay, so with a few simple steps, I've made my desktop web app work great on iPad, and I think you web developers out there can as well.

I'd like to now hand it off to Charles to summarize what we've covered today, and I'll see you at the labs.

Thanks, Wenson. Your web app looks awesome. In summary, iPad has made major new advancements to make today's desktop websites work great out of the box.

We want you to take advantage of these new features to make your app or website even better on iPad just like you did for iPhone.

For our web developers, consider building one responsive website using the techniques we talked about today. For app developers, let Safari View Controller and ASWebAuthenticationSession do the heavy lifting for you.

If using WKWebView, please test your app.

And please file bugs and send us your feedback.

We'd love to hear from you. For more information about our other exciting authentication and web platform features, you should definitely check out Session 504, What's New in Authentication, Safari, and WebKit on Thursday at 11:00 am.

There are also two labs related to the session today at 1:00 pm and Thursday at 12:00 pm.

Please come to talk and learn more about what you heard today.

Thank you and have a wonderful WWDC.