Freelance Front End Developer

Flexbox Responsive Calendar

Posted in: Front End Code

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 a 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%;
  }
}



Any improvements?

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

Tags: