Why Savvy Coders?
Why Savvy Coders?
This post is part of Savvy Tips, a series of technical posts about how to improve your skills as a developer, written by our very own Alex Pearson, Head Instructor of the St. Louis cohort.
If you’ve been in the Front End Web Development game for long, you’ve probably chosen a favorite language from the Big Three (HTML, CSS, and JavaScript). It’s been my experience that HTML doesn’t usually generate that much excitement among developers, unless they’re the types of folks to really dig berating others about semantic markup. The rest are usually divided into the CSS or the JavaScript camp. The designers, color-theorists, and beautifiers of the user interface are probably fans of delving into CSS, where the function-over-form and user experience types probably tend towards JavaScript. Obviously, both languages have their place, and are an absolute necessity for any Web Developer, but preferences usually surface pretty quickly. Me… I tend towards the JavaScript camp. While I can appreciate a visually beautiful design, I spend most of my time looking at code. And given that, the most beautiful part of any site (to me) is the elegant one-liner that describes a key feature. These are the haikus of the development world: the perfect .map, the single well-tempered and extensible Object, or the painless build process.
CSS, by default, has none of these things. Given the global scope and cascading nature of CSS rules, the solution to most CSS problems tends to be more CSS. This means that, for most stylesheets, there’s always lots of repetition, lots of class chaining (digging deeper and deeper into a specificity sinkhole), and very few ways of creating extensible configurations or shorthands for new rules. The user, of course, sees none of this. But such tedium will eventually get under the skin of any aspiring CSS guru.
in line 1, @for starts the loop by creating the placeholder variable $i and setting the range of numbers (in this case, 1 through 10).
in line 2, the baseline width value is saved to a variable (which standard CSS doesn’t have yet, but might some day have in the future).
in line 4, the .row- classes are set up using string interpolation (much like the the ${i} string literal syntax from ES6).
in line 5, we use SASS’s nesting capabilities to better illustrate the relationship between the .row- classes and their .column direct children. We could have simply written the loop over .row-#{$i} > .column{} without nesting, but this way is more extensible (in case we wanted to apply styles to other row children or to the row element itself).
This is huge for developers, and a big improvement over the standard CSS workflow. It’s hard to imagine professional CSS development happening without SASS these days (even for those firmly planted in Team CSS). But this is a post about the dangers of abusing SASS, right? So what’s so dangerous about CSS with superpowers?
The End Goal: A responsive grid system.
While grid systems like the one we saw above are nice, they’re not really suitable for responsive web development. To lock everything in to a set-width-percentage grid isn’t a great option once those screens get really small. Nor is it a great solution for setting up elements of different widths within a single row. The solution: adding breakpoint prefixes and width suffixes to our .column selectors. So .column becomes .small-column-1/2 .medium-column-1/3 .large-column-1/4 for a column that responds to small, medium, and large breakpoints by taking up 1/2, 1/3, or 1/4 of available row space (respectively).
We also want to make sure that we don’t restrict users to 1/X divisors. Let’s see if we can create a grid system that lets users choose any fractional width from 1/1 up to 10/10 (so 2/5 and 3/8 and 3/4 are allowed, but 1/12 wouldn’t be). This is a pretty standard API for grid systems, so we’ll want to include it in ours.
As you might imagine, we need to use CSS media queries to check the width of the viewport and respond accordingly. This is already supported as a part of the CSS spec, but is given superpowers with SASS’s ability to nest styles. So let’s see if we can add to our code above to add media query and prefix support.
So now we have a working grid, and we’ve saved about 10% of the file size by properly grouping our media queries. But you’ll also notice that we’ve created about twice as many column selectors as we need. In order to prune out the “super-wide” selectors (e.g. -column-4/1 with 400% width), we need to add an @if condition to our inner loop.
Setting the upper limit of our inner loop to $i restricts values of $j to less than or equal to $i, so we don’t have any more super-wide selectors any more! Hopefully you’re starting to see how easy it is to fall into the SASS-generated CSS bloat trap. With two simple refactors, we’ve reduced our initial file size by over 60%. But wait… there’s more!
The above output is generated with our nested @for loops. We had previously run these loops inside the @each breakpoint directive, but we’ve now extracted these to their own section to help make the next directive clearer.
On line 10, we try to access a list of suffixes in $suffix-map by a width label. If the map-get function (which is built into SASS) returns null, we know that the label doesn’t exist yet. The next two lines decide whether to create a new key or append a suffix to a list of pre-existing suffixes accessible by their shared width. And, finally, the generated list of suffixes is merged into $suffix-map at the proper percent identifier. This may look like overkill, but imagine doing this by hand for a multi-dimensional 24-column grid system. Or, worse, imagine doing this for a 24-column grid system, then being told that the designers would prefer a 16-column grid system. Best to create a method of generating that proper list automatically, regardless of the number of columns needed!
Now we can iterate over our map of suffixes within each breakpoint’s @media query. Line 23 turns an individual width’s suffix list into full class selectors like we’ve seen before. The standard SASS function append() uses a comma argument to create the comma-separated list that we’ll need to use these lists of classes in accordance with the CSS spec.
And, finally, we can use the exact same nested child selector syntax that we’ve used before, and our list of comma-separated child classes is automatically expanded into the proper list of full selectors. Try it out with your own project, and compare the final output with our original attempts!
While this might look intimidating (and requires a bit more up-front thought than slapping additional CSS rules onto a stylesheet), it’s an important part of using SASS responsibly! If we had stuck with our working-but-bloated example from attempt 1, we would have had 90% more CSS output for users to download. And in more complex grid systems, that extra CSS can become a huge burden for users, especially on mobile platforms. I may or may not have once added almost a full MB of additional render-blocking data for users to download by getting SASS-y without thinking about the cost of the final CSS output. This is not to say that you shouldn’t use SASS. A project like this one would be almost impossible to maintain by hand with native CSS! But always be aware of size and beauty of the final output, rather than focusing too much on the beauty of SASS itself.
Good luck, and post any questions you have to the comments!
Copyright All Rights Reserved © 2019 | Savvy Coders is proud to be lesbian owned and woman owned.
