Creating a React Calendar Component: Part 3
Tags: react, webdev, sass, css
Published: 2020-07-12 20:24:00

Creating an appealing visual component can be an effective way to draw someone towards a part of your web application. In [part 1](https://dev.to/bertbae/creating-a-react-calendar-component-part-1-31j1) and [part 2](https://dev.to/bertbae/creating-a-react-calendar-component-part-2-1nep) of this series, we explored the logic behind the dates to display and the React code that used a set of dates to render a visible component in the browser. In this next part, we will explore the code that gives our component the visual appeal.

When I first began coding, I never enjoyed CSS as much as the other aspects of development. My first job had a singular CSS file controlling the entire web application's styling. Having limited experience with CSS at the time, this was an absolute nightmare. However, that experience is what drove me to further explore the best way to structure my CSS code moving forward and in future projects. Much like other parts of development, CSS has its own design patterns and practices which were further enabled by technologies such as [SASS](https://sass-lang.com/) and [Styled Components](https://styled-components.com/).

Before we begin, the following examples will be in SASS which is a superset of CSS. SASS is compiled into CSS, but allows functionality which make organizing styling code easier. My 3 favorite aspects of SASS is that it allows nesting of CSS for specificity, creating variables for reusability, and importability of SASS files into each other. Let's do a breakdown of what we'll be taking a look at today:

1. _Styling the calendar header using Flexbox_
2. _Styling the month indicators using Flexbox_
3. _Styling the weekday indicators using Grid CSS_
4. _Styling the date indicators using Grid CSS_
5. _Adding in themes_

---

**Section 1: Styling the calendar header using Flexbox**

Before we begin, let's add some CSS to the main `BaeCalendar` component so we can see it on the browser and normalize some HTML element styles.

```css
.bae-calendar-container {
  box-sizing: border-box;
  box-shadow: 0 0 3px rgba(0, 0, 0, 0.5);
  height: 100%;
  width: 100%;

  h1,
  h2,
  h3,
  h4,
  h5,
  h6,
  p {
    padding: 0;
    margin: 0;
    line-height: 1;
    font-family: 'Varela Round', sans-serif;
  }
}
```

As you can see, elements with a class called `bae-calendar-container` is given a `100%` height and width of its parent element. This allows any users importing the component to wrap it in a different container so they can specify the height and width themselves. For now, it will take on the `body` element's height and width property so we can see it on the browser.

Aside from this, you'll notice that we are taking advantage of SASS's capability of nesting to style the `h1, h2, h3...` elements to normalize its default styles. Without going into great detail, nesting will translate this into a CSS code that will look like this:

```css
.bae-calendar-container h1, .bae-calendar-container h2 {
  padding: 0;
  margin: 0;
  line-height: 1;
  font-family: 'Varela Round', sans-serif;
}
// And so on...
```


![Header with no styling](https://dev-to-uploads.s3.amazonaws.com/i/ig40r6hvahcz3w3z7d92.png)

Above is the component with no styling that we will transform into the following.


![Header with styling](https://dev-to-uploads.s3.amazonaws.com/i/g5wjwum4iijfo37zcjyb.png)


Let's take a moment to look at the HTML layout of the `CalendarHeader` component.

```javascript
<div className="bae-calendar-header">
  <div className="left-container">
    <h1>{getReadableWeekday(selectDate)}</h1>
    <h1>{getReadableMonthDate(selectDate)}</h1>
  </div>
  <div className="right-container">
    <h3>{getYear(selectDate)}</h3>
  </div>
</div>
```

All of the elements we see here are `block` elements which by definition is an element that starts a new line. So how do we end up with 2 columns? Thankfully, flexbox is a very useful CSS property we can take advantage of.

```css
.bae-calendar-header {
  display: flex;
}
```

The `display: flex` property defines an element to act like a `flex` element. A `flex` element acts differently. Essentially, the outer element is still a `block` element, but any inner elements take on a more fluid form. Unless `display: flex` is specified, we're not able to apply any other `flex` based CSS properties.

Here is an example of the component with `display: flex`.


![Header with display flex](https://dev-to-uploads.s3.amazonaws.com/i/s9g2m751k4blkzu2kkbb.png)


Notice how the inner elements are no longer acting as `block` elements? The container itself wrapping the contents will maintain its `block` behavior, but this gives us freedom to control the layout of the inner elements. Let's make it so that the readable dates and the years end up on opposite sides by adding `justify-content: space-between`.

```css
.bae-calendar-header {
  display: flex;
  justify-content: space-between;
}
```

Self-explanatory right? When `flex` is in a row format (e.g. `left-container` and `right-container`), `justify-content` modifies the horizontal layout. Keep in mind, that if you are working with a `flex` element in a column format, `justify-content` will follow the new change and affect the vertical layout. The option we provide `space-between` does exactly what the name states. It provides an even spacing between all elements, but no spacing on the edges.

We're making progress, but let's see if we can provide some space to the edges and a border to show where the `CalendarHeader` component ends.

```css
.bae-calendar-header {
  padding: 15px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
  display: flex;
  justify-content: space-between;
}
```


![Header with padding & shadow](https://dev-to-uploads.s3.amazonaws.com/i/erzvnw21iux8sjr29rxm.png)


Great! Let's move on using the same `flex` tricks to our month indicators.

---

**Section 2: Styling the month indicators using Flexbox**

Similar to the `CalendarHeader` component, the `MonthIndicator` is a `div` element with 3 `block` elements inside.

```javascript
<div className="bae-month-indicator">
  <h4 data-date={monthSet.prev} onClick={changeDate}>
    {monthsFull[getMonth(monthSet.prev)]}
  </h4>
  <h3>{monthsFull[getMonth(monthSet.current)]}</h3>
  <h4 data-date={monthSet.next} onClick={changeDate}>
    {monthsFull[getMonth(monthSet.next)]}
  </h4>
</div>
```

Here's how it looks with no styling...


![Month indicator without styles](https://dev-to-uploads.s3.amazonaws.com/i/yc7jsuftqbrypa1xbyny.png)

And what we want to achieve...


![Month indicator with styles](https://dev-to-uploads.s3.amazonaws.com/i/fgwahe14195bbckpxii8.png)


It looks like we can apply similar `flex` properties so let's add the same things we did to the header.

```css
.bae-month-indicator {
  display: flex;
  justify-content: space-between;
}
```

It looks pretty good so far. However, let me show you one thing. Although you can't tell, these 3 elements are not perfectly centered. This is a case where it probably doesn't matter too much, but it's fairly simple to ensure that all elements are centered vertically with one simple rule.

```css
.bae-month-indicator {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
```

The property `align-items` is similar to `justify-content`. While the `flex` is in a row format, while `justify-content` works horizontally, `align-items` work vertically. A quick trick to center any item is to provide the parent a `display: flex` and `justify-content: center` with `align-items: center` and you ensure the item is centered inside of the parent element.

Knowledge of Flexbox can be incredibly useful. One of the best ways to learn Flexbox is through an interactive game which you can checkout [here](https://flexboxfroggy.com/)!

---

**Section 3: Styling the weekday indicators using Grid CSS**

Now that we've taken the time to see how Flexbox is applied to the calendar component, let's spend some time and explore the basics of Grid CSS. Similar to Flexbox, Grid is used to create the layout of the visual components in a web application. There are some differences between Flexbox and Grid, but for the most part, it is possible to achieve a similar layout result with both.

In my experience, I have some to utilise Flexbox and Grid for different scenarios. I use Flexbox primarily for positioning individual elements, but when it comes to repeated and predictable layouts, I prefer to use Grid. Let's explore this as we style the `WeekdayIndicator`.

Referring back to [part 2]() of the series, here is how the `WeekdayIndicator` looks as React code:

```javascript
const weekdays ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];

const WeekdayIndicator = () => {
  const weekdayIcons = weekdays.map((day, key) => {
    return (
      <div className="weekday-indicator-icon" key={key}>
        {day}
      </div>
    );
  });
  return <div className="bae-weekday-indicators">{weekdayIcons}</div>;
};
```

Which translates to the following HTML:

```javascript
<div className="bae-weekday-indicators">
  <div className="weekday-indicator-icon">
    Sun
  </div>
  <div className="weekday-indicator-icon">
    Mon
  </div>
  <div className="weekday-indicator-icon">
    Tue
  </div>
  <div className="weekday-indicator-icon">
    Wed
  </div>
  <div className="weekday-indicator-icon">
    Thu
  </div>
  <div className="weekday-indicator-icon">
    Fri
  </div>
  <div className="weekday-indicator-icon">
    Sat
  </div>
</div>
```

Without any styling, here is what these elements look like with their natural `block` behaviors.


![Weekday indicators with no styling](https://dev-to-uploads.s3.amazonaws.com/i/w6al6t2e2vrwzhepoxje.png)


And here is what we want it to look like after styling it.


![Weekday indicators styled](https://dev-to-uploads.s3.amazonaws.com/i/h157c4vngpskriaap0gq.png)


While Flexbox can certainly be used to achieve this outcome, we are going to use a combination of Grid to get the weekdays horizontal and evenly distributed while using Flexbox to center the text inside of each `div.weekday-indicator-icon` elements.

Let's start with the Grid using two properties called `grid-template-columns` and `grid-template-rows`.

```css
.bae-weekday-indicators {
  display: grid;
  grid-template-columns: repeat(7, minmax(auto, 1fr));
  grid-template-rows: 1;
  padding: 15px // To keep padding consistent with the other components so far
}
```

In `grid-template-columns`, the value `repeat(7, minmax(auto, 1fr))` is saying create 7 columns with inner elements with its minimum size to `auto` (change to fit) and maximum size to `1fr` (1/7 of the total container size). As an example, you could make it have only 3 columns whereby it will override the `grid-template-rows` by overflowing and creating new rows to fit the number of inner elements inside of the parent container resulting in this.


![Weekday overflow grid example](https://dev-to-uploads.s3.amazonaws.com/i/iql9gjcl5d5m262zq9rn.png)


Let's switch it back and move on. As you can see, the weekday indicator icons are not centered which we'll work on next by using the Flexbox concepts we used above. Since the grid lays out each of the containers wrapping the weekday texts, we need to add some styling to elements with the class name `weekday-indicator-icon`.

```css
.bae-weekday-indicators {
  display: grid;
  grid-template-columns: repeat(7, minmax(auto, 1fr));
  grid-template-rows: 1;
  padding: 15px;
  .weekday-indicator-icon {
    height: 25px;
    width: 25px;
    display: flex;
    justify-self: center;
    justify-content: center;
    align-items: center;
    padding: 5px;
    font-weight: bold;
    cursor: pointer;
  }
}
```

As you can see, I applied the Flexbox trick of centering using `justify-content: center` and `align-items: center`. This ensures that the elements internal to the `div.weekday-indicator-icon` are centered. But, what is `justify-self` doing? In this case, we're applying a horizontal CSS property, but to the element by referring to itself. Here is an example of how the elements look without `justify-self: center` and below that, with it.


![Weekday with no justify self](https://dev-to-uploads.s3.amazonaws.com/i/s1tol9gslf1dlzfwu6dr.png)


![Weekday with justify self](https://dev-to-uploads.s3.amazonaws.com/i/m8wt3dnk5msqnq1sj92x.png)

---

**Section 4: Styling the date indicators using Grid CSS**

Now that we have the grid example above with the `WeekdayIndicator` component, let's apply those same changes to the `DateIndicator` to achieve this.


![Date styled](https://dev-to-uploads.s3.amazonaws.com/i/abvvn70aokkgpsxnoxb7.png)


As a quick refresher, here's the `DateIndicator` component.

```javascript
const DateIndicator = ({ activeDates, selectDate, setSelectDate }) => {
  const changeDate = (e) => {
    setSelectDate(e.target.getAttribute('data-date'));
  };

  const datesInMonth = getDatesInMonthDisplay(
    getMonth(selectDate) + 1,
    getYear(selectDate)
  );

  const monthDates = datesInMonth.map((i, key) => {
    const selected = getMonthDayYear(selectDate) === getMonthDayYear(i.date) ? 'selected' : '';

    return (
      <div
        className=`date-icon ${selected}`
        data-active-month={i.currentMonth}
        data-date={i.date.toString()}
        key={key}
        onClick={changeDate}
      >
        {getDayOfMonth(i.date)}
      </div>
    );
  });

  return <div className="bae-date-indicator">{monthDates}</div>;
};
```

There are two things we want to do here. One is setup the grid using the same principles above using Grid CSS. The other, is visually distinguishing between the active dates in the current month, versus the overflow dates of the previous and following months. The grid part should be simple, so here's what we'll add.

```css
.bae-date-indicator {
  display: grid;
  grid-template-columns: repeat(7, minmax(auto, 1fr));
  grid-template-rows: repeat(6, minmax(35px, 1fr));
  grid-gap: 5px;
  padding: 0 15px;
  .date-icon {
    display: flex;
    justify-content: center;
    justify-self: center;
    align-items: center;
    height: 25px;
    width: 25px;
    padding: 5px;
    cursor: pointer;
  }
}
```

Now, we want to fade out the overflow dates. Looking at the component code, we can see that the data attribute `data-active-month={i.currentMonth}` is applied. This can either be `data-active-month="true"` or `data-active-month="false"`, providing us with a simple way to identify what is the current month or not in our CSS with the following change to target elements with the specificity `.bae-date-indicator .date-icon[data-active-month="false"]`.

```css
.bae-date-indicator {
  display: grid;
  grid-template-columns: repeat(7, minmax(auto, 1fr));
  grid-template-rows: repeat(6, minmax(35px, 1fr));
  grid-gap: 5px;
  padding: 0 15px;
  .date-icon {
    display: flex;
    justify-content: center;
    justify-self: center;
    align-items: center;
    height: 25px;
    width: 25px;
    padding: 5px;
    cursor: pointer;
    &[data-active-month='false'] {
      color: rgba(0, 0, 0, 0.3);
    }
  }
}
```

That's it! The last thing we're going to add is the styling for elements with the `selected` class name which we will use to improve our UI by making it clear to our users which date is currently selected on the calendar.

```css
.bae-date-indicator {
  display: grid;
  grid-template-columns: repeat(7, minmax(auto, 1fr));
  grid-template-rows: repeat(6, minmax(35px, 1fr));
  grid-gap: 5px;
  padding: 0 15px;
  .date-icon {
    display: flex;
    justify-content: center;
    justify-self: center;
    align-items: center;
    height: 25px;
    width: 25px;
    padding: 5px;
    cursor: pointer;
    &[data-active-month='false'] {
      color: rgba(0, 0, 0, 0.3);
    }
    &.selected {
      border-radius: 50%;
      box-shadow: 0 0 3px rgba(0, 0, 0, 0.5);
    }
  }
}
```

At this point, putting our components together with styling should look like this.


![Full component with no theme](https://dev-to-uploads.s3.amazonaws.com/i/o76kx0owi04k9x74nx1t.png)


---

**Section 5: Adding in themes**

Now that we have the layout in place for our component, let's add a little bit of life to it by adding colors. Color matching and design is definitely not my strong suite which is why I want to make this as easy to modify as possible. To apply our themes, we're going to take advantage of CSS specificity rules by using nesting in SASS. Here is our root `bae-calendar.sass` file.

```css
.bae-calendar-container {
  box-sizing: border-box;
  box-shadow: 0 0 3px rgba(0, 0, 0, 0.5);
  height: 100%;
  width: 100%;

  h1,
  h2,
  h3,
  h4,
  h5,
  h6,
  p {
    padding: 0;
    margin: 0;
    line-height: 1;
    font-family: 'Varela Round', sans-serif;
  }
  &.salmon-theme {
    @import './themes/salmon.sass';
  }

  @import './components/calendar-header.sass';
  @import './components/weekday-indicator.sass';
  @import './components/date-indicator.sass';
}
```

As you can see, this root SASS file imports multiple SASS files consisting of the styling for each of our individual sub-components that we styled above. You also see that there is a new file under a class name `salmon-theme` imported with the `&` selector to apply styles on elements with `.bar-calendar-container.salmon-theme`. Here is where we will outline the theme styles for our calendar component.

Inside of our `salmon.sass` file, we have the following:

```css
$primaryColor: #fa8072;
$secondaryColor: #ffa98f;
$highlightTextColor: #d95e39;
$activeTextColor: #f8f8ff;

h1,
h2,
h3,
h4,
h5,
h6 {
  color: $activeTextColor;
}

.bae-calendar-header {
  background-color: $primaryColor;
}

.bae-weekday-indicators {
  .weekday-indicator-icon {
    color: $highlightTextColor;
    &.active {
      background-color: $primaryColor;
      color: $activeTextColor;
    }
  }
}

.bae-date-indicator {
  .date-icon {
    &.active {
      background-color: $secondaryColor;
      color: $activeTextColor;
    }
    &.selected {
      background-color: $primaryColor;
      color: $activeTextColor;
    }
  }
}

.bae-month-indicator {
  background-color: $primaryColor;
}
```

Without going into much detail, can you see how we utilise SASS variable names and the organization of the theme file? Easy to understand right? All of our texts will apply the `color: $activeTextColor` and we see some other changes in the `background-color` properties as well. Here's how the component looks with this style.


![Component fully styled](https://dev-to-uploads.s3.amazonaws.com/i/e00qc1n4y6bcq062srfa.png)


Easy right? Can you see how we can take advantage of the way this has been organized? All we need to do now is select the colors we want for the following variables:

```css
$primaryColor: #fa8072;
$secondaryColor: #ffa98f;
$highlightTextColor: #d95e39;
$activeTextColor: #f8f8ff;
```

For example, `monochrome-theme` uses the following variables.

```css
$primaryColor: #646e78;
$secondaryColor: #8d98a7;
$highlightTextColor: #6d7c90;
$activeTextColor: #f8f8ff;
```

Once we change these variables, we can copy it to a new file and import it again like this.

```css
.bae-calendar-container {
  box-sizing: border-box;
  box-shadow: 0 0 3px rgba(0, 0, 0, 0.5);
  height: 100%;
  width: 100%;

  h1,
  h2,
  h3,
  h4,
  h5,
  h6,
  p {
    padding: 0;
    margin: 0;
    line-height: 1;
    font-family: 'Varela Round', sans-serif;
  }
  &.salmon-theme {
    @import './themes/salmon.scss';
  }
  &.monochrome-theme {
    @import './themes/monochrome.scss';
  }

  @import './components/calendar-header.scss';
  @import './components/weekday-indicator.scss';
  @import './components/date-indicator.scss';
}
```


![Monochrome styled](https://dev-to-uploads.s3.amazonaws.com/i/mno9w4lvjpowd4iarlvz.png)


To make things easier for our users, we can create preset themes and allow them to use the components by specifying which class names to append to the main element. As an example, our calendar component can do something like this to accept a `prop` called `theme` which translates to a specific `className`.

```javascript
import React, { useState } from 'react';
import { getToday } from './utils/moment-utils';
import './bae-calendar.scss';

import CalendarHeader from './components/calendar-header';
import WeekdayIndicator from './components/weekday-indicator';
import DateIndicator from './components/date-indicator';
import MonthIndicator from './components/month-indicator';
// https://uicookies.com/html-calendar/

import { presetDateTracker } from './utils/date-utils';

// preset themes that are available
const themes = {
  salmon: 'salmon-theme',
  monochrome: 'monochrome-theme',
  rouge: 'rouge-theme',
};

const BaeCalendar = ({ theme, activeDates, onDateSelect }) => {
  const presetActiveDates = useRef(presetDateTracker(activeDates || []));
  const [selectDate, setSelectDate] = useState(getToday());

  // Add the theme name to the main container `${themes[theme]}`
  return (
    <div className={`bae-calendar-container ${themes[theme]}`}>
      <CalendarHeader selectDate={selectDate} />
      <WeekdayIndicator />
      <DateIndicator
        activeDates={presetActiveDates.current}
        selectDate={selectDate}
        setSelectDate={setSelectDate}
      />
      <MonthIndicator selectDate={selectDate} setSelectDate={setSelectDate} />
    </div>
  );
};

export default BaeCalendar;
```

This can then be imported as such...

```javascript
<BaeCalendar
  theme="monochrome"
/>
```

And that's it! Hope this information provide some insight in how you can organize the styling for your components. Style files require just as much organization and design as other areas of code so make sure you don't skip out on careful planning when you are creating your next front-end projects. In the next part, I'll show you how the component files were structured and add a few features that will allow our component users to get the `date` they select back to use within their own programs as well as pass in pre-selected dates.