I’ve wanted to write a follow up article to my BEMU article for a little while now. I’ve been focused on improving my front-end coding skills for around a year or so now and wanted to document and write about my principles and methodologies, along with some wisdom from my coding experience over the years.
Starting with Principles
I set out to overhaul my tech stack with a few main principles. I wanted to create a toolkit that allowed me to maintain one main repo that could power all my projects. This would give me the parameters to work within and was flexible to work in a vanilla build, Next JS application or 11ty build. I wanted something fluid. I also defined a few other principles, too:
A fully native CSS solution, no SASS, LESS or anything that requires compiling.
Having relied on SASS heavily over the years, I really enjoyed creating reusable code with variables, mixins and more. However, I dropped IE support at the start of the year, which was a huge decision in changing from SASS to CSS. CSS variables now have great support, and I relied on SASS for variables 95% of the time. I now prefer working with CSS variables having worked with them for a long period of time.
Create design orientated code with design tokens
I’ve always been a bit of a neat freak when it comes to code. I am consistent about working towards refining my internal starter projects and frameworks and always put design at the front of the priority list. I’ve always had what we now call ‘design tokens,’ just in a different format. Quite often in SASS, I defined things like base font-size, types, colours, heading sizes and other elements. With the beauty of CSS variables and Blyth’s design token system, I can now generate everything that the design uses. This goes beyond anything I’ve done before, from spacing to breakpoints, to backgrounds. It feels so much lighter and more flexible, as it gives me, a developer, free reign on how I want to code. It means if one project was more suited to more of a utility first approach, I code write something like:
<div class="bg-navy pad-400 color-white radius-large"></div>
This is quite similar to the approach of Tailwind CSS, but without the large tech stack and build processes. You’re also in complete control with your code as you can define anything you want within the config file, and make it as small or as large as you wish too. If you’re like me and like a little bit of BEM, then you can write code that’s focused around the design tokens that also suits the BEM methodology. Here’s an example:
With your CSS containing the following:
Now having analysed both approaches you can see how powerful this is. It allows you to mix and match HTML classes and CSS variables to create a completely fluid system.
Create fluid and flexible code
Taking this all a step further, BEM is brilliant. It allows me to write consistent, flexible code that is well documented and easy to read. Quite often I found myself modifying all the time for variants though, and creating variants for even the tiniest of changes. It felt clumsy to have to create a modifier for one small colour change or a padding change. Hear me out, what if we mixed BEM with utilities and design tokens to create something that allowed reusable components and some flexible modifying. Well, let's try it! What if we took our card component and one variant of it was a white background and a dark text color? Well, we could do the following and create a variant of our card. Something like this:
<div class="card card--light"></div>
And the CSS:
This is pretty sweet! We've got a variant and it works well. Now as always, the client wants a different colour and wants to try something different. Let’s say a red background. We'd have to go and create yet another variation of our card and give it properties, and so on. On a larger scale, this can be very cumbersome and time consuming. But if we take a step back, what if we could just use our utilities directly to create variants of our base card, as a one-off, like so:
<div class="[ card ] [ bg-light color-dark ]"></div>
It's perfect. What it allows us to do is define what a base
.card looks like, but also allows us to modify the properties that might change with ease, and flexibility. It also means we haven't got many variants of cards. I do find there's a fine line between variants and modifiers. If I find myself writing the same utilities over and over, I usually then move it to a modifier as it's something that's more than just a one-off variant, and probably warrants it's own definition for consistency and maintainability.
Allow me to reuse good code
Quite often I found myself grabbing snippets of code from other projects, and adapting it to suit. We all do it with things like wrappers, CSS grids, and resets. I wanted the new toolkit to include a way to import these utilities and keep a central up-to-date repo of them. Blyth does this with utilities. By running one command,
blyth utility add reset, I can import the latest and up to date utility. And of course, as it's just copying it in I could always adapt it to suit a particular project. Most of the utilities utilise CSS variables to create a powerful system of overriding and customisation. Let's take our
grid-template-columns: repeat(var(--auto-grid-method, auto-fit), minmax(var(--auto-grid-min, 16rem), 1fr));
grid-gap: var(--auto-grid-gap, 1rem);
Auto grid is exactly what it says on the tin, it's a utility to create a responsive CSS grid with three lines of code. It allows us to stack a grid on mobile whilst setting a minimum size for the grid to be on desktop. So, I use this in a variety of ways:
<ul class="[ card-grid ] [ auto-grid ]"></ul>
This allows us to use our amazing utility to create the responsive grid, but also allows us to customise a few thing too. Let's say we want to create a 25% column grid on desktop, with it stacking on mobile with a
2rem gap for some nice spacing between the grid items.
--auto-grid-min: var(calc(25% - var(--auto-grid-gap));
This would give us a perfectly responsive grid, super neat. Now let's say we have a grid of cards that are much larger, and are 50% width, we could just create a new component:
--auto-grid-min: var(calc(50% - var(--auto-grid-gap));
This means we're not repeating code, and we're letting the utilities do their job, whilst having the flexibility to maintain a single source of truth.
I hope this gives you an insight into how I code, what sort of methodologies I use and how I'm trying to keep these core principles in mind when maintaining and growing Blyth. Please feel free to ask questions too. I'm more than happy to help!