Front End Web Development

Product teams from AirBnb and New York Times to Shopify and Artsy (among many others) are converging on a new set of best practices and technologies for building the web apps that their businesses depend on. This trend reflects core principles and solve underlying problems that we may share, so it is worth digging deeper.

Internal consistency: Created with static typing tools like TypeScript.

Data manipulation: These work with GraphQL-speaking clients like Apollo.

Data representation: Displayed with a library for reusable components and behaviors, like React.

Naming things is hard, and our industry has struggled to name this new generation of tooling for web apps. The inimitable Orta Theroux calls it an Omakase; I slimmed it down and opted for a simpler backronym pulled from letters in the tooling outlined above: STAR (Design Systems, TypeScript, Apollo, and React).

STAR apps are not "yet another front-end stack." They involve additional opinions and constraints. As such, STAR apps aren’t necessarily easy, either. They have a learning curve. A solo developer may find STAR apps unnecessarily verbose because they front-load communication overhead. STAR apps are more about product team workflow than they are about any specific technology.

However, we find that companies upon companies are finding this stack to be a worthwhile investment. We should ask why.

Context: From LAMP to MEAN

The LAMP stack was identified in 1998 by Michael Kunze to describe the combination of Linux, Apache, MySQL, and PHP as predominant open source technologies to write a full web server. In this model, all rendering and logic was done on the server side, and the role of JavaScript was extremely limited. To this day, this is the most common website architecture due to the popularity of long established frameworks like WordPress, which powers 30% of the Internet.

In the ensuing 20 years, the growth of the web platform (JavaScript in particular), led to a evolution of the "front end" discipline, as a complement to "back end" server-side concerns. Through a combination of Atwood’s Law and Metcalfe’s predictions on the triumph of the web over native platforms, these efforts culminated in a re-imagining of the monolithic architecture to straddle both front and back ends. One prominent encapsulation of this was the MEAN stack, coined by Val Karpov in 2013, to offer "full-stack" JavaScript alternatives, including MongoDB (for NoSQL data storage), Express and Node (to write web servers), and Angular (for writing reactive user interfaces).

What’s changed

However, in the last five years, multiple trends have chipped away at the MEAN stack and the ideal of the full-stack JavaScript monolith:

Instead of every developer writing bespoke endpoints, APIs have become an economy of their own with companies like Stripe, Twilio and Zapier growing purely through the strength of their APIs.

As for proprietary backends, it was clear that not all backend environments were going to be written in JavaScript, particularly with the continuing strength other language frameworks, including Rails, Laravel and .NET, and emerging languages like Go. Even the creators of Express.js and Node.js wound up abandoning JavaScript development altogether.

This has meant that the product engineer’s stack and primary work has shifted even more toward thefront end over what was envisioned by the MEAN stack. Chris has described this as a phenomenon that gives extraordinary powers to front-end developers because of the trend toward front-end tooling for what's traditionally been considered back-end territory. Front-end engineering has also evolved, mostly by incrementally adding a constraint layer on top of what we already use — adding a design philosophy, types, schemas, and component structure to how we make our apps.

Why all this change? Stop changing things!

The truth is that we now live in a world where product and business needs now have requirements to bring web app (including mobile web) engineering on par with Android, iOS, and desktop native app development, while our disparate web development tools are still woefully inadequate in comparison to those tightly scoped ecosystems. It’s not that there’s anything inherently wrong with older toolsets or that the new ones are perfect. Instead, the changes can be seen as responses to the underlying needs of product teams:

Integrated designer/developer workflows: A reliance on manual code tests and design reviews doesn’t scale. Design systems now are comprehensive documentation on the how and why of reusable components across an organization. Brad Frost has shown how to set up "workshop" and "storefront" environments for a style guide and design system workflow using Gatsby. Design tools, like Sketch and Framer, have even begun to tightly integrate React and design into streamlined workflows. TypeScript and GraphQL both also offer tightly coupled self-documenting features with TSDoc, GraphiQL, and related IDE integrations.

Optimized for change: As product teams embrace iterative agile sprints and split testing, it is increasingly important to use flexible paradigms that embrace incremental adjustments. Dan Abramov of the React team calls this "second order" API design — robustness to changing requirements. Design Systems and React make it easy to compose reusable components at breakneck pace, with TypeScript dramatically shortening feedback loops. Adam Neary of Airbnb shows a wonderful example of refactoring and iterating with React and Apollo GraphQL in production.

Note that "product teams" in this article primarily refer to product engineering teams, though it is often the case that product design and product management are co-located or have heavy, frequent input. Engineering workflows must explicitly take them into account as a result.

Remaining frontiers

Believe it or not, I am being descriptive, rather than prescriptive; I’m not recommending that everybody throw out their code and start writing STAR apps. Rather, I am observing and calling out what I see as a trend where great product teams are all converging on this new pattern. And they just may be on to something.

However, I don’t believe the evolution has reached its conclusion. There are still too many important aspects of modern web app development that need broader consensus, which has resulted in a hodgepodge of custom or one-off solutions and checklists. A big one is performance. The average amount of JavaScript shipped on desktop and mobile has doubled in the last five years. All the wonderful web app engineering in the world will be for nothing if the user navigates away before it loads. The traditional solution has been (often hand-rolled) server-side rendering that's later managed by frameworks like Next.js and After.js. However, this does still require running and managing a server, so static rendering solutions like Gatsby and React-Static have become popular to render apps straight to static markup to be lazily rehydrated later (the last piece of the JAMstack). Progressive Web App technology and patterns help make subsequent loads even faster and serve as a viable alternative to native experiences.

To be continued...

As this story continues to unfold, I believe that a lot more exploration and experimentation needs to happen to smooth the learning curve for more teams to adopt STAR app workflows. In fact, I am learning about it myself in the open at STAR Labs and invite you to tag along. If you have experiences to share or questions to ask, I am all ears.

Hooks make it possible to organize logic in components, making them tiny and reusable without writing a class. In a sense, they’re React’s way of leaning into functions because, before them, we’d have to write them in a component and, while components have proven to be powerful and functional in and of themselves, they have to render something on the front end. That’s all fine and dandy to some extent, but the result is a DOM that is littered with divs that make it gnarly to dig through through DevTools and debug.

Well, React Hooks change that. Instead of relying on the top-down flow of components or abstracting components in various ways, like higher-order components, we can call and manage flow inside of a component. Dan Abramov explains it well in his Making Sense of React post:

Hooks apply the React philosophy (explicit data flow and composition) inside a component, rather than just between the components. That’s why I feel that Hooks are a natural fit for the React component model.

Unlike patterns like render props or higher-order components, Hooks don’t introduce unnecessary nesting into your component tree. They also don’t suffer from the drawbacks of mixins.

The rest of Dan’s post provides a lot of useful context for why the React team is moving in this direction (they're now available in React v16.7.0-alpha) and the various problems that hooks are designed to solve. The React docs have an introduction to hooks that, in turn, contains a section on what motivated the team to make them. We’re more concerned with how the heck to use them, so let’s move on to some examples!

The important thing to note as we get started is that there are nine hooks currently available, but we're going to look at what the React docs call the three basic ones: useState(), useEffect, and setContext(). We’ll dig into each one in this post with a summary of the advanced hooks at the end.

Defining state with useState()

If you’ve worked with React at any level, then you’re probably familiar with how state is generally defined: write a class and use this.state to initialize a class:

Notice something else in this example? We’re constructing two different states (input and name). That’s because the useState() hook allows managing multiple states in the same component! In this case, input is the property and setValue holds the state of the input element, which is called by the handleInput function then triggers the updateName function that takes the input value and sets it as the new name state.

Create side effects with useEffect()

So, defining and setting states is all fine and dandy, but there’s another hook called useEffect() that can be used to—you guessed it—define and reuse effects directly in a component without the need for a class or the need to use both redundant code for each lifecycle of a method (i.e. componentDidMount, componentDidUpdate, and componentWillUnmount).

When we talk about effects, we’re referring to things like API calls, updates to the DOM, and event listeners, among other things. The React documentation cites examples like data fetching, setting up subscriptions, and changing the DOM as possible use cases for this hook. Perhaps the biggest differentiator from setState() is that useEffect() runs after render. Think of it like giving React an instruction to hold onto the function that passes and then make adjustments to the DOM after the render has happened plus any updates after that. Again, the React documentation spells it out nicely:

By default, it runs both after the first render and after every update. [...] Instead of thinking in terms of “mounting" and “updating", you might find it easier to think that effects happen “after render". React guarantees the DOM has been updated by the time it runs the effects.

Right on, so how do we run these effects? Well, we start off by importing the hook the way we did for setState().

import { useEffect } from 'react';

In fact, we can call both setState() and useEffect() in the same import:

import { useState, useEffect } from 'react';

Or, construct them:

const { useState, useEffect } = React;

So, let’s deviate from our previous name example by hooking into an external API that contains user data using axios inside the useEffect() hook then renders that data into a list of of users.

It’s worth noting that useEffect() is capable of so, so, so much more, like chaining effects and triggering them on condition. Plus, there are cases where we need to cleanup after an effect has run—like subscribing to an external resource—to prevent memory leaks. Totally worth running through the detailed explanation of effects with cleanup in the React documentation.

Context and useContext()

Context in React makes it possible to pass props down from a parent component to a child component. This saves you from the hassle of prop drilling. However, you could only make use of context in class components, but now you can make use of context in functional components using useContext() . Let’s create a counter example, we will pass the state and functions which will be used to increase or decrease the count from the parent component to child component using useContext(). First, let’s create our context:

const CountContext = React.createContext();

We’ll declare the count state and increase/decrease methods of our counter in our App component and set up the wrapper that will hold the component. We’ll put the context hook to use in the actual counter component in just a bit.

const App = () => {
// Use `setState()` to define a count variable and its state
const [count, setCount] = useState(0);
// Construct a method that increases the current `setCount` variable state by 1 with each click
const increase = () => {
setCount(count + 1);
};
// Construct a method that decreases the current `setCount` variable state by 1 with each click.
const decrease = () => {
setCount(count - 1);
};
// Create a wrapper for the counter component that contains the provider that will supply the context value.
return (
<div>
<CountContext.Provider
// The value is takes the count value and updates when either the increase or decrease methods are triggered.
value={{ count, increase, decrease }}
>
// Call the Counter component we will create next
<Counter />
</CountContext.Provider>
</div>
);
};

Alright, onto the Counter component! useContext() accepts an object (we’re passing in the CountContext provider) and allows us to tell React exactly what value we want (`count) and what methods trigger updated values (increase and decrease). Then, of course, we’ll round things out by rendering the component, which is called by the App.

We’ve merely scratched the surface of what React hooks are capable of doing, but hopefully this gives you a solid foundation. For example, there are even more advanced hooks that are available in addition to the basic ones we covered in this post. Here’s a list of those hooks with the descriptions offered by the documentation so you can level up now that you’re equipped with the basics:

Hook
Description
userReducer()
An alternative to useState. Accepts a reducer of type (state, action) => newState, and returns the current state paired with a dispatch method.
useCallback()
Returns a memoized callback. Pass an inline callback and an array of inputs. useCallback will return a memoized version of the callback that only changes if one of the inputs has changed.
useMemo()
Returns a memoized value. Pass a “create" function and an array of inputs. useMemo will only recompute the memoized value when one of the inputs has changed.
useRef()
useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.
useImperativeMethods
useImperativeMethods customizes the instance value that is exposed to parent components when using ref. As always, imperative code using refs should be avoided in most cases. useImperativeMethods should be used with forwardRef.
useLayoutEffect
The signature is identical to useEffect, but it fires synchronously after all DOM mutations. Use this to read layout from the DOM and synchronously re-render. Updates scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint.

I am attracted to the idea that you shouldn't care how the code you author ends up in the browser. It's already minified. It's already gzipped. It's already transmogrified (real word!) by things that polyfill it, things that convert it into code that older browsers understand, things that make it run faster, things that strip away unused bits, and things that break it into chunks by technology far above my head.

The trend is that the code we author is farther and farther away from the code we write, and like I said, I'm attracted to that idea because generally, the purpose of that is to make websites faster for users.

But as Dave notes, when something goes wrong...

As toolchains grow and become more complex, unless you are expertly familiar with them, it’s very unclear what transformations are happening in our code. Tracking the differences between the input and output and the processes that code underwent can be overwhelming. When there’s a problem, it’s increasingly difficult to hop into the assembly line and diagnose the issue and often there’s not an precise fix.

❌ "I'd like to be able to select an element based on if it contains another particular selector"

❌ "I'd like to be able to select an element based on the content it contains"

❌ "I'd like multiple pseudo elements"

❌ "I'd like to be able to animate/transition something to height: auto;"

❌ "I'd like things from Sass, like @extend, @mixin, and nesting"

❌ "I'd like ::nth-letter, ::nth-word, etc"

✅ "I'd like all the major browsers to auto-update"

Ouch. Oh well. I'm not sure how hotly requested all those actually are or how feasible it is to even implement them. They're merely ideas that I thought we be useful in 2013, and as it turns out, I still do.

This time, instead of me making my own list, let's have a gander around the internet at other people who have rounded up CSS desires.

TL;DR List

In observing several sources of conversation around things people desire in CSS, these seem like the most common asks:

Parent queries. As in, selecting an element any-which-way, then selecting the parent of that element. We have some proof it's possible with :focus-within.

Container queries. Select a particular element when the element itself is under certain conditions.

Standardized styling of form elements.

Has/Contains Selectors.

Transitions to auto dimensions.

Fixed up handling of viewport units.

Notably, what's interesting to me is the lack of people asking for style scoping. It came up a little, but not a ton. It seems like such a big part of the CSS-in-JS conversation, so I'm surprised to see so little mention of it in the context of general "what does CSS need?" conversations.

Jen Simmons asked what's on our lists

Making a wish list of all the things I’d love to see happen in the world of CSS in 2019. What’s on your list? Things you want to learn? Problems you want help solving? New properties you need? Design ideas you wish you could code? Resources you’d love to have? Name your desire.

Tons and tons of requests for a standardized way to style form elements — not just for styling desire, but for accessibility to prevent trading standards for styling.

Being able to test browser support of more than just property/values with @supports

Improved handling of viewport units and their relationship to other browser UI

Improved handling of multi-column layout handling

Elastic scrolling

Tommy Hodgins did a CSS+JS advent calendar on Twitter that documents interesting possibilities

Dec 1: Parent Selector &#x1f384;&#x1f381; Though CSS doesn't have a :parent selector, you can create your own with a small JavaScript function and use a selector like [--parent] in your CSS stylesheets today!

We believe you shouldn't have to spend hours creating online forms for your business. That's why we built WPForms, a drag and drop WordPress form builder that's both EASY and POWERFUL.

WPForms allows you to create beautiful contact forms, email subscription forms, payment forms, smart survey forms, and basically every other type of form for your website within minutes!

Since launching in 2016, WPForms has taken the market by surprise with it's pre-built templates and smart workflows. Currently, WPForms is being used on over 1 million WordPress websites.

WPForms also maintains a 4.9 out of 5 star rating average on WordPress.org with over 3,000 five-star ratings.

WPForms Features

Here are the features that makes WPForms the most powerful and user-friendly WordPress form plugin in the market.

Drag & Drop Form Builder - create smart forms in minutes without writing any code.
150+ Pre-Made Form Templates - start with our form templates to save time, and customize them as needed.
Smart Conditional Logic - customize your forms based on user interaction to collect the most relevant information.

Entry Management - See all your leads right inside your WordPress dashboard with Geo-location data, and other powerful features.

Instant Form Notifications - Get email notifications when a user submits a form. You can smart routing to send the inquiry to the right person in your team.

Email Marketing Integrations - Integrate with your email marketing service using our native integration for MailChimp, AWeber, Constant Contact, Drip, and countless others. You can use also use our Zapier integration to connect your forms to over 1000+ other apps.

Need more social proof? Read WPForms user reviews to see why over 1 million websites choose WPForms to build powerful WordPress forms.

If you're just starting out and don't need all the advanced features, then you can try our free WPForms Lite plugin for creating a simple contact form. You can simply download it from your WordPress site by searching for WPForms in the plugin search of your WordPress site.

My first smartphone was an iPhone 4s. I remember the excitement of exploring its capabilities at a time when it was the coolest thing around. Eventually, of course, I replaced it with a newer model and the old iPhone, still in mint condition, gathered dust for two years. What a waste!

But was it? It occurred to me that I could repurpose the old iPhone to create a useful weather clock for our hallway.

Who needs Nest anyway?

In the process, I discovered that reusing an old device is not only fun and economical, it can also deepen your understanding of web standards. In this tutorial, I will show how I created a small web page to display the date, time, and current weather conditions based on the current GPS location. Together, we’ll retrieve weather data from a public API and hide an API key in a PHP file for security. Finally, we’ll look at adding a manifest file and meta tags so that users can save the page to a device’s home screen and then launch it as a standalone app, including a custom icon for it.

The HTML for the clock has some placeholder text that we will eventually replace. All we really need at this moment is a centered <main> container element and a couple of semantic <time> elements for the date and time. The <span> tag within the second <time> element will be styled specially to display the running seconds and the meridian. The datetime attribute within the <time> elements will be updated dynamically with JavaScript.

A little responsive design is key here. We want this to fit nicely on an iPhone 4s screen or any other small-ish smartphone in both portrait and landscape modes. We also want it to work well on a desktop web browser, of course. We can’t use any bleeding-edge CSS or JavaScript, however, because older devices like my iPhone 4s won’t understand it.

I wound up taking things up a notch by creating styles specific for the time of day and we’ll definitely get to that as well. We could even leverage a media query to darken the daytime style for Mac users who have dark mode turned on in the latest MacOS.

The <span> tag with the running seconds and meridian will wrap nicely based on a width of 2em which is just large enough to accommodate two capital letters (i.e. AM and PM). The final CSS that’s needed is a media query to bump up font sizes when the clock is in landscape mode, or really any device with a viewport wider than 480px.

Here are the base styles we’re looking at, with the more decorative styles in the final app removed for brevity:

For the JavaScript, I chose ES5 because many features of ES6 don’t work on the mobile Safari browser in iOS 9.35, which is the final iOS that runs on the iPhone 4s. Fortunately, ES5 is more than up to the task and the app runs properly on newer devices as well.

We need variables for the current date and time (now), the element that will display the date (dd), the element that will display the time (td) and the names of the months and days. Once the page is loaded, an init function is called to update the time every second (1000 milliseconds). The getClockStrings() function updates the value in the now Date object and returns an object containing HTML strings for the date and time. Then the updateTime() function updates the HTML to show the time. One lesser-used feature here is the inclusion of the toISOString() method of the Date object which adds a machine-readable date string to the datetime attribute of the two <time> elements.

The Geolocation API is an easy way to get the user’s accurate location. We can do that when the page loads, but good manners and Chrome’s best practices audit dictate that we ask the user to initiate the location feature. So, we must add a button and a <div> to the HTML to display the GPS information we receive.

These new items require their own styles — mine can be seen in the embedded Pen above. The important thing is to add the class selector rule for the infotext class as well as the two ID selector rules for the gpsbutton.

The JavaScript requires a few new variables for the latitude, longitude, GPS <div>, and GPS <button>. When clicked, the GPS button calls the getLocation() function which tests for the availability of geolocation support in the browser. If it finds such support, it calls the getCurrentPosition method of the navigator.geolocation object and passes a reference to a success callback function named showPosition and an error callback function named geoError.

At this point, the browser will ask the user for permission to obtain their GPS location. If the visitor refuses, an appropriate message is displayed. If the user approves, the showPosition() function then displays the latitude and longitude and hides the GPS button.

Here’s the Javascript we get based on what we’ve covered in this section:

Adding current weather conditions to this clock is a useful feature. Fortunately, the good folks at OpenWeatherMap.org allow access to basic weather information for free. Sign up for a free account there and you will be given an API key you can use in your web development. The free account provides current conditions and 5-day forecasts for any valid GPS location you throw at it. Just be sure to call the API no more than 60 times per minute or you will be prodded to upgrade to a paid account. Once you have an API key, substitute it for the words YOUR_API_KEY_HERE in this code sample and then paste the code into the browser’s location bar. You will receive a JSON object containing the weather for my location here in Pennsylvania. Experiment with different latitudes and longitudes. You can find coordinates for any major city at LatLong.net where the coordinates are given in the decimal format you need.

The CSS needs styles for the new weather <div> and the icon image. Note that the icon is set to 0 opacity initially. That will be changed in the JavaScript code once valid weather information is retrieved.

We need to add variables to reference the weather URL, the weather <div> (wd), and the weather icon. We also need to decide on Fahrenheit or Celsius. The Boolean value for usephp should be set to false for now. We will discuss hiding your API key in a PHP document a little later. The locationRequested Boolean value will help us avoid calling the weather and geolocation APIs before the user has requested them. The sunset and sunrise time variables will allow us to change the appearance of the clock based on the time of day. The iconurl value provides the stem of the URL we need to retrieve weather icons. We also need a random number between 0 and 14 to use in our updateTime function so that all our users don’t request weather at the same minute each quarter hour. If you have your own set of icons, you can change the URL value for iconurl . The file names for the PNG icons are available at OpenWeatherMap.org.

Next we will modify the final if block of the updateTime() function. We wish to avoid unnecessary calls to the weather and geolocation APIs so it is important to test for sec === 0 to be sure we don't call either API 60 times in a given minute. We also wish to call the APIs only if the user has approved the browser’s geolocation request.

function updateTime(){
//...
if (locationRequested && sec === 0){
checkForSunset(); // Checks for sunset once each minute
if (minutes % 15 === weatherminute){
getWeather(); // Get weather every 15 minutes while the app is running
// weatherminute is a random number between 0 and 14 to ensure
// that users don't all hit the API at the same minute
}
if (minutes % 5 === 0){
getLocation(); // Get location every 5 minutes while the app is running
}
}
}

In the showPosition() function, the URL to request the weather data will be either a relative path to the PHP file or a full HTTPS URL pointing to the OpenWeatherMap.org service. In both cases, we will be passing the latitude and longitude in the query string of the URL. For the APPID, please substitute your own API Key for the words YOUR_API_KEY_HERE unless you are using the PHP solution discussed in Step 4 below.

The showPosition() function then calls getWeather() which updates the weather <div> to let the user know something is happening while the weather data is being retrieved. I opted to use the older XMLHttpRequest standard because fetch is not supported on old devices like the iPhone 4s. If the weather data request is being channeled through a PHP document, the response type will be "document" rather than plain text so we have to test for that. If that is the case, the JSON object we need will be in the textContent property of the body of the response. Otherwise, we only need the plain text found in the responseText property. The data is then parsed as a JSON object and sent to the processWeather() function.

From this JSON data, we need to grab the weather property that contains the description of the current conditions and the filename for the weather icon. The <img> tag in the html that has the ID of "icon" is then updated with a new src property value to load the icon image. The temperature is part of the main property but it has to be converted to Fahrenheit or Celsius (most humans don’t think in Kelvin). When that is done, the current conditions can be assigned to the innerHTML property of the weather <div>. The times for sunrise and sunset are found in the sys property of the data. Once we have those, we can call the checkForSunset() function and modify the style to match the time of day.

I used a disposable API key to create a working demo. Generally, however, putting an API key in plain text within the code of a front-end web application seems like a bad idea. Others might copy it and use up your API access quota. If you have access to a typical web server (CodePen doesn’t do PHP), you can hide the API key in a PHP file. Here is some sample code. Substitute the API key and upload the file as clock.php into the same directory as the main HTML file. The PHP file serves as a sort of proxy to the OpenWeatherMap API that we’re using to fetch data in the demo. If it has any problem retrieving weather data, it simply returns an appropriately structured object with "Weather Unavailable" as a description and a temperature that converts to 0° Fahrenheit. The API key is never transferred from the server to the browser so there is nothing for prying eyes to see.

I would be interested to hear from readers if you know of a secure, serverless solution to hiding an API key (Netlify or Docker perhaps?) because it’d be nice not to have to spin up our own server to store and hit the data. Chime in if you have some thoughts.

If anyone else tries to use this PHP file from another domain, the browser should throw an error like the one in the following example. I loaded a copy of the weather clock on my makepages.com domain and tried to access the PHP file on my shearspiremedia.com domain. These days, the same-origin policy is in place by default on typical commercial web server installations. You might need to confirm that is the case on the server you are using.

[Error] Origin https://makepages.com is not allowed by Access-Control-Allow-Origin.
[Error] XMLHttpRequest cannot load https://shearspiremedia.com/demos/clock/clock.php?lat=40.14616446413611&lon=-75.20946717104738 due to access control checks.

Next, update the usephp variable in the JavaScript file and test:

var usephp = true; // Set to true to use a PHP document to hide your api key
Step 5: Can I please go full screen?

Once the app is up and working, it can load on any smartphone browser. The iPhone, however, is forced to endure the presence of the location bar and footer. Yuck!

It would be lovely to be able to view it full screen instead. To do that, we need a manifest file to tell the device that we wish to view the clock as a standalone app and to tell Android devices where the app icons are located. Here is my manifest file which I saved as manifest.json in the same directory as the HTML file. You can create your own manifest file using the Web App Manifest Generator. Be sure to adjust the icon file names in your own manifest file and in the link tags in the HTML as we see here:

We also need a set of square PNG images at 192px, 152px, 128px, 96px, and 48px for the home screen icon. Save these into an icons folder within the same folder as your HTML file. Use the file names found in the manifest. The Web App Manifest Generator will create icons in all the required sizes other than 48px by uploading a single 512 x 512 pixel image. Here is the simple icon I made:

Home screen icon for my Weather Clock.

Finally, we need to add a bunch of meta and link tags in the head of the HTML file to make this all work. Here is the completed index.html file code including all the head tags you can’t see on CodePen.

If the user’s exact location isn’t a requirement, we could avoid the Geolocation API entirely and get the approximate location using any of several IP address services. In the demo above, a JSON object is received from extreme-ip-lookup.com to get the approximate GPS location of the user. That displays the city and region values found in the JSON instead of the GPS coordinates. It should be clear to the user in this case that the weather location is a neighboring town.

Since IP information is part of the normal request for a web page, an argument could be made that there is no ethical problem with displaying IP location information without user permission. That eliminates the need for the GPS button altogether. I actually switched the final app to use the IP address location feature with geolocation only as a fallback in the event the IP location service is down. I also added more weather information, custom background images, and custom weather icons to correspond to current weather conditions. The custom icons are available at this Pen. The final app is available here on my ShearSpireMedia.com site. I also created a Pen that generates a starry sky for night mode that can be used to make a night background.

That’s a wrap!

We covered a lot of ground in this article, but hopefully it gives you an idea that:

We can teach an old device new tricks.

The Geolocation API isn’t all that scary.

Fetching and using data from an external API is useful.

Using a small PHP file is one easy way to hide your API key from prying eyes.

Working with user permissions is a good (and often required) practice.

Like I mentioned earlier, a serverless solution for storing data would be ideal here, so if you have any thoughts on that — or really any ideas or questions at all — please let me know in the comments!

In a sense, it's just an app for keeping documents in one place: little notes, to-do lists, basic spreadsheets, etc. I like the native macOS Notes app just fine. It's quick and easy, it's desktop and mobile, it syncs... but there are enough limitations that I wanted something better. Plus, I wanted something team-based and web-friendly (shared URLs!) and Notion hits those nails on the head.

Here's a bunch of ways to use Notion as well as some scattered random notes and ideas about it.

Workspaces are your teams

The word "workspace" almost makes it seem like you could or should use them as your top-level organization structure within a team. Like different projects would use different workspaces. But I'd say: don't do that. Workspaces are teams, even if it's a party of you and only you.

Pricing is billed by workspace. Team members are organized by workspace. Search is scoped by workspace. Switching workspaces isn't too difficult, but it's not lightning fast, either. I'd say it's worth honoring those walls and keeping workspaces to a minimum. It's almost like Slack. It's easy to get Too-Many-Slack'd, so best to avoid getting Too-Many-Notion'd.

Meeting notes

We have a weekly all-hands meeting at CodePen where we lay out what we've done and what we're going to do. It's nice to have that as a document so it can include links, notes, comments, embeds, etc.

Those notes don't disappear next week — we archive them as a historical record of everything we do.

Publishing and advertising schedules

I like looking at a spreadsheet-like document to see upcoming CSS-
Tricks articles with their statuses:

But that same exact document can be viewed as a calendar as well, if that's easier to look at:

There can be all sorts of views for the same table of content, which is a terrific feature:

Knowledge bases

This might be the easiest selling point for Notion. I'm sure a lot of companies have a whole bunch of documents that get into how the company works, including employee databases, coding guidelines, deployment procedures, dashboards to other software, etc. Sometimes that works as a wiki, but I've never seen a lovely setup for this kind of thing. Notion works fantastically as a collaborative knowledge base.

Public documents

I can make a document public, and even open it to public comments with the flip of a switch.

Another super fun thing in Notion is applying a header image and emoji, which gives each document a lot of personality. It's done in a way that can't really be screwed up and made into a gross-looking document. Here's an example where I've customized the page header — and look at the public URL!

I've also used that for job postings:

It's also great for things like public accessibility audits where people can be sent to a public page to see what kinds of things are being worked on with the ability to comment on items.

Quick, collaborative documents

Any document can be shared. I can create a quick document and share it with a particular person easily. That could be a private document shared only with team members, or with someone totally outside the team. Or I can make the document publicly visible to all.

I use Notion to create pages to present possibilities with potential sponsorship partners, then morph that into a document to keep track of everything we're doing together.

We use a show calendar for ShopTalk episodes. Each show added to the calendar automatically creates a page that we use as collaborative show notes with our guests.

It's worth noting that a Notion account is required to edit a document. So anyone that's invited will have to register and be logged in. Plus, you'll need to share it with the correct email address so that person can associate their account with that address.

Blog post drafts

I've been trying to keep all my blog post drafts in Notion. That way, they can easily be shared individually and I can browse all the collected ideas at once.

Exporting posts come out as Markdown too, and that's great for blog post drafts because it translates nicely:

Private sub-team documents

Being able to share documents with specific people is great. For example, we have a Founder's Meetings section on CodePen:

There are probably a million more things

Notion is simply documents with interesting blocks. Simple, but brilliant. For me, it can replace and consolidate things like Trello and other kanban tools, lightweight project management platforms, GitHub issues (if you don't need the public-ness), wikis, Google Docs and Spreadsheets, and even simple one-off public websites.

I'd love to see Notion's web app links open in their desktop app instead of the web. I prefer the app because I have plenty of browser tabs open as it is. It's gotta be possible to do this with a browser extension. There used to be "Paws" for Trello and there was a browser extension that would open Trello links in that app, so there is prior precedent out there.

I'd also like Notion to explore a tabs feature. I constantly need more than one document open at a time and right now the only way to do that is with multiple windows. There is some back-and-forth arrow action that's possible, but it would be interesting to see native tabs or native window splitting somehow.

I'd love to see some performance upgrades too. It's not terribly bad, but working in Notion is so ubiquitous and important in my day-to-day that I'd love for it to feel more instantaneous than it currently does.

In this article, I want to show off the flexibility and real power of amCharts 4. We’re going to learn how to combine multiple charts that run together with animations that form a movie experience. Even if you’re only interested in creating a different kind of animation that has nothing to do with charts, you can still use this library, since it’s more than making charts. The core of amCharts is made to help with everything SVG: creation, layout, animation — basically a library that makes working with SVG fun!

Here’s the kind of thing I’m talking about. It's actually a demonstration of seven different charts animated together. We’ll walk through this together, covering how it works and how to re-create it so you can have amCharts in your tool belt the next time you’re working with charts or complex animations.

The first thing we’re hit is a pie chart rising from the depths with a curved line wrapped around it. There’s nothing special about the pie chart at this point, but we’ll cover it in the next section.

But what about that curved line? Remember, we make charts, so this line is simply a XY chart with a single line on it. All the other details — grid, labels, tick marks, etc. — are hidden. So, what we’re really looking at is a stripped-down line chart!

Setting up the line and pie chart animations

amCharts calls the lines on this chart a line series. A line series has a variable called tensionX and, in this case, it’s been set to 0.75, making for a curvy line. We have to think of tension like we would a rope that is being held by two people and both ends: the tighter the rope is pulled, the greater the tension; conversely, the tension gets looser as the two ends let up. That 0.75 value is a taking a quarter of a unit away from the initial value (1), creating less tension.

Initially, all the values of the series are the same: a flat line. Then, we set valueY value of the line’s animation to 80, meaning it pops up to the eights row of the chart height — that will make plenty of room for the pie when it comes in.

// Defines the animation that reveals the curved line
lineSeries.events.on("shown", function(){
setTimeout(showCurve, 2000)
});
// Sets the animation properties and the valueY so the line bounces up to
// 80 on the chart's y-axis
function showCurve() {
lineSeries.interpolationEasing = am4core.ease.elasticOut;
lineSeries.dataItems.getIndex(3).setValue("valueY", 80, 2000);
setTimeout(hideCurve, 2000);
}
// This is the initial state where the line starts at 30 on the y-axis
// before it pops up to 80
function hideCurve() {
lineSeries.interpolationEasing = am4core.ease.elasticOut;
lineSeries.dataItems.getIndex(3).setValue("valueY", 30, 2000);
setTimeout(showCurve, 2000);
}

Here is the line chart alone so we have a better visual for how that looks:

Next, the pie chart pops up from the bottom. Like a line series, amCharts includes a pie series and it has a dy property that we can set to hidden with a state of 400.

// Make the circle to show initially, meaning it will animate its properties from hidden state to default state
circle.showOnInit = true;
// Set the circle's easing and the duration of the pop up animation
circle.defaultState.transitionEasing = am4core.ease.elasticOut;
circle.defaultState.transitionDuration = 2500;
// Make it appear from bottom by setting dy of hidden state to 300;
circle.hiddenState.properties.dy = 300;

To illustrate this concept, here is a demo with that simple circle in place of a pie chart:

The idea of states in amCharts is this: you can have any number of custom states on any sprite. Then, instead of creating multiple animations with a slew of various different properties, state is changed from one to another and all the required properties that are set on the target state will animate from current values to the new state values.

Any numeric, percentage or color property of a sprite can be animated. By default, sprites have hidden and default states baked in. The hidden state is applied initially and followed by the revealed state, which is the default. There are other states, of course, like hover, active, disabled, among others, including custom states. Here is another demo showing a slice chart with innerRadius, radius and fill animating on hover:

If you look at the code in the demo, you will see some of properties of the slices are customized via pieSeries.slices.template or pieSeries.labels.template. Most of the customization, of course, can be done using themes (amCharts 4 supports using multiple themes at the same time), but since we only need to change a few properties, we can use a template. We’re using a pie chart type and all of the slices of the pie series will be created using the provided template which passes any of the inherited properties we use from the template onto our pie chart.

What if you want to set a custom color for the chart’s font? We can do this by adding a field in the data, like fontColor. That allows us to set custom colors there and then tell the label template that it should look at the field to inform the color property value.

Any property of a sprite can be customized like this. And even later, after the chart is initialized, we can change any property via the template, or if we need to access some individual object, we can get any value using something like series.slices.getIndex(3) to isolate it.

To summarize: there isn't a single object on the chart that can’t be customized or accessed, changed, even after the chart is built. We’re working with a lot of flexibility!

The pie chart morphs into a country

I’ll be blunt: There is no way to morph a whole pie chart or some other complex object to the shape of a country. In amCharts 4, one polygon can morph into another one. And there are prebuilt methods that simply morph a polygon to a circle or to a rectangle. Here’s what we’ll do:

First, we hide all the slices of a pie chart, except one. This makes effectively transforms the remaining slice into a circle.

Then we animate the innerRadius property to 0, and the slice becomes a true circle.

There’s already a map chart at this moment, but it is hidden out of view. While it hides, we zoom into a selected country and morph it into a circle as well.

Next, we’ll show the country (which is now a circle) and hide the pie chart (which looks like the same circle at this time).

Finally, we morph the country back to its original shape.

Here is a simplified demo where we zoom in to the country, morph it to a circle, then morph it back to its default state:

Inspect that code. Note that all the methods we call, like zoomToMapObject, morphToCircle or morphBack, return an Animation object. An animation object dispatches events like animationstarted, animationprogress or animationended and we can attach listeners to them. This ensures that one animation is triggered only after another one is finished. And, if we change the duration of an animation, we won't need to adjust timers accordingly, because events will handle it. In amCharts 4, Sprites, Components, DataItems, Animations, and other objects have an event dispatcher object which regulate any events. You can add listeners for these events and use them to make your applications super interactive.

The plane flies from one country to another

At one point, an airplane surfaces at London on a map chart and travels all the way to Silicon Valley.

It might look complex and scary, but it’s using a lot of the concepts we’ve already covered and the features come standard with the map chart included in amCharts:

MapImageSeries is created and sprites (circles and labels) are mapped to the actual longitude latitude coordinates of both cities.

MapLineSeries, like the standard line series we saw earlier, creates a line between the cities based on the coordinates that are provided, going from one map image to another. By default, the line is drawn so that it follows the shortest distance between the objects. That happens to be a curved line in this case. We could make it a straight line if we’d like.

// Create the map line series
var lineSeries = mapChart.series.push(new am4maps.MapLineSeries());
var mapLine = lineSeries.mapLines.create();
// Tell the line to connect the two cities (latitudes/longitudes an be used alternatively)
mapLine.imagesToConnect = [city1, city2]
// Draw the line in dashes
mapLine.line.strokeDasharray = "1,1";
mapLine.line.strokeOpacity = 0.2;

An object (plane) is added to the MapLine and it moves between the endpoints of the line by animating the plane’s position property from 0 to 1.

Notice that the plane becomes bigger when it hits the line’s halfway point? This is done with three additional lines of code we can stick at the end.

// Make the plane to be bigger in the middle of the line
planeContainer.adapter.add("scale", function(scale, target) {
return (0.07 - 0.10 * (Math.abs(0.5 - target.position))) / mapChart.zoomLevel;
})

We’re using a method that called an adapter, which is another super-powerful feature of amCharts 4. In this case, the adapter modifies the scale property (0.07 to 0.10), based on planes position (0.5).

The plane becomes big and flies away

When our plane reaches the target city (Silicon Valley in the full movie), we scale and rotate it to become horizontal and big.

At the same moment, we create another chart (the SlicedChart type) and add a PictorialSeries to it. The series share’s the same path as the plane, which creates a mask for the slices. We can use any SVG path here.

The trail of a plane is again made with a line series, similar to the one we had in the beginning. This time, it's a combination of two separate series: one with positive and another with negative values. When a series has the sequencedInterpolation property set to true, the animation happens with some delay for each data value and we get an effect like this:

Can you guess what happens in the final scene? The initial chart (which looks like a regular column chart) is actually what’s called a radar chart with a very small arc between the chart’s startAngle (269.9°) and endAngle (270.1°) properties.

The total arc angle is only 0.2° degrees and that's why the radius of a chart becomes very big and tough tell it apart from a regular XY chart. And all we do to bend the chart is animate the start and end angles. I told you… we can literally animate everything, including angles!

Oh, but there is one last, important thing I would like to mention: Containers. All charts and other non-chart objects in this movie are contained in a single div element. Initially, we created mainConatainer and arranged all the objects inside it. Containers support horizontal, vertical, grid and absolute layouts. Fixed or absolute positions can be used for a container's children, and containers can be nested inside other containers. They can be aligned horizontally or vertically, set to a fixed or absolute width or height… and so on. And did I mentioned, that amCharts has built-in date, number and text formatters? Seriously, I will stop here.

As a part of the amCharts team, I often hear comments like, "but you can do all of this with d3.” Yes, you are probably right. But there are still real benefits we’re working with here — fewer lines of code, time spent writing it, and a relatively simple startup. All the animation is made up of 1,000 lines of code, so this is also a pretty lightweight resource.

But, I’m really interested in what you think. Have you tried amCharts before and have examples to show off? How about questions about getting started? Or maybe you’re not sold on the concept altogether and want to chat the pros and cons? Let’s hear it in the comments!

The difference between a CSS good experience and a long frustrating one is oftentimes a matter of a few small details. CSS is indeed nuanced. One of the most common areas where I see struggles is layout. Personally, I like to study patterns. I notice that I tend to use a small group of patterns to solve the majority of my layout problems. This article is about those CSS patterns I use to get myself through layout challenges. It is also about approaching situations agnostically, regardless of the CSS methodologies used, whether that’s SMACSS, BEM, or even the hot topic of CSS-in-JS because they all focus on the properties themselves rather than architecture, organization, or strategy.

Just for fun, let’s start with a test

We’ll use a platform that I happen to have made called Questionable.io and I’ve used it to create a test that we’ll get to below. Don’t worry, there is no personal data collected, results are anonymous and it’s totally free.

The purpose of the test is to see if you can recognize specific CSS behaviors and problems in context without first being presented with the material. I didn’t set out to make the test difficult, but CSS layout nuances tend to be somewhat complex, especially without having a lot of exposure to them. Remember, this all for fun. The results are not an indication of your awesomeness, but hopefully you get value out of it.

Interested in the test but don’t want to take it? Here’s a link to the questions with their correct answers.

Done already? Great! Let’s go over the questions one-by-one to get a better understanding of the layout patterns that are covered in the test.

Question 1: Box Model

Learning the Box Model should be high priority on anyone’s list. While this CSS-Tricks Box Model Article may be a bit old, don’t underestimate its value and relevance to modern CSS. The Box Model is prerequisite knowledge for almost every CSS topic related to layout.

This particular question is testing how to get the Box Model’s computed width. The box clearly has width: 100px; but it turns out that the default rules of the Box Model apply width properties to the content layer of the box. The computed width (how wide is rendered on the page) is the sum of the content layer, padding layer, and border layer. For this reason, the answer is 112px:

If you’ve encountered a situation where the last column or tab in a UI wraps down to the next line and you were confident that five tabs (all set to width: 20%;) adds up to 100%, then it’s very possible that this was the issue. Five tabs at 20% width does add up to 100%, but if there’s padding and/or borders involved, those will add width there won’t be room for the last tab to fit on the same line.

Around the time of CSS3 being introduced, a new tool called box-sizing came to CSS. This allows us to change what layer of the Box Model we want width to apply. For example, we can do box-sizing: border-box; which means we want any width rules to apply to the outside of the border layer instead of the content layer. In this test question, if box-sizing: border-box; had been applied, the computed width would have been 100px.

This is old news for some of you but a good reminder for pros and novices alike.

The second test question could almost be considered "Part Two" of the first question. Remember, it’s one thing to read, "The Box Model has layers and they all contribute to the calculated width and hight." It’s another to be able to recognize a Box Model problem in a real situation. This particular problem is somewhat of a classic among those who have been doing CSS for a while. It stems from the fact that borders take up space and will push things around since they are a part of the Box Model. Introducing borders during a state-transition, like :hover, will mean that boxes get bigger and thus push subsequent boxes down. It can also create a jittery experience:

Out of all the possible solutions in the test question, doing border: 2px solid transparent on the initial "un-hovered" state would be the only one that fixes the problem. box-sizing doesn’t fix this problem because we are not explicitly setting a height. If we had, then the border would be factored on the inside of the height and there would be no shift — but this wasn’t the case.

There are also other solutions that weren’t mentioned as possible answers. One is faux borders with box-shadow and the other is to use outline instead of border. Either of those would have resulting in no shifting during state changes because they are not layers in the Box Model. Here’s another CSS-Tricks article to read more about these solutions

Keep in mind that outline does not support border-radius.

Question 3: Absolute position vs. fixed position

Aside from knowing when to use each and how they differ in visual behavior, it’s also very important to know the rules for how each positioning method attaches to a parent element with its top, right, bottom, or left properties.

First, let’s review Containing Block. The short definition is that a Containing Block is most often the parent of any given element. However, the rules for Containing Block are different between absolute and fixed elements:

1. For absolute elements: The Containing Block is the nearest ancestor parent that is not static. For example, when an element is absolute-positioned, and contains top, right, bottom, or left properties, it will position relative to any parent that has a position of absolute, relative, fixed, or sticky.
2. For fixed elements: The Containing Block is the viewport, regardless of any parents that have position values other than static. Also, the scrolling behavior is different than absolute in that position: fixed; elements stay "fixed" to the viewport as it scrolls, hence the name.

Many developers believe absolute-positioned elements only seek the nearest position: relative; parent. This is a common misconception simply because position: relative is most often paired with position: absolute; to make a Containing Block. The reason it’s commonly used is because relative keeps the parent in flow which is often the desirable behavior. There are times though that the Containing Block of an absolute positioned element is also absolute positioned. This is totally okay depending on the situation. If all parents are static, then the absolute positioned element will attach to the viewport — but in a way that scrolls with the viewport:

There is a lesser-known caveat to the two rules above: Anytime a parent has a transform property (among a few others) with a value other than none, then that parent will become the Containing Block for absolute- and fixed-positioned elements. This can be observed in this Pen where the notice is position: fixed; and the parent has transform but only when hovered:

This is one of those CSS details that can really bite you if you don’t know how it works. There is a CSS concept called Collapsing Margins and many people are familiar with the form of it called Adjacent Siblings Collapsing Margins. However, there is another form of it called Parent and First/Last Child Collapsing Margins which is lesser known. Here is a demo of both:

Each paragraph tag has a top and bottom margin of 1em that are provided by the browser. So far, that’s the easy part. But why is the gap between the paragraphs not 2em (the sum of the top and bottom)? This is called Adjacent Sibling Collapsing Margins. The margins overlap such that the larger of the two margins will be the total gap size, thus the gap in this case is 1em.

There’s something else happening that’s a little strange though. Did you notice that the top margin of the first paragraph doesn’t create a gap between it and the blue container div? Instead of a gap, it’s almost like it "contributes" the margin to the parent div as if the div had the top margin. This is called Parent and First/Last Child Collapsing Margins. This form of Collapsing Margins will not happen in some circumstances if the parent has any of these:

When I have the pleasure of explaining this small CSS detail to people and solving it with padding or border, the response is almost always, "what about padding or border of 0?" Well, that doesn’t work because the value must be a positive integer.

In the previous example, just 1px of padding allows us to toggle between using and preventing Parent/Child Collapsing Margins. The gap that shows up between the first/last paragraphs and the parent is the 1px of padding but now the margin is being factored to the inside of the container since the padding layer creates a barrier preventing collapsing margins.

Regarding the question, I’m confident you can see what the problem is in this UI:

The first .comment (without the .moderator class) is experiencing Collapsing Margins. Even without looking at the code, we can see that the moderator comment has a border and the non-moderator one does not. In the question, there were actually three answers that were considered correct. Each one is actually already applied in the source of the Pen, they're just commented out.

One reason why this form of Collapsing Margins isn’t as widely known as the others is the wide array of ways we can "accidentally" avoid it. Flexbox and grid items create a Block Formatting Context, so we don’t see this form of Collapsing Margins there. If our "comments" UI were a real project, chances are we would have had padding on all four coordinates to create spacing all the way around, which would fix any Collapsing Margins for us. As rare as it might be, I wouldn’t want you to spend a whole day scratching you head on this one, so it’s good to keep in your thoughts when working with layout.

When it comes to using percentage units, the percent is said to be based on the Containing Block’s width or height (usually related to the parent). As we stated earlier, an element with transform will become a Containing Block, so when an element is using transform, the percentage units (for transform only) are based on its own size rather than the parent.

In this example, we can see that 50% means two different things depending on context. The first red block has margin-left: 50%; and the second red block is using transform: translateX(50%);:

The hangover stems from the fact that we are using width: 100%; on the footer and also adding padding. The container is 500px wide which means the footer's content layer (being 100%) is 500px wide before padding is applied to the outside of that layer.

The hangover can be fixed with one of these two common techniques:

Use box-sizing on the footer directly or via a reset, like we discussed earlier.

Remove the width and do left: 0; right: 0; instead. This is a great use case for doing a left value and a right value at the same time. Doing so will avoid Box Model issues because the width will use its default value auto to take up any available space between paddings and borders when left: 0; right: 0; are set.

One of the options was "Remove the padding on the footer." This would technically work to fix the hangover because the content layer being 100% would have no padding or border to expand it beyond the width of the container. But I think this solution is the wrong approach because we shouldn’t have to change our UI to accommodate Box Model issues that are easily avoided.

The reality for me is that I always have box-sizing: border-box; as apart of my reset. If you also do this, then perhaps you don’t see this problem often. But I still like to do the left: 0; right: 0; trick anyways because, over time, it has been more stable (at least in my experience) than having to deal with Box Model issues arising from width: 100%; on positioned elements.

Question 7: Centering absolute and fixed elements

Now we’re really starting to combine all the material from above with the centering of absolute and fixed elements:

Since we’ve already covered most of the material in this test question, I’ll simply point out that horizontal and vertical centering can be done "the old school way" with negative margins or the newer "kinda old school but still good" way of doing transforms. Here is an amazing CSS-Tricks guide on all things centering.

It used to be said that if we know the width and height of the box, then we should use negative margins because they’re more stable than transitions, which were new to browsers. Now that transitions are stable, I use them almost all the time for this, unless I need to avoid a Containing Block.

Also know that we can’t use any margin: auto; tricks for this because we need modals to "hover" over the content which is why position is typically used to them out of Normal Flow.

Speaking of which, let’s move on to the next question, which deals with centering with Normal Flow.

Question 8: Centering elements with Normal Flow

Flexbox brought us many amazing tools for solving difficult layout problems. Before it’s release, it was said that vertical centering was one of the most difficult things to do in CSS. Now it’s somewhat trivial:

Notice that with flexbox items, the margin: auto is being applied to top, right, bottom, and left to center vertically and horizontally. Doing vertical centering with auto didn't work in the past with block-level elements which is why doing margin: 0 auto is common.

Question 9: Calculate mixed units

Using calc() is perfect when two units that we can’t add up on our own need to be mixed or when we need to make fractions easier to read. This test question asks us to figure out what calc(100% + 1em) would be based on the fact that the width of the div is 100px. This was a little tricky because it actually doesn't matter that the div is 100px wide. The percent is based on the parent's width so the answer is Whatever 100% of the containing block's (parent's) width is plus 1em.

There are a few key places where I see myself regularly reaching for calc(). One is anytime I want to offset something by 100% but also add a fixed amount of extra space. Dropdown menus can be a good example of this:

The trick here is that we want to make a "dropdown system" where the dropdown menu can be used with different trigger sizes (in this case, two different size buttons). We don’t know what the height of the trigger will be but we do know that top: 100%; will placed at the top of our menu and at the very bottom of the trigger. If every menu needs to be at the bottom of their respective trigger, plus .5em, then that can happen with top: calc(100% + 0.5em);. Sure, we could use top: 110%; as well, but that extra 10% would be context-dependent based on the height of the trigger and the container.

Question 10: Negative margins

Unlike positive margins that push away from their siblings, negative margins pull them closer together without moving the sibling elements. This final test question offers two solutions that technically work to eliminate the double border in our button group, but I strongly prefer the negative margins technique because removing borders would make it much more challenging to do certain tricks like this hover effect:

The effect is a "common border" that is shown between the buttons. Buttons can't actually share a common border so we need this negative margin trick to make the two borders overlap. Then I'm using a z-index to manage which border I want to be on top depending on the hover state. Note that z-index is useful here even without absolute positioning, but I did have to do position: relative. If I had used the technique to remove the left border of the second button, this effect would have been more difficult to pull off.

It all adds up!

There is one last demo I want to show you that utilizes many tricks we’ve discussed so far. The task is to create UI tiles that expand all the way to the left and right edges of the container with gutters. By tiles, I mean the ability to have a list of blocks that wraps down to the next line when there’s no more space. Hover over the tiles to see the full effect:

The hurdle with this task is the gutters. Without gutters, it would be trivial to get the tiles to touch the left and right edge of the container. The problem is that the gutters will be created by margin, and when we add margin to all sides of the tile, we create two problems:

Having three tiles with width: 33.33%; in combination with margin will mean three tiles cannot fit on one row. While box-sizing will allow us to have padding and borders on the .tile which will be contained within 33.33%, it will not help us with margins — that means the computed width of the three tiles will be more than 100%, forcing the last one down to the next line.

Tiles on the far left and right side will no longer touch the edges of the container.

The first problem can be solved with calc((100% / 3) - 1em). That’s 33.33% minus the left and right margins of each tile. Adjacent Sibling Collapsing Margins don’t apply here because there is no such thing as Collapsing Margins when it comes to left and right margin. As a result, the horizontal distance between each tile is the sum of the two margins (1em). It also doesn’t apply in this case with the top and bottom margins because the first tile and the fourth tile are technically not Adjacent Siblings, even though they happen to be right next to each other visually.

With the calc() trick, three tiles are able to fit on a row, but they still don’t extend to edges of the container. For that, we can use negative margins in the amount equal to the left and right margin of each tile. The green dotted line in the example is the container where we will apply negative margins to draw out the tiles to match the edge of the surrounding content. We can see that how it extends into its parent’s padding area (the main element). That’s okay because negative margins don’t push neighboring elements around.

The end result is that the tiles have nice gutters that extend edge-to-edge so that they align to the neighboring paragraph tags outside the tiles.

There’s a lot of ways to solve tiles (and they usually come with their own pros and cons). For example, there's a rather elegant solution using CSS Grid discussed by Heydon Pickering which is responsive using a technique that mimics container queries (but with Grid magic). Ultimately, his Grid solution to tiles is much nicer than the flexbox solution I presented, but it also has less browser support. Nonetheless, the flexbox solution is still a great way to demo all the tricks from this article at the same time.

You may already be familiar with Heydon's work. He's known for creating clever tricks like the Lobotomized Owl selector. If you're not familiar, it's certainly worth knowing and I have a video where I talk about it.

Summary

I stated at the start that I tend to look for patterns when solving problems. This article isn’t necessarily about the exact demo scenarios from above; it’s more about a set of tools that can be used to solve these and many other layout problems that we are all likely to come across. I hope these tools take you far and I look forward to hearing your contributions in the comments.

By the way, there are a number of excellent resources that cover the Box Model in thorough detail, most notably ones by Rachel Andrews and Jen Simmons that are certainly worth checking out. Rachel even has a newsletter completely dedicated to layout.

Box Alignment Cheatsheet - Great resource with visuals that highlight the various properties that affect how elements are aligned, either by themselves or relative to other elements.

Jen Simmons Labs - A slew of helpful posts, demos and experiments using modern layout methods.

I'm not so protective of CSS that I'm above hearing it criticized, but I'm certainly in agreement here. CSS does not suck. I love how the post is framed to hype up current CSS features the way features of other languages and tools are hyped:

Imagine if a tech dude walked on stage at a conference and said the following:

“This declarative language will gracefully continue on failure, allow you to write global and scoped code, and it will work across your entire front-end stack, whether it’s rendered by a framework, a CMS or a static HTML file”

... Now, if I make a slight amendment to that, the reception would probably be the exact opposite.

“CSS will gracefully continue on failure, allow you to write global and scoped code, and it will work across your entire front-end stack, whether it’s rendered by a framework, a CMS or a static HTML file”

A rather full-throated argument (or rather, response to arguments against) utility (atomic) CSS from Sarah Dayan. I wondered recently if redesigns were potentially a weakness of these types of systems (an awful lot of tearing down classes) which Sarah acknowledges and recommends more abstraction to help.

I also wonder about workflow. I sort of demand working in an environment which offers style injection, so working with CSS feels smooth. I also worry that having to change HTML every time I want to modify a design requires refreshing. I guess if you are in a hot module reloading situation, then it's fine.

Also this seems like it can just go too far. At some point, altering a space-separated string to do everything you wanna do has ergonomic limitations.

But! What if we want to render that child outside of the div somewhere else? That could be tricky because it breaks the convention that a component needs to render as a new element and follow a parent-child hierarchy. The parent wants to go where its child goes.

That’s where React Portals come in. They provide a way to render elements outside the DOM hierarchy so that elements are a little more portable. It may not be a perfect analogy, but Portals are sort of like the pipes in Mario Bros. that transport you from the normal flow of the game and into a different region.

The cool thing about Portals? Even though they trigger their own events that are independent of the child’s parent element, the parent is still listening to those events, which can be useful for passing events across an app.

We’re going to create a Portal together in this post then make it into a re-usable component. Let’s go!

Toggling an element’s visibility is nothing new. But, if you look at the code carefully, you’ll notice that the outputted element is controlled by the button even though it is not a direct descendent of it. In fact, if you compare the source code to the rendered output in DevTools, you’ll see the relationship:

So the outputted element’s parent actually listens for the button click event and allows the child to be inserted even though it and the button are separate siblings in the DOM. Let’s break down the steps for creating this toggled Portal element to see how it all works.

Step 1: Create the Portal element

The first line of a React application will tell you that an App element is rendered on the document root using ReactDOM. Like this;

ReactDOM.render(<App />, document.getElementById("root"));

We need to place the App element in an HTML file to execute it:

<div id="App"></div>

Same sort of thing with Portals. First thing to creating a Portal is to create a new div element in the HTML file.

<div id="portal"></div>

This div will serve as our target. We’re using #portal as the ID, but it doesn’t have to be that. Any component that gets rendered inside this target div will maintain React’s context. We need to store the div as the value of a variable so we can make use of the Portal component that we’ll create:

We create a new div element in the constructor and set it as a value to this.el. When the Portal component mounts, this.el is appended as a child to that div in the HTML file where we added it. That’s the <div id="portal"></div> line in our case.

If you’re new to React and are confused by the concept of mounting and unmounting an element, Jake Trent has a good explanation. TL;DR: Mounting is the moment the element is inserted into the DOM.

When the component unmounts we want to remove the child to avoid any memory leakage. We will import this Portal component into another component where it gets used, which is the the div that contains the header and button in our example. In doing so, we’ll pass the children elements of the Portal component along with it. This is why we have this.props.children.

Step 3: Using the Portal

To render the Portal component’s children, we make use of ReactDOM.createPortal(). This is a special ReactDOM method that accepts the children and the element we created. To see how the Portal works, let’s make use of it in our App component.

But, before we do that, let’s cover the basics of how we want the App to function. When the App loads, we want to display a text and a button — we can then toggle the button to either show or hide the Portal component.

Since we want to toggle the Portal on and off, we need to make use of component state to manage the toggling. That’s basically a method to set a state of on to either true or false on the click event. The portal gets rendered when on is true; else we render nothing.

This is how the DOM looks like when the on state is set to true.

When on is false, the Portal component is not being rendered in the root, so the DOM looks like this.

More use cases

Modals are a perfect candidate for Portals. In fact, the React docs use it as the primary example for how Portals work:

He has another slick example that demonstrates inserting elements and managing state:

Summary

That’s a wrap! Hopefully this gives you a solid base understanding of Portals as far as what they are, what they do, and how to use them in a React application. The concept may seem trivial, but having the ability to move elements outside of the DOM hierarchy is a handy way to make components a little more extensible and re-usable… all of which points to the core benefits of using React in the first place.

We rolled out a new site design on January 1! This is the 17th version of CSS-Tricks if you can believe that. The versions tend to evolve a decent amount beyond the initial launch, but we archive screenshots on this design history page. Like I said in our 2018 thank you post:

This is easily the most time, effort, and money that's gone into a redesign since the big v10 design. There are a lot of aesthetic changes, but there was also quite a bit of UX work, business goal orientation, workflow tweaking, and backend development work that went along with it.

This is a big one! The reception so far has been pretty great, but please know that we'll be refining it and squishing a lot of bugs here in the early days.

Here are some notes about who was involved, how it happened, and things to notice.

Kylie led this project

Kylie Timpani was the lead designer and really whole project lead on this.

I first reached out to her in April 2017, we chatted in May, and kicked off the work in June. From my perspective, this was a pretty casual process, as I had no particular deadlines and fairly loose goals. Let's make an attractive site that does better in all ways than we do them now.

Kylie was super organized and had a very thoughtful process around every aspect of this. Just in the first block of time that Kylie allocated for this project she:

Took a complete content inventory

Dug into analytic data to understand users, traffic, and usage at a high level

Created, distributed, and analyzed a reader survey to understand readers better and answer specific questions she had for them

Chatted with all the staff members of CSS-Tricks to understand their roles, workflows, and ideas

Kylie's obviously not the kind of the designer that just whips open a design tool and starts noodling around. As great of a visual designer as she is, the work was highly informed. She went on to speak with our advertising agency, clearly identify the site's current strengths and weaknesses, and do light wireframing.

I've been using Figma for visual design stuff, and Kylie was happy to use that as the design tool. That was nice since we both have Team level access and were able to use it collaboratively. For me, it was mostly useful for being able to see and reference everything, and make notes on the designs.

We also used Asana to track what was being worked on and ultimately as a place to track bugs and places where the design implementation needed attention.

Thanks so much, Kylie for all your excellent work on this project! If anything is a bit off or buggy about the site, it's my poor implementation. And good luck! ⤵️

⚡️ So! Speaking of big life changing news... in just a couple of weeks I'll be moving to San Francisco! I'm so SO excited to be joining @nrrrdcore and her truly incredible team over at Apple! &#x1f469;&#x1f3fb;‍&#x1f4bb;✨

I'll let y'all explore the design for yourself to find all the little touches we put in, but I'll give a shout out to a few of them where there is a technical detail you might enjoy.

Orange-to-pink

Clearly, we went all dark mode on this design. It's nothing to do with the new media query, although that reminds me we might consider alternations for those who specifically set prefers-color-scheme: light;.

The brand/accent/action colors are orange and pink, which looks quite striking against the darkness but works on light backgrounds as well.

I made a quickie little Sass @mixin that allowed me to use those colors (with variations, if needed) at different angles as backgrounds:

There is something about headers that always bring out more complexity than you might expect. I recently went through this with the new CodePen header/sidebar and it as complicated for this site. Part of what complicated this one was:

It has its own set of unique breakpoints. The header is pretty full, so the breakpoints are pretty specific and unique to it.

We wanted a fixed-position (but minified) header that showed up as you scroll down.

When you're logged in, there is a WordPress admin bar also fixed to the top of the page. I wanted to accommodate for that.

At one point, it was getting pretty messy and I wound up deleting all the CSS for the entire thing and re-wrote it, taking all the states into consideration, and writing media queries that used logic to clearly specify styles in each of those states.

The idea of a not-always-fixed-position header is interesting in and of itself. It means that:

You need to determine when to apply the fixed position

You need to make sure the shift from not-fixed to fixed (and back) doesn't cause layout shifting

I was dead nervous about attaching an onscroll listener and doing math and such to determine when to do the switch. I'm sure it can be done responsibly, but I haven't had great luck with that. Instead, I placed a tiny one-pixel element to the screen and attached an IntersectionObserver to it and reacted to that. That gave me the power to adjust where that is in CSS, which was a nice little touch.

One very cool feature of this design is the Mixup area on the homepage. It was one of Kylie's ideas to show and remind people of the variety and depth of content that is here on CSS-Tricks.

The line that goes through it needs to depend on the height of the HTML content in each of those boxes. The boxes are set on a CSS grid, but they can and should still expand as needed for titles and such. Rather than try to SVG this somehow, the line is essentially stitched together though border and border-radius on individual boxes. To make it line up, I occasionally had to nudge them around with transform.

There was some z-index involved too. It was fun making mistakes along the way:

Cards

I'm kinda in love with native scroll snapping. The cards kinda have a fun animation on desktop, revealing the entire card on hover/focus, and then on mobile you can see the whole card, but are easy to thumb through:

Thanks, Amelia!

The design called for these curved line separators:

I have a small degree of confidence with the SVG path syntax, so I took the first crack at it. I was able to design it in a way that it could draw that line OK and keep the stroke at the desired width, but it didn't scale quite right.

I brought in SVG expert Amelia Bellamy-Royds to help me get it right. Feel free to inspect the site to see how it was done. It involves masking and nested SVGs and rectangles and transforms and all sorts of fun stuff. Amelia actually created four variations of the code and carefully noted all the pros and cons of each one. Ultimately, we went with this:

Another thing Amelia helped with was the "circle of text" design element. Kylie had these instances mocked out and I thought they were so cool and I definitely wanted to pull it off. There is a really elaborate way to do it by splitting the characters in to spans and transforming them, but that's a bit messy compared to SVG's <textPath>. I knew I wanted to go the SVG route, but perhaps abstract it away into a reusable component so that it wasn't a heaping pile of code every time I want to use one.

It occurred to me that a web component might be the best way to go here because I can kind of invent the API myself. What I wanted a circle-of-text component to do:

Pass in the text to set on the circle

Declare the radius of the circle

Rotate the circle so I can start the text at any point along the circle

My expertise with web components is limited, so I reached out to Amelia again who is great both with web components and SVG—a perfect match! This is what she was able to do, which I easily integrated easily into this design.

Thanks, Ana!

Another design thing that Kylie cooked up that I was a bit perplexed by was this line:

I thought maybe SVG again, but I really wanted to nestle regular HTML content in there nicely. I was hoping to pull it over with borders or something CSS-y. I reached out to Ana Tudor who is fantastic at tricky design situations and solving them with native browser tech. Ana was able to whip up a good solution here using multiple gradient backgrounds in the main area and a border for the top right bit that flies off.

Fonts are a unique part of the loading experience of websites in that their presence (or lack of), how they appear, and how they change all play major roles in the perceived performance of the page.

I've had the good fortune of being able to chat with Zach Leatherman about font loading before, but I still don't feel entirely comfortable with what the best practices are in any given situation. For this design of CSS-Tricks, I made the call to use the system font stack for most of the body copy. That has the major benefit of being instantly available to render and aesthetically seems to work well on a technical site, not to mention generally pairing well with Rubik, our header font.

But we still needed to deal with Rubik. There will be an upcoming article from Zach going into this in more details, but the gist is:

Create a minimal subsetted version of Rubik that handles the majority of usage

The Forums is such a complicated area of the site to design and maintain, what I've done is just loaded the default bbPress styling for them, instead of trying to override things or start from scratch. I think that'll be the best route going forward.

There is a Gallery section of this site, but I'm not even linking to it anymore as we didn't really keep it up to date very well nor did it get used much. The URL's still work though. Maybe it can make a return someday, but for now, I'm enjoying the reduction of some technical and content debt.

Tech stack

It's somewhat boring. It's about the same thing I've done forever. It's a stock install of WordPress with a custom theme, a dozen or so plugins, and a bit of custom-coded functionality, like having the images powered by Cloudinary. It's running on a custom Media Temple-built box so it can have PHP 7 and MySQL 5.6, plus a firewall that also acts as a CDN. It's nice to have a pretty snappy foundation, so it's on me as a front-end dev to keep it that way.

I used SVG for the icons, Sass for the styling, and Babel to write jQuery-based functionality in ES6. I wrote up a Gulp file to do all that processing and run the local BrowserSync dev server. Local WordPress via Local by Flywheel.

I'm actually pretty happy with the stack as it felt quick and productive to me. But I admit, part of me wishes I dug a little harder into new tech, like building webpack-based processing or trying to go all-in on a server-rendered and React-powered headless WordPress via GraphQL kinda thing. The reason I didn't is because boring has served me so well and time is a major factor since I'm developing alone (my budget doesn't exactly make available a whole development team). My guess is a major front-end infastructure overhaul would have tripled the dev time for questionable benefits. It still sounds like fun and might open up future doors, but hey, another time.

My last regret is that I wish I had spun up a real pattern library system from the start. I think I did OK in breaking things up into reusable parts, but the site isn't truly componentized. As I approached the finish line, I started to see how this could have gone a bit smoother for me should I have worked with true components that accepted data and had variations and such. Native PHP isn't great for that, so it would have forced me into some kind of templating system, and I probably wouldn't have regretted it. If I stay in PHP next time, maybe I'd use something like Timber and Twig for all the components, and then Fractal for the pattern library since it supports Twig. I kind of dig the way Timber abstracts the data stuff from the views.

Project Wallace is a project aimed at gaining insights in your CSS over a longer period of time. It started a couple of years ago as a frustration with existing CSS analyzers that only do a one-time only analysis. As time went by, more and more features were added and now Wallace is place to go for developers who want to know if their complexicity has increased or for a designer who wants to know if all the correct colors and fonts are being used.

Bart Veneman set it up to watch CSS-Tricks, and you can see a before/after comparison and charts over time. Bart blogged about the numbers for us as well. Thanks Bart!

The true usefulness of CodePen Embed Themes came out here. The whole point of an embed theme is that you can use them to match the design of where the Pens will be embedded, and if you need to change that design, you can change them all in one fell swoop. There are probably thousands of embedded Pens on this site, and they all got updated at once with one theme change.

There are a few special things that I've done with CodePen embeds on this site:

The are resizable from the bottom right corner. Used jQuery. Like this.

They have a placeholder height. When you embed a Pen, you can choose how tall you want it to be. That's how tall the <iframe> will come in as. But I've adjust it so that the <p> that is there before the iframe comes in will be that same height, so there is no reflow jank.

We're gonna bring that feature to CodePen itself real soon. Notice in that RegEx above I'm also forcing the theme id. That way, all embedded Pens definitely have the correct theme, even if we forget.

Achievement unlocked: The custom scrollbar is the new feature that everyone either loves or hates

If there has been one constant in every CSS-Tricks design, it's that there's at least one feature people either love or hate. This time, I'm happy to announce it's the custom scrollbar. In a sense, it's for myself. I manually use scrollbars quite a bit and it feels both fun and highly usable to grab onto this big beefy chunk of pink love.

It's also a little inspired by VS Code, which features a pretty beefy scrollbar itself:

There are general usability considerations about custom scrollbars for sure, but I don't feel like they've been breached too heavily here, if at all. I've heard some "don't mess with my browsers UI" feedback, which I sorta get, but does that mean we shouldn't style any form controls, or even use CSS at all? (LOL.) And don't scrollbars come from the system, not the browser?

We have plenty of bug fixing and polishing to do still on this design. If you've emailed or tweeted or communicated with us in some way about it, I've probably seen it and have been log it all to make sure it's all addressed the best we can. Plus stay tuned for some fun new features!

Tim Kadlec on the issues surrounding poor web performance and why it’s so important for us to care about making our sites as fast as possible:

Poor performance can, and does, lead to exclusion. This point is extremely well documented by now, but warrants repeating. Sites that use an excess of resources, whether on the network or on the device, don’t just cause slow experiences, but can leave entire groups of people out.

There is a growing gap between what a high-end device can handle and what a middle to low-end device can handle. When we build sites and applications that include a lot of CPU-bound tasks (hi there JavaScript), at best, those sites and applications become painfully slow on people using those more affordable, more constrained devices. At worst, we ensure that our site will not work for them at all.

Forget about comparing this year’s device to a device a couple of years old. Exclusion can happen on devices that are brand-new as well. The web’s growth is being pushed forward primarily by low-cost, underpowered Android devices that frequently struggle with today’s web.

As Tim mentions at the end of that piece though, it’s easy to forget web performance and it’s sometimes hard to make the case for making a website fast. It’s often seen as a nice-to-have instead of as a core feature in and of itself, like semantic markup and accessibility compliance.

I’m optimistic that the conversation surrounding this topic is improving things though. Having tools like Lighthouse built straight into the browser makes things easier and the abundance of testing tools such as Calibre gives us insights into exactly what and where issues might be. But we also need to remember that this isn’t solely a technical problem — it’s an ethical one, too.

I recently came across an interesting sliced disc design. The disc had a diagonal gradient and was split into horizontal slices, offset a bit from left to right. Naturally, I started to think what would the most efficient way of doing it with CSS be.

Sliced gradient disc.

The first thought was that this should be doable with border-radius, right? Well, no! The thing with border-radius is that it creates an elliptical corner whose ends are tangent to the edges it joins.

My second thought was to use a circle() clipping path. Well, turns out this solution works like a charm, so let's take a close look at it!

Note that the following demos won't work in Edge as Edge doesn't yet support clip-path on HTML elements. It could all be emulated with nested elements with overflow: hidden in order to have cross-browser support, but, for simplicity, we dissect the clip-path method in this article.

Slicing a disc into equal parts

As far as the HTML structure goes, we generate it with a preprocessor to avoid repetition. First off, we decide upon a number of slices n. Then we pass this number to the CSS as a custom property --n. Finally, we generate the slices in a loop, passing the index of each to the CSS as another custom property --i.

To get the disc shape we use a circle() clipping path having the radius $r equal to half the diameter .5*$d and the central point dead in the middle of the assembly. Since we set this clip-path on the slices, the position of the central point for each slice is relative to the slice itself.

Horizontally, it's always in the middle, at 50% of the slice. Vertically, it needs to be in the middle of the assembly, so that's where the total number of items and the item's index which we've passed as CSS variables from the preprocessor code come into play.

In the middle of the assembly means at half the height of the assembly from the top of the assembly. Half the height of the assembly is half the diameter .5*$d, which is equivalent to the radius $r. But this value is relative to the whole assembly and we need one that's relative to the current slice. In order to get this, we subtract the vertical position of the current slice relative to the assembly, that is, how far the top of the current slice is relative to the top of the assembly.

The first slice (of index --i: 0) is at the very top of the assembly, so the amount we subtract in this case is 0.

The second slice (of index --i: 1) is at one slice height from the top of the assembly (the space occupied by the first slice), so the amount we subtract in this case is 1 slice heights.

The third slice (of index --i: 2) is at two slice heights from the top of the assembly (the space occupied by the first and second slices), so the amount we subtract in this case is 2 slice heights.

In the general case, the amount we subtract for each slice is the slice's index (--i) multiplied by one slice height.

The one little problem in this case is that it also cuts off the top of the first slice and the bottom of the last slice. This may not be an issue in some cases and we can always reset the padding-top on the :first-of-type and the padding-bottom on the :last-of-type to 0:

However, we also have a one-line solution to this problem of creating gaps in between the slices: add a mask on the container!

This mask is a repeating-linear-gradient() which creates transparent stripes of the thickness of the gap $g, repeats itself after a slice height and is limited to the disc diameter $d horizontally and to the disc diameter $d minus a gap $g vertically (so that we don't mask out the very top and the very bottom as we also did initially with the padding approach).

In order to have a continuous gradient background, we need to give this background a height equal to that of the disc and set its vertical position relative to each slice such that it always starts from the top of the assembly... wherever that may be located relative to the slice.

The top of the first slice (of index --i: 0) coincides with that of the assembly, so our background starts from 0 vertically.

The top of the second slice (of index --i: 1) is 1 slice height below that of the assembly, so its background starts from 1 slice height above vertically. Since the positive direction of the y axis is down, this means our background-position along the y axis is calc(-1*var(--h)) in this case.

The top of the third slice (of index --i: 2) is 2 slice heights below that of the assembly, so its background starts from 2 slice heights above vertically. This makes our background-position along the y axis is calc(-2*var(--h)).

We notice a pattern here: in general, the background-position along the y axis for a slice is calc(-1*var(--i)*var(--h)).

In order to fix this issue, we set the offset as a Sass variable $o, set the horizontal background-size to the slice width (100% or $d) plus twice the offset and make sure we attach the background for the slices that move to the left (in the negative direction of the x axis, so by -$o) on the left side of the slice (background-position along the x axis is 0%) and for the slices that move to the right (in the positive direction of the x axis, so by $o) on the right side of the slice (background-position along the x axis is 100%).

It also works for images, though in this case we need to remove the second background-size value so the image doesn't get distorted, which leaves us with the caveat of getting vertical repetition if the image's aspect ratio is greater than calc(#{$d} + #{2*$o}) : #{$d}. This isn't the case for the square image we're using below, but it's still something to keep in mind.

Another thing to note is that above, the top of the image is attached to the top of of the assembly. If we want the middle of the image to be attached to the middle of the assembly, we need to tweak the vertical component of the background-position a bit.

First off, to attach the middle of the image to the middle of a slice, we use a background-position value of 50%. But we don't want the middle of the image in the middle of each slice, we want it in the middle of the assembly for all slices. We already know the distance from the top of each slice to the vertical midpoint of the whole assembly - it's the y coordinate of the clipping circle's central point:

The distance from the vertical midpoint of each slice to that of the assembly is this value --y minus half a slice's height. So it results that the background-position we need along the y axis in order to have the vertical midpoint of the image attached to that of the assembly is calc(50% + var(--y) - .5*var(--h)).

Just like in the case of equal slices, the y coordinate of the central point of the clipping circle() is the disc radius $r minus the distance from the top of the assembly to the top of the current slice. This is the sum of the heights of all previous slices.

In the case of the first slice (--i: 0), we have no previous slice, so this sum is 0.

In the case of the second slice (--i: 1), we only have the first slice before and its height is the unit height (--h).

In the case of the third slice (--i: 2), the sum we want is that between the height of the first slice, which equals the unit height and that of the second slice, which is twice the unit height. That's calc(var(--h) + 2*var(--h)) or calc(var(--h)*(1 + 2)).

In the case of the third slice (--i: 3), the sum is that between the height of the first slice, which equals the unit height, that of the second slice, which is twice the unit height and that of the third slice, which is three times the unit height. That's calc(var(--h) + 2*var(--h) + 3*var(--h)) or calc(var(--h)*(1 + 2 + 3)).

Now we can see a pattern emerging! For every slice of index --i, we have that the added height of its previous slices is the unit height --h times the sum of the first --i natural numbers (and the sum of the first --i natural numbers is calc(var(--i)*(var(--i) + 1)/2)). This means our clip-path value becomes:

Sadly, having incremental slices means the repeating-linear-gradient() mask method of creating gaps cannot work anymore. What still works however just fine is the vertical padding method and we can set the padding values such that the top one is 0 for the first slice and the bottom one is 0 for the last slice.

For a gradient background, the main idea remains the same as in the case of the equal slices. There are just two things we need to take into account.

One, the background-position along the y axis is minus the distance (in absolute value) between the top of the assembly and the top of the current slice. This distance isn't calc(var(--i)*var(--h)) like in the case of equal slices of height --h anymore. Instead it's, as computed a bit earlier, calc(var(--i)*(var(--i) + 1)/2*var(--h)). So the background-position along the y axis is calc(-1*var(--i)*(var(--i) + 1)/2*var(--h)).

And two, we want our background clipped to the content-box so that we keep the gaps, but we need to keep the background-origin to its initial value of padding-box so that our gradient stays continuous.

For an image background whose midpoint is attached to the middle of our assembly, we need to take into account the fact that half a slice height isn't the same value for all slices anymore. Now the height of a slice is calc((var(--i) + 1)*var(--h)), so this is the value we need to subtract in the formula for the y component of the background-position.

We can also slice our disc along the other direction. This means removing the flex-direction: column declaration from the container and letting the flex-direction be the initial one (row), switching the width and the height, the x and y coordinates of the circular clipping path's central point, the direction along which we shift the slices, the dimensions and x and y positions of the masking gradient, which we also need to rotate so that it goes along the x axis.

To create the gaps, we use the padding method. But since we're now in the vertical case, we need horizontal paddings, on the left and on the right and to make sure the padding-left for the first slice is 0 and the padding-right for the last slice is also 0:

Again, we generate it with a bit of Pug, the total number of items being the product between the number of columns and the number of rows. For simplicity, we keep the number of rows and the number of columns equal.

We've also passed the column and row indices (--i and --j respectively) to the CSS.

Since we're in the 2D case, we switch from using a 1D layout (flex) to using a 2D one (grid). We also start with the disc diameter $d and, given the number of columns is equal to that of rows (--n), our disc gets divided into identical tiles of edge length --l: calc(#{$d}/var(--n)).

To create the gaps in between the tiles, we use the padding approach on the .tile elements and combine the horizontal and vertical cases such that we have the padding-top for the first row is 0, the padding-left for the first column is 0, the padding-bottom for the last row is 0 and the padding-right for the last-column is 0.

Note that we've used the row index --j for the top to bottom direction (vertical paddings) and the column index --i from the left to right direction (lateral paddings).

To get the disc shape, we again combine the horizontal and vertical cases, using the column index --i to get the x coordinate of the circular clipping path's central point and the row index --j to get its y coordinate.

For a gradient background, it's again combining the horizontal and the vertical cases and taking into account that here we have no offset at this point, which means the background-size is the disc diameter $d along both axes.

For an image background, we remove the second background-size value so we prevent the image from getting stretched if it's not square. We also adapt the code for attaching the image's midpoint to that of the grid from the 1D case to the 2D case:

For a gradient background, we adapt the equal tiles version to the incremental case. This means tweaking the background-position as we did before for the incremental slices, only this time we do it along both axes, not just along one:

Generating random colors won’t guarantee pleasing palettes, especially if a bunch of random colors are paired together. PleaseJS can help build color schemes that work together. You provide it a base color and other options (like what type of color scheme) and it spits out colors for you.

...they don't exactly tell you how to use them. Steve Schoger makes a point of this, rather hilariously in a blog post. This is a perfectly lovely color palette:

But if you just pick those colors and plop them onto a design, you could end up with something like this:

You might like that, but you'd be in the minority. It's not a refined design that gets out of the way and would be nice to use every day. Color usage is a bit more complicated than plopping five nice colors into a design. It's variations on those and using them in tasteful ways, like this:

Masonry layout, on the web, is when items of an uneven size are laid out such that there aren't uneven gaps. I would guess the term was coined (or at least popularized) for the web by David DeSandro because of his popular Masonry JavaScript library, which has been around since 2010.

JavaScript library. Nothing against JavaScript, but it's understandable we might not want to lean on it for doing layout. Is there anything we can do in CSS directly these days? Sorta.

If it's just the uneven brick-like look you're after, then horizontal masonry is way easier. You could probably even float stuff if you don't care about the ragged edge. If you wanna keep it a block... flexbox with allowed flex-grow is the ticket.

CSS grid is very amazing and useful in a CSS developer’s everyday life, but it's not really designed for masonry style layouts. CSS grid is about defining lines and placing things along those lines, where masonry is about letting elements end where they may, but still exerting some positional influence.

Balázs Sziklai has a nice example of auto-flowing grids that all stack together nicely, with pretty good horiziontal ordering:

Andy Barefoot wrote up a great guide. The trick is setting up repeating grid rows that are fairly short, letting the elements fall into the grid horizontally as they may, then adjusting their heights to match the grid with some fairly light math to calculate how many rows they should span.

What people generally want is column-stacking (varied heights on elements), but with horizontal ordering. That last grid demo above handles it pretty well, but it's not the only way.

Jesse Korzan tackled it with CSS columns. It needs JavaScript as well to get it done. In this case, it shifts the elements in the DOM to order them left-to-right while providing a horizontal stack using a CSS columns layout. This introduces a bit of an accessibility problem since the visual order (left-to-right) and source order (top-to-bottom) are super different & dash; though perhaps fixable with programmatic tabindex?

I’m a huge fan of CSS Grid and I use it on pretty much every project these days. However, there’s one part of it that makes things much more complicated than they really ought to be: the lack of subgrids. And in this post on the matter, Ken Bellows explains why they’d be so gosh darn useful:

But one thing still missing from the Level 1 spec is the ability to create a subgrid, a grid-item with its own grid that aligns in one or both dimensions with the parent grid. It was originally planned to be in Level 1, but the working group decided they needed more time to work out the details, so it was removed, and it will ship in CSS Grid Layout Module Level 2, which seems to be nearing completion.

There has been a lot of discussion over the last 2 years about the use cases for subgrid, how it should be implemented, and even some debate over whether you even need it. A lot of that discussion was centered around two other approaches that can handle many of the same problems as subgrid: nested grids and display: contents

I remember one of the very first websites I worked on was much like the demo that Ken uses as an example, but this was way back in 2012 and grid didn’t exist yet. Sadly, I had to write a lot more CSS than I felt was necessary to get elements in one div to line up with elements in another. Anyway, this article kinda riffs off of Rachel Andrew’s post about subgrid and what problems it would help solve which is definitely worth checking out, too.

A challenge I faced in building an image "emojifier" was that I needed to change the color spaces of values obtained using getImageData() from RGB to HSL. I used arrays of emojis arranged by brightness and saturation, and they were HSL-based for the best matches of average pixel colors with the emojis.

In this article, we’ll study functions that will be useful for converting both opaque and alpha-enabled color values. Modern browsers currently support the color spaces RGB(A), hex, and HSL(A). The functions and notations for these are rgb(), rgba(), #rgb/#rrggbb, #rgba/#rrggbbaa, hsl(), and hsla(). Browsers have always supported built-in names like aliceblue as well.

Along the way, we’ll encounter use of some color syntaxes provided by a new Level 4 of the CSS Colors Module. For example, we now have hex with alpha as we mentioned (#rgba/#rrggbbaa) and RGB and HSL syntaxes no longer require commas (values like rgb(255 0 0) and hsl(240 100% 50%) became legal!).

Browser support for CSS Colors Level 4 isn’t universal as of this writing, so don’t expect new color syntaxes to work in Microsoft browsers or Safari if trying them in CSS.

RGB to Hex

Converting RGB to hex is merely a change of radices. We convert the red, green, and blue values from decimal to hexadecimal using toString(16). After prepending 0s to single digits and under, we can concatenate them and # to a single return statement.

Alternatively, we can use a single string argument with the red, green and blue separated by commas or spaces (e.g. "rgb(255,25,2)", "rgb(255 25 2)"). Substring to eliminate rgb(, split what’s left by the ), then split that result’s first item by whichever the separator (sep) is. r, g, and b shall become local variables now. Then we use + before the split strings to convert them back to numbers before obtaining the hex values.

Converting RGBA to hex with the #rgba or #rrggbbaa notation follows virtually the same process as the opaque counterpart. Since the alpha (a) is normally a value between 0 and 1, we need to multiply it by 255, round the result, then convert it to hexadecimal.

To do this with one string (including with percentages), we can follow what we did earlier. Also note the extra step of splicing out a slash. Since CSS Colors Level 4 supports the syntax of rgba(r g b / a), this is where we allow it. Alpha values can now be percentages! This removes the 0-1-only shackles we used to have. Therefore, the for loop cycling through rgba shall include a part to wipe the % from the alpha without multiplying by 255 (when R is 3 for alpha). Soon we can use values like rgba(255 128 0 / 0.8) and rgba(100% 21% 100% / 30%)!

We know that the length of hex values must either be 3 or 6 (plus #). In either case, we begin each red (r), green (g), and blue (b) value with "0x" to convert them to hex. If we provide a 3-digit value, we concatenate the same value twice for each channel. If it’s a 6-digit value, we concatenate the first two for red, next two for green, and last two for blue. To get the values for the final rgb() string, we prepend the variables with + to convert them from strings back to numbers, which will yield the decimals we need.

Under the last if statement, using +s will convert r, g, and b to numbers. Each toFixed(1) along with them will round the result to the nearest tenth. Additionally, we won’t have whole numbers with .0 or the decades old quirk that produces numbers like 0.30000000000000004. Therefore, in the return, we omitted the +s right before the first r, g, and b to prevent NaNs caused by the %s. Now we can use hexToRGB("#ff0",true) to get rgb(100%,100%,0%)!

Hex (#rrggbbaa) to RGBA

The procedure for hex values with alpha should again be similar with the last. We simply detect a 4- or 8-digit value (plus #) then convert the alpha and divide it by 255. To get more precise output but not long decimal numbers for alpha, we can use toFixed(3).

Here’s a quick fix if the alpha ought to be a percentage, too: move the statement where a is redefined above the last if statement. Then in that statement, modify a to be like r, g, and b. When isPct is true, a must also gain the %.

When we enter #7f7fff80 now, we should get rgba(127,127,255,0.502) or rgba(49.8%,49.8%,100%,50.2%).

RGB to HSL

Obtaining HSL values from RGB or hex is a bit more challenging because there’s a larger formula involved. First, we must divide the red, green, and blue by 255 to use values between 0 and 1. Then we find the minimum and maximum of those values (cmin and cmax) as well as the difference between them (delta). We need that result as part of calculating the hue and saturation. Right after the delta, let’s initialize the hue (h), saturation (s), and lightness (l).

Next, we need to calculate the hue, which is to be determined by the greatest channel value in cmax (or if all channels are the same). If there is no difference between the channels, the hue will be 0. If cmax is the red, then the formula will be ((g - b) / delta) % 6. If green, then (b - r) / delta + 2. Then, if blue, (r - g) / delta + 4. Finally, multiply the result by 60 (to get the degree value) and round it. Since hues shouldn’t be negative, we add 360 to it, if needed.

All that’s left is the saturation and lightness. Let’s calculate the lightness before we do the saturation, as the saturation will depend on it. It’s the sum of the maximum and minimum channel values cut in half ((cmax + cmin) / 2). Then delta will determine what the saturation will be. If it’s 0 (no difference between cmax and cmin), then the saturation is automatically 0. Otherwise, it’ll be 1 minus the absolute value of twice the lightness minus 1 (1 - Math.abs(2 * l - 1)). Once we have these values, we must convert them to values out of 100%, so we multiply them by 100 and round to the nearest tenth. Now we can string together our hsl().

Compared to what we just did to convert RGB to HSL, the alpha counterpart will be basically nothing! We just reuse the code for RGB to HSL (the multi-argument version), leave a alone, and pass a to the returned HSLA. Keep in mind it should be between 0 and 1.

For string values, we apply the splitting and stripping logic again but use the fourth item in rgba for a. Remember the new rgba(r g b / a) syntax? We’re employing the acceptance of it as we did for RGBAToHexA(). Then the rest of the code is the normal RGB-to-HSL conversion.

It takes slightly less logic to convert HSL back to RGB than the opposite way. Since we’ll use a range of 0–100 for the saturation and lightness, the first step is to divide them by 100 to values between 0 and 1. Next, we find chroma (c), which is color intensity, so that’s (1 - Math.abs(2 * l - 1)) * s. Then we use x for the second largest component (first being chroma), the amount to add to each channel to match the lightness (m), and initialize r, g, b.

For the single string version, we modify the first few statements basically the same way we did for RGBToHSL(r,g,b). Remove s /= 100; and l /= 100; and we’ll use the new statements to wipe the first 4 characters and the ) for our array of HSL values, then the %s from s and l before dividing them by 100.

The next handful of statements shall handle hues provided with a unit—degrees, radians, or turns. We multiply radians by 180/? and turns by 360. If the result ends up over 360, we compound modulus divide to keep it within the scope. All of this will happen before we deal with c, x, and m.

Changing it to one argument, the way we’ll handle strings here will be not too much different than what we did earlier. A new HSLA syntax from Colors Level 4 uses (value value value / value) just like RGBA, so having the code to handle it, we’ll be able to plug in something like hsla(210 100% 50% / 0.5) here.

You might think this one and the next are crazier processes than the others, but they merely come in two parts with recycled logic. First, we convert the hex to RGB. That gives us the base 10s we need to convert to HSL.

There aren’t too many lines that change in this one. We’ll repeat what we recently did to get the alpha by converting the hex, but won’t divide it by 255 right away. First, we must get the hue, saturation, and lightness as we did in the other to-HSL functions. Then, before the ending return, we divide the alpha and set the decimal places.

Even the first few lines of this function will be like those in HSLToRGB() if we changed it to accept a single string. This is how we’ve been obtaining the hue, saturation, and lightness separately in the first place. Let’s not forget the step to remove the hue label and convert to degrees, too. All of this will be in place of s /= 100; and l /= 100;.

In the long run, every conversion from a name becomes a conversion from RGB after cracking the name.

Validating Colors

In all these functions, there haven’t been any measures to prevent or correct ludicrous input (say hues over 360 or percentages over 100). If we’re only manipulating pixels on a <canvas> fetched using getImageData(), validation of color values isn’t necessary before converting because they’ll be correct no matter what. If we’re creating a color conversion tool where users supply the color, then validation would be much needed.

It’s easy to handle improper input for channels as separate arguments, like this for RGB:

// Correct red
if (r > 255)
r = 255;
else if (r < 0)
r = 0;

If validating a whole string, then a regular expression is needed. For instance, this is the RGBToHex() function given a validation step with an expression:

Looking at the expressions for RGB(A) and HSL(A), you probably have big eyes right now; these were made comprehensive enough to include most of the new syntaxes from CSS Colors Level 4. Hex, on the other hand, doesn’t need expressions as long as the others because of only digit counts. In a moment, we’ll dissect these and decipher the parts. Note that case-insensitive values (/i) pass all these.

Because rgb() accepts either all integers or all percentages, both cases are covered. In the outmost group, between the ^rgb\( and \)$, there are inner groups for both integers and percentages, all comma-spaces or spaces only as separators:

In the first half, we accept two instances of integers for red and green from 0–99 or 111-199 ((1?[1-9]?\d)), 100–109 (10\d), 200-249 ((2[0-4]\d)), or 250–255 (25[0-5]). We couldn’t simply do \d{1,3} because values like 03 or 017 and those greater than 255 shouldn’t be allowed. After that goes the comma and optional space (,\s?). On the other side of the |, after the first {2} (which indicates two instances of integers), we check for the same thing with space separators if the left side is false. Then for blue, the same should be accepted, but without a separator.

In the other half, acceptable values for percentages, including floats, should either be 0–99, explicitly 100 and not a float, or floats under 1 with the 0 dropped. Therefore, the segment here is (([1-9]?\d(\.\d+)?)|100|(\.\d+)), and it appears three times; twice with separator (,\s?){2}, %\s){2}), once without.

It is legal to use percentages without space separators (rgb(100%50%10%) for instance) in CSS, but the functions we wrote don’t support that. The same goes for rgba(100%50%10%/50%), hsl(40 100%50%), and hsla(40 100%50%/0.5). This could very well be a plus for code golfing and minification!

The next expression is very similar to the pervious, but three instances of integers (((((1?[1-9]?\d)|10\d|(2[0-4]\d)|25[0-5]),\s?){3})) or percentages ((((([1-9]?\d(\.\d+)?)|100|(\.\d+))%,\s?){3})), plus comma optional space are checked. Otherwise, it looks for the same thing but with space separators, plus a slash and space (\/\s) after the blue. Next to that is ((0?\.\d+)|[01]|(([1-9]?\d(\.\d+)?)|100|(\.\d+))%) where we accept floats with or without the first 0 ((0?\.\d+)), 0 or 1 ([01]) on the dot, or 0–100% ((([1-9]?\d(\.\d+)?)|100|(\.\d+))%).

For both hex—with and without alpha—instances of numbers or letters a–f ([\da-f]) are accepted. Then one or two instances of this are counted for either short or longhand values supplied (#rgb or #rrggbb). As an illustration, we have this same short pattern: /^#([\da-f]{n}){1,2}$/i. Simply change n to 3 or 4.

([12]?[1-9]?\d) covers 0–99, 110–199, and 210–299. [12]0\d covers 110–109 and 200–209. Then (3[0-5]\d) takes care of 300–359. The reason for this division of ranges is similar to that of integers in the rgb() syntax: ruling out zeros coming first and values greater than the maximum. Since hues can be floating point numbers, the first (\.\d+)? is for that.

Next to the | after the aforementioned segment of code, the second (\.\d+) is for floats without a leading zero.

Now let’s move up a level and decipher the next small chunk:

(deg)?|(0|0?\.\d+)turn|((\[0-6\\.\d+)?)|(\.\d+))rad

This contains the labels we can use for the hue—degrees, turns, or radians. We can include all or none of deg. Values in turn must be under 1. For radians, we can accept any float between 0–7. We do know, however, that one 360° turn is 2?, and it stops approximately at 6.28. You may think 6.3 and over shouldn’t be accepted. Because 2? is an irrational number, it would be too messy for this example to try to satisfy every decimal place provided by the JavaScript console. Besides, we have this snippet in our HSLTo_() functions as a second layer of security if hues 360° or over were to happen:

// Keep hue fraction of 360 if ending up over
if (h >= 360)
h %= 360;

Now let’s move up a level and decipher the second chunk:

(,\s?(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2}

We’re counting two instances of comma-space-percentages for the saturation and lightness (space optional). In the group after the ,\s?, we test for values 0–99 with or without decimal points (([1-9]?\d(\.\d+)?)), exactly 100, or floats under 1 without the leading 0 ((\.\d+)).

The last part the HSL expression, before the ending (\)$/i), is a similar expression if spaces are the only separator:

(\s(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2}

\s is in the beginning instead of ,\s?. Then in the HSLA expression, this same chunk is inside another group with ,\s? after its {2}.

((,\s?(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2},\s?)

That counts the comma-space between the lightness and alpha. Then if we have spaces as separators, we need to check for a space-slash-space (\s\/\s) after counting two instances of space and a percentage.

((\s(([1-9]?\d(\.\d+)?)|100|(\.\d+))%){2}\s\/\s))

After that, we have this left to check the alpha value:

(((0?\.\d+)|[01])|(([1-9]?\d(\.\d+)?)|100|(\.\d+))%)

Matches for (0?\.\d+) include floats under 1 with or without the leading 0, 0 or 1 for [01], and 0–100%.

Conclusion

If your current challenge is to convert one color space to another, you now have some ideas on how to approach it. Because it would be tiresome to walk through converting every color space ever invented in one post, we discussed the most practical and browser-supported ones. If you’d like to go beyond supported color spaces (say CMYK, XYZ, or CIE L*a*b*), EasyRGB) provides an amazing set of code-ready formulas.

To see all the conversions demonstrated here, I’ve set up a CodePen demo that shows inputs and outputs in a table. You can try different colors in lines 2–10 and see the complete functions in the JavaScript panel.