Static Calendar
Requirements
Please note: This calendar is not a script and has to be updated by hand.
For a dynamic version that stays up to date automatically, you can read nick's tutorial here.
This tutorial shows how to create a calendar with highlighted events to hover.
I'm going to use June 2025 as an example.
Tip: I like to use my phone to compare calendars, so I would know on which day it starts (i.e. 26 for the previous month). π
First, the HTML:
<table cellpadding="5" cellspacing="0" width="100%">
<caption>
<h1>JUNE 2025</h1>
</caption>
<thead>
<tr>
<th>Mo</th>
<th>Tu</th>
<th>We</th>
<th>Th</th>
<th>Fr</th>
<th>Sa</th>
<th>Su</th>
</tr>
</thead>
<tbody>
<!-- Current month days -->
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<div id="tooltip" class="dfoverlib"></div>
These 7 td's are for each day in the week.
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
Now you can edit the <td></td> differently:
class="grey" makes the number slightly transparent, as to show, that it's not part of the current month
<td class="grey">26</td>
data-day="number" is needed for the javascript to recognize todays date and highlight it
<td data-day="1">1</td>
data-tooltip="EVENT" contains the event that is shown in the hover (overlib)
<td class="tooltip-trigger" data-tooltip="1st day!" data-day="1">EMOJI/PIXEL</td>
Second, the CSS:
Grey'ish numbers for previous/next month's days, highlighting today's date, some styling:
.grey {
opacity:0.5;
}
.today {
background: #eee;
}
th {
color:rgba(0,0,0,.5);
background:rgba(0,0,0,.15);
padding:3px 0;
text-transform:uppercase;
text-align:center;
text-shadow:1px 1px 1px rgba(255,255,255,.5);
font-size:9.5px;
}
td, tr, th {
text-align: center;
font-size: 9pt;
}
tr:nth-child(odd) {
background:rgba(255,255,255,.3);
}
Overlib on hover for events:
.tooltip-trigger:hover {
background: linear-gradient(to right, transparent 0%, rgba(255, 255, 255, 1) 50%, transparent 100%);
cursor:default;
}
.dfoverlib {
position: absolute;
background: #333;
color: #fff;
padding: 6px 10px;
border-radius: 5px;
font-size: 14px;
pointer-events: none;
opacity: 0;
transition: opacity 0.1s;
white-space: nowrap;
z-index: 1000;
}
Third, the Javascript:
Paste this right before the </body>-Tag. It highlights the current day and enables the overlib on hover for events.
<script>
const tooltip = document.getElementById("tooltip");
const triggers = document.querySelectorAll(".tooltip-trigger");
triggers.forEach((trigger) => {
trigger.addEventListener("mousemove", (e) => {
tooltip.textContent = trigger.dataset.tooltip;
tooltip.style.left = e.pageX + 10 + "px";
tooltip.style.top = e.pageY + 10 + "px";
});
trigger.addEventListener("mouseenter", () => {
tooltip.style.opacity = "1";
});
trigger.addEventListener("mouseleave", () => {
tooltip.style.opacity = "0";
});
});
const today = new Date();
const currentDay = today.getDate();
const todayCell = document.querySelector(`td[data-day="${currentDay}"]`);
if (todayCell) {
todayCell.classList.add("today");
}
</script>
Fourth, the icons:
You can pixel your own a icons/download them somewhere else, or as I do it: Use Emojis. π There's almost everything as an Emoji and I am just too lazy to pixel them all for myself. Maybe one day, but not today! π
So the full example for the month June looks like this now:
In this example, we included days 26β31 from the previous month, all the current month's days, and days 1β6 from the next month to match a real calendar layout. As I mentioned earlier, I like to keep my phone with the calendar open nearby for reference while building the HTML. βΊοΈ
<style>
.grey {
opacity: 0.5;
}
.today {
background: #eee;
}
th {
color: rgba(0, 0, 0, 0.5);
background: rgba(0, 0, 0, 0.15);
padding: 3px 0;
text-transform: uppercase;
text-align: center;
text-shadow: 1px 1px 1px rgba(255, 255, 255, 0.5);
font-size: 9.5px;
}
td, tr, th {
text-align: center;
font-size: 9pt;
}
tr:nth-child(odd) {
background: rgba(255, 255, 255, 0.3);
}
.tooltip-trigger:hover {
background: linear-gradient(
to right,
transparent 0%,
rgba(255, 255, 255, 1) 50%,
transparent 100%
);
cursor: default;
}
.dfoverlib {
position: absolute;
background: #333;
color: #fff;
padding: 6px 10px;
border-radius: 5px;
font-size: 14px;
pointer-events: none;
opacity: 0;
transition: opacity 0.1s;
white-space: nowrap;
z-index: 1000;
}
</style>
<table cellpadding="5" cellspacing="0" width="100%">
<caption>
<h1>JUNE 2025</h1>
</caption>
<thead>
<tr>
<th>Mo</th>
<th>Tu</th>
<th>We</th>
<th>Th</th>
<th>Fr</th>
<th>Sa</th>
<th>Su</th>
</tr>
</thead>
<tbody>
<tr>
<!-- Previous month days (greyed out) -->
<td class="grey">26</td>
<td class="grey">27</td>
<td class="grey">28</td>
<td class="grey">29</td>
<td class="grey">30</td>
<td class="grey">31</td>
<!-- Current month days -->
<td class="tooltip-trigger" data-tooltip="dinner with friends" data-day="1">π</td>
</tr>
<tr>
<td data-day="2">2</td>
<td data-day="3">3</td>
<td data-day="4">4</td>
<td class="tooltip-trigger" data-tooltip="watching Final Destination 6" data-day="5">πΏ</td>
<td data-day="6">6</td>
<td data-day="7">7</td>
<td data-day="8">8</td>
</tr>
<tr>
<td data-day="9">9</td>
<td data-day="10">10</td>
<td data-day="11">11</td>
<td data-day="12">12</td>
<td data-day="13">13</td>
<td class="tooltip-trigger" data-tooltip="game night with friends">π²</td>
<td class="tooltip-trigger" data-tooltip="supporting my friends at a tennis match" data-day="14">πΎ</td>
</tr>
<tr>
<td data-day="16">16</td>
<td data-day="17">17</td>
<td data-day="18">18</td>
<td data-day="19">19</td>
<td class="tooltip-trigger" data-tooltip="HURRICANE FESTIVAL" data-day="20">π»</td>
<td class="tooltip-trigger" data-tooltip="HURRICANE FESTIVAL" data-day="21">π»</td>
<td class="tooltip-trigger" data-tooltip="HURRICANE FESTIVAL" data-day="22">π»</td>
</tr>
<tr>
<td data-day="23">23</td>
<td data-day="24">24</td>
<td data-day="25">25</td>
<td data-day="26">26</td>
<td data-day="27">27</td>
<td class="tooltip-trigger" data-tooltip="mum's +1 at a party" data-day="28">πͺ©</td>
<td data-day="29">29</td>
</tr>
<tr>
<td data-day="30">30</td>
<!-- Next month days (greyed out) -->
<td class="grey">1</td>
<td class="grey">2</td>
<td class="grey">3</td>
<td class="grey">4</td>
<td class="grey">5</td>
<td class="grey">6</td>
</tr>
</tbody>
</table>
<div id="tooltip" class="dfoverlib"></div>
<script>
const tooltip = document.getElementById("tooltip");
const triggers = document.querySelectorAll(".tooltip-trigger");
triggers.forEach((trigger) => {
trigger.addEventListener("mousemove", (e) => {
tooltip.textContent = trigger.dataset.tooltip;
tooltip.style.left = e.pageX + 10 + "px";
tooltip.style.top = e.pageY + 10 + "px";
});
trigger.addEventListener("mouseenter", () => {
tooltip.style.opacity = "1";
});
trigger.addEventListener("mouseleave", () => {
tooltip.style.opacity = "0";
});
});
const today = new Date();
const currentDay = today.getDate();
const todayCell = document.querySelector(`td[data-day="${currentDay}"]`);
if (todayCell) {
todayCell.classList.add("today");
}
</script>