The Modern Guide For Making CSS Shapes

You have for sure googled “how to create [shape_name] with CSS” at least once in your front-end career if it’s not something you already have bookmarked. And the number of articles and demos you will find out there is endless.

Good, right? Copy that code and drop it into the ol’ stylesheet. Ship it!

The problem is that you don’t understand how the copied code works. Sure, it got the job done, but many of the most widely used CSS shape snippets are often dated and rely on things like magic numbers to get the shapes just right. So, the next time you go into the code needing to make a change to it, it either makes little sense or is inflexible to the point that you need an entirely new solution.

So, here it is, your one-stop modern guide for how to create shapes in CSS! We are going to explore the most common CSS shapes while highlighting different CSS tricks and techniques that you can easily re-purpose for any kind of shape. The goal is not to learn how to create specific shapes but rather to understand the modern tricks that allow you to create any kind of shape you want.

Table of Contents

You can jump directly to the topic you’re interested in to find relevant shapes or browse the complete list. Enjoy!

Why Not SVG?

I get asked this question often, and my answer is always the same: Use SVG if you can! I have nothing against SVG. It’s just another approach for creating shapes using another syntax with another set of considerations. If SVG was my expertise, then I would be writing about that instead!

CSS is my field of expertise, so that’s the approach we’re covering for drawing shapes with code. Choosing CSS or SVG is typically a matter of choice. There may very well be a good reason why SVG is a better fit for your specific needs.

Many times, CSS will be your best bet for decorative things or when you’re working with a specific element in the markup that contains real content to be styled. Ultimately, though, you will need to consider what your project’s requirements are and decide whether a CSS shape is really what you are looking for.

Your First Resource

Before we start digging into code, please spend a few minutes over at my CSS Shape website. You will find many examples of CSS-only shapes. This is an ever-growing collection that I regularly maintain with new shapes and techniques. Bookmark it and use it as a reference as we make our way through this guide.

Is it fairly easy to modify and tweak the CSS for those shapes?

Yes! The CSS for each and every shape is optimized to be as flexible and efficient as possible. The CSS typically targets a single HTML element to prevent you from having to touch too much markup besides dropping the element on the page. Additionally, I make liberal use of CSS variables that allow you to modify things easily for your needs.

Most of you don't have time to grasp all the techniques and tricks to create different shapes, so an online resource with ready-to-use snippets of code can be a lifesaver!

Clipping Shapes In CSS

The CSS clip-path property — and its polygon() function — is what we commonly reach for when creating CSS Shapes. Through the creation of common CSS shapes, we will learn a few tricks that can help you create other shapes easily.

Hexagons

Let’s start with one of the easiest shapes; the hexagon. We first define the shape’s dimensions, then provide the coordinates for the six points and we are done.

.hexagon {
  width: 200px;
  aspect-ratio: 0.866; 
  clip-path: polygon(
    0% 25%,
    0% 75%,
    50% 100%, 
    100% 75%, 
    100% 25%, 
    50% 0%);
}

We’re basically drawing the shape of a diamond where two of the points are set way outside the bounds of the hexagon we’re trying to make. This is perhaps the very first lesson for drawing CSS shapes: Allow yourself to think outside the box — or at least the shape’s boundaries.

Look how much simpler the code already looks:

.hexagon {
  width: 200px;
  aspect-ratio: cos(30deg); 
  clip-path: polygon(
    -50% 50%,
    50% 100%,
    150% 50%,
    50% 0
  );
}

Did you notice that I updated the aspect-ratio property in there? I’m using a trigonometric function, cos(), to replace the magic number 0.866. The exact value of the ratio is equal to cos(30deg) (or sin(60deg)). Besides, cos(30deg) is a lot easier to remember than 0.866.

Here’s something fun we can do: swap the X and Y coordinate values. In other words, let’s change the polygon() coordinates from this pattern:

clip-path: polygon(X1 Y1, X2 Y2, ..., Xn Yn)

…to this, where the Y values come before the X values:

clip-path: polygon(Y1 X1, Y2 X2, ..., Yn Xn)

What we get is a new variation of the hexagon:

I know that visualizing the shape with outside points can be somewhat difficult because we’re practically turning the concept of clipping on its head. But with some practice, you get used to this mental model and develop muscle memory for it.

Notice that the CSS is remarkably similar to what we used to create a hexagon:

.octagon {
  width: 200px;  
  aspect-ratio: 1;  
  --o: calc(50% * tan(-22.5deg));
  clip-path: polygon(
    var(--o) 50%,
    50% var(--o),
    calc(100% - var(--o)) 50%,
    50% calc(100% - var(--o))
  );
}

Except for the small trigonometric formula, the structure of the code is identical to the last hexagon shape — set the shape’s dimensions, then clip the points. And notice how I saved the math calculation as a CSS variable to avoid repeating that code.

If math isn’t really your thing — and that’s totally fine! — remember that the formulas are simply one part of the puzzle. There’s no need to go back to your high school geometry textbooks. You can always find the formulas you need for specific shapes in my online collection. Again, that collection is your first resource for creating CSS shapes!

And, of course, we can apply this shape to an <img> element as easily as we can a <div>:

It may sound impossible to make a star out of only five points, but it’s perfectly possible, and the trick is how the points inside polygon() are ordered. If we were to draw a star with pencil on paper in a single continuous line, we would follow the following order:

It’s the same way we used to draw stars as kids — and it fits perfectly in CSS with polygon()! This is another hidden trick about clip-path with polygon(), and it leads to another key lesson for drawing CSS shapes: the lines we establish can intersect. Again, we’re sort of turning a concept on its head, even if it’s a pattern we all grew up making by hand.

Here’s how those five points translate to CSS:

.star {
  width: 200px;
aspect-ratio: 1; clip-path: polygon(50% 0, /* (1) */ calc(50%*(1 + sin(.4turn))) calc(50%*(1 - cos(.4turn))), /* (2) */ calc(50%*(1 - sin(.2turn))) calc(50%*(1 - cos(.2turn))), /* (3) */ calc(50%*(1 + sin(.2turn))) calc(50%*(1 - cos(.2turn))), /* (4) */ calc(50%*(1 - sin(.4turn))) calc(50%*(1 - cos(.4turn))) /* (5) */ ); }

The funny thing is that starbursts are basically the exact same thing as polygons, just with half the points that we can move inward.

Figure 6.

I often advise people to use my online generators for shapes like these because the clip-path coordinates can get tricky to write and calculate by hand.

That said, I really believe it’s still a very good idea to understand how the coordinates are calculated and how they affect the overall shape. I have an entire article on the topic for you to learn the nuances of calculating coordinates.

Parallelograms & Trapezoids

Another common shape we always build is a rectangle shape where we have one or two slanted sides. They have a lot of names depending on the final result (e.g., parallelogram, trapezoid, skewed rectangle, and so on), but all of them are built using the same CSS technique.

First, we start by creating a basic rectangle by linking the four corner points together:

clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%)

This code produces nothing because our element is already a rectangle. Also, note that 0 and 100% are the only values we’re using.

Next, offset some values to get the shape you want. Let’s say our offset needs to be equal to 10px. If the value is 0, we update it with 10px, and if it’s 100% we update it with calc(100% - 10px). As simple as that!

But which value do I need to update and when?

Try and see! Open your browser’s developer tools and update the values in real-time to see how the shape changes, and you will understand what points you need to update. I would lie if I told you that I write all the shapes from memory without making any mistakes. In most cases, I start with the basic rectangle, and I add or update points until I get the shape I want. Try this as a small homework exercise and create the shapes in Figure 11 by yourself. You can still find all the correct code in my online collection for reference.

If you want more CSS tricks around the clip-path property, check my article “CSS Tricks To Master The clip-path Property” which is a good follow-up to this section.

Masking Shapes In CSS

We just worked with a number of shapes that required us to figure out a number of points and clip-path by plotting their coordinates in a polygon(). In this section, we will cover circular and curvy shapes while introducing the other property you will use the most when creating CSS shapes: the mask property.

Like the previous section, we will create some shapes while highlighting the main tricks you need to know. Don’t forget that the goal is not to learn how to create specific shapes but to learn the tricks that allow you to create any kind of shape.

Circles & Holes

When talking about the mask property, gradients are certain to come up. We can, for example, “cut” (but really “mask”) a circular hole out of an element with a radial-gradient:

mask: radial-gradient(50px, #0000 98%, #000);

Why aren’t we using a simple background instead? The mask property allows us more flexibility, like using any color we want and applying the effect on a variety of other elements, such as <img>. If the color and flexible utility aren’t a big deal, then you can certainly reach for the background property instead of cutting a hole.

Here’s the mask working on both a <div> and <img>:

Once again, it’s all about CSS masks and gradients. In the following articles, I provide you with examples and recipes for many different possibilities:

Be sure to make it to the end of the second article to see how this technique can be used as decorative background patterns.

This time, we are going to introduce another technique which is “composition”. It’s an operation we perform between two gradient layers. We either use mask-composite to define it, or we declare the values on the mask property.

The figure below illustrates the gradient configuration and the composition between each layer.

We start with a radial-gradient to create a full circle shape. Then we use a conic-gradient to create the shape below it. Between the two gradients, we perform an “intersect” composition to get the unclosed circle. Then we tack on two more radial gradients to the mask to get those nice rounded endpoints on the unclosed circle. This time we consider the default composition, “add”.

Gradients aren’t something new as we use them a lot with the background property but “composition” is the new concept I want you to keep in mind. It’s a very handy one that unlocks a lot of possibilities.

Ready for the CSS?

.arc {
  --b: 40px; /* border thickness */
  --a: 240deg; /* progression */
--_g:/var(--b) var(--b) radial-gradient(50% 50%,#000 98%,#0000) no-repeat; mask: top var(--_g), calc(50% + 50% * sin(var(--a))) calc(50% - 50% * cos(var(--a))) var(--_g), conic-gradient(#000 var(--a), #0000 0) intersect, radial-gradient(50% 50%, #0000 calc(100% - var(--b)), #000 0 98%, #0000) }

We could get clever and use a pseudo-element for the shape that’s positioned behind the set of panels, but that introduces more complexity and fixed values than we ought to have. Instead, we can continue using CSS masks to get the perfect shape with a minimal amount of reusable code.

It’s not really the rounded top edges that are difficult to pull off, but the bottom portion that curves inwards instead of rounding in like the top. And even then, we already know the secret sauce: using CSS masks by combining gradients that reveal just the parts we want.

We start by adding a border around the element — excluding the bottom edge — and applying a border-radius on the top-left and top-right corners.

.tab {
  --r: 40px; /* radius size */

  border: var(--r) solid #0000; /* transparent black */
  border-bottom: 0;
  border-radius: calc(2 * var(--r)) calc(2 * var(--r)) 0 0;
}

Next, we add the first mask layer. We only want to show the padding area (i.e., the red area highlighted in Figure 10).

mask: linear-gradient(#000 0 0) padding-box;

Let’s add two more gradients, both radial, to show those bottom curves.

mask: 
  radial-gradient(100% 100% at 0 0, #0000 98%, #000) 0 100% / var(--r) var(--r), 
  radial-gradient(100% 100% at 100% 0, #0000 98%, #000) 100% 100% / var(--r) var(--r), 
  linear-gradient(#000 0 0) padding-box;

Here is how the full code comes together:

.tab {
  --r: 40px; /* control the radius */

  border: var(--r) solid #0000;
  border-bottom: 0;
  border-radius: calc(2 * var(--r)) calc(2 * var(--r)) 0 0;
  mask: 
    radial-gradient(100% 100% at 0 0, #0000 98%, #000) 0 100% / var(--r) var(--r), 
    radial-gradient(100% 100% at 100% 0, #0000 98%, #000) 100% 100% / var(--r) var(--r), 
    linear-gradient(#000 0 0) padding-box;
  mask-repeat: no-repeat;
  background: linear-gradient(60deg, #BD5532, #601848) border-box;
}

As usual, all it takes is one variable to control the shape. Let’s zero-in on the border-radius declaration for a moment:

border-radius: calc(2 * var(--r)) calc(2 * var(--r)) 0 0;

Notice that the shape’s rounded top edges are equal to two times the radius (--r) value. If you’re wondering why we need a calculation here at all, it’s because we have a transparent border hanging out there, and we need to double the radius to account for it. The radius of the blue areas highlighted in Figure 13 is equal to 2 * R while the red area highlighted in the same figure is equal to 2 * R - R, or simply R.

We can actually optimize the code so that we only need two gradients — one linear and one radial — instead of three. I’ll drop that into the following demo for you to pick apart. Can you figure out how we were able to eliminate one of the gradients?

I’ll throw in two additional variations for you to investigate:

These aren’t tabs at all but tooltips! We can absolutely use the exact same masking technique we used to create the tabs for these shapes. Notice how the curves that go inward are consistent in each shape, no matter if they are positioned on the left, right, or both.

You can always find the code over at my online collection if you want to reference it.

More CSS Shapes

At this point, we’ve seen the main tricks to create CSS shapes. You will rely on mask and gradients if you have curves and rounded parts or clip-path when there are no curves. It sounds simple but there’s still more to learn, so I am going to provide a few more common shapes for you to explore.

Instead of going into a detailed explanation of the shapes in this section, I’m going to give you the recipes for how to make them and all of the ingredients you need to make it happen. In fact, I have written other articles that are directly related to everything we are about to cover and will link them up so that you have guides you can reference in your work.

Triangles

A triangle is likely the first shape that you will ever need. They’re used in lots of places, from play buttons for videos, to decorative icons in links, to active state indicators, to open/close toggles in accordions, to… the list goes on.

Creating a triangle shape is as simple as using a 3-point polygon in addition to defining the size:

.triangle {
  width: 200px;
  aspect-ratio: 1;
  clip-path: polygon(50% 0, 100% 100%, 0 100%);
}

But we can get even further by adding more points to have border-only variations:

We can cut all the corners or just specific ones. We can make circular cuts or sharp ones. We can even create an outline of the overall shape. Take a look at my online generator to play with the code, and check out my full article on the topic where I am detailing all the different cases.

Section Dividers

Speaking of visual transitions between sections, what if both sections have decorative borders that fit together like a puzzle?

I hope you see the pattern now: sometimes, we’re clipping an element or masking portions of it. The fact that we can sort of “carve” into things this way using polygon() coordinates and gradients opens up so many possibilities that would have required clever workarounds and super-specific code in years past.

See my article “How to Create a Section Divider Using CSS” on the freeCodeCamp blog for a deep dive into the concepts, which we’ve also covered here quite extensively already in earlier sections.

Floral Shapes

We’ve created circles. We’ve made wave shapes. Let’s combine those two ideas together to create floral shapes.

These shapes are pretty cool on their own. But like a few of the other shapes we’ve covered, this one works extremely well with images. If you need something fancier than the typical box, then masking the edges can come off like a custom-framed photo.

Here is a demo where I am using such shapes to create a fancy hover effect:

See the Pen Fancy Pop Out hover effect! by Temani Afif.

There’s a lot of math involved with this, specifically trigonometric functions. I have a two-part series that gets into the weeds if you’re interested in that side of things:

As always, remember that my online collection is your Number One resource for all things related to CSS shapes. The math has already been worked out for your convenience, but you also have the references you need to understand how it works under the hood.

Conclusion

I hope you see CSS Shapes differently now as a result of reading this comprehensive guide. We covered a few shapes, but really, it’s hundreds upon hundreds of shapes because you see how flexible they are to configure into a slew of variations.

At the end of the day, all of the shapes use some combination of different CSS concepts such as clipping, masking, composition, gradients, CSS variables, and so on. Not to mention a few hidden tricks like the one related to the polygon() function:

  • It accepts points outside the [0% 100%] range.
  • Switching axes is a solid approach for creating shape variations.
  • The lines we establish can intersect.

It’s not that many things, right? We looked at each of these in great detail and then whipped through the shapes to demonstrate how the concepts come together. It’s not so much about memorizing snippets than it is thoroughly understanding how CSS works and leveraging its features to produce any number of things, like shapes.

Don’t forget to bookmark my CSS Shape website and use it as a reference as well as a quick stop to get a specific shape you need for a project. I avoid re-inventing the wheel in my work, and the online collection is your wheel for snagging shapes made with pure CSS.

Please also use it as inspiration for your own shape-shifting experiments. And post a comment if you think of a shape that would be a nice addition to the collection.

References

CSS Responsive Multi-Line Ribbon Shapes (Part 2)

In my previous article, we tackled ribbons in CSS. The idea was to create a classic ribbon pattern using a single element and values that allow it to adapt to however much content it contains. We established a shape with repeating CSS gradients and tailor-cut the ribbon’s ends with clip-path() to complete the pattern, then used it and wound up with two ribbon variations: one that stacks vertically with straight strands of ribbons and another that tweaks the shape by introducing pseudo-elements.

If you are wondering why I am using 80%, then there is no particular logic to my approach. It’s because I found that covering more space with the color and leaving less space between lines produces a better result for my eye. I could have assigned variables to control the space without touching the core code, but there’s already more than enough complexity going on. So, that’s the reasoning behind the hard-coded value.

Styling The First Ribbon

We’ll start with the red ribbon from the demo. This is what we’re attempting to create:

It may look complex, but we will break it down into a combination of basic shapes.

Stacking Gradients

Let’s start with the gradient configuration, and below is the result we are aiming for. I am adding a bit of transparency to better see both gradients.

h1 {
  --c: #d81a14;

  padding-inline: .8lh;
  background:
    /* Gradient 1 */
    linear-gradient(var(--c) 80%, #0000 0) 
      0 .1lh / 100% 1lh,
    /* Gradient 2 */
    linear-gradient(90deg, color-mix(in srgb, var(--c), #000 35%) 1.6lh, #0000 0) 
      -.8lh 50% / 100% calc(100% - .3lh) repeat-x;
}

We already know all about the first gradient because we set it up in the last section. The second gradient, however, is placed behind the first one to simulate the folded part. It uses the same color variable as the first gradient, but it’s blended with black (#000) in the color-mix() function to darken it a smidge and create depth in the folds.

The thing with the second gradient is that we do not want it to reach the top and bottom of the element, which is why its height is equal to calc(100% - .3lh).

Note the use of padding in the inline direction, which is required to avoid text running into the ribbon’s folds.

Masking The Folded Parts

Now, it’s time to introduce a CSS mask. If you look closely at the design of the ribbon, you will notice that we are cutting triangular shapes from the sides.

We have applied a triangular shape on the left and right sides of the ribbon. Unlike the backgrounds, they repeat every two lines, giving us the complex repetition we want.

Imagine for a moment that those parts are transparent.

That will give us the final shape! We can do it with masks, but this time, let’s try using conic-gradient(), which is nice because it allows us to create triangular shapes. And since there’s one shape on each side, we’ll use two conical gradients — one for the left and one for the right — and repeat them in the vertical direction.


mask:
  conic-gradient(from 225deg at .9lh, #0000 25%, #000 0) 
    0 1lh / 50% 2lh repeat-y,
  conic-gradient(from 45deg at calc(100% - .9lh), #0000 25%, #000 0) 
    100% 0 / 50% 2lh repeat-y;

Each gradient covers half the width (50%) and takes up two lines of text (2lh). Also, note the 1lh offset of the first gradient, which is what allows us to alternate between the two as the ribbon adapts in size. It’s pretty much a zig-zag pattern and, guess what, I have an article that covers how to create zig-zag shapes with CSS masks. I highly recommend reading that for more context and practice applying masks with conical gradients.

Masking The Ribbon’s Ends

We are almost done! All we are missing are the ribbon’s cut edges. This is what we have so far:

We can fill that in by adding a third gradient to the mask:

mask:
  /* New gradient */
  linear-gradient(45deg, #000 50%, #0000 0) 100% .1lh / .8lh .8lh no-repeat,

  conic-gradient(from 225deg at .9lh, #0000 25%, #000 0) 
   0 1lh / 50% 2lh repeat-y,
  conic-gradient(from 45deg  at calc(100% - .9lh), #0000 25%, #000 0) 
   100% 0 / 50% 2lh repeat-y;

That linear gradient will give us the missing part at the top, but we still need to do the same at the bottom, and here, it’s a bit tricky because, unlike the top part, the bottom is not static. The cutout can be either on the left or the right based on the number of lines of text we’re working with:

We will fill in those missing parts with two more gradients. Below is a demo where I use different colors for the newly added gradients to see exactly what’s happening. Use the resize handle to see how the ribbon adjusts when the number of lines changes.

Styling The Second Ribbon

The second ribbon from the demo — the green one — is a variation of the first ribbon.

I am going a little bit faster this time around. We’re working with many of the same ideas and concepts, but you will see how relatively easy it is to create variations with this approach.

The first thing to do is to add some space on the top and bottom for the cutout part. I’m applying a transparent border for this. The thickness needs to be equal to half the height of one line (.5lh).

h1 {
  --c: #d81a14;

  border-block: .5lh solid #0000;
  padding-inline: 1lh;
  background: linear-gradient(var(--c) 80%, #0000 0) 0 .1lh / 100% 1lh padding-box;
}

Note how the background gradient is set to cover only the padding area using padding-box.

Now, unlike the first ribbon, we are going to add two more gradients for the vertical pieces that create the folded darker areas.

h1 {
  --c: #d81a14;

  border-block: .5lh solid #0000;
  padding-inline: 1lh;
  background:
    /* Gradient 1 */
    linear-gradient(var(--c) 80%, #0000 0) 0 .1lh / 100% 1lh padding-box,
    /* Gradient 2 */
    linear-gradient(#0000 50%, color-mix(in srgb, var(--c), #000 35%) 0) 
     0 0 / .8lh 2lh repeat-y border-box,
    /* Gradient 3 */
    linear-gradient(color-mix(in srgb, var(--c), #000 35%) 50%, #0000 0) 
     100% 0 / .8lh 2lh repeat-y border-box;
}

Notice how the last two gradients are set to cover the entire area with a border-box. The height of each gradient needs to equal two lines of text (2lh), while the width should be consistent with the height of each horizontal gradient. With this, we establish the folded parts of the ribbon and also prepare the code for creating the triangular cuts at the start and end of the ribbon.

Here is an interactive demo where you can resize the container to see how the gradient responds to the number of lines of text.

Applying only the conic gradients will also hide the cutout part, so I have to introduce a third gradient to make sure they remain visible:

mask:
  /* New Gradient */
  linear-gradient(#000 1lh, #0000 0) 0 -.5lh,
  /* Left Side */
  conic-gradient(from 225deg at .9lh, #0000 25%, #000 0) 
   0 1lh / 51% 2lh repeat-y padding-box,
  /* Right Side */
  conic-gradient(from 45deg at calc(100% - .9lh), #0000 25%, #000 0) 
   100% 0 / 51% 2lh repeat-y padding-box;

And the final touch is to use clip-path for the cutouts at the ends of the ribbon.

Notice how the clip-path is cutting two triangular portions from the bottom to make sure the cutout is always visible whether we have an odd or even number of lines.

This is how the final code looks when we put everything together:

h1 {
  --c: #d81a14;

  padding-inline: 1lh;
  border-block: .5lh solid #0000;
  background: 
    linear-gradient(var(--c) 80%, #0000 0)
      0 .1lh / 100% 1lh padding-box,
    linear-gradient(#0000 50%, color-mix(in srgb,var(--c), #000 35%) 0)
      0 0 / .8lh 2lh repeat-y border-box,
    linear-gradient(color-mix(in srgb, var(--c), #000 35%) 50%, #0000 0)
      100% 0 / .8lh 2lh repeat-y border-box;
  mask:
    linear-gradient(#000 1lh, #0000 0) 0 -.5lh,
    conic-gradient(from 225deg at .9lh,#0000 25%,#000 0)
     0 1lh/51% 2lh repeat-y padding-box,
    conic-gradient(from 45deg at calc(100% - .9lh), #0000 25%, #000 0)
     100% 0 / 51% 2lh repeat-y padding-box;
  clip-path: polygon(0 0, calc(100% - .8lh) 0,
    calc(100% - .4lh) .3lh,
    100% 0, 100% 100%,
    calc(100% - .4lh) calc(100% - .3lh),
    calc(100% - .8lh) 100%, .8lh 100%, .4lh calc(100% - .3lh), 0 100%);
}

I challenged you to find a way to reverse the direction of the first ribbon by adjusting the gradient values. Try to do the same thing here!

It may sound difficult. If you need a lifeline, you can get the code from my online collection, but it’s the perfect exercise to understand what we are doing. Explaining things is good, but nothing beats practicing.

The Final Demo

Here is the demo once again to see how everything comes together.

See the Pen Responsive multi-line ribbon shapes by Temani Afif.

Wrapping Up

There we go, two more ribbons that build off of the ones we created together in the first article of this brief two-part series. If there’s only one thing you take away from these articles, I hope it’s that modern CSS provides us with powerful tools that offer different, more robust approaches to things we used to do a long time ago. Ribbons are an excellent example of a long-living design pattern that’s been around long enough to demonstrate how creating them has evolved over time as new CSS features are released.

I can tell you that the two ribbons we created in this article are perhaps the most difficult shapes in my collection of ribbon shapes. But if you can wrap your head around the use of gradients — not only for backgrounds but masks and clipping paths as well — you’ll find that you can create every other ribbon in the collection without looking at my code. It’s getting over that initial hurdle that makes this sort of thing challenging.

You now have the tools to make your own ribbon patterns, too, so why not give it a try? If you do, please share them in the comments so I can see your work!

Further Reading On SmashingMag

Creating And Maintaining A Voice Of Customer Program

For those involved in digital and physical product development or leadership, consider a Voice of Customer (VoC) program. A VoC program systematically gathers and analyzes customer insights, channeling user opinions into actionable intelligence. VoC programs use surveys, analytics, interviews, and more to capture a broad range of customer sentiment. When implemented effectively, a VoC program transforms raw feedback into a roadmap for strategic decisions, product refinement, and service enhancements.

By proactively identifying issues, optimizing offerings for user satisfaction, and tailoring products to real-time demand, VoC programs keep companies ahead. Moreover, in a world of fleeting consumer loyalty, such programs build trust and enhance the overall brand experience. VoC has been a standard CX practice that UX and product teams can utilize to their advantage. We’ll focus on VoC for digital products for this article. However, the methods and lessons learned are equally applicable to those working with physical products.

Successful product teams and User Experience (UX) practitioners understand that customer feedback is invaluable. It guides decisions and fosters innovation for products and services. Whether it’s e-commerce platforms refining user interfaces based on shopper insights or social media giants adjusting algorithms in response to user sentiments, customer feedback is pivotal for digital success. Listening, understanding, and adapting to the customer’s voice are key to sustainable growth.

The role of UX research in capturing the Voice of the Customer

UX research serves as the bridge that spans the chasm between a company’s offerings and its customers’ perspectives. UX research plays a pivotal role in capturing the multifaceted VoC. Trained UX researchers transform raw feedback into actionable recommendations, guiding product development and design in a direction that resonates authentically with users.

Ultimately, UX research is the translator that converts the diverse, nuanced VoC into a coherent and actionable strategy for digital companies.

Setting Up A Voice Of Customer Program

Overview Of Steps

We’ve identified six key steps needed to establish a VoC program. At a high level, these steps are the following:

  1. Establishing program objectives and goals.
  2. Identifying the target audience and customer segments.
  3. Selecting the right research methods and tools.
  4. Developing a data collection framework.
  5. Analyzing and interpreting customer feedback.
  6. Communicating insights to stakeholders effectively.

We’ll discuss each of these steps in more detail below.

Establishing Program Objectives And Goals

Before establishing a VoC program, it’s crucial to define clear objectives and goals. Are you aiming to enhance product usability, gather insights for new feature development, or address customer service challenges? By outlining these goals, you create a roadmap that guides the entire program. You will also avoid taking on too much and maintain a focus on what is critical when you state your specific goals and objectives. Specific objectives help shape research questions, select appropriate methodologies, and ensure that the insights collected align with the strategic priorities of the company.

You should involve a diverse group of stakeholders in establishing your goals. You might have members of your product teams and leadership respond to a survey to help quantify what your team and company hope to get out of a VoC. You might also hold workshops to help gain insight into what your stakeholders consider critical for the success of your VoC. Workshops can help you identify how stakeholders might be able to assist in establishing and maintaining the VoC and create greater buy-in for the VoC from your stakeholders. People like to participate when it comes to having a say in how data will be collected and used to inform decisions. If you come up with a long list of goals that seem overwhelming, you can engage key stakeholders in a prioritization exercise to help determine which goals should be the VoC focus.

Identifying The Target Audience And Customer Segments

Once you create clear objectives and goals, defining the target audience and customer segments will be important. For example, you decide your objective is to understand conversion rates between your various customer segments. Your goal is to increase sign-up conversion. You would want to determine if your target audience should be people who have purchased within a certain time frame, people who have never made a purchase, people who have abandoned carts, or a mix of all three.

Analytics can be critical to help create shortcuts at this point. You might start by looking at analytical data collected on the sign-up page to identify age gaps to set the target audience to understand why that specific age gap(s) are not signing up, whether there is evidence certain segments are more likely to abandon carts, and which segments are less likely to visit your site at all. Then, based on these clear objectives and goals, as well as identifying a target audience and customer segment, you could select the right research method and tools to collect data from the audience segment(s) you’ve identified as critical to collect feedback from.

Selecting The Right Research Methods And Tools

The success of a VoC program hinges on the selection of appropriate research methods and tools. Depending on your objectives, you might employ a mix of quantitative methods like surveys and analytics to capture broad trends, along with qualitative methods like user interviews and usability testing to unearth nuanced insights. Utilizing digital tools and platforms can streamline data collection, aggregation, and analysis. These tools, ranging from survey platforms to sentiment analysis software, enhance efficiency and provide in-depth insights.

The key is to choose methods and tools that align with the program’s goals and allow for a holistic understanding of the customer’s voice.

Your UX researcher will be critical in helping to identify the correct methods and tools for collecting data.

For example, a company could be interested in measuring satisfaction with its current digital experience. If there are currently no metrics being captured by the company, then a mixed method approach could be used to try to understand customers’ current attitudes towards the digital experience at a large scale and then dive deeper at a smaller scale after analyzing the survey. The quantitative survey could contain traditional metrics to measure people’s feelings like Net Promoter Score (NPS), which attempts to measure customer loyalty using a single item and/or System Usability Scale (SUS), which attempts to measure system usability using a brief questionnaire, and then based on the data collected, would drive the types of questions asked in a qualitative interview.

To collect the survey information, an online survey tool could be used that can draft and calculate metric questions for you. Many tools have integrated analysis that allows users to do statistical analysis of quantitative data collected and light semantic reviews on qualitative data. You can share the survey data easily with your stakeholder groups and then shape an interview protocol that will allow you to reach out to a smaller group of users to get deeper insight into the findings from the survey.

Table 1: Commonly used UX research methods to consider as part of a VOC Program
UX Research Method Situations in which to use Type of data collected
User interviews
  • Gaining an in-depth understanding of user needs, motivations, and behaviors.
  • Uncovering hidden pain points and frustrations.
  • Generating new ideas and solutions.
Qualitative data (e.g., quotes, stories, opinions)
Surveys
  • Gathering quantitative data from a large number of users.
  • Measuring user satisfaction and attitudes.
  • Identifying trends and patterns.
Quantitative data (e.g., ratings, rankings, frequencies)
Focus groups
  • Generating a wide range of perspectives on a topic.
  • Exploring controversial or sensitive issues.
  • Gathering feedback on design concepts or prototypes.
Qualitative data (e.g., group discussions, consensus statements)
Usability testing
  • Identifying usability problems with a product or service.
  • Evaluating the effectiveness of design solutions.
  • Gathering feedback on user flows and task completion.
Qualitative and quantitative data (e.g., task completion rates, error rates, user feedback)
Analytics
  • Tracking user behavior on a website or app.
  • Identifying trends and patterns in user engagement.
  • Measuring the effectiveness of marketing campaigns.
Quantitative data (e.g., page views, time on site, conversion rates)

Developing A Data Collection Framework

Collecting feedback requires a structured approach to ensure consistency and reliability. Developing a data collection framework involves creating standardized surveys, questionnaires, and interview protocols that gather relevant information systematically. A well-designed framework ensures you capture essential data points while minimizing biases or leading questions. This framework becomes the backbone of data collection efforts, enabling robust analysis and comparison of feedback across various touchpoints and customer segments.

Your data collection framework should include the following:

  • Objectives and research questions.
  • Data sources, whether it’s surveys, user interviews, website analytics, or any other relevant means.
  • Data collection methods with an emphasis on reliability and validity.
  • A robust data management plan. This includes organizing data in a structured format, setting up appropriate storage systems, and ensuring data security and privacy compliance, especially if dealing with sensitive information.
  • Timing and frequency of data collection, as well as the duration of your study. A well-thought-out schedule ensures you gather data when it’s most relevant and over a suitable time frame.
  • A detailed data analysis plan that outlines how you will process, analyze, and draw insights from the collected data.

Analyzing And Interpreting Customer Feedback

Collecting data is only half the journey; the real value lies in analyzing and interpreting the data collected. This involves processing both quantitative data (such as survey responses) and qualitative data (such as open-ended comments). Data analysis techniques like sentiment analysis, thematic coding, and pattern recognition help distill valuable insights.

These insights unveil customer preferences, emerging trends, and pain points that might require attention. Your UX researcher(s) can take the lead, with assistance from other team members, in helping to analyze your data and interpret your findings. The interpretation phase transforms raw data into actionable recommendations, guiding decision-making for product improvements and strategic initiatives.

Communicating Insights To Stakeholders Effectively

The insights derived from a VoC program hold significance across various levels of the organization. Effectively communicating these insights to stakeholders is critical for driving change and garnering support. Presenting findings through clear, visually engaging reports and presentations helps stakeholders grasp the significance of customer feedback. Additionally, highlighting actionable recommendations and illustrating how they tie back to strategic objectives empowers decision-makers to make informed choices. Regularly updating stakeholders on progress, outcomes, and improvements reinforces the ongoing value of the VoC program and fosters a culture of customer-centricity within the organization.

Key Components Of A Successful Voice Of Customer Program

Building A Culture Of Feedback Within The Organization

A successful VoC program is rooted in an organizational culture that prioritizes feedback at all levels. This culture begins with leadership setting the example by actively seeking and valuing customer opinions. When employees perceive that feedback is not only encouraged but also acted upon, it fosters an environment of collaboration and innovation. This culture should extend across departments, from marketing to development to customer service, ensuring that every team member understands the role they play in delivering exceptional experiences. By integrating customer insights into the company’s DNA, a feedback culture reinforces the notion that everyone has a stake in the customer’s journey.

Start small and incorporate research activities into product development to start harnessing a user-centric approach. Develop reports that showcase the business purpose, findings, and recommendations that can be presented to the product development team and stakeholders, but also to other departments to show the value of VoC research. Lastly, provide opportunities to collaborate with other departments to help them incorporate VoC into their daily activities. As a result, a culture of incorporating a VoC program becomes reinforced.

There are many ways you can go about building this culture. Some specific examples we’ve used include facilitating cross-product or cross-discipline meetings to plan research and review findings, workshops bringing together stakeholders from various lines of business or roles to help shape the research agenda, and perhaps most importantly, identifying and utilizing a champion of insights to promote findings throughout the organization. Ideally, your champion would hold a position that allows them to have exposure horizontally across your business and vertically up to various key stakeholders and members of leadership. Your champion can help identify who should be attending meetings, and they can also be utilized to present findings or have one-off conversations with leadership to promote buy-in for your culture of feedback.

Implementing User-friendly Feedback Mechanisms

For a VoC program to thrive, feedback mechanisms must be accessible, intuitive, and seamless for customers. Whether it’s a user-friendly feedback form embedded within an app, a chatbot for instant assistance, or social media channels for open conversations, the channels for providing feedback should reflect the digital preferences of your audience. These mechanisms should accommodate both quantitative and qualitative inputs, enabling customers to share their experiences in a manner that suits them best. A key element here is the simplicity of the process; if users find it cumbersome or time-consuming to provide feedback, the program’s effectiveness can be compromised.

Encouraging Customer Participation And Engagement

Engaging customers is essential for gathering diverse perspectives. Incentivizing participation through rewards, gamification, or exclusive offers can increase engagement rates. Moreover, companies can foster a sense of ownership among customers by involving them in shaping future offerings. Beta testing, user panels, and co-creation sessions invite customers to actively contribute to product development, reinforcing the idea that their opinions are not only valued but directly influence the company’s direction. By making customers feel like valued collaborators, a VoC program becomes a mutually beneficial relationship.

Integrating Feedback Into The Decision-making Process

Customer feedback should not remain isolated; it needs to permeate the decision-making process across all departments. This integration demands that insights gathered through the VoC program are systematically channeled to relevant teams. Product teams can use these insights to refine features, marketers can tailor campaigns based on customer preferences, and support teams can address recurring pain points promptly. Creating feedback loops ensures that customer opinions are not only heard but also translated into tangible actions, demonstrating the organization’s commitment to iterative improvement driven by user insights.

Continuous Improvement And Iteration Of The VoC Program

A VoC program is a journey, not a destination. It requires a commitment to continuous improvement and adaptation. As customer behaviors and preferences evolve, the program must evolve in tandem. Regularly reviewing the program’s effectiveness, incorporating new data sources, and updating methodologies keep the program relevant. This also includes analyzing the program’s impact on KPIs such as customer satisfaction scores, retention rates, and revenue growth. By iterating the program itself, businesses ensure that it remains aligned with changing business goals and the ever-evolving needs of their customers.

Best Practices And Tips For An Effective VoC Program

Creating Clear And Concise Surveys And Questionnaires

The success of a VoC program often hinges on the quality of the surveys and questionnaires used to collect feedback. To ensure meaningful responses, it’s essential to design clear and concise questions that avoid ambiguity. Keep the surveys focused on specific topics to prevent respondent fatigue and make sure that the language used is easily understandable by your target audience. Utilize a mix of closed-ended (quantitative) and open-ended (qualitative) questions to capture both statistical data and rich, contextual insights. Prioritize brevity and relevance to encourage higher response rates and more accurate feedback.

Monitoring Feedback Across Multiple Channels

Customer feedback is shared through diverse channels: social media, email, app reviews, support tickets, and more. Monitoring feedback across these channels is essential for capturing a holistic view of customer sentiment. Centralize these feedback streams to ensure that no valuable insights slip through the cracks. By aggregating feedback from various sources, you can identify recurring themes and uncover emerging issues, allowing for proactive responses and continuous improvement. Note we have focused on digital products. However, if there is a physical component of your experience, such as a brick-and-mortar store, you should be collecting similar feedback from those customers in those settings.

Incorporating User Testing And Usability Studies

Incorporating user testing and usability studies is important to help evaluate an experience with users. While upfront activities like in-depth user interviews can articulate users’ desires and needs for an experience, they do not help evaluate the updated experience. Findings and recommendations from user testing and usability studies should be incorporated into development sprints or backlogs. This will ensure that the experience consistently considers and reflects the VoC.

Ensuring Privacy And Data Security In The VoC Program

As you talk to users and develop your VoC program, you will constantly be collecting data. The data that is shared in reports should always be anonymous. Additionally, creating documentation on how to collect consent and data policies will be very important. If data is not stored properly, you could face penalties and lose the trust of participants for future VoC activities.

Challenges Of Starting A Voice Of Customer Program

If you are committed to starting a VoC program from scratch and then maintaining that program, you are likely to encounter many challenges. Gaining buy-in and commitment from stakeholders is a challenge for anyone looking to establish a VoC program. You’ll need to commit to a concerted effort across various departments within an organization. Securing buy-in and commitment from key stakeholders, such as executives, managers, and employees, is crucial for its success. Without their support, the program may struggle to gain traction and achieve its goals.

Resources are always an issue, so you’ll need to work on securing adequate funding for the program. Establishing and maintaining a VoC program can be a costly endeavor. This includes the cost of software, training, and staff time. Organizations must be prepared to allocate the necessary resources to ensure the success of the program.

Allocating sufficient time and resources to collect, analyze, and act on feedback: collecting, analyzing, and acting on customer feedback can be a time-consuming process. Organizations must ensure that they have the necessary staff and resources in place to dedicate to the VoC program.

Case Study: Successful Implementation Of A VoC Program

We worked with a large US insurance company that was trying to transform its customers’ digital experience around purchasing and maintaining policies. At the start of the engagement, the client did not have a VoC program and had little experience with research. As a result, we spent a lot of time initially explaining to key stakeholders the importance and value of research and using the findings to make changes to their product as they started their digital transformation journey.

We created a slide deck and presentation outlining the key components of a VoC program, how a VoC program can be used to impact a product, methods of UX research, what type of data the methods would provide, and when to use certain methods. We also shared our recommendations based on decades of experience with similar companies. We socialized this deck through a series of group and individual meetings with key stakeholders. We had the benefit of an internal champion at the company who was able to identify and schedule time with key stakeholders. We also provided a copy of the material we’d created to socialize with people who were unable to attend our meetings or who wanted to take more time digesting information offline.

After our meetings, we fielded many questions about the process, including who would be involved, the resources required, timelines for capturing data and making recommendations, and the potential limitations of certain methods. We should have accounted for these types of questions in our initial presentation.

VoC Activity Purpose Involvement
In-Depth User Interviews One-on-one interviews that focused on identified customer’s current usages, desires, and pain points related to the current experience. Additionally, later in the product development cycle, understanding customer’s feelings towards the new product and features that should be prioritized/enhanced in future product releases. Product, sales, and marketing teams
Concept Testing One-on-one concept testing with customers to gather feedback on the high-level design concepts. Product, sales, and marketing teams
Unmoderated Concept Testing Unmoderated concept testing with customers to gather feedback on the materials provided by the business to customers. The goal was to be able to reach out to more people to increase the feedback. Product, sales, and marketing teams
Usability Testing One-on-one usability testing sessions with customers to identify behaviors, usability, uses, and challenges of the new product. Product, sales, and marketing teams
Kano Model Survey This survey is to gather customer input on features from the product backlog to help the business prioritize them for future development. Product Team
Benchmarking Survey This survey is to help understand users’ attitudes toward the digital experience that can be used to compare customers’ attitudes as enhancements are made to it. Metrics that were used include Net Promoter Score, Systematic Suability Scale, and Semantic Differential. Product, sales, and marketing teams

One large component of enhancing the customer’s digital experience was implementing a service portal. To help better understand the needs and desires of users for this service portal, we started with executing in-depth user interviews. This first VoC activity helped to show the value of VoC research to the business and how it can be used to develop a product with a user-centric approach.

Our biggest challenge during this first activity was recruiting participants. We were unable to use a third-party service to help recruit participants. As a result, we had to collect a pool of potential participants through the sales division. As mentioned before, the company didn’t have much exposure to VoC work, so while trying to execute our VoC research and implement a VoC program, any time we worked with a division in the company that hadn’t heard of VoC, we spent additional time walking through what VoC is and what we were doing. Once we explained to the sales team what we were doing, they helped with providing a list of participants for recruitment for this activity and future ones.

After we received a list of potential participants, we crafted an email with a link to a scheduling tool where potential participants could sign up for interview slots. The email would be sent through a genetic email address to over 50+ potential participants. Even though we sent multiple reminder emails to this potential list of participants, we could only gather 5–8 participants for each VoC activity.

As we conducted more VoC activities and presented our findings to larger audiences throughout the company, more divisions became interested in participating in the VoC program. For example, we conducted unmoderated concept testing for a division that was looking to redesign some PDFs. Their goal was to understand customers’ needs and preferences to drive the redesign process. Additionally, we also helped a vendor conduct usability testing for the company to understand how user-friendly an application system was. This was one way to help grow the VoC program within the company as well as their relationship with the vendor.

We needed to do more than foster a culture of gathering customer feedback. As we began to execute the VoC program more extensively within the company, we utilized methods that went beyond simply implementing feedback. These methods allowed the VoC program to continue growing autonomously.

We introduced a benchmarking survey for the new portal. This survey’s purpose was to gauge the customer experience with the new portal over time, starting even before the portal’s release. This not only served as a means to measure the customer experience as it evolved but also provided insights into the maturation of the VoC program itself.

The underlying assumption was that if the VoC program were maturing effectively, the data gathered from the customer experience benchmarking survey would indicate that customers were enjoying an improved digital experience due to changes and decisions influenced more by VoC.

Next, we focused on transferring our knowledge to the company so the VoC program could continue to mature over time without us there. From the beginning, we were transparent about our processes and the creation of material for a VoC activity. We wanted to create a collaborative environment to make sure we understand the company’s needs and questions, but also so the company could understand the process for executing a VoC activity. We accomplished this in part by involving our internal champion at the company in all of the various studies we conducted and conversations we were having with various business units.

We’d typically start with a request or hypothesis by a division of the company. For example, once the portal is launched, what are people’s opinions on the new portal, and what functionality should the business focus on? Then, we would craft draft materials of the approach and questions. In this case, we decided to execute in-depth user interviews to be able to dive deep into users’ needs, challenges, and desires.

Next, we would conduct a series of working sessions to align the questions and ensure that they still align with the company’s goals for the activity. Once we had all the materials finalized, we had them reviewed by the legal team and began to schedule and recruit participants. Lastly, we would conduct the VoC activity, synthesize the data, and create a report to present to different divisions within the company.

We started the transfer of knowledge and responsibilities to the company by slowly giving them some of these tasks related to executing a VoC activity. With each additional new task the company was in charge of, we set additional time aside to debrief and provide details on what was done well and what could be improved upon. The goal was for the individuals at the company to learn by doing and giving them incremental new tasks as they felt more comfortable. Lastly, we provided documentation to leave behind, including a help guide they could refer to when continuing to execute VoC activities.

We concluded our role managing the VoC program by handing over duties and maintenance to the internal champion who had worked with us from the beginning. We stayed engaged, offering a few hours of consulting time each month; however, we were no longer managing the program. Months later, the program is still running, with a focus on collecting feedback on updates being made to products in line with their respective roadmaps. The client has used many of the lessons we learned to continue overcoming challenges with recruiting and to effectively socialize the findings across the various teams impacted by VoC findings.

Overall, while helping to build this VoC program, we learned a lot. One of our biggest pain points was participant recruitment. The process of locating users and asking them to participate in studies was new for the company. We quickly learned that their customers didn’t have a lot of free time, and unmoderated VoC activities or surveys were ideal for the customers as they could complete them on their own time. As a result, when possible, we opted to execute a mixed-methods approach with the hope we could get more responses.

Another pain point was technology. Some of the tools we’d hoped to use were blocked by the company’s firewall, which made scheduling interviews a little more difficult. Additionally, some divisions had access to certain quantitative tools, but the licenses couldn’t easily be used across divisions, so workarounds had to be created to implement some surveys. As a result, being creative and willing to think about short-term workarounds was important when developing the VoC program.

Conclusion

Building a successful VoC program is an ongoing effort. It requires a commitment to continuously collecting, analyzing and acting on customer feedback. This can be difficult to sustain over time, as other priorities may take precedence. However, a successful VoC program is essential for any organization that is serious about improving the customer experience.

We’ve covered the importance of VoC programs for companies with digital products or services. We recommend you take the approach that makes the most sense for your team and company. We’ve provided details of starting and maintaining a VoC program, including the upfront work needed to define objectives and goals, targeting the right audience, choosing the right methods, putting this all in a framework, collecting data, data analysis, and communicating your findings effectively.

We suggest you start small and have fun growing your program. When done right, you will soon find yourself overwhelmed with requests from other stakeholders to expand your VoC to include their products or business units. Keep in mind that your ultimate goal is to create a product that resonates with users and meets their needs. A VoC program ensures you are constantly collecting relevant data and taking actionable steps to use the data to inform your product or business’s future. You can refine your VoC as you see what works well for your situation.

Additional Voice of Customer Resources

CSS Responsive Multi-Line Ribbon Shapes (Part 1)

Back in the early 2010s, it was nearly impossible to avoid ribbon shapes in web designs. It was actually back in 2010 that Chris Coyier shared a CSS snippet that I am sure has been used thousands of times over.

And for good reason: ribbons are fun and interesting to look at. They’re often used for headings, but that’s not all, of course. You’ll find corner ribbons on product cards (“Sale!”), badges with trimmed ribbon ends (“First Place!”), or even ribbons as icons for bookmarks. Ribbons are playful, wrapping around elements, adding depth and visual anchors to catch the eye’s attention.

I have created a collection of more than 100 ribbon shapes, and we are going to study a few of them in this little two-part series. The challenge is to rely on a single element to create different kinds of ribbon shapes. What we really want is to create a shape that accommodates as many lines of text as you throw at them. In other words, there is no fixed dimension or magic numbers — the shape should adapt to its content.

Here is a demo of what we are building in this first part:

Sure, this is not the exact ribbon shape we want, but all we are missing is the cutouts on the ends. The idea is to first start with this generic design and add the extra decoration as we go.

Both ribbons in the demo we looked at are built using pretty much the same exact CSS; the only differences are nuances that help differentiate them, like color and decoration. That’s my secret sauce! Most of the ribbons from my generator share a common code structure, and I merely adjust a few values to get different variations.

Let’s Start With The Gradients

Any time I hear that a component’s design needs to be repeated, I instantly think of background gradients. They are perfect for creating repeatable patterns, and they are capable of drawing lines with hard stops between colors.

We’re essentially talking about applying a background behind a text element. Each line of text gets the background and repeats for as many lines of text as there happens to be. So, the gradient needs to be as tall as one line of text. If you didn’t know it, we recently got the new line height (lh) unit in CSS that allows us to get the computed value of the element’s line-height. In our case, 1lh will always be equal to the height of one line of text, which is perfect for what we need.

Note: It appears that Safari uses the computed line height of a parent element rather than basing the lh unit on the element itself. I’ve accounted for that in the code by explicitly setting a line-height on the body element, which is the parent in our specific case. But hopefully, that will be unnecessary at some point in the future.

Let’s tackle our first gradient. It’s a rectangular shape behind the text that covers part of the line and leaves breathing space between the lines.

The gradient’s red color is set to 70% of the height, which leaves 30% of transparent color to account for the space between lines.

h1 {
  --c: #d81a14;

  background-image: linear-gradient(var(--c) 70%, #0000 0);
  background-position: 0 .15lh;
  background-size: 100% 1lh;
}

Nothing too complex, right? We've established a background gradient on an h1 element. The color is controlled with a CSS variable (--c), and we’ve sized it with the lh unit to align it with the text content.

Note that the offset (.15lh) is equal to half the space between lines. We could have used a gradient with three color values (e.g., transparent, #d81a14, and transparent), but it’s more efficient and readable to keep things to two colors and then apply an offset.

Next, we need a second gradient for the wrapped or slanted part of the ribbon. This gradient is positioned behind the first one. The following figure demonstrates this with a little opacity added to the front ribbon’s color to see the relationship better.

Here’s how I approached it:

linear-gradient(to bottom right, #0000 50%, red 0 X, #0000 0);

This time, we’re using keywords to set the gradient’s direction (to bottom right). Meanwhile, the color starts at the diagonal (50%) instead of its default 0% and should stop at a value that we’re indicating as X for a placeholder. This value is a bit tricky, so let’s get a visual that illustrates what we’re doing.

The green arrow illustrates the gradient direction, and we can see the different color stops: 50%, X, and 100%. We can apply some geometry rules to solve for X:

(X - 50%) / (100% - 50%) = 70%/100%
X = 85%

This gives us the exact point for the end of the gradient’s hard color stop. We can apply the 85% value to our gradient configuration in CSS:

h1 {
  --c: #d81a14;

  background-image: 
    linear-gradient(var(--c) 70%, #0000 0), 
    linear-gradient(to bottom left, #0000 50%, color-mix(in srgb, var(--c), #000 40%) 0 85%, #0000 0);
  background-position: 0 .15lh;
  background-size: 100% 1lh;
}

You’re probably noticing that I added the new color-mix() function to the second gradient. Why introduce it now? Because we can use it to mix the main color (#d81a14) with white or black. This allows us to get darker or lighter values of the color without having to introduce more color values and variables to the mix. It helps keep things efficient!

We have all of the coordinates we need to make our cuts using the polygon() function on the clip-path property. Coordinates are not always intuitive, but I have expanded the code and added a few comments below to help you identify some of the points from the figure.

h1 {
  --r: 10px; /* control the cutout */

  clip-path: polygon(
   0 .15lh, /* top-left corner */
   100% .15lh, /* top right corner */
   calc(100% - var(--r)) .5lh, /* top-right cutout */
   100% .85lh,
   100% calc(100% - .15lh), /* bottom-right corner  */
   0 calc(100% - .15lh), /* bottom-left corner */
   var(--r) calc(100% - .5lh), /* bottom-left cutout */
   0 calc(100% - .85lh)
  );
}

This completes the first ribbon! Now, we can wrap things up (pun intended) with the second ribbon.

The Second Ribbon

We will use both pseudo-elements to complete the shape. The idea can be broken down like this:

  1. We create two rectangles that are placed at the start and end of the ribbon.
  2. We rotate the two rectangles with an angle that we define using a new variable, --a.
  3. We apply a clip-path to create the triangle cutout and trim where the green gradient overflows the top and bottom of the shape.

First, the variables:

h1 {
  --r: 10px;  /* controls the cutout */
  --a: 20deg; /* controls the rotation */
  --s: 6em;   /* controls the size */
}

Next, we’ll apply styles to the :before and :after pseudo-elements that they share in common:

h1:before,
h1:after {
  content: "";
  position: absolute;
  height: .7lh;
  width: var(--s);
  background: color-mix(in srgb, var(--c), #000 40%);
  rotate: var(--a);
}

Then, we position each pseudo-element and make our clips:

h1:before {
  top: .15lh;
  right: 0;
  transform-origin: top right;
  clip-path: polygon(0 0, 100% 0, calc(100% - .7lh / tan(var(--a))) 100%, 0 100%, var(--r) 50%);
}

h1:after {
  bottom: .15lh;
  left: 0;
  transform-origin: bottom left;
  clip-path: polygon(calc(.7lh / tan(var(--a))) 0, 100% 0, calc(100% - var(--r)) 50%, 100% 100%, 0 100%);
}

We are almost done! We still have some unwanted overflow where the repeating gradient bleeds out of the top and bottom of the shape. Plus, we need small cutouts to match the pseudo-element’s shape.

It’s clip-path again to the rescue, this time on the main element:

clip-path: polygon(
    0 .15lh,
    calc(100% - .7lh/sin(var(--a))) .15lh,
    calc(100% - .7lh/sin(var(--a)) - 999px) calc(.15lh - 999px*tan(var(--a))),
    100% -999px,
    100% .15lh,
    calc(100% - .7lh*tan(var(--a)/2)) .85lh,
    100% 1lh,
    100% calc(100% - .15lh),
    calc(.7lh/sin(var(--a))) calc(100% - .15lh),
    calc(.7lh/sin(var(--a)) + 999px) calc(100% - .15lh + 999px*tan(var(--a))),
    0 999px,
    0 calc(100% - .15lh),
    calc(.7lh*tan(var(--a)/2)) calc(100% - .85lh),
    0 calc(100% - 1lh)
);

Ugh, looks scary! I’m taking advantage of a new set of trigonometric functions that help a bunch with the calculations but probably look foreign and confusing if you’re seeing them for the first time. There is a mathematical explanation behind each value in the snippet that I’d love to explain, but it’s long-winded. That said, I’m more than happy to explain them in greater detail if you drop me a line in the comments.

Our second ribbon is completed! Here is the full demo again with both variations.

You can still find the code within my ribbons collection, but it’s a good exercise to try writing code without. Maybe you will find a different implementation than mine and want to share it with me in the comments! In the next article of this two-part series, we will increase the complexity and produce two more interesting ribbon shapes.

Further Reading On SmashingMag

Chris Corner: The Tao of Demos¹

CodePen is a place where you can make demos. A Pen can be anything (a painting, a game, a landing page), but by and large, Pens today are demos. Why would you make a demo? Fam, why wouldn’t you make a demo? Demos are one of the most powerful tools we have as designers and developers. But the reason behind making one can be very different. That’s why it always made it hard for me to elevator pitch CodePen: I’d want to list a dozen reasons you’d want to use it, and I’d come across as unfocused. Well, too bad. I like that there is lots of reasons to use CodePen and make demos.

Let’s make an incomplete list of reasons to build a demo:

  • Prove something to yourself. You’ve seen a new library, a new web platform API, or just some kinda something you wanna try. It’s one thing to read a little something about it. That’s good, it helps you slot the [new thing] into some brain bucket where you might be able to find it later. But does it really do what you think it does? Does it? Really? Prove it with a demo.
  • Get everyone on the same page super quickly. I was just in a meeting the other day where a teammate of mine highly encouraged the meeting to start with the design comp. We actually had planned it the other way around. We wanted to go over the requirements and take a look at how the data structures were coming along, then see if the early design was on target. But after switching that up to see the design first, it immediately got everyone engaged and thinking about the same thing. How it ends up on the page is the end goal, after all. There is something about interactive visuals that gets everyone perked up. Come to that next meeting with a demo.
  • Demonstrate a concept in the most minimal way possible. If you’re trying to learn or teach something, removing the extraneous can be an effective way to focus on the core idea. Contextual demos are useful later once the ultra-focused demo has made its point. For example, see how effective Gabi’s Flexbox Playground is. After you understand that core concept, then you can move on to real-world usage.
  • Do it in a sandbox. The concept of a sandbox is usually referencing security. It’s a safe place where you can’t screw up. Cool; useful. But it also can imply that it is untethered. Like prototyping a component without the baggage of the rest of the site. Doing work free from the usual constraints can be freeing — you might think of things you wouldn’t have otherwise.
  • Help fix someone’s bug or help them fix their problem. When someone is stuck while coding, answering them with code is the best. You aren’t just running your mouth, you’re putting money in it. There is a lot of good karma to be had here — answering a question or fixing a bug with a public demo will help that person today, and more people tomorrow.
  • Whimsy. A coding job can be awfully serious. You’re doing important work. Work that affects people’s lives with things they are trying to do. You should take that seriously. But that doesn’t mean everything you do has to be so sober. Get loose. Get weird. Shake it up. Let it all hang out. Code doesn’t always have to be useful. A freaky demo might be just the catharsis the doctor ordered.
  • Build a variation on something. Great artists steal, they say. Grab it, tweak it, make it yours. Could be anything you see on the internet (I use the browser extension CSS Pro to do this sometimes: with it, you can select an element and click a button to whisk it over to CodePen with all the HTML and CSS intact). From a design systems perspective, component libraries are most useful when they have variations you can choose from that serve the needs of anyone using the system.
  • Relax. People like to knit, ya know. And crochet and cross-stitch and sew. Oh, and do crosswords and su-do-ku and that game where you spot the differences across two images. Those things require your brain to be turned on a little bit, but it’s still relaxing. It’s not weird to relax by building a little demo just for the fun of it.
  • Nothing shows you care more than a reduced test case. If you need help with code, the best thing you can possibly do is showcase your problem with as little in the way as absolutely possible. Strip away unrelated code as much as you can. Not only might you solve the problem yourself that way, anyone looking to help will be appreciative that you’re respecting their time.
  • Show off. The kids call it flexing, I think. Make something so cool people just have to stop and look. Everybody deserves a little vanity. You know plenty of people have gotten jobs based on demos they’ve made on CodePen, right? It’s true. Show the world what you can do.
  • Treat it like school. The secret goal of schools is getting you to learn how to learn. I’ll break it to you: if you’re building demos of your own volition outside of an educational context, school worked on you. Good on ya. If you wanted, you could work through a curriculum building code demos. That’s basically what coding schools are.
  • Test something. Does it work how you think it does? Is it actually possible? How do you know if it works if you don’t test it? And equally important: is it worth it?
  • Prep a hiring exercise. Live coding interviews are a little controversial, but asking someone to demonstrate that they can do the job of design and/or development during the hiring process for a job that is design and/or development is a good idea. Perhaps it’s an async thing. How much would you learn about someone if you asked them to think out the considerations around a hamburger menu? What if you asked someone to spend two hours building a tic-tac-toe game playable across two browser tabs using web sockets?
  • Write some code you know doesn’t really matter. There is a good chance you got into this field because you like code. A demo you know you’re going to throw away is proof you still like that journey. You’re a musician taking pleasure in practicing some scales.

If you look at all the wonderful demos above in this very newsletter, some of them will have been created for some of these reasons. But honestly, most demos are a little more throw-away than the ones that make it into The CodePen Spark. You’ll have your own reasons.

I was thinking about all of this after reading Andy’s The art of the demo, which starts:

Nothing enhances written content quite like a well-crafted demo, or collection of demos. They don’t have to be complex either — in fact, it’s better if they’re not complex. The most important thing is that good quality demos can help people really get what you’re trying to explain, especially if reading isn’t the most effective way for them to learn.

❤️


¹ Demtaos? Nah.

The post Chris Corner: The Tao of Demos¹ appeared first on CodePen Blog.

The Art Of Looking Back: A Critical Reflection For Individual Contributors

Have you ever looked back at your younger self and wondered, “What was I even thinking?” If you have, then you know how important it is to acknowledge change, appreciate growth, and learn from your mistakes.

Søren Kierkegaard, the first existentialist philosopher, famously wrote:

“Life can only be understood by looking backward, but it must be lived forwards.”
Søren Kierkegaard

By looking back at our past selves, we compare them not only to who we are today but to who we want to be tomorrow.

This process is called reflection.

Critical reflection is the craft of “bringing unconscious aspects of experience to conscious awareness, thereby making them available for conscious choice.” At its core, reflection focuses on challenging your takeaways from practical experiences, nudging you to explore better ways of achieving your goals.

Learning and growth are impossible without reflection. In the 1970s, David Kolb, an educational theorist, developed the “Cycle of Learning”, comprising four stages: Concrete Experience, Reflective Observation, Abstract Conceptualization, and Active Experimentation.

According to Kolb, each new experience yields learning when all of its aspects are analyzed, assessed, and challenged — in theory (through reflection and conceptualization) and in practice (through experimentation). In turn, new learning informs new experiences, therefore completing the circle: act, analyze, challenge, plan, repeat.

Reflection takes the central stage: it evaluates the outcomes of each concrete experience, informs future decisions, and leads to new discoveries. What’s more important, reflection takes every aspect of learning into consideration: from actions and feelings to thoughts and observations.

Design is, by nature, reflective. Ambiguity requires designers to be flexible and analyze the situation on the go. We need to adapt to the ever-changing environment, learn from every failure, and constantly doubt our expertise. Rephrasing Donald Schön, an American philosopher, instead of applying experience to a situation, designers should be open to the “situation’s back talk.”

On the other hand, designers often reflect almost unconsciously, and their reflections may lack structure and depth, especially at first. Reflection is the process of “thinking analytically” about all elements of your practice, but structureless reflection is not critical nor meaningful.

Luckily, a reflective framework exists to provide the necessary guidance.

Practicing Critical Reflection

In 1988, Professor Graham Gibbs published his book Learning by Doing, where he first introduced the Reflective Cycle — a framework represented by a closed loop of exercises, designed to help streamline the process of critical reflection.

In a nutshell, reflection comes down to describing the experience and your feelings towards it, analyzing your actions and thoughts, and devising an action plan. What sets it apart from a retrospective is continuity: the cycle is never complete, and every new iteration is built on the foundation of a previous one.

Imagine a situation: You are tasked with launching a survey and collecting at least 50 responses. However, a week later, you barely receive 15, despite having sent it to over a hundred people. You are angry and disappointed; your gut tells you the problem is in the research tool, and you are tempted to try again with another service.

Then, you take a deep breath and reflect on the experience.

Describe the situation

Begin by describing the situation in detail. Remember that a good description resembles a story, where every event is a consequence of past actions. Employ the “But & Therefore” rule of screenwriting and focus on drawing the connection between your actions and their outcomes.

First, provide a brief outline: What did you do, and how did it go?

Last week, I launched a research survey using Microsoft Forms, but despite my best efforts, it failed to collect a number of responses large enough to draw a meaningful conclusion. Upon analyzing the results, I noticed that a significant portion of the participants bounced from the question, which required them to choose a sector of a multi-layered pie chart.

Then, add some details: describe how you went about reaching your objective and what was your assumption at the time.

The technical limitations of Microsoft Forms made embedding a large image impossible, so I uploaded a low-resolution thumbnail and provided an external link (“Click to enlarge the image”). A large portion of participants, however, did not notice the link and couldn’t complete the task, stating that the image in the form was too small to comprehend. As a result, we have only collected 15 complete responses.

Recommendations

  • Avoid analyzing the experience at this stage. Focus on describing the situation in as many details as possible.
  • Disregard your experience and your gut urging you to solve a problem. Be patient, observant, and mindful.
  • Reflection doesn’t have to take place after the experience. In fact, you can reflect during the event or beforehand, trying to set the right expectations and plan your actions accordingly.

Describe Your Feelings

At this stage, focus on understanding your emotions before, during, and after the experience. Be mindful of the origin of your feelings and how they manifested and changed over time.

I was rather excited to see that Microsoft Forms offer a comprehensive set of survey tools. Moreover, I was captivated by the UI of the form, the option to choose a video background, and the feature that automatically calculated the time to complete the survey.

You will notice how describing your emotions helps you understand your motivations, beliefs, and convictions. In this particular example, by admitting to being enchanted by the platform’s interface, you essentially confirm that your decision was not a result of reasonable judgement or unbiased analysis.

I was somewhat disappointed to learn that I could not embed a large image, but I did not pay enough attention at the time.

This step is important: as your feelings change, so do your actions. A seemingly minor transition from “excitement” to “disappointment” is a signal that you have obviously overlooked. We will get back to it as we begin analyzing the situation.

Lastly, focus on your current state. How do you feel about your experience now when it’s over? Does any emotion stand out in particular?

I feel ashamed that I have overlooked such an obvious flaw and allowed it to impact the survey outcome.

Describing your emotions is, perhaps, the most challenging part of critical reflection. In traditional reflective practice, emotions are often excluded: we are commanded to focus on our actions, whether we are capable of acting rationally and disregard the feelings. However, in modern reflection, emotional reflection is highly encouraged.

Humans are obviously emotional beings. Our feelings determine our actions more than any facts ever could:

Our judgement is clouded by emotions, and understanding the connection between them and our actions is the key to managing our professional and personal growth.

Recommendations

  • Analyze your feelings constantly: before, during, and after the action. This will help you make better decisions, challenge your initial response, and be mindful of what drives you.
  • Don’t think you are capable of making rational decisions and don’t demand it of others, too. Emotions play an important role in decision–making, and you should strive to understand them, not obtain control over them.
  • Don’t neglect your and your team’s feelings. When reflecting on or discussing your actions, talk about how they made you feel and why.

Evaluate And Analyze

Evaluation and analysis is the most critical step of the reflective process. During this stage, you focus not only on the impact of your actions but on the root cause, challenging your beliefs, reservations, and decisions.

W3C’s guidelines for complex images require providing a long description as an alternative to displaying a complex image. Despite knowing that, I believed that providing a link to a larger image would be sufficient and that the participants would either be accessing my survey on the web or zooming in on their mobile devices.

Switching the focus from actions to the underlying motivation compliments the emotional reflection. It demonstrates the tangible impact of your feelings on your decisions: being positively overwhelmed has blinded you, and you didn’t spend enough time empathizing with your participant to predict their struggles.

Moreover, I chose an image that was quite complex and featured many layers of information. I thought providing various options would help my participants make a better-informed decision. Unfortunately, it may have contributed to causing choice overload and increasing the bounce rate.

Being critical of your beliefs is what sets reflection apart from the retelling. Things we believe in shape and define our actions — some of them stem from our experience, and others are imposed onto us by communities, groups, and leaders.

Irving Janis, an American research psychologist, in his 1972 study, introduced the term “groupthink”, an “unquestioned belief in the morality of the group and its choices.” The pressure to conform and accept the authority of the group, and the fear of rejection, make us fall victim to numerous biases and stereotypes.

Critical reflection frees us from believing the myths by doubting their validity and challenging their origins. For instance, your experience tells you that reducing the number of options reduces the choice overload as well. Critical reflection, however, nudges you to dig deeper and search for concrete evidence.

However, I am not convinced that the abundance of options led to overwhelming the participants. In fact, I managed to find some studies that point out how “more choices may instead facilitate choice and increase satisfaction.”

Recommendations

  • Learn to disregard your experience and not rely on authority when making important decisions. Plan and execute your own experiments, but be critical of the outcomes as well.
  • Research alternative theories and methods that, however unfamiliar, may provide you with a better way of achieving your goals. Don’t hesitate to get into uncharted waters.

Draw A Conclusion And Set A Goal

Summarize your reflection and highlight what you can improve. Do your best to identify various opportunities.

As a result, 85% of the participants dropped out, which severely damaged the credibility of my research. Reflecting on my emotions and actions, I conclude that providing the information in a clear and accessible format could have helped increase the response rate.
Alternatively, I could have used a different survey tool that would allow me to embed large images: however, that might require additional budget and doesn’t necessarily guarantee results.

Lastly, use your reflection to frame a SMART (Specific, Measurable, Achievable, Relevant, and Time-Bound) goal.

Only focus on goals that align with your professional and personal aspirations. Lock every goal in time and define clear and unambiguous success criteria. This will help you hold yourself accountable in the future.

As my next step, I will research alternative ways of presenting complex information that is accessible and tool-agnostic, as this will provide more flexibility, a better user experience to survey participants and ensure better survey outcomes for my future research projects. I will launch a new survey in 14 days and reflect accordingly.

At this point, you have reached the conclusion of this reflective cycle. You no longer blame the tool, nor do you feel disappointed or irate. In fact, you now have a concrete plan that will lead you to pick up a new, relevant, and valuable skill. More than that, the next time the thrill takes you, you will stop to think about whether you are making a rational decision.

Recommendations

  • SMART is a good framework, but not every goal has to fit it perfectly. Some goals may have questionable relevancy, and others may have a flexible timeline. Make sure you are confident that your goals are attainable, and constantly reflect on your progress.
  • Challenge your goals and filter out those that don’t make practical sense. Are your goals overly ambiguous? How will you know when you have achieved your goal?
Daily Reflection

Reflection guides you by helping you set clear, relevant goals and assess progress. As you learn and explore, make decisions, and overcome challenges, reflection becomes an integral part of your practice, channels your growth, and informs your plans.

Reflection spans multiple cognitive areas (“reflective domains”), from discipline and motivation to emotional management. You can analyze how you learn new things and communicate with your peers, how you control your emotions, and stay motivated. Reflecting on different aspects of your practice will help you achieve well–balanced growth.

As you collect your daily reflections, note down what they revolve around, for example, skills and knowledge, discipline, emotions, communication, meaningful growth, and learning. In time, you may notice how some domains will accumulate more entries than others, and this will signal you which areas to focus more on when moving forward.

Finally, one thing that can make a continuous, goal-driven reflective process even more effective is sharing.

Keeping a public reflective journal is a great practice. It holds you accountable, requires discipline to publish entries regularly, and demands quality reflection and impact analysis. It improves your writing and storytelling, helps you create more engaging content, and work with the audience.

Most importantly, a public reflective journal connects you with like-minded people. Sharing your growth and reflecting on your challenges is a great way to make new friends, inspire others, and find support.

Conclusion

In Plato’s “Apology,” Socrates says, “I neither know nor think I know.” In a way, that passage embodies the spirit of a reflective mindset: admitting to knowing nothing and accepting that no belief can be objectively accurate is the first step to becoming a better practitioner.

Reflection is an argument between your former and future selves: a messy continuous exercise that is not designed to provide closure, but to ask more questions and leave many open for further discussions. It is a combination of occasional revelations, uncomfortable questions, and tough challenges.

Reflection is not stepping out of your comfort zone. It is demolishing it, tearing it apart, and rebuilding it with new, better materials.

Don’t stop reflecting.

Further Reading on Smashing Magazine

Can ChatGPT Build a Useful WordPress Plugin That Actually Works?

Can AI turn non-coders into pro WordPress plugin developers? To find out, we asked ChatGPT to build a custom WordPress plugin from scratch and then had our crack team of developers examine the code…

AI is the world’s most popular two-letter word right now. With all the recent news headlines about whether AI will take our jobs away, we wanted to know… can ChatGPT actually develop a functional and useful WordPress plugin without having to write code from scratch? And does the plugin meet best coding practices?

Fortunately, WPMU DEV builds some of the most robust WordPress plugins in the world, so we have plenty of expert WordPress plugin developers who can put ChatGPT to the test.

In fact, if you take a stroll around our company’s virtual corridors, you can’t help but bump into WordPress coders and developers. It seems everyone around here can look at a WordPress plugin and see PHP the way Neo sees the Matrix’s digital rain code.

Everyone, that is …except me!

I’m just a blog writer with no coding skills and a burning desire to add “prompt engineer” to my resumé.

So, in this post, we’ll explore how to leverage the power of ChatGPT to build a simple custom WordPress plugin quickly and securely, even if you lack extensive coding skills.

We’ll do this by:

1) Asking ChatGPT to generate the code for a useful plugin that we can test, and

2) Running the code generated by ChatGPT past our team of professional plugin developers so they can review it and provide their honest feedback.

We’ll cover the following:

Exploring the Power of ChatGPT for WordPress Plugin Development

One of the reasons WordPress is the world’s most popular content management system is the software’s flexibility and extensibility to create sites that can do anything and everything using plugins.

While there are over 60,000 free WordPress plugins available covering every type of functionality you can imagine, sometimes you may need a custom solution to meet a client’s specific requirements.

If you’re not a WordPress plugin developer, then until recently, your only options were to either become a WordPress plugin developer and learn how to code a plugin from scratch or modify an existing plugin’s code, or hire a WordPress developer to do this for you.

With the explosion of new AI tools and technologies, we now have a third option, which while not necessarily promising to do things better, can help you get things done cheaper and faster (check out our recent article on ways that ChatGPT can help you as a WordPress developer).

ChatGPT, powered by OpenAI, is an advanced language model capable of generating human-like text based on given prompts. It also claims to be able to assist you in the process of building custom WordPress plugins by providing code snippets, explanations, and even recommendations for best practices.

So, we wanted to find out for ourselves if we can leverage ChatGPT to accelerate the WordPress plugin development process and ensure the security of your plugins.

To do this, we’ll ask ChatGPT to build us a simple, useful, and functional plugin.

Define Your Plugin’s Functionality

Before using ChatGPT to generate code, you need to have a clear understanding of the functionality you want your plugin to provide.

This starts by outlining the specific features, actions, or modifications you want to achieve with your plugin. This will then help you generate accurate prompts for ChatGPT and ensure that your AI-generated code aligns with your desired outcomes.

Generate Code Snippets with ChatGPT

To generate our code snippets using ChatGPT, we’ll keep things simple (and free) and use the freely available interface provided by OpenAI.

Take #1…

Note: My first attempt at using ChatGPT to create a WordPress plugin from scratch was way too ambitious. I asked ChatGPT to build me a simple WordPress time-travel dummy text generating plugin, using the prompt below…

ChatGPT - WordPress plugin creation prompt.
This prompt will surely kick ChatGPT’s WordPress plugin development tyres.

As someone who spends a lot of time writing WordPress tutorials, I often need dummy content to test plugins, create screenshots, etc.

So, I thought it would be great to model my AI-generated dummy content plugin on a plugin I often use called FakerPress.

ChatGPT immediately pushed back on my request and told me that creating a fully functional WordPress plugin with all the features I described was beyond the scope of a single response.

However, it did offer to provide me with a basic template, guide me on how to implement some of the features I mentioned, and even gave me instructions on how to create a plugin directory and file…

ChatGPT response to WordPress plugin development prompt.
Mmm…maybe my first attempt at creating a WordPress plugin was a tad too ambitious!

At first, I thought there was light at the end of the tunnel. ChatGPT output code that looked quite impressive to my untrained eyes and unskilled mind…

ChatGPT code
To someone who knows ‘zip’ about coding, the above code snippet sure looks impressive!

ChatGPT also output all the additional code required to complete the next steps.

ChatGPT - WordPress plugin development prompt.
Lacking coding skills, I relied on ChatGPT to guide me through each step of the plugin development process.

And it apologized for my lack of coding knowledge and experience…

ChatGPT- prompt
This is what happens when a dummy asks ChatGPT to create a dummy plugin.

It also guided me with step-by-step instructions, just like it had promised…

Screenshot of ChatGPT response.
I’m just gonna let ChatGPT do all the heavy lifting here…

I can’t really describe what I felt seeing ChatGPT spit out the code, but it’s probably similar to what Bill Gates must have felt when he first held a set of floppy disks containing the working code for the Windows operating system.

Set Up Your Test Site

With the plugin code generated, the next step was to set up a dummy site to test the plugin.

I quickly spun a new site in The Hub.

The Hub - Create A New Site
Let’s spin up a new test site using The Hub.

Note: If you are developing a plugin for an existing site, we recommend setting up a local development environment or a staging site to ensure that the plugin works as intended and does not conflict with other existing plugins or themes on the site.

After drumming my fingers on my desk for a few minutes, my new WordPress site was ready.

New WordPress site.
A brand new WordPress site with not much to see just yet.

With the test site set up, the next step was to upload the plugin and test to make sure it works.

Test Your Custom Plugin

I followed ChatGPT’s instructions and uploaded the plugin folder to the wp-content > plugins directory of my test site using The Hub’s file manager.

The Hub - File Manager
I could have used the Plugin uploader, but The Hub’s File Manager works just as well!

With the plugin file uploaded, I activated it inside the Plugins screen.

Plugins screen - Dummy Content Generator activation menu link.
And now for the moment we’ve all been waiting for [drumroll]…
And…I got a fatal error!

Plugins screen - Fatal error message.
What an anticlimax…the lowest point of my WordPress plugin development career!

Assuming that maybe I didn’t add the additional snippets that ChatGPT provided correctly, I went back and asked for all the code to be provided as a single file that I could simply copy and paste to overwrite and update the uploaded plugin file.

ChatGPT obliged and delivered me the complete code.

Screenshot of ChatGPT prompt and response.
Why couldn’t it just have given me the entire copy and paste code the first time?

There was a lot of code to output, so every time things came to a standstill, I instructed ChatGPT to continue the process.

Screenshot of ChatGPT prompt and response.
ChatGPT sometimes just needs a little gentle encouragement to keep going.

I repeated this process several times.

Screenshot of ChatGPT response.
Feels like ChatGPT and I are like an old married couple now…

Finally, the entire code was output. ChatGPT even provided some helpful information at the end of the process.

Screenshot of ChatGPT response.
After waiting a whole 2 minutes…ChatGPT finally output all of the plugin code!

I uploaded the plugin file with the new code to my test site, then jumped into the site’s admin area and activated the plugin.

It worked! Or so it seemed.

ChatGPT even gave the plugin its own menu item…

Dummy Content Generator WordPress plugin -created by ChatGPT
ChatGPT created all this!

And a settings screen with lots of fancy boxes and fields.

Dummy Content Generator WordPress plugin settings screen.
Looks impressive…but will it work?

My first impression was “Wow…ChatGPT created the plugin and made its own decisions about some of the plugin settings and the user interface layout and design!”

But… “does the plugin work?”

I couldn’t see a field where I could specify how many posts or pages to create, so I simply ticked some boxes, entered some values, and clicked on the “Save Changes” button to see what happens.

Dummy Content Generator WordPress plugin -created by ChatGPT
And now, for the moment of truth…

And…absolutely nothing happened!

WordPress Posts Table showing a sinlge "Hello world" post.
The newest lowest point of my WordPress plugin development career…a plugin that does nothing!

My fancy-ass plugin was all make-believe, just like one of those children’s playground spaceships that’s filled with useless knobs you can turn and buttons you can press while pretending to be flying in outer space.

So, I reached out to one of our developers via Slack and asked them to look at the plugin file.

Some of the comments that came back included the following:

  • “The plugin tries to generate random content on each and every admin page load”
  • “It calls a function dummy_content_generator_generate_content_paragraphs which, in turn, attempts to call dummy_content_generator_get_random_paragraph for every paragraph it tries to generate. The problem is that there is no dummy_content_generator_get_random_paragraph function.
  • “The page loads to a point, then it errors out because there’s more missing stuff”
  • “Other than all the mentioned issues, there’s no validation whatsoever, and no escaping of the output, which is terrible”
  • “In terms of having something as starting point, this is a very, very poor one. In terms of having a working plugin, this is nowhere near that”

After some back and forth discussions , it was deemed that there was quite a lot wrong with the plugin, that it would take a lot of work to fix it, and they suggested I scrap the project.

At this point, however, I was too invested in my mission to simply abandon everything and give up. Recalling Sir Winston Churchill’s famous words (“Fetch me another brandy” “Never give in, never give in, never, never, never…”), I decided it was time for…

Take #2…

Ok, so my first attempt to get ChatGPT to build a WordPress plugin from scratch was a dismal failure. This in itself provided some valuable insights into ChatGPT’s ability to perform complex coding tasks (or, as someone in the team put it, “you can’t code using ChatGPT without having extensive coding knowledge.”)

So, for “Take #2,” let’s reset and go again with a simpler plugin request.

Going back to the drawing board, a genius lightbulb moment lit up my mind…why not ask ChatGPT to suggest ideas for simple WordPress plugins it could then generate the code for?

So, that’s what I did. Here’s the prompt I promptly engineered…

ChatGPT prompt for generating WordPress plugin ideas.
My genius prompt.

And here’s the list of suggested plugins ChatGPT replied with…

ChatGPT - List of WordPress plugin ideas.
The blueprint of my WordPress plugin empire.

I liked the last suggestion, so I asked ChatGPT to generate the code for a functional content duplicator plugin, and it immediately went to work.

ChatGPT prompt to generate code for a WordPress plugin that duplicates content.
This is what a license to print money looks like!

ChatGPT generated the following code for the plugin in the blink of an eye:


<?php
/*
Plugin Name: Content Duplicator
Description: Allows users to duplicate posts, pages, or custom post types with one click.
Version: 1.0.0
Author: Your Name
Author URI: https://yourwebsite.com
*/

// Add duplicate action links to post row actions
function content_duplicator_add_duplicate_link( $actions, $post ) {
    if ( current_user_can( 'edit_posts' ) ) {
        $actions['duplicate'] = '<a href="' . wp_nonce_url( admin_url( 'admin-post.php?action=content_duplicator_duplicate_post&post_id=' . $post->ID ), 'content_duplicator_duplicate_post' ) . '" title="Duplicate this item" rel="permalink">Duplicate</a>';
    }
    return $actions;
}
add_filter( 'post_row_actions', 'content_duplicator_add_duplicate_link', 10, 2 );

// Duplicate the post
function content_duplicator_duplicate_post() {
    if ( isset( $_GET['post_id'] ) ) {
        $post_id = absint( $_GET['post_id'] );

        if ( current_user_can( 'edit_posts' ) && wp_verify_nonce( $_GET['_wpnonce'], 'content_duplicator_duplicate_post' ) ) {
            $post = get_post( $post_id );

            $args = array(
                'post_title' => $post->post_title . ' (Duplicate)',
                'post_content' => $post->post_content,
                'post_type' => $post->post_type,
                'post_status' => $post->post_status,
                'post_parent' => $post->post_parent,
                'post_author' => get_current_user_id(),
            );

            $new_post_id = wp_insert_post( $args );

            if ( $new_post_id ) {
                // Duplicate meta fields
                $meta_fields = get_post_custom( $post_id );
                foreach ( $meta_fields as $key => $values ) {
                    foreach ( $values as $value ) {
                        add_post_meta( $new_post_id, $key, $value );
                    }
                }

                // Redirect to the duplicated post
                wp_redirect( admin_url( 'post.php?action=edit&post=' . $new_post_id ) );
                exit;
            }
        }
    }
}
add_action( 'admin_post_content_duplicator_duplicate_post', 'content_duplicator_duplicate_post' );

It even provided a set of instructions after generating the code.

ChatGPT instructions.
The code not only comes with instructions, but ChatGPT even says “Please”!

I then went back to my test site and uploaded the new plugin file to the wp-content > plugins directory.

The Hub - File Manager
There’s a lot of action to unpack in this screenshot.

I also created a dummy post to test the plugin with…

WordPress test post.
Can the plugin duplicate this cheesy post? Let’s find out!

Here are the post’s menu options before activating the plugin…

WordPress Posts table with single post entry.
A default post with default options.

Let’s go and activate the plugin…

WordPress Plugins screen - Activate Content Duplicator
“Activate Content Duplicator” – Sounds like a line from a Star Trek movie.

Success! The plugin activated.

WordPress Plugins screen - Plugin activated message.
A gratuitous screenshot proving that the plugin successfully activated.

Now, let’s see if the plugin actually works.

Here’s my test post again. Note that the plugin has added a new “Duplicate” item to the menu after being activated.

Let’s click on “Duplicate” and see what happens…

WordPress Posts Table - Post entry with a new Duplicate item.
ChatGPT even added a “Duplicate” menu item tooltip to the code!

A duplicate post has been created…

An example of a duplicated WordPress post.
A duplicate of the original post.

Here’s the table of posts showing entries for the original and duplicated post.

WordPress Posts table with original post and duplicated post.
The original post and its cloned version.

I’m sure that with better prompt engineering and some code tweaking, the plugin could have been significantly improved. For example, I would prefer if the plugin set the post status of the newly-duplicated post to ‘draft’ instead of publishing it, but ChatGPT delivered a simple WordPress plugin that worked. It was basic, but it did the job.

All I needed to do now was to ask someone who codes WordPress plugins for a living to scrutinize the code and share how they felt about using a tool like ChatGPT to code plugins.

Feedback from Our Expert Plugin Developers

Shortly after uploading the plugin to one of our testing channels, I got pinged from one of our developers. Here are some of the key points provided in their feedback:

  • “ChatGPT et al are decent at answering questions you already know the answer to.”
  • “I also used it in the past to build a simple plugin for me, but as long as I was giving more complex prompts, it started to mess things around, so eventually I manually debug the given code to work, cause else it’d take ages for it to actually debug it line by line.”
  • “Basically, it does what it says. However, it does what it says very literally. As in, it will duplicate a post and its corresponding postmeta (custom) fields. However, it doesn’t seem like it will propagate any taxonomies to the duplicated post (the plugin should copy and assign the same taxonomy terms, such as categories or tags, from the original post to the duplicated post).”
  • If I was to be super-nitpicky about this, I’d also mention that it’s not L10n-ready – there won’t be an option to translate this plugin. But, that’s just being very nitpicky – I don’t really think, nor would I expect, a prompt-generated plugin to be generic enough to be needing something like that at all. To me, it is more of a “very specific problem domain” kind of a thing, including the language used. At the end of the day, it does do what it says it will, which is very nice.”

In addition to examining the WordPress plugin code generated by ChatGPT and providing feedback as shown above, our developers also agreed with the following key points after assessing ChatGPT’s current capabilities to generate code:

1 – It’s important to exercise caution and not blindly accept ChatGPT-generated code as correct.

  • ChatGPT can provide answers, but it’s up to the user to assess whether the answer is correct or not.
  • There is a risk of receiving incorrect or nonsensical answers from ChatGPT, both obvious and subtle.
  • It’s important to exercise caution and not blindly accept the generated code as correct.
  • ChatGPT’s output should be treated as autocomplete, making life easier but requiring caution and verification.

The ability to discern good code from bad or nonsensical code, however, implies that…

2 – Users should have sufficient knowledge and understanding of WordPress plugin development to evaluate and modify the generated code as needed.

  • ChatGPT can help simplify and expedite the process of building simple custom WordPress plugins, generate code snippets, and provide explanations. Anything more complex, however, requires plugin development experience and coding skills.
  • Using ChatGPT without sufficient knowledge can lead to negative consequences or unreliable code.
  • Relying solely on ChatGPT without critical thinking is similar to blindly copying the first answer from Stack Overflow (Stack Overflow threads provide more context, multiple answers, and feedback from other users, making it a more reliable resource, but Stack Overflow threads tend to be more generic, whereas ChatGPT can provide specific answers to individual queries).

3 – Utilizing other resources, such as WordPress documentation, tutorials, and community forums, alongside ChatGPT can provide a more comprehensive understanding of WordPress plugin development.

  • As you explore the possibilities of using ChatGPT for WordPress plugin development, it’s valuable to engage with the WordPress community. Share your experiences, ask for feedback, and seek guidance from experienced developers. Participating in forums, attending WordPress meetups, or joining online communities can provide valuable insights and help you refine your plugin development process.
  • Additionally, consider following WordPress security best practices to safeguard your website from potential vulnerabilities. ChatGPT can provide recommendations on security measures you can implement to protect your plugin.
  • It’s also essential to maintain a clear understanding of your desired functionality and thoroughly review and test the generated code and secure your custom plugins before deploying them to a production environment.

Can ChatGPT Turn You Into a WordPress Plugin Developer?

We’ve explored how you can utilize ChatGPT to build simple custom WordPress plugins quickly and securely, even if you have limited coding experience.

Beyond the core functionality of your plugin, you can also leverage ChatGPT to generate code for custom user interfaces. With its ability to understand prompts and generate HTML, CSS, and JavaScript snippets, you can create intuitive admin interfaces, front-end components, and interactive elements that will enhance the user experience and allow you to deliver a polished final product.

By utilizing the language model’s capabilities, you can generate code snippets, optimize functionality, and create user-friendly interfaces. However, it’s crucial to understand the generated code, review and refine it, and thoroughly test your custom plugins before deploying them to your live website.

The generated code may also require adjustments to align with coding standards, naming conventions, or specific project requirements. This requires a certain amount of coding knowledge and skills, as does optimizing the code and ensuring it follows WordPress coding guidelines for better maintainability.

The bottom line: AI-powered tools like ChatGP can streamline your workflow, boost productivity, and unlock new possibilities. While ChatGPT is a powerful and valuable tool to have in your arsenal, AI won’t replace skilled coders any time soon.

Interested in Developing WordPress plugins?

Check out some of our tutorials and articles on WordPress plugin development:

A Comprehensive Checklist For Running Design Workshops

The more workshops you organize, the better you realize how similar they actually are, regardless of the methodology or tool used. I would like to share a comprehensive checklist that will help you prepare for any workshop and take care of all the tiny details (i.e., enablers of the workshop’s success).

Table Of Contents

You can jump directly to the topic you’re interested in or browse through all the steps. Enjoy!

Step 1: Scoping

Problem

The better you understand a problem, the smoother your workshop will be. If you cannot name a problem, the workshop will fall apart.

What problem are you trying to solve?

Don’t skip this step while striving to come up with a robust solution. If you notice a workshop goes sideways at some point, you can always say, “Wait a minute. What problem are we trying to solve?” and refer to the agenda where it’s written.

Remember to formulate the problem negatively (risk of…, lack of…, low…, prefixes ‘un’ and ‘in’). Otherwise, it might not be a real problem but rather an attempt to push your preconceived solution.

Is a workshop the right choice?

Workshops are a tool. They require much preparation but help tackle problems where conventional methods — presentations or discussions — won’t help. Here is what workshops are capable of:

  • Align people: let them exchange opinions and reveal contradictions, not just be informed about something.
  • Reach agreements: resolve conflicts and create a joint action plan, vision, or set of principles.
  • Engage people: excite them about the topic and let them contribute.
  • Have fun: let the team also feel entertained during hard brainwork.

If you want to share fully reliable and accurate information, there is no need to arrange a workshop; a simple meeting will do.

Context

A problem doesn’t exist in a vacuum. There is a chance someone has tried to solve or investigate it before. Turn it to your advantage.

What information is already available?

You don’t want people to write known information again from scratch (it’s just wasting their time) or hear such complaints as:

  • “If only I knew you would need this, I would prepare it properly.”
  • “We already have it, and here is a Confluence link.”

What information can you prepare in advance?

Starting from an empty canvas rarely works in actual business conditions, and here is how you can prepare:

  • Partially pre-fill the workshop canvas with available facts.
  • Create a structure where participants will add their information.
  • Place essential reference information on the side of the canvas.
  • Share pre-reading with participants (documents to review, Loom videos to watch, questionnaires to fill in, and so on).

Expected Output

Planning a workshop is thinking backward. Visualize the ideal result at the beginning, and you won’t be stuck while composing its agenda.

What deliverable will you produce at the end?

Depending on the problem defined in the previous step, you may want to prepare a certain artifact after the workshop, for example:

  • Not clear what to do → an action plan;
  • Efforts without a focus → a list of priorities;
  • Dependencies and many parallel projects → a roadmap;
  • Broken processes → a guideline;
  • Many contradicting opinions → a vision document;
  • Lack of understanding → visualization or storyboard, and so on.

Note: There might be an immediate deliverable right after the workshop and a more elaborate one to be created later.

Who and how will use this deliverable?

Think of it in advance not to produce something people won’t find useful (even if it’s objectively great). A few things to consider:

  • A deliverable for workshop participants or a larger audience;
  • For regular or one-time use;
  • A final artifact or invitation to further discussion and contribution.

Expected Outcome

Apart from creating an immediate artifact, workshops are a great opportunity to make a long-lasting impact on the team.

What long-term effect can this workshop have on your team?

In other words, with what feeling should participants leave the meeting room or Zoom call? For example:

  • Higher transparency around who does what;
  • Better alignment and trust;
  • A new way of doing work and avoiding misunderstandings, and so on.

What other initiatives can build on the achievements of this workshop?

This is an optional point. If you skip it, nothing terrible will happen, but you might overlook an opportunity. Questions to answer here:

  • If this workshop goes as expected, will you want to scale the success to other teams or topics?
  • Can it serve as a role model for others? How can you show the value?
  • What can be the next workshop to build upon the current one?

Step 2: Participants

Expertise, Role, And Experience

Workshops are all about people. Even the best technique won’t help you extract good ideas from the minds of the wrong people.

What expertise do you need to be present in the workshop?

Depending on the subject, you’ll need to involve people with suitable knowledge and skills:

  • Technical (engineering, data science, quality assurance);
  • User experience (design, research, content);
  • Operations, marketing, finances, and so on.

For example, you need to involve software engineers if you’ll talk about the feasibility and business logic, you need to go for customer support and operations specialists, researchers, product managers, and designers if the topic is about user experience, or you need all of them if you are going to identify problems and search for holistic solutions.

Who exactly should participate?

When it comes to choosing particular people, you need to take into account their connection to the subject:

  • Topic promoters
    Who was involved in the workshop topic the most and consistently pushed it?
  • Domain experts
    Who is a go-to person for the topic? Is there anyone with deep knowledge of the subject? Remember: It’s often not the highest-ranking professional.
  • Local context
    If there are multiple representatives of the same role, do you want to involve people from countries where your business is in trouble or, let’s say, gets most of its profits?

Do you need decision-makers or knowledge-keepers? Or both?

There are workshops for gathering and structuring new information and workshops for decision-making with known data.

Depending on participants’ positions and experience, they can be inclined to certain workshop activities. Of course, this is quite simplified, but it narrows down to the following:

  • Hands-on folks are exposed to the topic every day and can share and structure useful information. At the same time, they may lack a bird’s-eye view of the situation.
  • Managers have a mindset of analyzing data and making decisions on the overlap of different factors and limitations. However, they might not be the best source of precise information.

Do you need a co-facilitator, and who can it be?

If your team is large and enthusiastic about workshopping, you might need help with facilitation. Below are several examples of what a co-facilitator can help you with, depending on the workshop format.

Online workshops:

  • Help the participants, who were disconnected, to rejoin the call;
  • Move disordered sticky notes into respective canvas cells;
  • Remove accidentally created elements, fix mistakenly unlocked and moved canvas parts, and so on.

Offline workshops:

  • Put sticky notes that fell on the floor back into place;
  • Quietly instruct participants who came late and missed the beginning;
  • Gently invite not very active participants to the conversation, and so on.

An optimal co-facilitator is a trusted team member who has participated in workshops before, understands workshop basics well, and is proficient with the tool (if you collaborate online).

Team Dynamics

Do not only participants matter but also the way they interact with each other.

How many representatives of one role or function do you need? How will you ensure equality?

When the list of expertise to involve is ready, it’s time to think about the number of people from each side. In a narrowly focused workshop, one expertise can prevail (for example, three-fourths of engineers for a technical feasibility workshop or more than half of product managers for product roadmap alignment).

But if the workshop goal is to co-create a new solution to a large problem, you’ll need an equal representation of product management, design, UX research, software engineering, operations, marketing, customer support, data analysis, and others.

Will you collaborate all together or split into working groups?

Efficient collaboration where everyone contributes equally is possible in groups of 3–5 people. When the team is larger, you’ll need to split it into workgroups. You can organize their work in two ways:

  1. Each group works on a separate aspect of a large problem.
  2. Groups work on the same problem in parallel and then exchange findings and find overlaps.

The number of workgroups also depends on how many representatives of essential roles participate in the workshop. For example, if one of the exercises is sketching potential solutions, it would be helpful to have at least one designer in each workgroup. If the exercise is about mapping pain points on a journey map, having at least one customer support or operations specialist makes sense.

Step 3: Format
When the problem and relevant people are chosen, it’s time to think about how to use everyone’s time in the most productive way possible.

✅ Format: online, offline, or hybrid? Several hours or full-day?

Many factors impact the format choice, for instance:

  • Topic complexity → how much time will you need for it?
  • Team location → can people gather offline without much traveling?
  • Secondary goals → what about an informal part (party) afterward?
  • Team size → how much space do you need?

And here are the main workshop formats:

  • Online works the best when it’s shorter than 3–4 hours; otherwise, people will experience ‘Zoom fatigue’ and be quickly distracted. Complex topics can be split into several sessions instead of working non-stop the whole day.
  • Offline is preferable for all workshop types by default if the team is already collocated, or you can bring them together without too much cost and effort. Such a workshop can be up to several days long and combined with entertainment after work.
  • Hybrid is a rare and fragile format. It works fine when participants are equally distributed and fails when, let’s say, ten people are sitting in the room, and two persons join remotely. In this case, remote team members will eventually drop off the call or make an extra effort to stay as engaged as their colleagues in the room.

✅ A digital tool or paper canvas?

The format influences other workshop parameters, including how people accomplish exercises.

  • Digital tools are a great option for both offline and online workshops. The choice of a particular tool (FigJam, Miro, Mural, Whimsical, and so on) depends on team members’ skills. It’s always better to pick something they’ve already used or just something as simple as possible. It’ll save you quite a few precious workshop minutes.
  • Paper canvases and colored sticky notes are a good match for offline workshops or non-tech-savvy participants. They give a feeling of more valuable contribution and collaboration, but after the workshop, you’ll need to decode and digitize all the handwritten notes.

✅ How to inform people so that they attend and know what to do?

Satisfaction is a matter of expectations. That’s why it’s essential to set the right expectations. If team members lack awareness and alignment, even a perfect workshop is destined to fail before it even starts.

The best approach is to blend in and use channels that people check regularly. If people don’t read emails, don’t spend time on them and better write a Slack message or compose a Google document.

Information to share in advance:

  • Agenda: with all the major stages, coffee breaks, and lunch (no need to describe exercises in detail);
  • Tool-related instructions: if this is a new tool or if not everyone knows how to work with it;
  • Questions or topics: to think about in advance if you expect participants to have certain data at hand or think in a specific direction.

Step 4: Agenda

Workshop Activities

This part will take most of your time to prepare, but is the easiest one if you’ve checked all the boxes above.

What will participants do in the workshop?

Formulate the plan on a high-level first, without diving into individual exercises. Try to fit it into one sentence.

It may sound like this: “Vote for the previously discovered (pre-filled) UX issues, take three top-voted items for brainstorming, generate solution ideas, vote for the strongest ideas, make sketches of the top-voted ideas, vote for the most promising sketches for further prototyping.”

Another example: “Write feedback about team collaboration in a matrix, group positive and negative feedback pieces by topics, rank the issues from minor to major ones, and assign people to solve five major issues.”

Note: Feel free to check a comprehensive anatomy of design workshops where I dwell on typical ‘atomic’ activities all workshops consist of.

✅ What will each exercise be like?

When the general plan is ready, it’s time to think about the way you’ll organize each exercise, for example:

  • How many sticky notes per participant will you give?
  • Do you need to compose input examples so that people submit the information in a proper format?
  • How many votes will you assign to participants? In what order should they vote (if offline)? And so on.

How much time will participants need for each exercise?

Usually, the simplest exercise requires 15–20 minutes, while more sophisticated ones may take 40–50 minutes. And don’t forget to include a few minutes to instruct the participants before each activity and summarize their contributions afterward. Besides, you cannot immediately dive into the workshop since people are a few minutes late and need to switch to a workshopping mood.

However, every team has its own dynamics, that’s why accurate estimations are possible only after you conduct a couple of workshops and witness your teammates’ actual speed and engagement.

Will you need to break the ice at the beginning and keep people energized during the workshop?

Unlike the main workshop activities, these ones play an auxiliary role and help you keep the team spirit high:

  • Ice-breaker or check-in: a fun exercise to fill the time while you are waiting for participants, help people get acquainted with each other, or create a creative mood in the team.
  • Warm-up or energizer: an intermediate short exercise between the sessions of concentrated work. Usually, it’s a physical activity like jumping or stretching.

Structure

When you know what you need from people, you can pack it into a visual structure to help everyone grasp the topic.

Input: where will participants get the information for each exercise?

Every exercise should be based on the previous one. You need to check if there are no logical gaps between the workshop stages.

For example, if you want people to vote for the most critical problems with your digital product, you must get a list of problems first. In this case, ask people to write them down in the previous exercise or add them to the canvas yourself in advance.

Output: how will each exercise feed into the next one?

This is just a double-check that each next exercise will build upon the findings from the previous one. If the information from a previous step doesn’t go anywhere further, the team will be naturally curious about why you even asked them to share this data.

Canvas: what visual structure will help the team to contribute?

Information is clearer and more useful when it’s structured and visualized. Besides, participants can submit better data if you guide them with a relevant canvas. Depending on the workshop goal and expected outcome, you might choose one of the following structures:

  • User journeys, storyboards, and project timelines: for time-based information like stories and processes;
  • Matrices or ratings: for priorities and comparison;
  • Tables, blueprints, and maps: for interconnected data;
  • Mindmaps: for hierarchies, and so on.

Step 5: Risk Management

Prevention is always better than a cure. When you involve many people in any activity, there will always be an element of ‘chaos’. But, like a seasoned standup comedian always finds a way out when they forget a joke or when a microphone stops working, the workshop facilitator should also have a trump up their sleeve just in case.

Workshops have many ‘moving parts’, which means things may not go as expected. So, having Plan B (and C, and D) is indispensable.

⚠️ Some people are skeptical and resist the workshop idea.

How to prevent it:

  • One-on-one interviews with skeptics before the workshop to learn more about their concerns and expectations.
  • A survey about expectations from the workshop and urgent needs.
  • Asking about participants’ expectations at the beginning of the workshop and then comparing them to the actual result at the end.
  • Sharing stories about similar successful workshops conducted with other teams in the company earlier.

When it has happened:

  • Figure out what the actual concern is and adjust if it makes sense.
  • Invite the workshop ‘sponsor’ (usually a domain, product, or discipline manager) to explain to a skeptical participant why this activity is crucial and how the team will benefit from it.
  • Ask people to stick to the agreed agenda since they received it in advance, and thus could’ve shared their concerns before the event instead of disrupting it.

⚠️ You are about to exceed the planned timeline.

How to prevent it:

  • Don’t let people discuss random things; politely interrupt and remind them about the workshop goal.
  • Use a ‘parking lot’ for spin-off topics and questions. Don’t dismiss ideas but keep people focused on the subject.
  • Split a large team into working groups where everyone can contribute more efficiently without long debates.
  • Analyze previous workshops and how long it actually took people to do corresponding activities.

When it has happened:

  • Skip or shorten inessential workshop steps (if you have any).
  • Predict the delay at least half an hour before the planned finish and openly announce it.
  • Properly finish the last exercise, summarize intermediate results, and announce another session for later.

⚠️ The online workshop tool stopped working.

How to prevent it:

  • Use a reliable and robust tool that has good reviews.
  • If participants have non-standard software or hardware setup, share a test canvas in advance so that they can try the tool and tell you if everything works fine.

When it has happened:

  • Wrap up the last step and schedule the workshop continuation.
  • If the remaining exercise is simple, create an empty canvas in another tool and continue there.

⚠️ The key people are absent.

How to prevent it:

  • Talk with all the major stakeholders one-on-one in advance and make sure they understand why their participation is important.
  • Choose the time slot when the key participants can attend.
  • Reach out to people who haven’t confirmed the appointment in their calendars and ask them to respond.

When it has happened:

  • If you receive last-minute appointment rejections, reschedule the workshop as soon as possible.
  • If the key people didn’t show up without any notification, reschedule the workshop or conduct a simplified session to gather preliminary information from the available people.

⚠️ In the middle of a workshop, you suddenly discover the actual hidden problem that changes your initial plan.

How to prevent it:

  • Choose a significant problem that people would have tried to solve anyway — with or without a workshop.
  • Confirm the workshop topic and format with the key stakeholders.
  • Send the workshop goal and agenda via a channel where participants will notice and, if needed, challenge it.
  • Justify the presence of each participant; be able to explain to yourself and others why these people are invited and others aren’t.

When it has happened:

  • Wrap up the workshop and agree to organize another, more relevant team activity soon.
  • Repurpose the existing workshop if you can use the same canvas and all the participants have sufficient expertise to work on the new problem.

⚠️ People get distracted during the workshop.

How to prevent it:

  • Don’t leave the team unattended for too long: even if the exercise needs a lot of time, split it into parts and check the progress, for example, every 20 minutes.
  • Include a reasonable dose of fun and humor in exercises.
  • Create a space for random stuff and jokes so that enthusiastic people can express themselves in a non-intrusive way.
  • Set the right expectations: participants shouldn’t think they’ll be able to multitask during the workshop. If someone has an urgent task, let them leave the room/call and turn back later.

When it has happened:

  • Conduct an energizer/warm-up activity or give people a short break.
  • Gently remind the team that you should achieve a certain result and that you don’t want to finish late or schedule another session, thus using everyone’s time inefficiently.

Summary

Design workshops are no rocket science. In the ideal world, there would probably be no workshops: people would just have common sense by default. Maybe, that’s why small, close-knit teams usually don’t need special workshop preparations because they share the same values and already speak the same language.

However, in most cases, workshops are inevitable. They help build trust if it doesn’t appear between team members naturally and align people of different backgrounds who haven’t collaborated a lot before. That’s why you should plan workshops with people in mind. And if I had to choose one point that contributes to the workshop’s success the most, it’s involving the right people.

Meanwhile, here is a short version of the checklist:

Problem framing

  • Choose the topic and formulate the problem.
  • Check if a workshop will be the right tool to use.
  • Analyze already available information on the subject.
  • Prepare known information for the workshop.
  • Decide what deliverable you’ll produce as a result of the workshop.
  • Think of making the future deliverable relevant and valuable for the team.
  • Envision the long-term effect of this workshop on your team.
  • Think of other initiatives that can build on the achievements of this workshop.

Participant selection

  • Define the expertise you need to be present in the workshop.
  • Think of the potential participants based on their knowledge and experience.
  • Invite decision-makers and knowledge-keepers according to the workshop goal.
  • If you’ll need help, assign a co-facilitator.
  • Ensure equality by inviting a certain number of representatives of each role or function.
  • If your team is large, plan how to create constructive group dynamics.

Workshop format

  • Define the optimal format and timeline based on the problem scale and team availability.
  • Choose a digital workshopping tool if you decide to work paperlessly.
  • Find an optimal way to invite people and set their expectations from the workshop.

Agenda and canvas

  • Formulate the general workshop outline in one or two sentences.
  • Plan the details of each exercise based on the workshop goals.
  • Allocate time for all exercises and auxiliary activities.
  • Prepare ice-breakers and energy boosters, just in case.
  • Make sure each exercise will be based on the results of the previous one.
  • Identify how each exercise will lead to the next one.
  • Choose or compose a canvas — a visual structure to organize information from the team.

Risk management: what can go wrong

  • Some people are skeptical and resist the workshop idea.
  • You are about to exceed the planned timeline.
  • The online workshop tool stopped working.
  • The key people are absent.
  • In the middle of a workshop, you suddenly discover the actual hidden problem that changes your initial plan.
  • People get distracted during the workshop.

Typographic Hierarchies

Simply defined, the concept of typographic hierarchies refers to the visual organization of content in terms of their relative importance. In other words, the manner in which we organize the text, the headers, the subheaders, the columns, the paragraphs, the callouts, and others on the page or space signify their importance.

That sounds easy enough, right? Yes, it does. The problem is that visually accomplishing this is more challenging than it sounds, especially for those unfamiliar with the nuances of typography. Everything in typography behaves like a domino effect causing a chain reaction of changes by the designer. That is why when a client asks for a “small change,” it is never small and never linear. Typography is symbiotic. Each element contributes to the other, even in a very small way.

These two words: typographic and hierarchies are not familiar concepts to those outside our field. In fact, even in the art and design field, fellow artists do not necessarily understand typographic hierarchy. The term typographic refers to matters related to typography: type choice, sizes, weights, how far or close we set the letters, and others. The term hierarchy refers to levels of priority or importance: what comes first, second, and third. Thus, when these two terms are put together, we mean to arrange content in levels of importance with the intention of communicating to the reader.

Choosing typefaces, arranging content in terms of visual importance, and organizing elements (title, subtitles, body copy, images, space, and so on) on the page evoke responses from the reader. When things are in competition on a page, we might feel confused. We all have a sense of it, and we can even recall moments of disgust when we see a printed note with bloody type or a website in which the typography is all jumbled up. However, learning to use typography is elusive. It is a matter of constant practice and honing visual acumen.

While it is true that the advent of the computer to our field has expedited the design and printing process, it is also true that typographic proportions do not look the same when looking at things online versus printing. The relationship between the reader and their monitor differs from the relationship between the reader and anything printed, whether hand-held or seen at a distance.

To provide an example, let me share my experience with typography. Before becoming a designer, I graduated with a BA in Art Education. I understood color, research, composition, contrast, drawing, images, sketching, painting, and so on. When I went back to school to study design and specifically graphic design, I was lost.

My biggest challenge was that I could not see the letters as something other than the semantic symbols of language. Questions constantly flooded my mind. For instance, “What do you mean that the letters have a grid? What do you mean I am doing too much? And what is too much? How is this too big?” The questions were endless and excruciating. My beginner’s typography was, to put it mildly, a prime example of what not to do. I did not know any better, but I also did not understand any better.

My “aha” moment came when another instructor explained to me that typography was like auditioning for a part in a play that I wanted really badly. She suggested that I enunciate the words as if I was playing in the theater. Mind you, I had no experience in theater whatsoever but somehow, the idea resonated with me. It was then that I realized, in a very experiential way, that typography was the spoken language in visual form.

That, somehow, the letters, words, titles, typeface choices, size, weight, color, spacing — all conspired together to emanate a visual language. The page was the stage. The letters, words, titles, paragraphs, and so on were performers on that stage. Another instructor used to say that the typographic hierarchy was like a ballet company where only one was the prima ballerina, and everything else bowed to her. Having a cultural background where music and dance were vital, I started to get the idea.

After I made it into graduate school, my exploration of typography intensified, leading to my thesis work. My graduate thesis combined two things that were special to me: dance, specifically ballroom dancing, and my newfound love for typography. To develop a body of work for my thesis, I used one of my classes’ undergraduate projects — Typographic Hierarchies. Since then, I have been teaching typography and hierarchy using this project.

The typographic hierarchies project is based on two books by professor Rob Carter from Virginia Commonwealth University. These books are Typographic Design: Form and Communication and Experimental Typography. The latter is out of print now. The objective of the project is to isolate six basic variables to establish a typographic hierarchy. These variables are:

  • Proximity or space,
  • Weight,
  • Size,
  • Size and weight,
  • Color,
  • Visual punctuation.

When we look at a typographic composition, a poster, a brochure, or a web page, what we see is the application of these variables together. We don’t often think of dissecting the composition in front of us to say, “How many sizes are there?” Even as designers, we are not accustomed to dissecting design work. Imagine a non-designer, even less, right? Yet, when we come to school or start as designers, we are all non-designers and need to retrain our brains to look at content as a relationship of shapes in a context, format, or space.

In this article, we will discuss the variables mentioned above, learn how to look at each differently, and in turn, design pieces by intentionally modifying each variable to create a typographic hierarchy effectively. Let’s get started with proximity or space.

Note: These are studies done in a university class intended to expose the students to a series of compositional exercises. These exercises will provide students with a skill set to innovate and push the boundaries when appropriate. It will also help them to acquire a good understanding of compositional parameters. Therefore, use your discernment and consider the project’s needs when applying and/or breaking these principles and variables.

Proximity Or Space

This variable requires us to briefly discuss the grid. The grid is an underlying tool that helps us organize elements on a page. It is so foundational that there are books dedicated to it. For example, the book by designer and design educator Timothy Samara, titled Making and Breaking the Grid is one of the most eloquent discussions of it.

A Short Discussion About The Grid

A grid is simply an underlying structure used to organize elements in a context. This context can be a page, printed or web, an app, a brochure, a poster, a book, a newspaper, a building, furniture, and so on. Though this article is not a study of the grid, it is important to understand that the variables we will learn work within a grid. A grid allows us to break up the space into modules or smaller chunks like pieces in a puzzle that must come together to create the bigger picture. There are usually two ways to approach the application of a grid: predetermined or improvisational (also known as a visual or linear association).

Predetermined Grid

A predetermined grid is the division of the space into a certain amount of columns. There is even a one-column grid, also commonly called a manuscript grid (commonly seen in wedding invites and perhaps the first page of an article in a magazine).

We can keep adding columns to our grids and have two, three, four, five, and sometimes more. Software such as Adobe InDesign, Affinity Publisher, and others come equipped with the ability to determine what type of grid we want to use. It is usually easy to spot the grid used in a design piece. For example, if we look at a web page, we can usually spot the type of grid used — two, three, or four columns.

Perhaps the best examples of predetermined grids come from Modernist design and the Swiss Typography schools of thought.

Later on, Post Modern typography came along. Characterized by the juxtaposition of graphic elements, typography, and page use in a more organic way, it sought to find alternative typographic organizational arrangements. John Choi, a former student at NYUAD, wrote on the blog NYUAD Types of Art the following:

“Postmodern typography would be born out of the rejection of the modernist idea that certain forms, due to their inherent characteristics, are able to perform certain objective functions such as neutrality or legibility.”

As a result, the grid became a more organic and playful tool.

Improvisational Grid

Alternatively to a predetermined grid, an improvisational grid can be used. An improvisational grid is created when we lay down one element, perhaps in a very large size, and use it to extend its lines to organize elements around it. Thus, visual alignments or associations are emphasized or highlighted by placing elements following invisible lines emanating from them. For example, the image below does not feature the traditional vertical and horizontal modules that are common on a column grid. The image and the pattern created for the Evince Diagnostics logo at the top are the foundation for the organization of the type on the banner.

It is one of the funniest ways to create hierarchy because it allows for playful and unexpected results. However, it calls for attention to detail and sensitivity to the composition as a whole. Thus, it is both easy and difficult to master. It is frequently achieved by a large letter, but it can also be done with images or graphics.

Now that we have a basic understanding of the grid, let’s discuss our first variable or principle of hierarchy — proximity — in more detail.

Proximity

Proximity refers to the relative distance between elements, right? An easy metaphor is to think of friends, close friends, and strangers. The closer the friend, the closer the distance. The stranger the person, the farthest we stand from them. Our proximity or space shrinks or grows depending on our familiarity with things or people. Because it is usually easier for the students to refer to it as space, we will refer to proximity as space throughout the article.

When we discuss or think of space in a typographic hierarchy, we refer to things like space between letters, words, titles, paragraphs, margins, and how and where we place elements on the page.

In order to really understand proximity or space, we need to set some limits:

  • All type has to be 8-12 point size depending on the typeface;
  • It all has to be one size (even the titles);
  • No color;
  • A grid should be used from two to five columns, or an improvisational grid can be used. Please note that though we discussed the use of an improvisational grid based on size, when we leave elements at the same size, an improvisational grid can be used based on space or alignments.

The goal of this variable is to explore only the distance between any elements we choose and where we place our paragraphs and titles. You might be wondering, “how does space work in relation to typographic hierarchies? To answer this question, we will discuss some examples.

In the example above, we have a set of instructions, How to Fold a Crane, written by Chrissy Pk. As we can see, the columns of text are diagonally arranged. The grid, then, has been set before any other element has been placed on the page. By using diagonals, we create a sense of movement and energy around the composition.

Repetition of the title has been applied to create a sense of framing the page, and it serves to anchor the eye. Otherwise, we might feel that our eyes want to wander away from the page. Having the title repeated creates a kind of loop around the page and helps us keep our eyes contained. The type size is all consistent. The sense of movement and hierarchy comes from the title set in uppercase. To indicate each new step, instead of numbers or bullets, space and upper case letters in the first three words of the sentence are used.

Below are two analyses of the grid. The first one lets us see that the designer has probably divided the page into a four-column grid. In the second example, we can see that the diagonal grid has been applied over the four-column one.

To summarize what we see in this example:

  • We can use diagonal columns in place of vertical columns.
  • We can use uppercase to create a sense of hierarchy.
  • We can add spaces between items that follow a sequence instead of numbers or bullets.
  • We can repeat one element as long as it supports the purpose and conceptually keeps our eyes and mind focused on the subject.

In my experience, my students find that thinking of only the space or proximity is the hardest aspect of this study. But it is all about looking at the paragraphs, sentences, columns, and pages as shapes. If we think of each component in the example above as only shapes, we will see something like this below:

The page, space, and background, whether two or three-dimensional, is a shape. It can be a rectangle in portrait or landscape orientation or something more circular, or something organic like the shape of a guitar like this one titled MTV Unplugged, First Edition by Sarah Maralkey published in 1995:

The text in one of the spreads follows the gentle curve of the book:

If we consider the area we are using to organize our design as a shape, then the rest is a matter of subdividing that space in interesting ways. Thus, we always need to take the format into consideration.

Here is an interesting example of how to use a simple two-column grid:

As we move forward to the next variables, it is essential to note that how we treat the space will continue to be something we experiment with. We do not leave it behind. Let’s see how only changing the weight (bold versus regular + space) changes things around.

Weight

Weight refers to changes in the typeface as bold, regular, italic, heavy, medium, and so on. In this variable, we keep the sizes all even. In other words, we do not change the size at all.

It is worth mentioning that a typeface with no weight options will not be helpful in our exploration, as well as funky or heavily ornamental typefaces. Those are great for one instance or for display purposes such as a poster. However, in creating a hierarchy, it is best to stick to typefaces with well-proportioned shapes and multiple font options in their family.

In the image above, the layout is more traditional — a two-column grid with the text aligned to the left. The bold weight is used on the word Fold on the title and in the rest of the content each time the word Fold is used. This visual detail helps with establishing a conceptual and visual connection as well as a hierarchy. It is a visual reminder that these instructions are about learning to fold a crane.

In the following example, we have a much less traditional layout. The designer used a circular grid to subdivide the format or the space in the composition. The bold weight is more delicate here since the typeface is also more delicate. The text’s organization resembles a clock. The design requires more participation from the reader as they would need to turn the page around.

In addition to our first summary, we can add the following:

  • We can use organic shapes to subdivide the format.
  • We can follow a logical system to establish a visual hierarchy: bold a word and consistently bold the same word throughout the text.

Now, let’s move on to applying size but without weight.

Size

We understand that size refers to, well, sizes. How large or small the font used is displayed. For the purposes of this exercise, we will limit ourselves to three sizes, and we will refer to them in categories:

  • Body copy
    Depending on the typefaces’ x-height, anywhere from 8 points to 12. Never over 12.
  • Titles
    Here you can have some fun and play with contrast — very, very large. Anything over 14 points is considered a display, but you will find that it is still too small to make an impact.
  • Subheaders or accents
    Depending on what sizes you are using for the titles, you can select something in between the body copy size and the titles.

Something worth mentioning: these parameters are not solely mathematical. There is much to learn about how things look (regardless of size) once something is printed.

Along those lines, let’s discuss a note about titles. The best way to think of titles is to see them as a group of little cousins or a group of best friends who are really tight. The spaces (again, proximity) you create between each word on the title affect how the title is seen. In other words, do the words go together? If so, should there be a gap? This will become more clear in the discussion of the examples below:

We can see how the designer decided to create a sense of upward direction by setting the title along the column pointing towards the beginning of the text. The designer not only used size to create emphasis on the word CRANE but cleverly led the reader to the top. The rest is pretty straightforward, as we can see — using bullet points and space between the steps to conform to the sequential nature of the content.

Here we have three sizes used following the expected pattern (title, numbers to indicate sequence, and the text). But, notice how the numbers are not the same size as the text. They are a size in between the title and the text, indicating read the title first and then read in order.

In addition to the items we have added to our summary, we can add the following:

  • We can set one word of the title much larger than the rest.
  • We can direct the reader with the title to the beginning of the content by setting the title in an upwards orientation.
  • We can set numbers slightly larger than the text to indicate the reading order.

Now we will discuss variables in combination.

Size And Weight

We start here by combining two variables and still using proximity to create a hierarchy. We are still limiting ourselves to three size changes. In terms of weight, we can change the weight of words we think need to be seen but are not as important as the title or things like that. We can certainly make a word very large and bold. But, as you are experimenting, keep an eye on the balance of the page. Are things too heavy on one side? Is the page too busy on one side versus the other?

Size and weight experimentation also allow you to start playing with an improvisational grid. When making a letter or word really large, you may use it to establish visual alignments from it.

The example below is a page from a calendar I designed last Christmas holiday. Calendars are a great playground to explore sizes and weights. In this instance, I opted for the number of the month, the largest element on the page, while also increasing its weight, but right under the name — April — is very light or thin, creating a nice contrast between the two. The year is smaller but bold, as bold as the number above it. Though the contrast is sharp, the three pieces together create a nice typographic unit working together to create the focal point of the piece. The right side is the list of the month’s dates in bold. The holidays are stated in lightweight.

Of particular note is that if you notice, the words April and 2022 are tucked in under the vertical line of the number. This typeface has serifs (the little eyelashes at the bottom of the number). I aligned the two words under the number within its serifs. By doing this, I reinforce the visual alignment and implied vertical lines of the number.

In addition to the items we have added to our summary, we can add the following:

  • We can make a word very large on the page. If you go big, go big.
  • We can bold the largest element. Though not always necessary, it can sometimes create a nice and juicy hierarchy.
  • We can create units or groupings by keeping the type contained within an imaginary box.
  • We can use visual alignments or improvised grids to reinforce the typographic grouping.

With what we have learned so far, we will move on to color.

Color

Discussing color can be an article all by itself. There are many resources available both online and printed about color. Indeed, here are a few Smashing articles by Cameron Chapman covering the subject more broadly:

In this article, however, we will focus on how color enhances or emphasizes hierarchy, how it helps to create a composition that keeps the eye inside of itself, and how it helps the eye navigate the page. For these reasons, when studying this variable, we limit the use of color to two or three colors. By limiting the use of color, we can focus on how it helps to establish a hierarchy in typography.

Factors That Affect The Use And Application Of Color

I do not mean we use color arbitrarily. It is important to read the content to establish a sense of the article. In other words, let’s assume we are designing a leaflet for a school-aged children’s birthday party. We would probably use vibrant colors and convey a sense of fun. Alternatively, if we are designing a leaflet for hospital patients with instructional material, perhaps the colors we use might be less vibrant, softer, and aimed to provide a sense of calm. There are usually three essential aspects to consider when using color and designing in general:

  • Content,
  • Audience,
  • Context.

The audience determines not only how the content is written but also the typefaces, sizes, weights, and overall design of the content. The context of the content also determines how we design: is the content meant to be read at a distance, as in a poster, or is the content meant to be read closer to us, as in a mobile device or a book? Because color affects how we perceive the content, we must become familiar with that content. Thus, reading the content given to us by our clients helps us make smart design decisions.

Now that we discussed factors that are important for the use of color, let’s look at examples of the use of color as it pertains to this exercise:

In the example above, we can see how all the colors and attention have been dedicated to the title. It has also been added to the name of the author of the instructions, but because of its small size, it does not create conflict. The layout takes advantage of once making everything on the title large; it creates a nice pocket of space where the instructions can be easily tucked in. In this way, even though there is no color used on the body copy, it does not matter because we have no choice but to land our eyes on the beginning of the text.

Above, we see how the background has been turned black. Once you read the title and read a little bit of the text, it makes sense. The text has a pessimistic and somber tone to it. Thus, no cheerful colors. With that, notice how the column of text is concentrated to the right side, creating asymmetry, once again creating a sense of visual instability to enhance the text’s meaning.

Below is a greeting card for Mother’s Day in the United States. I designed this card to honor my best friend’s mom. Though I am using a picture, it is used in a way that helps the text come together in the lowercase a. The lowercase a is the largest element on the page. Its bowl or empty space creates a nice place to tuck something in — a picture, pattern, letters, and so on. The rest of the letters are capitalized, but the lowercase a continues to be the focal point. We can also notice that there are four sizes here. I broke the rule of using only three sizes… but it does not feel that there is competition. The colors are vibrant because, in this case, Cuquin was a vibrant person, and the colors are needed to honor her.

In addition to the items we have added to our summary, we can add the following:

  • We can use color to convey personality and tone.
  • We can break a rule as long as it works within the system we have established and does not compete with the focal point.
  • We can create spaces within the letters or words to tuck in text, patterns, or pictures.

Our last variable to discuss is visual punctuation. Let’s take a look at how everything comes together in this variable.

Visual Punctuation

A common question I often hear from my students is, “What is visual punctuation?” We see it all the time but don’t think about it. Visual punctuation refers to the use of lines, shapes, symbols, and other geometric elements to enhance the hierarchy. Remember, the goal is always to enhance the hierarchy and help the reader’s eye move around the space.

Let’s see some examples of how visual punctuation is actually frequently used and applied in typographic compositions:

The example above uses visual punctuation in the form of the crane to cleverly point to the title. Then it repeats the use of white in the text at the beginning of the instructions. The similarity established creates unity, and the word FOLD pulls our eye back to the top. Notice how the designer also bolded the beginning of each instruction. We saw this before in the weight discussion. The use of the bold weight on each instruction helps us move from one to the other sequentially. It also helps to signal each new step without numbers.

The above example was designed to undermine the sometimes unnecessary rules and regulations that we find in places of worship. The point is not to follow all the rules but rather to focus on the object of affection. Here, a visual point is made to emphasize the conceptual point:

Circles are a great way to call attention to something. And so are the dotted lines. In this example, the dotted and playful line is colored in the same color as the circle on the top left. It points to the new number in the address aligned or set on the imaginary line the base of the number 2 provides. The rest of the address is provided following the same color palette. It creates a type of triangular movement from the top left to the middle right to the bottom left. Notice the sizes too. The numbers are the largest item on the card. There is a nice relationship between the numbers and the top left circle.

In addition to the items we have added to our summary, we can add the following:

  • We can and should use visual punctuation to enhance the meaning, the concept, or the message.
  • We can use only one color and one shape.
  • We can also use more than one color to create a hierarchy.

Now that we have discussed all the variables, it would be a good idea to see them all used together.

All Variables In Examples

We have discussed the variables of proximity, weight, size, size and weight, color, and visual punctuation. Take a look at the following examples and see how many you can identify:

Like these, we can find more examples of the variables used together. In fact, they are used and applied so ubiquitously that we don’t really see them independently from each other. When starting out with typography, it is a good idea to isolate what we see. This is true for any discipline: isolate and then combine them. Learn each one well and then start adding and mixing.

The poster below was designed for a youth program called Empowered. It was a research-based project led by Dr. Krista Mehari with the goal of empowering marginalized young teens to make effective and productive decisions. When she asked me to work with them, we had several brainstorming sessions. The Watch, Wave, and Wait is a poster intended to help the kids memorialize the process of dealing with emotions. In this poster, I broke some rules. While still sticking to the three sizes rule, I managed to create a pattern using repetition of the outline words mimicking the internal thought process we engage in when upset: calm down, calm down, or counting or something similar.

Your Turn!

At this point, after reading this article, you might want to give this process a try. If so, I have prepared a simple table for you to use. Below are some instructions:

  • Pick content that isn’t too long. For example, a two-page editorial would be too long. But a set of ten-step instructions would be better suited. An excerpt from an essay would be good too.
  • Do not use letter-size pages. Think smaller: eight inches by eight inches format would be best. We do this to focus on the content and not feel strange if the page does not look “full.” Your sketches, which should be small, will also be square.
  • Always do your sketches. Always do sketches first. It is the best way to literally think outside the box since you are outside the box, that is, the computer. Do as many sketches as you can think.
  • For each of the variables, sketch several. Maybe think of four options for each.
  • Then, take the best two or three for each variable and put them on the computer.
  • When you print, and you should always print to “see” how the proportions are working, use crop marks to cut the page.
  • Once you have printed them, tape them to a wall away from you. But tape them upside down. It is the best way to assess proportions, space, hierarchy, balance, tension, and so on.
  • After you do this, revise them on the computer, print them again, and tape them upside down again.
  • Once you are certain you have attained a good typographic hierarchy, you can make a small booklet out of them. Below you can see the booklet my former student Anh Dang did for her project, How to Fold a Crane. Or you can create a virtual flipbook showing your masterpieces!

And you needn’t stop there. As you get comfortable with the process, perhaps you want to try designing a poster. Or tackle that two-page editorial layout? Give it a try!

Conclusion

So far, we have seen how these six variables can powerfully transform the content in any format. It is all about how creative we are about organizing things within the parameters. After all, that is what design is about — creative solutions within a set of parameters. The more you practice, the better you get at something, right?

This old adage has proven itself to be true consistently. It applies to typography and anything design. Fine-tuning our senses comes with exposure and repetition. Take any opportunity to design and establish a hierarchy. Even small things like a business card can look incredible when you add a contrast of space, weight, size, size and weight, color, and visual punctuation. If we think about it, we are exposed to these variables daily and constantly. We just don’t look at them as isolated variables that can affect the entire composition. But they do. And once we know how to use them, we can push the boundaries and create pieces with more impact and intention.

Below I am listing resources to look at for more inspiration.

Resources

Overcoming Imposter Syndrome By Developing Your Own Guiding Principles

Design is one of those disciplines that has a very low barrier to entry, and this is amazing! What isn’t so easy is acquiring the softer skills that you’ll need when entering this job market.

Working on designs is just so much fun! But to become better designers, it’s also crucial to understand what makes a great team member and how to present your work to colleagues. Unfortunately, not everyone has access to a mentor, guide, or whatever word you’d like to use to describe advice from a more senior person in the design industry, which is why we often have to rely on “working it out” by ourselves.

This may be intimidating at first, but I firmly believe that if we take a step back from the pixels on the screen and reflect on who we want to be and what our core principles are, we can walk into these design critique meetings with more confidence and really deliver the best possible representation of our ideas.

“Yes, I’d love to present my work at the next meeting!”

“Yes, I’d love to present my work at the next meeting!” This has probably been you at some point during the past few months. Your boss has praised your design work, and you’ve been asked to share your design with the wider team. The thing is, you’re really not sure if you even like your work. You can see the inconsistent padding between the labels and the icons, the misalignment of the chevron, the lack of canvas organization, a glaring omission of meaningful layer names, and more.

Unfortunately, we’re raised in a world where seniority demands respect regardless of whether that is justified, and we need to be seen to grow within an organization. This means that we need to be able to present for the job we want in most cases, which is a fair ask for progression and, ultimately, also… money.

You know what? What you’re experiencing is within us all. The unfortunate side effect of being a creative is that you will never be satisfied with what you’ve produced and you’re not alone.

“It’s not uncommon for me to love the direction a design is going at the start of a project, but by the time it’s complete, I’m cringing and wishing I’d done so many little things better. And it’s not just imposter syndrome, it’s also that you have a gap between your vision and your skills. You can picture something in your mind — or you see inspiration elsewhere that you know you can match — but when it comes down to executing that vision, your skills and experience fall short of what you were aiming for.”

— Benek Lisefski, “Why Good Designers Can Never Do Their Best Work
“I’ve been designing for almost two decades, and I can tell you that I feel like a total amateur at least once a day.”

Daryl Ginn

Unless, like I have tried to force myself to do, you’ve resigned to the fact that 80% done is more often than not good enough to convince those at the table you’ve been desperate to sit at that we can produce good work and, ultimately, sell our product.

Presenting your work is fundamental to career growth, at least on every career ladder I’ve seen. This means we need to either become excellent actors or learn some coping mechanisms to handle that pressure. Weirdly enough, presenting work to your team should — and is often — the least pressured environment we will find ourselves in at work. Still, because we know each other and are unfortunately in competition with one another, it can feel like the most daunting task of them all.

This is where I can try to offer some help! Over the past many years, I’ve landed on a formula that works for me, and I’m happy to share what I have learned. Creating your own goals, rituals, and methods will help you succeed, but sometimes it’s hard to know where to start.

The Experience Paradox

You may be looking at your more experienced colleagues in awe, wondering how they present so well and seemingly without a bead of sweat. The funny thing, though, is that as your experience level increases, so does your self-doubt.

This oxymoron keeps us all sprinting along in blind panic, not stopping for air, burning out, and wondering what went wrong. But as Car Seat Headrest’s lead singer Will Toledo sings, “It doesn’t have to be like this”.

A second side effect of being a creative is that we get kicks out of focusing on the wrongs in the world, rather than appreciating what we have or what’s going well. This means that as we progress, become more successful, earn more money, buy that new iPhone, or spend $500 on some digital art, we will always fall into a slump at the first sniff of negative feedback. It’s in this slump that we are the most vulnerable, and here is where we need to rely on our personal values to keep our chins up and our spirits high.

Personal Principles

This is where I should probably coin a catchy marketing phrase like “The 5 P’s of Personal Principles,” but this isn’t the movie Dodgeball or an overpriced email course that you paid for (but could’ve Googled for free). So, let’s just pretend it has a catchy hook.

Principles also have a bit of a woo-woo reputation thanks to the boom in personal reflection over the past few years. But essentially, it’s about knowing yourself and understanding what you’re in control of and what you’re not. Control is a strong word and probably elicits some negative feelings, but it basically means “what we have within our reach.”

Knowing what you can and cannot control is incredibly important. Once you’ve grasped this, you will understand where you can win, and — you’ve guessed it — where you cannot. Knowing where you can win will present you with an endless amount of marginal gains that, once properly planned for, may turn you into a superstar. (Of course, there’s no such thing as a “superstar designer” or a “front-end ninja.” Although you’d be surprised at the number of hits if you Google one of these terms!)

What Are “Principles”?

If you’re like me, your mind probably went straight to something spiritual or religious when reading the word “principles,” but I promise you it’s not that.

Believing in yourself is more powerful than you think, and if you can stay humble whilst knowing your worth, you will become the best kind of colleague.

I’m not encouraging you to write down a list of commandments or commit to an “every day I will do this or that” type of routine here because we all know forced routine often fails.

What I would like you to do is start considering whether there is anything you find yourself repeating. Do you have a “motto” or a phrase that resonates with you particularly well? I have a few:

  • Always be two steps ahead.
    I try to work extra hard at being one further step in front so that when I fail, I’m still in the pole position. This also encourages me to anticipate scenarios and tension to prepare my responses in advance and build solutions to address them. In Shane Parrish’s “Decision by Design” course, he teaches us to “make our best decisions in advance,” and I strongly agree with this philosophy.
  • Expectations lead to disappointment.
    If we constantly set either ourselves, our peers, or our colleagues to out-of-reach expectations, we will more often than not feel let down. It’s easy for our minds to get away with themselves, and the best way to avoid this is to accept that we can’t bank on people doing what we think they will. The easiest way to address this is to be comfortable with knowing that people are human, will make good and bad decisions, and that it’s not something we are in control of. Removing that tension will help us to be more relaxed and in command of our own results.

I’m also not encouraging you to read more books. Take one look around where you live and count how many books you’ve bought over the years and still didn’t read. I’m going to bet there are at least five or ten unread books there, or probably more. This isn’t about reading what someone else thinks you should be but relying on what you believe to be true. No one can write or describe how you want your outlook to be, so let’s leave the books aside for a moment. (Note: The irony of this message arriving via an article is not beyond me.)

Much like reading someone else’s opinion on how to live your life, I’m also not telling you to steal or borrow principles from somewhere else. I can’t tell you how to think, but I can help get you into a frame of mind to encourage that discovery.

So, let’s do it.

Thinking Through Your Principles

What would it be if you were to boil down what you love the most about your current work into one short statement? This is hard for some people because there are moments when it can feel like nothing is enjoyable about work — which is kind of the point! Maybe it’s about the moments when you are not working that you enjoy the most, and this could form a statement, too.

Let’s make it harder. Try and trim that down into a tweet-sized amount.

Harder again, remove all of the descriptive words and see if you can get it under ten words.

Got there? If yes, well done, you have a principle or at least the beginning of one. You might need to shape and mold it into something catchy that you can remember. Important! This isn’t a principle that needs to be “sold” to others — it’s something you’d be comfortable repeating often. So, if it’s something like “I love it when I’m the best in the room,” no, this won’t work. Let’s think about how this can be shaped into something a little more comfortable, meaningful, and practical. Perhaps it’s “I love educating others”? This flips the point on its head and allows you to focus on the part you find enjoyable from an outwardly perspective, rather than being insular.

If you haven’t found your principle yet, here are some other questions you can ask yourself:

  • When do I feel most comfortable at work?
  • When do I feel least comfortable?
  • When was the last time I received praise, and what was it for?
  • Do I know where I’d like to be in six months from now? A year? Two years?
    • If the answer is “No, I don’t know,” this could help you spot a gap and a growth opportunity.
  • How would someone else describe you?
    • Pick out the keywords and try to expand them into a phrase.
  • What makes you distinctly unique?
    • Again, if the instinct here is to be negative, let’s try and flip that on its head.
  • What is my working style?
    • Am I most happy in the morning or in the evening? When do I produce my best work?
  • How do I prefer to communicate?
    • In person, via messages, email, or social media?

Jot down these answers onto a piece of paper if it helps. Or maybe in a new note on your phone if that’s how you prefer to take notes. (Again, the way you write down things is also something to be aware of).

Did that work for you?

Working With The Team

If this is something you’d prefer to do visually, I’ve shared a resource in the Figma community with some prompts to hopefully get you into “the zone.” This is something you can do yourself, or better yet, as a team. Finding these things out together actually offers an opportunity to spot each other’s strengths and weaknesses, encouraging a more open communication style and collaborative atmosphere as a unit.

When running this exercise with your team, you should hopefully find each other’s working styles and spot points where you can improve efficiency together. This encourages ownership by those that want it, domain expertise by those that specialize, and an acknowledgment of difference (this is the most important one!).

Now What?

You’ve read quite a few words so far, and you’re maybe wondering what to do with them from the practical side of things. Hopefully, by now, you’ve spotted a few potential principles to align with, so the moment has come when you should try to put them to work!

Remember the imposter syndrome scenario, where you have to present your work to the team, and you are, simply put, frightened? Let’s think about the principles that we defined earlier.

  • Taking the example “I love educating others” from above, you can quickly see how the tension can be relieved. This is what you love doing! Even though “presenting” isn’t at the top of your list, the ability to talk through why you’ve made certain decisions, the research you did to get there, and what others can learn from your work, are all enabling you to become that educator that you love so much to see helping others.
  • If your principle is that you work best in the morning, maybe you should encourage the critique/presentation session when you’re at the top of your game at 10 am? You can see how once you settle on something to guide you, your stress can definitely be managed more effectively.
  • Let’s say you really don’t enjoy the presentation side of your job because you find it hard to concentrate or remember everything you want to share. So, maybe the principle can become “Take notes about everything!” And you can litter your desk (or room) with notes about what it is you want to cover throughout. Or, even better, you can provide everyone at the meeting with a handout explaining the core concepts you want to discuss; the presentation will practically explain itself at this point.

I’ve written about this before, and here are the most important things to keep in mind when presenting projects internally to stakeholders:

  • Agenda! Create and share in advance a concise (important!) agenda way ahead of the time of the meeting.
  • Prepare your guests. Depending on how far out you schedule the meeting, make sure to nudge people before it to remind them of what’s coming up and whether you require anything from them.
  • Frame it. Whether you have the luxury of presenting to your immediate team or the potential pain of external stakeholders, context is everything so work on your short list of actionable points.
  • Work backwards. If you’re presenting something completely new and for the first time, let’s not start from the ground up. Show the finished design first, then go through your early scoping and decision-making.

I find that we become stressed most when we are trying to become someone we think other people want and expect (remember my point about expectations?). By trying to understand who it is that we are as individuals and not trying to double-think untrue expectations set by others, it provides us with a platform to shine.

Conclusion

We did it! We went through the first pass at crafting personal principles that should (or so I hope) leave you feeling stronger and more confident in yourself as you think about progressing as a designer in the industry.

The core messages that I want you to take away here are, first, that you are great at what you do, second, that sometimes we have to take control of our own paths, grabbing opportunities when they arise, and third, that you probably had no idea that you were that confident about your own methodologies before reading this article. I promised that I wouldn’t try to trick you!

I’d love to hear what principles you did manage to create during this process. You know where to find me — either leave a comment here or message me on Twitter.

Further Reading

How To Use Storytelling In UX

As human beings, we love stories. And stories come in all shapes and sizes. Children love fairy tales; teenagers ask “and then what happened?” when their friends recount gossip; history buffs explore biographies for insight into famous personalities; science lovers enjoy documentaries that offer explanations of the world around us; and everyone likes a satisfying conclusion to a good workplace drama or romantic exploit — be it fiction or fact.

We tell stories in our day-to-day lives. In fact, according to Scientific American, 65% of the conversation is stories! They’re in commercials to grab attention, in nonfiction books to make learning more personal, and in meetings — think of how storyboards help sell management on new product features. Magazines and news shows also tell stories rather than simply listing facts to make readers and viewers care about the information.

This is why, to quote Conversion Rate Experts on websites:

“You cannot have a page that’s too long — only one that’s too boring.”

We use storytelling skills to create positive user experiences as well, though it looks a little different when a story is a part of a product. Learning how to tell a good story with a digital experience will help people care about and engage more deeply with the experience. What’s more, using the same concepts that help create a good story will help you build a better product. A good story will influence marketing material, create a product that fits into the user journey, and highlight the opportunities that need more descriptive content or a more emotional connection to the end-user.

In this article, we’ll go over five steps to building a good experience using the steps an author uses. For consistency’s sake, we’ll also look at a single product to see how their choices can be evaluated through the five steps. I’ve chosen Sanofi’s Rheumatoid Arthritis Digital Companion, an app I helped to build in 2017. This product is a beneficial example to follow because it followed best-in-class storytelling practices, and also because it is no longer available in the app store, so we can look at it at a moment in time.

But before we get into any of that... why does storytelling work?

Why Does Storytelling Work?

In “Thinking, Fast and Slow” (Ch. 4, The Associative Machine), psychologist Daniel Kahneman explains how our brains make sense of information:

[…] look at the following words:

Bananas Vomit

[…] There was no particular reason to do so, but your mind automatically assumed a temporal sequence and a causal connection between the words bananas and vomit forming a sketchy scenario in which bananas caused the sickness. As a result, you experience a temporary aversion to bananas (don’t worry, it will pass).

Kahneman refers to the mind “assum[ing] a temporal sequence and a causal connection” between words. Put another way, even when there is no story, the human brain makes sense of information in two ways:

  • Temporal Sequence
    Assuming things happen linearly, or one after another.
  • Causal Connection
    When one thing causes another to happen.

Hubspot’s “Ultimate Guide to Storytelling” says the same thing as Kahneman, in a more readable way:

“Stories solidify abstract concepts and simplify complex messages.”

Our brains are built to find and understand stories. Therefore, it’s no wonder that a story can improve UX. UX is all about connecting an experience to a person’s mental model.

Learning how to tell a good story with a digital experience will help people care about and engage more deeply with the experience.

But writing a story is easier said than done. A story is more than just a list of things. It requires the “causal connection” that Kahneman referenced, and it must be emotionally charged in some way. Let’s review five steps that authors use which will also help you build a story into your user experience:

  1. Choose your genre;
  2. Create context;
  3. Follow the hero’s journey;
  4. Revise, revise, revise;
  5. Write the sequel.
Step 1: Choose Your Genre

Let’s forget about UX and digital products for a moment. Just think about your favorite story. Is it a murder mystery? Is it a sitcom? A romantic comedy? It’s probably not a murdery-mystery-romantic-comedy-sitcom-movie-newspaper-article. That’s because stories have genres.

Before beginning work on a product, it’s important to identify the genre. But where an author calls it a genre, in UX we might call it a “niche” or a “use case”. This is different from an industry — it’s not enough to be creating something “for healthcare” or “for finances.” Your product “genre” is the space in which it exists and can make a difference for the target audience.

One way to determine your product’s genres is to create an elevator pitch. The team at Asana suggests that the core of an elevator pitch is the problem, solution, and value proposition.

Another way to find the “genre” of your product is to fill in the blanks:

“We want to be the _____ of __________.”

Next, write a sentence or two explaining what that means to you. The key is specificity: the second blank should specifically describe what your organization does. This is your genre.

Here’s an example:

“We want to be the Amazon of find-a-doctor apps. That means we want to be known for offering personalized recommendations.” or “We want to be the Patagonia of ticket sales. We want people to know us for not only what we sell, but the ways that we care for the environment and the world.”

When my UX team worked on Sanofi’s Rheumatoid Arthritis (RA) Digital Companion, we took this approach to identify what features to include, and what a “digital companion” should do for someone with RA. We knew our audience needed a simple way to manage their RA symptoms, track their medication, and exercise their joints. But they also needed to feel supported.

Thus, the genre needed to reinforce that mentality. Internally, our UX team chose to focus on being “the Mary Poppins of medication adherence apps. That means we anticipate what the patient needs, we’re generally cheerful (though, not cheesy), and we focus on what works — not what’s expected.”

Step 2: Create Context

Step two is to add context to the experience. Context is everything that surrounds us. When I use an app I use it differently if I’m sitting at home giving it my full attention than if I’m at the grocery store. The noise, the visual stimuli, and the reason I’m logging in are all context. A UX designer also creates visual context by including headers at the top of screens or breadcrumbs to show someone on a website where they are in the grand scheme of things.

In Lisa Cron’s book Story Genius, she has a section called “What Kindergarten Got (And Still Gets) Very Very Wrong.” In it, Cron explains that too many people mistake a list of things that happen for an actual story. The difference is this idea of context: a good story is not just a sentence, but a descriptive sentence: the descriptors add the context.

To give a product context, it needs to be developed with an understanding of what the background of the audience is and the context in which they’ll be using the product. For UX, some of this comes from personas. But there’s more to it than that.

In storytelling, Cron believes the problem starts in kindergarten when students are given “What ifs” like these, which are used in a school in New Jersey:

  • What if Jane was walking along the beach and she found a bottle with a message in it? Write a story about what would happen next…
  • What if Freddy woke up and discovered that there’s a castle in his backyard? He hears a strange sound coming from inside, and then…
  • What if Martha walks into class and finds a great big sparkly box on her desk? She opens it and inside she finds….
[…] The problem is, these surprises don’t lead anywhere, because they lack the essential element we were talking about earlier: context.

[…]Because so what if Freddy discovers a castle, or Martha finds a big box on her desk or Jane finds a message in a bottle? Unless we know why these things would matter to Freddy, Martha, or Jane, they’re just a bunch of unusual things that happen, even if they do break a well-known external pattern. Not only don’t they suggest an actual story, but they also don’t suggest anything at all, other than the reaction: Wow, that’s weird!

— Lisa Cron, Story Genius

Cron suggests amending a “what if” to have a simple line providing context for the character. Joseph Campbell would call this “equilibrium” or “stability” in his well-known story structure, the Hero’s Journey (more on this in the next section). Both Campbell and Cron are saying that an author must show what stability looks like before the hero receives their call to adventure (or “what if”).

For example:

  • What if Jane, who had been waiting for years to hear from her long-lost father, was walking along the beach and she found a bottle with a message in it? Write a story about what would happen next…
  • What if Freddy, who dreamed of adventure in his small town, woke up and discovered that there was a castle in his backyard? He hears a strange sound coming from inside, and then…
  • What if Martha, who believed she had no friends and hated school, walks into class and finds a great big sparkly box on her desk? She opens it and inside she finds…

For your product, context means going beyond the moment someone uses your product. The UX context includes the problem that someone has, what prompts them to find the product, and where they are when they use the product. Simply having a new tool or product won’t help someone to use it or feel connected to it.

Make sure to ask these questions instead:

  • Who is my audience?
  • Where do they spend their time?
  • What were they thinking, feeling, and doing before seeing my product?

These questions will start to create a context for use. For the RA Digital Companion, we began by creating a journey map to pinpoint all the things someone with RA experiences in their daily life. We sketched out the steps of the journey map on a storyboard, focusing on moments of pain, frustration, or concern. These became the areas where we thought our app could have the most impact.

For example, we wrote the story by starting with Grace (our person with RA) waking up in the morning, her hands already hurting. We hypothesized that she reaches for her phone first thing, maybe turning off the alarm on her phone. This became an opportunity to suggest a quick “game” on her phone which would challenge her to exercise her fingers. While we might have thought of the game without the journey map or storyboard, we also might have missed this opportunity to connect to a moment in Grace’s life.

Step 3: Follow The Hero’s Journey

As exciting as a story that goes on forever might seem, ultimately it would bore the audience. A good book — and a good product — has a flow that eventually ends. The author or UX team needs to know what that flow is and how to end the experience gracefully.

In his book, The Hero with a Thousand Faces, Joseph Campbell outlines 17 steps that make up a hero’s journey (a common template of storytelling). While the hero’s journey is very detailed, the overarching concept is applicable to UX design:

So, where does your story end? At what point will your product’s story reach its climax and successfully allow your end-user to be a hero? For a tax accounting software like Turbotax, the climax might be the moment that the taxes are filed. But the story isn’t over there; people need to track their refunders. Even though Turbotax doesn’t have that information in their product, they still help set expectations by creating infographics and other content that benefits their users as an “offboarding” process. In other words, where some products would leave users on their own as soon as they don’t physically need the product anymore, Turbotax offers materials to benefit users even when they’re done using the Turbotax product.

Designing for this type of offboarding is just as important as designing for onboarding. When someone uses TurboTax, they don’t want to feel harassed to come back to the app long after tax season is over. TurboTax is better off letting users stop using their product and return on their own when they need to. That’s how they keep their users happy.

When your design team identifies your offboarding point, you might try creating your user’s Hero Journey. One way to do this is to literally write a story about the user’s hero journey. Feel free to be silly, and position the user of your product as a knight in shining armor. Use the Hero’s Journey template to think about the emotional, mental, and physical state of the person using your product (remember your context from before!).

For example, let’s take a closer look at the well-known laundry detergent brand Tide and one of their commercials, “Princess Dress”:

We could break it down like this:

Step Observation
Call to Adventure Lily’s princess dress getting dirty.
Refusal of the Call Lily’s procrastination: she doesn’t want to change out of her princess dress.
Road of Trials The father collecting the dress and giving Lily her sheriff’s costume, plus her accompanying power trip (a true trial for every parent).
Apotheosis When the wash is dry and can be worn again.
Refusal of the Return The parent wanting to leave everything nice and clear in the basket.
Crossing the Return Threshold When Lily gets her princess dress back.
Master of Two Worlds Lily is wearing clean clothes that are not yet dirty and having to be washed.

Important note: Notice where the product is used and where the user steps away from Tide and continues on with their life.

Ideally, with an app like the Digital Companion, a user could use our app for years. But there will be ebbs and flows. Building off our storyboard we created numerous vignettes for our user “Grace”.

When Grace needs to take her injection, we knew from user research she would be worried, uncertain, and even likely to skip the medication. Instead of seeing this as a static phase in her life, we imagined her on the “Road of Trials” in the Adventure of the Hero. This imagery became a part of the language we used to encourage users to overcome their fears and bravely give themselves their injections. Over time, as the app encouraged “Grace” to build a habit of taking and recording her injections, our app copy treated her like a conquering hero, moving through the Hero’s Journey to become a “Master of Two Worlds”.

Step 4: Revise, Revise, Revise

“Good writing is good editing,” so the saying goes. The same is true with UX. For an author, finishing the story is only the first step. They then work with an editor to get more feedback and make numerous revisions. In UX, we rely on user research, and instead of “revisions,” we have iterations. Usability testing, A/B testing, user research, and prototype testing are all ways to get feedback from the target audience.

But in UX we have a major advantage. Once a book is published, the feedback can’t be acted on. But digital products can be adapted and improved even after launch. Websites get redesigns, and apps get bug fixes.

For example, when healthcare.gov launched, it immediately crashed. Harvard Business School reported:

“The key issues discussed above resulted in the rollout of the healthcare.gov website ballooning the initial $93.7M budget to an ultimate cost of $1.7B. It is easy to observe that the launch of healthcare.gov was a major failure.”

Yet healthcare.gov didn’t fail. The website still exists, and in 2021 alone more than 2.5 million people gained health coverage during the special enrollment period, allowed by the website. A failed story is simply a failure. A failed product can be iterated on.

In some cases, even a physical product can be updated to succeed. Consider a product like the Garmin watch. Even within one year there are multiple designs and styles. Some are flops, and others are major successes. The product designers learn from the flops and improve on each watch style every few years.

Although the Digital Companion is no longer available on the app store, we did have multiple releases after the initial launch. Even before that first release came out, we had ideas about how to build on the product and expand its capabilities. For example, we were able to start desirability testing on having multiple “finger stretching” game options and A/B testing video instructions for taking injections. These concepts didn’t need to wait on results from our initial release — such is the way of UX work!

Step 5: Write The Sequel

Once a product is out in the world, it’s time for the sequel. Many great stories have excellent sequels. Sometimes it’s a planned sequel like adding a new beverage to the Starbucks line. Other times it’s more of an add-on based on demand or need. A good sequel either completes a planned series or builds off the previous story. Think of these like the Star Wars trilogies versus the movies labeled “a Star Wars story.” A trilogy is a planned storyline. A one-off film may be built on the previous story and add to the world.

But some sequels do a bad job of building on the original story. Specifically, they erase characters or facts. Many Game of Thrones fans, for example, were furious with the last few episodes of the series. They saw the episodes of ignoring a key character’s growth, ignoring the rest of the series to create a nice ending.

This can happen to products as well. The tobacco industry had to find a new story for the problem their product solved after the 1964 Surgeon General’s report labeled smoking a health risk. It wasn’t the sequel they had planned, but in the words of their fictional ad man Don Draper:

“If you don’t like what people are saying, change the conversation.”

Mad Men: Change the Conversation

As a result, they changed the product itself (adding filters to more cigarettes) as well as the advertising around it, to claim that filters were healthier and protected the smoker from tar and nicotine (they do not). And it worked!

According to an article in the medical journal BMJ Journals:

“The marketplace response was a continuation of smoking rates with a dramatic conversion from “regular” (short length, unfiltered) products to new product forms (filtered, king-sized, menthol, 100 mm).”

Each UX designer, UX writer, and engineer on the RA Digital Companion product has gone on to make our own sequels. What I learned from the RA Digital Companion — about user needs, rheumatoid arthritis, and behavior change — has played into my work, most recently at Verily. That product created a story to be shared with the end-user, but the sequels may affect health-tech as a whole.

Tell A Story And Connect

Ultimately, telling a story is how we connect to one another as humans. Whether you use Lisa Cron’s Storygenius, Joseph Campbell’s Hero with a Thousand Faces, or simply the mindset of Daniel Kahneman, you can use storytelling to adapt your product to your user’s needs and wants.

As a Forbes article stated back in 2018:

“As human beings, we’re often drawn to the narrative; in part, because our complex psychological makeup wires us for the sharing of information through storytelling and in part due to our natural curiosity.”

Products are no exception: a good user experience takes the form of a story. The IA feels familiar and gives context and flow. The product itself will fit into a user’s sense of their own life story, and users are drawn to that.

To tell a story, you need to focus on a genre or a type of product and audience. You need to build out a world or context for your audience. Limit your scope to where you can have the most impact, revise with user feedback, and then think about the sequels. All in all, storytelling is a powerful tool for any UX designer. It will help you to create your product and understand the people who use it.

Related Reading on Smashing Magazine

How To Create An Information Architecture That Is Easy To Use

When creating the information architecture of a website, users are often overlooked in favor of internal politics and organizational thinking. On larger websites, each departmental silo wants its own section of the site, and they often fight to ensure their section is featured in the main navigation. Even small sites suffer from problems with information architecture as business owners focus more on what they want to say rather than what users want to know.

In most cases, stakeholders are the worst people to decide on the information architecture, so I want to share an alternative process. However, before we can implement this process, we need to explain why our stakeholders should not be defining the site structure. To do that, we need to teach them about mental models.

Understanding And Explaining Mental Models

Mental models are used to explain how we organize pretty much anything in our world. For example, most of us would organize the following words into a group that we would probably refer to as “fruits”:

  • Apple,
  • Pear,
  • Banana,
  • Melon,
  • Orange.

However, not all of us see the world in the same way. For example, if you are an expert in fruits, you would probably include tomatoes in this list. But, of course, most of us would probably associate tomatoes more with salads than we do fruits.

Lots of things can influence our mental models from previous experience to culture. However, one of the most significant influencing factors is our level of experience in the subject matter.

As we become more knowledgeable in a particular subject, our mental model diverges from the rest of the population. For example, an expert in birds will tell you that there is no such thing as a seagull, yet most of us would think otherwise.

It is precisely this issue of experts having a different mental model from the rest of us that means stakeholders are the worst people to be making decisions about a site’s information architecture. After all, most stakeholders are experts in their products or services and think about them differently from most people visiting the website.

This issue makes them bad at organizing content and can undermine the content itself.

Creating Relevant Content

Poor content choices can significantly impact how effective your information architecture is. Hence, creating an information architecture must begin by defining the content that needs to appear on the website.

Organizations typically make two common mistakes when deciding on the content for their website. First, many mindlessly migrate the content from their old website to the new one. They fail to ask whether this content is relevant.

However, the second and more significant issue is that stakeholders start from the wrong premise when creating content. They start by asking themselves, “what do we want to say” rather than “what does our audience want to know?

The result of this wrong starting point is that any information architecture will fail because users won’t find answers to their questions. That is because the content doesn’t exist on the website.

The starting point for any information architecture project should not be to define what the organization wants to say, but what questions, objections, and tasks the user has in their mind as they come to the website. You can then build the site’s content and, by extension, the information architecture around these:

  • Questions
    They can range from general questions such as “what is this site about” or “how can this site help me” to more specific questions about the products or services the site provides.
  • Objections
    They are the reasons that users might not take action. For example, if your call to action is to sign up for a newsletter, users might have concerns about spam, privacy, or how easy it will be to unsubscribe. If you don’t address these objections, people will not act.
  • Tasks
    They relate to actions the user might want to take on the website. These might include booking an event, signing up for a newsletter, or contacting the organization behind the website.

On an e-commerce site, this would include finding a product, adding it to the cart, checking out, and managing the order.

You should use other elements such as social proof, or the companies value proposition to answer these questions and objections. However, the information architecture itself needs to be built around the user’s needs.

So, before we begin organizing content, we first need to gather a list of questions, objections and tasks. Now, depending on the breadth of what your organization does, this can turn into a huge list. But, that is okay, as our next step will be to identify which questions, objections, and tasks matter most to users.

Identify Your Top Content

Not all questions, objections, or tasks are equal. Some will be much more important to users or asked by many more people. It is, therefore, vital that we identify our top elements as we will want to ensure these are particularly easy to find.

Depending on time and budget, you could take a couple of approaches. I highly recommend identifying the critical elements using top task analysis if you have the time. This approach created by Gerry McGovern prioritizes content by surveying users and asking them what they most care about.

If you have limited time or budget, then a conversation with customer-facing staff such as sales representatives or call center staff is often enough to identify what questions, objections, and tasks come up the most.

In either case, you will find that a large percentage of user inquiries will revolve around a relatively small number of questions, objections, or tasks.

Now we know the top content, we can begin drafting our information architecture.

Create The First Draft With Open Card Sorting

To begin with, we are going to focus solely on organizing the top content and ignore the less important questions, objections, and tasks. That is to prevent ourselves and any users we involve from becoming overwhelmed. Ideally, you want to end up with no more than 30 to 60 cards.

Start by simplifying your top content. For example, if you have a question that reads “how much does it cost,” it could be simplified to “pricing.” You will also find similar content that is obvious enough to be grouped. For example, “how much does it cost” and “are there any extra charges” could be both displayed as “pricing.”

Once you have your simplified list of top content, you need to organize it into top-level sections. If the budget or timescales are tight, you could make an educated guess as we will be testing later. However, if you do, you will probably receive pushback from stakeholders and end up doing more rework. Therefore this route can often be a false economy.

A better approach is to do an open card sort. Traditionally, card sorting has been done in person. However, I prefer to do it online with a tool like UX Metrics, as it is quicker, easier to arrange, and makes analyzing results straightforward.

All you have to do within UX Metrics is create a separate ‘card’ for each of your simplified content areas. Then, UX Metrics will give you a URL to share with users via social media, email, or even your website.

Users who choose to complete the card sort will be asked to organize the content into any groupings that make sense to them and give each group a name. These groups will be their personal preference for sections on the website.

Note: I recommend getting as many people as possible to complete the card sort. However, as a minimum, you will need at least 13 users to weed out outliers.

Once the open card sort is finished, UX Metrics will provide you with a report showing you what groups users created and how often they occurred. You then need to merge similar groups and decide on a small number of top-level sections favoring often occurring groupings.

This process is not an exact science and takes experience. Try to keep the number of groups down as low as possible. Approximately four is ideal as we can only hold four pieces of information in our short-term memory at a time. Certainly avoid going above eight elements and when adding more sections, try chunking the groups into separate navigation.

For example, you could have a primary navigation bar and a secondary utility bar for sections such as help, log in or contact us. Don’t worry about getting it wrong at this stage, as we will test our structure later. You can also use the report to identify potential sub-sections underneath the primary navigation. However, these will be further defined when carrying out closed card sorting.

Ensure Your Information Architecture Scales With Closed Card Sorting

Closed card sorting is similar to open card sorting, with one difference. Instead of users organizing cards into their own set of groups, they organize them into a predefined set of sections that you give them.

In other words, we will ask users to organize cards into the top-level information architecture we have created:

Because a closed card sort only requires people to put cards into predefined groups, we can ask users to sort considerably more cards than in an open card sort. Because of that, we can use closed card sorting as a way of discovering whether our first draft will accommodate all of our content, not just our top tasks, questions, and objections.

We can use UX Metrics to run closed card sorting, but I suggest adding an extra top-level section labeled “I don’t know.” This approach helps collect cards that the user has no idea where they should be placed. That will help us identify if our site structure fails in certain areas.

After reviewing the results, you should be able to assign most content to a top-level section. If you have a lot of content that doesn’t fit comfortably, you may have to revise your draft architecture and repeat the closed card sorting exercise.

However, we have yet to address the subsections within those top-level sections. You may well be wondering if you need to do this entire process for each top-level section of the website.

How Low Do You Go In Card Sorting?

Unless you are working on an extensive site with a healthy budget for information architecture, you will probably only run card sorting for the top-level sections. Below that, you will be forced to make educated guesses based on what content people have placed within each of those top-level sections. If that is the case, do not worry because the first navigational selection a user makes is the most important.

One of the most influential usability studies was “First Click Usability Testing” by Bob Bailey and Cari Wolfson. They explored the importance of the user’s first click being correct. They found that if the first click was correct, people had an 87% chance of finding the content they wanted instead of just 46% if the first click was wrong.

In other words, if you only run card sorting on the top level of your information architecture, you have still doubled the chance users will succeed.

Of course, if time and budget allow, it would be advisable to run open card sorting for each of the sections of your site to help determine the structure of the subsections. Also, if your website is extensive (such as a university or government body), you may wish to run closed card sorting as well to make sure all the content lower in the site sits comfortably within its sections.

Whatever approach you adopt, it is worth testing your final information architecture one last time to ensure it works successfully.

Test Your Information Architecture With Tree Testing

I would recommend running a simple tree test to test your final information architecture. Once again, you can do this through UX Metrics or whatever tool you settle on.

To create a tree test, you need to recreate your site’s information architecture as a hierarchical tree. Once you have done that, you can decide what you wish to test. First, select a small number of pages you want to check that users can find. For example, you may wish to ask users to find a page on the site that they believe will answer specific questions or complete a particular task.

When considering what to test, I recommend focusing on three types of content:

  1. Content that is particularly important to the user or for the site’s success;
  2. Content that users placed in varying sections during the card sorting exercises;
  3. Content that you suspect may be hidden too deep in the information architecture.

Next, you will tell UX Metrics the fastest path for finding each piece of content.

Once you have done that, all that remains is to distribute the test as you did for card sorting. Users will be asked to find each piece of content, and then you will receive a report from UX Metrics. The report will show you how successful people were, how quickly they completed the task, and whether they took the best route.

If you are creating an entirely new information architecture, I recommend focusing on whether people can complete the task successfully within a reasonably short time. Whether they used the most direct route or not is a secondary consideration.

If your site structure has problems in your tree test, don’t panic, especially since users will probably only have trouble finding secondary content. It is important to remember that your information architecture does not exist in isolation.

Supplement Your Information Architecture

Navigating your site’s information architecture is just one method users can use to find content. For example, site search is also critical. However, the most crucial tool in your arsenal for improving findability is cross-linking.

When you view the results of your tree testing, probably the most significant issue you will find will be that users are unsure which of two or more sections a piece of content will be in. When faced with that situation, they often guess, and if they guess wrong, they may give up before exploring the other options.

To address this problem, you need to include links to the page in the alternative sections people look in. That way, they can recover even if they make an initial mistake.

You can identify pages for cross-linking by looking at your closed card sorting exercise. First, look for pages that are regularly placed in different sections by users. Then, make sure you include those links to that page from all those sections, even if it only sits in one of those sections in the hierarchy.

Also, don’t forget to highlight critical content that exists lower in the hierarchy than the page viewed. For example, a top-level section page should highlight the top content within that section of the site, and the homepage should provide quick access to the most important content across the entire site.

By combining a well-researched and tested information architecture with these other techniques, you will ensure that we can address users’ objections and questions as quickly as possible and complete any tasks they have.

Further Reading on Smashing Magazine

How to Make a Component That Supports Multiple Frameworks in a Monorepo

Your mission — should you decide to accept it — is to build a Button component in four frameworks, but, only use one button.css file!

This idea is very important to me. I’ve been working on a component library called AgnosticUI where the purpose is building UI components that aren’t tied to any one particular JavaScript framework. AgnosticUI works in React, Vue 3, Angular, and Svelte. So that’s exactly what we’ll do today in this article: build a button component that works across all these frameworks.

The source code for this article is available on GitHub on the the-little-button-that-could-series branch.

Table of contents

Why a monorepo?

We’re going to set up a tiny Yarn workspaces-based monorepo. Why? Chris actually has a nice outline of the benefits in another post. But here’s my own biased list of benefits that I feel are relevant for our little buttons endeavor:

Coupling

We’re trying to build a single button component that uses just one button.css file across multiple frameworks. So, by nature, there’s some purposeful coupling going on between the various framework implementations and the single-source-of-truth CSS file. A monorepo setup provides a convenient structure that facilitates copying our single button.css component into various framework-based projects.

Workflow

Let’s say the button needs a tweak — like the “focus-ring” implementation, or we screwed up the use of aria in the component templates. Ideally, we’d like to correct things in one place rather than making individual fixes in separate repositories.

Testing

We want the convenience of firing up all four button implementations at the same time for testing. As this sort of project grows, it’s safe to assume there will be more proper testing. In AgnosticUI, for example, I’m currently using Storybook and often kick off all the framework Storybooks, or run snapshot testing across the entire monorepo.

I like what Leonardo Losoviz has to say about the monorepo approach. (And it just so happens to align with with everything we’ve talked about so far.)

I believe the monorepo is particularly useful when all packages are coded in the same programming language, tightly coupled, and relying on the same tooling.

Setting up

Time to dive into code — start by creating a top-level directory on the command-line to house the project and then cd into it. (Can’t think of a name? mkdir buttons && cd buttons will work fine.)

First off, let’s initialize the project:

$ yarn init
yarn init v1.22.15
question name (articles): littlebutton
question version (1.0.0): 
question description: my little button project
question entry point (index.js): 
question repository url: 
question author (Rob Levin): 
question license (MIT): 
question private: 
success Saved package.json

That gives us a package.json file with something like this:

{
  "name": "littlebutton",
  "version": "1.0.0",
  "description": "my little button project",
  "main": "index.js",
  "author": "Rob Levin",
  "license": "MIT"
}

Creating the baseline workspace

We can set the first one up with this command:

mkdir -p ./littlebutton-css

Next, we need to add the two following lines to the monorepo’s top-level package.json file so that we keep the monorepo itself private. It also declares our workspaces:

// ...
"private": true,
"workspaces": ["littlebutton-react", "littlebutton-vue", "littlebutton-svelte", "littlebutton-angular", "littlebutton-css"]

Now descend into the littlebutton-css directory. We’ll again want to generate a package.json with yarn init. Since we’ve named our directory littlebutton-css (the same as how we specified it in our workspaces in package.json) we can simply hit the Return key and accept all the prompts:

$ cd ./littlebutton-css && yarn init
yarn init v1.22.15
question name (littlebutton-css): 
question version (1.0.0): 
question description: 
question entry point (index.js): 
question repository url: 
question author (Rob Levin): 
question license (MIT): 
question private: 
success Saved package.json

At this point, the directory structure should look like this:

├── littlebutton-css
│   └── package.json
└── package.json

We’ve only created the CSS package workspace at this point as we’ll be generating our framework implementations with tools like vite which, in turn, generate a package.json and project directory for you. We will have to remember that the name we choose for these generated projects must match the name we’ve specified in the package.json for our earlier workspaces to work.

Baseline HTML & CSS

Let’s stay in the ./littlebutton-css workspace and create our simple button component using vanilla HTML and CSS files.

touch index.html ./css/button.css

Now our project directory should look like this:

littlebutton-css
├── css
│   └── button.css
├── index.html
└── package.json

Let’s go ahead and connect some dots with some boilerplate HTML in ./index.html:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>The Little Button That Could</title>
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="css/button.css">
</head>
<body>
  <main>
    <button class="btn">Go</button>
  </main>
</body>
</html>

And, just so we have something visual to test, we can add a little color in ./css/button.css:

.btn {
  color: hotpink;
}
A mostly unstyled button with hot-pink text from the monorepo framework.

Now open up that index.html page in the browser. If you see an ugly generic button with hotpink text… success!

Framework-specific workspaces

So what we just accomplished is the baseline for our button component. What we want to do now is abstract it a bit so it’s extensible for other frameworks and such. For example, what if we want to use the button in a React project? We’re going to need workspaces in our monorepo for each one. We’ll start with React, then follow suit for Vue 3, Angular, and Svelte.

React

We’re going to generate our React project using vite, a very lightweight and blazingly fast builder. Be forewarned that if you attempt to do this with create-react-app, there’s a very good chance you will run into conflicts later with react-scripts and conflicting webpack or Babel configurations from other frameworks, like Angular.

To get our React workspace going, let’s go back into the terminal and cd back up to the top-level directory. From there, we’ll use vite to initialize a new project — let’s call it littlebutton-react — and, of course, we’ll select react as the framework and variant at the prompts:

$ yarn create vite
yarn create v1.22.15
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...

success Installed "create-vite@2.6.6" with binaries:
      - create-vite
      - cva
✔ Project name: … littlebutton-react
✔ Select a framework: › react
✔ Select a variant: › react

Scaffolding project in /Users/roblevin/workspace/opensource/guest-posts/articles/littlebutton-react...

Done. Now run:

  cd littlebutton-react
  yarn
  yarn dev

✨  Done in 17.90s.

We initialize the React app with these commands next:

cd littlebutton-react
yarn
yarn dev

With React installed and verified, let’s replace the contents of src/App.jsx to house our button with the following code:

import "./App.css";

const Button = () => {
  return <button>Go</button>;
};

function App() {
  return (
    <div className="App">
      <Button />
    </div>
  );
}

export default App;

Now we’re going to write a small Node script that copies our littlebutton-css/css/button.css right into our React application for us. This step is probably the most interesting one to me because it’s both magical and ugly at the same time. It’s magical because it means our React button component is truly deriving its styles from the same CSS written in the baseline project. It’s ugly because, well, we are reaching up out of one workspace and grabbing a file from another. ¯\_(ツ)_/¯

Add the following little Node script to littlebutton-react/copystyles.js:

const fs = require("fs");
let css = fs.readFileSync("../littlebutton-css/css/button.css", "utf8");
fs.writeFileSync("./src/button.css", css, "utf8");

Let’s place a node command to run that in a package.json script that happens before the dev script in littlebutton-react/package.json. We’ll add a syncStyles and update the dev to call syncStyles before vite:

"syncStyles": "node copystyles.js",
"dev": "yarn syncStyles && vite",

Now, anytime we fire up our React application with yarn dev, we’ll first be copying the CSS file over. In essence, we’re “forcing” ourselves to not diverge from the CSS package’s button.css in our React button.

But we want to also leverage CSS Modules to prevent name collisions and global CSS leakage, so we have one more step to do to get that wired up (from the same littlebutton-react directory):

touch src/button.module.css

Next, add the following to the new src/button.module.css file:

.btn {
  composes: btn from './button.css';
}

I find composes (also known as composition) to be one of the coolest features of CSS Modules. In a nutshell, we’re copying our HTML/CSS version of button.css over wholesale then composing from our one .btn style rule.

With that, we can go back to our src/App.jsx and import the CSS Modules styles into our React component with this:

import "./App.css";
import styles from "./button.module.css";

const Button = () => {
  return <button className={styles.btn}>Go</button>;
};

function App() {
  return (
    <div className="App">
      <Button />
    </div>
  );
}

export default App;

Whew! Let’s pause and try to run our React app again:

yarn dev

If all went well, you should see that same generic button, but with hotpink text. Before we move on to the next framework, let’s move back up to our top-level monorepo directory and update its package.json:

{
  "name": "littlebutton",
  "version": "1.0.0",
  "description": "toy project",
  "main": "index.js",
  "author": "Rob Levin",
  "license": "MIT",
  "private": true,
  "workspaces": ["littlebutton-react", "littlebutton-vue", "littlebutton-svelte", "littlebutton-angular"],
  "scripts": {
    "start:react": "yarn workspace littlebutton-react dev"
  }
}

Run the yarn command from the top-level directory to get the monorepo-hoisted dependencies installed.

The only change we’ve made to this package.json is a new scripts section with a single script to start the React app. By adding start:react we can now run yarn start:react from our top-level directory and it will fire up the project we just built in ./littlebutton-react without the need for cd‘ing — super convenient!

We’ll tackle Vue and Svelte next. It turns out that we can take a pretty similar approach for these as they both use single file components (SFC). Basically, we get to mix HTML, CSS, and JavaScript all into one single file. Whether you like the SFC approach or not, it’s certainly adequate enough for building out presentational or primitive UI components.

Vue

Following the steps from vite’s scaffolding docs we’ll run the following command from the monorepo’s top-level directory to initialize a Vue app:

yarn create vite littlebutton-vue --template vue

This generates scaffolding with some provided instructions to run the starter Vue app:

cd littlebutton-vue
yarn
yarn dev

This should fire up a starter page in the browser with some heading like “Hello Vue 3 + Vite.” From here, we can update src/App.vue to:

<template>
  <div id="app">
    <Button class="btn">Go</Button>
  </div>
</template>

<script>
import Button from './components/Button.vue'

export default {
  name: 'App',
  components: {
    Button
  }
}
</script>

And we’ll replace any src/components/* with src/components/Button.vue:

<template>
  <button :class="classes"><slot /></button>
</template>

<script>
export default {
  name: 'Button',
  computed: {
    classes() {
      return {
        [this.$style.btn]: true,
      }
    }
  }
}
</script>

<style module>
.btn {
  color: slateblue;
}
</style>

Let’s break this down a bit:

  • :class="classes" is using Vue’s binding to call the computed classes method.
  • The classes method, in turn, is utilizing CSS Modules in Vue with the this.$style.btn syntax which will use styles contained in a <style module> tag.

For now, we’re hardcoding color: slateblue simply to test that things are working properly within the component. Try firing up the app again with yarn dev. If you see the button with our declared test color, then it’s working!

Now we’re going to write a Node script that copies our littlebutton-css/css/button.css into our Button.vue file similar to the one we did for the React implementation. As mentioned, this component is a SFC so we’re going to have to do this a little differently using a simple regular expression.

Add the following little Node.js script to littlebutton-vue/copystyles.js:

const fs = require("fs");
let css = fs.readFileSync("../littlebutton-css/css/button.css", "utf8");
const vue = fs.readFileSync("./src/components/Button.vue", "utf8");
// Take everything between the starting and closing style tag and replace
const styleRegex = /<style module>([\s\S]*?)<\/style>/;
let withSynchronizedStyles = vue.replace(styleRegex, `<style module>\n${css}\n</style>`);
fs.writeFileSync("./src/components/Button.vue", withSynchronizedStyles, "utf8");

There’s a bit more complexity in this script, but using replace to copy text between opening and closing style tags via regex isn’t too bad.

Now let’s add the following two scripts to the scripts clause in the littlebutton-vue/package.json file:

"syncStyles": "node copystyles.js",
"dev": "yarn syncStyles && vite",

Now run yarn syncStyles and look at ./src/components/Button.vue again. You should see that our style module gets replaced with this:

<style module>
.btn {
  color: hotpink;
}
</style>

Run the Vue app again with yarn dev and verify you get the expected results — yes, a button with hotpink text. If so, we’re good to move on to the next framework workspace!

Svelte

Per the Svelte docs, we should kick off our littlebutton-svelte workspace with the following, starting from the monorepo’s top-level directory:

npx degit sveltejs/template littlebutton-svelte
cd littlebutton-svelte
yarn && yarn dev

Confirm you can hit the “Hello World” start page at http://localhost:5000. Then, update littlebutton-svelte/src/App.svelte:

<script>
  import Button from './Button.svelte';
</script>
<main>
  <Button>Go</Button>
</main>

Also, in littlebutton-svelte/src/main.js, we want to remove the name prop so it looks like this:

import App from './App.svelte';

const app = new App({
  target: document.body
});

export default app;

And finally, add littlebutton-svelte/src/Button.svelte with the following:

<button class="btn">
  <slot></slot>
</button>

<script>
</script>

<style>
  .btn {
    color: saddlebrown;
  }
</style>

One last thing: Svelte appears to name our app: "name": "svelte-app" in the package.json. Change that to "name": "littlebutton-svelte" so it’s consistent with the workspaces name in our top-level package.json file.

Once again, we can copy our baseline littlebutton-css/css/button.css into our Button.svelte. As mentioned, this component is a SFC, so we’re going to have to do this using a regular expression. Add the following Node script to littlebutton-svelte/copystyles.js:

const fs = require("fs");
let css = fs.readFileSync("../littlebutton-css/css/button.css", "utf8");
const svelte = fs.readFileSync("./src/Button.svelte", "utf8");
const styleRegex = /<style>([\s\S]*?)<\/style>/;
let withSynchronizedStyles = svelte.replace(styleRegex, `<style>\n${css}\n</style>`);
fs.writeFileSync("./src/Button.svelte", withSynchronizedStyles, "utf8");

This is super similar to the copy script we used with Vue, isn’t it? We’ll add similar scripts to our package.json script:

"dev": "yarn syncStyles && rollup -c -w",
"syncStyles": "node copystyles.js",

Now run yarn syncStyles && yarn dev. If all is good, we once again should see a button with hotpink text.

If this is starting to feel repetitive, all I have to say is welcome to my world. What I’m showing you here is essentially the same process I’ve been using to build my AgnosticUI project!

Angular

You probably know the drill by now. From the monorepo’s top-level directory, install Angular and create an Angular app. If we were creating a full-blown UI library we’d likely use ng generate library or even nx. But to keep things as straightforward as possible we’ll set up a boilerplate Angular app as follows:

npm install -g @angular/cli ### unless you already have installed
ng new littlebutton-angular ### choose no for routing and CSS
? Would you like to add Angular routing? (y/N) N
❯ CSS 
  SCSS   [ https://sass-lang.com/documentation/syntax#scss ] 
  Sass   [ https://sass-lang.com/documentation/syntax#the-indented-syntax ] 
  Less   [ http://lesscss.org ]

cd littlebutton-angular && ng serve --open

With the Angular setup confirmed, let’s update some files. cd littlebutton-angular, delete the src/app/app.component.spec.ts file, and add a button component in src/components/button.component.ts, like this:

import { Component } from '@angular/core';

@Component({
  selector: 'little-button',
  templateUrl: './button.component.html',
  styleUrls: ['./button.component.css'],
})
export class ButtonComponent {}

Add the following to src/components/button.component.html:

<button class="btn">Go</button>

And put this in the src/components/button.component.css file for testing:

.btn {
  color: fuchsia;
}

In src/app/app.module.ts:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';
import { ButtonComponent } from '../components/button.component';

@NgModule({
  declarations: [AppComponent, ButtonComponent],
  imports: [BrowserModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

Next, replace src/app/app.component.ts with:

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {}

Then, replace src/app/app.component.html with:

<main>
  <little-button>Go</little-button>
</main>

With that, let’s run yarn start and verify our button with fuchsia text renders as expected.

Again, we want to copy over the CSS from our baseline workspace. We can do that by adding this to littlebutton-angular/copystyles.js:

const fs = require("fs");
let css = fs.readFileSync("../littlebutton-css/css/button.css", "utf8");
fs.writeFileSync("./src/components/button.component.css", css, "utf8");

Angular is nice in that it uses ViewEncapsulation that defaults to to emulate which mimics, according to the docs,

[…] the behavior of shadow DOM by preprocessing (and renaming) the CSS code to effectively scope the CSS to the component’s view.

This basically means we can literally copy over button.css and use it as-is.

Finally, update the package.json file by adding these two lines in the scripts section:

"start": "yarn syncStyles && ng serve",
"syncStyles": "node copystyles.js",

With that, we can now run yarn start once more and verify our button text color (which was fuchsia) is now hotpink.

What have we just done?

Let’s take a break from coding and think about the bigger picture and what we’ve just done. Basically, we’ve set up a system where any changes to our CSS package’s button.css will get copied over into all the framework implementations as a result of our copystyles.js Node scripts. Further, we’ve incorporated idiomatic conventions for each of the frameworks:

  • SFC for Vue and Svelte
  • CSS Modules for React (and Vue within the SFC <style module> setup)
  • ViewEncapsulation for Angular

Of course I state the obvious that these aren’t the only ways to do CSS in each of the above frameworks (e.g. CSS-in-JS is a popular choice), but they are certainly accepted practices and are working quite well for our greater goal — to have a single CSS source of truth to drive all framework implementations.

If, for example, our button was in use and our design team decided we wanted to change from 4px to 3px border-radius, we could update the one file, and any separate implementations would stay synced.

This is compelling if you have a polyglot team of developers that enjoy working in multiple frameworks, or, say an offshore team (that’s 3× productive in Angular) that’s being tasked to build a back-office application, but your flagship product is built in React. Or, you’re building an interim admin console and you’d love to experiment with using Vue or Svelte. You get the picture.

Finishing touches

OK, so we have the monorepo architecture in a really good spot. But there’s a few things we can do to make it even more useful as far as the developer experience goes.

Better start scripts

Let’s move back up to our top-level monorepo directory and update its package.json scripts section with the following so we can kick any framework implementation without cd‘ing:

// ...
"scripts": {
  "start:react": "yarn workspace littlebutton-react dev",
  "start:vue": "yarn workspace littlebutton-vue dev ",
  "start:svelte": "yarn workspace littlebutton-svelte dev",
  "start:angular": "yarn workspace littlebutton-angular start"
},

Better baseline styles

We can also provide a better set of baseline styles for the button so it starts from a nice, neutral place. Here’s what I did in the littlebutton-css/css/button.css file.

View Full Snippet
.btn {
  --button-dark: #333;
  --button-line-height: 1.25rem;
  --button-font-size: 1rem;
  --button-light: #e9e9e9;
  --button-transition-duration: 200ms;
  --button-font-stack:
    system-ui,
    -apple-system,
    BlinkMacSystemFont,
    "Segoe UI",
    Roboto,
    Ubuntu,
    "Helvetica Neue",
    sans-serif;

  display: inline-flex;
  align-items: center;
  justify-content: center;
  white-space: nowrap;
  user-select: none;
  appearance: none;
  cursor: pointer;
  box-sizing: border-box;
  transition-property: all;
  transition-duration: var(--button-transition-duration);
  color: var(--button-dark);
  background-color: var(--button-light);
  border-color: var(--button-light);
  border-style: solid;
  border-width: 1px;
  font-family: var(--button-font-stack);
  font-weight: 400;
  font-size: var(--button-font-size);
  line-height: var(--button-line-height);
  padding-block-start: 0.5rem;
  padding-block-end: 0.5rem;
  padding-inline-start: 0.75rem;
  padding-inline-end: 0.75rem;
  text-decoration: none;
  text-align: center;
}

/* Respect users reduced motion preferences */
@media (prefers-reduced-motion) {
  .btn {
    transition-duration: 0.001ms !important;
  }
}

Let’s test this out! Fire up each of the four framework implementations with the new and improved start scripts and confirm the styling changes are in effect.

Neutral (gray) styled button from the monorepo framework

One CSS file update proliferated to four frameworks — pretty cool, eh!?

Set a primary mode

We’re going to add a mode prop to each of our button’s and implement primary mode next. A primary button could be any color but we’ll go with a shade of green for the background and white text. Again, in the baseline stylesheet:

.btn {
  --button-primary: #14775d;
  --button-primary-color: #fff;
  /* ... */
}

Then, just before the @media (prefers-reduced-motion) query, add the following btn-primary to the same baseline stylesheet:

.btn-primary {
  background-color: var(--button-primary);
  border-color: var(--button-primary);
  color: var(--button-primary-color);
}

There we go! Some developer conveniences and better baseline styles!

Updating each component to take a mode property

Now that we’ve added our new primary mode represented by the .btn-primary class, we want to sync the styles for all four framework implementations. So, let’s add some more package.json scripts to our top level scripts:

"sync:react": "yarn workspace littlebutton-react syncStyles",
"sync:vue": "yarn workspace littlebutton-vue syncStyles",
"sync:svelte": "yarn workspace littlebutton-svelte syncStyles",
"sync:angular": "yarn workspace littlebutton-angular syncStyles"

Be sure to respect JSON’s comma rules! Depending on where you place these lines within your scripts: {...}, you’ll want to make sure there are no missing or trailing commas.

Go ahead and run the following to fully synchronize the styles:

yarn sync:angular && yarn sync:react && yarn sync:vue && yarn sync:svelte

Running this doesn’t change anything because we haven’t applied the primary class yet, but you should at least see the CSS has been copied over if you go look at the framework’s button component CSS.

React

If you haven’t already, double-check that the updated CSS got copied over into littlebutton-react/src/button.css. If not, you can run yarn syncStyles. Note that if you forget to run yarn syncStyles our dev script will do this for us when we next start the application anyway:

"dev": "yarn syncStyles && vite",

For our React implementation, we additionally need to add a composed CSS Modules class in littlebutton-react/src/button.module.css that is composed from the new .btn-primary:

.btnPrimary {
  composes: btn-primary from './button.css';
}

We’ll also update littlebutton-react/src/App.jsx:

import "./App.css";
import styles from "./button.module.css";

const Button = ({ mode }) => {
  const primaryClass = mode ? styles[`btn${mode.charAt(0).toUpperCase()}${mode.slice(1)}`] : '';
  const classes = primaryClass ? `${styles.btn} ${primaryClass}` : styles.btn;
  return <button className={classes}>Go</button>;
};

function App() {
  return (
    <div className="App">
      <Button mode="primary" />
    </div>
  );
}

export default App;

Fire up the React app with yarn start:react from the top-level directory. If all goes well, you should now see your green primary button.

A dark green button with white text positioning in the center of the screen.

As a note, I’m keeping the Button component in App.jsx for brevity. Feel free to tease out the Button component into its own file if that bothers you.

Vue

Again, double-check that the button styles were copied over and, if not, run yarn syncStyles.

Next, make the following changes to the <script> section of littlebutton-vue/src/components/Button.vue:

<script>
export default {
  name: 'Button',
  props: {
    mode: {
      type: String,
      required: false,
      default: '',
      validator: (value) => {
        const isValid = ['primary'].includes(value);
        if (!isValid) {
          console.warn(`Allowed types for Button are primary`);
        }
        return isValid;
      },
    }
  },
  computed: {
    classes() {
      return {
        [this.$style.btn]: true,
        [this.$style['btn-primary']]: this.mode === 'primary',
      }
    }
  }
}
</script>

Now we can update the markup in littlebutton-vue/src/App.vue to use the new mode prop:

<Button mode="primary">Go</Button>

Now you can yarn start:vue from the top-level directory and check for the same green button.

Svelte

Let’s cd into littlebutton-svelte and verify that the styles in littlebutton-svelte/src/Button.svelte have the new .btn-primary class copied over, and yarn syncStyles if you need to. Again, the dev script will do that for us anyway on the next startup if you happen to forget.

Next, update the Svelte template to pass the mode of primary. In src/App.svelte:

<script>
  import Button from './Button.svelte';
</script>
<main>
  <Button mode="primary">Go</Button>
</main>

We also need to update the top of our src/Button.svelte component itself to accept the mode prop and apply the CSS Modules class:

<button class="{classes}">
  <slot></slot>
</button>
<script>
  export let mode = "";
  const classes = [
    "btn",
    mode ? `btn-${mode}` : "",
  ].filter(cls => cls.length).join(" ");
</script>

Note that the <styles> section of our Svelte component shouldn’t be touched in this step.

And now, you can yarn dev from littlebutton-svelte (or yarn start:svelte from a higher directory) to confirm the green button made it!

Angular

Same thing, different framework: check that the styles are copied over and run yarn syncStyles if needed.

Let’s add the mode prop to the littlebutton-angular/src/app/app.component.html file:

<main>
  <little-button mode="primary">Go</little-button>
</main>

Now we need to set up a binding to a classes getter to compute the correct classes based on if the mode was passed in to the component or not. Add this to littlebutton-angular/src/components/button.component.html (and note the binding is happening with the square brackets):

<button [class]="classes">Go</button>

Next, we actually need to create the classes binding in our component at littlebutton-angular/src/components/button.component.ts:

import { Component, Input } from '@angular/core';

@Component({
  selector: 'little-button',
  templateUrl: './button.component.html',
  styleUrls: ['./button.component.css'],
})
export class ButtonComponent {
  @Input() mode: 'primary' | undefined = undefined;

  public get classes(): string {
    const modeClass = this.mode ? `btn-${this.mode}` : '';
    return [
      'btn',
      modeClass,
    ].filter(cl => cl.length).join(' ');
  }
}

We use the Input directive to take in the mode prop, then we create a classes accessor which adds the mode class if it’s been passed in.

Fire it up and look for the green button!

Code complete

If you’ve made it this far, congratulations — you’ve reached code complete! If something went awry, I’d encourage you to cross-reference the source code over at GitHub on the the-little-button-that-could-series branch. As bundlers and packages have a tendency to change abruptly, you might want to pin your package versions to the ones in this branch if you happen to experience any dependency issues.

Take a moment to go back and compare the four framework-based button component implementations we just built. They’re still small enough to quickly notice some interesting differences in how props get passed in, how we bind to props, and how CSS name collisions are prevented among other subtle differences. As I continue to add components to AgnosticUI (which supports these exact same four frameworks), I’m continually pondering which offers the best developer experience. What do you think?

Homework

If you’re the type that likes to figure things out on your own or enjoys digging in deeper, here are ideas.

Button states

The current button styles do not account for various states, like :hover. I believe that’s a good first exercise.

/* You should really implement the following states
   but I will leave it as an exercise for you to 
   decide how to and what values to use.
*/
.btn:focus {
  /* If you elect to remove the outline, replace it
     with another proper affordance and research how
     to use transparent outlines to support windows
     high contrast
  */
}
.btn:hover { }
.btn:visited { }
.btn:active { }
.btn:disabled { }

Variants

Most button libraries support many button variations for things like sizes, shapes, and colors. Try creating more than the primary mode we already have. Maybe a secondary variation? A warning or success? Maybe filled and outline? Again, you can look at AgnosticUI’s buttons page for ideas.

CSS custom properties

If you haven’t started using CSS custom properties yet, I’d strongly recommend it. You can start by having a look at AgnosticUI’s common styles. I heavily lean on custom properties in there. Here are some great articles that cover what custom properties are and how you might leverage them:

Types

No… not typings, but the <button> element’s type attribute. We didn’t cover that in our component but there’s an opportunity to extend the component to other use cases with valid types, like button, submit, and reset. This is pretty easy to do and will greatly improve the button’s API.

More ideas

Gosh, you could do so much — add linting, convert it to Typescript, audit the accessibility, etc.

The current Svelte implementation is suffering from some pretty loose assumptions as we have no defense if the valid primary mode isn’t passed — that would produce a garbage CSS class:

mode ? `btn-${mode}` : "",

You could say, “Well, .btn-garbage as a class isn’t exactly harmful.” But it’s probably a good idea to style defensively when and where possible.

Potential pitfalls

There are some things you should be aware of before taking this approach further:

  • Positional CSS based on the structure of the markup will not work well for the CSS Modules based techniques used here.
  • Angular makes positional techniques even harder as it generates :host element representing each component view. This means you have these extra elements in between your template or markup structure. You’ll need to work around that.
  • Copying styles across workspace packages is a bit of an anti-pattern to some folks. I justify it because I believe the benefits outweigh the costs; also, when I think about how monorepos use symlinks and (not-so-failproof) hoisting, I don’t feel so bad about this approach.
  • You’ll have to subscribe to the decoupled techniques used here, so no CSS-in-JS.

I believe that all approaches to software development have their pros and cons and you ultimately have to decide if sharing a single CSS file across frameworks works for you or your specific project. There are certainly other ways you could do this (e.g. using littlebuttons-css as an npm package dependency) if needed.

Conclusion

Hopefully I’ve whet your appetite and you’re now really intrigued to create UI component libraries and/or design systems that are not tied to a particular framework. Maybe you have a better idea on how to achieve this — I’d love to hear your thoughts in the comments!

I’m sure you’ve seen the venerable TodoMVC project and how many framework implementations have been created for it. Similarly, wouldn’t it be nice to have a UI component library of primitives available for many frameworks? Open UI is making great strides to properly standardize native UI component defaults, but I believe we’ll always need to insert ourselves to some extent. Certainly, taking a good year to build a custom design system is quickly falling out of favor and companies are seriously questioning their ROI. Some sort of scaffolding is required to make the endeavor practical.

The vision of AgnosticUI is to have a relatively agnostic way to build design systems quickly that are not tied down to a particular frontend framework. If you’re compelled to get involved, the project is still very early and approachable and I’d love some help! Plus, you’re already pretty familiar with the how the project works now that you’ve gone through this tutorial!


How to Make a Component That Supports Multiple Frameworks in a Monorepo originally published on CSS-Tricks. You should get the newsletter and become a supporter.

Things To Expect From A Smashing Workshop: Form Design Masterclass

It took me around six months on and off to write the content for the workshop. After a lot of deliberation, I decided to structure it like I do in my book, Form Design Patterns.

It was a 4-day workshop split into two 45-minute segments, with 15-minute breaks followed by a 30-minute Q&A with optional homework between days. Each day we set out to solve one big problem. This provided a way to approach the problem like we do in real life: by analyzing and discussing the options before arriving at a good solution.

Overall, it was a fun experience. I learned a lot and had a great time teaching and chatting with everyone. I’m already looking forward to the next one which is tentatively planned for late 2021.

Some Of The Highlights Of Each Day

Here’s a quick rundown of each day including some of the highlights.

Day 1: Nailing The Basics Of Form Design

On the first day, we designed a simple registration form from scratch. This provided a perfect way to nail the basics of form design. It covered things like label positioning, form styling and input types. At the end of day 1, we had ourselves a registration form that covered the basics and made the form as simple as possible for users.

My highlight of this session was the question protocol exercise. Instead of focusing on how to artificially save space on forms (by using things like float labels, tooltips, left-aligned labels and placeholder text), we used a spreadsheet to help know why every question is being asked and the best way to elicit the answer.

For our registration form, this meant a thorough analysis of asking for someone’s name, email address and password. And by the end of the exercise we had halved the number of form fields and had clear justification for the ones that remained.

Day 2: Form Validation And Writing Good Error Messages

On the second day, we took our well-designed registration form and looked at how to help users recover from errors in two ways:

  1. We decided when to validate forms and how to display error messages;
  2. We learnt how to write clear, concise, consistent and specific error messages that help users get back on track quickly.

My highlight of this session was the exercise to redesign the error messages on Smashing Magazine’s very own membership sign up form.

Sophy Colbert, a content designer who attended the workshop, volunteered to share her new error messages explaining her rationale for each one.

Both the messages and the rationale were superb, and I think the group got a lot out of it as they could get an insight into Sophy’s content designer mindset.

Day 3: Redesigning A Real World Checkout Form

On day 3, we redesigned the ASOS checkout flow from scratch. This included guest checkout (first time experience) and checking out as someone with an account (repeat-use experience). We covered a lot of ground such as whether to use tabs, accordions or radio buttons. And we also looked at single page checkouts versus multi-page checkouts.

My highlight of this session was that the process of redesigning several interactions, exposed new content design and service design challenges. For example, we converted the tabs that ask the user to specify whether they have an account or not:

And we redesigned them into a form with radio buttons:

And this exposed the problem that in real life, choices are rarely binary. So I asked the group what the missing option was and they rightly said: ‘What if the user can’t remember?’

So even though we originally looked at this primarily as an interaction design problem, it became an issue of content and service design.

All of these problems nicely encapsulated one of the form UX rules: ‘Make friends with other departments’. As designers, we have to work effectively with stakeholders across the organisation to make sure we avoid as much complexity as possible. And this again is where the question protocol really shines.

Day 4: Using Shorthand Syntax And Designing Long And Complex Forms

Day 4 was split into two parts which I’ll discuss in reverse order.

In the second part, we looked at various patterns that help users fill out long and complex forms — the kind of forms that take days, weeks or even months to complete. I was really looking forward to running this because the design challenges around this are interesting and not well trodden.

In the first part, we redesigned Smashing Magazine’s registration form using shorthand syntax.

My highlight of this session was that Vitaly, Mr. Smashing Magazine himself, came along to be our business stakeholder. The group asked him questions to work out why the form was designed the way it was and asking why certain questions were asked.

Here are a few examples:

  • Sophy O asked why the country field is being asked for. Vitaly said that it depends on what the user is doing. If the user is buying a book, we need to know where it’s going. And the taxes on the book are based on the destination country. This resulted in either removing the field and asking for this information when someone buys the book — or just being clearer in hint text about why we’re asking for this information.
  • Milos Lazarevic questioned the need for the ‘Do you like cats?’ checkbox. And Dana Cottreau and Jaclyn Ziegler enjoyed the playfulness of the checkbox. But I would weigh up the joy it brings some people against the risk of alienating people who are, for example, less digitally savvy or are simply in a rush to access the content.
  • Emma Stotz questioned the use of live validation given all the usability issues that pop up around that. And Vitaly was keen to explore instantly validating the fields on submit instead.

My Overall Impression

For me, the workshop went very well overall and I was chuffed with the way things went and the feedback I received from the attendees. Everyone was so friendly, and tolerant of a couple of technical difficulties I had on the first day (thanks again, everyone!). Running the workshop remotely over Zoom has its problems (we won’t talk about how I accidentally left the meeting in a panic by accident on day 1), but actually I found the remote aspect useful on the whole.

For example, all being connected to Zoom, meant it was seamless for attendees to ask questions while sharing their screen to bring the problems to life.

I also really enjoyed meeting people across the world, something that would have been difficult with in-person workshops I think. Also, during the break, I got to quickly dash to put my kids to bed, so I imagine that also worked well for the attendees, too.

But there’s one thing I wish I knew earlier. I was worried, that with such a large group of people (81 to be exact), letting people talk freely would end up in a chaos. As a result, on day 1, I read out and answered group's questions from the shared Google Doc during the Q&A. This meant that other people’s voices weren’t heard and there was more of a barrier between me and the group.

This is something I rectified for day 2 and it really made a difference. It was nice to hear people’s voices and thoughts in their own words and it created more of an open dialogue where other people started to answer other people’s questions which I loved.

I remember Alex Price jumping in once to talk about his experience in dealing with a complicated form that needed to be completed by different people.

What I’ll Change For Next Time

While my overall impression of the workshop was very positive, there were some things I’d look to improve for next time.

1. Show The Basics, Not Learn The Basics

Day 1 covered a lot of the basics before going into greater detail on the following days, but it bothered me a bit to teach some of these things as I thought many attendees knew a lot of this stuff already. So next time I'd like to acknowledge that some people have come with a lot of knowledge and set the scene as ‘this is how I teach the basics’ as opposed to ‘this is how to learn the basics’ — thanks to Caroline Jarrett for this tip.

Also, I’ll probably ask the group if there’s any form design approach that they’ve struggled to convince teammates about as that’s certainly something I’ve struggled with before.

2. Split People Up In Bigger Groups

One of the exercises involved people splitting up into groups of 2 using the Zoom breakout rooms, but because people came to this workshop from all over the world, some of the people listening were not able to take part in the exercises.

For example, some people really needed to take a lunch break because their time zone was ahead of mine. This meant one or two people who did want to participate found themselves in a group on their own. Next time, I’d put people into groups of say 4 and make sure the exercises still work.

3. Add More Group Exercises

Despite the issue I just mentioned, the group exercises worked well. People enjoyed them, and it sparked some really interesting ideas from the participants. Some people messaged me after saying that they wished there were more group exercises, so I’ll aim to do just that.

A Poster Of All The Rules

As we moved through the workshop, we ticked off over 40 rules and principles of form design which brought a nice additional structure to the sessions.

A few of the attendees asked me if I had a poster of all the rules and I didn’t — so now I’ve made one.

Form Design Masterclass Poster (Plain Text Version)

For your convience, here’s a simple text version of the poster — feel free to adjust it and customize it to your needs.

Day 1: Nailing The Basics Of Form Design

  1. Make forms work well for everyone
  2. Every form control needs a label
  3. Only add hint text if it adds value
  4. Don’t use placeholder text
  5. Put hint text between the label and the input
  6. Put labels above the input
  7. Don’t use tooltips for hint text
  8. Know why you’re asking every question*
  9. Give text boxes a distinct border
  10. Position labels to be associated with the input
  11. Give inputs a clear focus state
  12. Use the right input type for the job
  13. Align the button to the left edge of the inputs
  14. Label the button with exactly what it does
  15. Make sure your form is actually necessary
  16. Avoid putting two forms on one page
  17. Use multiple inputs as a last resort
  18. Don’t use input masks

Day 2: Validating Forms And Writing Good Error Messages

  1. Don’t disable the submit button
  2. Don’t trigger errors when the user is answering
  3. Only validate when the user submits
  4. Put errors above the input
  5. Forgive trivial mistakes
  6. Track your errors
  7. Give users clear, concise and specific errors

Day 3: Redesigning A Real Checkout Flow

  1. Postpone questions you could ask later
  2. Use form controls inside forms
  3. Start without a progress bar*
  4. Begin prototyping with one thing per page
  5. Ask questions in a sensible order
  6. Use select boxes as a last resort
  7. Use sensible defaults
  8. Provide help in context of the question
  9. Avoid optional fields wherever possible
  10. Don’t hide the submit button
  11. Make the field width match the expected value
  12. Let users check their answers
  13. Put the back link at the top left of the form
  14. Make friends with other departments

Day 4: Using Shorthand And Designing Long And Complex Forms

  1. Break down huge forms into small tasks
  2. Tell users what they need before they start
  3. Help users check their eligibility

* This principle is from the GOV.UK Service Manual
** This principle is from the NHS Service Manual.

Thanks again to everyone who came for all their contributions. I’m looking forward to the next one.

Thanks to Caroline Jarrett for not only reviewing every detail of my workshop but for also editing this article.


Editor’s Note: You can also check a detailed overview of How We Run Smashing Online Workshops, and if you are interested in attending one, we have plenty of online workshops on front-end & UX coming up soon. We’d love to see you there!

Feature Prioritizing: Ways To Reduce Subjectivity And Bias

How familiar is this scenario: A team employs modern decision-making methods and performs all design-thinking rituals, but the result remains guesswork. Or this: Soon after having prioritized all features, the key stakeholders change their mind and you have to plan everything again. Both situations have happened to my team and my colleagues quite a few times.

Feature prioritization succeeds or fails because of one tiny thing, and I won’t keep you in suspense until the end of this article to find out. The key factor is selection criteria. But first things first. Let’s see what can go wrong, and then we’ll talk about ways to mitigate those risks.

Flaws of Popular Prioritization Methods

Challenge 1: Non-Experts and Experts Have the Same Voting Power

Product teams strive to make the right trade-offs and marry an infinite number of options with limited resources. Typically, a decision appears as a result of collaborative activities, such as dot voting, the value-versus-feasibility canvas, MoSCoW, the Kano model, etc.

While these techniques were invented by different people, they essentially work the same way: Team members put sticky notes with all feature ideas on a board, and then they shortlist the most promising ones. Either participants rate the ideas with marks or votes or they arrange them along the axes according to how feasible, desirable, or innovative each feature is.

Such a manifestation of democracy works great when you involve experts — people who know the topic inside out or who, as Danish physicist Niels Bohr puts it, “have made all the mistakes that can be made in a very narrow field.” When everyone on a team is an expert, then the distribution of votes will indicate the best ideas.

But let’s be honest: Workshops often have a flavor of office politics. For example, a workshop might involve high-power stakeholders with low interest in what you are building, or you might have to invite non-essential specialists who lose motivation and affect the work of the whole team. That’s why it becomes so easy to end up with only two or three people in the room who can make informed decisions.

In real life, “popular” doesn’t equal “the best”. And as a facilitator, you’re eager to bring the strongest opinions to light, which becomes problematic when an expert’s voice weighs the same as a non-expert’s.

Challenge 2: People Don’t Decide Rationally by Default

Even if you involve experts, they could represent diverse areas and domains; thus, they’ll make choices differently. Besides, rational thinking is not the default mode, even for knowledgeable and skilled people.

Humans have to cope with many concurrent thinking processes and are exposed to over 180 cognitive biases. The priming effect is an example: What happens to a person right before a workshop will affect their behavior during the workshop. So, how do you ensure that expertise — not personal preference or emotion — drives feature prioritization?

It’s almost impossible to guess the reasoning behind each choice afterwards — unless you somehow support rational thinking in advance.

Business is not all fun and games: Teams have to make hard decisions based on data and leave their whims, tastes, and prejudices at the door. As a facilitator, you certainly don’t want to make a business decision based on what stakeholders like or how they feel at the moment, do you? But in many exercises, “I love this idea” turns out to be no less trusted than “This will help our company grow.”

Challenge 3: Measurement Units Are Open to Interpretation

Another trap in prioritization activities is the measurement system, such as:

  • numeric marks (from 1 to 5, the Fibonacci scale, etc.);
  • symbols (dots, stars, smileys, etc.);
  • metaphors (for example, pebble, rock, boulder);
  • t-shirt sizing (S, M, L, XL);
  • the position of an item on the horizontal or vertical axis of a canvas.

Getting a certain number of votes or special measurement units is intended to balance opinions during a prioritization exercise. But they don’t take into account how differently people perceive reality, not to mention cultural differences on global teams. An aspect that is critical to one person might be insignificant to another.

For example, if I hear “good” instead of “awesome” or “fantastic” from a US client, I know I’m in trouble. It means they aren’t quite satisfied. But “good” is a common expression of praise in Europe. The same goes for votes: An S-size task will mean one thing to an in-house senior back-end developer and another thing to a marketing consultant.

Moreover, many people are now Design Thinking-savvy and Agile-savvy and can subconsciously manipulate votes or intentionally exploit the vagueness of a measurement system to push their own ideas.

If an argument between team members gets out of hand, you’ll spend a lot of time in vain and won’t reach consensus on time. Or worse, the debate will end up in forced agreement of the idea advocated by the most influential stakeholder in the room. So, how can we handle prioritization better?

Overcoming Prioritization Bias

Method 1: Annotated Marks

In one of my projects, we were designing a complex solution that involved technology, business processes, and the expertise of hundreds of people worldwide. Therefore, we couldn’t narrowly define the expected value of features (like user satisfaction or usability) because it wasn’t solely about end users or interfaces.

Our team identified five stakeholder types who would benefit from the solution, and we came up with a descriptive scale to evaluate features. It took into account both stakeholder coverage and the significance of tasks that the solution could potentially help them with.

Of course, we could have used a simple scale of 1 to 5, where 1 represented the lowest value and 5 the highest. But it wouldn’t have given us clarity on what each feature’s value means in reality. Besides, evaluating items in a vacuum is always challenging. “Low” related to what? “Medium” compared to what? Such questions will undoubtedly arise.

Another example from the same project: an effort estimation scale. Again, we decided to add real-life descriptions. Instead of the abstract “low”, “medium”, and “high”, we gave marks according to how much workforce and money should be involved in the feature’s implementation. We knew that the factor that would largely determine the level of effort required was whether we could do it ourselves or do it only together with a third party.

As a result, numbers gained meaning.

Later, we created a nerdy table that combined multiple characteristics. This helped us to check whether a feature had well-balanced feasibility, desirability, and profitability — simply put, whether it could be done, would be desired by customers, and would make money for the business.

Depending on your project, the criteria can vary. One project might call for you to evaluate revenue potential and implementation effort, whereas in another you might have to focus heavily on ease of adoption, expected deployment effort, and estimated cost of maintenance. In any case, the method remains the same: First, define essential criteria, then build a meaningful scale, and, finally, evaluate.

How to build such a scale? Start from the extremes — the minimal and maximal marks. What does 1 (or 0) mean? What does 5, 10, or whatever the maximum is mean?

When the minimal and maximal marks are defined (1 and 5 in the example above), you can write a description for the middle mark (3) and then for the remaining marks (2 and 4). Such an approach helps to maintain more or less equal increments between the mark definitions.

In a Nutshell
  • Method
    Add real-life descriptions to abstract numeric marks.
  • Strengths
    Clarity in selection criteria makes for easier agreement, less subjectivity, and less time spent on discussions.
  • Limitations
    Developing a meaningful scale needs time; such a scale is contextual and might not be reused for another project.

Method 2: Descriptive Canvas

This technique is a logical continuation of the previous one but adapted for use on a canvas. Unlike ranking in a table, a canvas offers more flexible representation and more distinct winners. However, with vague criteria, you run the risk of destroying the whole exercise.

The main problem with low-to-high scales is their categorical nature. No author of an idea will ever admit it is of low value. They’ll stand their ground persuading team members to put the sticky note anywhere but in the “low-low” zone. Alternatively, you might discover that all of the “outsider” ideas just belong to less powerful stakeholders.

Minimize subjectivity by using concrete descriptions, which participants can match with what they’ve experienced in previous projects. “Difficult” could mean anything, but “Needs external expertise and resources” gives a better impression of the difficulty. The same goes for the expected value: “Solves a proven critical pain” serves as a filter that won’t let people push forward ideas not backed up by any evidence — be it user research, customer support tickets, or market analysis.

This method streamlines prioritization but at the cost of some time spent on preparing the scale, particularly on formulating concise section names.

When you work with such a canvas, beware of traffic-light color-coding. It might be a decent choice for the final output presentation, but in the workshop, it will increase bias and make people unwilling to let their votes end up in the red area.

In a Nutshell
  • Method
    Add real-life descriptions to the axes of a canvas.
  • Strengths
    Clarity in mapping criteria makes for easier agreement, less subjectivity, and less time spent on discussions.
  • Limitations
    The canvas works best with three sections on each axis; scales are contextual and might not be reused in another project.

Method 3: Diversified Votes

Voting is a quick-and-dirty way to reach consensus. With anonymity, all votes are accepted and have equal weight. Voting empowers humble stakeholders and lowers hierarchical barriers. However, it also obscures the reason behind each individual choice. And the biggest challenge is that participants need to somehow weigh all possible criteria at once and choose quickly (and, hopefully, wisely).

I’ve included classic dot voting in many planning sessions with clients, and often it yielded decisions that we would completely change later. Naturally, I wanted to avoid double work. So, during one of the sessions, we tried an enhanced version and assigned specific colors to people with different expertise — green for the “keepers” of the customer’s voice, blue for people with financial thinking, and red for technical specialists who can evaluate feasibility.

First of all, this approach gave us a sense of what people might have thought about while making a choice. Secondly, we narrowed down the list of feature winners. Only a few sticky notes gained votes from all three colors and were recognized as profitable, feasible, and valuable to customers simultaneously.

This approach enabled us to focus on the best features and not be distracted by one-sidedly promising items. With classic voting, we usually had five to seven finalists. And diversified voting revealed only two or three top ideas that matched all of the criteria.

In a Nutshell
  • Idea
    Give people with different expertise dots of different colors.
  • Strengths
    It narrows down the number of final ideas; it takes into account both the number of votes and the balance of various benefits; and it remains a quick and simple exercise.
  • Limitations
    It still doesn’t fully eliminate subjectivity.

One More Thing: Language!

There is one utterance that can ruin prioritization: “Vote for the features you like the most”, or a variation, “Now choose your favorite ideas.” These words open the gates of the Hell of Subjectivity, and they grant your team an official invitation to fantasize and speculate.

Not Recommended
  • “Stick the dots on the features you like the most.”
  • “Now, please vote for the best features.”
  • “Choose the most valuable features and vote for them.”
  • “What are your favorite ideas on the whiteboard?”

Instead of giving these unhelpful instructions, put people in a rational mood and help them listen to their inner voice of reason.

Recommended
  • “Based on your knowledge and on precedents from your practice, which of the feature ideas would pay off the soonest?”
  • “Please recall a recent development project — specifically, how long it took and what slowed or blocked the work. Now, which of the feature ideas on the board would be easiest to implement?”
  • “In a minute, we’ll vote on the expected value for customers. Let’s recall what they complained about in support tickets, what they requested in interviews, and what they used the most according to our analytics. So, which of the features presented on the whiteboard address the most critical needs?”
  • “Recall your conversations with end users and recent user-research results. Which features address their most acute pains?”

Summary and Miro Templates

Subjectivity is a part of human nature. We inevitably make emotional decisions, but there are ways to make a choice a little less biased. Facilitators have no control over what is happening in experts’ minds, but we can try to put team members in the right decision-making mood. I recommend two fundamental things to streamline decision-making:

  1. Announce, repeat, and embed meaningful selection or voting criteria into your decision-making process.
  2. Push people to think about their relevant professional experience and data from prior research, rather than their own preference.

Feel free to use these Miro templates for prioritization exercises.

React Integration Testing: Greater Coverage, Fewer Tests

Integration tests are a natural fit for interactive websites, like ones you might build with React. They validate how a user interacts with your app without the overhead of end-to-end testing. 

This article follows an exercise that starts with a simple website, validates behavior with unit and integration tests, and demonstrates how integration testing delivers greater value from fewer lines of code. The content assumes a familiarity with React and testing in JavaScript. Experience with Jest and React Testing Library is helpful but not required.

There are three types of tests:

  • Unit tests verify one piece of code in isolation. They are easy to write, but can miss the big picture.
  • End-to-end tests (E2E) use an automation framework — such as Cypress or Selenium — to interact with your site like a user: loading pages, filling out forms, clicking buttons, etc. They are generally slower to write and run, but closely match the real user experience.
  • Integration tests fall somewhere in between. They validate how multiple units of your application work together but are more lightweight than E2E tests. Jest, for example, comes with a few built-in utilities to facilitate integration testing; Jest uses jsdom under the hood to emulate common browser APIs with less overhead than automation, and its robust mocking tools can stub out external API calls.

Another wrinkle: In React apps, unit and integration are written the same way, with the same tools. 

Getting started with React tests

I created a simple React app (available on GitHub) with a login form. I wired this up to reqres.in, a handy API I found for testing front-end projects.

You can log in successfully:

…or encounter an error message from the API:

The code is structured like this:

LoginModule/
├── components/
⎪   ├── Login.js // renders LoginForm, error messages, and login confirmation
⎪   └── LoginForm.js // renders login form fields and button
├── hooks/
⎪    └── useLogin.js // connects to API and manages state
└── index.js // stitches everything together

Option 1: Unit tests

If you’re like me, and like writing tests — perhaps with your headphones on and something good on Spotify — then you might be tempted to knock out a unit test for every file. 

Even if you’re not a testing aficionado, you might be working on a project that’s “trying to be good with testing” without a clear strategy and a testing approach of “I guess each file should have its own test?”

That would look something like this (where I’ve added unit to test file names for clarity):

LoginModule/
├── components/
⎪   ├── Login.js
⎪   ├── Login.unit.test.js
⎪   ├── LoginForm.js
⎪   └── LoginForm.unit.test.js
├── hooks/
⎪   ├── useLogin.js 
⎪   └── useLogin.unit.test.js
├── index.js
└── index.unit.test.js

I went through the exercise of adding each of these unit tests on on GitHub, and created a test:coverage:unit  script to generate a coverage report (a built-in feature of Jest). We can get to 100% coverage with the four unit test files:

100% coverage is usually overkill, but it’s achievable for such a simple codebase.

Let’s dig into one of the unit tests created for the onLogin React hook. Don’t worry if you’re not well-versed in React hooks or how to test them.

test('successful login flow', async () => {
  // mock a successful API response
  jest
    .spyOn(window, 'fetch')
    .mockResolvedValue({ json: () => ({ token: '123' }) });


  const { result, waitForNextUpdate } = renderHook(() => useLogin());


  act(() => {
    result.current.onSubmit({
      email: 'test@email.com',
      password: 'password',
    });
  });


  // sets state to pending
  expect(result.current.state).toEqual({
    status: 'pending',
    user: null,
    error: null,
  });


  await waitForNextUpdate();


  // sets state to resolved, stores email address
  expect(result.current.state).toEqual({
    status: 'resolved',
    user: {
      email: 'test@email.com',
    },
    error: null,
  });
});

This test was fun to write (because React Hooks Testing Library makes testing hooks a breeze), but it has a few problems. 

First, the test validates that a piece of internal state changes from 'pending' to 'resolved'; this implementation detail is not exposed to the user, and therefore, probably not a good thing to be testing. If we refactor the app, we’ll have to update this test, even if nothing changes from the user’s perspective.

Additionally, as a unit test, this is just part of the picture. If we want to validate other features of the login flow, such as the submit button text changing to “Loading,” we’ll have to do so in a different test file.

Option 2: Integration tests

Let’s consider the alternative approach of adding one integration test to validate this flow:

LoginModule/
├── components/
⎪   ├─ Login.js
⎪   └── LoginForm.js
├── hooks/
⎪   └── useLogin.js 
├── index.js
└── index.integration.test.js

I implemented this test and a test:coverage:integration script to generate a coverage report. Just like the unit tests, we can get to 100% coverage, but this time it’s all in one file and requires fewer lines of code.

Here’s the integration test covering a successful login flow:

test('successful login', async () => {
  // mock a successful API response
  jest
    .spyOn(window, 'fetch')
    .mockResolvedValue({ json: () => ({ token: '123' }) });


  const { getByLabelText, getByText, getByRole } = render(<LoginModule />);


  const emailField = getByLabelText('Email');
  const passwordField = getByLabelText('Password');
  const button = getByRole('button');


  // fill out and submit form
  fireEvent.change(emailField, { target: { value: 'test@email.com' } });
  fireEvent.change(passwordField, { target: { value: 'password' } });
  fireEvent.click(button);


  // it sets loading state
  expect(button.disabled).toBe(true);
  expect(button.textContent).toBe('Loading...');


  await waitFor(() => {
    // it hides form elements
    expect(button).not.toBeInTheDocument();
    expect(emailField).not.toBeInTheDocument();
    expect(passwordField).not.toBeInTheDocument();


    // it displays success text and email address
    const loggedInText = getByText('Logged in as');
    expect(loggedInText).toBeInTheDocument();
    const emailAddressText = getByText('test@email.com');
    expect(emailAddressText).toBeInTheDocument();
  });
});

I really like this test, because it validates the entire login flow from the user’s perspective: the form, the loading state, and the success confirmation message. Integration tests work really well for React apps for precisely this use case; the user experience is the thing we want to test, and that almost always involves several different pieces of code working together.

This test has no specific knowledge of the components or hook that makes the expected behavior work, and that’s good. We should be able to rewrite and restructure such implementation details without breaking the tests, so long as the user experience remains the same.

I’m not going to dig into the other integration tests for the login flow’s initial state and error handling, but I encourage you to check them out on GitHub.

So, what does need a unit test?

Rather than thinking about unit vs. integration tests, let’s back up and think about how we decide what needs to be tested in the first place. LoginModule needs to be tested because it’s an entity we want consumers (other files in the app) to be able to use with confidence.

The onLogin hook, on the other hand, does not need to be tested because it’s only an implementation detail of LoginModule. If our needs change, however, and onLogin has use cases elsewhere, then we would want to add our own (unit) tests to validate its functionality as a reusable utility. (We’d also want to move the file because it wouldn’t be specific to LoginModule anymore.)

There are still plenty of use cases for unit tests, such as the need to validate reusable selectors, hooks, and plain functions. When developing your code, you might also find it helpful to practice test-driven development with a unit test, even if you later move that logic higher up to an integration test.

Additionally, unit tests do a great job of exhaustively testing against multiple inputs and use cases. For example, if my form needed to show inline validations for various scenarios (e.g. invalid email, missing password, short password), I would cover one representative case in an integration test, then dig into the specific cases in a unit test.

Other goodies

While we’re here, I want to touch on few syntactic tricks that helped my integration tests stay clear and organized.

Big waitFor Blocks

Our test needs to account for the delay between the loading and success states of LoginModule:

const button = getByRole('button');
fireEvent.click(button);


expect(button).not.toBeInTheDocument(); // too soon, the button is still there!

We can do this with DOM Testing Library’s waitFor helper:

const button = getByRole('button');
fireEvent.click(button);


await waitFor(() => {
  expect(button).not.toBeInTheDocument(); // ahh, that's better
});

But, what if we want to test some other items too? There aren’t a lot of good examples of how to handle this online, and in past projects, I’ve dropped additional items outside of the waitFor:

// wait for the button
await waitFor(() => {
  expect(button).not.toBeInTheDocument();
});


// then test the confirmation message
const confirmationText = getByText('Logged in as test@email.com');
expect(confirmationText).toBeInTheDocument();

This works, but I don’t like it because it makes the button condition look special, even though we could just as easily switch the order of these statements:

// wait for the confirmation message
await waitFor(() => {
  const confirmationText = getByText('Logged in as test@email.com');
  expect(confirmationText).toBeInTheDocument();
});


// then test the button
expect(button).not.toBeInTheDocument();

It’s much better, in my opinion, to group everything related to the same update together inside the waitFor callback:

await waitFor(() => {
  expect(button).not.toBeInTheDocument();
  
  const confirmationText = getByText('Logged in as test@email.com');
  expect(confirmationText).toBeInTheDocument();
});

Interestingly, an empty waitFor will also get the job done, because waitFor has a default timeout of 50ms. I find this slightly less declarative than putting your expectations inside of the waitFor, but some indentation-averse developers may prefer it: 

await waitFor(() => {}); // or maybe a custom util, `await waitForRerender()`


expect(button).not.toBeInTheDocument(); // I pass!

For tests with a few steps, we can have multiple waitFor blocks in row:

const button = getByRole('button');
const emailField = getByLabelText('Email');


// fill out form
fireEvent.change(emailField, { target: { value: 'test@email.com' } });


await waitFor(() => {
  // check button is enabled
  expect(button.disabled).toBe(false);
});


// submit form
fireEvent.click(button);


await waitFor(() => {
  // check button is no longer present
  expect(button).not.toBeInTheDocument();
});

Inline it comments

Another testing best practice is to write fewer, longer tests; this allows you to correlate your test cases to significant user flows while keeping tests isolated to avoid unexpected behavior. I subscribe to this approach, but it can present challenges in keeping code organized and documenting desired behavior. We need future developers to be able to return to a test and understand what it’s doing, why it’s failing, etc.

For example, let’s say one of these expectations starts to fail:

it('handles a successful login flow', async () => {
  // beginning of test hidden for clarity


  expect(button.disabled).toBe(true);
  expect(button.textContent).toBe('Loading...');


  await waitFor(() => {
    expect(button).not.toBeInTheDocument();
    expect(emailField).not.toBeInTheDocument();
    expect(passwordField).not.toBeInTheDocument();


    const confirmationText = getByText('Logged in as test@email.com');
    expect(confirmationText).toBeInTheDocument();
  });
});

A developer looking into this can’t easily determine what is being tested and might have trouble deciding whether the failure is a bug (meaning we should fix the code) or a change in behavior (meaning we should fix the test).

My favorite solution to this problem is using the lesser-known test syntax for each test, and adding inline it-style comments describing each key behavior being tested:

test('successful login', async () => {
  // beginning of test hidden for clarity


  // it sets loading state
  expect(button.disabled).toBe(true);
  expect(button.textContent).toBe('Loading...');


  await waitFor(() => {
    // it hides form elements
    expect(button).not.toBeInTheDocument();
    expect(emailField).not.toBeInTheDocument();
    expect(passwordField).not.toBeInTheDocument();


    // it displays success text and email address
    const confirmationText = getByText('Logged in as test@email.com');
    expect(confirmationText).toBeInTheDocument();
  });
});

These comments don’t magically integrate with Jest, so if you get a failure, the failing test name will correspond to the argument you passed to your test tag, in this case 'successful login'. However, Jest’s error messages contain surrounding code, so these it comments still help identify the failing behavior. Here’s the error message I got when I removed the not from one of my expectations:

For even more explicit errors, there’s package called jest-expect-message that allows you to define error messages for each expectation:

expect(button, 'button is still in document').not.toBeInTheDocument();

Some developers prefer this approach, but I find it a little too granular in most situations, since a single it often involves multiple expectations.

Next steps for teams

Sometimes I wish we could make linter rules for humans. If so, we could set up a prefer-integration-tests rule for our teams and call it a day.

But alas, we need to find a more analog solution to encourage developers to opt for integration tests in a situation, like the LoginModule example we covered earlier. Like most things, this comes down to discussing your testing strategy as a team, agreeing on something that makes sense for the project, and — hopefully — documenting it in an ADR.

When coming up with a testing plan, we should avoid a culture that pressures developers to write a test for every file. Developers need to feel empowered to make smart testing decisions, without worrying that they’re “not testing enough.” Jest’s coverage reports can help with this by providing a sanity check that you’re achieving good coverage, even if the tests are consolidated that the integration level.

I still don’t consider myself an expert on integration tests, but going through this exercise helped me break down a use case where integration testing delivered greater value than unit testing. I hope that sharing this with your team, or going through a similar exercise on your codebase, will help guide you in incorporating integration tests into your workflow.

The post React Integration Testing: Greater Coverage, Fewer Tests appeared first on CSS-Tricks.

You Are Bad at Hiring Good Engineers

I have never asked a puzzle or a trick question to find some obscure algorithmic approach. It had always been instead asking to come up with design solutions and creative implementation for real-world problems.

But when I got to the other side as a candidate, I have never been lucky — not even once. It’s always some shitty puzzle or a whiteboarding exercise about some obscure algo that no one does on a typical day job. The software industry has a long way to go to be even considered “mature”.