Get Started With UI Design With These Tips To Speed Up Your Workflow

Get Started With UI Design With These Tips To Speed Up Your Workflow

Get Started With UI Design With These Tips To Speed Up Your Workflow

Tomáš Čakloš

This article is about creating limits and rules to follow throughout the entire design process. There is an unlimited number of ways in which you can combine elements in a user interface — and so you’ll need to set some rules and boundaries, or else the design workflow might become an unpleasant chore. You might be struggling with all of the possibilities and trying to pick the best option among many “correct” options. By setting (and following) some basic rules, you will make your design look more consistent, too.

This article is intended for beginner UI designers. You don’t need a lot of experience in order to be able to follow the tips and tricks shared in it.

setting limits and rules helps designing
Having a system is important!

The Importance of Making Your User-Interface Design Consistent

Let’s start at the very beginning. You want your design to look good and trustworthy, and you need to avoid chaos at all costs. For this to happen, it’s very important to have a system for your design work.

Your developers will appreciate a system, too — they’ll love the fact that your design has order, and that you are making their work easier.

A System Of Resizing By A Predetermined Size

It doesn’t matter whether you want to resize a text block, resize an image, or adjust some white space. You need to decide how big each element will be. And I’ll bet you have been in this situation: Have you ever chosen a size for an element, and after five minutes, you change it, and then again, and maybe again and again?

Which size is perfect? It could be one of the ones you tried, right? You need to avoid this endless time-wasting trap!

Start By Choosing The Basic Unit: The 8-Pixel Grid

To make the whole design look cleaner, it’s helpful to first set the measurement value that will then determine all of the sizes. It is completely up to you what value you choose, but quite often, the best option is to stick to a few proven rules. And one of these rules is to resize and move elements by exactly eight pixels. This rule will streamline your decision-making.

Aside on px versus dp: In addition to pixels (px), you may have heard of the term dp being used in screen design and prototyping. The dp unit is short for “density-independent pixel.” The unit is relative to a 160-dpi screen, so 1 dp is equal to 1 pixel on a 160-dpi screen, and equal 2 pixels on a 320-dpi screen, and so on. The numeric value formula is px = dp * (dpi/160).

Note: If you work with smaller elements or objects, it’s also OK to use 4-pixel increments, instead of 8 — occasionally, you can make further adjustments, when required.)

But Why Exactly 8 Pixels?

There are a few reasons why eight often works like a “magic number” here:

  • Eight pixels is a sufficient minimum “jump”.
  • Eight is a great number because it is divisible by four and two.
  • If you use eight, you can easily resize any element without ending up with half pixels, as 8 / 2 = 4, 4 / 2 = 2, and 2 / 2 = 1. If, on the other hand, you start with 10, you’ll end up with 5 pixels, then 2.5 pixels, then 1.25 pixels. When designing for screen, you’d like to avoid half pixels as much as possible. By using whole pixels, elements in the design will align to precise pixel boundaries, and so will look crisper.
  • Multiples of eight (8, 16, 24, 32, 40, 48, 56, 64, 72, 80, etc.) are intertwined with binary values (1, 2, 4, 8, 16, 32, 64, 128, 256, 512, etc.).
  • Finally, the numbers are easy to remember.

What Are The Advantages Of Using An 8-Pixel Grid?

  • As a designer, your decision time is precious. This will make you faster and more efficient.
  • If you are working with a developer, you can create a system that will help you and your team. If the developer needs to make some quick changes, he can adjust the values by 8-pixel increments. This will ensure consistency and order.
  • People using your website will feel comfortable when they visit it. They will trust the website, and it’ll be easier for them to use the interface.
8 pixel grid
An effective way of using the 8px grid
using an 8 pixel grid
The result of using an 8px grid

Work With A Grid To Lay Out All Elements

Horizontal Harmony

I’m sure you have already used a grid when designing websites. Using a grid helps you to accurately place all elements on the digital canvas.

The grid forms the skeleton of your interface and determines where you can place elements. The template holds the composition, and it defines clear boundaries so that your design will be more consistent. Now it will be easier for you to decide where to put the elements. As you gain more experience, you can update the boundaries as needed.

But how do you create this grid? We will cover the specifics next. Basically, the number and size of columns may be random and depends on your needs. The more detailed your design, the more columns the grid will require. If you’re hesitant, ask an experienced colleague for assistance.

Also, I recommend that you read “A Comprehensive Guide to UI Design”, which should help you understand user-interface design a bit more in depth.

horizontal harmony
Horizontal harmony

Vertical Harmony

Similar to maintaining horizontal harmony, it is important to keep vertical distances consistent in a design as well. Like the rows in a spreadsheet, they help you to keep text at evenly spaced intervals.

How big should these rows be? Again, it’s up to you. However, I recommend using 8 pixels or multiples of 8 (such as 16). Redefine boundaries where elements or text are to be aligned.

vertical harmony
Vertical harmony

Picking Font Sizes The Right Way

If you look at some well-crafted designs, you will see consistency in font sizes. This is for a reason.

Note: Keep in mind also that you need only two, maybe three, fonts in your design. However, selecting the right typefaces and making them work together is beyond the scope of this tutorial.

Begin by defining a few key font sizes to use throughout the project. (For example, it would be foolish to use 30, 31, and 32 pixels. Rather, combine these three very similar sizes into one.)

Standard Font Sizes Bring Two Benefits:

  • Your design will be more consistent and more elegant.
  • It will speed up the design process and make you more efficient.

Font Sizes

When you are defining font sizes, make sure not to increase sizes by the same increment. When you are enlarging text, it should be non-linear. This means that the larger the text you are creating, the larger the increment should be.

system in font sizes
Having a system in font sizes

Let’s say you have a text block with a 12-pixel font size, and you want to enlarge it. You try 14 pixels, and you are satisfied. But then imagine that you have a large headline (40 pixels) and you want to make it bigger. Would you increase the size by only 2 pixels, from 40 to 42? Of course not. Optically, the text requires a much bigger change. You might need to increase it by 24 pixels, giving you a bigger 64-pixel headline.

In short, this means that the bigger you want the text to be, the larger the increment you will need to use. This very simple principle applies not only to text, but also to the size of buttons, white space, and everything else.

It is typically based on a geometric progression. Here is a very useful chart demonstrating font scale:

geometric progression
Geometric progression

However, for typography, one proven scale is used with font sizes that you will want to stick with forever. The scale is 12, 14, 16, 18, 20, 24, 30, 36, 48, 60, and 72 pixels.

typography scale
Proven typography scale for sizes

Text Line Height

Once you have defined all font sizes, you will want to take care of line spacing. For line height, use increments of 4 pixels again. For example, for 16-pixel text, let’s set the line height to 24 pixels. If you want the text to breathe more, then increase the row height by 4 pixels to 28.

Define Your Project’s Colors

Do you know how many color combinations exist? A lot! You will waste too much time if you don’t predefine shades of color. You can’t limit yourself to black, white, and, say, blue. For each color, you will need other shades, and it is important to set them in advance, so that the shades are consistent throughout your design project. We don’t want to create chaos in the design. Aim for 5 to 10 shades for each color. I prefer to define 9 shades for each color.

Let’s take a closer look at color shades.

Why 9 Shades Of Each Color?

  • The first advantage is color naming. Whether you are using a graphics editor or CSS code, you will definitely benefit from this tip. Each shade would be assigned a number, such as 100, 200, 300, 400, 500, 600, 700, 800, and 900. (Why hundreds? Typically, this is how cuts of typefaces are also organized.)

  • Secondly, 9 is a handy number for defining colors. The best way to prepare these shades is to prepare a row of 9 squares and fill the squares with colors. The one in the middle will be the base color. Then, you define the lightest shade (at the far left) and the darkest shade (at the far right). The next step is to select the hues in between.

nine shades of each color
Nine shades of each color

Prepare The Different Sizes, Types, And States Of Elements

When working on a design, you will usually work with a countless number of icons, buttons, and other components. Again, it’s a good idea to prepare in advance several sizes for them, and limit the options to as few as possible. During the design process, do not add other sizes, and don’t try to adjust the size of components to suit your needs. Instead, just use the ones you have already defined, and the whole design will be more consistent and clean.

Let’s look at buttons as an example. When you begin, you’ll need to define their hierarchical structure. To do so, make a button with a primary action, a button with a secondary action, and perhaps another button with a less important action. For each button, specify its status (active, inactive) and the color variant. Always try to reduce the number of elements to the most important ones.

button styles
An example of button styles

Define Other Elements’ Properties

User interface designers often use shadows in their design work. However, for less-experienced designers, shadows can sometimes be a struggle. When creating a shadow, you must set the shadow’s distance along the x-axis and y-axis, and also the blur radius, color, and transparency. Shadows can take a lot of time to fine-tune, which is why you’ll want to prepare them before diving into the design. It is helpful to prepare a set of shadows (using the same method as for colors), and then just apply them throughout the design process.

Also, be aware of all the other properties of elements that you will be working with, such as corner radius, transparency, and color gradients.

shadow styles
An example of shadow styles

White Space

Properly adjusting white space is important. Whether you offset elements from the outside (margin) or from the inside (padding), you should rely on the magic number of 8 again. Increase the offset by 8 pixels (4 for small elements). As with font size, the larger the gap you want, the larger the increment will have to be (again, you’ll need to define these increments in advance).

white space
White space


To make your designs clean and consistent, define some boundaries and a clear path through the process.

When working on each element of your design, keep in mind the following:

  • See whether you have used it already somewhere in your design. If so, you can simply copy that element.
  • Follow a horizontal and vertical rhythm, and adjust the size of elements using the steps that you defined at the very beginning.
  • Avoid complicated decisions and never-ending battles with pixels. Have a system in place.
  • Do not create the same element twice. If there is order in your design, your work will be better and more efficient, you will be able to iterate faster, and you will be able to communicate with the developers more easily. The developers will set variables that follow your styles, so define them clearly. You’ll get a clean design, and the developers will be able to create better and more sustainable code. Everyone will be happy.
Smashing Editorial (mb, il)
Meet “Inclusive Components”: Accessible, Bulletproof Front-End Patterns

Meet “Inclusive Components”: Accessible, Bulletproof Front-End Patterns

Meet “Inclusive Components”: Accessible, Bulletproof Front-End Patterns

Vitaly Friedman

Front-end accessibility is still somewhat mysterious these days. How do we build accessible buttons and dropdowns? What about keyboard-friendly tooltips, tabs and notifications? Or inclusive accordions, sliders, data tables and modals? Let’s figure it out together. Meet Inclusive Components, our new handbook for building fully accessible digital products. Download a free sample PDF (1.1 MB).

<img style="border-radius:11px;" srcset=" 400w,,q_auto/w_800/ 800w,,q_auto/w_1200/ 1200w,,q_auto/w_1600/ 1600w,,q_auto/w_2000/ 2000w" src=",q_auto/w_400/" sizes="100vw" alt="Meet Inclusive Components, our new book for building accessible, inclusive interfaces. Written by one-and-only Heydon Pickering.”>

Print + eBook

{ “sku”: “inclusive-components”, “type”: “Book”, “price”: “39.00”, “prices”: [{ “amount”: “39.00”, “currency”: “USD”, “items”: [ {“amount”: “29.00”, “type”: “Book”}, {“amount”: “10.00”, “type”: “E-Book”} ] }, { “amount”: “39.00”, “currency”: “EUR”, “items”: [ {“amount”: “29.00”, “type”: “Book”}, {“amount”: “10.00”, “type”: “E-Book”} ] } ] } $

Quality hardcover. Free worldwide shipping. 100 days money-back-guarantee.


{ “sku”: “inclusive-components”, “type”: “E-Book”, “price”: “18.00”, “prices”: [{ “amount”: “18.00”, “currency”: “USD” }, { “amount”: “18.00”, “currency”: “EUR” } ] } $

DRM-free, of course. ePUB, Kindle, PDF.
Included with Smashing Membership.

About The Book

At its heart, Inclusive Components is a detailed, practical handbook for building fully accessible interfaces. The book examines 12 common interface patterns — accordions, tables, modals, notifications, tabs, toggles, and everything in-between — through the lens of inclusion. The result is accessible and robust components we author, plug in, and use daily.

For years, Heydon Pickering, a seasoned front-end developer with a focus on accessibility, has been writing about accessible solutions. We’ve teamed up with Heydon to produce a book with common challenges and solutions that he’s been refining over all these years.

For each component, the in-depth explorations are meticulously illustrated and all solutions are available as bulletproof code snippets, applicable to your work right away. Bonus: you’ll learn how to build your own accessible components with inclusive design in mind — all in a single book. Jump to table of contents ↓

332 pages. Quality hardcover with a stitched binding and ribbon page marker. The eBook is available as PDF, ePUB, Amazon Kindle. Written and designed by Heydon. Download a sample PDF (1.1 MB).

The cover of Inclusive Components, a new book by Heydon Pickering.
The inner spreads of Inclusive Components.

Print + eBook

{ “sku”: “inclusive-components”, “type”: “Book”, “price”: “39.00”, “prices”: [{ “amount”: “39.00”, “currency”: “USD”, “items”: [ {“amount”: “29.00”, “type”: “Book”}, {“amount”: “10.00”, “type”: “E-Book”} ] }, { “amount”: “39.00”, “currency”: “EUR”, “items”: [ {“amount”: “29.00”, “type”: “Book”}, {“amount”: “10.00”, “type”: “E-Book”} ] } ] } $

Quality hardcover. Free worldwide shipping. 100 days money-back-guarantee.


{ “sku”: “inclusive-components”, “type”: “E-Book”, “price”: “18.00”, “prices”: [{ “amount”: “18.00”, “currency”: “USD” }, { “amount”: “18.00”, “currency”: “EUR” } ] } $

DRM-free, of course. ePUB, Kindle, PDF.
Included with Smashing Membership.

Table Of Contents

Each chapter tackles a single component, addressing how different and vulnerable people might read and interact with it, and how they can be better accommodated. Download a sample PDF (1.1 MB).

1. Toggle Buttons
2. A Todo List
3. Menus & Menu Buttons
4. Tooltips & Toggletips
5. A Theme Switcher
6. Tabbed Interfaces
7. Collapsible Sections
8. A Content Slider
9. Notifications
10. Data Tables
11. Modal Dialogs
12. Cards
Inclusive Components, a peek inside.
A peek inside of Inclusive Components. A photo by Drew McMellan. See more photos. (Large preview)

About The Author

Heydon Pickering

Heydon Pickering (@heydonworks) has worked with The Paciello Group, The BBC, Smashing Magazine, and Bulb Energy as a designer, engineer, writer, editor, and illustrator. He was shortlisted for Designer Of The Year in The Net Awards.

Heydon previously wrote Inclusive Design Patterns which sold over 10,000 copies. Proceeds from this title were donated to the ACLU and The Democratic Socialists Of America, to help these organizations fight fascism and create a more inclusive society.

Here’s what Heydon shared when asked why he decided to write this book:

“A few years back, I was getting bored with web design. Then responsive design came along, presenting me with fresh challenges. It made my life and my job interesting again. Then I discovered the world of web accessibility, and my enthusiasm was once again renewed. Some people seem to think I do web accessibility because I feel morally obliged, or that I would feel guilty if I didn’t.

While I believe strongly that all sorts of people should be able to access the web, I’m also grateful for the web accessibility challenges I’ve encountered, and covered in this book, for stimulating me, and giving my work new depth.

I have written this book because I want you to know how fun it can be to make your interfaces more accessible, as well as how accomplished you can feel for having done so. Thank you for reading this, and hopefully the book as well.”


Artem Sapegin“Inclusive Components is a very deep and thorough explanation of development of accessible components with real world examples. Heydon Pickering shows several alternative approaches and explains pros and cons of each. It’s also a pleasure to read!”

Artem Sapegin, front-end developer, Wayfair

Sarah Federman“Inclusive Components is chock-full of practical and comprehensive advice on building accessible UI. It’s my go-to resource after the official WCAG and ARIA documentation. I’ve found it extremely helpful when building our design system!”

Sarah Federman, senior front-end developer

Andy Bell“What Heydon achieves with his work on Inclusive Components is a pragmatic, friendly and approachable set of guides that help you to generate not just accessible components, but also resilient and progressive starting-points that will help you to build better websites and web apps in general. I often describe this work as crucial learning material for this exact reason.”

Andy Bell, independent designer & developer

A preview of the book, with examples ranging from accordions to toggles, tables, notifications, dialogs etc. Download a sample PDF (1.1 MB). Large preview.

Why This Book Might Be For You

The devil is in the detail and often the things you do with good intentions can impose accessibility barriers unknowingly. Inclusive Components is for every front-end developer who wants to learn how to detect and address potential accessibility issues in their work. The book will teach you:

  1. How to use <button> elements, how to apply styles to your toggle buttons, and how to label them.
  2. How to create managed lists that allow users to create and delete content — in an inclusive way.
  3. How to address and resolve accessibility issues with navigation menus and submenus (aka “dropdowns”).
  4. How to create accessible and keyboard-friendly tooltips and toggletips.
  5. How to create a “dark mode” theme that’s both accessible and maintainable long-term.
  6. How to build an accessible content slider to prevent harm for motion-sensitive people.
  7. How to create inclusive notifications with live regions to communicate with your users through visual and aural channels simultaneously.
  8. How to create data tables that are semantically correct, responsive, and sortable.
  9. How to build accessible dialogs and modal dialogs with performance and inclusive design in mind.
  10. How to create and group inclusive cards (e.g. for teasers).

Technical Details

Community Matters ❤️

With Inclusive Components, we’ve tried to create a very focused handbook with applicable, long-living solutions and strategies to create accessible and inclusive interfaces.

Our hope is that with Heydon’s book, you will be able to make better design and coding decisions as you build your interfaces. Perhaps it will even become one of those reference books you’ll reach to every time you need to build one of those common UI components.

Producing a book takes quite a bit of time, and we couldn’t pull it off without the support of our wonderful community. A huge shout-out to Smashing Members for their ongoing support in our adventures. As a result, the eBook is and always will be free for Smashing Members. Plus, Members get a friendly discount when purchasing their printed copy.

Stay smashing, and thank you for your ongoing support, everyone!

The cover of Inclusive Components, a new book by Heydon Pickering.

Print + eBook

{ “sku”: “inclusive-components”, “type”: “Book”, “price”: “39.00”, “prices”: [{ “amount”: “39.00”, “currency”: “USD”, “items”: [ {“amount”: “29.00”, “type”: “Book”}, {“amount”: “10.00”, “type”: “E-Book”} ] }, { “amount”: “39.00”, “currency”: “EUR”, “items”: [ {“amount”: “29.00”, “type”: “Book”}, {“amount”: “10.00”, “type”: “E-Book”} ] } ] } $

Quality hardcover. Free worldwide shipping. 100 days money-back-guarantee.


{ “sku”: “inclusive-components”, “type”: “E-Book”, “price”: “18.00”, “prices”: [{ “amount”: “18.00”, “currency”: “USD” }, { “amount”: “18.00”, “currency”: “EUR” } ] } $

DRM-free, of course. ePUB, Kindle, PDF.
Included with Smashing Membership.

More Smashing Books

Promoting best practices and providing you with practical tips to master your daily coding and design challenges has always been (and will be) at the core of everything we do at Smashing. In the past few years, we were very lucky to have worked together with some talented, caring people from the web community to publish their wealth of experience as books that stand the test of time. Alla, Adam and Andy are some of these people. Have you checked out their books already?

Smashing Editorial (il, cm)
3 Essential Design Trends, December 2019

This month’s collection of design trends is a gift to behold. Each of the trends are highly usable options that are versatile, giving you plenty of room to play and make them your own. That’s the best kind of trend, right?

Here’s what’s trending in design this month.

Whimsical Illustrations

It seems like whimsical illustrations are practically everywhere. Fun drawings that can be anything from line-style illustrations to full-color pieces of art are popping up in all kinds of projects – even for brands, companies, of business types that you might not expect. Whimsical illustrations are trending for a number of reasons:

  • They create just the right feel for a project that doesn’t need to be heavy;
  • You can design the main imagery to be whatever you want;
  • They provide a source of delight for users;
  • Every project using illustrations looks a little different, creating a custom design;
  • The proliferation of illustration kits has made creating this style easier than ever.

The thing that might be best about using whimsical illustrations is the personality they bring to a project. The right illustrated element – or series of elements – can emotionally tie users to the project while setting a scene. The possibilities are almost endless. Illustrations don’t have to apply only to lighthearted projects, even though “whimsical” might imply it. The illustrations for Violence Conjugale feature a sense of whimsy for a serious topic, and it works. (Maybe we all need a little more whimsy in our life?)

Black and Blue

It’s a classic color combination that’s making a big return to projects – black and blue palettes.

The contrast of a dark background and blue accents is eye-appealing and creates a harmonious and pleasing visual aesthetic. The projects below use this color trend in different ways, all with the same cool result.

Arm Yourself uses a black background with a black and white illustration to make bright blue lettering and accents pop off the screen. Color draws users into the interactive part of the homepage design with a drag to the bullseye instruction.

Carey uses a lighter, more teal blue on a charcoal black background for a lighter feel. The blue color pulls from the logo and brand mark with buttons that have a lot of contrast from the background and brighter elements in the design. The blue continues on the scroll with bold text on a fully black background, showing the versatility of this color choice.

Adera uses a simple blue button on a black and white image to create contrast and draw the eye to the active part of the design. The color palette flips on the scroll to a blue background with darker elements on top. Contrast is a vital factor here, helping dictate user flow and how to digest and interact with information on the screen.

Anything-But-Flat Scroll Transitions

Disclaimer: I am totally in love with this trend and can’t get enough of it.

Anything-but-flat scroll transitions is a versatile design trend that’s visually interesting and contributes to usability. (It’s a win-win!) This trend is exemplified by a transition element between “screens” or “scrolls” with a visual that isn’t just a box or flat line. Think of how most parallax designs or screen-based vertical viewpoints advance from one box to another. With this trend the transition is more fluid. The examples below do this in different ways:

Oroscopo takes advantage of the blobs trend that’s been popular all year with blob elements that create waves between content elements. Contrast between light and dark backgrounds magnify this effect, while other blog shapes create visual consistency.

MAHA Agriculture Microfinance uses a simple line between scroll elements but it’s got a texture to it that makes it just a little more visually interesting that having a flat line between the hero image area at the top and the secondary content block.

Akaru uses a pretty amazing animated fluid design to offset the branding in the center of the screen. The animation carries into the background of the content below the scroll. (You’ll want to scroll all the way to the bottom of this one to see the transition from the dark animation back to white.) The effect is stunning.

Here’s why this trend works so beautifully: The fluid transition is somewhat disruptive because the user doesn’t expect this visual and seeing the edge of a transitional element encourages scrolling. Whether the user scrolls to see how the transition changes or to preview more content is irrelevant as long as the interaction happens. It’s brilliant and beautiful, especially on desktop screens.


The best design trends are versatile enough that you can use them in new projects or incorporate them into websites that are already live for a little refresh.

A custom illustration can add extra interest to a hero area or page within your website, a tweak to the color palette can create a brilliant black and blue combination, or a neat transition can help users engage with the scroll.

If you want to spice up a project, these trends fit the bill for sure.

p img {display:inline-block; margin-right:10px;}
.alignleft {float:left;}
p.showcase {clear:both;}
body#browserfriendly p, body#podcast p, div#emailbody p{margin:0;}

Smashing Podcast Episode 4 With Heydon Pickering: What Are Inclusive Components?

Smashing Podcast Episode 4 With Heydon Pickering: What Are Inclusive Components?

Smashing Podcast Episode 4 With Heydon Pickering: What Are Inclusive Components?

Drew McLellan

Jina Anne In this episode of the Smashing Podcast, Drew McLellan talks to Heydon Pickering about his new book, Inclusive Components. Heydon is known for his work and writing on Accessibility, so what is Inclusive Design, and where do components come into play? Heydon eplains all this and more in this episode. You can listen below, or subscribe wherever you get your podcasts.

Show Notes


Drew McLellan: He’s a freelance web accessibility consultant, interface designer and writer. His work focuses on accessible user experience design, as well as writing and editing for Smashing Magazine. He’s the author of the acclaimed book about accessible web application design, Apps For All, and has just released a new book, Inclusive Components, all about how to build accessible web interfaces, again, with Smashing Magazine. So he’s clearly an expert on the subject of accessible design, but did you know he was the first male human to jump the Sydney Harbor Bridge in a speedboat? My Smashing friends, please welcome Heydon Pickering. Hi, Heydon. How are you?

Heydon Pickering: I’m smashing. I’m on brand.

Drew: I wanted to talk to you today about the subject of your new book, Inclusive Components.

Heydon: Yes.

Drew: Obviously just a two word title, but I feel like each of those words does a lot of heavy lifting. Starting at the end, as is obviously logical to do, components, is this about sort of component-based design? What is that?

Heydon: Yeah, so I suppose it’s been a while now since people, front end developers, designers and everyone who collaborates on making interfaces, started to think about things in terms of components and dividing things up into digestible and reusable morsels. And I suppose if you’re not familiar with that way of working for whatever reason, it really is a bit like electronic components. My father is an electronic engineer. He works in the sort of analog world of circuit boards and solder and all that kind of thing.

Heydon: In fact, he’s made some components, very small components, which have been used to regulate the current going into electromagnets at CERN. And he had a lot of faith in me as a kid, because he got me to actually solder some of the bits for them. I think that batch has now been retired, so don’t worry about my poor soldering, my poor teenage soldering, being involved in CERN anymore. But yeah, I think it is analogous to … Oh, there’s too many analogs in there.

Heydon: It’s analogous to analog circuit boards in that the idea is you have single responsibilities for individual parts or components and, together, they make the circuit and, together, they augment the current in the case of a circuit or the, I guess, the interface or the outcome in whatever way, in a design system or in an interface as manifested through a design system. And so, Inclusive Components because I wanted to address the fact that, while, I mean, accessibility does tend to get left behind generally when we advance what we’re doing in different arenas, and I wanted to bring accessibility and, in the broader sense, inclusive design to bear on this kind of new way of thinking and making things using components or modules or whatever you want to call them.

Heydon: So the idea was to both bring accessibility to design systems, but by the same token, think systemically when it comes to trying to address accessibility. Think about solving kind of one problem in one place in terms of accessibility and making sure that simply propagates around the pattern [inaudible 00:03:16] the design system at large.

Drew: In a sort of a practical sense, what does it actually look like to work in a component based way? What might an example of a component be?

Heydon: So, there’s different ways of conceiving and coding components. I tend to get straight into the sort of nitty gritty of it, past the conceptual stuff and think about how I might organize the code. I’ve actually come to focus a lot on custom elements, or if not custom elements, then normal elements but with kind of JavaScript behavior attached to them in a kind of isolated, componentized way. I really like the idea of components which are interoperable. And by that, I mean that they can be used across different frameworks and systems and approaches and technical stacks. And custom elements are nice in that because they’re native. You can define them in one place and then they could be used, say, in a react application or they could be used in a view application or they could be used in an angular application, or whatever sort of larger state management technology you’re using.

Heydon: So for me, usually a component will probably be a custom element. I’ve worked on a project recently which isn’t so much focused on accessibility, although I’ve tried to make it as accessible as possible, called Every Layout, and it’s all about kind of trying to isolate very specific kind of algorithms for CSS layout. And they’re defined as custom elements and kind of they sort of deploy themselves and run their own CSS and work as kind of like primitives within the larger system.

Drew: I mean, in actual practical terms, we’re talking a component might be something like a form field?

Heydon: Yeah, so it could be something as simple as an input. Say, like a text input or it could be something complex like a tab interface. And so, the idea with Inclusive Components was to take the concept of one component with its, hopefully, single purpose, like a text input, and then think about all of the different things that could trip up different kinds of people and try and avoid them. Not avoid the people, avoid the problems. It’s not so much about including people, it’s about trying not to arbitrarily exclude people.

Heydon: That seems to be the easiest way of approaching an inclusive design process for me, is to kind of identify the potential exclusionary elements of something and try and step around them. So with a text input, with a label, you’ve got a number of different things there that you might want to worry about. So, you’d have whether or not it’s actually labeled correctly for a start. So are you using a label element and is that label element pointing to the text field using a for attribute so that the two things are programmatically associated so that when a screen reader user focuses the input, they actually hear the label being announced? So that’s one thing to get right.

Heydon: Then, on a sort of more visual level, making sure that the label is clearly associated with that field and not a different fields, and that’s a question of white space and that kind of stuff. Also, making sure that the label is not, you’re not doing something fancy like putting the label underneath their form input because then when you, for instance, when a virtual keyboard comes up, that might become obscured. So, it’s taking into consideration those sorts of things.

Heydon: Making sure that the input itself has a focus style, so when you focus it with a keyboard, whether you’re a habitual keyboard user who uses keyboards to navigate or otherwise, making sure that it’s clear from the focus style that that’s the input that you’re focused on. Making sure that, I mean, things like autocomplete, worrying about that, whether autocomplete is appropriate and helpful in the context or whether it’s not. And a lot of these things address disability directly, but a lot of them are sort of broader in terms of usability and just making things as understandable as possible.

Heydon: Quite often, there’s a very sort of fine line or perhaps a blurred line between what addresses sort of usability for everyone and what addresses disability. And then, to make it even more kind of difficult to pin down, cognitive disabilities. So if something is not very usable for someone who does not have cognitive disabilities, then it’s going to be even more difficult to work out and be able to use for someone who does have those kinds of challenges.

Heydon: So it’s just trying to think about all of those things in one place. And really, the book is just my, it’s my thought process as I’m approaching each of those. So I did it as a blog to begin with. And each pattern or each component is a blog post and the text is just me going, “So, let’s now address this potential issue. How do we go about that? Okay, we’ve checked that one off. I think we’re okay there.” And, by no means am I trying to say that these are perfect and that I’ve thought of everything, because that’s not possible.

Drew: So does taking a component based approach to how you work on individual parts of an interface, I guess, it allows you to go really deep on that particular item and make sure that you’ve really heavily optimized it in the best way you can so that it’s accessible to everyone. Is there a danger in doing that and doing that on lots of different components and then putting them all together on a page? Is there a danger that you can create issues that you weren’t aware of because you’re testing them individually and not together?

Heydon: That’s a really good point, and I was going to bring that up earlier actually. I’m glad you said that. So, in lots of ways, I think we have, philosophically, we’ve decided that we need to separate things into these individual components. And there’s virtue to doing that, because if it’s isolated then it’s easier to kind of test and sort of treat as a single thing. And you can kind of, in terms of the way we work, it makes things easier to manage. We do have to consider, as well, the fact that these things ultimately have to share the same space and join together into a larger system.

Heydon: And I don’t think, actually, enough of our effort and thought goes into that, funnily enough. I think we componentize things more to make our lives as engineers easier, so that we know what we’re working on at what time. And, but then, we often do neglect the fact that these things will be living in dynamic systems and they have to be …

Heydon: I mean, the Every Layout project, although it’s more about visual design and about layout, is all about trying to make these little CSS primitives, these little layout primitives, in such a way that they can sort of self-manage algorithmically. It’s so that you can take them out of a narrow column and put them then a wide column and then it will be, the code itself will determine how many items abreast there should be or whether it should reconfigure itself in some other way. Because we can’t afford to constantly be intervening, and it has to be a system which is sort of self-knowing and intelligent, I think.

Heydon: There’s always things which you can forget about. So maybe you make a tab interface, you’ve got a row of tabs, you choose between the tab and the tab corresponds to a tab panel, that opens something up. And then, someone will come along and they’ll say, “Well, what if I want to put a tab interface inside a tab interface, or some other component inside a tap interface?”

Heydon: And of course, I mean, it’s partially a technical concern as to whether that would be possible, but yeah, you’ve got to make the choice about whether you’re going to make things as flexible as you can so that it’s possible to sort of imbricate things in a complex way, or simply write hard rules which say, “You can’t put something inside here because the level of complexity in terms of the code would probably be too high, but also possibly in terms of how the user can perceive and use the thing.” I’m all for writing rules which say, “Don’t nest loads of complex functionality inside itself,” because it’s just not likely that people are going to be able to get their head around it, really.

Drew: Is it possible to take a fully algorithmic or automated approach to designing for accessibility?

Heydon: I don’t believe so. No. So we have automated tools and I don’t want to disparage automated tools in any way. I think they are very useful, but I use them as kind of like an early warning system to try and kind of get an impression of where the problem areas are. So, if I was doing an audit for an organization who wanted some advice on how to make their products more accessible. So it’s a good way of kind of funding where the problem areas are, but I mean, you can have an interface which is technically 100% accessible, perhaps, according to some tool, even a good tool for judging it, say, against WCAG, the web content accessibility guidelines, or some other acceptance specification. And, even though it’s a 100% sort of all the boxes checked, it can still be entirely unusable for various reasons.

Heydon: For instance, going back to what we were saying before, it can just be entirely too complex. You can just overwhelm someone with links and there’s just no way that they’d be able to get through it and then that becomes, it’s a very sort of tacit thing and difficult thing to pin down, but it’s bound to just alienate people. But there’s also, you can get, it’s very easy to get false positives and things like that. I had a thing the other day, I said the other day, it was the other month, I was working for an organization and of course they wanted to have a 100% accessibility lighthouse school and there was an iframe which was dropped in there dynamically by a analytic script or something. You know the kind of thing where it’s some sort of slightly gross code, which is just sort of chucked in the page to do some task like that.

Heydon: Now I would recommend not using analytics at all, and I recommended to them to at least support the do not track protocol so that people could opt out. Unfortunately, that protocol is kind of, doesn’t really work anymore because it was never really supported properly. But this iframe, it was saying it doesn’t have a title on it. So the idea is that if you have an iframe, it should have a title attribute because that’s the best sort of longstanding way of identifying what the iframe is for to a screen reader user. But this was an iframe that also was set to display none, so it wasn’t even perceivable to a screen reader in the first place because display none, just as it hides things visually in a screen reader, it will essentially remove it from the interface, so it won’t be encountered or announced in any way.

Heydon: So it was a false positive. I mean, it was asking me to identify an iframe that was not there to be perceived in the first place. So, there’s always going to be those kinds of errors and problems in automated testing. But ultimately, it is about knowing, although it’s just sort of a thing that programmers, I guess, don’t really like to think that they’re involved in and they find it a bit icky, but it is about human behavior and about how people understand things and that’s a very difficult thing to just have a set of kind of binary sort of, or boolean sort of rules about.

Heydon: So, I mean, I spoke to a developer when I was consulting sometime ago about this and they kept saying, “Well, as long as we’ve got automated testing, we’re fine, aren’t we? It’s just, then we can just move forward.” And I said, “You still have to test manually. There’s no automated test which can really tell you if using the interface by keyboard is impossible in one way or another.” There are sort of discrete things you can look for, but the overall experience is still something that needs to be judged by human being. Yeah.

Drew: Sometimes the danger with automated tools is they look at items in isolation or they look at one interface in isolation and not see the wider context.

Heydon: Yes.

Drew: Certainly with using lighthouse for performance audits, sometimes I might make a decision as a developer to include, there may be a lot more CSS than is used on that one page and strictly speaking, I’m downloading too much CSS, but actually, I know that once that file is loaded, by the time the user browses to the next page, they’ve already got the CSS. So it’s an an optimization that’s being made across multiple pages the tool, looking at one page in isolation, sees as an error.

Heydon: Yes, absolutely. You’re thinking ahead and you’re making a judgment call, and until we get to advanced AI to anticipate that, then yeah, you really need human beings looking at it and going through it and going … I mean, so automated testing should be in place, as I say, a sort of early warning system, diagnostic system, but there should also be, if you’re interested in your organization really caring and making things more inclusive and more accessible, there needs to be training as well. There needs to be Q & A.

Heydon: So I would write scripts for, “This is how it should work when you interact with this component with a keyboard,” or, “This is how it should work when you interact with it with a screen reader and not actually step through it. So, when you do this, this should happen. When you do this, this should happen. When you do this, this should appear,” or that kind of stuff. So, and the kind of journey stuff, as you say, automated tools aren’t aware of that. They only just see, “Oh, this doesn’t have alt text here.” And actually, in a lot of cases, maybe it shouldn’t have alt text. And also, it can’t judge whether you’ve written the alt text well or not. So I think an image without all alternative text is probably better than an image with misleading or just bad alternative text. And that’s a judgment call again, isn’t it?

Drew: One of the things that I’ve struggled with, historically, in building things in an accessible way is keeping my knowledge of the best practice up to date because it’s, each time I refer to any documentation or any sort of recommendations, it seems like what I was doing and thought I was doing the right thing, the recommendations have moved on and now I should be doing something else. And that’s a familiar story with all areas of working on the web. But I think the danger is, of course, with accessibility issues, is that, if you’re not following the best practice, if you leave something in your interface that is now not good practice, that could be affecting your users in a negative way. Does a component based approach to building an interface or a site, does it help with that at all in any way?

Heydon: I think purely in the sense that, because you have one component which then, the idea of course in all cases and not just in terms of accessibility, is that you have this component defined in one place which will then be used in different places, at least when aspects or browser support or whatever it is changes and you want to update the component, you only then have to do it in one place and then wherever it’s used, that enhancement or that change will be felt. So from that regard, I think it’s certainly more useful to have things divided into components.

Heydon: But then, yeah, as I say, that doesn’t just affect accessibility, that can affect anything that changes. But then, I’m not sure really how much changes in its … I mean, there’ll be few sort of breaking changes in terms of sort of HTML accessibility, which is, obviously, a very narrow area. But in terms of the code quality or how the code works, things are introduced into the HTML spec, obviously, very slowly and not quite as slowly but fairly slowly into the ARIA spec as well. And then, much of ARIA just mirrors what’s in the underlying baseline HTML anyway.

Heydon: I think, more so than the technology, the perception and understanding of these things tends to change over time. I mean, there was recent, in the WebAIM survey recently, they identified the sites were using ARIA were more inaccessible than sites that didn’t use it. So this technology specifically conceived in order to help people make websites more accessible, was making it worse. So it’s really, it’s just a knowledge gap, not a technology gap or a technology shortcoming. It’s people just taking the technology and misusing it because they didn’t really actually understand how it’s intended to work, unfortunately. Hopefully, some of my writing might be able to help with that. In some areas, anyway.

Drew: But a sort of well structured component-based system would enable you, once you realize that something is out of date or you’ve made a poor decision and you now know better, would enable you to more easily go in and fix that across your application.

Heydon: Yeah, exactly. Yeah, yeah, absolutely. I mean, it’s all about efficiency isn’t it, really? And this dry principle or what have you, and see, that’s why I guess I was originally very excited about this opportunity, because people always complain that making things accessible is extra work and it’s hard and it’s upsetting and all of that. And so, it was kind of an opportunity to say, “Well, you know how you’re making this stuff really, efficiently building these component systems? Get your accessibility in there for that one component that you’re making, and then you didn’t have to worry about the accessibility anymore apart from the occasional spec change or update or what have you.”

Heydon: Or just, I mean, probably most of the time, the iteration will simply be based on user feedback and ongoing research, which, obviously, you should be, as much as possible, conducting with a diverse group of people. So, you should be getting people who use different devices and have different browsing habits and use different assistive technologies and that kind of thing. And you know, things are bound to come up all the time. I think I’ve really pinned down a component, think it’s really rock solid, and then I do a bit of research and I find that I’ve made some pretty bad assumptions. But yeah, with a component system you only have to fix it in one place.

Drew: Can a component ever be fully inclusive or is it a spectrum where you’re just working ever more towards inclusivity?

Heydon: Yeah, it would be possible for a component to be, in terms of let’s say WCAC error free, it meets all of the WCAC criteria, but as I said, that only takes you so far and it could still be entirely unusable or impossible to understand even with those technical criteria met. So yeah, this is something that I talk about a lot. I try to convince people that accessibility is like any other area of design, it’s just a part of the design process and nothing can be perfectly designed just like nothing can be perfectly accessible. I think, unfortunately, a lot of folks think of it just in terms of just making sure that it is compatible with screen readers, which is obviously a very narrow scope in terms of accessibility and inclusion in general.

Heydon: So then, there will be people who, some good folks I’ve worked with like at the Paciello Group, who would say, “Well actually, I want to be known as a accessible UX person.” So it’s not just about this box ticking exercise, it’s more about actually trying to make the experience better and more valuable for the greater number of people and move more towards sort of broader principles and things which are less binary. But ultimately, it’s all that, and WCAC and other such criteria can only really identify the real hard and fast, “This is wrong,” stuff, I suppose.

Drew: So if I’m a developer, what should I be doing differently as I approach how I design and plan and build a component? Is there anything that I should be considering in my work to make sure that that component is going to end up being as inclusive as possible?

Heydon: So, I mean, depending on what you’re building, there’s going to be different criteria which need to be met. So, for instance, not every component is going to have the to have accessible imagery with alternative text, because it might not use imagery at all. It might just be text-based or what have you. Some might not be interactive. So, in terms of the specific requirements, then, it would change between component, but hopefully what some of my writing and what the Inclusive Components book helps you to do is to fall into or kind of adopt a discipline of just thinking inclusively.

Heydon: So, when you’re approaching this stuff, not just thinking, well, basically just getting out of the mindset of, “If it works for me, it probably works for everyone else,” because it’s simply not the case that the way that you or I browse things, I mean, we’ll probably do things completely differently, just us two, right?

Drew: Right.

Heydon: And we’re Western, white, English as first language people. And so, yeah, the amount of diversity in terms of the people consuming this, I mean performance people always talk about this as well, people who are interested in advocating for better performance. You’re used to using a high spec set up on a good network and a lot of your users or a lot of your potential users will certainly not be, and same with accessibility. It’s just a question of, basically, just getting out of thinking about yourself, really. Literally just that. And trying, obviously, to reach out beyond just your immediate colleagues and people in your same social group as well.

Heydon: So hopefully, it’s really just, “Here’s what I solved for this stuff,” and what I was thinking about at the time. You can reuse some of those ideas and apply precisely what I’ve applied, if that’s useful or relevant to you. Hopefully, the book is more about just, “Here is a case study of a person who tries to think inclusively. See, the kind of things they were thinking about, when you’re designing something completely different, perhaps just employ the same kind of thinking and try and open your mind up to the diversity of of users and how they go about things.”

Drew: So the book itself, how did you decide how to structure it? It seems very fiercely practical, which I like in a book, but how have you structured it?

Heydon: Very much like the previous book, actually was Inclusive Design Patterns and I had a lot of trouble that book, to begin with, because I tried to organize it in terms of kind of abstract criteria. So I started out doing a chapter which was all about keyboard accessibility, but that was very hard because then I had to kind of, every time I talked about a different type of keyboard accessibility or the thing that you have to think about, then I had to conjure some sort of component and then ditch that component and then move onto something else.

Heydon: And so, it just made more sense for me to organize things in terms of components themselves. So, Inclusive Design Patterns does this and now Inclusive Components is really just a continuation, which just covers different components. It’s different in that, in terms of features, it’s a bit different because it also includes live code examples and stuff, which I didn’t do so much for the previous books. But yeah, it is literally just, “We’re going to do this component,” whether it’s a tap interface or a collapsible section or a theme switcher or a notification flash card or toaster or whatever they’re called, and then just everything is then organized around that component.

Heydon: So it’s, “This is what we’re doing and these are the things we should consider while we’re doing it to be more inclusive,” because that’s how I work and that’s how other folks work. And as soon as I started doing it like that, it was really just me working and writing notes as I worked. And so, the thing kind of wrote itself, and then all of the effort was really in actually just making sure that I was doing a decent job of making those things not inaccessible, I guess.

Drew: Yes, I mean the table of contents really reads almost like documentation or like a self-help manual or something. Straight in with chapter one, toggle buttons. If you want to implement some toggle buttons, go to this chapter, read it and you’ll get everything you need to know about how to do that, which is an approach I really like. I see things like collapsible sections, tabbed interface, theme switches, data tables, loads of actual, real practical stuff that we’re all building every day and I think we all, probably, could be building better.

Heydon: Yeah, that was totally the idea because it wasn’t just about me making my components, it was a case, and you’ve touched on it there, which I’m glad you did, which is it was of identifying common patterns that we all use. So I mean, there’s tab interfaces everywhere and they’re all implemented differently and they’re all implemented, variously, very badly. I mean, I’ve implemented terrible tab interfaces and that I’ve learned a little about how bad they were for people, and then I’ve tried to make them a bit better and a bit better and a bit better. I’ve probably made 15 or 16 different versions of tab interfaces in my time, having been doing this kind of thing for years now.

Heydon: And you know, they’re getting a bit better, hopefully, every time. But it is just a common thing. It was a common thing that I would use quite often between different websites, I use and everyone uses. So, part of the idea was to say, “Well, actually, let’s do a design system, kind of an accessible design system for the web.” Now, people are going to branch out and they’re going to do their own versions of these things, but to kind of get the core stuff down and the accessibility is a core thing that should be in things. It shouldn’t be an add on, it shouldn’t be an either/or, it shouldn’t be a feature. It should be a core thing. And if you get that core stuff paired down, then yeah, hopefully people would look at the chapters and go, “Oh, okay, I’ve made those. I’ve seen those. Let’s see how to do it as inclusively as possible,” and then hopefully they get some value from that.

Drew: Well, what I like about it is, certainly I know I’ve, in the past, I’ve had some interface features I’ve needed to implement and I know that it’s going to be tricky from an accessibility point of view, say some sort of a fly out menu, drop down menu, something like that. I think, “Okay, here be dragons in terms of accessibility. I need to make sure I do this right.” And so, I Google for how to do it, I find a reputable source saying, “Use this method,” I use that method, I implement it and I move on, but I actually haven’t learnt anything. I haven’t learnt why the solution was that. And what I really like about the way you go into it in the book is I can do two things at once. I can figure out how I should be doing it and I can figure out why I should be doing it like that because it’s all very carefully explained. So, I think it’s really successful from that point of view.

Heydon: Oh, great. That was what I was going for. So that’s good. But yeah, that seems to be my thing. I mean, I’ve been working with the BBC for some months and we’ve kind of made a thing a bit like Inclusive Components but for the BBC, so we’ve done this sort of technical implementation with a through the lens of accessibility version of their design language called GEL. And yeah, it explains the why as well as the how and it’s not a pattern, really. The idea is that the individual departments at the BBC, because there’s so many of them, because it’s such a large organization, so there’ll be BBC Sport, BBC Weather, BBC News, they’re the ones who would be taking care of the kind of technical stack and making their pattern library. And what we’ve really provided is just, we’ve just tried to exemplify the best practices. So it was really much more of a learning resource than a simple plug and play pattern library. Yeah.

Drew: Was it difficult deciding what patterns to include in the book? Was there anything that you left out?

Heydon: The only ones I really had problems with or second thoughts about were the ones where, the tab interface, for instance, I wasn’t going to include, because I really hate tab interfaces, but then I had folks saying, “Could you please do a tab interface? Could you include a chapter of that?” Because I get asked to put them in my interface all the time by clients or whoever. So, I ended up doing one. But it’s full of caveats. And the main caveat is, probably don’t use a tab interface. Use maybe an accordion, it’s a simpler interaction paradigm. It’s easier to make responsive, it’s easier to make compatible with screen readers, et cetera, et cetera.

Heydon: So I put all those caveats in. But yeah, and some of them were ones where I just thought, “Oh, I haven’t written about this before and I could do with having sort of thought about it so that I could actually use it in my design work.” And others were people requesting, saying, “I know this is a gnarly one, I just don’t know how to go about it. Could you give it a go?” And so I gave it a go as best as I could. That is going to be the last time I write a book about accessibility because I’ve done three now.

Heydon: So if anyone wants to know any more and if they think of any other components that they might want doing, just DM me on Twitter or something and I’ll try and deal with it in that way rather than writing a whole article, because those articles are quite long and they take up quite a lot of time and I’m kind of doing other things at the moment. But I’m always happy to chat with anyone who has any questions about this stuff. They might be working on something similar to what I’ve covered and there was just something that they were unsure about or which I, for whatever reason, I hadn’t made as clear as they’d liked it. Yeah, then just contact me because I’m always happy to talk about the stuff because it helps me to sort of ruminate over things and try to, it might challenge my assumptions and help me to do a better job as well.

Drew: So, the book, Inclusive Components, is available right now from Smashing Magazine,, and I’d recommend everybody check it out.

Heydon: Thank you.

Drew: So I always like to ask people, I mean, Smashing is all about learning, right, with the books, the conferences, the magazine, we’re all about learning. What is it that you’ve been learning lately?

Heydon: So, recently, well, a couple of years ago I made something, I made a drum machine using the web audio API called Beads and it’s still available as a PWA, it’s a progressive web app. If you Google search Beads GitHub or something like that, you should get the GitHub page which has it on there. But that was a alpha version and I’m now working on doing a much more advanced version of this. And it’s a different kind of drum machine because it’s polymetric, it has different, you can have different tracks of different lengths. So you can have a track which has seven beats and a track which has nine beats, a track which has four beats. And then, the rhythm evolves over time because of the changing syncopation. You’ve got these, it’s multi-threaded.

Heydon: That was the main reason that I wanted to build it, because, as someone who’s interested in kind of experimental music, that’s something I wanted to play with. Obviously, I’m trying to make this drum machine as accessible as possible. And that’s been interesting from the point of view now that I’m working with, I’m turning it into an Electron app. So, for those of you that know Electron allows you to kind of wrap a sandbox version of Chromium browser and create a desktop application but using web technology, which was really great because it makes things, for this projects anyways, because it gets around a lot of performance problems.

Heydon: But also, although I’ve been doing cross browser testing for 12 years now, it’s really nice to have a break and just to design stuff for one browser. And it’s got some, so there’s a flag in Chromium. It’s a, what’s it called, an experimental web platform feature for focus visible. So I’ve been able to make this drum machine completely keyboard accessible with these really nice, big focus outlines without those appearing for mouse users, because focus visible uses this heuristic where it detects whether or not you’re using a keyboard. So that’s been nice, to be able to incorporate that.

Heydon: But the thing recently that I’ve been learning about, just I’ve, I guess, been learning about some of the more advanced things you can do with the web audio API itself. So I had this problem where I have, you can put multiple sounds on one track so you can load in an array of audio files and it places them one after the other, and by default they overlap, so they’ll always play out out, the audio buffer will play until it finishes. So if the next sounds comes before the end of that, it just overlaps, which is fine. It’s kind of like a reverb or something. But sometimes if you’re doing an arpeggio, like a baseline or something, you don’t want them to open up. That’s not how a bass guitar works, right? If you’re on the same string, you press the next note, the first one has to finish.

Heydon: So, I was stopping a note as the next one started and there was always an audible popping sound and it’s not the web audio API having a problem or anything like that. It’s just the human ear will hear a kind of a nasty popping sound where you kind of sever away from. You just cut it, stop it dead, it’s going to sound nasty. And then, so I found that there’s a function as part of the web audio API, which allows you to choose a point where you can taper off the sound. And so I was able to detect where the sounds should end because the other sound is beginning, then taper it off very quickly, but it is a taper, like a fade out, rather than a hard cut off thing.

Heydon: So I solved that problem after it annoying me for ages. So it’s basically been web audio API stuff and messing around with sounds because I’ve always been into, as I say, into experimental music and messing about with that sort of stuff. And I’m trying to write a talk about this. And in the talk, I’m using Billy Jean by Michael Jackson because it’s a very straight, fall to the floor rhythm and I’m going to kind of warp it in various different ways. So I’ve actually had to learn the parts for Billy Jean to kind of sequence that and stuff. So, weirdly enough, that was what I was doing before doing this podcast.

Drew: That sounds like a lot of fun. So if you, dear listener, would like to learn more about Heydon or hire him to consult on your projects, you can follow him on Twitter, where he’s @heydonworks, or visit his website at Thanks for joining us, Heydon. Do you have any parting words?

Heydon: Goodbye.

Smashing Editorial (dm, ra, il)
10 Awesome Cyber Monday Deals That You Should Check Out (Up to 94% Off)

It’s that time of the year again. If you skipped Black Friday there’s still Cyber Monday.

And there are tools and resources out there that you’ll find only once a year at this great price. We’ve gathered the best of them in this article.

Bookmarking this page isn’t a solution. Don’t let a slow decision ruin your chance of getting these products or resources at a discounted rate. Act fast and don’t let them slip by.

1. Brizy Cloud Website Builder

In addition to the Cyber Monday savings you could realize, Brizy has lots of free stuff to offer as well, including the website builder itself. This premium product enables designers to create multipage websites fast, easily, and efficiently. Thanks to the 150+ customizable layouts and more than 700 design blocks that come with the package, coding is not necessary.

There is a small catch. You should sign up for a free account to be able to save what you build. You have full control over what you build and how your websites and those of your clients will appear on responsive devices.

Where does the Cyber Monday deal come in? Aside from the Free Forever plan, there are two annual paid plans (Personal and Studio). These are very affordable plans that have much more to offer than the free plan and can be yours at a 40% discount if you order 2 December or 3 December. You might check out Brizy before that date to see if a paid plan is the preferred choice for you. Use discount code CM40OFF.

2. Smart Slider 3 Pro

Smart Slider Pro 3 features a free plan and three paid plans (Single Domain, Business, and Unlimited). Give it a test drive for free. Yes, you can demo it for free. And if you like it, you can buy one of the paid plans at a discount until December 3.

Smart Slider 3 Free allows you to build beautiful responsive sliders. The paid plans provide additional special effects such as parallax animation and the popular Ken Burns effect.

You won’t get bogged down with extraneous features you have no use for. There are two different editing modes (content and canvas) to work with. In the content mode the slide acts as a page builder, in the canvas mode you can work with layers unobstructed.

Features include 180+ sample sliders (one click installation), a layer animation builder, and an astonishing array of animations and special effects. Any of the pro plans can be yours at a Cyber Monday discount of 40%. Use code SAVE4019.

3. Paymo

Paymo is a work management software application teams can use to plan their workflow, track time, and invoice clients. Having all these functions managed by a single app ensures that everything is kept in sync. Project managers will know exactly how much time (including billable hours) is spent on each task, making it easy to send accurate reports and charge clients correctly and fairly.

Paymo integrates with Adobe to allow you to track work time directly on Photoshop, InDesign, Illustrator, and Premier.

Live, up-to-date reports can be generated for sharing with team members and project stakeholders. Paymo also helps team management with task planning, tracking expenses, performing leave management tasks, and more. Other important features include Gantt charting, Kanban Boards, and a resource scheduler.

Upon subscribing, use code GVX233 for a 30% Paymo discount.

4. TheGem – Creative Multi-Purpose High-Performance WordPress Theme

TheGem is a bold and beautiful WordPress theme that is regarded by many, including its 40K satisfied customers, as featuring the most complete designer’s toolkit of any theme on the market. WPBakery ensures easy and intuitive front-end page editing, plus more than 90 pre-built complete websites, installable with one click, allows you to get off to a quick start.

Premade templates and design elements are easily combinable. This premier theme is yours at a 50% discount Cyber Monday.

5. Beaver Builder

Beaver Builder is drag and drop website building plugin that enables you to separate page building from your theme, giving you total control over your content. Beaver Builder works with any theme, and you can even switch themes without any loss of content. 30+ landing pages come with the package, no coding is necessary, and Beaver Builder is SEO friendly and mobile responsive. No discount code is required. The 25% discount is automatically applied during checkout from Black Friday through Cyber Monday.

6. Kalium

Kalium is a superior choice for beginners and advanced WordPress users alike. It is easy to use and easy to maintain, it includes several of the most popular WordPress plugins, and it supports all the better known ones. This creative multipurpose theme is an award winning top seller. It’s trusted by 30,000+ clients and provides top customer support.

Normally selling for $60, Kalium can be yours Cyber Monday for $30; a 50% discount and a great deal.

7. Mobirise Website Builder

Mobirise is an offline website building app for Windows and Mac that features an extremely easy to use interface. It’s mobile friendly, it’s free for both commercial and non-profit use, and you can build fast, responsive, Google-friendly websites in minutes. Mobirise’s Website Builder Kit, normally priced at $2,654, features all premium themes, 200+ blocks, and 66 Mobirise themes and extensions.

On Monday you can purchase this all-in-one Kit for $149, a whopping 94% discount!

8. Simple Author Box

The Simple Author Box plugin’s features give you the ability to add guest authors and multiple authors to your posts, add links to author’s social networks, and select specific post types where you want your author box to show up. Simple Author Box is Gutenberg block compatible.

Although a free version is available, signing up for a subscription plan is recommended. The Cyber Monday offer of a 30% discount on all lifetime licenses is valid until 4 December.

9. MyThemeShop

This is for the designer who wants to be able to choose among a huge variety of premium themes, domain licenses, plugins, and memberships in conversion tracking, user behavior, and social media platform tool usage and more.

For Cyber Monday, MyThemeShop’s annual fee is discounted to $99.47. This Cyber Monday special is valid from 1 December to 7 December.

10. WordLift

As you write content, WordLift is busy adding semantic markups to feed to search engine crawlers with the intent of helping you reach a broader audience. This semantic SEO tool uses natural language processing, analyzes content, and transforms any text into machine-friendly content.

A quality SEO tool like WordLift makes a great investment as it produces greatly superior results as compared to attempting to create SEO friendly content on your own. Cyber Monday features a 50% discount on your first subscription.


Cyber Monday will fall on the 2nd of December you can witness some of the biggest online deals of the year on tech-related items. Many of these deals are available in physical stores as well. However, online shopping is generally much more convenient, especially if all you have to do is confirm your order and perhaps press “Download”. As far as comparisons with Black Friday are concerned, Cyber Monday discounts are at least as large, and often larger.


[– This is a sponsored post on behalf of BeTheme –]

p img {display:inline-block; margin-right:10px;}
.alignleft {float:left;}
p.showcase {clear:both;}
body#browserfriendly p, body#podcast p, div#emailbody p{margin:0;}

Popular Design News of the Week: November 25, 2019 – December 1, 2019

Every week users submit a lot of interesting stuff on our sister site Webdesigner News, highlighting great content from around the web that can be of interest to web designers. 

The best way to keep track of all the great stories and news being posted is simply to check out the Webdesigner News site, however, in case you missed some here’s a quick and useful compilation of the most popular designer news that we curated from the past week.

Note that this is only a very small selection of the links that were posted, so don’t miss out and subscribe to our newsletter and follow the site daily for all the news.

Fascinating CSS Grid Layout Examples and Tutorials


The Defining Movie Poster Trend of the Decade


A Brutally Honest Landing Page


Hierarchy of Needs in UX


Developing a Website Redesign Strategy for 2020


Flowkit 3.0


Site Design: The Geek Designer


CSS Grid Layout Vs CSS Frameworks


The Third Generation of Interfaces


Buttons: Attention to Detail


Building Trust as a Designer


10 Essential UI (user-interface) Design Tips


WhoCanUse: Find Out Who Can Use your Color Combination


Shopify Vs WooCommerce Product Comparison


11 Christmas Icon Fonts Free for Commercial Use in 2019


7 Ways to Find a Niche for your Ecommerce Store


Tips for Choosing a Typeface (with Infographic)


Google Also A/B Tests the List Vs the Grid


Apple Pulls all Customer Reviews from Online Apple Store


How to Expand Globally as a Freelance Designer


Mouseless – Unleash your Keyboard’s Superpower


Designer Gift Guide 2019: The Best Holiday Gift Ideas for Designers


Good Design was Always Accessible


Can You Really Convey Luxury Through Digital Product Design?


Gratitude Goes a Long Way to Increase Creativity and Innovation


Want more? No problem! Keep track of top design news from around the web with Webdesigner News.

p img {display:inline-block; margin-right:10px;}
.alignleft {float:left;}
p.showcase {clear:both;}
body#browserfriendly p, body#podcast p, div#emailbody p{margin:0;}

Beating The End Of The Year Rush (December 2019 Wallpapers Edition)

Beating The End Of The Year Rush (December 2019 Wallpapers Edition)

Beating The End Of The Year Rush (December 2019 Wallpapers Edition)

Cosima Mielke

As the year is coming to a close, many of us feel rushed, trying to meet deadlines, finishing off projects, and preparing for the holidays. Do you remember what December felt like when you were little? It was a time of wonder and expectation, a time to slow down and enjoy the small things: watching the first snowflakes fall, drinking hot chocolate, and admiring the Christmas decorations in your neighborhood, for example.

This month’s wallpapers post is a little reminder to treat yourself to some quiet moments like these in the midst of this end of the year rush. To refuel your batteries and gather some fresh inspiration. The wallpapers in this collection might be a good start.

As every month since more than nine years already, the wallpapers were designed by artists and designers from across the globe, and each one of them is available with and without a calendar for December 2019. For some extra variety, we also added a selection of wallpaper favorites from past editions at the end of this post. We wish you happy holidays and a lovely, and hopefully not too stressful, December!

Please note that:

  • All images can be clicked on and lead to the preview of the wallpaper,
  • We respect and carefully consider the ideas and motivation behind each and every artist’s work. This is why we give all artists the full freedom to explore their creativity and express emotions and experience through their works. This is also why the themes of the wallpapers weren’t anyhow influenced by us but rather designed from scratch by the artists themselves.

Submit your wallpaper

Do you have an idea for a wallpaper to welcome 2020? We are always looking for creative talent to be featured in our wallpapers posts. Don’t be shy, join in! →

Dear Moon, Merry Christmas

“Please visit Vladstudio website if you like my works!” — Designed by Vlad Gerasimov from Russia.

Dear Moon, Merry Christmas

Christmas Mood

Designed by MasterBundles from the United States.

Christmas Mood

The Month of Winter Songs

“Here comes the finale of a dazzling year. The month of snow with a tinge of warmth from the carols in the wind. All made merrier with the mistletoe, cakes and gifts. Let’s welcome the winter of our lives as spring is not far away now.” — Designed by Odoo Apps from India.

The Month of Winter Songs

The Camels Wish You A Merry Christmas!

“The year is coming to an end and we say good-bye with a very christmassy animal, the camel. We wish you a merry christmas and a happy new year.” — Designed by Veronica Valenzuela from Spain.

The Camels Wish You A Merry Christmas!

Hanukkah Candles

“Hanukkah is a joyous celebration of all that is important in life; family, freedom, and light. So we wanted to make a calendar that celebrated this Jewish holiday and what better way than by featuring the beautiful Hanukkiyah. Happy Hanukkah everyone!” — Designed by Ever Increasing Circles from the United Kingdom.

Hanukkah Candles

Happy Holidays!

Designed by Ricardo Gimenes from Sweden.

Happy Holidays!

Reindeers Go Rah-Rah!

“Reindeers go rah-rah when the season of snow is on the way. Let’s get excited as our beloved Santa comes with gifts for the tiny tots and fill the air with nothing but cheer! It’s Christmas time, y’all!” — Designed by Riddlebook from London.

Reindeers Go Rah-Rah!

Oldies But Goodies

Since the beginning of our monthly challenge, December and the holiday season have inspired so many designers to create a wallpaper. Below we collected some favorites from our archives that are just too good to be forgotten. Enjoy! (Please note that these designs don’t come with a calendar.)

Silver Winter

Designed by Violeta Dabija from Moldova.

Smashing Wallpaper - January 2011

Cardinals In Snowfall

“During Christmas season, in the cold, colorless days of winter, Cardinal birds are seen as symbols of faith and warmth! In the part of America I live in, there is snowfall every December. While the snow is falling, I can see gorgeous Cardinals flying in and out of my patio. The intriguing color palette of the bright red of the Cardinals, the white of the flurries and the brown/black of dry twigs and fallen leaves on the snow-laden ground, fascinates me a lot, and inspired me to create this quaint and sweet, hand-illustrated surface pattern design as I wait for the snowfall in my town!” — Designed by Gyaneshwari Dave from the United States.

Cardinals In Snowfall

Sweet Snowy Tenderness

“You know that warm feeling when you get to spend cold winter days in a snug, homey, relaxed atmosphere? Oh, yes, we love it too! It is the sentiment we set our hearts on for the holiday season, and this sweet snowy tenderness is for all of us who adore watching the snowfall from our windows. Isn’t it romantic?” — Designed by PopArt Studio from Serbia.

Sweet Snowy Tenderness

’Tis The Season Of Snow

“The tiny flakes of snow have just begun to shower and we know it’s the start of the merry hour! Someone is all set to cram his sleigh with boxes of love as kids wait for their dear Santa to show up! Rightly said, ’tis the season of snow, surprise and lots and lots of fun! Merry Christmas!” — Designed by Sweans Technologies from London.

’Tis The Season Of Snow

Abstract Winter

“Winter is cold and dark up here in the north.” Designed by Terese Brännström from Sweden.

Abstract winter

All That Belongs To The Past

“Sometimes new beginnings make us revisit our favorite places or people from the past. We don’t visit them often because they remind us of the past but enjoy the brief reunion. Cheers to new beginnings in the new year!” Designed by Dorvan Davoudi from Canada.

All That Belongs To The Past

All Of Them Lights

“I created this design in honor of the 9th of December, the day of lights.” — Designed by Mathias Geerts from Belgium.

All Of Them Lights

Tongue Stuck On Lamppost

Designed by Josh Cleland from the United States.

Smashing Wallpaper - december 11

Christmas Wreath

“Everyone is in the mood for Christmas when December starts. Therefore I made this Christmas wreath inspired wallpaper. Enjoy December and Merry Christmas to all!” — Designed by Melissa Bogemans from Belgium.

Christmas Wreath

Winter Garphee

“Garphee’s flufiness glowing in the snow.” Designed by Razvan Garofeanu from Romania.

Smashing Wallpaper - December 2012

Ice Flowers

“I took some photos during a very frosty and cold week before Christmas.” Designed by Anca Varsandan from Romania.

Smashing Wallpaper - january 10

Winter Morning

“Early walks in the fields when the owls still sit on the fences and stare funny at you.” — Designed by Bo Dockx from Belgium.

Winter Morning

Celebration Galore Is Here Again

“Christmas bells are swinging above the snow fields, we hear sweet voices ringing from lands of long ago… It’s time to count your blessings, sing your Christmas carols, open your gifts, and make a wish under the Christmas tree!” — Designed by Norjimm Pvt Ltd from India.

Celebration Galore Is Here Again


“Surprise is the greatest gift which life can grant us.” — Designed by PlusCharts from India.


Enchanted Blizzard

“A seemingly forgotten world under the shade of winter glaze hides a moment where architecture meets fashion and change encounters steadiness.” — Designed by Ana Masnikosa from Belgrade, Serbia.

Enchanted Blizzard


“Blessed is he who expects nothing, for he shall never be disappointed.” — Designed by StarAdmin from India.



“The winter solstice has always been special to me as a barren darkness that gives birth to a verdant future beyond imagination, a time of pain and withdrawal that produces something joyfully inconceivable, like a monarch butterfly masterfully extracting itself from the confines of its cocoon, bursting forth into unexpected glory. (Gary Zukav)” — Designed by Dipanjan Karmakar from India.


Merry Christmas

Designed by Delphine Pagès from France.

Christmas Wallpaper — Merry Christmas

Season Of Joy

Designed by Antun Hirsman from Croatia.

Season Of Joy

Christmas Fail

Designed by Doud – Elise Vanoorbeek from Belgium.

Christmas Wallpaper — Christmas Fail

The Matterhorn

“Christmas is always such a magical time of year so we created this wallpaper to blend the majestry of the mountains with a little bit of magic.” — Designed by Dominic Leonard from the United Kingdom.

Christmas Wallpaper — The Matterhorn

Season’s Greetings From Australia!

Designed by Tazi Designs from Australia.

Christmas Wallpaper — Season’s Greetings From Australia!

December Deer

“I love the simplicity of the deer head silhouette trend with just a touch of hand-drawn detail. Happy December my dears!” — Designed by Jordan Thoma from the United States.

December deer

Father Frost

Designed by from Russia.

Smashing Wallpaper - december 11

Winter Solstice

“In December there’s a winter solstice; which means that the longest night of the year falls in December. I wanted to create the feeling of solitude of the long night into this wallpaper.” — Designed by Alex Hermans from Belgium.

Winter Solstice

Have A Minimal Christmas

“My brother-in-law has been on a design buzzword kick where he calls everything minimal, to the point where he wishes people, “Have a minimal day!” I made this graphic as a poster for him.” — Designed by Danny Gugger from Madison, Wisconsin, USA.

Have a Minimal Christmas

Delicate Frost

“Don’t let Jack Frost nip too much at your nose but do let him decorate your windows!” Designed by Tirelessweaver from Canada.

Smashing Desktop Wallpapers - January 2012

The Deer In My Garden

“Every year at the onset of winter, a deer appears in my garden looking for food. I usually catch it early in the morning and we’ll be exchanging glances through the patio doors.” — Designed by Andrea Ludszeweit from Germany.

The deer in my garden

Don’t Stop

“It’s been such an incredible year for space; I wanted to celebrate that with a simple wallpaper to keep everyone inspired this month. The year isn’t over yet — don’t stop pushing yourself!” — Designed by Shawna Armstrong from the United States.

Don’t Stop

The Twelve Days Of Christmas

“This wallpaper celebrates the classic carol ‘The Twelve Days of Christmas’. Each day is represented with a cheerful illustration representing gifts.” — Designed by Daphne Firos from Cleveland.

The 12 Days of Christmas

It’s In The Little Things

Designed by Thaïs Lenglez from Belgium.

It's in the little things


“‘December is plowing yet. When the smoke-clouds break, high in the sky shines a field as wide as the world. There he toils for the Kingdom of Heaven’s sake. Ah, he is taller than clouds of the little earth. Only the congress of planets is over him, and the arching path where new sweet stars have birth. Wearing his peasant dress, his head bent low, December, that angel of Peace, is plowing yet; Forward, across the field, his horses go.’ (Based on Chinese Nightingale, 1917).” Designed by Dynomite from Germany.

Smashing Wallpaper - December 2012

Best Friends

“Best friends posing for a photo.” Designed by Nenad S. Lazich from Serbia.

Smashing Wallpaper - December 2012

Catch Your Perfect Snowflake

“This time of year people tend to dream big and expect miracles. Let your dreams come true!” Designed by Igor Izhik from Canada.

Catch Your Perfect Snowflake

Cool Winter

“Wanted to showcase the cool, crisp colors and give an overall feeling of winter. Designed by Matt Noa from the United States.

Cool Winter

Robin Bird

“I have chosen this little bird in honor of my grandfather, who passed away. He was fascinated by nature, especially birds. Because of him, I also have a fascination with birds. When I think of winter, I think of the birds, flying around searching for food. And why a robin? Because it is a cute little bird, who is also very recognizable.” — Designed by Engin Seline from Belgium.

Robin Bird

Join In Next Month!

Thank you to all designers for their participation. Join in next month!

Black Friday 2019: Support Indie Makers

Black Friday 2019: Support Indie Makers

Black Friday 2019: Support Indie Makers

Rachel Andrew

Every time I have checked my email over the last two weeks, it has been full of Black Friday deals. We will get a short respite before the New Year offers start to roll in. I like a bargain as much as anyone, however, I think that plenty of sites will be covering the best offers on electronics and tech.

I thought we would do something different this year at Smashing. I’ve launched a number of independent products over the years — downloadable software, software as a service, self-published books, and a course. I know how difficult it can be to get the word out about your products when self-funding, so I thought we could give a boost to all the indie makers out there and feature some of their products.

We asked the Smashing community for their suggestions, and so here is a list covering pretty much every kind of product you can imagine. I hope you can find something you need in these, and help support these hard-working folks.

Search by category:

  1. Books
  2. Gifts, Artwork, And Posters
  3. Printed Magazines
  4. Courses And Training
  5. Software And Tools
  6. Other


A collection of independently published books, and small publishers, with a shoutout to a very special project.

Oddly Amazing Animals

Oddly Amazing AnimalsA book project started by the talented Cindy Li, who was a friend to many of us in the web community. After Cindy passed away, her friends got together to finish the book, and all proceeds will go to Cindy’s two young sons.

The Power Of Digital Policy

The Power Of Digital PolicyYou don’t need an army of consultants to help you protect your organization from brand degradation and reputational threats. This practical guide by Kristina Podnar will guide you in minimizing risks and maximizing opportunities.

The Game Engine Black Book

The Game Engine Black BookThis book details techniques such as raycasting, compiled scalers, deferred rendition, VGA Mode-Y, linear feedback shift register, fixed point arithmetic, and many others tricks. Fabien Sanglar also went into much detail to describe the hardware of 1991, and has released the source code under GPL license.

404 Ink

404 InkFounded by two publishing freelancers, Heather McDaid and Laura Jones, this publisher has one goal: supporting careers of new and emerging writers — and making as much noise as possible about each.

A Book Apart

A Book ApartBooks available in two formats (Standards and Briefs) on topics ranging from technical to theory: responsive web design, Git, and JavaScript to content strategy, design principles, management, and more. For people who design, write, and code.

Smashing Books

Smashing BooksOur very own Smashing books aim to deliver in-depth knowledge and expertise shared by experts and practitioners from the industry. Our most recent one, Inclusive Components, explores bulletproof solutions for building accessible interfaces.

Gifts, Artwork, And Posters

If you are finding gifts for friends and family for the holidays, why not support these independent makers.


DoodleCatsTopple the cat approves of this website of cat-themed products! Created by artist Beth Wilson, you’ll find a wide range of cute cat-themed greetings cards, gifts and accessories.

Seb Lester

Seb LesterMost of us have already heard of Seb Lester, but you’ll be thrilled to know that his beautiful hand-lettered work is also available to call your own.

Jessica Hische

Jessica HischeJessica is a lettering artist who has been creating custom lettering artwork for established brands, classic books and postage stamps for over the past ten years. You’ll find a wonderful collection of prints, cards and pins on her site.

Draplin Design Co. Merch

Draplin Design Co. MerchAaron James Draplin is the founder of the Draplin Design Co., based in Portland, Oregon. You’ll find a range of fun items on his site.


HeyShopThe graphic design and illustration studio Hey launched an online shop back in 2014. Since then, they’ve been sharing their personal creations with the public.

Print Workers Barcelona

Print Workers BarcelonaA nice and lovely graphic shop that focuses on handmade, limited, signed, numbered and self-published production. Artists who mainly use screen printing without forgetting other techniques such as risograph printing or letterpress. Graphic work of more than 100 international and local artists. All at affordable and real prices.

The Oh No Shop

The Oh No ShopBrought to life by Alex Norris, we’re sure that you’ll be all smiles when you check out his prints, pins and more.


LevensIf you’re a fan of jewels, check out these beautifully handmade ones done in ceramic, silver and gold. Designed by Mar del Hoyo from Barcelona, Spain.

Cristina Junquero

Cristina JunqueroInspired by Andalusian religious imaginary and classical jewellery, the work of Cristina Junquero revisits tradition to bring something new. Her studio is set in Barcelona.

Casa Atlántica

Casa AtlánticaSince 2014, Casa Atlântica works to give value to trades that are gradually being lost: their objects are born in villages of Galicia and Portugal from the hands of artisans who, with materials such as ceramics, wicker or wood, give life to their designs.

Après Ski

Après SkiEstablished in 2009, this accessories and objects studio creates designs that are inspired from the observation of different cultures and traditions — seeking people and places authenticity through books and travels.


PimoroniFounded in 2012 by Jon Williamson and Paul Beech, Pimoroni makes tech treasure for tinkerers.


Varianto:25A small startup based in Bulgaria that create fun and innovative products for developers worldwide.


YsoldaAn online store for knitters, based in Edinburgh.

Printed Magazines

As I know from launching our own print magazine here at Smashing Magazine this year, creating a print magazine requires a huge amount of work. Here are some of your favorites.

Offscreen Magazine

Offscreen MagazineOffscreen is an independent print magazine that examines how we shape technology and how technology shapes us. Offscreen Magazine is a favorite of many Smashing readers. Also check out the Dense Discovery email newsletter (I always find something new there).

Bubblesort Zines

Bubblesort ZinesZines about computer science, for ages 8-100! Each zine focuses on one concept and is filled with comics, diagrams, stories, examples, and exercises.


WizardZinesZines by Julia Evans that are aimed at working programmers who want to know how to use grep / tcpdump / strace in a fun way. (A lot of them are focused on systems/Linux concepts.)

Like The Wind

Like The WindThis running magazine is a favorite of mine. Beautifully printed, with inspiring stories from the world of runners and running.

Courses And Training

3D Fundamentals

3D Fundamentals3D is a creative playground for designers, yet still uncharted territory for most of us. 3D Fundamentals teaches you shape, form, lighting, color, and animation in a beginner-friendly course.

Every Layout

Every LayoutIf you find yourself wrestling with CSS layout, Every Layout is for you. Through a series of simple, composable layouts, you will learn how to better harness the built-in algorithms that power browsers and CSS.

Terminal Training

Terminal TrainingWorking with Terminal can be daunting. This video course wants to cure you from any fear of the terminal. For designers, new developers, UX, UI, product owners, and anyone who’s been asked to “just open the terminal”.

Universal JavaScript with Next.js

Universal JavaScript with Next.jsIf you’re tired of configuration, build tools, spagetti code and want to focus on building amazing web apps with the latest features, this complete video course will get you fit for building web apps with Next.js for React.

The CSS Workshop

The CSS WorkshopLearn CSS layout through a series of video tutorials. Straightforward and practical examples help you banish layout confusion for good.

Software And Tools

A whole selection of interesting products and tools. Many of these have free plans. If you love one of these products, however, do consider signing up for the paid version if you can. Bootstrapped products need sales, or they go away!

Better Blocker

Better BlockerA privacy tool for Safari on iPhone, iPad, and Mac. Launched by Aral Balkan and Laura Kalbag, the aim is to protect users from behavioural ads and companies that track and profile folks on the web.


PolypaneBuilt for designers and developers, the browser Polypane lets you create sites and apps that work for everyone. Features include multiple synced viewports for responsive design, visual impairment simulators, built-in accessibility testing tools, live refreshing, layout debugging and screenshotting.

Common Ninja

Common NinjaThe Common Ninja team creates plugins with the purpose to help web designers, developers, and site owners to upgrade and color their website with zero effort, time, and knowledge.


HelperbirdThe browser extension wants to bring the benefits of accessbility and customization to everyone, with features such as dyslexia fonts, changing the font and background color, text to speech, overlays, dyslexia rulers and more to make the web accessible to your needs.

Lunch Money

Lunch MoneyNo matter where you are in the world, Lunch Money keeps track of every dollar, euro, and yen spent. At the end of the day, they add it all up in your currency of choice so that you stay on top of your spendings without doing the maths.


TimematorTimemator automatically captures everything you do on your Mac. You define the rules, and once you open your working file or application, Timemator will start the timer for you automatically.


ExistBy combining data from services you already use, Exist can help you understand what makes you more happy, productive, and active. Bring your activity from your phone or fitness tracker and add other services like your calendar for greater context on what you’re up to.


ProxymanProxyman is a native, high-performance macOS application, which enables developers to observe and manipulate HTTP/HTTPS requests. Intuitive and friendly.

Standard Notes

Standard NotesSometimes all you need is a reliable and fuss-free tool to jot down your thoughts and ideas. Standard Notes is just that, a free, open-source, and completely encrypted notes app.

Leave Me Alone

Leave Me AloneUnsubscribing from the emails you don’t want to receive any longer can be time-consuming. Leave Me Alone shows you all of your subscription emails in one place so that you can unsubscribe from them with a single click.


ReadermodeYou’re getting distracted easily when you read? Reader Mode instantly removes clutter, ads, and distractions from any article. Dyslexia support is built in, too.


FathomStop scrolling through pages of reports and collecting gobs of personal data about your visitors, both of which you probably don’t need. Fathom is a simple and private website analytics platform that lets you focus on what’s important: your business.


ButtondownButtondown is a small elegant tool for producing newsletters. The minimalist interface makes it easy to write great emails; the automation acts like the editorial assistant you wish you had; and the portable subscription widget helps grow your audience from anywhere.

Carrd.coNo matter if it’s a personal profile, a landing page to capture emails, or something a bit more elaborate, Carrd lets you create simple and responsive one-page sites for pretty much anything.


PlacidFacebook, Twitter, Pinterest — all of them have different requirements when it comes to social share images. To save you time, Placid creates your social share images automatically. You define a template once, the tool does the rest.


CalibreCalibre helps you monitor and audit web performance and make meaningful improvements where it matters. You can simulate real-world conditions to understand what your audience is experiencing, see the impact of third-party code, receive monthly reports on crucial metrics without having to spend hours on distilling performance data, and much more.


TransistorHave you ever considered starting your own podcast? Transistor helps you with the rather boring part, storing your MP3 files, generating your RSS feed, hosting your podcast’s website, and distributing your show to Apple Podcasts, Spotify, and more.


KirbyKirby is a file-based CMS for building your own ideal interface. Combine forms, galleries, articles, spreadsheets and more into an amazing editing experience.


PerchThe Really Little CMS. Used by thousands of happy customers around the world, Perch does not dictate your front-end code but lets you bring your own code to your project.


StatamicStatamic cuts out the database and creates a faster, more productive way for you to build, manage, and version control beautifully creative, bespoke websites.

Other Things

A bunch of things that didn’t really fit into any other category.

rooki.designAn online magazine for design students and free design awards, Rookie was born out of the frustration in finding good, free resources for design students. Now, you can find everything you need in one single place.

Femtech Insider

Femtech InsiderStay up-to-date and read about the latest industry trends, while you learn more about founders, companies, organizations and investors at the intersection of tech and women’s health.

Rapscallion Soda

Rapscallion Soda“We are living in a world today where lemonade is made from artificial flavours & furniture polish is made with real lemons.” The handmade, bootstrapped soda company from Glasgow wants to change that.

Tech ladies

Tech ladiesTech Ladies connects you with the best jobs and opportunities in tech. Join the community or post to their job board if you are looking for employees.

Front-End Challenges Club

Front-End Challenges ClubDo you want to put your front-end skills to the test? The Front-End Challenges Club gives you a new fun challenge to master every two weeks.

Diversify Tech

Diversify TechDiversify Tech connects underrepresented people in tech. Once a week, they’ll send you scholarships, events, job opportunities, and more.

With Jack

With JackWith Jack is all about insurance for freelance creatives, giving designers, developers, illustrators, and other web professionals the insurance they need. No endless features or stale service but one solid policy and the personal touch.

Find Support If You Are An Indie Maker

There are some excellent communities that seek to support bootstrapped businesses, sole founders and small teams. Check these out to find interesting products — or to get help in shipping your own.

  • IndieHackers: Work together to build profitable online businesses.
  • Makerlog: A collaborative task log that helps over 3000+ creators get things done.
  • WIP: Maker Community.

Add Your Favorites To The Comments!

Did we miss one of your favorite independent products? If so, please add a link in the comments, and don’t forget to let us know what it is and why you love it, too!

Smashing Editorial (il, cm)
10 Popular Design Trends It’s Time to Let Die

If you’ve ever moved from one home to another, you know how difficult it can be to get rid of things you’ve owned for years. While digging through your closet, you find an old pair of pants you used to wear all the time despite the growing holes in the knees. But you tell yourself, “Maybe I’ll wear them around the house when it gets warmer” or “I bet the grunge look will come back in style”.

It’s easy to make these kinds of justifications in web design and development, too. You think:

“I’ve used the keyword meta tag for as long as I can remember. What can it hurt to keep doing so?”

Similar to how your clothes may become outdated or your appliances obsolete over time, the same thing happens with design and development trends. Rather than hold onto techniques that no longer serve you and only add more to your workload, it’s a much better idea to clear them out and make way for modern trends that’ll have a greater impact.

10 Popular Design Trends It’s Time to Let Die

When we talk about outdated web design trends, we’re not just talking about ones that have been obsolete for years. We’re also referring to trends and techniques that we know for a fact compromise the user experience and need to go away ASAP.

1. Cheesy Stock Photos

There’s nothing inherently bad about using stock photos. Many clients don’t have the budgets or wherewithal to create their own company photos and stock photos are a viable alternative.

That said, there was a time when “bad” (i.e. super cheesy and unrealistic) stock photos were all the rage. Even today, you’ll find websites that use these kinds of photos because there’s still an assumption that two people shaking hands in a well-lit conference room signals trust. (It doesn’t.)

Image via DepositPhotos.

2. Hero Sliders

Image slider technology was pretty great in its heyday. It allowed web designers to conserve space while displaying a number of promotional offers at once. In addition to sliders often slowing down page speeds, they also have a tendency to slow users down as they distract them from moving onto other parts of a website.

Verizon Wireless, for instance, has a great example of a strong but simple hero image design in 2019:

This is vastly different from the image slider it used back in 2013:

For the most part, we’ve learned to be more efficient with this space, though there are still some websites that can’t make up their minds about which offer to show above the fold… which only makes it more difficult for visitors to decide next steps. Take the initiative and do it for them with a single hero banner.

3. Autoplay

It’s not common to find websites with background audio, let alone autoplay audio, these days. That said, what you do occasionally find are websites that automatically play videos or ads with audio. Needless to say, this needs to stop. If your video (or audio) players don’t allow your visitors to take control of when they start, change that up now.

4. The 3-Click Rule

Over the years, web designers have looked for ways to decrease friction in the user experience. The three-click rule was meant to be one of the ways to do this. However, according to a recent report from the Nielsen Norman Group, there’s never been any data to back up this claim:

“In fact, a study by Joshua Porter has debunked it; the study showed that user dropoff does not increase when the task involves more than 3 clicks, nor does satisfaction decrease. Limiting interaction cost is indeed important, but the picture is more complicated than simply counting clicks and having a rule of thumb for the maximum number allowed.”

Rather than minimize for minimization’s sake, consider the complexity of the task or funnel you’re designing when determining quantity of steps.

5. (External) Links That Open in the Same Tab

There are a number of reasons to add links to your content: for navigational purposes, promotional purposes, and referential purposes. But when you add a hyperlink to your text, consider the following: Is it okay if the link directs visitors to a page in this same browser tab?

External links, for instance, should always open in a new browser tab. Your goal in designing a website is to get more visitors to convert. Letting an external link replace your website in the open tab will only decrease the chances of that happening. In some cases, internal links shouldn’t be opened in the same tab either. So, be sure to think about this the next time you add a link to your site.

6. Non-Traditional Scrolling

Although we’ve become accustomed to swipe gestures in mobile apps, horizontal and other non-traditional scrolling isn’t something that’s caught on with websites. While it’s definitely a design trend that helped many businesses set themselves apart from the pack a few years back, it’s just too gimmicky to use these days.

Robby Leonardi’s interactive resume website was one of the first I remember seeing and it was a brilliant way to capture attention — especially from those of us who grew up with Mario.

But today? Any sort of non-traditional scrolling is just impractical and unnecessary. Even Robby’s current website has broken up this side-scrolling design and turned it into a vertical-scrolling page:

If you want to keep visitors engaged with your website in this day and age, don’t make them figure out how to scroll through your website. 

7. Keyword Meta Tag

For years (we’re talking nearly a decade), the keyword meta tag has not been supported by popular search engines. Despite knowing that the meta tag is useless, some designers still take the time to add it in. But why bother if it’s an extra step that gets you nothing in return?

8. Bad Pop-ups

Although pop-ups have undergone an evolution over the years — from the super-annoying pop-up ads that appeared outside the browser to the ever-present privacy notices we now see thanks to GDPR. While there is certainly some value in using pop-ups on a website, there are just too many kinds of bad pop-ups that need to disappear.

“Bad” pop-ups are ones that:

  • Show up too early on a website (like the second someone enters it);
  • Appear too many times during a single or return visit;
  • Send users to Facebook Messenger to collect their lead magnet and then bombard them with messages there;
  • Contain two buttons. Users that accept the offer, get a friendly message. Those that don’t are served up aggressive or shame-inducing language;
  • Repeat an offer that’s already designed into the website as a promotional banner.

9. Slow-Loading Websites

Mobile websites are notoriously difficult to optimize for speed when compared to their desktop counterparts. Unlike in years past where you could’ve rationalized away speed optimizations for mobile, today, it needs to be a priority with Google’s mobile-first indexing. PWAs are one way to give your mobile site an instant speed boost.

On a related note, by designing a PWA instead of a mobile-responsive website, you’d be able to cater to users with poor or no wi-fi connectivity — a segment that’s often been overlooked in web design.

10. Flash

I cannot believe I’m having to include this last one in 2019, but it seems there are still websites using the Flash Player.

Adobe has already told us that it would be cutting support for Flash next year. Web browsers are starting to remove their support for Flash players as well. And good riddance. Flash has long had issues with security flaws and usability issues.

If you’re trying to hold out on this (or your clients are dragging their feet), keep in mind that this is what visitors will see on many browsers in 2020 and beyond:

Bottom line: If the creator of Flash is pulling support, you need to do the same for any of your websites that still use it.


It’s easy to get wrapped up in what the next big thing is in web design — AR tech, typography trends, color gradients, etc. But what about all of those trends and techniques that have become a habit over the years?

Rather than hold onto outdated design strategies that will only hinder your progress as a web designer and hold your clients’ websites back, start shedding these obsolete (or soon-to-be obsolete) practices now.

p img {display:inline-block; margin-right:10px;}
.alignleft {float:left;}
p.showcase {clear:both;}
body#browserfriendly p, body#podcast p, div#emailbody p{margin:0;}

Abstracting WordPress Code To Reuse With Other CMSs: Implementation (Part 2)

Abstracting WordPress Code To Reuse With Other CMSs: Implementation (Part 2)

Abstracting WordPress Code To Reuse With Other CMSs: Implementation (Part 2)

Leonardo Losoviz

In the first part of this series, we learned the key concepts to build an application that is as CMS-agnostic as possible. In this second and final part, we will proceed to abstract a WordPress application, making its code ready to be used with Symfony components, Laravel framework, and October CMS (which is based on Laravel).

Accessing Services

Before we start abstracting the code, we need to provide the layer of dependency injection to the application. As described in the first part of this series, this layer is satisfied through Symfony’s DependencyInjection component. To access the defined services, we create a class ContainerBuilderFactory which simply stores a static instance of the component’s ContainerBuilder object:

use Symfony\Component\DependencyInjection\ContainerBuilder; class ContainerBuilderFactory { private static $instance; public static function init() { self::$instance = new ContainerBuilder(); } public static function getInstance() { return self::$instance; }

Then, to access a service called "cache", the application requests it like this:

$cacheService = ContainerBuilderFactory::getInstance()->get('cache');
// Do something with the service
// $cacheService->...

Abstracting WordPress Code

We have identified the following pieces of code and concepts from a WordPress application that need be abstracted away from WordPress’s opinionatedness:

  • accessing functions
  • function names
  • function parameters
  • states (and other constant values)
  • CMS helper functions
  • user permissions
  • application options
  • database column names
  • errors
  • hooks
  • routing
  • object properties
  • global state
  • entity models (meta, post types, pages being posts, and taxonomies —tags and categories—)
  • translation
  • media.

Let’s proceed to abstract them, one by one.

Note: For ease of reading, I have omitted adding namespaces to all classes and interfaces throughout this article. However, adding namespaces, as specified in PHP Standards Recommendation PSR-4, is a must! Among other advantages, the application can then benefit from autoloading, and Symfony’s dependency injection can rely on automatic service loading as to reduce its configuration to the bare minimum.

Accessing functions

The mantra “code against interfaces, not implementations” means that all those functions provided by the CMS cannot be accessed directly anymore. Instead, we must access the function from a contract (an interface), on which the CMS function will simply be the implementation. By the end of the abstraction, since no WordPress code will be referenced directly anymore, we can then swap WordPress with a different CMS.

For instance, if our application accesses function get_posts:

$posts = get_posts($args);

We must then abstract this function under some contract:

interface PostAPIInterface
{ public function getPosts($args);

The contract must be implemented for WordPress:

class WPPostAPI implements PostAPIInterface
{ public function getPosts($args) { return get_posts($args); }

A service "posts_api" must be added to the dependency injection services.yaml configuration file, indicating which class resolves the service:

services: posts_api: class: \WPPostAPI

And finally, the application can reference the function through service "posts_api":

$postsAPIService = ContainerBuilderFactory::getInstance()->get('posts_api');
$posts = $postsAPIService->getPosts($args);

Function names

If you have noticed from the code demonstrated above, function get_posts is abstracted as getPosts. There are a couple of reasons why this is a good idea:

  • By calling the function differently, it helps identify which code belongs to WordPress and which code belongs to our abstracted application.
  • Function names must be camelCased to comply with PSR-2, which attempts to define a standard for writing PHP code.

Certain functions can be redefined, making more sense in an abstract context. For instance, WordPress function get_user_by($field, $value) uses parameter $field with values "id", "ID", "slug", "email" or "login" to know how to get the user. Instead of replicating this methodology, we can explicitly define a separate function for each of them:

interface UsersAPIInterface
{ public function getUserById($value); public function getUserByEmail($value); public function getUserBySlug($value); public function getUserByLogin($value);

And these are resolved for WordPress:

class WPUsersAPI implements UsersAPIInterface
{ public function getUserById($value) { return get_user_by('id', $value); } public function getUserByEmail($value) { return get_user_by('email', $value); } public function getUserBySlug($value) { return get_user_by('slug', $value); } public function getUserByLogin($value) { return get_user_by('login', $value); }

Certain other functions should be renamed because their names convey information about their implementation, which may not apply for a different CMS. For instance, WordPress function get_the_author_meta can receive parameter "user_lastname", indicating that the user’s lastname is stored as a “meta” value (which is defined as an additional property for an object, not originally mapped in the database model). However, other CMSs may have a column "lastname" in the user table, so it doesn’t apply as a meta value. (The actual definition of “meta” value is actually inconsistent in WordPress: function get_the_author_meta also accepts value "user_email", even though the email is stored on the user table. Hence, I’d rather stick to my definition of “meta” value, and remove all inconsistencies from the abstracted code.)

Then, our contract will implement the following functions:

interface UsersAPIInterface
{ public function getUserDisplayName($user_id); public function getUserEmail($user_id); public function getUserFirstname($user_id); public function getUserLastname($user_id); ...

Which are resolved for WordPress:

class WPUsersAPI implements UsersAPIInterface
{ public function getUserDisplayName($user_id) { return get_the_author_meta('display_name', $user_id); } public function getUserEmail($user_id) { return get_the_author_meta('user_email', $user_id); } public function getUserFirstname($user_id) { return get_the_author_meta('user_firstname', $user_id); } public function getUserLastname($user_id) { return get_the_author_meta('user_lastname', $user_id); } ...

Our functions could also be re-defined as to remove the limitations from WordPress. For instance, function update_user_meta($user_id, $meta_key, $meta_value) can receive one meta attribute at a time, which makes sense since each of these is updated on its own database query. However, October CMS maps all meta attributes together on a single database column, so it makes more sense to update all values together on a single database operation. Then, our contract can include an operation updateUserMetaAttributes($user_id, $meta) which can update several meta values at the same time:

interface UserMetaInterface
{ public function updateUserMetaAttributes($user_id, $meta);

Which is resolved for WordPress like this:

class WPUsersAPI implements UsersAPIInterface
{ public function updateUserMetaAttributes($user_id, $meta) { foreach ($meta as $meta_key => $meta_value) { update_user_meta($user_id, $meta_key, $meta_value); } }

Finally, we may want to re-define a function to remove its ambiguities. For instance, WordPress function add_query_arg can receive parameters in two different ways:

  1. Using a single key and value: add_query_arg('key', 'value', '');
  2. Using an associative array: add_query_arg(['key1' => 'value1', 'key2' => 'value2'], '');

This becomes difficult to keep consistent across CMSs. Hence, our contract can define functions addQueryArg (singular) and addQueryArgs (plural) as to remove the ambiguity:

public function addQueryArg(string $key, string $value, string $url);
public function addQueryArgs(array $key_values, string $url);

Function parameters

We must also abstract the parameters to the function, making sure they make sense in a generic context. For each function to abstract, we must consider:

  • renaming and/or re-defining the parameters;
  • renaming and/or re-defining the attributes passed on array parameters.

For instance, WordPress function get_posts receives a unique parameter $args, which is an array of attributes. One of its attributes is fields which, when given the value "ids", makes the function return an array of IDs instead of an array of objects. However, I deem this implementation too specific for WordPress, and for a generic context I’d prefer a different solution: Convey this information through a separate parameter called $options, under attribute "return-type".

To accomplish this, we add parameter $options to the function in our contract:

interface PostAPIInterface
{ public function getPosts($args, $options = []);

Instead of referencing WordPress constant value "ids" (which we can’t guarantee will be the one used in all other CMSs), we create a corresponding constant value for our abstracted application:

class Constants
{ const RETURNTYPE_IDS = 'ids';

The WordPress implementation must map and recreate the parameters between the contract and the implementation:

class WPPostAPI implements PostAPIInterface
{ public function getPosts($args, $options = []) { if ($options['return-type'] == Constants::RETURNTYPE_IDS) { $args['fields'] = 'ids'; } return get_posts($args); }

And finally, we can execute the code through our contract:

$options = [ 'return-type' => Constants::RETURNTYPE_IDS,
$post_ids = $postsAPIService->getPosts($args, $options);

While abstracting the parameters, we should avoid transferring WordPress’s technical debt to our abstracted code, whenever possible. For instance, parameter $args from function get_posts can contain attribute 'post_type'. This attribute name is somewhat misleading, since it can receive one element (post_type => "post") but also a list of them (post_type => "post, event"), so this name should be in plural instead: post_types. When abstracting this piece of code, we can set our interface to expect attribute post_types instead, which will be mapped to WordPress’s post_type.

Similarly, different functions accept arguments with different names, even though these have the same objective, so their names can be unified. For instance, through parameter $args, WordPress function get_posts accepts attribute posts_per_page, and function get_users accepts attribute number. These attribute names can perfectly be replaced with the more generic attribute name limit.

It is also a good idea to rename parameters to make it easy to understand which ones belong to WordPress and which ones have been abstracted. For instance, we can decide to replace all "_" with "-", so our newly-defined argument post_types becomes post-types.

Applying these prior considerations, our abstracted code will look like this:

class WPPostAPI implements PostAPIInterface
{ public function getPosts($args, $options = []) { ... if (isset($args['post-types'])) { $args['post_type'] = $args['post-types']; unset($args['post-types']); } if (isset($args['limit'])) { $args['posts_per_page'] = $args['limit']; unset($args['limit']); } return get_posts($args); }

We can also re-define attributes to modify the shape of their values. For instance, WordPress parameter $args in function get_posts can receive attribute date_query, whose properties ("after", "inclusive", etc) can be considered specific to WordPress:

$date = current_time('timestamp');
$args['date_query'] = array( array( 'after' => date('Y-m-d H:i:s', $date), 'inclusive' => true, )

To unify the shape of this value into something more generic, we can re-implement it using other arguments, such as "date-from" and "date-from-inclusive" (this solution is not 100% convincing though, since it is more verbose than WordPress’s):

class WPPostAPI implements PostAPIInterface
{ public function getPosts($args, $options = []) { ... if (isset($args['date-from'])) { $args['date_args'][] = [ 'after' => $args['date-from'], 'inclusive' => false, ]; unset($args['date-from']); } if (isset($args['date-from-inclusive'])) { $args['date_args'][] = [ 'after' => $args['date-from-inclusive'], 'inclusive' => true, ]; unset($args['date-from-inclusive']); } return get_posts($args); }

In addition, we need to consider if to abstract or not those parameters which are too specific to WordPress. For instance, function get_posts allows to order posts by attribute menu_order, which I don’t think it works in a generic context. Then, I’d rather not abstract this code and keep it on the CMS-specific package for WordPress.

Finally, we can also add argument types (and, since here we are, also return types) to our contract fuction, making it more understandable and allowing the code to fail in compilation time instead of during runtime:

interface PostAPIInterface
{ public function getPosts(array $args, array $options = []): array;

States (and other constant values)

We need to make sure that all states have the same meaning in all CMSs. For instance, posts in WordPress can have one among the following states: "publish", "pending", "draft" or "trash". To make sure that the application references the abstracted version of the states and not the CMS-specific one, we can simply define a constant value for each of them:

class PostStates { const PUBLISHED = 'published'; const PENDING = 'pending'; const DRAFT = 'draft'; const TRASH = 'trash';

As it can be seen, the actual constant values may or may not be the same as in WordPress: while "publish" was renamed as "published", the other ones remain the same.

For the implementation for WordPress, we convert from the agnostic value to the WordPress-specific one:

class WPPostAPI implements PostAPIInterface
{ public function getPosts($args, $options = []) { ... if (isset($args['post-status'])) { $conversion = [ PostStates::PUBLISHED => 'publish', PostStates::PENDING => 'pending', PostStates::DRAFT => 'draft', PostStates::TRASH => 'trash', ]; $args['post_status'] = $conversion[$args['post-status']]; unset($args['post-status']); } return get_posts($args); }

Finally, we can reference these constants throughout our CMS-agnostic application:

$args = [ 'post-status' => PostStates::PUBLISHED,
$posts = $postsAPIService->getPosts($args);

This strategy works under the assumption that all CMSs will support these states. If any CMS does not support a particular state (eg: "pending") then it should throw an exception whenever a corresponding functionality is invoked.

CMS helper functions

WordPress implements several helper functions that must also abstracted, such as make_clickable. Because these functions are very generic, we can implement a default behavior for them that works well in an abstract context, and which can be overridden if the CMS implements a better solution.

We first define the contract:

interface HelperAPIInterface
{ public function makeClickable(string $text);

And provide a default behaviour for the helper functions through an abstract class:

abstract class AbstractHelperAPI implements HelperAPIInterface
{ public function makeClickable(string $text) { return preg_replace('!(((f|ht)tp(s)?://)[-a-zA-Zа-яА-Я()0-9@:%_+.~#?&;//=]+)!i', '<a href="$1">$1</a>', $text); }

Now, our application can either use this functionality or, if it runs on WordPress, use the WordPress-specific implementation:

class WPHelperAPI extends AbstractHelperAPI
{ public function makeClickable(string $text) { return make_clickable($text); }

User permissions

For all CMSs which support user management, in addition to abstracting the corresponding functions (such as current_user_can and user_can in WordPress), we must also make sure that the user permissions (or capabilities) have the same effect across all CMSs. To achieve this, our abstracted application needs to explicitly state what is expected from the capability, and the implementation for each CMS must either satisfy it through one of its own capabilities or throw an exception if it can’t satisfy it. For instance, if the application needs to validate if the user can edit posts, it can represent it through a capability called "capability:editPosts", which is satisfied for WordPress through its capability "edit_posts".

This is still an instance of the “code against interfaces, not implementations” principle, however here we run against a problem: Whereas in PHP we can define interfaces and classes to model contracts and service providers (which works in compilation time, so that the code doesn’t compile if a class implementing an interface does not implement all functions defined in the interface), PHP offers no similar construct to validate that a contract capability (which is simply a string, such as "capability:editPosts") has been satisfied through a capability by the CMS. This concept, which I call a “loose contract”, will need to be handled by our application, on runtime.

To deal with “loose contracts”, I have created a service LooseContractService through which:

  • the application can define what “contract names” must be implemented, through function requireNames.
  • the CMS-specific implementations can satisfy those names, through function implementNames.
  • the application can get the implementation of a name through function getImplementedName.
  • the application can also inquire for all non-satisfied required names through function getNotImplementedRequiredNames, as to throw an exception or log the error if needed.

The service looks like this:

class LooseContractService
{ protected $requiredNames = []; protected $nameImplementations = []; public function requireNames(array $names): void { $this->requiredNames = array_merge( $this->requiredNames, $names ); } public function implementNames(array $nameImplementations): void { $this->nameImplementations = array_merge( $this->nameImplementations, $nameImplementations ); } public function getImplementedName(string $name): ?string { return $this->nameImplementations[$name]; } public function getNotImplementedRequiredNames(): array { return array_diff( $this->requiredNames, array_keys($this->nameImplementations) ); }

The application, when initialized, can then establish loose contracts by requiring names:

$looseContractService = ContainerBuilderFactory::getInstance()->get('loose_contracts');
$looseContractService->requireNames([ 'capability:editPosts',

And the CMS-specific implementation can satisfy these:

$looseContractService->implementNames([ 'capability:editPosts' => 'edit_posts',

The application can then resolve the required name to the implementation from the CMS. If this required name (in this case, a capability) is not implemented, then the application may throw an exception:

$cmsCapabilityName = $looseContractService->getImplementedName('capability:editPosts');
if (!$cmsCapabilityName) { throw new Exception(sprintf( "The CMS has no support for capability \"%s\"", 'capability:editPosts' ));
// Now can use the capability to check for permissions
$userManagementAPIService = ContainerBuilderFactory::getInstance()->get('user_management_api');
if ($userManagementAPIService->userCan($user_id, $cmsCapabilityName)) { ...

Alternatively, the application can also fail when first initialized if any one required name is not satisfied:

if ($notImplementedNames = $looseContractService->getNotImplementedRequiredNames()) { throw new Exception(sprintf( "The CMS has not implemented loose contract names %s", implode(', ', $notImplementedNames) ));

Application options

WordPress ships with several application options, such as those stored in table wp_options under entries "blogname", "blogdescription", "admin_email", "date_format" and many others. Abstracting application options involves:

  • abstraction the function getOption;
  • abstracting each of the required options, aiming to make the CMS satisfy the notion of this option (eg: if a CMS doesn’t have an option for the site’s description, it can’t return the site’s name instead).

Let’s solve these 2 actions in turn. Concerning function getOption, I believe that we can expect all CMSs to support storing and retrieving options, so we can place the corresponding function under a CMSCoreInterface contract:

interface CMSCoreInterface
{ public function getOption($option, $default = false);

As it can be observed from the function signature above, I’m making the assumption that each option will also have a default value. However, I don’t know if every CMS allows setting default values for options. But it doesn’t matter since the implementation can simply return NULL then.

This function is resolved for WordPress like this:

class WPCMSCore implements CMSCoreInterface
{ public function getOption($option, $default = false) { return get_option($option, $default); }

To solve the 2nd action, which is abstracting each needed option, it is important to notice that even though we can always expect the CMS to support getOption, we can’t expect it to implement each single option used by WordPress, such as "use_smiles" or "default_ping_status". Hence, we must first filter all options, and abstract only those that make sense in a generic context, such as "siteName" or "dateFormat".

Then, having the list of options to abstract, we can use a “loose contract” (as explained earlier on) and require a corresponding option name for each, such as "option:siteName" (resolved for WordPress as "blogname") or "option:dateFormat" (resolved as "date_format").

Database column names

In WordPress, when we are requesting data from function get_posts we can set attribute "orderby" in $args to order the results, which can be based on a column from the posts table (such as values "ID", "title", "date", "comment_count", etc), a meta value (through values "meta_value" and "meta_value_num") or other values (such as "post__in" and "rand").

Whenever the value corresponds to the table column name, we can abstract them using a “loose contract”, as explained earlier on. Then, the application can reference a loose contract name:

$args = [ 'orderby' => $looseContractService->getImplementedName('dbcolumn:orderby:posts:date'),
$posts = $postsAPIService->getPosts($args);

And this name is resolved for WordPress:

$looseContractService->implementNames([ 'dbcolumn:orderby:posts:date' => 'date',

Now, let’s say that in our WordPress application we have created a meta value "likes_count" (which stores how many likes a post has) to order posts by popularity, and we want to abstract this functionality too. To order results by some meta property, WordPress expects an additional attribute "meta_key", like this:

$args = [ 'orderby' => 'meta_value', 'meta_key' => 'likes_count',

Because of this additional attribute, I consider this implementation WordPress-specific and very difficult to abstract to make it work everywhere. Then, instead of generalizing this functionality, I can simply expect every CMS to add their own, specific implementation.

Let’s do that. First, I create a helper class to retrieve the CMS-agnostic query:

class QueryHelper
{ public function getOrderByQuery() { return array( 'orderby' => $looseContractService->getImplementedName('dbcolumn:orderby:posts:likesCount'), ); }

The OctoberCMS-specific package can add a column "likes_count" to the posts table, and resolve name "dbcolumn:orderby:posts:likesCount" to "like_count" and it will work. The WordPress-specific package, though, must resolve "dbcolumn:orderby:posts:likesCount" as "meta_value" and then override the helper function to add the additional property "meta_key":

class WPQueryHelper extends QueryHelper
{ public function getOrderByQuery() { $query = parent::getOrderByQuery(); $query['meta_key'] = 'likes_count'; return $query; }

Finally, we set-up the helper query class as a service in the ContainerBuilder, configure it to be resolved to the WordPress-specific class, and we obtain the query for ordering results:

$queryHelperService = ContainerBuilderFactory::getInstance()->get('query_helper');
$args = $queryHelperService->getOrderByQuery();
$posts = $postsAPIService->getPosts($args);

Abstracting the values for ordering results that do not correspond to column names or meta properties (such as "post__in" and "rand") seems to be more difficult. Because my application doesn’t use them, I haven’t considered how to do it, or even if it is possible. Then I took the easy way out: I have considered these to be WordPress-specific, hence the application makes them available only when running on WordPress.


When dealing with errors, we must consider abstracting the following elements:

  • the definition of an error;
  • error codes and messages.

Let’s review these in turn.

Definition of an error:

An Error is a special object, different than an Exception, used to indicate that some operation has failed, and why it failed. WordPress represents errors through class WP_Error, and allows to check if some returned value is an error through function is_wp_error.

We can abstract checking for an error:

interface CMSCoreInterface
{ public function isError($object);

Which is resolved for WordPress like this:

class WPCMSCore implements CMSCoreInterface
{ public function isError($object) { return is_wp_error($object); }

However, to deal with errors in our abstracted code, we can’t expect all CMSs to have an error class with the same properties and methods as WordPress’s WP_Error class. Hence, we must abstract this class too, and convert from the CMS error to the abstracted error after executing a function from the CMS.

The abstract error class Error is simply a slightly modified version from WordPress’s WP_Error class:

class Error { protected $errors = array(); protected $error_data = array(); public function __construct($code = null, $message = null, $data = null) { if ($code) { $this->errors[$code][] = $message; if ($data) { $this->error_data[$code] = $data; } } } public function getErrorCodes() { return array_keys($this->errors); } public function getErrorCode() { if ($codes = $this->getErrorCodes()) { return $codes[0]; } return null; } public function getErrorMessages($code = null) { if ($code) { return $this->errors[$code] ?? []; } // Return all messages if no code specified. return array_reduce($this->errors, 'array_merge', array()); } public function getErrorMessage($code = null) { if (!$code) { $code = $this->getErrorCode(); } $messages = $this->getErrorMessages($code); return $messages[0] ?? ''; } public function getErrorData($code = null) { if (!$code) { $code = $this->getErrorCode(); } return $this->error_data[$code]; } public function add($code, $message, $data = null) { $this->errors[$code][] = $message; if ($data) { $this->error_data[$code] = $data; } } public function addData($data, $code = null) { if (!$code) { $code = $this->getErrorCode(); } $this->error_data[$code] = $data; } public function remove($code) { unset($this->errors[$code]); unset($this->error_data[$code]); }

We implement a function to convert from the CMS to the abstract error through a helper class:

class WPHelpers
{ public static function returnResultOrConvertError($result) { if (is_wp_error($result)) { // Create a new instance of the abstracted error class $error = new Error(); foreach ($result->get_error_codes() as $code) { $error->add($code, $result->get_error_message($code), $result->get_error_data($code)); } return $error; } return $result; }

And we finally invoke this method for all functions that may return an error:

class UserManagementService implements UserManagementInterface
{ public function getPasswordResetKey($user_id) { $result = get_password_reset_key($user_id); return WPHelpers::returnResultOrConvertError($result); }
Error codes and messages:

Every CMS will have its own set of error codes and corresponding explanatory messages. For instance, WordPress function get_password_reset_key can fail due to the following reasons, as represented by their error codes and messages:

  1. "no_password_reset": Password reset is not allowed for this user.
  2. "no_password_key_update": Could not save password reset key to database.

In order to unify errors so that an error code and message is consistent across CMSs, we will need to inspect these and replace them with our custom ones (possibly in function returnResultOrConvertError explained above).


Abstracting hooks involves:

  • the hook functionality;
  • the hooks themselves.

Let’s analyze these in turn.

Abstracting the hook functionality

WordPress offers the concept of “hooks”: a mechanism through which we can change a default behavior or value (through “filters”) and execute related functionality (through “actions”). Both Symfony and Laravel offer mechanisms somewhat related to hooks: Symfony provides an event dispatcher component, and Laravel’s mechanism is called events; these 2 mechanisms are similar, sending notifications of events that have already taken place, to be processed by the application through listeners.

When comparing these 3 mechanisms (hooks, event dispatcher and events) we find that WordPress’s solution is the simpler one to set-up and use: Whereas WordPress hooks enable to pass an unlimited number of parameters in the hook itself and to directly modify a value as a response from a filter, Symfony’s component requires to instantiate a new object to pass additional information, and Laravel’s solution suggests to run a command in Artisan (Laravel’s CLI) to generate the files containing the event and listener objects. If all we desire is to modify some value in the application, executing a hook such as $value = apply_filters("modifyValue", $value, $post_id); is as simple as it can get.

In the first part of this series, I explained that the CMS-agnostic application already establishes a particular solution for dependency injection instead of relying on the solution by the CMS, because the application itself needs this functionality to glue its parts together. Something similar happens with hooks: they are such a powerful concept that the application can greatly benefit by making it available to the different CMS-agnostic packages (allowing them to interact with each other) and not leave this wiring-up to be implemented only at the CMS level. Hence, I have decided to already ship a solution for the “hook” concept in the CMS-agnostic application, and this solution is the one implemented by WordPress.

In order to decouple the CMS-agnostic hooks from those from WordPress, once again we must “code against interfaces, not implementations”: We define a contract with the corresponding hook functions:

interface HooksAPIInterface
{ public function addFilter(string $tag, $function_to_add, int $priority = 10, int $accepted_args = 1): void; public function removeFilter(string $tag, $function_to_remove, int $priority = 10): bool; public function applyFilters(string $tag, $value, ...$args); public function addAction(string $tag, $function_to_add, int $priority = 10, int $accepted_args = 1): void; public function removeAction(string $tag, $function_to_remove, int $priority = 10): bool; public function doAction(string $tag, ...$args): void;

Please notice that functions applyFilters and doAction are variadic, i.e. they can receive a variable amount of arguments through parameter ...$args. By combining this feature (which was added to PHP in version 5.6, hence it was unavailable to WordPress until very recently) with argument unpacking, i.e. passing a variable amount of parameters ...$args to a function, we can easily provide the implementation for WordPress:

class WPHooksAPI implements HooksAPIInterface
{ public function addFilter(string $tag, $function_to_add, int $priority = 10, int $accepted_args = 1): void { add_filter($tag, $function_to_add, $priority, $accepted_args); } public function removeFilter(string $tag, $function_to_remove, int $priority = 10): bool { return remove_filter($tag, $function_to_remove, $priority); } public function applyFilters(string $tag, $value, ...$args) { return apply_filters($tag, $value, ...$args); } public function addAction(string $tag, $function_to_add, int $priority = 10, int $accepted_args = 1): void { add_action($tag, $function_to_add, $priority, $accepted_args); } public function removeAction(string $tag, $function_to_remove, int $priority = 10): bool { return remove_action($tag, $function_to_remove, $priority); } public function doAction(string $tag, ...$args): void { do_action($tag, ...$args); }

As for an application running on Symfony or Laravel, this contract can be satisfied by installing a CMS-agnostic package implementing WordPress-like hooks, such as this one, this one or this one.

Finally, whenever we need to execute a hook, we do it through the corresponding service:

$hooksAPIService = ContainerBuilderFactory::getInstance()->get('hooks_api');
$title = $hooksAPIService->applyFilters("modifyTitle", $title, $post_id);
Abstracting the hooks themselves

We need to make sure that, whenever a hook is executed, a consistent action will be executed no matter which is the CMS. For hooks defined inside of our application that is no problem, since we can resolve them ourselves, most likely in our CMS-agnostic package. However, when the hook is provided by the CMS, such as action "init" (triggered when the system has been initialized) or filter "the_title" (triggered to modify a post’s title) in WordPress, and we invoke these hooks, we must make sure that all other CMSs will process them correctly and consistently. (Please notice that this concerns hooks that make sense in every CMS, such as "init"; certain other hooks can be considered too specific to WordPress, such as filter "rest_{$this->post_type}_query" from a REST controller, so we don’t need to abstract them.)

The solution I found is to hook into actions or filters defined exclusively in the application (i.e. not in the CMS), and to bridge from CMS hooks to application hooks whenever needed. For instance, instead of adding an action for hook "init" (as defined in WordPress), any code in our application must add an action on hook "cms:init", and then we implement the bridge in the WordPress-specific package from "init" to "cms:init":

$hooksAPIService->addAction('init', function() use($hooksAPIService) { $hooksAPIService->doAction('cms:init');

Finally, the application can add a “loose contract” name for "cms:init", and the CMS-specific package must implement it (as demonstrated earlier on).


Different frameworks will provide different solutions for routing (i.e. the mechanism of identifying how the requested URL will be handled by the application), which reflect the architecture of the framework:

  • In WordPress, URLs map to database queries, not to routes.
  • Symfony provides a Routing component which is independent (any PHP application can install it and use it), and which enables to define custom routes and which controller will process them.
  • Laravel’s routing builds on top of Symfony’s routing component to adapt it to the Laravel framework.

As it can be seen, WordPress’s solution is the outlier here: the concept of mapping URLs to database queries is tightly coupled to WordPress’s architecture, and we would not want to restrict our abstracted application to this methodology (for instance, October CMS can be set-up as a flat-file CMS, in which case it doesn’t use a database). Instead, it makes more sense to use Symfony’s approach as its default behavior, and allow WordPress to override this behavior with its own routing mechanism.

(Indeed, while WordPress’s approach works well for retrieving content, it is rather inappropriate when we need to access some functionality, such as displaying a contact form. In this case, before the launch of Gutenberg, we were forced to create a page and add a shortcode "

" to it as content, which is not as clean as simply mapping the route to its corresponding controller directly.)

Hence, the routing for our abstracted application will not be based around the modeled entities (post, page, category, tag, author) but purely on custom-defined routes. This should already work perfectly for Symfony and Laravel, using their own solutions, and there is not much for us to do other than injecting the routes with the corresponding controllers into the application’s configuration.

To make it work in WordPress, though, we need to take some extra steps: We must introduce an external library to handle routing, such as Cortex. Making use of Cortex, the application running on WordPress can have it both ways:

  • if there is a custom-defined route matching the requested URL, use its corresponding controller.
  • if not, let WordPress handle the request in its own way (i.e. retrieving the matched database entity or returning a 404 if no match is successful).

To implement this functionality, I have designed the contract CMSRoutingInterface to, given the requested URL, calculate two pieces of information:

  • the actual route, such as contact, posts or posts/my-first-post.
  • the nature of the route: core nature values "standard", "home" and "404", and additional nature values added through packages such as "post" through a “Posts” package or "user" through a “Users” package.

The nature of the route is an artificial construction that enables the CMS-agnostic application to identify if the route has extra qualities attached to it. For instance, when requesting the URL for a single post in WordPress, the corresponding database object post is loaded into the global state, under global $post. It also helps identify which case we want to handle, to avoid inconsistencies. For instance, we could have defined a custom route contact handled by a controller, which will have nature "standard", and also a page in WordPress with slug "contact", which will have nature "page" (added through a package called “Pages”). Then, our application can prioritize which way to handle the request, either through the controller or through a database query.

Let’s implement it. We first define the service’s contract:

interface CMSRoutingInterface
{ public function getNature(); public function getRoute();

We can then define an abstract class which provides a base implementation of these functions:

abstract class AbstractCMSRouting implements CMSRoutingInterface
{ const NATURE_STANDARD = 'standard'; const NATURE_HOME = 'home'; const NATURE_404 = '404'; public function getNature() { return self::NATURE_STANDARD; } public function getRoute() { // By default, the URI path is already the route (minus parameters and trailing slashes) $route = $_SERVER['REQUEST_URI']; $params_pos = strpos($route, '?'); if ($params_pos !== false) { $route = substr($route, 0, $params_pos); } return trim($route, '/'); }

And the implementation is overriden for WordPress:

class WPCMSRouting extends AbstractCMSRouting
{ const ROUTE_QUERY = [ 'custom_route_key' => 'custom_route_value', ]; private $query; private function init() { if (is_null($this->query)) { global $wp_query; $this->query = $wp_query; } } private function isStandardRoute() { return !empty(array_intersect($this->query->query_vars, self::ROUTE_QUERY)); } public function getNature() { $this->init(); if ($this->isStandardRoute()) { return self::NATURE_STANDARD; } elseif ($this->query->is_home() || $this->query->is_front_page()) { return self::NATURE_HOME; } elseif ($this->query->is_404()) { return self::NATURE_404; } // Allow components to implement their own natures $hooksAPIService = ContainerBuilderFactory::getInstance()->get('hooks_api'); return $hooksAPIService->applyFilters( "nature", parent::getNature(), $this->query ); }

In the code above, please notice how constant ROUTE_QUERY is used by the service to know if the route is a custom-defined one, as configured through Cortex:

$hooksAPIService->addAction( 'cortex.routes', function(RouteCollectionInterface $routes) { // Hook into filter "routes" to provide custom-defined routes $appRoutes = $hooksAPIService->applyFilters("routes", []); foreach ($appRoutes as $route) { $routes->addRoute(new QueryRoute( $route, function (array $matches) { return WPCMSRouting::ROUTE_QUERY; } )); } }

Finally, we add our routes through hook "routes":

$hooksAPIService->addFilter( 'routes', function($routes) { return array_merge( $routes, [ 'contact', 'posts', ] ); }

Now, the application can find out the route and its nature, and proceed accordingly (for instance, for a "standard" nature invoke its controller, or for a "post" nature invoke WordPress’s templating system):

$cmsRoutingService = ContainerBuilderFactory::getInstance()->get('routing');
$nature = $cmsRoutingService->getNature();
$route = $cmsRoutingService->getRoute();
// Process the requested route, as appropriate
// ...

Object properties

A rather inconvenient consequence of abstracting our code is that we can’t reference the properties from an object directly, and we must do it through a function instead. This is because different CMSs will represent the same object as containing different properties, and it is easier to abstract a function to access the object properties than to abstract the object itself (in which case, among other disadvantages, we may have to reproduce the object caching mechanism from the CMS). For instance, a post object $post contains its ID under $post->ID in WordPress and under $post->id in October CMS. To resolve this property, our contract PostObjectPropertyResolverInterface will contain function getId:

interface PostObjectPropertyResolverInterface { public function getId($post);

Which is resolved for WordPress like this:

class WPPostObjectPropertyResolver implements PostObjectPropertyResolverInterface { public function getId($post) { return $post->ID; }

Similarly, the post content property is $post->post_content in WordPress and $post->content in October CMS. Our contract will then allow to access this property through function getContent:

interface PostObjectPropertyResolverInterface { public function getContent($post);

Which is resolved for WordPress like this:

class WPPostObjectPropertyResolver implements PostObjectPropertyResolverInterface { public function getContent($post) { return $post->post_content; }

Please notice that function getContent receives the object itself through parameter $post. This is because we are assuming the content will be a property of the post object in all CMSs. However, we should be cautious on making this assumption, and decide on a property by property basis. If we don’t want to make the previous assumption, then it makes more sense for function getContent to receive the post’s ID instead:

interface PostObjectPropertyResolverInterface { public function getContent($post_id);

Being more conservative, the latter function signature makes the code potentially more reusable, however it is also less efficient, because the implementation will still need to retrieve the post object:

class WPPostObjectPropertyResolver implements PostObjectPropertyResolverInterface { public function getContent($post_id) { $post = get_post($post_id); return $post->post_content; }

In addition, some properties may be needed in their original value and also after applying some processing; for these cases, we will need to implement a corresponding extra function in our contract. For instance, the post content needs be accessed also as HTML, which is done through executing apply_filters('the_content', $post->post_content) in WordPress, or directly through property $post->content_html in October CMS. Hence, our contract may have 2 functions to resolve the content property:

interface PostObjectPropertyResolverInterface { public function getContent($post_id); // = raw content public function getHTMLContent($post_id);

We must also be concerned with abstracting the value that the property can have. For instance, a comment is approved in WordPress if its property comment_approved has the value "1". However, other CMSs may have a similar property with value true. Hence, the contract should remove any potential inconsistency or ambiguity:

interface CommentObjectPropertyResolverInterface { public function isApproved($comment);

Which is implemented for WordPress like this:

class WPCommentObjectPropertyResolver implements CommentObjectPropertyResolverInterface { public function isApproved($comment) { return $comment->comment_approved == "1"; }

Global state

WordPress sets several variables in the global context, such as global $post when querying a single post. Keeping variables in the global context is considered an anti-pattern, since the developer could unintentionally override their values, producing bugs that are difficult to track down. Hence, abstracting our code gives us the chance to implement a better solution.

An approach we can take is to create a corresponding class AppState which simply contains a property to store all variables that our application will need. In addition to initializing all core variables, we enable components to initialize their own ones through hooks:

class AppState
{ public static $vars = []; public static function getVars() { return self::$vars; } public static function initialize() { // Initialize core variables self::$vars['nature'] = $cmsRoutingService->getNature(); self::$vars['route'] = $cmsRoutingService->getRoute(); // Initialize $vars through hooks self::$vars = $hooksAPIService->applyFilters("AppState:init", self::$vars); return self::$vars; }

To replace global $post, a hook from WordPress can then set this data through a hook. A first step would be to set the data under "post-id":

$hooksAPIService->addFilter( "AppState:init", function($vars) { if (is_single()) { global $post; $vars['post-id'] => $post->ID; } return $vars; }

However, we can also abstract the global variables: instead of dealing with fixed entities (such as posts, users, comments, etc), we can deal with the entity in a generic way through "object-id", and we obtain its properties by inquiring the nature of the requested route:

$hooksAPIService->addFilter( "AppState:init", function($vars) { if ($vars['nature'] == 'post') { global $post; $vars['object-id'] => $post->ID; } return $vars; }

From now own, if we need to display a property of the current post, we access it from the newly defined class instead of the global context:

$vars = AppState::getVars();
$object_id = $vars['object-id'];
// Do something with it
// ...

Entity models (meta, post types, pages being posts, and taxonomies —tags and categories—)

We must abstract those decisions made for WordPress concerning how its entities are modeled. Whenever we consider that WordPress’s opinionatedness makes sense in a generic context too, we can then replicate such a decision for our CMS-agnostic code.


As mentioned earlier, the concept of “meta” must be decoupled from the model entity (such as “post meta” from “posts”), so if a CMS doesn’t provide support for meta, it can then discard only this functionality.

Then, package “Post Meta” (decoupled from, but dependent on, package “Posts”) defines the following contract:

interface PostMetaAPIInterface
{ public function getMetaKey($meta_key); public function getPostMeta($post_id, $key, $single = false); public function deletePostMeta($post_id, $meta_key, $meta_value = ''); public function addPostMeta($post_id, $meta_key, $meta_value, $unique = false); public function updatePostMeta($post_id, $meta_key, $meta_value);

Which is resolved for WordPress like this:

class WPPostMetaAPI implements PostMetaAPIInterface
{ public function getMetaKey($meta_key) { return '_'.$meta_key; } public function getPostMeta($post_id, $key, $single = false) { return get_post_meta($post_id, $key, $single); } public function deletePostMeta($post_id, $meta_key, $meta_value = '') { return delete_post_meta($post_id, $meta_key, $meta_value); } public function addPostMeta($post_id, $meta_key, $meta_value, $unique = false) { return add_post_meta($post_id, $meta_key, $meta_value, $unique); } public function updatePostMeta($post_id, $meta_key, $meta_value) { return update_post_meta($post_id, $meta_key, $meta_value); }
Post types:

I have decided that WordPress’s concept of a custom post type, which allows to model entities (such as an event or a portfolio) as extensions of posts, can apply in a generic context, and as such, I have replicated this functionality in the CMS-agnostic code. This decision is controversial, however, I justify it because the application may need to display a feed of entries of different types (such as posts, events, etc) and custom post types make such implementation feasible. Without custom post types, I would expect the application to need to execute several queries to bring the data for every entity type, and the logic would get all muddled up (for instance, if fetching 12 entries, should we fetch 6 posts and 6 events? but what if the events were posted much earlier than the last 12 posts? and so on).

What happens when the CMS doesn’t support this concept? Well, nothing serious happens: a post will still indicate its custom post type to be a “post”, and no other entities will inherit from the post. The application will still work properly, just with some slight overhead from the unneeded code. This is a trade-off that, I believe, is more than worth it.

To support custom post types, we simply add a function getPostType in our contract:

interface PostAPIInterface
{ public function getPostType($post_id);

Which is resolved for WordPress like this:

class WPPostAPI implements PostAPIInterface
{ public function getPostType($post_id) { return get_post_type($post_id); }
Pages being posts:

While I justify keeping custom post types in order to extend posts, I don’t justify a page being a post, as it happens in WordPress, because in other CMSs these entities are completely decoupled and, more importantly, a page may have higher rank than a post, so making a page extend from a post would make no sense. For instance, October CMS ships pages in its core functionality, but posts must be installed through plugins.

Hence we must create separate contracts for posts and pages, even though they may contain the same functions:

interface PostAPIInterface
{ public function getTitle($post_id);
} interface PageAPIInterface
{ public function getTitle($page_id);

To resolve these contracts for WordPress and avoid duplicating code, we can implement the common functionality through a trait:

trait WPCommonPostFunctions
{ public function getTitle($post_id) { return get_the_title($post_id); }
} class WPPostAPI implements PostAPIInterface
{ use WPCommonPostFunctions;
} class WPPageAPI implements PageAPIInterface
{ use WPCommonPostFunctions;
Taxonomies (tags and categories):

Once again, we can’t expect all CMSs to support what is called taxonomies in WordPress: tags and categories. Hence, we must implement this functionality through a package “Taxonomies”, and, assuming that tags and categories are added to posts, make this package dependent on package “Posts”.

interface TaxonomyAPIInterface
{ public function getPostCategories($post_id, $options = []); public function getPostTags($post_id, $options = []); public function getCategories($query, $options = []); public function getTags($query, $options = []); public function getCategory($cat_id); public function getTag($tag_id); ...

We could have decided to create two separate packages “Categories” and “Tags” instead of “Taxonomies”, however, as the implementation in WordPress makes evident, a tag and a category are basically the same concept of entity with only a tiny difference: categories are hierarchical (i.e. a category can have a parent category), but tags are not. Then, I consider that it makes sense to keep this concept for a generic context, and shipped under a single package “Taxonomies”.

We must pay attention that certain functionalities involve both posts and taxonomies, and these must be appropriately decoupled. For instance, in WordPress we can retrieve posts that were tagged "politics" by executing get_posts(['tag' => "politics"]). In this case, while function getPosts must be implemented in package “Posts”, filtering by tags must be implemented in package “Taxonomies”. To accomplish this separation, we can simply execute a hook in the implementation of function getPosts for WordPress, allowing any component to modify the arguments before executing get_posts:

class WPPostAPI implements PostAPIInterface
{ public function getPosts($args) { $args = $hooksAPIService->applyFilters("modifyArgs", $args); return get_posts($args); }

And finally we implement the hook in package “Taxonomies for WordPress”:

$hooksAPIService->addFilter( 'modifyArgs', function($args) { if (isset($args['tags'])) { $args['tag'] = implode(',', $args['tags']); unset($args['tags']); } if (isset($args['categories'])) { $args['cat'] = implode(',', $args['categories']); unset($args['categories']); } return $args; }

Please notice that in the abstracted code the attributes were re-defined (following the recommendations for abstracting function parameters, explained earlier on): "tag" must be provided as "tags" and "cat" must be provided as "categories" (shifting the connotation from singular to plural), and these values must be passed as arrays (i.e. removed accepting comma-separated strings as in WordPress, to add consistency).


Because calls to translate strings are spread all over the application code, translation is not a functionality that we can opt out from, and we should make sure that the other frameworks are compatible with our chosen translation mechanism.

In WordPress, which implements internationalization through gettext, we are required to set-up translation files for each locale code (such as ‘fr_FR’, which is the code for french language from FRance), and these can be set under a text domain (which allows themes or plugins to define their own translations without fear of collision with the translations from other pieces of code). We don’t need to check for support for placeholders in the string to translate (such as when doing sprintf(__("Welcome %s"), $user_name)), because function sprintf belongs to PHP and not to the CMS, so it will always work.

Let’s check if the other frameworks support the required two properties, i.e. getting the translation data for a specific locale composed of language and country, and under a specific text domain:

  • Symfony’s translation component supports these two properties.
  • The locale used in Laravel’s localization involves the language but not the country, and text domains are not supported (they could be replicated through overriding package language files, but the domain is not explicitly set, so the contract and the implementation would be inconsistent with each other).

However, luckily there is library Laravel Gettext which can replace Laravel’s native implementation with Symfony’s translation component. Hence, we got support for all frameworks, and we can rely on a WordPress-like solution.

We can then define our contract mirroring the WordPress function signatures:

interface TranslationAPIInterface
{ public function __($text, $domain = 'default'); public function _e($text, $domain = 'default');

The implementation of the contract for WordPress is like this:

class WPTranslationAPI implements TranslationAPIInterface
{ public function __($text, $domain = 'default') { return __($text, $domain); } public function _e($text, $domain = 'default') { _e($text, $domain); }

And to use it in our application, we do:

$translationAPI = ContainerBuilderFactory::getInstance()->get('translation_api');
$text = $translationAPI->__("translate this", "my-domain");


WordPress has media management as part of its core functionality, which represents a media element as an entity all by itself, and allows to manipulate the media element (such as cropping or resizing images), but we can’t expect all CMSs to have similar functionality. Hence, media management must be decoupled from the CMS core functionality.

For the corresponding contract, we can mirror the WordPress media functions, but removing WordPress’s opinionatedness. For instance, in WordPress, a media element is a post (with post type "attachment"), but for the CMS-agnostic code it is not, hence the parameter must be $media_id (or $image_id) instead of $post_id. Similarly, WordPress treats media as attachments to posts, but this doesn’t need to be the case everywhere, hence we can remove the word “attachment” from the function signatures. Finally, we can decide to keep the $size of the image in the contract; if the CMS doesn’t support creating multiple image sizes for an image, then it can just fall back on its default value NULL and nothing grave happens:

interface MediaAPIInterface
{ public function getImageSrcAndDimensions($image_id, $size = null): array; public function getImageURL($image_id, $size = null): string;

The response by function getImageSrcAndDimensions can be asbtracted too, returning an array of our own design instead of simply re-using the one from the WordPress function wp_get_attachment_image_src:

class WPMediaAPI implements MediaAPIInterface
{ public function getImageSrcAndDimensions($image_id, $size = null): array { $img_data = wp_get_attachment_image_src($image_id, $size); return [ 'src' => $img_data[0], 'width' => $img_data[1], 'height' => $img_data[2], ]; } public function getImageURL($image_id, $size = null): string { return wp_get_attachment_image_url($image_id, $size); }


Setting-up a CMS-agnostic architecture for our application can be a painful endeavor. As it was demonstrated in this article, abstracting all the code was a lengthy process, taking plenty of time and energy to achieve, and it is not even finished yet. I wouldn’t be surprised if the reader is intimidated by the idea of going through this process in order to convert a WordPress application into a CMS-agnostic one. If I hadn’t done the abstraction myself, I would certainly be intimidated too.

My suggestion is for the reader is to analyze if going through this process makes sense based on a project-by-project basis. If there is no need whatsoever to port an application to a different CMS, then you will be right to stay away from this process and stick to the WordPress way. However, if you do need to migrate an application away from WordPress and want to reduce the effort required, or if you already need to maintain several codebases which would benefit from code reusability, or even if you may migrate the application sometime in the future and you have just started a new project, then this process is for you. It may be painful to implement, but well worth it. I know because I’ve been there. But I’ve survived, and I’d certainly do it again. Thanks for reading.

Smashing Editorial (dm, yk, il)
Privacy Settings
We use cookies to enhance your experience while using our website. If you are using our Services via a browser you can restrict, block or remove cookies through your web browser settings. We also use content and scripts from third parties that may use tracking technologies. You can selectively provide your consent below to allow such third party embeds. For complete information about the cookies we use, data we collect and how we process them, please check our Privacy Policy
Consent to display content from Youtube
Consent to display content from Vimeo
Google Maps
Consent to display content from Google