Tag: Create

How to Create a Realistic Motion Blur with CSS Transitions

Before we delve into making a realistic motion blur in CSS, it’s worth doing a quick dive into what motion blur is, so we can have a better idea of what we’re trying to reproduce.

Have you ever taken a photo of something moving quickly, especially under low light, and it turned into a blurry streak? Or maybe the whole camera shook and the whole shot became a series of streaks? This is motion blur, and it’s a byproduct of how a camera works.

Motion Blur 101

Imagine a camera. It’s got a shutter, a door that opens to let light in, and then closes to stop the light from coming in. From the time it opens, to when it closes, is a single photograph, or a single frame of a moving image.

A blurry man wearing a red shirt on a blue bike speeding through the forest.
Real motion blur in action (Photo: Kevin Erdvig, Unsplash)

If the subject of the frame is moving during the time the shutter is open, we end up taking a photo of an object moving through the frame. On film, this shows up as being a steady smear, with the subject being in an infinite number of places between its starting point to its end. The moving object also ends up being semi-transparent, with parts of the background visible behind it.

What computers do to fake this is model several subframes, and then composite them together at a fraction of the opacity. Putting lots of copies of the same object in slightly different places along the path of motion creates a pretty convincing facsimile of a motion blur.

Video compositing apps tend to have settings for how many subdivisions their motion blur should have. If you set this value really low, you can see exactly how the technique works, like this, a frame of an animation of a simple white dot at four samples per frame:

Four overlapping white opaque circles on a black background.
Four samples per frame.
Twelve overlapping white opaque circles on a black background.
Here is 12 samples per frame.
Thirty-two overlapping white opaque circles on a black background.
And by the time we’re at 32 samples per frame, it’s pretty close to fully real, especially when seen at multiple frames per second.

The number of samples it takes to make a convincing motion blur is entirely relative to the content. Something small with sharp edges that’s moving super fast will need a lot of subframes; but something blurry moving slowly might need only a few. In general, using more will create a more convincing effect.

Doing this in CSS

In order to approximate this effect in CSS, we need to create a ton of identical elements, make them semi-transparent, and offset their animations by a tiny fraction of a second.

First, we’ll set up the base with the animation we want using a CSS transition. We’ll go with a simple black dot, and assign it a transform on hover (or tap, if you’re on mobile). We’ll also animate the border radius and color to show the flexibility of this approach.

Here is the base animation without motion blur:

Now, let’s make 20 identical copies of the black dot, all placed in the exact same place with absolute positioning. Each copy has an opacity of 10%, which is a little more than might be mathematically correct, but I find we need to make them more opaque to look solid enough.

The next step is where the magic happens. We add a slightly-increasing transition-delay value for each clone of our dot object. They’ll all run the exact same animation, but they’ll each be offset by three milliseconds. 

The beauty of this approach is that it creates a pseudo-motion blur effect that works for a ton of different animations. We can throw color changes on there, scaling transitions, odd timings, and the motion blur effect still works.

Using 20 object clones will work for plenty of fast and slow animations, but fewer can still produce a reasonable sense of motion blur. You may need to tweak the number of cloned objects, their opacity, and the amount of transition delay to work with your particular animation. The demo we just looked at has the blur effect slightly overclocked to make it more prominent.


Eventually, with the progress of computer power, I expect that some of the major browsers might start offering this effect natively. Then we can do away with the ridiculousness of having 20 identical objects. In the meantime, this is a reasonable way to approximate a realistic motion blur.


The post How to Create a Realistic Motion Blur with CSS Transitions appeared first on CSS-Tricks.

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

CSS-Tricks

, , , ,

Let’s Create Our Own Authentication API with Nodejs and GraphQL

Authentication is one of the most challenging tasks for developers just starting with GraphQL. There are a lot of technical considerations, including what ORM would be easy to set up, how to generate secure tokens and hash passwords, and even what HTTP library to use and how to use it. 

In this article, we’ll focus on local authentication. It’s perhaps the most popular way of handling authentication in modern websites and does so by requesting the user’s email and password (as opposed to, say, using Google auth.)

Moreover, This article uses Apollo Server 2, JSON Web Tokens (JWT), and Sequelize ORM to build an authentication API with Node.

Handling authentication

As in, a log in system:

  • Authentication identifies or verifies a user.
  • Authorization is validating the routes (or parts of the app) the authenticated user can have access to. 

The flow for implementing this is:

  1. The user registers using password and email
  2. The user’s credentials are stored in a database
  3. The user is redirected to the login when registration is completed
  4. The user is granted access to specific resources when authenticated
  5. The user’s state is stored in any one of the browser storage mediums (e.g. localStorage, cookies, session) or JWT.

Pre-requisites

Before we dive into the implementation, here are a few things you’ll need to follow along.

Dependencies 

This is a big list, so let’s get into it:

  • Apollo Server: An open-source GraphQL server that is compatible with any kind of GraphQL client. We won’t be using Express for our server in this project. Instead, we will use the power of Apollo Server to expose our GraphQL API.
  • bcryptjs: We want to hash the user passwords in our database. That’s why we will use bcrypt. It relies on Web Crypto API‘s getRandomValues interface to obtain secure random numbers.
  • dotenv: We will use dotenv to load environment variables from our .env file. 
  • jsonwebtoken: Once the user is logged in, each subsequent request will include the JWT, allowing the user to access routes, services, and resources that are permitted with that token. jsonwebtokenwill be used to generate a JWT which will be used to authenticate users.
  • nodemon: A tool that helps develop Node-based applications by automatically restarting the node application when changes in the directory are detected. We don’t want to be closing and starting the server every time there’s a change in our code. Nodemon inspects changes every time in our app and automatically restarts the server. 
  • mysql2: An SQL client for Node. We need it connect to our SQL server so we can run migrations.
  • sequelize: Sequelize is a promise-based Node ORM for Postgres, MySQL, MariaDB, SQLite and Microsoft SQL Server. We will use Sequelize to automatically generate our migrations and models. 
  • sequelize cli: We will use Sequelize CLI to run Sequelize commands. Install it globally with yarn add --global sequelize-cli  in the terminal.

Setup directory structure and dev environment

Let’s create a brand new project. Create a new folder and this inside of it:

yarn init -y

The -y flag indicates we are selecting yes to all the yarn init questions and using the defaults.

We should also put a package.json file in the folder, so let’s install the project dependencies:

yarn add apollo-server bcrpytjs dotenv jsonwebtoken nodemon sequelize sqlite3

Next, let’s add Babeto our development environment:

yarn add babel-cli babel-preset-env babel-preset-stage-0 --dev

Now, let’s configure Babel. Run touch .babelrc in the terminal. That creates and opens a Babel config file and, in it, we’ll add this:

{   "presets": ["env", "stage-0"] }

It would also be nice if our server starts up and migrates data as well. We can automate that by updating package.json with this:

"scripts": {   "migrate": " sequelize db:migrate",   "dev": "nodemon src/server --exec babel-node -e js",   "start": "node src/server",   "test": "echo "Error: no test specified" && exit 1" },

Here’s our package.json file in its entirety at this point:

{   "name": "graphql-auth",   "version": "1.0.0",   "main": "index.js",   "scripts": {     "migrate": " sequelize db:migrate",     "dev": "nodemon src/server --exec babel-node -e js",     "start": "node src/server",     "test": "echo "Error: no test specified" && exit 1"   },   "dependencies": {     "apollo-server": "^2.17.0",     "bcryptjs": "^2.4.3",     "dotenv": "^8.2.0",     "jsonwebtoken": "^8.5.1",     "nodemon": "^2.0.4",     "sequelize": "^6.3.5",     "sqlite3": "^5.0.0"   },   "devDependencies": {     "babel-cli": "^6.26.0",     "babel-preset-env": "^1.7.0",     "babel-preset-stage-0": "^6.24.1"   } }

Now that our development environment is set up, let’s turn to the database where we’ll be storing things.

Database setup

We will be using MySQL as our database and Sequelize ORM for our relationships. Run sequelize init (assuming you installed it globally earlier). The command should create three folders: /config /models and /migrations. At this point, our project directory structure is shaping up. 

Let’s configure our database. First, create a .env file in the project root directory and paste this:

NODE_ENV=development DB_HOST=localhost DB_USERNAME= DB_PASSWORD= DB_NAME=

Then go to the /config folder we just created and rename the config.json file in there to config.js. Then, drop this code in there:

require('dotenv').config() const dbDetails = {   username: process.env.DB_USERNAME,   password: process.env.DB_PASSWORD,   database: process.env.DB_NAME,   host: process.env.DB_HOST,   dialect: 'mysql' } module.exports = {   development: dbDetails,   production: dbDetails }

Here we are reading the database details we set in our .env file. process.env is a global variable injected by Node and it’s used to represent the current state of the system environment.

Let’s update our database details with the appropriate data. Open the SQL database and create a table called graphql_auth. I use Laragon as my local server and phpmyadmin to manage database tables.

What ever you use, we’ll want to update the .env file with the latest information:

NODE_ENV=development DB_HOST=localhost DB_USERNAME=graphql_auth DB_PASSWORD= DB_NAME=<your_db_username_here>

Let’s configure Sequelize. Create a .sequelizerc file in the project’s root and paste this:

const path = require('path') 
 module.exports = {   config: path.resolve('config', 'config.js') }

Now let’s integrate our config into the models. Go to the index.js in the /models folder and edit the config variable.

const config = require(__dirname + '/../../config/config.js')[env]

Finally, let’s write our model. For this project, we need a User model. Let’s use Sequelize to auto-generate the model. Here’s what we need to run in the terminal to set that up:

sequelize model:generate --name User --attributes username:string,email:string,password:string

Let’s edit the model that creates for us. Go to user.js in the /models folder and paste this:

'use strict'; module.exports = (sequelize, DataTypes) => {   const User = sequelize.define('User', {     username: {       type: DataTypes.STRING,     },     email: {       type: DataTypes.STRING,       },     password: {       type: DataTypes.STRING,     }   }, {});   return User; };

Here, we created attributes and fields for username, email and password. Let’s run a migration to keep track of changes in our schema:

yarn migrate

Let’s now write the schema and resolvers.

Integrate schema and resolvers with the GraphQL server 

In this section, we’ll define our schema, write resolver functions and expose them on our server.

The schema

In the src folder, create a new folder called /schema and create a file called schema.js. Paste in the following code:

const { gql } = require('apollo-server') const typeDefs = gql`   type User {     id: Int!     username: String     email: String!   }   type AuthPayload {     token: String!     user: User!   }   type Query {     user(id: Int!): User     allUsers: [User!]!     me: User   }   type Mutation {     registerUser(username: String, email: String!, password: String!): AuthPayload!     login (email: String!, password: String!): AuthPayload!   } ` module.exports = typeDefs

Here we’ve imported graphql-tag from apollo-server. Apollo Server requires wrapping our schema with gql

The resolvers

In the src folder, create a new folder called /resolvers and create a file in it called resolver.js. Paste in the following code:

const bcrypt = require('bcryptjs') const jsonwebtoken = require('jsonwebtoken') const models = require('../models') require('dotenv').config() const resolvers = {     Query: {       async me(_, args, { user }) {         if(!user) throw new Error('You are not authenticated')         return await models.User.findByPk(user.id)       },       async user(root, { id }, { user }) {         try {           if(!user) throw new Error('You are not authenticated!')           return models.User.findByPk(id)         } catch (error) {           throw new Error(error.message)         }       },       async allUsers(root, args, { user }) {         try {           if (!user) throw new Error('You are not authenticated!')           return models.User.findAll()         } catch (error) {           throw new Error(error.message)         }       }     },     Mutation: {       async registerUser(root, { username, email, password }) {         try {           const user = await models.User.create({             username,             email,             password: await bcrypt.hash(password, 10)           })           const token = jsonwebtoken.sign(             { id: user.id, email: user.email},             process.env.JWT_SECRET,             { expiresIn: '1y' }           )           return {             token, id: user.id, username: user.username, email: user.email, message: "Authentication succesfull"           }         } catch (error) {           throw new Error(error.message)         }       },       async login(_, { email, password }) {         try {           const user = await models.User.findOne({ where: { email }})           if (!user) {             throw new Error('No user with that email')           }           const isValid = await bcrypt.compare(password, user.password)           if (!isValid) {             throw new Error('Incorrect password')           }           // return jwt           const token = jsonwebtoken.sign(             { id: user.id, email: user.email},             process.env.JWT_SECRET,             { expiresIn: '1d'}           )           return {            token, user           }       } catch (error) {         throw new Error(error.message)       }     }   }, 
 } module.exports = resolvers

That’s a lot of code, so let’s see what’s happening in there.

First we imported our models, bcrypt and  jsonwebtoken, and then initialized our environmental variables. 

Next are the resolver functions. In the query resolver, we have three functions (me, user and allUsers):

  • me query fetches the details of the currently loggedIn user. It accepts a user object as the context argument. The context is used to provide access to our database which is used to load the data for a user by the ID provided as an argument in the query.
  • user query fetches the details of a user based on their ID. It accepts id as the context argument and a user object. 
  • alluser query returns the details of all the users.

user would be an object if the user state is loggedIn and it would be null, if the user is not. We would create this user in our mutations. 

In the mutation resolver, we have two functions (registerUser and loginUser):

  • registerUser accepts the username, email  and password of the user and creates a new row with these fields in our database. It’s important to note that we used the bcryptjs package to hash the users password with bcrypt.hash(password, 10). jsonwebtoken.sign synchronously signs the given payload into a JSON Web Token string (in this case the user id and email). Finally, registerUser returns the JWT string and user profile if successful and returns an error message if something goes wrong.
  • login accepts email and password , and checks if these details match with the one that was supplied. First, we check if the email value already exists somewhere in the user database.
models.User.findOne({ where: { email }}) if (!user) {   throw new Error('No user with that email') }

Then, we use bcrypt’s bcrypt.compare method to check if the password matches. 

const isValid = await bcrypt.compare(password, user.password) if (!isValid) {   throw new Error('Incorrect password') }

Then, just like we did previously in registerUser, we use jsonwebtoken.sign to generate a JWT string. The login mutation returns the token and user object.

Now let’s add the JWT_SECRET to our .env file.

JWT_SECRET=somereallylongsecret

The server

Finally, the server! Create a server.js in the project’s root folder and paste this:

const { ApolloServer } = require('apollo-server') const jwt =  require('jsonwebtoken') const typeDefs = require('./schema/schema') const resolvers = require('./resolvers/resolvers') require('dotenv').config() const { JWT_SECRET, PORT } = process.env const getUser = token => {   try {     if (token) {       return jwt.verify(token, JWT_SECRET)     }     return null   } catch (error) {     return null   } } const server = new ApolloServer({   typeDefs,   resolvers,   context: ({ req }) => {     const token = req.get('Authorization') || ''     return { user: getUser(token.replace('Bearer', ''))}   },   introspection: true,   playground: true }) server.listen({ port: process.env.PORT || 4000 }).then(({ url }) => {   console.log(`🚀 Server ready at $ https://css-tricks.com/lets-create-our-own-authentication-api-with-nodejs-and-graphql/`); });

Here, we import the schema, resolvers and jwt, and initialize our environment variables. First, we verify the JWT token with verify. jwt.verify accepts the token and the JWT secret as parameters.

Next, we create our server with an ApolloServer instance that accepts typeDefs and resolvers.

We have a server! Let’s start it up by running yarn dev in the terminal.

Testing the API

Let’s now test the GraphQL API with GraphQL Playground. We should be able to register, login and view all users — including a single user — by ID.

We’ll start by opening up the GraphQL Playground app or just open localhost://4000 in the browser to access it.

Mutation for register user

mutation {   registerUser(username: "Wizzy", email: "ekpot@gmail.com", password: "wizzyekpot" ){     token   } }

We should get something like this:

{   "data": {     "registerUser": {       "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTUsImVtYWlsIjoiZWtwb3RAZ21haWwuY29tIiwiaWF0IjoxNTk5MjQwMzAwLCJleHAiOjE2MzA3OTc5MDB9.gmeynGR9Zwng8cIJR75Qrob9bovnRQT242n6vfBt5PY"     }   } }

Mutation for login 

Let’s now log in with the user details we just created:

mutation {   login(email:"ekpot@gmail.com" password:"wizzyekpot"){     token   } }

We should get something like this:

{   "data": {     "login": {       "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTUsImVtYWlsIjoiZWtwb3RAZ21haWwuY29tIiwiaWF0IjoxNTk5MjQwMzcwLCJleHAiOjE1OTkzMjY3NzB9.PDiBKyq58nWxlgTOQYzbtKJ-HkzxemVppLA5nBdm4nc"     }   } }

Awesome!

Query for a single user

For us to query a single user, we need to pass the user token as authorization header. Go to the HTTP Headers tab.

Showing the GraphQL interface with the HTTP Headers tab highlighted in red in the bottom left corner of the screen,

…and paste this:

{   "Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTUsImVtYWlsIjoiZWtwb3RAZ21haWwuY29tIiwiaWF0IjoxNTk5MjQwMzcwLCJleHAiOjE1OTkzMjY3NzB9.PDiBKyq58nWxlgTOQYzbtKJ-HkzxemVppLA5nBdm4nc" }

Here’s the query:

query myself{   me {     id     email     username   } }

And we should get something like this:

{   "data": {     "me": {       "id": 15,       "email": "ekpot@gmail.com",       "username": "Wizzy"     }   } }

Great! Let’s now get a user by ID:

query singleUser{   user(id:15){     id     email     username   } }

And here’s the query to get all users:

{   allUsers{     id     username     email   } }

Summary

Authentication is one of the toughest tasks when it comes to building websites that require it. GraphQL enabled us to build an entire Authentication API with just one endpoint. Sequelize ORM makes creating relationships with our SQL database so easy, we barely had to worry about our models. It’s also remarkable that we didn’t require a HTTP server library (like Express) and use Apollo GraphQL as middleware. Apollo Server 2, now enables us to create our own library-independent GraphQL servers!

Check out the source code for this tutorial on GitHub.


The post Let’s Create Our Own Authentication API with Nodejs and GraphQL appeared first on CSS-Tricks.

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

CSS-Tricks

, , , ,
[Top]

The Trickery it Takes to Create eBook-Like Text Columns

There’s some interesting CSS trickery in Jason Pamental’s latest Web Fonts & Typography News. Jason wanted to bring swipeable columns to his digital book experience on mobile. Which brings up an interesting question right away… how do you set full-width columns that add columns horizontally, as-needed ? Well that’s a good trick right there, and it’s a one-liner:

columns: 100vw auto;

But it gets more complicated and disappointing from there.

With just a smidge more formatting to the columns:

main {   columns: 100vw auto;   column-gap: 2rem;   overflow-x: auto;   height: calc(100vh - 2rem);   font: 120%/1.4 Georgia; }

We get this:

Which is so close to being perfect!

We probably wouldn’t apply this effect on desktop, but hey, that’s what media queries are for. On mobile we get…

That herky-jerky scrolling makes this a bad experience right there. We can smooth that out with -webkit-overflow-scrolling: touch;

The smoothness is maybe better, but the fact that the columns don’t snap into place makes it almost just as bad of a reading experience. That’s what scroll-snap is for, but alas:

Unfortunately it turns out you need a block-level element to which you can snap, and the artificially-created columns don’t count as such.

Oh noooooo. So close! But so far!

If we actually want scroll snapping, the content will need to be in block-level elements (like <div>). It’s easy enough to set up a horizontal row of <div> elements with flexbox like…

main {   display: flex; } main > div {   flex: 0 0 100vw; }

But… how many divs do we need? Who knows! This is arbitrary content that might change. And even if we did know, how would we flow content naturally between the divs? That’s not a thing. That’s why it sucks that CSS regions never happened. So to make this nice swiping experience possible in CSS, we either need to:

  • Allow scroll snapping to work on columns
  • Have some kind of CSS regions that is capable of auto-generating repeating block level elements as-needed by content

Neither of which is possible right now.

Jason didn’t stop there! He used JavaScript to figure out something that stops well short of some heavy scrolljacking thing. First, he figures out how many “pages” wide the CSS columns technique produces. Then, he adds spacer-divs to the scrolling element, each one the width of the page, and those are the things the scrolling element can scroll-snap to. Very clever.

At the moment, you can experience it at the book site by flipping on an optional setting.

The post The Trickery it Takes to Create eBook-Like Text Columns appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,
[Top]

Using CSS Masks to Create Jagged Edges

I was working on a project that had this neat jagged edge along the bottom of a banner image.

Looking sharp… in more ways than one.

It’s something that made me think for a second and I learned something in the process! I thought I’d write up how I approached it so you can use it on your own projects.

I started out with a good old fashioned HTML image in a wrapper element:

<div class="jagged-wrapper">   <img src="path-to-image.jpg" /> </div>

Then I used its ::after pseudo element to drop a repeating background image on it:

.jagged-wrapper::after {   content: "";   background-image: url('data:image/svg+xml;utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1 1" preserveAspectRatio="none"><polygon style="fill:white;" points="1,0 1,1 0,1 "/></svg>');   background-size: 30px 30px;   width: 100%;   height: 30px;   position: absolute;   bottom: 0px;   right: 0;   z-index: 2; }

That background image? It’s SVG code converted to a Data URI. Here’s the original SVG code. Chris has a nice video where he walks through that conversion.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1 1" preserveAspectRatio="none">   <polygon style="fill: white;" points="1,0 1,1 0,1 "/> </svg>

“There we go!” I thought.

While that certainly works, geez, that’s a lot of hassle. It’s difficult to read SVG markup in CSS like that. Plus, it’s annoying to have to remember to quote them (e.g. url('data:image/svg+xml'...)). Sure, we can base64 encode the SVG to avoid that, but that’s even more annoying. Plus, the SVG needs to be filled with the same background color as the image (or wherever it is used), or else it won’t work.

Wait, isn’t this what masking is for? Yes! Yes, this is what masking is for.

That led me to a new approach: use an image like the one above as a CSS mask so that the “missing” bits of the banner image would actually be missing. Rather than drawing triangles of the background color on top of the banner, we should instead mask away those triangles from the banner entirely and let the real background show through. That way, it works on any background!

Masking is pretty much supported everywhere — at least in the simple way I’m talking about here. We’re also talking about something that can be implemented with progressive enhancement; if masks aren’t supported in a given browser, then you just don’t get the sawtooth effect. Definitely not the end of the world.

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

Desktop

Chrome Firefox IE Edge Safari
85* 53 No 81* TP*

Mobile / Tablet

Android Chrome Android Firefox Android iOS Safari
81* 68 81* 13.4*

One way a CSS mask works is to provide an image with an alpha channel as a mask-image. The underlying element — the one that’s being masked — becomes (semi-)transparent to the degree that the alpha channel of the mask-image dictates. So if your mask image is a white teapot on a transparent background, then the masked element will be cut to the shape of the teapot and everything outside that will be hidden.

Masking can be a tricky concept to grok. Sarah Drasner has an article that takes a deep-dive on masking, including how it is different from clipping. There’s much more that masks can do than what we’re covering here. Check the specs, caniuse, and MDN for even more information.

What we need is a single “sawtooth”-like image similar to the SVG above, where the top-left half is filled white and the bottom-left is left semi-transparent. And, ideally that image wouldn’t be an actual SVG, since that would land us back into the ugly data URI mess we were in before.

At this point you might be thinking: “Hey, just embed the SVG in the CSS directly, define a mask in it, then point the CSS at the mask ID in the SVG!”

Nice idea! And it’s certainly doable, if you can edit the HTML. For my specific project, however, I was working in WordPress and I really wanted to confine my changes to pure CSS rather than injecting extra parts into the HTML. That would have been a lot more work. I don’t think this is uncommon; for a presentational change like this, not having to edit the HTML is useful. We’re mostly on board with the idea of avoiding semantically worthless wrapper elements just to provide styling hooks, but I feel that also applies to adding entire SVG markup to the document… or even a WordPress template.

We can use a CSS linear gradient to create a triangle shape instead:

.el {   linear-gradient(     to bottom right,     white,     white 50%,     transparent 50%,     transparent   ); }

Here that is on a radial background, so you can see it’s really transparent:

Great! We can just use that as a mask-image on our banner, right? We need to set mask-size, which is like background-size, and mask-repeat, like background-repeat, and we’re good?

Unfortunately, no. Not so good.

The first reason is that, unless you’re using Firefox, you’ll likely see no masking at all on that example above. This is because Blink and WebKit still only support masking with a vendor prefix at the time of writing. That means we need -webkit- prefixed versions of everything.

Vendor prefixing aside, what we’re doing is also conceptually wrong. If we confine the mask to just the bottom stripe of the image with mask-size, then the rest of the image has no mask-image at all, which masks it out entirely. As a result, we can’t use the sawtooth alone as a mask. What we need is a mask-image that is a rectangle the size of the image with just a sawtooth at the bottom. 

Something like this:

We do that with two gradient images. The first image is the same sawtooth triangle as above, which is set to repeat-x and positioned at the bottom so that it repeats only along the bottom edge of the image. The second image is another gradient that is transparent for the bottom 30px (so as to not interfere with the sawtooth), opaque above that (which is shown going from black to white in the demo), and takes up the whole size of the element. 

So we now have this wedge-shaped piece, with a single triangle sawtooth at the bottom, and it occupies the entire height of our banner image in two separate pieces. Finally, we can use these pieces with mask-image by repeating them horizontally across our image, and it should have the effect we want:

And there we have it!

The post Using CSS Masks to Create Jagged Edges appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

How to Create Custom WordPress Editor Blocks in 2020

Peter Tasker on creating blocks right now:

It’s fairly straightforward these days to get set up with the WP CLI ‘scaffold’ command. This command will set up a WordPress theme or plugin with a ‘blocks’ folder that contains the PHP and base CSS and JavaScript required to create a custom block. The only drawback that I noticed is that the JavaScript uses the old ES5 syntax rather than modern ESNext. Modern JavaScript allows us to write more concise code and use JSX in our custom block code.

You can also use the ‘create-guten-block’ tool by Ahmad Awais. It gives you a lot of the boilerplate stuff you need out of the box, like Webpack, ESNext support etc. Setting it up is fairly straightforward, and it’s similar to Create React App.

I’ve used create-guten-block for the handful of custom blocks I’ve made so far, and have found it a pretty nice experience.

But… I feel like I just sort of lucked into being comfortable with all this. I have one foot in WordPress development and just so happen to have one foot in React development. Building blocks with both technologies together feels decently natural to me. If blocks were Angular or something, I feel like I might not have even given it a shot.

I’ll echo this sentiment:

I also found it really annoying working on a block that’s actively changing in code. Every time you reload Gutenberg, you’ll get the “This block appears to have been modified externally…” message because the markup of the block has changed.

I get why it’s throwing the error, but it slows you down.

At the end, Peter mentions the approach of building blocks that Advanced Custom Fields has. It almost feels like a weird bizarro-reverso world. The ACF approach seems more like what WordPress would have done in a normal world (building blocks with just PHP and templating) and third-parties would be the ones adding all the fancy React stuff.

Direct Link to ArticlePermalink

The post How to Create Custom WordPress Editor Blocks in 2020 appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,
[Top]

Create a Responsive CSS Motion Path? Sure We Can!

There was a discussion recently on the Animation at Work Slack: how could you make a CSS motion path responsive? What techniques would be work? This got me thinking.

A CSS motion path allows us to animate elements along custom user-defined paths. Those paths follow the same structure as SVG paths. We define a path for an element using offset-path.

.block {   offset-path: path('M20,20 C20,100 200,0 200,100'); }

These values appear relative at first and they would be if we were using SVG. But, when used in an offset-path, they behave like px units. This is exactly the problem. Pixel units aren’t really responsive. This path won’t flex as the element it is in gets smaller or larger. Let’s figure this out.

To set the stage, the offset-distance property dictates where an element should be on that path:

Not only can we define the distance an element is along a path, but we can also define an element’s rotation with offset-rotate. The default value is auto which results in our element following the path. Check out the property’s almanac article for more values.

To animate an element along the path, we animate the offset-distance:

OK, that catches up to speed on moving elements along a path. Now we have to answer…

Can we make responsive paths?

The sticking point with CSS motion paths is the hardcoded nature. It’s not flexible. We are stuck hardcoding paths for particular dimensions and viewport sizes. A path that animates an element 600px, will animate that element 600px regardless of whether the viewport is 300px or 3440px wide.

This differs from what we are familiar with when using SVG paths. They will scale with the size of the SVG viewbox.

Try resizing this next demo below and you’ll see:

  • The SVG will scale with the viewport size as will the contained path.
  • The offset-path does not scale and the element goes off course.

This could be okay for simpler paths. But once our paths become more complicated, it will be hard to maintain. Especially if we wish to use paths we’ve created in vector drawing applications.

For example, consider the path we worked with earlier:

.element {   --path: 'M20,20 C20,100 200,0 200,100';   offset-path: path(var(--path)); }

To scale that up to a different container size, we would need to work out the path ourselves, then apply that path at different breakpoints. But even with this “simple” path, is it a case of multiplying all the path values? Will that give us the right scaling?

@media(min-width: 768px) {   .element {     --path: 'M40,40 C40,200 400,0 400,200'; // ????   } }

A more complex path such as one drawn in a vector application is going to be trickier to maintain. It will need the developer to open the application, rescale the path, export it, and integrate it with the CSS. This will need to happen for all container size variations. It’s not the worst solution, but it does require a level of maintenance that we might not want to get ourselves into.

.element {   --path: 'M40,228.75L55.729166666666664,197.29166666666666C71.45833333333333,165.83333333333334,102.91666666666667,102.91666666666667,134.375,102.91666666666667C165.83333333333334,102.91666666666667,197.29166666666666,165.83333333333334,228.75,228.75C260.2083333333333,291.6666666666667,291.6666666666667,354.5833333333333,323.125,354.5833333333333C354.5833333333333,354.5833333333333,386.0416666666667,291.6666666666667,401.7708333333333,260.2083333333333L417.5,228.75';   offset-path: path(var(--path)); } 
 @media(min-width: 768px) {   .element {     --path: 'M40,223.875L55.322916666666664,193.22916666666666C70.64583333333333,162.58333333333334,101.29166666666667,101.29166666666667,131.9375,101.29166666666667C162.58333333333334,101.29166666666667,193.22916666666666,162.58333333333334,223.875,223.875C254.52083333333334,285.1666666666667,285.1666666666667,346.4583333333333,315.8125,346.4583333333333C346.4583333333333,346.4583333333333,377.1041666666667,285.1666666666667,392.4270833333333,254.52083333333334L407.75,223.875';   } } 
 @media(min-width: 992px) {   .element {     --path: 'M40,221.625L55.135416666666664,191.35416666666666C70.27083333333333,161.08333333333334,100.54166666666667,100.54166666666667,130.8125,100.54166666666667C161.08333333333334,100.54166666666667,191.35416666666666,161.08333333333334,221.625,221.625C251.89583333333334,282.1666666666667,282.1666666666667,342.7083333333333,312.4375,342.7083333333333C342.7083333333333,342.7083333333333,372.9791666666667,282.1666666666667,388.1145833333333,251.89583333333334L403.25,221.625';   } }

It feels like a JavaScript solution makes sense here. GreenSock is my first thought because its MotionPath plugin can scale SVG paths. But what if we want to animate outside of an SVG? Could we write a function that scales the paths for us? We could but it won’t be straightforward.

Trying different approaches

What tool allows us to define a path in some way without the mental overhead? A charting library! Something like D3.js allows us to pass in a set of coordinates and receive a generated path string. We can tailor that string to our needs with different curves, sizing, etc.

With a little tinkering, we can create a function that scales a path based on a defined coordinate system:

This definitely works, but it’s also less than ideal because it’s unlikely we are going to be declaring SVG paths using sets of coordinates. What we want to do is take a path straight out of a vector drawing application, optimize it, and drop it on a page. That way, we can invoke some JavaScript function and let that do the heavy lifting.

So that’s exactly what we are going to do.

First, we need to create a path. This one was thrown together quickly in Inkscape. Other vector drawing tools are available.

A path created in Inkscape on a 300×300 canvas

Next, let’s optimize the SVG. After saving the SVG file, we’ll run it through Jake Archibald’s brilliant SVGOMG tool. That gives us something along these lines:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 79.375 79.375" height="300" width="300"><path d="M10.362 18.996s-6.046 21.453 1.47 25.329c10.158 5.238 18.033-21.308 29.039-18.23 13.125 3.672 18.325 36.55 18.325 36.55l12.031-47.544" fill="none" stroke="#000" stroke-width=".265"/></svg>

The parts we’re interested are path and viewBox.

Expanding the JavaScript solution

Now we can create a JavaScript function to handle the rest. Earlier, we created a function that takes a set of data points and converts them into a scalable SVG path. But now we want to take that a step further and take the path string and work out the data set. This way our users never have to worry about trying to convert their paths into data sets.

There is one caveat to our function: Besides the path string, we also need some bounds by which to scale the path against. These bounds are likely to be the third and fourth values of the viewBox attribute in our optimized SVG.

const path = "M10.362 18.996s-6.046 21.453 1.47 25.329c10.158 5.238 18.033-21.308 29.039-18.23 13.125 3.672 18.325 36.55 18.325 36.55l12.031-47.544"; const height = 79.375 // equivalent to viewbox y2 const width = 79.375 // equivalent to viewbox x2 
 const motionPath = new ResponsiveMotionPath({   height,   width,   path, });

We won’t go through this function line-by-line. You can check it out in the demo! But we will highlight the important steps that make this possible.

First, we’re converting a path string into a data set

The biggest part of making this possible is being able to read the path segments. This is totally possible, thanks to the SVGGeometryElement API. We start by creating an SVG element with a path and assigning the path string to its d attribute.

// To convert the path data to points, we need an SVG path element. const svgContainer = document.createElement('div'); // To create one though, a quick way is to use innerHTML svgContainer.innerHTML = `   <svg xmlns="http://www.w3.org/2000/svg">     <path d="$ {path}" stroke-width="$ {strokeWidth}"/>   </svg>`; const pathElement = svgContainer.querySelector('path');

Then we can use the SVGGeometryElement API on that path element. All we need to do is iterate over the total length of the path and return the point at each length of the path.

convertPathToData = path => {   // To convert the path data to points, we need an SVG path element.   const svgContainer = document.createElement('div');   // To create one though, a quick way is to use innerHTML   svgContainer.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg">                               <path d="$ {path}"/>                             </svg>`;   const pathElement = svgContainer.querySelector('path');   // Now to gather up the path points.   const DATA = [];   // Iterate over the total length of the path pushing the x and y into   // a data set for d3 to handle 👍   for (let p = 0; p < pathElement.getTotalLength(); p++) {     const { x, y } = pathElement.getPointAtLength(p);     DATA.push([x, y]);   }   return DATA; }

Next, we generate scaling ratios

Remember how we said we’d need some bounds likely defined by the viewBox? This is why. We need some way to calculate a ratio of the motion path against its container. This ratio will be equal to that of the path against the SVG viewBox. We will then use these with D3.js scales.

We have two functions: one to grab the largest x and y values, and another to calculate the ratios in relation to the viewBox.

getMaximums = data => {   const X_POINTS = data.map(point => point[0])   const Y_POINTS = data.map(point => point[1])   return [     Math.max(...X_POINTS), // x2     Math.max(...Y_POINTS), // y2   ] } getRatios = (maxs, width, height) => [maxs[0] / width, maxs[1] / height]

Now we need to generate the path

The last piece of the puzzle is to actually generate the path for our element. This is where D3.js actually comes into play. Don’t worry if you haven’t used it before because we’re only using a couple of functions from it. Specifically, we are going to use D3 to generate a path string with the data set we generated earlier.

To create a line with our data set, we do this:

d3.line()(data); // M10.362000465393066,18.996000289916992L10.107386589050293, etc.

The issue is that those points aren’t scaled to our container. The cool thing with D3 is that it provides the ability to create scales. These act as interpolation functions. See where this is going? We can write one set of coordinates and then have D3 recalculate the path. We can do this based on our container size using the ratios we generated.

For example, here’s the scale for our x coordinates:

const xScale = d3   .scaleLinear()   .domain([     0,     maxWidth,   ])   .range([0, width * widthRatio]);

The domain is from 0 to our highest x value. The range in most cases will go from 0 to container width multiplied by our width ratio.

There are times where our range may differ and we need to scale it. This is when the aspect ratio of our container doesn’t match that of our path. For example, consider a path in an SVG with a viewBox of 0 0 100 200. That’s an aspect ratio of 1:2. But if we then draw this in a container that has a height and width of 20vmin, the aspect ratio of the container is 1:1. We need to pad the width range to keep the path centered and maintain the aspect ratio.

What we can do in these cases is calculate an offset so that our path will still be centered in our container. 

const widthRatio = (height - width) / height const widthOffset = (ratio * containerWidth) / 2 const xScale = d3   .scaleLinear()   .domain([0, maxWidth])   .range([widthOffset, containerWidth * widthRatio - widthOffset])

Once we have two scales, we can map our data points using the scales and generate a new line.

const SCALED_POINTS = data.map(POINT => [   xScale(POINT[0]),   yScale(POINT[1]), ]); d3.line()(SCALED_POINTS); // Scaled path string that is scaled to our container

We can apply that path to our element by passing it inline via a CSS property 👍

ELEMENT.style.setProperty('--path', `"$ {newPath}"`);

Then it’s our responsibility to decide when we want to generate and apply a new scaled path. Here’s one possible solution:

const setPath = () => {   const scaledPath = responsivePath.generatePath(     CONTAINER.offsetWidth,     CONTAINER.offsetHeight   )   ELEMENT.style.setProperty('--path', `"$ {scaledPath}"`) } const SizeObserver = new ResizeObserver(setPath) SizeObserver.observe(CONTAINER)

This demo (viewed best in full screen) shows three versions of the element using a motion path. The paths are present to easier see the scaling. The first version is the unscaled SVG. The second is a scaling container illustrating how the path doesn’t scale. The third is using our JavaScript solution to scale the path.

Phew, we did it!

This was a really cool challenge and I definitely learned a bunch from it! Here’s a couple of demos using the solution.

It should work as a proof of concept and looks promising! Feel free to drop your own optimized SVG files into this demo to try them out! — it should catch most aspect ratios.

I’ve created a package named “Meanderer” on GitHub and npm. You can also pull it down with unpkg CDN to play with it in CodePen, if you want to try it out.

I look forward to seeing where this might go and hope we might see some native way of handling this in the future. 🙏

The post Create a Responsive CSS Motion Path? Sure We Can! appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

Create Diagonal Layouts Like it’s 2020

Nils Binder covers the ways:

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

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

3. Using CSS Transforms

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

Here’s a kinda dumb clip-path way:

And Nils incredibly fancy playground:

Direct Link to ArticlePermalink

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

CSS-Tricks

, , , , ,
[Top]

How to Create a “Skip to Content” Link

Skip links are little internal navigation links that help users move around a page. It’s possible you’ve never actually seen one before because they’re often hidden from view and used as an accessibility enhancement that lets keyboard users and screen readers jump from the top of the page to the content without have to go through other elements on the page first.

In fact, you can find one right here on CSS-Tricks if you crack DevTools open.

In my opinion, the best way to implement a skip link is to hide it and then bring it into view when it is focused. So, let’s say we have a link in the HTML:

<a class="skip-to-content-link" href="#main">Skip to content</a>

…we can give it an absolute position and translate it off the screen:

.skip-to-content-link {   left: 50%;   position: absolute;   transform: translateY(-100%); }

Then we can bring it back into view when it’s in focus and style it up a bit in the process:

.skip-to-content-link {   background: #e77e23;   height: 30px;   left: 50%;   padding: 8px;   position: absolute;   transform: translateY(-100%);   transition: transform 0.3s; }  .skip-to-content-link:focus {   transform: translateY(0%); }

This will hide our link until it is focused and then put it into view when it becomes focused.

Now, allow me to elaborate, starting with this quote from Miles Davis:

Time isn’t the main thing. It’s the only thing.

As I sit down to write this article in a rainy Ireland, I think of the challenge that many users face as they use the web that I take for granted. We put so much thought into creating a great user experience without thinking of all our users and how to meet their needs. Admittedly, a skip link was something I had never heard of until completing a course on Frontend Masters by Marcy Sutton. Since learning the power and simplicity of using a skip link, I decided to make it my mission to spread more awareness — and what platform better than CSS-Tricks!

A solution is an answer to a problem, so what’s the solution for helping keyboard users and screen readers find the content of a page quickly? In short, the solution is time. Giving users the ability to navigate to parts of our website that they are most interested in gives them the power to save valuable time.

Take the Sky News website as an example. It offers a “Skip to content” button that allows users to skip all the navigation items and jump straight to the main content.

You can see this button by navigating to the top of the page using your keyboard.  This is similar to the implementation shown above. The link is always in the document but only becomes visible when it’s in focus.

This is the type of skip link we are going to create together in this post.

Our sample website

I built a sample website that we will use to demonstrate a skip link.

The website has a number of navigation links but, in the interest of time, there are only two pages: the home page and the blog page. This is all we need to see how things work.

Identifying the problem

Here’s the navigation we’re working with:

In total, we have eight navigation items that a keyboard user and screen reader must tab through before reaching the main content below the navigation.

That’s the problem. The navigation may be irrelevant for the user. Perhaps the user was given a direct link to an article and they simply want to get to the content.

This is a perfect use case for a skip link.

Creating the link

There are a couple of approaches to creating a Skip to content link.  What I like to do is similar to the Sky News example, which is hiding the link until it is focused.  That means we can drop the link at or near the top of the page, like inside the <header> element.

<header>   <a class="skip-link" href='#main'>Skip to content</a>   <!-- Navigation--> </header>

This link has a .skip-link class so we can style it. Thehref attribute points to #main, which is the ID we will add to the <main> element that comes further down the page. That’s what the link will jump to when clicked.

<header>   <a class="skip-link" href='#main'>Skip to content</a>   <!-- Navigation--> </header> 
 <!-- Maybe some other stuff... --> 
 <main id="main">   <!-- Content --> </main>

Here’s what we have if we simply drop the link into the header with no styling.

This does not look great, but the functionality is there. Try navigating to the link with your keyboard and pressing Enter when it’s in focus.

Now it’s time to make it look pretty. We must first fix the positioning and only show our skip link when it is in focus.

.skip-link {   background: #319795;   color: #fff;   font-weight: 700;   left: 50%;   padding: 4px;   position: absolute;   transform: translateY(-100%); }  .skip-link:focus {   transform: translateY(0%); }

The magic here is in the transform property, which is hiding and showing our skip link depending on whether it is focused or not. Let’s make it look a little nicer with a quick transition on the transform property.

.skip-link {   /* Same as  before */   transition: transform 0.3s; }

It will now transition into view which makes that bit better.

You should now (hopefully) have what I have below:

As you can see, the skip link bypasses the navigation and jumps straight down to the <main> element when clicked.

It’s OK to use more than one link!

Right now, the link only serves one purpose and that is skip to the content of our website. But we don’t have to stop there.

We can go even further and create a skip link that has more options, such as a way to jump to the footer of the site. As you might imagine, this is quite similar to what we’ve already done.

Let’s make the blog page of the example site a little more usable by using multiple skip links. It’s common for blogs to use AJAX that loads in more posts when reaching the bottom of the page. This can make it very difficult to get to the footer of the website. That’s the problem we want to solve in this case.

So let’s add a second link that bypasses all that auto-loading business and jumps the user straight to a #footer element on the page.

<div class="skip-link" >   Skip to <a href='#main'>content</a> or <a href='#footer'>footer</a> </div>

We also need to amend our CSS a little and use the :focus-within pseudo selector.

.skip-link {   transform: translateY(-100%); }  .skip-link:focus-within {   transform: translateY(0%); }

This is saying if anything within our .skip-link element has focus, then we show it. Sadly, neither Internet Explorer nor Opera Mini support focus-within, but its coverage is pretty darn good and there is a polyfill available.

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

Desktop

Chrome Firefox IE Edge Safari
60 52 No 79 10.1

Mobile / Tablet

Android Chrome Android Firefox Android iOS Safari
80 68 No 10.3

Last thing we need to do is make sure there’s an ID on our footer element so the link has something to jump to.

<footer id="footer">

Here’s what this gives us:

If we wanted to go one step further (and I’d encourage it), we could style each link a little differently so users can distinguish between the two. Both links in this example are plain white which works great for a single button that does a single thing, but it’d be clearer that we’re dealing with two links if they were presented differently.

Jumping to conclusions

Are you using skip links on your site? Or, if not, does this convince you to use one? I hope it’s clear that skip links are a great value add when it comes to the accessibility of a site. While it’s not a silver bullet that solves every accessibility need, it does solve some use cases that make for a much more polished user experience.

Here are some prominent sites that are using this or a similar technique:

The post How to Create a “Skip to Content” Link appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

Programming Sass to Create Accessible Color Combinations

We’re all looking for low-hanging fruit to make our sites and apps more accessible. One of the easier things we can do is make sure the colors we use are easy on the eyes. High color contrast is something that benefits everyone. It not only reduces eye strain in general, but is crucial for folks who deal with reduced vision.

So let’s not only use better color combinations in our designs but find a way to make it easier for us to implement high contrasts. There’s one specific strategy we use over at Oomph that lets a Sass function do all the heavy lifting for us. I’ll walk you through how we put that together.

Want to jump right to the code because you already understand everything there is to know about color accessibility? Here you go.

What we mean by “accessible color combinations”

Color contrast is also one of those things we may think we have handled. But there’s more to high color contrasts than eyeballing a design. There are different levels of acceptable criteria that the WCAG has defined as being accessible. It’s actually humbling to crack open the WebAIM Contrast Checker and run a site’s color combinations through it.

My team adheres to WCAG’s Level AA guidelines by default. This means that: 

  • Text that is 24px and larger, or 19px and larger if bold, should have a Color Contrast Ratio (CCR) of 3.0:1.
  • Text that is smaller than 24px should have a CCR of 4.5:1.

If a site needs to adhere to the enhanced guidelines for Level AAA, the requirements are a little higher:

  • Text that is 24px and larger, or 19px and larger if bold, should have a CCR of 4.5:1.
  • Text that is smaller than 24px should have a CCR of 7:1.

Ratios? Huh? Yeah, there’s some math involved here. But the good news is that we don’t need to do it ourselves or even have the same thorough understanding about how they’re calculated the way Stacie Arellano recently shared (which is a must read if you’re into the science of color accessibility).

That’s where Sass comes in. We can leverage it to run difficult mathematical computations that would otherwise fly over many of our heads. But first, I think it’s worth dealing with accessible colors at the design level.

Accessible color palettes start with the designs

That’s correct. The core of the work of creating an accessible color palette starts with the designs. Ideally, any web design ought to consult a tool to verify that any color combinations in use pass the established guidelines — and then tweak the colors that don’t. When our design team does this, they use a tool that we developed internally. It works on a list of colors, testing them over a dark and a light color, as well as providing a way to test other combinations. 

ColorCube provides an overview of an entire color palette, showing how each color performs when paired with white, black, and even each other. It even displays results for WCAG Levels AA and AAA next to each result. The tool was designed to throw a lot of information at the user all at once when evaluating a list of colors.

This is the first thing our team does. I’d venture to guess that many brand colors aren’t chosen with accessibility at the forefront. I often find that those colors need to change when they get translated to a web design. Through education, conversation, and visual samples, we get the client to sign off on the new color palette. I’ll admit: that part can be harder than the actual work of implementing accessible colors combinations.

The Color Contrast Audit: A typical design delivery when working with an existing brand’s color palette. Here, we suggest to stop using the brand color Emerald with white, but use an “Alt” version that is slightly darker instead. 

The problem that I wanted to solve with automation are the edge cases. You can’t fault a designer for missing some instance where two colors combine in an unintended way — it just happens. And those edge cases will come up, whether it is during the build or even a year later when new colors are added to the system.

Developing for accessibility while keeping true to the intent of a color system

The trick when changing colors to meet accessibility requirements is not changing them so much that they don’t look like the same color anymore. A brand that loves its emerald green color is going to want to maintain the intent of that color — it’s “emerald-ness.” To make it pass for accessibility when it is used as text over a white background, we might have to darken the green and increase its saturation. But we still want the color to “read” the same as the original color.

To achieve this, we use the Hue Saturation Lightness (HSL) color model. HSL gives us the ability to keep the hue as it is but adjust the saturation (i.e. increase or decrease color) and lightness (i.e. add more black or more white). The hue is what makes a green that green, or a blue that blue. It is the “soul” of the color, to get a little mystical about it. 

Hue is represented as a color wheel with a value between 0° and 360° — yellow at 60°, green at 120°, cyan at 180°, etc. Saturation is a percentage ranging from 0% (no saturation) to 100% (full saturation). Lightness is also a value that goes from 0% to 100%, where no lightness is at 0%, no black and no white is at 50%, and 100% is all lightness, or very light.

A quick visual of what tweaking a color looks like in our tool:

With HSL, changing the low-contrast green to a higher contrast meant changing the saturation from 63 to 95 and the lightness from 45 to 26 on the left. That’s when the color gets a green check mark in the middle when used with white. The new green still feels like it is in the same family, though, because the Hue remained at 136, which is like the color’s “soul.”

To learn more, play around with the fun HSL visualizer mothereffinghsl.com. But for a more in-depth description of color blindness, WCAG color contrast levels, and the HSL color space, we wrote an in-depth blog post about it.

The use case I want to solve

Designers can adjust colors with the tools that we just reviewed, but so far, no Sass that I have found could do it with mathematical magic. There had to be a way. 

These are some similar approaches I have seen in the wild:

  • An idea by Josh Bader uses CSS variables and colors split into their RGB values to calculate whether white or black is the best accessible color to use in a given situation.
  • Another idea by Facundo Corradini does something similar with HSL values and a very cool “switch function” in CSS.

I didn’t like these approaches. I didn’t want to fallback to white or black. I wanted colors to be maintained but adjusted to be accessible. Additionally, changing colors to their RGB or HSL components and storing them with CSS variables seemed messy and unsustainable for a large codebase.

I wanted to use a preprocessor like Sass to do this: given two colors, automagically adjust one of them so the pair receives a passing WCAG grade. The rules state a few other things to consider as well — size of the text and whether or not the font is bold. The solution had to take this into account. 

In code terms, I wanted to do this:

// Transform this non-passing color pair: .example {   background-color: #444;   color: #0094c2; // a 2.79 contrast ratio when AA requires 4.5   font-size: 1.25rem;   font-weight: normal; } 
 // To this passing color pair: .example {   background-color: #444;   color: #00c0fc; // a 4.61 contrast ratio   font-size: 1.25rem;   font-weight: normal; }

A solution that does this would be able to catch and handle those edge cases we mentioned earlier. Maybe the designer accounted for a brand blue to be used over a light blue, but not a light gray. Maybe the red used in error messages needs to be tweaked for this one form that has a one-off background color. Maybe we want to implement a dark mode feature to the UI without having to retest all the colors again. These are the use cases I had in mind going into this. 

With formulas can come automation

The W3C has provided the community with formulas that help analyze two colors used together. The formula multiplies the RGB channels of both colors by magic numbers (a visual weight based on how humans perceive these color channels) and then divides them to come up with a ratio from 0.0 (no contrast) to 21.0 (all the contrast, only possible with white and black). While imperfect, this is the formula we use right now:

If L1 is the relative luminance of a first color  And L2 is the relative luminance of a second color, then - Color Contrast Ratio = (L1 + 0.05) / (L2 + 0.05) Where - L = 0.2126 * R + 0.7152 * G + 0.0722 * B And - if R sRGB <= 0.03928 then R = R sRGB /12.92 else R = ((R sRGB +0.055)/1.055) ^ 2.4 - if G sRGB <= 0.03928 then G = G sRGB /12.92 else G = ((G sRGB +0.055)/1.055) ^ 2.4 - if B sRGB <= 0.03928 then B = B sRGB /12.92 else B = ((B sRGB +0.055)/1.055) ^ 2.4 And - R sRGB = R 8bit /255 - G sRGB = G 8bit /255 - B sRGB = B 8bit /255

While the formula looks complex, it’s just math right? Well, not so fast. There is a part at the end of a few lines where the value is multiplied by a decimal power — raised to the power of 2.4. Notice that? Turns out that it’s complex math which most programming languages can accomplish — think Javascript’s math.pow() function — but Sass is not powerful enough to do it. 

There’s got to be another way…

Of course there is. It just took some time to find it. 🙂

My first version used a complex series of math calculations that did the work of decimal powers within the limited confines of what Sass can accomplish. Lots of Googling found folks much smarter than me supplying the functions. Unfortunately, calculating only a handful of color contrast combinations increased Sass build times exponentially. So, that means Sass can do it, but that does not mean it should. In production, build times for a large codebase could increase to several minutes. That’s not acceptable. 

After more Googling, I came across a post from someone who was trying to do a similar thing. They also ran into the lack of exponent support in Sass. They wanted to explore “the possibility of using Newtonian approximation for the fractional parts of the exponent.” I totally understand the impulse (not). Instead, they decided to use a “lookup table.” It’s a genius solution. Rather than doing the math from scratch every time, a lookup table provides all the possible answers pre-calculated. The Sass function retrieves the answer from the list and it’s done.

In their words:

The only part [of the Sass that] involves exponentiation is the per-channel color space conversions done as part of the luminance calculation. [T]here are only 256 possible values for each channel. This means that we can easily create a lookup table.

Now we’re cooking. I had found a more performant direction. 

Usage example

Using the function should be easy and flexible. Given a set of two colors, adjust the first color so it passes the correct contrast value for the given WCAG level when used with the second color. Optional parameters will also take the font size or boldness into account.

// @function a11y-color( //   $ color-to-adjust, //   $ color-that-will-stay-the-same, //   $ wcag-level: 'AA', //   $ font-size: 16, //   $ bold: false // ); 
 // Sass sample usage declaring only what is required .example {   background-color: #444;   color: a11y-color(#0094c2, #444); // a 2.79 contrast ratio when AA requires 4.5 for small text that is not bold } 
 // Compiled CSS results: .example {   background-color: #444;   color: #00c0fc; // which is a 4.61 contrast ratio }

I used a function instead of a mixin because I preferred the output of a single value independent from a CSS rule. With a function, the author can determine which color should change. 

An example with more parameters in place looks like this:

// Sass .example-2 {   background-color: a11y-color(#0094c2, #f0f0f0, 'AAA', 1.25rem, true); // a 3.06 contrast ratio when AAA requires 4.5 for text 19px or larger that is also bold   color: #f0f0f0;   font-size: 1.25rem;   font-weight: bold; } 
 // Compiled CSS results: .example-2 {   background-color: #087597; // a 4.6 contrast ratio   color: #f0f0f0;   font-size: 1.25rem;   font-weight: bold; }

A deeper dive into the heart of the Sass function

To explain the approach, let’s walk through what the final function is doing, line by line. There are lots of helper functions along the way, but the comments and logic in the core function explain the approach:

// Expected: // $ fg as a color that will change // $ bg as a color that will be static and not change // Optional: // $ level, default 'AA'. 'AAA' also accepted // $ size, default 16. PX expected, EM and REM allowed // $ bold, boolean, default false. Whether or not the font is currently bold // @function a11y-color($ fg, $ bg, $ level: 'AA', $ size: 16, $ bold: false) {   // Helper: make sure the font size value is acceptable   $ font-size: validate-font-size($ size);   // Helper: With the level, font size, and bold boolean, return the proper target ratio. 3.0, 4.5, or 7.0 results expected   $ ratio: get-ratio($ level, $ font-size, $ bold);   // Calculate the first contrast ratio of the given pair   $ original-contrast: color-contrast($ fg, $ bg);      @if $ original-contrast >= $ ratio {     // If we pass the ratio already, return the original color     @return $ fg;   } @else {     // Doesn't pass. Time to get to work     // Should the color be lightened or darkened?     // Helper: Single color input, 'light' or 'dark' as output     $ fg-lod: light-or-dark($ fg);     $ bg-lod: light-or-dark($ bg); 
     // Set a "step" value to lighten or darken a color     // Note: Higher percentage steps means faster compile time, but we might overstep the required threshold too far with something higher than 5%     $ step: 2%;          // Run through some cases where we want to darken, or use a negative step value     @if $ fg-lod == 'light' and $ bg-lod == 'light' {       // Both are light colors, darken the fg (make the step value negative)       $ step: - $ step;     } @else if $ fg-lod == 'dark' and $ bg-lod == 'light' {       // bg is light, fg is dark but does not pass, darken more       $ step: - $ step;     }     // Keeping the rest of the logic here, but our default values do not change, so this logic is not needed     //@else if $ fg-lod == 'light' and $ bg-lod == 'dark' {     //  // bg is dark, fg is light but does not pass, lighten further     //  $ step: $ step;     //} @else if $ fg-lod == 'dark' and $ bg-lod == 'dark' {     //  // Both are dark, so lighten the fg     //  $ step: $ step;     //}          // The magic happens here     // Loop through with a @while statement until the color combination passes our required ratio. Scale the color by our step value until the expression is false     // This might loop 100 times or more depending on the colors     @while color-contrast($ fg, $ bg) < $ ratio {       // Moving the lightness is most effective, but also moving the saturation by a little bit is nice and helps maintain the "power" of the color       $ fg: scale-color($ fg, $ lightness: $ step, $ saturation: $ step/2);     }     @return $ fg;   } }

The final Sass file

Here’s the entire set of functions! Open this in CodePen to edit the color variables at the top of the file and see the adjustments that the Sass makes:

All helper functions are there as well as the 256-line lookup table. Lots of comments should help folks understand what is going on. 

When an edge case has been encountered, a version in SassMeister with debug output was helpful while I was developing it to see what might be happening. (I changed the main function to a mixin so I can debug the output.) Feel free to poke around at this as well.

Play with this gist on SassMeister.

And finally, the functions have been stripped out of CodePen and put into a GitHub repo. Drop issues into the queue if you run into problems. 

Cool code! But can I use this in production?

Maybe. 

I’d like to say yes, but I’ve been iterating on this thorny problem for a while now. I feel confident in this code but would love more input. Use it on a small project and kick the tires. Let me know how the build time performs. Let me know if you come across edge cases where passing color values are not being supplied. Submit issues to the GutHub repo. Suggest improvements based on other code you’ve seen in the wild. 

I’d love to say that I have Automated All the A11y Things, but I also know it needs to be road-tested before it can be called Production Ready™. I’m excited to introduce it to the world. Thanks for reading and I hope to hear how you are using it real soon.

The post Programming Sass to Create Accessible Color Combinations appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,
[Top]

How To Create A Headless WordPress Site On The Jamstack

Just this morning, Chris shared a streamlined way to get a static site up and running with Netlify. As it happens, Sarah and I also wrote up a little something that expands that idea where a static site can pull content from WordPress using the REST API.

Using Vue, Nuxt, axios, and Netlify, it’s possible to get both the performance and continuous integration benefits of Jamstack with the powerful publishing and editing features of a CMS. It’s really amazing what pairing different stacks can do these days!

Being a WordPress junkie myself, I learned from a lot from Sarah about setting up a progressive web app and working with a component-driven architecture. She equipped me with several resources, all of which are linked up in the article. There’s even a complete video where Sarah walks through the same steps we followed to set things up for this app.

In other words, it’s worth the estimated 18 minutes it takes to read the article. I hope you walk away with as much as I did getting to work on it.

Direct Link to ArticlePermalink

The post How To Create A Headless WordPress Site On The Jamstack appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]