CSS4 Variables và Sass bạn có biết ?

5.0/5 (1 Reviews)

Tóm Tắt

The CSS4 Spec

Đối với những người chưa bắt đầu (tôi cách đây một tuần), các biến CSS mới sẽ trông như thế này:

 

.class {
  --color-background: #FFBB00;
  background-color: var(--color-background);
}

 

The variable --color-background is bound to the scope of the .class selector. Global variables can be defined outside of an element in a :root pseudo-class:

 

/* global variables in the :root class */
:root {
  --color-background: #FFBB00;
}
/* calling our global variable outside the :root class */.class {
  background-color: var(--color-background);
}

 

Bạn có thể đọc thêm về các biến trong W3C Spec hoặc trên Mozilla Developer Network.

Sass

Bất kể bạn có thích cú pháp biến mới hay không, rất có thể bạn đã sử dụng bộ xử lý trước để quản lý các biến của mình và muốn tiếp tục làm như vậy. Một số cá nhân cực kỳ có tổ chức thậm chí có thể sử dụng các bản đồ biến đổi trong Sass.

// a variable map in Sass$colors: (
  primary: #FFBB00,
  secondary: #0969A2
);

// using `map-get` in Sassbody { 
  color: map-get($colors, primary); 
}
 
body { 
  color: #FFBB00; 
}
Thật không may, cú pháp để truy cập bản đồ Sass hơi dài dòng, đó là lý do tại sao một số người thích sử dụng các hàm tùy chỉnh để truy cập các biến bản đồ của họ. Kiểm tra Sử dụng các hàm Sass để truy cập các bản đồ biến nếu bạn muốn tìm hiểu cách bạn có thể tìm hiểu sâu hơn về các bản đồ biến.

Working together

Chỉ vì bạn có thể thích cú pháp biến Sass hơn thông số CSS4 không có nghĩa là bạn chỉ nên bỏ thông số mới. Nếu bạn có thể tham chiếu đến một biến trong CSS vani, bạn nên làm như vậy. Thời đại của các màu sắc dư thừa đã qua!

Trình kiểm tra Chrome chắc chắn sẽ cho phép bạn thay đổi định nghĩa biến CSS4 trong trình duyệt và bạn sẽ thấy kết quả cập nhật trực tiếp trên trang. Trong trường hợp màu sắc, điều đó có nghĩa là chúng tôi có thể thay đổi một biến và yêu cầu mọi thứ sử dụng biến đó thay đổi màu ngay lập tức. Nếu chúng tôi sử dụng các phương pháp cũ của mình, tính năng đó sẽ vô dụng. Nếu các bảng định kiểu của chúng tôi có thể mang tính mô tả nhiều hơn bao giờ hết, thì chúng nên như vậy.

Điều gì sẽ xảy ra nếu chúng ta có thể sử dụng Sass để tạo và sử dụng các biến tuân thủ CSS4 của mình? Chúng ta có thể. Trước tiên, hãy tạo tất cả các khai báo biến màu CSS4 toàn cầu của chúng tôi bằng cách sử dụng bản đồ biến Sass và lặp lại nó bằng vòng lặp @each:

// sass variable map$colors: (
  primary: #FFBB00,
  secondary: #0969A2
);

// ripped CSS4 vars out of color map
:root {
  // each item in color map@each$name, $colorin$colors {
    --color-#{$name}: $color;
  }
}
 

Điều này sẽ biên dịch thành các khai báo biến CSS4 hợp lệ. 

:root {
  --color-primary: #FFBB00;
  --color-secondary: #0969A2;
}

 

Những gì chúng tôi vừa làm là khá đơn giản. Chúng tôi lặp lại từng mục trong bản đồ $ Colors của chúng tôi. Trong khi lặp lại, chúng tôi có thể truy cập cả tên và màu sắc của mục trong bản đồ. Sau đó, sử dụng cú pháp nội suy (# {}), chúng ta có thể lấy ra tên của biến dưới dạng một chuỗi và kết hợp nó với một tiền tố biến --color- như định nghĩa biến CSS4.

Vì vậy, nó bao gồm việc khai báo các biến, bây giờ chúng ta cần có thể tham chiếu chúng. Chúng ta có thể làm điều đó với @function:

 

@functioncolor($color-name) {
  @returnvar(--color-#{$color-name});
}

 

This color function will take a color name, and return our CSS4 variable reference. We can then use this function and have our color references be syntactically-awesome:

 

// calling our sass functionbody { 
  color: color(primary); 
}

 

 

/* compiles to */body { 
  color: var(--color-primary); 
}

 

Further Sophistication

Let’s say we have a complex variable map of sizes that includes both nested maps and top-level values:

 

$sizes: (
  gutter: 30px,
  spacer: 15px,
  container: (sm: 750px, md: 970px, lg: 1170px),
  viewport:  (sm: 768px, md: 992px, lg: 1200px)
);

 

To access our nested maps container and viewport, while maintaining the ability to reference our top-level variables gutter and spacer, we’ll need to slightly modify our prior approach:

 

// ripping CSS4 vars out of size map
:root {

  // each item in size map@each$name, $sizein$sizes {
    // maps require a second loop@iftype-of($size) == "map" {

      // each item in sub map@each$subname, $subsizein$size {  
        // --size-viewport-md--size-#{$name}-#{$subname}: $subsize;
      }

    // top-level sizes
    } @elseiftype-of($size) == "number" {

      // --size-background--size-#{$name}: $size;

    }
  }
}

 

As you can see, we have the ability to detect the type-of value for each item in our $sizes map. If it is not a "number" and it is a "map", we do another loop on that item’s map value. The output is a tidy collection of dash-spaced variable references:

 

:root {
  --size-gutter: 30px;
  --size-spacer: 15px;
  --size-container-sm: 750px;
  --size-container-md: 970px;
  --size-container-lg: 1170px;
  --size-viewport-sm: 768px;
  --size-viewport-md: 992px;
  --size-viewport-lg: 1200px;
}

 

In order to reference our more sophisticated variables, we need a more sophisticated @function:

 

// retrieve size from map with Sass ie. `size(viewport, sm)`@functionsize($size-name, $size-variant:null) {
  // size variant is optional@if ($size-variant!= null) {
    // map inception, need two names@returnvar(--sizes-#{$size-name}-#{$size-variant});

  } @else {
    // single-level size, one name@returnvar(--sizes-#{$size-name});

  }
}

 

As you can see, we will optionally pass in a $size-variant value. In this case, those would be smmd, or lg for container or viewport. If the variant is there, we know that it is a nested map value and can write the appropriate CSS4 variable reference. If it is null, we can spit out our standard top-level reference.

 

UPDATE 7/21/2016: I'm about to show CSS4 variables being used in @media queries. @keithjgrant was kind enough to point out that these wont actually work since @media queries are outside the :root scope. I am leaving it in as an example of iteration. Towards the end of this article there is a section on adding an override to spit out the true value and not the var notation. You could use that here inside the @media query.

 

Here’s how we use it:

 

// referencing a top-level size variable with CSS via Sass.container { 
  margin: size(spacer) auto; 
}

// referencing our nested size variables with CSS via Sass@media (min-width: size(viewport, sm)) {
  .container { width: size(container, sm); }
}

 

Here’s the valid CSS4 output:

 

.container {
  margin: var(--size-spacer) auto;
}

@media (min-width: var(--size-viewport-sm)) {
  .container {
    width: var(--size-container-sm);
  }
}

 

By managing our CSS4 variables in Sass, we are free to take advantage of all the awesome things about Sass, like looping over our viewport sizes and outputting container sizes for each.

 

// referencing our size variables with CSS via Sass @each loop@each$name, $sizeinmap-get($sizes, viewport) {
  @media (min-width: size(viewport, $name)) {
    .container { width: size(container, $name); }
  }
}

 

The above @each loop will output container sizes for each of our viewport widths.

 

@media (min-width: var(--size-viewport-sm)) {
  .container {
    width: var(--size-container-sm);
  }
}
@media (min-width: var(--size-viewport-md)) {
  .container {
    width: var(--size-container-md);
  }
}
@media (min-width: var(--size-viewport-lg)) {
  .container {
    width: var(--size-container-lg);
  }
}

 

Over the top

Let’s crank up the funk and groove out to the tune of complexity. Say that there was a case where we wanted to get the true Sass value of the variable instead of the CSS4 selector. To illustrate, I am going to move back to colors.

 

// color variable map$colors: (
  text: #FFF,
  background: #333,
  // nested map inceptionprimary: (
    base: #FFBB00,
    light: lighten(#FFBB00, 15%),
    dark: darken(#FFBB00, 15%),
    trans: transparentize(#FFBB00, 0.5)
  ),
  // and another. is the totem still spinning?secondary: (
    base: #0969A2,
    light: lighten(#0969A2, 15%),
    dark: darken(#0969A2, 15%),
    trans: transparentize(#0969A2, 0.5)
  )
);

 

To to get the actual value of the color, we need another optional parameter in our function. Let’s call it $true-val.

 

@functioncolor($color-name, $color-variant:null, $true-val:false) {

  // if we are returning the true color value@if$true-val == true {
    // color variant is optional@if ($color-variant!= null) {
      // map inception, need two deep@returnmap-get(map-get($colors,$color-name),$color-variant);

    } @else {
      // single-level color, one deep@returnmap-get($colors,$color-name);

    }

  // if we’re only returning the CSS4 variable
  } @else {
    // color variant is optional@if ($color-variant!= null) {
      // map inception, need two names@returnvar(--color-#{$color-name}-#{$color-variant});

    } @else {
      // single-level color, one name@returnvar(--color-#{$color-name});

    }

  }
}

 

We are defaulting the $true-val value to false, which means the second block of code will run unless we pass in a true value.

 

body {
  color: color(primary,base);
  color: color(primary,base,false);
  color: color(primary,base,true);
  color: color(background,null,true);
}

 

 

body {
  color: var(--colors-primary-base);
  color: var(--colors-primary-base);
  color: #FFBB00;
  color: #333;
}

 

You might be wondering when this would ever be useful, and I will answer that inadvertently through another important example. Remember at the beginning of this post where we saw that CSS4 variable could be defined inside of a selector’s scope? After briefly mentioning it I jumped straight into global variables using the :root pseudo class. I didn’t forget about scoped variables.

The fact is, the vanilla syntax for defining a variable is fairly concise. We can create a @mixin to create variables, but it would take more code to write the inclusion of the mixin than to just write the standard CSS4 variable. Just to take this to the nth-degree and to appease the Sass gods, we will make both a @mixin to create a variable and a custom @function to reference any variable—global or scoped.

 

// define local variable just because@mixinvar($name,$value) {
  #{--$name}: $value;
}
// access any variable@functionv($name) {
  @returnvar(--#{$name});
}

 

We then define a local variable by including our @mixin, and refer to the variable using our @function. Note that I am also defining the variable in vanilla syntax so you can see how unnecessary the @mixin is.

 

h1 {
  // getting a darker form of our true value$darker-primary: darken(color(primary,base,true), 5%);
  // defining the new variable without the extra -- fluff@includevar(darker-primary, $darker-primary);
  // CSS4 syntax is much more terse--darker-primary: $darker-primary;
  // accessing our locally-scoped CSS4 variablecolor: v(darker-primary);
  border-color: v(darker-primary);
}

 

This will output the following valid CSS4 locally-scoped variable and references.

 

h1 {
  --darker-primary: #e6a800; /* mixin */--darker-primary: #e6a800; /* vanilla */color: var(--darker-primary);
  border-color: var(--darker-primary);
}

 

This level of complexity may not suit everyone, but it is certainly doable if you enjoy writing less code.

Bonanza!

Techniques like this allow us to utilize the power of Sass without having to ignore important advances in CSS. Any Sass we write should always output well-written modern CSS. Just because the W3C implements seemingly competing features into CSS does not mean we need to choose one over the other. Embrace the webs!

Props for making it to the end of this post. Here is everything we have learned, fully-functioning in a pen. Click “View Compiled” to see the output. Obviously the CSS4 variables are invalid CSS right now, but you can see that the Sass compiler can already handle it.

Dịch vụ seo