Fundamental №1 —Typography
Let’s start off with typography, a fundamental basis for design. The first step is to define typography constants. And unlike Sass or Less, constants for Aphrodite can go in a JavaScript or JSON file.
Define typography constants
When creating constants, use semantic names for your variables. For example, instead of naming one of your font-sizes h2, use a name like displayLarge that describes its role. Similarly, for font-weights, instead of naming one of your weights 600, give it a name like semibold to describe its effect.
It’s important to get the values right for variables like font-sizes and line-heights. This is because they directly affect the vertical rhythm within a design. Vertical rhythm is a concept that helps you achieve consistent spacing between elements.
For more on vertical rhythm, you can read this article: Why is Vertical Rhythm an Important Typography Practice?
Use a calculator to determine line-heights
There is a science behind choosing the values for your line-heights and font-sizes. We can use mathematic ratios to generate a set of potential sizes candidates. A few weeks ago, I wrote an article detailing the methodology: Typography can make or break your design: a process for choosing type. For determining font-sizes, you use Modular Scale. For determining line-heights, you can use this vertical rhythm calculator.
Define a heading component
After defining our typography constants, the next step is to create a component to consume the values. The goal of the component is to enforce consistency in design and implementation for headings across the codebase.
The Heading component is a stateless function that takes in a tag as a property and returns the tag with its associated style. This is possible because we defined the tag mappings earlier in the constants file.
At the bottom of the component file, we define our styles object. This is where we use the typography constants.
And this is how the Heading component would be used:
With this approach, we reduce unexpected variability in our type system. We avoid the pitfall of a hundred different font sizes by removing the need for global styles and standardizing headings across the codebase. In addition, this approach we took to building the Heading component can be applied to building a Text component for body copy.
Fundamental №2 — Spacing
Spacing controls both vertical and horizontal rhythm in design. That makes spacing pivotal to establishing a visual design system. Just like in the typography section, the first step to address spacing is to define spacing constants.
Define spacing constants
When defining spacing constants for the margins between elements, we can adopt a mathematic approach. Using a spacingFactor constant, we can generate a set of distances based on a common factor. This approach ensures that we have logical and consistent spacing between elements.
The example above uses a linear scale, one to thirteen. However, experiment with different scales and ratios. Designs require different scales based on their purpose, their audience, and the devices they target. As an example, here are the first six computed distances using the golden ratio with a spacingFactor of eight.
Golden Ratio (1:1.618)
8.0 x (1.618 ^ 0) = 8.000
8.0 x (1.618 ^ 1) = 12.94
8.0 x (1.618 ^ 2) = 20.94
8.0 x (1.618 ^ 3) = 33.89
8.0 x (1.618 ^ 4) = 54.82
8.0 x (1.618 ^ 5) = 88.71
This is what the spacing scale would look like in code. I added a helper function to handle the computation and round off the output to its nearest pixel value.
After defining our spacing constants, we can use them to add margins to elements in our design. One approach we can take is to import the spacing constants and consume them in components.
For example, let’s add a marginBottom to the Button component.
This works in most scenarios. However, what happens if we want to change the marginBottom property of the button based on where the button is place?
One way to achieve variable margins is to override the margin style from the consuming parent component. An alternative approach is to create a Spacing component to control the vertical margins on elements.
Using this approach, we can remove the responsibility of setting margins out of the child component and into the parent component. This way, the child component becomes layout agnostic, not requiring any knowledge of where to place itself in relation to other elements.
This works because components like buttons, inputs, and cards may need variable margins. For example, a button in a form may need larger margins than a button in a navigation bar. The caveat is that if a component always has consistent margins, then it would make more sense to handle margins within the component.
Also you may have noticed that the examples only use marginBottom. This is because defining all your vertical margins in one direction allows you avoid collapsing margins and keep track of the vertical rhythm of your design. You can read more on this in Harry Robert’s article, Single-direction margin declarations.
On a final note, you can also use the spacing constants you defined as padding.
By using using the same spacing constants for both margins and padding, you can achieve more visual consistency in your design.
Here’s what the result might look like:
By using spacing constants for your margins and padding, you can achieve more visual consistency.