 So Matt has been so kind all day and introduced all these lovely speakers, so I thought it was only fair if somebody introduced him today. So fun facts about Matt. If you're familiar with his previous Twitter profile photo, it was a professionally done photograph of his dog Dash, who's the tiniest little mop of a dog. He's very cute. He's our polymer mascot. He's also a terrible driver, and our co-worker Kevin shrieks usually when he's in the car. The shriek that you heard on Chris's video wasn't Chris. It was Kevin, who's at home right now, and he was just shrieking out of sheer excitement. So there you go. Matt McNulty. While you're looking at my speaker notes, PWA is a cool new thing, you guys. We've heard about it. We know our apps should be, well, sure. I'm Monica. I'm not a well-referred Twittering GitHub. That is my Polymone. May it bring you luck in battle. It's a meowing cat. It has a lot of eyes because it's an unchecked Unicode experiment. But PWA, cool new thing. We like PWAs. We want our apps to be PWAs. They should have the native feel. They should work offline. They should be awesome. They should make Jake Archibald really happy. But that's like a really daunting thing to do if you start from scratch. I have an index on HTML. It's really blank. How do I actually get to have a PWA? Would it be nice if we had an element for that? Two years ago, we used to say this a lot. There's an element for that. Turns out there is. And I'm going to tell you how to use all of them and write the least amount of code possible. So here's the thing. I've never actually built an app. And I'm super bummed because Eric Bidleman stole my joke without knowing. Because the joke is I've built single-page apps and they're not what you think a single-page app is. It's literally just an index on HTML with a lot of crappy JavaScript on it. And it's just client-side and has one route and it's index on HTML. That's the kind of things that I normally write. I write a lot of elements. I've worked on other people's apps, but I've never started with a thing. And I was like, I will make an app and it will work offline and it will have a database. So I thought, if I make the most complicated app that I can think of, and it only takes me three days, that means you guys can make literally any app that you can think of in also three days. So I looked at everything that I used every day and I didn't look at any games that I play on my phone because the challenge with games isn't to make them a PWA is to have a game idea and I don't have a game idea. So I looked at Twitter and Facebook and inbox and Reddit and whatever I use. And they're basically the same app. They look a little bit different and they serve different kind of data, but they're the same app. They have the same things in common. They've got users, it's not just me and my cat hanging out tweeting at each other. These users are authenticated and they're not authenticated because we're trying to steal their names and credit cards, they're authenticated because they look at different kind of data based on who they are. I have different Facebook friends that you do, I follow different people on Twitter. The timelines that we see are very different. And that comes from authentication. This data that they look at, it's in the cloud. I don't download the entire Facebook written word to my phone so that I can read it on the bus. I get it from a cloud and when I'm done with it, it goes away. And ideally, this data should also be available offline because if I'm on a bus and I run out of internet, I don't want to make eye contact with a stranger next to me. I don't want to keep staring at my tweets. It should obviously be responsive. It should not look the same on desktop and mobile. It should be awesome. It should be fast because I'm on a bus. If I'm looking at something, it better also not be a 12-minute loading screen. It's got to have routes because you've got to hard code to things. I can link to a post. I can link to a Twitter list. And finally, it should be accessible. And here under accessibility, I'm doing a lot of things. It should be accessible by keyboard and by voiceover and stuff like that. But I think it should also be internationalized. And this isn't a thing we talk about a lot. Here we're in England. Everybody speaks English. I'm Romanian. Not everybody speaks English in Romania. So I would like Romanian users who don't speak English to also be able to use my app. What I didn't write on this slide here was push notifications because at the time, I was like, ain't nobody writing a server. I'm not writing a server. No push notifications because Michael Bly didn't tell me he was going to solve push notifications with Firebase. So maybe in a week we'll have that done. So let's build this up. This is my app. It's called MojiBrag. You can, oh, nope, nope. Let's try this. Let's try to click on the button. Nice. So this is the app. It's basically like a Twitter Slack clone. Everybody posts emoji things. You can like them. You have different channels. You can create private channels with only your friends. You can create global channels that everybody can read. And every time you type in it, any English that you type gets translated to emoji because, obviously, why not? If there's multiple words that translate to the same emoji, you get to pick your favorite one. This app is also internationalized, which means you're about to see it in traditional Chinese, which is kind of exciting. It's not a language that I speak. All the languages that you saw out there are the languages that the people in the Polymer team speak. So basically I just harassed everybody that I know. And I was like, translate these strings for me. It'll be great. Do less, be lazy. So Steve has said this. Everybody has said this all day. I said it in Google I.O. Do less, be lazy. Somebody asked me on Twitter, Steve said it first. He got it from this weird podcast that he listens to, where it's a man yelling at you to do less and be lazy and be more efficient. But I've embodied this in my life. I try to do the least amount of things possible. I am the laziest human being. I enjoy sitting on the couch and getting up when I run out of food on the couch. And this is exactly how I built this app. I looked at all of the elements everybody else wrote, and I took them, and I was like, not writing anything else other than that. And thankfully, the beginning is the Polymer CLI. You run one line of code, and you get a freaking app. You download the starter kit template, which has got everything that you possibly need. Look at it. It looks amazing. It's responsive. It looks different on mobile and on desktop. It has routes. It has three different views that already have different content. You can link to them. I've got two items off the list. Even how a service worker, so my app already works offline. Setting up a service worker isn't that easy. But the Polymer CLI does it for you. It even has a built pipeline, so it bundles all of your things so that you only make one HTTP request for all the bucket of dependencies that you need. This is amazing. I've made so much progress, and all I've done is a copy-paste something from our website. Let's talk about authentication. This was the thing that I was the most scared out of, because previous times that I've tried to look at OAuth in the eye, I can't even pronounce it. This is trauma. It was awful. It was not great. There was many different kinds. And my callbacks didn't work. My tokens weren't right. And nothing ever worked. But you heard my goal, Firebase. Firebase is that knife that cuts through the shoe and also the coke can. You can use it for literally everything. And Firebase and Polymer are kind of magical together, because they both have the same goal. They will try to do all the heavy lifting for you so that you get to be a lazy developer and get everything for free. Firebase app is an element from Polymer Fire, which I will call PolyFire, because that's what it is in my head for some reason. Sorry, Chris. So with this one element, you get like three things off the back. You initialize your connection to Firebase, which gives you, bam, a database, sorted, bam, an auth domain, so that you can actually authenticate with your favorite authentication provider. You get a storage bucket so that you can actually deploy your code on something that isn't GitHub Pages. You get SSL. You get HTTPS. That's like many step stores being a PWA. And with OneLine, you can actually authenticate. Here, I'm choosing Google to be my authentication provider. And when authentication completes, I get this user property. So I can bind things from this user property. I can bind the avatar or the name somewhere in my app and display it. And if I got an entire app with OneLine and Polymer, I get authentication with OneLine and Firebase. That is the OneLine. This is A plus for lazy. This is how you log out. And every time you change your state, you get a callback. It's on off-state change. And you can respond to this user property. This is amazing. That's it. That's all we have to do for authentication. Problem solved. The next thing I was scared of was databases. So story time. I used to work for a bank. And if you know anything about banks, they run on, you know, they have big databases that are usually, like, really terrifying SQL or side-base. And they have really scary tables that are indexed weirdly. And you have to do a lot of joins and ordered buys. I don't really know what that means. So the idea that I had to do this on my own was like nobody holding my hand. Not a good idea. Guys, Firebase. Firebase is everything for you. It does a database. And it's a really nice database. And Polyfire gives us elements. Firebase query is, oh, no, no. I wasn't ready. Firebase query is basically like a declarative select star. It gets the entire contents of a query and it puts them somewhere. And app indexed to be mirror is this amazing magical thing that basically proxies that data and holds it so that it can serve it to the application whether your application is online or not. This is amazing because this means you're getting offline data basically for free. And what I mean is this. In your normal situation, you have a database and you have your app. And you get stuff from the database and you show it on your app. And it's really great. The mirror sits in the middle. So you get your data from the Firebase query. You put it in this intermediate mirror. And then you serve that data to the app. This is really great because if your internet connection goes away, you no longer have live data, you can't talk to Firebase to get this amazing new emoji feed. The mirror still has those posts. It can continue serving all of this offline data until the database comes back online and it can get new data from it. And it's really easy to set up. From your query, you're querying from a path and it returns to you some data. And I'm putting it here in a private variable. You can tell because it has an underscore at the beginning. And that's our Polymer convention that if it has an underscore, you probably shouldn't muck around with it. And then from my indexed to be mirror, I'm basically going to index it by this query that I made so that I don't stomp over my data. And then I'm going to take this data that I got from Firebase and put it in this little intermediate place where the indexed to be mirror has to massage it. And when it's done massaging it, it produces this post variable. And this is my persisted data. It's the data that I'm going to use in my application. If the Firebase query goes away, this posts will still be populated and will still continue being used even if I refresh my app. You can debug this in the Chrome Dev Tools in the Applications tab, which is the most useful tab for everything. It tells you why your service worker is misbehaving, why your indexed to be mirror is misbehaving. And here on the left, you're going to have a little entry for indexed to be. I guess it is on my left. And what you actually see as the values are all these queries that I've made. Channels is all my private channels. And then there is a list of posts for a particular channel that super confusing name is the key of a channel. And that's my data. And that's my data. And it's hanging out there in the browser. And it gets updated every time you push new data to Firebase, which is pretty great. So now take a step back. I wrote two lines of JavaScript to sign in and sign out. I copy-pasted some elements from other people's demo apps. And I basically have a real app. It has authentication. It has a database. It has a service worker. It has views. I'm doing really good. I can actually make my app now. I can start thinking about how it works. And thinking about how it works is really important. Because I find that if you think about what an app does rather than how it does it, it's actually really easy to reason about it. And you don't get muddled down with features that you don't need to think about yet. And if you think about features, as Ellie mentioned earlier, features translate really well to web components. So think about this. Somehow, we need to authenticate our user. And once we authenticate our user, we can show them something. And that's something that can aggregate a whole bunch of different elements. It can probably give you the option to look at all of the other emoji posts that people have written. And you can write an emoji post, because that's how we get a list. And once you figure this one out, you can look back at each of these individual elements and figure out what they should actually do. Well, when I look at posts, maybe I should be able to interact with them. Maybe I can delete them. Maybe I can like them. And when I'm writing a post, I definitely have to emulate that. So that's going to be a thing that I'm going to have to figure out. But if you want to figure out what your application does, just look at the features that it needs to do. And once you do that, they translate really easily to web components. That authentication bit is probably going to use something like Firebase Auth, because, you know, that's the one element that we know does authentication. The app is probably going to be like this MyApp element that's going to contain a whole bunch of other elements, like new post, where I'm going to bang on, mash on the keyboard. The list of an iron list is going to show my posts and be really fast. And this is great. I have an architecture now. And I still didn't actually have to implement these elements. I just have to think about implementing them. And this was actually my original architecture for the app. But then as the app was growing, this mess that I've highlighted was super messy and it was growing out of control. And it turned out the auth element was poking at the insides of the MyApp element, which is usually like a code smell and you have to take a step back and be like, probably shouldn't be doing this. So then I reworked it. And what ended up happening is that I have one element, the main one, MyApp, and it delegates to two different views. If you're logged in, it will show you the main screen. If you're logged out, it will ask you to log in. And then the main screen again will delegate to elements that it has. And once you think about your application this way, data comes really easily. As Grace said earlier, or I'm just going to paraphrase if you didn't, properties grow down and events go up. Elements at the leaf nodes shouldn't really be poking up on anything above them. They shouldn't be trying to figure out what data other elements are storing. And things at the top shouldn't be really poking directly at functions at elements below it. They should just pass data through data bindings. And it kind of looks like this. If you think about your main screen and your new post, new post lives inside the main screen. The main screen has some information about what the state of the world is. You might know what channel you're looking at and what channel you're trying to post in. The new post doesn't really know that. It's just the main app that does. So we're going to have a property that is going to come from the main app to the new post. And then when you're done mashing on the keyboard and you press the post button, the new post element will say, hey, I got a post. Does anybody care? Anybody care? Then the main app cares and is going to listen to that event and intercept it and do something with it. Similarly, the main app might know that we have suddenly gone offline and we need to hide some UI. And the new post will have a observer to that property and then do something, like hide its post button or something like that. And that's basically how properties go down and events go up. If your app doesn't really do that and you find that you're calling too many private or public methods, maybe you want to reconsider and take a step back and think about how you can rewrite it to have a good unidirectional data flow. So let's talk about offline because you saw me there responding to this offline property. One of the big things of a PWA is that it needs to work offline. And as much as I love the dinosaur game, if it doesn't work offline, I really want to look at all of my emoji and I really want to look at my entire list of things. So this is why it needs to work offline. And we have a service worker already. So we know that it does that. The application will load and Firebase will serve the right data. But one thing that we want to consider is that you might want to show different UI based on what your actual state is. If you're, it doesn't make sense maybe to show your authentication screen when you're offline because you can't actually authenticate. You need to speak the server. So the way we do this is with, excuse me, the navigator.online API, which basically gives you a Boolean whether you're online or not. It also fires events online and offline, very creatively named. And you can respond to these events and store the value in a property. Once you have it in a property, you can start binding through it. So your UI can magically show and hide based on what the state of your application is. And this is how you would do it. If I have a sign in button for you to authenticate with your provider and you're offline, this button will just magically go away and you can replace it with a nice informational message that, hey, buddy, can't really do anything if you don't have an internet connection, but let me know when you're back. And MojiBrag does that. On the right-hand side, you see a nice yellow sign-in button that lets you connect to Google. But if you're not offline, if you're offline, you get just a message instead. And this UI doesn't actually need any JavaScript. It's just like binding through that offline property. You can also do this for individual elements in your application. So if you've gone offline, that new post goes completely away and any interaction that you can do with the application is sort of frozen. You can still scroll and read things. You just can't post or modify any of those posts. Amazing. So you have an app. It's like the app that we need and we deserve right now. It does everything that we want. We just have to make it fast because nobody really wants to look at a slow app. We already did things as Steve told us to do. We did less. We used an iron list. We didn't use a DOM repeat that had 100 posts in it. We used an iron list. That's an element that's explicitly made to display a really long list and make them really fast. So that's cool. We were also really lazy because we did not import things if we didn't actually need them. And that's where the import href that you also saw in Steve's talk. So import href basically lets you lazy load an element so that you can only bring it if you need it. This is part of the purple pattern. I have to mention it. It's basically like Polymer Bingo at this point. The problem is that because I don't have a server, I actually just have the ERPL part of purple, which I want to call Ripple and I want to brand it as my own pattern but nobody's going to let me. And the way that I do it is like this. This is my main app. And I'm importing that sign-in view because if you're signed out, there's no point in me doing all the work to assemble that list and the list of channels and the beautiful yellow background and stuff, whatever I have. I have the moji feed element. So if I'm ready to import it, it's there in the DOM. It can eventually get upgraded. But I'm only going to do that if my authentications stay changed. Remember, Firebase fires this callback for me when I finally get a user. And if I do get a user, then I can lazily load that element. And when the callback, the success callback is called, I can start doing things. I can maybe hide the existing splash screen. I can do something to that other element. You can also do this again for little components in your app. One of the things that MojiBrag lets you do is create a channel. You can create a private channel or a public channel, whatever you want. The likelihood of every user that goes to this app on every instance is going to click on that button is incredibly low. And I know this because the app has been live for like four days and people have only created like five channels. So there's no point in me actually loading the DOM to create a new channel. When I press that button, I'm going to go ahead and lazily import that little DOM element. And I can add a spinner because this loading might actually be a little slow. So you can add a spinner while it's loading and then it appears magically. But don't do any work that nobody asks you to do. Again, this is like a life lesson. Don't do any work that nobody has asked you to do. Nobody's gonna care. The way you're gonna test that your app is really awesome is with Lighthouse. Lighthouse is a cool extension, a Chrome extension that tells you how much of a PWA, your PWA actually is. My PWA was an 88, which was neither good nor bad. It was doing some terrible things. But I was pretty proud. 88 is a good grade. It's not amazing, but you're doing okay. And then I talked to Paul Lewis. And here's the thing about talking to Paul Lewis. At no point in your life will Paul Lewis look at your app and be like, you did good, son, you did great. Paul Lewis will look at your app and be like, your app is slow, make it faster. I'm done looking at your app. So he looked at my app and my app on the slowest connection, the slowest 3G connection in, which is in Dulles, took eight seconds to load. And eight seconds is a hell of a long time. Here it is slightly faster. This one only takes about six seconds to load. The white part, I can't control. It's the time it takes me to download a byte on that connection. The blue part is the time it takes the browser to upgrade my element. I'm literally staring at nothing while this is happening. And then you saw that elements finally started coming in and upgrading. 5.3 seconds is a long time to look at your phone to discover this is a dumb app that just uses emoji. I could have bailed so much earlier on, but I had to wait for 5.3 seconds because I wrote a shitty app that took five seconds to load for me to figure out I didn't want to use it. And we can do better. Fake it till you make it is part of being lazy. So here's the thing about how the parser works. This is like browser parsing 101. The parser is gonna go through your element. Let me actually show you this screen. As I get to the My App tag, and it's gonna be like, I don't know what a My App is. I don't really care. I'm gonna skip it. It's gonna go to the div and be like, oh, I definitely know what a div is. So it's gonna start rendering this content that it actually knows. This is like the second it downloaded this byte. And then later on when it gets to the import, because at some point you imported this My App element. So when that import completes, when all the dependencies have been downloaded, the browser is gonna be like, oh shit, I do know what a My App is, hold on. And it's gonna go created here, and it's gonna stamp over everything that it already existed, and it's gonna replace the content. And this is what I mean by fake it till you make it. While the browser is trying to figure out where your custom element actually is, you're still showing some content to the user. After that two seconds that it took me to download the first byte, I can tell the user, buddy, this is a really dumb application about Emoji. If you're not invested in it, you can like go now, it's fine. And this is what EmojiBrag does. This is the loading screen. This is like plain HTML. It's not a custom element. It's just like that local DOM that I had, I threw on the page. And then when My App upgrades, it changes a loading screen with a sign in button. You as a user don't really know that a lot of magic and like a goat sacrifice happened behind. But like the browser actually did a whole bunch of work, but you're delighted, you're like sweet, you're gonna ask for my location. I know what it's gonna be for. For the record, nobody reads that message and then everybody's surprised when EmojiBrag asks you for a location and then you bug me on Twitter while I'm asking you for a location, even though I try to tell you, read your splash screens, people. And the end result is much better. So when I have the change, so two seconds later, I instantly show you a message and then I upgrade and it also got a little bit faster because I deleted some shitty code. But it was still great. Look, we can do it again. Look how nice, it's like with change. Even though you're on the slowest internet connection in America, you're like sweet, I got something super fast, I feel good about myself. Every other website that I visit on this phone takes like eight seconds to load, but EmojiBrag, this like dumb application with Emoji, is four seconds, it's amazing. Fake your content, people. Give, first paint is really important. First paint is really important when your internet is incredibly slow. First paint is really important if you have to work with Paul Lewis, who's gonna bug you about it until your first paint is gonna be really fast. Have a little Paul Lewis at the back of your head that tells you all the time, it's too slow, it's too slow, it's too slow. Make it better. The last thing we have to talk about is internationalization. And it's not because it's like not important that I left it at the end, is that it's so easy that you can leave it till the end. And here's the story about that. The way you make a button in your application is by typing some letters out. I speak English, I put sign out as my sign out button. But imagine that you didn't hard code your string and instead you bound it to the result of this magical localized function that just took a key. And this key can live in a JSON file. In a language, you have a whole bunch of a list of keys and they represent translated messages. So you would have sign out in English be sign out in French would be the connection. In traditional Chinese, it would be that. And all of this works because of an element. It's called app localized behavior. That's what it does is job is to translate stuff for you. It has like two properties, one of them is a language, one of them is a resources file, or is a resources object, sorry. And the way you do that is by calling this.loadResources and you give it a JSON file. If you don't want to use a file, you can actually just like, you know, type out your DOM, your JSON object in the element. App localized behavior doesn't care. If you're trying to load the same file multiple times, it caches it for you so that it doesn't actually load the same file multiple times. It tries to like do everything for you. And the result is super easy. All you have to do is just replace all of your hard coded text with, you know, these keys and localized function and ask all of your friends with languages they speak and make them translate 25 strings. And the result is this. This is Mojibrag in English. This is a whole bunch of languages. This is Mojibrag in traditional Chinese. And it's so easy and it's so exciting because in the last four days that I've like had this shipped on Twitter, I received three different pull requests for people adding new languages. They were like, sweet, here's German, here's Portuguese, here's Ukrainian. I don't speak any of these languages, but now this app is available sort of because it only translates English, Mojibrag, but it's a different problem. But now this app is available to a whole bunch of users that I wasn't able to access before. Bringing emoji to the people, that is my job. So what did we do? I can tell you what we didn't do and that's write a lot of code because remember, lazy as developer out there didn't want to write any code. I basically just took all these awesome elements that my coworkers wrote, put them together and wrote a little bit of like HTML and CSS and JavaScript just to make it look not terrible. We used a Polymer CLI to start with a basic application that gave us app layout. So our application was really nice and responsive. It gave us a service worker so our application worked offline. We used Polyfire to talk to Firebase. God bless Firebase, my hero. You got easy authentication. You got an easy database, a little bit of work. We thought about our application as being this collection of elements and the way elements talk to each other is by properties go down, events go up. Properties go down, events go up. Repeat this to yourself, your new mantra. Do less be lazy, properties go down, events go up. Make your application faster, make your application faster. We just navigated online to figure out if we're online or not and show conditional UI space on that and make our users feel happy and not click on a button that doesn't do anything because you're not connected to the internet. We made our application fast by doing less and using elements that are fast and try to render less dumb. And we were lazy. We didn't import any elements that we didn't actually need. And finally, we localized things. We translated to different languages with app localized behavior, who just does that for you, which also doesn't have a U in the name because I'm Canadian, English people, you appreciate this. I put a U there for you. At work, they forced me to spell behavior without a U and it's incredibly painful. So I thought in my slide, I can finally win this one back. MojiBrag.Firebaseapp.com is where it lives. If you look at this talk or at the repo, it's open source and you make an app of your own, please tweet at us, please show it to us. We're gonna be very excited. I'm Monica, have a lovely evening.