Thinking in design-code
I've said before that I've built this personal site three times so far, and in all three cases it was without any design file in site. Now, as a designer this might seem like a really weird approach to building out something that requires design, but I designed it in code. Hear me out.
If you've read any of my other Thoughts before then you'll be familiar with my concept of Code as Design but for this piece I want to take it a little bit further.
The premise
So to understand why I took this approach---and for those who haven't read my previous articles---you'll need to know that I am a designer but I've had a varied background that grew from learning the basics of HTML and CSS in the early 2000's. So really, I actually learnt the principles of web design through the lens of code.
So below I'm going to outline some practical steps of how to relate code and design. This list won't be exhaustive or complete necessarily (although I may come back and add to it) but it should match pretty well.
Some caveats:
- All references to design tools will be through the use of Figma's terminology. This is for three reasons:
- It's a pretty ubiquitous design tool now, used by many
- It's what I use day to day for my job, so I know it very well
- Most of Figma's terminology for tools map fairly synonymously with code (particularly the new flex box-inspired auto layout
- This is how I think of code and design being linked, it doesn't mean that it's the only way to think about it
- I'll do my best to explain any concepts in as much detail as I can, but I can't promise it's exact
- This entire piece revolves around the principle of a web project build, you can of course map these same ideas to mobile builds in SwiftUI for example, the exact names will vary slightly (a div for example is more likely to be an Stack or even a Struct depending on how you build your projects, and grids are called LazyVGrid/LazyHGrid respectively)
Projects == Folders
Where better to start than projects. In Figma, a "Project" is a folder that contains a series of files that you work on. You can have one, several or many. In Code, we use folders (just a regular old computer folder) and inside those folders we keep files which contain our code. Like with a Figma project, a folder can contain one, several or many files.
If you work in modern Javascript-based frameworks, you'll likely have a whole bunch of files. If you're just keeping it minimal you may only use a single HTML file.
Files == Files
Well this ones easier right? Each Figma file inside your project is essentially synonymous with the file structure of a development project. You can create multiple files within a project and link them all together with Figma's Shared Libraries feature.
For most development projects, especially those that utilise one of the Javascript frameworks, there will be a bunch of files that all play together nicely (hopefully) to build your site. Figma projects are no different if you take full advantage of the Shared Libraries in Figma (and you should). Decentralising files is a key fundamental in modern development and allows for greater reusability and manageability of your code. Thinking of design files like this too will not only make your files more manageable but will also create a wider synergy to your development project when it progresses.
Frames == Divs
Frames in Figma are your canvases. Frames are where you "paint" the interface of the product or thing you're designing, and in development the notion of "painting" is true too.
Sidenote: You may see the phrase "Time to first paint" if you ever do a Google Lighthouse review of your site, this is Google telling you the time it takes for your site to load the initial interface and allow first interaction. If it's slow, you'll score low for SEO and with Javascript-based Single Page Apps this can be a problem, so keep an eye on it.
Just like in Figma, Divs act as a container for your code and you can stack as many Divs inside each other as you need (just like Frames). By doing this, you build up a structure for your page. This is where it can start to take life. It's important to stay focused on building out the core Div structure before getting into the details. Use quick "styles" on your Divs to create visual separation---even if it looks garish---such as `class="border-2 border-black bg-red-500". I'd use primary colours to separate the independent elements and stop you from nitpicking the details.
This Gist is an example of building up the basic structure of my home page container utilising the TailwindCSS library.
Auto Layout == Flexbox and Grid
In the above code you'll see some examples of 'flex', 'flex-row' and 'grid'. These are all layout mechanisms that help to structure the page content in a more fluid and dynamic way. They're particularly important for when you want to create responsive layouts, such as having a 1 x 4 grid turn from a single row into a single column between desktop and mobile. This is exactly what happens in that first grid line <divclass="grid xs:grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4">
. At the smallest breakpoint (phone) it's a single column, at the next breakpoint (tablet portrait) it's two, and then at the next and up (tablet landscape or desktop) it turns into a four-column row. At each stage I implement a 4-point gap to separate the elements.
If you look at the new Figma Auto Layout and Constraints controls, you'll see a lot of familiarity with these flex principles too.
An image of the Flexbox-like layout
In the above we can see the flex direction (the down and right arrow) the spacing between elements (in grid, this is the grid-space-row or grid-space-col or in TailwindCSS, gap-{n}), the overall padding (here it's 16px on every side) and the alignment (in our example this is align-center, justify-center).
You can also set resizing constraints. This defines how the object behaves when resized. We can choose between "Hug contents"---where the frame itself (i.e. the Div) is perfectly connected to the width of the objects inside and maintains the padding and wrapping---or "Fixed width" where the the frame width never changes regardless of changes to the internal components. The same rules can be applied to the height property.
Below is how this looks in practice. Hug Contents is above and Fixed Width below. Notice that because I have the align-center and justify-center properties in place, the objects stay perfectly in the middle with fixed width.
Image example of the different container options on Figma
Layer naming == ID or Class
Now let's talk about layers and naming. I know, I know, it's totally boring and it's fine to have 'Rectangle 33201' or 'Frame 1000010' on your projects if the actual designs look cool. I've done it many times too. But there is a really valid reason to think more carefully about layer naming, implementation in code.
If you can handover a file that's name logically from a layering perspective (especially if you've taken all the above steps too) then you'll find your Engineer(s) have a much easier time implementing the code as they can use it as their file architecture or map.
Groups should be given a name that characterises the contents. This is what can relate directly to an 'ID' property or a 'Class'. IDs by their very nature must be unique. In real life you can only have a single form of ID from a single source, the same applies to code. You should use an ID once in your code and not again. If you find yourself repeating them then you should swap these to become classes instead.
An ID gives your particular Div or object a unique identifier which determines what makes it different. You could name a Nav Div, for example, NAV
as you should only have one implementation of your core Navigation and reuse that component everywhere (more on this later). If that is true, ID it as NAV
and apply the relevant classes.
Notice the use of uppercase. Everyone has different practices, but one I think is particularly useful is to use UPPERCASE words to represent IDs and kebab-case for classes. But really you can do what you want. The reason for a difference? Scanability.
Styles == Classes
Now, in Figma, if you find yourself reusing a particular style everywhere then you've probably decided (at least I hope so) to create a Figma Style from it. Doing this allows you to create local Text Styles, Color Styles and Effect Styles for a document. That way, repetition is more easy to do and consistency is more likely to be had.
In code we do this too, they're known as classes. Depending on whether you're using a CSS library (like TailwindCSS) or not will likely determine your approach to classes. Tailwind uses what are known as 'Utility Classes', essentially breaking classes up into atomic parts (see: Atomic Design). What this means in practice is, like in my example earlier, there's lots of bitty classes used to build up a base idea.
Just look at this button: <button type="button"class="flex flex-row w-full justify-center text-center font-medium btn btn-primary rounded-xl px-6 py-3 bg-blue-400 text-white hover:bg-blue-500 focus:outline-none focus:shadow-outline transition duration-500 ease">Button 1</button>
That's a big ol' lot of classes to get the button behaving how I want. But equally, it behaves how I want. I've have complete control in the place I'm creating the button, instead of relying on styling in a separate CSS file. And, once you learn the notation style, it actually gets pretty fast to write (see: Brian Lovin on migrating to TailwindCSS).
The next logical step, of course, is to reduce the amount you do this. And you do this, just like in Figma, through making reusable elements called Components.
Components == Components
Now on to the final piece of the puzzle. You've written a bunch of stuff, but you find yourself copying and pasting it all the time. Doesn't it seem silly to do that? Couldn't it be easier?
This is where component based design and development comes in. Breaking things up into reusable chunks makes it easier to manage a big build and increases consistency. The beauty of Components too, is that you can decide how atomic you want to make them. Atomic Design (mentioned earlier) is the extreme end of the spectrum, but if you're already using TailwindCSS then you've already got a bunch of atomic elements to play with.
If I dissect my site, I have:
- Four core unique pages
- One subpage (for blog posts)
- Fourteen components
I could be much more atomic with my components. If it was a commercial product, I would be, but the reality is that I don't need to be that strict about it. I'm building it to my needs, just like I would a personal Figma file. The most important part of creating a component of anything is thinking about who's going to use it. Think of your website the same way you think of a Figma Design System. If you created icons with their labels as components, do the same, they'll be easier to swap out in a conditional statement. If you created different card component types, do the same, again you can use conditionals to control data differences.
Conclusion
If you're working with engineers on a project, talk it through with them. Ask them how they're planning to execute at a code level and build your project to match. If they're going atomic, go atomic. It'll feel tough in the beginning but the 1:1 match will be worth it.
Most importantly though, learn to make these connections in your head. If you can map what you think of in design to what the end engineering build case could be, you'll find that the process of documenting design decisions and handing over projects become far easier.