Optimise your CSS by taking better care of your HTML
Published on
Semantic HTML for cleaner CSS, this post looks at the role HTML can play in producing more optimised CSS.
Published on
Semantic HTML for cleaner CSS, this post looks at the role HTML can play in producing more optimised CSS.
Published on
This post looks at the role HTML can play in producing more optimised CSS. When discussing issues with CSS bloat, specificity and selector limits, focus is often drawn to the misuse of Sass. It’s true that refactoring your Sass can go a long way however paying attention to your website markup can be just as effective.
Going back to basics and creating meaningful class names could dramatically improve your website performance. Naming classes effectively can aid modular CSS coding and will therefore reduce CSS bloat and help control the number of selectors used (Necessary for large sites due to the IE selector limit).
The best way to reduce selectors is to target tags individually rather then using the DOM structure as a guide. Take the following navigation example…
<nav class="navigation-main">;
<ul>
<li class="navigation-main-list"><a href="" class="navigation-main-link">home</a></li>
<li class="navigation-main-list"><a href="" class="navigation-main-link">about</a></li>
<li class="navigation-main-list"><a href="" class="navigation-main-link">blog</a></li>
<li class="navigation-main-list"><a href="" class="navigation-main-link">contact</a></li>
</ul>
</nav>
Bad example…
nav {
width: 100%;
}
nav ul li {
list-style-type: none;
float: left;
}
nav ul li a {
display: block;
width: 120px;
padding:10px;
text-align: center;
background: green;
color: white;
text-decoration: none;
}
nav ul li a:hover {
background: grey;
text-decoration: underline;
}
Good example…
.navigation-main {
width: 100%;
}
.navigation-main-list {
float: left;
list-style:none;
}
.navigation-main-link {
display: block;
width: 120px;
padding:10px;
text-align: center;
background: green;
color: white;
text-decoration: none;
}
.navigation-main-link:hover {
background: grey;
text-decoration: underline;
}
In the second example the styles are not effected by the HTML structure making the code a little more future proof. We have also avoided using generic element selectors like nav, ul or li reducing the risk of effecting other areas of the website unintentionally. Most importantly though the second example uses significantly less selectors and will load faster as a result.
Markup should also be free of specific style or grid related class names such as .eight-columns or .grid-6, classes like these have become commonplace in responsive grids and frameworks but become meaningless at smaller breakpoints….
@media (max-width: 480px) {
.eight-columns {
width: 100%;
}
}
A major issue with this markup is that it restricts how a layout adapts to different screen sizes. Any element with the class .eight-columns will be forced to take on the same styles as the site responds. For example in the layout below there are two rows each with three blocks. As the class names will all be .four-columns there is no way to differ the behaviour of each row at tablet or mobile size.
Lets say we wanted to change how the two rows above respond at the mobile breakpoint we would need to target them separately using parent selectors…
@media (max-width: 480px) {
.header .eight-columns {
width: 100%;
}
}
This introduces extra selectors and also creates DOM reliant styling making it difficult to re-use across the site.
A better solution would be to use class names relevant to the content for example .news-feed, .social-links or .main-cta and include the grid CSS from a mixin. Doing so has many benefits including…
.logo,
.search,
.social {
@include grid(span-3);
}
@media (max-width: 480px) {
.logo,
.search,
.social {
@include grid(span-12);
}
}
Being too content unique with your class names can however be equally damaging. In order to successfully minimise your CSS a modular approach is needed, creating re-usable blocks of code will keep file size and selectors down as well as being easier to maintain.
Naming every element uniquely will create repetitive CSS…
.primary-button {
display:block;
color:$white;
background: $orange;
font-size:14px;
text-align:center;
padding: 10px;
width:150px;
}
.secondary-button {
display:block;
color:$white;
background: $blue;
font-size:14px;
text-align:center;
padding: 10px;
width:120px;
}
One solution to this is would be to use @extend to copy styles, however if overused @extend can create long, messy selectors creating more issues then it solves…
%btn {
display:block;
color:$white;
font-size:14px;
text-align:center;
padding: 10px;
}
.primary-button {
@extend %btn;
background: $orange;
width:150px;
}
.secondary-button {
@extend %btn;
background: $blue;
width:120px;
}
The CSS output here will be…
.primary-button,
.secondary-button {
display:block;
color:$white;
font-size:14px;
text-align:center;
padding: 10px;
}
.primary-button {
background: $orange;
width:150px;
}
.secondary-button {
background: $blue;
width:120px;
}
Using a Base Class and a Modifier Class in your markup is a much cleaner way to re-use styles, reduce selectors and keep file size down…
<a class="button primary-button"></a>
<a class="button secondary-button"></a>
.button {
display:block;
color:$white;
font-size:14px;
text-align:center;
padding: 10px;
}
.primary-button {
a: $orange;
width:150px;
}
.secondary-button {
background: $blue;
width:120px;
}
In this example styles from the base class are re-used without creating long selectors. Styles can also be easily adapted using the modifier class without the need to use !important or to add specificity with a parent selector.