Calendar Widget — Free HTML CSS JS Snippet

Calendar Widget · Dashboards · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Monthly grid: firstDay padding + daysInMonth + trailing padding — always 7 columns
today class: highlighted with accent background colour
has-event class: ::after dot indicator at bottom of day cell
selected class: click-to-select with accent tint, auto-deselects previous
Upcoming events panel: filtered to next 30 days, sorted ascending, limited to 4
Event items: coloured dot + truncated title + formatted short date
shift(dir): setMonth handles month boundary wrap automatically
Prev/next nav buttons with hover accent border effect

About this UI Snippet

Calendar Widget — Monthly Grid, Today Highlight, Event Dots & Upcoming Events Panel

Screenshot of the Calendar Widget snippet rendered live

A calendar widget is a fundamental dashboard component for displaying date-based context alongside other metrics and tasks. This snippet provides a complete mini monthly calendar: previous/next month navigation, a 7-column day grid with today highlighted, event dot indicators on days that have events, click-to-select days, and a dynamic upcoming events panel that filters events within the next 30 days — all in plain HTML, CSS, and vanilla JavaScript.

How the calendar grid is generated

The render() function computes three things: the day of the week the month starts on (firstDay), the number of days in the month, and the number of days in the previous month (for padding). The grid always starts on Sunday. Padding days from the previous month fill the start of the first row; padding days from the next month fill the end of the last row. Each day div is created with classList flags: .other (padding days), .today (today's date), .has-event (dot indicator), and .selected (click selection).

The event dot indicator

Days with matching events get the .has-event class which adds a ::after pseudo-element — a 4px circle positioned at the bottom of the day cell. The dot is accent coloured for normal days and white on the .today cell (since the today background is the accent colour). This is a standard calendar pattern used by Apple Calendar, Google Calendar, and iOS date pickers.

The upcoming events panel

Below the calendar grid, an upcoming events list filters the events array to entries within the next 30 days from today. Events are sorted by date ascending and limited to 4 items. Each event shows a coloured dot matching the event's colour, the event title (truncated with text-overflow: ellipsis), and the formatted date. If no upcoming events exist, a "No upcoming events" italic message shows instead.

Month navigation

The shift(dir) function calls cur.setMonth(cur.getMonth() + dir) — JavaScript's Date handles month boundary arithmetic automatically. Shifting from January by -1 gives December of the previous year. The calendar re-renders fully on each navigation.

Customising events

Replace the events array with your own event data. Each event needs date (YYYY-MM-DD format), title, and color. Wire to a calendar API (Google Calendar, CalDAV) by replacing the events array with data fetched from the API on render.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You don't have to work out the padding math by hand to see how this grid always comes out to complete weeks. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how firstDay, daysInMonth, and prevDays combine to produce leading and trailing padding days so the grid is always a multiple of seven cells, and why new Date(year, month, 0) correctly returns the last day of the previous month. The same assistant can help optimize it — asking whether recomputing the entire events filter and sort on every render call is wasteful compared to memoizing it per month, or whether string-concatenating the date in three separate padStart calls could be simplified. It's also useful for extending the widget: ask it to support multi-day events spanning several cells, add a week-view mode, or wire the events array to a real Google Calendar or CalDAV feed. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a "monthly calendar widget" in plain HTML, CSS, and JavaScript with an events list, using native Date arithmetic only — no date library.

Requirements:
- Render a 7-column grid for the current month that always fills complete rows: compute the weekday the first day of the month falls on to determine how many trailing days of the previous month to show as padding at the start, render every day of the current month, then compute how many leading days of the next month are needed to fill out the final row to a multiple of seven.
- Visually distinguish three day states with separate CSS classes: padding days from adjacent months (dimmed), today's actual date (filled with an accent background), and any day with at least one matching event (a small dot indicator) — and today with an event must correctly show both states simultaneously.
- Store events as a plain array of objects with a date string, a title, and a color, and match events to calendar days by exact date-string comparison.
- Clicking any day in the current month (but not today, and not padding days) must toggle a "selected" visual state on that cell, removing the selected state from any previously selected cell first.
- Below the grid, render an "upcoming events" list that filters the events array down to only dates between today and 30 days from now, sorts them chronologically, limits the list to at most 4 entries, and shows a colored dot, the event title (truncated with ellipsis if too long), and a short formatted date for each — with a distinct empty-state message when no events fall in that window.
- Provide previous/next navigation buttons that shift the displayed month by exactly one month using Date's setMonth (which must correctly roll over year boundaries) and fully regenerate the grid and events list on every navigation.

Step by step

How to Use

  1. 1
    Navigate monthsClick the ‹ and › buttons to move to the previous or next month. The grid regenerates, today highlight remains on the current date, and event dots update for each month.
  2. 2
    Click a day to select itClick any day in the current month to highlight it with a selected state (indigo tint). The previously selected day deselects. Today cannot be selected — it always shows the filled accent background.
  3. 3
    Update the events arrayIn the JS panel, edit the events array. Each event needs: date in YYYY-MM-DD format, title string, and color hex. Events show as dots on the calendar and appear in the upcoming panel if within the next 30 days.
  4. 4
    Change the week start dayThe calendar starts on Sunday (getDay() = 0). To start on Monday, subtract 1 from firstDay and adjust the day-names header order: Mo Tu We Th Fr Sa Su.
  5. 5
    Extend upcoming events rangeIn render(), change the cutoff from 30 days to any number: cutoff.setDate(cutoff.getDate() + N). Increase the .slice(0, 4) limit to show more upcoming events in the panel.
  6. 6
    Export in your formatClick "HTML" for a standalone file, "JSX" for a React component managing cur state with useState and events with useMemo, or "Tailwind" for a Tailwind CSS version.

Real-world uses

Common Use Cases

Dashboard date context and event overview widget
Show the current month alongside key events — sprint deadlines, team meetings, product launches — in a compact sidebar widget. The upcoming events panel gives date-aware context without opening a full calendar application.
Booking and appointment date picker for scheduling UIs
Wire the day click handler to a time slot picker panel, or use a single-field date picker for compact forms. When a day is clicked, fetch available slots from your API and display them below the calendar. The visual selected state confirms the chosen date before the user picks a time.
Content calendar and publishing schedule displays
Mark content publishing dates, social media post days, and email campaign sends as events — for a week-grid view, see the schedule timetable. Colour-code by content type (blog = indigo, social = pink, email = green). The dot indicators give a visual density map of publishing frequency.
Integration with Google Calendar or CalDAV APIs
Replace the static events array with a fetch call to the Google Calendar API or any CalDAV endpoint. Parse the events from the API response into the {date, title, color} format the widget expects. Re-fetch on each month navigation to load events for the new month.
Learn JavaScript Date arithmetic for calendar generation
The render function demonstrates how to use JavaScript Date to compute month grids: getDay() for the starting weekday, setDate(0) to get the last day of the previous month, and setMonth() for month navigation. These techniques apply to any date-driven UI.
Team availability and leave calendar overview widgets
Show team leave days, public holidays, and team events in a shared calendar widget. Each team member can have a distinct event colour. The upcoming list surfaces the next important dates across all event categories in one scannable list.

Got questions?

Frequently Asked Questions

render() creates a Date for the first day of cur month and reads .getDay() to know which column (0=Sunday, 6=Saturday) the month starts on. It fills leftward from that column with trailing days from the previous month. It then places all days in the current month. Finally it fills any remaining cells in the last row with leading days of the next month. This always produces a complete 7-column grid, typically 5 rows (35 cells) or occasionally 6 rows (42 cells) for months that start on Saturday or Sunday.

Call the Google Calendar Events API: fetch("https://www.googleapis.com/calendar/v3/calendars/primary/events?timeMin="+startOfMonth+"&timeMax="+endOfMonth+"&key="+API_KEY). Map the response items to {date: item.start.dateTime || item.start.date, title: item.summary, color: "#6366f1"}. Call render() after the fetch resolves. For authentication, use Google OAuth2 and pass the access token in the Authorization header instead of the API key.

Change the firstDay calculation: const firstDay = (new Date(year, month, 1).getDay() + 6) % 7. This converts Sunday (0) to 6 and Monday (1) to 0, shifting the week start. Also update the day-names header to Mo Tu We Th Fr Sa Su. The padding calculations for prev/next month days remain the same.

Click "JSX" to download. Manage cur with useState<Date>(new Date(year, month, 1)). Compute grid days with useMemo([cur]) — run the same padding and day calculations, returning an array of day objects. Render the array to day divs. Manage selectedDate with useState<string|null>(null). For events, fetch from your API in a useEffect that re-runs when cur changes.