สำรวจ SMACSS: สถาปัตยกรรมที่ปรับขนาดได้และโมดูลาร์สำหรับ CSS
เผยแพร่แล้ว: 2022-03-11เมื่อเราทำงานในโครงการขนาดใหญ่หรือกับกลุ่มนักพัฒนา เรามักจะพบว่าโค้ดของเรายุ่งเหยิง อ่านยาก และขยายออกยาก นี่เป็นเรื่องจริงโดยเฉพาะอย่างยิ่งเมื่อเวลาผ่านไป และเรากลับมาดูอีกครั้ง—เราต้องพยายามอยู่ในกรอบความคิดเดียวกันกับที่เราเคยเขียนไว้
สิ่งที่หลายคนทำคือพวกเขาได้สร้างสถาปัตยกรรม CSS เพื่อช่วยในการจัดรูปแบบโค้ดเพื่อให้ CSS อ่านง่ายขึ้น SMACSS —ie , Scalable and Modular Architecture for CSS —มีจุดมุ่งหมายที่จะทำเช่นนั้น เป็นชุดแนวทางสถาปัตยกรรม CSS โดยเฉพาะจาก Jonathan Snook ที่ฉันนำมาใช้
แนวทางสถาปัตยกรรมของ SMACSS แตกต่างไปจากเฟรมเวิร์ก CSS เช่น Bootstrap หรือ Foundation เล็กน้อย แต่เป็นชุดของกฎเกณฑ์ เหมือนกับเทมเพลตหรือคู่มือมากกว่า มาดูรูปแบบการออกแบบ CSS เพื่อดูว่าเราจะใช้รูปแบบเหล่านี้เพื่อทำให้โค้ดของเราดีขึ้น สะอาดขึ้น อ่านง่ายขึ้น และเป็นแบบแยกส่วนได้อย่างไร
โครงสร้างโครงการ SMACSS ทั้งหมดใช้ห้าหมวดหมู่:
- ฐาน
- เค้าโครง
- โมดูล
- สถานะ
- ธีม
ฐาน
ใน SMACSS สไตล์พื้นฐานจะกำหนดว่าองค์ประกอบควรมีลักษณะอย่างไรที่ใดก็ได้บนหน้า พวกเขาเป็นค่าเริ่มต้น หากคุณกำลังใช้สไตล์ชีตรีเซ็ต วิธีนี้จะช่วยให้แน่ใจว่าสไตล์ผลลัพธ์ของคุณจะเหมือนกันในเบราว์เซอร์ต่างๆ แม้ว่าจะมีความแตกต่างระหว่างค่าเริ่มต้น CSS พื้นฐานภายในที่ฮาร์ดโค้ดไว้ก็ตาม
ในสไตล์พื้นฐาน คุณควรรวมเฉพาะตัวเลือกองค์ประกอบเปล่า หรือตัวเลือกที่มีคลาสหลอก แต่ไม่รวมถึงตัวเลือกคลาสหรือ ID (คุณควรมีเหตุผลที่ดีจริง ๆ ที่จะใส่คลาสหรือ ID ไว้ข้างใน บางทีก็ต่อเมื่อคุณกำหนดสไตล์ให้กับองค์ประกอบของปลั๊กอินของบุคคลที่สาม และคุณจำเป็นต้องแทนที่สไตล์เริ่มต้นสำหรับองค์ประกอบนั้นโดยเฉพาะ)
นี่คือตัวอย่างลักษณะของหน่วยไฟล์พื้นฐาน:
html { margin: 0; font-family: sans-serif; } a { color: #000; } button { color: #ababab; border: 1px solid #f2f2f2; }
ดังนั้นควรรวมถึงขนาดเริ่มต้น ระยะขอบ สี เส้นขอบ และค่าเริ่มต้นอื่นๆ ที่คุณวางแผนจะใช้ในเว็บไซต์ของคุณ ตัวพิมพ์และองค์ประกอบของแบบฟอร์มควรมีลักษณะที่เป็นหนึ่งเดียวซึ่งปรากฏบนทุกหน้า และให้ความรู้สึกและรูปลักษณ์ว่าเป็นส่วนหนึ่งของการออกแบบและธีมเดียวกัน
SMACSS หรือไม่ ฉันขอแนะนำอย่างยิ่งให้หลีกเลี่ยงการใช้ !important
ให้มากที่สุด และอย่าใช้ Deep nesting แต่ฉันจะพูดถึงเรื่องนี้มากขึ้นในโพสต์นี้ นอกจากนี้ หากแนวทางปฏิบัติของคุณคือการใช้ reset CSS นี่คือที่ที่คุณควรรวมไว้ (ฉันชอบใช้ Sass มากกว่า ดังนั้นฉันจึงรวมไว้ที่ด้านบนของไฟล์ แทนที่จะต้องคัดลอกหรืออ้างอิงแยกจากองค์ประกอบ <head>
ของแต่ละหน้า)
เค้าโครง
สไตล์เลย์เอาต์จะแบ่งหน้าออกเป็นส่วนๆ เช่น ไม่ใช่ส่วนต่างๆ เช่น การนำทางหรือหีบเพลง แต่เป็นการแบ่งส่วนระดับบนสุดจริงๆ
เลย์เอาต์เหล่านี้จะมีโมดูล CSS หลายตัว เช่น กล่อง การ์ด รายการที่ไม่เรียงลำดับ แกลเลอรี และอื่นๆ แต่ฉันจะพูดถึงโมดูลเพิ่มเติมในหัวข้อถัดไป ลองพิจารณาหน้าเว็บตัวอย่างเพื่อดูว่าเราสามารถแบ่งออกเป็นรูปแบบใดบ้าง:
ที่นี่เรามีส่วนหัว หลัก และส่วนท้าย เลย์เอาต์เหล่านี้มีโมดูลต่างๆ เช่น ลิงก์และโลโก้ที่ส่วนหัวด้านบน กล่องและบทความในหัวข้อหลัก และลิงก์และลิขสิทธิ์สำหรับส่วนท้าย เรามักจะให้ตัวเลือก ID แก่เลย์เอาต์ เนื่องจากเลย์เอาต์ไม่ได้แสดงซ้ำบนเพจและมีเอกลักษณ์เฉพาะตัว
นอกจากนี้ คุณควรนำหน้ากฎสำหรับรูปแบบเค้าโครงด้วยตัวอักษร l
เพื่อแยกความแตกต่างออกจากรูปแบบโมดูล โดยปกติแล้ว คุณจะต้องจัดรูปแบบสิ่งต่างๆ ให้กับเลย์เอาต์ เช่น เส้นขอบ การจัดแนว ระยะขอบ ฯลฯ นอกจากนี้ พื้นหลังของส่วนนั้นของหน้าก็อาจดูสมเหตุสมผล แม้ว่าจะดูไม่เจาะจงเลย์เอาต์เท่าไหร่ก็ตาม
นี่คือตัวอย่างว่าควรมีลักษณะอย่างไร:
#header { background: #fcfcfc; } #header .l-right { float: right; } #header .l-align-center { text-align: center; }
คุณยังสามารถเพิ่มตัวช่วยเหล่านี้สำหรับการจัดตำแหน่ง ซึ่งคุณสามารถใช้เพื่อจัดตำแหน่งองค์ประกอบได้อย่างง่ายดายโดยเพียงแค่เพิ่มคลาสที่เหมาะสมให้กับลูกหรือเพื่อจัดแนวข้อความ
สำหรับตัวอย่างอื่น คุณสามารถใช้ระยะขอบเริ่มต้นบางอย่างในกล่องเค้าโครง เช่น .l-margin
ที่มีระยะขอบ 20px
จากนั้น ทุกที่ที่คุณต้องการช่องว่างภายในสำหรับคอนเทนเนอร์ องค์ประกอบ การ์ด หรือกล่อง คุณเพียงแค่เพิ่มคลาส l-margin
ลงไป แต่คุณต้องการบางสิ่งที่นำกลับมาใช้ใหม่ได้:
.l-full-width { width: 100%; }
ไม่ใช่สิ่งที่เชื่อมต่อภายในเช่นนี้:
.l-width-25 { width: 25px; }
ฉันต้องการใช้เวลาสักครู่เพื่อพูดคุยเกี่ยวกับหลักการตั้งชื่อใน SMACSS หากคุณไม่เคยได้ยินแนวคิดของเนมสเปซใน CSS โดยทั่วไปแล้ว จะเป็นการเพิ่มชื่อที่จุดเริ่มต้นขององค์ประกอบอื่นเพื่อช่วยแยกความแตกต่างจากสิ่งอื่น แต่ทำไมเราถึงต้องการสิ่งนี้?
ฉันไม่รู้ว่าคุณเคยประสบปัญหาต่อไปนี้หรือไม่ คุณกำลังเขียน CSS และคุณมีป้ายกำกับสำหรับบางสิ่ง คุณใส่สไตล์ใดก็ได้ที่คุณต้องการ และเรียกคลาสของคุณ . .label
แต่หลังจากนั้น คุณมาที่องค์ประกอบอื่นในภายหลัง และคุณต้องการให้เป็น .label
แต่จัดรูปแบบให้แตกต่างออกไป ดังนั้นสองสิ่งที่แตกต่างกันจึงมีชื่อเหมือนกัน—การตั้งชื่อที่ขัดแย้งกัน
Namespacing ช่วยคุณแก้ปัญหานี้ได้ ในท้ายที่สุด สิ่งเหล่านี้ถูกเรียกว่าเป็นสิ่งเดียวกันในระดับหนึ่ง แต่มีเนมสเปซที่แตกต่างกัน—คำนำหน้าที่แตกต่างกัน—และสามารถแสดงสไตล์ที่แตกต่างกันสองแบบ:
.box--label { color: blue; } .card--label { color: red; }
โมดูล
เช่นเดียวกับที่ฉันได้กล่าวไว้ก่อนหน้านี้ โมดูล SMACSS เป็นโค้ดขนาดเล็กกว่าที่นำมาใช้ใหม่ได้บนหน้า และเป็นส่วนหนึ่งของเค้าโครงเดียว นี่เป็นส่วนหนึ่งของ CSS ที่เราต้องการจัดเก็บไว้ในโฟลเดอร์แยกต่างหาก เนื่องจากเราจะมีสิ่งเหล่านี้จำนวนมากในหน้าเดียว และเมื่อโปรเจ็กต์เติบโตขึ้น เราสามารถแบ่งได้โดยใช้แนวทางปฏิบัติที่ดีที่สุดของโครงสร้างโฟลเดอร์ กล่าวคือ ตามโมดูล/หน้า:

ในตัวอย่างก่อนหน้านี้ เรามีบทความซึ่งสามารถเป็นโมดูลได้ด้วยตัวเอง CSS ควรมีโครงสร้างอย่างไรที่นี่ เราควรจะมี class .article
ซึ่งสามารถมี title
องค์ประกอบลูกและ text
ได้ เพื่อให้สามารถเก็บไว้ในโมดูลเดียวกันได้ เราจำเป็นต้องเติมคำนำหน้าองค์ประกอบย่อย:
.article { background: #f32; } .article--title { font-size: 16px; } .article--text { font-size: 12px; }
คุณอาจสังเกตเห็นว่าเราใช้ยัติภังค์สองตัวหลังคำนำหน้าโมดูล เหตุผลก็คือบางครั้งชื่อโมดูลมีสองคำหรือคำนำหน้าของตัวเอง เช่น big-article
ใหญ่ เราจำเป็นต้องมียัติภังค์สองตัวเพื่อบอกว่าส่วนใดขององค์ประกอบย่อย—เช่น เปรียบเทียบ big-article-title
กับ big-article--title
และ big-article--text
นอกจากนี้ คุณสามารถซ้อนโมดูลภายในโมดูลได้ หากโมดูลใดใช้พื้นที่ส่วนใหญ่ของหน้า:
<div class="box"> <div class="box--label">This is box label</div> <ul class="box--list list"> <li class="list--li">Box list element</li> </ul> </div>
ในตัวอย่างง่ายๆ นี้ คุณจะเห็นว่า box
นั้นเป็นโมดูล และ list
นั้นเป็นอีกโมดูลหนึ่งในนั้น ดังนั้น list--li
เป็นลูกของโมดูล list
และไม่ใช่ของ box
แนวคิดหลักประการหนึ่งที่นี่คือการใช้ตัวเลือกสูงสุดสองตัวต่อกฎ CSS แต่ละรายการ แต่ในสถานการณ์ส่วนใหญ่จะมีตัวเลือกเพียงตัวเดียวพร้อมคำนำหน้า
ด้วยวิธีนี้ เราสามารถหลีกเลี่ยงกฎที่ซ้ำกันและยังมีตัวเลือกเพิ่มเติมสำหรับองค์ประกอบย่อยที่มีชื่อเดียวกัน ซึ่งจะช่วยปรับปรุงความเร็ว แต่ยังช่วยเราหลีกเลี่ยงการใช้กฎ !important
-style ที่ไม่ต้องการ ซึ่งเป็นสัญญาณของโปรเจ็กต์ CSS ที่มีโครงสร้างไม่ดี
ดี (สังเกตตัวเลือกเดียว):
.red--box { background: #fafcfe; } .red-box--list { color: #000; }
ไม่ดี (สังเกตการซ้ำซ้อนภายในตัวเลือกและวิธีการอ้างอิงที่ทับซ้อนกัน):
.red .box { background: #fafcfe; } .red .box .list { color: #000; } .box ul { color: #fafafa; }
สถานะ
สถานะใดที่กำหนดใน SMACSS เป็นวิธีอธิบายว่าโมดูลของเรามีลักษณะอย่างไรในสถานการณ์แบบไดนามิกที่แตกต่างกัน ดังนั้นส่วนนี้จึงเหมาะสำหรับการโต้ตอบ: เราต้องการพฤติกรรมที่แตกต่างกันหากองค์ประกอบถูกพิจารณาว่าถูกซ่อน ขยาย หรือแก้ไข ตัวอย่างเช่น หีบเพลง jQuery ต้องการความช่วยเหลือในการกำหนดเมื่อคุณสามารถหรือไม่เห็นเนื้อหาขององค์ประกอบ ช่วยให้เรากำหนดรูปแบบขององค์ประกอบในเวลาที่กำหนด
สถานะถูกนำไปใช้กับองค์ประกอบเดียวกันกับเลย์เอาต์ ดังนั้นเราจึงเพิ่มกฎเพิ่มเติมซึ่งจะแทนที่กฎก่อนหน้า หากมี กฎของรัฐมีความสำคัญกว่าเนื่องจากเป็นกฎสุดท้ายในสายโซ่
เช่นเดียวกับรูปแบบเลย์เอาต์ เรามักจะใช้คำนำหน้าที่นี่ สิ่งนี้ช่วยให้เราจดจำและให้ความสำคัญกับพวกเขา ที่นี่เราใช้คำนำหน้า is
เช่นใน is-hidden
หรือ is-selected
<header> <ul class="nav"> <li class="nav--item is-selected">Contact</li> <li class="nav--item">About</li> </ul> </header>
.nav--item.is-selected { color: #fff; }
ในที่นี้ อาจใช้ !important
เนื่องจาก state มักใช้เป็นการแก้ไข JavaScript ไม่ใช่เวลาแสดงผล ตัวอย่างเช่น คุณมีองค์ประกอบที่ซ่อนอยู่ในการโหลดหน้าเว็บ เมื่อคลิกปุ่มคุณต้องการแสดง แต่คลาสเริ่มต้นจะเป็นดังนี้:
.box .element { display: none; }
ดังนั้นหากคุณเพิ่มสิ่งนี้:
.is-shown { display: block; }
มันจะยังคงซ่อนอยู่แม้ว่าคุณจะเพิ่มคลาส .is-shown
ให้กับองค์ประกอบผ่าน JavaScript เนื่องจากกฎข้อแรกมีความลึกสองระดับและจะแทนที่กฎนั้น
ดังนั้นคุณสามารถกำหนดคลาสของรัฐได้ดังนี้:
.is-shown { display: block !important; }
นี่คือวิธีที่เราแยกแยะตัวแก้ไขสถานะออกจากตัวแก้ไขรูปแบบ ซึ่งมีผลกับการโหลดเริ่มต้นของหน้าเท่านั้น สิ่งนี้จะได้ผลในขณะที่ยังคงข้อได้เปรียบของตัวเลือกขั้นต่ำ
ธีม
อันนี้ควรมีความชัดเจนที่สุด เนื่องจากมันถูกใช้เพื่อให้มีกฎของสีหลัก, รูปร่าง, เส้นขอบ, เงา และอื่นๆ ส่วนใหญ่เป็นองค์ประกอบที่ทำซ้ำทั่วทั้งเว็บไซต์ เราไม่ต้องการกำหนดใหม่ทุกครั้งที่เราสร้าง เราต้องการกำหนดคลาสเฉพาะที่เราเพิ่มในภายหลังให้กับองค์ประกอบเริ่มต้นเท่านั้น
.button-large { width: 60px; height: 60px; }
<button class="button-large">Like</button>
อย่าสับสนกฎของธีม SMACSS เหล่านี้กับกฎพื้นฐาน เนื่องจากกฎพื้นฐานกำหนดเป้าหมายเฉพาะลักษณะเริ่มต้น และมีแนวโน้มที่จะเป็นเหมือนการรีเซ็ตการตั้งค่าเบราว์เซอร์เริ่มต้น ในขณะที่ยูนิตของธีมเป็นประเภทของการจัดสไตล์มากกว่าที่จะให้รูปลักษณ์สุดท้าย เฉพาะสำหรับชุดสีเฉพาะนี้
กฎของธีมยังมีประโยชน์หากไซต์มีรูปแบบมากกว่าหนึ่งรูปแบบหรืออาจมีสองรูปแบบที่ใช้ในสถานะต่างๆ กัน ดังนั้นจึงสามารถเปลี่ยนแปลงหรือสลับระหว่างเหตุการณ์บางอย่างบนหน้าเว็บได้อย่างง่ายดาย เช่น ด้วยปุ่มเปลี่ยนธีม อย่างน้อยที่สุด พวกเขาเก็บรูปแบบธีมทั้งหมดไว้ในที่เดียว เพื่อให้คุณสามารถเปลี่ยนได้อย่างง่ายดายและจัดระเบียบอย่างสวยงาม
ระเบียบวิธีขององค์กร CSS
ฉันได้กล่าวถึงแนวคิดหลักบางประการของแนวคิดสถาปัตยกรรม CSS นี้แล้ว หากคุณต้องการเรียนรู้เพิ่มเติมเกี่ยวกับแนวคิดนี้ คุณสามารถเยี่ยมชมเว็บไซต์อย่างเป็นทางการของ SMACSS และเจาะลึกลงไปได้
ได้ คุณสามารถใช้วิธีการขั้นสูง เช่น OOCSS และ BEM หลังครอบคลุมเวิร์กโฟลว์ส่วนหน้าเกือบทั้งหมดและเทคโนโลยี ตัวเลือก BEM อาจใช้ได้ผลดีสำหรับบางคน แต่บางคนอาจพบว่าตัวเลือกยาวเกินไปและยากเกินไป และยังซับซ้อนเกินไปที่จะใช้งาน หากคุณต้องการบางสิ่งที่ง่ายกว่าซึ่งง่ายต่อการหยิบจับและรวมเข้ากับเวิร์กโฟลว์ของคุณ—และสิ่งที่กำหนดกฎพื้นฐานสำหรับคุณและทีมของคุณ SMACSS เหมาะสมอย่างยิ่ง
สมาชิกในทีมใหม่จะเป็นเรื่องง่าย ไม่เพียงแต่จะเข้าใจว่านักพัฒนาคนก่อนๆ ทำอะไร แต่ยังสามารถเริ่มทำงานได้ทันที โดยไม่มีความแตกต่างในสไตล์การเขียนโค้ด SMACSS เป็นเพียงสถาปัตยกรรม CSS และทำงานตามที่กล่าวไว้ในดีบุก ไม่มีอะไรมากไปกว่านี้และไม่น้อยไปกว่านั้น