Pixel Pioneers Bristol 2023 Speaker Spotlight: Jeremy Keith
Oliver asked me some questions about my upcoming talk at Pixel Pioneers in Bristol in June. Here are my answers.
Oliver asked me some questions about my upcoming talk at Pixel Pioneers in Bristol in June. Here are my answers.
Here’s the video of the talk I gave at Monday’s meet-up here in Brighton—it’s a very condensed version of my longer conference talk on declarative design.
I feel like we need a name for this era, when CSS started getting real good.
I think this is what I’ve been calling declarative design.
There’s a broader point here about declarative design:
Setting very specific values may feel like you’re in more control, but you’re actually rescinding control by introducing fragility in the form of overly-specific CSS.
I like to think of CSS as a conditional design language.
Yes! This is exactly what I’m talking about with declarative design!
Read on for some fantastic examples. And also, Ahmad makes a comparison between CSS and Figma, pointing out that the conditional, declarative possibilities currently aren’t available in graphic design tools.
At the end of next week, I will sally forth to California. I’m going to wend my way to San Francisco where I will be speaking at An Event Apart.
I am very much looking forward to speaking at my first in-person AEAs in exactly three years. That was also in San Francisco, right before The Situation.
I hope to see you there. There are still tickets available.
I’ve put together a brand new talk that I’m very excited about. I’ve already written about the prep for this talk:
So while I’ve been feeling somewhat under the gun as I’ve been preparing this new talk for An Event Apart, I’ve also been feeling that the talk is just the culmination; a way of tying together some stuff I’ve been writing about it here for the past year or two.
The talk is called Declarative Design. Here’s the blurb:
Different browsers, different devices, different network speeds…designing for the web can feel like a never-ending battle for control. But what if the solution is to relinquish control? Instead of battling the unknowns, we can lean into them. In the world of programming, there’s the idea of declarative languages: describing what you want to achieve without specifying the exact steps to get there. In this talk, we’ll take this concept of declarative programming and apply it to designing for the web. Instead of focusing on controlling the outputs of the design process, we’ll look at creating the right inputs instead. Leave the final calculations for the outputs to the browser—that’s what computers are good at. We’ll look at CSS features, design systems, design principles, and more. Then you’ll be ready to embrace the fluid, ever-changing, glorious messiness of the World Wide Web!
If you’d a glimpse into the inside of my head while I’ve been preparing this talk, here’s a linkdump of various resources that are either mentioned in the talk or influenced it…
James describes his process for designing fluid grid layouts, which very much involves working with the grain of the web but against the grain of our design tools:
In 2022 our design tools are still based around fixed-size artboards, while we’re trying to design products which scale gracefully to suit any screen.
If, like me, you despair at the tech stacking and JavaScriptification of everything, shut that out and pay attention to those who understand the material of the web, its inherent resilience and efficiency. We’re lucky that principled voices still advocate for simple and inclusive methods because building with efficiency and a lightness of touch makes the work feel meaningful and, sometimes, fun.
I wrote a while back about descriptive and prescriptive design systems—and a follow-up post—but I didn’t realise there was such a thing as descriptive and prescriptive engineering.
As you can probably tell from the stuff I’ve been linking to today and today’s Clearleft newsletter, I’ve got design systems on my mind.
What I like about design systems is they encourage systems thinking …in theory. I mean, it’s right there in the name, right? But in practice I see design sytems focusing on the opposite of systems thinking: analytical thinking.
Okay, I realise that’s a gross oversimplification of both systems and thinking and analytical thinking, but why stop now?
Analytical thinking is all about breaking a big thing down into its constituent parts. By examining the individual parts you gain an understanding of the whole.
This is a great approach to understanding the world, particularly when it comes to phenonema that work the same everywhere in the universe. But it doesn’t work so well with messy phenonema like, say, people doing things together.
Systems thinking takes the opposite approach. You look at the bigger picture with the understanding that the individual parts are all interconnected somehow and can’t really be viewed in isolation.
To put it very bluntly, analytical thinking is about zooming in whereas systems thinking is about zooming out.
When it comes to design systems—or design in general—you need to have a mix of both.
If you neglect the analytical thinking, you may end up with a design system that has well-documented processes for how it operates, but is lacking the individual components.
If you neglect the systems thinking, you may end up with a design system that’s a collection of components, but with no understanding of how they’re supposed to work together.
Ideally, you want a good mix of both.
But I’ve got to be honest: if I had to err on one side more than the other, I think I’d rather have less analytical thinking and more systems thinking.
Jim shares his thoughts on my recent post about declarative design systems. He picks up on the way I described a declarative design systems as “a predefined set of boundary conditions that can be used to generate components”:
I like this definition of a design system: a set of boundaries. It’s about saying “don’t go there” rather than “you can only go here”. This embraces the idea of constraints as the mother of invention: it opens the door to creativity while keeping things bounded.
When I wrote about the idea of declarative design it really resonated with a lot of people.
I think that there’s a general feeling of frustration with the imperative approach to designing and developing—attempting to specify everything exactly up front. It just doesn’t scale. As Jason put it, the traditional web design process is fundamentally broken:
This is the worst of all worlds—a waterfall process creating dozens of artifacts, none of which accurately capture how the design will look and behave in the browser.
In theory, design systems could help overcome this problem; spend a lot of time up front getting a component to be correct and then it can be deployed quickly in all sorts of situations. But the word “correct” is doing a lot of work there.
If you’re approaching a design system with an imperative mindset then “correct” means “exact.” With this approach, precision is seen as valuable: precise spacing, precise numbers, precise pixels.
But if you’re approaching a design system with a declarative mindset, then “correct” means “resilient.” With this approach, flexibility is seen as valuable: flexible spacing, flexible ranges, flexible outputs.
These are two fundamentally different design approaches and yet the results of both would be described as a design system. The term “design system” is tricky enough to define as it is. This is one more layer of potential misunderstanding: one person says “design system” and means a collection of very precise, controlled, and exact components; another person says “design system” and means a predefined set of boundary conditions that can be used to generate components.
Personally, I think the word “system” is the important part of a design system. But all too often design systems are really collections rather than systems: a collection of pre-generated components rather than a system for generating components.
The systematic approach is at the heart of declarative design; setting up the rules and ratios in advance but leaving the detail of the final implementation to the browser at runtime.
Let me give an example of what I think is a declarative approach to a component. I’ll use the “hello world” of design system components—the humble button.
Two years ago I wrote about programming CSS to perform Sass colour functions. I described how CSS features like custom properties and calc()
can be used to recreate mixins like darken()
and lighten()
.
I showed some CSS for declaring the different colour elements of a button using hue, saturation and lightness encoded as custom properties. Here’s a CodePen with some examples of different buttons.
See the Pen Button colours by Jeremy Keith (@adactio) on CodePen.
If these buttons were in an imperative design system, then the output would be the important part. The design system would supply the code needed to make those buttons exactly. If you need a different button, it would have to be added to the design system as a variation.
But in a declarative design system, the output isn’t as important as the underlying ruleset. In this case, there are rules like:
For the hover state of a button, the lightness of its background colour should dip by 5%.
That ends up encoded in CSS like this:
button:hover {
background-color: hsl(
var(--button-colour-hue),
var(--button-colour-saturation),
calc(var(--button-colour-lightness) - 5%)
);
}
In this kind of design system you can look at some examples to see the results of this rule in action. But those outputs are illustrative. They’re not the final word. If you don’t see the exact button you want, that’s okay; you’ve got the information you need to generate what you need and still stay within the pre-defined rules about, say, the hover state of buttons.
This seems like a more scalable approach to me. It also seems more empowering.
One of the hardest parts of embedding a design system within an organisation is getting people to adopt it. In my experience, nobody likes adopting something that’s being delivered from on-high as a pre-made sets of components. It’s meant to be helpful: “here, use this pre-made components to save time not reinventing the wheel”, but it can come across as overly controlling: “we don’t trust you to exercise good judgement so stick to these pre-made components.”
The declarative approach is less controlling: “here are pre-defined rules and guidelines to help you make components.” But this lack of precision comes at a cost. The people using the design system need to have the mindset—and the ability—to create the components they need from the systematic rules they’ve been provided.
My gut feeling is that the imperative mindset is a good match for most of today’s graphic design tools like Figma or Sketch. Those tools deal with precise numbers rather than ranges and rules.
The declarative mindset, on the other hand, increasingly feels like a good match for CSS. The language has evolved to allow rules to be set up through custom properties, calc()
, clamp()
, minmax()
, and so on.
So, as always, there isn’t a right or wrong approach here. It all comes down to what’s most suitable for your organisation.
If your designers and developers have an imperative mindset and Figma files are considered the source of truth, than they would be better served by an imperative design system.
But if you’re lucky enough to have a team of design engineers that think in terms of HTML and CSS, then a declarative design system will be a force multiplier. A bicycle for the design engineering mind.
This is really interesting. I hadn’t thought too much about the connection between design engineering and declarative design before now, but Jim’s post makes the overlap very clear indeed.
Some interesting thoughts from Tim here. What if CSS could “displace” design decisions from one area to another?
For example, a flexible line spacing value in one container could influence margins that surround the text block. That change in spaciousness may mean that nearby headings need size or spacing adjustments to stay feeling connected.
This feels like the complete opposite way that most people approach design systems—modular, componentised, and discrete—but very in-line with the way that CSS has been designed—interconnected, relational and cascading.
To complement her talk at Beyond Tellerrand, Stephanie goes through some of the powerful CSS features that enable intrinsic web design. These are all great tools for the declarative design approach I was talking about:
This one-page site that Andy has made to illustrate his talk at All Day Hey is exactly what I was talking about with declarative design.
Give the browser some solid rules and hints, then let it make the right decisions for the people that visit it, based on their device, connection quality and capabilities. This is how they will get a genuinely great user experience, rather than a fragmented, broken one.
I feel like in the past few years there’s been a number of web design approaches that share a similar mindset. Intrinsic web design by Jen; Every Layout by Andy and Heydon; Utopia by Trys and James.
To some extent, their strengths lie in technological advances in CSS: flexbox, grid, calc, and so on. But more importantly, they share an approach. They all focus on creating the right inputs rather than trying to control every possible output. Leave the final calculations for those outputs to the browser—that’s what computers are good at.
As Andy puts it:
Be the browser’s mentor, not its micromanager.
Reflecting on Utopia’s approach, Jim Nielsen wrote:
We say CSS is “declarative”, but the more and more I write breakpoints to accommodate all the different ways a design can change across the viewport spectrum, the more I feel like I’m writing imperative code. At what quantity does a set of declarative rules begin to look like imperative instructions?
In contrast, one of the principles of Utopia is to be declarative and “describe what is to be done rather than command how to do it”. This approach declares a set of rules such that you could pick any viewport width and, using a formula, derive what the type size and spacing would be at that size.
Declarative! Maybe that’s the word I’ve been looking for to describe the commonalities between Utopia, Every Layout, and intrinsic web design.
So if declarative design is a thing, does that also mean imperative design is also a thing? And what might the tools and technologies for imperative design look like?
I think that Tailwind might be a good example of an imperative design tool. It’s only about the specific outputs. Systematic thinking is actively discouraged; instead you say exactly what you want the final pixels on the screen to be.
I’m not saying that declarative tools—like Utopia—are right and that imperative tools—like Tailwind—are wrong. As always, it depends. In this case, it depends on the mindset you have.
If you agree with this statement, you should probably use an imperative design tool:
CSS is broken and I want my tools to work around the way CSS has been designed.
But if you agree with this statement, you should probably use a declarative design tool:
CSS is awesome and I want my tools to amplify the way that CSS had been designed.
If you agree with the first statement but you then try using a declarative tool like Utopia or Every Layout, you will probably have a bad time. You’ll probably hate it. You may declare the tool to be “bad”.
Likewise if you agree with the second statement but you then try using an imperative tool like Tailwind, you will probably have a bad time. You’ll probably hate it. You may declare the tool to be “bad”.
It all depends on whether the philosophy behind the tool matches your own philosophy. If those philosophies match up, then using the tool will be productive and that tool will act as an amplifier—a bicycle for the mind. But if the philosophy of the tool doesn’t match your own philosophy, then you will be fighting the tool at every step—it will slow you down.
Knowing that this spectrum exists between declarative tools and imperative tools can help you when you’re evaluating technology. You can assess whether a web design tool is being marketed on the premise that CSS is broken or on the premise that CSS is awesome.
I wonder whether your path into web design and development might also factor into which end of the spectrum you’d identify with. Like, if your background is in declarative languages like HTML and CSS, maybe intrisic web design really resonates. But if your background is in imperative languages like JavaScript, perhaps Tailwind makes more sense to you.
Again, there’s no right or wrong here. This is about matching the right tool to the right mindset.
Personally, the declarative design approach fits me like a glove. It feels like it’s in the tradition of John’s A Dao Of Web Design or Ethan’s Responsive Web Design—ways of working with the grain of the web.
Some thoughts on CSS, media queries, and fluid type prompted by Utopia:
We say CSS is “declarative”, but the more and more I write breakpoints to accommodate all the different ways a design can change across the viewport spectrum, the more I feel like I’m writing imperative code. At what quantity does a set of declarative rules begin to look like imperative instructions?
In contrast, one of the principles of Utopia is to be declarative and “describe what is to be done rather than command how to do it”. This approach declares a set of rules such that you could pick any viewport width and, using a formula, derive what the type size and spacing would be at that size.
I feel like it’s high time I revived some interest in my proposal for button type="share"
. Last I left it, I was gathering use cases and they seem to suggest that the most common use case for the Web Share API is sharing the URL of the current page.
If you want to catch up on the history of this proposal, here’s what I’ve previously written:
Remember, my proposal isn’t to replace the JavaScript API, it’s to complement it with a declarative option. The declarative option doesn’t need to be as fully featured as the JavaScript API, but it should be able to cover the majority use case. I think this should hold true of most APIs.
A good example is the Constraint Validation API. For the most common use cases, the required
attribute and input type
s like “email”, “url”, and “number” have you covered. If you need more power, reach for the JavaScript API.
A bad example is the Geolocation API. The most common use case is getting the user’s current location. But there’s no input type="geolocation"
(or button type="geolocation"
). Your only choice is to use JavaScript. It feels heavy-handed.
I recently got an email from Taylor Hunt who has come up with a good litmus test for JavaScript APIs that should have a complementary declarative option:
I’ve been thinking about how a lot of recently-proposed APIs end up having to deal with what Chrome devrel’s been calling the “user gesture/activation budget”, and wondering if that’s a good indicator of when something should have been HTML in the first place.
I think he’s onto something here!
Think about any API that requires a user gesture. Often the documentation or demo literally shows you how to generate a button in JavaScript in order to add an event handler to it in order to use the API. Surely that’s an indication that a new button type
could be minted?
The Web Share API is a classic example. You can’t invoke the API after an event like the page loading. You have to invoke the API after a user-initiated event like, oh, I don’t know …clicking on a button!
The Fullscreen API has the same restriction. You can’t make the browser go fullscreen unless you’re responding to user gesture, like a click. So why not have button type="fullscreen"
in HTML to encapsulate that? And again, the fallback in non-supporting browsers is predictable—it behaves like a regular button—so this is trivial to polyfill. I should probably whip up a polyfill to demonstrate this.
I can’t find a list of all the JavaScript APIs that require a user gesture, but I know there’s more that I’m just not thinking of. I’d love to see if they’d all fit this pattern of being candidates for a new button type
value.
The only potential flaw in this thinking is that some APIs that require a user gesture might also require a secure context (either being served over HTTPS or localhost
). But as far as I know, HTML has never had the concept of features being restricted by context. An element is either supported or it isn’t.
That said, there is some prior art here. If you use input type="password"
in a non-secure context—like a page being served over HTTP—the browser updates the interface to provide scary warnings. Perhaps browsers could do something similar for any new button type
s that complement secure-context JavaScript APIs.
I like this proposal for a declarative Ajax pattern. It’s relatively straightforward to polyfill, although backward-compatibility is an issue because of existing browser behaviour with the target
attribute.