Lập trình CSS - Đưa ngôn ngữ lập trình vào CSS (Phần 1)

T.V.T Marine Automation Company Limited, Vũ Thành Luân

Từ trước tới nay, việc viết các file css đơn thuần chỉ là viết các dòng mã css vào một file, giống như kiểu bạn ghi một bài văn vào một tờ giấy vậy, viết hết dòng này rồi lại dòng khác, có phải là sẽ rất nhàm chán phải không nào?

Sẽ có đôi lúc bạn tự hỏi liệu có thể biến css thành một dạng lập trình, cũng có hàm, có biến, có tham số ... việc duy nhất chỉ cần là truyền tham số vào, thay đổi biến, thay đổi hàm và kết quả chúng ta sẽ có một file css như ý, và câu trả lời là điều này hoàn toàn có thể.

Bài viết hôm nay, tôi sẽ giới thiệu cho các bạn một cách viết css hoàn toàn mới, một cách viết mang phong cách lập trình và hoàn toàn linh động sử dụng less.

less

Trong phạm vi bài này, tôi chỉ hướng dẫn thao tác trên môi trường Ubuntu 12.04

 

Bước 1: Chuẩn bị

Đầu tiên, chúng ta cần cài một số phần mềm hỗ trợ:

sudo apt-get install node-less yui-compressor

Chú ý: việc cài đặt phần mềm này chỉ cần cài đặt ở máy của lập trình viên, được sử dụng để biên dịch file less thành file css khi cần.

Bạn cần download file javascript này và đưa nó vào thư mục chứa bộ mã nguồn website.

Tiếp theo, trong thẻ head, chúng ta đưa các thẻ sau vào:

<link rel="stylesheet/less" type="text/css" href="styles.less" />
 <script src="less-1.4.1.min.js" type="text/javascript"></script>

Trong đó   less-1.4.1.min.js  chính là file mà tôi đã tải về ở trên. Bạn cần tạo một file styles.less để viết mã css ở bước sau.

Tới bước này, chúng ta đã xong công tác chuẩn bị.

 

Bước 2: Làm quen với less

Như đã nói ở đầu bài, less mô phòng theo ngôn ngữ lập trình bởi vậy các khái niệm lập trình trong less tương tự như các ngôn ngữ lâp trình khác.

 less css

Biến trong less

Để sử dụng biến trong less, rất đơn giản

// LESS
@color: #3F926E;
#nav{
  color: @color;
}
p {
  color: @color;
}

Tại ví dụ trên, tôi khai báo một biến @color với giá trị là #3F926E theo cấu trúc:

@<tên biến>: <giá trị của biến>

Sau đó để set màu cho #nav hay thẻ p tôi chỉ cần đặt thuộc tính color: @color thay vì phải viết color:#3F926E như trước nữa, rất ngắn gọn và dễ hiểu.

Đoạn mã trên tương ứng với đoạn css

/* Compiled CSS */
#nav{
  color: #3F926E;
}
p{
  color: #3F926E;
}

Ngoài ra khi thay đổi màu của #nav hoặc p tôi chỉ cần thay đổi giá trị của biến @color thì lập tức tất cả các đối tượng sử dụng biến @color sẽ thay đổi theo. Viêc này sẽ tiết kiệm rất nhiều thời gian so với trước, thay vì phải đi tìm kiếm tất cả các mã màu #3F926E và thay thế bằng mà màu mới, đôi khi việc này còn gây ra sự nhầm lần khi sửa đổi.

Bạn cũng có thể sử dụng biến theo phương pháp ghép chuỗi

@fnord:"I am fnord.";
@var: 'fnord';
content: @@var;

và chúng ta sẽ có:

content: "I am fnord.";

 

Chú ý khi sử dụng biến:

Biến được load từ dưới lên.

Khi bạn định nghĩa một biến 2 lần hoặc hơn, thì less sẽ ghi nhận giá trị của biến sau cùng mà bạn khai báo(cùng cấp). Nếu ở cấp hiện tại, giá trị của biến chưa được khai báo, less sẽ nhận giá trị của biến ở cấp cao hơn và vẫn theo quy tắc nhận giá trị biến sau cùng.

 

Để hiểu rõ thêm, chúng ta cùng xem ví dụ dưới đây.

@var: 0;
.class1{
  @var:1;
  .class {
    @var: 2;
    three: @var;
    @var: 3;
  }
  one: @var;
}

Nếu như chúng ta nhìn sơ qua, thì biến @var sẽ được set bằng 2, rồi gán cho thuộc tính three,(=2) rồi biến @var được set lại bằng 3 (theo cách lập trình thông thường), nhưng trong less, nó sẽ nhận giá trị của biến var là 3, thay vì 2. Nhưng @var hoàn toàn không bị  thay đổi ở ngoài dấu {}.

Và đây là kết quả:

.class1 .class {
  three: 3;
}
.class {
  one: 1;
}

Thêm một điểm đặc biệt nữa, vì biến được load sau nên bạn không nhất thiết phải khai báo biến trước khi sử dụng. Chúng ta cùng xem một ví dụ.

.lazy-eval-scope { 
  width: @var; 
}
@var: @a; 
@a: 9%;
.lazy-eval-scope {
  width: @var;
  @a: 9%;
}
@var: @a;
@a: 100%;

Cả 2 ví dụ trên đều cho ra chung một kết quả:

.lazy-eval-scope{
  width: 9%;
}

Có thể bạn sẽ thắc mắc tại sao ở ví dụ 2, @a không phải là 100%. Chúng ta sẽ cần phải làm y hệt như less đã làm, đầu tiên chúng ta chạy từ dưới lên, less set @a = 100%, tiếp tục set @var = @a = 100%.

Nhưng khi vào trong .lazy-eval-scope, less set lại @a = 9%. tiếp tục set width = @var.

@var = @a = 9% => width : 9%.

 

Hàm trong less

Less còn cung cấp cho chúng ta các khai báo một hàm để định dạng một nhóm các câu lệnh css.

// LESS
.rounded-corners (@radius: 5px) {
  -webkit-border-radius: @radius;
  -moz-border-radius: @radius;
  -ms-border-radius: @radius;
  -o-border-radius: @radius;
  border-radius: @radius;
}

#nav{
  .rounded-corners;
}
#footer {
  .rounded-corners(10px);
}

Tại ví dụ trên, chúng ta khai báo một hàm có tên là .rounded-corners với tham số là @radius với giá trị mặc định là 5px.

Tiếp theo, ta đặt thuộc tính này cho đối tượng #nav và #footer.

Đối với #nav ta không truyền tham số, thì giá trị mặc định sử dụng sẽ là 5px

Đối với #footer ta truyền tham số là 10px.

Và kết quả trả về ta sẽ có một đoạn css tương ứng:

/* Compiled CSS */

#nav {
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
  -ms-border-radius: 5px;
  -o-border-radius: 5px;
  border-radius: 5px;
}
#footer {
  -webkit-border-radius: 10px;
  -moz-border-radius: 10px;
  -ms-border-radius: 10px;
  -o-border-radius: 10px;
  border-radius: 10px;
}

Chú ý: nếu chúng ta khai báo thêm một class .rounded-corners vào phía sau hàm .rounded-corners

.rounded-corners{
  -webkit-border-radius: 20px;
  -moz-border-radius: 20px;
  -ms-border-radius: 20px;
  -o-border-radius: 20px;
  border-radius: 20px;
}

Trong trường hợp sử dụng

#nav {
  .rounded-corners;
}

Thì less sẽ thêm class .rounded-corners vào phía sau giá trị của hàm .rounded-corners. (đúng theo thứ tự mà bạn khai báo trong less)

Lúc này ta sẽ có:

#nav {
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
  -ms-border-radius: 5px;
  -o-border-radius: 5px;
  border-radius: 5px;

  -webkit-border-radius: 20px;
  -moz-border-radius: 20px;
  -ms-border-radius: 20px;
  -o-border-radius: 20px;
  border-radius: 20px;
}

Theo như thứ tự css thì các  thuộc tính của class .rounded-corners sẽ có hiệu lực.

Đặc biệt, chúng ta cũng có thể tạo ra một hàm mà không cần tham số.

.rounded-corners() {
  -webkit-border-radius: 3px;
  -moz-border-radius: 3px;
  -ms-border-radius: 3px;
  -o-border-radius: 3px;
  border-radius: 3px;
}

Vậy điều gì sẽ xảy ra, nếu chúng ta để cả hàm có tham số, hàm không có tham số, class cùng tên chung với nhau.

Less sẽ sắp xếp các thuộc tính theo thứ tự hàm không tham số, hàm có tham số, class mà bạn đã sắp xếp.

#nav {
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
-ms-border-radius: 3px;
-o-border-radius: 3px;
border-radius: 3px;

-webkit-border-radius: 5px;
-moz-border-radius: 5px;
-ms-border-radius: 5px;
-o-border-radius: 5px;
border-radius: 5px;

-webkit-border-radius: 20px;
-moz-border-radius: 20px;
-ms-border-radius: 20px;
-o-border-radius: 20px;
border-radius: 20px; 
}


Bạn cũng có thể thử thay đổi thứ tự của các hàm và bạn sẽ thấy sự khác biệt.

 

Với hàm có một tham số thì như vậy, vậy nếu chúng ta muốn truyền nhiều tham số vào một hàm thì sẽ như nào.

Trong less, để khai báo hàm với nhiều tham số, chúng ta ngăn cách giữa các tham số bằng dầu phẩy (,) hoặc dấu chấm phẩy (;). Tuy nhiên, dấu chấm phẩy (;) được khuyến khích sử dụng.

.mixin(@color) {
  color-1: @color;
}
.mixin(@color; @padding:2) {
  color-2: @color;
  padding-2: @padding;
}
.mixin(@color; @padding; @margin: 2) {
  color-3: @color;
  padding-3: @padding;
  margin: @margin @margin @margin @margin;
}
.some .selector div {
  .mixin(#008000);
}

Trên đây là một hàm với số lượng tham số khác nhau.

Tùy thuộc vào giá trị mặc định của hàm mà hàm đó có được sử dụng hay không.

Trong trường hợp giá trị của tham số không được set(không đủ tham số) hoặc số lượng tham số vượt quá, thì hàm đó sẽ không được thực thi.

Kết quả của đoạn mã trên sẽ là:

.some .selector div {
  color-1: #008000;
  color-2: #008000;
  padding-2: 2;
}

Vì hàm mixin thứ 3 không đủ tham số (yêu cầu 3 tham số) nên less sẽ không thực thi hàm này.

Trong trường hợp chúng ta truyền 2 tham số

 

.some .selector div {
  .mixin(#008000, 3);
}

 

Thì kết quả trả về sẽ là:

 

.some .selector div {
  color-2: #008000;
  padding-2: 3;
  color-3: #008000;
  padding-3: 3; 
  margin: 2 2 2 2;
}

 

Hàm mixin 1 sẽ không được thực thi vì nó chỉ yêu cầu 1 tham số.

 

Một số từ khóa trong less

Biến @arguments

Biến này tượng trưng cho tất cả giá trị của các tham số của hàm. Nếu một tham số không có giá trị, hàm này sẽ hiển thị lỗi.
Nó được sử dụng khi bạn muốn truyền toàn bộ các giá trị của hàm một lần. Chúng ta cùng xem ví dụ:

.box-shadow (@x: 0; @y: 0; @blur: 1px; @color: #000) {
  box-shadow: @arguments;
  -moz-box-shadow: @arguments;
  -webkit-box-shadow: @arguments;
}
.box-shadow(2px; 5px);

Và đây là kết quả:

box-shadow: 2px 5px 1px #000;
  -moz-box-shadow: 2px 5px 1px #000;
  -webkit-box-shadow: 2px 5px 1px #000;

Đến đây, chúng ta đã sơ lược hiểu về less, hiểu về biến và hàm trong less.

Trong (Phần 2), tôi sẽ trình bày tiếp cho các bạn về một số tính năng nâng cao trong less, toán tử, kế thừa, hàm mặc định vv..

Mọi ý kiến đóng góp & bình luận để giúp bài viết này tốt hơn và có ích hơn với cộng đồng đều được hoan nghênh.