Teaching CSS to
talk like a designer

Developers & designers often speak different languages

CSS gives us the illusion
it understands design


Taunting us with terms like
`line-height` & `baseline`

How would you style a 21px heading over 27px line height?

.card-heading {
  font-size: 21px;
  line-height: 27px;
}
<div class="card">
  <h1 class="card-heading">Heading</h1>
</div>
CSS “line-height”

Heading

Typographic line height

Heading

Devs are left to “CSS” until it works


.card-heading {
  font-size: 21px;
  line-height: 27px;
}.card-heading {
  font-size: 21px;
  line-height: 27px;
  padding-top: 1px;
}.card-heading {
  font-size: 21px;
  line-height: 27px;
  padding-top: 2px;
}.card-heading {
  font-size: 21px;
  line-height: 27px;
  padding-top: 3px;
}.card-heading {
  font-size: 21px;
  line-height: 27px;
  padding-top: 4px;
}.card-heading {
  font-size: 21px;
  line-height: 27px;
  padding-top: 4px;
  margin-bottom: -1px;
}.card-heading {
  font-size: 21px;
  line-height: 27px;
  padding-top: 4px;
  margin-bottom: -2px;
}.card-heading {
  font-size: 21px;
  line-height: 27px;
  padding-top: 4px;
  margin-bottom: -3px;
}

System left behind is fragile


Thrown away the designer’s thinking

Good design is not an accident

There’s a ‘why’ for everything

Grid systems, type hierarchy, etc


We start with the best intentions

@row-height: 9px;
@column-width: 60px;
@column-gutter: 20px;

@heading-size: 21px;
@heading-line-height: 27px;
.card-heading {
  font-size: @heading-size;
  line-height: @heading-line-height;
  padding-top: 4px;
  margin-bottom: -3px;
}

How can we fix this?


CSS “line-height”

Heading

Typographic line height

Heading


Text needs to move down

Height of the descender*

Heading

*not available in CSS

We have to calculate this manually

@heading-descender-height: 3px;

Font centered to line height

Heading

Half the difference betweeen line-height and font-size


(27px - 21px) / 2 = 3px

@heading-line-height-offset: 3px;
@heading-offset: (@heading-descender-height + @heading-line-height-offset);
.card-heading {
  font-size: @heading-size;
  line-height: @heading-line-height;
  transform: translateY(@heading-offset);
}

Starting to align with the designer’s intent

Heading

There will be multiple headings

.headingText() {
  font-size: @heading-size;
  line-height: @heading-line-height;
  transform: translateY(@heading-offset);
}

.card-heading {
  .headingText();
}

Design and dev can start
talking a common language

.headingText()

…but solution is hardcoded

/* Design principles */
@heading-size: 21px;
@heading-line-height: 27px;

/* Offset hacks */
@heading-descender-height: 3px;
@heading-line-height-offset: 3px;

Designers are still having to translate principles into pixels


Let’s reverse engineer
what’s behind these pixels


Line height is simply a row span

@heading-line-height: 27px;
@row-height: 9px;

.headingText() {
  font-size: 21px;
  line-height: (3 * @row-height);
  transform: translateY(@heading-offset);
}    

Where does the font size come from?

@heading-font-size: 21px;

The values in the hierarchy are all a scale of a base size, I tend to work off 10px as it scales cleanly


@base-font-size: 10px;

.headingText() {
  font-size: (2.1 * @base-font-size);
  line-height: (3 * @row-height);
  transform: translateY(@heading-offset);
}

Extract the principles

/* Design principles */
@base-font-size: 10px;
@row-height: 9px;
@heading-scale: 2.1;
@heading-row-span: 3;

/* Offset hacks */
@heading-descender-height: 3px;
@heading-line-height-offset: 3px;
@heading-offset: (@heading-descender-height + @heading-line-height-offset);

Recap: Heading at 21px over 27px line heightRecap: Heading at 2.1x over 3 grid rows

We’re capturing principles rather than pixels

Implement another hierarchy level?

Add support for subheadings

/* Design principles */
@subheading-scale: 1.8;
@subheading-row-span: 3;

/* Offset hacks */
@subheading-descender-height: ???;
@subheading-line-height-offset: ???;
@subheading-offset: ???;

Express the offset as a scale of the type


CSS can help us here — em units


Calculate the descender height as scale

Rather than defining for each level

@heading-descender-height: ???;
@subheading-descender-height: ???;

One descender scale per font family

@roboto-descender-scale: 0.15;
/* Offset hacks */
@roboto-descender-scale: 0.15;
@roboto-offset: @roboto-descender-scale * @base-font-size;

@heading-line-height-offset: 3px;
@heading-offset: (@roboto-offset * @heading-scale) + @heading-line-height-offset;

@subheading-line-height-offset: ???;
@subheading-offset: (@roboto-offset * @subheading-scale) + @subheading-line-height-offset;

This is better, but you know…

Do this for every level of the hierarchy?


Basekick

(github.com/michaeltaranto/basekick)

Translate design principles into CSS rules to align type


.headingText() {
  .basekick(
     @heading-scale,
     @roboto-descender-scale,
     @heading-row-span,
     @row-height,
     @base-font-size
  );
}
.card-heading {
  font-size: 21px;
  line-height: 27px;
  transform: translateY(.27935714em);
}

Transform doesn’t push neighbouring elements

Preserves CSS properties for semantic use

Ability to edit design primitives confidently

@heading-scale: 2.1;
@heading-row-span: 3;

@subheading-scale: 1.8;
@subheading-row-span: 3;

Putting it to the test...

.basekick(
   @heading-scale,
   @roboto-descender-scale,
   @heading-row-span,
   @row-height,
   @base-font-size
);.basekick(
   @heading-scale,
   @zapfino-descender-scale,
   @heading-row-span,
   @row-height,
   @base-font-size
);.basekick(
   @heading-scale,
   @papyrus-descender-scale,
   @heading-row-span,
   @row-height,
   @base-font-size
);

Design conversations are now centered around common language


2½ years into our journey

Hierarchy has grown

@hero-type-row-span: 5;
@hero-type-scale: 4.2;

@headline-type-row-span: 4;
@headline-type-scale: 2.8;

@heading-type-row-span: 3;
@heading-type-scale: 2.1;

@subheading-type-row-span: 3;
@subheading-type-scale: 1.8;

@standard-type-row-span: 2;
@standard-type-scale: 1.4;

@small-type-row-span: 2;
@small-type-scale: 1.2;

Added responsive variants with ease

@standard-type-row-span: 2;
@standard-type-scale: 1.4;

@standard-type-row-span-mobile: 3;
@standard-type-scale-mobile: 1.8;

System has been adopted in over 10 projects

(across multiple frameworks)

Proof is in production



Most developers don’t know basekick exists


CSS is speaking the same
language as our designers

Powerful mindset change for both disciplines

Striving for a common language everywhere

Apply this thinking to our markup

</>

Teach HTML to talk design

Component abstractions can help define a design language


<Text heading>Heading</Text>

Rather than discuss code like this:

<div class="card">
  <h1 class="card-heading">Heading</h1>
  <p class="card-body">The cards content</p>
</div>

We can more declaratively compose this:

<Card>
  <Text heading>My Heading</Text>
  <Text>The cards content</Text>
</Card>

More approachable code


Match designer’s intent out of the box


Help find your team’s design language