Why Read This? 🤔
This article is focused on developers that spend more than 15-30 minutes on coding layout per one screen. Today I'm going to tell you how to properly interact with Figma and write code for your UI layout to make UI 5-10 times faster.
I've worked on projects, where it was taking 4 hours for developers to create simple mobile responsive page, and it would take additionally 2 hours for testing and fixing stuff.
It took me a month to reduce that time to 15 minutes per screen + 15 minutes for testing + mobile responsivity + logic inside services and component interaction about 30 minutes would sum up to about 1 hour per feature ready to go to production.
Therefore, this article is also useful for developers that want to be able to reduce actual costs for business.
Core concepts
At the beginning we will break down every concept that is used and must be very clearly understood before we proceed to actual coding of the layout. Otherwise we're going to be randomly guessing what is going on which is no good.
Domain modeling
At this stage you don't need to be an expert and be able to define bounded contexts and entities lifecycle, no. However, you should be able to distinct what is entity and what is value object in order to be able to breakdown your layout into reusable components properly.
Understanding domain model helps you to distinct your UI components: what should be generic and what should be domain specific.
System analysis
You should have basic understanding what is user story, use-case, test case and so on. Basically because when developer is given a task to develop some feature for end user, it's important to be able to handle all the possible scenarios where something went unexpected and implement proper layout for it or handle this somehow else. This saves you time later on testing. Actually every early stage analysis mostly saves time later with 10x profit.
Design tools and terms
Design tokens, variant, state, typography, flex layouts, grid layouts, UI kit, design system — these are all important terms you need to learn. Please check this out on internet on your own or send a drop a comment if you want another article with detailed explanation and pictures.
Understanding of very basic linear algebra is also important: there is some coordinate system and some position vectors placed in it.
Simple arithmetics
To make layout you need to be able to divide and multiply values by 16 and 4. Although you will use calculator for these cases, it's important to be able to do it in your head, so that you understand the whole process.
Why 16 and 4: if you want to make layout responsive and scalable, it does make sense to set almost all the sizing's and spacings in rem
units. And then you can set value of rem in px
units depending on screen size. For example, it can be 16px for small mobile screen, 18px for bigger mobile screen, 20px for tablet screen, 22px for desktop and 24px for wider screens.
So the base of the document is rem is usually set to 16px. And if you use TailwindCSS, which is very convenient to implement atomic CSS approach in your project, they use 1 unit for 0.25 rem which is 4px which is 1/4 of rem. That's why when you need to set block width for example 40px you write w-10
because 40px / 4 = 10 tailwind css units, also 40 / 16 = 2.5rem.
Very simple, but very powerful thing, now it's easy to distinguish all the spacings and not to set weird values like padding-right: 39px; padding-top: 27px
Figma and its auto layouts
You must know how to use figma and how to use it fluently: UI, keyboard shortcuts, X and Y coordinates, navigating on left and right sidebars, detecting sizings, typographies, flex layouts, grid layouts.
It is also important to be able to visually comprehend structure of UI mockup in figma and translate it into code.
Remember: web app layout is just a bunch of rows and columns. Rows place content horizontally and columns place content vertically.
If designers in your Figma don't use auto layouts and most of the spacings are not divisible by 4, then mockup is not designed correctly and needs to be fixed. Always make sure both design and frontend development teams respect and follow fundamental concepts.
Getting to practice 💪
First we need to use to set up following things:
1. Get file with proper design mockups for practicing
This particular one seems to be ok to practice basic flex layout:
https://www.figma.com/design/T6yHwOh7iJmmCYqsTK5mDb/Auto-Layout-Projects-(Community)?node-id=4-483&t=2TJLJ1OEpAd8UZqK-4
2. Bootstrap application in your Frontend tech stack
We'll be using Angular for today's article, but you can use any other technologies you want to.
3. Install and set up TailwindCSS
This step differs for different frameworks and is described in the official documentation.
After all of this done, let's proceed to actual making layout.
Break down the design visually 🔍
This is the section we are going to work with. Our goal is to implement this as a layout pixel to pixel.
How to work with it:
First focus out frame you need to work on and then hover it with your cursor. That gives you rough overview of layout structure in this component.
When you look at some design you should visually parse it like shown on the screen:
Now let's define which pieces are reusable components to extract them in our code:
Now before proceeding to making actual layout, let's inspect the typographies used in the design, here is how you can get those:
As you see, there is a list with all typographies. This way you don't need to manually copy and paste over and over.
Actually setting up all the typographies is the first essential step in making every layout: we start from fully atomic entities and then go to complex composed layout components.
Code 👨💻
Finally, we are getting to some coding.
Set up typographies as Tailwind components
The design file I picked has huge collection of typographies included in its design system. To simplify this article I will use only 3 of them that present in our component for practicing layout:
- Heading/Heading 2 — Rubik font size 24px, line height auto, letter spacing 0, font weight 700
- Body/Base — Rubik font size 14px, line height 16px, letter spacing 0, font weight 300
- Body/Label — Rubik font size 12px, line height 16px, letter spacing 0, font weight 300
- Component/Base — Rubik font size 14px, line height 16px, letter spacing 0, font weight 500
There are many ways to configure typographies. I prefer doing this by adding custom components to tailwind configuration as described in the official documentation.
Here is our tailwind.config.js
after adding typographies:
/* eslint-env es6 */
const plugin = require('tailwindcss/plugin');
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{html,ts}"],
theme: {
extend: {},
},
plugins: [
plugin(({ addComponents }) => {
// Typographies are being added here
addComponents({
'.typo-h-2': {
fontFamily: 'Rubik',
fontSize: '24px',
lineHeight: 'auto',
letterSpacing: '0',
fontWeight: '700',
},
'.typo-body-base': {
fontFamily: 'Rubik',
fontSize: '14px',
lineHeight: '16px',
letterSpacing: '0',
fontWeight: '300',
},
'.typo-body-label': {
fontFamily: 'Rubik',
fontSize: '12px',
lineHeight: '16px',
letterSpacing: '0',
fontWeight: '300',
},
'.typo-component-base': {
fontFamily: 'Rubik',
fontSize: '14px',
lineHeight: '16px',
letterSpacing: '0',
fontWeight: '500',
}
})
})
],
}
We also need to add Rubik font to our app, this can be easily done:
- Search for Rubik https://fonts.google.com/?query=rubik
- Copy embedded code
- Paste it inside the head of your
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>PixelPerfectTraining</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<!-- Rubik from Google Fonts goes here -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,300..900;1,300..900&display=swap" rel="stylesheet">
<!-- -->
</head>
<body>
<app-root></app-root>
</body>
</html>
Prepare atomic UI components
Here we need to prepare these components mentioned before:
- icon
- button
- list item
I will put these components in ui-kit
directory for simplicity, since this article is not about directories naming.
Icon
To copy icon from svg you need to find closes proper square container (usually sizes 12x12, 16x16, 18x18, 20x20, 24x24, 32x32)
Then paste it into your icon component
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.99998 10.78L3.21998 8L2.27332 8.94L5.99998 12.6667L14 4.66667L13.06 3.72667L5.99998 10.78Z" fill="black" />
</svg>
List item
As we see, list item is a flex container with gap 8px (0.5 rem or 2 tw units) contains an icon and text passed from parent container. We can use content projection here to make our layout flexible enough.
Also list item can be disabled, so let's support this state as well. Pass though 20% basically stands for opacity which is 0.2 for disabled item.
import { Component, input } from '@angular/core';
import { IconCheckComponent } from "../icon-check/icon-check.component";
import { NgClass } from "@angular/common";
@Component({
selector: 'ui-list-item',
standalone: true,
imports: [
IconCheckComponent,
NgClass
],
templateUrl: './list-item.component.html',
styleUrl: './list-item.component.css'
})
export class ListItemComponent {
public disabled = input(false);
}
<div class="flex gap-2" [ngClass]="{ 'opacity-20': disabled() }">
<ui-icon-check></ui-icon-check>
<p class="typo-body-base text-black">
<!-- ng-content is for content projection -->
<ng-content></ng-content>
</p>
</div>
Button
Button is a flex container justified and aligned its content in the center with border and rounded corners. There are multiple types of buttons, but we will support only type Secondary for now.
import { Component, input } from '@angular/core';
export type ButtonType =
| 'secondary';
@Component({
selector: 'ui-button',
standalone: true,
imports: [],
templateUrl: './button.component.html',
styleUrl: './button.component.css'
})
export class ButtonComponent {
public type = input.required<ButtonType>();
}
@switch (type()) {
@case('secondary') {
<button class="flex w-full justify-center items-center py-3 px-4 rounded-lg border border-[#5A45F2] inset-1">
<span class="typo-component-base text-black"><ng-content></ng-content></span>
</button>
}
}
Final component layout 🎬
Basically we can complete work on our component just in a few steps. Note that since we are using more analytical approach instead of imperative trying to match the mockup, we haven't even tested our layout in code. Therefore let's make 80% of the job and then test and then make some fixes.
Go back to the step where I broke down layout a bit.Now go to Figma file, check the containers and how they actually lay out within the space.
This is how we can present it in the code:
<div class="flex flex-col gap-4 py-4 px-4 rounded-lg border-2 inset-2 border-[#DAE9EF]">
<img class="self-center" src="assets/images/pricing-plan/starter.png" alt="Starter plan" />
<div class="flex flex-col gap-1">
<span class="typo-body-label text-black">Starter</span>
<span class="typo-h-2 text-black">Free</span>
<span class="typo-body-label text-[#5F6974]">per month</span>
</div>
<div class="flex flex-col gap-4">
@for (item of [
{text: 'Full courses library', disabled: false},
{text: 'A new daily meditation', disabled: false},
{text: 'Access to the meditation guru', disabled: false},
{text: 'Sleep podcasts and exercises', disabled: true},
{text: 'Mindfulness exercises', disabled: true},
{text: 'Guided meditations', disabled: true},
{text: 'Cooking recipes', disabled: true}
]; track $index; ) {
<ui-list-item [disabled]="item.disabled">{{ item.text }}</ui-list-item>
}
</div>
<ui-button type="secondary">Get Started</ui-button>
</div>
This is my result from literally first attempt:
Conclusion 📝
As you see, making pixel perfect layout is not that hard and doesn't take that much time. You just need to follow following rules:
- Extract visually repeated pieces into reusable atomic UI components
- Present your layout as set of nested rows and columns. Make sure you don't use margins, instead you should use gaps
- Make sure designers and developers follow the same guidelines
Homework to practice 📚
If you want to get better at this, try implementing the whole section from the design. Publish your solutions at Github public repository and share in comments if you want to receive feedback and a code review.
Top comments (1)
I just realized that I've used pixels for typographies in this article. I decided not to update it and leave it as it is right now. I will show cases for leveraging using rems for highly responsive layouts.
Another thing that is not mentioned here is colors. Colors are hardcoded in this article, because it would be too much to get into colors. I will dedicate a whole another article for this topic.
Let me know please if there is anything more you'd like to learn about