Grid system

The grid system in Halfmoon is a flexbox based, 12-column system, that can be used to quickly (and easily) build websites and pages which look good on all screen sizes.

First column
Second column
Third column
<div class="container-fluid">
  <div class="row">
    <div class="col-sm">First column</div>
    <div class="col-sm">Second column</div>
    <div class="col-sm">Third column</div>
  </div>
</div>

How it works

Here's a list of things to understand about the grid system in Halfmoon.

  • Columns must be placed inside a container with the .row class for them to work properly.
  • Because of flexbox, columns without a specified width will automatically take up equal widths. For example, four instances of .col-sm will each automatically be 25% wide from the small breakpoint and up.
  • The maximum number of columns that can be fit into one row is 12. If this number is exceeded, a new row will be automatically created. The column that does the exceeding will drop down to the new row, along with ones coming after it.
  • If the column value is specified, the column will take up */12 of the width of the parent container, where * is the value of the column.
  • There are five breakpoints — extra small, small, medium, large, and extra large, so columns can have different widths on different screen sizes.
  • Grid breakpoints in the class names apply to that one breakpoint and all those above it. For instance, in the above example, .col-sm applies to small, medium, large, and extra large devices, but not the first extra small breakpoint
  • Rows and columns have no margin or padding (no gutters). See the practical examples section to see how this works out better for alignment in real life use cases.

The following table explains how the grid system works for the different breakpoints.

All
(Incl. <= 576px)
Small and up
(> 576px)
Medium and up
(> 768px)
Large and up
(> 992px)
Extra large
(> 1200px)
Class prefix .col- .col-sm- .col-md- .col-lg- .col-xl-
No. of columns 12 12 12 12 12
Gutters None None None None None

With all that said, let's take a look at another example.

1
11
2
10
3
9
4
8
5
7
6
6
7
5
8
4
9
3
10
2
11
1
<div class="container-fluid">
  <div class="row">
    <div class="col-1">1</div>
    <div class="col-11">11</div>
  </div>
  <div class="row">
    <div class="col-2">2</div>
    <div class="col-10">10</div>
  </div>
  ...
  <div class="row">
    <div class="col-6">6</div>
    <div class="col-6">6</div>
  </div>
  ...
  <div class="row">
    <div class="col-10">10</div>
    <div class="col-2">2</div>
  </div>
  <div class="row">
    <div class="col-11">11</div>
    <div class="col-1">1</div>
  </div>
</div>

Auto-layout columns #

Thanks to flexbox, width of the columns can be set automatically. A few things to note about auto-layout columns:

  • Columns with the class .col will take up equal widths on all screen sizes. See the first row in the example below.
  • If a column has any of the .col-{breakpoint}-* classes, that column will take up */12 of the width of the parent container. The rest of the columns (with the .col class) will take up equal chunks of what is left. See the second row in the example below.
  • If a column has the class .col-auto or .col-{breakpoint}-auto, its width will be based on the natural width of its content. See the third row in the example below.
.col
.col
.col
.col
.col-6
.col
.col-auto (150px)
.col
<div class="container-fluid">
  <!-- First row -->
  <div class="row">
    <div class="col">.col</div>
    <div class="col">.col</div>
    <div class="col">.col</div>
  </div>
  <!-- Second row -->
  <div class="row">
    <div class="col">.col</div>
    <div class="col-6">.col-6</div>
    <div class="col">.col</div>
  </div>
  <!-- Third row -->
  <div class="row">
    <div class="col-auto">
      <div class="w-150"> <!-- w-150 = width: 15rem (150px) -->
        .col-auto (150px)
      </div>
    </div>
    <div class="col">.col</div>
  </div>
</div>

Responsive columns #

Columns can be made to take up different widths depending on screen sizes using the different class prefixes. Multiple column classes can be used together. This is the best way of making websites responsive.

.col-xl-6
.col-xl-6
.col-md-6
.col-md-6
.col-lg-3
.col-md-4
.col-sm-5
.col-6
.col-lg-3
.col-md-4
.col-sm-5
.col-6
.col-lg-3
.col-md-4
.col-sm-5
.col-6
.col-lg-3
.col-md-4
.col-sm-5
.col-6
.col-lg-3
.col-md-4
.col-sm-5
.col-6
.col-lg-3
.col-md-4
.col-sm-5
.col-6
<div class="container-fluid">
  <!-- First row -->
  <div class="row">
    <div class="col-xl-6">
      ...
    </div>
    <div class="col-xl-6">
      ...
    </div>
  </div>
  <!-- Second row -->
  <div class="row">
    <div class="col-md-6">
      ...
    </div>
    <div class="col-md-6">
      ...
    </div>
  </div>
  <!-- Third row -->
  <div class="row">
    <div class="col-6 col-sm-5 col-md-4 col-lg-3">
      ...
    </div>
    <div class="col-6 col-sm-5 col-md-4 col-lg-3">
      ...
    </div>
  </div>
  <!-- Fourth row -->
  <div class="row">
    <div class="col-6 col-sm-5 col-md-4 col-lg-3">
      ...
    </div>
    <div class="col-6 col-sm-5 col-md-4 col-lg-3">
      ...
    </div>
    <div class="col-6 col-sm-5 col-md-4 col-lg-3">
      ...
    </div>
    <div class="col-6 col-sm-5 col-md-4 col-lg-3">
      ...
    </div>
  </div>
</div>

How it works

Here's a list of things to understand about responsive columns in Halfmoon.

  • Columns with breakpoints specified in their class names will maintain their widths as long as the screen width remains above the breakpoint. For example, in the first row above, the columns have the class .col-xl-6, so they will take up half of the parent's width on extra large screens (width > 1200px). Below this width, the columns will "collapse" and take up the full width of their parent container. The same thing also applies to the second row, but there, 768px is the breakpoint.
  • Larger column class prefixes are given priority over the smaller ones when applicable. For example, with the third row above, the columns will take up 3/12 of the width on large (and up) screens. As the screen size decreases, each column will take up 4/12 on medium screens, then 5/12 on small screens, and finally stop at 6/12 on extra small screens.

Practical examples #

As mentioned above, columns have no margins or padding by default. In practice, we recommend always using content containers (.content) and cards (.card) inside columns for framing, since they are the main tools for framing content in the main section of the page (opens in new tab).

It was going to rain

The weather forecast didn't say that, but the steel plate in his hip did. He had learned over the years to trust his hip over the weatherman. It was going to rain, so he better get outside and prepare.

It was going to rain

The weather forecast didn't say that, but the steel plate in his hip did. He had learned over the years to trust his hip over the weatherman. It was going to rain, so he better get outside and prepare.

<div class="container-fluid">
  <div class="row">
    <!-- First column -->
    <div class="col-sm-6">
      <div class="content">
        <h2 class="content-title">
          It was going to rain
        </h2>
        <p>
          The weather forecast didn't say that, but the steel plate in his hip did. He had learned over the years to trust his hip over the weatherman. It was going to rain, so he better get outside and prepare.
        </p>
      </div>
    </div>
    <!-- Second column -->
    <div class="col-sm-6">
      <div class="content">
        <h2 class="content-title">
          It was going to rain
        </h2>
        <p>
          The weather forecast didn't say that, but the steel plate in his hip did. He had learned over the years to trust his hip over the weatherman. It was going to rain, so he better get outside and prepare.
        </p>
      </div>
    </div>
  </div>
</div>

Title of the card

Title of the card

Title of the card

Title of the card

<div class="container-fluid">
  <!-- First row -->
  <div class="row">
    <div class="col-6">
      <div class="card">
        ...
      </div>
    </div>
    <div class="col-6">
      <div class="card">
        ...
      </div>
    </div>
  </div>
  <!-- Second row -->
  <div class="row">
    <div class="col-6">
      <div class="card">
        ...
      </div>
    </div>
    <div class="col-6">
      <div class="card">
        ...
      </div>
    </div>
  </div>
</div>

Equal in-between spacing #

In the previous example with the cards, you may have noticed that the spacing between the cards looks a bit off. This is because the margins between the cards stack up. The in-between spacing can be made equal by adding the .row-eq-spacing (or .row-eq-spacing-{breakpoint} for particular screen sizes) to the rows. The following table explains how it works.

Breakpoint All
(Incl. <= 576px)
Small and up
(> 576px)
Medium and up
(> 768px)
Large and up
(> 992px)
Extra large
(> 1200px)
Class .row-eq-spacing .row-eq-spacing-sm .row-eq-spacing-md .row-eq-spacing-lg .row-eq-spacing-xl

Now, let's fix the last example with the cards using the .row-eq-spacing class.

Title of the card

Title of the card

Title of the card

Title of the card

<!-- Equal in-between spacing with cards -->
<div class="container-fluid">
  <!-- First row -->
  <div class="row row-eq-spacing">
    <div class="col-6">
      <div class="card">
        ...
      </div>
    </div>
    <div class="col-6">
      <div class="card">
        ...
      </div>
    </div>
  </div>
  <!-- Second row -->
  <div class="row row-eq-spacing">
    <div class="col-6">
      <div class="card">
        ...
      </div>
    </div>
    <div class="col-6">
      <div class="card">
        ...
      </div>
    </div>
  </div>
</div>

It is important to remember that equal in-between spacing only works with cards (.card) and content containers (.content) placed directly inside the columns.

Equal in-between spacing works with content containers (.content), therefore, everything can be equally spaced. For instance, in the next example, four alert boxes are placed inside columns with equal in-between spacing. The alerts themselves are placed inside content containers (.content). Here, the .row-eq-spacing-md class is used because we want equal spacing only on medium screens and above, since below this, the columns will "collapse" and take up the full width of their parent container.

<!-- Equal in-between spacing with content -->
<div class="container-fluid">
  <!-- First row -->
  <div class="row row-eq-spacing-md">
    <div class="col-md-6">
      <div class="content">
        <div class="alert alert-primary" role="alert">
          <h4 class="alert-heading">Primary alert</h4>
          Contextual alert box with some placeholder content and <a href="#" class="alert-link">a link</a>.
        </div>
      </div>
    </div>
    <div class="col-md-6">
      <div class="content">
        <div class="alert alert-success" role="alert">
          <h4 class="alert-heading">Success alert</h4>
          Contextual alert box with some placeholder content and <a href="#" class="alert-link">a link</a>.
        </div>
      </div>
    </div>
  </div>
  <!-- Second row -->
  <div class="row row-eq-spacing-md">
    <div class="col-md-6">
      <div class="content">
        <div class="alert alert-secondary" role="alert">
          <h4 class="alert-heading">Secondary alert</h4>
          Contextual alert box with some placeholder content and <a href="#" class="alert-link">a link</a>.
        </div>
      </div>
    </div>
    <div class="col-md-6">
      <div class="content">
        <div class="alert alert-danger" role="alert">
          <h4 class="alert-heading">Danger alert</h4>
          Contextual alert box with some placeholder content and <a href="#" class="alert-link">a link</a>.
        </div>
      </div>
    </div>
  </div>
</div>

A few more things to note

Here's a list of a few more things worth noting about equal in-between spacing:

  • The content container (.content) or card (.card) must be the immediate child of the column.
  • Only the class for the smallest breakpoint is required even if columns are set to different widths for larger breakpoints. For example, on the first and second rows in the example below, each column has the classes .col-sm-6, .col-md-5, and .col-lg-4. The rows however only have the .row-eq-spacing-sm class because the classes for the larger breakpoints, ie, .row-eq-spacing-md and .row-eq-spacing-lg are not needed.
  • When a row overflows, ie, column values exceed 12, a vertical spacer (.v-spacer) should be used right at the point of overflow. This can be hidden for breakpoints where overflow does not occur using utility classes. See the third row in the example below.

Card inside column with...

.col-lg-4
.col-md-5
.col-sm-6

Card inside column with...

.col-lg-4
.col-md-5
.col-sm-6

Card inside column with...

.col-lg-4
.col-md-5
.col-sm-6

Card inside column with...

.col-lg-4
.col-md-5
.col-sm-6

Title of the card

Title of the card

Title of the card

Title of the card

<div class="container-fluid">
  <!-- First row -->
  <div class="row row-eq-spacing-sm justify-content-center"> <!-- justify-content-center = justify-content: center -->
    <div class="col-sm-6 col-md-5 col-lg-4">
      <div class="card">
        ...
      </div>
    </div>
    <div class="col-sm-6 col-md-5 col-lg-4">
      <div class="card">
        ...
      </div>
    </div>
  </div>
  <!-- Second row -->
  <div class="row row-eq-spacing-sm justify-content-center"> <!-- justify-content-center = justify-content: center -->
    <div class="col-sm-6 col-md-5 col-lg-4">
      <div class="card">
        ...
      </div>
    </div>
    <div class="col-sm-6 col-md-5 col-lg-4">
      <div class="card">
        ...
      </div>
    </div>
  </div>
  <!-- Third row -->
  <div class="row row-eq-spacing">
    <!-- First column -->
    <div class="col-6 col-lg-3">
      <div class="card">
        ...
      </div>
    </div>
    <!-- Second column -->
    <div class="col-6 col-lg-3">
      <div class="card">
        ...
      </div>
    </div>

    <!-- Overflow occurs here on medium screens and down, so vertical spacer is added -->
    <div class="v-spacer d-lg-none"></div> <!-- d-lg-none = display: none only on large screens and up (> 992px) -->

    <!-- Third column -->
    <div class="col-6 col-lg-3">
      <div class="card">
        ...
      </div>
    </div>
    <!-- Fourth column -->
    <div class="col-6 col-lg-3">
      <div class="card">
        ...
      </div>
    </div>
  </div>
</div>

Vertical alignment #

Columns can be vertically aligned using flex utilities (opens in new tab).

.col (1 of 3)
.col (2 of 3)
.col (3 of 3)



.col (1 of 3)
.col (2 of 3)
.col (3 of 3)



.col (1 of 3)
.col (2 of 3)
.col (3 of 3)



.col (1 of 3)
.col (2 of 3)
.col (3 of 3)
<div class="container-fluid">
  <!-- Row with items aligned at the start -->
  <div class="row align-items-start h-100"> <!-- align-items-start = align-items: flex-start, h-100 = height: 10rem (100px) -->
    <div class="col">.col (1 of 3)</div>
    <div class="col">.col (2 of 3)</div>
    <div class="col">.col (3 of 3)</div>
  </div>
  <!-- Row with items aligned at the center -->
  <div class="row align-items-center h-100"> <!-- align-items-center = align-items: center, h-100 = height: 10rem (100px) -->
    <div class="col">.col (1 of 3)</div>
    <div class="col">.col (2 of 3)</div>
    <div class="col">.col (3 of 3)</div>
  </div>
  <!-- Row with items aligned at the end -->
  <div class="row align-items-end h-100"> <!-- align-items-end = align-items: flex-end, h-100 = height: 10rem (100px) -->
    <div class="col">.col (1 of 3)</div>
    <div class="col">.col (2 of 3)</div>
    <div class="col">.col (3 of 3)</div>
  </div>
  <!-- Row with items aligned individually -->
  <div class="row h-100"> <!-- h-100 = height: 10rem (100px) -->
    <div class="col align-self-start">.col (1 of 3)</div> <!-- align-self-start = align-self: flex-start -->
    <div class="col align-self-center">.col (2 of 3)</div> <!-- align-self-center = align-self: center -->
    <div class="col align-self-end">.col (3 of 3)</div> <!-- align-self-end = align-self: flex-end -->
  </div>
</div>

Horizontal alignment #

Similarly, columns can be horizontally aligned using flex utilities (opens in new tab).

.col-4
.col-4
.col-4
.col-4
.col-4
.col-4
.col-4
.col-4
.col-4
.col-4
<div class="container-fluid">
  <!-- Row with items justified at the start -->
  <div class="row justify-content-start"> <!-- justify-content-start = justify-content: flex-start -->
    <div class="col-4">.col-4</div>
    <div class="col-4">.col-4</div>
  </div>
  <!-- Row with items justified at the center -->
  <div class="row justify-content-center"> <!-- justify-content-center = justify-content: center -->
    <div class="col-4">.col-4</div>
    <div class="col-4">.col-4</div>
  </div>
  <!-- Row with items justified at the end -->
  <div class="row justify-content-end"> <!-- justify-content-end = justify-content: flex-end -->
    <div class="col-4">.col-4</div>
    <div class="col-4">.col-4</div>
  </div>
  <!-- Row with items justified around -->
  <div class="row justify-content-around"> <!-- justify-content-around = justify-content: space-around -->
    <div class="col-4">.col-4</div>
    <div class="col-4">.col-4</div>
  </div>
  <!-- Row with items justified between -->
  <div class="row justify-content-between"> <!-- justify-content-between = justify-content: space-between -->
    <div class="col-4">.col-4</div>
    <div class="col-4">.col-4</div>
  </div>
</div>

Nested columns #

Columns can be easily nested by placing rows inside of columns. No additional classes are required for this.

.col-5
.col-6
.col-6
.col-7
.col-4
.col-4
.col-4
<div class="container-fluid">
  <div class="row">
    <!-- First column -->
    <div class="col-5">
      .col-5
      <!-- Nested -->
      <div class="row">
        <div class="col-6">.col-6</div>
        <div class="col-6">.col-6</div>
      </div>
    </div>
    <!-- Second column -->
    <div class="col-7">
      .col-7
      <!-- Nested -->
      <div class="row">
        <div class="col-4">.col-4</div>
        <div class="col-4">.col-4</div>
        <div class="col-4">.col-4</div>
      </div>
    </div>
  </div>
</div>

Column offsets #

Columns can be offset using .offset-* or .offset-{breakpoint}-*, where * is the left margin of the column (and it can have any value between 0 and 11). For example, the class .offset-md-4 adds a margin to the left of the column equal to the width of .col-md-4.

.col-6
.col-5
.col-5
.col-lg-4
.col-md-5
.col-sm-6
.col-lg-4
.col-md-5
.col-sm-6
.col-lg-6
.col-md-4
.col-lg-6
.col-md-4
<!-- Rows with column offsets -->
<div class="container-fluid">
  <!-- First row -->
  <div class="row">
    <div class="col-6 offset-3">.col-6</div> <!-- With offset -->
  </div>
  <!-- Second row -->
  <div class="row">
    <div class="col-5">.col-5</div>
    <div class="col-5 offset-2">.col-5</div> <!-- With offset -->
  </div>
  <!-- Third row -->
  <div class="row">
    <div class="col-sm-6 col-md-5 col-lg-4">
      ...
    </div>
    <div class="col-sm-6 col-md-5 col-lg-4 offset-md-2 offset-lg-4"> <!-- With offset -->
      ...
    </div>
  </div>
  <!-- Fourth row -->
  <div class="row">
    <div class="col-md-4 col-lg-6">
      ...
    </div>
    <div class="col-md-4 col-lg-6 offset-md-4 offset-lg-0"> <!-- With offset and reset -->
      ...
    </div>
  </div>
</div>

Please note that you may need to reset the offset in some cases using .offset-{breakpoint}-0. For example, this is done in the fourth row above to prevent overflow on large screens (and up).

Ordering columns #

The visual order of the columns can be controlled using the following classes:

  • .order-*/.order-{breakpoint}-*, where * is the order value
  • .order-first/.order-{breakpoint}-first
  • .order-last/.order-{breakpoint}-last
First
(unordered)
Second,
but last
Third,
but first
First,
but last
(When screen > 768px)
Second,
but first
(When screen > 768px)
First,
but last
Second
(unordered)
Third,
but first
<!-- Rows with ordering -->
<div class="container-fluid">
  <!-- First row -->
  <div class="row">
    <div class="col">First (unordered)</div>
    <div class="col order-12">Second, but last</div>
    <div class="col order-1">Third, but first</div>
  </div>
  <!-- Second row -->
  <div class="row">
    <div class="col order-md-12">First, but last (When screen > 768px)</div>
    <div class="col order-md-1">Second, but first (When screen > 768px)</div>
  </div>
  <!-- Third row -->
  <div class="row">
    <div class="col order-last">First, but last</div>
    <div class="col">Second (unordered)</div>
    <div class="col order-first">Third, but first</div>
  </div>
</div>

Adjacent rows #

There are two types of rows in Halfmoon:

  • Normal rows
  • Rows with equal in-between spacing

Due to there being two types of rows, only four combinations of adjacent rows are possible in a layout:

  1. Normal row — normal row
  2. Normal row — row with equal in-between spacing
  3. Row with equal in-between spacing — row with equal in-between spacing
  4. Row with equal in-between spacing — normal row

For combinations 1, 2 and 4, the content-containers (.content) and cards (.card) of the normal rows need to be given the utility classes .mt-0 and/or .mb-0 depending on what comes before and/or after. This is so that everything is properly aligned and spaced vertically.

However, the easier thing to do is to simply add the .row-eq-spacing/.row-eq-spacing-{breakpoint} classes to all the rows. This will automatically fix the spacing and alignment. For instance, in the next example, a fairly complex grid layout (with multiple adjacent rows) is created with ease using these classes.

Title of the section

Title of the section

Title of the card

Title of the card

Title of the section

Title of the card

Title of the card

<!-- Adjacent rows inside the container-fluid -->
<div class="container-fluid">
  <!-- First row (equally spaced on small screens and up) -->
  <div class="row row-eq-spacing-sm">
    <div class="col-sm-6">
      <div class="content">
        ...
      </div>
    </div>
    <div class="col-sm-6">
      <div class="content">
        ...
      </div>
    </div>
  </div>
  <!-- Second row (equally spaced on small screens and up) -->
  <div class="row row-eq-spacing-sm">
    <div class="col-sm-10">
      <div class="content">
        ...
      </div>
    </div>
    <div class="col-sm-2">
      <div class="content">
        ...
      </div>
    </div>
  </div>
  <!-- Third row (equally spaced) -->
  <div class="row row-eq-spacing">
    <div class="col-6">
      <div class="card">
        ...
      </div>
    </div>
    <div class="col-6">
      <div class="card">
        ...
      </div>
    </div>
  </div>
  <!-- Fourth row (equally spaced on large screens and up) -->
  <div class="row row-eq-spacing-lg">
    <div class="col-lg-8">
      <div class="content">
        ...
      </div>
      <div class="card">
        ...
      </div>
      <div class="content">
        ...
      </div>
    </div>
    <div class="col-lg-4">
      <div class="card">
        ...
      </div>
    </div>
  </div>
</div>

Building a page #

We built a page in the content and cards section (opens in new tab). Lets improve that page using the grid system to add some content to the side.

Page title

Card title

Content title #

Card title

On this page

<body>
  <!-- Page wrapper with content-wrapper inside -->
  <div class="page-wrapper">
    <div class="content-wrapper">
      <!-- Container-fluid -->
      <div class="container-fluid">
        <div class="row">
          <!-- Column with the main content -->
          <div class="col-lg-9">
            <div class="content">
              <h1 class="content-title font-size-22"> <!-- font-size-22 = font-size: 2.2rem (22px) -->
                Page title
              </h1>
              ...
            </div>
            <div class="card">
              <h2 class="card-title">Card title</h2>
              ...
            </div>
            <div class="content">
              <h2 class="content-title">Content title <a href="#">#</a></h2>
              ...
            </div>
            <div class="card">
              <h2 class="card-title">Card title</h2>
              ...
            </div>
            <div class="content">
              ...
            </div>
          </div>
          <!-- Column with the side content -->
          <div class="col-lg-3 d-none d-lg-block"> <!-- d-none = display: none, d-lg-block = display: block only on large screens and up (> 992px) -->
            <div class="content">
              <h2 class="content-title font-size-16">On this page</h2> <!-- font-size-16 = font-size: 1.6rem (16px) -->
              ...
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</body>

In the above example, there is only one row, so there is no point in making it equally spaced using .row-eq-spacing-lg. Moreover, it is strongly recommended that the page wrapper (.page-wrapper), along with the content wrapper (.content-wrapper), is always used to build pages. You can learn more about this in the page building section (opens in new tab) of the docs.

Building a dashboard #

On this section, let's build a dashboard using the things described above (along with some utility classes). The page below is very similar to ones found in real-life dashboards of applications.

Dashboard

Orders

Sales

Costs

Profits

Customers

Breakdown


Customer stories #

Transactions

Activity log #

<body>
  <!-- Page wrapper with content-wrapper inside -->
  <div class="page-wrapper">
    <div class="content-wrapper">
      <!-- Container-fluid -->
      <div class="container-fluid">
        <!-- First comes a content container with the main title -->
        <div class="content">
          <h1 class="content-title font-size-22"> <!-- font-size-22 = font-size: 2.2rem (22px) -->
            Dashboard
          </h1>
          ...
        </div>
        <!-- First row (equally spaced) -->
        <div class="row row-eq-spacing">
          <div class="col-6 col-xl-3">
            <div class="card">
              <h2 class="card-title">Orders</h2>
              ...
            </div>
          </div>
          <div class="col-6 col-xl-3">
            <div class="card">
              <h2 class="card-title">Sales</h2>
              ...
            </div>
          </div>
          <!-- Overflow occurs here on large screens (and down) -->
          <!-- Therefore, a v-spacer is added at this point -->
          <div class="v-spacer d-xl-none"></div> <!-- d-xl-none = display: none only on extra large screens (> 1200px) -->
          <div class="col-6 col-xl-3">
            <div class="card">
              <h2 class="card-title">Costs</h2>
              ...
            </div>
          </div>
          <div class="col-6 col-xl-3">
            <div class="card">
              <h2 class="card-title">Profits</h2>
              ...
            </div>
          </div>
        </div>
        <!-- Second row (equally spaced on large screens and up) -->
        <div class="row row-eq-spacing-lg">
          <div class="col-lg-8">
            <div class="card h-lg-250 overflow-y-lg-auto"> <!-- h-lg-250 = height = 25rem (250px) only on large screens and up (> 992px), overflow-y-lg-auto = overflow-y: auto only on large screens and up (> 992px) -->
              <h2 class="card-title">Customers</h2>
              ...
            </div>
          </div>
          <div class="col-lg-4">
            <div class="card h-lg-250 overflow-y-lg-auto"> <!-- h-lg-250 = height = 25rem (250px) only on large screens and up (> 992px), overflow-y-lg-auto = overflow-y: auto only on large screens and up (> 992px) -->
              <h2 class="card-title">Breakdown</h2>
              ...
            </div>
          </div>
        </div>
        <!-- Third row (equally spaced on large screens and up) -->
        <div class="row row-eq-spacing-lg">
          <div class="col-lg-8">
            <div class="content">
              <h2 class="content-title">Customer stories <a href="#">#</a></h2>
              ...
            </div>
            <div class="card">
              <h2 class="card-title">Transactions</h2>
              ...
            </div>
            <div class="content">
              ...
            </div>
          </div>
          <div class="col-lg-4">
            <div class="content">
              <h2 class="content-title">Activity log <a href="#">#</a></h2>
              ...
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</body>

In the above example, a .v-spacer is used in the first row to adjust for the overflow of columns. Also, in the row with the Customers and Breakdown cards, the height of the cards are fixed and the overflow-y is set to auto for large screens and up. This is because the Customers card can have varying content, and ideally, the two cards side by side should have the same height to look good.

It is worth repeating that it is strongly recommended that the page wrapper (.page-wrapper), along with the content wrapper (.content-wrapper), is always used to build pages. Again, you can learn more about this in the page building section (opens in new tab) of the docs.

Credits and special thanks #

The grid system in Halfmoon is almost identical to the one found in Bootstrap (with a few changes). Therefore, huge credits (and special thanks) must be given to the Bootstrap team and contributors. The appropriate copyright notice can be found in the CSS files.


Up next: Navbar

Coming soon

  • More components
  • More variables and customization
  • Form builder and validator JS
  • Themes and templates

Subscribe for updates


  • We will notify you when the framework gets a substantial update. No spam or marketing.
  • You can also follow us on Twitter and stay updated that way.