User Interface

Hero image for User Interface

Layout

A layout is the shared structure of an application screen. A layout is used to maintain the same structure across multiple application screens with many dynamic contents.

There can be multiple layout files. Some pages have a single-column layout, while others have a two-column layout (e.g., a sidebar and the main area). In this case, there would be two layouts.

Layout types

  • Default layout: Every Web application must have a default layout. This default layout is an application-wide generic layout and should be used by default on most of the application’s screens.

  • Custom layout: Along with the default layout, there can be as many custom layouts based on different UI structures of the application screens. For example, a new authentication layout can be used for authentication UIs structure, a new admin layout for the admin UIs structure. Note that the custom layout is only used when the UI structure differs from the default layout.

Why are layouts needed?

The layout is essential for building UI. It helps structure the UI and allows the same structure to be reusable across multiple application screens.

Consider the authentication layout style below to keep all authentication-related application screens’ content centered. Developers can re-use this layout style for all authentication-related application screens, and all content will be centered.

// File name will be authentication.scss
.layout-authentication {
  body {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
  }
}

When in need of changing the layout based on the device screen, only changes in the layout style are needed. It will affect all the application screens that use the layout.

// File name will be authentication.scss
.layout-authentication {
  body {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
  }

  .app-content {
    width: 300px;
    @include media-breakpoint-up(md) {
      width: 420px;
    }
  }
}

By using layout, developers can easily control the UI structure of multiple application screens.

Defining the layout

Defining a layout consists of an HTML layout file and a CSS layout file. The HTML layout file (e.g., application.html.erb) structure can be like

<!DOCTYPE html>
<html class="layout-default">
  <head>
    ...
  </head>
  <body>
    <header class="app-header">Header stuff goes here</header>
    <main class="app-content"></main>
    <footer class="app-footer">Footer stuff goes here</footer>
  </body>
</html>

The layout class name must be defined at the topmost tag, which is <html>. The CSS layout file, e.g. default.scss can be like

.layout-default {
  .app-header {
  }

  .app-content {
  }

  .app-footer {
  }
}

Identify separate layout

When in need of a different UI structure than the existing layouts, a separate layout must be introduced. For example, add a two-column layout (e.g. two_column.html.erb, two-column.scss) when in need of a two-column layout.

Best practices

  • Every application must have the default layout.
  • Introduce a separate layout when in need of a different UI structure than the existing layouts.
  • Do not add any component or screen-specific style in the layout styling.
  • Prefer using grid instead of flex for layout styling as it provides more flexibility.

Component-based UI

A component is any part of the UI that can be logically grouped and used as a single element. They are reusable and act as the building blocks for creating the UI. Most blocks on the application screen are components. Cards, navigation menus, listing tables are some examples of components.

A component may have nested components within it or maybe used within other components, but each individual component is a standalone thing that is suited to display one piece of content.

Why build component-based UI?

Building the component-based UI means splitting the UI into smaller, manageable components and using those components to create the UI with the benefits:

  • Speed up the development: since the components are independent and reusable, it makes splitting the works easier and creates the new UI faster.
  • Ensure consistency: leveraging the components ensure consistency across the application.
  • Keep the codebase DRY and reduce the maintenance overhead since there is less code to maintain.

How to identify components?

Generally, any part of the UI that can be logically grouped and thought of as a singular element and can represent a piece of content is a good choice to be extracted as a reusable component.

Best practices

  • Avoid coupling the components with the layout. Components should not have limited sizing as they can be used in any layout. When in need of fitting a component to a specific screen, use a screen stylesheet to modify the sizing instead of modifying the component style.

    // Bad
    // components/_card-product.scss
    .card-product {
      // other style
      width: 200px;
    }
    
    // Good
    // components/_card-product.scss
    .card-product {
      // the card product style
    }
    
    // screens/_checkout.scss
    body.checkout {
      &.new {
        .card-product {
          width: 200px;
        }
      }
    }
    
  • Prefer to place the element classes in the same order of HTML structure to help understand the order when each class is used.

    <div class="card">
      <div class="card__header"></div>
      <div class="card__boby"></div>
    </div>
    
    // Bad
    .card {
      &__body {
      }
    
      &__header {
      }
    }
    
    // Good
    .card {
      &__header {
      }
    
      &__body {
      }
    }
    
  • Prefer splitting the complex UI element into smaller components. A component is not necessary a standalone module. It can have nested components inside. For example, this UI is a chat message, but it can nest a user-avatar component. The avatar is a separate component and not necessarily a part of the message component:

    Message component

    <div class="chat-message">
      <div class="chat-message__avatar avatar"></div>
      <div class="chat-message__content"></div>
    </div>
    
    .chat-message {
      &__avatar {
      }
    
      &__content {
      }
    }
    
  • When nesting the components, only use the element class name to target the element to avoid coupling between components. e.g., given an avatar component nested in a chat-message component to style the avatar within the chat-message component, another class can be introduced to target the avatar

    <div class="chat-message">
      <div class="chat-message__avatar avatar"></div>
      <div class="chat-message__content"></div>
    </div>
    
    // Bad
    .chat-message {
      .avatar {
      }
    }
    
    // Good
    .chat-message {
      &__avatar {
      }
    }
    
  • All created components must be documented for reference. The document contains the HTML structure with the component classes. We usually use LivingStyleGuide in Ruby projects or Github Wiki in other projects.

    // given a product card component
    .card {
      &__header {
      }
    
      &__body {
      }
    }
    
    <!-- the document must contain the HTML structure to demonstrate the usage of the component -->
    <div class="card">
      <div class="card__header"></div>
      <div class="card__body"></div>
    </div>
    

Managing specificities at each application screen level.

An application screen is all about a whole page, not just about the layout of components. To style an application screen, a separate styling can be introduced. The idea of application screen styling is to style a particular UI that is only related to that specific application screen.

Also, in the application screen style, some styling adjustments can be added. For example, adding margin between two different components.

Best practices

  • Use screen-specific class names to style the application screen. Prefer using the controller name as an extra class to make it very screen-specific.
body.products {
  &.index {
  }
}
  • Do not put any component and layout styling in application screen styling.

JS Frameworks

When it comes to rendering the views, there are options to use server-side rendering (SSR) or client-side rendering (CSR) using the JS libraries & frameworks.

  • Prefer using SSR:
    • When the application is a monolithic application where backend and frontend are in the same repository. For example, Rails application, Phoenix application.
    • When the UI is simple and does not require high interaction with the user.
    • When the UI feature is easily achievable with the Vanilla JS.
  • Prefer using CSR:
    • When the applications have a very complex UI with many features and require complex state management.
    • When the complex UI features are hard to achieve with Vanilla JS.
    • When the backend and frontend applications are separate.
  • Prefer using React.js or Vue.js for client-side rendering.