Babel Coder

สร้าง UI แบบ Masonry ด้วย Flexbox, Multi-Columns Layout และ CSS Grid Layout

beginner

Masonry ศัพท์ทางวิศวกรรมที่หมายถึงการก่อสร้างด้วยอิฐ หากเราพูดถึง Masonry Wall ความหมายจึงเป็นกำแพงที่สร้างจากอิฐนั่นเอง

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

Masonry Wall

การสร้างรูปแบบ Masonry สามารถใช้ JavaScript ได้ แต่ก็ไม่ยากเช่นกันที่จะสำเร็จด้วยความสามารถของ CSS อย่างเดียว สำหรับบทความนี้เราจะมาใช้ Flexbox, CSS Multiple Columns Layout และ CSS Grid Layout เพื่อสร้างความสามารถนี้ให้ปรากฎในหน้าเว็บของเรา

สารบัญ

ก่ออิฐแนวนอนด้วย Horizontal Masonry

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

Horizontal Masonry

เริ่มแรกเราต้องมีส่วนของ HTML ซะก่อน โดยแต่ละ box ถือว่าเป็นอิฐแต่ละก้อน ส่วนขนาดของอิฐและการวางตัวนั้น เราจะใช้ CSS เป็นเครื่องกำหนดครับ

<div class="container">
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
</div>

การวางกล่องนั้นเราวางไปตามแถวต่าง ๆ โดยเริ่มวางจากซ้ายไปขวาและเมื่อสุดขอบของแต่ละแถวให้ทำการขึ้นแถวใหม่ ตรงจุดนี้เราจะใช้ flex-wrap เพื่อปัดอิฐก้อนถัดไปให้ตกไปแถวใหม่แทนที่จะต่อแถวยาวเฟื้อยทะลุขอบจักรวาล

Horizontal Wrap

.container {
  counter-reset: box;
  display: flex;
  flex-wrap: wrap;
  max-width: 1080px;
  margin: 0 auto;
  border: 1px dashed #AAB2BD;
}

เพื่อไม่ให้อิฐดูพิการเราจึงกำหนดความสูงให้กับมันไปเลย

.box {
  height: 100px;
  flex: 1 1 auto;
}

เพราะเราสั่งอิฐมาจากต่างโรงงานกันขนาดจึงต่างกันโดยสิ้นเชิง อิฐหมายเลข 1, 4, 8, … จะมีขนาด 50px และมีขนาดเป็น 200px ถ้าเป็นอิฐหมายเลข 2, 6, 10, … โดยอิฐหมายเลข 3, 7, 11, … ขนาดเป็น 150px ส่วนสุดท้ายคืออิฐหมายเลข 4, 8, 12, … จะมีขนาด 100px เราเขียนเงื่อนไขทั้งหมดได้ด้วย CSS ดังนี้

// อิฐหมายเลข 1, 4, 8, ...
.box:nth-child(4n + 1) {
  width: 50px;
  background-color: #48CFAD;
}

// อิฐหมายเลข 2, 6, 10, ...
.box:nth-child(4n + 2) {
  width: 200px;
  background-color: #4FC1E9;
}

// อิฐหมายเลข 3, 7, 11, ...
.box:nth-child(4n + 3) {
  width: 150px;
  background-color: #ED5565;
}

// อิฐหมายเลข 4, 8, 12, ...
.box:nth-child(4n + 4) {
  width: 100px;
  background-color: #AC92EC;
}

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

.container {
  // reset ค่า counter โดย box จะเป็นตัวนีบเพื่อทำการเพิ่มค่าในแต่ละรอบ
  counter-reset: box;
  // ...
}

.box {
  // ...
  position: relative;
}

.box::before {
  // เพื่มค่าตัวเลขในแต่ละกล่อง
  counter-increment: box;
  content: counter(box);
  color: #fff;
  // จัดตำแหน่งตัวเลข
  position: absolute;
  left: calc(50% - 10px);
  top: calc(50% - 8px);
  line-height: 18px;
  width: 20px;
  text-align: center;
}

เมื่อทุกอย่างเสร็จสิ้นเราจะได้ผลลัพธ์เป็นการเรียงอิฐลักษณะเช่นนี้

Horizontal Masonry

หากเพื่อน ๆ คนไหนต้องการดูผลลัพธ์ด้วยตาตัวเองสามารถคัดลอกโค้ดข้างล่างนี้ไปทดสอบดูได้ครับ

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Horizontal Masonry</title>
  <style>
    *,
    ::before,
    ::after {
      box-sizing: border-box;
    }

    .container {
      counter-reset: box;
      display: flex;
      flex-wrap: wrap;
      max-width: 1080px;
      margin: 0 auto;
      border: 1px dashed #AAB2BD;
    }

    .box {
      height: 100px;
      flex: 1 1 auto;
      position: relative;
      border: 2px solid #FFCE54;
    }

    .box::before {
      counter-increment: box;
      content: counter(box);
      color: #fff;
      position: absolute;
      left: calc(50% - 10px);
      top: calc(50% - 8px);
      line-height: 18px;
      width: 20px;
      text-align: center;
    }

    .box:nth-child(4n + 1) {
      width: 50px;
      background-color: #48CFAD;
    }

    .box:nth-child(4n + 2) {
      width: 200px;
      background-color: #4FC1E9;
    }

    .box:nth-child(4n + 3) {
      width: 150px;
      background-color: #ED5565;
    }

    .box:nth-child(4n + 4) {
      width: 100px;
      background-color: #AC92EC;
    }
  </style>
</head>

<body>
  <div class="container">
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
  </div>
</body>

</html>

ก่ออิฐแนวตั้งด้วย Vertical Masonry ผ่าน Flexbox

บางครั้งการเรียงกล่องตามแนวนอนให้มีความสูงเท่ากันก็เป็นเรื่องที่น่าเบื่อ โดยเฉพาะกับเว็บที่เน้นรูปภาพ เมื่อแต่ละภาพมีความสูงไม่เท่ากัน ถ้าเรายังดื้อดึงให้ทุกภาพมีความสูงเท่ากันด้วยการทำ Horizontal Masonry สัดส่วนของภาพก็จะเสียไปทันที ทางออกของ Masonry ที่ดีกว่าจึงเป็นการวางอิฐตามแนวตั้งหรือก็คือการทำ Vertical Masonry นั่นเอง

Vertical Masonry

ยังจำกันได้ไหมครับ ถ้าการเรียงอิฐตามแนวนอน Flexbox จะทำให้อิฐทุกก้อนมีความสูงเท่ากันหมด แน่นอนว่าถ้าเรายังคงใช้ flex-direction: row อิฐก้อนที่ 1 กับก้อนที่ 6 จะเป็นแบบในรูปไม่ได้ เพราะการวางตัวแบบนั้น 1 และ 6 จะต้องสูงเท่ากัน

เพื่อให้ข้อจำกัดด้านความสูงไม่เป็นอุปสรรคในการสร้าง Masonry เราจึงจำเป็นต้องเรียงอิฐตามแนวดิ่งด้วยการตั้งค่า flex-direction: column

Masonry Column Flow

.container {
  counter-reset: box;
  display: flex;
  // wrap หมายถึง หากไปล่างสุดของแถวแล้ว อิฐก้อนถัดไปให้ขึ้นแถวใหม่
  flex-flow: column wrap;
  max-width: 1080px;
  // จำเป็นต้องตั้งค่าความสูง ไม่เช่นนั้นอิฐจะเรียงยาวลงไปอยู่แค่แถวเดียว
  max-height: 100vh;
  margin: 0 auto;
  border: 1px dashed #AAB2BD;
}

.box {
  // ตั้งใจให้เรียงอิฐแค่ 3 ก้อนใน 1 แถว
  // จึงกำหนดให้อิฐแต่ละก้อนมีความกว้าง 33.3% เมื่อครบสามก้อนจะเป็น 100%
  width: 33.333%;
  flex: 0 0 auto;
  // ...
}

ทุกอย่างก็เหมือนจะไปได้สวย แต่… หลังจากลองดูผลลัพธ์แล้วอุบาทมาก

Vertical Masonry Problem

จากภาพพบว่าแม้เราต้องการจะให้มีผลลัพธ์เกิดขึ้นแค่สามคอลัมน์ แต่ความจริงนั้นไซร์คือล้นกรอบหนักมาก นั้นเพราะเราตั้งความสูงของ Flex Container ไว้ที่ 100vh อิฐจึงไม่สามารถวางซ้อนได้เกินกว่านี้แล้ว เมื่อขึ้นคอลัมน์ใหม่ไปเรื่อย ๆ จึงเป็นผลให้เกิดการทะลุจอดังภาพ

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

อะ ๆ เอาโค้ดทั้งหมดไปรันให้ช้ำใจอีกรอบก่อนไปสู่วิิธีแก้ปัญหาในหัวข้อถัดไปครับ

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Vertical Masonry</title>
  <style>
    *,
    ::before,
    ::after {
      box-sizing: border-box;
    }

    .container {
      counter-reset: box;
      display: flex;
      flex-flow: column wrap;
      max-width: 1080px;
      max-height: 100vh;
      margin: 0 auto;
      border: 1px dashed #AAB2BD;
    }

    .box {
      width: 33.333%;
      flex: 0 0 auto;
      position: relative;
      border: 2px solid #FFCE54;
    }

    .box::before {
      counter-increment: box;
      content: counter(box);
      color: #fff;
      position: absolute;
      left: calc(50% - 10px);
      top: calc(50% - 8px);
      line-height: 18px;
      width: 20px;
      text-align: center;
    }

    .box:nth-child(4n + 1) {
      min-height: 50px;
      background-color: #48CFAD;
    }

    .box:nth-child(4n + 2) {
      min-height: 200px;
      background-color: #4FC1E9;
    }

    .box:nth-child(4n + 3) {
      min-height: 150px;
      background-color: #ED5565;
    }

    .box:nth-child(4n + 4) {
      min-height: 100px;
      background-color: #AC92EC;
    }
  </style>
</head>

<body>
  <div class="container">
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
  </div>
</body>

</html>

ก่ออิฐแนวตั้งด้วย Vertical Masonry ผ่าน CSS Multi-column Layout

CSS Multi-column Layout เป็นส่วนหนึงของมาตรฐาน CSS ที่ทำให้เราสามารถสร้างคอลัมน์ขึ้นมาตามจำนวนที่กำหนด และควบคุมการไหลของข้อมูลให้ไปตามคอลัมน์ต่าง ๆ ได้ เมื่อเราประยุกต์ใช้คุณสมบัตินี้กับ Masonry ผลลัพธ์ที่ได้ควรเป็นเช่นนี้

Column Masonry

เริ่มแรกเราต้องการสร้าง Masonry แบบสามคอลัมน์โดยห้ามมีช่องว่าง (gap) ระหว่างคอลัมน์ .container ของเราหน้าตาจึงเป็นเช่นนี้

.container {
  // ...
  column-count: 3;
  column-gap: 0;
  max-width: 1080px;
  margin: 0 auto;
  border: 1px dashed #AAB2BD;
}

จากโค้ดข้างต้นสังเกตได้ว่าเรามิได้กำหนดความสูงของ .container เลย เจ้าตัวคอลัมน์จะพยายามแบ่งความสูงแบบเฉลี่ย ๆ กันไป โดยไม่มีข้อมูลล้นกรอบแบบการใช้ Flexbox อีกแล้ว

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

Column Masonry Problem

ปัญหานี้สามารถแก้ได้ง่ายมากด้วยการบอกเบราเซอร์ว่า ได้โปรดเถอะข้าขอร้อง อย่าหั่นอิฐราคาแพงของข้าเป็นสองท่อนเลยด้วย break-inside: avoid

.box {
  // ...
  break-inside: avoid;
}

เมื่อทุกอย่างเสร็จสิ้นเราก็จะได้ผลลัพธ์อันสุดยอดแบบนี้

Column Masonry

และนี่คือโค้ดทั้งหมดของเราที่สร้าง Vertical Masonry ด้วย CSS Multi-column Layout

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Vertical Masonry</title>
  <style>
    *,
    ::before,
    ::after {
      box-sizing: border-box;
    }

    .container {
      counter-reset: box;
      column-count: 3;
      column-gap: 0;
      max-width: 1080px;
      margin: 0 auto;
      border: 1px dashed #AAB2BD;
    }

    .box {
      position: relative;
      border: 2px solid #FFCE54;
      break-inside: avoid;
    }

    .box::before {
      counter-increment: box;
      content: counter(box);
      color: #fff;
      position: absolute;
      left: calc(50% - 10px);
      top: calc(50% - 8px);
      line-height: 18px;
      width: 20px;
      text-align: center;
    }

    .box:nth-child(4n + 1) {
      min-height: 100px;
      background-color: #48CFAD;
    }

    .box:nth-child(4n + 2) {
      min-height: 200px;
      background-color: #4FC1E9;
    }

    .box:nth-child(4n + 3) {
      min-height: 150px;
      background-color: #ED5565;
    }

    .box:nth-child(4n + 4) {
      min-height: 250px;
      background-color: #AC92EC;
    }
  </style>
</head>

<body>
  <div class="container">
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
  </div>
</body>

</html>

CSS Multi-column Layout ช่วยให้การทำ Verticla Masonry เป็นเรื่องง่ายพอ ๆ กับที่เราสร้าง Horizontal Masonry ได้อย่างง่ายด้วย Flexbox แต่นั่นก็ยังไม่ช่วยให้ปัญหาของเราหมดไป ลองจินตนาการดูครับหากความต้องการของเราคือมีอิฐบางก้อนใหญ่ชนิดข้ามแถวหรือข้ามคอลัมน์ได้ แบบนี้จะทำอย่างไร?

Grid Masonry

สร้าง Masonry อย่างสมบูรณ์แบบด้วย CSS Grid Layout

เราอธิบายเรื่องความหมายและการใช้งาน CSS Grid Layout กันไปแล้ว ดังนั้นผมจะไม่กล่าวซ้ำอีกครั้งในบทความนี้ แต่เราจะเสริมบางสิ่งที่ขาดหายไปเพื่อเติมเต็มให้ Masonry มีความสมบูรณ์

สิ่งแรกที่เราต้องการให้ Grid ตัดสินใจคือจำนวนคอลัมน์สำหรับ Masonry วิธีการของเราคือสั่งให้ Grid พิจารณาเองด้วยการสร้างจำนวนคอลัมน์ให้พอเหมาะตามเงื่อนไขที่ว่า แต่ละคอลัมน์จะมีความกว้างได้ไม่ต่ำกว่า 200px และความกว้างสูงสุดคือ 1fr คำสั่งนี้ให้ทำการสร้างคอลัมน์ไปเรื่อย ๆ (repeat) จนกว่าจะเต็มพื้นที่แสดงผล

.container {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  // ...
}

เมื่อเราบอกว่า Grid จะสร้าง tracks ขึ้นมาเองตามความเป็นไปได้ เราจึงควรกำหนดด้วยว่าเมื่อ Grid สร้างแต่ละแถวขึ้นมาจะให้ tracks ของแต่ละแถวมีความสูงเท่าไร ในที่นี้คือ 1fr หรือก็คือให้ทุกแถวมีความสูงเท่ากัน

.container {
  grid-auto-rows: 1fr;
  // ...
}

ถึงตอนนี้โค้ดเต็ม ๆ ของเราจะเป็นดังนี้ครับ

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Grid Masonry</title>
  <style>
    *,
    ::before,
    ::after {
      box-sizing: border-box;
    }

    .container {
      counter-reset: box;
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
      grid-auto-rows: 1fr;
      max-width: 1080px;
      height: 100vh;
      margin: 0 auto;
      border: 1px dashed #AAB2BD;
    }

    .box {
      position: relative;
      border: 2px solid #FFCE54;
    }

    .box::before {
      counter-increment: box;
      content: counter(box);
      color: #fff;
      position: absolute;
      left: calc(50% - 10px);
      top: calc(50% - 8px);
      line-height: 18px;
      width: 20px;
      text-align: center;
    }

    .box:nth-child(4n + 1) {
      background-color: #48CFAD;
    }

    .box:nth-child(4n + 2) {
      grid-row: span 2;
      grid-column: span 2;
      background-color: #4FC1E9;
    }

    .box:nth-child(4n + 3) {
      background-color: #ED5565;
    }

    .box:nth-child(4n + 4) {
      grid-row: span 2;
      grid-column: span 2;
      background-color: #AC92EC;
    }
  </style>
</head>

<body>
  <div class="container">
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
    <div class="box"></div>
  </div>
</body>

</html>

เมื่อทดสอบโปรแกรมเราจะได้ผลลัพธ์อันไม่น่าพึงพอใจ ดังนี้

Grid Masonry Problem

คุณหลอกดาว ทำไม Masonry ของเราช่างมีรูแหว่งอย่างกับคนฟันหลอเยี่ยงนี้

CSS Grid Layout มีหลักการที่เรียกว่า Auto-Placement หลักการดังกล่าวเป็นตัวช่วยในการตัดสินใจว่า เมื่อเราไม่ทำการระบุตำแหน่งการวางของ Grid Item จะให้ของชิ้นนั้นวางตำแหน่งไหนดี เราสามารถกำหนดให้เบราเซอร์ทำการสแกนหาช่องวางในแนวแถวเพื่ออุดฟันหลอด้วยการวาง Grid Item ที่พอเหมาะผ่าน grid-auto-flow: dense ดังนี้

.container {
  grid-auto-flow: dense;
  // ...
}

เพียงเท่านี้เราก็จะได้ Masonry สวยงามตามแบบฉบับของ CSS Grid Layout

Grid Masonry

สิ่งหนึ่งที่ต้องพึงระวังเป็นพิิเศษคือ เมื่อเราใช้ grid-auto-flow: dense Grid Item ของเราอาจไม่ได้เรียงตามลำดับอีกต่อไปครับ

แสดงรูปภาพแบบ Masonry ด้วย CSS Grid Layout

หลังจากผ่านทฤษฎีมาเยอะแล้ว เราลองนำหลักการจากตัวอย่างที่แล้วมาเปลี่ยนเป็นรูปภาพกันครับ ตัวอย่างภาพและโค้ดของเราจะเป็นดังนี้ ไม่อธิบายเยอะแล้วเนอะ เจ็บขี้ฟัน

Grid Image Masonry

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Masonry</title>
  <style>
    *,
    ::before,
    ::after {
      box-sizing: border-box;
    }

    .container {
      counter-reset: box;
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
      grid-auto-rows: 1fr;
      grid-auto-flow: dense;
      grid-gap: 5px;
      max-width: 1080px;
      height: 100vh;
      margin: 0 auto;
    }

    .box {
      margin: 0;
      display: flex;
      flex-direction: column;
    }

    .box>img {
      flex: 1;
      max-width: 100%;
      object-fit: cover;
    }

    .box>figcaption {
      padding: 0.5rem 1rem;
      background-color: #48CFAD;
      color: #fff;
      text-align: center;
    }

    .box:nth-child(2n + 1) {
      grid-row: span 2;
      grid-column: span 2;
    }
  </style>
</head>

<body>
  <div class="container">
    <figure class="box">
      <img src="https://source.unsplash.com/collection/311028" />
      <figcaption>Autumn</figcaption>
    </figure>
    <figure class="box">
      <img src="https://source.unsplash.com/collection/395888" />
      <figcaption>Halloween</figcaption>
    </figure>
    <figure class="box">
      <img src="https://source.unsplash.com/collection/1999207" />
      <figcaption>Political</figcaption>
    </figure>
    <figure class="box">
      <img src="https://source.unsplash.com/collection/827807" />
      <figcaption>Technic</figcaption>
    </figure>
    <figure class="box">
      <img src="https://source.unsplash.com/collection/1538150" />
      <figcaption>Milkyway</figcaption>
    </figure>
    <figure class="box">
      <img src="https://source.unsplash.com/collection/1424240" />
      <figcaption>Animals</figcaption>
    </figure>
    <figure class="box">
      <img src="https://source.unsplash.com/collection/1767181" />
      <figcaption>Blancs</figcaption>
    </figure>
  </div>
</body>

</html>

สรุป

การสร้าง Masonry ด้วยเทคนิคต่าง ๆ ของ CSS นั้นไม่ใช่เรื่องยากครับ แต่ละเทคนิคก็มีความยากง่ายต่างกัน หากเราต้องการสร้าง Horizontal Masonry การใช้งาน Flexbox ก็จะดูง่าย แต่หากต้องการสร้าง Vertical Masonry แทน การใช้งาน CSS Multi-columns Layout ดูเหมือนจะเป็นทางออกมากกว่า และเมื่อไหร่ที่เราต้องการให้แต่ละกล่องของเราข้ามไปมาระหว่างแถวและคอลัมน์ได้ CSS Grid Layout ถือเป็นตัวช่วยที่ดีที่สุด


แสดงความคิดเห็นของคุณ


No any discussions