Kodokon kodokon.com

สถาปัตยกรรม CSS: BEM, utility-first, การจัดระเบียบไฟล์

เปรียบเทียบ BEM กับแนวทาง utility-first ข้อดีข้อเสียที่แท้จริงในทีม และจัดโครงสร้างไฟล์ของคุณด้วย cascade layers

11 นาที · 3 คำถาม

เปิดบทเรียนนี้ใน Kodokon

CSS ไม่พังในระดับของ component มันพังในระดับของโปรเจกต์: specificity ที่ไต่สูงขึ้นเพียงเพื่อ "เอาชนะ" กฎที่มีอยู่แล้ว, ความกลัวที่จะลบโค้ดที่คุณไม่รู้ขอบเขตการเข้าถึง, สไตล์ที่รั่วจากหน้าจอหนึ่งไปอีกหน้าจอ สถาปัตยกรรม CSS ก่อนอื่นเลยคือ กลยุทธ์สำหรับจัดการ specificity และ scope สองสำนักที่ครองตลาด BEM กับ utility-first ตอบปัญหาเดียวกันด้วยเส้นทางที่ตรงกันข้าม

BEM (Block, Element, Modifier) ตั้งชื่อทุกโหนดที่ถูกจัดสไตล์: .card (block ที่อยู่ได้เอง), .card__title (element ที่เป็นของ block), .card--featured (variant ของ block) แบบแผนนี้รับประกัน specificity แบบ แบนราบ คลาสเดียวทุกที่ ไม่มี descendant selector เลย จึงไม่มีความขัดแย้งเรื่องลำดับ และชื่อที่บอกความเป็นเจ้าของ ต้นทุนคือ: ต้องคิดชื่อตลอดเวลา และการซ้ำซ้อนของค่าอย่างแท้จริง (margin-top: 16px เดียวกันถูกเขียนซ้ำในยี่สิบ block)

CSS
.card {
  padding: 16px;
  border-radius: 8px;
}

.card__title {
  font-size: 1.25rem;
}

.card__title--muted {
  color: #6b7280;
}

.card--featured {
  border: 2px solid var(--brand);
}
BEM: specificity แบบแบนราบ ความเป็นเจ้าของที่ชัดเจน

แนวทาง utility-first (โดยมี Tailwind เป็นตัวแทนที่ครองตลาด) พลิกตรรกะ: ไม่มีชื่ออีกต่อไป มีแค่คลาสอะตอมที่รับผิดชอบอย่างเดียวประกอบเข้าด้วยกันใน HTML ประโยชน์วัดได้จริง: ไม่ต้องออกแรงตั้งชื่อ, สไตล์อยู่ร่วมกับ markup (การลบ component ก็ลบสไตล์ของมันไปด้วย) และ CSS bundle สุดท้ายที่มีขนาดเกือบคงที่เพราะ utility ถูกใช้ร่วมกัน ข้อแลกเปลี่ยนก็มี: HTML ที่ยืดยาว, การซ้ำซ้อนที่ย้ายไปอยู่ที่ markup ซึ่งต้องมี component layer (React, Vue, partials) เพื่อให้คง DRY ไว้ และเส้นโค้งการเรียนรู้สำหรับคำศัพท์

HTML
<article class="flex flex-col gap-4 rounded-lg
  border border-gray-200 p-4 shadow-sm">
  <h2 class="text-lg font-semibold">Invoice</h2>
  <p class="text-sm text-gray-600">
    Paid on March 12
  </p>
</article>
Utility-first: สไตล์อาศัยอยู่ใน markup

แนวทางเหล่านี้ไม่ได้ใช้ทดแทนกันไม่ได้: ทีมที่โตเต็มที่หลายทีมผสมมันเข้าด้วยกัน utility สำหรับ layout และ spacing, คลาส component (BEM หรือ CSS Modules) สำหรับรูปแบบและสถานะที่ซับซ้อน ส่วนเรื่องการจัดระเบียบไฟล์ หลักการ ITCSS ยังคงเป็นมาตรฐานอ้างอิง: เรียงลำดับจากทั่วไปไปเฉพาะเจาะจง (reset, base elements, components, utilities) กฎ @layer แบบเนทีฟเปลี่ยนแบบแผนนี้ให้เป็นการรับประกันในระดับเอนจิน: ลำดับความสำคัญระหว่างเลเยอร์ เป็นอิสระจาก specificity ของ selector ที่มันบรรจุอยู่

CSS
@layer reset, base, components, utilities;

@layer base {
  h2 { font-size: 1.5rem; }
}

@layer components {
  #hero .btn { padding: 12px 24px; }
}

@layer utilities {
  .p-0 { padding: 0; }
}
Cascade layers: ลำดับชนะ specificity

ทดสอบความรู้

ตรวจสอบว่าคุณจำประเด็นสำคัญของบทเรียนนี้ได้ครบถ้วน

  1. ใน .card__title--muted ส่วน --muted หมายถึงอะไร?
    • CSS custom property
    • Modifier: variant ของ element card__title
    • element ลูกของ card__title
    • สถานะที่จัดการโดย JavaScript
  2. การมีส่วนร่วมหลักของ @layer ต่อ cascade คืออะไร?
    • มันเพิ่ม specificity ของ selector ในเลเยอร์
    • มันทำให้ลำดับของเลเยอร์มีความสำคัญเหนือ specificity ของ selector
    • มันแยกสไตล์ของ component ออกมาแบบเดียวกับที่ Shadow DOM ทำ
    • มันโหลดเลเยอร์แบบขนานเพื่อเร่งการเรนเดอร์
  3. คุณยอมรับข้อแลกเปลี่ยนใดเมื่อเลือกใช้แนวทาง utility-first?
    • ไฟล์ CSS ที่โตขึ้นตามสัดส่วนของจำนวนหน้า
    • specificity ที่สูงและ override ได้ยาก
    • HTML ที่ยืดยาวซึ่งการลดความซ้ำซ้อนพึ่งพา component layer