Web Standards

Here's the situation: You've bashed out a complicated design over two weeks of near full-time effort, gotten everything down to the exact spec of the design file, turn it in for stakeholder review and... you're way off scope. Turns out a few folks on the team put their heads together, made some changes, and never sent you an updated comp.

Boo!

The unfortunate truth is that this happens all too often in front-end development, but it's really no one person's fault because it boils down to simple collective miscommunication and a lack of transparency on the team.

Well, that's where a project management platform like monday.com comes into play. Even if you're on a remote team or sitting in an office with cubicle walls up to the ceiling, monday.com bridges gaps and tears down walls that could throw a project off-track. With powerful and intuitive tools, like instant messaging for those ad hoc meetings, file storage for a centralized repository of assets, and an activity dashboard for catching up on the status of a project at a glance, monday.com brings project out into the light so everyone is in the loop and on the same page.

Seriously, you'll wonder what you ever did without monday.com in your life once you've used it. It does everything any other project management tool can do — from task lists and milestones to budgets and human resources — but does one important thing that separates it from the rest: the personal touch that makes everyone on the team feel valuable, included and heard. That's tough to beat and unique to the project management landscape.

As you may know, the recent updates and additions to CSS are extremely powerful. From Flexbox to Grid, and — what we’re concerned about here — Custom Properties (aka CSS variables), all of which make robust and dynamic layouts and interfaces easier than ever while opening up many other possibilities we used to only dream of.

The other day, I was thinking that there must be a way to use Custom Properties to color an element's background while maintaining a contrast with the foreground color that is high enough (using either white or black) to pass WCAG AA accessibility standards.

It’s astonishingly efficient to do this in JavaScript with a few lines of code:

This takes the red, green and blue (RGB) values of an element’s background color, multiplies them by some special numbers (299, 587, and 144, respectively), adds them together, then divides the total by 1,000. When that sum is greater than 128, it will return black; otherwise, we’ll get white. Not too bad.

The only problem is, when it comes to recreating this in CSS, we don't have access to a native if statement to evaluate the sum. So,how can we replicate this in CSS without one?

Luckily, like HTML, CSS can be very forgiving. If we pass a value greater than 255 into the RGB function, it will get capped at 255. Same goes for numbers lower than 0. Even negative integers will get capped at 0. So, instead of testing whether our sum is greater or less than 128, we subtract 128 from our sum, giving us either a positive or negative integer. Then, if we multiply it by a large negative value (e.g. -1,000), we end up with either very large positive or negative values that we can then pass into the RGB function. Like I said earlier, this will get capped to the browser’s desired values.

If my math is correct (and it's very possible that it's not) we get a total of 16,758, which is much greater than 255. Pass this total into the rgb() function for all three values, and the browser will set the text color to white.

At this point, everything seems to be working in both Chrome and Firefox, but Safari is a little cranky and gives a different result. At first, I thought this might be because Safari was not capping the large values I was providing in the function, but after some testing, I found that Safari didn't like the division in my calculation for some reason.

Taking a closer look at the calc() function, I noticed that I could remove the division of 1,000 by increasing the value of 128 to 128,000. Here’s how that looks so far:

Throw in a few range sliders to adjust the color values, and there you have it: a dynamic UI element that can swap text color based on its background-color while maintaining a passing grade with WCAG AA.

Below is a Pen showing how this technique can be used to theme a user interface. I have duplicated and moved the --accessible-color variable into the specific CSS rules that require it, and to help ensure backgrounds remain accessible based on their foregrounds, I have multiplied the --accessible-color variable by -1 in several places. The colors can be changed by using the controls located at the bottom-right. Click the cog/gear icon to access them.

A little while back, Facundo Corradini explained how to do something very similar in this post. He uses a slightly different calculation in combination with the hsl function. He also goes into detail about some of the issues he was having while coming up with the concept:

Some hues get really problematic (particularly yellows and cyans), as they are displayed way brighter than others (e.g. reds and blues) despite having the same lightness value. In consequence, some colors are treated as dark and given white text despite being extremely bright.

What in the name of CSS is going on?

He goes on to mention that Edge wasn’t capping his large numbers, and during my testing, I noticed that sometimes it was working and other times it was not. If anyone can pinpoint why this might be, feel free to share in the comments.

So, count this as yet another approach to throw into the mix. It’s incredibly awesome that Custom Properties open up these sorts of possibilities for us while allowing us to solve the same problem in a variety of ways.

There have been some interesting, boundary-pushing typography-related experiments lately. I was trying to think of a joke like "somethings in the descenders" but I just can't find something that will stand on its own leg without being easy to counter.

Note the special <div> at the top with the full word in it, while those empty <b> elements do the actual drawing of the glyphs. I wouldn't call it entirely accessible, as I'd argue part of accessibility is stuff like find-on-page with highlighting and selectable text. But this is obviously an artistic experiment and your implementation could go any number of ways. I'd love to see an attempt at setting transparent SVG <text> over the top of it that is sized the same so that the text is selectable.

Support seems kinda decent to me, but it's complicated because there are a bunch of different kinds all with different support across browsers.

The other story is that they are, well, kinda humongous, size-wise. The woff2 file is probably the most relevant here as it's such a modern feature anyway. The download from the site (RocherColorGX.woff2) clocks in at 178KB. Not something you'd just toss on a page for a single headline probably, considering it's not just weight with fonts — but you're also always fighting the FOUT/FOIT battle.

I think to justify using a beefy color font like this you'd...

Use it quite a bit around the site for dynamic headlines

Customize the colors to be perfect for you (ahead of time)

Make use of the fancy variable font features like the bevel and shadow adjustments (on the fly)

If you don't see yourself doing those things, you might be better off using <svg> with these shapes all expanded out to paths. You could still use this font to make the SVG, assuming your design tool supports this kind of font. You won't get text wrapping or anything, but the file size and loading speed will be much faster. Or you could even use a graphic format like PNG/WebP, and there's no terrible shame in that if you still use a semantic element for the headline (visually hidden, of course). You won't get find-on-page highlighting or select-ability, but might be an OK trade-off for a one-off.

Hidden for assistive technology too, so careful there, but it seems to me this project is more about exploring possibilities. After all, it's the letters that power Diana's remarkable CSS paintings like Zigaro.

Don't miss Kenneth's post, as he links to lots more fascinating examples of people being typographers with very unconventional tools. Kenneth takes a crack himself with this fascinating little experiment, using a button there to expose the tricks within:

I popped over to Facebook to verify that and what I saw was a different and even more nested mess:

They are trying to fight your ad blocker browser extension. Of course they are. I'm sure at their scale not doing this means losing millions of dollars. But I wonder if it's really losing money when you factor in losing trust, and potentially losing people on the platform entirely.

It just feels so rude, doesn't it? Like a user specifically installs technology onto their computer in order to exert some control over what they allow onto their computers and into their eyeballs. And they are saying, "No, we do not respect that choice. We are going to fight your technology with our technology and force feed this stuff onto your computer and your eyeballs." Doesn't sit right.

I'm not unaware that ad blockers have ad adverse effect on the ability for websites to make money. That's quite literally how I make money. But I don't want to do it fighting and at the expense of breaking trust. I want to do it gracefully while building trust.

Anyway.

I wonder what writing HTML to help ad blockers would look like instead:

This span-based lettering stuff makes me think of libraries like Splitting.js and Lettering.js that break up text into individual <span>s for styling reasons.

Turns out that doesn't affect on-page search (i.e. if you search for "dog," you'll find <span>d</span><span>o</span><span>g</span>), but it does affect some screen readers in that they will treat each letter distinctly, which can result in pretty awful audio output, like pauses between letters where you wouldn't expect or want them.

Well, Diana has also listed the top five CSS properties she uses to get these masterpieces done, and they are surprising in their plainness:

border-radius

box-shadow

transform

gradients

overflow

...but of course, layered in trickery!

... for custom rounded elements that are meant to mimic organic objects like faces, it is imperative that you become intimately familiar with all eight available parameters in the border-radius property.

Diana shows her famous Francine drawing with each of the most used properties turned off:

Without border-radius
Without transform

Be sure to check out this VICE interview she did as well. She covers gems like the fact that Francine was inspired by American Dad (lol) and that the cross-browser fallbacks are both a fascinating and interesting mess.

I love the idea of programmatically generated images. That power is close at hand these days for us front-end developers, thanks to the concept of headless browsers. Take Puppeteer, the library for controlling headless Chrome. Generating images from URLs is their default use case:

That ought to get the ol' mind grape going. What if we had URLs on our site that — with the power of our HTML and CSS skills — created perfect little designs for sharing using dynamic data... then turned them into images and used them for our meta tags?

Since the design part is entirely an HTML/CSS adventure, I'm sure you could imagine a setup where the URL passed in parameters that did things like set copy and typography, colors, sizes, etc. Zeit built exactly that!

Kind of amazing that you can spin up an entire browser in a cloud function! Netlify also offers cloud functions, and when I mentioned this to Phil Hawksworth, he told me he was already doing this for his blog!

So on a blog post like this one, an image like this is automatically generated:

Brad points out some UX problems with a variety of apps that are doing things a little outside of the norm when it comes to their login forms. There is already a bunch of things to get right with forms to begin with (e.g. use the right input types, label your inputs, don't have whack password requirements, use SSL, etc.)... OMG why complicate it even more?!

A "password manager test" should be a development best practice here. Does it work cleanly with the built-in browser password manager? How about 1Password and LastPass? No? Give it some love, please and thank you.

The web development market is a crowded one and consumers are spoilt for choice. Therefore, having a website that serves as a business card is no longer competitive. Your site should act as a direct...

CSS has a neat feature that allows us to test if the browser supports a particular property or property:value combination before applying a block of styles — like how a @media query matches when, say, the width of the browser window is narrower than some specified size and then the CSS within it takes effect. In the same spirit, the CSS inside this feature will take effect when the property:value pair being tested is supported in the current browser. That feature is called @supports and it looks like this:

@supports (display: grid) {
.main {
display: grid;
}
}

Why? Well, that's a bit tricky. Personally, I find don't need it all that regularly. CSS has natural fallback mechanisms such that if the browser doesn't understand a property:value combination, then it will ignore it and use something declared before it if there is anything, thanks to the cascade. Sometimes that can be used to deal with fallbacks and the end result is a bit less verbose. I'm certainly not a it's-gotta-be-the-same-in-every-browser kinda guy, but I'm also not a write-elaborate-fallbacks-to-get-close kinda guy either. I generally prefer a situation where a natural failure of a property:value doesn't do anything drastic to destroy functionality.

That said, @supports certainly has use cases! And as I found out while putting this post together, plenty of people use it for plenty of interesting situations.

A classic use case

The example I used in the intro is a classic one that you'll see used in lots of writing about this topic. Here it is a bit more fleshed out:

Nice grid! Repeating and auto-filling columns is a sweet feature of CSS grid. But, of course, there are browsers that don't support grid, or don't support all the specific features of it that I'm using above there.

For example, iOS shipped support for CSS grid in version 10.2, but iOS has had flexbox support since version 7. That's a decent gap of people with older iOS devices that do support flexbox but not grid. I'm sure there are more example gaps like that, but you probably get the idea.

I was running on an older version of mobile safari and many many many many many sites were flat out broken that used grid

It may be acceptable to let the fallback for this be nothing, depending on the requirements. For example, vertically stacked block-level elements rather than a multi-column grid layout. That's often fine with me. But let's say it's not fine, like a photo gallery or something that absolutely needs to have some basic grid-like structure. In that case, starting with flexbox as the default and using @supports to apply grid features where they're supported may work better...

The "fallback" is the code outside of the @supports block (the properties above the block in the example above), and the grid code is either inside or after. The @supports block doesn't change any specificity, so we need the source order to help make sure the overrides work.

Notice I had to reset the margin on the divs inside the @supports block. That's the kind of thing I find a bit annoying. There is just enough crossover between the two scenarios that it requires being super aware of how they impact each other.

Doesn't that make you wish it could be entirely logically separated...

There is "not" logic in @supports blocks, but that doesn't mean it should always be used

Notice the not operator in the first block. That's checking for browsers that do not support grid in order to apply certain styles to those browsers. The reason this approach is considered bad practice is that the browser support for @supports itself has to be considered!. That's what makes this so dang tricky.

It's very appealing to write code in logically separated @supports blocks like that because then it's starting from scratch each time and doesn't need to be overriding previous values and dealing with those logical mind-benders. But let's go back to the same iOS situation we considered before... @supports shipped in iOS in version 9 (right between when flexbox shipped in iOS 7 and grid shipped in iOS 10.2). That means any flexbox fallback code in a @supports block using the not operator to check for (display: grid) {} support wouldn't work in either iOS 7 or 8, meaning the fallback now needs a fallback from working in browsers it otherwise would have. Phew!

The big reason to reach for @supports is to account for very different implementations of something depending on feature support where it becomes easier to reason and distinguish between those implementations if the blocks of code are separated.

We'll probably get to a point where we can use mutually-exclusive blocks like that without worry. Speaking of which...

@supports is likely to be more useful over time.

Once @supports is supported in all browsers you need to support, then it's good to start using it more aggressively and without having to factor in the complication of considering whether @supports itself is supported. Here's the support grid on that:

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

On some level, that makes perfect logical sense. If transforms are supported, use them. But it's unnecessary if nothing different is happening in a non-support scenario. In this case, the transform can fail without the @supports block and the result is the same.

They are both centered around the idea that we can write @supports blocks in CSS and then toggle them on and off as if we're looking at a rendering of the code in a browser that does or doesn't support that feature.

Here's a video of Keith's tool applied to the scenario using grid with a flexbox fallback:

This is fun to play with and is very neat tech. But in this exact scenario, if I was able to pull off the layout identically with flexbox, then I'd probably just do that and save that little bit of technical debt.

Ire's tool, which she wrote about in the article Creating The Feature Queries Manager DevTools Extension, has a slightly different approach in that it shows the feature queries that you actually wrote in your CSS and provides toggles to flip them on and off. I don't think it works through iframes though, so I popped open Debug Mode to use it on CodePen.

@supports (transform: rotate(1turn)) and (opacity: 0) {
/* all the styling for Erik's custom checkboxes and radio buttons */
}

Here are several more I've come across:

Joe Wright and Tiago Nunes mentioned using it for position: sticky;. I'd love to see a demo here! As in, where you go for position: sticky;, but then have to do something different besides let it fail for a non-supporting browser.

Keith Grant and Matthias Ott mention using it for object-fit: contain;. Matthias has a demo where positioning trickery is used to make an image sort of fill a container, which is then made easier and better through that property when it's available.

Ryan Filler mentions using it for mix-blend-mode. His example sets more opacity on an element, but if mix-blend-mode is supported, it uses a bit less and that property which can have the effect of seeing through an element on its own.

Stacy Kvernmo mentioned using it for the variety of properties needed for drop capping characters. Jen Simmons mentions this use case in her article as well. There is an initial-letter CSS property that's pretty fantastic for drop caps, but is used in conjunction with other properties that you may not want to apply at all if initial-letter isn't supported (or if there's a totally different fallback scenario).

Here's a bonus one from Nick Colley that's not @supports, but @media instead! The spirit is the same. It can prevent that "stuck" hover state on touch devices like this:

instant.page is a pretty cool project from Alexandre Dieulot. Alexandre has been at this idea for half a decade now, as InstantClick is his and is essentially the same exact idea.

The idea is that there is a significant delay between hovering over a link and clicking that link. Say it takes you 300ms of delay. That 300ms could have been spent preloading the next page. And if you do use that time preloading, that page loads that much faster.

This new project makes use of newer tech to get it done. It's hardly any code., the core of which is appending a <link rel="prefetch" href=""> to the document of the link you're about to click/touch.

The page encourages you to hotlink the script, which means possible cache-hits in case you've already visited a page using this. It's not risky in the way other third-party JavaScript can be because the integrity attribute means that if you trust the code as it is now, it can't ever change unless you change that attribute along with it. It also cleverly uses the type="module" to prevent it from loading anything in browsers that don't support prefetching anyway.

Still, you could self-host it if you wanted. I have no idea who's ponying up the for the bandwidth here, so another risk is a hung script should it stop responding one day.

You could argue that it doesn't do the prefetching as absolutely responsibly as it could. Google's similar quick link library (which we covered here) does two interesting things in which to attempt to be more responsible with prefetching: 1) wait for requestIdleCallback and 2) respects info from navigator.connection, like a user enabling data-saver mode.

If you work on web applications that support older browsers, and have lusted after CSS Grid from the sidelines like I have, I have some good news: I've discovered a clever CSS-only way to use grid auto-placement in IE10+!

Now, it's not actually CSS Grid, but without looking at the code itself, you wouldn't be able to tell. The HTML structure looks like CSS Grid. It has a defined set of columns with an undefined amount of rows and it has gutters that support borders and shadows on the cells without hacks. But what’s actually happening behind the scenes is a combination of flexbox and margins.

In this article, I'll walk through the approach. Here’s a demo of what we’re looking at:

Getting the basic grid setup is very simple. If you're at all familiar with flexbox, I'm certain you've already guessed flex-wrap: wrap is the trick here. And you'd be right.

Let's get the HTML markup in place before we write any CSS. We want it to resemble the same structure as if we were using auto-placement — a .grid container and an undefined number of .grid__cells.

<div class="grid">
<div class="grid__cell">...</div>
...
</div>

We set three grid breakpoints. A single-column, two-column, and three-column layout for mobile-devices, small screens, and medium screens, respectively. I'm using the breakpoints used in Bootstrap for this article, though we’d want to define them at actual points where the layout breaks if we were working with real content.

A mobile-first approach means our single-column layout is already complete since each .grid__cell is already a block. We set .grid to become a flexbox container after the first breakpoint, and wrap cells.

Our two- and three-column layouts need explicit widths and flex properties; otherwise they'll cram onto a single line. While testing IE10, I experienced unexpected behavior with the flex-basis property, and found setting an explicit width with flex-basis: auto was more consistent. This didn't seem to be a problem with IE11 though.

We don't need to wrap .grid__cell in a media query since its flex properties won't have the effect when the parent isn't a flexbox container. We also define an upper-limit to the two-column media query so it doesn't affect the three-column grid.

And that's it! We now have a responsive, fluid, wrapping flexbox grid. The easy part is done… well, as long as we only ever have items that are multiples of two and three. With flex: 1 1 auto, the last item will always take up any remaining space in the last row.

Two-column grid on smaller screens
Three-column grid on large screens
Aligning cells in the last row

The elusive last row is why we're here, right? By default, each cell will stretch to the end of the row in a flexbox layout, but grid leaves a blank spot. How do we do that in flexbox? With pseudo-elements!

The trick is to add a pseudo-element to the .grid container and set it like a cell. We define the :after pseudo-element cell at each of our breakpoints with the same width as a real cell.

This creates a fake cell that will push against our real cells and align our two-column grid when the cells are odd. Leaving its height undefined allows it to collapse to nothing when the cells are even.

Two-column grid with odd cells, snapping into place

Our three-column grid is a bit more complex because we need to handle multiple states, like when there is one empty cell and when there are two empty cells.

Three-column grid with one empty cell

Our one empty cell state is already handled because it isn't really any different from one empty cell in two columns. The :after cell has its width set and completes the row. The story changes when there are two empty cells though because flex: 1 1 auto rears its head again: the last cell now stretches across 50% of the width when pushed against the pseudo-element.

Three-column grid with two empty cells

Using CSS :nth-of-type selectors, we can target the first column in each row. Since our rows are multiples of three, we target them with 3n then count backwards by 2 to get the first element in each row.

We're broadly targeting all the cells in the first column, but we need to limit the selection to only the last row. Actually, we need to limit it to when it's the last cell in the first column of the last row. Luckily, there's a handy pseudo-selector for targeting the last item of its kind. We chain :last-of-type to create the logical statement.

Here's our flexbox-defined-auto-placement-grid-imitator in full. Look at its beautifully lined up rows. I bet you can't even tell it's not CSS Grid!

Our complete three-column grid.
Adding gutters with margins

CSS Grid's spec has a column and row gap to provide space between each cell. Creating gutters in flexbox is much more challenging. It looks like it's coming to flexbox, but we're not there yet…and IE will never be.

In Daniel Tonon’s guide on CSS Grid in IE, he used an inner-cell div with negative margins, borders, a bit of padding, and overflow: hidden. While maybe a bit hacky, the effect works, but it breaks our desire to maintain CSS Grid-like HTML structure. The approach I prefer might feel a bit crude, but I also found it the easiest to read and understand. Further, it continues using :nth-of-type pseudo-selectors which makes the overall approach feel consistent.

We want gaps between the cells, but not around the outside. We also want our cells to sit flush with the container.

Gaps between the cells, not on the outside.

Our mobile or single-column grid only needs a bottom margin on the cells. We add that and override the very last cell with margin-bottom: 0 so the cell fits flush against the container. Normally I'd use initial, but there's no support in IE.

Our two- and three-column grids need margins on the right of the cells, no right margins in the last column, and no bottom margins on any of the last row's cells. Because of the margins, we'll also need to recalculate our widths since the cells will wrap if they don't fit.

In a two-column layout, getting the right (or second) column is fairly easy with :nth-of-type(2n) or :nth-of-type(even). I prefer an n-multiplier for consistency with our three-column grid and for calculating the last row.

Our last row is a bit more tricky. When we have odd cells our mobile-first CSS takes care of removing the bottom margins since the cell is the :last-of-type and our :after cell doesn't have margins applied.

Two-columns with even cells

When we have even cells we need to target the second last cell, but only when it is in the first column position. If we didn't qualify it, the second last cell will grow vertically with to match the height of the second last row. We can target it with :nth-of-type(2n-1):nth-last-of-type(2).

Our three-column gutters take the same approach. We add margin-right to all of them, remove it from the third column, and remove bottom margins from the last row. Again our last cell is handled by our mobile-first approach, but now we need to cover when there are two cells in the last row and when when there are three cells. We can qualify our selectors with nth-of-type and nth-last-of-type.

I believe this technique could also support IE9 with minor adjustments, like using inline-blocks instead of flexbox. We could also expand to a four-column grid by adding another breakpoint and using the same approach as the three-column grid. Feel free to use this approach and I hope it helps!

One way to deal with long, complex forms is to break them up into multiple steps. You know, answer one set of questions, move on to another, then maybe another, and so on and so forth. We often refer to these as multi-step forms (for obvious reasons), but others also take to calling it a “wizard” form.

Multi-step forms can be a great idea! By only showing a few inputs on a screen at a time, the form may feel more digestible and prevent users from feeling overwhelmed by a sea of form fields. Although I haven’t looked it up, I’m willing to say no one enjoys completing a ginormous form — that’s where multiple steps can come in handy.

The problem is that multi-step forms — while reducing perceived complexity on the front end — can feel complex and overwhelming to develop. But, I’m here to tell you that it’s not only achievable, but relatively straightforward using React as the base. So, that’s what we’re going to build together today!

The easiest way to create a multi-step form is to create a container form element that contains all the steps inside of it as components. Here’s a visual showing that container (<MasterForm/>), the components inside of it (<Step1/>, <Step2/>, <Step3/>) and the way states and props are passed between them.

<MasterForm/> serves as the container while three child components inside of it act as each step of the form.

Although it seems to be more complex than a regular form, a multi-step form still uses the same principles as a React form:

State is used for storing data and user inputs.

Component is used for writing methods and the interface.

Props are used for passing data and function into elements.

Instead of having one form component, we will have one parent component and three child components. In the diagram above, <MasterForm/> will send data and functions to the child components via props, and in turn, the child components will trigger a handleChange() function to set values in the state of <MasterForm/>. It’s one big happy family over here!

We'll need a function to move the form from one step to another as well, and we’ll get to that a little later.

The step child (get it?) components will receive props from the <MasterForm/> parent component for value and onChange props.

<Step1/> component will render an email address input

<Step2/> will render a username input

<Step3/> will render a password input and a submit button

<MasterForm/> will supply both data and function into child components, and child components will pass user inputs back to the parent using its props.

Creating the step (child) components

First, we’ll create the form’s child components. We’re keeping things pretty barebones for this example by only using one input per step, but each step could really be as complex as we’d like. Since the child components look almost similar between one another, I’m just gonna show one of them here. But be sure to take a look at the demo for the full code.

Now we can put this child component into the form’s render() function and pass in the necessary props. Just like in React’s form documentation, we can still use handleChange() to put the user’s submitted data into state with setState(). A handleSubmit() function will run on form submit.

Next up, the parent component

Let’s make the parent component — which we’re all aware by now, we’re calling <MasterForm/> — and initialize its state and methods.

We’re using a currentStep state that will be initialized with a default value of 1, indicating the first step (<Step1/>) of the form. We’ll update the state as the form progresses to indicate the current step.

OK, that’s the baseline functionality we’re looking for. Next, we want to create the shell UI for the actual form add call the child components in it, including the required state props that will be passed from <MasterForm/> via handleChange().

So far, we’ve allowed users to fill the form fields, but we've provided no actual way to proceed to the next step or head back to the previous one. That calls for next and previous functions that check if the current step has a previous or next step; and if it does, push the currentStep prop up or down accordingly.

We’ll use a get function that will check whether the current step is 1 or 3. This is because we have three-step form. Of course, we can change these checks as more steps are added to the form. We also want to display the next and previous buttons only if there actually are next and previous steps to navigate to, respectively.

That was the last step in this multi-step tutorial on multi-step forms. Whoa, how meta! While we didn’t go deep into styling, hopefully this gives you a solid overview of how to go about making complex forms less… complex!

Here’s that final demo again so you can see all the code in it’s full and glorious context:

React was made for this sort of thing considering it makes use of states, property changes, reusable components and such. I know that React may seem like a high barrier to entry for some folks, but I’ve written a book that makes it a much lower hurdle. I hope you check it out!

You know about the State of JavaScript survey, where thousands upon thousands of developers were surveyed about all-things-JS, from frameworks to testing and many other things in between? Well, Sacha Greif has launched one focused entirely on CSS.

This is super timely given a lot of the content we and other sites have been posting lately centered around learning, complexity, changing roles, and more. Sacha captures it nicely:

This is especially interesting since it comes at a time where many are talking about a “Great Divide” between the “front” of the front-end (HTML, CSS) and the “back” of the front-end (JavaScript and its many frameworks and libraries). [...] [T]he survey will be a great chance to take a snapshot of the community as it currently exists, and see how this evolves over the next couple years.

Sounds like a good goal. Let's help by putting some responses in there!

The Airtable web app is pretty neat. You can use it like a spreadsheet but it’s useful for all sorts of other things too. The neatest thing about it for me is that it has an API so that you can treat it like a database.

I’ve been thinking about making weekly notes for the different teams I work with at Gusto to read about what the design systems team is working on, things we've fixed, and any bugs we've encountered during that might impact other designers and engineers across our organization. I’ve spent a couple of hours thinking about how we might use Airtable to collect a whole bunch of data and then use its API to extract that info and show it in a web app.

Here’s an example of what we’ll end up building, which is basically a React web app that’s using Airtable as a nifty sorta CMS:

To get started, we have to head on over to the command line and run the following (but first make sure npm is installed):

npx create-react-app airtable-test

This will create a new directory called airtable-test which is where we’ll be making our little React app. If we run yarn start in the command line after that’s finished installing, then we’ll see the default page for the project:

And, if we open up src/App.js in that airtable-test directory, we can see how this page is being rendered with React:

Now that we have React up and running, we can go ahead and install airtable in the command line which will let us interact with the Airtable API:

npm i airtable

Once we’ve done that, we’ll need to create an Airtable account and create a project. We should wind up with something like this spreadsheet:

Now we can then head to airtable.com/api and select our project so that is serves as data we're pulling from. In this case, I selected “Design Systems Projects” which you can see right at the bottom here:

This will send us to a handy docs website that gives us an incredibly easy to read API for our specific project! Scrolling down we’ll find our API key which we’ll need to access this data as well as a ton of examples that we can use to manipulate the data we get back:

Let's head back to App.js in our airtable-test directory, delete all the code in that file, and replace it with the following:

Make sure to replace those Xs with the details that you can see in that Airtable API doc we just opened. But now that we’ve done all the setup, we can finally get around to creating our interface by calling data from our spreadsheet.

All this will do for now is setup the app’s state and then render “Hello” on the page. Next up, we’ll be add each record from Airtable to that state.

First thing that’s important to note below: in componentDidMount() we’ll be selecting Updates which is just a way of telling Airtable that we want the spreadsheet called Updates. Make sure that this name is the same name as the spreadsheet. We’ll also be looping through all the records, or rows, of our table in that function too:

fetchNextPage() is the Airtable API’s way of giving us the next record in our spreadsheet and it’s neat that it will keep going until there are no more records. Again, we’re not doing anything with that data yet; we only want to make sure everything is working correctly at this point.

Open up the console in DevTools and we should see something like this:

Array(4) [ {…}, {…}, {…}, {…} ]

And if we dive through each of the objects in this array, then we should find all the data from the spreadsheet! Doing this bit always feels like magic to me.

We’re going to be looping through the state that we setup earlier and then rendering the record.fields[] for each column in our spreadsheet. We have a Date, UI Kit, and Component Library column, and once we’ve updated our App.js with the code above, we should see all the content from our spreadsheet!

It’s like magic! But why does this data look so weird? Well, it’s because I wanted to write Markdown in each cell, so now we’ll need to use a parser to convert that data into good ol’ fashioned HTML. First, we need to head back to the command line though:

npm i showdown

showdown will help us parse all that Markdown we’ve written in our Airtable spreadsheet. After installing it, we only need to import it at the top of our App.js file, like this:

We’re doing a couple of new things here: we’re using dangerouslySetInnerHTML in each div which, in turn, uses our createHTML function to convert the data from each column (specifically, the UI Kit and Component Library columns). We’re also converting the dates of each row into headings to make things a bit easier to read.

There’s still a ton of updates we could make to improve things. I took a first pass at styling, but we probably want to do things like improve the date format and maybe have some kind of indication as to which updates refer to which rows in the spreadsheet. Maybe we could even toggle showing which information to show depending on whether you’re a designer or engineer.

Anyway! I think this is a good start to getting to grips with the Airtable API and I’d love to hear about how you use it in the comments below.

We've talked quite a bit about project management and workflows around here at CSS-Tricks, not because it's the core of what we do as designers and developers, but because we all play a role in it as part of a team and because it impacts the quality of our work at the end of the day.

That's why having a good system in place is such a benefit both to us and to teams as a whole. Where can you find a system like that? You might want to start by looking at monday.com. Yes, it's a project management tool but it actually goes way beyond that. Where some other platforms out there stop at task lists, calendars, and milestones, monday.com does those plus team collaboration.

If you've ever felt out of the loop on a project, had a surprise change in scope, or even been curious what other folks on your team have been up to, that's where monday.com really shines. It's people-centric, giving you and others insight into activity across an entire project through news feeds, messaging, shared assets, clearly defined user roles, among any other things. It's what a healthy, transparent, and collaborative team environment looks like.

We've only scratched the surface here, but lucky for you, there's a free 14-day trial to check out everything that monday.com has to offer. Go for it!

The world of software development offers an infinite amount of ways to mess up: deleting the wrong things, coding into dead ends, littering commit messages with typos, are a mere few of the plentitude.
??
??Fortunately, however, we have a wonderful safety net under our feet in the form of Git when we’re working with version control. Not that you and I need a safety net, of course, because we never make mistakes, right? Sure, sure. But for the benefit of everyone else, let's take a tour of some of the "undo" tools in Git that can save us from ourselves.
??
??
??
??

Fixing the last commit

??
??Messing up a commit is all too easy. Classic case in point: making a typo in a commit message. Another? Forgetting to add a change to the staging area. And in many cases, we instantly realize our mistake — right after hitting the Enter key, naturally.
??
??Luckily, Git makes it ridiculously easy to fix the very last commit. Let's say we had just hit Enter on the following command:

??

git commit -m "Massage full of typohs"

??
??And (as if this orthographic mess wasn't bad enough) let's say we also forgot to add another changed file to the staging area. We can correct both of our mistakes with the following two commands:
??

??
??The magic ingredient is the --amend? flag: when using it on a commit, Git will correct the very last commit — with any staged changes and the new message.
??
??A short word of warning, though: only use --amend? on commits that haven't been pushed to a remote repository, yet. The reason is that Git replaces the original, bad commit with the amended version. Afterwards, it looks as if the original commit never happened. Yeah, that’s good for concealing mistakes, but only if we haven't already published this mistake on the remote server.
????
??

Undoing local changes

??
??Everyone’s had days like this: spend all morning hacking away, only to admit to yourself that the last few hours were a waste of time. Gotta start over and undo much (or all) of that work.
??
??But this is one of the reasons for using Git in the first place — to be able to try out things without the fear that we might break something.
??
??Let's take stock in an example situation:
??

??
??Now, let's assume that this is one of the wasted hacking days described above. We ought to have kept our hands off of about.html and not deleted imprint.html. What we now want is to discard our current changes in these files — while keeping the brilliant work done in index.html. ??The git checkout? command can help in this case. Instead, we’ve gotta get more specific with which files to check out, like this:

??

git checkout HEAD about.html imprint.html

??This command restores both about.html and imprint.html to their last committed states. Phew, we got away from a black eye!
??
??We could take this one step further and discard specific individual lines in a changed file instead of tossing out the entire thing! I’ll admit, it’s rather complicated to make it happen on the command line, but using a desktop Git client like Tower is a great way to go about it:

??
??For those really bad days, we might want to bring out the big guns in the form of:
??
??

git reset --hard HEAD

??
??While we only restored specific files with checkout?, this command resets our whole working copy. In other words, reset? restores the complete project at its last committed state. ??Similar to --amend?, there's something to keep in mind when using checkout? and reset?: discarding local changes with these commands cannot be undone! They have never been committed to the repository, so it's only logical that they cannot be restored. Better be sure that you really want to get rid of them because there’s no turning back!
??
??

Undoing and reverting an older commit

??
??In many cases, we only realize a mistake much later, after it has long been committed to the repository.

??How can we get rid of that one bad commit? Well, the answer is that we shouldn't… at least in most cases. Even when "undoing" things, Git normally doesn't actually delete data. It corrects it by adding new data. Let's see how this works using our "bad guy" example:
??
??

git revert 2b504bee

??
??By using git revert? on that bad commit, we haven't deleted anything. Quite the contrary:

??Git automatically created a new commit with changes that reverts the effects of the "bad" commit. So, really, if we started with three commits and were trying to correct the middle one, now we have four total commits, with a new one added that corrects the one we targeted with revert?.
????
??

Restoring a previous version of a project

??
??A different use case is when we want to restore a previous version of our project. Instead of simply undoing or reverting a specific revision somewhere in our commit history, we might really want to turn back time and return to a specific revision.
??
??In the following example scenario, we would declare all the commits that came after "C2" as unwanted. What we want is to return to the state of commit "C2" and forget everything that came after it in the process:

??The command that's necessary is already (at least partly) familiar to you based on what we’ve already covered:
??
??

git reset --hard 2b504bee

??
??This tells git reset? the SHA-1 hash of the commit we want to return to. Commits C3 and C4 then disappear from the project's history.
??
??If you're working in a Git client, like Tower, both git revert? and git reset are available from the contextual menu of a commit item:

??
??Of course, there are many other ways to mess up things in a software project. But luckily, Git also offers many more tools for undoing the mess.
??
??Have a look at the "First Aid Kit for Git" project that I and other folks on the Tower team have created if you want to learn more about the scenarios we covered in this post, or about other topics, like how to move commits between branches, delete old commits, restore deleted branches or gracefully deal with merge conflicts. It’s a totally free guide that includes 17 videos and a handy cheat sheet you can download and keep next to your machine.

????Uncaught TypeError: Cannot read property 'foo' of undefined.? The dreaded error we all hit at some point in JavaScript development. Could be an empty state from an API that returns differently than you expected. Could be something else. We don’t know because the error itself is so general and broad.

??I recently had an issue where certain environment variables weren't being pulled in for one reason or another, causing all sorts of funkiness with that error staring me in the face. Whatever the cause, it can be a disastrous error if it’s left unaccounted for, so how can we prevent it in the first place?

??Let’s figure it out.

??Utility library

??If you are already using a utility library in your project, there is a good chance that it includes a function for preventing this error. _.get? in lodash? (docs) or R.path in Ramda? (docs) allow accessing the object safely.
??
??If you are already using a utility library, this is likely the simplest solution. If you are not using a utility library, read on!

??

Short-circuiting with &&

????One interesting fact about logical operators in JavaScript is that they don't always return a boolean. According to the spec, "the value produced by a &&? or ||? operator is not necessarily of type Boolean. The value produced will always be the value of one of the two operand expressions.”
??
????In the case of the &&? operator, the first expression will be used if it a "falsy" value. Otherwise, the second expression will be used. This means that the expression 0 && 1? will be evaluated as 0? (a falsy value), and the expression 2 && 3? will be evaluated as 3?. If multiple &&? expressions are chained together, they will evaluate to either the first falsy value or the last value. For example, 1 && 2 && 3 && null && 4? will evaluate to null?, and 1 && 2 && 3? will evaluate to 3?.

????How is this useful for safely accessing nested object properties? Logical operators in JavaScript will "short-circuit." In this case of &&?, this means that the expression will cease moving forward after it reaches its first falsy value.

??Similar to the short-circuit example above, this method works by checking if a value is falsy. If it is, it will attempt to access the next property on an empty object. In the example above, favorites.reading? is null?, so the books? property is being accessed from an empty object. This will result in an undefined?, so the 0? will likewise be accessed from an empty array.

??The advantage of this method over the &&? method is that it avoids repetition of property names. On deeper objects, this can be quite a significant advantage. The primary disadvantage would be readability — it is not a common pattern, and may take a reader a moment to parse out how it is working.?

??Unfortunately, in JavaScript, try...catch? statements are not expressions. They do not evaluate to a value as they do in some languages. This prevents a concise try? statement as a way of setting a variable.

??One option is to use a let? variable that is defined in the block above the try...catch?.

??By wrapping the access to the object in a function, you can delay the "unsafe" code and pass it into a try...catch?.

??A major advantage of this method is how natural it is to access the property. As long as properties are wrapped in a function, they are safely accessed. A default value can also be specified in the case of a non-existent path.

??Merge with a default object

??
By merging an object with a similarly shaped object of "defaults," we can ensure that the path that we are trying to access is safe.
??
??

??This pattern is common with plugins or components that accept a large settings object with included defaults.

??A bonus about this approach is that, by writing a default object, we’re including documentation on how an object should look. Unfortunately, depending on the size and shape of the data, the "merging" can be littered with copying each nested object.

???

The future: optional chaining

??There is currently a TC39 proposal for a feature called "optional chaining." This new operator would look like this:

??The ?.? operator works by short-circuiting: if the left-hand side of the ?.? operator evaluates to null? or undefined?, the entire expression will evaluate to undefined? and the right-hand side will remain unevaluated.

??To have a custom default, we can use the ||? operator in the case of an undefined.

??

console.log(favorites?.audio?.audiobooks[0] || "The Hobbit");

??

Which method should you use?

??The answer, as you might have guessed, is that age-old answer… "it depends." If the optional chaining operator has been added to the language and has the necessary browser support, it is likely the best option. If you are not from the future, however, there are more considerations to take into account. Are you using a utility library? How deeply nested is your object? Do you need to specify defaults? Different cases may warrant a different approach.