Web Standards

The Story Behind TryShape, a Showcase for the CSS clip-path property

Css Tricks - Wed, 09/08/2021 - 4:30am

I love shapes, especially colorful ones! Shapes on websites are in the same category of helpfulness as background colors, images, banners, section separators, artwork, and many more: they can help us understand context and inform our actions through affordances.

A few months back, I built an application to engage my 7-year old daughter with mathematics. Apart from basic addition and subtraction, my aim was to present questions using shapes. That’s when I got familiar with the CSS clip-path property, a reliable way to make shapes on the web. Then, I ended up building another app called, TryShape using the power of clip-path.

I’ll walk you through the story behind TryShape and how it helps create, manage, share, and export shapes. We’ll cover a lot about CSS clip-path along the way and how it helped me quickly build the app.

Here are a few important links:

First, the CSS clip-path property and shapes

Imagine you have a plain piece of paper and a pencil to draw a shape (say, a square) on it. How will you proceed? Most likely, you will start from a point, then draw a line to reach another point, then repeat it exact three more times to come back to the initial point. You also have to make sure you have opposite lines parallel and of the same length.

So, the essential ingredients for a shape are points, lines, directions, curves, angles, and lengths, among many others. The CSS clip-path helps specify many of these properties to clip a region of an HTML element to show a specific region. The part that is inside the clipped region is shown, and the rest is hidden. It gives an ocean of opportunities to developers to create various shapes using clip-path property.

Learn more about clipping and how it is different from masking.

The clip-path values for shape creation

The clip-path property accepts the following values for creating shapes:

  • circle()
  • ellipse()
  • inset()
  • polygon()
  • A clip source using url() function
  • path()

We need to understand the basic coordinate system a bit to use these values. When applying the clip-path property on an element to create shapes, we must consider the x-axis, y-axis, and the initial coordinates (0,0) at the element’s top-left corner.

Here is a div element with its x-axis, y-axis, and initial coordinates (0,0).

Initial coordinates(0,0) with x-axis and y-axis

Now let’s use the circle() value to create a circular shape. We can specify the position and radius of the circle using this value. For example, to clip a circular shape at the coordinate position (70, 70) with a radius of 70px, we can specify the clip-path property value as:

clip-path: circle(70px at 70px 70px)

So, the center of the circle is placed at the coordinate (70, 70) with a 70px radius. Now, only this circular region is clipped and shown on the element. The rest of the portion of the element is hidden to create the impression of a circle shape.

The center of the circle is placed at (70, 70) coordinates with a 70px x 70px area clipped. Hence the full circle is shown.

Next, what if we want to specify the position at (0,0)? In this case, the circle’s center is placed at the (0,0) position with a radius of 70px. That makes only a portion of the circle visible inside the element.

The center of the circle is placed at (0, 0) coordinates with a 70px x 70px area clipping the bottom-left region of the circle.

Let’s move on to use the other two essential values, inset() and polygon(). We use an inset to define a rectangular shape. We can specify the gap that each of the four edges may have to clip a region from an element. For example:

clip-path: inset(30px)

The above clip-path value clips a region by leaving out the 30px values from element’s edges. We can see that in the image below. We can also specify a different inset value for each of the edges.

The inset() function allows us to clip and area from the outside edge of a shape.

Next is the polygon() value. We can create a polygonal shape using a set of vertices. Take this example:

clip-path: polygon(10% 10%, 90% 10%, 90% 90%, 10% 80%)

Here we are specifying a set of vertices to create a region for clipping. The image below shows the position of each vertex to create a polygonal shape. We can specify as many vertices as we want.

The polygon() function allows us to create polygonal shapes using the set of vertices passed to it.

Next, let’s take a look at the ellipse() and the url() values. The ellipse() value helps create shapes by specifying two radii values and a position. In the image below, we see an ellipse at the position where the radii is at (50%,50%) and the shape is 70px wide and 100px tall.

We need to specify two radii values and a position to create an ellipse.

url() is a CSS function to specify the clip-path element’s ID value to render an SVG shape. Please take a look at the image below. We have defined a SVG shape using clipPath and path elements. You can use the ID value of the clipPath element as an argument to the url() function to render this shape.

Here, we are creating a heart shape using the url() function

Additionally, we can use the path values directly in the path() function to draw the shape.

Here we are creating a curvy shape using the path() function.

Alright. I hope you have got an understanding of different clip-path property values. With this understanding, let’s take a loot at some implementations and play around with them. Here is a Pen for you. Please use it to try adding, modifying values to create a new shape.

CodePen Embed Fallback Let’s talk about TryShape

It’s time to talk about TryShape and its background story. TryShape is an open-source application that helps create, export, share, and use any shapes of your choice. You can create banners, circles, arts, polygons and export them as SVG, PNG, and JPEG files. You can also create a CSS code snippet to copy and use in your application.

TryShape is built using the following framework and libraries (and clip-path, of course):

  • CSS clip-path: We’ve already discussed the power of this awesome CSS property.
  • Next.js: The coolest React-based framework around. It helped me create pages, components, interactions, and APIs to connect to the back-end database.
  • HarperDB: A flexible database to store data and query them using both SQL and No-SQL interactions. TryShape has its schema and tables created in the HarperDB cloud. The Next.js APIs interact with the schema and tables to perform required CRUD operations from the user interface.
  • Firebase: Authentication services from Google. TryShape uses it to get the social login working using Google, GitHub, Twitter, and other accounts.
  • react-icons: One shop for all the icons for a React-based application
  • date-fns: The modern, lightweight library for date formatting
  • axios: Making the API calls easy from the React components
  • styled-components: A structured way to create CSS rules from react components
  • react-clip-path: A homegrown module to handle clip-path property in a React app
  • react-draggable: Make an HTML element draggable in a React app. TryShape uses it to adjust the position of shape vertices.
  • downloadjs: Trigger a download from JavaScript
  • html-to-image: Converts an HTML element to image (including SVG, JPEG, and PNG)
  • Vercel: Best for hosting a Next.js app
Creating shapes in TryShape using CSS clip-path

Let me highlight the source code that helps create a shape using the CSS clip-path property. The code snippet below defines the user interface structure for a container element (Box) that’s 300px square. The Box element has two child elements, Shadow and Component.

<Box height="300px" width="300px" onClick={(e) => props.handleChange(e)}> { props.shapeInformation.showShadow && <Shadow backgroundColor={props.shapeInformation.backgroundColor} id="shapeShadow" /> } <Component formula={props.shapeInformation.formula} backgroundColor={props.shapeInformation.backgroundColor} id="clippedShape" /> </Box>

The Shadow component defines the area that is hidden by the clip-path clipping. We create it to show a light color background to make this area partially visible to the end user. The Component is to assign the clip-path value to show the clipped area.

See the styled-component definitions of Box, Shadow, and Component below:

// The styled-components code to create the UI components using CSS properties // The container div const Box = styled.div` width: ${props => props.width || '100px'}; height: ${props => props.height || '100px'}; margin: 0 auto; position: relative; `; // Shadow defines the area that is hidden by the `clip-path` clipping // We show a light color background to make this area partially visible. const Shadow = styled.div` background-color: ${props => props.backgroundColor || '#00c4ff'}; opacity: 0.25; position: absolute; top: 10px; left: 10px; right: 10px; bottom: 10px; `; // The actual component that takes the `clip-path` value (formula) and set // to the `clip-path` property. const Component = styled.div` clip-path: ${props => props.formula}; // the formula is the clip-path value background-color: ${props => props.backgroundColor || '#00c4ff'}; position: absolute; top: 10px; left: 10px; right: 10px; bottom: 10px; `; The components to show a shape(both visible and hidden areas) after the clipping.

Please feel free to look into the entire codebase in the GitHub repo.

The future scope of TryShape

TryShape works well with the creation and management of basic shapes using CSS clip-path in the background. It is helpful to export the shapes and the CSS code snippets to use in your web applications. It has the potential to grow with many more valuable features. The primary one will be the ability to create shapes with curvy edges.

To support the curvy shapes, we need the support of the following values in TryShape:

  • a clip source using url() and
  • path().

With the help of these values, we can create shapes using SVG and then use one of the above values. Here is an example of the url() CSS function to create a shape using the SVG support.

<div class="heart">Heart</div> <svg> <clipPath id="heart-path" clipPathUnits="objectBoundingBox"> <path d="M0.5,1 C 0.5,1,0,0.7,0,0.3 A 0.25,0.25,1,1,1,0.5,0.3 A 0.25,0.25,1,1,1,1,0.3 C 1,0.7,0.5,1,0.5,1 Z" /> </clipPath> </svg>

Then, the CSS::

.heart { clip-path: url(#heart-path); }

Now, let’s create a shape using the path() value. The HTML should have an element like a div:

<div class="curve">Curve</div>


.curve { clip-path: path("M 10 80 C 40 10, 65 10, 95 80 S 150 150, 180 80"); } Before we end…

I hope you enjoyed meeting my TryShape app and learning about the idea that leads to it, the strategies I considered, the technology under the hood, and its future potential. Please consider trying it and looking through the source code. And, of course, feel free to contribute to it with issues, feature requests, and code.

Before we end, I want to leave you with this short video prepared for the Hashnode hackathon where TryShape was an entry and finally in the list of winners. I hope you enjoy it.

Let’s connect. You can @ me on Twitter (@tapasadhikary) with comments, or feel free to follow.

The post The Story Behind TryShape, a Showcase for the CSS clip-path property appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Fire SVG animations (SMIL) when the SVG is visible

Css Tricks - Tue, 09/07/2021 - 7:54am

When requirements read “when visible” your brain should go straight to IntersectionObserver. That’s exactly what Zach is doing here to kick off an animation when it scrolls into view.

Except this animation is an SVG SMIL animation: an <animate> situation. SMIL animations have some kinda cool things they can do, like begin when another animation ends, which is something CSS doesn’t help with that much. Turns out SMIL has a JavaScript API as well, so it’s possible to kick off the animation on demand that way, while also respecting prefers-reduced-motion.

Also check this out:

.querySelectorAll(`:scope [begin="indefinite"]`);

That :scope thing is new to me.

Direct Link to ArticlePermalink

The post Fire SVG animations (SMIL) when the SVG is visible appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Firefox’s `bolder` Default is a Problem for Variable Fonts

Css Tricks - Tue, 09/07/2021 - 4:41am

Variable fonts make it easy to create a large set of font styles from a single font file. Unfortunately, the default rendering of the <b> and <strong> elements in browsers today is not very compatible with the wide range of font-weight values enabled by variable fonts.

https://twitter.com/zachleat/status/1374443096281280517 Browsers disagree on the default font-weight of <b>

The purpose of the <b> and <strong> elements is to draw attention to a specific word or span of text on the page. Browsers make these elements stand out by increasing their font-weight. This works well under normal conditions. For example, MDN Web Docs uses <b> in a few places in the “Found a problem?” card at the bottom of each page.

Things become more complicated when the text on the page has a custom font-weight. The default weight of text is 400, but the font-weight property accepts any number between 1 and 1000 (inclusive). Let’s take a look at how Chrome and Firefox render text wrapped in <b> by default depending on the font-weight of the surrounding text.

View on CodePen

Chrome and Firefox disagree on the default rendering of <b> elements. Chrome uses a constant font-weight of 700 (Safari behaves the same), while Firefox chooses between three values (400, 700, and 900) depending on the font-weight of the surrounding text.

Where is this difference coming from?

As you might have guessed, Chrome and Firefox use different font-weight values for the <b> and <strong> elements in their user agent stylesheets.

/* Chrome and Safari’s user agent stylesheet */ strong, b { font-weight: bold; } /* Firefox’s user agent stylesheet */ strong, b { font-weight: bolder; }

The bold and bolder values are specified in the CSS Fonts module; bold is equivalent to 700, while bolder is a relative weight that is calculated as follows:

If the outer text has a font-weight of…the bolder keyword computes to…1 to 349400350 to 549700550 to 899900900 to 1000No change (same value as outer text)

Chrome and Firefox disagree on the default rendering of <b>, but which browser follows the standards more closely? The font-weight property itself is defined in the CSS Fonts module, but the suggested font-weight values for different HTML elements are located in the Rendering section of the HTML Standard.

/* The HTML Standard suggests the following user agent style */ strong, b { font-weight: bolder; }

The HTML Standard started suggesting bolder instead of bold all the way back in 2012. As of today, only Firefox follows this recommendation. Chrome and Safari have not made the switch to bolder. Because of this inconsistency, the popular Normalize base stylesheet has a CSS rule that enforces bolder across browsers.

Which of the two defaults is better?

There are two different defaults in browsers, and Firefox’s default matches the standard. So, should Chrome align with Firefox, or is Chrome’s default the better one? Let’s take another look at the default rendering of the <b> element.

View on CodePen

Each of the two defaults has a weak spot: Chrome’s bold default breaks down at higher font-weight values (around 700), while Firefox’s bolder default has a problem with lower font-weight values (around 300).

In the worst-case scenario for Firefox, text wrapped in <b> becomes virtually indiscernible. The following screenshot shows text at a font-weight of 349 in Firefox. Can you spot the single word that is wrapped in <b>? Firefox renders this element at a default font-weight of 400, which is an increase of only 51 points.

(View on CodePen) The takeaway

If you use thin fonts or variable fonts at font-weight values below 350, be aware that the <b> and <strong> elements may not always be discernible in Firefox by default. In this case, it is probably a good idea to manually define a custom font-weight for <b> and <strong> instead of relying on the browser’s sub-optimal default, which insufficiently increases the font-weight of these elements.

/* Defining the regular and bold font-weight at the same time */ body { font-weight: 340; } b, strong { font-weight: 620; }

The bolder value is outdated and doesn’t work well with variable fonts. Ideally, text wrapped in <b> should be easy to spot regardless of the font-weight of the surrounding text. Browsers could achieve that by always increasing the font-weight by the same or a similar amount.

On that note, there is a discussion in the CSS Working Group about allowing percentages in font-weight in the same manner as in font-size. Lea Verou writes:

A far more common use case is when we want a bolder or lighter stroke than the surrounding text, in a way that’s agnostic to the weight of the surrounding text.

/* Increasing font-size by 100% */ h1 { font-size: 200%; } /* PROPOSAL - Increasing font-weight by 50% */ strong, b { font-weight: 150%; }

Taking variable fonts into account, a value like 150% would probably be a better default than the existing bold/bolder defaults in browsers today.

The post Firefox’s `bolder` Default is a Problem for Variable Fonts appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Early Days for CSS Scoping

Css Tricks - Mon, 09/06/2021 - 8:47am

There is a working draft spec for CSS scoping now. Other than a weird period where <style scoped> shipped and then was subsequently removed from the spec (and browsers), this is the furthest a scoping proposal has gotten (the Level 1 spec never got anywhere).

This one comes from Miriam Suzanne.

The basics:

<div class="media"> <img alt="Proper alt." src="..."> <div class="content"> <p>...</p> </div> </div>

If I’m thinking of this bit of HTML as a “component,” it’s nice to be able to write styles for it that are very explicitly just for it. That’s what @scope is for, so…

@scope (.media) { :scope { display: grid; grid-template-columns: 50px 1fr; } img { filter: grayscale(100%); border-radius: 50%; } .content { ... } }

What I like about that is:

  1. This bit of CSS is very explicitly for this media component. It reads like that and can be maintained like that.
  2. I didn’t have to come up with a name and class for the <img>. I’m applying styling there without it “leaking out” to other images.

But wait, isn’t this just like prepending selectors with the parent class?

It kind of is… like we could also write:

.media { } .media img { } .media .content { }

And now we’ve scoped things internal to the media component. That’s rather repetitive, but with native CSS nesting on the way, it’s just this:

.media { & img { } & .content { } }

So yes, I’d say nesting takes care of some basic types of scoping, but there are some things that are very unique to this new scope proposal.

One unique feature is “donut scope” meaning I stop the scoping where I want to. Maybe I want my scoping to stop at a particular class:

@scope (.media) to (.content) { p { } }

Now I can write styles that won’t mess with areas that I don’t want them to mess with. Perhaps:

<div class="media"> <img alt="Proper alt." src="..."> <p>This is stylable in scope.</p> <div class="content"> <p>This is NOT styleable in scope.</p> </div> </div>

But that’s not the only unique problem this new spec solves. I think the “nearest ancestor” situation that Miriam lays out is perhaps the most interesting thing. I’ll send you over to the blog post to read about that — it’s pretty wild that we don’t have a good tool for that yet.

There is a lot to wrap your mind around here, especially as you think of more complex situations, like multiple overlapping scopes and how the nesting syntax might interplay with scoping. Fortunately, Miriam is blogging these things very clearly.

The post Early Days for CSS Scoping appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

AWS Lambdas: Easy, Easier, Easiest

Css Tricks - Fri, 09/03/2021 - 12:16pm

I’d say cloud functions are one of the most transformative technologies in the last bunch of years. They are (usually) cheap, scale well, secure in their inherent isolation, and often written in JavaScript—comfortable territory for front-end developers. Nearly every cloud provider offers them, but AWS Lambda was the OG and remains the leader.

But also: The DX around cloud functions is just as interesting to watch as the tech behind the functions themselves. There is all sorts of tech that has sprung up around them to make them easy to use and relatively transparent. Emrah Samdan wrote that it’s a win-win for both customers and companies. Another example:

Two of the most popular Jamstack hosting platforms, Netlify and Vercel, offer idiot-proof wrappers for AWS Lambda deployments, each more developer-friendly than the next.

Joey Anuff, “AWS Lambdas: Easy, Easier, Easiest”

AWS’ own Amplify is a front-runner for easiness as well, which is in stark contrast to trying to manage your functions right through the AWS console itself.

Joey found Vercel to be easiest by a narrow margin, with the caveat that he was already using Next.js which is from Vercel.

My favorite bit here is that in the research repo for this article, Joey listed in great detail (with action GIFs) the steps for each of the services cloud functions offerings.

Direct Link to ArticlePermalink

The post AWS Lambdas: Easy, Easier, Easiest appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Links on Performance IV

Css Tricks - Thu, 09/02/2021 - 12:28pm
More links! Article on Aug 30, 2021 Links on Performance I Chris Coyier Article on Aug 30, 2021 Links on Performance II Chris Coyier Article on Aug 30, 2021 Links on Performance III Chris Coyier

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

I completely ignored the front-end development scene for 6 months. It was fine.

Css Tricks - Thu, 09/02/2021 - 12:26pm

Have you ever fretted that front-end web development moves so fast that if you stepped away for a while, you’d be lost coming back? Rachel Smith has:

The hectic pace of needing to learn one thing after the next didn’t bother me so much because when I was 26 because I was quite happy to spend much of my free time outside of my day job coding. I was really enjoying myself, so the impression that I had to constantly up-skill to maintain my career wasn’t a concern. I did wonder, though, how I would ever take enough time off to have a baby, or have other responsibilities that would prevent me from being able to spend so much of my time mastering languages and learning new libraries and frameworks.

And then, as is inevitable for most of us, she did take a break. And as you read in the title, it was fine:

What I’ve learnt through experience is that the number of languages I’ve learned or the specific frameworks I’ve gained experience with matters very little. What actually matters is my ability to up-skill quickly and effectively. My success so far has nothing to do with the fact I know React instead of Vue, or have experience with AWS and not Azure. What has contributed to my success is the willingness to learn new tools as the need arises.

I might be extra qualified to verify this claim, as I work directly with Rachel. She’s better than “fine” as a team member and technological contributor, both on the front-end and back. She’s extremely good. And you will be too if you heed Rachel’s advice: be a lifelong learner and be willing to learn new tools as the needs arise.

Direct Link to ArticlePermalink

The post I completely ignored the front-end development scene for 6 months. It was fine. appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

You don’t need external assets in an HTML file

Css Tricks - Wed, 09/01/2021 - 2:29pm

A fun exercise from Terence Eden. You can send an HTML file over the wire including anything a website might need without requesting any other files. CSS and JavaScript are easy, because there are <script> and <style> tags. Images and fonts (and pretty much whatever other kind of asset) aren’t too hard because Data URLs exist. See Terence’s post for an extra-tricky final version including .zip files.

Reminds me of a couple of other tricks…

Direct Link to ArticlePermalink

The post You don’t need external assets in an HTML file appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Accessing Your Data With Netlify Functions and React

Css Tricks - Wed, 09/01/2021 - 2:28pm

(This is a sponsored post.)

Static site generators are popular for their speed, security, and user experience. However, sometimes your application needs data that is not available when the site is built. React is a library for building user interfaces that helps you retrieve and store dynamic data in your client application. 

Fauna is a flexible, serverless database delivered as an API that completely eliminates operational overhead such as capacity planning, data replication, and scheduled maintenance. Fauna allows you to model your data as documents, making it a natural fit for web applications written with React. Although you can access Fauna directly via a JavaScript driver, this requires a custom implementation for each client that connects to your database. By placing your Fauna database behind an API, you can enable any authorized client to connect, regardless of the programming language.

Netlify Functions allow you to build scalable, dynamic applications by deploying server-side code that works as API endpoints. In this tutorial, you build a serverless application using React, Netlify Functions, and Fauna. You learn the basics of storing and retrieving your data with Fauna. You create and deploy Netlify Functions to access your data in Fauna securely. Finally, you deploy your React application to Netlify.

Getting started with Fauna

Fauna is a distributed, strongly consistent OLTP NoSQL serverless database that is ACID-compliant and offers a multi-model interface. Fauna also supports document, relational, graph, and temporal data sets from a single query. First, we will start by creating a database in the Fauna console by selecting the Database tab and clicking on the Create Database button.

Next, you will need to create a Collection. For this, you will need to select a database, and under the Collections tab, click on Create Collection.

Fauna uses a particular structure when it comes to persisting data. The design consists of attributes like the example below.

{ "ref": Ref(Collection("avengers"), "299221087899615749"), "ts": 1623215668240000, "data": { "id": "db7bd11d-29c5-4877-b30d-dfc4dfb2b90e", "name": "Captain America", "power": "High Strength", "description": "Shield" } }

Notice that Fauna keeps a ref column which is a unique identifier used to identify a particular document. The ts attribute is a timestamp to determine the time of creating the record and the data attribute responsible for the data.

Why creating an index is important

Next, let’s create two indexes for our avengers collection. This will be pretty valuable in the latter part of the project. You can create an index from the Index tab or from the Shell tab, which provides a console to execute scripts. Fauna supports two types of querying techniques: FQL (Fauna’s Query language) and GraphQL. FQL operates based on the schema of Fauna, which includes documents, collections, indexes, sets, and databases. 

Let’s create the indexes from the shell.

This command will create an index on the Collection, which will create an index by the id field inside the data object. This index will return a ref of the data object. Next, let’s create another index for the name attribute and name it avenger_by_name.

Creating a server key

To create a server key, we need to navigate the Security tab and click on the New Key button. This section will prompt you to create a key for a selected database and the user’s role.

Getting started with Netlify functions and React

In this section, we’ll see how we create Netlify functions with React. We will be using create-react-app to create the react app.

npx create-react-app avengers-faunadb

After creating the react app, let’s install some dependencies, including Fauna and Netlify dependencies.

yarn add axios bootstrap node-sass uuid faunadb react-netlify-identity react-netlify-identity-widget

Now let’s create our first Netlfiy function. To make the functions, first, we need to install Netlifiy CLI globally.

npm install netlify-cli -g

Now that the CLI is installed, let’s create a .env file on our project root with the following fields.

FAUNADB_SERVER_SECRET= <FaunaDB secret key> REACT_APP_NETLIFY= <Netlify app url>

Next, Let’s see how we can start with creating netlify functions. For this, we will need to create a directory in our project root called functions and a file called netlify.toml, which will be responsible for maintaining configurations for our Netlify project. This file defines our function’s directory, build directory, and commands to execute.

[build] command = "npm run build" functions = "functions/" publish = "build" [[redirects]] from = "/api/*" to = "/.netlify/functions/:splat" status = 200 force = true

We will do some additional configuration for the Netlify configuration file, like in the redirection section in this example. Notice that we are changing the default path of the Netlify function of /.netlify/** to /api/. This configuration is mainly for the improvement of the look and field of the API URL. So to trigger or call our function, we can use the path:


 …instead of:


Next, let’s create our Netlify function in the functions directory. But, first, let’s make a connection file for Fauna called util/connections.js, returning a Fauna connection object.

const faunadb = require('faunadb'); const q = faunadb.query const clientQuery = new faunadb.Client({ secret: process.env.FAUNADB_SERVER_SECRET, }); module.exports = { clientQuery, q };

Next, let’s create a helper function checking for reference and returning since we will need to parse the data on several occasions throughout the application. This file will be util/helper.js.

const responseObj = (statusCode, data) => { return { statusCode: statusCode, headers: { /* Required for CORS support to work */ "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Headers": "*", "Access-Control-Allow-Methods": "GET, POST, OPTIONS", }, body: JSON.stringify(data) }; }; const requestObj = (data) => { return JSON.parse(data); } module.exports = { responseObj: responseObj, requestObj: requestObj }

Notice that the above helper functions handle the CORS issues, stringifying and parsing of JSON data. Let’s create our first function, getAvengers, which will return all the data.

const { responseObj } = require('./util/helper'); const { q, clientQuery } = require('./util/connection'); exports.handler = async (event, context) => { try { let avengers = await clientQuery.query( q.Map( q.Paginate(q.Documents(q.Collection('avengers'))), q.Lambda(x => q.Get(x)) ) ) return responseObj(200, avengers) } catch (error) { console.log(error) return responseObj(500, error); } };

In the above code example, you can see that we have used several FQL commands like Map, Paginate, Lamda. The Map key is used to iterate through the array, and it takes two arguments: an Array and Lambda. We have passed the Paginate for the first parameter, which will check for reference and return a page of results (an array). Next, we used a Lamda statement, an anonymous function that is quite similar to an anonymous arrow function in ES6.

Next, Let’s create our function AddAvenger responsible for creating/inserting data into the Collection.

const { requestObj, responseObj } = require('./util/helper'); const { q, clientQuery } = require('./util/connection'); exports.handler = async (event, context) => { let data = requestObj(event.body); try { let avenger = await clientQuery.query( q.Create( q.Collection('avengers'), { data: { id: data.id, name: data.name, power: data.power, description: data.description } } ) ); return responseObj(200, avenger) } catch (error) { console.log(error) return responseObj(500, error); } };

To save data for a particular collection, we will have to pass, or data to the data:{} object like in the above code example. Then we need to pass it to the Create function and point it to the collection you want and the data. So, let’s run our code and see how it works through the netlify dev command.

Let’s trigger the GetAvengers function through the browser through the URL http://localhost:8888/api/GetAvengers.

The above function will fetch the avenger object by the name property searching from the avenger_by_name index. But, first, let’s invoke the GetAvengerByName function through a Netlify function. For that, let’s create a function called SearchAvenger.

const { responseObj } = require('./util/helper'); const { q, clientQuery } = require('./util/connection'); exports.handler = async (event, context) => { const { queryStringParameters: { name }, } = event; try { let avenger = await clientQuery.query( q.Call(q.Function("GetAvengerByName"), [name]) ); return responseObj(200, avenger) } catch (error) { console.log(error) return responseObj(500, error); } };

Notice that the Call function takes two arguments where the first parameter will be the reference for the FQL function that we created and the data that we need to pass to the function.

Calling the Netlify function through React

Now that several functions are available let’s consume those functions through React. Since the functions are REST APIs, let’s consume them via Axios, and for state management, let’s use React’s Context API. Let’s start with the Application context called AppContext.js.

import { createContext, useReducer } from "react"; import AppReducer from "./AppReducer" const initialState = { isEditing: false, avenger: { name: '', description: '', power: '' }, avengers: [], user: null, isLoggedIn: false }; export const AppContext = createContext(initialState); export const AppContextProvider = ({ children }) => { const [state, dispatch] = useReducer(AppReducer, initialState); const login = (data) => { dispatch({ type: 'LOGIN', payload: data }) } const logout = (data) => { dispatch({ type: 'LOGOUT', payload: data }) } const getAvenger = (data) => { dispatch({ type: 'GET_AVENGER', payload: data }) } const updateAvenger = (data) => { dispatch({ type: 'UPDATE_AVENGER', payload: data }) } const clearAvenger = (data) => { dispatch({ type: 'CLEAR_AVENGER', payload: data }) } const selectAvenger = (data) => { dispatch({ type: 'SELECT_AVENGER', payload: data }) } const getAvengers = (data) => { dispatch({ type: 'GET_AVENGERS', payload: data }) } const createAvenger = (data) => { dispatch({ type: 'CREATE_AVENGER', payload: data }) } const deleteAvengers = (data) => { dispatch({ type: 'DELETE_AVENGER', payload: data }) } return <AppContext.Provider value={{ ...state, login, logout, selectAvenger, updateAvenger, clearAvenger, getAvenger, getAvengers, createAvenger, deleteAvengers }}>{children}</AppContext.Provider> } export default AppContextProvider;

Let’s create the Reducers for this context in the AppReducer.js file, Which will consist of a reducer function for each operation in the application context.

const updateItem = (avengers, data) => { let avenger = avengers.find((avenger) => avenger.id === data.id); let updatedAvenger = { ...avenger, ...data }; let avengerIndex = avengers.findIndex((avenger) => avenger.id === data.id); return [ ...avengers.slice(0, avengerIndex), updatedAvenger, ...avengers.slice(++avengerIndex), ]; } const deleteItem = (avengers, id) => { return avengers.filter((avenger) => avenger.data.id !== id) } const AppReducer = (state, action) => { switch (action.type) { case 'SELECT_AVENGER': return { ...state, isEditing: true, avenger: action.payload } case 'CLEAR_AVENGER': return { ...state, isEditing: false, avenger: { name: '', description: '', power: '' } } case 'UPDATE_AVENGER': return { ...state, isEditing: false, avengers: updateItem(state.avengers, action.payload) } case 'GET_AVENGER': return { ...state, avenger: action.payload.data } case 'GET_AVENGERS': return { ...state, avengers: Array.isArray(action.payload && action.payload.data) ? action.payload.data : [{ ...action.payload }] }; case 'CREATE_AVENGER': return { ...state, avengers: [{ data: action.payload }, ...state.avengers] }; case 'DELETE_AVENGER': return { ...state, avengers: deleteItem(state.avengers, action.payload) }; case 'LOGIN': return { ...state, user: action.payload, isLoggedIn: true }; case 'LOGOUT': return { ...state, user: null, isLoggedIn: false }; default: return state } } export default AppReducer;

Since the application context is now available, we can fetch data from the Netlify functions that we have created and persist them in our application context. So let’s see how to call one of these functions.

const { avengers, getAvengers } = useContext(AppContext); const GetAvengers = async () => { let { data } = await axios.get('/api/GetAvengers); getAvengers(data) }

To get the data to the application contexts, let’s import the function getAvengers from our application context and pass the data fetched by the get call. This function will call the reducer function, which will keep the data in the context. To access the context, we can use the attribute called avengers. Next, let’s see how we could save data on the avengers collection.

const { createAvenger } = useContext(AppContext); const CreateAvenger = async (e) => { e.preventDefault(); let new_avenger = { id: uuid(), ...newAvenger } await axios.post('/api/AddAvenger', new_avenger); clear(); createAvenger(new_avenger) }

The above newAvenger object is the state object which will keep the form data. Notice that we pass a new id of type uuid to each of our documents. Thus, when the data is saved in Fauna, We will be using the createAvenger function in the application context to save the data in our context. Similarly, we can invoke all the netlify functions with CRUD operations like this via Axios.

How to deploy the application to Netlify

Now that we have a working application, we can deploy this app to Netlify. There are several ways that we can deploy this application:

  1. Connecting and deploying the application through GitHub
  2. Deploying the application through the Netlify CLI

Using the CLI will prompt you to enter specific details and selections, and the CLI will handle the rest. But in this example, we will be deploying the application through Github. So first, let’s log in to the Netlify dashboard and click on New Site from Git button. Next, It will prompt you to select the Repo you need to deploy and the configurations for your site like build command, build folder, etc.

How to authenticate and authorize functions by Netlify Identity

Netlify Identity provides a full suite of authentication functionality to your application which will help us to manage authenticated users throughout the application. Netlify Identity can be integrated easily into the application without using any other 3rd party service and libraries. To enable Netlify Identity, we need to login into our Neltify dashboard, and under our deployed site, we need to move to the Identity tab and allow the identity feature.

Enabling Identity will provide a link to your netlify identity. You will have to copy that URL and add it to the .env file of your application for REACT_APP_NETLIFY. Next, We need to add the Netlify Identity to our React application through the netlify-identity-widget and the Netlify functions. But, first, let’s add the REACT_APP_NETLIFY property for the Identity Context Provider component in the index.js file.

import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import "react-netlify-identity-widget/styles.css" import 'bootstrap/dist/css/bootstrap.css'; import App from './App'; import { IdentityContextProvider } from "react-netlify-identity-widget" const url = process.env.REACT_APP_NETLIFY; ReactDOM.render( <IdentityContextProvider url={url}> <App /> </IdentityContextProvider>, document.getElementById('root') );

This component is the Navigation bar that we use in this application. This component will be on top of all the other components to be the ideal place to handle the authentication. This react-netlify-identity-widget will add another component that will handle the user signI= in and sign up.

Next, let’s use the Identity in our Netlify functions. Identity will introduce some minor modifications to our functions, like the below function GetAvenger.

const { responseObj } = require('./util/helper'); const { q, clientQuery } = require('./util/connection'); exports.handler = async (event, context) => { if (context.clientContext.user) { const { queryStringParameters: { id }, } = event; try { const avenger = await clientQuery.query( q.Get( q.Match(q.Index('avenger_by_id'), id) ) ); return responseObj(200, avenger) } catch (error) { console.log(error) return responseObj(500, error); } } else { return responseObj(401, 'Unauthorized'); } };

The context of each request will consist of a property called clientContext, which will consist of authenticated user details. In the above example, we use a simple if condition to check the user context. 

To get the clientContext in each of our requests, we need to pass the user token through the Authorization Headers. 

const { user } = useIdentityContext(); const GetAvenger = async (id) => { let { data } = await axios.get('/api/GetAvenger/?id=' + id, user && { headers: { Authorization: `Bearer ${user.token.access_token}` } }); getAvenger(data) }

This user token will be available in the user context once logged in to the application through the netlify identity widget.

As you can see, Netlify functions and Fauna look to be a promising duo for building serverless applications. You can follow this GitHub repo for the complete code and refer to this URL for the working demo.


In conclusion, Fauna and Netlify look to be a promising duo for building serverless applications. Netlify also provides the flexibility to extend its functionality through the plugins to enhance the experience. The pricing plan with pay as you go is ideal for developers to get started with fauna. Fauna is extremely fast, and it auto-scales so that developers will have the time to focus on their development more than ever. Fauna can handle complex database operations where you would find in Relational, Document, Graph, Temporal databases. Fauna Driver support all the major languages such as Android, C#, Go, Java, JavaScript, Python, Ruby, Scala, and Swift. With all these excellent features, Fauna looks to be one of the best Serverless databases. For more information, go through Fauna documentation.

The post Accessing Your Data With Netlify Functions and React appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

What I Wish I Knew About CSS When Starting Out As A Front-Ender

Css Tricks - Wed, 09/01/2021 - 1:25pm

Nathan Hardy shares when things “clicked”:

Reflecting back on this time, I think there are a few key concepts that were vital to things finally all making sense and fitting together. These were:

• The Box Model (e.g. box-sizing, height, width, margin, padding)
• Layout (e.g. display)
• Document Flow and Positioning (e.g. position, top, left, etc.)

I called this my ah-ha moment a few years back:

For me, it was a couple of concepts that felt like an unlocking of real power. It was a combination of these concepts that were my “Ah-ha!” moment.

• Every page element is a box.
• I can control the size and position of those boxes.
• I can give those boxes background images.

People shared their own as well. And again.

It’s really the deal.

At the end of the day, CSS is just painting, stacking, and nudging boxes.

— Stephanie Eckles (@5t3ph) August 26, 2021

Direct Link to ArticlePermalink

The post What I Wish I Knew About CSS When Starting Out As A Front-Ender appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Some Typography Links VII

Css Tricks - Wed, 09/01/2021 - 4:37am
Article on Aug 24, 2021 Some Typography Links I Chris Coyier Article on Aug 24, 2021 Some Typography Links II Chris Coyier Article on Aug 24, 2021 Some Typography Links III Chris Coyier Article on Aug 24, 2021 Some Typography Links IV Chris Coyier Article on Sep 1, 2021 Some Typography Links V Chris Coyier Article on Sep 1, 2021 Some Typography Links VI Chris Coyier

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

Application-Specific Links

Css Tricks - Tue, 08/31/2021 - 4:30am

You know like https:? That’s a URL Scheme. You’re probably familiar with the concept, thanks to others that come up in front-end development, like mailto:. You can actually make your own, which is pretty cool. There are a lot of them.

I find that custom URL schemes come up the most with apps that are both web apps and native apps. For example, two that I use nearly every day: Notion and Figma. I love that the things I work on in these apps have URLs. URLs for everything! &#x1f389;

And yet. When I grab the URL to a Notion page, which I do regularly to share with co-workers, I get a URL like…


That’s fine, and works to open that Notion page in the browser. But I prefer Notion-the-native-app. It’s Electron, so it’s still a web app I guess, but I don’t use it from my web browser, I use it from the application Notion.app on my literal machine.

Geoff shared with me an article the other day that documents how easy it is to make an application’s browser URL open up in the native app instead:

Fortunately, Notion’s dev team thought about that, and built the notion:// link protocol. If you replace the https:// portion of any Notion page link with notion://, your link will automatically open within the native app instead of a web browser.

Thomas Frank, “How to Share Notion Links That Open Directly in the App”

That’s great that the native scheme is essentially the same as the web scheme, aside from the name. Thomas goes super deep on this with methods to alter the content of clipboard to replace Notion links with the custom scheme.

I just wanted to note a method I think works nicely for me. The trick isn’t to alter the links themselves, but to react to links that you know are Notion links by redirecting them to open in Notion.app.

The trick, on Macs, is Choosy:

I prefer to set up Choosy such that it never asks me what browser to use, it just does it based on rules. So under the settings, I have a bunch of apps set up:

For Notion, I watch for links to Notion, and have it open up Notion… that’s it!

The other apps basically do the exact same thing. Works great.

One caveat though! Once in a blue moon, I have to come in here and flip certain applications off. For example, a password reset flow might send me to slack.com or something, for a certain page as part of the flow that is only available through the web. If Choosy is doing its thing, it tries to force that page to open in Slack.app, which it won’t, and you can kinda get trapped. So, I have to come in here and flip it off temporarily.

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

CSS Pseudo Commas

Css Tricks - Mon, 08/30/2021 - 11:20am

A bonafide CSS trick if there ever was one! @ShadowShahriar created a CodePen demo that uses pseudo-elements to place commas between list items that are displayed inline, and the result is a natural-looking complete sentence with proper punctuation.

CodePen Embed Fallback How it works

The trick? First, it’s to make an unordered list an inline element with no markers or spacing:

ul { padding: 0; margin: 0; display: inline; list-style-type: none; }

Next, we display list items inline so they flow naturally as text in a sentence:

li { display: inline; }

Then we add commas between list items by selecting their ::after pseudo-element, and setting it’s content property with a comma (,) value.

li::after{ content: var(--separator); }

Oh, but wait! What about the ol’ Oxford comma? Use :nth-last-of-type() to select the second-to-last list item, and set its ::after pseudo-element’s content property to ", and" before the last list item.

li:nth-last-of-type(2)::after{ content: ", and "; }

We’re not done. @ShadowShahriar considers an edge case where there are only two items. All we need is to display an “and” between those two items, so:

li:first-of-type:nth-last-of-type(2)::after { content: " and "; }

I had to look that up on Selectors Explained to make sure I was reading it correctly. That’s saying:

The after pseudo-element

… of a <li> element provided it is the first of its type in its parent and the nth of its type from the end (formula) in its parent.

What a mouthful! The final touch is a period at the end of the list:

li:last-of-type::after { content: "."; } Using custom properties

We just looked at an abridged version of the actual code. @ShadowShahriar does a nice thing by setting a comma and the “and” as custom properties:

ul { --separator: ","; --connector: "and"; padding: 0; margin: 0; display: inline; list-style-type: none; }

That way, when we can swap those out for other ways to separate list items later. Nice touch.

This caught my eye not only for its clever use of pseudo-element trickery, but also for its simplicity. It’s using tried and true CSS principles in a way that supports semantic HTML — no extra classes, elements, or even JavaScript to help manipulate things. It almost makes me wonder if HTML could use some sort of inline list element (<il> anyone???) to help support sentences convey list items without breaking out of a paragraph.

Direct Link to ArticlePermalink

The post CSS Pseudo Commas appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

My tiny side project has had more impact than my decade in the software industry

Css Tricks - Fri, 08/27/2021 - 9:14am

That’s a heartwrenching title from Michael Williamson. I believe it though. It’s kinda like a maximized version of the blogging phenomenon where if you work on a post for weeks it’ll flop compared to a post that’s some dumb 20-minute thought. Or how your off-handed remark to some developer at the perfect time might cause some huge pivot in what they are doing, changing the course of a project forever. For Mike, it was a 3,000 line-of-code side project that had more impact on the world than a career of work as a software developer.

I’ve tried to pick companies working on domains that seem useful: developer productivity, treating diseases, education. While my success in those jobs has been variable – in some cases, I’m proud of what I accomplished, in others I’m pretty sure my net effect was, at best, zero – I’d have a tough time saying that the cumulative impact was greater than my little side project.

Impact is fuzzy though, isn’t it? I don’t know Mike, but assuming he is a kind and helpful person, think of all the people he’s likely helped along the way. Not by just saving them minutes of toil, but helped. Helped grow, helped through hard times, helped guide to where they ought to go. Those things are immeasurable and awfully important.

Direct Link to ArticlePermalink

The post My tiny side project has had more impact than my decade in the software industry appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

The Fixed Background Attachment Hack

Css Tricks - Fri, 08/27/2021 - 4:42am

What options do you have if you want the body background in a fixed position where it stays put on scroll? background-attachment: fixed in CSS, at best, does not work well in mobile browsers, and at worst is not even supported by the most widely used mobile browsers. You can ditch this idea completely and let the background scroll on small screens using media queries.

Or get around it with a small fix. I suppose we could call it a “hack” since it’s a workaround in code that arguably we shouldn’t have to do at all.

The issue

Before I show you the fix, let’s examine the issue. We can see it by looking at two different approaches to CSS backgrounds:

  1. a background using a linear gradient
  2. a background using an image
Linear gradient

I want to keep the background gradient in a fixed position on scroll, so let’s apply basic CSS styling to the body that does exactly that:

body { background: linear-gradient(335deg, rgba(255,140,107,1) 0%, rgba(255,228,168,1) 100%); background-attachment: fixed; background-position: center; background-repeat: no-repeat; height: 100vh; }

Here are the results in Chrome and Firefox, both on Android, respectively:

Chrome Android Firefox Android

The gradient simply scrolls along with other content then jumps back. I don’t know exactly why that is — maybe when the URL tab goes up or disappears on scroll and the browser finds it difficult to re-render the gradient in real time? That’s my best guess since it only seems to happen in mobile browsers.

If you’re wondering about iOS Safari, I haven’t tested on iOS personally, but the issue is there too. Some have already reported the issue and it appears to behave similarly.

Background image

This issue with images is no different.

body { background: url(../assets/test_pic.jpg); background-repeat: no-repeat; background-size: cover; background-position: center; background-attachment: fixed; height: 100vh; } The grey section at the top just indicates the presence of an actual URL bar in Chrome Android.

Another interesting thing to note is that when background-attachment: fixed is applied, the height is ignored even if we explicitly specify it. That’s because background-attachment calculates a fixed background position relative to the viewport.

Even if we say the body is 100vh, background-attachment: fixed is not exactly in accordance with it. Weird! Perhaps the reason is that background-attachment: fixed relies on the smallest possible viewport while elements rely on the largest possible viewport. David Bokan explains,

Lengths defined in viewport units (i.e. vh) will not resize in response to the URL bar being shown or hidden. Instead, vh units will be sized to the viewport height as if the URL bar is always hidden. That is, vh units will be sized to the “largest possible viewport”. This means 100vh will be larger than the visible height when the URL bar is shown.

The issues are nicely documented over at caniuse:

  • Firefox does not appear to support the local value when applied on a textarea element.
  • Chrome has an issue that occurs when using the will-change property on a selector which also has background-attachment: fixed defined. It causes the image to get cut off and gain whitespace around it.
  • iOS has an issue preventing background-attachment: fixed from being used with background-size: cover.

Let’s fix it

Call it a temporary hack, if you will. Some of you may have already tried it. Whatever the case, it fixes the linear gradient and background image issues we just saw.

So, as you know, we are getting in trouble with the background-attachment: fixed property and, as you might have guessed, we are removing it from our code. If it’s looking at the smallest possible viewport, then maybe we should be working with an element that looks for the largest possible viewport and position that instead.

So, we are creating two separate elements — one for the background-gradient and another for the rest of the content. We are replacing background-attachment: fixed with position: fixed.

<div class="bg"></div> <div class="content"> <!-- content --> </div> .bg { background: linear-gradient(335deg, rgba(255,140,107,1) 0%, rgba(255,228,168,1) 100%); background-repeat: no-repeat; background-position: center; height: 100vh; width: 100vw; position: fixed; /* z-index usage is up to you.. although there is no need of using it because the default stack context will work. */ z-index: -1; // this is optional }

Now, wrap up the rest of the content — except for the element containing the background image — inside a main container.

.content{ position: absolute; margin-top: 5rem; left: 50%; transform: translateX(-50%); width: 80%; }


Chrome Android Firefox Android

We can use the same trick hack with background images and it works fine. However, you do get some sort of background scrolling when the URL bar hides itself, but the white patch is no longer there.

.img { background: url('../assets/test_pic.jpg'); background-position: center; background-repeat: no-repeat; background-size: cover; position: fixed; height: 100vh; width: 100vw; } .content { position: absolute; left: 50%; margin-top: 5rem; transform: translateX(-50%); width: 80%; } Chrome Android Firefox Android Here are my takeaways

A fixed-position element with a height set to 100% behaves just like the element with background-attachment: fixed property, which is clearly evident in the example below! Just observe the right-most bar (purple color) in the video.

The website which is being tested is taken from this article.

Even though, David Bokan in his article states that:

That is, a position: fixed element whose containing block is the ICB will resize in response to the URL bar showing or hiding. For example, if its height is 100% it will always fill exactly the visible height, whether or not the URL bar is shown. Similarly for vh lengths, they will also resize to match the visible height taking the URL bar position into account.

If we take into account that last sentence, that doesn’t seem to be the case here. Elements that have fixed positioning and 100vh height don’t change their height whether the URL bar is shown or not. In fact, the height is according to the height of the “largest possible viewport”. This is evident in the example below. Just observe the light blue colored bar in the video.

The website which is being tested is taken from this article.

So, it appears that, when working with a container that is 100vh, background-attachment: fixed considers the smallest possible viewport height while elements in general consider the largest possible viewport height.

For example, background-attachment: fixed simply stops working when a repaint is needed, like when a mobile browser’s address bar goes away on scroll. The browser adjusts the background according to the largest possible viewport (which is now, in fact, the smallest possible viewport as URL bar is hidden) and the browser isn’t efficient enough to repaint on the fly, which results in a major lag.

Our hack addresses this by making the background an element instead of, well, an actual background. We give the element containing the content an absolute position to stack it on top of the element containing the image, then apply a fixed position on the latter. Hey, it works!

Note that the viewport height is calculated excluding the navigation bar at the bottom (if present). Here’s a comparison between the presence and absence of navigation bar at the bottom in Chrome Android.

Is there a downside? Perhaps! We’re using a general <div> instead of an actual <img> tag, so I wouldn’t say the markup is semantic. And that can lead to accessibility issues. If you’re working with an image that adds meaning or context to the content, then an <img> is the correct way to go, utilizing a proper alt description for screen readers.

But if we go the proper <img> route, then we’re right back where we started. Also, if you have a navigation bar at the bottom which too auto hides itself, then I can’t help it. If the hack won’t cut it, then perhaps JavaScript can come to the rescue.

The post The Fixed Background Attachment Hack appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Designing for the Unexpected

Css Tricks - Thu, 08/26/2021 - 1:07pm

When I think about what front-end development really is and feels like, this is at the heart of it: designing around a huge set of unknowns, and really embracing that notion as a strength of the web rather than a weakness or unfortunate truth we have to work around.

Cathy Dutton digs into this with real code and examples over on A List Apart. A recurring theme is the idea that content (certainly an unknown, since content changes) can and should drive design decisions. It is even floated that container queries might not be all they are cracked up to be since they are still based on a parent, not content.

It’s hard to say for sure whether container queries will be a success story until we have solid cross-browser support for them. Responsive component libraries would definitely evolve how we design and would improve the possibilities for reuse and design at scale. But maybe we will always need to adjust these components to suit our content.

We can’t design the same way we have for this ever-changing landscape, but we can design for content. By putting content first and allowing that content to adapt to whatever space surrounds it, we can create more robust, flexible designs that increase the longevity of our products. 

Direct Link to ArticlePermalink

The post Designing for the Unexpected appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Some Articles About Accessibility I’ve Saved Recently III

Css Tricks - Thu, 08/26/2021 - 9:35am

The post Some Articles About Accessibility I’ve Saved Recently III appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

How I Made a Generator for SVG Loaders With Sass and SMIL Options

Css Tricks - Thu, 08/26/2021 - 4:35am

While learning Vue.js, I started building free web tools that involved the exploration of SVG, with the goal of learning something about both! Let’s take a look at one of those tools: a generator that makes SVG loaders and lets you choose between SMIL or Sass animation, different styles, colors, shapes, and effects. It even lets you paste in a custom path or text, and then download the final SVG, copy the code, or open a demo over at CodePen.

How it started

Three coincidences led me to build a generator for SVG loaders.

Coincidence 1: Sarah Drasner’s book

The first time I read about Sass loops was in Sarah Drasner’s SVG Animations. She shows how to stagger animations with a Sass function (like the does in Chapter 6, “Animating Data Visualizations”).

I was inspired by that chapter and the possibilities of Sass loops.

Coincidence 2: A GIF

At that same point in life, I was asked to replicate a “loader” element, similar to Apple’s old classic.

This is a mockup of the loader I was asked to make.

I referenced Sarah’s example to make it happen. This is the Sass loop code I landed on:

@for $i from 1 to 12 { .loader:nth-of-type(#{$i}) { animation: 1s $i * 0.08s opacityLoader infinite; } } @keyframes opacityLoader { to { opacity: 0; } }

This defines a variable for a number (i) from 1 to 12 that increases the delay of the animation with every :nth-child element. It was the perfect use case to animate as many elements as I wanted with only two lines of Sass, saving me CSS declarations for each of the delays I needed. This is the same animation, but written in vanilla CSS to show the difference:

.loader:nth-of-type(1) { animation: 1s 0.08s opacityLoader infinite; } .loader:nth-of-type(2) { animation: 1s 0.16s opacityLoader infinite; } /* ... */ .loader:nth-of-type(12) { animation: 1s 0.96s opacityLoader infinite; } @keyframes opacityLoader { to { opacity: 0; } } Coincidence 3: An idea

With these things going on in my head, I had an idea for a gallery of loaders, where each loader is made from the same Sass loop. I always struggle to find these kinds of things online, so I thought it might be useful for others, not to mention myself.

I had already built this kind of thing before as a personal project, so I ended up building a loader generator. Let me know if you find bugs in it!

One loader, two outputs

I was blocked by my own developer skills while creating a generator that produces the right Sass output. I decided to try another animation approach with SMIL animations, and that’s what I wound up deciding to use.

But then I received some help (thanks, ekrof!) and got Sass to work after all.

So, I ended up adding both options to the generator. I found it was a challenge to make both languages return the same result. In fact, they sometimes produce different results.

SMIL vs. CSS/Sass

I learned quite a bit about SMIL and CSS/Sass animations along the way. These are a few of the key takeaways that helped me on my way to making the generator:

  • SMIL doesn’t rely on any external resources. It animates SVG via presentation attributes directly in the SVG markup. That’s something that neither CSS nor Sass can do.
  • SMIL animations are preserved when an SVG is embedded as an image or as a background image. It is possible to add a CSS <style> block directly inside the SVG, but not so much with Sass, of course. That’s why there is an option to download the actual SVG file when selecting the SMIL option in the generator.
  • SMIL animations look a bit more fluid. I couldn’t find the reason for this (if anyone has any deeper information here, please share!). I though it was related to GPU acceleration, but it seems they both use the same animation engine.
SMIL (left) and Sass (right)

You might notice a difference in the chaining of the animations between both languages:

  • I used additive="sum" in SMIL to add animations one after the other. This makes sure each new animation effect avoids overriding the previous animation.
  • That said, in CSS/Sass, the W3C points out that [when] multiple animations are attempting to modify the same property, then the animation closest to the end of the list of names wins.

That’s why the order in which animations are applied might change the Sass output.

Working with transforms

Working with transformations in the loader’s styling was a big issue. I had applied transform: rotate inline to each shape because it’s a simple way to place them next to each other in a circle and with a face pointing toward the center.

<svg> <!-- etc. --> <use class="loader" xlink:href="#loader" transform="rotate(0 50 50)" /> <use class="loader" xlink:href="#loader" transform="rotate(30 50 50)" /> <use class="loader" xlink:href="#loader" transform="rotate(60 50 50)" /> <!-- etc. --> </svg>

I could declare a type in SMIL with <animateTransform> (e.g. scale or translate) to add that specific transform to the original transformation of each shape:

<animateTransform attributeName="transform" type="translate" additive="sum" dur="1s" :begin="`${i * 0.08}s`" repeatCount="indefinite" from="0 0" to="10" />

But instead, transform in CSS was overriding any previous transform applied to the inline SVG. In other words, the original position reset to 0 and showed a very different result from what SMIL produced. That meant the animations wound up looking identical no matter what.

The (not very pretty) solution to make the Sass similar to SMIL was to place each shape inside a group (<g>) element, and apply the inline rotation to the groups, and the animation to the shapes. This way, the inline transform isn’t affected by the animation.

<svg> <!-- etc. --> <g class="loader" transform="rotate(0 50 50)"> <use xlink:href="#loader" /> </g> <g class="loader" transform="rotate(30 50 50)"> <use xlink:href="#loader" /> </g> <!-- etc. --> </svg>

Now both languages have a very similar result.

The technology I used

I used Vue.js and Nuxt.js. Both have great documentation, but there are more specific reasons why I choose them.

I like Vue for lots of reasons:

  • Vue encapsulates HTML, CSS, and JavaScript as a “single file component” where all the code lives in a single file that’s easier to work with.
  • The way Vue binds and dynamically updates HTML or SVG attributes is very intuitive.
  • HTML and SVG don’t require any extra transformations (like making the code JSX-compatible).

As far as Nuxt goes:

  • It has a quick boilerplate that helps you focus on development instead of configuration.
  • There’s automatic routing and it supports auto-importing components.
  • It’s a good project structure with pages, components, and layouts.
  • It’s easier to optimize for SEO, thanks to meta tags.
Let’s look at a few example loaders

What I like about the end result is that the generator isn’t a one-trick pony. There’s no one way to use it. Because it outputs both SMIL and CSS/Sass, there are several ways to integrate a loader into your own project.

Download the SMIL SVG and use it as a background image in CSS

Like I mentioned earlier, SMIL features are preserved when an SVG is used as a background image file. So, simply download the SVG from the generator, upload it to your server, and reference it in CSS as a background image.

CodePen Embed Fallback

Similarly, we could use the SVG as a background image of a pseudo-element:

CodePen Embed Fallback Drop the SVG right into the HTML markup

The SVG doesn’t have to be a background image. It’s just code, after all. That means we can simply drop the code from the generator into our own markup and let SMIL do its thing.

CodePen Embed Fallback Use a Sass loop on the inline SVG

This is what I was originally inspired to do, but ran into some roadblocks. Instead of writing CSS declarations for each animation, we can use the Sass loop produced by the generator. The loop targets a .loader class that’s already applied to the outputted SVG. So, once Sass is compiled to CSS, we get a nice spinning animation.

CodePen Embed Fallback I’m still working on this

My favorite part of the generator is the custom shape option where you can add text, emojis, or any SVG element to the mix:

Custom text, emoji, and SVG

What I would like to do is add a third option for styles to have just one element where you get to work with your own SVG element. That way, there’s less to work with, while allowing for simpler outputs.

The challenge with this project is working with custom values for so many things, like duration, direction, distance, and degrees. Another challenge for me personally is becoming more familiar with Vue because I want to go back and clean up that messy code. That said, the project is open source, and pull requests are welcome! Feel free to send suggestions, feedback, or even Vue course recommendations, especially ones related to SVG or making generators.

This all started with a Sass loop that I read in a book. It isn’t the cleanest code in the world, but I’m left blown away by the power of SMIL animations. I highly recommend Sarah Soueidan’s guide for a deeper dive into what SMIL is capable of doing.

If you’re curious about the safety of SMIL, that is for good reason. There was a time when Chrome was going to entirely deprecated SMIL (see the opening note in MDN). But that deprecation has been suspended and hasn’t (seemingly) been talked about in a while.

Can I use SMIL?

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

DesktopChromeFirefoxIEEdgeSafari54No796Mobile / TabletAndroid ChromeAndroid FirefoxAndroidiOS Safari929036.0-6.1

The post How I Made a Generator for SVG Loaders With Sass and SMIL Options appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

One Way to Convert Code Indentation

Css Tricks - Wed, 08/25/2021 - 12:34pm

A question:

If you copy a code sample that uses two-space indentation and you want to convert it to four-space indentation, what’s the *fastest* and easiest option?

Matt Stauffer, Twitter

I wrote about doing this in Sublime Text a few years back. It’s not terribly different in VS Code, I don’t think.

But here’s another way: Use CodePen.

Step 1: Copy and paste to CodePen

Say you found some code elsewhere, just copy and paste it in:

Step 2: Adjust code indentation settings

If you already have a CodePen account, you’ve probably already got it set up, so the default is how you like it. But if it’s not yet, adjust it in the Pen Settings area:

The code was 2-spaces as copy/pasted, and now we’re changing that to 4-spaces as a setting. Step 3: Format the code

You can manually do it here:

You may not have to use that option at all if you save the Pen and have the “Format on Save” option on, as it will automatically happen.

It’ll kick over to that new 4-space preference right quick:

CodePen uses Prettier under the hood to do this. So you could do this anywhere you have access to a working version of Prettier, but it might be easier on CodePen since there’s nothing like editing config or anything to adjust how you want it. Prettier has its own playground as well, which is likely just as easy as CodePen, except that on CodePen you might already have your preferences set up, saving a step.

The post One Way to Convert Code Indentation appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Stealing Game Animation Techniques to Engage Users

Css Tricks - Wed, 08/25/2021 - 4:55am

Today’s websites are overflowing with animations—often too many. They get in the way of the content and slow down our busy users. But at the same time: they’re wonderful. They bring websites to life, are fun to implement and can be incredibly impressive to show off. I think they’re great. Sorry impatient users.

The way I see it, the problem isn’t necessarily that websites have too many animations, but that the animations don’t vibe with the content they’re promoting. They’re out of place with their subject matter. They feel contrived and provide no additional value.

This is an article for web developers who want to get fancy-shmancy with the finest animations around, but don’t want to do it at the cost of annoying users. I’ll show you some of the ways I’ve personally used website animations while trying to annoy very few users. You might be thinking that “not annoying users” is a very low bar that I’ve set and well, uh.. hmm.. yes. Good point.

This is a topic I’ve somewhat stumbled my way into. I work as a web developer for an indie video game publisher called Devolver Digital. I, along with Vieko, make websites for these video games. While I’m primarily a dev, a good chunk of my job is to conceptualize and create designs. I once thought that design didn’t seem that hard, the truth is, as most of you probably know, it’s not that straightforward. It’s really, really hard.

My first design for the Ape Out website &#x1f62c;

When I first started making video game websites, I would sketch it out in a notebook. This would be fine, but then when I tried to implement the design in Photoshop, it just wouldn’t come together. I would restart from scratch and try again. It still wouldn’t work. Then I would try to skip the whole design step and jump into the code, hoping that the animations would bring it all together. The animations were cool, but it still wasn’t good enough. My design skills were lacking.

But then I figured out a way to fake it.

Now, when I show website concepts to my colleagues and clients for feedback, they tell me that I’m the best designer in the whole world and they send me cookies. They don’t know that I’m hiding a terrible secret.

I don’t know if I should reveal this secret. Web designers around the world are going to hate me. My colleagues will ask for their cookies back. OK—I ate the cookies. I’ll let you in on the secret. The secret is that…

I copy everything from video games.

I copy the colors, the buttons, the modal boxes, and even the core concepts and gameplay mechanics. I play the games (they’re fun), take screenshots, capture footage, and then I steal it all. Most important of all, I steal the animations.

While video games make for a perfect medium from which to burgle, I believe that people like you can also steal from your corresponding industry. While stealing from games helped me cheat as a designer, it was stealing the animations that really unlocked the full potential of each website.

It’s free real estate

We, at Devolver Digital, have a massive variety of games. Some are calming, narrative experiences, while others have very intriguing gameplay elements. Some have very simple visuals that hide deep secrets, and others are Shadow Warrior 3.

Each one of these games has a unique way of wiggling themselves into a player’s brain and setting up a cozy little living space. With each website, we attempt to replicate this brain connection and harness it. Modern web tech is at a place now where we can make websites that effectively engage our users into making this brain connection before they’ve even grabbed a gamepad.

So basically, we heist assets/animations/mechanics/everything from the game to give the user a glimpse at what it would be like to play it.

For example

I want to start with one of my favorite projects. It’s the website for a game called Olija. It’s about a harpoon-wielding hero exploring hostile lands to find his way home. The game has two core aspects. The first is its visual fluidity. The pixel size in its pixel art is fairly large, so it can be difficult to convey this game’s beauty without seeing it in motion. The second core aspect is its story. It feels like the game is based on a pre-existing book or TV series.

The goal for the website was to take these two core aspects, and entice the user to engage with them; to make them feel like a part of the story and to make them feel in control of the action.

I’m going to jump away from Olija for a second here. I should have mentioned this earlier. Websites have an aspect that gives them an advantage over most other media: they’re interactive (obviously). This is great because we can use this interactivity to get the user to engage with the site at a deeper level. This is key. We have a few tools we can use to make their experience more vibrant and memorable. These tools are things like mouse movement, mouse hovers, pointer down/up states, the scrollbar, and the keyboard. My favorite is the scrollbar. You’ll see it used often in my projects. I feel it’s the most intuitive to use and I like that it requires little thought or active energy from the user.

Back to Olija. One of the first things the user sees upon scrolling down is a movie-credits-like fade animation. It’s slow. It takes three viewport heights (300vh) to scroll through it all but it’s a critical part. It sets up the pacing and immediately shows the tone of the game. It’s a very simple animation but it was vital. The rest of the page felt much more natural once this section was extended and slowed down.

Olija credits sequence

After the story sections, the user gets to dynamic screenshot-like sections. There’s one section in particular in which the hero runs across a lively forest backdrop. Showing a lighter moment like this one reinforces the epic-ness of the story and shows off the game’s unique style.

One of the trickier parts for Olija, is that pixel-art is based on sprite sheets. We can’t just animate an element’s transform properties to move it around the screen. We have to also animate its current position in the sprite sheet. Here’s a Pen that shows how we did it.

We took a similar approach with Ape Out. The game feels like an action movie. As the game’s title accurately describes, the player is an ape trying to escape from various scenarios. The idea for the accompanying website was to show a scene that had already been played out, letting the user imagine all the action that leads up to the final epic moment. Again, they control the pace of the camera that explores this scene with the scroll bar.

Ape Out Camera Pan

At first glance it might look like a 3D WebGL canvas, but it’s actually a lot of divs placed with a 3D transform where the corresponding perspective origin is updated based on the scroll position. Not sure if it’s any better or easier than using ThreeJS but it’s.. uhh.. doable. Here’s a barebones version of it in CodePen.

Enter the Gungeon in the Living Room (Illustration by Björn Feldmann)

As a last example for story-driven experiences, I want to mention the Enter the Gungeon website. It’s a mix of the Olija and Ape Out ideas, but instead of trying to capture the game’s narrative, it attempts to celebrate the time players have spent with it. The goal was to slow down and let the user reminisce, then use this nostalgia to promote the Exit the Gungeon and House of the Gundead games.

Unique selling point

Sometimes games have a unique hook or gameplay element and the website is the perfect place to showcase it. A great example is Loop Hero. It’s a very simple game on the surface. The hero automatically travels along a looping path and fights the monsters they encounter.

The player controls the hero’s equipment and whether they abandon the path to return to the village. This made infinite looping a clear concept for the website. When the user reaches the bottom of the page they are seamlessly transported back to the top and the page resets. The scene in the viewport is exactly the same so the user is none-the-wiser.

Loop Hero’s looping website

There are a few other concepts on this site that are directly taken from the game. Once the user scrolls to the battle trigger area, the scrollbar is disabled (sorry, I know it’s bad) and they are forced to watch the battle play out. The hero and monsters each have health points, attack speed, and attack power values with random variance. This means that the battle outcomes are not hard-coded. Each time the user loops through the page, the background map is updated with new tiles to show how the game progresses and evolves.

One of my favorite features of the Loop Hero website is the fade effect between the top section and the looped road section below it. The game developer at Four Quarters sent me the shader code (written with help from @kartonnnyi) and the Perlin Noise image used for this effect. I can’t say I fully understand how it all works, but I was able to put it together with gl-react.

People with a deep understanding of how to write shaders have my deepest respect. I don’t think I’ll ever fully be able to wrap my head around them. The stuff at Shadertoy blows my mind.

Boomerang X and Shadow Warrior are similar websites that use gameplay footage as a direct way to show off the game. They both engage the user with elements connected with the background video. The Shadow Warrior website content shakes when there’s an impact in the video and blood slowly gathers on the menu/logos. The Boomerang X logo zips back and forth along with the game footage’s boomerang. The logo is further connected with the user by having it react to the user’s mouse position.

Alright, now let me tell you about Devolver Tumble Time. I think most web developers, upon being given the opportunity to make a website for this game, would feel an urge to replicate the tumbler mechanic. From first-hand experience, I can say that it’s not as simple as it looks. I knew that it was critical to get the tumbler to run as fluidly as possible, otherwise it would reflect poorly on the game and turn people away.

I started out with MatterJS. It seemed like the obvious choice. The tumbler is two-dimensional, so pick the most popular 2D engine right? Well, it turns out the tumbler isn’t 2D and I don’t think centrifugal force is a thing in MatterJS. I, like another soul before me, attempted to hack it in. I tried to implement their attempt unsuccessfully.


To get the tumbler to work, I needed two directions for the gravity. The items should fall down the y-axis, but they also need to be able to “stick” to a spinning disc through gravity/friction on the z-axis. I spent a weekend refactoring the tumbler code in ThreeJS and achieved the desired fluidity.

Down into the abyss

I want to end this article with a somewhat deep-dive into the Phantom Abyss website. It encompasses a lot of the techniques used in recent projects and it only weighs 4MB.

The website might seem fairly barebones, but the longer the user looks at the page, the more there is to see. The shifting blocks and fading phantoms should be obvious, and then the user might see that the waterfalls are animated, along with some dust particles, the torches, the woman’s hair and whip, and then lastly the phantom’s eyes above the logo, a sparkle on some of the relics, and the fires and birds in the far background.

Phantom Abyss Animations (Illustration by Dan Mumford)

Now, I’m definitely not expecting many users to notice all these minor details, but they bring depth to the page. They engage the user into subconsciously understanding that the game holds secrets and depth.

This is the first project on which I used SVGator, a really neat tool for animating SVGs. I used this for the torches, the woman’s hair/whip, and the phantom’s blinking eyes.

The torches were not easy. My first attempt looked like creepy wild tentacles reaching out to grab whatever they could find. My second attempt was better but not quite there. My third attempt still wasn’t good enough, but adding blur and some sparks brought it to the point where it is today. If you look very closely the torches still look a bit like tentacles. In my next project I saw that the key-art featured a lot of tentacles. I knew what I had to do.

Start inside

These are just a few of the examples of animation tricks we’ve used on websites. Devolver Digital has a never-ending catalog of games and from that site you can check out other websites we’ve made. Ragnorium, Heave Ho, and Gato Roboto are some of our sites that stole a lot from their games. Also, full credit to Vieko as he was the original game animation burglar with his sites for Minit and Sludge Life.

Our sites are mostly all hosted on Vercel ❤ with NextJS &#x1f44c;. We also rely heavily on Framer Motion &#x1f929; and often react-three-fiber &#x1f386;. There are dozens of alternative tools that are also great but we love these ones and they’ve made our lives much easier.

Today, we developers have access to an immense set of tools and techniques with new ones being announced all the time. It’s very easy to get caught up in what other talented devs are doing and we then try to replicate their effects. This isn’t necessarily a bad approach, but it can lead to being closed-off and repetitious. This causes us to then create animations that users have already seen dozens of times.

Instead of looking at other devs and their work for inspirational animations and transitions, we should look at the content and subject matter we’re showcasing. Invent completely new animations. We should take a step back and really contemplate how we can engage the users to connect with our websites and deepen their experiences.

The post Stealing Game Animation Techniques to Engage Users appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Syndicate content
©2003 - Present Akamai Design & Development.