Front End Web Development

Front-End Dissatisfaction (and Backing Off)

Css Tricks - Sat, 02/13/2021 - 8:21am

Asko Nõmm reached a breaking point with front end:

I want to have a personal life and not have to spend my nights reading up on some new flavour of *.js in fear that if I don’t I would soon be made irrelevant. I don’t want to learn nor use a million different tools. I don’t want to know a bit about everything and a lot about nothing.

Thus, I don’t want to do front-end development anymore. The joy is gone.

They literally spun up this blog to say that, but money-where-mouth-is:

I’ve given in my resignation at my current place of employment and will be seeking an exclusively back-end role for my next adventure

I have some doubts that back end is 100% better in regards to technology churn, but fair enough, I don’t hear about it as much. Front-end dissatisfaction is awfully high. I don’t go a day without hearing someone complain broadly about the state of front end.

Remy Sharp addressed this in The web didn’t change; you did:

If you didn’t gather off the bat from the title, the problem with developing front end projects isn’t that it’s harder or more complicated, it’s that you made it harder and more complicated.

Minor pushback there: a lot of people don’t get any choice in the technologies they are tasked with.

Remy’s point is that literally any simplicity that you hold nostalgia for on the web is still there and there is nothing stopping you from using it. Other than, ya know, if your client or boss prevents that.

Marc (last name appears intentionally not-on-the-internet) says that just HTML is a perfectly fine building tool:

Despite increasing leniency on frameworks being the only way to build for the web, hand-written HTML never disappeared and I feel is still a perfectly suitable way to build a personal website.

Remember Steren Giannini said recently they build websites with HTML alone and zero build process. And Terence Eden praised HTML for its unreasonable effectiveness:

Are you developing public services? Or a system that people might access when they’re in desperate need of help? Plain HTML works. A small bit of simple CSS will make look decent. JavaScript is probably unnecessary – but can be used to progressively enhance stuff. Add alt text to images so people paying per MB can understand what the images are for (and, you know, accessibility).

It’s nice to think that you can build an important website, avoid any sort of wild complexity, and have it do its job without any harm, with HTML.

Personally, I don’t harbor any ill will toward the front-end ecosystem at the moment. I’m aware that I can step backward from complexity if I have to, and that I can lean into complexity when it buys me things (speed, features, DX, etc) and know what it costs me (and users) and why.

The post Front-End Dissatisfaction (and Backing Off) appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Weekly Platform News: WebKit autofill, Using Cursor Pointer, Delaying Autoplay Videos

Css Tricks - Fri, 02/12/2021 - 11:53am

In this week’s roundup, WebKit’s prefixed autofill becomes a standard, the pointer cursor is for more than just links, and browsers are jumping on board to delay videos set to autoplay until they’re in view… plus more! Let’s jump right into it.

CSS ::-webkit-autofill has become a standard feature

Chrome, Safari, and pretty much every other modern web browser except Firefox (more on that later) have supported the CSS :-webkit-autofill pseudo-class for many years. This selector matches form fields that have been autofilled by the browser. Websites can use this feature to style autofilled fields in CSS (with some limitations) and detect such fields in JavaScript.

let autofilled = document.querySelectorAll(":-webkit-autofill");

There currently does not exist a standard autocomplete or autofill event that would fire when the browser autofills a form field, but you can listen to the input event on the web form and then check if any of its fields match the :-webkit-autofill selector.

The HTML Standard has now standardized this feature by adding :autofill (and :-webkit-autofill as an alias) to the list of pseudo-classes that match HTML elements. This pseudo-class will also be added to the CSS Selectors module.

The :autofill and :-webkit-autofill pseudo-classes must match <input> elements that have been autofilled by the user agent. These pseudo-classes must stop matching if the user edits the autofilled field.

Following standardization, both pseudo-classes have been implemented in Firefox and are expected to ship in Firefox 86 later this month.

You can use CSS Grid to define spacing in buttons and links

In the article “Let’s Bring Spacer GIFs Back!” Josh W. Comeau argues for using a “spacer” <span> element instead of a simple CSS margin to define the spacing between the icon and text of a button component.

In our home-button example, should the margin go on the back-arrow, or the text? It doesn’t feel to me like either element should “own” the space. It’s a distinct layout concern.

CSS Grid is an alternative to such spacer elements. For example, the “Link to issue” link in CSS-Tricks’s newsletter section contains two non-breaking spaces (&nbsp;) to increase the spacing between the emoji character and text, but the link could instead be turned into a simple grid layout to gain finer control over the spacing via the gap property.

Websites agree that the pointer cursor is not just for links

The CSS Basic User Interface module defines the CSS cursor property, which allows websites to change the type of cursor that is displayed when the user hovers specific elements. The specification has the following to say about the property’s pointer value:

The cursor is a pointer that indicates a link. … User agents must apply cursor: pointer to hyperlinks. … Authors should use pointer on links and may use on other interactive elements.

Accordingly, browsers display the pointer cursor (rendered as a hand) on links and the default cursor (rendered as an arrow) on buttons. However, most websites (including Wikipedia) don’t agree with this default style and apply cursor: pointer to other interactive elements, such as buttons and checkboxes, as well.

Another interactive element for which it makes sense to use the pointer cursor is the <summary> element (the “toggle button” for opening and closing the parent <details> element).

CodePen Embed Fallback Browsers delay autoplay until the video comes into view

Compared to modern video formats, animated GIF images are up to “twice as expensive in energy use.” For that reason, browsers have relaxed their video autoplay policies (some time ago) to encourage websites to switch from GIFs to silent or muted videos.

<!-- a basic re-implementation of a GIF using <video> --> <video autoplay loop muted playsinline src="meme.mp4"></video>

If you’re using <video muted autoplay>, don’t worry about pausing such videos when they’re no longer visible in the viewport (e.g., using an Intersection Observer). All major browsers (except Firefox) already perform this optimization by default:

<video autoplay> elements will only begin playing when visible on-screen such as when they are scrolled into the viewport, made visible through CSS, and inserted into the DOM.

CodePen Embed Fallback

(via Zach Leatherman)

Chrome introduces three new @font-face descriptors

Different browsers and operating systems sometimes use different font metrics even when rendering the same font. These differences affect the vertical position of text, which is especially noticeable on large headings.

Similarly, the different font metrics of a web font and its fallback font can cause a layout shift when the fonts are swapped during page load.

To help websites avoid layout shift and create interoperable text layouts, Chrome recently added the following three new CSS @font-face descriptors for overriding the font’s default metrics:

  • ascent-override (ascent is the height above the baseline)
  • descent-override (descent is the depth below the baseline)
  • line-gap-override
@font-face { font-family: Roboto; /* Merriweather Sans has 125.875px ascent * and 35px descent at 128px font size. */ ascent-override: calc(125.875 / 128 * 100%); descent-override: calc(35 / 128 * 100%); src: local(Roboto-Regular); }

The following video shows how overriding the ascent and descent metrics of the fallback font (Roboto) to match the same metrics of the web font (Merriweather Sans) can avoid layout shift when swapping between these two fonts.

The post Weekly Platform News: WebKit autofill, Using Cursor Pointer, Delaying Autoplay Videos appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Don’t put pointer-events: none on form labels

Css Tricks - Fri, 02/12/2021 - 10:03am

Bruce Lawson with the tip of the day, warning against the use of pointer-events: none on forms labels. We know that pointer-events is used to change how elements respond to click, tap, hover, and active states. But it apparently borks form labels, squashing their active hit target size to something small and tough to interact with. Bruce includes examples in his post.

That’s not the striking part of the post though. It’s that the issue was pinned to an implementation of Material Design’s floating labels component. Bruce fortunately had pointer events expert Patrick Lauke’s ear, who pointed (get it?) out the issue.

aha, now i remember when i first saw a few weeks ago – testing something based on material design for web https://t.co/YkEKXkU0To pic.twitter.com/31S74X1i4R

— patrick h. lauke #toryScum #clapForFlagWankers (@patrick_h_lauke) February 5, 2021

That isn’t a dig at frameworks. It’s just the reality of things. Front-end developers gotta be aware, and that includes awareness of third-party code.

Direct Link to ArticlePermalink

The post Don’t put pointer-events: none on form labels appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Responsible Web Applications

Css Tricks - Fri, 02/12/2021 - 10:03am

Joy Heron bought a cool domain name and published an article there:

Luckily, with modern HTML and CSS, we can create responsive and accessible web apps with relative ease. In my years of doing software development, I have learned some HTML and CSS tips and tricks, and I want to present these in this post. This list is not exhaustive, but these are tried and true patterns that I frequently use in different projects.

Sure, it’s a collection of tips and tricks, but it’s a great one that covers modern best practices across HTML, CSS, and JavaScript. If someone asked me what they should read if they missed out on the last, say, three years of front-end and wanted to remind themselves of the important stuff, I’d send them this.

I like the casual use of a massive shape-outside in the header.

Direct Link to ArticlePermalink

The post Responsible Web Applications appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Reconciling Editor Experience and Developer Experience in the CMS

Css Tricks - Fri, 02/12/2021 - 5:56am

Components are great, aren’t they? They are these reusable sources of truth that you can use to build rock-solid front-ends without duplicating code.

You know what else is super cool? Headless content management! Headless content management system (CMS) products offer a content editing experience while freeing that content in the form of data that can be ported, well, to any API-consuming front-end UI. You can structure your content however you’d like (depending on the product), and pull that content into your front-end applications.

Using these two things together — a distributed CMS solution with component-based front-end applications — is a core tenet of the Jamstack.

But, while components and headless CMSs are great on their own, it can be difficult to get them to play nicely together. I‘m not saying it‘s difficult to hook one up to the other. In a lot of cases, it’s actually quite painless. But, to craft a system of components that is reusable and consistent, and to have that system maintain parity with a well-designed CMS experience is a difficult thing to achieve. It’s that win-win combo of being able to freely write content and then have that content structured into predictable components that makes headless content management so appealing.

Achieving parity between a CMS and front-end components

My favorite demonstrating this complexity is a simple component: a button. Let‘s say we’re working with React to build components and our button looks like this:

<Button to="/">Go Home</Button>

In the lovely land of React, that means the <Button> component has two props (i.e. properties, attributes, arguments, etc.) — to and children. children is a React thing that holds all the content within the opening and closing tags, which is “Go Home” in this case.)

If we’re going to enable users in the content editor to add buttons to the site, we want a system for them that makes it easy to understand how their actions in the CMS affect what appears on screen in the front-end app. But we also want our developer(s) to work productively with component properties that make sense to them and within the framework they’re working (i.e. React in our example).

How do we do that?

We could…

…use fields in the CMS that match the components’ properties, though I’ve had little success with this approach. to and children don‘t make much sense to content editors trying to build a button. Believe me, I‘ve tried. I‘ve tried with beginners and experienced editors alike. I‘ve tried helper text. It doesn’t matter. It’s confusing.

What makes more sense is using words editors are more likely to understand, like label or text for children and url for to.

&#x1f615; &#x1f913;

But then we’d be out of sync with our code.

Or what if we…

masked attributes in the CMS. Most headless CMS solutions enable you to have a different value for the label of the field than the name that is used when delivering content via an API.

We could label our fields Label and URL, but use children and to as the names. We could. But we probably shouldn’t. Remember what Ian Malcolm said?

On the surface, masking attributes makes sense. It’s a separation of concerns. The editors see something that makes them happy and productive, and the developers work with the names that make sense to them. I like it, but only in theory. In practice, it confuses developers. Debugging a content editor issue often requires digging through extra layers (i.e. time) to find the relationship between labels and field names.

Or why not …

…change the properties. Wouldn’t it be easier for developers to be flexible? They’re the ones designing the system, after all.

Yes, that’s true. But if you follow that rule exclusively, it’s inevitable that you’re going to run into some issue along the way. You’ll likely end up fighting against the framework, or props will just feel goofy.

In our example, using label and url as props for a button works totally fine for data that originates from the CMS. But that also means that any time our developers want to use a button within the code, it looks like this:

<Button label="Go Home" url="/" />

That may seem okay on the surface, but it significantly limits the power of the button. Let’s say I want to support some other feature, like adding an icon within the label. I’m going to need some additional logic or another property for it. If I would have used React’s children approach instead, it would have just worked (likely after some custom styling support).

Okay, so… what do we do?

Introducing transformers

The best approach I’ve found is to separately optimize the editor and developer experiences. Craft a CMS experience that is catered to the editors. Build a codebase that is easy for developers to navigate, understand, and enhance.

The result is that the two experiences will not be in parity with one another. We need some set of utilities to transform the data from the CMS structure into something that can be used by the front-end, regardless of the framework and tooling you’re using.

I call these utilities transformers. (Aren’t I so good at naming things!?) Transformers are responsible for consuming data from your CMS and transforming it into a shape that can be easily consumed by your components.

While I‘ve found that transforming data is the smoothest means to get great experiences in both the CMS and the codebase, I don‘t have an obvious solution for how (or perhaps where) those transformations should happen. I‘ve used three different approaches, all of which have their pros and cons. Let’s take a look at them.

1. Alongside components

One approach is to put transformers right alongside the components they are serving. This is the approach I typically take in organizing component-based projects — to keep related files close to one another.

That means that I often have a directory for every component with a predictable set of files. The index.js acts as the controller for the component. It is responsible for importing and exporting all other relevant files. That makes it trivial to wrap the component with some logic-based behavior. In other words, it could transform properties of the component before rendering it. Here’s what that might look like for our button example:

import React from "react" import Component from "./component" import transform from "./transformer" const Button = props => <Component {...transform(props)} /> export default Button

The transform.js file might look like this:

export default input =&gt; { return { ...input, children: input.children || input.label, to: input.to || input.url } }

In this example, if to and children were properties sent to the component, it works just fine! But if label and url were used instead, they are transformed to children and to. That means the <Button> component (component.js) only has to worry about using children and to.

const Button = ({ children, to }) => <a href={to}>{children}</a>

I personally love this approach. It keeps the logic tightly coupled with the component. The biggest downside I‘ve found thus far is that it’s a large number of files and transforms, when the entire dataset for any given page could be transformed earlier in the stack, which would be…

2. At the top of the funnel

The data has to be pulled into the application via some mechanism. Developers use this mechanism to retrieve as much data for the current page or view as possible. Often, the fewer number of queries/requests a page is required to make, the better its performance.

In other words, that mechanism often exists near the top of the funnel (or stack), as opposed to each component pulling its own data in dynamically. (When that’s necessary, I use adapters.)

The mechanism that retrieves the page data could also be responsible for transforming all the data for the given page before it renders any of its components.

In theory, this is a better approach than the first one. It decreases the amount of work the browser has to do, which should improve the front-end performance. That means the server has to do more work, but that’s often a better choice.

In practice, though, this is a lot of work. Data structures can be big, complex, and interwoven. It can take a heck of a lot of work to transform everything into the right format at the top of the funnel, and then pass the transformed data down to components. It’s also more difficult to test because of the potential complexity and variation of the giant data blob retrieved at the top of the stack. With the first approach, testing the transformer logic for the button is trivial. With this approach, you’d want to account for transforming button data anywhere that it might appear in the retrieved data object.

But, if you can pull it off, this is generally the better approach.

3. The middleman engine

The third and final (and magical) approach is to do all this work somewhere else. In this case, we could build an engine (i.e. a small application) that would do the transformations for us, and then make the content available for the application to consume.

This is likely even more work than the second approach. And it has added cost and maintenance in running an additional application, which takes more effort to ensure it is rock solid.

The major upside to this approach is that we could build this as an abstracted engine. In other words, any time we bring in data to any front-end application, it goes through this middleman engine. That means if we have two projects that use the same CMS or data source, our work is cut down significantly for the second project.

If you aren‘t doing any of this today and want to start, my advice is to treat these approaches like stepping stones. They grow in complexity and maintenance and power as the application grows. Start with the first approach and see how far that gets you. Then, if you feel like you could benefit from a jump to the second, do it! And if you’re feeling like living dangerously, go for the third!

In the end, what matters most is crafting an experience that both your editors and your developers understand and enjoy. If you can do that, you win!

The post Reconciling Editor Experience and Developer Experience in the CMS appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

The Devil’s Albatross

Css Tricks - Fri, 02/12/2021 - 5:54am

Nils Binder talks about a technique for spacing between two elements. Picture a header on a large screen with a logo in the upper left and nav in the upper right. Then a small screen, when they can no longer be on the same “row” and need to wrap, they don’t just wrap but are centered.

A video explains better:

My mind goes: I’d just find the exact pixel value for the breakpoint I want this to happen and then write a media query that re-styles things to do that.

But… media queries are only for the entire browser window width. While that probably works in a case like this because it’s a full-site kinda concern, I get the desire to not have to write media queries. Nils’ idea borrow concepts from Heydon’s Holy Albatross to make this work without any media queries at all. So, you could use this on a smaller-scope component where you need to adjust the breaking point at a certain size that has nothing to do with the size of the browser window.

CodePen Embed Fallback

Direct Link to ArticlePermalink

The post The Devil’s Albatross appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Is CSS float deprecated?

Css Tricks - Thu, 02/11/2021 - 1:21pm

An interesting conversation came up at work the other day: Should we use the CSS float property now that we have CSS Grid and Flexbox?

The short answer

No! Well, mostly. I’d only use it today for wrapping text around images, though and I’d avoid using float entirely for layouts.

The longer, more annoying answer

Before flexbox and grid, we had to use the CSS float property to make grids and layouts. In fact, it was the first thing I learned about web design. On one hot summer afternoon I cracked open a copy of Designing with Web Standards by Jeffrey Zeldman and then moved a tiny red div with float: right. It was magic. There was power in the float.

It was so easy to move something around on the screen that I now wonder how many designers fell in love with the web simply because of how easy it is to use move things around like that.

But using float to build complex layouts was always a hack: it was only really designed to let text wrap around an image.

img { width: 150px; float: left; } CodePen Embed Fallback

The problems with float begin when we try to build giant layouts and magazine-style grids. But that’s what we had to do since there were no alternatives back then like we do today.

One problem with the float property is that you’d have to wrap floated elements with something called a clearfix that looked like this:

<div class="clearfix"> <div class="float-left">Column</div> <div class="float-left">Column</div> <div class="float-left">Column</div> </div> clearfix:after { content: ""; display: table; clear: both; }

Jay Hoffman described the clearfix hack a while back:

The clearfix, for those unaware, is a CSS hack that solves a persistent bug that occurs when two floated elements are stacked next to each other. When elements are aligned this way, the parent container ends up with a height of 0, and it can easily wreak havoc on a layout. All you might be trying to do is position a sidebar to the left of your main content block, but the result would be two elements that overlap and collapse on each other. To complicate things further, the bug is inconsistent across browsers. The clearfix was invented to solve all that.

Things began to slowly change after that. Back in 2017, Rachel Andrew explained how browsers can handle the clearfix problem without any hacks at all. All we need is the following CSS to make the same fix:

.container { display: flow-root; }

The odd thing is that I didn’t know the flow-root value existed until about three minutes before I typed that. But I guess this sort of defends the argument I’m about to make here: with CSS Grid and Flexbox we don’t really need float at all. The property was really designed to do one thing: let text wrap around images. But now, with grid and flexbox, we have wonderful powers that can do all the heavy lifting for real layouts.

Back to the argument I was having at work. Some folks said that we should go back and delete all the instances of float in our codebase because it’s old code and we can easily replace it with flexbox or grid. But this is where I’d say, Whoa! hold up a sec. I don’t think having the float property in a few places in our codebase is doing that much harm at all — this isn’t radioactive code that’s causing problems.

So should we be using CSS float for anything besides letting text wrap around text? Nope. But should we all go out and immediately purge the web of CSS float declarations because it’s not pure and not the “correct” way of doing things? Nope again.

The nifty thing about the web is that old code shouldn’t break things. Just ask Chris. A website that isn’t using the fanciest CSS properties or the coolest tricks isn’t useless or bad. We’ve simply replaced float with alternatives that are better. I think it’s a good lesson here that these CSS properties are likely going to stick around forever because they still have applicable use cases in modern web design.

And that’s okay.

The post Is CSS float deprecated? appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Progressive Web Apps in 2021

Css Tricks - Thu, 02/11/2021 - 1:21pm

Maximiliano Firtman has a look at PWAs this year, including trying to get a bead on how widespread they are:

At the end of 2020, approximately 1% of websites included a Service Worker, and 2.2% had an installable Web App Manifest file. Remember that some platforms -such as Safari on iOS or Chrome on Android- do not require a Service Worker to have a standalone experience after installation. We can assume that 2.2% of websites are installable, and 1% may pass the PWA criteria on Android, 71% of which offer some offline experience.

That data is from the HTTP Archive, which looked at 7.5 million websites. So 1% might seem like a small number, but that’s lots of sites with PWA tech on them, and 170% year-over-year growth. Those are just the minimum requirements, though. I’m sure fully embracing PWA-ness (e.g. real offline usage) is a tiny fraction of that. Maximiliano has lots of more detailed data, so be sure to dig into the article if you’re interested in the nuance.

Anecdotally, I’d say PWAs fell out of general conversation last year. I don’t think anybody is exactly against the technologies that make them up, but they aren’t embracing them either. My guess? Everyone is scared of Service Workers. I’m scared of Service Workers. They do scary things, like aggressively hold onto cache. I think a whole dev team really needs to understand them and embrace them into their workflow and build process for them to be effective. Generally speaking, we just aren’t there yet.

Direct Link to ArticlePermalink

The post Progressive Web Apps in 2021 appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

WordPress 5.7: Big ol’ jQuery Update

Css Tricks - Thu, 02/11/2021 - 6:08am

WordPress core is making the jump from jQuery 1.12.4 to jQuery 3.5.1! This is a big deal for lots of reasons — like modern features, better DX, and security improvements to name a few. Right now, the plan is to release the update in WordPress 5.7, which is slated to release on March 9. &#x1f91e;

WordPress is notorious for its backwards compatibility and you could say this change is a relic of that philosophy. A line has been drawn in the sand when it comes to jQuery, and 1.x ain’t a part of plans moving forward. But it also represents a breaking change, and that’s sorta rare in the WordPress world. Because WordPress ships with jQuery installed, many developers call that version of it rather than re-installing it in another location. That includes lots of theme and plugin developers, all of whom now need to make sure their code is compatible with jQuery 3.x.

Not doing so could result in lots on borked sites. But, hey, we have about a month left to work on it, right?

The change has actually been in the works for some time. The work began in WordPress 5.5, and 5.7 is technically the third of three phases. WordPress 5.6 is where the Core Team bumped jQuery up to version 3.5.1 and updated jQuery Migrate to help developers revert back to legacy jQuery, if needed. In other words, this has been a super methodical approach. The Core Team deserves a lot of kudos for that, including all of the communications that have gone out about the change.

I wrote something up about the transition a couple of weeks ago, including a sort of how-to for testing things in advance, and troubleshooting issues after the fact. It’s aimed at beginners, but maybe you’ll find it helpful too. Make WordPress Support has its own thorough article as well, and it calls out a plugin that the WordPress team made just for this transition. It’s pretty sweet: it can roll your site back to jQuery1.x automatically if it detects a fail. It also documents those fails and sends notifications when they happen.

The key is to start testing now in WordPress 5.6. The plan is to disable jQuery Migrate in WordPress 5.7, so waiting for that release is too late. If you do wait that long and find issues, your best path forward is likely to roll back to 5.6 anyway to take advantage of jQuery Migrate and the helper plugin.

The post WordPress 5.7: Big ol’ jQuery Update appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

iframe feedback

Css Tricks - Thu, 02/11/2021 - 4:24am

What if an <iframe> had within it another <iframe> of the exact same source? Inception, as they say. Baptise Crespy does this all-important research in the name of art and science.

Turns out browsers are smart enough to not allow this infinite looping to occur (and likely crash your browser/computer). They strip the content after the 2nd nest. But! If you change the URL of the src to be unique (but still essentially serve the same document), it works. Adding some random colors and animations and things get weird really fast.

Motion warning video:

Direct Link to ArticlePermalink

The post iframe feedback appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Progressive Enhancement reading list, draft 1

QuirksBlog - Thu, 02/11/2021 - 1:24am

In March I am going to teach the Progressive Enhancement course of the Web minor at university for the third time. I decided to expand the reading list for this course, and here I presented draft 1 and asked for feedback.

Meanwhile the reading list is done and I removed this draft. Also, I have a question about progressive enhancement and accessibility that I wasn't able to solve.

HTML Video Sources Should Be Responsive

Css Tricks - Wed, 02/10/2021 - 1:40pm

Scott Jehl doesn’t mince words here:

Removing media support from HTML video was a mistake. It means that for every video we embed in HTML, we’re stuck with the choice of serving source files that are potentially too large or small for many users’ devices (resulting in poor performance, wasteful data consumption, and even sub-optimal quality on larger screens), or resorting to more complicated server-side or scripted or third-party solutions to deliver a correct size.

I remember when responsive images were just starting to come out. One way to explain it was to say it’s like <video> in that you can have multiple <source> elements inside which (in supporting browsers) allowed you to specify attributes like type (e.g. video format) and media (e.g. screen size). But then

Despite being implemented in multiple browsers, the feature was removed from browsers and the HTML specification, without any proposed replacement for the functionality it once provided. One exception is the feature was never removed from Webkit, so it still works in Safari browsers, which is great.

I don’t remember that. That feels like a big WTF moment (some background). I think of the web as being tremendous at backwards compatibility. It’s a rare day when we just yank stuff, and even more rare is a yanking with no alternative whatsoever.

So now with responsive images being a success (it’s a success, right? I can’t imagine how incredibly much bandwidth it has saved the world)… can’t we… put it back?

When I have an immediate need for this, I always think of Cloudinary, because I can alter the size and format of video by changing the URL. Like here’s a video URL where the video codec is automatically determined and the size is forced down to 400px:

https://res.cloudinary.com/css-tricks/video/upload/c_scale,q_auto,vc_auto,w_400/v1612795501/intro-patreon_jpd8er.mp4

It’s nice to have tools like this, but that doesn’t mean the platform shouldn’t be helping.

Direct Link to ArticlePermalink

The post HTML Video Sources Should Be Responsive appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

:focus-visible Support Comes to Firefox

Css Tricks - Wed, 02/10/2021 - 10:51am

Look at that! The :focus-visible pseudo-selector is now supported in Firefox, as of version 85 which shipped yesterday. I had to rush over to the MDN Docs just to confirm, and yep, the :focus-visible page has been updated to reflect the news.

What’s so cool about :focus-visible? It’s all about the blue focus ring that displays around elements that are in focus. It’s sort of a happy medium between loving the outline for accessibility purposes (gotta know what element is selected when tabbing on a keyboard) but not-really-loving how it looks (gotta have everything follow brand).

The strategy has largely been an all-or-nothing choice between using a custom outline when any element is in :focus (great, but that means for both keyboard tabbing and mouse clicks) or ditching the outline altogether (not great, like ever). :focus-visible accomplishes the same thing as :focus, but uses a browser’s knowledge of user inputs (or heuristics) to determine whether the focus is coming from a keyboard or a mouse.

(Are a browser’s heuristics perfect at determining the input? That depends. Things get murky once we start factoring in things like touch interactions.)

That means, we get to remove the default focus ring (yay!) for the right types of interactions (double yay!) and display our own custom styles while we’re at it (triple yay!). Allow me to pluck Andy Adams’ fine example straight from our almanac. Note that :focus-visible cannot remove the focus ring like :focus can, so the two are used together:

.next-image-button:focus { outline: none; } .next-image-button:focus-visible { outline: 3px solid blanchedalmond; /* That'll show 'em */ } CodePen Embed Fallback

Chrome implemented :focus-visible back in 2018. Firefox had it’s own prefixed version, :-moz-focusring, prior to this implementation. Safari? Go vote for the feature!

Igalia is gathering funding and working on getting it into Safari! Here’s Brian Kardell on this.

The post :focus-visible Support Comes to Firefox appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

How to Favicon in 2021

Css Tricks - Wed, 02/10/2021 - 8:21am

I always appreciate someone looking into and re-evaluating the best practices of something that literally every website needs and has a complex set of requirements. Andrey Sitnik has done that here with favicons.

The final suggestion:

<link rel="icon" href="/favicon.ico"><!-- 32×32 --> <link rel="icon" href="/icon.svg" type="image/svg+xml"> <link rel="apple-touch-icon" href="/apple-touch-icon.png"><!-- 180×180 --> <link rel="manifest" href="/manifest.webmanifest"> { "icons": [ { "src": "/192.png", "type": "image/png", "sizes": "192x192" }, { "src": "/512.png", "type": "image/png", "sizes": "512x512" } ] }

It was good timing to do this here on CSS-Tricks, so I tried following the advice to the letter, and it’s working great so far. I think I got fed up at how complex it was at some point that I went ultra-minimalist and only had favicon.ico file. Now I’ve got all of the above in place.

Where I differed…

  • I don’t have GIMP or Inkscape installed, which can export .ico format, so I used this favicon generator (I fed it my “main” SVG”) just for that one icon.
  • I found Figma helpful for resizing frames and exporting the right sizes.
  • I used ImageOptim for optimizing all the images.
  • I was nervous about adding a “manifest” because I don’t have any other PWA-like steps in place and it feels like an extra web request for little value. But I did it anyway.
  • I have a theme color (<meta name="theme-color" content="rgb(255, 122, 24)">) because I was told it was a nice touch. Feels related.

I love the dark mode SVG concept:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500"> <style> @media (prefers-color-scheme: dark) { .a { fill: #f0f0f0 } } </style> <path class="a" fill="#0f0f0f" d="…" /> </svg>

But I didn’t do any trickery there as I think my icon looks fine either way without changes:

I also haven’t gotten around to making a special development-only favicon again, but I will because I find it extremely handy.

Direct Link to ArticlePermalink

The post How to Favicon in 2021 appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

The Differences in Web Hosting (Go with the Happy Path)

Css Tricks - Wed, 02/10/2021 - 5:46am

One of our readers checked out “Helping a Beginner Understand Getting a Website Live” and had some follow up questions specifically about hosting providers. Here’s what they asked:

What’s the difference between hosting providers? For example, what is the difference between GoDaddy and Hostgator, which seems like “traditional” web hosting providers, to others like Heroku, Digital Ocean, AWS, and Firebase?

When would I use one over the other?

They were hoping for detailed thoughts, so I’m going to oblige!

Choosing a plan

You mentioned GoDaddy first, so let’s take a peak at GoDaddy’s hosting offerings as I type:

To be honest, I’m already confused. (Sorry, I promise I’ll try to be more helpful as we go on.) Why is WordPress hosting one dollar more expensive than the Web Hosting plan? If you buy the $5.99 Web Hosting plan are you prevented from installing WordPress on it? Or is it just convenient in that if you pick the WordPress hosting it comes pre-installed and configured? WooCommerce is just a plugin for WordPress, so are you prevented from installing that on the WordPress hosting plan until you upgrade to the WordPress Ecommerce Hosting plan? (To be fair, WordPress.com unlocks WooCommerce at the highest plan as well, so it’s trod territory.) Why is the VPS Hosting plan the cheapest? I don’t blame you if you also find this as confusing as I do, especially as this is just one of many different charts of hosting options they offer.

GoDaddy makes a billion zillion dollars a year, so I’m sure they’ve got this stuff figured out, but I’ll tell ya, after a couple of decades of web development experience, I’d be totally guessing at choosing a plan from options like this. Cynically, it feels like confusion might be a sales tactic.

Technology

I do know this: these plans are for PHP / MySQL sites. That means WordPress, Craft, Perch, Ghost, Drupal, Joomla, etc. This is the LAMP stack which has all the big CMSs covered. Just the way it is. This is going to be the case at Media Temple, Hostgator, Bluehost, and lots of hosts like that. I think a “traditional” web host, as you put it, isn’t a bad way to think about it.

Do you wanna run PostgreSQL or MariaDB instead of MySQL? Or you wanna run ASP instead of PHP? I’ll bet you all these hosts have some kind of answer for those things. The answer is going to be something like “Don’t use our shared hosting product, use our raw VPS (‘Virtual Private Server’) product which has direct root access, and you can install it yourself.” I guess that’s fine, but just know those things aren’t first-class citizens of their hosting. If you have trouble, I’d worry you’ll have a hard time getting good support.

Which leads me to my point: you should go with the happy path offerings from hosting providers.

Say I want to write a Python app. I’m not going to buy a Hostgator server. I’m sure you can get it to work, but it’s not something they really promote. It doesn’t feel like it’s on a happy path. Whereas if I look around at Heroku, they make it a first-class citizen of what they offer:

I can’t vouch for it directly as I’ve never used Heroku, but I’ve heard lots of good things and they’ve been doing this for a good 15 years.

Happy paths are about friendly pairings

Heroku reminds me of another divide in hosting providers that I think is significant. Those “traditional” web hosts don’t lift a finger to help you get your websites over to them. It’s more like: here’s your FTP credentials, good luck. With a host like Heroku, they are giving you a CLI to like heroku container:push to deploy your local code to production. Better, it will deploy right from your GitHub repository. Why every single web host in the world doesn’t help with that is a mystery to me. A web host that helps you with deployment is a valuable thing.

We were talking about happy paths, right? Heroku calls themselves a “Cloud Application Platform.” The happy path there is those server-y languages. Node, Ruby, Python, Go. What if you don’t need any of that? Say you’re building a static site, using a static site generator (like Eleventy) at the core (Jamstack, as it were). Do you pick Heroku? Probably not. While surely you could pull it off on Heroku, static site hosting isn’t core to Heroku, and so not a happy path.

Where should you host a static site? That’s Netlify’s whole ball game. Netlify is a super happy path for static sites.

In fact, Netlify nailed the Jamstack-style hosting thing so strongly that lots of companies have been trying to provide similar offerings. I think of Azure’s Static Web Apps as an example. So why use Azure over Netlify? If it feels like a happy path, and it might if you’re using other Azure products, assuming their products play well together. Azure is a massive cloud platform with loads of other offerings. Or you might just have more experience and developer muscle memory for Microsoft products. We’ll get to that later.

Jamstack (essentially meaning static hosting + services) is available in lots of places now. Cloudflare has Cloudflare Pages, which you might take advantage of because of the unlimited promises (unlimited sites, unlimited requests, unlimited bandwidth, and even unlimited team seats).

You might choose Cloudflare Pages because your Cloudflare products like access or workers that are important to you and it feels like the happy path to keep it all together.

Vercel has Jamstack hosting, but they’ll run servers for you if you need them. Their popular framework, Next.js, prebuilds pages, but can also deliver server-side rendered pages with a Node back end. Vercel gives you that back end.

Next.js on Vercel is a very happy path. “Deploy on the platform made for Next.js,” they say. Hard to beat that.

AWS Amplify is ultimately Jamstack hosting, and the happy path there is using Amplify to stitch together other AWS services. That’s literally the point of AWS Amplify.

Need auth? It’s Amazon Cognito under the hood, but Amplify helps you stitch it into what you are doing. Need storage? S3 is an industry standard, and Amplify helps you integrate it. Need a database? Amplify helps you model it and build APIs.

Firebase has Jamstack-style hosting, and the happy path is leaning into the Firebase framework.

Firebase has lots of very useful features, like real-time data storage, authentication, and RUM analytics. If I wasn’t using any of those things, I’m not sure I’d pick Firebase hosting. Like for a basic Jekyll blog, can it be done? Absolutely. Would I personally do it? Probably not. It’s not really leaning into the Firebase offerings, making it way less of a happy path.

It’s worth talking about &#x1f4aa; developer “muscle memory” for a moment. You build muscle memory for the things you do a lot. If you’ve got five sites on Netlify already, and you’ve gone through those motions over and over, it makes sense that your sixth site is on Netlify as well — even if some other host might be a slightly better fit. Knowing your tools well and feeling comfortable is a big deal. You can compare pricing and features and all the bits and bobs, but muscle memory is one of the most powerful choice influences, and I think that’s perfectly fine.

Your host should take care of your core needs

Remember how I mentioned a web host that helps you with deployment is a valuable thing? All of these hosts do that: Netlify, Vercel, AWS Amplify, Google Firebase, Cloudflare Pages, Azure Static Sites. That’s become table-stakes for hosting providers. There are more table-stakes as well.

The table stakes of modern web hosts.

Beyond, ya know, hosting the website.

  1. HTTPS. The host should give my site an SSL certificate. Probably automatically, and probably for free (since Let’s Encrypt is free).
  2. CDN. The host should help serve as much as my site as is practical from a CDN, even if it’s a paid feature or requires configuration.
  3. Deployment. The host should connect to Git repositories and move files from the main branch to the production site.
  4. Staging. The host should provide staging environment(s).

I should circle back to the WordPress (and other PHP/MySQL CMS) thing. That’s what this site is. Traditional hosts serve this market. WordPress is 35.2% of all websites, which is bananas, and means there are wheelbarrows full of money in that hosting market. But in my experience, the traditional hosts do almost none of what I just called table stakes in hosting. A lot of times, you’re on your own for HTTPS. You’re on your own for integrating a CDN. You’re on your own for deployment. Staging just means buy another server. It’s just a weird time for hosting right now, with such a wide gap in modern web hosts doing so much and traditional web hosts doing so little.

That’s not true of all WordPress-specific hosts though. Using a WordPress-specific host for hosting WordPress is about as happy path as you can get. I’m on Flywheel now and appreciate all they do. They cover that entire list of table stakes, and go further still, helping with local development.

You asked about Digital Ocean specifically…

I feel the least qualified to explain Digital Ocean, but I think it’s fair to say that Digital Ocean has a lot of happy paths. They have this concept of a “Droplet” (it’s a server) which is spun up from “containers.” I wouldn’t worry terribly much about the idea of containers at this point, but suffice it to say, they are pre-configured servers that can run any sort of combination of technologies. If you want to fire up a LAMP stack thing in a Droplet, that’s a first-class citizen. But so are lots of other technologies. Consider Strapi, a CMS that is Node, Nginx, and PostgreSQL. Digital Ocean has a Droplet for that’s ready for it out of the box.

Droplets also start at $5/month, so they are just as economical as other hosts, if not more so. You might find hosting products that are actually Digital Ocean under the hood! For example, the WordPress hosting tool SpinupWP allows you to quickly create configured WordPress hosting environments, but it doesn’t do the hosting itself, you “bring your own” host, which is likely Digital Ocean or AWS (Amazon Web Services).

It only gets more complicated from here

If Digital Ocean seems complex, wait until you hear about AWS. We talked about AWS Amplify earlier, but that’s like AWS designed for individual developers like you and me to scaffold apps quickly. It’s very cool, but it’s a small wedge of what all that is AWS.

AWS is this massive cloud services provider, meaning that, sure, you can spin up web servers, but there are also hundreds of other services for things, like databases, storage, serverless stuff, APIs, logs, heck — you can rent a damn quantum computer there, which is like sci-fi stuff. It’s not that a normal developer can’t use AWS for a web host, it’s just like, not really designed with that kind of DX in mind. This guide on installing WordPress makes me sweat. AWS is super powerful, has solutions for everything, and is priced as low as it gets. Perhaps it’s useful to think of AWS as like down-to-the-metal web infrastructure, designed for large-scale operations. Web hosts might even be built on top of AWS, for example.

Matching your needs to what’s available

Let’s do some quick hits of needs matched to options. This is in no way comprehensive. I just slapped it together with things that popped to mind that feel happy path aligned.

BudgetTypicalWordPressMediaTemple / GoDaddyFlywheel or WP EngineOther PHP + MySQL (e.g. Craft CMS)BluehostCloudways or fortrabbitRuby on RailsLinodeHerokuNode.jsPut in in a LambdaDigital OceanPythonVercelHerokuGoVercelCloud RunJamstackGitHub PagesNetlify or Cloudflare PagesGraphQL APIHasuraAWS Amplify / AppSyncImage StorageS3Cloudinary

It’s worth re-iterating that there is a lot of commonality in hosting. Say you’ve got an index.html file you want to host and that’s your entire website; literally any web host will do that. These are all web hosts, after all. They serve files and run code. They aren’t that different. We’re largely talking about DX here: do they run what I need to run? Is it straightforward? Do they help make it easy? Do they clearly offer support for it?

Is it the happy path?

Then there’s pricing

We haven’t really talked much about price. I know that’s a major consideration for a lot of people and I don’t want to downplay it. But it’s hard to talk about without knowing your needs. I also don’t want people to make major web hosting decisions based on something like a few dollars difference in monthly cost. If you spend half an hour troubleshooting you otherwise wouldn’t have had to, those savings are blown.

I find that web hosting is somewhat of a commodity market. The prices are fairly stable. If a host seems expensive, it’s probably because they offer a lot. If a host seems cheap, it’s probably because they cut costs in a way you’ll eventually feel. If you’ve got a little baby site, chances are, you’re going to be hosting it for free. And if and when the site grows up, the hosting costs will feel minimal and fair.

&#x1f6e0; Does all this seem kinda fun and exciting to you? If it does, you might think about a career in DevOps dealing with servers, deployment, infrastructure, and supporting developers doesn’t have to be a side-job to other development work, it can be a whole job.

Happy happy pathing.

The post The Differences in Web Hosting (Go with the Happy Path) appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

A table with both a sticky header and a sticky first column

Css Tricks - Tue, 02/09/2021 - 3:06pm

We’ve covered that individual <table> cells, <th> and <td> can be position: sticky. It’s pretty easy to make the header of a table stick to the top of the screen while scrolling through a bunch or rows (like this demo).

But stickiness isn’t just for the top of the screen, you can stick things in any scroll direction (horizontal is just as fun). In fact, we can have multiple sticky elements stuck in different directions inside the same element, and even single elements that are stuck in multiple directions.

Here’s a video example of a table that sticks both the header and first column:

Why would you do that? Specifically for tabular data where cross-referencing is the point. In this table (which represents, of course, the scoring baseball game where somehow 20 teams are all playing each other at once because that’s how baseball works), it “makes sense” that you wouldn’t want the team name or the inning number to scroll away, as you’d lose context of what you’re looking at.

CodePen Embed Fallback

Not all tables need to be bi-directionally cross-referenceable. A lot of tables can smash rows into blocks on small screens for a better small-screen experience.

The “trick” at play here is partially the position: sticky; usage, but moreso to me, how you have to handle overlapping elements. A table cell that is sticky needs to have a background, because otherwise we’ll see overlapping content. It also needs proper z-index handling so that when it sticks in place, it’ll be on top of what it is supposed to be on top of. This feels like the trickiest part:

  • Make sure the tbody>th cells are above regular table cells, so they stay on top during a horizontal scroll.
  • Make sure the thead>th cells are above those, for vertical scrolling.
  • Make sure the thead>th:first-child cell is the very highest, as it needs to be above the body cells and it’s sibling headers again for horizontal scrolling.

A bit of a dance, but it’s doable.

High five to Cameron Clark who emailed me demoed this and showed me how cool it is. And indeed, Cameron, it is cool. When I shared that around, Estelle Weyl showed me a demo she made several years ago. That feels about right, Estelle is always a couple of years ahead of me.

The post A table with both a sticky header and a sticky first column appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Nested Media Queries

Css Tricks - Tue, 02/09/2021 - 10:34am

Using media queries in CSS as part of responsive websites is bread and butter stuff to todays front-end developer. Using preprocessors to make them more comfortable to write and easier to maintain has become common practice as well.

I spent a few months experimenting with a dozen different approaches to media queries in Sass and actually used a few in production. All of them eventually failed to cater for everything I needed to do in an elegant way. So I took what I liked about each of them and created a solution that covered all scenarios I came across.

Why use a preprocessor at all?

That’s a fair question. After all, what’s the point of doing all this if one can simply write media queries using pure CSS? Tidiness and maintainability.

The most common use for media queries is the transformation of a layout based on the browser’s viewport width. You can make a layout adapt in such a way that multiple devices with different screen sizes can enjoy an optimal experience. As a consequence, the expressions used to define the media queries will make reference to the typical screen width of those devices.

So if your code contains 5 media queries that target tablet devices with a width of 768px, you will hardcode that number 5 times, which is something ugly that my OCD would never forgive. First of all, I want my code to be easy to read to the point that anyone understands instantly that a media query is targeting tablet devices just by looking at it – I reckon the word tablet would do that better than 768px.

Also, what if that reference width changes in the future? I hate the idea of replacing it in 5 instances around the code, especially when it’s scattered around multiple files.

A first step would be to store that breakpoint in a variable and use it to construct the media query.

/* Using plain CSS */ @media (min-width: 768px) { } /* Using SCSS variables to store breakpoints */ $breakpoint-tablet: 768px; @media (min-width: $breakpoint-tablet) { }

Another reason to write media queries with a preprocessor like Sass is that it can sometimes provide some precious help with the syntax, in particular when writing an expression with a logical or (represented with a comma in CSS).

For example, if you want to target retina devices, the pure CSS syntax starts getting a bit verbose:

/* Plain CSS */ @media (min-width: 768px) and (-webkit-min-device-pixel-ratio: 2), (min-width: 768px) and (min-resolution: 192dpi) { } /* Using variables? */ @media (min-width: $bp-tablet) and ($retina) { // or #{$retina} }

It does look nicer, but unfortunately it won’t work as expected.

A problem with logic

Because of the way the CSS “or” operator works, I wouldn’t be able to mix the retina conditions with other expressions since a (b or c) would be compiled into (a or b) c and not a b or a c.

$retina: "(-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi)"; // This will generate unwanted results! @media (min-width: 480px) and #{$retina} { body { background-color: red; } } /* Not the logic we're looking for */ @media (min-width: 480px) and (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { body { background-color: red; } }

I realized I needed something more powerful, like a mixin or a function, to address this. I tried a few solutions.

Dmitry Sheiko’s technique

One I tried was Dmitry Sheiko’s technique, which had a nice syntax and includes Chris’ retina declaration.

// Predefined Break-points $mediaMaxWidth: 1260px; $mediaBp1Width: 960px; $mediaMinWidth: 480px; @function translate-media-condition($c) { $condMap: ( "screen": "only screen", "print": "only print", "retina": "(-webkit-min-device-pixel-ratio: 1.5), (min--moz-device-pixel-ratio: 1.5), (-o-min-device-pixel-ratio: 3/2), (min-device-pixel-ratio: 1.5), (min-resolution: 120dpi)", ">maxWidth": "(min-width: #{$mediaMaxWidth + 1})", "<maxWidth": "(max-width: #{$mediaMaxWidth})", ">bp1Width": "(min-width: #{$mediaBp1Width + 1})", "<bp1Width": "(max-width: #{$mediaBp1Width})", ">minWidth": "(min-width: #{$mediaMinWidth + 1})", "<minWidth": "(max-width: #{$mediaMinWidth})" ); @return map-get( $condMap, $c ); } // The mdia mixin @mixin media($args...) { $query: ""; @each $arg in $args { $op: ""; @if ( $query != "" ) { $op: " and "; } $query: $query + $op + translate-media-condition($arg); } @media #{$query} { @content; } }

But the problem with logical disjunction was still there.

.section { @include media("retina", "<minWidth") { color: white; }; } /* Not the logic we're looking for */ @media (-webkit-min-device-pixel-ratio: 1.5), (min--moz-device-pixel-ratio: 1.5), (-o-min-device-pixel-ratio: 3 / 2), (min-device-pixel-ratio: 1.5), (min-resolution: 120dpi) and (max-width: 480px) { .section { background: blue; color: white; } } Landon Schropp’s technique

Landon Schropp’s was my next stop. Landon creates simple named mixins that do specific jobs. Like:

$tablet-width: 768px; $desktop-width: 1024px; @mixin tablet { @media (min-width: #{$tablet-width}) and (max-width: #{$desktop-width - 1px}) { @content; } } @mixin desktop { @media (min-width: #{$desktop-width}) { @content; } }

He has a single-responsibility retina version as well.

But another problem hit me when I was styling an element that required additional rules on intermediate breakpoints. I didn’t want to pollute my list of global breakpoints with case-specific values just so I could still use the mixin, but I definitely didn’t want to forgo the mixin and go back to using plain CSS and hardcoding things every time I had to use custom values.

/* I didn't want to sometimes have this */ @include tablet { } /* And other times this */ @media (min-width: 768px) and (max-width: 950px) { } Breakpoint technique

Breakpoint-sass was next on my list, as it supports both variables and custom values in its syntax (and, as a bonus, it’s really clever with pixel ratio media queries).

I could write something like:

$breakpoint-tablet: 768px; @include breakpoint(453px $breakpoint-tablet) { } @include breakpoint($breakpoint-tablet 850px) { } /* Compiles to: */ @media (min-width: 453px) and (max-width: 768px) { } @media (min-width: 768px) and (max-width: 850px) { }

Things were looking better, but I personally think that Breakpoint-sass’ syntax feels less natural than Dmitry’s. You can give it a number and it assumes it’s a min-width value, or a number and a string and it assumes a property/value pair, to name just a few of the combinations it supports.

That’s fine and I’m sure it works great once you’re used to it, but I hadn’t given up on finding a syntax that was both simple and as close as possible to the way I orally describe what a media query must target.

Also, if you look at the example above you’ll see that a device with a width of exactly 768px will trigger both media queries, which may not be exactly what we want. So I added the ability to write inclusive and exclusive breakpoints to my list of requirements.

My (Eduardo Bouças’s) technique

This is my take on it.

Clean syntax, dynamic declaration

I’m a fan of Dmitry’s syntax, so my solution was inspired by it. However, I’d like some more flexibility in the way breakpoints are created. Instead of hardcoding the names of the breakpoints in the mixin, I used a multidimensional map to declare and label them.

$breakpoints: (phone: 640px, tablet: 768px, desktop: 1024px) !default; @include media(">phone", "<tablet") { } @include media(">tablet", "<950px") { }

The mixin comes with a set of default breakpoints, which you can override anywhere in the code by re-declaring the variable $breakpoints.

Inclusive and exclusive breakpoints

I wanted to have a finer control over the intervals in the expressions, so I included support for the less-than-or-equal-to and greater-than-or-equal-to operators. This way I can use the same breakpoint declaration in two mutually exclusive media queries.

@include media(">=phone", "<tablet") { } @include media(">=tablet", "<=950px") { } /* Compiles to */ @media (min-width: 640px) and (max-width: 767px) { } @media (min-width: 768px) and (max-width: 950px) { } Infer media types and handle logic disjunction

Similarly to the breakpoints, there’s a list for media types and other static expressions declared by default (which you can override by setting the variable $media-expressions). This adds support for optional media types, such as screen or handheld, but it’s also capable of correctly handling expressions with logical disjunctions, such as the retina media query we saw before. The disjunctions are declared as nested lists of strings.

$media-expressions: (screen: "screen", handheld: "handheld", retina2x: ("(-webkit-min-device-pixel-ratio: 2)", "(min-resolution: 192dpi)")) !default; @include media("screen", ">=tablet") { } @include media(">tablet", "<=desktop", "retina2x") { } /* Compiles to */ @media screen and (min-width: 768px) { } @media (min-width: 769px) and (max-width: 1024px) and (-webkit-min-device-pixel-ratio: 2), (min-width: 769px) and (max-width: 1024px) and (min-resolution: 192dpi) { }

There’s no rocket science under the hood, but the full implementation of the mixin isn’t something I could show in just a few lines of code. Instead of boring you with huge code snippets and neverending comments, I included a Pen with everything working and I’ll briefly describe the process it goes through to construct the media queries.

CodePen Embed Fallback How it works
  1. The mixin receives multiple arguments as strings and starts by going through each one to figure out if it represents a breakpoint, a custom width, or one of the static media expressions.
  2. If an operator is found, it is extracted and any matching breakpoint will be returned, or else we assume it’s a custom value and cast it to a number (using SassyCast).
  3. If it’s a static media expression, it checks for any or operators and generates all the combinations necessary to represent the disjunction.
  4. The process is repeated for all the arguments and the results will by glued together by the and connector to form the media query expression.

If you’d like to look at the complete Sass for it, it’s here. It’s called include-media on GitHub.

Final thoughts
  • I’m a big fan of this technique to make Sass talk to JavaScript. Because we declare breakpoints as a multidimensional list with their names as keys, exporting them in bulk to JavaScript becomes really straightforward and can be done automatically with just a few lines of code.
  • I’m not trying to put down other people’s solutions and I’m definitely not saying this one is better. I mentioned them to show some of the obstacles I found along the way to my ideal solution, as well as some great things they introduced that inspired my own solution.
  • You might have some concerns about the length and complexity of this implementation. While I understand, the idea behind it is that you download one single file, @import it into your project and start using it without having to touch the source code. Ping me on Twitter though if you have any questions.
  • You can get it from GitHub and you are very welcome to contribute with issues/code/love. I’m sure there’s still a lot we can do to make it better.
Update!

Eduardo made a website for his approach: @include-media.

Direct Link to ArticlePermalink

The post Nested Media Queries appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Recreating Game Elements for the Web: The Among Us Card Swipe

Css Tricks - Tue, 02/09/2021 - 6:13am

As a web developer, I pay close attention to the design of video games. From the HUD in Overwatch to the catch screen in Pokemon Go to hunting in Oregon Trail, games often have interesting mechanics and satisfying interactions, many of which inspire my own coding games at Codepip.

Beyond that, implementing small slices of these game designs on a web stack is a fun, effective way to broaden your skills. By focusing on a specific element, your time is spent working on an interesting part without having to build out a whole game with everything that entails. And even in this limited scope, you often get exposed to new technologies and techniques that push on the boundaries of your dev knowledge.

As a case study for this idea, I’ll walk you through my recreation of the card swipe from Among Us. For anyone in the dark, Among Us is a popular multiplayer game. Aboard a spaceship, crewmates have to deduce who among them is an imposter. All the while, they complete mundane maintenance tasks and avoid being offed by the imposter.

The card swipe is the most infamous of the maintenance tasks. Despite being simple, so many players have struggled with it that it’s become the stuff of streams and memes.

Here’s my demo

This is my rendition of the card swipe task:

CodePen Embed Fallback

Next, I’ll walk you through some of the techniques I used to create this demo.

Swiping with mouse and touch events

After quickly wireframing the major components in code, I had to make the card draggable. In the game, when you start dragging the card, it follows your pointer’s position horizontally, but stays aligned with the card reader vertically. The card has a limit in how far past the reader it can be dragged to its left or right. Lastly, when you lift your mouse or finger, the card returns to its original position.

All of this is accomplished by assigning functions to mouse and touch events. Three functions are all that‘s needed to handle mouse down, mouse move, and mouse up (or touch start, touch move, and touch end if you‘re on a touchscreen device). Here’s the skeleton of that JavaScript code:

const card = document.getElementById('card'); const reader = document.getElementById('reader'); let active = false; let initialX; // set event handlers document.addEventListener('mousedown', dragStart); document.addEventListener('mousemove', drag); document.addEventListener('mouseup', dragEnd); document.addEventListener('touchstart', dragStart); document.addEventListener('touchmove', drag); document.addEventListener('touchend', dragEnd); function dragStart(e) { // continue only if drag started on card if (e.target !== card) return; // get initial pointer position if (e.type === 'touchstart') { initialX = e.touches[0].clientX; } else { initialX = e.clientX; } active = true; } function drag(e) { // continue only if drag started on card if (!active) return; e.preventDefault(); let x; // get current pointer position if (e.type === 'touchmove') { x = e.touches[0].clientX - initialX; } else { x = e.clientX - initialX; } // update card position setTranslate(x); } function dragEnd(e) { // continue only if drag started on card if (!active) return; e.preventDefault(); let x; // get final pointer position if (e.type === 'touchend') { x = e.touches[0].clientX - initialX; } else { x = e.clientX - initialX; } active = false; // reset card position setTranslate(0); } function setTranslate(x) { // don't let card move too far left or right if (x < 0) { x = 0; } else if (x > reader.offsetWidth) { x = reader.offsetWidth; } // set card position on center instead of left edge x -= (card.offsetWidth / 2); card.style.transform = 'translateX(' + x + 'px)'; } Setting status with performance.now()

Next, I had to determine whether the card swipe was valid or invalid. For it to be valid, you must drag the card across the reader at just the right speed. Didn’t drag it far enough? Invalid. Too fast? Invalid. Too slow? Invalid.

To find if the card has been swiped far enough, I checked the card’s position relative to the right edge of the card reader in the function dragEnd:

let status; // check if card wasn't swiped all the way if (x < reader.offsetWidth) { status = 'invalid'; } setStatus(status);

To measure the duration of the card swipe, I set start and end timestamps in dragStart and dragEnd respectively, using performance.now().

function setStatus(status) { // status is only set for incomplete swipes so far if (typeof status === 'undefined') { // timestamps taken at drag start and end using performance.now() let duration = timeEnd - timeStart; if (duration > 700) { status = 'slow'; } else if (duration < 400) { status = 'fast'; } else { status = 'valid'; } } // set [data-status] attribute on reader reader.dataset.status = status; }

Based on each condition, a different value is set on the reader’s data-status attribute. CSS is used to display the relevant message and illuminate either a red or green light.

#message:after { content: "Please swipe card"; } [data-status="invalid"] #message:after { content: "Bad read. Try again."; } [data-status="slow"] #message:after { content: "Too slow. Try again."; } [data-status="fast"] #message:after { content: "Too fast. Try again."; } [data-status="valid"] #message:after { content: "Accepted. Thank you."; } .red { background-color: #f52818; filter: saturate(0.6) brightness(0.7); } .green { background-color: #3dd022; filter: saturate(0.6) brightness(0.7); } [data-status="invalid"] .red, [data-status="slow"] .red, [data-status="fast"] .red, [data-status="valid"] .green { filter: none; } Final touches with fonts, animations, and audio

With the core functionality complete, I added a few more touches to get the project looking even more like Among Us.

First, I used a free custom font called DSEG to imitate the segmented type from old LCDs. All it took was hosting the files and declaring the font face in CSS.

@font-face { font-family: 'DSEG14Classic'; src: url('../fonts/DSEG14-Classic/DSEG14Classic-Regular.woff2') format('woff2'), url('../fonts/DSEG14-Classic/DSEG14Classic-Regular.woff') format('woff'), url('../fonts/DSEG14-Classic/DSEG14Classic-Regular.ttf') format('truetype'); }

Next, I copied the jitter animation of the text in the original. Game developers often add subtle animations to breath life into an element, like making a background drift or a character, well, breathe. To achieve the jitter, I defined a CSS animation:

@keyframes jitter { from { transform: translateX(0); } to { transform: translateX(5px); } }

At this point, the text glides smoothly back and forth. Instead, what I want is for it to jump back and forth five pixels at a time. Enter the steps() function:

#message { animation: jitter 3s infinite steps(2); }

Finally, I added the same audio feedback as used in Among Us.

let soundAccepted = new Audio('./audio/CardAccepted.mp3'); let soundDenied = new Audio('./audio/CardDenied.mp3'); if (status === 'valid') { soundAccepted.play(); } else { soundDenied.play(); }

Sound effects are often frowned upon in the web development world. A project like this an opportunity to run wild with audio.

And with that, the we’re done! Here’s that demo again:

CodePen Embed Fallback Try your own

Given how standardized the web has become in look and feel, this approach of pulling an element from a game and implementing it for the web is a good way to break out of your comfort zone and try something new.

Take this Among Us card swipe. In a small, simple demo, I tinkered with web fonts and animations in CSS. I monkeyed with input events and audio in JavaScript. I dabbled with an unconventional visual style.

Now it’s time for you to survey interesting mechanics from your favorite games and try your hand at replicating them. You might be surprised what you learn.

The post Recreating Game Elements for the Web: The Among Us Card Swipe appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

SVG within CSS

Css Tricks - Mon, 02/08/2021 - 11:07am

Stefan Judis has a “Today I Learned” (TIL) post explaining how SVGs filters can be inlined in CSS. The idea is that CSS has the filter property which supports some built-in functions, like grayscale(100%) and stuff like that.

But it can also point to a filter defined by SVG. So you could do filter: url(#my-custom-filter) which is in some inline <svg> as <filter id="my-custom-filter">. It’s kinda funny to have to refer out to the HTML like that. A filter is such a visual thing that it makes sense to bring it into the CSS. That looks like this:

img { filter: url('data:image/svg+xml,\ <svg xmlns="http://www.w3.org/2000/svg">\ <filter id="waves" x="-20%" y="-20%" width="140%" height="140%" filterUnits="objectBoundingBox" primitiveUnits="userSpaceOnUse" color-interpolation-filters="linearRGB">\ <feTurbulence type="turbulence" baseFrequency="0.01 0.01" numOctaves="1" seed="1" stitchTiles="noStitch" result="turbulence" />\ <feDisplacementMap in="SourceGraphic" in2="turbulence" scale="20" xChannelSelector="G" yChannelSelector="A" result="displacementMap" />\ </filter>\ </svg>#waves') ; }

That’s Stefan’s turbulence filter example, something CSS alone definitely cannot do.

Look at all those backslashes (\). Makes ya wish CSS had template literals, eh? Makes me nervous that a code formatter or minifier would choke on that, but I don’t actually know, maybe it would be fine.

What’s nice is that the SVG remains fairly intact (readable and editable). So here you can edit the SVG filter in the CSS and have a play:

CodePen Embed Fallback

I also think of Yoksel’s tools. This editor for two-tone and three-tone images is so cool. I can pick up one of those filters and drop it into some CSS as well:

CodePen Embed Fallback

Filters aren’t the only kind of SVG that makes some sense to inline into CSS though. You can put SVG drawing right into CSS as well.

CodePen Embed Fallback

This works everywhere but Safari in a quick blast through modern browsers. But I think in the not-so-distant past, we needed to encode more of the special characters in the SVG to get it to work (although you didn’t have to resort to base64). Yoskel’s URL-encoder is literally just for this — I just don’t know that it’s necessary anymore.

The post SVG within CSS appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Animating a CSS Gradient Border

Css Tricks - Mon, 02/08/2021 - 5:24am

This little trick for gradient borders is super useful:

.border-gradient { border: 5px solid; border-image-slice: 1; border-image-source: linear-gradient(to left, #743ad5, #d53a9d); }

Here’s some basic demos from our article on the subject. Sephanie Eckles was sharing around the idea with more detail. Bramus Van Damme saw that and stretched it a bit by adding, then animating an angle to the gradient. Like:

div { --angle: 0deg; /* … */ border-image: linear-gradient(var(--angle), green, yellow) 1; animation: 10s rotate linear infinite; } @keyframes rotate { to { --angle: 360deg; } }

But wait! That’s not actually going to animate as-is. The browser doesn’t know that 360deg is an actual angle value, and not just some random string. If it did know it was an angle value, it could animate it. So, tell it:

@property --angle { syntax: '<angle>'; initial-value: 0deg; inherits: false; }

See Bramus’ article for the demos there. Bonafide CSS trick. I can’t wait for more support for @property (Chrome only, as I write), because it really unlocks some cool CSS trickery. Animating numbers visually, for example.

Direct Link to ArticlePermalink

The post Animating a CSS Gradient Border appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Syndicate content
©2003 - Present Akamai Design & Development.