Through the pipeline: An exploration of front-end bundlers

I really like the kind of tech writing where a fellow developer lays out some specific needs, tries out different tech to fulfill those needs, and documents how it went for them.

That’s exactly what Andrew Walpole did here. He wanted to try out bundlers in the context of WordPress themes and needing a handful of specific files built. Two JavaScript and two Sass files, which can import things from npm, and need to be minified with sourcemaps and all that. Essentially the same crap we were doing when I wrote Grunt for People Who Think Things Like Grunt are Weird and Hard. The process hasn’t gotten any easier, but at least it’s gotten faster.

The winner for Andrew: esbuild through Estrella.


The post Through the pipeline: An exploration of front-end bundlers appeared first on CSS-Tricks.

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

CSS-Tricks

, , , ,

Weekly Platform News: Focus Rings, Donut Scope, Ditching em Units, and Global Privacy Control

In this week’s news, Chrome tackles focus rings, we learn how to get “donut” scope, Global Privacy Control gets big-name adoption, it’s time to ditch pixels in media queries, and a snippet that prevents annoying form validation styling.

Chrome will stop displaying focus rings when clicking buttons

Chrome, Edge, and other Chromium-based browsers display a focus indicator (a.k.a. focus ring) when the user clicks or taps a (styled) button. For comparison, Safari and Firefox don’t display a focus indicator when a button is clicked or tapped, but do only when the button is focused via the keyboard.

The focus ring will stay on the button until the user clicks somewhere else on the page.

Some developers find this behavior annoying and are using various workarounds to prevent the focus ring from appearing when a button is clicked or tapped. For example, the popular what-input library continuously tracks the user’s input method (mouse, keyboard or touch), allowing the page to suppress focus rings specifically for mouse clicks.

[data-whatintent="mouse"] :focus {   outline: none; }

A more recent workaround was enabled by the addition of the CSS :focus-visible pseudo-class to Chromium a few months ago. In the current version of Chrome, clicking or tapping a button invokes the button’s :focus state but not its :focus-visible state. that way, the page can use a suitable selector to suppress focus rings for clicks and taps without affecting keyboard users.

:focus:not(:focus-visible) {   outline: none; }

Fortunately, these workarounds will soon become unnecessary. Chromium’s user agent stylesheet recently switched from :focus to :focus-visible, and as a result of this change, button clicks and taps no longer invoke focus rings. The new behavior will first ship in Chrome 90 next month.

The enhanced CSS :not() selector enables “donut scope”

I recently wrote about the A:not(B *) selector pattern that allows authors to select all A elements that are not descendants of a B element. This pattern can be expanded to A B:not(C *) to create a “donut scope.”

For example, the selector article p:not(blockquote *) matches all <p> elements that are descendants of an <article> element but not descendants of a <blockquote> element. In other words, it selects all paragraphs in an article except the ones that are in a block quotation.

The donut shape that gives this scope its name

The New York Times now honors Global Privacy Control

Announced last October, Global Privacy Control (GPC) is a new privacy signal for the web that is designed to be legally enforceable. Essentially, it’s an HTTP Sec-GPC: 1 request header that tells websites that the user does not want their personal data to be shared or sold.

The DuckDuckGo Privacy Essentials extension enables GPC by default in the browser

The New York Times has become the first major publisher to honor GPC. A number of other publishers, including The Washington Post and Automattic (WordPress.com), have committed to honoring it “this coming quarter.”

From NYT’s privacy page:

Does The Times support the Global Privacy Control (GPC)?

Yes. When we detect a GPC signal from a reader’s browser where GDPR, CCPA or a similar privacy law applies, we stop sharing the reader’s personal data online with other companies (except with our service providers).

The case for em-based media queries

Some browsers allow the user to increase the default font size in the browser’s settings. Unfortunately, this user preference has no effect on websites that set their font sizes in pixels (e.g., font-size: 20px). In part for this reason, some websites (including CSS-Tricks) instead use font-relative units, such as em and rem, which do respond to the user’s font size preference.

Ideally, a website that uses font-relative units for font-size should also use em values in media queries (e.g., min-width: 80em instead of min-width: 1280px). Otherwise, the site’s responsive layout may not always work as expected.

For example, CSS-Tricks switches from a two-column to a one-column layout on narrow viewports to prevent the article’s lines from becoming too short. However, if the user increases the default font size in the browser to 24px, the text on the page will become larger (as it should) but the page layout will not change, resulting in extremely short lines at certain viewport widths.

If you’d like to try out em-based media queries on your website, there is a PostCSS plugin that automatically converts min-width, max-width, min-height, and max-height media queries from px to em.

(via Nick Gard)

A new push to bring CSS :user-invalid to browsers

In 2017, Peter-Paul Koch published a series of three articles about native form validation on the web. Part 1 points out the problems with the widely supported CSS :invalid pseudo-class:

  • The validity of <input> elements is re-evaluated on every key stroke, so a form field can become :invalid while the user is still typing the value.
  • If a form field is required (<input required>), it will become :invalid immediately on page load.

Both of these behaviors are potentially confusing (and annoying), so websites cannot rely solely on the :invalid selector to indicate that a value entered by the user is not valid. However, there is the option to combine :invalid with :not(:focus) and even :not(:placeholder-shown) to ensure that the page’s “invalid” styles do not apply to the <input> until the user has finished entering the value and moved focus to another element.

The CSS Selectors module defines a :user-invalid pseudo-class that avoids the problems of :invalid by only matching an <input> “after the user has significantly interacted with it.”

Firefox already supports this functionality via the :-moz-ui-invalid pseudo-class (see it in action). Mozilla now intends to un-prefix this pseudo-class and ship it under the standard :user-invalid name. There are still no signals from other browser vendors, but the Chromium and WebKit bugs for this feature have been filed.


The post Weekly Platform News: Focus Rings, Donut Scope, Ditching em Units, and Global Privacy Control appeared first on CSS-Tricks.

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

CSS-Tricks

, , , , , , , , , , ,
[Top]

How to Develop and Test a Mobile-First Design in 2021

The internet has connected 4.66 billion people with each other as of October 2020. A total of 59% of the world’s total population. Amazingly, this is not even the surprising part. The stat to look out for is mobile users and their rise in the internet world. Out of 4.66 billion people connected to the internet, 4.28 billion are mobile internet users. A number that reminds us of how vital mobile users are and why we need to keep them on priority. As surprising as these numbers are, everyone saw this coming with the sudden rise in mobile users and mobile sales all around the world. As a web developer, we have been prepared in shifting our design methodologies and development tactics according to mobile users for some time now. One such design paradigm is the mobile-first design which is a witness to our commitment to mobile internet users all around the world. A design strategy that starts with mobile users and works around their interest. This post takes the mobile-first design into the centre and explores the complexities of mobile-first design development and testing and how it affects our business in a positive way.

What is a Mobile-First Design?

Mobile-first design is the process of planning and developing a website keeping in mind the mobile-users first. This methodology of development changed from desktop-first, which has always been the way, reacting to the surge in mobile internet users around the world. Mobile-first design is a part of progressive advancement method in which we progress towards more advance design slowly.

Progressive advancement starts from a basic design fulfilling the requirements of the mobile device. This basic design consists of minimal elements on the webpage eliminating everything that a mobile user is not interested in. From here, we take another step and add a few more elements increasing the complexity of our design while sticking to the modern web design techniques and mobile-friendliness. This goes on until we are satisfied and have implemented all the necessary modules in our application.

Credit: Smartz

Mobile-first design is a practice of starting the development with respect to the mobile user or a mobile device first. Contributing 52% of the internet traffic today, a mobile-first website can help us lift the overall engagement and make us visible on the internet (on Google). With the start of mobile-first indexing, Google has also twisted their algorithm to show that mobile users are the priority today. Mobile-first design is not a complex task but a series of small development changes that can help your website render perfectly on the mobile with a happy user. To develop a mobile-first design website, we need to absorb some habits and remember a few tips to test the application efficiently.

How To Develop a Mobile-First Design

The journey of developing a mobile-first design can be roughly divided into the following stages:

Wireframing

Wireframe’s importance in a mobile-first design is similar to a map when we construct a new building. Wireframe provides an architectural way of how things are going to be. With wireframes in hand, not only technical teams, everyone related to the project can understand the high-level view of the application or in user’s terms: how will my application look?

While clients and analysts can tick mark their checklist of requirements, developers can understand how the elements will be laid down on to the application.

Credit: Balsamiq

A research paper published by KPMG, determining “Unsuccessful Information Technology Projects” revealed poor project planning as one of the reasons for project failure. The other good reason to use wireframes is that they increase the efficiency of the developers because of the clearer objectives. A similar result can be seen with TDD and BDD approach in testing.

Use Responsiveness and A Responsive Framework

A responsive framework is a crucial step in mobile-first design development. A responsive website adjusts itself with the environment it is rendered on such as screen size, platform or orientation. Mobile devices do not have a fixed size in the market. An overwhelming list on Screensiz will make you believe that applying media queries and meta tag will not work for all the devices and a generic approach is the only solution. Responsiveness is not only adjusting to the screen of the device, but also to the user’s experience. For example, images are an important part of a web page. Even though a user may skim the content, they will glance at an image at least once. It needs to be perfect! But while shrinking the aspect ratio of the webpage, we unknowingly focus out the image subject from the image. In the following image, the comparison speaks this point as the family becomes out of focus on a smaller screen device:

Simply resizing is a risky business as an image may lose its importance altogether. A good responsive website takes care of this aspect and render a different lower quality but cropped image on a smaller screen as below:

Or if I just show the mobile image, it has been cropped to as follows:

Since the focus is not the tree but the family, we need the users to see our subject with clarity. This is a responsive nature according to the user experience which is a part of a responsive website.

A responsive framework keeps the responsive needs in mind and has built-in capabilities to enhance the responsiveness of the website. With a responsive framework, the developers need not take care of every little thing and can focus on major issues such as the image resizing shown above. Responsive frameworks are more successful and popular in a mobile-first design strategy.

Follow the thumb rule

Before placing our content on the web page, we have to decide the location of each element with respect to the interaction habits of mobile users. A simple reason for this thought is the generic way most people use their phone; with one hand.

75% of people use their thumb to operate the mobile device which when painted as a red-green zone looks as follows:

Credit: Biracial Booty

The green zone in the above image shows the easily accessible area on a mobile screen. Our most important elements, such as CTAs, should reside in the green zone for a mobile-user to access easily. Remember that the user is not using a mouse to operate a mobile device. Reaching the red zone will take efforts and repeated actions of which, voluntarily or involuntarily, a user always notices.

The below-given image shows the efforts a user would have to take with a very small design change (which in my way, is not a mobile-first design):

Credit: Ranjith Manoharan

This small change can lead to increased user engagement demanding no efforts from our user.

Untangle Your Mobile-First Content and Design

Designing a web page desktop-style demands no extra attention towards the content. The screen is big and can accommodate whatever content you wish to add. The same attitude does not work in a mobile-first way. When the screen is smaller and we have only less than 3 seconds to impress a user, the content needs to be concise and to the point. A good solution to replace a lot of content from your screen is to use images, a hierarchical method of design or through a better user interface.

The content thoughts are to be replicated when we work with elements in a mobile-first design for the same reason of lesser screen space. Providing a congested screen with too many elements spread throughout confuses a user and slides him away from the CTA conversion goal. This is also called a minimalistic approach (or minimalism) in web design. A minimalistic approach distributes too few elements on to the screen leaving out a considerable white space for the user. The following image shows the minimalistic design:

Credit: Johnyvino

But a mobile design is different from a mobile-first design. Eventually, we would have to extend this web page for the desktop users too. Minimalism on the desktop is also a good approach given that the font-size and hero images are equally proportionate.

Decluttering our design and content reminds us why it is important to move from basic to advanced and not vice-versa (also called graceful degradation). Had it been the desktop design at the start, the team has to conduct the brainstorm sessions first to fill a large screen and then removing them one by one for the mobile device. By that time, management becomes too complex, hard to confine our elements and takes too much time and efforts. Therefore, start basic with a minimal design and then move forward which is the initial step in a mobile-first design strategy.

Prioritize UI and UX

A mobile-first design needs to revolve around mobile users to increase engagements and conversions on our web application. While animations and transitions are as fancy to look as to touch, the user experience is much more than explicit elements. Our user experience need not be too ostentatious but should engage the users without them realising our intentions. For example, elements should be extremely easy to find on a web page. A mobile user should never struggle to find the search button Conventional locations of the elements work in this case such as a navigation bar is always expected to be in the corner (left or right).

Another aspect in prioritizing the user experience is to enlarge the touch targets for comfortable interaction. Unlike a desktop with a small pointed arrow, we touch our screens with our thumbs which require a considerable large area. A mobile-first design encourages large clickable elements with white space between them to avoid unwanted clicks.

It is not a bad idea to keep these parameters intact while progressing towards the desktop side. Businesses have started to keep a better UI including large boxes to touch on desktops as well which shows their mobile-first design approach clearly. Enlarging elements also include determining the best font-size for your web page considering the smaller screen size. Font-size are easier to switch through media queries and you can follow the following chart to decide which size to go for in which scenario:

Credit: Airbus

Remember that font-type also affects the font-size visibility and readability on a mobile device. Therefore, it is better to test and find your perfect size taking the above chart for reference.

Tip: A small thing to remember in developing a mobile-first design is to avoid hover-only elements on your web page. Hovering is a great tool on desktops but mobiles do not have any support for hover. They work on touch interaction. You can keep the hover design along with the touch facility but constructing elements only with hover property is not a good idea.

CTA Placement

CTA is an important button. It helps in conversion goals and every business wants its users to click that button and increase their conversion rate. Therefore it demands special attention from the team members. The location of the CTA is the first thing that should be finalised carefully reminding yourself not to let our user work too hard.

CTAs should always be in the reach of the thumb (remember the green zone?) and on the first presented screen (above the fold) as well.

Apart from CTA placement, the message and presentation of a CTA is also an art in itself but let’s leave that for mobile-friendliness discussions.

Navigation Bar

The navigation bar on a mobile-first design needs simplification more than the desktop ones. While the desktop design has also transformed navigation bars into different unique designs, mobile-first is still enjoying the conventional hamburger menu style. People expect that today! If a user cannot find an option on the landing screen, he looks for those three horizontal lines that he knows will take him to what he is looking for.

The following image shows LambdaTest transformation of the navigation bar on two different devices:

The mobile-first approach helps us in shrinking down the available links on the navigation menu as long lists of links are not appreciated well. For those who cannot sacrifice, a nested layout seems a better choice than intimidating the user with a long list of links. In addition, it also keeps our presentation clean and encourages minimal design with decluttered content approach.

Say No To Heavy Elements

A web page’s loading speed has become a make or breaks parameter in website designing. An Unbounce survey shows that 70% of the customers are influenced by a website’s speed. Their decisions are affected by the FCP or full page load. Rendering the FCP (the first thing visible on your website) is a better choice as the user has something to get engaged in.

Google recommends a loading time of 2 seconds and under. Currently, the majority of these websites do not follow this criteria. As much as 57% of peopleleave a website that takes more than three seconds to load. The conversion rates also take a toll when the page speed is higher than expected affecting business directly. So, how can we save ourselves from this?

Using lighter elements on a web page crafted for mobile users is the first step to go for. If images exist, they probably should be in a lossless algorithm format such as JPEG and of lower size. Resizing them to a lower ratio helps too since the mobile user is rarely concerned about high-quality images apart from the product images. Using CDNs can also help in decreasing the page load time. For a WordPress website, plugins should be as minimal and light-weight as possible. Static plugins are a good start but eventually, the elements on a web page should be lighter, using asynchronous algorithms for FCP and should make fewer requests to the server.

How to test a mobile-first design?

The above points assist us during the development of a mobile-first design that starts with a basic minimal design for the mobile user and increases the complexities without hindering the user experience. But an equally important aspect of a web application is testing it. Testing an application can point out hidden bugs and functionalities that either is not liked by the people or behave inappropriately. Let’s check out how we can go ahead and polish our mobile-first website by testing it.

Use Tools

Similar to using responsive frameworks in the development which gives us in-built functionality and takes care of common code, tools do the same in testing. A mobile web testing tool not only creates an environment for the website to render as, on a mobile device, but it also provides certain features that are extremely important for a mobile-first design.

Consider one such tool LT browser I recently discovered on ProductHunt.

LT Browser is a browser made specifically for mobile web testing and responsive testing of the website. It provides 45+ screen sizes for the testers to render their website on. With such a tool, you can easily find bugs using in-built debuggers and leverage hot reload features to help you in development as well. With built-in integrations and performance reports, you can analyze the performance and share it with your teammates easily.

LT Browser showing two devices side-by-side with different orientations

Test & Debug on the go – Using LT Browser users can test and debug their websites on the go, its in-built developer’s tool really comes in handy to make a website seamless across devices.

Network Throttling: This is an amazing and unique feature offered by LT Browser utilizing which a user can check how the website performs under high and low network bandwidth.

Local Testing: Local testing allows the developer to test their website even before pushing their website online. With the local tunnel, they can view the website on any of the 45+ devices from their local system.

Performance Report: To analyse the final website performance, developers and testers can view the google lighthouse based performance report that will help them change certain website aspect in order to score more both on mobile and desktop devices.

Tools help you increase productivity and keep you efficient during the process. The choice of tools is the personal choice of the tester but they definitely should play a part in the overall testing.

Cross-Browser Testing

Cross-browser testing is the process of analyzing your website on different target browsers, operating systems and resolutions. For a mobile-friendly website to be successful, it should render as intended on a mobile screen without worrying about the platform and browser used. This can be tested through cross-browser tools like LambdaTest

As a tester and a developer, it is definitely not a good idea to take these efforts manually. There is an overwhelming number of OS, browser and resolution combinations that will take too much efforts to install and test. A better way is to go for online cross-browser testing tools with mobile browser and OS support or a mobile-specific browser like LT Browser discussed above.

So, what are we looking for in a cross-browser testing process?

Cross-browser testing looks for issues with the elements of a web page and whether they are supported or not. While functionality testing is another segment of testing, cross-browser testing points out the cross-browser compatibility issues. For example, if you have used CSS subgrids on the web page, they might not render on Google Chrome version 62. The same goes for Javascript libraries and other code. With a browser matrix in our hand, we can rest assured after performing the testing that our user will not be confused as he would when an element crashes on the webpage.

Validate HTML and CSS code

Every mishap on the web page is not the browser’s fault, sometimes programmers commit mistakes too! Since a web page is rendered or parsed by the browser and not compiled, errors and warnings do not stop a web page from loading. Now we have performed cross-browser testing but still cannot find an issue with a missing element is generally a wrong syntax fault. Such syntax errors and not following the W3C web standards can land us in trouble when we progress from mobile-first to complete desktop designs.

HTML and CSS code is very easy to validate. There are a lot of tools available which can do this job for us. Some of them are Validator.nu, W3CMarkup Validator and W3C CSS Validator.

Network Performance

In our efforts to test the page load speed of the web page, a major hurdle is a network. A slower network means slower downloading of web pages and more page load time. For a mobile-first design, it is extremely important to cover all types of users while performing the testing. One such section is the users with slower networks such as 3G network constituting 12% of North American internet users. Only 4% of people use the 5G network in North America now. Imagine this number for countries with poor network infrastructures!!

Network performance can be tested on a real device by switching the connections or through an online tool that provides such features. LT Browser has a network throttling feature to test the website on different connections which helps while performing responsive or cross-browser testing.

A/B Testing

A/B Testing is a type of variation testing or split testing that shows different variations of a web page to different segments of users. Website owners then analyze the performance of both versions and choose the better performing one. For a mobile-first design application, we may develop everything perfectly following every rule in the textbook but the final verdict is up to the user. If the user is not pressing that shiny CTA button, we need to fix that by knowing what the user wants.

A popular question in A/B testing is, where do we create the variation? We cannot jumble up every element on the web page and create fifty variations for the users. This can have an adverse impact on the business. To understand where we are going wrong and which elements need adjustments we can choose the Heatmap features. Heatmap allows the web app owners to see the user’s engagement with the web page and which part are they ignoring.

A famous case study of A/B testing includes the 40% improved sales on a varying page of EA Sports’ SimCity 5 from this:

To this:

Buyers were least interested in the pre-order offer I guess!!

Usability Testing

The final step in completing our mobile-first web design is to present it to real-users and take their feedback. A/B testing is good, but even if you see the Heatmap of the web page, you cannot talk to a real user and ask them why are they not pressing the CTA button? Usability testing covers this hole.

Usability testing is performed with the real users who should be the target audience of the application. For example, you cannot ask a poet to check out a coding website right? Once these users are selected, we ask them to either record their session, their screens and speak their thoughts out loud. Sometimes, the testers can sit with the users too and make their notes by asking the questions. Sometimes, we can just ask them to fill a form with various options. Whatever way you do, usability testing is important and uncovers hidden bugs that are hard to find in a mobile-first design which is a tricky business in itself.

Why does mobile presence matter?

Our analysis of mobile-first design and its development techniques will make you wonder, why should I do mobile-first design? Does mobile presence matter that much?

A few days ago, I was going through my website on Google Search Console and the popped up message was the following:

Mobile-first design is so important today that Google considers mobile-first indexing as the primary search index technique and increasing the visibility on mobile searches which constitutes 52% of the internet traffic!! But this is from Google’s side, what do we have in the box for us?

Better Google Ranking

A mobile-first design is mobile-friendly. It is for mobile users. Therefore, Google realizes that we have a website that is perfect for a mobile user and makes us more visible on queries generated from a smartphone. As a result, our rankings improve. Better Google ranking attracts other businesses as we are more visible and can advertise for them on our website if we want. Since users generally do not remember the web site’s name, a Google search will help us generate traffic and conversions.

Higher Conversion Rates

A mobile-first design will ensure a decrease in the bounce rate. When bounce rates are low and people are actually interested in your website, they will stick to it and will also return back often. Given that the CTA is positioned right with all the eligibility criteria satisfied, a mobile-first design will push your conversion rates higher directly generating a better engagement and goal establishments. As a result, you will get a steady business from your application.

Large Audience Coverage

Businesses also generate a large audience base when they are visible on the Google rankings and have built a mobile-first design. A large audience base is the strength of any business. They require lesser efforts to be engaged as the trust has been established. On the other hand, seeing a larger involvement with your application, you can also introduce other features and services. Such a strong base is a marketing bonus for businesses.

Better Market Presence

Satisfying all the three requirements discussed above directly results in a better market presence. Even though mobile-first designs are recommended for every business, they are currently rare. Mobile-first design is yet to become a standard in web development and choosing it will keep you ahead in the race. A Google search keyword that shows your link in the query results increases your market presence among the competitors. They not only have to work harder to overtake you, but they might also need to restructure their designs if they are still working on desktop ones.

A better market presence means better word of mouth about your happenings, features and upcoming highlights. Such a presence is a direct cause for better revenues and a better future.

Is Mobile-First similar to Mobile-Responsive?

A short answer; no! Mobile-first is a design method. With the mobile-first design, we develop our web application for mobile users first. This starts from a very basic design and gradually advances towards a more complex design structure while keeping in view the mobile-friendliness and mobile users on priority. Mobile-first design is not a development technique but a design strategy that works as a catalyst in development. The developers can get a clear objective and work faster with the defined design.

Mobile-responsive is the ability of the website to adjust itself according to the mobile screen size. A mobile-responsive design need not start with the mobile version of the website and neither takes the thumb area or content relevance into account. Mobile-responsiveness is just concerned with rendering the website on a smaller device.

A mobile-responsive design strategy can be considered a part of the mobile-first design since to produce a mobile-first design, the application needs to be responsive in nature. The mobile-responsive design strategy was a good call when the mobile users had just started to increase. Today, there have been extensive researches on mobile designs in which a mobile-responsive design strategy is hard to survive. To cater to the needs of 4.2 billion mobile internet users, we need a mobile-first design.


The post How to Develop and Test a Mobile-First Design in 2021 appeared first on CSS-Tricks.

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

CSS-Tricks

, , , ,
[Top]

Exploring @property and its Animating Powers

Uh, what’s @property? It’s a new CSS feature! It gives you superpowers. No joke, there is stuff that @property can do that unlocks things in CSS we’ve never been able to do before.

While everything about @property is exciting, perhaps the most interesting thing is that it provides a way to specify a type for custom CSS properties. A type provides more contextual information to the browser, and that results in something cool: We can give the browser the information is needs to transition and animate those properties!

But before we get too giddy about this, it’s worth noting that support isn’t quite there. As it current stands at the time of this writing, @property is supported in Chrome and, by extension, Edge. We need to keep an eye on browser support for when we get to use this in other places, like Firefox and Safari.

First off, we get type checking

@property --spinAngle {   /* An initial value for our custom property */   initial-value: 0deg;   /* Whether it inherits from parent set values or not */   inherits: false;   /* The type. Yes, the type. You thought TypeScript was cool */   syntax: '<angle>'; }  @keyframes spin {   to {     --spinAngle: 360deg;   } }

That’s right! Type checking in CSS. It’s sorta like creating our very own mini CSS specification. And that’s a simple example. Check out all of the various types we have at our disposal:

  • length
  • number
  • percentage
  • length-percentage
  • color
  • image
  • url
  • integer
  • angle
  • time
  • resolution
  • transform-list
  • transform-function
  • custom-ident (a custom identifier string)

Before any of this, we may have relied on using “tricks” for powering animations with custom properties.

What cool stuff can we do then? Let’s take a look to spark our imaginations.

Let’s animate color

How might you animate an element either through a series of colors or between them? I’m a big advocate for the HSL color space which breaks things down into fairly understandable numbers: hue, saturation, and lightness, respectively.

Animating a hue feels like something fun we can do. What’s colorful? A rainbow! There’s a variety of ways we could make a rainbow. Here’s one:

In this example, CSS Custom Properties are set on the different bands of the rainbow using :nth-child() to scope them to individual bands. Each band also has an --index set to help with sizing.

To animate those bands, we might use that --index to set some negative animation delays, but then use the same keyframe animation to cycle through hues.

.rainbow__band {   border-color: hsl(var(--hue, 10), 80%, 50%);   animation: rainbow 2s calc(var(--index, 0) * -0.2s) infinite linear; }  @keyframes rainbow {   0%, 100% {     --hue: 10;   }   14% {     --hue: 35;   }   28% {     --hue: 55;   }   42% {     --hue: 110;   }   56% {     --hue: 200;   }   70% {     --hue: 230;   }   84% {     --hue: 280;   } }

That might work out okay if you want a “stepped” effect. But, those keyframe steps aren’t particularly accurate. I’ve used steps of 14% as a rough jump.

We could animate the border-color and that would get the job done. But, we’d still have a keyframe step calculation issue. And we need to write a lot of CSS to get this done:

@keyframes rainbow {   0%, 100% {     border-color: hsl(10, 80%, 50%);   }   14% {     border-color: hsl(35, 80%, 50%);   }   28% {     border-color: hsl(55, 80%, 50%);   }   42% {     border-color: hsl(110, 80%, 50%);   }   56% {     border-color: hsl(200, 80%, 50%);   }   70% {     border-color: hsl(230, 80%, 50%);   }   84% {     border-color: hsl(280, 80%, 50%);   } }

Enter @property. Let’s start by defining a custom property for hue. This tells the browser our custom property, --hue, is going to be a number (not a string that looks like a number):

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

Hue values in HSL can go from 0 to 360. We start with an initial value of 0. The value isn’t going to inherit. And our value, in this case, is a number. The animation is as straightforward as:

@keyframes rainbow {   to {     --hue: 360;   } }

Yep, that’s the ticket:

To get the starting points accurate, we could play with delays for each band. This gives us some cool flexibility. For example, we can up the animation-duration and we get a slow cycle. Have a play with the speed in this demo.

It may not be the “wildest” of examples, but I think animating color has some fun opportunities when we use color spaces that make logical use of numbers. Animating through the color wheel before required some trickiness. For example, generating keyframes with a preprocessor, like Stylus:

@keyframes party    for $  frame in (0..100)     {$  frame * 1%}       background 'hsl(%s, 65%, 40%)' % ($  frame * 3.6)

We do this purely because this isn’t understood by the browser. It sees going from 0 to 360 on the color wheel as an instant transition because both hsl values show the same color.

@keyframes party {   from {     background: hsl(0, 80%, 50%);    }   to {     background: hsl(360, 80%, 50%);   } }

The keyframes are the same, so the browser assumes the animation stays at the same background value when what we actually want is for the browser to go through the entire hue spectrum, starting at one value and ending at that same value after it goes through the motions.

Think of all the other opportunities we have here. We can:

  • animate the saturation
  • use different easings
  • animate the lightness
  • Try rgb()
  • Try degrees in hsl() and declare our custom property type as <angle>

What’s neat is that we can share that animated value across elements with scoping! Consider this button. The border and shadow animate through the color wheel on hover.

Animating color leads me think… wow!

Straight-up numbering

Because we can define types for numbers—like integer and number—that means we can also animate numbers instead of using those numbers as part of something else. Carter Li actually wrote an article on this right here on CSS-Tricks. The trick is to use an integer in combination with CSS counters. This is similar to how we can work the counter in “Pure CSS” games like this one.

The use of counter and pseudo-elements provides a way to convert a number to a string. Then we can use that string for the content of a pseudo-element. Here are the important bits:

@property --milliseconds {   inherits: false;   initial-value: 0;   syntax: '<integer>'; }  .counter {   counter-reset: ms var(--milliseconds);   animation: count 1s steps(100) infinite; }  .counter:after {   content: counter(ms); }  @keyframes count {   to {     --milliseconds: 100;   } }

Which gives us something like this. Pretty cool.

Take that a little further and you’ve got yourself a working stopwatch made with nothing but CSS and HTML. Click the buttons! The rad thing here is that this actually works as a timer. It won’t suffer from drift. In some ways it may be more accurate than the JavaScript solutions we often reach for such as setInterval. Check out this great video from Google Chrome Developer about JavaScript counters.

What other things could you use animated numbers for? A countdown perhaps?

Animated gradients

You know the ones, linear, radial, and conic. Ever been in a spot where you wanted to transition or animate the color stops? Well, @property can do that!

Consider a gradient where we‘re creating some waves on a beach. Once we’ve layered up some images we could make something like this.

body {   background-image:     linear-gradient(transparent 0 calc(35% + (var(--wave) * 0.5)), var(--wave-four) calc(75% + var(--wave)) 100%),     linear-gradient(transparent 0 calc(35% + (var(--wave) * 0.5)), var(--wave-three) calc(50% + var(--wave)) calc(75% + var(--wave))),     linear-gradient(transparent 0 calc(20% + (var(--wave) * 0.5)), var(--wave-two) calc(35% + var(--wave)) calc(50% + var(--wave))),     linear-gradient(transparent 0 calc(15% + (var(--wave) * 0.5)), var(--wave-one) calc(25% + var(--wave)) calc(35% + var(--wave))), var(--sand); }

There is quite a bit going on there. But, to break it down, we’re creating each color stop with calc(). And in that calculation, we add the value of --wave. The neat trick here is that when we animate that --wave value, all the wave layers move.

This is all the code we needed to make that happen:

body {   animation: waves 5s infinite ease-in-out; } @keyframes waves {   50% {     --wave: 25%;   } }

Without the use of @property, our waves would step between high and low tide. But, with it, we get a nice chilled effect like this.

It’s exciting to think other neat opportunities that we get when manipulating images. Like rotation. Or how about animating the angle of a conic-gradient… but, within a border-image. Bramus Van Damme does a brilliant job covering this concept.

Let’s break it down by creating a charging indicator. We’re going to animate an angle and a hue at the same time. We can start with two custom properties:

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

The animation will update the angle and hue with a slight pause on each iteration.

@keyframes load {   0%, 10% {     --angle: 0deg;     --hue: 0;   }   100% {     --angle: 360deg;     --hue: 100;   } }

Now let’s apply it as the border-image of an element.

.loader {   --charge: hsl(var(--hue), 80%, 50%);   border-image: conic-gradient(var(--charge) var(--angle), transparent calc(var(--angle) * 0.5deg)) 30;   animation: load 2s infinite ease-in-out; }

Pretty cool.

Unfortunately, border-image doesn‘t play nice with border-radius. But, we could use a pseudo-element behind it. Combine it with the number animation tricks from before and we’ve got a full charging/loading animation. (Yep, it changes when it gets to 100%.)

Transforms are cool, too

One issue with animating transforms is transitioning between certain parts. It often ends up breaking or not looking how it should. Consider the classic example of a ball being throw. We want it to go from point A to point B while imitating the effect of gravity.

An initial attempt might look like this

@keyframes throw {   0% {     transform: translate(-500%, 0);   }   50% {     transform: translate(0, -250%);   }   100% {     transform: translate(500%, 0);   } }

But, we’ll soon see that it doesn’t look anything like we want.

Before, we may have reached for wrapper elements and animated them in isolation. But, with @property, we can animate the individual values of the transform. And all on one timeline. Let’s flip the way this works by defining custom properties and then setting a transform on the ball.

@property --x {   inherits: false;   initial-value: 0%;   syntax: '<percentage>'; }  @property --y {   inherits: false;   initial-value: 0%;   syntax: '<percentage>'; }  @property --rotate {   inherits: false;   initial-value: 0deg;   syntax: '<angle>'; }  .ball {   animation: throw 1s infinite alternate ease-in-out;   transform: translateX(var(--x)) translateY(var(--y)) rotate(var(--rotate)); }

Now for our animation, we can compose the transform we want against the keyframes:

@keyframes throw {   0% {     --x: -500%;     --rotate: 0deg;   }   50% {     --y: -250%;   }   100% {     --x: 500%;     --rotate: 360deg;   } }

The result? The curved path we had hoped for. And we can make that look different depending on the different timing functions we use. We could split the animation into three ways and use different timing functions. That would give us different results for the way the ball moves.

Consider another example where we have a car that we want to drive around a square with rounded corners.

We can use a similar approach to what we did with the ball:

@property --x {   inherits: false;   initial-value: -22.5;   syntax: '<number>'; }  @property --y {   inherits: false;   initial-value: 0;   syntax: '<number>'; }  @property --r {   inherits: false;   initial-value: 0deg;   syntax: '<angle>'; }

The car’s transform is using calculated with vmin to keep things responsive:

.car {   transform: translate(calc(var(--x) * 1vmin), calc(var(--y) * 1vmin)) rotate(var(--r)); }

Now can write an extremely accurate frame-by-frame journey for the car. We could start with the value of --x.

@keyframes journey {   0%, 100% {     --x: -22.5;   }   25% {     --x: 0;   }   50% {     --x: 22.5;   }   75% {     --x: 0;   } }

The car makes the right journey on the x-axis.

Then we build upon that by adding the travel for the y-axis:

@keyframes journey {   0%, 100% {     --x: -22.5;     --y: 0;   }   25% {     --x: 0;     --y: -22.5;   }   50% {     --x: 22.5;     --y: 0;   }   75% {     --x: 0;     --y: 22.5;   } }

Well, that’s not quite right.

Let’s drop some extra steps into our @keyframes to smooth things out:

@keyframes journey {   0%, 100% {     --x: -22.5;     --y: 0;   }   12.5% {     --x: -22.5;     --y: -22.5;   }   25% {     --x: 0;     --y: -22.5;   }   37.5% {     --y: -22.5;     --x: 22.5;   }   50% {     --x: 22.5;     --y: 0;   }   62.5% {     --x: 22.5;     --y: 22.5;   }   75% {     --x: 0;     --y: 22.5;   }   87.5% {     --x: -22.5;     --y: 22.5;   } }

Ah, much better now:

All that‘s left is the car‘s rotation. We‘re going with a 5% window around the corners. It’s not precise but it definitely shows the potential of what’s possible:

@keyframes journey {   0% {     --x: -22.5;     --y: 0;     --r: 0deg;   }   10% {     --r: 0deg;   }   12.5% {     --x: -22.5;     --y: -22.5;   }   15% {     --r: 90deg;   }   25% {     --x: 0;     --y: -22.5;   }   35% {     --r: 90deg;   }   37.5% {     --y: -22.5;     --x: 22.5;   }   40% {     --r: 180deg;   }   50% {     --x: 22.5;     --y: 0;   }   60% {     --r: 180deg;   }   62.5% {     --x: 22.5;     --y: 22.5;   }   65% {     --r: 270deg;   }   75% {     --x: 0;     --y: 22.5;   }   85% {     --r: 270deg;   }   87.5% {     --x: -22.5;     --y: 22.5;   }   90% {     --r: 360deg;   }   100% {     --x: -22.5;     --y: 0;     --r: 360deg;   } }

And there we have it, a car driving around a curved square! No wrappers, no need for complex Math. And we composed it all with custom properties.

Powering an entire scene with variables

We‘ve seen some pretty neat @property possibilities so far, but putting everything we’ve looked at here together can take things to another level. For example, we can power entire scenes with just a few custom properties.

Consider the following concept for a 404 page. Two registered properties power the different moving parts. We have a moving gradient that’s clipped with -webkit-background-clip. The shadow moves by reading the values of the properties. And we swing another element for the light effect.

That’s it!

It’s exciting to think about what types of things we can do with the ability to define types with @property. By giving the browser additional context about a custom property, we can go nuts in ways we couldn’t before with basic strings.

What ideas do you have for the other types? Time and resolution would make for interesting transitions, though I’ll admit I wasn’t able to make them work that way I was hoping. url could also be neat, like perhaps transitioning between a range of sources the way an image carousel typically does. Just brainstorming here!

I hope this quick look at @property inspires you to go check it out and make your own awesome demos! I look forward to seeing what you make. In fact, please share them with me here in the comments!


The post Exploring @property and its Animating Powers appeared first on CSS-Tricks.

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

CSS-Tricks

, , ,
[Top]

How to increase conversions on an e-commerce website

[Top]

A Bare-Bones Approach to Versatile and Reusable Skeleton Loaders

UI components like spinners and skeleton loaders make waiting for a page load less frustrating and might even affect how loading times are perceived when used correctly. They won’t completely prevent users from abandoning the website, but they might encourage them to wait a bit longer. Animated spinners are used in most cases since they are easy to implement and they generally do a good enough job. Skeleton loaders have a limited use-case and might be complex to implement and maintain, but they offer an improved loading experience for those specific use-cases.

I’ve noticed that developers are either unsure when to use skeleton loaders to enhance the UX or do not know how to approach the implementation. More common examples of skeleton loaders around the web are not all that reusable or scalable. They are usually tailor-made for a single component and cannot be applied to anything else. That is one of the reasons developers use regular spinners instead and avoid the potential overhead in the code. Surely, there must be a way to implement skeleton loaders in a more simple, reusable, and scalable way.

Spinner elements and skeleton loaders

A spinner (or progress bar) element is the simplest and probably most commonly used element to indicate a loading state. A spinner might look better than a blank page, but it won’t hold user’s attention for long. Spinners tell the user that something will load eventually. Users have to passively wait for content to load, meaning that they are unable to interact with other elements on the page or consume any other content on the page. Spinners take up the entire screen space and no content is available to the user.

The spinner element is displayed and covers the entire screen until all content has finished loading.

However, skeleton loaders (or skeleton screens) tell the user that the content is about to load and they might provide a better loading UX than a simple spinner. Empty boxes (with a solid color or gradient background) are used as a placeholder for the content that is being loaded. In most cases, content is gradually being loaded which allows users to maintain a sense of progress and perception that a page load is faster than it is. Users are actively waiting, meaning that they can interact with the page or consume at least some part of the content while the rest is loading.

Empty boxes (with a solid color or gradient background) are used as a placeholder while content is being gradually loaded. Text content is loaded and displayed first, and images are loaded and displayed after that.

It’s important to note that loading components should not be used to address performance issues. If a website is experiencing performance issues due to the problem that can be addressed (un-optimized assets or code, back-end performance issues, etc.), they should be fixed first. Loading elements won’t prevent users from abandoning websites with poor performance and high loading times. Loading elements should be used as a last resort when waiting is unavoidable and when loading delay is not caused by unaddressed performance issues.

Using skeleton loaders properly

Skeleton loaders shouldn’t be treated as a replacement for full-screen loading elements but instead when specific conditions for content and layout have been met. Let’s take this step-by-step and see how to use loading UI components effectively and how to know when to go with skeleton loaders instead of regular spinners.

Is loading delay avoidable?

The best way to approach loading in terms of UX is to avoid it altogether. We need to make sure that loading delay is unavoidable and is not the result of the aforementioned performance issues that can be fixed. The main priority should always be performance improvements and reducing the time needed to fetch and display the content.

Is loading initiated by the user and is the feedback required?

In some cases, user actions might initiate additional content to load. Some examples include lazy-loading content (e.g. images) in the user’s viewport while scrolling, loading content on a button click, etc. We need to include a loading element for cases where a user needs to get some kind of feedback for their actions that have initiated the loading process.

As seen in the following mockup, without a loading element to provide feedback, a user doesn’t know that their actions have initiated any loading process that is happening in the background.

We are asynchronously loading the content in a modal when the button is clicked. In the first example, no loading element is displayed and users might think that their click hasn’t been registered. In the second example, users get the feedback that their click has been registered and that the content is being loaded.

Is the layout consistent and predictable?

If we’ve decided to go with a loader element, we now need to choose what type of loader element best fits our use-case. Skeleton loaders are most effective in cases when we can predict the type and layout of the content that is being loaded in. If the skeleton loader layout doesn’t accurately represent the loaded content’s layout to some degree, the sudden change may cause layout shift and leave the user confused and disoriented. Use skeleton loaders for elements with predictable content for consistent layouts.

The grid layout on the left (taken from discogs.com) represents an ideal use-case for skeleton loaders, while the comments example on the right (taken from CSS-Tricks) is an ideal use-case for spinners.

Is there content on the page that is immediately available to the user?

Skeleton loaders are most effective when there are sections or page elements already present on the page while the skeleton loader is active and additional content is actively loading. Gradually loading in content means that static content is available on page load and asynchronously-loaded content is displayed as it becomes available (for example, the first text is loaded and images after that). This approach ensures that the user maintains a sense of progression and is expecting the content to finish loading at any moment. Having the entire screen covered in skeleton loaders without any content present and without gradual content loading is not significantly better than having the screen covered by a full-page spinner or progress bar.

The mockup on the left shows a skeleton loader covering all elements until everything has loaded. The mockup on the right shows a skeleton loader covering only content that is being asynchronously loaded. The page is usable since they have a part of the website’s content displayed and the user maintains a sense of progression.

Creating robust skeleton loaders

Now that we know when to use skeleton loaders and how to use them properly, we can finally do some coding! But first, let me tell you how we are going to approach this.

Most skeleton loading examples from around the web are, in my opinion, over-engineered and high-maintenance. You might have seen one of those examples where skeleton screens are created as a separate UI component with separate CSS styles or created with elaborate use of CSS gradients to simulate the final layout. Creating and maintaining a separate skeleton loader or skeleton styles for each UI component can become serious overhead in development with such a highly-specific approach. This is especially true when we look at scalability, as any change to the existing layout also involves updating the skeleton layout or styles.

Let’s try and find a bare-bones approach to implementing skeleton loading that should work for most use-cases and will be easy to implement, reuse and maintain!

Card grid component

We’ll use regular HTML, CSS, and JavaScript for implementation, but the overall approach can be adapted to work with most tech stacks and frameworks.

We are going to create a simple grid of six card elements (three in each row) as an example, and simulate asynchronous content loading with a button click.

We’ll use the following markup for each card. Notice that we are setting width and height on our images and using a 1px transparent image as a placeholder. This will ensure that the image skeleton loader is visible until the image has been loaded.

<div class="card">   <img width="200" height="200" class="card-image" src="..." />   <h3 class="card-title"></h3>   <p class="card-description"></p>   <button class="card-button">Card button</button> </div>

Here is our card grid example with some layout and presentation styles applied to it. Content nodes are added or removed from the DOM depending on the loading state using JavaScript to simulate asynchronous loading.

Skeleton loader styles

Developers usually implement skeleton loaders by creating replacement skeleton components (with dedicated skeleton CSS classes) or by recreating entire layouts with CSS gradients. Those approaches are inflexible and not reusable at all since individual skeleton loaders are tailor-made for each layout. Considering that layout styles (spacings, grid, inline, block and flex elements, etc.) are already present from the main component (card) styles, skeleton loaders just need to replace the content, not the entire component!

With that in mind, let’s create skeleton loader styles that become active only when a parent class is set and use CSS properties that only affect the presentation and content. Notice that these styles are independent from the layout and content of the element they’re being applied to, which should make them highly reusable.

.loading .loading-item {   background: #949494 !important; /* Customizable skeleton loader color */   color: rgba(0, 0, 0, 0) !important;   border-color: rgba(0, 0, 0, 0) !important;   user-select: none;   cursor: wait; }  .loading .loading-item * {   visibility: hidden !important; }  .loading .loading-item:empty::after, .loading .loading-item *:empty::after {   content: "a0"; }

Base parent class .loading is used to activate the skeleton loading styles. The .loading-item class is used to override element’s presentational styles to display a skeleton element. This also ensures that the layout and dimensions of the element are preserved and inherited by the skeleton. Additionally, .loading-item makes sure that all child elements are hidden and have at least an empty space character (a0) inside it so that element is displayed and its layout is rendered.

Let’s add skeleton loader CSS classes to our markup. Notice how no additional HTML elements have been added, we are only applying additional CSS classes.

<div class="card loading">   <img width="200" height="200" class="card-image loading-item" src="..." />   <h3 class="card-title loading-item"></h3>   <p class="card-description loading-item"></p>   <button class="card-button loading-item">Card button</button> </div>

Once the content has loaded, we only need to remove loading CSS class from the parent component to hide the skeleton loader styles.

These few lines should work for most, if not all, use cases depending on your custom CSS since these skeleton loaders inherit the layout from the main (content) styles and create a solid box that replaces the content by filling out the empty space left in the layout. We’re also applying these classes to non-empty elements (button with text) and replacing it with a skeleton. A button might have the text content ready from the start, but it might be missing additional data that is required for it to function correctly, so we should also hide it while that data is loaded in.

This approach can also adapt to most changes in the layout and markup. For example, if we were to remove the description part of the card or decide to move the title above the image, we wouldn’t need to make any changes to the skeleton styles, since skeleton responds to all changes in the markup.

Additional skeleton loading override styles can be applied to a specific element simply by using the .loading .target-element selector.

.loading .button, .loading .link {   pointer-events: none; }

Multi-line content and layout shifts

As you can see, the previous example works great with cards and the grid layout we’re using, but notice that the page content slightly jumps the moment it is loaded. This is called a layout shift. Our .card-description component has a fixed height with three lines of text, but the skeleton placeholder spans only one line of text. When the extra content is loaded, the container dimensions change and the overall layout is shifted as a result. Layout shift is not bad in this particular case, but might confuse and disorient the user in more severe cases.

This can be easily fixed directly in the placeholder element. Placeholder content is going to get replaced by the content that is being loaded anyway, so we can add anything we need inside it. So, let’s add a few <br /> elements to simulate multiple lines of text.

<div class="card loading">   <img width="200" height="200" class="card-image loading-item" src="..." />   <h3 class="card-title loading-item"></h3>   <p class="card-description loading-item"><br/><br/><br/></p>   <button class="card-button loading-item">Card button</button> </div>

We’re using basic HTML to shape the skeleton and change the number of lines inside it. Other examples on the web might achieve this using CSS padding or some other way, but this introduces overhead in the code. After all, content can span any number of lines and we would want to cover all those cases.

As an added benefit of using <br /> elements, they inherit the CSS properties that affect the content dimensions (e.g. the line height, font size, etc.). Similarly, &nbsp characters can be used to add additional spacing to the inline placeholder elements.

With a few lines of CSS, we’ve managed to create versatile and extensible skeleton loader styles that can be applied to a wide range of UI components. We’ve also managed to come up with a simple way of vertically extending the skeleton boxes to simulate content that spans multiple lines of text.

To further showcase how versatile this skeleton loader CSS snippet is, I’ve created a simple example where I’ve added the snippet to a page using Bootstrap CSS framework without any additional changes or overrides. Please note that in this example no text content will be displayed or simulated, but it will work as in previous examples. This is just to showcase how styles can be easily integrated with other CSS systems.

Here is an additional example to showcase how these styles can be applied to various elements, including input, label and a elements.

Accessibility requirements

We should also take accessibility (a11y) requirements into account and make sure that the content is accessible to all users. Skeleton loaders without a11y features might disorientate and confuse users that have visual disabilities or browse the web using screen readers.

Contrast

You might have noticed that the skeleton loaders in our example have a high contrast and they look more prominent compared to the common low-contrast skeleton loaders in the wild. Some users might experience difficulties perceiving and using low-contrast UI components. That is why Web Content Accessibility Guidelines (WCAG) specify a 3:1 minimum contrast for non-text UI components.

The upcoming “Media queries level 5” draft contains a prefers-contrast media query that will enable us to detect user contrast preferences. This will give us more flexibility by allowing us to assign a high-contrast background color to skeleton loaders for users that request a high-contrast version, and have a subtle low-contrast background color for others. I would suggest implementing high-contrast skeleton loaders by default until the prefers-contrast media query becomes more widely supported.

/* NOTE: as of the time of writing this article, this feature is not supported in browsers, so this code won't work */  .loading .loading-item { /* Default skeleton loader styles */ }  @media (prefers-contrast: high) {   .loading .loading-item {     /* High-contrast skeleton loader styles */   } } 

Animations

Depending on the design and the implementation of animated skeleton loaders, users suffering from visual disorders might feel overwhelmed by the animations and find the site unusable. It’s always a good idea to prevent animations from firing for users that prefer reduced motion. This media query is widely-supported in modern browsers and can be used without any caveats.

.loading .loading-item {   animation-name: skeleton;   background: /* animated gradient background */; }  @media (prefers-reduced-motion) {   .loading .loading-item {     animation: none !important;     background: /* solid color */;   } }

Screen readers

To better support screen readers, we need to update our HTML with ARIA (Accessible Rich Internet Applications) markup. This markup won’t affect our content or presentation, but it will allow users using screen readers to better understand and navigate around our website content, including our skeleton loaders.

Adrian Roselli has very detailed research on the topic of accessible skeleton loaders for cases when skeleton loaders are implemented as separate UI components. For our example, I’ll use the aria-hidden attribute in combination with visually hidden text to give screen readers a hint that content is in the process of loading. Screen readers will ignore the content with aria-hidden="true", but they’ll use the visually-hidden element to indicate the loading state to the user.

Let’s update our cards with the ARIA markup and loading indicator element.

<div class="card loading">   <span aria-hidden="false" class="visually-hidden loading-text">Loading... Please wait.</span>   <img width="200" height="200" class="card-image loading-item" aria-hidden="true" src="..." />   <h3 class="card-title loading-item" aria-hidden="true"></h3>   <p class="card-description loading-item" aria-hidden="true"><br/><br/><br/></p>   <button class="card-button loading-item" aria-hidden="true">Card button</button> </div>

We also could have applied aria-hidden to the grid container element and add a single visually hidden element before the container markup, but I wanted to keep the markup examples focused on a single card element rather than on the full grid, so I went with this version.

When the content has finished loading and is displayed in the DOM, we need to toggle aria-hidden to false for content containers and toggle aria-hidden to true on a visually hidden loading text indicator.

Here’s the finished example

That’s a wrap

Implementing skeleton loaders requires a slightly different approach than implementing regular loading elements, like spinners. I’ve seen numerous examples around the web that implement skeleton loaders in a way that severely limits their reusability. These over-engineered solutions usually involve creating separate skeleton loader UI components with dedicated (narrow-scope) skeleton CSS markup or recreating the layout with CSS gradients and magic numbers. We’ve seen that only the content needs to be replaced with the skeleton loaders, and not the entire component.

We’ve managed to create simple, versatile, and reusable skeleton loaders that inherit the layout from the default styles and replace the content inside the empty containers with solid boxes. With just two CSS classes, these skeleton loaders can easily be added to virtually any HTML element and extended, if needed. We’ve also made sure that this solution is accessible and doesn’t bloat the markup with additional HTML elements or duplicated UI components.

Thank you for taking the time to read this article. Let me know your thoughts on this approach and let me know how did you approach creating skeleton loaders in your projects.


The post A Bare-Bones Approach to Versatile and Reusable Skeleton Loaders appeared first on CSS-Tricks.

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

CSS-Tricks

, , , , ,
[Top]

React Without Build Tools

Jim Nielsen:

I think you’ll find it quite refreshing to use React A) with a JSX-like syntax, and B) without any kind of build tooling.

Refreshing indeed:

It’s not really the React that’s the hard part to pull off without build tools (although I do wonder what we lose from not tree shaking), it’s the JSX. I’m so used to JSX I think it would be hard for me to work on a front-end JavaScript project without it. But I know some people literally prefer a render function instead. If that’s the case, you could use React.createComponent directly and skip the JSX, or in the case of Preact, use h:

I work on a project that uses Mithril for the JavaScript templating which is a bit like that, and it’s not my favorite syntax, but you totally get used to it (and it’s fast):

Direct Link to ArticlePermalink


The post React Without Build Tools appeared first on CSS-Tricks.

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

CSS-Tricks

, , ,
[Top]

How to Animate the Details Element

Here’s a nice simple demo from Moritz Gießmann on animating the triangle of a <details> element, which is the affordance that tells people this thing can be opened. Animating it, then is another kind of affordance that tells people this thing is opening now.

The tricks?

  1. Turn off the default triangle: details summary::-webkit-details-marker { display:none; }. You can’t animate that one.
  2. Make a replacement triangle with the CSS border trick and a pseudo element.
  3. Animate the new triangle when the state is open: details[open] > summary::before { transform: rotate(90deg); }.

This only animates the triangle. The content inside still “snaps” open. Wanna smooth things out? Louis Hoebregts’ “How to Animate the Details Element Using WAAPI” covers that.

Here’s a fork where I’ll combine them just because:

I see Moritz put the cursor: pointer; on the summary as well like Greg Gibson suggests.


The post How to Animate the Details Element appeared first on CSS-Tricks.

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

CSS-Tricks

, ,
[Top]

The Best Font Loading Strategies and How to Execute Them

Zach Leatherman wrote up a comprehensive list of font loading strategies that have been widely shared in the web development field. I took a look at this list before, but got so scared (and confused), that I decided not to do anything at all. I don’t know how to begin loading fonts the best way and I don’t want to feel like an idiot going through this list.

Today, I gave myself time to sit down and figure it out. In this article, I want to share with you the best loading strategies and how to execute all of them.

The best strategies

Zach recommends two strategies in his article:

  1. FOUT with Class – Best approach for most situations. (This works whether we use a font-hosting company or hosting our own fonts.)
  2. Critical FOFT – Most performant approach. (This only works if we host our own fonts.)

Before we dive into these two strategies, we need to clear up the acronyms so you understand what Zach is saying.

FOIT, FOUT, FOFT

The acronyms are as follows:

  • FOIT means flash of invisible text. When web fonts are loading, we hide text so users don’t see anything. We only show the text when web fonts are loaded.
  • FOUT means flash of unstyled text. When web fonts are loading, we show users a system font. When the web font gets loaded, we change the text back to the desired web font.
  • FOFT means flash of faux text. This one is complicated and warrants more explanation. We’ll talk about it in detail when we hit the FOFT section.

Self-hosted fonts vs. cloud-hosted fonts

There are two main ways to host fonts:

  1. Use a cloud provider.
  2. Self-host the fonts.

How we load fonts differs greatly depending on which option you choose.

Loading fonts with cloud-hosted fonts

It’s often easier to cloud-hosted fonts. The provider will give us a link to load the fonts. We can simply plunk this link into our HTML and we’ll get our web font. Here’s an example where we load web fonts from Adobe Fonts now (previously known as Typekit).

<link rel="stylesheet" href="https://use.typekit.net/your-kit-id.css">

Unfortunately, this isn’t the best approach. The href blocks the browser. If loading the web font hangs, users won’t be able to do anything while they wait.

Typekit used to provide JavaScript that loads a font asynchronously. It’s a pity they don’t show this JavaScript version anymore. (The code still works though, but I have no idea when, or if, it will stop working.)

Loading fonts from Google Fonts is slightly better because it provides font-display: swap. Here’s an example where we load Lato from Google Fonts. (The display=swap parameter triggers font-display: swap).

<link   href="https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,700;1,400;1,700&display=swap"   rel="stylesheet" />

Loading fonts with self-hosted fonts

You can only self-host your fonts if you have the license to do so. Since fonts can be expensive, most people choose to use a cloud provider instead.

There’s a cheap way to get fonts though. Design Cuts runs font deals occasionally where you can get insanely high-quality fonts for just $ 29 per bundle. Each bundle can contain up to 12 fonts. I managed to get classics like Claredon, DIN, Futura, and a whole slew of fonts I can play around by camping on their newsletter for these deals.

If we want to self-host fonts, we need to understand two more concepts: @font-face and font-display: swap.

@font-face

@font-face lets us declare fonts in CSS. If we want to self-host fonts, we need to use @font-face to declare your fonts.

In this declaration, we can specify four things:

  • font-family: This tells CSS (and JavaScript) the name of our font.
  • src: Path to find the font so they can get loaded
  • font-weight: The font weight. Defaults to 400 if omitted.
  • font-style: Whether to italicize the font. Defaults to normal if omitted.

For src, we need to specify several font formats. For a practical level of browser support, we can use woff2 and woff.

Here’s an example where we load Lato via @font-face.

@font-face {   font-family: Lato;   src: url('path-to-lato.woff2') format('woff2'),        url('path-to-lato.woff') format('woff'); }  @font-face {   font-family: Lato;   src: url('path-to-lato-bold.woff2') format('woff2'),        url('path-to-lato-bold.woff') format('woff');   font-weight: 700; }  @font-face {   font-family: Lato;   src: url('path-to-lato-italic.woff2') format('woff2'),        url('path-to-lato-italic.woff') format('woff');   font-style: italic; }  @font-face {   font-family: Lato;   src: url('path-to-lato-bold-italic.woff2') format('woff2'),        url('path-to-lato-bold-italic.woff') format('woff');   font-weight: 700;   font-style: italic; }

If you don’t have woff2 or woff files, you can upload your font files (either Opentype or Truetype) into a font-face generator to get them.

Next, we declare the web font in a font-family property.

html {   font-family: Lato, sans-serif; }

When browsers parse an element with the web font, they trigger a download for the web font.

font-display: swap

font-display takes one of four possible values: auto, swap, fallback, and optional. swap instructs browsers to display the fallback text before web fonts get loaded. In other words, swap triggers FOUT for self-hosted fonts. Find out about other values from in the CSS-Tricks almanac.

Browser support for font-display: swap is pretty good nowadays so we can use it in our projects.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

Chrome Firefox IE Edge Safari
60 58 No 79 11.1

Mobile / Tablet

Android Chrome Android Firefox Android iOS Safari
88 85 81 11.3-11.4

FOUT vs. FOUT with Class

FOUT means flash of unstyled text. You always want FOUT over FOIT (flash of invisible text) because it’s better for users to read words written with system fonts compared to words written with invisible ink. We mentioned earlier that font-display: swap gives you the ability to use FOUT natively.

FOUT with Class gives you the same results—FOUT—but it uses JavaScript to load the fonts. The mechanics are as follows:

  • First: Load system fonts.
  • Second: Load web fonts via JavaScript.
  • Third: When web fonts are loaded, add a class to the <html> tag.
  • Fourth: Switch the system font with the loaded web font.

Zach recommends loading fonts via JavaScript even though font-display: swap enjoys good browser support. In other words, Zach recommends FOUT with Class over @font-face + font-display: swap.

He recommends FOUT with Class because of these three reasons:

  1. We can group repaints.
  2. We can adapt to user preferences.
  3. We can skip font-loading altogether if users have a slow connection.

I’ll let you dive deeper into the reasons in another article. While writing this article, I found a fourth reason to prefer FOUT with Class: We can skip font-loading when users already have the font loaded in their system. (We do this with sessionStorage as we’ll see below.)

FOUT with Class (for cloud-hosted fonts)

First, we want to load our fonts as usual from your cloud-hosting company. Here’s an example where I loaded Lato from Google Fonts:

<head>   <link     href="https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,700;1,400;1,700&display=swap"     rel="stylesheet"   /> </head>

Next, we want to load fonts via JavaScript. We’ll inject a script into the <head> section since the code footprint is small, and it’s going to be asynchronous anyway.

<head>   <link     href="https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,700;1,400;1,700&display=swap"     rel="stylesheet"   />   <script src="js/load-fonts.js"></script> </head>

In load-fonts.js, we want to use the CSS Font Loading API to load the font. Here, we can use Promise.all to load all fonts simultaneously. When we do this, we’re grouping repaints.

The code looks like this:

if ('fonts' in document) {   Promise.all([     document.fonts.load('1em Lato'),     document.fonts.load('700 1em Lato'),     document.fonts.load('italic 1em Lato'),     document.fonts.load('italic 700 1em Lato')   ]).then(_ => () {     document.documentElement.classList.add('fonts-loaded')   }) }

When fonts are loaded, we want to change the body text to the web font. We can do this via CSS by using the .fonts-loaded class.

/* System font before [[webfont]]s get loaded */ body {   font-family: sans-serif; }  /* Use [[webfont]] when [[webfont]] gets loaded*/ .fonts-loaded body {   font-family: Lato,  sans-serif; }

Pay attention here: We need to use the .fonts-loaded class with this approach.

We cannot write the web font directly in the <body>‘s font-family; doing this will trigger fonts to download, which means you’re using @font-face + font-display: swap. It also means the JavaScript is redundant.

/* DO NOT DO THIS */ body {   font-family: Lato, sans-serif; }

If users visit additional pages on the site, they already have the fonts loaded in their browser. We can skip the font-loading process (to speed things up) by using sessionStorage.

if (sessionStorage.fontsLoaded) {   document.documentElement.classList.add('fonts-loaded') } else {   if ('fonts' in document) {   Promise.all([     document.fonts.load('1em Lato'),     document.fonts.load('700 1em Lato'),     document.fonts.load('italic 1em Lato'),     document.fonts.load('italic 700 1em Lato')   ]).then(_ => () {     document.documentElement.classList.add('fonts-loaded')   })   } }

If we want to optimize the JavaScript for readability, we can use an early return pattern to reduce indentation.

function loadFonts () {   if (sessionStorage.fontsLoaded) {     document.documentElement.classList.add('fonts-loaded')     return    }     if ('fonts' in document) {   Promise.all([     document.fonts.load('1em Lato'),     document.fonts.load('700 1em Lato'),     document.fonts.load('italic 1em Lato'),     document.fonts.load('italic 700 1em Lato')   ]).then(_ => () {     document.documentElement.classList.add('fonts-loaded')   })   }  loadFonts()

FOUT with Class (for self-hosted fonts)

First, we want to load our fonts as usual via @font-face. The font-display: swap property is optional since we’re loading fonts via JavaScript.

@font-face {   font-family: Lato;   src: url('path-to-lato.woff2') format('woff2'),        url('path-to-lato.woff') format('woff'); }  /* Other @font-face declarations */

Next, we load the web fonts via JavaScript.

if ('fonts' in document) {   Promise.all([     document.fonts.load('1em Lato'),     document.fonts.load('700 1em Lato'),     document.fonts.load('italic 1em Lato'),     document.fonts.load('italic 700 1em Lato')   ]).then(_ => () {     document.documentElement.classList.add('fonts-loaded')   }) }

Then we want to change the body text to the web font via CSS:

/* System font before webfont is loaded */ body {   font-family: sans-serif; }  /* Use webfont when it loads */ .fonts-loaded body {   font-family: Lato,  sans-serif; }

Finally, we skip font loading for repeat visits.

if ('fonts' in document) {   Promise.all([     document.fonts.load('1em Lato'),     document.fonts.load('700 1em Lato'),     document.fonts.load('italic 1em Lato'),     document.fonts.load('italic 700 1em Lato')   ]).then(_ => () {     document.documentElement.classList.add('fonts-loaded')   }) }

CSS Font Loader API vs. FontFaceObserver

The CSS Font Loader API has pretty good browser support, but it’s a pretty cranky API.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

Chrome Firefox IE Edge Safari
35 41 No 79 10

Mobile / Tablet

Android Chrome Android Firefox Android iOS Safari
88 85 81 10.0-10.2

So, if you need to support older browsers (like IE 11 and below), or if you find the API weird and unwieldy, you might want to use Bramstein’s FontFaceObserver. It’s super lightweight so there’s not much harm.

The code looks like this. (It’s much nicer compared to the CSS Font Loader API).

new FontFaceObserver('lato')   .load()   .then(_ => {     document.documentElement.classList.add('fonts-loaded')   })

Make sure to use fontfaceobserver.standalone.js if you intend to it to load fonts on browsers that don’t support Promises.

FOFT

FOFT means flash of faux text. The idea here is we split font loading into three stages:

  • Step 1: Use fallback font when web fonts aren’t loaded yet.
  • Step 2: Load the Roman (also called “book” or “regular”) version of the font first. This replaces most of the text. Bold and italics will be faked by the browser (hence “faux text”).
  • Step 3: Load the rest of the fonts

Note: Zach also calls this Standard FOFT.

Using Standard FOFT

First, we load the Roman font.

@font-face {   font-family: LatoInitial;   src: url('path-to-lato.woff2') format('woff2'),        url('path-to-lato.woff') format('woff');   unicode-range: U+65-90, U+97-122; }  .fonts-loaded-1 body {   font-family: LatoInitial; }
if('fonts' in document) {   document.fonts.load("1em LatoInitial")     .then(_ => {       document.documentElement.classList.add('fonts-loaded-1')     }) }

Then, we load other fonts.

Pay attention here: we’re loading Lato again, but this time, we set font-family to Lato instead of LatoInitial.

/* Load this first */ @font-face {   font-family: LatoInitial;   src: url('path-to-lato.woff2') format('woff2'),        url('path-to-lato.woff') format('woff');   unicode-range: U+65-90, U+97-122; }  /* Load these afterwards */ @font-face {   font-family: Lato;   src: url('path-to-lato.woff2') format('woff2'),        url('path-to-lato.woff') format('woff');   unicode-range: U+65-90, U+97-122; }  /* Other @font-face for different weights and styles*/  .fonts-loaded-1 body {   font-family: LatoInitial; }  .fonts-loaded-2 body {   font-family: Lato; }
if ('fonts' in document) {   document.fonts.load('1em LatoInitial')     .then(_ => {       document.documentElement.classList.add('fonts-loaded-1')        Promise.all([         document.fonts.load('400 1em Lato'),         document.fonts.load('700 1em Lato'),         document.fonts.load('italic 1em Lato'),         document.fonts.load('italic 700 1em Lato')       ]).then(_ => {         document.documentElement.classList.add('fonts-loaded-2')       })     }) }

Again, we can optimize it for repeat views.

Here, we can add fonts-loaded-2 to the <html> straightaway since fonts are already loaded.

function loadFonts () {   if (sessionStorage.fontsLoaded) {     document.documentElement.classList.add('fonts-loaded-2')     return   }    if ('fonts' in document) {     document.fonts.load('1em Lato')       .then(_ => {         document.documentElement.classList.add('fonts-loaded-1')          Promise.all([           document.fonts.load('400 1em Lato'),           document.fonts.load('700 1em Lato'),           document.fonts.load('italic 1em Lato'),           document.fonts.load('italic 700 1em Lato')         ]).then(_ => {           document.documentElement.classList.add('fonts-loaded-2')            // Optimization for Repeat Views           sessionStorage.fontsLoaded = true         })       })   } }

Critical FOFT

The “critical” part comes from ‘critical css” (I believe) – where we load only essential CSS before loading the rest. We do this because it improves performance.
When it comes to typography, the only critical things are letters A to Z (both capitals and small letters). We can create a subset of these fonts with unicode-range.

When we create this subset, we can also create a separate font file with the necessary characters.

Here’s what @font-face declaration looks like:

@font-face {   font-family: LatoSubset;   src: url('path-to-optimized-lato.woff2') format('woff2'),        url('path-to-optimized-lato.woff') format('woff');   unicode-range: U+65-90, U+97-122; }

We load this subset first.

.fonts-loaded-1 body {   font-family: LatoSubset; }
if('fonts' in document) {   document.fonts.load('1em LatoSubset')     .then(_ => {       document.documentElement.classList.add('fonts-loaded-1')     }) }

And we load other font files later.

.fonts-loaded-2 body {   font-family: Lato; }
if ('fonts' in document) {   document.fonts.load('1em LatoSubset')     .then(_ => {       document.documentElement.classList.add('fonts-loaded-1')        Promise.all([         document.fonts.load('400 1em Lato'),         document.fonts.load('700 1em Lato'),         document.fonts.load('italic 1em Lato'),         document.fonts.load('italic 700 1em Lato')       ]).then(_ => {         document.documentElement.classList.add('fonts-loaded-2')       })     }) }

Again, we can optimize it for repeat views:

function loadFonts () {   if (sessionStorage.fontsLoaded) {     document.documentElement.classList.add('fonts-loaded-2')     return   }    if ('fonts' in document) {     document.fonts.load('1em LatoSubset')       .then(_ => {         document.documentElement.classList.add('fonts-loaded-1')          Promise.all([           document.fonts.load('400 1em Lato'),           document.fonts.load('700 1em Lato'),           document.fonts.load('italic 1em Lato'),           document.fonts.load('italic 700 1em Lato')         ]).then(_ => {           document.documentElement.classList.add('fonts-loaded-2')            // Optimization for Repeat Views           sessionStorage.fontsLoaded = true         })       })   } }

Critical FOFT variants

Zach proposed two additional Critical FOFT variants:

  • Critical FOFT with Data URI
  • Critical FOFT with Preload

Critical FOFT with data URIs

In this variant, we load the critical font (the subsetted roman font) with data URIs instead of a normal link. Here’s what it looks like. (And it’s scary.)

@font-face {   font-family: LatoSubset;   src: url("data:application/x-font-woff;charset=utf-8;base64,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") format("woff");   /* ... */ }

This code looks scary, but there’s no need to be scared. The hardest part here is generating the data URI, and CSS-Tricks has us covered here.

Critical FOFT with preload

In this variant, we add a link with the preload tag to the critical font file. Here’s what it looks like:

<link rel="preload" href="path-to-roman-subset.woff2" as="font" type="font/woff2" crossorigin>

We should only preload one font here. If we load more than one, we can adversely affect the initial load time.

In the strategy list, Zach also mentioned he prefers using a data URI over the preload variant. He only prefers it because browser support for preload used to be bad. Today, I feel that browser support is decent enough to choose preloading over a data URI.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

Chrome Firefox IE Edge Safari
50 85 No 79 11.1

Mobile / Tablet

Android Chrome Android Firefox Android iOS Safari
88 85 81 11.3-11.4

Final note from Zach

Chris ran this article via Zach and Zach wished he prioritized a JavaScript-free approach in his original article.

I think the article is good but I think my article that its based off of is probably a little dated in a few ways. I wish it prioritized no-JS approaches more when you’re only using one or two font files (or more but only 1 or 2 of each typeface). The JS approaches are kind of the exception nowadays I think (unless you’re using a lot of font files or a cloud provider that doesn’t support font-display: swap)

This switches the verdict a tiny bit, which I’m going to summarize in the next section.

Which font loading strategy to use?

If you use a cloud-hosted provider:

  • Use font-display: swap if the host provides it.
  • Otherwise, use FOUT with class

If you host your web fonts, you have a few choices:

  1. @font-face + font-display: swap
  2. FOUT with Class
  3. Standard FOFT
  4. Critical FOFT

Here’s how to choose between them:

  • Choose @font-face + font-display: swap if you’re starting out and don’t want to mess with JavaScript. It’s the simplest of them all. Also choose this option if you use only few font files (fewer than two files) for each typeface.
  • Choose Standard FOFT if you’re ready to use JavaScript, but don’t want to do the extra work of subsetting the Roman font.
  • Choose a Critical FOFT variant if you want to go all the way for performance.

That’s it! I hope you found all of this useful!

If you loved this article, you may like other articles I wrote. Consider subscribing to my newsletter 😉. Also, feel free to reach out to me if you have questions. I’ll try my best to help!


The post The Best Font Loading Strategies and How to Execute Them appeared first on CSS-Tricks.

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

CSS-Tricks

, , , , ,
[Top]

The “Gray Dead Zone” of Gradients

Erik D. Kennedy notes an interesting phenomenon of color gradients. If you have a gradient between two colors where the line between them in the color space goes through the zero-saturation middle, you get this “gray dead zone” in the middle.

It’s a real thing. See the gray middle here:

You can also see how colors might not do that, like red and blue here shooting right through purple instead, which you can visualize on that color circle above.

Erik says one solution can be taking a little detour instead of going straight through the gray zone:

His updated gradient tool deals with this by using different “interpolation modes” and easing the gradient with a choice of precision stops. Don’t miss the radial and conic options as well, with the ability to place the centers “offscreen” which can yield pretty cool looks you can’t do any other way.

Oh and speaking of conic gradients, Adam Argyle has a little gallery of possibilities that is pretty unique.


The post The “Gray Dead Zone” of Gradients appeared first on CSS-Tricks.

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

CSS-Tricks

, , ,
[Top]