Level up your Sass with the ampersand

Như mình đã từng giới thiệu về CSS Preprocessor ở bài viết CSS Preprocessor - SASS (SASS & SCSS. Ở bài viết đó mình đã giới thiệu một số khái niệm căn bản về SASS. Về phần Parent selector mình có giới thiệu qua công dụng của dấu & (ampersand) nhưng chỉ sơ qua và căn bản (vì lúc đó mình cũng chỉ biết được đến đó (yaoming)). Nhân tiện mình đọc được một bài viết khá hay với tiêu đề Level up your Sass with the ampersand nên mình sẽ dịch lại để chia sẻ với mọi người nhé. Bắt đầu nào.

Level up your Sass with the ampersand

Lần cuối cùng bạn viết CSS thuần là bao giờ? Nếu bạn là một front-end developer, hãy tưởng tượng sẽ khó khăn như thế nào nếu chúng ta không có các bộ CSS preprocessor với các chức năng mà chúng cung cấp cho chúng ta như: biến, function và cách tổ chức như chia file thành các phần khác nhau và được nhúng vào những nơi chúng ta cần hay việc lồng các selector?

Trong các bộ CSS preprocessor phổ biến hiện nay thì chỉ có SASS được coi là tốt nhất, nhưng với những gì chúng ta biết về nó thì chỉ là phần nổi của tảng băng chìm so với những gì mà nó có thể làm được. Một trong những tính năng được sử dụng nhiều nhất của SASS là khả năng lồng các rulesets trong các khối khai báo (declaration blocks) để biểu diễn một bộ chọn nhiều tầng (descendant selector) trong lúc biên dịch. Không chỉ có vậy, việc lồng các selector còn giúp chúng ta nhóm các kiểu (styles) lại với nhau để giúp bạn dễ hiểu hơn về việc chúng liên quan với nhau như thế nào do chúng được đặt cùng một vị trí (khối).

Trong SASS, ký hiệu & giống như một biến biểu diễn một parent selector. Kết hợp với việc lồng selector, dấu & còn cho phép chúng ta làm được nhiêu hơn thế nữa. Chúng ta cùng tìm hiểu nhé.

& pseudo-classes

Nếu bạn là một front-end developer, thì việc thường xuyên sử dụng dấu & trong SASS để lồng các lớp giả (class-pseudo) hay phần tử giả (element-pseudo) như a:hover hay div:before. Với dấu &, chúng ta có thể lồng các pseudo-class vào trong khối khai báo hiện tại giống như việc bạn đang làm với việc lồng các selector với nhau. Có nghĩa là nếu bạn đang trong một khối có 2 cấp chẳng hạn, thì dấu & sẽ biểu diễn đầy đủ bộ selector đó. Nghe có vẻ phức tạp, nhưng nó là một khái niệm khá quan trọng để chúng ta có thể hiểu về thằng & này. Bạn có thể xem ví dụ dưới đây cho trực quan nhé (yaoming):

.pagination {
  a {
    &:hover,
    &:focus {
      color: red;
    }
  }
}

// in this case, the & represents `.pagination a`, and will compile to:

.pagination a:hover, 
.pagination a:focus {
  color: red;
}

Như ví dụ trên, chúng ta thấy dấu & đang đại diện cho bộ selector .pagination a.

& concatenation

Như ví dụ ở trên, dấu & có thể gắn bản thân nó vào một selector khác. Nhưng không chỉ có vậy, nó còn có thể giúp chúng ta nối các tên class, ID, ... lại với nhau thay vì tạo thêm một selector con khác. Chúng ta cùng xem ví dụ để hiểu rõ hơn nhé:

.pagination {
  &-number {
    // & = .pagination-number
    &.is-current {
      border: 3px solid green;
    }
  }
  // & = .pagination
  &-prev {
    background-color: red;
    // & = .pagination-prev
    &:after {
      content: '<';
    }
  }
}

// which compiles to 

.pagination-number.is-current {
  border: 3px solid green;
}
.pagination-prev {
  background-color: red;
}
.pagination-prev:after {
  content: '<';
}

Như ví dụ trên, &-number sẽ được nối vào .pagination để thành .pagination-number mà không hề tạo ra class nào có tên .pagination. Thật kỳ diệu, phải không 😄? Vậy giờ chúng ta muốn có 2 class .paginationpagination-number cùng một tổ hợp style thì phải làm sao? Chúng ta sang phần kế tiếp nhé.

& self

Đến thời điểm này, chúng ta đã biết dấu & có thể đại diện cho các class, ID, pseudo-class, ... Vậy, nó còn có thể làm gì được nữa? Vâng, dấu & còn có thể biểu diễn chính bản thân selector hiện tại khi mà bạn sử dụng dấu & như một selector.

Giả sử chúng ta muốn class .pagination.pagination-number có style display: flex. Nếu không có dấu & thì bạn cần phải viết như sau:

.pagination {
  display: flex;
  &-num {
    display: flext;
  }
}

// Compile to

.pagination {
  display: flex;
}
.pagination-num {
  display: flext;
}

Thật là mất công, đúng không ạ (trước khi đọc được bài này thì mình vẫn phải viết kiểu đó (chandoi))? Và giờ, với dấu &, chúng ta có thể viết như sau:

.pagination {
  &,
  &-num {
    display: flext;
  }
}

// which compiles to 

.pagination, .pagination-num {
  display: flext;
}

Một trường hợp khác, bạn muốn áp dụng các style cho class anh em lân cận của class hiện tại. Ví dụ bạn muốn áp dụng kiểu căn lề trái cho tất cả class .pagination-number nằm cạnh .pagination-number khác. Với CSS thuần, bạn có thể viết lại hai lần tên class đó .classs + .classs (adjacent sibling selector). Nhưng với SASS, bạn có thể sử dụng & như sau:

.pagination {
    &-num {
        & + & {
            margin-left: 1em;
        }
    }
}

// Compile to

.pagination-num + .pagination-num {
  margin-left: 1em;
}

& changing parents

Dấu & không phải lúc nào cũng cần phải viết ở đầu selector. Nếu bạn đưa dầu & ra đằng sau, nó cho phép bạn chuyển đổi thứ tự, bản chất là thay đổi bộ parent selector. Ví dụ, giờ chúng ta muốn class .pagination-container nằm trong class blog-list sẽ có màu nền. Bạn có thể viết tiếp như sau:

.blog-list {
    .pagination {
        &-container {
            background-color: #639;
        }
    }
}

Nhưng nếu như thế, chúng ta sẽ thấy thằng .pagination được xuất hiện thêm nên dễ gây ra sự nhầm lẫn với .pagination trước đó và đồng nghĩa với việc bạn phải viết lại mà không thể sử dụng .pagination trước được. Với dấu &, bạn có thể giải quyết bằng cách sau:

.pagination {
    &-num {
        color: #F00;
    }
    &-container {
        .blog-list & {
            background: #639;
        }
    }
}

// Compile to

.pagination-num {
    color: #F00;
}
.blog-list .pagination-container {
    background: #639;
}

Thật là vi diệu, đúng không 😄!

& variable

Như đã đề cập trước đó. Dấu & luôn luôn đại diện cho bộ selector hiện tại. Bạn cũng có thể gán toàn bộ selector đó vào một biến để sử dụng lại sau này. Bạn có thể xem ví dụ sau:

.panel {
  $parent: &; // equals .panel
  &-right {
    float: right;
  }
  &-image {
    margin-bottom: 1em;
    // & = .panel-image
    #{$parent}-right & {
      margin-left: 1em;
    }
  }
}

// which compiles to

.panel-right {
  float: right;
}
.panel-image {
  margin-bottom: 1em;
}
.panel-right .panel-image {
  margin-left: 1em;
}

Vậy là chúng ta đã đi tìm hiểu về khả năng của thằng & trong SASS. Bài dịch này ngắn hơn so với bài viết gốc là do mình đã lược bỏ những ý không quan trọng. Hy vọng mọi người có thể hiểu được những gì mình đã viết và chia sẻ. Hoặc nếu không, các bạn có thể vào bài viết gốc để tham khảo. Hẹn gặp lại mọi người ở những bài viết sau (seeyou)!

Tham khảo: https://codepen.io/hidanielle/post/level-up-your-sass-with-the-ampersand

All Rights Reserved