Back to my Blog

Flexbox Responsive Calendar


Last modified: 22nd September 2022

There’s been a long running argument as to the most semantic way to code a calendar. For me the answer is tables, but in the age of responsive websites they aren’t always a viable option.

I had to develop a calendar recently for a project I was working on and I decided to build it using Flexbox. If you’re not familiar, this article ‘A Guide to Flexbox‘ is a great starting point.

View my demo on Codepen

The HTML

I went with an ordered list as this felt like the most semantic, non-tabular way of displaying a calendar. It’s pretty simple, nothing out of the ordinary.

<ol class="calendar-month">
  <li class="week">
    <ol class="days">
      <div class="week__header">Monday</div>
      <div class="week__header">Tuesday</div>
      <div class="week__header">Wednesday</div>
      <div class="week__header">Thursday</div>
      <div class="week__header">Friday</div>
      <div class="week__header">Saturday</div>
      <div class="week__header">Sunday</div>
    </ol>
  </li>
  <li class="week">
    <ol class="days">
      <li class="day day--empty"></li>
      <li class="day day--empty"></li>
      <li class="day day--empty"></li>
      <li class="day">
        <a href="#">
          <span class="day__number">1</span>
        </a>
      </li>
      <li class="day">
        <a href="#">
          <span class="day__number">2</span>
        </a>
      </li>
      <li class="day">
        <a href="#">
          <span class="day__number">3</span>
        </a>
      </li>
      <li class="day">
        <a href="#">
          <span class="day__number">4</span>
        </a>
      </li>
    </ol>
  </li>
   <li class="week">
    <ol class="days">
      <li class="day">
        <a href="#">
          <span class="day__number">5</span>
        </a>
      </li>
      <li class="day">
        <a href="#">
          <span class="day__number">6</span>
        </a>
      </li>
      <li class="day">
        <a href="#">
          <span class="day__number">7</span>
        </a>
      </li>
      <li class="day">
        <a href="#">
          <span class="day__number">8</span>
        </a>
      </li>
      <li class="day">
        <a href="#">
          <span class="day__number">9</span>
        </a>
      </li>
      <li class="day">
        <a href="#">
          <span class="day__number">10</span>
        </a>
      </li>
      <li class="day">
        <a href="#">
          <span class="day__number">11</span>
        </a>
      </li>
    </ol>
  </li>
  <li class="week">
    <ol class="days">
      <li class="day">
        <a href="#">
          <span class="day__number">12</span>
        </a>
      </li>
      <li class="day">
        <a href="#">
          <span class="day__number">13</span>
        </a>
      </li>
      <li class="day">
        <a href="#">
          <span class="day__number">14</span>
        </a>
      </li>
      <li class="day">
        <a href="#">
          <span class="day__number">15</span>
        </a>
      </li>
      <li class="day">
        <a href="#">
          <span class="day__number">16</span>
        </a>
      </li>
      <li class="day">
        <a href="#">
          <span class="day__number">17</span>
        </a>
      </li>
      <li class="day">
        <a href="#">
          <span class="day__number">18</span>
        </a>
      </li>
    </ol>
  </li>
  <li class="week">
    <ol class="days">
      <li class="day">
        <a href="#">
          <span class="day__number">19</span>
        </a>
      </li>
      <li class="day">
        <a href="#">
          <span class="day__number">20</span>
        </a>
      </li>
      <li class="day">
        <a href="#">
          <span class="day__number">21</span>
        </a>
      </li>
      <li class="day">
        <a href="#">
          <span class="day__number">22</span>
        </a>
      </li>
      <li class="day">
        <a href="#">
          <span class="day__number">23</span>
        </a>
      </li>
      <li class="day">
        <a href="#">
          <span class="day__number">24</span>
        </a>
      </li>
      <li class="day">
        <a href="#">
          <span class="day__number">25</span>
        </a>
      </li>
    </ol>
  </li>
  <li class="week">
    <ol class="days">
      <li class="day">
        <a href="#">
          <span class="day__number">26</span>
        </a>
      </li>
      <li class="day">
        <a href="#">
          <span class="day__number">27</span>
        </a>
      </li>
      <li class="day">
        <a href="#">
          <span class="day__number">28</span>
        </a>
      </li>
      <li class="day">
        <a href="#">
          <span class="day__number">29</span>
        </a>
      </li>
      <li class="day">
        <a href="#">
          <span class="day__number">30</span>
        </a>
      </li>
      <li class="day day--empty"></li>
      <li class="day day--empty"></li>
    </ol>
  </li>
</ol>

The CSS

I’m using Sass here but feel free to adjust for CSS or LESS. The ‘flex’ is applied to the .week class, treating each .week as an individual row.

The &:first-letter removes the rest of the word when you reduce down to mobile.

I did it originally using span’s and display:none but this felt tidier.

$border: #e8e8e8;
$cell-width: 14.2857%;

/* Calendar Styles */

.calendar-month {
  width: 90%;
  margin: 0 auto;
  padding: 20px;
  background: #fff;
  list-style-type: none;

  .week {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;

    &__header {
      order: 1;
      background: #fff;
      border-right: 1px solid $border;
      text-align: center;
      padding: 20px;
      text-transform: uppercase;
      color: #333;
      letter-spacing: 2px;
      width: $cell-width;
      box-sizing: border-box;

      &:nth-child(7) {
        border-right: 0;
      }
      
      &:first-letter {
        font-size: 12px;
      }

      @media (max-width: 767px) {
        padding: 10px 0;
        font-size: 0;
      }
    }
  }
  
  .days {
    list-style-type: none;
    margin: 0;
    padding: 0;
    display: flex;
    width: 100%;
  }

  .day {
    position: relative;
    order: 1;
    width: $cell-width;
    background: #fff;
    border-right: 1px solid $border;
    border-top: 1px solid $border;
    transition: 0.5s;
    box-sizing: border-box;

    &:nth-child(7n + 7) {
      border-left: none;
    }

    &:nth-child(7n + 7) {
      border-right: none;
    }

    &:after {
      /* Create responsive square div */
      content: "";
      display: block;
      padding-bottom: 100%;
    }

    &:hover {
      background: #eee;
    }

    &__number {
      position: absolute;
      right: 20px;
      top: 20px;
      margin: 0;

      @media (max-width: 767px) {
        right: 5px;
        top: 5px;
      }
    }

    &--empty {
      background: #f8f8f8;
      
      &:hover {
        cursor: default;
        background: #f8f8f8; 
      }
    }
  }

  a {
    display: block;
    width: 100%;
    height: 100%;
  }
}

View my demo on Codepen

Any improvements?

I’m all ears! Hit me up on Twitter @jo_eyre or drop me an email.

Leave a Reply

Your email address will not be published. Required fields are marked *


«
»