Global vs. Local Styling ใน Next.js

เผยแพร่แล้ว: 2022-03-10
สรุปอย่างย่อ ↬ Next.js มีความคิดเห็นที่หนักแน่นเกี่ยวกับวิธีการจัดระเบียบ JavaScript แต่ไม่ใช่ CSS เราจะพัฒนารูปแบบที่สนับสนุนแนวทางปฏิบัติ CSS ที่ดีที่สุดในขณะที่ปฏิบัติตามลอจิกของเฟรมเวิร์กได้อย่างไร คำตอบนั้นง่ายอย่างน่าประหลาดใจ — ในการเขียน CSS ที่มีโครงสร้างอย่างดีซึ่งสร้างสมดุลให้กับความกังวลเกี่ยวกับสไตล์ระดับโลกและระดับท้องถิ่น

ฉันมีประสบการณ์ที่ยอดเยี่ยมในการใช้ Next.js เพื่อจัดการโครงการส่วนหน้าที่ซับซ้อน Next.js มีความคิดเห็นเกี่ยวกับวิธีการจัดระเบียบโค้ด JavaScript แต่ไม่มีความคิดเห็นในตัวเกี่ยวกับวิธีการจัดระเบียบ CSS

หลังจากทำงานภายในเฟรมเวิร์กแล้ว ฉันได้พบชุดรูปแบบการจัดองค์กรที่ฉันเชื่อว่าทั้งสองสอดคล้องกับปรัชญานำทางของ Next.js และปฏิบัติตามแนวทางปฏิบัติ CSS ที่ดีที่สุด ในบทความนี้ เราจะสร้างเว็บไซต์ (ร้านน้ำชา!) ร่วมกันเพื่อแสดงรูปแบบเหล่านี้

หมายเหตุ : คุณอาจไม่จำเป็นต้องมีประสบการณ์ Next.js มาก่อน แม้ว่าจะมีความเข้าใจพื้นฐานเกี่ยวกับ React และเปิดใจเรียนรู้เทคนิค CSS ใหม่ๆ บ้างก็ดี

การเขียน CSS แบบ “ล้าสมัย”

เมื่อดู Next.js เป็นครั้งแรก เราอาจต้องพิจารณาใช้ไลบรารี CSS-in-JS บางประเภท แม้ว่าอาจมีประโยชน์ขึ้นอยู่กับโปรเจ็กต์ แต่ CSS-in-JS ก็มีข้อควรพิจารณาทางเทคนิคมากมาย ต้องใช้ไลบรารีภายนอกใหม่ ซึ่งจะเพิ่มขนาดของบันเดิล CSS-in-JS ยังสามารถมีผลกระทบต่อประสิทธิภาพโดยทำให้เกิดการเรนเดอร์และการพึ่งพาสถานะโกลบอลเพิ่มเติม

การอ่านที่แนะนำ : “ The Unseen Performance Costs of Modern CSS-in-JS Libraries In React Apps)” โดย Aggelos Arvanitakis

ยิ่งไปกว่านั้น จุดรวมของการใช้ไลบรารีอย่าง Next.js คือการแสดงเนื้อหาแบบสแตติกทุกครั้งที่ทำได้ ดังนั้นจึงไม่สมเหตุสมผลเลยที่จะเขียน JS ที่ต้องเรียกใช้ในเบราว์เซอร์เพื่อสร้าง CSS

มีคำถามสองสามข้อที่เราต้องพิจารณาเมื่อจัดรูปแบบภายใน Next.js:

เราจะเข้ากับอนุสัญญา/แนวปฏิบัติที่ดีที่สุดของกรอบงานได้อย่างไร?

เราจะสร้างสมดุลระหว่างความกังวลเกี่ยวกับสไตล์ "โดยรวม" (แบบอักษร สี เลย์เอาต์หลัก และอื่นๆ) กับสไตล์ "ในพื้นที่" (สไตล์เกี่ยวกับส่วนประกอบแต่ละรายการ) ได้อย่างไร

คำตอบที่ฉันคิดสำหรับคำถามแรกคือการ เขียน CSS แบบ เก่าที่ดี Next.js ไม่เพียงแต่รองรับการทำเช่นนั้นโดยไม่ต้องตั้งค่าเพิ่มเติม ยังให้ผลลัพธ์ที่มีประสิทธิภาพและคงที่

ในการแก้ปัญหาที่สอง ฉันใช้แนวทางที่สามารถสรุปได้เป็นสี่ส่วน:

  1. โทเค็นการออกแบบ
  2. สไตล์สากล
  3. คลาสยูทิลิตี้
  4. รูปแบบส่วนประกอบ

ฉันเป็นหนี้บุญคุณความคิดของ Andy Bell เกี่ยวกับ CUBE CSS (“องค์ประกอบ ยูทิลิตี้ บล็อก ข้อยกเว้น”) ที่นี่ หากคุณไม่เคยได้ยินหลักการขององค์กรนี้มาก่อน เราขอแนะนำให้คุณตรวจสอบเว็บไซต์อย่างเป็นทางการหรือคุณสมบัติใน Smashing Podcast หลักการหนึ่งที่เราจะนำมาจาก CUBE CSS คือแนวคิดที่เราควร ยอมรับ มากกว่าที่จะกลัว CSS cascade มาเรียนรู้เทคนิคเหล่านี้โดยนำไปใช้กับโครงการเว็บไซต์

เริ่มต้น

เราจะสร้างร้านน้ำชาเพราะว่าชาก็อร่อย เราจะเริ่มต้นด้วยการเรียกใช้งาน yarn create next-app เพื่อสร้างโครงการ Next.js ใหม่ จากนั้น เราจะลบทุกอย่างใน styles/ directory (เป็นโค้ดตัวอย่างทั้งหมด)

หมายเหตุ : หากต้องการติดตามผลงานที่ทำเสร็จแล้ว สามารถตรวจสอบได้ที่นี่

โทเค็นการออกแบบ

ในการตั้งค่า CSS แทบทุกประเภท จะมีประโยชน์ที่ชัดเจนในการ จัดเก็บค่าที่ใช้ร่วมกันทั้งหมดไว้ในตัวแปร หากลูกค้าขอให้เปลี่ยนสี การนำการเปลี่ยนแปลงไปใช้นั้นเป็นเพียงขั้นตอนเดียว แทนที่จะเป็นปัญหาในการค้นหาและแทนที่ ดังนั้น ส่วนสำคัญของการตั้งค่า Next.js CSS ของเราจะจัดเก็บค่าทั่วทั้งไซต์เป็น โทเค็นการออกแบบ

เราจะใช้คุณสมบัติที่กำหนดเอง CSS ในตัวเพื่อจัดเก็บโทเค็นเหล่านี้ (หากคุณไม่คุ้นเคยกับรูปแบบนี้ คุณสามารถดู "คู่มือกลยุทธ์สำหรับคุณสมบัติที่กำหนดเองของ CSS" ได้) ฉันควรพูดถึงว่า (ในบางโครงการ) ฉันเลือกใช้ตัวแปร SASS/SCSS เพื่อจุดประสงค์นี้ ฉันไม่พบข้อได้เปรียบที่แท้จริงใดๆ เลย ดังนั้นฉันมักจะรวม SASS ไว้ในโปรเจ็กต์หากพบว่าฉันต้องการฟีเจอร์ SASS อื่นๆ (มิกซ์-อิน การวนซ้ำ การนำเข้าไฟล์ และอื่นๆ) ในทางตรงกันข้าม คุณสมบัติที่กำหนดเองของ CSS ยังทำงานกับ cascade และสามารถเปลี่ยนแปลงได้เมื่อเวลาผ่านไป แทนที่จะคอมไพล์แบบสแตติก ดังนั้น สำหรับวันนี้ เรามาติด CSS ธรรมดา กัน

ในไดเร็กทอรี styles/ ของเรา มาสร้างไฟล์ design_tokens.css ใหม่:

 :root { --green: #3FE79E; --dark: #0F0235; --off-white: #F5F5F3; --space-sm: 0.5rem; --space-md: 1rem; --space-lg: 1.5rem; --font-size-sm: 0.5rem; --font-size-md: 1rem; --font-size-lg: 2rem; }

แน่นอน รายการนี้สามารถและจะเติบโตเมื่อเวลาผ่านไป เมื่อเราเพิ่มไฟล์นี้แล้ว เราต้องข้ามไปที่ไฟล์ pages/_app.jsx ซึ่งเป็นเลย์เอาต์หลักสำหรับเพจทั้งหมดของเรา และเพิ่ม:

 import '../styles/design_tokens.css'

ฉันชอบคิดว่าโทเค็นการออกแบบเป็นกาวที่รักษาความสม่ำเสมอทั่วทั้งโครงการ เราจะอ้างอิงตัวแปรเหล่านี้ในระดับโลก เช่นเดียวกับภายในส่วนประกอบแต่ละส่วน เพื่อให้มั่นใจว่าภาษาการออกแบบที่เป็นหนึ่งเดียว

เพิ่มเติมหลังกระโดด! อ่านต่อด้านล่าง↓

สไตล์สากล

ถัดไป มาเพิ่มหน้าเว็บไซต์ของเรากันเถอะ! ไปที่ไฟล์ pages/index.jsx กัน (นี่คือหน้าแรกของเรา) เราจะลบต้นแบบทั้งหมดและเพิ่มบางอย่างเช่น:

 export default function Home() { return <main> <h1>Soothing Teas</h1> <p>Welcome to our wonderful tea shop.</p> <p>We have been open since 1987 and serve customers with hand-picked oolong teas.</p> </main> }

น่าเสียดายที่มันจะดูค่อนข้างธรรมดา เรามากำหนด สไตล์โกลบอลสำหรับองค์ประกอบพื้นฐานกัน เช่น แท็ก <h1> (ฉันชอบคิดว่ารูปแบบเหล่านี้เป็น "ค่าเริ่มต้นทั่วโลกที่สมเหตุสมผล") เราอาจลบล้างได้ในบางกรณี แต่เป็นการคาดเดาที่ดีว่าเราต้องการอะไรหากไม่ต้องการ

ฉันจะใส่สิ่งนี้ในไฟล์ styles/globals.css (ซึ่งมาโดยค่าเริ่มต้นจาก Next.js):

 *, *::before, *::after { box-sizing: border-box; } body { color: var(--off-white); background-color: var(--dark); } h1 { color: var(--green); font-size: var(--font-size-lg); } p { font-size: var(--font-size-md); } p, article, section { line-height: 1.5; } :focus { outline: 0.15rem dashed var(--off-white); outline-offset: 0.25rem; } main:focus { outline: none; } img { max-width: 100%; }

แน่นอนว่าเวอร์ชันนี้ค่อนข้างพื้นฐาน แต่ไฟล์ globals.css ของฉันไม่จำเป็นต้องมีขนาดใหญ่เกินไป ที่นี่ ฉันจัดรูปแบบองค์ประกอบ HTML พื้นฐาน (ส่วนหัว เนื้อหา ลิงก์ และอื่นๆ) ไม่จำเป็นต้องห่อองค์ประกอบเหล่านี้ในส่วนประกอบ React หรือเพิ่มคลาสอย่างต่อเนื่องเพื่อจัดเตรียมรูปแบบพื้นฐาน

ฉันยังรวมการ รีเซ็ตสไตล์เบราว์เซอร์เริ่มต้น ด้วย ในบางครั้ง ฉันจะมีรูปแบบเลย์เอาต์ทั่วทั้งไซต์เพื่อให้มี "ส่วนท้ายที่เหนียว" แต่จะอยู่ที่นี่ก็ต่อเมื่อทุกหน้าใช้เลย์เอาต์เดียวกัน มิเช่นนั้นจะต้องกำหนดขอบเขตไว้ภายในส่วนประกอบแต่ละส่วน

ฉันมักจะใส่ :focus styling เพื่อ ระบุ องค์ประกอบเชิงโต้ตอบสำหรับผู้ใช้คีย์บอร์ดอย่างชัดเจนเมื่อโฟกัส ทางที่ดีควรทำให้มันเป็นส่วนสำคัญของ DNA การออกแบบของไซต์!

ตอนนี้เว็บไซต์ของเรากำลังเริ่มเป็นรูปเป็นร่าง:

รูปภาพของเว็บไซต์ระหว่างดำเนินการ พื้นหลังของหน้าเปลี่ยนเป็นสีน้ำเงินเข้ม และพาดหัว 'ชาผ่อนคลาย' เป็นสีเขียว เว็บไซต์ไม่มีเลย์เอาต์/ระยะห่าง จึงขยายความกว้างของหน้าต่างเบราว์เซอร์ได้อย่างสมบูรณ์
รูปภาพของเว็บไซต์ระหว่างดำเนินการ พื้นหลังของหน้าเปลี่ยนเป็นสีน้ำเงินเข้ม และพาดหัว 'ชาผ่อนคลาย' เป็นสีเขียว เว็บไซต์ไม่มีเลย์เอาต์/ระยะห่าง จึงขยายความกว้างของหน้าต่างเบราว์เซอร์ได้อย่างสมบูรณ์ (ตัวอย่างขนาดใหญ่)

คลาสยูทิลิตี้

พื้นที่หนึ่งที่หน้าแรกของเราสามารถปรับปรุงได้อย่างแน่นอนคือข้อความจะขยายออกไปที่ด้านข้างของหน้าจอเสมอ ดังนั้นเรามาจำกัดความกว้างของข้อความกันเถอะ เราต้องการเลย์เอาต์นี้ในหน้านี้ แต่ฉันคิดว่าเราอาจต้องการเลย์เอาต์นี้ในหน้าอื่นด้วย นี่เป็นกรณีการใช้งานที่ยอดเยี่ยมสำหรับคลาสยูทิลิตี้!

ฉันพยายาม ใช้คลาสยูทิลิตี้เท่า ที่จำเป็นแทนที่จะแทนที่แค่เขียน CSS เกณฑ์ส่วนตัวของฉันเมื่อเหมาะสมที่จะเพิ่มหนึ่งรายการในโครงการคือ:

  1. ฉันต้องการมันซ้ำแล้วซ้ำอีก
  2. มันทำสิ่งหนึ่งได้ดี
  3. ใช้กับส่วนประกอบหรือหน้าต่างๆ ที่หลากหลาย

ฉันคิดว่ากรณีนี้ตรงตามเกณฑ์ทั้งสาม เรามาสร้างไฟล์ CSS ใหม่ style/utilities.css และเพิ่ม:

 .lockup { max-width: 90ch; margin: 0 auto; }

จากนั้น มาเพิ่มการนำเข้า '../styles/utilities.css' ใน หน้าของเรา/_app.jsx สุดท้าย เรามาเปลี่ยนแท็ก <main> ใน pages/index.jsx เป็น <main className="lockup">

ตอนนี้เพจของเรากำลังมากันมากยิ่งขึ้น เนื่องจากเราใช้คุณสมบัติ max-width เราจึงไม่ต้องการคิวรี่สื่อใดๆ เพื่อให้เลย์เอาต์ของเราตอบสนองต่ออุปกรณ์เคลื่อนที่ และเนื่องจากเราใช้หน่วยวัด ch ซึ่งเท่ากับความกว้างของอักขระหนึ่งตัว การปรับขนาดของเราจึงเป็นไดนามิกตามขนาดแบบอักษรของเบราว์เซอร์ของผู้ใช้

เว็บเดิมแต่ตอนนี้ข้อความโดนหนีบตรงกลางไม่กว้างเกินไป
เว็บเดิมแต่ตอนนี้ข้อความถูกหนีบไว้ตรงกลางและไม่กว้างเกินไป (ตัวอย่างขนาดใหญ่)

เมื่อเว็บไซต์ของเราเติบโตขึ้น เราก็สามารถเพิ่มคลาสยูทิลิตี้เพิ่มเติมต่อไปได้ ฉันใช้แนวทางที่ค่อนข้างเป็นประโยชน์ที่นี่: ถ้าฉันทำงานและพบว่าฉันต้องการชั้นเรียนอื่นสำหรับสีหรืออะไรบางอย่าง ฉันจะเพิ่มมันเข้าไป ฉันไม่ได้เพิ่มทุกคลาสที่เป็นไปได้ภายใต้ดวงอาทิตย์ — มันจะทำให้ขนาดไฟล์ CSS ขยายใหญ่และทำให้โค้ดของฉันสับสน บางครั้ง ในโครงการขนาดใหญ่ ฉันชอบแบ่งสิ่งต่าง ๆ ออกเป็นไดเร็กทอรี styles/utilities/ ด้วยไฟล์ที่แตกต่างกันสองสามไฟล์ แล้วแต่ความต้องการของโครงการ

เราสามารถนึกถึงคลาสยูทิลิตี้เป็น ชุดเครื่องมือทั่วไปของคำสั่งการจัดรูปแบบซ้ำๆ ที่แชร์กันทั่วโลก ช่วยป้องกันไม่ให้เราเขียน CSS เดิมซ้ำระหว่างส่วนประกอบต่างๆ

ลักษณะส่วนประกอบ

เราได้ทำโฮมเพจเสร็จแล้วในขณะนี้ แต่เรายังคงต้องสร้างส่วนหนึ่งของเว็บไซต์ของเรา นั่นคือ ร้านค้าออนไลน์ เป้าหมายของเราคือการแสดง ตารางการ์ดของชาทั้งหมดที่เราต้องการขาย ดังนั้นเราจะต้องเพิ่มส่วนประกอบบางอย่างในไซต์ของเรา

มาเริ่มกันด้วยการเพิ่มเพจใหม่ที่ pages/shop.jsx :

 export default function Shop() { return <main> <div className="lockup"> <h1>Shop Our Teas</h1> </div> </main> }

จากนั้น ก็ต้องมีชามาโชว์ เราจะใส่ชื่อ คำอธิบาย และรูปภาพ (ในไดเรกทอรีสาธารณะ/) สำหรับแต่ละชา:

 const teas = [ { name: "Oolong", description: "A partially fermented tea.", image: "/oolong.jpg" }, // ... ]

หมายเหตุ : นี่ไม่ใช่บทความเกี่ยวกับการดึงข้อมูล ดังนั้นเราจึงใช้เส้นทางที่ง่ายและกำหนดอาร์เรย์ที่จุดเริ่มต้นของไฟล์

ต่อไป เราจะต้องกำหนดองค์ประกอบเพื่อแสดงชาของเรา เริ่มต้นด้วยการสร้าง components/ ไดเร็กทอรี (Next.js ไม่ได้สร้างสิ่งนี้โดยค่าเริ่มต้น) จากนั้น มาเพิ่มไดเร็กทอรี components/TeaList กัน สำหรับส่วนประกอบใด ๆ ที่ต้องการมากกว่าหนึ่งไฟล์ ฉันมักจะใส่ไฟล์ที่เกี่ยวข้องทั้งหมดไว้ในโฟลเดอร์ การทำเช่นนี้จะป้องกันไม่ให้ components/ โฟลเดอร์ของเราใช้งานไม่ได้

ตอนนี้ มาเพิ่มไฟล์ ส่วนประกอบ/TeaList/TeaList.jsx ของเรา:

 import TeaListItem from './TeaListItem' const TeaList = (props) => { const { teas } = props return <ul role="list"> {teas.map(tea => <TeaListItem tea={tea} key={tea.name} />)} </ul> } export default TeaList

จุดประสงค์ของส่วนประกอบนี้คือเพื่อวนซ้ำบนชาของเราและแสดงรายการสำหรับแต่ละรายการ ดังนั้นตอนนี้ เรามากำหนด ส่วนประกอบ/TeaList/TeaListItem.jsx ส่วนประกอบของเรา:

 import Image from 'next/image' const TeaListItem = (props) => { const { tea } = props return <li> <div> <Image src={tea.image} alt="" objectFit="cover" objectPosition="center" layout="fill" /> </div> <div> <h2>{tea.name}</h2> <p>{tea.description}</p> </div> </li> } export default TeaListItem

โปรดทราบว่าเรากำลังใช้องค์ประกอบรูปภาพในตัวของ Next.js ฉันตั้งค่าแอตทริบิวต์ alt เป็นสตริงว่างเนื่องจากรูปภาพมีการตกแต่งเท่านั้นในกรณีนี้ เราต้องการหลีกเลี่ยงไม่ให้ผู้ใช้โปรแกรมอ่านหน้าจอสะดุดด้วยคำอธิบายรูปภาพแบบยาวที่นี่

สุดท้าย มาสร้างไฟล์ component/TeaList/index.js เพื่อให้ง่ายต่อการนำเข้าส่วนประกอบจากภายนอก:

 import TeaList from './TeaList' import TeaListItem from './TeaListItem' export { TeaListItem } export default TeaList

จากนั้น มารวมเข้าด้วยกันโดยเพิ่มการนำเข้า TeaList จาก ../ ../components/TeaList และองค์ประกอบ <TeaList teas={teas} /> ในหน้าร้านค้าของเรา ตอนนี้ ชาของเราจะแสดงในรายการ แต่มันจะไม่สวยนัก

Colocating Style ด้วยส่วนประกอบผ่านโมดูล CSS

เริ่มต้นด้วยการจัดสไตล์การ์ดของเรา (องค์ประกอบ TeaListLitem ) เป็นครั้งแรกในโปรเจ็กต์ของเรา เราต้องการเพิ่มสไตล์ที่เฉพาะเจาะจงสำหรับองค์ประกอบเดียว มาสร้างไฟล์ใหม่ component/TeaList/TeaListItem.module.css

คุณอาจสงสัยเกี่ยวกับโมดูลในนามสกุลไฟล์ นี่คือ โมดูล CSS Next.js รองรับโมดูล CSS และมีเอกสารประกอบที่ดีอยู่ด้วย เมื่อเราเขียนชื่อคลาสจากโมดูล CSS เช่น .TeaListItem ชื่อคลาสจะถูกแปลงเป็นอย่างอื่นโดย . TeaListItem_TeaListItem__TFOk_ . TeaListItem_TeaListItem__TFOk_ พร้อมอักขระพิเศษจำนวนมากที่ถูกตรึงไว้ ดังนั้น เราจึงสามารถใช้ชื่อคลาสใดก็ได้ที่เราต้องการ โดยไม่ต้องกังวลว่าจะขัดแย้งกับชื่อคลาสอื่นในไซต์ของเรา

ข้อดีอีกประการของโมดูล CSS คือประสิทธิภาพ Next.js มีคุณลักษณะการนำเข้าแบบไดนามิก ถัดไป/ไดนามิกช่วยให้เราโหลดคอมโพเนนต์แบบ Lazy Loading เพื่อให้โค้ดของพวกเขาโหลดได้เมื่อจำเป็นเท่านั้น แทนที่จะเพิ่มขนาดบันเดิลทั้งหมด หากเรานำเข้าสไตล์โลคัลที่จำเป็นลงในส่วนประกอบแต่ละส่วน ผู้ใช้ก็จะสามารถ โหลด CSS สำหรับส่วนประกอบที่นำเข้าแบบไดนามิก ได้ สำหรับโปรเจ็กต์ขนาดใหญ่ เราอาจเลือกที่จะขี้เกียจโหลดโค้ดจำนวนมากและโหลด JS/CSS ที่จำเป็นที่สุดล่วงหน้าเท่านั้น ด้วยเหตุนี้ ฉันมักจะจบลงด้วยการสร้างไฟล์โมดูล CSS ใหม่สำหรับส่วนประกอบใหม่ทั้งหมดที่ต้องการการจัดสไตล์ในเครื่อง

เริ่มต้นด้วยการเพิ่มรูปแบบเริ่มต้นลงในไฟล์ของเรา:

 .TeaListItem { display: flex; flex-direction: column; gap: var(--space-sm); background-color: var(--color, var(--off-white)); color: var(--dark); border-radius: 3px; box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1); }

จากนั้น เราสามารถนำเข้าสไตล์จาก ./TeaListItem.module.css ในองค์ประกอบ TeaListitem ของเรา ตัวแปรสไตล์มาในรูปแบบออบเจกต์ JavaScript ดังนั้นเราจึงสามารถเข้าถึง style.TeaListItem.

หมายเหตุ : ชื่อคลาสของเราไม่จำเป็นต้องเป็นตัวพิมพ์ใหญ่ ฉันพบว่าแบบแผนของชื่อคลาสที่เป็นตัวพิมพ์ใหญ่ภายในโมดูล (และตัวพิมพ์เล็กด้านนอก) แยกความแตกต่างของชื่อคลาสท้องถิ่นและระดับโลกด้วยสายตา

ลองใช้คลาสท้องถิ่นใหม่ของเราและกำหนดให้กับ <li> ในองค์ประกอบ TeaListItem ของเรา:

 <li className={style.TeaListComponent}>

คุณอาจกำลังสงสัยเกี่ยวกับเส้นสีพื้นหลัง (เช่น var(--color, var(--off-white)); ) ข้อมูลโค้ดนี้หมายความว่า โดยค่าเริ่มต้น พื้นหลังจะเป็นค่า --off-white แต่ถ้าเราตั้งค่า --color คุณสมบัติกำหนดเองบนการ์ด มันจะแทนที่และเลือกค่านั้นแทน

ในตอนแรก เราจะต้องการให้การ์ดทั้งหมดของเรา --off-white แต่เราอาจต้องการเปลี่ยนค่าของการ์ดแต่ละใบในภายหลัง สิ่งนี้ทำงานคล้ายกับอุปกรณ์ประกอบฉากใน React เราสามารถตั้งค่าเริ่มต้นได้ แต่สร้างช่องที่เราสามารถเลือกค่าอื่นได้ในบางสถานการณ์ ดังนั้นฉันจึงแนะนำให้เรา นึกถึงคุณสมบัติที่กำหนดเองของ CSS เช่น props เวอร์ชันของ CSS

สไตล์จะยังดูไม่ดีนักเพราะเราต้องการให้แน่ใจว่ารูปภาพอยู่ในคอนเทนเนอร์ องค์ประกอบรูปภาพของ Next.js ที่มีเสา layout="fill" ได้รับ position: absolute; จาก framework จึงสามารถจำกัดขนาดโดยใส่คอนเทนเนอร์ที่มีตำแหน่ง: ญาติ;.

มาเพิ่มคลาสใหม่ให้กับ TeaListItem.module.css ของเรา:

 .ImageContainer { position: relative; width: 100%; height: 10em; overflow: hidden; }

จากนั้นให้เพิ่ม className={styles.ImageContainer} ใน <div> ที่มี <Image> ของเรา ฉันใช้ชื่อที่ค่อนข้าง "ธรรมดา" เช่น ImageContainer เนื่องจากเราอยู่ในโมดูล CSS ดังนั้นเราจึงไม่ต้องกังวลว่าจะขัดแย้งกับรูปแบบภายนอก

สุดท้าย เราต้องการ เพิ่มช่องว่างภายในเล็กน้อย ที่ด้านข้างของข้อความ ดังนั้น ให้เพิ่มคลาสสุดท้ายและใช้ตัวแปรการเว้นวรรคที่เราตั้งค่าเป็นโทเค็นการออกแบบ:

 .Title { padding-left: var(--space-sm); padding-right: var(--space-sm); }

เราสามารถเพิ่มคลาสนี้ใน <div> ที่มีชื่อและคำอธิบายของเรา ตอนนี้การ์ดของเราไม่ได้ดูแย่นัก:

การ์ดกำลังแสดงชา 3 ชนิดที่เพิ่มเป็นข้อมูลเมล็ด มีรูปภาพ ชื่อ และคำอธิบาย ขณะนี้พวกเขาแสดงในรายการแนวตั้งโดยไม่มีช่องว่างระหว่างพวกเขา
การ์ดกำลังแสดงชา 3 ชนิดที่เพิ่มเป็นข้อมูลเมล็ด มีรูปภาพ ชื่อ และคำอธิบาย ขณะนี้พวกเขาแสดงในรายการแนวตั้งโดยไม่มีช่องว่างระหว่างพวกเขา (ตัวอย่างขนาดใหญ่)

ผสมผสานสไตล์ระดับโลกและระดับท้องถิ่น

ต่อไป เราต้องการให้การ์ดของเราแสดงในรูปแบบตาราง ในกรณีนี้ เราแค่อยู่ใกล้เส้นแบ่งระหว่างสไตล์ท้องถิ่นและสไตล์สากล เราสามารถเขียนโค้ดเลย์เอาต์ของเราโดยตรงบนคอมโพเนนต์ TeaList แต่ฉันสามารถจินตนาการได้ด้วยว่าการมี คลาสยูทิลิตี้ที่เปลี่ยนรายการเป็นเลย์เอาต์กริด อาจมีประโยชน์ในที่อื่นๆ อีกหลายแห่ง

ลองใช้แนวทางสากลที่นี่และเพิ่มคลาสยูทิลิตี้ใหม่ใน styles/utilities.css ของเรา:

 .grid { list-style: none; display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--min-item-width, 30ch), 1fr)); gap: var(--space-md); }

ตอนนี้ เราสามารถเพิ่มคลาส .grid ในรายการใดก็ได้ และเราจะได้รับเค้าโครงกริดที่ตอบสนองโดยอัตโนมัติ นอกจากนี้เรายังสามารถเปลี่ยน --min-item-width กำหนดเองคุณสมบัติ (โดยค่าเริ่มต้น 30ch ) เพื่อเปลี่ยนความกว้างขั้นต่ำของแต่ละองค์ประกอบ

หมายเหตุ : อย่าลืมนึกถึงคุณสมบัติที่กำหนดเอง เช่น อุปกรณ์ประกอบฉาก! หากไวยากรณ์นี้ดูไม่คุ้นเคย คุณสามารถตรวจสอบ “ตาราง CSS ที่ตอบสนองภายในด้วย minmax() และ min() ” โดย Chris Coyier

เนื่องจากเราได้เขียนสไตล์นี้ไปทั่วโลก การเพิ่ม className="grid" ลงในองค์ประกอบ TeaList ของเรานั้นไม่ต้องการความหรูหราใดๆ แต่สมมติว่าเราต้องการจับคู่สไตล์สากลนี้กับร้านค้าในพื้นที่เพิ่มเติม ตัวอย่างเช่น เราต้องการเพิ่ม "ความงามของชา" เข้าไปอีกเล็กน้อย และทำให้การ์ดใบอื่นๆ มีพื้นหลังสีเขียว สิ่งที่เราต้องทำคือสร้างไฟล์ component/TeaList/TeaList.module.css ใหม่:

 .TeaList > :nth-child(even) { --color: var(--green); }

จำได้ไหมว่าเราสร้าง --color custom บนองค์ประกอบ TeaListItem ของเราได้อย่างไร ตอนนี้เราสามารถตั้งค่าได้ภายใต้สถานการณ์เฉพาะ โปรดทราบว่าเรายังคงใช้ตัวเลือกย่อยภายในโมดูล CSS ได้ และไม่สำคัญว่าเราจะเลือกองค์ประกอบที่มีรูปแบบภายในโมดูลอื่น ดังนั้น เรายังสามารถใช้รูปแบบองค์ประกอบในเครื่องของเราเพื่อส่งผลต่อองค์ประกอบย่อย นี่เป็นคุณลักษณะมากกว่าข้อบกพร่อง เนื่องจากช่วยให้เรา ใช้ประโยชน์จาก CSS cascade ได้ ! หากเราพยายามจำลองเอฟเฟกต์นี้ด้วยวิธีอื่น เราอาจจบลงด้วยซุป JavaScript บางประเภท แทนที่จะเป็น CSS สามบรรทัด

แล้วเราจะเก็บคลาส .grid ทั่วโลกไว้ในองค์ประกอบ .grid ของเรา TeaList ในขณะที่เพิ่มคลาส .TeaList ในพื้นที่ด้วย นี่คือจุดที่ไวยากรณ์อาจดูขี้ขลาดเล็กน้อยเพราะเราต้องเข้าถึงคลาส .TeaList ของเราจากโมดูล CSS โดยทำบางอย่างเช่น style.TeaList

ทางเลือกหนึ่งคือใช้การแก้ไขสตริงเพื่อรับบางสิ่งเช่น:

 <ul role="list" className={`${style.TeaList} grid`}>

ในกรณีเล็กๆ นี้ มันอาจจะดีพอ หากเรากำลังผสมคลาสต่างๆ เข้าด้วยกันมากขึ้น ฉันพบว่าไวยากรณ์นี้ทำให้สมองของฉันระเบิดขึ้นเล็กน้อย ดังนั้นบางครั้งฉันจะเลือกใช้ไลบรารี่ชื่อคลาส ในกรณีนี้ เราจะได้รายการที่ดูสมเหตุสมผลกว่า:

 <ul role="list" className={classnames(style.TeaList, "grid")}>

ตอนนี้ เราได้ทำเพจร้านค้าเสร็จแล้ว และเราได้ทำให้องค์ประกอบ TeaList ของเราใช้ประโยชน์จากสไตล์ ทั้งแบบ สากลและแบบท้องถิ่น

การ์ดชาของเราจะแสดงเป็นตารางแล้ว จำนวนเต็มคู่จะเป็นสีเขียว ในขณะที่รายการคี่จะเป็นสีขาว
การ์ดชาของเราจะแสดงเป็นตารางแล้ว จำนวนเต็มคู่จะเป็นสีเขียว ในขณะที่รายการคี่จะเป็นสีขาว (ตัวอย่างขนาดใหญ่)

พระราชบัญญัติสมดุล

ตอนนี้เราได้สร้างร้านน้ำชาของเราโดยใช้ CSS ธรรมดาในการจัดการสไตล์ คุณอาจสังเกตเห็นว่าเราไม่ต้องใช้เวลานานในการจัดการกับการตั้งค่า Webpack แบบกำหนดเอง การติดตั้งไลบรารีภายนอก และอื่นๆ นั่นเป็นเพราะรูปแบบที่เราเคยใช้กับ Next.js มาก่อน นอกจากนี้ ยัง สนับสนุนแนวทางปฏิบัติ CSS ที่ดีที่สุด และเหมาะสมกับสถาปัตยกรรมเฟรมเวิร์ก Next.js อย่างเป็นธรรมชาติ

องค์กร CSS ของเราประกอบด้วยสี่ส่วนสำคัญ:

  1. โทเค็นการออกแบบ
  2. สไตล์สากล
  3. คลาสยูทิลิตี้
  4. สไตล์คอมโพเนนต์

ขณะที่เราสร้างไซต์ของเราต่อไป รายการโทเค็นการออกแบบและคลาสยูทิลิตี้ของเราจะเติบโตขึ้น สไตล์ใดๆ ที่ไม่เหมาะสมที่จะเพิ่มเป็นคลาสยูทิลิตี้ เราสามารถเพิ่มลงในสไตล์ส่วนประกอบโดยใช้โมดูล CSS ด้วยเหตุนี้ เราจึงพบความสมดุลอย่างต่อเนื่องระหว่างความกังวลเกี่ยวกับสไตล์ท้องถิ่นและระดับโลก นอกจากนี้เรายังสามารถ สร้างโค้ด CSS ที่มีประสิทธิภาพและใช้งานง่าย ซึ่งเติบโตอย่างเป็นธรรมชาติควบคู่ไปกับไซต์ Next.js ของเรา