How We Build Front-End Code from Design Specifications
How many common design patterns exist on one website? Let’s take typography, for instance. An average site design will use styles for headings (h1, h2, h3, h4, h5, and h6 default styles at least), body text (usually with larger and smaller font size variants), bolds, italics, preheadings, button text, quotes, and blockquotes, unordered and ordered lists, hyperlinks, navigation text, and more. Designers create a style guide to provide direction for each of these patterns on a granular level: the font size, line height, letter spacing, color, weight, hover effects (for interactive elements), the spacing between block typography elements, and the way each style responds in different browser sizes.
Documenting these details to hand off to front-end developers can be overwhelming on the scale of a large website. What unit types are “best practice”? How are responsive changes or themes conveyed? How are different states (hover, focus, disabled, etc.) handled?
At Adage, our designers use software called Figma that allows front-end developers to inspect and retrieve exact values for CSS attributes from the design, which helps bridge the documentation gap.
A front-end developer’s job is to analyze and translate this raw output into extendable, maintainable CSS styles. This is a guide to some of the units, requirements, and other ground we need to cover when building out a style guide in code.
DISCLAIMER: CSS is a constantly evolving language, and browsers are always improving the way it is interpreted. Web design tools are also rapidly changing and improving. My advice here is not the One Correct Way to do things forever, it’s just an explanation of the way we follow best practices at Adage right now, and some of the reasons behind our decisions.
Sizing Units (Rems, Ems, Pixels)
Understanding these three unit types is key to understanding modern CSS. They define font sizes, responsive breakpoints, margins, padding, and countless other properties. These are not the only ways to define size and distance, but they will be the most useful units to use in a style guide.
- Pixels: The most basic unit of CSS, and the one that most people are familiar with. In CSS, a pixel’s size is always consistent, unless a user zooms in or out with their browser. If they try to enlarge text in their browser settings, it has no effect, which makes pixels an inferior choice for font sizes. However, since they’re a predictable measure, they are ideal to use for breakpoints in media queries. I also use pixels for edge effects like border widths and box shadow size, since they don’t always need to scale with text.
- Ems: One em is equal to an element’s font size, in context. This means that an element with a 2em font size will have an effective font size of 2 times its parent’s font size. As they are completely determined by their context, ems can get too complicated to manage on large websites, which may have unpredictable nesting with CMS blocks and modular components. Ems are, however, extremely useful in situations where an element must be sized or spaced relative to its font size; like super/subscripts, letter spacing, or margins between blocks of text.
- Rems (responsive ems): One rem unit is equal to the base font size of a website. The default is 16 pixels in modern browsers, and sites can use CSS to manipulate that value. The most important thing about rems though, is that the browser’s base font size is not always 16px: a user can make it larger or smaller by changing their browser settings. We should always honor user preferences, so rem units are crucial to use for font sizes, and any measurements set in the layout that might restrict larger text if they didn’t scale to match. I use rems for element padding as well to keep it proportional to the base font size, and because it makes layout geometry and math easier when font sizes and padding use the same units.
This example shows two containers with their font-size set in pixels. The paragraphs inside show the difference between using rems and ems.
Style Guide Requirement Examples
This is a small selection of requirements and unit types listed in our handoff documentation. All Adage projects start with these values defined by the designer, ready to implement by the front-end developer.
Maximum line length for typography (preferred units: rem or ch)
Implementing a maximum line length for text is essential to keep prose readable for all users. Without a max line length, text can spread all the way across the screen in some layouts, making it difficult for users to find the next line as they scan the page. This is also important for accessibility, since users with dyslexia, cognitive difficulties, or vision problems will be more severely impacted by awkwardly long lines of text.
The character unit (ch) in CSS is representative of the width of the “0” (zero) character in an element’s current font. Character units are useful because they are determined by the font size, weight, and typeface being used. A comfortable, readable maximum character width for body text is 60-70ch, but for larger headings this should be reduced to 30-40ch. The downside to using ch units is that text elements using different font styles may have different measurements of the width of the “0” character, creating uneven alignment on the rag edge (wrapping edge of text). In cases where this is undesirable, use rems instead.
These containers apply a max-width to each of their child elements. The ch units are dependent on the child element’s own font size, while the rem units are consistent for each.
Breakpoints (preferred units: px)
Designers should always keep a list of breakpoints intended when handing off a new design, even if designs aren’t fully fleshed out for every size. A breakpoint list is a map that the front-end developer can use to read the designs and plan accordingly. Having a breakpoint list also helps to identify awkward pinch points in advance so that the designer can provide solutions before issues occur. The breakpoints don’t need to be too granular though; project shouldn’t require more than four or five distinct breakpoints.
Units for breakpoints are still a hotly debated topic around front-end blogs. The only consensus is that, since media queries operate outside of other CSS settings, rems aren’t useful here. The purpose of breakpoints is to accurately interpret the current width of the browser, and in my opinion, pixels do the job perfectly well. Anecdotally, I’ve found that using ems for breakpoints has led to issues in some devices and pixel breakpoints have proven more accurate and reliable, especially if a design requires the use of media queries that use min-width and max-width in conjunction.
Color Palette (preferred formats: hex or rgb)
At Adage, we use Sass – a CSS preprocessor that gives us access to powerful tools that help keep our styles organized and consistent. One of the many benefits of using Sass is that we can keep strict control of all the reusable constants in our project, including color values. Designers can hand front-end developers a list of hexadecimal (#FFFFFF) or RGB (rgb(255,255,255)) formatted color values at the start of a project, so that they’re easily available when needed. Keeping the color constants documented globally also means that if there are design changes later on, the color can be changed throughout the project by just changing one value in one file.
Along with hex or RGB values, CSS colors can be represented in the RGBA format. RGBA colors contain values for red, green, and blue, like RGB, but with a 4th value for “alpha”. The alpha value corresponds to the color’s opacity, where 0 is fully transparent, and 1 is fully opaque. For example, the color rgba(255, 0, 0, 0.5) would show up as semitransparent red. Sass also has functions for manipulating colors directly, so that one color can be processed to generate lighter, darker, more transparent, brighter, duller, or hue-shifted variants programmatically.
Documenting color constants also helps immensely when styling with themes – pages or elements with different backgrounds and text colors than the default. Creating theme palette configurations in Sass opens up opportunities to leverage tools like mixins (a reusable pattern that takes variables as arguments and outputs custom CSS) to easily create a robust and dynamic theming system.
Our theming variables are usually organized in maps like this. It’s shown with named CSS colors for fun, but usually we’d set common color variables like “$primary-color” in one place and just utilize those here.
Typography (preferred units: rem font-size, unitless ratio line-height, em letter-spacing)
There’s a complex ecosystem of typographical styles needed to support a large scale website. Elements like <p>, <li>, <label>, and <h1> should have default styles, but also need the ability to add CSS classes with attribute overrides. Since there are so many cases to cover, it’s helpful to use Sass tools like placeholder classes and mixins to generate styles from clearly documented configuration.
At Adage, we typically use Sass maps to represent these settings by listing the font-size, line-height, and letter-spacing at several different breakpoints. These maps are configured for each unique typography style and are applied to element and class styles from there.
The font-size property is set with rem units to support user preferences, but the line-height and letter-spacing property are different. Line-height is a special CSS attribute, since it has the ability to take a plain number, like “1.5” as a value. A ratio value is easier and cleaner to maintain in CSS than a static number, since it will scale proportionally to the element’s font size. Letter-spacing is easiest to set with em values for the same reason – it will scale proportionally to the font size. Using values for line-height and letter-spacing that are tied to font-size means that resizing fonts at different breakpoints, different user font size preferences, and browser zoom all work seamlessly together.
This example may look like complex SCSS, but just focus on the maps at the top and the element styles at the bottom. Everything in between is just connecting the dots so that our maps can be used with one mixin anywhere in our SCSS.
Aside from the styles that describe appearance, there are more considerations designers need to make for elements that users will interact with. With so many components and so many states, it can be easy to overlook some of the variants, leaving the developer without any direction. There’s also more technical background and knowledge needed when designing interactive states, especially for form elements. Accessibility should be at the forefront of any design decisions made about interactions; you should be confident in the design stage that a user navigating via keyboard or touch can complete tasks successfully.
Buttons and Hyperlinks (hover, focus, active, disabled states)
Using <button> and <a> tags improperly is an extremely common error on many websites, but the rules for their correct usage can be boiled down to: buttons make web pages do stuff; hyperlinks make web pages go places. Most website designs will require some stylistic variants for hyperlinks: inline hyperlinks, links in navigation, links styled to look like buttons, links that look like icons, etc. Interactive and form buttons will also have variants: standard buttons, buttons that are styled to look like hyperlinks, and buttons that look like icons.
For each of these variants, there’s a default state, hover state, focus state, and active (clicked) state that can be styled differently. Additionally, hyperlinks have a visited state, and buttons have a disabled state (Note that hyperlinks DO NOT have a disabled state. That’s not how links work!) Some buttons might trigger additional states as well, like when an accordion toggle button’s “expanded” state flips a down-pointing caret to face up.
Focus states in particular should be carefully designed to be visible on any color (or imagery) that could fall behind the interactive element. Default browser focus outlines, usually blue boxes that are either glowing or dashed, can sometimes clash with a site’s design, or blend in and get lost. Focus states can use a shifted background color, double border (using the box-shadow property), underline, shadow, or any other CSS effect to highlight the active element, as long as it has a 3:1 color contrast with the background. Well-designed focus states are visible for all users, and facilitate a smooth experience for both mouse and keyboard users.
Sometimes hover or focus states have fancy animations or transitions (speaking for myself, I love to implement these! Get creative! As long as we make sure to provide a version without movement for users who prefer reduced motion.) These should be presented to the developer by providing an existing reference, a video or gif animation, or a static storyboard. The duration and ease (changes in the speed of the animation as it runs; e.g. speeding up or slowing down at the beginning and/or end) should also be provided or described with as much detail as possible.
Forms (labels, optional/required, hints, focus, invalid, error messages, error validation summary)
Forms have a tendency to be drastically underestimated in terms of complexity, accessibility, and semantics. There are very clear requirements for making forms correctly, and designers must take these into account. When users encounter badly built forms, they could get confused, make mistakes, find themselves blocked from progressing, or might need customer service help. In worst case scenarios, users who encounter difficulties may take legal action under the Americans with Disabilities Act (ADA), or similar laws in countries other than the US. Forms aren’t the only opening to a lawsuit, but forms may request and handle potentially sensitive information and transactions, making them an especially vulnerable point of failure.
From a developer’s perspective, there’s a lot of work needed “under the hood” to assure a form is constructed well. But from a designer’s perspective, there are a few specific areas under their influence: structure, instructions, appearance, and states.
Form inputs require labels. Specifically, one label per input, and it must stay available while the user is interacting with the input. This means that placeholder text can’t serve as a label, the default option of a select dropdown can’t serve as a label, and one label can’t apply to multiple inputs in a row (there are rare cases where one visible label may appear to label a group of inputs, but there still must be one label per input, even if they are visually hidden and only available to screenreader users). For checkboxes and radio buttons, the text next to the control is the label. A group of checkboxes or radio buttons should be nested inside a fieldset, and the instruction text for the group is the legend. If placeholders are used (there’s evidence that placeholders may be detrimental), they should show an example input value, rather than crucial instructions (e.g. [email protected] in an email field).
Designers also must keep in mind that one form cannot have another form inside it. Think about it as a user; if you are filling out fields and hit “enter”, there can’t be any ambiguity over which form was submitted. And a form is, importantly, a distinct and contiguous HTML container. If a front-end developer needs to splice or split up a form to follow a disjointed design, it may not be technically feasible.
There are many differing opinions out there on the use of “required”, “optional” or “*” markers on inputs. From my experience, and information from a Normal Nielsen Group article on the topic:
- It’s an established, easily understood convention that asterisks mean “required” (even to screenreader users!). There is stronger affordance when the asterisk is red.
- It’s very helpful when required fields are marked.
- It also helps users when optional fields are marked as optional.
- It’s important to mark both types of fields on longer forms with a mix of required/optional inputs, like registration forms, while on simple login/password forms it’s not always necessary.
Even though it might seem to “clutter” the design, marked inputs are proven to reduce user friction and should, at least, be added to inputs in longer forms. Instructions at the top of a form, such as “all fields are required unless marked otherwise”, are often ignored.
Some inputs may need further information to help users, such as formatting for a date field or password security requirements. These hints are extremely helpful when there is complex validation and users could easily make mistakes. The hint should be located adjacent to and either above or below the related field.
When it comes to form appearance, I have some very strong recommendations and advice for designers. Just trust me here. If you take anything away from this post, remember these:
- Use a distinct border (minimum 3:1 contrast ratio against the page and field backgrounds) around all inputs. This has been proven over and over to be vital to form readability.
- We can’t style select dropdown options (the panel that pops up), besides the font styles, text color, and background color. Any other fancy effects would require building a custom dropdown from scratch, and no one wants that…especially your users.
- The minimum font size for form inputs is 16px. Any smaller than that, and iOS devices automatically zoom in when an input is tapped. This has been the case for years, but it still surprises people.
Aside from those tips, there are some more subtle things that should be documented in designs. Input labels should always be adjacent to the field they are labeling, usually on top. Labels should be a similar font size to input field text, to further strengthen the relationship. If placeholder text is desired, be aware that the default placeholder text color in most browsers does not meet minimum color contrast requirements for accessibility guidelines. Designers should provide a color for placeholder text that meets at least a 3:1 contrast ratio against the background color.
Focus states for form inputs should also not be overlooked. Even people who primarily use a mouse will briefly become keyboard users while filling out forms, and tab through fields expecting to easily see where they are. The input style should change visually so it’s extremely clear which input is focused, since the blinking cursor in text inputs is often not noticeable on its own.
Error states and messaging seem like an afterthought in some designs, but they are crucial to the development of a successful site. Designers don’t need to brainstorm for every possible validation error on a form, but should plan for the most likely setups:
- Short form with one or few invalid inputs and error message
- Long form with any number of invalid inputs, and a validation summary
Error messages should appear below invalid inputs, for all invalid inputs. A user should never be confused why a field is marked red, or why their form won’t submit. When a longer form is returned with multiple errors, a validation summary should be shown at the top of the page. This is usually a numbered list of errors with on-page links down to the field that contains the error described. There should be a heading describing how many errors there are, and how to fix them.
This type of pattern might be new to you, but it’s a clear and explicit way to communicate errors to all users. It’s strongly recommended for accessibility reasons, and in my experience, makes fixing form errors painless.
As a front-end developer, I’m always excited to work with designers who are curious, bold, and willing to push web designs to the next level. But the pre-requisite for creative exploration is learning to speak the same language. When designers learn more about the technical requirements, limitations, and opportunities of HTML and CSS, handoffs are smoother and it becomes possible to make some really cool stuff.
Stay in-the-know by subscribing to Adage communications.
We’ll share industry insights, events, and how-tos and more.