4 min read

Better control of css with scoped contexts

After working on a few typography led sites recently (including this one) it became apparent that there was some control lacking applying margins and list-styles to elements in a reliable and predictable way.

Single direction margins

Something Harry Roberts suggests to manage spacing on a page is to ensure that margins are only ever applied to the bottom of elements, in a single direction.

While this is great for typography, you don’t want to add margins to everything in a blanket fashion; especially on root elements as there are probably parts of your design that need custom styles without margins or with different values.

Root elements

Often content is created using a WYSIWYG editor in a CMS and you don’t have control of what markup is output. You have to make sure that all root elements can work together in any combination as you won’t have the opportunity to add classes.

If you set all your root elements to have the same margin, you will end up having to override a lot of them for UI elements unrelated to content. This could be one reason behind Eric Meyer’s reset removing all margins as opposed to Normalize that just ensures consistency. Combine the two for a better reset.

Scope styles for different contexts

One way to solve the issue of haphazardly overriding styles is to only apply certain styles when elements are within a container with a specific class. Creating different container classes (contexts) allows you to scope different types of styles together.

You gain complete control over typography with vertical rhythm while never having to override a margin for a UI element. Nothing has a margin applied until you manually add it (in the case of UI components) or add a class to a container.

Taking some liberty with the BEM naming conventions means it’s easy to quickly see which styles control which styling for child elements.

Font size and line height

Line heights are applied within .context__rhythm and make sure that they match the vertical rhythm based on the font size. You can use pixels, ems or rems. This ensures that everything will line up and create a ‘base’ rhythm.

// Vertical rhythm generated at https://drewish.com/tools/vertical-rhythm/
body {
font-size: 12px;
font-family: Georgia, serif;
line-height: 1.5; // 18px as vertical rhythm base
max-width: 600px;
margin-left: auto;
margin-right: auto;
}
.context__rhythm {
p {
font-size: 1em; // 12px
line-height: 1.5; // 18px
}
.class-for-24px {
font-size: 2em; // 24px
line-height: 1.5; // 36px
}
.class-for-18px {
font-size: 1.5em; // 18px
line-height: 1; // 18px
}
}
view raw _rhythm.scss hosted with ❤ by GitHub

Any elements not specified (like lists) will inherit the line-height from the body; something that needs to be included in the vertical rhythm calculations.

Check out Andrew Morton’s vertical rhythm generator. You can even base your rhythms on a modular scale.

Margins

Margins are applied using .context__margins on the parent. You only want to apply margins based on the elements’ font sizes so, when you generate the modular scale, use the same classes as in .context__rhythm but only include the margins.

.context__rhythm {
// Add a context just for adding margins but only allow it
// to be used when we're already maintaining a rhythm with line-heights
.context__margins,
&.context__margins {
p, ul, ol {
margin-bottom: 1.5em;
}
.class-for-24px {
margin-bottom: 0.75em;
}
.class-for-18px {
margin-bottom: 1em;
}
}
}
view raw _margins.scss hosted with ❤ by GitHub

You also probably don’t want to include margins when there isn’t already a ‘base’ rhythm on the elements you’re targeting. It’s easy to forget a class so the .context__margins class must a sibling or descendant of the .context__rhythm class.

Flourishes

Stylistic content — styles that don’t affect vertical layout, like bullets — applied within .context__flourish ensure that you know all of your styles have no list-styles or extra padding, borders etc. until you place them within a specific class.

.context__flourish {
ul {
list-style: disc;
margin-left: 1.5em; /* Left and right margins are allowed here */
}
}
view raw _flourish.scss hosted with ❤ by GitHub

Markup

The markup is straight forward as you simply apply a class to a container. You may end up adding some extra <div> tags with more complex projects but the befits of knowing exactly how the child elements will behave is worth the extra markup.

<html>
<head>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h2 class="class-for-24px">Reset Browser Styles</h2>
<ul>
<li><b>The ul has:</b>
<li>inherited line-height from body
<li>no margin</li>
<li>no bullets</li>
</ul>
<hr>
<div class="context__rhythm">
<h2 class="class-for-24px">24px with line-height but no margin</h2>
<h2 class="class-for-18px">18px with line-height but no margin</h2>
<ul>
<li><b>The ul has:</b>
<li>inherited line-height from body
<li>no margin</li>
<li>no bullets</li>
</ul>
<hr>
<div class="context__margins">
<h2 class="class-for-24px">24px with line-hight and margin</h2>
<h2 class="class-for-18px">18px with line-hight and margin</h2>
<ul>
<li><b>The ul has:</b>
<li>inherited line-height from body
<li>margin</li>
<li>no bullets</li>
</ul>
<hr>
<div class="context__flourish">
<h2 class="class-for-24px">24px with line-height, margin and optional styles</h2>
<h2 class="class-for-18px">18px with line-height, margin and optional styles</h2>
<ul>
<li><b>The ul has:</b>
<li>inherited line-height from body
<li>margin</li>
<li>bullets</li>
</ul>
<hr>
</div>
</div>
<div class="context__flourish">
<h2 class="class-for-24px">24px with line-heights, no margin but optional styles</h2>
<h2 class="class-for-18px">18px with line-heights, no margin but optional styles</h2>
<ul>
<li><b>The ul has:</b>
<li>inherited line-height from body
<li>no margin</li>
<li>bullets</li>
</ul>
<hr>
</div>
</div>
<div class="context__rhythm context__margins context__flourish">
<h2 class="class-for-24px">24px You can include all contexts on the same element</h2>
<h2 class="class-for-18px">18px You can include all contexts on the same element</h2>
<ul>
<li>The ul has:
<li>inherited line-height from body
<li>margin</li>
<li>bullets</li>
</ul>
</div>
</body>
</html>
view raw index.html hosted with ❤ by GitHub

Using Sass

Sass makes is easy to set contexts. The following examples are simplified to show you the principles. You can see the full gist and open in your browser to see how it works.

Maintainability

The examples shown are just to give you an idea about the methodology. You can use Sass to make things much more robust and maintainable by using maps and loops to do all the heavy lifting for you. There’s also the fact that you can use rems with px fallbacks for IE as opposed to using ems.

Have a look at a more complex example or [see it in action on emilylhardy.com][11]

Posted