A Modern Approach to CSS Pt. 2: Building Resources

css resources

The use of Sass on compiled themes is being deprecated, and stylesheets should migrate to native CSS features. For more information on this change and how to use Sass locally in your theme development workflow, please read our announcement blog post ondeprecating Sass

Design systems and the modern spec play a huge part in how to approach CSS problem solving today.In part oneof this series, we looked at how modern CSS tools can help us write more organized and programmatic CSS. Part two builds on those tools by looking at how we can use them to create resources for our websites. We will use Sass to auto-generate a CSS system, complete with namespacing. Then we’ll dive into navigating the CSS spec and touch on examples of how the current state of the spec helps solve common problems.

Grow your business with the Shopify Partner Program

Whether you offer web design and development services or want to build apps for the Shopify App Store, the Shopify Partner Program will set you up for success. Join for free and access revenue share opportunities, developer preview environments, and educational resources.

Sign up

Systems

It seems like everywhere you turn there is talk about usingdesign systems.There are wholeconferences about design systems, surveys reportingdesign system use and statistics, andSlack teamsdevoted to the concept. When looking at design systems from the outside, the high-level view may seem simple, but as you get closer, it becomes more and more complex. The major thing to understand is that a design system is a term for a collection of independent systems that work together to give designers and developers a way to quickly create consistent, efficient designs and code.

CSS systems are often at the heart of a good design system. Typography, spacing, layout, grid, and color are commonplace elements of a design system, but they can be used independently of a full design system. Tools like Sass and PostCSS can help automate the creation of these systems that are used in the HTML. Organizational methods like BEMIT provide a framework to structure these systems’ class names. As an example I’ll step through using Sass to automate the creation of a simple spacing system that I have used many times over the last several years. The great thing about this system is that it’s very malleable and can be adjusted to fit the needs of each particular project.

You might also like:A Modern Approach to CSS Pt. 1: Tools and Organization

Step 1: Variables and maps

When starting to create this system, it’s helpful to lay out some guidelines, essentially some docs-driven development. The goal of this system is to provide classes that can be used anywhere in our HTML to add padding or margin values to elements. These classes need to provide a quick option to addpaddingormarginto the top, right, bottom, left positions individually as well as have classes that allow for adding an equal spacing to all positions, the horizontal positions, and the vertical positions. This system ought to be auto-generated to allow a consistent output with minimal code written.

This will be a Sass file that follows the ITCSS method to organize the file, so we will name this file_trumps.utilities.spacing.scss.The naming here is how I have found organization helpful. I start the name off with the ITCSS section the CSS will fit into, then I provide a descriptive category, in this case utilities. Since it’s quite likely I will have multiple utility systems, I’ve added a further description. Being descriptive like this with your file names will help you and others find the appropriate files quickly as the codebase grows.

In this new Sass partial file we will begin by adding a variable called$spacing-limitand set the value to4

This variable is the maximum value for our spacing system. The system we write will iterate to create classes for spacing values from0to4remin1remincrements.

Next, we create a map of the positions we want to target. As mentioned before, we’ll want to target the top, right, bottom, and left positions, and provide a method for creating classes that target all positions equally, the combined top and bottom values as a vertical class, the combined right and left values as a horizontal class. As mentioned in the namespacing section, it’s important to keep naming concise yet human-readable. With namespacing in mind, we will create a key-value pair where the key is our concise name, limited to three characters, and the value will be the full name for the property. Note that since all, vertical, and horizontal, are collections of positions, their value isnull

Step 2: Incremental looping with the class names

Likea Tarantino movie, we’re going to put the ending of this demo in the middle. The reason for this is to understand what information we’re going to be passing into our loop mixin in the next step. For starters we’ll create an@forloop that will increment from0to our$spacing-limitvalue of4.This increments in whole numbers, so our classes will consist of 0, 1, 2, 3, and 4 as values in our spacing classes.

Next we’re going to create a new variable that takes the$spaceincremental value and adds the unit to it, in this case arem.Sass就像添加那么简单ing the two together.

Now we need to create our class names, or the start of them at least. Since these are utility classes, we’ll namespace them asutil-,n for thepaddingclasses we’ll usepad, andmarginwe’ll leave as is.

Our next step will take care of creating the mixin we’ll need to add in to these two selector blocks, but for now we can call the mixin we’re about to create. This mixin will be named ‘spacing-loop’, which is initiated by the@include, which includes a mixin in the selector block. Note at this point your Sass will error when generating because it will not have a mixin calledspacing-loop.There are three values we want to pass through to our yet to be mixin, that is the name of the property we are targeting as a string, so "padding" for the.util-padclass and "margin" for the.util-marginclass. Then we want to pass through the$spacevalue, as well as our unit version$value

Step 3: The positions loop mixin

Our final step is the most complex step and may require some additional reading to fully understand. For starters, let’s make the mixin and define the properties it will accept. We’ll try to keep the names the same to prevent confusion,$propertyis the property name we’re targeting, eithermarginorpadding.Then we’ll pass through the$spaceand$valueproperties from the previous step’s variables.

Now that our Sass compiles again, we will create an@eachloop that goes through our$spacing-keymap from the first step. The structure here tells the@eachto get the key and the value from the map and assign them to the variables$poskeyand$posvalue, respectively. Theposin the names refers to the position.

Next we’re going to programmatically create our class names by using a mixture of variables. The first variable isrepresented by an ampersand(&). This is a special feature of Sass called theParent Selector,and it captures the scope of the selector up to the point it’s called. In this case it will translate to.util-marginand.util-pad, depending on which selector block the mixin is running in.

The next two portions are standard Sass variables, but since they are being used in a class name, they must first be interpolated so they output safely as Sass, otherwise Sass will think the class name isutil-margin-$poskey-$space.As the@eachloop runs through all the entries of the map, it will insert the$poskeyvalue and the current$spaceiteration that has been passed down from the@forloop from Step 2. In the case of a utility class that targets all margin positions with a spacing of2,class created from this setup will read.util-margin-all-2.Note that when a Sass variable is used as a selector, it must be interpolated by wrapping the variable in a curly bracket set that begins with a hash/pound sign, for example#{$变量}

Now that our class selectors are created, it is time to add in the last bit of information, the selector block properties. Since our keys forall,vert, andhorizarenull, we need to perform and@ifstatement to detect those cases and output the correct property or properties. The first check will see if$poskeyis equal to "all", in which case it will grab the$propvalue of either "margin" or "padding" and output the$value

Note that when using a variable as a property name, it must also be interpolated. Anytime a variable is outside of being called as the value as a property, it must be interpolated. We can repeat this check forvertandhorizand create the appropriate properties. Once we have gone through all our if conditions, we can wrap up with an@elsecondition that will grab the$posvalue, i.e. key value, for the appropriate iteration, in the case of$poskeyequaling "right" the$posvaluewill outputright

Pulling this all together is roughly 50 lines of Sass that will output 70 unique spacing utility classes. These classes can then be put to work rapidly spacing out content on your site by adding the appropriate class for the amount of spacing needed. The classes created by this system have tremendous browser support as it uses long-standing properties and approaches. As older browsers become less necessary to support, we can start expanding on this system with the use ofCSS Custom Properties, asKasey Bonifacioshows in her articleManageable Utility Systems with CSS Variables

You might also like:Reactive UI Animations with CSS Variables

The CSS spec

When it comes to writing modern CSS, we have to come back to theCSS specitself. TheCSS Working Grouphas taken many cues from the last decade of web development trends and practices to heart. Over the last few years CSS has gainedfeature queries,custom variables,math capabilities, and unique ways to identify elements. And lest we forget all the layout options we have withFlexboxandCSS Gridin the mix.

With all these new features it can be difficult to know where to start. My day-to-day work involves referencing theMDN Web DocsandCan I Usewebsites regularly. There are other resources available, but these two have become my development companions. It isn’t necessary to know every property, but knowing how to navigate which property to use at which circumstance is an important skill. Simply exploring these two websites can reveal a lot about the options available to you as a front end developer.

I want to leave you with three things specific to CSS that I have really come to rely on. These things are—unlike all the fancy tooling and methodologies that have been discussed so far—pure CSS.

Progressive enhancement and feature queries

CSS出色的特性之一是prope的方式rties become more important the further down the list they are. This means that when CSS properties are well-ordered, they can provide specific styles for specific browsers without the need to rely on JavaScript-based feature detectors such asModernizr.For example, when writing layout styles, start off with floats, then add in Flexbox, and only then add in CSS Grid. This will allow browsers like Internet Explorer 9 to get the float-based layout.

Here is a quick example. Let’s start with the older layout method of floats, and what we’ll need to accomplish it. In this example, I’ll be usingdisplay: inline-blockas the method to clear the floated elements.

With IE 10 and 11 along with older versions of modern browsers, we can add in the Flexbox version. But, since we want this to beprogressiveand flow with the cascade, we append like properties below the version for older properties:

It’s pretty incredible that all we need to add isdisplay: flex;and these classes are now using Flexbox. The properties around Flexbox negate the floats in the children classes, but the widths are still used. This works because the natural state of a Flexbox is to keep all the children elements on a single line, and our widths on the children allow the space to be filled fully, without concern of the second child wrapping below the first.

The last step in the enhancing example is to add CSS Grid. Although the parent class properties are as simple as addingdisplay: gridand thegrid-columns-templateproperty, a problem arises with the widths of the children. Unlike the float and Flexbox approaches, CSS Grid widths are determined by the parent via thegrid-columns-template.孩子们现在将百分之一的宽度age of the column width. To remove these widths, we can add in a feature query to check if CSS Grid is supported with@supports (display: grid)

As you can see,@supportsworks just like@media, and a feature query can be nested within a media query as well. One thing to note when using this approach is that if you are using Autoprefixer to automatically create vendor prefixes, it will create a vendor prefix ofdisplay: gridfor IE 11 asdisplay: -ms-grid.While IE11 has a very early implementation for CSS Grid, it does not have any support for feature queries. This can cause issues as grid properties are added to the element, but the widths can’t be controlled. In this case, it’s better to group thedisplay: griddeclaration within the feature query as well, and force IE 11 to use the Flexbox approach.

You might also like:What is Progressive Enhancement and Why Should You Care?

Consider using alternative selectors

When we talk about CSS naming methodologies and organization, it’s almost always regarding the use of class selectors. It’s understandable why, since class selectors are a defined selector type that can have multiple values, an aspect that is heavily relied on. However, there are many other ways to target an element that provide a level of conditional-like logic to CSS selectors. Two in particular, which I have found work powerfully together, are attribute selectors and the:not()selector.

Attribute selectorshave been around since CSS 2.1 with support back to IE 7. They are useful in tracking down and stylinginputelements bytype, such as ainput[type="radio"]to select radio buttons. They can also be helpful in tracking down a particular value in a space-separate lis in a data attribute, with something like[data-date~="tuesday"].Recently, I’ve seen an excellent use of attribute selectors with thearia-expandedattribute to check if an element is expanded or not and provide the necessary CSS to hide or show the element.

The:not()pseudo-class is a CSS3 addition that allows a selector scenario to be excluded from applying styles. This comes in handy as a way to apply styles when a particular state-indicating class is not present. For example, if we want to have specific styles for a button when it doesn’t have an icon present, we can write a selector like this:.button:not('.has-icon') {...}.At first it may not seem like a worthwhile approach to use:not(), but I have found once it’s considered an option, the problem it solves reveals itself.

An approach I have commonly taken over the last year or so is to combine these two selectors to create styles for generic elements. When writing generic element selectors, such asporaorh4, those selectors will affect the outcome of every instance of those elements. Element selectors are generally frowned upon because of this cause, and any class that is applied to one of those elements in the DOM will require extra CSS to counteract the styles of the element.

However, it’s terribly convenient to have default styles for elements that might show up without a class. This has led me to include element selectors that do not have a class by adding:not([class])to those selectors. This allows generic elements to get custom styling but not to the detriment of class selectors. Ap {...}selector becomesp:not([class]) {...}and now won’t come in conflict with the styles specific to this class used in the DOM:

...

You might also like:Using CSS Animations to Guide a Better Ecommerce Experience

Play with CSS

The most important thing that I have learned in my years as a CSS developer is that knowing CSS requires play time with CSS. This idea became quickly apparent when I started usingCodePen在2012年。我开OB欧宝娱乐APP始通过一个animation of an oscillating fan, as a way to try nested element animations. From there I have found the best way to play with CSS is to take a challenge and add restrictions.

"The most important thing that I have learned in my years as a CSS developer is that knowing CSS requires play time with CSS."

Go through and try one of the CSSdailychallengesout there, but add your own stipulation to the challenge. Try a new property, force yourself touse only one HTML elementto complete the task, or see if you can complete akata, likeFizzBuzz, withCSS alone.When you play with CSS, you learn more than if your usage is limited to only the needs of a project.

CSS as a bridge

CSS is a bridge between design and development, resulting in a language that is unique, capable, and sometimes underestimated. The rich and complex abilities allow for experienced developers to create truly fascinating styles. At the same time the language is still simple and approachable as an introduction to web technologies. These characteristics of CSS require a particular mindset when writing CSS that is neither design nor programming, but somewhere in the middle. The technology has experienced robust expansion over the last several years and there is no sign of slowing.

Sass和节点带来programmi等技术ng and automation to the language, and provide concepts programmers understand that can cause confusion. In return, the CSS spec has been expanded to incorporate ideas introduced by CSS tools, adding feature detection, math, and variables. Despite these further enhancements of adding a programming layer to CSS, the language still requires a specific mindset that keeps the cascade functionality central to developing efficient and practical styles.

Grow your business with the Shopify Partner Program

Whether you offer web design and development services or want to build apps for the Shopify App Store, the Shopify Partner Program will set you up for success. Join for free and access revenue share opportunities, developer preview environments, and educational resources.

Sign up

Grow your business with the Shopify Partner Program

Learn more