Developer News

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.

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.

(Jay Freestone’s) Front-end predictions for 2021

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

React framework maturity, early container queries, WASM adoption, and monoliths. I’ll take all four, please. Not feeling like a particularly front-end-y? Jay says:

Interestingly, the biggest developments in the front-end are unlikely to be traditionally front-end concerns. Back in our 2019 forecast, we noted that the role of the front-end developer was increasingly shifting towards ‘full-stack’, and this has borne out to be true. Even the evolution of our frameworks and tools suggests this, with an increased focus on data-fetching, concurrency, security and scalability.

If I’m allowed to predict, and thus manifest, improvements in the web platform in 2021, I’d like to predict HTML’s inert attribute shipping without a flag in all three browser engines.

Direct Link to ArticlePermalink

The post (Jay Freestone’s) Front-end predictions for 2021 appeared first on CSS-Tricks.

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

Exploring the Complexities of Width and Height in CSS

Css Tricks - Fri, 02/05/2021 - 12:51pm

The following article is co-authored by Uri Shaked and Michal Porag.

Let’s explore the complexities of how CSS computes the width and height dimensions of elements. This is based on countless late-night hours debugging and fiddling with lots of combinations of CSS properties, reading though the specs, and trying to figure out why some things seem to behave one way or another.

But before jumping in, let’s cover some basics so we’re all on the same page.

The basics

You have an element, and you want it to be 640px wide and 360px tall. These are just arbitrary numbers that conform to 16:9 pixel ratio. You can set them explicitly like this:

.element { width: 640px; height: 360px; }

Now, the design calls for some padding inside that element. So you modify the CSS:

.element { width: 640px; height: 360px; padding: 10px; }

What is the rendered width and height of the element now? I bet you can guess… it’s not 640×360px anymore! It’s actually 660×380px, because the padding adds 10px to each side (i.e. top, right, bottom and left), for an additional 20px on both the height and width.

This has to do with the box-sizing property: if it’s set to content-box (the default value), the rendered size of the element is the width and height plus the padding and border. That might mean that the rendered size is bigger than we intend which is funny because it might wind up that an element’s declared width and height values are completely different than what’s rendered.

That’s the power of The CSS Box Model. It calculates width and height like so:

/* Width */ width + padding-left + padding-right + border-left + border-right /* Height */ height + padding-top + padding-bottom + border-top + border-bottom

What we just saw is how the dimensions for a block element are computed. Block elements include any element that naturally takes up the full width that’s available. So, by nature, it doesn’t matter how much content the element contains because its width is always 100%, that is, until we alter it. Think of elements like <p>, <article>, <main>, <div>, and so many more.

But now we ought to look at inline elements because they behave differently when it comes to The Box Model and how their dimensions are computed. After that, we’ll look at the relationship between parent and child elements, and how they affect the width and height computations of each other.

The curious case of inline elements

As we just saw, the padding and border of any element are both included in the element’s computed width and height dimensions. Funny enough, there are cases where the width and height properties have no effect whatsoever. Such is the case when working with inline elements.

An inline element is an element that’s width and height are determined by the content it contains. Inline elements, such as a <span>, will completely ignore the width and height as well the the top and bottom margin properties because, well, the content is what determines the dimensions. Here, sometimes a visual can help.

CodePen Embed Fallback

Just look at how nesting a block element inside of an inline element breaks the inline element’s shape simply because the block element is not defined by the amount of content it contains, but rather the amount of available space. You can really see that in action when we add a border to the inline element. Look how the inline element abruptly stops where the paragraph comes in, then continues after the paragraph.

CodePen Embed Fallback

The span sees the paragraph, which interrupts the inline flow of the span and essentially breaks out of it. Fascinating stuff!

But there’s more! Look how the inline element completely overlooks width and margin, even when those are declared right on it.

CodePen Embed Fallback

Crazy!

Parent and child elements

The parent-child relationship is a common pattern. A parent element is one that contains other elements nested inside it. And those nested elements are the parent element’s children.

<!-- The parent element --> <div class="parent"> <!-- The children --> <div class="child"></div> <div class="another-child"></div> <div class="twins">Whoa!</div> </div>

The width and height of an element gets super interesting with parent and child elements. Let’s look at all the interesting quirks we get with this.

Relative units

Let’s start with relative units. A relative unit is computed by its context, or relation to other elements. Yeah, that’s sort of a convoluted definition. There are several different types of relative units, but let’s take percentages as an example. We can say that an element is 100% wide.

<div class="child"> <!-- nothing yet --> </div> .parent { width: 100%; }

Cool. If we plop that element onto an empty page, it will take up 100% of the available horizontal space. And what that 100% computes to depends on the width of the browser, right? 100% of a browser that’s 1,500 pixels is 1,500 pixels wide. 100% of a browser that’s 500 pixels is 500 pixels wide. That’s what we mean by a relative unit. The actual computed value is determined by the context in which it’s used.

So, the astute reader may already be thinking: Hey, so that’s sort of like a child element that’s set to a parent element’s width. And that would be correct. The width of the child at 100% will compute based on the actual width of the parent element that contains it.

Height works much the same way: it’s relative to the parent’s height. For example, two parent elements with different height dimensions but identical children result in children with different heights.

CodePen Embed Fallback

Padding and margin

The width and height of parent-child combinations get even more interesting when we look at other properties, such as padding and margin. Apparently, when we specify a percentage value for padding or margin, it is always relative to the width of the parent, even when dealing with vertical edges.

Some clever designers have taken advantage of it to create boxes of equal width and height, or boxes that keep a certain aspect ratio when the page resizes. This is particularly useful for video or image content, but can also be (ab)used in creative ways. Go ahead, type whatever you want into the editable element in this demo. The box maintains a proportional height and width, no matter how much (or little) content is added.

CodePen Embed Fallback

This technique for creating aspect ratio boxes is lovingly referred to as the “padding hack.” Chris has covered it extensively. But now that we have the aspect-ratio property gaining wide browser support, there’s less reason to reach for it.

display: inline and inline-block

Now that we’ve taken looks at how parent and child element dimensions are computed, we should check out two other interesting property values that affect an element’s width: min-content and max-content.

These properties tell the browser to look at the content of the element in order to determine its width. For instance, if we have the text: “hello CSS encyclopedia, nice to meet you!”, the browser would calculate the space that text would take up on the screen, and use it as the width.

The difference between min-content and max-content lies in how the browser does this calculation. For max-content, the browser pretends it has infinite space, and lays all the text in a single line while measuring its width.

For min-content, the browser pretends it has zero space, so it puts every word / child inline element in a different line. Let’s see this in action:

CodePen Embed Fallback CodePen Embed Fallback

We actually saw max-content in action when we looked at the difference between block and inline elements. Inline elements, remember, are only as wide and tall as the content they contain. We can make most elements inline elements just by declaring display: inline; on it.

CodePen Embed Fallback

Cool. Another weapon we have is display: inline-block;. That creates an inline element, but enhanced with block-level computations in The Box Model. In other words, it’s an inline element that respects margin, width and height. The best of both worlds!

CodePen Embed Fallback Cyclic percentage size

Did that last point make sense? Well, hopefully I won’t confuse you with this:

CodePen Embed Fallback

The child element in this example has a relative width of 33%. The parent element does not have a width declared on it. How the heck is the child’s computed width get calculated when there’s nothing relative to it?

To answer that, we have to look at how the browser calculates the size of the elements in this example. We haven’t defined a specific width for the parent element, so the browser uses the initial value for width , which is auto. And since the parent element’s display is set to inline-block, auto behaves like max-content. And max-content, as we saw, should mean the parent element is as wide as the content in it, which is everything inside the child element.

So, the browser looks at the element’s content (children) to determine its width. However, the width of the child also depends on the parent’s width! Gah, this is weird!

The CSS Box Sizing Module specification calls this cyclic percentage sizing. I’m not sure why it’s called that exactly, but it details the complex math the browser has to do to (1) determine the parent element’s width, and (2) reconcile that width to the relative width of the child.

The process is actually pretty cool once you get over the math stuff. The browser starts by calculating a temporary width for the child before its declared value is applied. The temporary width the browser uses for the child is auto which we saw behaves like max-content which, in turn, tells the browser that the child needs to be as wide as the content it contains. And right now, that’s not the declared 33% value.

That max-content value is what the browser uses to calculate the parent’s width. The parent, you see, needs to be at least as wide as the content that it contains, which is everything in the child at max-content. Once that resolves, the browser goes back to the child element and applies the 33% value that’s declared in the CSS.

This is how it looks:

There! Now we know how a child element can contribute to the computed value of its parent.

M&Ms: the min- and max- properties

Hey, so you’re probably aware that the following properties exist:

  • min-width
  • min-height
  • max-width
  • max-height

Well, those have a lot to do with an element’s width and height as well. They specify the limits an element’s size. It’s like saying, Hey, browser, make sure this element is never under this width/height or above this width/height.

So, even if we have declared an explicit width on an element, say 100%, we can still cap that value by giving it a max-width:

element { width: 100%; max-width: 800px; }

This allows the browser to let the element take up as much space as it wants, up to 800 pixels. Let’s look what happens if we flip those values around and set the max-width to 100% and width to 800px:

element { width: 800px; max-width: 100%; }

Hey look, it seems to result in the exact same behavior! The element takes up all the space it needs until it gets to 800 pixels.

CodePen Embed Fallback

Apparently, things start to get more complex as soon as we add a parent element into the mix. This is the same example as above, with one notable change: now each element is a child of an inline-block element. Suddenly, we see a striking difference between the two examples:

CodePen Embed Fallback

Why the difference? To understand, let’s consider how the browser calculates the width of the elements in this example.

We start with the parent element (.parent). It has a display property set to inline-block, and we didn’t specify a width value for it. Just like before, the browser looks at the size of its children to determine its width. Each box in this example is wrapped in the .parent element.

The first box (#container1) has a percentage width value of 100%. The width of the parent resolves to the width of the text within (the child’s max-content), limited by the value we specified by max-width, and that is used to calculate the width of the child as well.

The second box (#container2) has a set width of 800px, so its parent width is also 800px — just wide enough to fit its child. Then, the child’s max-width is resolved relative to the parent’s final width, that is 800px. So both the parent and the child are 800px wide in this case.

So, even though we initially saw the two boxes behave the same when we swapped width and max-width values, we now know that isn’t always true. In this case, introducing a parent element set to display: inline-block; threw it all off!

Adding min(), max() and clamp() into the mix

The min(), max() and clamp() are three useful CSS functions that let us define the size of elements responsively… without media queries!

  • min(): Returns the minimum value of its arguments. The arguments can be given in different units, and we can even mix and match absolute and relative units, like min(800px, 100%).
  • max(): Returns the maximum value of its arguments. Just like min(), you can mix and match different units.
  • clamp(): A shorthand function for doing min and max at once: clamp(MIN, VAL, MAX) is resolved as max(MIN, min(VAL, MAX)). In other words, it will return VAL, unless it exceeds the boundaries defined by MIN and MAX, in which case it’ll return the corresponding boundary value.

Like this. Check out how we can effectively “re-write” the max-width example from above using a single CSS property:

.element { width: min(800px, 100%); } /* ...is equivalent to: */ .element { width: 800px; max-width: 100%; }

That would set the width of the element to 800px, but make sure we don’t exceed the width of the parent (100%). Just like before, if we wrap the element with an inline-block parent, we can observe it behaving differently than the max-width variation:

CodePen Embed Fallback

The width of the children (800px) is the same. However, if you enlarge the screen (or use CodePen’s 0.5x button to zoom out), you will notice that the second parent is actually larger.

It boils down to how the browser calculates the parent’s width: we didn’t specify a width for the parent, and as child’s width value is using relative units, the browser ignores it while calculating the parent’s width and uses the max-content child of the child, dictated by the “very long … long” text.

Wrapping up

Phew! It’s crazy that something as seemingly simple as width and height actually have a lot going on. Sure, we can set explicit width and height values on an element, but the actual values that render often end up being something completely different.

That’s the beauty (and, frankly, the frustration) with CSS. It’s so carefully considered and accounts for so many edge cases. I mean, the concept of The Box Model itself is wonderfully complex and elegant at the same time. Where else can we explicitly declare something in code and have it interpreted in different ways? The width isn’t always the width.

And we haven’t even touched on some other contributing factors to an element’s dimensions. Modern layout techniques, like CSS Flexbox and Grid introduce axes and track lines that also determine the rendered size of an element.

Authors: Uri Shaked and Michal Porag

The post Exploring the Complexities of Width and Height in CSS appeared first on CSS-Tricks.

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

Weekly Platform News: The :not() pseudo-class, Video Media Queries, clip-path: path() Support

Css Tricks - Fri, 02/05/2021 - 11:02am

Hey, we’re back with weekly updates about the browser landscape from Šime Vidas.

In this week’s update, the CSS :not pseudo class can accept complex selectors, how to disable smooth scrolling when using “Find on page…” in Chrome, Safari’s support for there media attribute on <video> elements, and the long-awaited debut of the path() function for the CSS clip-path property.

Let’s jump into the news…

The enhanced :not() pseudo-class enables new kinds of powerful selectors

After a years-long wait, the enhanced :not() pseudo-class has finally shipped in Chrome and Firefox, and is now supported in all major browser engines. This new version of :not() accepts complex selectors and even entire selector lists.

For example, you can now select all <p> elements that are not contained within an <article> element.

/* select all <p>s that are descendants of <article> */ article p { } /* NEW! */ /* select all <p>s that are not descendants of <article> */ p:not(article *) { }

In another example, you may want to select the first list item that does not have the hidden attribute (or any other attribute, for that matter). The best selector for this task would be :nth-child(1 of :not([hidden])), but the of notation is still only supported in Safari. Luckily, this unsupported selector can now be re-written using only the enhanced :not() pseudo-class.

/* select all non-hidden elements that are not preceded by a non-hidden sibling (i.e., select the first non-hidden child */ :not([hidden]):not(:not([hidden]) ~ :not([hidden])) { } CodePen Embed Fallback The HTTP Refresh header can be an accessibility issue

The HTTP Refresh header (and equivalent HTML <meta> tag) is a very old and widely supported non-standard feature that instructs the browser to automatically and periodically reload the page after a given amount of time.

<!-- refresh page after 60 seconds --> <meta http-equiv="refresh" content="60">

According to Google’s data, the <meta http-equiv="refresh"> tag is used by a whopping 2.8% of page loads in Chrome (down from 4% a year ago). All these websites risk failing several success criteria of the Web Content Accessibility Guidelines (WCAG):

If the time interval is too short, and there is no way to turn auto-refresh off, people who are blind will not have enough time to make their screen readers read the page before the page refreshes unexpectedly and causes the screen reader to begin reading at the top.

However, WCAG does allow using the <meta http-equiv="refresh"> tag specifically with the value 0 to perform a client-side redirect in the case that the author does not control the server and hence cannot perform a proper HTTP redirect.

(via Stefan Judis)

How to disable smooth scrolling for the “Find on page…” feature in Chrome

CSS scroll-behavior: smooth is supported in Chrome and Firefox. When this declaration is set on the <html> element, the browser scrolls the page “in a smooth fashion.” This applies to navigations, the standard scrolling APIs (e.g., window.scrollTo({ top: 0 })), and scroll snapping operations (CSS Scroll Snap).

Unfortunately, Chrome erroneously keeps smooth scrolling enabled even when the user performs a text search on the page (“Find on page…” feature). Some people find this annoying. Until that is fixed, you can use Christian Schaefer’s clever CSS workaround that effectively disables smooth scrolling for the “Find on page…” feature only.

@keyframes smoothscroll1 { from, to { scroll-behavior: smooth; } } @keyframes smoothscroll2 { from, to { scroll-behavior: smooth; } } html { animation: smoothscroll1 1s; } html:focus-within { animation-name: smoothscroll2; scroll-behavior: smooth; }

In the following demo, notice how clicking the links scrolls the page smoothly while searching for the words “top” and “bottom” scrolls the page instantly.

CodePen Embed Fallback Safari still supports the media attribute on video sources

With the HTML <video> element, it is possible to declare multiple video sources of different MIME types and encodings. This allows websites to use more modern and efficient video formats in supporting browsers, while providing a fallback for other browsers.

<video> <source src="/flower.webm" type="video/webm"> <source src="/flower.mp4" type="video/mp4"> </video>

In the past, browsers also supported the media attribute on video sources. For example, a web page could load a higher-resolution video if the user’s viewport exceeded a certain size.

<video> <source media="(min-width: 1200px)" src="/large.mp4" type="video/mp4"> <source src="/small.mp4" type="video/mp4"> </video>

The above syntax is in fact still supported in Safari today, but it was removed from other browsers around 2014 because it was not considered a good feature:

It is not appropriate for choosing between low resolution and high resolution because the environment can change (e.g., the user might fullscreen the video after it has begun loading and want high resolution). Also, bandwidth is not available in media queries, but even if it was, the user agent is in a better position to determine what is appropriate than the author.

Scott Jehl (Filament Group) argues that the removal of this feature was a mistake and that websites should be able to deliver responsive video sources using <video> alone.

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 … or resorting to more complicated server-side or scripted or third-party solutions to deliver a correct size.

Scott has written a proposal for the reintroduction of media in video <source> elements and is welcoming feedback.

The CSS clip-path: path() function ships in Chrome

It wasn’t mentioned in the latest “New in Chrome 88” article, but Chrome just shipped the path() function for the CSS clip-path property, which means that this feature is now supported in all three major browser engines (Safari, Firefox, and Chrome).

The path() function is defined in the CSS Shapes module, and it accepts an SVG path string. Chris calls this the ultimate syntax for the clip-path property because it can clip an element with “literally any shape.” For example, here’s a photo clipped with a heart shape:

CodePen Embed Fallback

The post Weekly Platform News: The :not() pseudo-class, Video Media Queries, clip-path: path() Support appeared first on CSS-Tricks.

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

Some React Blog Posts I’ve Bookmarked and Read Lately

Css Tricks - Fri, 02/05/2021 - 5:38am
  • The React Hooks Announcement In Retrospect: 2 Years Later — Ryan Carniato considers hooks to be the most significant turning point in front end in the past five years, but he also says hooks have muddied the waters as well.
  • Mediator Component in React — Robin Wieruch’s article made me think just how un-opinionated React is and how architecturally on-your-own you are. It’s tough to find the right abstractions.
  • No One Ever Got Fired for Choosing React — Jake Lazaroff’s article is a good balance to the above. Sometimes you pick a library like React because it solves problems you’re likely to run into and lets you get to work.
  • A React “if” component — I kinda like how JSX does conditional rendering, but hey, a lightweight abstraction like this might just float your boat.
  • State of the React Ecosystem in 2021 — Hooks are big. State management is all over the place, and state machines are growing in popularity. webpack is still the main bundler. Everyone is holding their breath about Suspense… so suspenseful.
  • Blitz.js — “The Fullstack React Framework” — interesting to see a framework built on top of another framework (Next.js).
  • Introducing Zero-Bundle-Size React Server Components — Feels like it will be a big deal, but will shine brightest when frameworks and hosts zero-in on offerings around it. I’m sure Vercel and Netlify are all  &#x1f440;.

The post Some React Blog Posts I’ve Bookmarked and Read Lately appeared first on CSS-Tricks.

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

Some Typography Blog Posts I’ve Bookmarked and Read Lately

Css Tricks - Thu, 02/04/2021 - 10:36am
  • Font-size: An Unexpectedly Complex CSS Property — From Manish Goregaokar in 2017. Of many oddities, I found the one where font: medium monospace renders at 13px where font: medium sans-serif renders at 16px particularly weird.
  • The good line-height — Since CSS supports unitless line-height, you probably shouldn’t be setting a hard number anyway.
  • Time to Say Goodbye to Google Fonts — Simon Wicki doesn’t mean don’t use them, they mean self-host them. Browsers are starting to isolate cache on a per-domain basis so that old argument that you buy speed because “users probably already have it cached” doesn’t hold up. I expected to hear about stuff like having more control over font loading, but this is just about the cache.
  • My Favorite Typefaces of 2020 — John Boardley’s picks for the past year. Have you seen these “color fonts”? They are so cool. Check out LiebeHeide, it looks like real pen-on-paper.
  • How to avoid layout shifts caused by web fonts — We’ve got CLS (Cumulative Layout Shift) now and it’s such an important performance metric that will soon start affecting SEO. And because we have CSS control over font loading via font-display, that means if we set things up such that font loading shifts the page, that’s bad. I like Simon Hearne’s suggestion that we tweak both our custom font and fallback font to match perfectly. I think perfect fallback fonts are one of the best CSS tricks.
  • How to pick a Typeface for User Interface and App Design? — Oliver Schöndorfer makes the case for “functional text” which is everything that isn’t body text (e.g. paragraphs of text) or display text (e.g. headers). “Clarity is key.”

The post Some Typography Blog Posts I’ve Bookmarked and Read Lately appeared first on CSS-Tricks.

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

Lazy Load Routes in Vue with webpack Dynamic Comments

Css Tricks - Thu, 02/04/2021 - 5:53am

The way routing works in JavaScript is usually that you specify which relative URL pattern you want for which component to render. So for /about you want the <About /> component to render. Let’s take a look at how to do this in Vue/Vue Router with lazy loading, and do it as cleanly as possible. I use this little tip all the time in my own work.

A repo that includes everything covered in this post is available on GitHub.

You’ve probably seen Vue routes (URLs) like this:

import Vue from 'vue' import VueRouter from 'vue-router' import Home from '../views/Home.vue' import About from '../views/About.vue' import Login from '../views/Login.vue' Vue.use(VueRouter) const routes = [ { path: '/', name: 'Home', component: Home }, { path: '/about', name: 'About', component: About }, { path: '/login', name: 'Login', component: Login } ] const router = new VueRouter({ routes }) export default router

That will load the <Home /> component at the / route, the <About /> component at the /about route, and the <Login /> component at the /login route.

That doesn’t do a very good job of code splitting though, since all three of those components will be bundled together rather than loaded dynamically as needed.

Here’s another way to do the same, only with code splitting with dynamic import statements and webpack chunk names:

const routes = [ { path: '/', name: 'Home', component: () => import(/* webpackChunkName: "Home" */ '../views/Home.vue') }, { path: '/about', name: 'About', component: () => import(/* webpackChunkName: "About" */ '../views/About.vue') }, { path: '/login', name: 'Login', component: () => import(/* webpackChunkName: "Login" */ '../views/Login.vue') } ]

This is perfectly fine and doesn’t have any major downsides, other than being a bit verbose and repetitive. Since we’re awesome developers, let’s do a bit of abstraction to help, using an array that we’ll .map over.

const routeOptions = [ { path: '/', name: 'Home' }, { path: '/about', name: 'About' }, { path: '/login', name: 'Login' } ] const routes = routeOptions.map(route => { return { ...route, component: () => import(`@/views/${route.name}.vue`) } }) const router = new VueRouter({ routes })

Now we’ve reduced the use of the component key by using the route name as param in the import function.

But what happens if we want to set the chunk name?

As far as I know, you can’t have dynamic comments in JavaScript without some kind of build step. So, we are sacrificing comments (webpackChunkName) in favor of having to write less code in this case. It’s entirely up to you which you prefer.

Just kidding, let’s fix it.

As of webpack 2.6.0 , the placeholders [index] and [request] are supported, meaning we can set the name of the generated chunk like this:

// ... const routeOptions = [ { path: '/', name: 'Home' }, { path: '/about', name: 'About' }, { path: '/login', name: 'Login' } ] const routes = routeOptions.map(route => { return { ...route, component: () => import(/* webpackChunkName: "[request]" */ `../views/${route.name}.vue`) } }) const router = new VueRouter({ routes })

Nice! Now we have all the power, plus dynamically loaded routes with named chunks. And it works with Vue 2 and Vue 3. You can check it out by running npm run build in the terminal:

See that? Now the components are chunked out… and the build did all the naming for us!

Buuuuut, we can still take this one step further by grouping the lazy loaded routes into named chunks rather than individual components. For example, we can create groups that group our most important components together and the rest in another “not so important” group. We merely update the webpack chunk name in place of the [request] placeholder we used earlier:

const routes = [ { path: "/", name: "Home", component: () => import(/* webpackChunkName: "VeryImportantThings" */ "../views/Home.vue") }, { path: "/about", name: "About", component: () => import(/* webpackChunkName: "VeryImportantThings" */ "../views/About.vue") }, { path: "/login", name: "Login", component: () => import(/* webpackChunkName: "NotSoImportant" */ "../views/Login.vue") }, { path: "/contact", name: "Contact", component: () => import(/* webpackChunkName: "NotSoImportant" */ "../views/Contact.vue") } ];

Now our four components are groups into two separate chunks.

There you have it! A technique for lazy loading routes in Vue, plus some ideas for how to name and group them together at build.

The post Lazy Load Routes in Vue with webpack Dynamic Comments appeared first on CSS-Tricks.

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

Algolia

Css Tricks - Thu, 02/04/2021 - 4:51am

Algolia is for search. Literally any website can take advantage of Algolia-powered search. You put JSON data (“records”) in, and then you can search them at lightning speed. The magic of Algolia is that they help you with both of those things: getting data in and getting search results out.

As far as getting data in, there are all sorts of ways. The most likely situation is that you already have data somewhere and you just need to give it to Algolia. They’ve got great docs on that. You basically write an integration that updates Algolia as you update your own data. There are so many things to help with this though. Got a WordPress site? They’ve got a PHP API client and people have built plugins around it.

\

Got a Rails site? They have integrations for you. What about a Jamstack site? No problem, they’ve got a Netlify build plugin. So, for example, your Jekyll site can have great search.

One way, and I’ve used this myself plenty of times, is literally entering records manually. While manually typing in records isn’t particularly scalable, I like that it’s possible.

So that’s all the “getting data in” part. Now the fun part: building a search UI experience. Great news here too: there is lots of help to make this awesome.

The core of it is InstantSearch.js, which Algolia provides directly. That native version also has versions in React, Vue, and Angular (you could make it work with anything). Wanna see it work super quickly? Try their Create InstantSearchApp flow, which will spin up a search UI super quickly for you.

While you don’t have to use any particular library, I find them very easy to use, very flexible configuration-wise, and no problem to style. Wanna see? CDNjs has everything on it in an Algolia index, so here’s a Pen that connects to that and provides a search UI:

CodePen Embed Fallback

You can see in the code how I’m controlling the number of results, the template and styles the results are shown in, and what happens when a search result is selected. That’s powerful stuff.

This is just the surface of Algolia though. Algolia is a very mature platform with all kinds of bells and whistles you can take advantage of. You can tweak algorithms, you can dig into analytics, you can take advantage of AI, you can use it on native mobile apps… and all with real customer support along the way.

The post Algolia appeared first on CSS-Tricks.

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

Syndicate content
©2003 - Present Akamai Design & Development.