The Flexible Box

In this tutorial, we will deal with the flexible box. The first version of the flexbox layout module was introduced as a Candidate Recommendation by the World Wide Web Consortium (W3C) in 2012. In 2016 the final version of the specification was published as a W3C Recommendation.

Let's start by creating a new project folder. In the terminal we type:

mkdir restaurant_menu

Let's change directories into its folder:

cd restaurant_menu

Next, we will create two files: index.html and index.css:

touch index.html index.css

We will start by editing the index.html file. We will add a Doctype declaration, an html element, and a head and a body elements inside the html element. Inside the head element, we will add a viewport meta tag, import the EB Garamond font from Google Fonts, link to the index.css file, and finally add a title :

<!DOCTYPE html>
<html lang="en">
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  <link href="https://fonts.googleapis.com/css2?family=EB+Garamond:wght@400;700&display=swap" rel="stylesheet">
  <link rel="stylesheet" href="/index.css">
  <title>Our Restaurant Menu</title>
</head>
<body></body>
</html>

Let's edit the index.css file to make use of the font we imported and to add some basics styles to our document:

body {
  font-family: "EB Garamond", serif;
  font-size: 16px;
  font-weight: 400;
  max-width: 1280px;
}

Inside the body element, we will add an h1 heading:

...
<body>
  <h1>Our Menu</h1>
</body

Let's style this element in index.css:

h1 {
  text-align: center;
  font-weight: 700;
  font-size: 32px;
  margin-bottom: 30px;
}

Our menu will have four sections: Appetizers, Soups, Main Courses, and Desserts.

We will wrap the four sections inside a div element, which is a  flex container:

<div class="menu-container">
</div>

Let's style it. To turn this element into a flex container, we will utilize the display property and assign the value of flex to it.

.menu-container {
  display: flex;
}

The first section of our menu is the Appetizers section. Below is the HTML:

<section class="menu-section">
  <h2>Appetizers</h2>
  <ul>
    <li>
      <div class="menu-title">
        <div>
          <span class="menu-number">01.</span>
          <span class="menu-text">Crispy Fried Calamari</span>
        </div>
        <span>$12</span>
      </div>
      <p>A classic appetizer with a crispy exterior and tender, flavorful calamari inside.
      <p>
    </li>
    <li>
      <div class="menu-title">
        <div>
          <span class="menu-number">02.</span>
          <span class="menu-text">Baked Stuffed Mushrooms</span>
        </div>
        <span>$9</span>
      </div>
      <p>Plump and juicy mushrooms filled with savory stuffing and baked to perfection.</p>
    </li>
    <li>
      <div class="menu-title">
        <div><span class="menu-number">03.</span>
          <span class="menu-text">Cheese and Charcuterie Board</span>
        </div>
        <span>$15</span>
      </div>
      <p>A delightful assortment of artisanal cheeses, cured meats, nuts, and fruit.</p>
    </li>
  </ul>
</section>

Let's add some basics styles to the h2 and ul elements:

h2 {
  font-size: 24px;
  font-weight: 700;
}
ul {
  list-style-type: none;
  padding: 0;
}

Please notice the structure of our menu. The menu is numbered, and each item has a title with the name and the price. Below this title is a short description of the dish.

The paragraph with the class of menu-title is a flex container:

.menu-title {
  display: flex;
}

The content in a flex container can be laid out in any direction. To determine the arrangement of items within a flex container, we use the flex-direction property, which sets the direction of the main axis of the flex container. Its default value is row. That means that the direction of the main axis is the same as the inline direction of the current writing mode. The initial value of our writing mode is left-to-right since we use English as our document language.  We want the content of this paragraph to be laid out leftwards, so we don't need to specify any value for the flex-direction property.

If we would change the direction of the flow, from right to left, we would set the value of this property to row-reverse.

If we would like to change our items' direction from top to bottom, we will use the column value for this property. If we would like to reverse the flow, from bottom to top, we would use the column-reverse value.

We want the name of the dish on the left, and the price to be pushed to the right. How can we achieve that? We will use the justify-content property, which aligns items along the main axis of the current line of the flex container.  Its initial value is flex-start, which means the items are packed at the start of the line. We will change this value to space-between, which will distribute the items evenly along the line:

justify-content: space-between;

Other possible values of this property are flex-end, center, space-around, and space-evenly.

The flex-end value aligns items to the end of the line.

The space-around value distributes the items evenly along the line with equal space around them.

The space-evenly value distributes the items evenly along the line with equal space around them and at the beginning and end of the line.

Let's add a right margin to the span with the menu-number class:

.menu-number {
  margin-right: 10px;
}

We'll also change the color of the span with the menu-text class and make it bold:

.menu-text {
  color: #7b5f2a;
  font-weight: 700;
}

Between the name of the dish and its price, we'll add a decorative border. Let's change our html to include another span element between the number and the name:

<section class="menu-section">
  <h2>Appetizers</h2>
  <ul>
    <li>
      <div class="menu-title">
        <div>
          <span class="menu-number">01.</span>
          <span class="menu-text">Crispy Fried Calamari</span>
        </div>
        <span class="decorative-border"></span>
        <span>$12</span>
      </div>
      <p>A classic appetizer with a crispy exterior and tender, flavorful calamari inside.
      <p>
    </li>
    <li>
      <div class="menu-title">
        <div>
          <span class="menu-number">02.</span>
          <span class="menu-text">Baked Stuffed Mushrooms</span>
        </div>
        <span class="decorative-border"></span>
        <span>$9</span>
      </div>
      <p>Plump and juicy mushrooms filled with savory stuffing and baked to perfection.</p>
    </li>
    <li>
      <div class="menu-title">
        <div><span class="menu-number">03.</span>
          <span class="menu-text">Cheese and Charcuterie Board</span>
        </div>
        <span class="decorative-border"></span>
        <span>$15</span>
      </div>
      <p>A delightful assortment of artisanal cheeses, cured meats, nuts, and fruit.</p>
    </li>
  </ul>
</section>

And add a styling rule in index.css:

.decorative-border {
  border-bottom: 1px dotted black;
}

We don't see this span because it has no width. But we can make this item grow using the flex-grow property, which sets how much an item will grow relative to the items in the same flex container. Its initial value is 0. But, if we set its value to 1, this item will take up all the available space. Let's do it:

flex-grow: 1

The opposite of the flex-grow property is the flex-shrink property. This property sets how much a flex item will shrink relative to the flex items in the container when negative space is distributed. Its initial value is 1.

The flex shorthand property sets the flex-grow property, and the flex-shrink property, along with another property called flex-basis, which sets the main size of a flex item. It is advised to use the flex shorthand property when possible. We will use here the auto value, which makes the item fully flexible, meaning it will occupy all the available space. This auto value is equivalent to flex: 1 1 auto.

So let's use the flex property instead of the flex-grow property:

flex: auto;

Let's add some margins to the left and right of this border:

 margin: 0 10px;

Now let's add another section to our menu. Let's open the index.html file and add this below the Appetizer section:

<section class="menu-section">
  <h2>Soups</h2>
  <ul>
    <li>
      <div class="menu-title">
        <div>
          <span class="menu-number">01.</span>
          <span class="menu-text">Lobster Bisque</span>
        </div>
        <span class="decorative-border"></span>
        <span>$12</span>
      </div>
      <p>A creamy and decadent soup made with tender chunks of lobster and savory broth.</p>
    </li>
    <li>
      <div class="menu-title">
        <div>
          <span class="menu-number">02.</span>
          <span class="menu-text">Tomato and Basil Soup</span>
        </div>
        <span class="decorative-border"></span>
        <span>$8</span>
      </div>
      <p>A comforting and classic soup made with fresh tomatoes and fragrant basil.</p>
    </li>
    <li>
      <div class="menu-title">
        <div>
          <span class="menu-number">03.</span>
          <span class="menu-text">Butternut Squash Soup</span>
        </div>
        <span class="decorative-border"></span>
        <span>$10</span>
      </div>
      <p>A rich and creamy soup with sweet roasted butternut squash and warm spices.</p>
    </li>
  </ul>
</section>

The two sections are side by side now because the initial value of the flex-direction property is row. That's why we will change it to column. Let's add this inside the styling rule for our container:

.menu-container {
  display: flex;
  flex-direction: column;
}

Now it looks better on small devices, but on large devices, we want two columns, because it is a lot of space. So let's add a rule for devices larger than 1024px:

@media only screen and (min-width: 1024px) {
  .menu-container {
    flex-flow: row wrap;
  } 
}

On large devices, we will add some margins to the sections too. Add this inside the @media rule :

.menu-section {
   margin: 0 auto 40px;
  }

To achieve a two-column layout for large devices, we'll use the flex property to set its size and add some white space between them:

flex: 0 1 calc(50% - 60px);

It is equivalent to:

flex-grow: 0;
flex-stretch: 1;
flex-basis:  calc(50% - 60px);

Now, let's add another two sections to our menu:

<section class="menu-section">
  <h2>Main Courses</h2>
  <ul>
    <li>
      <div class="menu-title">
        <div>
          <span class="menu-number">01.</span>
          <span class="menu-text">Pan-Seared Salmon</span>
        </div>
        <span class="decorative-border"></span>
        <span>$22</span>
      </div>
      <p>A succulent and flavorful fillet of salmon, seared to perfection and served with a fresh herb
        salad.</p>
    </li>
    <li>
      <div class="menu-title">
        <div>
          <span class="menu-number">02.</span>
          <span class="menu-text">Grilled Ribeye Steak</span>
        </div>
        <span class="decorative-border"></span>
        <span>$28</span>
      </div>
      <p>A juicy and tender ribeye steak, grilled to your liking and served with garlic mashed potatoes
        and
        grilled
        vegetables.</p>
    </li>
    <li>
      <div class="menu-title">
        <div>
          <span class="menu-number">03.</span>
          <span class="menu-text">Roasted Vegetable Lasagna</span>
        </div>
        <span class="decorative-border"></span>
        <span>$30</span>
      </div>
      <p>Layered pasta, creamy ricotta, and roasted vegetables topped with marinara sauce.</p>
    </li>
  </ul>
</section>
<section class="menu-section">
  <h2>Desserts</h2>
  <ul>
    <li>
      <div class="menu-title">
        <div>
          <span class="menu-number">01.</span>
          <span class="menu-text">Tiramisu</span>
        </div>
        <span class="decorative-border"></span>
        <span>$6</span>
      </div>
      <p>Classic Italian dessert made with layers of espresso-soaked ladyfingers and creamy mascarpone
        cheese.</p>
    </li>
    <li>
      <div class="menu-title">
        <div>
          <span class="menu-number">02.</span>
          <span class="menu-text">Key Lime Pie</span>
        </div>
        <span class="decorative-border"></span>
        <span>$6</span>
      </div>
      <p>Tart and sweet pie made with fresh key lime juice and a graham cracker crust.</p>
    </li>
    <li class="list-item">
      <div class="menu-title">
        <div>
          <span class="menu-text">03.</span>
          <span class="menu-text">Raspberry Cheesecake</span>
        </div>
        <span class="decorative-border"></span>
        <span>$6</span>
      </div>
      <p>Cheesecake with graham cracker crust and raspberry sauce.</p>
    </li>
  </ul>
</section>

  Notice how they occupy a new line on large devices.

Conclusion

We have built a simple restaurant menu web page using the Flexbox layout. The layout adjusts to different screen sizes in a flexible manner. In today's digital landscape, where users access websites on a wide range of devices with varying screen sizes and capabilities, flexible and adaptable web design is more important than ever. Furthermore, using Flexbox allows us to create complex layouts with less code, making our web pages more efficient and easier to maintain. In conclusion, mastering the use of CSS Flexbox is a valuable skill for any web developer. Due to its flexibility and ease of use, it allows us to create beautiful and responsive layouts that improve the user experience on a wide range of devices.

Post last updated on May 13, 2023