Web Standards

My petite-vue review

Css Tricks - Fri, 07/23/2021 - 5:10am

Dave:

petite-vue is a new cut of the Vue project specifically built with progressive enhancement in mind. At 5kb, petite-vue is a lightweight Alpine (or jQuery) alternative that can be “sprinkled” over your project requiring no extra bundling steps or build processes. Add a <script> tag, set a v-scope, and you’re off to the races. This is up my alley.

Lots of us are still fond of jQuery, but didn’t like how fragile things could be, entirely separating interactive features and the HTML. “Separation of concerns” felt right at the time, but was ultimately too dogmatic. Authoring languages like JSX felt wrong at first, and now feel rather right, and a lot of JavaScript templating has fell in line. But heavy frameworks tend to be involved. Framework-free approaches began to show up, like Alpine.js, which allow us to sprinkle in interactive technology right into the HTML. Vue has always been sprinkable, to a degree, but now much moreso with petite-vue.

Direct Link to ArticlePermalink

The post My petite-vue review appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Organize your CSS declarations alphabetically

Css Tricks - Thu, 07/22/2021 - 12:33pm

Eric, again not mincin’ no words with blog post titles. This is me:

The most common CSS declaration organization technique I come across is none whatsoever.

Almost none, anyway. I tend to group them by whatever dumps out of my brain as I’m writing them, which usually ends up with somewhat logical groups, like box model stuff grouped together and color stuff grouped together. It just… hasn’t mattered to me. But that is strongly influenced by typically working on small teams or alone. Eric recommends the alphabetical approach because:

[…] it imposes a baseline sense of structure across a team. This ask is usually enough, especially if it means cleaning up what’s come before.

And his (probably bigger) point is that the imparted structure helps legitimize CSS in a world where CSS skills are undervalued. Not going to argue against that, but I would argue that hand-alphabetizing CSS on an existing project is very likely not a good use of time. Worse, it might break stuff if done blindly, which is why Prettier punted on it. If you and your team agree this is a good idea, I’d find a way to get this into an on-save function in your code editor and make it a pre-commit hook. Alphabetizing is a task for computers to do and the output can be verified as you are authoring.

Direct Link to ArticlePermalink

The post Organize your CSS declarations alphabetically appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Using Google Drive as a CMS

Css Tricks - Thu, 07/22/2021 - 4:44am

We’re going to walk through the technical process of hooking into Google Drive’s API to source content on a website. We’ll examine the step-by-step implementation, as well as how to utilize server-side caching to avoid the major pitfalls to avoid such as API usage limits and image hotlinking. A ready-to-use npm package, Git repo, and Docker image are provided throughout the article.

But… why?

At some point in the development of a website, a crossroads is reached: how is content managed when the person managing it isn’t technically savvy? If the content is managed by developers indefinitely, pure HTML and CSS will suffice — but this prevents wider team collaboration; besides, no developer wants to be on the hook for content updates in perpetuity.

So what happens when a new non-technical partner needs to gain edit access? This could be a designer, a product manager, a marketing person, a company executive, or even an end customer.

That’s what a good content management system is for, right? Maybe something like WordPress. But this comes with its own set up of disadvantages: it’s a new platform for your team to juggle, a new interface to learn, and a new vector for potential attackers. It requires creating templates, a format with its own syntax and idiosyncrasies. Custom or third-party plugins may need to be to vetted, installed, and configured for unique use cases — and each of these is yet another source of complexity, friction, technical debt, and risk. The bloat of all this setup may end up cramping your tech in a way which is counterproductive to the actual purpose of the website.

What if we could pull content from where it already is? That’s what we’re getting at here. Many of the places where I have worked use Google Drive to organize and share files, and that includes things like blog and landing page content drafts. Could we utilize Google Drive’s API to import a Google Doc directly into a site as raw HTML, with a simple REST request?

Of course we can! Here’s how we did it where I work.

What you’ll need

Just a few things you may want to check out as we get started:

Authenticating with the Google Drive API

The first step is to establish a connection to Google Drive’s API, and for that, we will need to do some kind of authentication. That’s a requirement to use the Drive API even if the files in question are publicly shared (with “link sharing” turned on). Google supports several methods of doing this. The most common is OAuth, which prompts the user with a Google-branded screen saying, “[So-and-so app] wants to access your Google Drive” and waits for user consent — not exactly what we need here, since we’d like to access files in a single central drive, rather than the user’s drive. Plus, it’s a bit tricky to provide access to only particular files or folders. The https://www.googleapis.com/auth/drive.readonly scope we might use is described as:

See and download all your Google Drive files.

That’s exactly what it says on the consent screen. This is potentially alarming for a user, and more to the point, it is a potential security weakness on any central developer/admin Google account that manages the website content; anything they can access is exposed through the site’s CMS back end, including their own documents and anything shared with them. Not good!

Enter the “Service account”

Instead, we can make use of a slightly less common authentication method: a Google service account. Think of a service account like a dummy Google account used exclusively by APIs and bots. However, it behaves like a first-class Google account; it has its own email address, its own tokens for authentication, and its own permissions. The big win here is that we make files available to this dummy service account just like any other user — by sharing the file with the service account’s email address, which looks something like this:

google-drive-cms-example@npm-drive-cms.iam.gserviceaccount.com

When we go to display a doc or sheet on the website, we simply hit the “Share” button and paste in that email address. Now the service account can see only the files or folders we’ve explicitly shared with it, and that access can be modified or revoked at any time. Perfect!

Creating a service account

A service account can be created (for free) from the Google Cloud Platform Console. That process is well documented in Google’s developer resources, and in addition it’s described in step-by-step detail in the companion repo of this article on GitHub. For the sake of brevity, let’s fast-forward to immediately after a successful authentication of a service account.

The Google Drive API

Now that we’re in, we’re ready to start tinkering with what the Drive API is capable of. We can start from a modified version of the Node.js quickstart sample, adjusted to use our new service account instead of client OAuth. That’s handled in the first several methods of the driveAPI.js we are constructing to handle all of our interactions with the API. The key difference from Google’s sample is in the authorize() method, where we use an instance of jwtClient rather than the oauthClient used in Google’s sample:

authorize(credentials, callback) { const { client_email, private_key } = credentials; const jwtClient = new google.auth.JWT(client_email, null, private_key, SCOPES) // Check if we have previously stored a token. fs.readFile(TOKEN_PATH, (err, token) => { if (err) return this.getAccessToken(jwtClient, callback); jwtClient.setCredentials(JSON.parse(token.toString())); console.log('Token loaded from file'); callback(jwtClient); }); } Node.js vs. client-side

One more note about the setup here — this code is intended to be called from server-side Node.js code. That’s because the client credentials for the service account must be kept secret, and not exposed to users of our website. They are kept in a credentials.json file on the server, and loaded via fs.readFile inside of Node.js. It’s also listed in the .gitignore to keep the sensitive keys out of source control.

Fetching a doc

After the stage is set, loading raw HTML from a Google Doc becomes fairly simple. A method like this returns a Promise of an HTML string:

getDoc(id, skipCache = false) { return new Promise((resolve, reject) => { this.drive.files.export({ fileId: id, mimeType: "text/html", fields: "data", }, (err, res) => { if (err) return reject('The API returned an error: ' + err); resolve({ html: this.rewriteToCachedImages(res.data) }); // Cache images this.cacheImages(res.data); }); }); }

The Drive.Files.export endpoint does all the work for us here. The id we’re passing in is just what shows up in your browsers address bar when you open the doc, which is shown immediately after https://docs.google.com/document/d/.

Also notice the two lines about caching images — this is a special consideration we’ll skip over for now, and revisit in detail in the next section.

Here’s an example of a Google document displayed externally as HTML using this method.

Fetching a sheet

Fetching Google Sheets is almost as easy using Spreadsheets.values.get. We adjust the response object just a little bit to convert it to a simplified JSON array, labeled with column headers from the first row of the sheet.

getSheet(id, range) { return new Promise((resolve, reject) => { this.sheets.spreadsheets.values.get({ spreadsheetId: id, range: range, }, (err, res) => { if (err) reject('The API returned an error: ' + err); // console.log(res.data.values); const keys = res.data.values[0]; const transformed = []; res.data.values.forEach((row, i) => { if(i === 0) return; const item = {}; row.forEach((cell, index) => { item[keys[index]] = cell; }); transformed.push(item); }); resolve(transformed); }); }); }

The id parameter is the same as for a doc, and the new range parameter here refers to a range of cells to fetch values from, in Sheets A1 notation.

Example: this Sheet is read and parsed in order to render custom HTML on this page.

…and more!

These two endpoints already get you very far, and forms the backbone of a custom CMS for a website. But, in fact, it only taps the surface of Drive’s potential for content management. It’s also capable of:

  • listing all files in a given folder and display them in a menu,
  • importing complex media from a Google Slides presentation, and
  • downloading and caching custom files.

The only limits here are your creativity, and the constraints of the full Drive API documented here.

Caching

As you’re playing with the various kinds of queries that the Drive API supports, you may end up receiving a “User Rate Limit Exceeded” error message . It’s fairly easy to hit this limit through repeated trial-and-error testing during the development phase, and at first glance, it seems as if it would represent a hard blocker for our Google Drive-CMS strategy.

This is where caching comes in — every time we fetch a new version of any file on Drive, we cache it locally (aka server-side, within the Node.js process). Once we do that, we only need to check the version of every file. If our cache is out of date, we fetch the newest version of the corresponding file, but that request only happens once per file version, rather than once per user request. Instead of scaling by the number of people who use the website, we can now scale by the number of updates/edits on Google Drive as our limiting factor. Under the current Drive usage limits on a free-tier account, we could support up to 300 API requests per minute. Caching should keep us well within this limit, and it could be optimized even further by batching multiple requests.

Handling images

The same caching method is applied to images embedded inside Google Docs. The getDoc method parses the HTML response for any image URLs, and makes a secondary request to download them (or fetches them directly from cache if they’re already there). Then it rewrites the original URL in the HTML. The result is that static HTML; we never use hotlinks to Google image CDNs. By the time it gets to the browser, the images have already been pre-cached.

Respectful and responsive

Caching ensures two things: first, that we are being respectful of Google’s API usage limits, and truly utilize Google Drive as a front end for editing and file management (what the tool is intended for), rather than leaching off of it for free bandwidth and storage space. It keeps our website’s interaction with Google’s APIs to the bare minimum necessary to refresh the cache as needed.

The other benefit is one that the users of our website will enjoy: a responsive website with minimal load times. Since cached Google Docs are stored as static HTML on our own server, we can fetch them immediately without waiting for a third-party REST request to complete, keeping website load times to a minimum.

Wrapping in Express

Since all this tinkering has been in server-side Node.js, we need a way for our client pages to interact with the APIs. By wrapping the DriveAPI into its own REST service, we can create a middleman/proxy service which abstracts away all the logic of caching/fetching new versions, while keeping the sensitive authentication credentials safe on the server side.

A series of express routes, or the equivalent in your favorite web server, will do the trick, with a series of routes like this:

const driveAPI = new (require('./driveAPI'))(); const express = require('express'); const API_VERSION = 1; const router = express.Router(); router.route('/getDoc') .get((req, res) => { console.log('GET /getDoc', req.query.id); driveAPI.getDoc(req.query.id) .then(data => res.json(data)) .catch(error => { console.error(error); res.sendStatus(500); }); }); // Other routes included here (getSheet, getImage, listFiles, etc)... app.use(`/api/v${API_VERSION}`, router);

See the full express.js file in the companion repo.

Bonus: Docker Deployment

For deployment to production, we can can run the Express server alongside your existing static web server. Or, if it’s convenient, we could easily wrap it in a Docker image:

FROM node:8 # Create app directory WORKDIR /usr/src/app # Install app dependencies # A wildcard is used to ensure both package.json AND package-lock.json are copied # where available (npm@5+) COPY package*.json ./ RUN npm install # If you are building your code for production # RUN npm ci --only=production # Bundle app source COPY . . CMD [ "node", "express.js" ]

…or use this pre-built image published on Docker Hub.

Bonus 2: NGINX Google OAuth

If your website is public-facing (accessible by anyone on the internet), then we’re done! But for our purposes where I work at Motorola, we are publishing an internal-only documentation site that needs additional security. That means Link Sharing is turned off on all our Google Docs (they also happened to be stored in an isolated and dedicated Google Team Drive separated from all other company content).

We handled this additional layer of security as early as possible at the server level, using NGINX to intercept and reverse-proxy all requests before they even make it to the Express server or any static content hosted by the website. For this, we use Cloudflare’s excellent Docker image to present a Google sign-on screen to all employees accessing any website resources or endpoints (both the Drive API Express server and the static content alongside it). It seamlessly integrates with the corporate Google account and single-sign-on they already have — no extra account needed!

Conclusion

Everything we just covered in this article is exactly what we’ve done where I work. It’s a lightweight, flexible, and decentralized content management architecture, in which the raw data lives where Google Drive, where our team already works, using a UI that’s already familiar to everyone. It all gets tied together into the website’s front end which retains the full flexibility of pure HTML and CSS in terms of control over presentation, and with minimal architectural constraints. A little extra legwork from you, the developer, creates a virtually seamless experience for both your non-dev collaborators and your end users alike.

Will this sort of thing work for everyone? Of course not. Different sites have different needs. But if I were to put together a list of use cases for when to use Google Drive as a CMS, it would look something like this:

  • An internal site with between a few hundred to a few thousand daily users — If this had been the front page of the global company website, even a single request for file version metadata per user might approach that Drive API usage limit. Further techniques could help mitigate that — but it’s the best fit for small to medium-size websites.
  • A single-page app — This setup has allowed us to query the version numbers of every data source in a single REST request, one time per session, rather than one time per page. A non-single-page app could use the same approach, perhaps even making use of cookies or local storage to accomplish the same “once per visit” version query, but again, it would take a little extra legwork.
  • A team that’s already using Google Drive — Perhaps most important of all, our collaborators were pleasantly surprised that they could contribute to the website using an account and workflow they already had access to and were comfortable using, including all of the refinements of Google’s WYSIWYG experience, powerful access management, and the rest.

The post Using Google Drive as a CMS appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Hashnode: A Blogging Platform for Developers

Css Tricks - Thu, 07/22/2021 - 4:39am

Hashnode is a free developer blogging platform. Say you’ve just finished an ambitious project and want to write about 10 important lessons you’ve learned as a developer during it. You should definitely blog it—I love that kind of blog post, myself. Making a jump into the technical debt of operating your own blog isn’t a small choice, but it’s important to own your own content. With Hashnode, the decision gets a lot easier. You can blog under a site you entirely own, and at the same time, reap the benefits of hosted software tailor-made for developer blogging and be part of a social center around developer writing.

Here are some things, technologically, I see and like:

  • Write in Markdown. I’m not sure I’ve ever met a developer who didn’t prefer writing in
    Markdown.
  • Its not an “own your content” as in theoretically you could export content. Your content is in your GitHub repo. You wanna migrate it later? Go for it.
  • Your site, whether at your own custom domain or at a free subdomain, is hosted, CDN-backed, and SSL secured, while being customizable to your own style.
  • Developer specific features are there, like syntax highlighting for your code.
  • You get to be part of on-site community as well as a behind-the-scenes Discord community.
  • Your blog is highly optimized for performance, accessibility, and SEO. You’ll be hitting 100’s on Lighthouse reports, which is no small feat.

Your future biggest fans are there waiting for you ;).

Example of my personalized Hashnode newsletter with the best stuff from my feed.

The team over there isn’t oblivious to the other hosted blogging platforms out there. We’ve all seen programming blog posts on Medium, right? They tend to be one-offs in my experience. Hashnode is a Medium-alternative for developers. Medium just doesn’t cater particularly well to the developer audience. Plus you never know when your content will end up being behind a random paywall, a mega turn-off to fellow developers just trying to learn something. No ads or paywalls on Hashnode, ever.

The smart move, I’d say, is buying a domain name to represent yourself right away. I think that’s a super valuable stepping stone in all developer journeys. Then hook it up to Hashnode. Then wherever you do from that day forward, you are building domain equity there. You’re never going to regret that. That domain strength belongs entirely to you forever. Not to mention Medium wants $50/year to map a domain and DEV doesn’t let you do it at all.

But building your own site can be a lonely experience at first. The internet is a big place and you’ll be a small fish and first. By starting off at Hashnode, it’s like having a cheat code for being a much bigger fish right on day one.

DEV is out there too being a developer writing hub, but they don’t allow you to host your own site and build your own domain equity, as Hashnode does, or customize it to your liking as deeply.

Hashnode is built by developers, for developers, for real. Blogging for devs! The team there is very interested and receptive to your feature requests—so hit them up!

One more twist here that you might just love.

Hashnode Sponsors is a new way your fans can help monetize your blog directly, and Hashnode doesn’t take a cut of it at all.

Explore Hashnode Today

The post Hashnode: A Blogging Platform for Developers appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

How to Use Emotional Content For Your E-Commerce Site

Usability Geek - Wed, 07/21/2021 - 11:49pm
You may have the fastest e-commerce site on the web or the most user-friendly interface. Still, to optimize your conversion, you need to focus on more than the technical aspects of your offerings....
Categories: Web Standards

Yet Another Mobile Context Menu With No Indication it Can Scroll

Css Tricks - Wed, 07/21/2021 - 11:08am

Remember Tyler Hall’s personal story of a UX moment where the popup sharing context menu on iOS had no visible indication that the content inside was scrollable? The thing his mom wanted to do seemed impossible because it was out of view. iOS isn’t alone here — Terence Eden documents essentially the same problem on Android:

I tried sharing a website using Google Chrome for Android. I hit the share button, and a panel popped-up from the bottom of the screen.

Hmmm. It didn’t have the share destination that I wanted. It was early in the morning – when I’m not at my cognitive best – and I was stumped. There is nothing on this screen – other than the icons – to tell me how I can interact with it. There’s no scrollbar, no handle, no “more” icon, nothing.

I would think even just fairly subtle “scroll shadows” would go a long way in both cases, but some serious user testing should be in order here.

The post Yet Another Mobile Context Menu With No Indication it Can Scroll appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

ECMAScript proposal: JSON modules

Css Tricks - Wed, 07/21/2021 - 11:08am

Dr. Axel Rauschmayer looks at JSON modules, which is already live in Chrome 91 (but nothing else). It looks just like an ES Modules-style import, only you asset the type at the end.

import configData from './config-data.json' assert {type: 'json'};

How nice is that? Once this makes its way across browsers, we’ve gone on a journey from “you’ll almost definitely want to use an Ajax library” because of the cross-browser complexity and weirdness of XMLHttpRequest to the much nicer (but you still gotta write some code) fetch API, to a one-liner (if what you need is JSON data).

Snagging some JSON data seems like it should be as easy as a one-liner to me, and now it is. I like how the URL can be dynamic now too.

Direct Link to ArticlePermalink

The post ECMAScript proposal: JSON modules appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

A Step-By-Step Process for Turning Designs Into Code

Css Tricks - Wed, 07/21/2021 - 4:37am

Turning website design files into a combination of HTML, CSS and JavaScript is the bread and butter of many front-end web development jobs, but there’s a part of this work that doesn’t neatly fit in to tutorials on any specific topic. There’s a process of breaking down a design and figuring out how to approach the build that seems to fall under on-the-job training for new front-end developers. It’s not something taught alongside core technologies (no matter where we are learning those technologies—college, bootcamp, or on our own).

In this post, we’ll take a look at how to go from design to code, and why you might want to follow a process like this instead of just diving into code head-first—which, let’s face it, we love to do! The contents of this post are based on my experiences onboarding new developers, the kinds of conversations we’ve had, and the help and feedback I’ve provided in those situations.

One reason the process of moving from design to code is a core professional skill for a front-end developer is that without having some way to dig in and predict how you will approach something, it’s very difficult to provide an estimate for how long it takes to make or what questions you might need answered before you start. Many designs might appear simple at first glance, but quickly become complex once you get into the weeds. I’ve seen this lead to overpromises, which often leads to tighter deadlines, stress and even worse side effects. Suddenly everything takes much longer than we initially thought. Yuck. Let’s see if we can avoid that.

Evaluating a design

As a way to talk about this, let’s use an example design for a “marketing-style” web page and assume we have been asked to implement it. We can also assume this site is created in a context where marketing professionals may come in and edit the content via some content management system (CMS), re-order the sections, replace images, and make style changes. So we need to decide on the components of the page that will be the building blocks used in the CMS.

This gets at another reason that this can be missed in education: often in our own solo projects, we can have static content right there in the HTML, and component parts aren’t going to be Frankenstein-ed together by strangers to form whole new pages and sections. But once you step into more real-world dev situations, things are a lot more dynamic, and we are often working at the layer of “make things that a non-developer can use to make a web page.”

Design made by and courtesy of my friend, Dan Ott

Let’s use this website for a clinical trial is example. As we can see there are a lot of familiar design elements. Marketing sites tend to share common patterns:

  • a big hero section
  • product images
  • small separate sections of short-form content emphasizing one thing or another
  • information about the company
  • etc.

On mobile, we can take it as a given that in each section, the left columns will stack on top of the right, and some other fairly typical reflow will happen. Nothing structural will change on mobile. So what we are looking at is the core of the design.

In this example, there is a header, then a lot of distinct sections, and a footer. At a glance, some of the sections look kind of similar—several have a two-column layout, for example. There are button and heading styles that seem to be consistent throughout. As soon as you take a look at something like this, your eye will start to notice repeated patterns like that.

This is where we start making notes. Before we do any coding, let’s understand the ideas contained in the design. These ideas can fall into a few buckets, and we want our final product—the web page itself—to correctly represent all these ideas. Here are the buckets I commonly use:

  1. Layout-level patterns—repeating layout ideas and the overall grid
  2. Element-level patterns—headings, text sizes, fonts, spacing, icons, button sizes
  3. Color palette
  4. Structural ideas—the logical organization of sections, independent from the layout
  5. Everything else—ideas that are only present in one component

Documenting the patterns this way comes in handy for figuring out our CSS approach, but also for choosing what HTML elements to use and starting conversations with the designer or other stakeholders if something is unclear. If you have access to the source files for the design, sections and layers might be labelled in there that give you a good idea what the designer was thinking. This can be helpful when you want to talk about specific sections.

So let’s look at the ideas contained in our example design. We’re going to do several passes through the design, and in all of them, we’re going outside-in, top-to-bottom, and left-to-right to ensure we evaluate every element. In each of the five passes, we’re looking for stuff that goes into just one of the buckets.

We’re unconcerned with getting this list perfect; it can always be changed later—we just want to record our first impressions.

Pass 1: Layout-level ideas

In this design we have a few layout ideas that stand out right off the bat.

  • A header with a horizontal nav section
  • A main content column within the content area—left and right edges align within all sections from header to footer
  • Sections with two columns
  • Sections with a single centered column
  • Sections with a single left-aligned column
  • A footer with three columns
  • Fairly generous padding inside each section
First impressions

We should note any other first impressions we have during this first pass, good or bad. We can never have a first impression twice, and some of our gut reactions and questions can be forgotten if we neglect noting them now. Plus, identifying specific stuff that you like in the design can be nice when we get to talking with the designer. It both helps to celebrate the good stuff and mix it in with other constructive criticism.

Our first impressions might be things like:

  • &#x1f44d; The design is clean-looking and readable.
  • &#x1f44d; The sections are all titled by questions (good, helps draw reader in and gives each section a clear purpose).
  • &#x1f928; Question marks are used inconsistently in the titles (possibly just an oversight?).
  • &#x1f64b;‍♀️ Sometimes there are very similar font sizes right next to each other (may need to follow up to see if this is intentional because it seems a less slick and professional than the rest of the site).
  • &#x1f44d; The logo is nice with that little gradient going on.
Pass 2: Element-level ideas

Here are things we might notice in this second pass:

  • Primary (blue) and Secondary (white) button styles, plus a “Learn more” button in the header with a little arrow (an expanding menu maybe?)
  • Heading and sub-heading styles
  • Three “body text” sizes (16px, 18px, 20px)
  • A “dark-mode” section where text color is white and the background is dark
  • A consistent presentation of “image & caption” sets
  • Custom bullet points of various kinds
  • Inline links in the text are underlined and, other links, like those in the footer, are not.
  • A repeated card component with an icon on top, and a heading and a list inside the card
  • The logo repeats a few times in different contexts/sizes.
  • The footer contains uppercase headings that don’t appear elsewhere.
Pass 3: Color palette

There is not too much going on in this design color-wise.

  • blue/purple primary color
  • light/dark body text colors
  • light/dark link colors
  • nice gradient under the word “hope” in the logo
  • light gray background color
  • dark navy background color
  • specific red and green “checkmark” and “stop” icon colors

Some design tools let you export the color hex values used in the design file, or even full-on Sass or CSS variable declarations. If you have that option, use it. Otherwise, find your own way to grab those values and name them because those are the foundation for lots of our initial CSS work.

Throughout our CSS and other code, we want to be refer to colors with labels or classes like “primary” and “secondary” that we can reuse throughout the code. This makes it easier to adjust values in the future, and to add themes down the line if we ever need to.

Pass 4: Structural ideas

This is where we might actually name the regions of the page in ways that make sense to us, and identify the hierarchy of the content. We can start to understand the accessibility needs of our implementation by documenting in plain language what we see as the nature and structure of the content in the page. As always, going outside-in, top-to bottom, left-to-right as we make our evaluations.

Focusing on structure helps us figure out the underlying patterns that eventually become our components, and also helps us understand the way we want people who use assistive technology to perceive the content. In turn, that guides us as far as what HTML elements we need to use to write semantic HTML. Semantic HTML speaks to the nature and structure of the content so that it can be perceived correctly by browsers. Browsers use our HTML to create the accessibility tree that assistive tech, like screen readers, uses to present the page. They need a strong outline for that to succeed and semantic HTML provides that solid structure.

This means we need to understand the nature of what’s on the page well enough that we could explain it verbally with no visual support if we had to. From that understanding, we can work backwards to figure out the correct HTML that expresses this understanding via the accessibility tree, which can be inspected in you browser’s developer tools.

Here’s a quick example of the accessibility tree in Chrome if everything on the page is a div, and if elements are correctly chosen to match the nature of the content. Determining the best element choice in a given situation is outside the scope of this post, but suffice it to say that the expressive, non-”generic generic generic” accessibility tree below is entirely created with HTML elements and attributes, and makes the content and its organization much easier for somebody to perceive using assistive technology.

So, in this fourth pass, here are notes we might make:

  • Top-level structure:
    • Header
    • Main Content
    • Footer

Not so bad for the first top-to-bottom pass. Let’s go a level deeper. We’re still unconcerned with the child inside elements of the sections themselves yet—we want just enough info to label the top level items inside each sections.

  • Within Header there is:
    • Home link
    • Navigation section
  • Within Main Content there is:
    • Hero section
    • Short explainer about the disease itself
    • Explainer about the treatment
    • Intro to the trial
    • Explainer with more details about the trial
    • Statement about who can join the study
    • Call-to-action to participate
    • Short explainer about the company
  • Within Footer there is:
    • Logo
    • Summary Sentence
    • Some lists of links with titles
    • Divider
    • Copyright notice

This pass reveals a few things. First, the Header and Footer sections are pretty shallow and are already revealing raw elements like links and text. Second, the Main section has eight distinct subsections, each with its own topic.

We’re going to do one more pass here and get at some of the deeper details in those sections.

  • Header home link—Woohoo, it’s just a link and we’re done.
  • Header nav—This is an expanding hover nav that needs JavaScript to work correctly. There are probably lots of accessible examples to follow, but still will take more time to develop and test than if we were working with raw links.
  • Hero
    • Title
    • Column 1
      • Subtitle (we missed this in the first element-level pass)
      • Paragraph
      • Primary button link
      • Secondary button link
    • Column 2
      • Hero image
  • Disease Explainer
    • Title
    • Paragraph
    • Unordered list
    • Large text
    • Unordered list
    • Image and caption (figure and figcaption)
    • List of links
  • Treatment Explainer
    • Title
    • Column 1
      • Paragraphs
    • Column 2
      • Image and caption (figure and figcaption)
  • Trial—Intro
    • Title
    • Column 1
      • YouTube Video Player
    • Column 2
      • Paragraphs
  • Trial—More Details
    • Title
    • Subtitle
    • Cards (with Icon, Title, and List)
  • Who Can Join” statement
    • Title
    • Column 1
      • Paragraph
      • Unordered list
    • Column 2
      • Paragraph
      • Unordered list
  • Call-to-Action
    • Title
    • Paragraph
    • Secondary button link
  • About the Company
    • Title
    • Paragraph

Yowza, that got long fast! But now we understand pretty well the kinds of things we need to build, and what parts might be tricky. We also see that there may be some helper components to be built that aren’t quite represented by any one of these sections, for example, a two-column layout component that stacks to one column on mobile and has a title on top, or a generic “section container” component that takes a background and foreground color and provides the right styles plus standardized internal padding.

Incidentally, with this breakdown we’ve done a pretty good job expressing the final accessibility tree we want our HTML to create, so we are well on our way to having the implementation be a smooth experience without a lot of re-work to get accessibility right.

Pass 5: Everything else

What are some other ideas contained in this design, things that stick out, or challenges we notice? Maybe not much, but this is kind of the other side of the “first impressions” notes. Now our heads are filled with context for what it is in the design.

If something stands out now, especially if it’s a question about how something works, capture it. An example is, “Hmm, what is the ‘Learn More’ link in the nav supposed to do?” The answer might be: “It’s a list of links to each section that open when you hover there.” Every now and then, a designer expects that this kind of thing is already implied in the design, even if it is not explicitly documented, and it only comes up at the design review stage after that thing is developed—which is often too late to correct without affecting dates and deadlines.

We should also now look deeply and identify any hidden “glue work”— things like getting our styles in order, handling mobile, configuring the CMS, adding and testing authoring options and arrangements for our building blocks, and adding automated tests. Stuff like that.

At this point, we are ready to nail down exactly what components can be created in the CMS. Maybe we already have some of the basic setup done in the current system from past work. Whatever the case, we have enough to draw on to offer a decent estimate of the work needed, grouped into categories. For example, we might categorize components that:

  • are already 100% ready (no dev time needed),
  • exist but need tweaks for this new purpose (predictable dev time needed),
  • are totally new, but the path is obvious and safe (predictable dev time needed),
  • are totally new and need some research to implement. Or the design is unclear, or something about it gives you the heebie-jeebies and you need to have discussions with stakeholders. The earlier you can spot this, the better. Talk it over with whoever is running the project.

Now we have enough information to make a reasonable estimate. And more to the point, we’ve reduced the total time the project will take, and limited the trouble we might have along the way, because we’ve gained several advantages from planning it out.

The advantages of having a process

The exact steps we take and what order they are completed in is not the main point. What matters most is understanding the kind of information you need to gather when starting on a project. You might have your own ideas about how the work is done and in what order, whatever works for you is great.

Here are the advantages I’ve realized when assessing a design with a process in mind, compared to just kinda diving in, “getting stuff working,” and handling things as they come up.

You do a little extra discovery

As much as we’d like every project to arrive fully formed and perfectly ready to start, in reality, designs often contain assumptions that might be impractical to implement, or contradict something else we care about, like accessibility. In that case, you can assess the whole thing up front and get the conversations started with people who can resolve those issues early in the process. This can happen while you dive into the pieces that are ready to code, and will stop you from bumping into these roadblocks later when you are about to build that part of it. Flagging concerns early is definitely appreciated by the people who need to solve them.

You can be helped by others

Without a plan, it can be difficult to understand how far along you are in completing the project, as well as knowing if you need help meeting a deadline. Even if you do need help and are able to ask for it, it’s tough to use extra helping hands effectively without the work being broken out in to separate little chunks that be easily divided. When you have a plan that makes sense, you can quickly delegate certain tickets, knowing that the jigsaw pieces will fit together in the end.

It’s easy (and common) for a new developer to think think that huge workloads and working around the clock is a good thing. But as you mature in the role, you’ll see that having a deep understanding of the whole picture of a project, or even a single ticket, is more valuable, while creating a better impression that you are “on top of things.” Recognizing early that a timeline doesn’t look right gives you options about how to address it in ways other than trying to be a hero and throwing some weekends at it.

Component architecture flows better

Architectural decisions are the worst for me. Naming components, figuring out where stuff should live, which components need to talk to each other, where to break stuff up into smaller components. A lot of those choices only make sense when we look at the bigger picture and think about all various ways that certain elements might be used by visitors and content creators. And a lot of these choices are marginal—choosing the “best” option between two acceptable solutions can be a huge time suck or completely subjective.

Have a process helps with that because you are going to get all, or most, of the information you need about the designs before digging deeply into the development work. For me, figuring out what pieces I need to make, and figuring out the best possible code to make those pieces, are two different things. Sometimes what I need is not the thing that comes most naturally from the code. And sometimes, after learning a bit about what is needed, I can avoid time spent bikeshedding marginal decisions because it’s more clear which decisions truly don’t matter.

You still learn new things as you write the code, of course, but you’re now better prepared to handle those things when they pop up. You even have a good idea about the kinds of that might present themselves.

Styles make more sense

As you plan the work, you can truly figure out which styles are global, which are section-specific, which are or component-specific, and which are one-off exceptions. If you don’t already have a system you like for this, Andy Bell’s Cube CSS pairs very well with the kind of breakdown I’m talking about. Here’s a video of Andy working through an example with Chris Coyier that you can check out for more about this approach.

Accessibility starts early in the process

This one is huge for me. By really understanding the ideas contained in the design, you will have an easier time picking semantic HTML elements and finding appropriate accessible patterns to build out what you see there. Accessibility can work its way into the daily work you do, rather than an afterthought or extra burden. Our perspective becomes that high-quality front-end code correctly expresses the nature and structure of its content to all users, and accessibility is how we measure that.

After a pretty short period, you’ll see how often designs conform to one known pattern or another, and the flow of breaking something down into known patterns to implement will speed up dramatically. Carie Fisher nicely sums up ideas related to this “Accessibility-first” approach.

Wrapping up

Like I said at the start, a lot of this falls under on-the-job training, the “oral tradition” of web development. It’s the kind of stuff you might hear from a senior developer on your team as you’re getting started in your first front-end role. I’m sure lots of people would have different priorities than I do and recommend a slightly different process. I also know for sure that a lot of folks out there work in situations without a solid process in place, and no one senior to consult.

If you are in that situation, or not yet in your first role, I hope this gives you a baseline you can relate to when you think about how to do the job. Ideally the job is not just diving in and putting stuff in divs until things look “right” but that is often our mode of operation. We are eager to make progress and see results.

I’m very grateful that I did have somebody working with me at my first development job who showed me how to split up pieces of a design and estimate work for large, long-term projects. That’s what inspired me to start thinking about this and—as I began supervising other developers and teams—thinking about how I wanted to adapt the process and make it my own. I also realized it wasn’t something I’d noticed people talking much about when teaching technical skills about using a particular language. So thanks, Nate!

Thanks also to Drew Clements and Nerando Johnson for providing early feedback on this post. You are the best.

The post A Step-By-Step Process for Turning Designs Into Code appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Custom properties and @property

QuirksBlog - Wed, 07/21/2021 - 3:18am

You’re reading a failed article. I hoped to write about @property and how it is useful for extending CSS inheritance considerably in many different circumstances. Alas, I failed. @property turns out to be very useful for font sizes, but does not even approach the general applicability I hoped for.

Grandparent-inheriting

It all started when I commented on what I thought was an interesting but theoretical idea by Lea Verou: what if elements could inherit the font size of not their parent, but their grandparent? Something like this:

div.grandparent { /* font-size could be anything */ } div.parent { font-size: 0.4em; } div.child { font-size: [inherit from grandparent in some sort of way]; font-size: [yes, you could do 2.5em to restore the grandparent's font size]; font-size: [but that's not inheriting, it's just reversing a calculation]; font-size: [and it will not work if the parent's font size is also unknown]; }

Lea told me this wasn’t a vague idea, but something that can be done right now. I was quite surprised — and I assume many of my readers are as well — and asked for more information. So she wrote Inherit ancestor font-size, for fun and profit, where she explained how the new Houdini @property can be used to do this.

This was seriously cool. Also, I picked up a few interesting bits about how CSS custom properties and Houdini @property work. I decided to explain these tricky bits in simple terms — mostly because I know that by writing an explanation I myself will understand them better — and to suggest other possibilities for using Lea’s idea.

Alas, that last objective is where I failed. Lea’s idea can only be used for font sizes. That’s an important use case, but I had hoped for more. The reasons why it doesn’t work elsewhere are instructive, though.

Tokens and values

Let’s consider CSS custom properties. What if we store the grandparent’s font size in a custom property and use that in the child?

div.grandparent { /* font-size could be anything */ --myFontSize: 1em; } div.parent { font-size: 0.4em; } div.child { font-size: var(--myFontSize); /* hey, that's the grandparent's font size, isn't it? */ }

This does not work. The child will have the same font size as the parent, and ignore the grandparent. In order to understand why we need to understand how custom properties work. What does this line of CSS do?

--myFontSize: 1em;

It sets a custom property that we can use later. Well duh.

Sure. But what value does this custom property have?

... errr ... 1em?

Nope. The answer is: none. That’s why the code example doesn’t work.

When they are defined, custom properties do not have a value or a type. All that you ordered the browsers to do is to store a token in the variable --myFontSize.

This took me a while to wrap my head around, so let’s go a bit deeper. What is a token? Let’s briefly switch to JavaScript to explain.

let myVar = 10;

What’s the value of myVar in this line? I do not mean: what value is stored in the variable myVar, but: what value does the character sequence myVar have in that line of code? And what type?

Well, none. Duh. It’s not a variable or value, it’s just a token that the JavaScript engine interprets as “allow me to access and change a specific variable” whenever you type it.

CSS custom properties also hold such tokens. They do not have any intrinsic meaning. Instead, they acquire meaning when they are interpreted by the CSS engine in a certain context, just as the myVar token is in the JavaScript example.

So the CSS custom property contains the token 1em without any value, without any type, without any meaning — as yet.

You can use pretty any bunch of characters in a custom property definition. Browsers make no assumptions about their validity or usefulness because they don’t yet know what you want to do with the token. So this, too, is a perfectly fine CSS custom property:

--myEgoTrip: ppk;

Browsers shrug, create the custom property, and store the indicated token. The fact that ppk is invalid in all CSS contexts is irrelevant: we haven’t tried to use it yet.

It’s when you actually use the custom property that values and types are assigned. So let’s use it:

background-color: var(--myEgoTrip);

Now the CSS parser takes the tokens we defined earlier and replaces the custom property with them:

background-color: ppk;

And only NOW the tokens are read and intrepreted. In this case that results in an error: ppk is not a valid value for background-color. So the CSS declaration as a whole is invalid and nothing happens — well, technically it gets the unset value, but the net result is the same. The custom property itself is still perfectly valid, though.

The same happens in our original code example:

div.grandparent { /* font-size could be anything */ --myFontSize: 1em; /* just a token; no value, no meaning */ } div.parent { font-size: 0.4em; } div.child { font-size: var(--myFontSize); /* becomes */ font-size: 1em; /* hey, this is valid CSS! */ /* Right, you obviously want the font size to be the same as the parent's */ /* Sure thing, here you go */ }

In div.child he tokens are read and interpreted by the CSS parser. This results in a declaration font-size: 1em;. This is perfectly valid CSS, and the browsers duly note that the font size of this element should be 1em.

font-size: 1em is relative. To what? Well, to the parent’s font size, of course. Duh. That’s how CSS font-size works.

So now the font size of the child becomes the same as its parent’s, and browsers will proudly display the child element’s text in the same font size as the parent element’s while ignoring the grandparent.

This is not what we wanted to achieve, though. We want the grandparent’s font size. Custom properties — by themselves — don’t do what we want. We have to find another solution.

@property

Lea’s article explains that other solution. We have to use the Houdini @property rule.

@property --myFontSize { syntax: "<length>"; initial-value: 0; inherits: true; } div { border: 1px solid; padding: 1em; } div.grandparent { /* font-size could be anything */ --myFontSize: 1em; } div.parent { font-size: 0.4em; } div.child { font-size: var(--myFontSize); }

Now it works. Wut? Yep — though only in Chrome so far.

@property --myFontSize { syntax: ""; initial-value: 0; inherits: true; } section.example { max-width: 500px; } section.example div { border: 1px solid; padding: 1em; } div.grandparent { font-size: 23px; --myFontSize: 1em; } div.parent { font-size: 0.4em; } div.child { font-size: var(--myFontSize); } This is the grandparent This is the parent This is the child

What black magic is this?

Adding the @property rule changes the custom property --myFontSize from a bunch of tokens without meaning to an actual value. Moreover, this value is calculated in the context it is defined in — the grandfather — so that the 1em value now means 100% of the font size of the grandfather. When we use it in the child it still has this value, and therefore the child gets the same font size as the grandfather, which is exactly what we want to achieve.

(The variable uses a value from the context it’s defined in, and not the context it’s executed in. If, like me, you have a grounding in basic JavaScript you may hear “closures!” in the back of your mind. While they are not the same, and you shouldn’t take this apparent equivalency too far, this notion still helped me understand. Maybe it’ll help you as well.)

Unfortunately I do not quite understand what I’m doing here, though I can assure you the code snippet works in Chrome — and will likely work in the other browsers once they support @property.

Misson completed — just don’t ask me how.

Syntax

You have to get the definition right. You need all three lines in the @property rule. See also the specification and the MDN page.

@property --myFontSize { syntax: "<length>"; initial-value: 0; inherits: true; }

The syntax property tells browsers what kind of property it is and makes parsing it easier. Here is the list of possible values for syntax, and in 99% of the cases one of these values is what you need.

You could also create your own syntax, e.g. syntax: "ppk | <length>"

Now the ppk keyword and any sort of length is allowed as a value.

Note that percentages are not lengths — one of the many things I found out during the writing of this article. Still, they are so common that a special value for “length that may be a percentage or may be calculated using percentages” was created:

syntax: "<length-percentage>"

Finally, one special case you need to know about is this one:

syntax: "*"

MDN calls this a universal selector, but it isn’t, really. Instead, it means “I don’t know what syntax we’re going to use” and it tells browsers not to attempt to interpret the custom property. In our case that would be counterproductive: we definitely want the 1em to be interpreted. So our example doesn’t work with syntax: "*".

initial-value and inherits

An initial-value property is required for any syntax value that is not a *. Here that’s simple: just give it an initial value of 0 — or 16px, or any absolute value. The value doesn’t really matter since we’re going to overrule it anyway. Still, a relative value such as 1em is not allowed: browsers don’t know what the 1em would be relative to and reject it as an initial value.

Finally, inherits: true specifies that the custom property value can be inherited. We definitely want the computed 1em value to be inherited by the child — that’s the entire point of this experiment. So we carefully set this flag to true.

Other use cases

So far this article merely rehashed parts of Lea’s. Since I’m not in the habit of rehashing other people’s articles my original plan was to add at least one other use case. Alas, I failed, though Lea was kind enough to explain why each of my ideas fails.

Percentage of what?

Could we grandfather-inherit percentual margins and paddings? They are relative to the width of the parent of the element you define them on, and I was wondering if it might be useful to send the grandparent’s margin on to the child just like the font size. Something like this:

@property --myMargin { syntax: "<length-percentage>"; initial-value: 0; inherits: true; } div.grandparent { --myMargin: 25%; margin-left: var(--myMargin); } div.parent { font-size: 0.4em; } div.child { margin-left: var(--myMargin); /* should now be 25% of the width of the grandfather's parent */ /* but isn't */ }

Alas, this does not work. Browsers cannot resolve the 25% in the context of the grandparent, as they did with the 1em, because they don’t know what to do.

The most important trick for using percentages in CSS is to always ask yourself: “percentage of WHAT?”

That’s exactly what browsers do when they encounter this @property definition. 25% of what? The parent’s font size? Or the parent’s width? (This is the correct answer, but browsers have no way of knowing that.) Or maybe the width of the element itself, for use in background-position?

Since browsers cannot figure out what the percentage is relative to they do nothing: the custom property gets the initial value of 0 and the grandfather-inheritance fails.

Colours

Another idea I had was using this trick for the grandfather’s text colour. What if we store currentColor, which always has the value of the element’s text colour, and send it on to the grandchild? Something like this:

@property --myColor { syntax: "<color>"; initial-value: black; inherits: true; } div.grandparent { /* color unknown */ --myColor: currentColor; } div.parent { color: red; } div.child { color: var(--myColor); /* should now have the same color as the grandfather */ /* but doesn't */ }

Alas, this does not work either. When the @property blocks are evaluated, and 1em is calculated, currentColor specifically is not touched because it is used as an initial (default) value for some inherited SVG and CSS properties such as fill. Unfortunately I do not fully understand what’s going on, but Tab says this behaviour is necessary, so it is.

Pity, but such is life. Especially when you’re working with new CSS functionalities.

Conclusion

So I tried to find more possbilities for using Lea’s trick, but failed. Relative units are fairly sparse, especially when you leave percentages out of the equation. em and related units such as rem are the only ones, as far as I can see.

So we’re left with a very useful trick for font sizes. You should use it when you need it (bearing in mind that right now it’s only supported in Chromium-based browsers), but extending it to other declarations is not possible at the moment.

Many thanks to Lea Verou and Tab Atkins for reviewing and correcting an earlier draft of this article.

The Nine States of Design

Css Tricks - Tue, 07/20/2021 - 1:34pm

Here’s a really good ol’ post from way back in 2015 all about the nine states of design and how we should think all the edge cases whenever we’re building interfaces. Vince Speelman writes:

Modern UI teams are designing components first; Interfaces are merely the thoughtful composition of components. This leaves an often glaring hole for users on “the unhappy path” — The places where users may, intentionally or not, stray from your idealized flow. As we learn to craft systems rather than pages, we must invest effort into shaping these often missed states of design and create with a component lifecycle that can support everyone. Here’s the lifecycle as I see it:


  1. Nothing state
  2. Loading
  3. None
  4. One
  5. Some
  6. Too many
  7. Incorrect
  8. Correct
  9. Done

During the design process I think everyone (including me!) tends to focus on the ideal state of a component or interface, often leaving the extremely important edge cases forgotten until the last moment. I think I need to stick this list to my screen so I don’t forget it in my next project.

Direct Link to ArticlePermalink

The post The Nine States of Design appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Your Image Is Probably Not Decorative

Css Tricks - Tue, 07/20/2021 - 10:02am

Eric doesn’t mince words, especially in the title, but also in the conclusion:

In modern web design and development, displaying an image is a highly intentional act. Alternate descriptions allow us to explain the content of the image, and in doing so, communicate why it is worth including.

Just because an image displays something fanciful doesn’t mean it isn’t worth describing. Announcing its presence ensures that anyone, regardless of ability or circumstance, can fully understand your digital experience.

I like the bit where, even when a CSS background-image is used, you can still use a “spacer GIF” to add alt text. And speaking of alt descriptions, did you know even Open Graph images can have them?

Direct Link to ArticlePermalink

The post Your Image Is Probably Not Decorative appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Typewriter Animation That Handles Anything You Throw at It

Css Tricks - Tue, 07/20/2021 - 5:03am

I watched Kevin Powell’s video where he was able to recreate a nice typewriter-like animation using CSS. It’s neat and you should definitely check it out because there are bonafide CSS tricks in there. I’m sure you’ve seen other CSS attempts at this, including this site’s very own snippet.

Like Kevin, I decided to recreate the animation, but open it up to JavaScript. That way, we have a few extra tools that can make the typing feel a little more natural and even more dynamic. Many of the CSS solutions rely on magic numbers based on the length of the text, but with JavaScript, we can make something that’s capable of taking any text we throw at it.

So, let’s do that. In this tutorial, I’m going to show that we can animate multiple words just by changing the actual text. No need to modify the code every time you add a new word because JavaScript will do that for you!

Starting with the text

Let’s start with text. We are using a monospace font to achieve the effect. Why? Because each character or letter occupies an equal amount of horizontal space in a monospaced font, which will come handy when we’ll use the concept of steps() while animating the text. Things are much more predictable when we already know the exact width of a character and all characters share the same width.

We have three elements placed inside a container: one element for the actual text, one for hiding the text, and one for animating the cursor.

<div class="container"> <div class="text_hide"></div> <div class="text">Typing Animation</div> <div class="text_cursor"></div> </div>

We could use ::before and ::after pseudo-elements here, but they aren’t great for JavaScript. Pseudo-elements are not part of the DOM, but instead are used as extra hooks for styling an element in CSS. It’d be better to work with real elements.

We’re completely hiding the text behind the .text_hide element. That’s key. It’s an empty div that stretches the width of the text and blocks it out until the animation starts—that’s when we start to see the text move out from behind the element.

In order to cover the entire text element, position the .text_hide element on top of the text element having the same height and width as that of the text element. Remember to set the background-color of the .text_hide element exactly same as that of the background surrounding the text so everything blends in together.

.container { position: relative; } .text { font-family: 'Roboto Mono', monospace; font-size: 2rem; } .text_hide { position: absolute; top: 0; bottom: 0; left: 0; right: 0; background-color: white; } The cursor

Next, let’s make that little cursor thing that blinks as the text is being typed. We’ll hold off on the blinking part for just a moment and focus just on the cursor itself.

Let’s make another element with class .text_cursor. The properties are going to be similar to the .text_hide element with a minor difference: instead of setting a background-color, we will keep the background-color transparent (since its technically unnecessary, then add a border to the left edge of the new .text_cursor element.

.text_cursor{ position: absolute; top: 0; bottom: 0; left: 0; right: 0; background-color: transparent; border-left: 3px solid black; }

Now we get something that looks like a cursor that’s ready to move as the text moves:

JavaScript animation

Now comes the super fun part—let’s animate this stuff with JavaScript! We’ll start by wrapping everything inside a function called typing_animation().

function typing_animation(){ // code here } typing_animation();

Next task is to store each and every character of text in a single array using the split() method. This divides the string into a substring that has only one character and an array containing all the substrings is returned.

function typing_animation(){ let text_element = document.querySelector(".text"); let text_array = text_element.innerHTML.split(""); }

For example, if we take “Typing Animation” as a string, then the output is:

We can also determine the total number of characters in the string. In order to get just the words in the string, we replace split("") with split(" "). Note that there is a difference between the two. Here, " " acts as a separator. Whenever we encounter a single space, it will terminate the substring and store it as an array element. Then the process goes on for the entire string.

function typing_animation(){ let text_element = document.querySelector(".text"); let text_array = text_element.innerHTML.split(""); let all_words = text_element.innerHTML.split(" "); }

For example, for a string ‘Typing Animation’, the output will be,

Now, let’s calculate the length of the entire string as well as the length of each and every individual word.

function typing_animation() { let text_element = document.querySelector(".text"); let text_array = text_element.innerHTML.split(""); let all_words = text_element.innerHTML.split(" "); let text_len = text_array.length; const word_len = all_words.map((word) => { return word.length; }); }

To get the length of the entire string, we have to access the length of the array containing all the characters as individual elements. If we’re talking about the length of a single word, then we can use the map() method, which accesses one word at a time from the all_words array and then stores the length of the word into a new array called word_len. Both the arrays have the same number of elements, but one contains the actual word as an element, and the other has the length of the word as an element.

Now we can animate! We’re using the Web Animation API because we’re going with pure JavaScript here—no CSS animations for us in this example.

First, let’s animate the cursor. It needs to blink on and off infinitely. We need keyframes and animation properties, both of which will be stored in their own JavaScript object. Here are the keyframes:

document.querySelector(".text_cursor").animate([ { opacity: 0 }, { opacity: 0, offset: 0.7 }, { opacity: 1 } ], cursor_timings);

We have defined three keyframes as objects which are stored in an array. The term offset: 0.7 simply means that after 70% completion of the animation, the opacity will transition from 0 to 1.

Now, we have to define the animation properties. For that, let’s create a JavaScript object that holds them together:

let cursor_timings = { duration: 700, // milliseconds (0.7 seconds) iterations: Infinity, // number of times the animation will work easing: 'cubic-bezier(0,.26,.44,.93)' // timing-function }

We can give the animation a name, just like this:

let animation = document.querySelector(".text_cursor").animate([ // keyframes ], //properties);

Here’s a demo of what we have done so far:

CodePen Embed Fallback

Great! Now, let’s animate the .text_hide element that, true to its name, hides the text. We define animation properties for this element:

let timings = { easing: `steps(${Number(word_len[0])}, end)`, delay: 2000, // milliseconds duration: 2000, // milliseconds fill: 'forwards' }

The easing property defines how the rate of animation will change over time. Here, we have used the steps() timing function. This animates the element in discrete segments rather than a smooth continuous animation—you know, for a more natural typing movement. For example, the duration of the animation is two seconds, so the steps() function animates the element in 9 steps (one step for each character in “Animation”) for two seconds, where each step has a duration of 2/9 = 0.22 seconds.

The end argument makes the element stay in its initial state until the duration of first step is complete. This argument is optional and its default value is set to end. If you want an in-depth insight on steps(), then you can refer this awesome article by Joni Trythall.

The fill property is the same as animation-fill-mode property in CSS. By setting its value to forwards, the element will stay at the same position as defined by the last keyframe after the animation gets completed.

Next, we will define the keyframes.

let reveal_animation_1 = document.querySelector(".text_hide").animate([ { left: '0%' }, { left: `${(100 / text_len) * (word_len[0])}%` } ], timings);

Right now we are animating just one word. Later, we will see how to animate multiple words.

The last keyframe is crucial. Let’s say we want to animate the word “Animation.” Its length is 9 (as there are nine characters) and we know that it’s getting stored as a variable thanks to our typing_animation() function. The declaration 100/text_len results to 100/9, or 11.11%, which is the width of each and every character in the word “Animation.” That means the width of each and every character is 11.11% the width of the entire word. If we multiply this value by the length of the first word (which in our case is 9), then we get 100%. Yes, we could have directly written 100% instead of doing all this stuff. But this logic will help us when we are animating multiple words.

The result of all of this is that the .text_hide element animates from left: 0% to left: 100%. In other words, the width of this element decreases from 100% to 0% as it moves along.

We have to add the same animation to the .text_cursor element as well because we want it to transition from left to right along with the .text_hide element.

CodePen Embed Fallback

Yayy! We animated a single word. What if we want to animate multiple words? Let’s do that next.

Animating multiple words

Let’s say we have two words we want typed out, perhaps “Typing Animation.” We animate the first word by following the same procedure we did last time. This time, however, we are changing the easing function value in the animation properties.

let timings = { easing: `steps(${Number(word_len[0] + 1)}, end)`, delay: 2000, duration: 2000, fill: 'forwards' }

We have increased the number by one step. Why? Well, what about a single space after a word? We must take that into consideration. But, what if there is only one word in a sentence? For that, we will write an if condition where, if the number of words is equal to 1, then steps(${Number(word_len[0])}, end). If the number of words is not equal to 1, then steps(${Number(word_len[0] + 1)}, end).

function typing_animation() { let text_element = document.querySelector(".text"); let text_array = text_element.innerHTML.split(""); let all_words = text_element.innerHTML.split(" "); let text_len = text_array.length; const word_len = all_words.map((word) => { return word.length; }) let timings = { easing: `steps(${Number(word_len[0])}, end)`, delay: 2000, duration: 2000, fill: 'forwards' } let cursor_timings = { duration: 700, iterations: Infinity, easing: 'cubic-bezier(0,.26,.44,.93)' } document.querySelector(".text_cursor").animate([ { opacity: 0 }, { opacity: 0, offset: 0.7 }, { opacity: 1 } ], cursor_timings); if (all_words.length == 1) { timings.easing = `steps(${Number(word_len[0])}, end)`; let reveal_animation_1 = document.querySelector(".text_hide").animate([ { left: '0%' }, { left: `${(100 / text_len) * (word_len[0])}%` } ], timings); document.querySelector(".text_cursor").animate([ { left: '0%' }, { left: `${(100 / text_len) * (word_len[0])}%` } ], timings); } else { document.querySelector(".text_hide").animate([ { left: '0%' }, { left: `${(100 / text_len) * (word_len[0] + 1)}%` } ], timings); document.querySelector(".text_cursor").animate([ { left: '0%' }, { left: `${(100 / text_len) * (word_len[0] + 1)}%` } ], timings); } } typing_animation();

For more than one word, we use a for loop to iterate and animate every word that follows the first word.

for(let i = 1; i < all_words.length; i++){ // code }

Why did we take i = 1? Because by the time this for loop is executed, the first word has already been animated.

Next, we will access the length of the respective word:

for(let i = 1; i < all_words.length; i++){ const single_word_len = word_len[i]; }

Let’s also define the animation properties for all words that come after the first one.

// the following code goes inside the for loop let timings_2 = { easing: `steps(${Number(single_word_len + 1)}, end)`, delay: (2 * (i + 1) + (2 * i)) * (1000), duration: 2000, fill: 'forwards' }

The most important thing here is the delay property. As you know, for the first word, we simply had the delay property set to two seconds; but now we have to increase the delay for the words following the first word in a dynamic way.

The first word has a delay of two seconds. The duration of its animation is also two seconds which, together, makes four total seconds. But there should be some interval between animating the first and the second word to make the animation more realistic. What we can do is add a two-second delay between each word instead of one. That makes the second word’s overall delay 2 + 2 + 2, or six seconds. Similarly, the total delay to animate the third word is 10 seconds, and so on.

The function for this pattern goes something like this:

(2 * (i + 1) + (2 * i)) * (1000)

…where we’re multiplying by 1000 to convert seconds to milliseconds.

Length of the wordDuration taken by one character to animate62/6 = 0.33 seconds82/8 = 0.25 seconds92/9 = 0.22 seconds122/12 = 0.17 seconds* Total duration is 2 seconds

The longer the word, the faster it is revealed. Why? Because the duration remains the same no matter how lengthy the word is. Play around with the duration and delay properties to get things just right.

Remember when we changed the steps() value by taking into consideration a single space after a word? In the same way, the last word in the sentence doesn’t have a space after it, and thus, we should take that into consideration in another if statement.

// the following code goes inside the for loop if (i == (all_words.length - 1)) { timings_2.easing = `steps(${Number(single_word_len)}, end)`; let reveal_animation_2 = document.querySelector(".text_hide").animate([ { left: `${left_instance}%` }, { left: `${left_instance + ((100 / text_len) * (word_len[i]))}%` } ], timings_2); document.querySelector(".text_cursor").animate([ { left: `${left_instance}%` }, { left: `${left_instance + ((100 / text_len) * (word_len[i]))}%` } ], timings_2); } else { document.querySelector(".text_hide").animate([ { left: `${left_instance}%` }, { left: `${left_instance + ((100 / text_len) * (word_len[i] + 1))}%` } ], timings_2); document.querySelector(".text_cursor").animate([ { left: `${left_instance}%` }, { left: `${left_instance + ((100 / text_len) * (word_len[i] + 1))}%` } ], timings_2); }

What’s that left_instance variable? We haven’t discussed it, yet it is the most crucial part of what we’re doing. Let me explain it.

0% is the initial value of the first word’s left property. But, the second word’s initial value should equal the first word’s final left property value.

if (i == 1) { var left_instance = (100 / text_len) * (word_len[i - 1] + 1); }

word_len[i - 1] + 1 refers to the length of the previous word (including a white space).

We have two words, “Typing Animation.” That makes text_len equal 16 meaning that each character is 6.25% of the full width (100/text_len = 100/16) which is multiplied by the length of the first word, 7. All that math gives us 43.75 which is, in fact, the width of the first word. In other words, the width of the first word is 43.75% the width of the entire string. This means that the second word starts animating from where the first word left off.

Last, let’s update the left_instance variable at the end of the for loop:

left_instance = left_instance + ((100 / text_len) * (word_len[i] + 1)); CodePen Embed Fallback

You can now enter as many words as you want in HTML and the animation just works!

Bonus

Have you noticed that the animation only runs once? What if we want to loop it infinitely? It’s possible:

CodePen Embed Fallback

There we go: a more robust JavaScript version of a typewriting animation. It’s super cool that CSS also has an approach (or even multiple approaches) to do the same sort of thing. CSS might even be the better approach in a given situation. But when we need enhancements that push beyond what CSS can handle, sprinkling in some JavaScript does the trick quite nicely. In this case, we added support for all words, regardless of how many characters they contain, and the ability to animate multiple words. And, with a small extra delay between words, we get a super natural-looking animation.

That’s it, hope you found this interesting! Signing off.

The post Typewriter Animation That Handles Anything You Throw at It appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

WordPress Admin Warnings in the Block Editor

Css Tricks - Fri, 07/16/2021 - 10:27am

We sent out an email the other week that ultimately had a <video> in the HTML markup. We send the newsletter by creating it here in the WordPress block editor, which is fetched through RSS-to-Mailchimp. Mailchimp dutifully sent it out, but the HTML was such that it totally borked the layout. This lead to some charming totally fair emails like this:

You actually can send <video> in HTML email, but our system just isn’t set up for it. It requires some fancy dancing CSS (e.g. hiding it for non-supporting users with a fallback, and detecting support is super tricky, etc.) and HTML (e.g. making sure the width/height attributes are small-screen friendly). We could do it, but I don’t think it’s worth it for the handful of times we would want to do it.

So instead, to prevent us from doing it again, I used (drumroll)…. CSS.

I have some CSS that gets loaded in the admin area only when the block editor is loaded, which is in a functionality plugin:

wp_register_style( 'css-tricks-code-block-editor-css', plugins_url('location/of/styles.css', dirname( __FILE__ )), array('wp-edit-blocks'), filemtime( plugin_dir_path(__DIR__) . 'location/of/styles.css') );

I can put anything I want in that CSS file and it will effect styles of the block editor but nothing on the public front end of the site.

I also wanted to scope this CSS only to the newsletters page. Fortunately, WordPress has body classes in the editor as well. We have a Custom Post Type for newsletters, and that expresses itself as a class here:

So I chuck these styles in:

/* Warn about videos in newsletters */ .post-type-newsletters .wp-block-video { border: 5px solid red; } .post-type-newsletters .wp-block-video::before { content: "WARNING: NO VIDEOS IN EMAILS"; display: block; color: red; }

And boom, I have styles that warn about this problem before it happens again:

The post WordPress Admin Warnings in the Block Editor appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Of Course We Can Make a CSS-Only Clock That Tells the Current Time!

Css Tricks - Fri, 07/16/2021 - 4:43am

Let’s build a fully functioning and settable “analog” clock with CSS custom properties and the calc() function. Then we’ll convert it into a “digital” clock as well. All this with no JavaScript!

Here’s a quick look at the clocks we’ll make:

GitHub repo Brushing up on the calc() function

CSS preprocessors teased us forever with the ability to calculate numerical CSS values. The problem with pre-processors is that they lack knowledge of the context after the CSS code has compiled. This is why it’s impossible to say you want your element width to be 100% of the container minus 50 pixels. This produces an error in a preprocessor:

width: 100% - 50px; // error: Incompatible units: 'px' and '%'

Preprocessors, as their name suggests, preprocess your instructions, but their output is still just plain old CSS which is why they can’t reconcile different units in your arithmetic operations. Ana has gone into great detail on the conflicts between Sass and CSS features.

The good news is that native CSS calculations are not only possible, but we can even combine different units, like pixels and percentages with the calc() function:

width: calc(100% - 50px);

calc() can be used anywhere a length, frequency, angle, time, percentage, number, or integer is allowed. Check out the CSS Guide to CSS Functions for a complete overview.

What makes this even more powerful is the fact that you can combine calc() with CSS custom properties—something preprocessors are unable to handle.

The hands of the clock

Let’s lay out the foundations first with a few custom properties and the animation definition for the hands of the analog clock:

:root { --second: 1s; --minute: calc(var(--second) * 60); --hour: calc(var(--minute) * 60); } @keyframes rotate { from { transform: rotate(0); } to { transform: rotate(1turn); } }

Everything starts in the root context with the --second custom property where we defined that a second should be, well, one second (1s). All future values and timings will be derived from this.

This property is essentially the heart of our clock and controls how fast or slow all of the clock’s hands go. Setting --second to 1s makes the clock match real-life time but we could make it go at half speed by setting it to 2s, or even 100 times faster by setting it to 10ms.

The first property we are calculating is the --minute hand, which we want equal to 60 times one second. We can reference the value from the --second property and multiply it by 60 with the help of calc() :

--minute: calc(var(--second) * 60);

The --hour hand property is defined using the exact same principle but multiplied by the --minute hand value:

--hour: calc(var(--minute) * 60);

We want all three hands on the clock to rotate from 0 to 360 degrees—around the shape of the clock face! The difference between the three animations is how long it takes each to go all the way around. Instead of using 360deg as our full-circle value, we can use the perfectly valid CSS value of 1turn.

@keyframes rotate { from { transform: rotate(0); } to { transform: rotate(1turn); } }

These @keyframes simply tell the browser to turn the element around once during the animation. We have defined an animation named rotate and it is now ready to be assigned to the clock’s second hand:

.second.hand { animation: rotate steps(60) var(--minute) infinite; }

We’re using the animation shorthand property to define the details of the animation. We added the name of the animation (rotate), how long we want the animation to run (var(--minute), or 60 seconds) and how many times to run it (infinite, meaning it never stops running). steps(60) is the animation timing function which tells the browser to perform the 1-turn animation in 60 equal steps. This way, the seconds hand ticks at each second rather than rotating smoothly along the circle.

While we are discussing CSS animations, we can define an animation delay (animation-delay) if we want the animation to start later, and we can change whether the animation should play forwards or backwards using animation-direction. We can even pause and restart the animations with animation-play-state.

The animation on the minute and hour hands will work very much like on the second hand. The difference is that multiple steps are unnecessary here—these hands can rotate in a smooth, linear fashion.

The minute hand takes one hour to complete one full turn, so:

.minute.hand { animation: rotate linear var(--hour) infinite; }

On the other hand (pun intended) the hour hand takes twelve hours to go around the clock. We don’t have a separate custom property for this amount of time, like --half-day, so we will multiply --hour by twelve:

.hour.hand { animation: rotate linear calc(var(--hour) * 12) infinite; }

You probably get the idea of how the hands of the clock work by now. But it would not be a complete example if we didn’t actually build the clock.

The clock face

So far, we’ve only looked at the CSS aspect of the clock. We also need some HTML for all that to work. Here’s what I’m using:

<main> <div class="clock"> <div class="second hand"></div> <div class="minute hand"></div> <div class="hour hand"></div> </div> </main>

Let’s see what we have to do to style our clock:

.clock { width: 300px; height: 300px; border-radius: 50%; background-color: var(--grey); margin: 0 auto; position: relative; }

We made the clock 300px tall and wide, made the background color grey (using a custom property, --grey, we can define later) and turned it into a circle with a 50% border radius.

There are three hands on the clock. Let’s first move these to the center of the clock face with absolute positioning:

.hand { position: absolute; left: 50%; top: 50%; }

Notice the name of this class (.hands) because all three hands use it for their base styles. That’s important because any changes to this class are applied to all three hands.

Let’s define the hand dimensions and color things up:

.hand { position: absolute; left: 50%; top: 50%; width: 10px; height: 150px; background-color: var(--blue); }

The hands are now all in place:

Getting proper rotation

Let’s hold off celebrating just a moment. We have a few issues and they might not be obvious when the clock hands are this thin. What if we change them to be 100px wide:

We can now see that if an element is positioned 50% from the left, it is aligned to the center of the parent element—but that’s not exactly what we need. To fix this, the left coordinate needs to be 50%, minus half the width of the hand, which is 50px in our case:

Working with multiple different measurements is a breeze for the calc() function:

.hand { position: absolute; left: calc(50% - 50px); top: 50%; width: 100px; height: 150px; background-color: var(--grey); }

This fixes our initial positioning, however, if we try to rotate the element we can see that the transform origin, which is the pivot point of the rotation, is at the center of the element:

We can use the transform-origin property to change the rotation origin point to be at the center of the x-axis and at the top on the y-axis:

.hand { position: absolute; left: calc(50% - 50px); top: 50%; width: 100px; height: 150px; background-color: var(--grey); transform-origin: center 0; }

This is great, but not perfect because our pixel values for the clock hands are hardcoded. What if we want our hands to have different widths and heights, and scale with the actual size of the clock? Yes, you guessed right: we need a few CSS custom properties!

.second { --width: 5px; --height: 140px; --color: var(--yellow); } .minute { --width: 10px; --height: 90px; --color: var(--blue); } .hour { --width: 10px; --height: 50px; --color: var(--dark-blue); }

With this, we’ve defined custom properties for the individual hands. What’s interesting here is that we gave these properties the same names: --width, --height, and --color. How is it possible that we gave them different values but they don’t overwrite each other? And which value will I get back if I call var(--width), var(--height) or var(--color)?

Let’s look at the hour hand:

<div class="hour hand"></div>

We assigned new custom properties to the .hour class and they are locally scoped to the element, which includes the element and all its children. This means any CSS style applied to the element—or its children accessing the custom properties—will see and use the specific values that were set within their own scope. So if you call var(--width) inside the hour hand element or any ancestor elements inside that, the value returned from our example above is 10px. This also means that if we try using any of these properties outside these elements—for example inside the body element—they are inaccessible to those elements outside the scope.

Moving on to the hands for seconds and minutes, we enter a different scope. That means the custom properties can be redefined with different values.

.second { --width: 5px; --height: 140px; --color: var(--yellow); } .minute { --width: 10px; --height: 90px; --color: var(--blue); } .hour { --width: 10px; --height: 50px; --color: var(--dark-blue); } .hand { position: absolute; top: 50%; left: calc(50% - var(--width) / 2); width: var(--width); height: var(--height); background-color: var(--color); transform-origin: center 0; }

The great thing about this is that the .hand class (which we assigned to all three hand elements) can reference these properties for the calculations and declarations. And each hand will receive its own properties from its own scope. In a way, we’re personalizing the .hand class for each element to avoid unnecessary repetition in our code.

Our clock is up and running and all hands are moving at the correct speed:

We could stop here but let me suggest a few improvements. The hands on the clock start at 6 o’clock, but we could set their initial positions to 12 o’clock by rotating the clock 180 degrees. Let’s add this line to the .clock class:

.clock { /* same as before */ transform: rotate(180deg); }

The hands might look nice with rounded edges:

.hand { /* same as before */ border-radius: calc(var(--width) / 2); }

Our clock looks and works great! And all hands start from 12 o’clock, exactly how we want it!

Setting the clock

Even with all these awesome features, the clock is unusable as it fails terribly at telling the actual time. However, there are some hard limitations to what we can do about this. It’s simply not possible to access the local time with HTML and CSS to automatically set our clock. But we can prepare it for manual setup.

We can set the clock to start at a certain hour and minute and if we run the HTML at exactly that time it will keep the time accurately afterwards. This is basically how you set a real-world clock, so I think this is an acceptable solution. &#x1f605;

Let’s add the time we want to start the clock as custom properties inside the .clock class:

.clock { --setTimeHour: 16; --setTimeMinute: 20; /* same as before */ }

The current time for me as I write is coming up to 16:20 (or 4:20) so the clock will be set to that time. All I need to do is refresh the page at 16:20 and it will keep the time accurately.

OK, but… how can we set the time to these positions and rotate the hands if a CSS animation is already controlling the rotation?

Ideally, we want to rotate and set the hands of the clock to a specific position when the animation starts at the very beginning. Say you want to set the hour hand to 90 degrees so it starts at 3:00 pm and initialize the rotation animation from this position:

/* this will not work */ .hour.hand { transform: rotate(90deg); animation: rotate linear var(--hour) infinite; }

Well, unfortunately, this will not work because the transform is immediately overridden by the animation, as it modifies the very same transform property. So, no matter what we set this to, it will go back to 0 degrees where the first keyframe of the animation starts.

We can set the rotation of the hand independently from the animation. For example, we could wrap the hand into an extra element. This extra parent element, the “setter,” would be responsible for setting the initial position, then the hand element inside could animate from 0 to 360 degrees independently. The starting 0-degree position would then be relative to what we set the parent setter element to.

This would work but luckily there’s a better option! Let me amaze you! &#x1fa84;✨✨

The animation-delay property is what we usually use to start the animation with some predefined delay.

The trick is to use a negative value, which starts the animation immediately, but from a specific point in the animation timeline!

To start 10 seconds into the animation:

animation-delay: -10s;

In case of the hour and minute hands, the actual value we need for the delay is calculated from the --setTimeHour and --setTimeMinute properties. To help with the calculation, let’s create two new properties with the amount of time shifting we need:

  • The hour hand needs to be the hour we want to set the clock, multiplied by an hour.
  • The minute hand shifts the minute we want to set the clock multiplied by a minute.
--setTimeHour: 16; --setTimeMinute: 20; --timeShiftHour: calc(var(--setTimeHour) * var(--hour)); --timeShiftMinute: calc(var(--setTimeMinute) * var(--minute));

These new properties contain the exact amount of time we need for the animation-delay property to set our clock. Let’s add these to our animation definitions:

.second.hand { animation: rotate steps(60) var(--minute) infinite; } .minute.hand { animation: rotate linear var(--hour) infinite; animation-delay: calc(var(--timeShiftMinute) * -1); } .hour.hand { animation: rotate linear calc(var(--hour) * 12) infinite; animation-delay: calc(var(--timeShiftHour) * -1); }

Notice how we multiplied these values by -1 to convert them to a negative number.

We have almost reached perfection with this, but there’s a slight issue: if we set the number of minutes to 30, for example, the hour hand needs to already be halfway through to the next hour. An even worse situation would be to set the minutes to 59 and the hour hand is still at the beginning of the hour.

fix this, all we need to do is add the minute shift and the hour shift values together for the hour hand:

.hour.hand { animation: rotate linear calc(var(--hour) * 12) infinite; animation-delay: calc( (var(--timeShiftHour) + var(--timeShiftMinute)) * -1 ); }

And I think with this we have fixed everything. Let’s admire our beautiful, pure CSS, settable, analog clock:

Let’s go digital

In principle, an analog and a digital clock both use the same calculations, the difference being the visual representation of the calculations.

Here’s my idea: we can create a digital clock by setting up tall, vertical columns of numbers and animate these instead of rotating the clock hands. Removing the overflow mask from the final version of the clock container reveals the trick:

The new HTML markup needs to contain all the numbers for all three sections of the clock from 00 to 59 on the second and minute sections and 00 to 23 on the hour section:

<main> <div class="clock"> <div class="hour section"> <ul> <li>00</li> <li>01</li> <!-- etc. --> <li>22</li> <li>23</li> </ul> </div> <div class="minute section"> <ul> <li>00</li> <li>01</li> <!-- etc. --> <li>58</li> <li>59</li> </ul> </div> <div class="second section"> <ul> <li>00</li> <li>01</li> <!-- etc. --> <li>58</li> <li>59</li> </ul> </div> </div> </main>

To make these numbers line up, we need to write some CSS, but to get started with the styling we can copy over the custom properties from the :root scope of the analog clock straight away, as time is universal:

:root { --second: 1s; --minute: calc(var(--second) * 60); --hour: calc(var(--minute) * 60); }

The outermost wrapper element, the .clock, still has the very same custom properties for setting the initial time. All that’s changed is that it becomes a flexbox container:

.clock { --setTimeHour: 14; --setTimeMinute: 01; --timeShiftHour: calc(var(--setTimeHour) * var(--hour)); --timeShiftMinute: calc(var(--setTimeMinute) * var(--minute)); width: 150px; height: 50px; background-color: var(--grey); margin: 0 auto; position: relative; display: flex; }

The three unordered lists and the list items inside them that hold the numbers don’t need any special treatment. The numbers will stack on top of each other if their horizontal space is limited. Let’s just make sure that there is no list styling to prevent bullet points and that we center things for consistent placement:

.section > ul { list-style: none; margin: 0; padding: 0; } .section > ul > li { width: 50px; height: 50px; font-size: 32px; text-align: center; padding-top: 2px; }

The layout is done!

Outside each unordered list is a <div> with a .section class. This is the element that wraps each section, so we can use it as a mask to hide the numbers that fall outside the visible area of the clock:

.section { position: relative; width: calc(100% / 3); overflow: hidden; }

The structure of the clock is done and the rails are now ready to be animated.

Animating the digital clock

The basic idea behind the whole animation is that the three strips of numbers can be moved up and down within their masks to show different numbers from 0 to 59 for seconds and minutes, and 0 to 23 for hours (for a 24-hour format).

We can do this by changing the translateY transition function in CSS for the individual strips of numbers from 0 to -100%. This is because 100% on the y-axis represents the height of the whole strip. A value of 0% will show the first number, and 100% will show the last number of the current strip.

Previously, our animation was based on rotating a hand from 0 to 360 degrees. We now have a different animation that moves the number strips from 0 to -100% on the y-axis:

@keyframes tick { from { transform: translateY(0); } to { transform: translateY(-100%); } }

Applying this animation to the seconds number strip can be done the same way as the analog clock. The only difference is the selector and the name of the animation that’s referenced:

.second > ul { animation: tick steps(60) var(--minute) infinite; }

With the step(60) setting, the animation ticks between numbers like we did for the second hand on the analog clock. We could change this to ease and then the numbers would smoothly slide up and down as if they were on a ribbon of paper.

Assigning the new tick animation to the minute and hour sections is just as straightforward:

.minute > ul { animation: tick steps(60) var(--hour) infinite; animation-delay: calc(var(--timeShiftMinute) * -1); } .hour > ul { animation: tick steps(24) calc(24 * var(--hour)) infinite; animation-delay: calc(var(--timeShiftHour) * -1); }

Again, the declarations are very similar, what’s different this time is the selector, the timing function, and the animation name.

The clock now ticks and keeps the correct time:

One more detail: The blinking colon (:)

Again we could stop here and call it a day. But there’s one last thing we can do to make our digital clock a little more realistic: make the colon separator between the minutes and seconds blink as each second passes.

We could add these colons in the HTML but they are not part of the content. We want them to be an enhancement to the appearance and style of the clock, so CSS is the right place to store this content. That’s what the content property is for and we can use it on the ::after pseudo-elements for the minutes and hours:

.minute::after, .hour::after { content: ":"; margin-left: 2px; position: absolute; top: 6px; left: 44px; font-size: 24px; }

That was easy! But how can we make the seconds colon blink too? We want it animated so we need to define a new animation? There are many ways to achieve this but I thought we should change the content property this time to demonstrate that, quite unexpectedly, it is possible to change its value during an animation:

@keyframes blink { from { content: ":"; } to { content: ""; } }

Animating the content property is not going to work in every browser, so you could just change that to opacity or visibility as a safe option…

The final step is to assign the blink animation to the pseudo-element of the minute section:

.minute::after { animation: blink var(--second) infinite; }

And with that, we are all done! The digital clock keeps the time accurately and we even managed to add a blinking separator between the numbers.

Book: All you need is HTML and CSS

This is just one example project from my new book, All you need is HTML and CSS. It’s available on Amazon in both the U.S and U.K.

If you are just getting started with web development, the ideas in the book will help you level up and build interactive, animated web interfaces without touching any JavaScript.

If you are a seasoned JavaScript developer, the book is a good reminder that many things can be built with HTML and CSS alone, especially now that we have a lot more powerful CSS tools and features, like the ones we covered in this article. There are many examples in the book pushing the limits including interactive carousels, accordions, calculating, counting, advanced input validation, state management, dismissible modal windows, and reacting to mouse and keyboard inputs. There’s even a fully working star rating widget and a shopping basket.

Thanks for spending the time to build clocks with me!

The post Of Course We Can Make a CSS-Only Clock That Tells the Current Time! appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Some Typography Links

Css Tricks - Fri, 07/16/2021 - 4:42am
  • Glitter text — whO (I learned a name for people who go by a one-word moniker like that: Mononymous) created a builder for fancy SVG-based type. It’s a custom font with <text>, and the fancy comes in with a gradient and somewhat exotic filters that make noise and blend the noise into the color.
  • Optical Size tweaking for dark mode — Mark Boulton opens by questioning the usefulness of variable fonts (blaspheme!) but then finds a nice use case in adjusting the optical size in dark mode. Robin covered that right here not too long ago.
  • Optical size, the hidden superpower of variable fonts — Speaking of optical size, Roel Nieskens digs into that here. It’s not just a weight thing… This feature will make letters actually change the way they look when shown in small or large sizes. It all happens automatically in the browser.”
  • Updates to v-fonts.com — Annnnd speaking of variable fonts, Piper Haywood talks about some updates to v-fonts.com, introducing some browsable taxonomies. It takes me about 20 seconds browing this site to want to redesign everything using variable fonts.
  • All you need is 5 fonts — (Matej Latin) I’ve been hearing about these mythical designers who focus all their creative energy on deep-learning how to use a very limited set of fonts. Maybe it’s kinda the same as us web nerds who only know HTML, CSS, and JavaScript and leave it at that.
  • Best practices for fontsJust like CSS, fonts affect Web Core Vitals in big ways (e.g. layout shifts and paints). Katie Hempenius is at it again here covering how to make fonts faster. I think these best practices are starting to set in a bit… preconnecting to the font host, subsetting, font-display, etc.
  • Sans Bullshit Sans — Kinda like the Cloud to Butt browser plugin, only the text replacements are done via ligatures (&#x1f92f;) converting them into little Comic Sans badges. Some of them are angled? How the heck does that work?
  • Bryan Font — Jon Hicks builds a font for his father, John Bryan Hicks, who passed away. What a loving tribute.
  • Inherit ancestor font-size, for fun and profit — Lea Verou finds yet another use case for @property. I wonder if it’s an emerging best practice to register all your custom properties, since it unlocks possibilites and makes them behave more like you expect them to behave. Lea shows how you can browser test for @property in JavaScript, but if you for some reason you can’t do that and don’t mind pretty mind-bending CSS, Jane has pure CSS way.

The post Some Typography Links appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

How to Get a Pixel-Perfect, Linearly Scaled UI

Css Tricks - Thu, 07/15/2021 - 4:40am

Dynamically scaling CSS values based on the viewport width is hardly a new topic. You can find plenty of in-depth coverage right here on CSS-Tricks in articles like this one or this one.

Most of those examples, though, use relative CSS units and unitless values to achieve fluid scaling. That loses pixel perfection and usually introduces text wrapping and layout shifts once the screen goes below or above a certain threshold.

But what if we really do want pixel perfection? What if, let’s say, we are developing a complex real-time analytics dashboard to be viewed on large TVs at a conference room or as some PWA to be opened exclusively on mobile and tablet devices, as opposed to text-heavy blogs and news websites? Those are cases where we need more precision.

In other words, what if we want to scale designs uniformly? Of course, one can scale the content with CSS transforms based on the available width as covered in this article — this way, the correct ratios are preserved.

However, we can also achieve fluid proportional scaling UIs using pixel values in CSS. They scale appropriately based on the device screen real estate, all while preserving their pixel-perfect proportions. Further, we can still use pixel values and automatically convert them to relative CSS units if working in pixels is more comfortable or familiar.

Scaling our UI

Let’s try to implement this awesome dashboard, courtesy of Craftwork. We need to make it in such a way that it scales perfectly and preserves all the texts line counts, margins, image sizes, etc.

Let’s work in CSS pixel values and use SCSS for speed and convenience. So, if we are to target the title of one of these card widgets, our SCSS might look something like this:

.cardWidget { .cardHeading { font-size: 16px; } }

Nothin’ fancy. Nothing we have not seen before. Being a pixel value, this will not scale.

This design was created with a container that’s 1600px wide. Let’s assume that at 1600px, the ideal font size for the titles of the cards should be 16px since that’s how it’s designed.

Now that we have the “ideal” container width font size for this width, let’s scale our CSS pixel values accordingly using the current* viewport width:

/* 1600px is the ideal viewport width that the UI designers who created the dashboard used when designing their Figma artboards Please not we are not using pixel units here, treating it purely as a numeric value. */ --ideal-viewport-width: 1600; /* The actual width of the user device */ --current-viewport-width: 100vw; .cardWidget { .cardHeading { /* 16px is the ideal font size that the UI designers want for 1600px viewport width. Please note that we are not using pixel units here, treating it purely as a numeric value. */ --ideal-font-size: 16; /* Calculate the actual font size: We take our idealFontSize and multiply it by the difference between the current viewport width and the ideal viewport width. */ font-size: calc( var(--ideal-font-size) * (var(--current-viewport-width) / var(--ideal-viewport-width) ); } }

As you can see, we treat the ideal font size we obtained from the design as a base and multiply it by the difference between the current and ideal viewport widths. How does this look mathematically? Let’s say we are viewing this web app on a screen with the exact same width as the mockup:

--current-device-width: 100vw; // represents 1600px or full width of the screen --ideal-viewport-width: 1600; // notice that the ideal and current width match --ideal-font-size: 16; // this evaluates to: font-size: calc(16 * 1600px / 1600); // same as: font-size: calc(16 * 1px); // final result: font-size: 16px;

So, since our viewport width matches perfectly, our font-size ends being exactly 16px at the ideal viewport width of 1600px.

As another example, let’s say we are viewing the web app on a smaller laptop screen that’s 1366px wide. Here is the updated math:

font-size: calc(16 * 1366px / 1600); // same as: font-size: calc(16 * 0.85375px); // final result: font-size: 13.66px;

Or let’s say we are viewing this on a full high-definition display at 1920px wide:

font-size: calc(16 * 1920px / 1600); // same as: font-size: calc(16 * 1.2px); // final result: font-size: 19.2px;

You can see for yourself how even though we use pixel values as reference, we are actually able to proportionally scale our CSS values based on the difference in width between the ideal and current viewport sizes.

Here is a small demo I built to illustrate the technique:

CodePen Embed Fallback

Here’s a video for convienence:

Clamping the min and max viewport width

Using this current approach, the design scales to match the viewport size, no matter how big or small the viewport gets. We can prevent this with CSS clamp() which allows us to set a minimum width of 350px and maximum width of 3840px. This means that if we are to open the web app on a device with 5000px width, our layout will stay locked at 3840px:

--ideal-viewport-width: 1600; --current-viewport-width: 100vw; /* Set our minimum and maximum allowed layout widths: */ --min-viewport-width: 350px; --max-viewport-width: 3840px; .cardWidget { .cardHeading { --ideal-font-size: 16; font-size: calc( /* The clamp() function takes three comma separated expressions as its parameter, in the order of minimum value, preferred value and maximum value: */ --clamped-viewport-width: clamp(var(--min-viewport-width), var(--current-viewport-width), var(--max-viewport-width); /* Use the clamped viewport width in our calculation */ var(--ideal-font-size) * var(--clamped-viewport-width) / var(--ideal-viewport-width) ); } } Let’s make a helper for the unit conversions

Our code is quite verbose. Let’s write a simple SCSS function that converts our values from pixels to relative units. That way, we can import and reuse anywhere this anywhere without so much duplication:

/* Declare a SCSS function that takes a value to be scaled and ideal viewport width: */ @function scaleValue( $value, $idealViewportWidth: 1600px, $min: 350px, $max: 3840px ) { @return calc( #{$value} * (clamp(#{$min}, 100vw, #{$max}) / #{$idealViewportWidth}) ); } /* We can then apply it on any numeric CSS value. Please note we are passing not pixel based, but numeric values: */ .myElement { width: #{scaleValue(500)}; height: #{scaleValue(500)}; box-shadow: #{scaleValue(2)} #{scaleValue(2)} rgba(black, 0.5); font-size: #{scaleValue(24)}; } Porting this to Javascript

Sometimes CSS doesn’t cut it and we have to use JavaScript to size a component. Let’s say we are constructing an SVG dynamically and we need to size its width and height properties based on an ideal design width. Here is the JavaScript to make it happen:

/* Our helper method to scale a value based on the device width */ const scaleValue = (value, idealViewportWidth = 1600) => { return value * (window.innerWidth / idealViewportWidth) } /* Create a SVG element and set its width, height and viewbox properties */ const IDEAL_SVG_WIDTH = 512 const IDEAL_SVG_HEIGHT = 512 const svgEl = document.createElement('svg') /* Scale the width and height */ svgEl.setAttribute('width', scaleValue(IDEAL_SVG_WIDTH)) svgEl.setAttribute('height', scaleValue(IDEAL_SVG_WIDTH)) /* We don't really need to scale the viewBox property because it will perfectly match the ratio of the scaled width and height */ svg.setAttribute('viewBox', `0 0 ${IDEAL_SVG_WIDTH} ${IDEAL_SVG_HEIGHT}`) The drawbacks of this technique

This solution is not perfect. For example, one major drawback is that the the UIs are no longer zoomable. No matter how much the user zooms, the designs will stay locked as if they are viewed at 100% zoom.

That said, we can easily use traditional media queries, where we set different ideal numeric values at different viewport widths:

.myElement { width: #{scaleValue(500)}; height: #{scaleValue(500)}; box-shadow: #{scaleValue(2)} #{scaleValue(2)} rgba(black, 0.5); font-size: #{scaleValue(24)}; @media (min-width: 64em) { width: #{scaleValue(800)}; font-size: #{scaleValue(42)}; } }

Now we can benefit from both media queries and our pixel-perfect linear scaling.

Wrapping up

All of this is an alternative way to implement fluid UIs. We treat the pixel-perfect values as pure numeric values, and multiply them by the difference between the current viewport width and the “ideal” viewport width from the designs.

I have used this technique extensively in my own work and hope that you will find some use of it too.

The post How to Get a Pixel-Perfect, Linearly Scaled UI appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Build Complex CSS Transitions using Custom Properties and cubic-bezier()

Css Tricks - Wed, 07/14/2021 - 4:31am

I recently illustrated how we can achieve complex CSS animations using cubic-bezier() and how to do the same when it comes to CSS transitions. I was able to create complex hover effect without resorting to keyframes. In this article, I will show you how to create even more complex CSS transitions.

This time, let’s use the @property feature. It’s only supported on Chrome-based browsers for now but we can still play with it and demonstrate how it, too, and can be used to build complex animations.

I highly recommend reading my previous article because I will be referring to a few concepts I explained in detail there. Also, please note that the demos in this article are best viewed in Chromium-based browsers while @property support is still limited.

Let’s start with a demo:

CodePen Embed Fallback

Click on the button (more than once) and see the “magic” curve we get. It may look trivial at first glance because we can achieve such effect using some complex keyframes. But the trick is that there is no keyframe in there! That animation is done using only a transition.

Awesome right? And this is only the beginning, so let’s dig in!

The main idea

The trick in the previous example relies on this code:

@property --d1 { syntax: '<number>'; inherits: false; initial-value: 0; } @property --d2 { syntax: '<number>'; inherits: false; initial-value: 0; } .box { top: calc((var(--d1) + var(--d2)) * 1%); transition: --d1 1s cubic-bezier(0.7, 1200, 0.3, -1200), --d2 1s cubic-bezier(0.5, 1200, 0.5, -1200); } .box:hover { --d1: 0.2; --d1: -0.2; }

We’re defining two custom properties, --d1 and --d2. Then, we declare the top property on a .box element using the sum of both those properties. Nothing overly complex yet—just calc() applied to two variables.

The two properties are defined as <number> and I multiply those values by 1% to convert them into a percentage. We could define these as <percentage> right away to avoid the multiplication. But I’ve chosen numbers instead in favor of more flexibility for more complex operations later.

Notice that we apply a different transition to each variable—more precisely, a different timing-function with the same duration. It’s actually a different sinusoidal curve for both variables which is something I get deep into in my previous article.

From there, the property values change when the .box is hovered, triggering the animation. But why do we get the result we see in the demo?

It’s all about math. We are adding two functions to create a third one. For --d1, we have a function (let’s call it F1); for --d2 , we have another one (let’s call it F2). That means the value of top is F1 + F2.

An example to better illustrate:

CodePen Embed Fallback

The first two transitions illustrate each variable individually. The third one is the sum of them. Imagine that at in each step of the animation we take the value of both variables and we add them together to get each point along the final curve.

Let’s try another example:

CodePen Embed Fallback

This time, we combine two parabolic curve to get a… well, I don’t know its name it but it’s another complex curve!

This trick is not only limited to the parabolic and sinusoidal curve. It can work with any kind of timing function even if the result won’t always be a complex curve.

CodePen Embed Fallback

This time:

  • --d1 goes from 0 to 30 with an ease-in timing function
  • --d2 goes from 0 to -20 with an ease-out timing function

The result? The top value goes from 0 to 10 (30-20) with a custom timing function (the sum of ease-in and ease-out).

We are not getting a complex transition in this case—it’s more to illustrate the fact that it’s a generic idea not only limited to cubic-bezier().

I think it’s time for an interactive demo.

CodePen Embed Fallback

All you have to do is to adjust a few variables to build your own complex transition. I know cubic-bezier() may be tricky, so consider using this online curve generator and also refer to my previous article.

Here are some examples I made:

As you can see, we can combine two different timing functions (created using cubic-bezier() ) to create a third one, complex enough to achieve a fancy transition. The combinations (and possibilities) are unlimited!

In that last example, I wanted to demonstrate how adding two opposite functions lead to the logical result of a constant function (no transition). Hence, the flat line.

Let’s add more variables!

You thought we’d stop at only two variables? Certainly not! We can extend the logic to N variables. There is no restriction—we define each one with a timing function and sum them up.

An example with three variables:

CodePen Embed Fallback

In most cases, two variables are plenty to create a fancy curve, but it’s neat to know that the trick can be extended to more variables.

Can we subract, multiply and divide variables?

Of course! We can also extend the same idea to consider more operations. We can add, subtract, multiply, divide—and even perform a complex formula between variables.

Here, we’re multiplying values:

CodePen Embed Fallback

We can also use one variable and multiply it by itself to get a quadratic function!

CodePen Embed Fallback

Let’s add more fun in there by introducing min()/max() to simulate an abs() function:

CodePen Embed Fallback

Notice that in the second box we will never get higher than the center point on the y-axis because top is always a positive value. (I added a margin-top to make the center of box the reference for 0.)

I won’t get into all the math, but you can imagine the possibilities we have to create any kind of timing function. All we have to do is to find the right formula either using one variable or combining multiple variables.

Our initial code can be generalized:

@property --d1 { /* we do the same for d2 .. dn */ syntax: '<number>'; inherits: false; initial-value: i1; /* the initial value can be different for each variable */ } .box { --duration: 1s; /* the same duration for all */ property: calc(f(var(--d1),var(--d2), .. ,var(--dn))*[1UNIT]); transition: --d1 var(--duration) cubic-bezier( ... ), --d2 var(--duration) cubic-bezier( ... ), /* .. */ --dn var(--duration) cubic-bezier( ... ); } .box:hover { --d1:f1; --d2:f2; /* .. */ --dn:f3; }

This is pseudo-code to illustrate the logic:

  1. We use @property to define numeric custom properties, each with an initial value.
  2. Each variable has its own timing function but the same duration.
  3. We define an f function that is the formula used between the variables. The function provides a number that we use to multiply the relevant unit. All this runs in calc() applied to the property.
  4. We update the value of each variable on hover (or toggle, or whatever).

Given this, the property transitions from f(i1,i2,…,in) to f(f1,f2,..,fn) with a custom timing function.

Chaining timing functions

We’ve reached the point where we were able to create a complex timing function by combining basic ones. Let’s try another idea that allow us to have more complex timing function: chaining timing functions together.

The trick is to run the transitions sequentially using the transition-delay property. Let’s look back at the interactive demo and apply a delay to one of the variables:

CodePen Embed Fallback

We are chaining timing functions instead of adding them together for yet another way to create more complex timing functions! Mathematically, it’s still a sum, but since the transitions do not run at the same time, we will be summing a function with a constant, and that simulates the chaining.

Now imagine the case with N variables that we are incrementally delayed. Not only can we create complex transitions this way, but we have enough flexibility to build complex timelines.

Here is a funny hover effect I built using that technique:

CodePen Embed Fallback

You will find no keyframes there. A small action scene is made entirely using one element and a CSS transition.

Here is a realistic pendulum animation using the same idea:

CodePen Embed Fallback

Or, how about a ball that bounces naturally:

CodePen Embed Fallback

Or maybe a ball rolling along a curve:

CodePen Embed Fallback

See that? We just created complex animations without a single keyframe in the code!

That’s a wrap!

I hope you took three key points away from this article and the previous one:

  1. We can get parabolic and sinusoidal curves using cubic-bezier() that allow us to create complex transitions without keyframes.
  2. We can create more curves by combining different timing functions using custom properties and calc().
  3. We can chain the curves using the transition-delay to build a complex timeline.

Thanks to these three features, we have no limits when it comes to creating complex animations.

CodePen Embed Fallback

The post Build Complex CSS Transitions using Custom Properties and cubic-bezier() appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

A Bashful Button Worth $8 Million

Css Tricks - Wed, 07/14/2021 - 4:25am

Most of us grumble when running across a frustrating UX experience online (like not being able to complete a transaction because of a misplaced button). We might pen a whiny tweet. Jason Grigsby is like I’m going to write 2,000 words on this and show them what’s what. And of course, he has a strong point. An out-of-viewport button that you need to press to complete an order, on a checkout experience for some major restaurant brand, even with estimates that are conservative at every angle, is worth millions of dollars in lost sales.

Direct Link to ArticlePermalink

The post A Bashful Button Worth $8 Million appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Links on React and JavaScript

Css Tricks - Tue, 07/13/2021 - 9:57am

As a day-job, React-using person, I like to stay abreast of interesting React news. As such, I save a healthy amount of links. Allow me to dump out my latest pile. Most of this is about React but not all of it.

  • The Plan for React 18 — A bunch of people from the React team put this post out giving us all a heads up of what’s coming. Alpha is out, beta is months away. I thought Cassidy’s article on it was the most clear about what we’re likely to care about.
  • React Query — Looks like a pretty robust tool… “the missing data-fetching library for React.” Don’t know how I missed it as it even seems more popular than Apollo. I’ve been pretty happy with using Apollo (as a user, my biggest pain is unclear error reporting), and it seems like that’s probabably the right choice if you’re heavy into GraphQL, but React Query looks awfully nice with clear docs and nice DevTools.
  • Data Fetching in Redux Made Easy With RTK Query — Matt Stobbs looks at RTK Query, which looks like yet another alternative to the Apollo / React Query stuff. Take a look at the Redux store in an app you’re working on now. If it’s anything like mine, you’ll see a mix of data from the backend (which is behaving as a cache) and UI state (the data that isn’t persisted when the page reloads). These two types of data are treated as if they are the same, which ends up making both more complicated.
  • Just-In-Time translations and code that writes itself — Dan Laush looks at a bunch of modern options for conditional and lazy loading JavaScript. This stuff is probably more complicated than it should be, but it’s getting better. Suspense in React 18 will be helpful. Top-level await is helpful. Load what you need when you need it. Astro is good at this. And, speaking of all this, Nicholas C. Zakas’ “The lazy-loading property pattern in JavaScript” is a great read with a clever pattern for defining objects that only do expensive things once, lazily when asked, then redefine that property on themselves with the result.
  • Bringing JSX to Template Literals — People think of JSX as a React thing, which is kinda fair, but it’s really a separate thing that can be useful with other frameworks (certainly Preact and even Vue). We looked at how it can be fun with even no framework at all in a previous video. Andrea Giammarchi goes deep here and shows how it can work with the already nicely-ergnomic template literals. “You can see it working in CodePen via uhtmlulandube, or lit-html.”
  • React Hooks: Compound Components — Shout out to Kent Dodds! We’ve started using this in our pattern library at CodePen. It’s been nice for keeping components a bit more consolidated rather than a sprawling tree of similarly-named sub components with hand-rolled state sharing.
  • JavaScript: What is the meaning of this? — Jake Archibald puts out the canonical article on this.
  • Human-Readable JavaScript: A Tale of Two Experts — Laurie Barth compares examples of code that do the same thing, but have different levels of readability. There isn’t always a straight answer “… but when you’re looking at code that is functionally identical, your determination should be based on humans—how humans consume code.”
  • petite-vue — jQuery was amazing and there is plenty of perfectly fine jQuery code, but the reason jQuery is a bit looked down upon these days is the messy code bases that were made with it. Some lessons were learned. While inline JavaScript handlers were once heavily scorned, nearly every popular JavaScript library today has brought them back. But let’s say something like React is too heavy-handed for you—what is the jQuery of light on-page interactivity stuff? Vue sort of walks the line between that and being more of a “big framework.” Alpine.js is probably the main player. But here comes Vue again with a poke at Alpine with a version of itself that is pretty darn small and does the same sort of stuff.

The post Links on React and JavaScript appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Meta Theme Color and Trickery

Css Tricks - Tue, 07/13/2021 - 4:58am

Starting with Version 15, Safari supports the theme-color <meta> tag both on macOS and iOS. That’s exciting news because now the first desktop browser supports this <meta> tag and it also supports the media attribute and the prefers-color-scheme media feature.

I never really took much note of the theme-color meta tag, but now is a good time to learn about its features and limitations and try to discover some interesting use cases.

Heads up! Safari removed support for the theme-color meta tag in the current release of Safari Technology Preview (127). This seems to be a temporary thing.

.

Features and limitations

Here’s how I’ve been using the theme-color meta tag for the past few years: just a good ‘ol hex code for the content attribute.

<meta name="theme-color" content="#319197">

According to tests I made earlier this year, this works in Chrome, Brave and Samsung Internet on Android, installed PWAs in Chrome and now also in Safari Technology Preview.

Hex color support is great in all supported browsers. CSS color support

One of the first questions that came to my mind was “Can we use color keywords, hsl(), rgb(), too?” According to the HTML spec, the value of the attribute can be any CSS color. I’ve created this theme-color testing CodePen to verify that.

<meta name="theme-color" content="hsl(24.3, 97.4%, 54.3%)"> The theme-color meta tags supports CSS colors in any form: keywords, rgb(), hsl() or hex code. Looking at Chrome 90 on an Android Galaxy S20

All supported browsers also support hsl() and rgb(). This is awesome because it allows us to do some pretty cool stuff with JavaScript. We’ll talk about that later, but first let’s look at some limitations.

Transparency

HEX codes, rbg(), hsl() and keywords are well and consistently supported, but colors that include transparency: not so much. Actually, they are supported in most browsers, but the results aren’t very consistent and sometimes unexpected.

transparent is a CSS color and used in the theme-color meta tag most browsers do what you’d expect. All regular mobile browsers don’t change color and display the default tab bar, but Safari on macOS and the Chrome Canary PWA on macOS turn the tab bar black. The PWA on Android falls back to theme-color defined in the manifest.json, which we’ll talk about in a bit.

Browser with a transparent theme-color meta tag

All browsers interpret hsla() and rgba(), but they set the alpha value to 1. The only exception is Safari on macOS; it interprets the transparency, but it seems like the transparent color has a black baseline. This has the effect that the light orange color looks like dark orange.

hsla() applied to the theme-color meta tag New color functions

Safari 15 is the first browser to support lab(), lch(), and hwb() color functions. These functions work if you use them in CSS, but not if you use them in the theme-color meta tag.

All three declarations work fine in Safari 15:

body { background-color: hwb(27 10% 28%); background-color: lch(67.5345% 42.5 258.2); background-color: lab(62.2345% -34.9638 47.7721); }

If you use any of the new color functions in the theme-color meta tag, Safari doesn’t interpret them and falls back to its own algorithm of picking the color. It’s likely that Safari uses the background color of your <body> for the theme-color, which means that you might get the expected result without defining the theme-color explicitly.

<meta name="theme-color" content="lab(29.2345% 39.3825 20.0664)">

Please be aware that at the time of writing Safari 15 is the only browser to support these new colors functions.

currentColor

If CSS colors are supported, currentColor should work, too, right? No, unfortunately not in any browser. It’s probably an uncommon use case, but I would expect that we can set the theme-color to the current color of the <body> or <html> element.

<style> body { color: blue; } </style> <meta name="theme-color" content="currentColor">

I found a ticket in the WebKit bug tracker titled “<meta name="theme-color" content="..."> should also support CSS currentcolor.” Support might change in the future, if someone picks the ticket up.

Prohibited colors

When I was testing CSS color keywords, I used the color red and it didn’t work. First, I thought that keywords weren’t supported, but blue, hotpink, and green worked fine. As is turns out, there’s a narrow range of colors that Safari doesn’t support, colors that would get in the way of using the interface. red doesn’t work because it’s visually too close to the background color of the close button in the tab bar. This limitation is specific to Safari, in all other supported browsers any color seem to work fine.

If you set the theme-color to red, Safari uses any color it deems appropriate. Custom properties

I don’t know enough about the internals of browsers and custom properties and if it’s even possible to access custom properties in the <head>, but I tried it anyway. Unfortunately, it didn’t work in any browser.

<style> :root { --theme: blue; } </style> <meta name="theme-color" content="var(--theme)">

That’s pretty much everything I wanted to know about basic support of the theme-color meta tag. Next, let’s see how to and how not to implement dark mode for the tab bar.

Dark mode

Safari 15 is the first desktop browser to support the media attribute and the prefers-color-scheme media feature on theme-color meta tags. Starting with version 93, Chrome supports it too, but only for installed progressive web apps.

According to the web app manifest page on web.dev, if you define multiple theme-color meta tags, browsers pick the first tag that matches.

<meta name="theme-color" content="#872e4e" media="(prefers-color-scheme: dark)">

I was eager to find out what happens in browsers that don’t support the media attribute. I’ve created a demo page for testing dark mode that includes the meta tags above and also allows you to install the site as a PWA. The webmanifest.json includes another color definition for the theme-color.

{ "name": "My PWA", "icons": [ { "src": "https://via.placeholder.com/144/00ff00", "sizes": "144x144", "type": "image/png" } ], "start_url": "/theme-color-darkmode.html", "display": "standalone", "background_color": "hsl(24.3, 97.4%, 54.3%)", "theme_color": "hsl(24.3, 97.4%, 54.3%)" }

Here’s how supported browsers display the tab bar in light mode. It doesn’t matter if a browser supports the media attribute or not, it will interpret the first meta tag regardless.

Here’s how the tab bar on the same page looks like in dark mode. These results are more interesting because they vary a bit. The Canary PWA and Safari support and show the dark color. All mobile browsers use their default dark tab bar styling, except for Samsung Internet, which uses the light styling because it doesn’t support the prefers-color-scheme media feature. (TIL: This should change in the near future.)

I did one last test. I wanted to see what happens if I only define a theme color for dark mode, but access the page in light mode.

<meta name="theme-color" content="#872e4e" media="(prefers-color-scheme: dark)">

These results surprised me the most because I expected all mobile browsers to ignore the media attribute and just use the dark color in the meta tag regardless, but ordinary Chrome Canary completely ignores the whole meta tag, even though it doesn’t support the media attribute. As expected, both Canary PWAs fall back to the color defined in the manifest file.

The other interesting thing is that Safari displays a theme-color even though I haven’t defined one for light mode. That’s because Safari will pick a color on its own, if you don’t provide a theme-color. In this case, it uses the background color of the page, but it also might use the background color of the <header> element, for example.

If you want to define a theme color for light and dark mode, your best bet is to define both colors and use the first meta tag as a fallback for browsers that don’t support the media feature.

<meta name="theme-color" content="#319197" media="(prefers-color-scheme: light)"> <meta name="theme-color" content="#872e4e" media="(prefers-color-scheme: dark)">

Safari has proven that theme-color works great on desktop browsers, too. I’m sure that designers and developers will find many creative ways to use this meta tag, especially considering that the value can be changed via JavaScript. I’ve collected and created some interesting demos for your inspiration.

Demos and use cases Theming

poolsuite.net provides different themes for the site and changes the theme-color accordingly.

Max Böck also changes the theme-color on his website when you change the theme.

Page theming

Most websites don’t provide custom themes, but you can still give your pages that certain something. Dave uses different key colors in his blog posts for links and icons, and now also in the tab bar.

Gradients

If you’re using gradients on your page, you can highlight your styling by making the gradient span the whole browser. The theme-color meta tag doesn’t support gradients, but you can use the same color for the meta tag and the start color of the gradient of you page’s background.

<meta name="theme-color" content="rgb(0, 235, 255)"> <style> body { background: linear-gradient(rgb(0, 235, 255), #08124a); } </style> Form validation

I built this proof of concept of a form that changes theme-color on form validation. It starts with a blue tab bar which turns red if the submitted data is invalid or green if it’s valid.

const email = document.querySelector('input') const themeColor = document.querySelector('meta[name="theme-color"]') const msg = document.querySelector('[aria-live]') let color = '#FA0000' let message = 'Error message' document.querySelector('button').addEventListener('click', (e) => { e.preventDefault() email.reportValidity() email.setAttribute('aria-invalid', true) if (email.validity.valid) { color = '#00FF00' message = "Success message!" email.setAttribute('aria-invalid', false) } msg.textContent = message themeColor.setAttribute('content', color) }); Disco mode

I’m not saying that you should, but you could put your site in &#x1f483; Disco Mode &#x1f57a; by combining setInterval and hsl() colors.

/* Inspired by https://twitter.com/argyleink/status/1408184587885309952 */ const motion = window.matchMedia("(prefers-reduced-motion: no-preference)"); // Check if users don't have a preference for reduced motion if (motion.matches) { let scheme = document.querySelector('meta[name="theme-color"]') let hue = 0 let color setInterval(() => { color = `hsl(${hue+=5} 50% 30%)` document.body.style.background = color; scheme.setAttribute('content', color) }, 50) Scrolling

Stuart had a great idea, he suggested changing theme color on scroll. I built this quick prototype, again using hsl() colors.

Please only do this if it doesn’t affect performance negatively.

Max built a demo in which he changes the theme-color according to the background color of the current section in the viewport using Intersection Observer.

const setThemeColor = (color) => { const meta = document.querySelector('meta[name="theme-color"]') if (meta) { meta.setAttribute('content', color) } } if ("IntersectionObserver" in window) { const observer = new IntersectionObserver(entries => { entries.forEach(entry => { const { isIntersecting, target } = entry if (isIntersecting) { const color = window.getComputedStyle(target).getPropertyValue("background-color"); setThemeColor(color) } }) }, { root: document.getElementById('viewport'), rootMargin: "1px 0px -100% 0px", treshold: 0.1 }) document.querySelectorAll('.section').forEach(section => { observer.observe(section) }) } Extracting color

Another interesting idea is to extract the dominant or average color from your header images automatically and use it as the theme-color.

<script type="module"> import fastAverageColor from "https://cdn.skypack.dev/fast-average-color@6.4.0"; const fac = new fastAverageColor(); fac.getColorAsync(document.querySelector('img')) .then(color => { document.querySelector('meta[name="theme-color"]').setAttribute('content', color.rgba) }) .catch(e => { console.log(e); }); </script> <img src="/amy-humphries-2M_sDJ_agvs-unsplash.jpg" alt="A sea star on blue sand." />

That is just a handful of ideas, but I already like where this is going and I’m sure that you’ll come up with even more creatives ways of using the theme-color meta tag.

Resources

The post Meta Theme Color and Trickery appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Syndicate content
©2003 - Present Akamai Design & Development.