Front End Web Development

Can JavaScript Detect the Browser’s Zoom Level?

Css Tricks - Mon, 04/20/2020 - 1:08pm

No, not really.

My first guess was that this was intentionally not exposed in browsers because browsers intentionally don’t want us fighting it — or making well-intentioned but bad-outcome decisions based on that info. But I don’t see any evidence of that.

StackOverflow answers paint how weird cross-browser it can be. This script from 2013 works for me in Chrome, but not at all in Safari and reports incorrectly in Firefox. Even if that script worked, it relies on user agent detection (which is not long for this world) and some incredibly weird hacks.

So please, feel free to correct me if I’m wrong, but I think the answer is that we can’t really do this right now.

There is a thing called the Visual Viewport API

I’m kinda confused by it.

  • The spec is a draft
  • The support chart lists a lot of support
  • window.visualViewport is defined in Firefox, Safari, and Chrome (desktop)
  • But… window.visualViewport.scale is always 1 in Safari and Chrome, and undefined in Firefox. In other words: useless.

I don’t entirely know if that is supposed to accurately represent the browser zoom level, because the spec talks about specifically about pinch-zoom. So, maybe it’s just not intended for desktop browser zoom levels.

What’s a use case here?

I had a fella spell out a situation like this:

He wanted to use CSS grid to layout cemetery plots (interesting already), like a top-down blueprint of a graveyard. There was lots of information in the layout. If you were “zoomed out” so you could see the whole graveyard on one page, the text in each area would be too small to read (sincethe type would be sized to fit within the boxes/graves). Ideally, the page would hide that text while the browser is zoomed out (perhaps a .hide-text class). When zoomed in far enough, the text is shown again.


// Dunno if "resize" is best. I don't know what the "change zoom" event would be window.visualViewport.addEventListener("resize", viewportHandler); function viewportHandler(event) { // NOTE: This doesn't actually work at time of writing if ( > 3) { document.body.classList.remove("hide-text"); } else { document.body.classList.add("hide-text"); } } There is Pixel Density…

Ben Nadel recently blogged: Looking At How Browser Zoom Affects CSS Media Queries And Pixel-Density.

If you look at window.devicePixelRatio and zoom in, the pixel density in Chrome and Firefox will increase as you zoom in and decrease as you zoom out. Theoretically, you could test the original value (it might start at different places for users with different screens) and use changes in that value to guess zoom. But… in Safari it does nothing, as in, it stays the same regardless of zoom. Plus, the operating system zoom level can affect things here, making it extra tricky; not to mention that a page might start at a zoomed in level which could throw off the whole calculation from the start.

The post Can JavaScript Detect the Browser’s Zoom Level? appeared first on CSS-Tricks.

The Contrast Triangle

Css Tricks - Mon, 04/20/2020 - 12:15pm

Chip Cullen:

Let’s say you’re building a site, and you’re working with a designer. They come to you with some solid designs, and you’re ready to go. You’re also a conscientious front end developer and you like to make sure the sites you build are accessible. The designs you’re working from have some body copy, but you notice that the links inside the body copy are missing underlines.

You are now in The Contrast Triangle.

The “triangle” meaning a three-sided comparison:

  • The contrast between the background and text
  • The contrast between the background and links
  • the contrast between text and links

I would think this matters whether you underline links or not, but I take the point that without underlines the problem is worse.

I’d also argue the problem is even more complicated. You’ve also got to consider the text when it is selected, and make sure everything has contrast when that is the case too. And the hover states, active states, visited states, and focus states. So it’s really like hexacontakaienneagon of contrast or something.

Direct Link to ArticlePermalink

The post The Contrast Triangle appeared first on CSS-Tricks.

The Most Dangerous Book in the World

Typography - Mon, 04/20/2020 - 2:35am

On a cold morning in early autumn of 1536, in a small town on the outskirts of Brussels, William Tyndale was led from a tiny prison cell, then chained to a stake, strangled and burned. His crime? Daring to challenge the Catholic Church and his insistence on translating the Bible into English. Tyndale’s translation was […]

The post The Most Dangerous Book in the World appeared first on I Love Typography.

How (some) good corporate engineering blogs are written

Css Tricks - Sat, 04/18/2020 - 10:23am

Interesting research from Dan Luu¹:

… it’s pretty common for my personal blog to get more traffic than the entire corp eng blog for a company with a nine to ten figure valuation and it’s not uncommon for my blog to get an order of magnitude more traffic.

I think this is odd because tech companies in that class often have hundreds to thousands of employees. They’re overwhelmingly likely to be better equipped to write a compelling blog than I am and companies get a lot more value from having a compelling blog than I do.

First, yes. There is a crapload of value in having a good blog (top of funnel traffic, showcasing culture for hiring, establishing industry leadership…) yet so few companies do it well even when they have more than enough resources to do so.

Dan doesn’t just speculate on this, he interviewed people at companies that have actually good engineering blogs: Heap, Segment, and Cloudflare. Then he listed their internal process for blogging. The first step in all three is the same: “Someone has an idea to write a post”. That makes sense, but I would think there is something deeper going on with good blogs: engineers that want to come up with ideas because it is encouraged and incentivized. And then after the ball is rolling, there is a positive feedback loop and as few blockers as possible.

Random observations from me:

  • We recently started using Appcues at CodePen, and it was on our radar at all because people at CodePen read their blog and liked it.
  • While Appcues largely blogs about stuff that is directly related to stuff their software can help with, Logrocket, a software product for tracking JavaScript errors, has a blog that isn’t terribly different than CSS-Tricks. It’s just about front-end everything and every single blog post has a section in it that is a pitch for the product. Looks like they’ve been doing it for about 3 years now, so my hunch is that it works extremely well.
  • All the browser vendors do a pretty good job of blogging overall, but at the same time, feel a bit disjointed. What blog(s) should I be reading for Mozilla/Firefox stuff? I don’t know really. Is it the official looking one? Or the “hacks” one? Or the planet one? Or nightly one? Or the one for releases? Google seems to have the same problem. There isn’t an obvious hub of writing.
  • Stripe has a strong engineering blog, but they take it to another level by producing a fancy publication (Increment) that is both online and in print.
  1. A little CSS would go a long way on this site: body { max-width: 600px; margin: 20px auto; }

Direct Link to ArticlePermalink

The post How (some) good corporate engineering blogs are written appeared first on CSS-Tricks.

Front-End Challenges

Css Tricks - Fri, 04/17/2020 - 11:50am

My favorite way to level up as a front-end developer is to do the work. Literally just build websites. If you can do it for money, great, you should. If the websites you make can help yourself or anyone else you care about, then that’s also great. In lieu of that, you can also make things simply for the sake of making them, and you’ll still level up. It’s certainly better than just reading about things!

Here’s some resources that encourage you to level up by building things for the sake of leveling up, if you’re up to it.

Frontend Mentor

It looks like this recently launched, and it’s what inspired this post. This idea of giving people front-end work to do is enough to build a business around! Some of them are free, and some of them are not.


Other businesses have centered themselves around this too. HackerRank is all about getting jobs and hiring, so they’ve got a very strong agenda, but part of the way they do that is putting you through these skill tests (solving challenges) which are meant to asses you, but you can certainly learn from them too.

Others like this: Codewars, ChallengeRocket, Codesignal, Topcoder (Jeepers, VCs must love this idea.)


Coderbyte has paid plans too, and they’re designed to leveling up your job interviewing skills with challenges.

Classic situation: sometimes the site is the product and you’re the customer, and sometimes hiring companies are the customer and you’re the product.

Build Dribbble shots

Here’s the classic move: find something you like on Dribbble, rebuild it. The @keyframers often do it. Tim Evko’s Practice site used to pick a shot for you (along with random GitHub issues and random coding challenges), but the Dribbble part appears broken at the moment. The other stuff still works!

Matt Delac used to do a series along these lines. Indrek Lasn also does it in Medium posts.

Front-End Challenges Club

Andy Bell did a Front-End Challenges Club for a while, and while I think it’s on break, you can view the archives.

CodePen Challenges

CodePen Challenges run every week are are a prompt (along with ideas and resources) for building whatever you like. Low key.

100 Days of CSS Challenge

Matthias Martin created 100 days of CSS challenges. They are all there for you to see, including entries from other people — but the point is for you to give it a shot yourself, of course.

Daily UI

Daily UI challenges gives you a new challenge every day that starts when you sign up (and it’s free). Lots of people complete the challenge with code.


Frontloops charges $19 for 30 challenges, which includes information, advice, assets, and a solution.


If your idea of a fun challenge is mimicking a design in as few bytes of code as possible, CSSBattle will appeal to you.

Writing things as tersely as possible is often called “Code Golf” and there is a challenge site for that too.

Ace Front End

Ace Front End has challenges that focus specifically on vanilla HTML, CSS, and JavaScript.

I just happened to notice that the first challenge is a drop down navigation menu, and it doesn’t handle things like aria-expanded. I’m not entirely sure how big of a problem that is and I don’t mean to pick on Ace Front End — it’s just a reminder that there could be problems with any of these challenges. But that doesn’t mean you can’t learn something from them.


Codier has public challenges that include solutions posted by other users.

rendezvous with cassidoo

Cassidy’s weekly newsletter includes a challenge in every issue.

Rina Diane Caballar quoting Tim Carry in Extending the Limits of CSS:

Carry’s advice is to start with a real-world object—the interface of a gaming console or a calculator, for example—and to try to recreate it using only CSS. “A great way to push the boundaries with a language is to make something that the language wasn’t meant to be doing in the first place,” he says.

The post Front-End Challenges appeared first on CSS-Tricks.

Pseudo-Randomly Adding Illustrations with CSS

Css Tricks - Fri, 04/17/2020 - 10:28am

Between each post of Eric Meyer’s blog there’s this rather lovely illustration that can randomly be one of these five options:

Eric made each illustration into a separate background image then switches out that image with the nth-of-type CSS property, like this:

.entry:nth-of-type(2n+1)::before { background-image: url(image-1.png); } .entry:nth-of-type(3n+1)::before { background-image: url(image-2.png); } .entry:nth-of-type(4n+1)::before { background-image: url(image-3.png); } .entry:nth-of-type(5n+1)::before { background-image: url(image-4.png); }

This seems like a good time to plug our very own little :nth Tester tool. It definitely helps me understand what something like (2n+1) means in English. You can type in any string you like and see what effect that has on your site:

Anyway, back to Eric’s post. As he mentions, his technique is pseudo-random in that it looks like a random image on the page but it technically isn’t. Either way, I think it’s a really lovely technique! And it certainly breaks up the visual monotony that happens when you’re looking at a website for too long.

Here’s what it looks like in practice:

Lovely stuff!

Another way to do this is to use random numbers in CSS. For example, we could set a variable in JavaScript and then apply it with CSS custom properties. Or we could put all the images in a single sprite file and change the background-position based on a random number.

This is definitely one of those things in CSS where there are no wrong answers; just different ways to do the same awesome thing!

Direct Link to ArticlePermalink

The post Pseudo-Randomly Adding Illustrations with CSS appeared first on CSS-Tricks.

Better Form Inputs for Better Mobile User Experiences

Css Tricks - Fri, 04/17/2020 - 5:08am

Here’s one simple, practical way to make apps perform better on mobile devices: always configure HTML input fields with the correct type, inputmode, and autocomplete attributes. While these three attributes are often discussed in isolation, they make the most sense in the context of mobile user experience when you think of them as a team. 

There’s no question that forms on mobile devices can be time-consuming and tedious to fill in, but by properly configuring inputs, we can ensure that the data entry process is as seamless as possible for our users. Let’s take a look at some examples and best practices we can use to create better user experiences on mobile devices.

Use this demo to experiment on your own, if you’d like. Using the correct input type

This is the easiest thing to get right. Input types, like email, tel, and url, are well-supported across browsers. While the benefit of using a type, like tel over the more generic text, might be hard to see on desktop browsers, it’s immediately apparent on mobile.

Choosing the appropriate type changes the keyboard that pops up on Android and iOS devices when a user focuses the field. For very little effort, just by using the right type, we will show custom keyboards for email, telephone numbers, URLs, and even search inputs

Text input type on iOS (left) and Android (right) Email input type on iOS (left) and Android (right) URL input type on iOS (left) and Android (right) Search input type on iOS (left) and Android (right)

One thing to note is that both input type="email" and input type="url" come with validation functionality, and modern browsers will show an error tooltip if their values do not match the expected formats when the user submits the form. If you’d rather turn this functionality off, you can simply add the novalidate attribute to the containing form.

A quick detour into date types

HTML inputs comprise far more than specialized text inputs — you also have radio buttons, checkboxes, and so on. For the purposes of this discussion, though, I’m mostly talking about the more text-based inputs

There is a type of input that sits in the liminal space between the more free-form text inputs and input widgets like radio buttons: date. The date input type comes in a variety of flavors that are well-supported on mobile, including date, time, datetime-local, and month. These pop up custom widgets in iOS and Android when they are focused. Instead of triggering a specialized keyboard, they show a select-like interface in iOS, and various different types of widgets on Android (where the date and time selectors are particularly slick). 

I was excited to start using native defaults on mobile, until I looked around and realized that most major apps and mobile websites use custom date pickers rather than native date input types. There could be a couple reasons for this. First, I find the native iOS date selector to be less intuitive than a calendar-type widget. Second, even the beautifully-designed Android implementation is fairly limited compared to custom components — there’s no easy way to input a date range rather than a single date, for instance. 

Still, the date input types are worth checking out if the custom datepicker you’re using doesn’t perform well on mobile. If you’d like to try out the native input widgets on iOS and Android while making sure that desktop users see a custom widget instead of the default dropdown, this snippet of CSS will hide the calendar dropdown for desktop browsers that implement it:

::-webkit-calendar-picker-indicator {   display: none; } Date input type on iOS (left) and Android (right) Time input type on iOS (left) and Android (right)

One final thing to note is that date types cannot be overridden by the inputmode attribute, which we’ll discuss next.

Why should I care about inputmode?

The inputmode attribute allows you to override the mobile keyboard specified by the input’s type and directly declare the type of keyboard shown to the user. When I first learned about this attribute, I wasn’t impressed — why not just use the correct type in the first place? But while inputmode is often unnecessary, there are a few places where the attribute can be extremely helpful. The most notable use case that I’ve found for inputmode is building a better number input.

While some HTML5 input types, like url and email, are straightforward, input type="number" is a different matter. It has some accessibility concerns as well as a somewhat awkward UI. For example, desktop browsers, like Chrome, show tiny increment arrows that are easy to trigger accidentally by scrolling.

So here’s a pattern to memorize and use going forwards. For most numeric inputs, instead of using this: 

<input type="number" />

…you actually want to use this:

<input type="text" inputmode="decimal" />

Why not inputmode="numeric" instead of inputmode="decimal" ? 

The numeric and decimal attribute values produce identical keyboards on Android. On iOS, however, numeric displays a keyboard that shows both numbers and punctuation, while decimal shows a focused grid of numbers that almost looks exactly like the tel input type, only without extraneous telephone-number focused options. That’s why it’s my preference for most types of number inputs.

iOS numeric input (left) and decimal input (right) Android numeric input (left) and decimal input (right)

Christian Oliff has written an excellent article dedicated solely to the inputmode attribute.

Don’t forget autocomplete

Even more important than showing the correct mobile keyboard is showing helpful autocomplete suggestions. That can go a long way towards creating a faster and less frustrating user experience on mobile.

While browsers have heuristics for showing autocomplete fields, you cannot rely on them, and should still be sure to add the correct autocomplete attribute. For instance, in iOS Safari, I found that an input type="tel" would only show autocomplete options if I explicitly added a autocomplete="tel" attribute.

You may think that you are familiar with the basic autocomplete options, such as those that help the user fill in credit card numbers or address form fields, but I’d urge you to review them to make sure that you are aware of all of the options. The spec lists over 50 values! Did you know that autocomplete="one-time-code" can make a phone verification user flow super smooth?

Speaking of autocomplete…

I’d like to mention one final element that allows you to create your own custom autocomplete functionality: datalist. While it creates a serviceable — if somewhat basic — autocomplete experience on desktop Chrome and Safari, it shines on iOS by surfacing suggestions in a convenient row right above the keyboard, where the system autocomplete functionality usually lives. Further, it allows the user to toggle between text and select-style inputs.

On Android, on the other hand, datalist creates a more typical autocomplete dropdown, with the area above the keyboard reserved for the system’s own typeahead functionality. One possible advantage to this style is that the dropdown list is easily scrollable, creating immediate access to all possible options as soon as the field is focused. (In iOS, in order to view more than the top three matches, the user would have to trigger the select picker by pressing the down arrow icon.)

You can use this demo to play around with datalist:

CodePen Embed Fallback

And you can explore all the autocomplete options, as well as input type and inputmode values, using this tool I made to help you quickly preview various input configurations on mobile.

In summary

When I’m building a form, I’m often tempted to focus on perfecting the desktop experience while treating the mobile web as an afterthought. But while it does take a little extra work to ensure forms work well on mobile, it doesn’t have to be too difficult. Hopefully, this article has shown that with a few easy steps, you can make forms much more convenient for your users on mobile devices.

The post Better Form Inputs for Better Mobile User Experiences appeared first on CSS-Tricks.

Thank You, Christopher Schmitt

Css Tricks - Thu, 04/16/2020 - 10:15am

It’s incredibly sad that Christopher Schmitt passed away last week¹. I keep thinking about how Christopher was one of the best dudes I knew. Just incredibly kind and thoughtful all the way through. I know everyone says that about people after they pass, but I really mean it here. I’m sure we all know people that we like quite a bit, but hey, we know they can be an asshole sometimes too. Not Christopher, at least not to me. He was always good to me in ways I’ll never be able to reciprocate.

Here’s the most important thing:

We (a group of developer friends) would like to build a site of memories that people have of Christopher. We have our own, but we’d like to collect as many memories from as many people as possible to make this a true memorial to him. If you have one you would like to share publicly, please submit it to this little site just for that.

I hardly know where to start reminiscing about Christopher. Broadly: I spoke at more conferences thrown by Christopher (and Ari) than anyone else. At least a dozen. He used to throw one called In Control in Orlando that I spoke at a number of times and met some early web friends that I’m still close with today! Stephanie Rewis mentioned to me that’s where we met the first time and she remembers me coding away on a yet-to-be released CodePen while there. In Control went to Hawaii one year, along with the first-ever CSS Dev Conf, bringing me to Hawaii for the first time in my life, where I remember driving around the island with Daniel Burka. CSS Dev Conf went all sorts of amazing places, and I went to all of them! Estes Park, New Orleans, The Queen Mary… all these incredible locations that were all incredible memories for me.

And all I have are blurry, distant photos of him like this:

Christopher and Ari also ran an absolute ton of online conferences. CSS Summit, RWD Summit, SVG Summit… they ran a ton of them and they were really ahead of their time. I remember a funny moment where I had just moved to a new city and my internet wasn’t installed yet, but I still had to present online at one of these, so I did it out of my neighbors garage (as their kids slept), and the light in the garage kept going out because it didn’t detect movement. &#x1f62c;

The people I met through Christopher are too many to count. He was a great connector in that way. His influence on the web was tremendous through his direct work, like writing (what, a dozen books?), blogging (even here!), speaking, and podcasting (e.g. The Non Breaking Space Show) but when you consider the people he brought together, his influence is immeasurable.

I literally made money off the guy. In the early years of ShopTalk, when it was tough to sell podcast spots at all, Christopher would sponsor every episode of ShopTalk, which probably gave us the boost that ensured it’s still happening today. Who knows what would have happened without that.

One of the things I liked best about Christopher is that he was a damn nerd. Through and through. In a way that made you envious that you could one day hope to up your nerd level that high. Guy had a Chewbacca costume that he wore regularly. His Instagram is full of snaps of him meeting other famous nerds.

I hope you still got those black Converses on wherever you are, buddy.

(And remember, if you have a memory with Christopher, please share it.)

  1. It wasn’t COVID-19. Poor guy was dealt bad cards from day one for his health and he fought it the the best he could.

The post Thank You, Christopher Schmitt appeared first on CSS-Tricks.

Creating Color Themes With Custom Properties, HSL, and a Little calc()

Css Tricks - Thu, 04/16/2020 - 10:01am

Before the advent of CSS custom properties (we might call them “variables” in this article as that’s the spirit of them), implementing multiple color schemes on the same website usually meant writing separate stylesheets. Definitely not the most maintainable thing in the world. Nowadays, though, we can define variables in a single stylesheet and let CSS do the magic.

Even if you aren’t offering something like user-generated or user-chosen color themes, you might still use the concept of theming on your website. For example, it is fairly common to use different colors themes across different areas of the site.

We’re going to build out an example like this:

Same layout, different colors.

In this example, all that changes between sections is the color hue; the variations in lightness are always the same. Here’s an example of a simplified color palette for a specific hue:

CodePen Embed Fallback

A palette of multiple hues might look something like this:

CodePen Embed Fallback

This would take effort to do with RGB color value, but in HSL only one value changes.

Enter custom properties

Custom properties have been around for a while and are widely supported. Polyfills and other solutions for IE 11 are also available.

The syntax is very similar to traditional CSS. Here is an overview of the basic usage:

CodePen Embed Fallback

It’s common to see variables defined on the :root pseudo-element, which is always <html> in HTML, but with higher specificity. That said, variables can be defined on any element which is useful for scoping specific variables to specific elements. For example, here are variables defined on data attributes:

CodePen Embed Fallback Adding calc() to the mix

Variables don’t have to be fixed values. We can leverage the power of the calc() function to automatically calculate values for us while adhering to a uniform pattern:

CodePen Embed Fallback

Since CSS doesn’t support loops, a preprocessor would be handy to generate a part of the code. But remember: CSS variables are not the same as Sass variables.

Implementing CSS variables

What we’re basically trying to do is change the color of the same component on different sections of the same page. Like this:

CodePen Embed Fallback

We have three sections in tabs with their own IDs: #food, #lifestyle, and #travel. Each section corresponds to a different hue. The  data-theme-attribute on the div.wrapper element defines which hue is currently in use.

When #travel is the active tab, we’re using the --first-hue variable, which has a value of 180°. That is what gets used as the --hue value on the section, resulting in a teal color:

<div class="wrapper" data-theme="travel"> .wrapper[data-theme="travel"] {   --hue: var(--first-hue);  /* = 180° = teal */ }

Clicking any of the tabs updates the data-theme attribute to the ID of the section, while removing the hash (#) from it. This takes a smidge of JavaScript. That’s one of the (many) nice things about CSS: they can be accessed and manipulated with JavaScript. This is a far cry from preprocessor variables, which compile into values at the build stage and are no longer accessible in the DOM.

<li><a href="#food">Food</a></li> const wrapper = document.querySelector('.wrapper'); document.querySelector("nav").addEventListener('click', e => {   e.preventDefault();   e.stopPropagation();   // Get theme name from URL and ditch the hash   wrapper.dataset.theme ='href').substr(1); }) Progressive enhancement

When we use JavaScript, we should be mindful of scenarios where a user may have disabled it. Otherwise, our scripts — and our UI by extension — are inaccessible. This snippet ensures that the site content is still accessible, even in those situations:

document.querySelectorAll('section').forEach((section, i) => {   if (i) { // hide all but the first section = 'none';   } })

This merely allows the tabs to scroll up the page to the corresponding section. Sure, theming is gone, but providing content is much more important.

While I chose to go with a single-page approach, it’s also possible to serve the sections as separate pages and set [data-theme] on the server side. 

Another approach

So far, we’ve assumed that color values change linearly and are thus subject to a mathematical approach. But even in situations where this is only partially true, we may still be able to benefit from the same concept. For instance, if lightness follows a pattern but hue doesn’t, we could split up the stylesheet like this:

<head>   <style>     :root {       --hue: 260;     }   </style>   <link rel="stylesheet" href="stylesheet-with-calculations-based-on-any-hue.css"> </head> Supporting web components

Web components are an exciting (and evolving) concept. It’s enticing to think we can have encapsulated components that can be reused anywhere and theme them on a case-by-case basis. One component with many contexts!

We can use CSS variable theming with web components. It requires us to use a host-context() pseudo-selector. (Thanks to habemuscode for pointing this out to me!)

:host-context(body[data-theme="color-1"]) {   --shade-1: var(--outsideHSL); } In summary…

Theming a website with CSS custom properties is much easier than the workaround approaches we’ve resorted to in the past. It’s more maintainable (one stylesheet), performant (less code), and opens up new possibilities (using JavaScript). Not to mention, CSS custom properties become even more powerful when they’re used with HSL colors and the calc() function.

We just looked at one example where we can change the color theme of a component based on the section where it is used. But again, there is much more opportunity here when we start to get into things like letting users change themes themselves – a topic that Chris explores in this article.

The post Creating Color Themes With Custom Properties, HSL, and a Little calc() appeared first on CSS-Tricks.

Jetpack Instant Search!

Css Tricks - Thu, 04/16/2020 - 9:19am

Jetpack has had a search feature for a while. Flip it on, and it replaces your built-in WordPress search (which is functional, but not particularly good) with an Elasticsearch-powered solution that is faster and has better results. I’ve been using that for quite a while here on CSS-Tricks, which was an upgrade from using a Google Custom Search Engine.

Jetpack just upped their game again with a brand new search upgrade. You can use Jetpack search how you were already, or you can flip on Instant Search and take advantage of this all-new search experience (inside and out).

Flipping on Jetpack Instant Search from Jetpack Settings A Full Page Experience

Instant Search provides a full page-covering search experience. I think it’s awesome. When a user is searching, that’s the mindset they are in and giving them all the space they need to accomplish that goal is great. Here’s me searching (video):

Note that I misspell the word “margin” and the results are fine. You get a full page experience on mobile as well.

Best I can tell, CSS-Tricks gets a couple hundred thousand on-site searches per month, so having a great experience there is very important to me. I don’t even wanna mess around with bad on-site search experiences, or products that are too expensive. I’d rather send people to a site-scoped Google search than a bad on-site search. Fortunately, Instant Search is about as good of an on-site search experience as I can imagine, especially for the zero-work it takes to implement.

Design Control

You have some control over the look of things from the Customizer.

Instant Search is designed to work on any site, so you probably don’t need to do much. I was really surprised how well it worked out-of-the-box for CSS-Tricks. As a CSS control freak, I did ship a handful of design tweaks to it, but that’s just because I love doing that kind of thing.

Tweaks No Longer Needed

With the previous version of Jetpack Search, I had custom code in place to tweak Elasticsearch. I did things like factoring in comment counts as an indicator of popularity, so that I could be sure our best content was high in the results. Remember as powerful as this search is, it doesn’t have a model of the entire internet to calculate relevancy from like Google does. Good news though:

To further improve our search algorithm, we started experimenting with adding the percentage of pageviews from the past 30 days into the index. We ended up finding that pageviews are a much better ranking signal because it somewhat combines both popularity and recency. So now most of our result ranking is strongly influenced by the number of pageviews a post or page gets. Conveniently, if you get a lot of Google Search traffic, our search results should be heavily influenced by Google’s ranking algorithm.

Emphasis mine. With Jetpack Instant Search, I was able to rip all that custom code out (removing code always feels great) because the new algorithms are doing a great job with ranking results.


Now Jetpack Search is ala-carte rather than baked into specific plans. Don’t need it? You don’t pay for it. Only need this feature? You can buy it regardless of what plan you are on.

I’m told the pricing is about scope. Jetpack plans are about features, not scale of site, but that doesn’t make much sense for search where the scale of the site matters a ton. So it’s a sliding scale based on the “records” you have, which are basically posts and pages.

Sliding scale of pricing

I would think a lot of sites fall into the $25/month (15% off for annual) category. You probably mostly start caring about on-site search above 1,000 records and 10,000 records is a ton. I pay for the tier one up from that (~$612 a year) only because our (now archived) bbPress forums pushes the number over 10,000. That’s a perfectly fair price for this for a site like mine.

Wish List

My #1 thing is that I wish it was easy to remove certain things from search results. We have tons and tons of records from our bbPress forums that I made the (hard) call to close this year. Removing those records would pull me down into a smaller pricing tier, but more importantly, I’d rather just not show those results in search at all.

It’s not just CSS-Tricks being in an unusual situation. I’ve also turned on Jetpack Instant Search on the CodePen Documentation.

In that circumstance, I’d consider turning removing blog posts (believe it or not) from the search results, so instead just the Pages would show up which are our core documentation there. Or perhaps even better, blog posts are just turned off as a filter by default, but users could flip them on to see them in the results.

All in all, this is a huge upgrade to Jetpack and yet another reason I consider it the most important plugin I run on my WordPress sites. If you’re curious about other Jetpack features we use, we made a special page for that.

The post Jetpack Instant Search! appeared first on CSS-Tricks.

CSS Scrollbar With Progress Meter

Css Tricks - Wed, 04/15/2020 - 11:49am

Scrollbars are natural progress meters. How far the scrollbar is down or across is how much progress has been made scrolling through that element (often the entire page). But, they are more like progress indicators than meters, if you think of a meter as something that “fills up” as you go.

We can use some CSS trickery to make the scrollbar fill up as we go.

This will only work with -webkit- vendor-prefixed scrollbar-styling properties. In other words, these are non-standard. The standardized scrollbar styling properties are scrollbar-width and scrollbar-color, which can’t pull this kind of thing off, but are probably a safer bet in the long run. Still, the vendor-prefixed versions probably aren’t going anywhere, so if you consider this a weird form of progressive enhancement, that’s probably fine.

What’s the trick?

Essentially, it’s hanging a huge box-shadow off the top of the scrollbar thumb — or off the side if it’s a horizontally scrolling element.

:root { --shadow: #43a047; --scrollbarBG: #eee; --thumbBG: #66bb6a; } ::-webkit-scrollbar { width: 16px; } ::-webkit-scrollbar-track { background: var(--scrollbarBG); } ::-webkit-scrollbar-thumb { background-color: var(--thumbBG); box-shadow: 0 -100vh 0 100vh var(--shadow), 0 0 15px 5px black; } Demo CodePen Embed Fallback

I first saw this in a Pen by Myk.

That example didn’t differentiate the thumb part of the scrollbar at all, which makes it more meter-like, but also harder to use. My demo has a slightly different color thumb.

Can I really use this?

No! Aside from it being super weird and non-standard. Safari flips it’s lid and I have no idea how to fix it.

I do happen to have a favorite CSS trick that is highly related to this though.

I want to learn more about styling scrollbars

Cool, here you go.

The post CSS Scrollbar With Progress Meter appeared first on CSS-Tricks.

Using CSS to Set Text Inside a Circle

Css Tricks - Tue, 04/14/2020 - 4:57am

You want to set some text inside the shape of a circle with HTML and CSS? That’s crazy talk, right?

Not really! Thanks to shape-outside and some pure CSS trickery it is possible to do exactly that. 

However, this can be a fiddly layout option. We have to take lots of different things into consideration, like character count, word count, typeface variations, font sizing, font formatting, and responsive requirements to name a few. One size, does not fit all here. But hey, let’s do it anyway.

Here’s the goal: we want to display a <blockquote> and an author citation inside a circle shape. We also want to make the layout as flexible as we can. This layout won’t require any additional files and keeps the HTML markup squeaky clean.

This is what we’re striving for:

CodePen Embed Fallback

The shape-outside feature is not supported in Internet Explorer or Microsoft Edge 18 and below at the time of this writing.

First up, the HTML

We’re going to end up needing a wrapper element to pull this off, so let’s use the semantic <blockquote> as the inner element. The outside wrapper can be a div:

<div class="quote-wrapper">   <blockquote class="text" cite="">     <p>Experience design is the design of anything, independent of medium, or across media, with human experience as an explicit outcome, and human engagement as an explicit goal.</p>     <footer>– Jesse James Garrett</footer>   </blockquote> </div>

If you’re interested in a deep-dive on the HTML of quotes, you’re in luck. We’re going to set the quote itself in a <p> and the name of the author inside a <footer>. We’ve got class names for the CSS styling hooks we’ll need.

Next, some baseline CSS

Let’s start with the div wrapper. First, we’ll set the minimum (responsive) square size at 300px so it fits on smaller screens. then, we’ll add relative positioning (because we will need it later). 

.quote-wrapper {   height: 300px;   position: relative;   width: 300px; }

Now we’ll make the blockquote fill the whole wrapper and fake a circle shape with a radial gradient background. (That’s right, we are not using border-radius in this example).

.text {   background: radial-gradient(     ellipse at center,     rgba(0, 128, 172, 1) 0%,     rgba(0, 128, 172, 1) 70%,     rgba(0, 128, 172, 0) 70.3%   );   height: 100%;   width: 100%; }

One thing to note is that 70% displays a much rougher edge. I manually added very small percentage increments and found that 70.3% looks the smoothest.

Notice the edge on the right is much smoother than the edge on the left.

Now we have our base circle in place. Add these additional style rules to .text.

.text {   color: white;   position: relative;   margin: 0; }

Here’s what we have so far:

Giving text the CSS treatment

Let’s style the paragraph first:

.text p {   font-size: 21px;   font-style: italic;   height: 100%;   line-height: 1.25;   padding: 0;   text-align: center;   text-shadow: 0.5px 0.5px 1px rgba(0, 0, 0, 0.3); }

Let’s use the blockquote’s ::before pseudo-element to create our shaping. This is where the shape-outside property comes into play. We plot out the polygon() coordinates and float it to the left so the text wraps inside the shape.

.text::before {   content: "";   float: left;   height: 100%;   width: 50%;   shape-outside: polygon(     0 0,   98% 0,     50% 6%,     23.4% 17.3%,     6% 32.6%,     0 50%,     6% 65.6%,     23.4% 82.7%,     50% 94%,     98% 100%,     0 100%   );   shape-margin: 7%; }

Let’s change the radial background color to red. The path editor polygon points and connecting lines are also blue. We are changing this color temporarily for greater contrast with the editor tool.

background: radial-gradient(   ellipse at center,   rgba(210, 20, 20, 1) 0%,   rgba(210, 20, 20, 1) 70%,   rgba(210, 20, 20, 0) 70.3% );

I like Firefox’s developer tools because it has super handy features like a shape-outside path editor.  Click on the polygon shape in the inspector to see the active shape in the browser window. Big thumbs up to the Mozilla dev team for creating a very cool interface!

The Firefox shape editor tool also works for clip-path and <basic-shape> values.

Here’s what we have at this point:

Those points along the shape are from Firefox’s editing tool.

We can do the same sort of thing for the paragraph’s ::before pseudo-element. We use the shape-outside to make the same polygon, in reverse, then float it to the right.

.text p::before {   content: "";   float: right;   height: 100%;   width: 50%;   shape-outside: polygon(     2% 0%,     100% 0%,     100% 100%,     2% 100%,     50% 94%,     76.6% 82.7%,     94% 65.6%,     100% 50%,     94% 32.6%,     76.6% 17.3%,     50% 6%     );   shape-margin: 7%; }

Looking good, but where did the footer go? It overflowed the <blockquote> (where the circular colored background is), so we’re unable to see that white text on a white background.

Styling the footer

Now we can style the <footer> and give it an absolute position to bring it back on top of the circle.

.quote-wrapper blockquote footer {   bottom: 25px;   font-size: 17px;   font-style: italic;   position: absolute;   text-align: center;   text-shadow: 0.5px 0.5px 1px rgba(0, 0, 0, 0.3);   width: 100%; }

Again, feel free to change the background color to suit your needs.

This is where the fiddly part comes in. The text itself needs to be styled in such a way that the number of words and characters work inside the shape. I used these CSS rules to help make it fit nicely:

  • font-size
  • shape-margin (we have two exclusion areas to adjust)
  • line-height
  • letter-spacing
  • font-weight
  • font-style
  • min-width  and min-height (to size of the .quote-wrapper container)
Adding the quote mark for some flourish

Did you see the giant quotation mark in the original demo? That’s what we want to make next.

We’ll take advantage of the ::before  pseudo-element for .quote-wrapper. Yet again, this will take a fair amount of fiddling to make it look right. I found line-height has a huge effect on the mark’s vertical position.

.quote-wrapper::before {   content: "\201C";   color: #ccc;   font-family: sans-serif, serif;   font-size: 270px;   height: 82px;   line-height: 1;   opacity: .9;   position: absolute;   top: -48px;   left: 0;   z-index: 1; } .dumb-quotes::before { content: "\0022"; } .dumb-quotes::after { content: "\0022"; }

There’s actually a difference between curly (“smart”) quote marks and straight (dumb) ones. I’d suggest using curly quote marks for dialogue and straight quote marks for coding.

Handling responsive styles

We should probably make our quote bigger on larger screens. I’m setting a breakpoint at 850px, but you may want to use something different.

@media (min-width: 850px) {   .quote-wrapper {     height: 370px;     width: 370px;   }   .quote-wrapper::before {     font-size: 300px;   }   .text p {     font-size: 26px;   }   .quote-wrapper blockquote footer {     bottom: 32px;   } } There we have it! CodePen Embed Fallback

We set HTML text inside a circular shape using a combination of old and new CSS techniques to make an appealing <blockquote> that commands attention. And we achieved our display goal without any additional dependencies, while still keeping the HTML markup clean and semantic.

I hope this article encourages you to explore new layout possibilities with shape-outside. Stay tuned for shape-inside.

The post Using CSS to Set Text Inside a Circle appeared first on CSS-Tricks.

No-Class CSS Frameworks

Css Tricks - Mon, 04/13/2020 - 11:51am

I linked up Water.css not long ago as an interesting sort of CSS framework. No classes. No <h2 class="is-title">. You just use semantic HTML and get styles. Is that going to “scale” very far? Probably not, but it sure is handy for styling things quickly, where — of course — you’re writing semantic HTML but don’t need to care tremendously about the look, other than it should look as decent as it can with low effort.

This week I saw MVP.css making the rounds. Same idea. There are a bunch more!

Even Foundation, while being a big honkin’ framework, does some pretty decent stuff classless-ly™.

The post No-Class CSS Frameworks appeared first on CSS-Tricks.

Styling in the Shadow DOM With CSS Shadow Parts 

Css Tricks - Mon, 04/13/2020 - 4:41am

Safari 13.1 just shipped support for CSS Shadow Parts. That means the ::part() selector is now supported in Chrome, Edge, Opera, Safari, and Firefox. We’ll see why it’s useful, but first a recap on shadow DOM encapsulation…

The benefits of shadow DOM encapsulation

I work at giffgaff where we have a wide variety of CSS code that has been written by many different people in many different ways over the years. Let’s consider how this might be problematic. 

Naming collisions

Naming collisions between classes can easily crop up in CSS. One developer might create a class name like .price. Another developer (or even the same one) might use the same class name, without knowing it.

CSS won’t alert you to any error here. Now, any HTML elements with this class will receive the styling intended for two completely different things.

Shadow DOM fixes this problem. CSS-in-JS libraries, like Emotion and styled-components, also solve this issue in a different way by generating random class names, like .bwzfXH. That certainly does help avoid conflicts! However, CSS-in-JS doesn’t prevent anybody from breaking your component in other ways. For example…

Base styles and CSS resets

Styles can be applied using HTML element selectors like <button> and <div>. These styles could break a component. Shadow DOM is the only thing that offers (almost) full encapsulation — you can rest assured that your component will look the same, even in a messy  !important  strewn codebase because each component is encapsulated.

/* This will have no effect on buttons inside shadow DOM */ button { background-color: lime !important; }

I wouldn’t say it’s good practice to style elements this way, but it happens. Even it does, those styles will have no effect on the shadow DOM.

It’s worth noting that inheritable styles like color, font and line-height are still inherited in a shadow DOM. To prevent that, use all: initial  or, preferably, all: revert once it has better browser support.

Let’s look at a common example of CSS applied directly to HTML elements. Consider this code from Eric Meyer’s reset

html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed,  figure, figcaption, footer, header, hgroup,  menu, nav, output, ruby, section, summary, time, mark, audio, video {   margin: 0;   padding: 0;   border: 0;   font-size: 100%;   font: inherit;   vertical-align: baseline; }

What if the component we’re working with makes use of the user agent’s default values for margin and padding? This reset might cause it to appear broken since those defaults are effectively wiped out.

Shadow DOM is a way to avoid these problems. Shadow DOM allows us to feel fully confident that a component will render as expected, regardless of what codebase it ends up in. Equally, none of the code meant only for a component can inadvertently affect anything else — all without resorting to onerous class naming conventions. Shadow DOM offers a level of encapsulation that can’t be achieved any other way.

Encapsulation is great, but we also want our components to be themeable and customizable. That’s been made far easier with the ::part selector. 

Styling shadow DOM with ::part()

Until now, the only way for CSS to modify the styling of a custom element from outside of the shadow DOM was to use CSS custom properties. In a strict design system where you only want to allow limited changes, that might be ideal. If you want your component to be more versatile, it creates a problem. Every CSS property you want to offer up for styling needs to be defined using a custom property. Just the sound of that seems tedious.

The situation is compounded further if we want to style a component differently based on pseudo-classes, like :hover. Basically, we end up with loads of custom properties. Let’s look at an example from Ionic, an open source set of web components. Just look at all the custom properties defined on the Ionic button component.

Go ahead, I’ll wait.

I counted 23 custom properties. Needless to say, that’s less than ideal.

Here’s an example using ::part() to style the element instead. 

CodePen Embed Fallback

In this Pen, I’m simply changing the color, border and background-color properties, but I could use whatever I want without being constrained by what custom properties have been defined. Notice that I can also style different states of the part using pseudo-classes, like :hover and :focus.

The entire component in this button example is being exposed for styling, but if your web component consists of multiple HTML elements, you can expose only selected parts of the component to this sort of styling — hence the name ::part. This stops users of the component from styling any arbitrary element inside the shadow tree. It is up the the component author to expose the parts of the component they explicitly want to. Other parts of the component can be kept visually uniform or make use of custom properties for a more minimal customizability. 

So, how do we set this up for our own components? Let’s look at using ::part to make certain elements of a web component eligible for styling. All we do is add a part attribute on the element we want to be exposed.

<div part="box">...</div>   <button>Click me</button>

In this example the div is customizable with the full gamut of CSS — any CSS property can be changed. The button, however, is locked down — it cannot be visually changed by anybody except the component author.

CodePen Embed Fallback

And the same way an HTML element can have multiple classes, an element can have multiple part names: 

<div part="box thing">...</div>

So that’s what we get with ::part: by exposing “parts” of an element we can provide some flexibility in how a web component is used while exercising protection in other areas. Whether it’s your design system, a component library, or what have you, the fact that CSS Shadow Parts are becoming mainstream gives us yet another exciting tool to work with.

The post Styling in the Shadow DOM With CSS Shadow Parts  appeared first on CSS-Tricks.

When debugging, your attitude matters

Css Tricks - Sun, 04/12/2020 - 2:32am

Julia Evans:

I was debugging some CSS last week, and I think that post is missing something important: your attitude.

Now – I’m not a very good CSS developer yet. I’ve never written CSS professionally and I don’t understand a lot of basic CSS concepts (I think I finally understood for the first time recently how position: absolute works). And last week I was working on the most complicated CSS project I’d ever attempted.

While I was debugging my CSS, I noticed myself doing some bad things that I normally would not! I was:

• making random changes to my code in the hopes that it would work
• googling a lot of things and trying them without understanding what they did
• if something broke, reverting my changes and starting again

This strategy was exactly as effective as you might imagine (not very effective!), and it was because of my attitude about CSS! I had this unusual-for-me belief that CSS was Too Hard and impossible for me to understand. So let’s talk about that attitude a bit!

It’s super unfortunate this specific bug (a difference in z-index behavior between Chrome and Firefox) is what was giving Julia a hard time. Those kind of cross-browser differences are fewer and farther between these days, thankfully. I’m certainly sympathetic to CSS being super tricky sometimes, and it can be that way without dealing with an actual browser bug.

But I like the sentiment: if you go into a tricky problem with a positive I can do this attitude starting with the basics, you can do it.

I think literally everything in life is easier and better with a better attitude. Food tastes better! Reminds me of my favorite mis-remembered quote from good ol’ Uncle Iroh from The Last Airbender:

The best tasting tea is the tea you drink when you are in a good mood.

Direct Link to ArticlePermalink

The post When debugging, your attitude matters appeared first on CSS-Tricks.

Thinking in Behaviors, Not Screen Sizes

Css Tricks - Fri, 04/10/2020 - 12:01pm

Chase McCoy wrote a nifty post about the “gap problem” when making a grid of items. His argument might be summarized like this: how should we space elements with margins in CSS? He notes that the gap property isn’t quite ready for prime time when it comes to using it with flexbox, like this:

.grid { display: flex; gap: 10px; }

Right now, using gap with flexbox is only supported in Firefox and I’ve already caught myself forgetting about that in a few projects. So watch out for that.

Anyway, the part about Chase’s blog post that I love is where he mentions Andy Bell’s technique for creating a responsive layout with no media queries, like this:

.grid { display: grid; grid-gap: 10px; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }

This CSS is doing the following:

  • Make a grid with a 10px gap between each column and row.
  • Each column should have a minimum width (150px).
  • Each column should also be equal width (1fr).
  • The grid should auto-fill as many columns that can fit.

The nifty thing about all this is that our grid is now effectively responsive because of minmax — if you resize the browser, then the grid will snap down into fewer columns, just like this:

No media queries at all! Although sure, there’s a few other ways that you could get this to work but I think this is neat not just because we’re avoiding media queries — instead, it’s because it teaches us to think in a new way when designing and building components.

Chase continues:

With this technique, instead of using breakpoints to specify the screen size where your items should stack, you specify the minimum size an element should be before it stacks. I like this because it encourages developers to think about responsive design in terms of behaviors instead of screen sizes.

“Behaviors instead of screen sizes” is such a great way to think about component design! A lot of the problems I’ve encountered when making components for a design system is when I’ve been thinking about screen sizes — mobile, tablet, desktop, etc. — and trying to make those components fit within those constraints.

Thinking in behaviors is always more effective because there are so many things that can impact a component beyond what screen or device width we’re working with. Perhaps we want that component to fit inside another component. Or we want to align some helper text to the side of it for comparison.

Either way, thinking about behaviors instead of screen sizes isn’t really going to be fully possible until we have container queries, as Chris writes:

Container queries are always on the top of the list of requested improvements to CSS. The general sentiment is that if we had container queries, we wouldn’t write as many global media queries based on page size. That’s because we’re actually trying to control a more scoped container, and the only reason we use media queries for that now is because it’s the best tool we have in CSS. I absolutely believe that.

The post Thinking in Behaviors, Not Screen Sizes appeared first on CSS-Tricks.

Tips for Writing Animation Code Efficiently

Css Tricks - Fri, 04/10/2020 - 4:46am

I’ve been coding web animations and helping others do the same for years now. However, I have yet to see a concise list of tips focused on how to efficiently build animations, so here you go!

I will be using the GreenSock Animation Platform (GSAP). It provides a simple, readable API and solves cross-browser inconsistencies so that you can focus on animating. The code and concepts should be understandable even if you’ve never used GSAP. If you’d like to familiarize yourself with the basics of GSAP first so that you can get the most out of this article, the best place to begin is GSAP’s getting started page (includes a video). 

Tip #1: Use an animation library

Some developers think that using an animation library is wasteful because they can just use native browser technologies like CSS transitions, CSS animations or the Web Animations API (WAAPI) to accomplish the same thing without loading a library. In some cases, that’s true. However, here are a few other factors to consider:

  • Browser bugs, inconsistencies, and compatibility: An animation library, like GSAP, solves these for you and is universally compatible. You can even use motion paths in IE9! There are many problematic areas when it comes to cross-browser issues, including handling transform-origin on SVG elements, path stroke measurements, 3D origins in Safari, and many more that we don’t have the space to list.
  • Animation workflow: Building even moderately complex animations is much faster and more fun with a tool like GSAP. You can modularize animations, nest them as deeply as you want, and have their timing adjusted automatically. This makes it so much easier to experiment. Trust me: once you try building an animation sequence in CSS and then in GSAP, you’ll see what I mean. Night and day! Future edits are faster too.
  • Animate beyond the DOM: Canvas, WebGL, generic objects, and complex strings can’t be animated with native technologies. Using one consistent tool for all your animations is much cleaner. 
  • Runtime control: Using a good animation library can enable you to pause, resume, reverse, seek through, or even gradually change the speed of an entire animation sequence. You can control each transform component independently (rotation, scale, x, y, skew, etc.). You can also retrieve those values at any time as well. JavaScript animations give you ultimate flexibility.
  • Easing options (bounce, elastic, etc.): CSS only gives you two control points for eases. GSAP’s CustomEase lets you literally create any ease you can imagine. 
  • Lag smoothing: GSAP can prioritize absolute timing or adjust things on the fly to avoid jumps if the CPU gets bogged down.
  • Advanced capabilities: Using GSAP, it’s easy to morph SVGs, add physics/inertia, edit motion paths directly in the browser, use position-aware staggers, and more.

Most of the top animators in the industry use a tool like GSAP because they’ve learned these same things over the years. Once you get beyond very basic animations, a JavaScript library will make your life much, much easier and open up entirely new possibilities. 

Tip #2: Use timelines

A good animation library will provide some way of creating individual animations (called tweens) and a way to sequence animations in a timeline. Think of a timeline like a container for your tweens where you position them in relation to one another. 

const tl = gsap.timeline();".box", { duration: 1, x: 100 }) .to(".box", { duration: 1, backgroundColor: "#f38630" }, "+=0.5") .to(".box", { duration: 1, x: 0, rotation: -360 }, "+=0.5")

By default in GSAP, tweens added to a timeline will wait for the previous tweens to complete before running. The +=0.5 adds an additional offset or delay of a half-second as well, so the second tween will start 0.5 seconds after the first tween finishes no matter how long the first tween’s duration is.

To increase the amount of time between the tween to 1 second, all you need to do is change the +=0.5 to +=1! Super easy. With this approach, you can iterate on your animations quickly without worrying about doing the math to combine previous durations.

Tip #3: Use relative values

By “relative values” I mean three things:

  1. Animate values relative to their current value. GSAP recognizes += and -= prefixes for this. So x: "+=200" will add 200 units (usually pixels) to the current x. And x: "-=200" will subtract 200 from the current value. This is also useful in GSAP’s position parameter when positioning tweens relative to one another.
  2. Use relative units (like vw, vh and, in some cases, %) when values need to be responsive to viewport size changes.
  3. Use methods like .to() and .from() (instead of .fromTo()) whenever possible so that the start or end values are dynamically populated from their current values. That way, you don’t need to declare start and end values in every tween. Yay, less typing! For example, if you had a bunch of differently-colored elements, you could animate them all to black like".class", {backgroundColor: "black" }).
Tip #4: Use keyframes

If you find yourself animating the same target over and over in a row, that’s a perfect time to use keyframes! You do so like this:".box", { keyframes: [ { duration: 1, x: 100 }, { duration: 1, backgroundColor: "#f38630", delay: 0.5 }, { duration: 1, x: 0, rotation: -360, delay: 0.5 } ]});

No timeline necessary! To space out the tweens we just use the delay property in each keyframe. (It can be negative to create overlaps.)

Tip #5: Use smart defaults

GSAP has default values for properties like ease ("power1.out") and duration (0.5 seconds). So, the following is a valid tween that will animate for half a second.".box", { color: "black" })

To change GSAP’s global defaults, use gsap.defaults():

// Use a linear ease and a duration of 1 instead gsap.defaults({ ease: "none", duration: 1 });

This can be handy, but it’s more common to set defaults for a particular timeline so that it affects only its children. For example, we can avoid typing duration: 1 for each of the sub-tweens by setting a default on the parent timeline:

const tl = gsap.timeline({ defaults: { duration: 1 } });".box", { x: 100 }) .to(".box", { backgroundColor: "#f38630" }, "+=0.5") .to(".box", { x: 0, rotation: -360 }, "+=0.5") Tip #6: Animate multiple elements at once

We mentioned this briefly in the third tip, but it deserves its own tip.

If you have multiple elements that share the same class of .box, the code above will animate all of the elements at the same time!

You can also select multiple elements with different selectors by using a more complex selector string:".box, .circle", { ... });

Or you can pass an array of variable references as long as the elements are of the same type (selector string, variable reference, generic object, etc.):

var box = document.querySelector(".box"); var circle = document.querySelector(".circle"); // some time later…[box, circle], { ... }); Tip #7: Use function-based values, staggers, and/or loops Function-based values

Use a function instead of a number/string for almost any property, and GSAP will call that function once for each target when it first renders the tween. Plus, it’ll use whatever gets returned by the function as the property value! This can be really handy for creating a bunch of different animations using a single tween and for adding variance.

GSAP will pass the following parameters into the function:

  1. The index
  2. The specific element being affected
  3. An array of all of the elements affected by the tween

For example, you could set the movement direction based on the index:

Or you could choose items from an array:


Make your animations look more dynamic and interesting by offsetting the start times with a stagger. For simple staggered offsets in a single tween, just use stagger: 0.2 to add 0.2 seconds between the start time of each animation.

You can also pass in an object to get more complex stagger effects, including ones that emanate outward from the center of a grid or randomize the timings:

CodePen Embed Fallback

For more information about GSAP’s staggers, check out the stagger documentation.


It can be helpful to loop through a list of elements to create or apply animations, particularly when they are based on some event, like a user’s interaction (which I’ll discuss later on). 

To loop through a list of items, it’s easiest to use .forEach(). But since this isn’t supported on elements selected with .querySelectorAll() in IE, you can use GSAP’s utils.toArray() function instead.

In the example below, we are looping through each container to add animations to its children that are scoped to that container.

Tip #8: Modularize your animations

Modularization is one of the key principles of programming. It allows you to build small, easy-to-understand chunks that you can combine into larger creations while still keeping things clean, reusable, and easy to modify. It also lets you to use parameters and function scope, increasing the re-usability of your code.


Use functions to return tweens or timelines and then insert those into a master timeline:

function doAnimation() { // do something, like calculations, maybe using arguments passed into the function // return a tween, maybe using the calculations above return".myElem", { duration: 1, color: "red"}); } tl.add( doAnimation() );

Nesting timelines can truly revolutionize the way you animate. It lets you sequence really complex animations with ease while keeping your code modular and readable.

function doAnimation() { const tl = gsap.timeline();;; // many animations as you’d like! // When you’re all done, return the timeline return tl; } const master = gsap.timeline(); master.add( doAnimation() ); master.add( doAnotherAnimation() ); // Add even more timelines!

Here’s a real-world use case modified from Carl Schooff’s “Writing Smarter Animation Code” post.

Here’s a more complex demo showing the same technique using a Star Wars theme by Craig Roblewsky:

CodePen Embed Fallback

Wrapping your animation-building routines inside functions also makes recreating animations (say, on resize) a breeze!

var tl; // keep an accessible reference to our timeline function buildAnimation() { var time = tl ? tl.time() : 0; // save the old time if it exists // kill off the old timeline if it exists if (tl) { tl.kill(); } // create a new timeline tl = gsap.timeline(); .to(...); // do your animation tl.time(time); // set the playhead to match where it was } buildAnimation(); //kick things off window.addEventListener("resize", buildAnimation); // handle resize

If you find yourself repeating the same code and switching out one variable for another that’s usually a sign you should make a general function or use a loop instead in order to keep your code DRY (Don’t Repeat Yourself).


With effects, you can turn a custom animation into a named effect that can be called anytime with new targets and configurations. This is especially helpful when you have standards for your animations or if you are going to be calling the same animation from different contexts.

Here’s a super-simple “fade” effect to show the concept:

// register the effect with GSAP: gsap.registerEffect({ name: "fade", defaults: {duration: 2}, //defaults get applied to the "config" object passed to the effect below effect: (targets, config) => { return, {duration: config.duration, opacity:0}); } }); // now we can use it like this: gsap.effects.fade(".box"); // Or override the defaults: gsap.effects.fade(".box", {duration: 1}); Tip #9: Use control methods

GSAP provides many methods to control the state of a tween or timeline. They include .play(), .pause(), .reverse(), .progress(), .seek(), .restart(), .timeScale(), and several others. 

Using control methods can make transitions between animations more fluid (such as being able to reverse part way through) and more performant (by reusing the same tween/timeline instead of creating new instances each time). And by giving you finer control over the state of the animation, it can help with debugging as well.

Here’s a simple example:

One amazing use case is tweening the timeScale of a timeline!

CodePen Embed Fallback Use case: interaction events that trigger animations

Inside of event listeners for user interaction events, we can use control methods to have fine control over our animation’s play state.

In the example below, we are creating a timeline for each element (so that it doesn’t fire the same animation on all instances), attaching a reference for that timeline to the element itself, and then playing the relevant timeline when the element is hovered, reversing it when the mouse leaves.

CodePen Embed Fallback Use case: Animating between multiple states of a timeline

You may want a set of animations to affect the same properties of the same elements, but only in certain sequences (e.g. active/inactive states, each with mouseover/mouseout states). It may get tricky to manage. We can simplify it by using states of a timeline and control events. 

Use case: Animating based on the scroll position

We can easily fire animations based on the scroll position by using control methods. For example, this demo plays a full animation once a scroll position has been reached:

You can also attach the progress of an animation to the scroll position for more fancy scroll effects!

But if you’re going to do this, it’s best to throttle the scroll listener for performance reasons:

Hot tip: GreenSock is working on a plugin to make scroll-based animations even easier! You’re in for quite a treat. Keep your eyes peeled for news.

Bonus tip: Use GSAP’s plugins, utility methods, and helper functions

GSAP plugins add extra capabilities to GSAP’s core. Some plugins make it easier to work with rendering libraries, like PixiJS or EaselJS, while other plugins add superpowers like morphing SVG, drag and drop functionality, etc. This keeps the GSAP core relatively small and lets you add features when you need them.


MorphSVG morphs between any two SVG shapes, no matter the number of points, and gives you fine control over how the shapes are morphed.

CodePen Embed Fallback

DrawSVG progressively reveals (or hides) the stroke of an SVG element, making it look like it’s being drawn. It works around various browser bugs that affect typical stroke-dashoffset animations.

MotionPath animates anything (SVG, DOM, canvas, generic objects, whatever) along a motion path in any browser. You can even edit the path in-browser using MotionPathHelper!

CodePen Embed Fallback

GSDevTools gives you a visual UI for interacting with and debugging GSAP animations, complete with advanced playback controls, keyboard shortcuts, global synchronization and more.

CodePen Embed Fallback

Draggable provides a surprisingly simple way to make virtually any DOM element draggable, spinnable, tossable, or even flick-scrollable using mouse or touch events. Draggable integrates beautifully (and optionally) with InertiaPlugin so the user can flick and have the motion decelerate smoothly based on momentum.

CodePen Embed Fallback

CustomEase (along with CustomBounce and CustomWiggle) add to GSAP’s already extensive easing capabilities by enabling you to register any ease that you’d like.

SplitText is an easy to use JavaScript utility that allows you to split HTML text into characters, words and lines. It’s easy to use, extremely flexible, works all the way back to IE9, and handles special characters for you.

CodePen Embed Fallback

ScrambleText scrambles the text in a DOM element with randomized characters, refreshing new randomized characters at regular intervals, while gradually revealing your new text (or the original) over the course of the tween. Visually, it looks like a computer decoding a string of text.

CodePen Embed Fallback

Physics2D lets you tween the position of elements based on velocity and acceleration as opposed to going to specific values. PhysicsProps is similar but works with any property, not just 2D coordinates.

CodePen Embed Fallback Utility methods

GSAP has built-in utility methods that can make some common tasks easier. Most are focused on manipulating values in a particular way, which can be especially helpful when generating or modifying animation values. The ones that I use most often are .wrap(), .random, .interpolate(), .distribute(), .pipe(), and .unitize(), but there are many others you might find helpful.

Helper functions

In a similar light, but not built into GSAP’s core, are some helper functions GreenSock has created over the years to deal with specific use cases. These functions make it easy to FLIP your animations, return a random number based on an ease curve, blend two ease curves, and much more. I highly recommend checking them out!


You’ve made it to the end! Hopefully, you’ve learned a thing or two along the way and this article will continue to be a resource for you in the years to come.

As always, if you have any questions about GSAP, feel free to drop by the GreenSock forums. They’re incredibly helpful and welcoming! As an employee of GreenSock, that’s where I hang out often; I love helping people with animation-related challenges!

The post Tips for Writing Animation Code Efficiently appeared first on CSS-Tricks.

Playing board games online

QuirksBlog - Thu, 04/09/2020 - 4:56am

One of the things that keeps me fairly upbeat these days is playing board games and D&D with my friends online. Since others might want to do the same, I thought I’d jot down some notes on how I do it.

I briefly tried Tabletopia but didn"t like it. I understand why they built the interface as they did, but I found it very hard and very confusing to use, and it took us about 45 minutes to even start understanding the system. Granted, we picked Teotihuacan for our test game, which may not have been the best of choices.

So I continued using my homebrew system, and it works great so far.

Technical set-up

I use Whereby (the former, a WebRTC service that works absolutely GREAT. I totally recommend it to everyone for your online communication needs. The greatest thing about it is that you just go to a URL, ask the people you want to communicate with to go to the same URL, give permissions, enter the room, and start talking. No sign-ups or logins or whatever.

I have a pro account (or whatever it’s called) that allows 12 simultaneous connections to my room. You can also just grab a room name, go there, and start communicating, but these free rooms have a maximum of four simultaneous connections. So I advise you to take a paid account; you will most likely need more than four connections for playing board games online.

Besides, fuck free. The free Internet is slowly coming to an end and you should pay for services you like and use, or they won’t survive (or sell your data; see also Zoom).

Whereby works on modern Chromium-based browsers, and also in Firefox (though I haven’t tried Firefox on Android yet). It does not work in Safari iOS, but an app is available that works as simply as the web client.

Then figure out how many devices you own that you can use. On the whole, I send out three streams: my 'social' stream (my face, basically) from my laptop, the main board stream from my iPad, and a secondary board stream from a Samsung S6 I happened to have lying around. I occasionally use my real Samsung phone (an S7) as a third cam, for instance to make sure that everyone has the same bits and pieces on mirrored player boards.

Plug in all devices you use, and make sure any phones are on at least 25% charge or so before starting. My Samsung phones, especially, tend to spend a lot of juice on keeping the streams running, and even though plugged in all the time they might end up with less battery charge after a gaming session.

Mute Whereby on all devices except for your social stream. One very annoying thing I noticed is that, both on the iPad and on the Samsungs, it is impossible to turn off the sound completely. Therefore you need to do two things:

  1. Disable sound input by clicking on the microphone icon in the bottom bar.
  2. Disable sound output of all connections by clicking the Mute option in the menu you get after clicking on the three bullets icon in the upper right corner. You must repeat this for every connection.

You can only mute the output once everyone else has joined the stream. If someone drops out and re-joins you must mute them again. This is annoying; but it’s caused by idiotic device vendors not allowing you to mute the sound completely by using the provided hardware buttons — don’t ask me why they took this stupid step.

Now ask the others to join you. If possible and necessary they can also add their own cameras, for instance to show their player boards.

Picking the game

With the technical set-up out of the way, you should pick your game. I found that there are two absolute necessities here:

  1. All players must own the game, so that they can copy the moves of the other players.
  2. The game should have little to no hidden information.

So you might need to buy the same game as your friends. If you are in the Amsterdam area, please support your friendly local game store Friends & Foes instead of the big online retailers. Friends & Foes deliver in Amsterdam (I just ordered Tzolkin from them).

The two games I played most often so far are Azul and Alchemists. I am currently gearing up to try Madeira, Istanbul and Tzolkin; they should work as well.

Azul, Madeira, and Tzolkin have no hidden information at all. They have a variable set-up (and in case of Azul this is repeated each round), but that should be no problem.

Appoint one player or group of players as the Master; the other ones have Copies. The Master players draw all the randoms and show them to the other players, who copy them on to their Copy boards. Having the Master set provide all random draws is very important, since usually quite a bit of design thought went in to deciding exactly how many of one type of card or tile are available. These distributions should not be disturbed!


With Azul it is very important that all players set up copies of all other players’ personal boards. Part of the game is figuring out which tiles other players are likely to want, and for that all players need an overview of who has which tiles in which position.

Wnen I stream Azul, the main camera is on the central part with the available tiles. Other players can copy that if they like, but it’s not really necessary if the stream is clear enough. My secondary camera is on my own player board, so that everyone can see what I’m doing.

During the game all players clearly state their moves; for instance “I take the two blues with the star, and I put them on my three row.” I take the tiles from the central part, and the other players see me doing that, so they can correct me. They don’t see my copy of their playing baords, but that has never been a problem yet, as long as everyone gives clear instructions.

After a round has ended but before scoring I start up my tertiary camera to stream my copies of everyone else’s player boards, just to make sure no mistakes were made. Then I score each player’s board while showing it on camera. We repeat our final scores orally, just to be sure, and then the Master player sets up for the next round by drawing random tiles from my Master bag.


Alchemists does have a little bit of hidden information: random ingredients drawn, and random helper cards we always call Friendly Friends. (I forget their official name.) The Master player draws these cards for me and shows them on their camera without looking. I take the corresponding cards from my own copy of the game. This works fine, and the distribution of ingredients and Friendly Friends remains intact.

Alchemists really only needs a Master main board stream and social streams; there is no reason to add more cameras.

Although Alchemists’ board is pretty big, it doesn’t contain all that much information, which is good for online gaming. I just need to see which artifacts and ingredients are drawn (and copy them to my own board), and where players place their action cubes (and copy them as well). If I can’t see it clearly I just ask, and that works fine.

Part of Alchemists becomes much easier. In real life every player needs a beautifully-designed but sometimes cumbersone player contraption to both visualise their research and hide it from the other players.

Credit: Karel_danek

Online, it’s not necessary, and I find that my research and thinking flows much easier. Other players cannot see my board, and that gives me a lot more space to work with.

Madeira, Istanbul and Tzolkin

I haven’t played Madeira, Istanbul and Tzolkin yet, but they do not contain hidden information; just start-of-game randoms, plus the random buildings that occasionally appear in Tzolkin and the bonus cards in Istanbul. I do not think these will cause a problem.

The bigger problem might be that their boards are much more involved, and there’s a lot of game state to track. I might need to use two cameras to stream them accurately; I’m not sure yet. We’ll figure that out once we do the first session.

CSS Foldable Display Polyfill

Css Tricks - Thu, 04/09/2020 - 4:51am

Foldable phones are starting to be a thing. Early days, for sure, but some are already shipping, and they definitely have web browsers on them. Stands to reason that, as web designers, we are going to want to know where that fold is so we can design screens that fit onto the top half and bottom half… or left half and right half¹.

Looks like that’s going to make its way to us in the form of env() constants, just like all that notch stuff.

The code block in the polyfill repo is:

@media (spanning: single-fold-vertical) { body { flex-direction: row; } .map { flex: 1 1 env(fold-left) } .locations-list { flex: 1; } }

I would also think it could be…

@media (spanning: single-fold-vertical) { .page-wrap { display: grid; grid-template-columns: env(fold-left) 1fr; } }

Interesting how there is no fold-right, isn’t it? And aren’t we trying to stay away from directional terms like that and use logical properties? Why not fold-inline-start?

  1. It’ll be interesting to see how that sentence ages. Just watch the first really popular foldable phone will have three segments.

The post CSS Foldable Display Polyfill appeared first on CSS-Tricks.

Create Diagonal Layouts Like it’s 2020

Css Tricks - Thu, 04/09/2020 - 4:44am

Nils Binder covers the ways:

1. Use an SVG in the form of a triangle. This technique is nicely described by Erik Kennedy on CSS-Tricks.

2. Hide part of your section using clip-path. Read Diagonal Containers in CSS by Sebastiano Guerriero or Sloped edges with consistent angle in CSS by Kilian Valkhof.

3. Using CSS Transforms

I would normally be a #2 kinda guy — slice off the top and bottom a bit, make sure there is ample padding, and call it a day. But Nils almost has me convinced this fancy math is better.

Here’s a kinda dumb clip-path way:

CodePen Embed Fallback

And Nils incredibly fancy playground:

CodePen Embed Fallback

Direct Link to ArticlePermalink

The post Create Diagonal Layouts Like it’s 2020 appeared first on CSS-Tricks.

Syndicate content
©2003 - Present Akamai Design & Development.