Sử dụng PostCSS cùng các phương pháp BEM và SUIT
() translation by (you can also view the original English article)
Trong hướng dẫn này, chúng tôi sẽ học cách sử dụng PostCSS để giúp phát triển CSS style BEM/SUIT dễ dàng và hiệu quả hơn.
Hai phương thức này đề ra một quy ước đặt tên cho các class giúp dễ dàng giúp các style của bạn theo định hướng về hàm chặt chẽ và giúp các nhà phát triển khác nhận ra mục đích của các class khác nhau từ cách chúng được đặt tên.
BEM là tiền thân của loại phương pháp đặt tên class này, do Yandex tạo ra. Phương pháp SUIT là một phương pháp dựa trên BEM, nhưng với một số điều chỉnh và bổ sung do Nicholas Gallagher thực hiện. SUIT làm được mọi thứ như BEM, nhưng với nhiều người dùng, đó là một sự cải tiến.
Làm việc với các phương thức này chắc chắn giúp tạo ra CSS tốt hơn, có cấu trúc tốt hơn. Tuy nhiên, cái khó là có thể trở nên mệt mỏi khi tự gõ các tên class được yêu cầu trong cấu trúc này và theo dõi làm thế nào các class liên quan có thể đau đầu một chút.
Plugin postcss-bem của Malte-Maurice Dreyer giảm bớt các vấn đề này thông qua sự kết hợp giữa shortcut và nesting, bạn sẽ học cách sử dụng chúng khi chúng tôi chuyển qua hướng dẫn này.
Nhưng trước tiên, hãy lướt qua bản tóm tắt nhanh về các phương pháp BEM và SUIT, để đảm bảo bạn có một bức tranh rõ ràng về lợi ích của việc sử dụng plugin postcss-bem và về cách sử dụng nó.
Cơ bản về BEM
Block
Trong các block của BEM là các phần cao cấp của một thiết kế; các block xây dựng nên một trang. Một block sẽ là một phần trong website của bạn nhưng độc lập với các phần khác và về mặt lý thuyết có thể được đặt ở bất kỳ đâu trong bố cục của bạn, thậm chí được lồng trong một block khác.
Ví dụ: các block biểu mẫu tìm kiếm trên website của bạn có thể sử dụng class .search-form
.
Element
Một element trong BEM là phần phụ bên trong một block. Chúng được biểu thị bằng cách nối thêm hai separator __
và một tên element vào tên parent block.
Ví dụ: một hình thức tìm kiếm có thể bao gồm tiêu đề, trường văn bản và các yếu tố nút gửi. Tên class của chúng có thể là .search-form__heading
, .search-form__text-field
và .search-form__submit-button
tương ứng.
Modifier
Modifier được áp dụng cho một block hoặc element để biểu thị sự thay đổi trong phần trình bày hoặc thay đổi state của nó. Chúng được biểu thị bằng cách nối thêm một separator và tên của modifier vào block hoặc component được đề cập.
Các tài liệu chính thức của website BEM khẳng định rằng các separator của modifier phải là một dấu gạch dưới đơn _
. Tuy nhiên, quy ước về các nguyên tắc "kiểu BEM" của các hướng dẫn CSS từ Harry Roberts sử dụng hai dấu gạch ngang --
và có lẽ được sử dụng rộng rãi và được biết đến nhiều hơn so với quy ước BEM chính thức.
Ví dụ: trong một thiết kế, bạn có thể muốn trình bày các biểu mẫu tìm kiếm nâng cao khác biệt với các biểu mẫu tìm kiếm thông thường và do đó tạo class sửa đổi .search-form_advified
(BEM chính thức) hoặc .search-form--advanced
(kiểu BEM).
Trong một ví dụ khác, bạn có thể muốn thay đổi giao diện của biểu mẫu do một thay đổi state, chẳng hạn như nếu nội dung không hợp lệ vừa được gửi và tạo modifier .search-form_invalid
(BEM chính thức) hoặc .search-form-invalid
(kiểu BEM).
Cơ bản về SUIT
SUIT bao gồm các Utilities và Components. Trong các component có thể có Modifiers, Descendants và States.
SUIT sử dụng kết hợp kiểu chữ pascal (PascalCase), camelCase và dash (dấu gạch ngang). Các quy ước của nó thi hành một giới hạn về số lượng dấu gạch ngang và dấu gạch dưới đôi có thể xuất hiện trong BEM. Ví dụ: BEM class .search-form__text-field
sẽ là .SearchForm-textField
trong SUIT.
Utility
Các utility xử lý cấu trúc và tạo style theo vị trí, và được viết theo cách mà chúng có thể được áp dụng ở bất cứ đâu trong một component. Chúng có tiền tố là u-
và được viết theo kiểu camel case. Ví dụ: .u-ClearFix.
Component
Một component trong SUIT thay thế một block trong BEM. Các component luôn được viết theo pascal case và chỉ là một phần của SUIT sử dụng pascal case, giúp chúng dễ dàng xác định. Ví dụ: .SearchForm
.
Component Namespace
Các component có thể tùy chọn được thêm tiền tố với một namespace và các nmsp-
để đảm bảo ngăn chặn các xung đột, ví dụ: .mine-SearchForm
.
Descendent
Một descendent trong SUIT thay thế một element trong BEM. Nó sử dụng một dấu gạch ngang duy nhất - và được theo camel case. Ví dụ: .SearchForm-title
, .SearchForm-textField
và .SearchForm-submitButto
.
Modifier
SUIT sử dụng modifier giống với BEM, tuy nhiên vai trò của chúng được kiểm soát chặt chẽ hơn. Modifier của SUIT thường chỉ được áp dụng trực tiếp cho một component, không áp dụng cho descendant. Nó cũng không nên được sử dụng để thể hiện các thay đổi state, vì SUIT có một quy ước đặt tên riêng cho các state.
Modifier được viết trong theo camel case và theo sau hai dấu gạch ngang --
. Ví dụ: .SearchForm--advanced
.
State
Các class của state có thể được sử dụng để phản ánh các thay đổi với một state của component. Điều này cho phép chúng được phân biệt rõ ràng với các modifier, phản ánh sự thay đổi của giao diện cơ sở của component, bất kể state. Nếu cần thiết, một state cũng có thể được áp dụng cho một descendent.
Các state có tiền tố là is-
và được viết theo kiểu camel case. Chúng cũng luôn được viết là các class liền kề. Ví dụ: .SearchForm.is-invalid
.
Thiết lập dự án của bạn
Bây giờ bạn đã có các yếu tố cần thiết của BEM và SUIT, đã đến lúc thiết lập dự án của bạn.
Bạn sẽ cần một dự án trống bằng cách sử dụng Gulp hoặc Grunt, tùy theo sở thích của bạn. Nếu bạn không có sẵn ưa thích giữa hai cái thì tôi khuyên bạn nên sử dụng Gulp vì bạn sẽ ít code hơn và đạt được cùng một kết quả, vì vậy bạn nên tìm cách đơn giản hơn một chút để làm việc.
Bạn có thể đọc về cách thiết lập các dự án Gulp hoặc Grunt cho PostCSS trong các hướng dẫn trước
tương ứng.
Nếu bạn không muốn tự tay thiết lập dự án của mình từ đầu, bạn có thể tải xuống các file nguồn được đính kèm với hướng dẫn này và trích xuất dự án khởi động của Gulp hoặc Grunt vào một thư mục project trống. Sau đó, với một terminal hoặc command prompt chạy lệnh npm install
.
Cài đặt plugin
Tiếp theo, bạn sẽ cần cài đặt plugin postcss-bem. Chúng tôi cũng sẽ cài đặt một plugin có thể hoạt động khá tốt với nó: postcss-nested.
Cho dù bạn sử dụng Gulp hay Grunt, hãy chạy lệnh sau trong thư mục project của bạn:
1 |
npm install postcss-bem postcss-nested --save-dev |
Bây giờ chúng tôi đã sẵn sàng để tải các plugin vào dự án của bạn.
Tải các plugin thông qua Gulp
Nếu bạn sử dụng Gulp, hãy thêm các biến này vào dưới các biến đã có trong file:
1 |
var bem = require('postcss-bem'); |
2 |
var nested = require('postcss-nested'); |
Bây giờ thêm từng tên biến mới vào array processors
:
1 |
var processors = [ |
2 |
bem, |
3 |
nested
|
4 |
];
|
Thực hiện test cho thấy mọi thứ đều hoạt động bằng cách chạy lệnh gulp css
sau đó kiểm tra xem có phải có một file "style.css" mới đã xuất hiện trong thư mục "dest" project của bạn.
Tải các plugin thông qua Grunt
Nếu bạn sử dụng Grunt, hãy cập nhật đối tượng processors
, năm trong đối tượng options
, vào phần sau đây:
1 |
processors: [ |
2 |
require('postcss-bem')(), |
3 |
require('postcss-nested')() |
4 |
]
|
Thực hiện test cho thấy rằng mọi thứ đều hoạt động bằng cách chạy lệnh grunt postcss
sau đó kiểm tra xem một file "style.css" mới đã xuất hiện trong thư mục "dest" project của bạn.
Được rồi, bạn đã sẵn sàng. Hãy cùng học cách thức tạo cấu trúc BEM và SUIT.
BEM và SUIT với postcss-bem
Bạn có thể gặp vài khó khăn khi phát triển với cấu trúc BEM hoặc SUIT khi tự viết code, vì việc liên tục lặp lại các identifier tương tự trong tên class có thể khiến bạn chán chường và việc theo dõi các element và descendent nào thuộc về block nào và component nào có thể gây nhầm lẫn.
Tuy nhiên, khi bạn sử dụng postcss-bem, bạn sẽ dễ dàng hiểu được cấu trúc code của mình nhanh chóng và quá trình lặp lại của việc gõ tên class gần như biến mất.
Tạo cấu trúc SUIT
Theo mặc định postcss-bem sẽ xuất ra theo cú pháp SUIT chứ không phải BEM. Bạn có thể xuất theo cú pháp BEM, chúng tôi sẽ trình bày sau, nhưng plugin được thiết kế chủ yếu để tạo kết quả theo SUIT, vì lý do đó, chúng tôi sẽ bắt đầu với cú pháp SUIT.
Tạo một component
Để tạo một component, hãy sử dụng cú pháp @component ElementName {...}
.
Hãy thử nghiệm bằng cách thêm một component SearchForm
vào file "src/style.css" của bạn:
1 |
@component SearchForm { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
}
|
Biên dịch và bạn sẽ thấy kết quả là:
1 |
.SearchForm { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
}
|
Tạo ra một descendent
Để tạo một descendent, hãy sử dụng cú pháp @descendent descName {...}
được lồng bên trong parent component.
Thêm một descendent có tên textField
bên trong component SearchForm
của bạn như sau:
1 |
@component SearchForm { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
|
5 |
/* Nest descendent under component */
|
6 |
@descendent textField { |
7 |
border: 1px solid #ccc; |
8 |
}
|
9 |
|
10 |
}
|
Sau khi biên dịch, giờ bạn sẽ thấy:
1 |
.SearchForm { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
}
|
5 |
|
6 |
.SearchForm-textField { |
7 |
border: 1px solid #ccc; |
8 |
}
|
Tạo một modifier
Tạo một modifier cho một component với cú pháp @modifier name {...}
, được lồng bên trong component mà nó tạo ra. Modifier thường được đặt trên cùng của component của bạn, trên bất kỳ descendent và state nào.
Thêm một modifier có tên advanced
vào component SearchForm
của bạn với code sau:
1 |
@component SearchForm { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
|
5 |
/* Typically, place modifiers above descendents */
|
6 |
@modifier advanced { |
7 |
padding: 1rem; |
8 |
}
|
9 |
|
10 |
@descendent textField { |
11 |
border: 1px solid #ccc; |
12 |
}
|
13 |
|
14 |
}
|
Biên dịch lại code của bạn và bạn sẽ thấy modifier component advanced
mới của mình:
1 |
.SearchForm { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
}
|
5 |
|
6 |
.SearchForm--advanced { |
7 |
padding: 1rem; |
8 |
}
|
9 |
|
10 |
.SearchForm-textField { |
11 |
border: 1px solid #ccc; |
12 |
}
|
Tạo một state
Các state được tạo thông qua cú pháp @when name {...}
và có thể được lồng bên trong một component hoặc descendent.
Thêm state có tên invalid
vào descendent textField
của bạn bằng code này:
1 |
@component SearchForm { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
|
5 |
@modifier advanced { |
6 |
padding: 1rem; |
7 |
}
|
8 |
|
9 |
@descendent textField { |
10 |
border: 1px solid #ccc; |
11 |
|
12 |
/* This creates a state for the textField descendant */
|
13 |
@when invalid { |
14 |
border: 1px solid red; |
15 |
}
|
16 |
}
|
17 |
|
18 |
}
|
Bây giờ khi bạn biên dịch code của mình, bạn sẽ thấy nó chứa state invalid
mới của bạn:
1 |
.SearchForm { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
}
|
5 |
|
6 |
.SearchForm--advanced { |
7 |
padding: 1rem; |
8 |
}
|
9 |
|
10 |
.SearchForm-textField { |
11 |
border: 1px solid #ccc; |
12 |
}
|
13 |
|
14 |
.SearchForm-textField.is-invalid { |
15 |
border: 1px solid red; |
16 |
}
|
Namespace cho các component
Bạn có thể namespace các component của mình và tất cả các descendent, modifier và state được lồng trong chúng, bằng cách bao quanh chúng với @component-namcespace name {...}
. Nếu bạn muốn, bạn có thể bao bọc toàn bộ stylesheet của mình với namespace này để tất cả các class của bạn được tự động gán thêm tiền tố vào nó.
Hãy thử bằng cách đóng gói tất cả code của bạn từ đầu đến giờ với @component-namespace mine {...}
:
1 |
@component-namespace mine { |
2 |
|
3 |
@component SearchForm { |
4 |
padding: 0; |
5 |
margin: 0; |
6 |
|
7 |
@modifier advanced { |
8 |
padding: 1rem; |
9 |
}
|
10 |
|
11 |
@descendent textField { |
12 |
border: 1px solid #ccc; |
13 |
|
14 |
@when invalid { |
15 |
border: 1px solid red; |
16 |
}
|
17 |
}
|
18 |
|
19 |
}
|
20 |
|
21 |
}
|
Sau khi biên dịch, bạn sẽ thấy rằng bây giờ mỗi component của bạn đều có tiền tố là của mine-
:
1 |
.mine-SearchForm { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
}
|
5 |
|
6 |
.mine-SearchForm--advanced { |
7 |
padding: 1rem; |
8 |
}
|
9 |
|
10 |
.mine-SearchForm-textField { |
11 |
border: 1px solid #ccc; |
12 |
}
|
13 |
|
14 |
.mine-SearchForm-textField.is-invalid { |
15 |
border: 1px solid red; |
16 |
}
|
Tạo một utility
Các utility được tạo bằng cú pháp @utility UtilityName {...}
. Bạn sẽ nhớ lại rằng khi thiết lập dự án của mình, bạn đã cài đặt plugin postcss-nested. Chúng tôi đã cài đặt plugin đó vì nó có thể rất thuận tiện để sử dụng cùng với postcss-bem, như bạn sẽ thấy trong ví dụ này nơi chúng tôi tạo ra một utility ClearFix:
1 |
@utility clearFix { |
2 |
&:before, &:after { |
3 |
content: ""; |
4 |
display: table; |
5 |
}
|
6 |
&:after { |
7 |
clear: both; |
8 |
}
|
9 |
/* If supporting IE 6/7 */
|
10 |
*zoom: 1; |
11 |
}
|
Sau khi thêm đoạn code trên, hãy biên dịch và bạn sẽ thấy utility mới này đã được tạo:
1 |
.u-clearFix { |
2 |
/* If supporting IE 6/7 */
|
3 |
zoom: 1; |
4 |
}
|
5 |
|
6 |
.u-clearFix:before, .u-clearFix:after { |
7 |
content: ""; |
8 |
display: table; |
9 |
}
|
10 |
|
11 |
.u-clearFix:after { |
12 |
clear: both; |
13 |
}
|
Tạo cấu trúc BEM
Để kích hoạt kết quả của cú pháp BEM trong postcss-bem, hãy đưa vào tùy chọn style: 'bem'
trong Gulpfile hoặc Gruntfile của bạn như vậy:
1 |
/* Gulpfile */
|
2 |
var processors = [ |
3 |
bem({style: 'bem'}), |
4 |
nested
|
5 |
];
|
6 |
|
7 |
/* Gruntfile */
|
8 |
processors: [ |
9 |
require('postcss-bem')({style: 'bem'}), |
10 |
require('postcss-nested')() |
11 |
]
|
Theo mặc định, postcss-bem sẽ sử dụng separator chính thức cho modifier của một dấu gạch dưới _
. Nếu dự án bạn cần dùng separator thông dụng hơn --
, bạn có thể thay đổi cấu hình cho plugin postcss-bem bằng cách truy xuất thư mục node_modules/postcss-bem của dự án của bạn, mở index.js , tìm dòng 15 và thay đổi:
1 |
bem: { |
2 |
separators: { |
3 |
namespace: '--', |
4 |
descendent: '__', |
5 |
modifier: '_' |
6 |
}
|
7 |
}
|
... đến đây:
1 |
bem: { |
2 |
separators: { |
3 |
namespace: '_', |
4 |
descendent: '__', |
5 |
modifier: '--' |
6 |
}
|
7 |
}
|
Tạo một block
Bởi vì "blockv" trong BEM liên quan với một "component" của trong SUIT, hãy sử dụng cú pháp @component block-name {...}
để tạo một block.
Để tạo block search-form, thêm code này:
1 |
@component search-form { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
}
|
Sau khi biên dịch và bạn sẽ thấy:
1 |
.search-form { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
}
|
Tạo một element
Khi một element trong BEM liên quan với một "descendent" trong SUIT, chúng có thể được tạo bằng cú pháp @descendent Element-name {...}
được lồng bên trong parent block.
Để tạo component text-field
, bổ sung phần sau đây:
1 |
@component search-form { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
|
5 |
@descendent text-field { |
6 |
border: 1px solid #ccc; |
7 |
}
|
8 |
|
9 |
}
|
Khi biên dịch, bạn sẽ thấy element mới của mình đã được tạo:
1 |
.search-form { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
}
|
5 |
|
6 |
.search-form__text-field { |
7 |
border: 1px solid #ccc; |
8 |
}
|
Tạo một modifier
Mặc dù BEM cho phép sửa đổi cả block và element, plugin postcss-bem sẽ chỉ xử lý chúng nếu được nằm bên trong block chứ không phải element, do quy ước SUIT của modifier được áp dụng cho các component không phải là descendent. Chúng có thể được tạo bằng cú pháp @modifier name {...}
, được lồng bên trong parent block của nó.
Thêm một modifier advanced
vào component search-form
của bạn như vậy:
1 |
@component search-form { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
|
5 |
@modifier advanced { |
6 |
padding: 1rem; |
7 |
}
|
8 |
|
9 |
@descendent text-field { |
10 |
border: 1px solid #ccc; |
11 |
}
|
12 |
|
13 |
}
|
Và khi biên dịch, kết quả sẽ là:
1 |
.search-form { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
}
|
5 |
|
6 |
.search-form_advanced { |
7 |
padding: 1rem; |
8 |
}
|
9 |
|
10 |
.search-form__text-field { |
11 |
border: 1px solid #ccc; |
12 |
}
|
Không có utility hoặc state, nhưng có namespace
khi trong BEM, các cú pháp @utility
và @when
sẽ không biên dịch thành bất cứ thứ gì, BEM cho không sử dụng các utility hoặc state.
Tuy nhiên, mặc dù nó nói chung không phải là một phần của BEM, cú pháp @component-namespace vẫn sẽ hiệu quả nếu bạn muốn sử dụng nó trong BEM stylesheet của bạn. Nó sẽ có tiền tố cho các class của bạn với name--
:
1 |
.mine--search-form { |
2 |
padding: 0; |
3 |
margin: 0; |
4 |
}
|
5 |
|
6 |
.mine--search-form_advanced { |
7 |
padding: 1rem; |
8 |
}
|
9 |
|
10 |
.mine--search-form__text-field { |
11 |
border: 1px solid #ccc; |
12 |
}
|
Tổng kết
Bây giờ bạn đã biết tất cả cách để ngắn gọn quá trình phát triển BEM và SUIT của bạn và làm cho quá trình tổng thể dễ dàng hơn. Hãy tóm tắt tất cả mọi thứ chúng tôi đã đề cập:
- BEM và SUIT là các quy ước đặt tên class giúp giữ cho các stylesheet hoạt động theo định hướng chức nằng và có tổ chức, cũng như giúp các nhà phát triển khác nhận ra mục đích của các class khác nhau.
- SUIT giống như BEM, nhưng với một số bổ sung được thêm vào và điều chỉnh
- Plugin postcss-bem cung cấp các cách vắn tắt để tạo các class BEM và SUIT, chẳng hạn như
@component
,@descendent
,@modifier
, v.v. - Plugin cũng cho phép code được lồng bên trong một cách hữu ích, ví dụ: modifier được lồng bên trong component hoặc block mà chúng sửa đổi.
- Namespace có thể được thực hiện tự động bằng cách gói các class với tên
@component-namespace name{}
Trong hướng dẫn tiếp theo
Tiếp đến chúng tôi xem xét một cách tuyệt vời khác để tận dụng PostCSS, và đó là kết hợp một bộ công cụ ngắn gọn và các phím tắt mà chúng tôi có thể thực hiện để code nhanh hơn và hiệu quả hơn.
Hẹn các bạn vào lúc đó!