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.