I shared how to build a calendar with CSS Grid in the previous article. Today, I want to share how to build a Flexbox fallback for the same calendar.

How to provide support

Generally, there are three ways to provide support when it comes to CSS.

First method: Write fallback code. Overwrite fallback code.

.selector {
  property: fallback-value;
  property: actual-value;
}

Second method: Write fallback code. Overwrite fallback code in CSS Feature Queries (@supports). Reset properties inside @supports if you need.

.selector {
  property: fallback-value;
}

@supports (display: grid) {
  property: actual-value;
}

Third method: Write everything in @supports.

@supports not (display: grid) {
  .selector {
    property: fallback-value;
  }
}

@supports (display: grid) {
  .selector {
    property: actual-value;
  }
}

These three methods are listed in order of decreasing-complexity. (If you need to overwrite code, it's more complicated). This means writing everything in @supports is the simplest of the three.

How you choose to support your project depends on browser support for:

  1. The feature
  2. The fallback feature
  3. Support for Feature Queries

Checking for support

The best place to check for support is caniuse. Here, I see that support for CSS Grid is decent. Browsers I have to worry about are:

  1. Opera Mini: 1.42% global usage
  2. Android Browsers 2.1 to 4.4.4: 0.67% global usage
  3. Blackberry browser: 0.02% global usage (Not gonna worry about this one).
css-grid-support

Support for the fallback (Flexbox) is also good.

But we have a problem: Flexbox fallback wouldn't work for Android 2.1 to 4.3 (it doesn't support wrapping). Global usage for Android 2.1 to 4.3 is 0.37%.

Here, I have to decide:

  1. Is providing Flexbox fallback for Opera Mini (1.42%), Android 4.4.4 (0.3%), and Blackberry (0.02%) worth the effort?
  2. Should I change fallback from Flexbox to an older feature to support Android 2.1 to 4.3 (another 0.37%)?
flexbox-support

Let's assume, for this project, I decide that Flexbox fallback is sufficient. I'm not going to worry about Android 2.1 to 4.3.

Next, I want to check whether browsers support CSS Feature Queries.

Here, I see:

  1. Opera Mini supports Feature Queries
  2. Android 4.4.4 supports Feature Queries
  3. Blackberry browser doesn't support Feature Queries
  4. IE 11 does't support Feature Queries
feature-queries-support

Deciding how to write fallback code

Earlier, I mentioned there are three ways to write fallback code for CSS:

  1. Write fallback code. Overwrite fallback code.
  2. Write fallback code. Overwrite fallback code in @supports.
  3. Write everything in @supports.

If I write everything inside @supports, I can provide support for:

  1. Opera Mini (1.43%)
  2. Android 4.4.4 (0.3%)

But I lose support for:

  1. IE 11 (2.3%)
  2. Blackberry (0.02%)

I do not want to forsake the 2.3% of IE users, which means Method 3 (write everything in @supports) is out.

If I use Method 2 (Write fallback code. Overwrite fallback code in @supports), I can provide support for:

  1. IE 11 (2.3%)
  2. Opera Mini (1.43%)
  3. Android 4.4.4 (0.3%)
  4. Blackberry browser (0.02%)

That's everything I need. That's why I'm gonna go with Method 2.

Note: If you want to code along, you can use demo from my previous article as the starting point.

Disabling Grid code

First, we park the CSS Grid code under @supports (like we discussed above).

@supports (display: grid) {
  .day-of-week,
  .date-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
  }

  .date-grid button:first-child {
    grid-column: 6;
  }
}

We can disable the CSS Grid code by setting display to an invalid value (not grid). This disables the entire block of code.

(Thank Rachel Andrew for this neat trick. I believe I learned it from her ?).

@supports (display: gridx) {
  /*...*/
}
Initial layout.

Writing Flexbox code

We need to build the same seven-column grid with Flexbox. The first thing we need to do is acknowledge that Flexbox and Grid work differently. We won't be able to get a perfect replica, but we can get close.

The first thing is set display to flex.

.day-of-week,
.date-grid {
  display: flex;
}
Results after setting display to flex.

We need the buttons in .date-grid to wrap, so we set flex-wrap to wrap.

.date-grid {
  flex-wrap: wrap;
}
Buttons in date grid wrapped at the edges.

We need to replicate the seven-column grid. An easy way to do this is calculate the width of the grid according to the width of each button. Here, I have already set each button to 4.5ch. This means the width of the grid should be 7 x 4.5ch.

(We can use CSS Calc to do the math for us).

.day-of-week,
.date-grid {
  max-width: calc(4.5ch * 7);
}
Wrapping at 7 columns

We need the elements in .day-of-week to spread out across the available width. One simple way is to set justify-content to space-between.

.day-of-week {
  justify-content: space-between;
}
After setting space-between.

Here, we can see that elements in .day-of-week extend past the grid. This extension happens because we let Flexbox calculate flex-basis for us. If we want every element in .day-of-week to be have the same width, we need to set flex-basis ourselves.

In this case, the easiest way is to set flex-basis to the width of one grid item (or 4.5ch). Note: I adjusted font-size of each item in .day-of-week to 0.7em (for visual aesthetics). We have to account for this change.

.day-of-week > * {
  flex-basis: calc(4.5ch / 0.7);
}
Adjusted .day-of-week for size.

Finally, we need to push the 1 February to Friday. (Five columns). Since column is 4.5ch, we simply push it by 4.5ch x 5.

(Again, we can use CSS Calc to help us with this).

.date-grid button:first-child {
  margin-left: calc(4.5ch * 5);
}
Pushed 1 Febuary to Friday

Fixing the CSS Grid version

We can reactivate the CSS Grid code and make any necessary changes now.

@supports (display: grid) {
  /* ... */
}
Activating CSS Grid code

Here, we see some values fly far out to the right. This happens because we added margin-left to the first grid item. We need to reset the added margin.

@supports (display: grid) {
  /* ... */
  .date-grid button:first-child {
    grid-column: 6;
    margin-left: 0;
  }
}
Removed margin-left.

Another thing: We can remove max-width because we don't need it in the CSS Code. (Even though this doesn't affect the CSS Code, we still want to remove it. Always better to have less properties).

@supports (display: grid) {
  .day-of-week,
  .date-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    max-width: initial;
  }

  /* ... */
}

Here's the visual difference between the Flexbox and CSS Grid versions. Not too bad!

Visual difference between the Flexbox and CSS Grid code

One fun thing

CSS Grid is cool because it follows writing direction. We can easily change the flow from left-to-right to right-to-left.

Note: I don't know if calendars are read from right to left in rtl languages. I just thought it'll fun to mention this ?).

Switching between ltr and rtl.

Our code for CSS Grid supports this behaviour naturally. If you want to support the same behaviour with Flexbox, you need to use CSS Logical Properties.

Support for CSS Logical Properties.

Since support for CSS Logical Properties is not-so-great, we need to provide fallback for it. (Best way is to through Method 1: Write fallback; overwrite fallback).

.date-grid button:first-child {
  margin-left: calc(4.5ch * 5);
  margin-inline-start: calc(4.5ch * 5);
}

@supports (display: grid) {
  /* ... */
  .date-grid button:first-child {
    grid-column: 6;
    margin-left: 0;
    margin-inline-start: 0;
  }
}

That's it! Here's a Codepen for the final code:

See the Pen Building a Calendar with CSS Grid (and fallback with Flexbox) by Zell Liew (@zellwk) on CodePen.


Thanks for reading. This article was originally posted on my blog. Sign up for my newsletter if you want more articles to help you become a better frontend developer.