Viết CSS tốt hơn bằng cách mượn ý tưởng từ các hàm JavaScript

theanh

Administrator
Nhân viên
Tôi thích nghĩ về việc viết CSS giống như viết các hàm mô tả cách bố cục của bạn phản ứng với sự thay đổi. Khi chúng ta quên các nguyên tắc viết một hàm tốt, đây là một số điều có thể xảy ra:
  • Chúng ta mất thời gian.
    Khi chúng ta phải lo lắng về các tác dụng phụ, các thay đổi sẽ mất nhiều thời gian hơn.
  • Chúng ta tạo ra lỗi.
    Ví dụ yêu thích của tôi là một cửa hàng trực tuyến nơi các nút "Mua" bị ẩn do sử dụng sai các đơn vị khung nhìn.
  • Chúng ta xây dựng ít tính năng hơn.
    Khi các thay đổi đáng sợ và tốn thời gian, chúng thường không xảy ra.
Hãy xem cách chúng ta có thể mượn các phương pháp hay nhất và ý tưởng từ việc viết các hàm JavaScript tốt để viết CSS dễ sử dụng, không có các tác dụng phụ không mong muốn và có khả năng phục hồi sau khi thay đổi.

Tránh các tác dụng phụ không mong muốn​

Khi bạn thay đổi một thứ gì đó trong hệ thống của mình, nó không nên làm thay đổi bất ngờ một thứ khác. Điều này đúng với CSS cũng như với các hàm JavaScript.

Chúng ta hãy xem biểu tượng mũi tên trong vòng tròn này làm ví dụ:



Trông có vẻ ổn, nhưng giả sử chúng ta muốn một biểu tượng mũi tên hẹp hơn:



Bây giờ vòng tròn chứa đã bị đè bẹp! Đây là ví dụ về tác dụng phụ không mong muốn. Sử dụng mũi tên hẹp hơn sẽ làm hỏng hình dạng của hình tròn.

Nếu chúng ta kiểm tra phần tử trong DevTools, chúng ta có thể thấy rằng hình dạng của hình tròn chứa phụ thuộc vào kích thước của biểu tượng bên trongkhoảng đệm xung quanh biểu tượng.



Trong trường hợp lý tưởng, biểu tượng bên trong không nên thay đổi hình dạng của hình tròn chứa. Sau đây là bản demo về cách sửa biểu tượng bị nén:

Xem Bút [Ví dụ về biểu tượng mũi tên [phân nhánh]](https://codepen.io/smashingmag/pen/OJBpNMv) của Yaphi.
Xem Bút Ví dụ về biểu tượng mũi tên [phân nhánh] của Yaphi.
Có hai cải tiến trong ví dụ này:
  1. Kích thước vùng chứa đã được tách biệt khỏi nội dung.
    Theo cách này, chúng ta có thể sử dụng các biểu tượng khác nhau mà không làm hỏng vùng chứa.
  2. Kích thước của vùng chứa đã được tách biệt khỏi vị trí của biểu tượng.
    Vì chúng ta đã sử dụng Flexbox để căn giữa biểu tượng theo chiều ngang và chiều dọc, nên vị trí của biểu tượng sẽ không bị hỏng khi kích thước vùng chứa thay đổi.
Những cải tiến trong ví dụ này có thể không phù hợp với mọi trường hợp sử dụng. Ví dụ: nếu bạn cần hình dạng và kích thước của vùng chứa thay đổi theo biểu tượng bên trong, thì phiên bản "trước" của mã có thể phù hợp với bạn hơn.

Khi tôi nói tránh các tác dụng phụ không mong muốn, từ khóa ở đây là "không mong muốn". Một số tác dụng phụ là mong muốn! Ví dụ, nếu tôi có ba đoạn văn và thêm văn bản vào đoạn đầu tiên, tôi muốn phần nội dung còn lại tạo không gian cho đoạn văn đó bằng cách dịch chuyển xuống trang.

Tôi đoán ý tôi là mục tiêu của tôi ở đây không phải là chỉ định một cách cụ thể để thực hiện mọi việc. Tôi hy vọng rằng bạn sẽ cân nhắc những tác dụng phụ (dự đoán và không dự đoán) mà bạn có thể gặp phải và sau đó chọn một phương án hành động phù hợp với nhu cầu của mình. Quá thường xuyên, chúng ta tìm kiếm các giải pháp đã công bố cho các vấn đề của người khác mà không quan tâm đến việc liệu mã đó có tạo ra nhiều vấn đề hơn là giải quyết được hay không.

Viết mã thuận tiện​

Các tham số hàm trong JavaScript cung cấp một cách thuận tiện để xác định đầu vào nào bạn muốn kiểm soát. Giống như quyết định nên đặt núm nào trên TV.

Chúng ta có thể viết CSS dễ kiểm soát như vậy. Để làm được điều đó, hãy cùng xem cách tránh hai vấn đề mà chúng ta có thể gặp phải khi viết hàm JavaScript:
  • Quá nhiều tham số.
    Cấu hình càng nhiều thì càng tốn kém và khó sử dụng.
  • Không đủ tham số.
    Cấu hình càng ít thì có thể không cung cấp đủ quyền kiểm soát cho các trường hợp sử dụng của bạn.

Quá nhiều tham số​

Giả sử chúng ta có một hàm JavaScript bật và tắt bóng đèn. Nếu mục tiêu của chúng ta là làm cho hàm dễ sử dụng nhất có thể, có lẽ chúng ta sẽ muốn có một tham số trong đó để xác định trạng thái của bóng đèn.

Đây là một hàm tiện lợi và dễ sử dụng:
Mã:
switchLightbulb(ON);
So sánh hàm đó với hàm phức tạp đến phát đau này:
Mã:
switchLightbulb(getConnectedWires, isCompleteCircuit, setUpBattery, batteryStatus, isUsingWallOutlet, hasPowerOutage, isElectricityBillPaid, v.v.);
Trong trường hợp này, chúng ta chỉ muốn bật bóng đèn, nhưng khi có quá nhiều tham số, chúng ta có quá nhiều bước khác để hoàn thành. Chắc chắn, tất cả các tham số khác đó đều gọn gàng và có thể hữu ích trong một số tình huống. Hoặc có thể không. Dù sao đi nữa, chúng nằm ngoài phạm vi mục tiêu thiết yếu của hàm: chuyển đổi giữa trạng thái BẬT và trạng thái TẮT.

Một ý tưởng tương tự cũng áp dụng cho CSS. Giả sử chúng ta có một thẻ có hình ảnh, văn bản tiêu đề, văn bản nội dung và một nút. Chúng ta muốn dễ dàng thay đổi chiều rộng của thẻ.

Dòng lệnh này dễ sử dụng và dễ hiểu:
Mã:
.card { max-width: 300px;}
Một cách tiếp cận ít thuận tiện hơn là xác định rõ ràng max-width của từng thành phần .card:
Mã:
.card-image { max-width: 300px;}.card-title-text { max-width: 300px;}.card-body-text { max-width: 300px;}.card-button { max-width: 300px;}
Nếu chúng ta muốn thay đổi kích thước thẻ trong ví dụ thứ hai, chúng ta cần thực hiện bốn lần cập nhật để đạt được một thay đổi. Đối với trường hợp sử dụng của chúng ta, ví dụ đầu tiên thuận tiện hơn nhiều để làm việc.

Quá ít tham số​

Bây giờ chúng ta đã thấy điều gì có thể xảy ra với quá nhiều tham số, hãy xem điều gì có thể xảy ra với quá ít. Giả sử chúng ta có một hàm thiết lập tốc độ của ô tô và góc quay của ô tô theo độ. Chúng ta sẽ cần ít nhất hai tham số cho các thuộc tính đó.

Hàm của chúng ta có thể trông như thế này:
Mã:
setCarState({ speed: 60, turnAngleDegrees: 2 });
Chúng ta có thể cố gắng cô đọng hơn và kết hợp các tham số:
Mã:
setCarState({ speedAndTurnAngle: 60 });
Chắc chắn, cô đọng, nhưng cũng rất đáng sợ khi sử dụng. Hãy tưởng tượng một chiếc ô tô mà khi tăng tốc cũng làm quay vô lăng! Chúng ta chắc chắn không muốn gộp tốc độ và góc quay thành một tham số duy nhất vì tham số đó không cung cấp đủ khả năng kiểm soát và nó tạo ra các tác dụng phụ đáng báo động.

Trong CSS, có những trường hợp tương tự khi chúng ta muốn kiểm soát một tham số mà không ảnh hưởng đến tham số khác. Ví dụ, có lẽ chúng ta có một thành phần thẻ khác, lần này có ảnh, tên, tiểu sử ngắn và nút "Đọc thêm" và chúng ta muốn thay đổi chiều rộng của thẻ mà không ảnh hưởng đến kích thước của ảnh.

Thật không may, phương pháp max-width mà chúng ta đã sử dụng trước đó trên vùng chứa thẻ sẽ cung cấp quá ít tham số cho nhu cầu của chúng ta:
Mã:
.card { max-width: 300px;}
Như chúng ta đã lưu ý trước đó, các phần tử con của thẻ sẽ thích ứng với chiều rộng vùng chứa thẻ lên đến 300px. Bao gồm cả hình ảnh, sẽ co lại khi chiều rộng vùng chứa co lại dưới 300px.

Những gì chúng ta cần là một tham số nữa cho ảnh:
Mã:
.card { max-width: 300px;}.photo { width: max(150px, 50%);}
Vì chúng ta muốn thay đổi chiều rộng của ảnh độc lập với thẻ, nên chúng ta cần đủ tham số để có thể kiểm soát ở mức độ đó. Trong trường hợp này, điều đó có nghĩa là cung cấp cho ảnh một chiều rộng riêng biệt với thẻ bằng cách đặt chiều rộng đó trên một lớp được giới hạn trong ảnh.

Cho dù bạn cần nhiều tham số hơn hay ít hơn, phần quan trọng là xem xét trường hợp sử dụng của bạn.

Viết Kiểu đàn hồi​

Khi viết một hàm, bạn nên hỏi, Điều gì xảy ra với đầu ra khi đầu vào thay đổi?

Chúng ta có thể hỏi các biến thể của cùng một câu hỏi đó trong CSS, chẳng hạn như:
  • Nếu chiều rộng của một phần tử bị hạn chế, điều gì sẽ xảy ra với chiều cao của phần tử đó?
  • Nếu một phần tử trượt vào từ bên cạnh cửa sổ, điều gì sẽ xảy ra với khả năng truy cập của trang?
  • Nếu người dùng chuyển từ chuột sang chạm, điều gì sẽ xảy ra với các tương tác khi di chuột qua?
Giả sử chúng ta có một bố cục với ba thẻ được sắp xếp theo chiều ngang trong một vùng chứa.



CSS đặt max-width: 900px trên vùng chứa và mỗi thẻ có một chút không gian thở với padding: 5vw. Nhìn bề ngoài thì có vẻ ổn, nhưng có một vấn đề: vùng chứa có giới hạn trên trong khi phần đệm thì không. Khi màn hình rộng hơn, nội dung sẽ bị nén lại.

Xem Bút [Ví dụ về đệm nén nội dung [phân nhánh]](https://codepen.io/smashingmag/pen/RwepaQQ) của Yaphi.
Xem Bút Ví dụ về đệm nén nội dung [phân nhánh] của Yaphi.
Các giải pháp khả thi bao gồm:
  • Sử dụng khung nhìn hoặc vùng chứa breakpoint để giữ cho phần đệm được kiểm soát,
  • Sử dụng hàm CSS min() để đặt giới hạn trên cho phần đệm hoặc
  • Sử dụng các đơn vị cố định, chẳng hạn như pixel, sẽ không tăng vô hạn theo cửa sổ.
Điểm chung của các giải pháp này là chúng tính đến những gì xảy ra khi chiều rộng của khung nhìn thay đổi. Tương tự như vậy, chúng ta có thể tránh được nhiều vấn đề về CSS bằng cách coi bố cục là đầu ra và dự đoán những gì có thể xảy ra khi đầu vào thay đổi.

Ahmad Shadeed có một cái tên tuyệt vời cho kỹ thuật này: Defensive CSS. Ý tưởng là chúng ta có thể "bảo vệ tương lai" cho các kiểu dáng bằng cách coi chúng là đầu vào tạo ra giao diện người dùng (UI) và dự đoán các tình huống có thể làm giảm khả năng sử dụng của đầu ra.

Kết luận​

Việc viết mã cho một bố cục không phải là sắp xếp mọi thứ trên một trang mà là mô tả cách chúng phản ứng với sự thay đổi. Vì lý do đó, việc coi CSS như hằng số thay vì hàm là rất nguy hiểm.

May mắn thay, những ý tưởng giúp chúng ta viết hàm tốt cũng có thể giúp chúng ta viết CSS tốt, cụ thể là:
  • Tránh các tác dụng phụ không mong muốn.
  • Sử dụng đúng tham số.
  • Xem xét cách đầu vào thay đổi đầu ra.
Tôi hy vọng bạn sẽ tự hỏi mình câu hỏi này vào lần viết CSS tiếp theo, Bố cục này nên phản ứng thế nào với sự thay đổi?

Đọc thêm trên SmashingMag​

  • Cách tạo biểu đồ Donut động bằng TailwindCSS và React, Paul Scanlon
  • Cách xây dựng bố cục tạp chí bằng vùng lưới CSS, Jennifer Brehm
  • Nâng cao kỹ năng CSS của bạn bằng bộ chọn :has(), Stephanie Eckles
  • Các tính năng CSS ít được biết đến và ít được sử dụng Vào năm 2022, Adrian Bece
 
Back
Bên trên