The Power of Sass
Paige Ponzeka
Today I'm going to talk to you about Sass. A simple and lovely front-end technology that makes our lives much easier. We use it at Yext to make writing and maintaining CSS less terrible.
Writing Vanilla CSS Sucks
Writing Vanilla CSS Sucks. It's repetitive. You have to write the same classes over and over again. Which is also time consuming. If you need to change the color of something? Well Hopefully you used consistent hexidecimal colors, because you're only option is to use Find & Replace in a text editor. There's no concept of variables in CSS. Trying to maintain vanilla CSS on a large platform such as storm would be an absolute nightmare. Fortunately, they've come up with some solutions to the CSS maintenance nightmare and one of those solutions includes Sass.Sass is a CSS preprocessor
Sass is a CSS preprocessor. It's a scripting language that extends CSS. I like to think that it makes writing CSS not suck by turning it into a programming language. Sass Gives CSS the power of nesting, variables, functions, mixins, & inline partials. And those features let you write so some pretty cool shit.Sass Nesting
Nesting in Sass makes your CSS easier to read and maintain. It gives you a visual hierarchy. So you can go from something like this table css. To this Table Sass. Not only is the Sass a bit shorter than its CSS counterpart. You'll notice in the CSS file we had to write the rounded-table class a total of five times. In our Sass file we just needed it once and the Sass compiler will handle the rest. The Inception Rule
Don't go more than four levels deep
There's a problematic side to having convinent nesting. You need to police yourself and be careful to limit your nesting. You shouldn't nest over 4 times otherwise you will create overqualified CSS that will be hard to override and maintain later on.
What is an overqualified selector?
CSS = Cascading Stylesheet
p {
color: blue;
}
p {
color: purple;
}
I just want to take a second to explain what an overqualified selector is since I'm not sure everyone knows what the means. To understand why an Overqualified selector is bad you need to understand a little bit about how CSS works. CSS stands for Cascading Style Sheet. The important thing to note here is the "Cascading" part. It means that if you have two equal selectors in a stylesheet. Such as in this snippet of CSS. The later declared selector is applied. So here all of the text in our paragraph tags will be purple since it is declared after blue.
Selector Values
Source: https://css-tricks.com/specifics-on-css-specificity/
You can think of CSS selectors as a sort of point system. The selector with the most points gets their styles applied. And different types of selectors have different weights. If you're interested in learning more about this I recommend Checking out this article at css-tricks.com on CSS selector values and their weights. I got this little diagram from over there since I thought it explained it fairly well. Looking at it note that it goes from left to right with the most specific or values with the most points on the left and least specific values (less points) are on the right. Also Note a "Style Attribute" is also called an inline style. It's simply styles that are declared directly in the HTML. But you don't have to worry about those since you should never do this UNLESS you are setting an element as display none for javascript reasons or styling an email. But anyway looking at this selector you can see it has a weight of 0, 1, 1, 3. Which means it has no inline styles, has a single ID (#nav) one calss (active) and 3 elements (ul, list-item and link) so if it was to be overridden it would need a score of 1,1,3 or more. Which is already a pretty hefty selector.
& - Ampersand
Ampersand is another Sass directive used when nesting. It tells Sass to pull down any parent selector into the child selector. In this example all the pseudo class children hover, visited and active are nested inside of an link tag and have an ampersand infront of their declarations. This will compile to the following CSS where it pulls down the parent link tag to be in front of its pseudo class children.Nested Selectors don't have to start with &
But Nested Selectors don't have to start with an Ampersand, they can also end with it. Putting the ampersand at the end like we're doing in the .child selector will still pull the parent class down. The compiled CSS will look something like this.
_partials.scss
Sass also has the ability to use inline imports to help us organize our SCSS files into partial files just as we do with our html templates. Partials are generally, denoted with an underscore which just tell the compiler not to generate that partial into a CSS file. The underscore isn't as important for us since our Sass files are compiled explicitly in Play. Partials also lets us import shared content across multiple files, as shown by the lib/meta file here which is a partial that contains all our System wide variables, mixins, and compass imports. I'll talk More about Compass later.
Functions
I actually couldn't find an instance of a function being used in our Storm Sass. But It's similar to another directive we use heavily called a mixin. Functions return a value. There are already some build in Sass functions that can be pretty useful.
Sass Functions
You can find the documention for the built in functions at sass-lang.com. It can do basic mathematical operations such as addition, subtraction, division etc. You can see it also has some rounding functionality and ability to convert numbers to percentages. There's also some String and color functions that can be helpful. For example: say if you wanted to lighten or darken a color for an accent. You can just pass in a color and a percentage you want to darken the color and it will generate the darkened hexidecimal value for that color. I actually use those a lot to generate accent colors in my personal projects.
The Power of Variables: A Scenario
So here's a scenario were Sass can make your life much easier. Say, after spending a number of late nights at the office. Yext CEO, Howard has decided that, for some reason, he feels really inspired to remove all of the colors in Storm and replace them with various shades of gray. Say... Maybe 50 Shades og Gray.
If all of our code was written in CSS and we didn't use variables for our colors. We would have to find and replace all instances of the hexidecimal colors. You'd have to manually comb through hundreds and maybe thousands of lines of code.
So, This actually happened
So this actually happened at Yext a year or so ago. I don't really remember when it happened since I try to block it from my memory. We actually were using Sass at the time but we didn't assign any of our colors to variables, instead we just used the hexidecimal representation of the color everywhere. So I had to Comb through thousands of lines of CSS, find every instance of a color and change it to another color. I spent a pretty long time changing one shade of gray to a slightly different shade of gray. It was tedious. But I made sure when I did it I added variables to our color assignments. You can see the colors variables in the _color.scss files. Some are shown here. Also Note that I named the variables to describe what color they represent. This is useful particularly since not many people can just look at a hexidecimal value and know its corresponding color.Naming Conventions
Using all of the color's names as variables throughout our Sass wouldn't be very useful if say we needed to change all the colors later. So for usability for other developers initial variables are named after their colors but when they're used they should be given more generic names. For example looking at this we can tell that Storm's representative color for location-cms is the yext blue, for listings is green and pages is purple. These colors are used in the navigation. So if we wanted to change location-cms on the navigation to a gray we can just change it here in the colors.Sass partial.
And if we change again?
Since we've used variables to declare the colors in our Sass. If and When Howard ever feels that itch of inspiration again. We can change all of the colors of storm by just changing a few lines in the _color.scss partial, recompiling the CSS and deploying our changes. Let's Refactor
We know some of the basics of Sass. Now lets refactor some living and breathing Storm Sass code using some of the basics we already know and we can also show off some of the cooler and more powerful features of Sass.Here's an example of existing code from Prescence builder. Let's take note of a couple things. It looks like we're declaring a few variables for the background image of a few classes. Specifically a full-star, half-star and empty-star and it looks like the images change slightly depending on its container. There's some simple Repetitive Code here. But We can fix that using a feature in Sass called a Mixin.What is a @mixin?
Mixins are created using the @mixin directive. It can include required or optional arguments, and it can even take blocks of css.Storm Mixins live in the _mixin.scss partial. Now, let's take a look at our new Mixin for the prescence builder code.
@mixin
You can see in the prescence-builder-star mixin I've added several arguments declared just like variables with a dollar sign. The $full-star variable ( which lives at the end of the arguments since it's optional). Has a default value which is set to $iconStarBlue, that way if we don't set it in any of our calls it will automatically be assigned. We also have required variables $half-star and $empty-star. @include the Mixin
Now we can just include our mixin in our prescence builder Sass. We can call the mixin with @include declartion and send it our variables just like you would call a function. Loops
@for, @while & @each
$num: 4;
@while $num > 0 {
.module-#{$num} {
content: "#{$num}";
}
$num: $num - 1;
}
Sass also supports loops which can come in handy. Sass currently supports a for loop with the @for notation, @each loop and @while. I'm going to demo a @for and @each but here's an example of how to use a while loop in Sass. Also just note that pound sign bracket notation for the module dash $num in the selector is string interpolation. It actually comes from Ruby. So its just going to result in module dash 4, module dash 3 etcRefactoring with @for
Using a for loop to can limit this Stars Sass to just a couple lines. The refactor is pretty straight forward here. It loops from 1 to 10 and generates the star value and the calculated width of the element in multiples of 8.Refactoring Partner Icons
Here's a more complicated one and a much larger refactor. This is over a hundred lines for the Partner Logos on the Social Page, this code is located in the _icon sass partial. There's two different sized sets of Icons, 20px and 30px that include all of the same partners. They've followed a naming convention for the files, which is really nice actualy. That will make this a really quick refactor.Refactoring Partner Icons - Map
Sass also supports maps which are used in @each loops. Here is a Simple map we can put in the _vars sass partial. It just maps the partner Ids to their respective names then we can use a loop in our mixin to generate our logos.Refactoring Partner Icons - Mixins
This is the partner-logos mixin that could live in the _mixins.scss partial. The only thing it needs in this mixin is the dimensions of the logo, it uses the partners variable map and an each loop to iterate though the map and generate the logo class based on the id and the file name nased on the partner name and its dimensions. Also you might notice there's a background-position set only when the dimensions are 30 pixels thanks to an if statement.
Refactoring Partner Icons - Includes
And here's the call we make to generate each of the logo sizes. Cutting down a few hundred lines and making it much easier to add additional partner icons later but just updating the partners map.
@extend
Let's take a break from refactoring to take a look at another powerful Sass feature.
@extend is another nifty directive in Sass. It allows you to share rules between selectors. Extended selectors are grouped together.
We use @extend heavily in the Storm buttons
You can see it in action here in the _buttons partial. You might ask yourself why do you have btn-orange and btn-blue if all of the storm buttons are gray. Well remember that 50 Shades of Grey scenario i mentioned earlier? An old branding of Storm had different colored buttons for different actions. When we rebranded we got rid of those buttons and shifted to all gray buttons (minus the warning button which is red). To keep compatibility with old styles and to potentinally keep from breaking any javascript or css that might happen to use those styling classes for their code I kept the classes around and just inherited style from other buttons.
(Also for the record, never use a styling class as a JQuery selector, whether or not I want a dark gray or light gray button shouldn't also affect whether the buttons runs an ajax call or not.)Compiled CSS using @extends
Anyway back to some CSS.
I deleted some of the actual compiled CSS just so you can see what's going on but here's an example of compiled CSS from the @extend directive. Here you can see that .btn-action and .btn-orange are now side by side in the CSS. This is BASICALLY what @extend does. It creates the idea of inheritence in Sass but instead of duplicating the CSS and having it in both places (which we could simply achieve with a mixin), it comma seperates the original selector and applys the styles more effiently. Which is Great. Except.
@extend does bad things sometimes
@extend does bad things sometimes.
It doesn't always work the way you expect it to. It can lead to generating some pretty hefty selectors that bloat your CSS. Let's take a look at one example.Styling the button later
Say, after using @extend on the action-button class. We wanted style the button later on a particular page. Maybe we had a sidebar and the button needed padding for some reason and I also wanted to change what it does when the user hovers over it. Obviously you would do something like this and just make .btn-action a child of sidebar and add the custom namespaced styling. Does anyone have an idea what this would compile to?Compiled CSS:
It generates into something like this, which is a bit unexpected and if you need custom styles on your extended button class often or if you extend it in a lot of places, these selectors will grow larger and larger. Which is what we don't want.What can we do about this?
One fix is to just turn the shared styling into a mixin and include it on both classes. But That will duplicate the CSS on both selectors. Which will still lead to bloated css. Lucky for us, the creators of Sass introduced a new directive to address this very problem with @extend. Meet Placeholders.
%Placeholders
Instead of using a period infront of the extended class, you can use the percent sign and create to create a placeholder, that prevents the selector from being inserted into the CSS. Now only extended selectors will be included in the CSS. Here's an example where I created %btn-action-pl, a placeholder that is extended by both btn-action and .btn-orange.
Compiled CSS:
This compiles to the above CSS.
That's a little bit more of what we expected. Notice that .btn-orange is no longer called in the code for the sidebar. We actually currently don't use placeholders in Storm buttons but we should probably consider it.
Compass
compass-style.org
Now on to compass. Some people call it the jQuery of CSS. Which is ehhh kinda of truish. I guess. But it's a bit more general then that. It's a library of SCSS functions, mixins and patterns that make it easier for getting started with Sass. You can read the documentation and all the available mixins over at compass-style.org. I'm not going to spend much time on it since the Compass documentation is pretty extensive and actually really nice so you can go check out everything you can do over on their site but I'll show you an example of some cool little functions it has built in. opposite-position
$direction: left;
$opposite: opposite-position($direction);
.div1 {
float: $direction;
}
.div2 {
float: $opposite;
}
Take a look at opposite-position which takes in a direction in this case we declared direction as left and returns the css equivalent of the oppostive which is right in this case.
opposite-position Compiled CSS
.div1 {
float: left;
}
.div2 {
float: right;
}
Here's what it would compile to. It can also take in the CSS value Top Left and will Return Bottom Right.
Experimental
SCSS
.element {
@include experimental('animation', highlight 1s linear, webkit, moz, o, ms, not khtml);
}
Compiled CSS
.element {
-webkit-animation: highlight 1s linear;
-moz-animation: highlight 1s linear;
-ms-animation: highlight 1s linear;
-o-animation: highlight 1s linear;
animation: highlight 1s linear;
}
Another nice little helper is the experimental mixin which comes in handy for writing Experimental CSS3 features (which need the browser specific prefixs). And as you can see by the output they are very annoying to write and update.Branded Spinner
I actually used experimental in the branded spinning loader you might have seen around storm. Compass especially made my life easier for development of this since I only had to change values once when tweaking the timing of the animation. Unfortunately since it uses CSS3 animations and transformations this little spinner doesn't work in some versions of internet explorer.How most Engineers feel about IE Users
But thats okay. If you're using IE you probably have much bigger problems than whether or not you see an animated Logo.Resources & References
Here's some references and resources for you to check out if you want to learn more. You can go to Sass-lang.com for Sass documentions. Compass-style.org for the Compass documention (which is actually really great and really easy to use) they have code snippets and show you the compass mixin and then CSS it compiles to in little tabs alone with previews of the generated html results. And if you ever want to play around with some Sass and compass to see what it will compile to, check out Sass Meister. It even lets you import Sass frameworks such as compass if you adjust the little settings cog in the toolbar. It's very handy and I used it a lot to generate code for this talk. You can actually connect it to your github account (that's the little dino in the corner) and save your experiments as gists (or ghists if you're Breezy) to your account so you can look at them later. Questions?
Now I will go back to the gif of the IE user and the baby and see if you have any questions
The Power of Sass
Paige Ponzeka
Today I'm going to talk to you about Sass. A simple and lovely front-end technology that makes our lives much easier. We use it at Yext to make writing and maintaining CSS less terrible.