Building a Ubiquitous Design Language with Components
(upbeat computer music) - Right thanks John, as you've heard my name's Michael Taranto, I'm one of the UI leads at SEEK here in Melbourne and today I'll be talking to you about our journey towards building a ubiquitous design language, and leveraging components to do so.
Conversations that take place between designers and developers are more often than not focused on the pixels that are on the screen rather than the quite structured principles that go into the design process underpinning our wireframes, and unfortunately wireframes are still the most common way that we're communicating between the design and development teams.
I say unfortunately because they're only telling part of the story, sharing only the designer's computed output, abstracting away all of the detailed design process that takes place behind closed doors.
This model, in my opinion, is fundamentally broken. It relies too heavily on the developers to reverse engineer these very heavily marked up wireframes, leaving too many responsibilities to the build stage of our products.
Having to remember things like, using semantic mark up, is my solution accessible, and filling in any blanks between the responsive wireframes that they get handed to start with.
And unfortunately this is landing in front of our users in the form of a very inconsistent user experience as it's left to be a more volatile build time concern where every developer is going to infer requirements from those wireframes differently.
So instead, if we stop focusing on the wireframes as the contract between the teams.
If we focused on the language that we were using, and strive to simplify this, what would that language look like? Would it look something like this where we've got HTML requiring the developer to go apply very specific classes and remember to do good practise things like have the correct ID on a label, on an input field, so that they connect together.
Or do we strive for a more declarative and compositional approach? Instead writing that same code a little bit more semantically like this where we can have a heading and a subheading, but then rolling together something like an email field into a single concern rather than leaving it to the HTML, in order to be more accessible.
And the beauty of this style of language is that we're encapsulating the principles into a singular unit, both from a design perspective but then also the implementation.
We're not leaving it open to that ambiguity that arises at build time.
And we can do this today using components, which I'm sure is not a new idea to many people but we need to start seeing components as being more than just buttons and form fields on a page.
We can use them for things like white space for our text, and encapsulate all of our design principles inside those. So at SEEK we've been on this journey for a little while now where we've been shifting our discussion to be more about the principles underpinning that design rather than the pixels, with the end goal being that if we can capture the designer's intent and codify it rather than just taking the output via the wireframe, we'll be able to get all that good design knowledge and ideas that's taken place into the code and then drive that out through multiple teams, rather than relying on the implementation in every individual project.
But to do this we've had to unravel this pixel translation layer that sits on top of all of this solid design process and so once we remove the wireframes it allows us to start getting to the bottom of why we're actually pixel nudging at build time. So we had a listen to some of the discussion that the teams were having any everything was about the white space, in and around text and around elements and how much additional spacing I needed above or below something.
And so it seemed like the most logical place to start was with our typography.
Given it's so core to design, if we got this right, surely it would be, the composing of our documents would flow a lot easier.
So I'll get you to consider this text component that I've got up on screen and if we introduce something like a heading modifier there.
What does a heading look like in our new language, in our system? So in our world we can define this as a 21 pixel piece of text, running over 27 pixels of line height. But that's just the output, and the only reason we know that, we know those values in pixel is because we've trained the designers to translate all of their work into pixels to form the contract at build time. So if we wind that back.
How can we understand what the designer wants when they're asking for a 21 pixel font size? And the best way to do that is to look at what it's representing and a heading is really just one element within a typographic hierarchy and what's important is the scales between the elements rather than the pixel values themselves.
So it would make sense that we express that accordingly. So in our system a base font size is 10 pixels so the scale that we're really looking to capture for a heading is 2.1 and this'll become important as I'll show you in the coming slides.
So the other facet of our heading definition was its line height being 27 pixels.
And we're used to using things like grids to govern our white space with paddings and margins but we don't often roll it through our line height as well so everything that effects the vertical spacing in a grid based system should ideally be expressed as a factor of grid rows.
In our system a row height is set to nine pixels, so that 27 pixels that we're asking for was really just the designer saying, I want a heading in this system to run over three grid rows.
So let's now instead capture that in our CSS and we can now extract this out to basically a table of principals where we've got our low level base font size and row height being captured in pixels, but from then the discussion should flow quite smoothly referring to a heading as a 2.1 scale running over three grid rows. So we can start to see the language taking shape here, that we're no longer referring to pixels, rather the principles the designer original intended. So let's run this exercise one more time and now looking at the subheading within our system. We'll introduce a new modifier to our component that will conditionally apply the subheading classes. But this time we'll describe it as being a 1.8 scale over three grid rows.
We can introduce the variables to capture that representation, apply that to the subheading class, and we're done.
So we've just extended our language without any mention of pixels, by introducing a subheading using solely the principles that it's implemented in.
So let's have a look at what that looks like on screen. So over on the left we've got CSS's interpretation of that 21 over 27 that we just mentioned, and on the right is what the design team was expecting to see. And even though we've used the same values, the results are yielding vastly different and that's because CSS doesn't speak design. It might allude to the fact that it thinks it knows what design is, with terms like line height and vertical line baseline, but fact of the matter is CSS will vertically align its font size within the specified line height, whereas what the design team's actually expecting to see is that the line height be measured from the baseline of the text all the way up. Now it might seem minor but that means every piece of text on your page is going to be off, and that's the root cause of all the pixel nudging that we're seeing within our UI's.
Every single card, every single heading was having to be nudged down the page, and every team is leaving this until the very last minute to correct.
So by aligning our output we can start to improve the quality of the conversation that's happening between design and development.
But to do that we need to shift the text down on the web. Unfortunately you can't see that there is a grid running over the heading up the top there but what's highlighted in blue is the descender height. Now we need to know how tall this is so that we can shift the text down for every element within our hierarchy but unfortunately that's not available in CSS. So what I do have here is a little calculator but you could use developer tools or anything else similar but I'm moving that line closer to the baseline of the text and you can see that comes to the magic number of three pixels, telling me that the header and the descender height, in this case, is three pixels. So I'll capture that accordingly and the second part of that distance is made up of the difference between the line height and the font size, so you see there the additional line height space being spread evenly above and below the text. To calculate this is a little bit more tricky but we only care about the bottom portion so if we take the 27 pixel line height that we specified, minus the font size, and then divide it by two 'cause we only care about that bottom portion, we happen to also end up with three pixels. So we'll capture that as the line height offset, add the two values together, and we'll now be able to transform our heading down onto the baseline, exactly as the designer intended it to be.
Now the reason for using a transform is that we don't want to push our neighbouring elements down 'cause that would just kick off the whole process all over again, and we also wanna preserve the other properties for semantic use with paddings and margins et cetera, we don't wanna waste them as they're valuable for laying out our UI's. So what happens now when we need to offset our subheadings? We need to go through the whole process again and recalculate all these values, that sees horribly inefficient.
And in our case the font that we're using for subheadings is the same font family as our main headings so if we could calculate our descender as a scale we'd be able to leverage CSS em units to express that as a scale of the font size. So we'll revisit our demo again but then instead this time we're calculating it as a scale value rather than pixels.
And so as that line gets closer to baseline, all the way up to around .15.
That's telling me that header for the Roboto font, sorry the header, the descender for the Roboto font is actually 15% of the entire height of the font. So that means it doesn't matter what the font size is for a heading, for copy, for your body text, it can all be expressed as 15%.
Where this gets tricky is rolling that into a calculation for every element now 'cause we can no longer transform by pixels like we did previously, we need to do that as a scale as well.
So I won't drag you through that right now but I have open sourced a library called Basekick which does take all of these principles that's we're talking about as inputs, and what it returns you as a CSS rule's required to align the type on the web with what the designer was intending. So here's an example usage.
All the values you'll be familiar with now, having the heading scale and row span, the specific descender, height for Roboto there, and then our low level principles of the row height and base font size.
All of these passed into the mixing, will now return me the font size and line height that we saw at the start of the talk, including a CSS transform specified as a scale value. It might sound like a bit of overkill but given we've arrived at the same values but now this has empowered the team to change any of the elements in the hierarchy specification, without talking about pixels or introducing any new elements into that hierarchy, and the pixels are nothing more than a compile target allowing us to iterate on these principles across multiple products, and very quickly have them all update.
So to see this in action, if we were to talk about introducing a new element, that's very faint but you can. So as I increase the number of rows that that type runs over you can see it allows the additional space. As the scale increases it grows, sitting on baseline. And it doesn't matter how exaggerated the descender is on you font, once you calculate that once for any given font it'll be reflected by the Basekick library as sitting on proper baseline.
So it seems like a lot of work but it's actually ended up being very little code and the benefit of it is we're reducing the time required for designers to spec out our interfaces because they can stay in their world of principles and have that be the contract, rather than a wireframe.
And then we've also saved a heap of time with the developers not needing to nudge elements around the UI at build time either.
So now if we revisit our registration example that we saw earlier we actually can write code as declarative of this and have it yield the correct result. So while it's not the prettiest design in the world, if you could see the grid on that projector you would see that each, the heading and the subheading, are perfectly running over three grid rows exactly as the designer intended it.
So we're starting to now see our language grow, code first, out of our projects.
Now it's important that we recognise this because, over time, the component interface.
So the fact that were were adding these modifiers for heading and subheading, that's becoming the cornerstone of our language, which is a good thing because it means we're not throwing these specced up wireframes back and forth.
It's the language that we can have a very semantic discussion with the design team and know exactly what we need to write as a developer. But that also comes with its own challenges so there's always gonna be someone that comes and asks you can you make that text green, or can you make it blue. And rather than going away and introducing a green modifier to that component, we need to spend time understanding why are they asking for this, what's the reason, because ultimately green is to the colour property what pixels is to font size.
It's simply the desired end state, it doesn't convey any intent about what the designer was hoping to achieve.
So we went away and had a look at all of our existing products and determined that every time that we use green text within our applications it was always to convey a positive tone or sentiment, some kind of affirmation that you've successfully registered so by capturing that as a positive modifier rather than green, we've future-proofed the language allowing us to change the implementation in the future and not need to update every code base.
It's captured as positive text.
If we change what positive text looks like in the future we can update that centrally.
And the really nice side effect of all this is that we've encouraged design discussions to happen on a daily. Every time someone hits up against the edge of this language or this system they're not just pushing pixels on their own machine, and committing their code, they're jumping in the design channel on Slack or having a conversation about the missing edge cases. And ultimately the solution's having a much broader impact than just one particular project.
So is pixel perfect really necessary? In our case it's not the fact that it's pixel perfect that we're focusing on.
We've invested very heavily in a good design team so why have their work go to waste and all that additional time speccing up designs and having the developers get it wrong, when we can build a hell of a lot faster and have higher quality conversations at build time. More seeing this as an improvement or an optimization. And as we've got quite a large site, with multiple teams delivering a single experience, having all of those, having all of that align across the various products is pretty critical so being able to centralise that in code is essential. So where are we now, we've got, that's only a subset but we've got a whole heap of components that we've now got in our library. Hopefully if this works, it's not again the colour contrast is not really gonna achieve the means in this demo but there's a grid overlay on that that shows every piece of text on every application. I won't bother clicking through 'cause you can't see it but trust me, they're all aligned and it's all very deliberate following the designer's intent. So a couple of quick lessons, so what's worked for us, components everywhere, there's so many principles going into this design process that the smaller the component, the more we can recompose it into another idea down the track so we've gone all in on that model and it's paid dividends.
And as Rebecca alluded to the cross-discipline pairing has been amazing. Developers getting more invested in the design that we're building, and designers getting a better appreciation for how to propagate their ideas faster, at scale. What takes a little bit more work is naming. In software we're pretty used to this, that it's the hardest thing you can do.
And having to bike shed every single decision because it will ultimately become part of your language is just part of everyday life at the moment. But more importantly what I think takes work and will continue to take work is justifying the why. There's a reason for everything being the way it is in such a structured design process and often we get caught trying to design with our hearts rather than our minds but there, we're yet to find something that we can't actually unpack the why and capture as code. So if you're interested in our journey we have got a much more immature site than what you saw with the gel but we've come at this code first so we've open sourced it, it's on GitHub but it's really solving a problem for our organisation across our product offering, with the site not being the, in fact you can't even see it in the background. There's a picture of the site in the background. I apologise for the contrast.
The slides will be up later anyway.
But yeah the URL if you're interested in having a look at our journey, please jump on and have a look. So hopefully in giving you a bit of an idea about how we've gone at defining our own design language, you're now inspired to go help your team find your design language and building off the back of Rebecca's talk hopefully you've got a number of tips for how to go about influencing your organisation. But that's it for me, sorry it went a little bit over but a couple of links there if you're interested. Thank you.
(audience applauding) (upbeat computer music)