background

  • Since the birth of CSS, the basic syntax and core mechanism has not changed substantially, its development is almost all the level of performance improvement. At the beginning of the ROLE of CSS in the web page is only auxiliary decoration, light and easy to learn is the biggest demand; However, the complexity of web sites is no longer comparable, and native CSS is becoming more and more difficult for developers
  • When a language’s capabilities are inadequate and the user’s operating environment does not support other options, the language becomes a “compilation target” language, where developers choose another higher-level language to develop in and compile to the underlying language for actual execution
  • So in the front-end domain, CSS preprocessors came into being, and CSS, the ancient language, “re-adapted” to the needs of web development in another way

flowers

  • CSS preprocessor is a program that allows you to generate CSS using the preprocessor’s own unique syntax
  • There are many CSS preprocessors available on the market, andMost CSS preprocessors add advanced features that native CSS does not have, or are not perfect for, making CSS structures more readable and maintainable, CSS preprocessors currently represented by the community are mainly as follows:
    • Sass: Born in 2007, the earliest and most mature CSS preprocessor, has the support of the Ruby community and Compass, the most powerful CSS framework, currently under the influence of LESS, has evolved to fully CSS compatible Scss
    • Less: Introduced in 2009, it is greatly influenced by Sass, but it also uses the syntax of CSS, making it easier for most developers and designers to get started. It has far more supporters outside the Ruby community than Sass. Its disadvantage is that it has less programmable functions than Sass, but its advantage is that it is simple and compatible with CSS. This, in turn, has influenced the evolution of Sass into Scss. The famous Twitter Bootstrap uses Less as the underlying language
    • Stylus: Stylus is a CSS preprocessing framework, created in 2010 from the Node.js community to support CSS preprocessing for Node projects, so Stylus is a new language for creating robust, dynamic, and expressive CSS. Relatively young, essentially doing similar things to Sass/Less etc

Superpowers conferred by the preprocessor

Document segmentation

  • As pages become more complex and CSS files need to be loaded, it is necessary to slice up large files, otherwise they are difficult to maintain
  • Traditional CSS file sharding schemes, which are basically CSS native @import directives or loading multiple CSS files in HTML, often fail to meet performance requirements

modular

  • To take the idea of file segmentation one step further, it is “modularization”. After a large CSS file is properly partitioned, the relationship between these small files should be a tree structure
  • The root nodules of the tree are called entry files, and the other nodes of the tree are called module files. Entry files often depend on multiple module files, and each module file may depend on other modules further down the tree to form the whole tree
  • Modularization is a very good code organization, is an important means for developers to design code structure, module can be very clear to achieve hierarchical code, reuse and dependency management, so that the development process of CSS can also enjoy the convenience of modern program development

Selector nesting

  • Selector nesting is a way of organizing code within a file that allows a hierarchy of related rules. To achieve this in the past, you would have to manually maintain the indentation relationship, and when the upper selector changes, all the associated lower selector changes; It’s also hard to read each rule on a single line, and it’s awkward to comment a single statement (you have to insert it between the statements).

    .nav {margin: auto /* Horizontal center */; width: 1000px; color: #333; } .nav li {float: left /* horizontal */; width: 100px; } .nav li a {display: block; text-decoration: none; }Copy the code
  • In CSS preprocessing languages, nested syntax makes it easy to express hierarchical relationships between rules, and comments for individual declarations are clear and readable

.nav {
     margin: auto  // Horizontal center
     width: 1000px
     color: #333
     li {
         float: left  // horizontal arrangement
         width: 100px
         a {
             display: block
             text-decoration: none
         }
     }
}    
Copy the code

variable

  • Before variables, all property values in CSS are “magic numbers”. I don’t know how this value comes from, what it means, etc. After variables, I can give these “magic numbers” a name, easy to remember, read, and understand
  • Variables are a simple and effective abstraction that eliminates duplication when a particular value is used in multiple places, making it easier for developers to unify the visual style of a site, and making it easier to “skin” things
// Native CSS code
strong {
    color: #ff4466;
    font-weight: bold;
}
.notice {
    color: #ff4466;
}

// Write in Stylus
$color-primary = #ff4466

strong
    color: $color-primary
    font-weight: bold

.notice
    color: $color-primary
Copy the code

operation

  • It’s not enough to have variables, it’s also necessary to have operations, and if variables make values meaningful, then operations can make values relate to values
  • Some attribute values are closely related to other attribute values, and CSS syntax cannot express this relationship. In a pre-processed language, variables and expressions are used to represent this relationship
// We can only use a comment to express the value of max-height, and the value of 3 is a magic number
// In the future, when the line height or the number of lines changes, the max-height value and the calculation in the comment will need to be updated simultaneously, which will be very inconvenient to maintain
.wrapper {
    overflow-y: hidden;
    line-height: 1.5;
    max-height: 4.5em;  /* = 1.5 x 3 */
}

// Preprocessing the language to improve
// In later maintenance, just modify those two variables
.wrapper
    $max-lines = 3
    $line-height = 1.5

    overflow-y: hidden
    line-height: $line-height
    max-height: unit($line-height * $max-lines, 'em')
 
 // Another benefit of this approach is that the $line-height variable can be local to the.wrapper definition or retrieved from a higher scope
// This means that.wrapper can inherit the row height from the ancestor without having to write out its own row height for the need to display only three rows
// With operations, you have the ability to express associations between attributes, which makes your code more flexible and DRY
$line-height = 1.5  // Global uniform line height
body
    line-height: $line-height
.wrapper
    $max-lines = 3
    max-height: unit($line-height * $max-lines, 'em')
    overflow-y: hidden
Copy the code

function

  • Functions are obtained by abstracting out common operations
  • Developers can customize the functions, the preprocessor itself has a large number of built-in functions, the most commonly used built-in functions should be the color calculation function, with them, you don’t even need to open Photoshop to color, you can get a color of the same color variation
  • Preprocessor functions also support advanced functions such as default parameters, named arguments, arguments objects, etc., and conditional branches can be set internally to meet complex logic requirements
// Add a hover effect to a button
.button {
    background-color: #ff4466;
}
.button:hover {
    background-color: #f57900;
}
#ff4466 #f57900 #ff4466
// If the code is written in a preprocessed language, things are much more straightforward
.button
    $color = #ff9833
    background-color: $color
    &:hover
        background-color: darken($color, 20%)
Copy the code

Mixins

Mixins are one of the most powerful features of the CSS preprocessor language. In simple terms, Mixins can be used repeatedly by many selectors by pulling out styles as individually defined modules

The mixture of Sass

  • Used when declaring Mixins in the Sass style@mixin, followed by the Mixins name, you can also define a parameter and give the parameter a default value, but the parameter name is used$A colon is required between the symbol and the parameter value:separate
  • The Mixins defined in the selector call are needed@include, followed by the name of the Mixins to be called, but Sass also supports the old calling method, which uses the plus sign+Call Mixins in the+It is followed by the name of the Mixins
// Declare a Mixins called error
@mixin error($borderWidth: 2px) {
  border: $borderWidth solid #f00;
  color: #f00;
}

// Call error mixins
.generic-error {
  @include error(); /* Call error mixins*/ directly
}
.login-error {
  @include error(5px); /* Call error mixins and redefine the value of the argument $borderWidth to 5px*/
}
Copy the code

The mixture of Less

  • In Less, blending means to be definedClassATo introduce another already definedClass, just like adding an attribute to the previous Class
  • In LESS, Mixins can be treated as a class selector. Of course, Mixins can also set parameters and default values, but the variable names of parameters are used@Start, again, with a colon between the parameter and the default parameter value:separated
// Declare a Mixin called error
.error(@borderWidth: 2px){
  border: @borderWidth solid #f00;
  color: #f00;
}
// Call error Mixins
.generic-error {
  .error(); /* Call error mixins*/ directly
}
.login-error {
  .error(5px); /* Call error mixins and redefine the @borderWidth parameter to 5px*/
}
Copy the code

The mixture of Stylus

  • The mixing in Stylus is slightly different from the mixing in the previous two CSS preprocessor languages by declaring Mixins names without using any symbols and then using an equal sign between the definition parameters and the default values=To connect to
// Declare a Mixin called error
error(borderWidth=2px){
  border: borderWidth solid #f00;
  color: #f00;
}
// Call error Mixins
.generic-error {
  error(); /* Call error mixins*/ directly
}
.login-error {
  error(5px); /* Call error mixins and redefine the value of the argument $borderWidth to 5px*/
}
Copy the code

All three examples will translate to the same CSS code

.generic-error {
  border: 2px solid #f00;
  color:#f00;
}
.login-error {
  border: 5px solid #f00;
  color: #f00;
} 
Copy the code

disadvantages

  • Extra compilation configuration: Some extra compilation configuration work is required before writing styles, and the sass-Node installation and compiled configuration can choke off a bunch of front-end novices

  • Compilation cost: Every change in code requires recompilation, taking up time and CPU

  • Learning cost: Different CSS preprocessors have different syntax, which increases learning cost. Several styles of preprocessors may be used in the same team or even project

    // Sass
    $color: #f00;
    $images: ".. /img";
    @mixin clearfix {
      &:after {
        content: "";
        display: block;
        clear: both;
      }
    }
    body {
      color: $color;
      background: url("#{images}/1.png");
      @include clearfix;
    }
    
    // Less
    @color: #f00;
    @images: ".. /img";
    .clearfix() {
      &:after {
        content: "";
        display: block;
        clear: both;
      }
    }
    body {
      color: @color;
      background: url("@{images}/1.png");
      .clearfix;
    }
    Copy the code
  • Debugging: When using CSS preprocessors, SourceMap is often configured to assist debugging, but even then, some debugging difficulties can occur

Return the CSS

Various CSS preprocessors have more and more fancy functions in the process of updating, but most people still use the same core functions: Variables, Mixing, Nested, Module, and at most some utility class functions. Is there a way to have the best of both worlds without the costs and drawbacks of a preprocessor? CSS has been learning from the community for years to accelerate evolution and iteration, can we find the answer from the CSS standard?

Variables in CSS

  • CSS Custom Properties, also known as CSS variables, allow variables to be declared in styles and used through the var() function
  • The CSS Custom Properties for Cascading Variables specification was first proposed as a working draft (WD) in October 2012 and reached the candidate recommendation (CR) stage in October 2015, Browser support is now approaching 93%
  • CSS variables are defined and used as shown below. The types that can be defined are extremely rich. Unlike the compile-time handling of SASS preprocessor variables, CSS variables are handled by the browser at runtime, making CSS variables more powerful and flexible
/* declaration */
--VAR_NAME: <declaration-value>;
/* usage */
var(--VAR_NAME)

/* root element selector (global scope), e.g. <html> */
:root {
  /* CSS variables declarations */
  --main-color: #ff00ff;
  --main-bg: rgb(200.255.255);
  --logo-border-color: rebeccapurple;

  --header-height: 68px;
  --content-padding: 10px 20px;

  --base-line-height: 1.428571429;
  --transition-duration: .35s;
  --external-link: "external link";
  --margin-top: calc(2vh + 20px);
}
body {
  /* use the variable */
  color: var(--main-color);
}
Copy the code

Why does the definition of a variable start with –? Here’s why: Let’s Talk about CSS Variables

Operators

  • The calculation can be done using calc()
:root {
  --block-font-size: 1rem;
}

.block__highlight {
  /* WORKS */
  font-size: calc(var(--block-font-size)*1.5);
}
Copy the code

Generate Colors

Can be used to Generate and calculate Colors through functions such as RGB: Generate Colors

CSS to JS

Before CSS variables appeared, passing values from CSS to JS was very difficult, and even required some Hack techniques. Now using CSS variables, you can directly obtain variable values through JS and modify

.breakpoints-data {
  --phone: 480px;
  --tablet: 800px;
}
const breakpointsData = document.querySelector('.breakpoints-data');

// GET
const phone = getComputedStyle(breakpointsData).getPropertyValue('--phone');

// SET
breakpointsData.style.setProperty('--phone'.'custom');
Copy the code

Custom Theme

  • Using CSS variables, customizing and dynamically switching website themes is easy and convenient
  • First define the variables for each topic, and then write in the normal style
  • Dynamically switch themes by changing element attributes through JS
html {
  --hue: 210; /* Blue */
  --text-color-normal: hsl(var(--hue), 77%, 17%); . } html[data-theme='dark'] {
  --text-color-normal: hsl(var(--hue), 10%, 62%); . }// Dynamically switch themes by changing element attributes with JS
document.documentElement.setAttribute('data-theme'.'dark')
document.documentElement.setAttribute('data-theme'.'light')
Copy the code
  • CSS Custom Properties (Native Variables) In Depth

Mixins in CSS

  • CSS has a proposal: CSS @apply Rule. According to the draft description, users can directly use CSS variables to store declaration blocks, and then use @apply Rule
  • This proposal has been Abandoned. If you are interested in Why I Abandoned @apply, check out this article. While Mixins does not yet have a good CSS implementation standard, we believe that sooner or later a better specification will emerge to fill the gap in CSS
:root {
    --pink-schema: {
        color: #6A8759;
        background-color: #F64778;
    }
}

body{
  @apply --pink-schema;
}
Copy the code

Nesting in CSS

  • Nesting specifications are already available in the CSS, though this is only in the Editor’s Draft stage: Using the CSS Nesting Module Level 3, you can see that the Nesting specification is basically the same as the preprocessor according to the CSS Nesting Module
/* Dropdown menu on hover */
ul {
  /* direct nesting (& MUST be the first part of selector)*/
  & > li {
    color: #000;

    & > ul { display: none; }

    &:hover {
      color: #f00;

      & > ul { display: block; }}}}Copy the code

Module in CSS

  • In fact, CSS has a very long modular scheme, namely @import, using CSS @import rules can reference other file styles, this feature has been supported by all browsers since IE 5.5, so why has been few users, there are many reasons:
    • There are load order bugs in some older browsers
    • Unable to load in parallel
    • Resulting in excessive number of requests
    • .
  • At present, front-end projects are basically packaged with construction tools (Gulp, Webpack, etc.) and then launched, so the above disadvantages do not exist, and in the CSS-Loader of Webpack, it is possible to configure whether to enable @import

Selector Helpers

  • In addition to some of the main features described above, CSS also provides a number of new features to aid in more elegant writing styles

:matches pseudo-class (renamed :is())

  • The :matches() CSS pseudo-class function takes a list of selectors as an argument and selects elements that can be selected by any of the selectors in that list. This is useful for writing large selectors in a more compact form, and browser support is approaching 93%
  • For more details, see the specifications: Selectors Level 4
/ * * / grammar
:matches( selector[, selector]* )

.nav:matches(.side,.top) .links:matches(:hover, :focus) {
  color: #BADA55;
}

/* corresponds to the following code */
.nav.side .links:hover,
.nav.top  .links:hover,
.nav.side .links:focus,
.nav.top  .links:focus {
  color: #BADA55;
}
Copy the code
  • @custom-selector
    • Custom selectors can also be used to define aliases that match complex selectors
    / * * / grammar
    @custom-selector: <custom-selector> <selector-list>;
    Copy the code
    • CSS variables are defined in a similar way, but are used slightly differently
    @custom-selector:--text-inputs input[type="text"], input[type="password"];
    
    :--text-inputs.disabled,
    :--text-inputs[disabled] {
      opacity: 0.5
    }
    
    /* corresponds to the following code */
    input[type="text"].disabled,
    input[type="password"].disabled,
    input[type="text"][disabled],
    input[type="password"][disabled] {
      opacity: 0.5
    }
    Copy the code

To use

  • Although the CSS features mentioned above are in different stages and are supported by different browsers, postCSS-PRESET -env is a good way to get a sneak preview of the latest CSS features
  • The configuration of postCSS-Preset -env is also very simple, using Webpack as an example
rules: [
  {
    test: /\.css$/,
    use: [
      'style-loader',
      { loader: 'css-loader'.options: { importLoaders: 1}}, {loader: 'postcss-loader'.options: {
        ident: 'postcss'.plugins: () = > [
          postcssPresetEnv(/* pluginOptions */]}]}]Copy the code

conclusion

  • After some sorting, although CSS has accelerated the speed of update iteration under the stimulation of the community, but so far still can not reach the point of CSS preprocessor VS CSS, can only say that when using CSS preprocessor, you can also try some excellent CSS new features in the project, that is: CSS preprocessor + CSS
  • I still firmly believe that, under the promotion of W3C, with the continuous improvement of CSS, CSS preprocessor will eventually become a transitional product of The Times like CoffeScript and Jade. At that time, there is no need to struggle with the configuration and technology selection of various CSS preprocessors, and directly open the editor. Can be happy writing style