2015-08-26-css-that-doesnt-suck.md 10.5 KB
Newer Older
1 2
---
layout: post
3 4
title: CSS That Doesn't Suck
subtitle: _Make writing CSS not suck with this one weird trick. Web developers hate him!_
5 6 7 8
tags: [css, scss]
published: True
---

Connor Shea's avatar
Connor Shea committed
9
If you've ever done web development, or worked with web developers, you've probably heard that CSS is the bane of our existance. To the contrary, I've actually begun to really enjoy using CSS, it just took a lot of figuring my way around things.
10

11
{% include image.html url="/images/posts/css-that-doesnt-suck/css-in-a-nutshell.gif" caption="Most people's experience with CSS."%}
12 13 14

This article covers some of the things that have helped me to make CSS not just tolerable, but even – dare I say it – enjoyable to use.

15

16 17 18 19 20 21 22 23 24 25
### box-sizing: border-box

I include the following in every website I develop, and I've never had problems with it.

{% highlight scss %}
*, *:before, *:after {
  box-sizing: border-box;
}
{% endhighlight %}

Connor Shea's avatar
Connor Shea committed
26
By default, browsers use `content-box`, this rule sets the `box-sizing` mode to `border-box` for all elements and pseudo-elements (`:before` and `:after`) on every part of the website. With `box-sizing: content-box`, the dimensions of an element can grow to accomodate padding, whereas `border-box` retains a fixed height and width with padding being "inset" within the element.
27 28 29

The best way to explain how the two differ is with an image.

30
{% include image.html url="/images/posts/css-that-doesnt-suck/content-box-vs-border-box.png" caption="The difference between `content-box` and `border-box`." %}
31 32 33

If you're using third-party plugins, scripts, etc. this may cause their layout to break, but it can be overridden on a case-by-case basis as necessary by applying `box-sizing: content-box` to the problematic element's respective CSS selector.

34

35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
### Use a CSS pre-processor

Currently, the two largest CSS pre-processors are SCSS and LESS. Of the two, I would personally recommend [SCSS][sass-lang], as LESS has some weird quirks like `calc()` not always working as expected because LESS performs the math when processing your stylesheets. So `calc(100vw - 20px)` in LESS is delivered as `calc(80px)` to the browser. Other than that, they have essentially identical feature-sets.

Why use a pre-processor? They make CSS a _lot_ simpler, and therefore faster to both write and read.

You can take code like this:

{% highlight css %}
.container {
  display: block;
  text-align: center;
}

.container li {
  display: inline-block;
  padding: 5px;
}

.container li a {
  font-weight: 500;
}

.container li a:hover {
  text-decoration: none;
}
{% endhighlight %}
62

63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
And rewrite it like this instead:

{% highlight scss %}
.container {
  display: block;
  text-align: center;

  li {
    display: inline-block;
    padding: 5px;

    a {
      font-weight: 500;

      &:hover {
        text-decoration: none;
      }
    }
  }
}
{% endhighlight %}

With vanilla CSS, you're not able to nest selectors. SCSS and LESS allow you to do this and a lot more, and they provide no performance hit for the user. CSS written with a pre-processor are "processed", or compiled, down into vanilla CSS by the server, and that CSS is sent to the client for parsing.

Besides nesting, SCSS also includes variables, functions, mixins, and color modifiers.

Variables are represented by a `$`, and can be used anywhere in a stylesheet after being defined.

{% highlight scss %}
$brand-color: red;
$header-width: 100vw;

.header-text {
  color: $brand-color;
  width: $header-width;
}
{% endhighlight %}

For more complex websites with more than one stylesheet, you can use `@include` to include variables from a distinct `_variables.scss` file.


{% highlight scss %}
/* _variables.scss */
$brand-color: red;


/* header.scss */
@include 'variables.scss';

.header-text {
  color: $brand-color;
}
{% endhighlight %}


Also note that there's a difference between "Sass" and "SCSS". "Sass" doesn't include any brackets or semicolons, it looks like this:

{% highlight sass %}
.container
  display: block
  text-align: center

  li 
    display: inline-block
    padding: 5px
{% endhighlight %}

Personally, I prefer SCSS because it's closer to vanilla CSS, so you'll be able to more easily follow CSS tutorials, better understand open source examples, and be able to write vanilla CSS or use other pre-processors should you ever need to. I also personally find SCSS easier to read, with an easily understood, well-defined visual hierarchy.

More information about using SCSS and its features is available in [the SCSS documentation][scss-documentation].
133 134 135 136


### Avoid using float

137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
I'm sure others will disagree with me on this, but I have tried to avoid using `float` in almost all my projects. Those that I have used `float` in have had their fair share of problems because of it. Perhaps my problems stem from ignorance, in which case I'd love to be told how to correctly use floats.

When writing stylesheets I'd prefer to write once and have it work everywhere. Of course, that isn't usually a reasonable expectation. When using flexbox for layout, it's at least made relatively simple, with only a handful of properties to modify if you've set things up correctly. When using `display: flex`, the `float` property will no longer work for elements within a flexbox container.

With flexbox, modifying a list of "cards" that fill the full width of the page on mobile to become listed horizontally and overflow into more rows as screen size increases is as easy as:

{% highlight scss %}
.cards {
  display: flex;
  flex-flow: row wrap;
  justify-content: center;
}

.card {
  width: 100%;
  max-width: 300px;
}
{% endhighlight %}

No need to change anything between platforms for this to work, it just does. If your layout requires using a lot of `float`, using flexbox becomes significantly more difficult.

A common reason to use `float` is to align content to the right side of the page. However, this brings its own complications.

160
Let's say you have a list of navigation elements:
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185

{% highlight html %}
<header>
  <ul class="header-left">
    <li>Home
  </ul>

  <ul class="header-right">
    <li>Messages
    <li>Notifications
    <li>Profile
  </ul>
</header>
{% endhighlight %}

If you apply the following CSS, the expectation would be that Profile is the right-most element, because it's listed last in the HTML, correct?

{% highlight scss %}
.header-right li {
  float: right;
}
{% endhighlight %}

The problem is that `float` actually places "Messages" as the right-most element, then "Notifications" and "Profile" are placed to the left of "Messages". You could fix this by applying `float: right` to the `header-right` class instead of the list items themselves, but what if you wanted to add a search box to the inside of the right side, certainly it wouldn't go inside the `header-right` element? It isn't a list item like the other navigational elements. So you'd need to place it outside of the unordered list, and apply `float: right` to it as well. However, it would have to go _after_ the `header-right` element if you wanted to place it on the inner side of the right-aligned navigation elements.

186
The point I'm trying to make is that the `float` property tends to cause unnecessary complexity and unexpected results. As with all things, there are exceptions to these rules. Float can be very useful in articles if you're trying to get text to flow around a picture, for example. That said, I believe that – as a means of aligning items in a layout (i.e. a navigation bar, header, etc.) – there are many means of achieving this which simply work better.
187 188 189 190


### Understand selector specificity

191
I won't cover this myself, as others have covered it better than I can, but Shay Howe's [getting to know CSS][getting-to-know-css] guide is great for this. This is an incredibly important concept to understand.
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222


### Always avoid using `!important`

On the topic of selector specificity, `!important` is a common way of hacking around the specificity hierarchy for newer web designers and developers. I would advise _strongly_ against this practice, as it can become a nightmare when misused.

The key thing to understand with regards to `!important` is that it breaks the delicate specificity system. It overrides all style rules regardless of their specificity. The only rules capable of overriding an `!important` declaration are in-line styles _with `!important` declarations themselves_.

For example:

{% highlight scss %}
.colored-text {
  color: red !important;
  text-align: center;
}
{% endhighlight %}


{% highlight html %}
<div class="colored-text">This text will be red.</div>

<div class="colored-text" style="color: green !important">This text will be green.</div>
{% endhighlight %}


{% raw %}
<style>
  .colored-text {
    color: red !important;
    text-align: center;
  }
Connor Shea's avatar
Connor Shea committed
223 224 225 226

  .colored-text:last-of-type {
    margin-bottom: 20px;
  }
227 228 229 230 231
</style>

<div class="colored-text">This text will be red.</div>
<div class="colored-text" style="color: green !important">This text will be green.</div>
{% endraw %}
232 233


234 235 236
Except in the cases of `!important` and in-line styles, there will always be a way to override another selector's properties. `!important`, especially when a site becomes larger, with hundreds or thousands of different CSS classes and IDs, using `!important` will only cause headaches down the road.


Connor Shea's avatar
Connor Shea committed
237 238
### Wrapping up

Connor Shea's avatar
Connor Shea committed
239
I want to note that I'm not saying CSS is infallible and some great style language. It should have sensible defaults, but sometimes it doesn't – usually for backwards compatibility reasons. Ideally it wouldn't require experience with every CSS property to throw together a relatively simple website, but I'd challenge _you_ to develop a stylesheet language that works in as many use-cases as CSS does, as well as it does.
240

Connor Shea's avatar
Connor Shea committed
241
If you want to learn more about web development, I'd definitely recommend checking out these links:
242 243 244

* [The Codrops CSS Reference][codrops-css-reference] - Easily the most useful CSS reference I've found. Goes in-depth on every CSS property I know of.
* [The Mozilla Developer Network's CSS section][mdn-css] - Mozilla is the company that develops Firefox. They also host a developer wiki that provides documentation, tutorials, and example code for a huge number of HTML, CSS, and JavaScript features.
245
* [Learn to Code HTML & CSS][learn-to-code-html-css] - A great introduction to web development, especially HTML and CSS.
246

247 248 249 250
[sass-lang]: http://sass-lang.com
[scss-documentation]: http://sass-lang.com/documentation/file.SASS_REFERENCE.html
[codrops-css-reference]: http://tympanus.net/codrops/css_reference/
[mdn-css]: https://developer.mozilla.org/en-US/docs/Web/CSS
251
[learn-to-code-html-css]: http://learn.shayhowe.com/
252
[getting-to-know-css]: http://learn.shayhowe.com/html-css/getting-to-know-css/#cascade