Conf42 JavaScript 2023 - Online

Stop using JavaScript for that: moving features from JS to CSS and HTML

Video size:

Abstract

We’ll walk through common patterns that you might think can only be done with JS but actually now have native CSS and HTML equivalents that you can use, or will soon be able to use.

Summary

  • Today I'm going to show you how to use less javascript in your website. I prefer HTML and CSS over JavaScript. But just because something needs Javascript doesn't necessarily mean it's still true in modern browsers.
  • Using appearance none, we can tell the browser to not replace our form controls. Modern browsers ship something called focus visible. We need to provide a different indicator for keyboard users. That way we can have a fully interactive toggle using CSS and native browser APIs.
  • Data list is the browser's built in way to show a list of match suggestions as a user types into an input. Because the browser has more rights than you as a web page, they can even offer more functionality. You can have native form elements but still adhere to whatever color scheme your user prefers.
  • When moving from one section of the page to another, browsers jump by default. It will be much nicer to scroll the user to the new location so they have a sense of where they are now on the page. With just a single line of CSS scroll behavior, browsers can always scroll smoothly.
  • With scroll snap we can create sliders that snap to their parent elements in different ways while accepting scroll input like normal. There's a whole range of cool tricks you can do with this, like animations, overscroll effects, and other things. We can also just use HTML for an accordion and a modal.
  • The dialog element in HTML is like a better alert or a confirm or a prompt. It implements a modal dialog for you. It helps keep focus inside of the dialog so you can't tap out of it. Container queries are going to replace a ton of custom settings on all of your components.
  • Masonry layout will apply a masonry layout to grids like the Pinterest layout. Select list has all the logic and semantics of a regular select, but every part is completely stylable. It's still very early days for this element and it's still under development.
  • Hash selector allows us for the first time to style elements based on their relation to other elements. At this moment it's available in Chrome and Webkit, but it's in Firefox's nightly. It's easy to follow CSS and there's no javascript needed.
  • scroll driven animations are now available in chromium. They let you link animations in CSS to scroll positions. So those are the things you can do in modern browsers that no longer need JavaScript. I think we have a great future ahead of us once these features land in browsers.

Transcript

This transcript was autogenerated. To make changes, submit a PR.
Hi everyone. Today I'm here to tell you all how to use less javascript in. It's going to be fun. So let's get the big question out of the way first. Do I hate JavaScript? Well, I build Polypane, which is a browser for developers that's built using web technologies. So I actually write a ton of javascript all day, every day. Now this talk won't be about polypane, so if a browser specifically built for web developers sounds interesting to you, head over to Polypane app. Now, with all the JavaScript I write, you could say that I love JavaScript, but I also love CSS, and I even love HTML because it gives me the content editable attribute. So the reason I love all of these technologies is something called the rule of least power. It's one of the core principles of web development, and it means that you should choose the least powerful language suitable for a given purpose. Now, on the web, this means preferring HTML over CSS over JavaScript. Your javascript can break, it can have an error, it can fail to load, it takes actual resources to download and run, and it can exclude keyword users and people using assistive technologies. CSS and HTML, on the other hand, are declarative, and that means that you use them to tell the browser what to do and not how to do it. Your browser will instead do the heavy lifting for you, and your end user will have a better experience. Now of course, the CSS can also filter load, but if your HTML is solid and semantic, then people can still access all your content. It really is the foundation. And in recent times, both browser makers and specification writers have ported a lot of functionality to HTML and CSS that a few years ago needed JavaScript. So that's what we'll be discussing today. Now, the reason this is important is because once you learn something on the web, you never have to learn it again. Once you figured out how to implement, for example, a slider in JavaScript that then becomes part of your toolbox. And because it's the web, that thing will keep working forever, and there's never a reason for you to learn it again, even if there are now better ways available in modern browsers. So the techniques I'll be sharing today are cool, and that's why I'm showing them. But what I want you to take away is that just because you know that something needs Javascript, that doesn't necessarily mean that it's still true in modern browsers. You can make better websites if you check every now and then. If that thing you're implementing really still does need JavaScript. So let's get started. It's going to be a bit of a grab bag of features, new and old. Some of these you might know, some of these might be new to you. There's going to be a couple of live demos, so if you see me fumble about, just roll with it. Let's get started. Now we'll start with custom toggles, and when implementing them we often reach for a JavaScript solution because that then handles the click events and the states for us. But we can do the same with an ordinary checkbox and the checked pseudoclass. That way we can have a fully interactive toggle using CSS and native browser APIs, and that means that the accessibility is preserved. This is the HTML that we're going to use, and as you can see, it's pretty simple. There's a label and inside of it there's a checkbox. Now the neat thing here is that having the input inside the label already gives us stuff for free because the browser has associated this input and this label, and now I can click anywhere in the label to toggle the checkbox. This is something the browser gives us for free. But of course we can't ship just this checkbox because it doesn't look like a toggle. So we'll make it look like a toggle and we can use all of this CSS for that. Now none of this CSS is super important except for this first line here, appearance none. Now what appearance none does is it tells the browser to stop treating the input elements like replaced content. Replaced content in browsers, which are form controls and images are things that the browser will replace from your HTML. So as it builds the web page and renders the web page using your HTML and your CSS, when a browser encounters replaced content, what it actually does is it reserves that space in your HTML and then adds a native form controller, adds an image on top of it. So that's not really part of your web page. And that's also the reason why form controls are historically difficult to style. They're not really part of your HTML page. Another effect of that is that before and after pseudo elements don't work for replaced content. Because before and after pseudo elements are part of your element and that entire element gets replaced so they disappear again with appearance none. We can tell the browser to not replace our form controls. We can tell the browser essentially, let me be responsible for the styling of this element. Don't use a native form control. I'm going to do the work to make this look the way that it should look. So that's what the rest of this HTML does. And by using appearance none. We also get back that before pseudo element. So we're using the regular input as the background here, the gray part of the toggle, and then we're using the before element to add the nib inside of the toggle. Now we've added the CSS, but everything is still functional, so I can click to toggle the checkbox. Now you'll have to trust me here, but it's really toggled. Of course we need to make sure that the user also knows that it's toggled and that's where the checked pseudoclass comes in. To make that change visible, we use the checked pseudo class. This will match whenever the input would normally show the checkbox. So as I click, the checked pseudo class resolves to true and we can give our input green background and move the nip to the right to indicate that the toggle is currently on. Now there's one last thing we need to do, and that is accessibility. So we want to add an outline to the toggle when it's selected by the keyboard. If you use your mouse, it's very clear which element you're interacting with because your mouse is right on top of it. But that's not the case with a keyboard, and we need to provide a different indicator for keyboard users. Now you might have used outline none in the past for your input elements to get rid of that ugly dotted line, but that line, even if it was ugly, is important. Modern browsers ship something called focus visible. Focus visible only works when you interact with an element using the keyboard instead of the mouse. So we can use that to add our outline back in. Now, as I toggle the checkbox using my mouse, nothing happens. But as soon as I switch to the spacebar to toggle, you can see that the outline appears because it now matches focus visible. Historically, outline wasn't very stylable, but that too has changed in modern browsers. For one, as you can see, it follows the border radius of the element, but we can also offset it to move it a little away from the element. Or if you use a negative offset into the element, and that way we get quite a bit of stylability. One other thing I want you to do is instead of using outline none, I want you to use outline color transparent. The effect will be the same because the outline won't be visible for outline none because you've hidden it completely and outline color because it's there, but it's transparent. This changes, however, when a user uses forced color mode. What forced color mode does on Windows is it replaces all the colors on the screen, including the ones in your website, with a palette that the user chose. That means that your outline color transparent will be replaced with a color that the user can see if they use forced color mode, and that way we can give them additional hints without having to show that color or that outline to everyone. Now, I don't have time to go into forced color mode today, but if you want to learn more, go to polypane app forced colors, where I wrote about it extensively. Now, while we're working with forms, have you heard of data list? So, data list is the browser's built in way to show a list of match suggestions as a user types into an input. In other words, it's an autosuggest. It works like this. You add a data list element to your HTML with options and you then give it an id. You will link that id using the list attribute on any input element. Now, as I write into the input element, you can see that the data list options get filtered down to the suggestions matching what I've already written. And you can also see there's a drop down that shows you all the options to select. Now, because it's an auto suggest, you can still type in any other value, and that will work too. So that's quite a bit of functionality for a single HTML element that you would instead ship a very large JavaScript framework or JavaScript library for. Similarly, instead of shipping a JavaScript color picker with like a full canvas renderer and a little picker et cetera, you can let the browser handle it. And because the browser has more rights than you as a web page, they can even offer more functionality. So now as I open the color picker, which unfortunately doesn't get picked up, it seems. No, it unfortunately doesn't get picked up by OBS, which I'm using to record this, but it shows a native browser control with a canvas UI, with an rgb picker, et cetera, but also with a color dropper icon. And if I click that color dropper icon, what happens is that I can pick any color on the screen, including outside of the browser, and have that be the color a user selected. So that actually gives you color picking functionality from your entire screen, and that's just not something your browser will ever let you as a web page do. Now another thing that's nice about this input type color is that it's unstyled, but instead of being white it's sort of gray. And that's because we've told the browser that this input actually uses a dark color scheme. With the CSS color scheme dark, and that then lets the browser switch out the form controls from the default white ones to the dark mode set that it ships or that the operating system ships. So you can have native form elements but still adhere to whatever color scheme your user prefers. Now if you have a website that uses both dark and light mode, you can also cater for that using color scheme dark space light, and then the browser will automatically switch between them depending on which prefers color scheme or light or dark mode is active on the operating system. So that's pretty nice for just a single line of CSS. Now on to something a little bigger in page transitions, when moving from one section of the page to another, browsers jump by default. And this can be jarring and disorienting because you no longer know where on the page you are. It will be much nicer to scroll the user to the new location so they have a sense of where they are now on the page. Now in the past we might have used jquery for that, and in just a few lines of code, plus hundreds and hundreds of lines of jquery, we could get all the links that link to another part of the page. And then when you click it we find where they go to the page and then we animate HTML to it. So that was pretty awesome. But you no longer need these jquery skills because we can achieve the same thing with just a single line of CSS scroll behavior. Smooth. Now this tells the browser to always scroll smoothly when navigating to a fragment identifier, which is an id with a hash in front of it as part of the URL. Now every internal link is magically upgraded. Browsers will figure out how long the animation should take and how fast they should scroll based on how far they have to scroll and how much cpu power they have available, et cetera. And that means that the animation is always snappy and there's never a different part of your page that has to wait on the main thread to finish this animation before doing anything else. So as I click the link, it gently scrolls down to wherever I link to. Now don't worry, this is also available in JavaScript in any of the scroll APIs. So scroll to scroll by, et cetera. By adding behavior smooth to the object that you pass the function. So that still saves you shipping a bunch of Javascript where you would normally do the scroll animation yourself. Now there's one important consideration though, and that is accessibility. Because while for most people, jumping from one part of the page to another part of the page is the jarring thing for people that have vestibular disorders, it's the scrolling that actually poses a problem, as it can make them nauseous and unwell. Now browsers have a way of catering to these users with the preferred reduced motion media query, and the way to implement that is to only set smooth scrolling when the user doesn't mind motion. So instead of disabling scroll behavior when the user has prefers reduced motion reduced, we want to use that as the default case, and then only when the user indicates that they don't mind more motion with prefers reduced motion, no preference do we add the scroll behavior. So now we have smooth scrolling for the people that want that. No smooth scrolling for those that don't. And we're not shipping any javascript, so that's a great start. But we can add more. So what if we want to scroll to an element, but we want to keep a little headroom, for example, to make sure that our fixed header doesn't overlap the title we just scroll to. Well, CSS has a solution for that called scroll margin. Scroll margin and scroll padding, which also exists, work just like regular margin and padding, except they're only applied when scrolling to can element. So now as I scroll to the target, it leaves room above the element to make sure that it doesn't sit behind the header. Now if I scroll back to top where I haven't set a scroll margin, you can see that it ends up behind the header and I still have to scroll down to read the first line again. Now that sure beats manually subtracting the offset in Javascript before each animation. Right. Now, as a finishing touch, what if we want to highlight our target in some way to give it some extra prominence? For that we could use JavaScript to add a class to the target as soon as we click a link. But there's a CSS solution too, the target pseudoclass. Now when an element matches the fragment which is the little bit in the URL after the hash, which happens as a result of clicking can internal link the target pseudo, CSS will be active and then the transition will play. So now as I click to target, we can see that the animation played while the target was scrolling into view. So now we've built a way to smoothly scroll to a specific section and highlight it while keeping some space for the rest of the UI, and we're doing it all without JavaScript. What about another staple of JavaScript, scroll related features? I'm talking about image carousels or sliders. For that we have the scroll snap APIs. With scroll snap we can create sliders that snap to their parent elements in different ways while accepting scroll input like normal. So for the parent we have to tell it what the scroll snap type is. And this takes two values, an axis, which is x for horizontal or y for vertical, and a snap type, which is mandatory for always snapping or proximity. And that means it only snaps when the edge is close enough. Now what the edge is is something you define on the children, on the child elements. Here you have scroll snap align, and that takes a value of start, end or center, and it tells you what to snap to. Now, for left to right languages, start is left, but this is different in other scripts. And as you can see, I now scroll. And as I scroll it follows my scrolling one to one and it's a regular native scroll. But as soon as I release the mouse you can see that it snaps to the edge. Now by changing the scroll snap align to center, we get center aligning. So now as I scroll it will automatically center the element. Now I want you to notice that all of these cards have different sizes, and I want you to take a moment to think how much Javascript you would need to center this scroll area for randomly sized cards. And then look back at the screen and see that we get all of that with a single line of CSS. Scroll snap align, center. Now there's a whole range of cool tricks you can do with this, like animations, overscroll effects, and other things that I don't have time for. But this presentation by Adam Argal goes into the nitty gritty of scroll snapping. So check that out if you want to learn more onto two UI elements that we often solve with JavaScript, that we can also just use HTML for an accordion and a modal. So having an accordion on your page can help you keep the content organized by showing the titles of sections and only expanding the full section when a user clicks on them. Now there's a pair of native HTML elements that does exactly this, which is the details and summary elements. All contents except the summary will be hidden by default until I click the summary and then the rest is made visible as well. Now of course it's very common to have the first bit of the accordion, the first details open, so that people know that there's more content to see and that's very easy to do in HTML as well, because you can just add the open attribute to your details. Now if you've been writing react or anything else that uses JSX, you might look at this and think okay, that's great, but now it's just open forever. Luckily in HTML that's not the case, because open is just the initial state and it will update automatically as we open and close the dialog or the details element. In terms of styling, that triangle is a marker pseudo element and you can use CSS to style it, though it only supports a subset of CSS like colors and sizes. You can't move the marker to another place on the page that doesn't work, but you can, for example, switch out the triangle with an emoji of your choice, and then we can use that open attribute because it updates on the fly and switch out the design or pick a different emoji like I've done here so you can style it essentially just the way you want it, or just the way your designer designed it. Now a gotcha here is that even though the summary is clickable like a link or a button, it doesn't get a different cursor like a link, or it also doesn't look, well, buttony. So without getting into only links should have pointer cursors. I think you can improve the discoverability of your accordions by adding a hover effect and a pointer cursor to your summary, so that people know that it's actually something I can click and clicking it makes something happen on the page. Now for this next one dialog I am going to cheat a little, because for now it does need a tiny bit of JavaScript. The dialog element in HTML is like a better alert or a confirm or a prompt. It implements a modal dialog for you, and it takes care of all the things that matter. It helps keep focus inside of the dialog so you can't tap out of it. It shows a backdrop to make sure that the dialogue is what the user focuses on. It helps prevent setting that issue so you no longer have to worry about chat widgets popping up over your dialogues. And it also handles closing the element for you. And the best thing is that unlike alert, prompt and confirm, it doesn't block your main javascript thread because it's a regular HTML element. It's not a bit of browser UI. So to create a dialog you use a dialog element, but in the dialog you add a form with the method dialog. Now a dialog will be completely hidden from view. And to show it you can call the show modal function it exposes. For example, after clicking a button now as I click the button it calls show modal and it opens up this dialog. Now this dialog doesn't come with any UI that we don't ship ourselves, so there is no close button for example, and I can click anywhere I want and nothing happens. The browser expects us to implement this UI ourselves and that's where that form comes in with the method dialog. So any submit button in that form, or any form submission which you can trigger by a submit button will tell the browser that it can now close the dialog. So that's what that form does. And it's important to note that this dialog sits on top of everything and with on top of everything, I really mean on top of everything because this dialog is no longer part of your HTML page, but it sits on something called the top layer. And the top layer is a new concept in browsers that sits on top of your HTML page and you can promote elements to the top layer. That means that even the highest set index will still be below this top layer and you never have set index writing. So that's how easy it is to implement a custom confirm dialog. But what if you want to show multiple options? For example, you have a save UI where it's safe and cancel. Well you can add multiple buttons to that dialog with different values and then listen to the close event. Then when the close event is called you can get the chosen value with dialog return value. So now as I show the modal there are two buttons and depending on which button I click the return value will be wrong or correct. Now lastly, you can style the dialog however you want with rounded corners, drop shadows, whatever. It's just a regular HTML element. But you can also show a backdrop that overlays the rest of the page so it sits between your dialogue and the rest of the page, for example to dim it to make the dialogue stand out more. And you can do that with the backdrop pseudo element. Now the backdrop automatically takes the full size of the viewport without you needing to do any calculations. So all you really need to do is add a background image or other background property. Like for example here I've added a backdrop filter to blur the page, but you can really do anything you want here. Now you've probably already heard of container queries, but if not, imagine if media queries didn't look at the browser width but at the width of a parent element. Container queries are going to replace a ton of custom settings on all of your components. So container queries provide a built in way of changing the layout of a component depending on which parent element they're in and how much space there is available in that element. So if you use a component based framework, it will save you passing a prop to change your component depending on the location. For example, here we have one container, and depending on if it has a lot of room in the horizontal direction, in the inline size or not a lot of room, we flip the flex direction from having two columns to just a single column. Now one thing I want to call out here is that it's the size of the image, which I've written down as 50 CQW. You're probably already familiar with viewport units like VH, VW V min and Vmax that give you a percentage of the entire screen. Now for container queries, we now have CQW for width, CQH for container query height, et cetera that give you percentages of the container dimensions. Now, container queries are relatively new, but once it gets more widespread adoption among developers, it's going to unlock a whole new way of doing responsive design because you no longer need to keep track of where on the page you use a component because the component itself is now responsible for its responsive design. So as the component moves around the page as well as on different screen sizes, it can adapt automatically. All the things I mentioned up to now are available in browsers today, but I also wanted to give you a quick overview of some upcoming features. Each of those could be a full presentation on their own, so I'm not going to do any of them justice, but I just want to show you the cool things that are coming, like quote unquote soon, starting with masonry layout. So instead of using masonry js or packery JS, this native masonry implementation will apply a masonry layout to grids like the Pinterest layout. It's available in Firefox behind the feature flag, and it's in Safari's tech preview. And if you want to learn more, check out this meshing magazine link that is shown on the screen now and is also in the slides now. Another very cool upcoming element is select list because if you've ever implemented your own custom select using javascript, you basically did a poor job because it's incredibly hard to do a good select and take care of all the keyword interactions, of all the accessibility, of all the different ways that the browser subtly implements, selects, but a regular select element is super limited, so you kind of have to regular select can't even contain icons or whatever. So enter select list. This new element has all the logic and the semantics of a regular select, but every part is completely stylable. Now when I say it implements all the logic and semantics of a regular select, that means that I can just add a select list element to the page and have it behave like a regular select, but I can also go wild and style it. The select list itself contains a number of parts that you can style separately using the new parts pseudo function. So apart from button and list box you can also style each value, the selected value, the drop down marker and more. It's all HTML, so you can style the list box however you want. So for example, do you want to create a grid layout that just works? Do you want rounded corners that also just works? Do you want to create a full fledged emoji picker? Yeah, go ahead. A date picker also works. However, it's still very early days for this element and it's still under development. In fact, it hasn't been called select list for that long because less than a month ago it was select menu and they changed the name. Recently. They're still working on the right implementation for this element. However, if you want to play with it, you can try it out in polypane and if you set the right feature flag in other recent chromium browsers. The hash selector can also replace a ton of JavaScript because it allows us for the first time to style elements based on their relation to other elements. Now regular CSS selectors will only allow you to style based on their parent on where they are in the tree. But with the hash selector you can style based on child elements or siblings, or entirely different parts of the dom. In other words, they allow you to style parent elements based on their children, which is why they've been called the parent selector. At this moment it's available in Chrome and Webkit, so it's not quite ready for regular usage. But it's in Firefox's nightly, and that means that it's very close to being available in all browsers. So while there are hundreds of really really cool examples, I'm going to give you just one. There is a form where a user can select out of a list of options, and when they choose other, a new input field appears where people can fill in their own value. Now, instead of adding a JavaScript listener to the radio element and then hiding and showing we can do the same with this CSS selector. So now as I click other, what happens is that normally other text has display none, but then we add this has part to the selector. So when other is checked has starts to match and then the rest of the CSS selector also matches and we can style other text and show it. So that's zero listeners. It's easy to follow CSS and there's no javascript needed. Now that really scratches the surface in terms of what has can do. If you want to learn more and read up on other examples I wrote an article about hasis and where that you can read over on the polypane blog at Polypane app where is has now? As a last example, scroll driven animations are now available in chromium and what they do is they let you link animations in CSS to scroll positions. Previously, if you wanted to do any sort of animation on scroll or like a parallax effect, you'd have to use JavaScript, for example the excellent Greenstock library. But with this spec scroll driven animations you can do all of that in CSS. So this example uses no javascript at all. If you want to learn more you can go to brem us because that is a blog with a ton of cool scroll driven examples. But that's about all the time I have for this particular demo because I'm at the end of my presentation. So those are the things you can do in modern browsers that no longer need JavaScript and some that you'll be able to use soon. I think we have a great future ahead of us once these features land in browsers, and instead of managing things with JavaScript, we can let the browser deal with it based on the rules we declare with CSS and HTML. So I hope all of you now like or even love CSS and HTML a little bit more. All the live demos are of upcoming features are courtesy CEO of Polypane Shipping with experimental features turned on. You can find me on Twitter as at kiliovalkov and you can find Polypane at Polypane app. Thanks for listening and watching.
...

Kilian Valkhof

Founder @ Polypane

Kilian Valkhof's LinkedIn account Kilian Valkhof's twitter account



Awesome tech events for

Priority access to all content

Video hallway track

Community chat

Exclusive promotions and giveaways