Advertisement
  1. Web Design
  2. HTML/CSS
  3. CSS

Sử dụng PostCSS cùng các phương pháp BEM và SUIT

Scroll to top
Read Time: 16 min
This post is part of a series called PostCSS Deep Dive.
Using PostCSS Together With Sass, Stylus, or LESS
PostCSS Deep Dive: Shortcuts and Shorthand

() 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.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 UtilitiesComponents. Trong các component có thể có Modifiers, DescendantsStates.

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.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@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 đó!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Web Design tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.