Cally

Theming

Learn how to style the components

This serves as a general theming guide. Please read the respective component API docs to see all available styling options.

Out of the box, the components are not particularly pretty. This is intentional. The goal is to provide just enough styles so that the components are functional, and no more. Every style bundled with the components is potentially a style you need to override or revert, and thus is wasted bytes.

<calendar-range
  value="2024-01-10/2024-01-20"
  min="2024-01-01"
  max="2024-12-31"
  locale="en-GB"
  months="2"
>
  <calendar-month></calendar-month>
  <calendar-month offset="1"></calendar-month>
</calendar-range>

Thankfully, styling the components is not difficult. Let's make the above example look nice.

HTML

The first thing we want to do is stack the components horizontally if we have space. Cally's components are designed to allow for flexible markup, so we can add a grid around the <calendar-month> components. Additionally, we can constrain the width of the <calendar-range> component to fit the content

<style>
  .grid {
    display: flex;
    gap: 1.5em;
    flex-wrap: wrap;
    justify-content: center;
  }
</style>
<calendar-range
  value="2024-01-10/2024-01-20"
  min="2024-01-01"
  max="2024-12-31"
  locale="en-GB"
  months="2"
>
  <div class="grid">
    <calendar-month></calendar-month>
    <calendar-month offset="1"></calendar-month>
  </div>
</calendar-range>

That's already much better. We use flexbox to wrap the components if there is not enough space, but otherwise stack them horizontally.

Next, let's replace the "Previous" and "Next" button text with icons. We can do this with slots. Slots are a way to pass content into a component from the outside. The <calendar-range> and <calendar-date> components offer previous and next slots for this purpose.

Note: for brevity we will use native CSS nesting in these examples. This is a fairly new feature, and may not be supported in all browsers.

<style>
  .grid {
    display: flex;
    gap: 1.5em;
    flex-wrap: wrap;
    justify-content: center;
  }

  calendar-range {
    svg {
      height: 16px;
      width: 16px;
      fill: none;
      stroke: currentColor;
      stroke-width: 1.5;
    }

    path {
      stroke-linecap: round;
      stroke-linejoin: round;
    }
  }
</style>
<calendar-range
  value="2024-01-10/2024-01-20"
  min="2024-01-01"
  max="2024-12-31"
  locale="en-GB"
  months="2"
>
  <svg
    aria-label="Previous"
    slot="previous"
    xmlns="http://www.w3.org/2000/svg"
    viewBox="0 0 24 24"
  >
    <path d="M15.75 19.5 8.25 12l7.5-7.5"></path>
  </svg>
  <svg
    aria-label="Next"
    slot="next"
    xmlns="http://www.w3.org/2000/svg"
    viewBox="0 0 24 24"
  >
    <path d="m8.25 4.5 7.5 7.5-7.5 7.5"></path>
  </svg>
  <div class="grid">
    <calendar-month></calendar-month>
    <calendar-month offset="1"></calendar-month>
  </div>
</calendar-range>

Since we are replacing the "Previous"/"Next" text, we need to add some kind of label back in ourselves. We can do this with an aria-label attribute on the SVGs.

CSS

Cally's components can be themed and styled via CSS parts and custom properties. This allows for both coarse and fine-grained styling. If these are new concepts to you, please read the linked MDN docs.

CSS Custom properties

Custom properties are for coarse-grained styling. There are only a few custom properties available, and they relate to accent colors (likely your brand color). Let's imagine our brand color is #7048e8 . For ideal contrast, we will use #ffffff for text on this color.

<style>
  .grid {
    display: flex;
    gap: 1.5em;
    flex-wrap: wrap;
    justify-content: center;
  }

  calendar-range {
    svg {
      height: 16px;
      width: 16px;
      fill: none;
      stroke: currentColor;
      stroke-width: 1.5;
    }

    path {
      stroke-linecap: round;
      stroke-linejoin: round;
    }
  }

  calendar-month {
    --color-accent: #7048e8;
    --color-text-on-accent: #ffffff;
  }
</style>
<calendar-range
  value="2024-01-10/2024-01-20"
  min="2024-01-01"
  max="2024-12-31"
  locale="en-GB"
  months="2"
>
  <svg
    aria-label="Previous"
    slot="previous"
    xmlns="http://www.w3.org/2000/svg"
    viewBox="0 0 24 24"
  >
    <path d="M15.75 19.5 8.25 12l7.5-7.5"></path>
  </svg>
  <svg
    aria-label="Next"
    slot="next"
    xmlns="http://www.w3.org/2000/svg"
    viewBox="0 0 24 24"
  >
    <path d="m8.25 4.5 7.5 7.5-7.5 7.5"></path>
  </svg>
  <div class="grid">
    <calendar-month></calendar-month>
    <calendar-month offset="1"></calendar-month>
  </div>
</calendar-range>

CSS Parts

CSS parts are for fine-grained styling. Only certain elements are exposed as a part. An element may have multiple parts, and a part may be used on multiple elements. Parts may be added or removed conditionally, depending on the state of the component. Parts can be used in isolation or combined.

Each component offers many parts. Please consult the component API docs for the complete list.

Let's style the previous and next buttons first. These can be targeted via the button part. Additionally, the previous and next buttons have previous and next parts, should you wish to style them differently.

<style>
  .grid {
    display: flex;
    gap: 1.5em;
    flex-wrap: wrap;
    justify-content: center;
  }

  calendar-range {
    svg {
      height: 16px;
      width: 16px;
      fill: none;
      stroke: currentColor;
      stroke-width: 1.5;
    }

    path {
      stroke-linecap: round;
      stroke-linejoin: round;
    }

    &::part(button) {
      border: 1px solid #adb5bd;
      background-color: #fff;
      border-radius: 3px;
      width: 26px;
      height: 26px;
    }

    &::part(button):focus-visible {
      outline: 2px solid #7048e8;
    }
  }

  calendar-month {
    --color-accent: #7048e8;
    --color-text-on-accent: #ffffff;
  }
</style>
<calendar-range
  value="2024-01-10/2024-01-20"
  min="2024-01-01"
  max="2024-12-31"
  locale="en-GB"
  months="2"
>
  <svg
    aria-label="Previous"
    slot="previous"
    xmlns="http://www.w3.org/2000/svg"
    viewBox="0 0 24 24"
  >
    <path d="M15.75 19.5 8.25 12l7.5-7.5"></path>
  </svg>
  <svg
    aria-label="Next"
    slot="next"
    xmlns="http://www.w3.org/2000/svg"
    viewBox="0 0 24 24"
  >
    <path d="m8.25 4.5 7.5 7.5-7.5 7.5"></path>
  </svg>
  <div class="grid">
    <calendar-month></calendar-month>
    <calendar-month offset="1"></calendar-month>
  </div>
</calendar-range>

Things are starting to look good. Notice that parts can be combined with pseudo-classes like :focus-visible.

Now let's add some final tweaks. Perhaps we want rounded corners for the start and end of the range.

<style>
  .grid {
    display: flex;
    gap: 1.5em;
    flex-wrap: wrap;
    justify-content: center;
  }

  calendar-range {
    svg {
      height: 16px;
      width: 16px;
      fill: none;
      stroke: currentColor;
      stroke-width: 1.5;
    }

    path {
      stroke-linecap: round;
      stroke-linejoin: round;
    }

    &::part(button) {
      border: 1px solid #adb5bd;
      background-color: #fff;
      border-radius: 3px;
      width: 26px;
      height: 26px;
    }

    &::part(button):focus-visible {
      outline: 2px solid #7048e8;
    }
  }

  calendar-month {
    --color-accent: #7048e8;
    --color-text-on-accent: #ffffff;

    &::part(button) {
      border-radius: 3px;
    }

    &::part(range-inner) {
      border-radius: 0;
    }

    &::part(range-start) {
      border-start-end-radius: 0;
      border-end-end-radius: 0;
    }

    &::part(range-end) {
      border-start-start-radius: 0;
      border-end-start-radius: 0;
    }

    &::part(range-start range-end) {
      border-radius: 3px;
    }
  }
</style>
<calendar-range
  value="2024-01-10/2024-01-20"
  min="2024-01-01"
  max="2024-12-31"
  locale="en-GB"
  months="2"
>
  <svg
    aria-label="Previous"
    slot="previous"
    xmlns="http://www.w3.org/2000/svg"
    viewBox="0 0 24 24"
  >
    <path d="M15.75 19.5 8.25 12l7.5-7.5"></path>
  </svg>
  <svg
    aria-label="Next"
    slot="next"
    xmlns="http://www.w3.org/2000/svg"
    viewBox="0 0 24 24"
  >
    <path d="m8.25 4.5 7.5 7.5-7.5 7.5"></path>
  </svg>
  <div class="grid">
    <calendar-month></calendar-month>
    <calendar-month offset="1"></calendar-month>
  </div>
</calendar-range>

If you're wondering about border-end-end-radius and other strange looking CSS properties, these are logical properties. Logical properties make your code tolerant to changes in writing direction, simplifying localization efforts

And finally, let's use a slightly different shade #845ef7 for the inner parts of the range:

<style>
  .grid {
    display: flex;
    gap: 1.5em;
    flex-wrap: wrap;
    justify-content: center;
  }

  calendar-range {
    svg {
      height: 16px;
      width: 16px;
      fill: none;
      stroke: currentColor;
      stroke-width: 1.5;
    }

    path {
      stroke-linecap: round;
      stroke-linejoin: round;
    }

    &::part(button) {
      border: 1px solid #adb5bd;
      background-color: #fff;
      border-radius: 3px;
      width: 26px;
      height: 26px;
    }

    &::part(button):focus-visible {
      outline: 2px solid #7048e8;
    }
  }

  calendar-month {
    --color-accent: #7048e8;
    --color-text-on-accent: #ffffff;

    &::part(button) {
      border-radius: 3px;
    }

    &::part(range-inner) {
      border-radius: 0;
      background-color: #845ef7;
    }

    &::part(range-start) {
      border-start-end-radius: 0;
      border-end-end-radius: 0;
    }

    &::part(range-end) {
      border-start-start-radius: 0;
      border-end-start-radius: 0;
    }

    &::part(range-start range-end) {
      border-radius: 3px;
    }
  }
</style>
<calendar-range
  value="2024-01-10/2024-01-20"
  min="2024-01-01"
  max="2024-12-31"
  locale="en-GB"
  months="2"
>
  <svg
    aria-label="Previous"
    slot="previous"
    xmlns="http://www.w3.org/2000/svg"
    viewBox="0 0 24 24"
  >
    <path d="M15.75 19.5 8.25 12l7.5-7.5"></path>
  </svg>
  <svg
    aria-label="Next"
    slot="next"
    xmlns="http://www.w3.org/2000/svg"
    viewBox="0 0 24 24"
  >
    <path d="m8.25 4.5 7.5 7.5-7.5 7.5"></path>
  </svg>
  <div class="grid">
    <calendar-month></calendar-month>
    <calendar-month offset="1"></calendar-month>
  </div>
</calendar-range>

Finally

We've taken the components from their default unattractive state to something that looks much nicer. We've used a combination of HTML, CSS custom properties, CSS parts, and SVGs to achieve this. You can take this as far as you wish, or perhaps with a little tweaking this is enough for your needs.

We haven't paid any mind to mobile styles throughout this guide. On mobile, perhaps you'd want to only show one month at a time. Or render an overlay rather than a popup. These decisions are beyond the scope of this guide, and are left as an exercise for the reader.

To find out more about each component, please see the respective component API docs. Or visit the usage guide for an in-depth look at how to use Cally with existing components.