Making UI Layout: Fast, Pixel Perfect, Without Stress πŸš€

Niko Borisov - Aug 29 - - Dev Community

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.
Pricing plans, our test feature to work on

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.
Checking boundaries of the content

When you look at some design you should visually parse it like shown on the screen:
Recognizing essential layout properties as well as overall content grouping and direction

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:

Image description

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',
        }
      })
    })
  ],
}

Enter fullscreen mode Exit fullscreen mode

We also need to add Rubik font to our app, this can be easily done:

  1. Search for Rubik https://fonts.google.com/?query=rubik
  2. Copy embedded code
  3. 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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

<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>
Enter fullscreen mode Exit fullscreen mode

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>();
}
Enter fullscreen mode Exit fullscreen mode
@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>
}
}
Enter fullscreen mode Exit fullscreen mode

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>

Enter fullscreen mode Exit fullscreen mode
This is my result from literally first attempt:

Image description

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.

. .
Terabox Video Player