ในบทช่วยสอนวันนี้ เราจะสร้าง Navigation Bar บนสุดด้วย HTML และ CSS เราจะดู 2 วิธี: Flexbox และ CSS Grid
จะเป็นโอกาสดีในการเปรียบเทียบความแตกต่างของทั้งสองวิธี และคุณสามารถเลือกวิธีที่ชอบได้
นี่คือลักษณะ Navigation Bar ที่เสร็จสมบูรณ์:
บน Desktop ลิงก์ทั้งหมดอยู่แถวเดียว Home อยู่ซ้าย ลิงก์อื่นๆ อยู่ขวา
บน Mobile จะมี Home บนแถวบน และลิงก์อื่นๆ บนแถวล่าง ลิงก์จะจัดกึ่งกลางหน้า
เริ่มต้นด้วยการตั้งค่าไฟล์
ตั้งค่าไฟล์
เพื่อสร้าง Navigation เราจะสร้างไฟล์ index.html และ style.scss ซึ่งจะคอมไพล์เป็น style.css ด้วย extension VS Code Live Sass
ในส่วน <head> เราจะเพิ่ม element <link> โหลด style.css
เริ่มสร้าง Navigation Bar กันเลย!
สร้าง HTML Markup
ใน index.html ให้สร้าง HTML markup สำหรับ Navigation ผมพยายามใช้ Semantic HTML tags มากที่สุด เพื่อหลีกเลี่ยงการใช้แค่ div
เราจะสร้าง element <nav> ที่จะ contain ลิงก์นำทางทั้งหมด แล้ววางลิงก์ใน unordered list <ul> เช่นนี้:
<nav> <ul> <li><a href="#">Home</a></li> <li><a href="#">About</a></li> <li><a href="#">Blog</a></li> <li><a href="#">Contact</a></li> </ul> </nav>
นี่คือ markup พื้นฐานสำหรับ Navigation เราจะปรับแต่งเมื่อสร้าง Flexbox และ Grid versions แต่เริ่มต้นด้วยการสร้าง 2 ชุดของ element <nav> นี้
หากโหลด index.html ในเบราว์เซอร์ จะดูพื้นฐานมาก
ไม่ต้องกังวล! เริ่มเพิ่มสไตล์ใน style.scss กันเลย
เริ่มเพิ่ม Global Styles และ Media Queries
ขั้นแรก ผมเพิ่ม global styles ที่ใช้บ่อย:
html { font-size: 100%; box-sizing: border-box; } body { margin: 0px; padding: 0px; min-height: 100vh; } *, *::before, *::after { box-sizing: inherit; }
เพิ่มเติม ให้สีสันน่าสนใจขึ้น ในแท็ก <body> ตั้ง background-color: #1b1b1b และให้ทุก element และลิงก์ <a> เป็นสีขาว color: #ffffff เพื่อธีมมืด
สำหรับ element <ul> ใน <nav>: เพิ่ม list-style-type: none ซ่อน bullet และตั้ง margin padding เป็น 0px
เพิ่ม padding ให้ <nav> ด้วย padding: 10px และเพิ่ม padding สำหรับ tablet/desktop ด้วย media query:
nav { padding: 10px; @media (min-width: 600px){ padding: 20px; } }
ดูดีขึ้นแล้ว! แทนฟอนต์ serif เริ่มต้น ผมอยากใช้ Google Font Open Sans
นำเข้า font family เข้า style.scss โดยเพิ่มบรรทัดนี้บนสุด:
@import url('https://fonts.googleapis.com/css?family=Open+Sans&display=swap');
ในสไตล์ <body> ตั้ง font-family: 'Open Sans', Arial, Helvetica, sans-serif โหลด Open Sans หรือ fallback font
เพิ่ม Link Styles พร้อม Linear Gradient Bottom Border
ทำให้ link styles น่าสนใจขึ้น
ลบ underline เริ่มต้นด้วย text-decoration: none ให้ element <a> และแทนด้วย underline สีชมพูหนาขึ้นด้วย border-bottom พร้อมสร้าง gradient underline เมื่อ hover สีชมพูไปม่วง
สำหรับสี ผมชอบสร้าง Sass variables บนสุดของ style.scss ใต้ @import เพิ่ม:
$linkPink: rgb(216, 29, 216); $linkPurple: rgb(98, 16, 250);
ตัวแปรจะทำให้ง่ายขึ้นเวลาเพิ่มสีในสไตล์อื่นๆ
ตอนนี้ สร้าง underline หนาด้วย pseudo-element ของ <a> ให้มี bottom border
โค้ด:
a { position: relative; text-decoration: none; } a::before { content: ""; position: absolute; height: 3px; right: 0px; bottom: -3px; left: 0px; background-color: $linkPink; }
ที่นี่เราทำให้ ::before pseudo-element สูง 3px พื้นหลังสีชมพู เพื่อจัดตำแหน่งด้านล่าง ตั้ง position: absolute และให้ <a> มี position: relative เพื่อไม่ให้บินขึ้นไป (อธิบายเพิ่มเติม ที่นี่)
จัดตำแหน่ง pseudo-element ใต้ข้อความด้วย bottom: -3px เว้นช่องว่างเล็กน้อยระหว่างข้อความลิงก์และเส้น และให้เส้นขยายเต็มความกว้างของลิงก์ด้วย left: 0px และ right: 0px
ลิงก์ดูสวยขึ้นแล้ว!
Linear Gradient Hover Style
เพิ่ม hover effect ให้ underline เปลี่ยนเป็น gradient สีชมพูไปม่วง และข้อความลิงก์เปลี่ยนจากขาวเป็นชมพูเมื่อ hover
ใช้ selector a:hover พร้อมสไตล์:
a:hover { color: $linkPink; &::before { background: linear-gradient(to right, $linkPink 40%, $linkPurple 75%); } }
สำหรับ ::before พื้นหลังเปลี่ยนเป็น linear-gradient ในฟังก์ชัน gradient ระบุให้เปลี่ยนจากชมพูไปม่วงเมื่อเคลื่อนจากซ้ายไปขวา
ควบคุมจุดเปลี่ยนสีด้วยเปอร์เซ็นต์ ต้องการสีชมพูแรกจาก 0% (ซ้ายสุด) ถึง 40% ความกว้าง และสีม่วงที่สองจาก 100% (ขวาสุด) ถึง 75% ความกว้าง จะมี gradient ระหว่าง 40% ถึง 75% ของความกว้าง element
ยังเปลี่ยนสีข้อความลิงก์เป็นชมพูเมื่อ hover
ตอน hover ลิงก์จะลักษณะ:
ดู fancy ขึ้นใช่ไหม?
ตอนนี้เราเสร็จ global styles ที่ใช้สำหรับ Flexbox และ Grid navigation bars ทั้งสอง
เริ่มเขียนสไตล์สำหรับ Flexbox Navigation กันเลย
Flexbox Navigation
แบ่งลิงก์เป็น Flex Child Groups
ผมมักเริ่มเขียน Desktop styles ก่อน แล้วค่อย Mobile เพราะ Desktop มักซับซ้อนกว่ามีหลายคอลัมน์ ส่วน Mobile มัก stack เป็นคอลัมน์เดียว แต่คุณเขียนลำดับไหนก็ได้
ใน Desktop Navigation เราต้องการ Home link ซ้าย และลิงก์รอง 3 ตัวขวา
เพื่อทำได้ด้วย Flexbox ต้องแบ่งลิงก์ 2 กลุ่ม: กลุ่มซ้ายและกลุ่มขวา ทั้งคู่จะเป็น flex children ของ flex parent
กลับไปที่ HTML markup เราต้องการ <nav> เป็น flex parent ดังนั้นย้าย Home link ออกจาก <ul> ทำให้เป็น direct child ของ <nav>
ลิงก์อื่นๆ อยู่ใน <ul> ต่อ
เพิ่ม class "flexbox" ให้ <nav> แยกสไตล์ flexbox จาก grid styles
Markup ที่อัปเดต:
<nav class="flexbox"> <a href="#">Home</a> <ul> <li><a href="#">About</a></li> <li><a href="#">Blog</a></li> <li><a href="#">Contact</a></li> </ul> </nav>
เพิ่ม Flexbox Styles สำหรับ Desktop
ตอนนี้ ใน Navigation เราเปิด Flexbox โดยตั้ง nav.flexbox เป็น display: flex เพิ่ม justify-content: space-between วางกลุ่มแรกซ้าย กลุ่มอื่นขวา
ทำ <ul> เป็น flex parent อีกทีด้วย display: flex และ justify-content: flex-end จัดลิงก์ขวา เพิ่ม align-items: center; จัดกึ่งกลางแนวตั้ง flex children
ดูดีขึ้น!
ตอนนี้เราต้องการลิงก์รอง 3 ตัวติดกันใน Flexbox ดังนั้นทำ <ul> เป็น flex parent ของ 3 ลิงก์ ตั้ง align-items: center เพิ่มระยะห่างระหว่างลิงก์ด้วย margin-right: 20px แต่ทำเป็น 0px สำหรับ selector :last-child เพื่อไม่ให้ลิงก์สุดท้ายมีระยะว่างเกิน
Desktop styles ตอนนี้:
nav.flexbox { display: flex; justify-content: space-between; align-items: center; ul { display: flex; align-items: center; } ul li { margin-right: 20px; &:last-child { margin-right: 0px; } } }
Navigation Bar จะลักษณะ:
เพิ่ม Flexbox Styles สำหรับ Mobile
เพิ่ม Mobile Styles ขั้นแรก จัดกึ่งกลาง flex content บน Mobile
ตอนนี้บน Mobile Home link จัดซ้ายเพราะ justify-content: space-between ของ flex parent
จัดกึ่งกลางได้โดยย้าย style rule นั้นเข้า media query สำหรับความกว้างใหญ่ แล้วตั้ง justify-content เป็น center สำหรับ Mobile
ต่อไป ต้องการลิงก์รอง 3 ตัว wrap ไปแถวใหม่ เพิ่ม flex-wrap: wrap ให้ selector nav.flexbox เพื่อเปิด wrapping
Styles ควรเป็น:
nav.flexbox { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; @media (min-width: 600px){ justify-content: space-between; } }
เพื่อบังคับ wrapping เราจะทำให้ <ul> flex child กินความกว้าง 100% จึงต้องเลื่อนใต้ Home link
ผมชอบ helper classes เพิ่ม class "fullwidth" ให้ <ul> สร้าง style rules สำหรับ .fullwidth ใน rule nav.flexbox
ตั้ง flex: 1 0 100% คุณสมบัติ flex เป็น shorthand ของ flex-grow, flex-shrink, flex-basis
อนุญาตให้ grow ถ้าจำเป็น flex-grow 1 ไม่อนุญาต shrink flex-shrink 0 ตั้งความกว้างเริ่มต้น 100% ด้วย flex-basis
จัดกึ่งกลางลิงก์ใน flex parent บน Mobile ด้วย justify-content: center
แต่ไม่ต้องการลิงก์รอง fullwidth และ centered บน Tablet/Desktop สำหรับความกว้างใหญ่ ต้องการ flex เป็น 0 1 auto ไม่ grow สามารถ shrink ได้ ความกว้างเริ่มต้นเป็น natural width ของเนื้อหา
เพิ่ม media query สำหรับ Desktop Styles:
.fullwidth { flex: 1 0 100%; justify-content: center; @media (min-width: 600px){ flex: 0 1 auto; } }
อาจเขียน max-width media query เพื่อ target style rules แรกเฉพาะ Mobile แต่ผมชอบใช้ Mobile style rules เป็นค่าเริ่มต้น แล้วค่อยเพิ่ม min-width media queries ขึ้นเรื่อยๆ เพื่อหลีกเลี่ยง conflict
ขึ้นอยู่กับคุณชอบ media query แบบไหน แต่อย่างไรก็ตามแนะนำเลือกแบบเดียว (min หรือ max) ใช้ส่วนใหญ่
สำหรับลิงก์รอง 3 ตัว ผมอยากตั้ง width ให้ข้อความ "Blog" จัดกึ่งกลางใต้ "Home" ตอนนี้จัดกึ่งกลางตามความกว้างรวมของแถวที่สอง แต่เพราะ "Contact" ยาวกว่า "About" จุดกึ่งกลางของแถวเลยขวาของ "Blog" เล็กน้อย
แก้โดยทำให้ flex child column แต่ละตัวในแถวที่สอง มี static width เท่ากัน 100px ตั้ง flex เป็น 0 0 100px จัดข้อความกึ่งกลาง ต้องการเฉพาะ Mobile ยกเลิกสำหรับ Desktop ตั้ง flex เป็น 0 0 auto
ลดระยะห่างระหว่าง item Mobile เป็น 10px คง 20px สำหรับ Desktop
โค้ดทั้งหมด:
ul li { flex: 0 0 100px; margin-right: 10px; text-align: center; @media (min-width: 600px){ flex: 0 0 auto; margin-right: 20px; } &:last-child { margin-right: 0px; } }
การปรับแต่งสุดท้าย เพิ่มระยะห่างระหว่าง Home link แรก และลิงก์รอง 3 ตัวด้วย margin-bottom สำหรับ Mobile เขียน style rule target element <a> ที่เป็น direct child ของ nav.flexbox parent เพื่อให้เฉพาะ Home link ได้ผล
nav.flexbox { > a { margin-bottom: 10px; @media (min-width: 600px){ margin-bottom: 0px; } } }
Mobile Navigation จะลักษณะนี้ (เปิด Firefox Flexbox Inspector ดูเส้นคอลัมน์):
เสร็จแล้วสำหรับ Flexbox Navigation! ไปสร้างด้วย CSS Grid ต่อ
CSS Grid Method
ความแตกต่างหลักระหว่าง Flexbox และ Grid คือ Flexbox จัดเรียงคอลัมน์/แถวโดย flex properties บน flex children ใน property flex และ flex parent ตั้ง wrap หรือไม่ Flexbox เป็น "content-first" approach
CSS Grid เป็น "grid-first" approach คุณออกแบบ Grid Template (คอลัมน์/แถว) ด้วย grid properties ใน parent element และควบคุมตำแหน่ง grid children ใน template ได้แต่ละตัว
ด้วยการควบคุมระดับรายตัว ใน Grid Navigation Bar เราสามารถวางลิงก์ทั้งหมดรวม Home link ใน parent <ul> เดียวกัน
<nav class="grid"> <ul> <li><a href="#">Home</a></li> <li><a href="#">About</a></li> <li><a href="#">Blog</a></li> <li><a href="#">Contact</a></li> </ul> </nav>
เพิ่ม Grid Styles สำหรับ Mobile
เปลี่ยนวิธีกับ Grid Navigation เริ่มเขียน Mobile Styles ก่อน!
บน Mobile ต้องการ Home link แถวแรก กิน 100% ความกว้าง แล้วลิงก์รอง 3 ตัวแถวที่สอง จัดกึ่งกลาง
ใน <ul> grid parent element เราเปิด Grid ด้วย display: grid
ออกแบบ Grid Template มีลิงก์ 3 ตัวแถวที่สอง สร้าง 3-column template
อยากให้คอลัมน์กว้างเท่ากัน แต่ไม่กว้างเกินไป ตั้ง grid-template-columns: repeat(3, 100px) บน <ul> grid parent
ใช้ฟังก์ชัน repeat() สร้าง 3 คอลัมน์ กว้าง 100px
คุณสมบัติมีประโยชน์ของ CSS Grid คือสร้าง gutters (ช่องว่าง) ระหว่าง grid items แทนที่จะต้องเพิ่ม margin ให้ grid children เหมือน Flexbox ใช้ grid-gap: 10px แทน
จัดกึ่งกลาง grid ทั้งหมดด้วย justify-content: center และจัดข้อความในแต่ละคอลัมน์กึ่งกลางด้วย justify-items: center
เปิด Firefox Grid Inspector (เก่งมาก ลองดูสิ) Grid จะลักษณะ:
เห็น 3 คอลัมน์กึ่งกลางชัดเจน แต่ Home link ต้องอยู่แถวเดียว กิน 3 คอลัมน์
ทำได้โดยใช้ helper class อีกครั้ง เพิ่ม class "fullwidth" ให้ element <li> ของ Home
ในสไตล์ ให้ selector .fullwidth rule grid-column: 1 / 4 ทำให้ grid child column เริ่มที่ grid line แนวตั้งแรก และขยายถึง grid line ที่ 4 ท้ายสุด
ลักษณะ (เปิด line numbers ดูการ編號):
เพิ่ม Grid Styles สำหรับ Desktop
สำหรับ Desktop ต้องการลิงก์ทั้งหมดแถวเดียว Home ซ้าย ลิงก์อื่น 3 ตัวขวา
อยากให้ 3 คอลัมน์สุดท้าย ขนาดตามเนื้อหา เพื่อให้ระยะห่างระหว่างลิงก์เท่ากัน
ทั้งหมดนี้หมายถึงต้องเปลี่ยน Grid Template สำหรับความกว้างใหญ่ด้วย Media Query
ควรเปลี่ยน Template เป็นอะไร? ต้องการลิงก์ทั้งหมดแถวเดียว ต้องการ 4-column grid
เริ่มต้นเปลี่ยน grid-template-columns จาก repeat(3, 100px) เป็น repeat(4, 100px) บน Grid Parent สำหรับความกว้างมากกว่า 600px
จะสร้าง 4 คอลัมน์ แล้วค่อยปรับต่อ
อัปเดตสไตล์ .fullwidth ให้กิน 1 คอลัมน์บน Desktop grid-column: 1 / 2 ทำให้คอลัมน์ Home แรก ขยายจาก grid line 1 ถึง 2
ตอนนี้เป็น:
ลิงก์อยู่ 4 คอลัมน์ แต่กว้าง 100px ทุกคอลัมน์ จึงแค่จัดกึ่งกลางหน้า
เพื่อให้ Home link แรกอยู่ซ้ายสุด ต้องทำให้ Grid เองกิน 100% ความกว้างหน้า ทำได้โดยให้คอลัมน์แรกใน Grid Template กินพื้นที่ว่าง
เปลี่ยน grid-template-columns เป็น 1fr repeat(3, 100px) จะคง 3 ลิงก์รองไว้ 100px คอลัมน์แรกกินพื้นที่ที่เหลือ
ใกล้แล้ว! แต่เพื่อให้ 3 คอลัมน์สุดท้ายกว้างตามเนื้อหา ใช้ฟังก์ชัน fit-content() ถ้าตั้ง 3 คอลัมน์สุดท้ายเป็น fit-content(50px) คอลัมน์จะปรับขนาดตามเนื้อหา
grid-template-columns สำหรับ Desktop จะเป็น 1fr repeat(3, fit-content(50px))
ความกว้างคอลัมน์ดูดีแล้ว!
ปรับการจัดข้อความลิงก์ในคอลัมน์ ตอนนี้จัดกึ่งกลางเพราะ justify-items: center สำหรับ Mobile
เปลี่ยนสำหรับ Desktop เป็น justify-items: end จัดลิงก์ขวา แล้วจัด Home link ซ้ายด้วย justify-self: start สำหรับ selector .fullwidth
ตอนนี้เห็น Desktop Navigation สำหรับ Grid เสร็จแล้ว ตรงกับ Flexbox Navigation:
Flexbox หรือ Grid?
ในการสร้าง Navigation นี้ Flexbox Styles ใช้ 46 บรรทัดใน style.scss และ HTML Markup ซับซ้อนกว่า
Grid Styles ใช้เพียง 24 บรรทัด! ส่วนใหญ่ต่างกันที่ Flexbox ต้องเพิ่ม margin-bottom และ margin-right สำหรับระยะห่าง Grid จัดการหมดด้วย grid-gap
ผมชอบโค้ดที่เบามากของ CSS Grid แต Flexbox ยังเป็นตัวเลือกดีหากคุณคล่องกับ Flexbox!
ดู Source Code ได้ ที่นี่ บน Codepen