Tìm kiếm bài viết

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

27.09.2022

5.0/5 (1 Reviews)

CSS4 Variables và Sass bạn có biết ? Cùng tôi đọc qua bài viết này để biết thêm một số kiến thức thú vụ từ SASS , CSS4 bạn nhé.

    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.

    CÓ THỂ BẠN QUAN TÂM

    Bài Viết Cùng Chuyên Mục

    XEM THÊM
    thumbnail

    Kubernetes bài 6 - Vận hành k8s Day-Two Operations và Quản trị bằng GitOps

    22.05.2026

    Khi cụm Kubernetes của bạn đã được bảo mật cấu hình, tối ưu tài nguyên và thiết lập tự phục hồi, câu hỏi đặt ra là làm sao để duy trì sự ổn định đó trong nhiều năm tiếp theo mà không bị phụ thuộc

    thumbnail

    Kubernetes bài 5 - bảo mật Cloud Native và chuẩn DevSecOps cho K8s

    22.05.2026

    Việc siết chặt an ninh (Hardening) không phải là cấu hình một vài thông số rồi bỏ đó, mà là một tư duy phòng thủ chiều sâu.

    thumbnail

    Kubernetes bài 4 - Tối ưu Resource Auto-Healing và Scale Zero-Downtime

    22.05.2026

    Bài viết này sẽ đi sâu vào các cơ chế ở tầng Kernel giúp hệ thống tự phục hồi, chống lại các đợt tấn công cạn kiệt tài nguyên và cập nhật phiên bản mới mà người dùng không hề hay biết.

    thumbnail

    Kubernetes bài 3 - Bảo mật cấu hình k8s và config Security trên Production

    22.05.2026

    Kubernetes giải quyết bài toán này bằng hai đối tượng chuyên biệt nhưng nếu không hiểu rõ bản chất bảo mật ở tầng dưới, bạn đang tự tay dâng toàn bộ chìa khóa hệ thống cho hacker.

    thumbnail

    Kubernetes bài 2 - Mạng lưới k8s và luồng Traffic ở Packet Level

    22.05.2026

    Pod không chỉ là một container: Rất nhiều người nhầm lẫn Pod 1-1 với Container. Thực chất, Pod là đơn vị triển khai nhỏ nhất, có thể chứa một hoặc nhiều container

    Mục lục bài viết