Design Systems, Pt. 1: The Base
This document outlines properties and naming conventions for design systems that can be implemented in SASS/SCSS or in vanilla CSS.
For each item, several properties are listed:
- Key: The theme configuration is assigned to a
$theme
variable. The key is the map key under which the value is found. - Function: The function name for accessing values from map variables. It is preferred to define accessor functions for maps, since this allows us to return error messages that list the valid values in case a key does not exist.
- Naming: The format of the base name for custom properties and class names.
- Custom properties: The format of the custom property, using the base name. If SASS is used, the custom properties are generated from the SASS variables.
- Examples: These are examples for the base name.
While the document lists SASS variables and function names, the naming conventions can be used for class names and custom properties without SASS.
All variable names and values use kebab case (e.g. my-variable
). The naming should be consistent across all design documents and in the code base.
Colors
- Key:
colors
(map) - Function:
color($name)
- Naming:
[(bg|border|text)-]<name>[-(<modifier>|<shade>)]
- Custom properties:
--color-<name>
Semantic color names are preferred over concrete color names to support multiple themes. For neutral tones, background
and foreground
can be used instead of black
and white
, unless there are other background and foreground colors. shade
can be used for shades of gray. The base name should not consist of more than two segments.
Color names can be prefixed by an identifier for the designation (bg
for background, border
and text
).
Color names can be suffixed by a modifier (e.g. -hover
, -disabled
).
A color schema can include color variations. If color variations are included, the base color is suffixed with -500
. Lighter versions of the color are suffixed with 600
, 700
, 800
, 900
, while darker versions are suffixed with 400
, 300
, 200
, 100
.
A modifier and a shade should not be combined.
Examples: foundation
, foreground
, background
, primary
, secondary
, tertiary
, success
, info
, warning
, error
, link
, border-primary
, bg-primary
, bg-primary-600
, text-primary
, link-hover
, primary-500
, shade-700
Example definition
As an example, you would define the $colors
variable in SASS like this:
$theme: (
colors: (
foundation: #09080d,
primary: #62a1d9,
secondary: #363859,
warning: #d9b343,
error: #bf3636
)
);
Which translates to these custom properties:
--color-foundation: #09080d;
--color-primary: #62a1d9;
--color-secondary: #363859;
--color-warning: #d9b343;
--color-error: #bf3636;
--color-shade-500: #666666;
You would access a color value in SASS with: color(primary)
(after defining the function).
Font families
- Key:
font-families
(map) - Function:
font-family($name)
- Naming:
<name>
- Custom properties:
--font-family-<name>
Examples: main
, serif
, monospace
Font sizes
Base size
- Key:
base-size
(length, default:100%
) - Custom property:
--base-size
Serves as the body font size and as the base for generated type scales and line heights.
Font sizes can either be defined manually or generated from a modular scale.
Manually defined font sizes
- Key:
sizes
(map) - Function:
size($name)
- Naming:
<size-in-px> | <clothing-size>
- Custom properties:
--size-<name>
Define a fixed set of allowed font size.
Either use the pixel sizes as identifiers for sizes, or use clothing sizes (xs
, s
, m
, l
, xl
etc.).
If you use clothing sizes, ensure that the font styles in the graphic software consistently use the same names. If that is not possible, it is preferred to use pixel sizes as names, since this makes it easier to transfer font sizes from the mock-up to the code base.
Examples: 12
, 14
, 16
, 14
or xxs
, xs
, s
, m
, l
, xl
, xxl
Generated font sizes based on a modular scale
- Variables:
$modular-scale
(number),$larger-sizes
(number),$smaller-sizes
(number)
$modular-scale
defines the ratio for the modular type scale (e.g. 1.25, 1.333).
$larger-sizes
defines how many font sizes should be generated larger than the base size.
$smaller-sizes
defines how many font sizes should be generated smaller than the base size.
Example
$base-size: 100%; // 16px
$modular-scale: 1.25;
$larger-sizes: 3;
$smaller-sizes: 2;
This generates the following font size variables:
size-xs
: 0.64rem = 10.24pxsize-s
: 0.8rem = 12.8pxsize-m
: 1rem = 16px (size-m always matches the base size)size-l
: 1.25rem = 20pxsize-xl
: 1.563rem = 25pxsize-xxl
: 1.953rem = 31.25px
If more font sizes are generated, more x’s will be added.
Line heights
- Key:
base-line-height
(number) - Custom property:
--base-line-height
$base-line-height
defines the base line height related to the font size in em.
Line heights can either be defined manually or generated from a list of factors.
In order to keep a vertical rhythm, it is recommended to only use the defined line heights for vertical spacing.
Manually defined line heights
- Key:
lines
(map) - Function:
lines($factor)
- Naming:
<factor>
- Custom properties:
--lines-<name>
Lines are defined as multiples of the base line height. Fractions in property and variable names are written with an underscore.
Examples: 1_2
, 1
, 3_2
, 2
Generated line heights
- Key:
line-heights
(list) - Function:
lines($factor)
- Custom properties:
--lines-<name>
$line-heights
defines the multiples of the base line height to be used.
Example
$base-size: 16px;
$base-line-height: 1.5;
$line-heights: (0.5, 1, 1.5, 2, 3);
This results in the following properties (fractions in property names are written with an underscore):
--lines-1_2
: 16px * 1.5 * 0.5 = 12px--lines-1
: 16px * 1.5 * 1 = 24px--lines-3_2
: 16px * 1.5 * 1.5 = 36px--lines-2
: 16px * 1.5 * 2 = 48px--lines-3
: 16px * 1.5 * 3 = 72px
- Variable:
$line-heights
(list)
Letter spacing
- Key:
letter-spacings
(map) - Function:
letter-spacing($name)
- Naming:
<name>
- Custom properties:
--letter-spacing-<name>
Examples: narrow
, normal
, medium
, wide
Font weights
- Key:
weights
(map) - Function:
weight($name)
- Naming:
<name>
- Custom properties:
--weight-<name>
Examples: normal
, medium
, bold
Radius
- Key:
radii
(map) - Function:
radius($name)
- Naming:
<name>
- Custom properties:
--radius-<name>
Examples: small
, medium
, large
, full
Spacers
Spacers are mainly for horizontal margins and paddings. For vertical margins and paddings, lines
should be used if possible.
Spacers can either be defined manually or generated from a list of factors.
Manually defined spacers
- Key:
spacers
(map) - Function:
spacer($name)
- Naming:
<integer>
- Custom properties:
--spacer-<name>
Examples: 1
, 2
, 3
, 4
Generated spacers
- Keys:
spacer-base
(length),spacer-factors
(list) - Function:
spacer($factor)
- Custom properties:
--spacer-<n>
$spacer-base
defines the smallest spacing unit (e.g. 4px
or 0.25rem
).
Spacers are defined as multiples of the spacer base.
$spacer-factors
defines for which factors classes and custom properties will be generated. The variable does not affect the spacer
function.
Border widths
- Key:
border-widths
(map) - Function:
border-width($name)
- Naming:
<name>
- Custom properties:
--border-width-<name>
Examples: border-width-small
, border-width-medium
, border-width-large
Prose width
- Key:
measure
(length, default:60ch
) - Custom property:
--measure
The maximum width for paragraphs.
Gutter
- Key:
gutter
(length) - Custom property:
--gutter
The size of the space between columns.
Breakpoints for media queries
- Key:
breakpoints
(map) - Function:
breakpoint($name)
- Naming:
<device>
- Custom properties:
--breakpoint-<name>
The names for breakpoints should be derived from devices.
Examples: mobile
, tablet
, desktop
, widescreen
Example theme
This is the base theme for this blog:
@use "sass:string";
$theme: (
colors: (
text: #202c31,
primary: #71819c,
selection: #b15e5e,
shade-primary: #a0a0a0,
shade-secondary: #6b7380,
light-primary: #eaeaea,
light-secondary: #fafafa,
light-tertiary: #b0b0b0,
background: #fff
),
font-families: (
main: "Lato, sans-serif",
alt: "Montserrat, sans-serif",
mono: string.unquote('"Source Code Pro", monospace')
),
base-size: 100%,
sizes: (),
modular-scale: 1.149,
smaller-sizes: 2,
larger-sizes: 6,
base-line-height: 1.7411,
lines: (),
line-heights: (
0.25,
0.5,
0.75,
1,
1.5,
2
),
letter-spacings: (),
weights: (
normal: 400,
code: 500,
header-bold: 600,
bold: 700
),
radii: (),
spacer-base: 0.25rem; border-widths:
(
small: 1px,
medium: 3px,
large: 6px
),
gutter: 1.5rem,
measure: 75ch,
breakpoints: ()
);
Which is translated into these custom properties:
:root {
--color-text: #202c31;
--color-primary: #71819c;
--color-selection: #b15e5e;
--color-shade-primary: #a0a0a0;
--color-shade-secondary: #6b7380;
--color-light-primary: #eaeaea;
--color-light-secondary: #fafafa;
--color-light-tertiary: #b0b0b0;
--color-background: #fff;
--font-family-main: Lato, sans-serif;
--font-family-alt: Montserrat, sans-serif;
--font-family-mono: "Source Code Pro", monospace;
--size-xs: 0.757460417rem;
--size-s: 0.8703220191rem;
--size-m: 1rem;
--size-l: 1.149rem;
--size-xl: 1.320201rem;
--size-xxl: 1.516910949rem;
--size-xxxl: 1.7429306804rem;
--size-xxxxl: 2.0026273518rem;
--size-xxxxxl: 2.3010188272rem;
--lines-1_4: 0.435275rem;
--lines-1_2: 0.87055rem;
--lines-3_4: 1.305825rem;
--lines-1: 1.7411rem;
--lines-3_2: 2.61165rem;
--lines-2: 3.4822rem;
--weight-normal: 400;
--weight-code: 500;
--weight-header-bold: 600;
--weight-bold: 700;
--border-width-small: 1px;
--border-width-medium: 3px;
--border-width-large: 6px;
--measure: 75ch;
--gutter: 1.5rem;
}
Next part: Design Systems, Pt. 2: Generating Sizes, Line Heights and Spacers