If you have been running a Blogger (Blogspot) site for any length of time, you know the default Blog Archive widget can get clunky very quickly. Whether it’s an endlessly long bulleted list or a nested dropdown, traditional archives consume valuable sidebar space and are rarely engaging for readers.
What if, instead of an endless list, you could turn that archive into a sleek, interactive mini-calendar?
In this tutorial, you’ll learn how to transform your standard Blogger archive widget into a responsive, modern calendar archive. Days with published posts will automatically light up with an indicator and a link directly to that post.
I've been wanting to add a calendar widget to my blog ever since I first saw one on other blogs. I thought it was such a cool and visually appealing way to display archived posts.
Instead of adding a separate HTML/JavaScript widget, I decided to use the built-in archive widget. Since I haven't really made much use of my blog archive, I thought this would be a great way to give it a purpose while maintaining the personal feel of my blog rather than making it look too corporate.
I hope you like it! I also customized Blogger’s built-in Labels Widget. If you’d like to see how I did it, check out the tutorial!
Why Use This Calendar Archive?
- Smart Navigation: Clicking the next/previous arrows jumps straight to months with published posts, skipping inactive months automatically.
- Direct Post Linking: Days with published content are highlighted with an indicator icon (♥) and jump to posts from specific dates or daily archive.
- Zero External Dependencies: Built purely with lightweight HTML, CSS, and vanilla JavaScript—no heavy jQuery or external libraries required.
- Space-Saving: Replaces long, scrolling text lists with a clean, compact box that fits neatly into any sidebar.
You can view the demo on my homepage. Keep in mind that it may look slightly different, as I’ve customized mine to suit my personal preferences.
Step 1: Ensure the Archive Widget Is Added to Your Layout
- Navigate to Layout
- Look for your sidebar and ensure the default Blog Archive widget is added.
- If not, click Add a Gadget → select Blog Archive.
- Set the following options:
- Style: Hierarchy
- Show Post Titles: Checked (Yes)
- Archive Frequency: Daily

- Click Save
Why is this needed? This helps you specify where you want the Calendar widget to appear.
Step 2: Replace the Archive Code in Your Theme
Always back up your theme before editing your XML template. Go to Theme → click the arrow next to Customize → choose Backup.
- In the Theme section, click the down arrow next to Customize and select Edit HTML
- In the HTML editor, click the Jump to Widget icon to view the list of widgets and their corresponding IDs.

- Once you select BlogArchive1, or the Blog Archive widget you just created in your layout, select the entire
<b:widget ...> ... </b:widget>block (from opening tag to closing tag) and replace it completely with the code below:<b:widget id='BlogArchive1' locked='false' title='' type='BlogArchive' version='2' visible='true'> <b:widget-settings> <b:widget-setting name='showStyle'>HIERARCHY</b:widget-setting> <b:widget-setting name='yearPattern'>yyyy</b:widget-setting> <b:widget-setting name='showWeekEnd'>true</b:widget-setting> <b:widget-setting name='monthPattern'>MMMM yyyy</b:widget-setting> <b:widget-setting name='dayPattern'>MMM dd</b:widget-setting> <b:widget-setting name='weekPattern'>MM/dd</b:widget-setting> <b:widget-setting name='chronological'>false</b:widget-setting> <b:widget-setting name='showPosts'>true</b:widget-setting> <b:widget-setting name='frequency'>MONTHLY</b:widget-setting> </b:widget-settings> <b:includable id='main' var='this'> <b:with value='data:messages.archive' var='defaultTitle'> <b:include name='super.main'/> </b:with> </b:includable> <b:includable id='content'> <div class='widget-content'> <!-- CALENDAR WIDGET CONTAINER --> <div id='calendar-blog-widget'> <div class='cal-header'> <button aria-label='Previous month with posts' class='cal-btn' id='calPrevBtn' type='button'>❮</button> <span class='cal-month-year' id='calMonthYear'/> <button aria-label='Next month with posts' class='cal-btn' id='calNextBtn' type='button'>❯</button> </div> <div class='cal-weekdays'> <span>Su</span><span>Mo</span><span>Tu</span><span>We</span><span>Th</span><span>Fr</span><span>Sa</span> </div> <div class='cal-days-grid' id='calDaysGrid'/> </div> <!-- STYLES --> <style> /* - Blogger Archive Widget - Source code: https://writewhim.blogspot.com/2026/11/turn-blog-archive-into-calendar-style.html */ #calendar-blog-widget { font-family: inherit; max-width: 320px; margin: 0 auto 15px auto; background: #ffffff; border-radius: 14px; padding: 14px; box-shadow: 0 4px 15px rgba(0,0,0,0.06); user-select: none; } .cal-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; } .cal-month-year { font-weight: 700; font-size: 1.05rem; color: #2b2b2b; } .cal-btn { background: #f7f7f9; border: 1px solid #e1e4e8; color: #444; font-size: 0.85rem; width: 30px; height: 30px; border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.2s ease; padding: 0; outline: none; } .cal-btn:hover:not(:disabled) { background-color: #ff4d6d; border-color: #ff4d6d; color: #fff; } .cal-btn:disabled { opacity: 0.3; cursor: not-allowed; pointer-events: none; } .cal-weekdays { display: grid; grid-template-columns: repeat(7, 1fr); text-align: center; font-weight: 600; font-size: 0.75rem; color: #8c8c8c; margin-bottom: 6px; } .cal-days-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; } .cal-day-cell { aspect-ratio: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; font-size: 0.82rem; border-radius: 8px; position: relative; color: #555; text-decoration: none; transition: background 0.15s ease; } /* Highlight Today */ .cal-day-cell.today { border: 2px solid #ff4d6d; font-weight: 700; color: #ff4d6d; } /* Days with posts */ .cal-day-cell.has-post { background-color: #fff0f3; cursor: pointer; font-weight: 600; } .cal-day-cell.has-post:hover { background-color: #ffe0e6; } /* Heart indicator */ .cal-heart { font-size: 0.68rem; color: #e63946; line-height: 1; margin-top: 1px; animation: pop 0.25s ease-in-out; } .cal-day-cell.empty { visibility: hidden; } #ArchiveList { display: none; margin-top: 12px; font-size: 0.88rem; } @keyframes pop { 0% { transform: scale(0); } 100% { transform: scale(1); } } </style> <!-- ORIGINAL ARCHIVE LIST (Preserved for data retrieval) --> <div id='ArchiveList'> <div expr:id='data:widget.instanceId + "_ArchiveList"'> <b:include cond='data:this.style == "HIERARCHY"' name='hierarchy'/> <b:include cond='data:this.style in {"FLAT", "MENU"}' name='flat'/> </div> </div> <!-- INTERACTIVE CALENDAR LOGIC --> <script> //<![CDATA[ document.addEventListener("DOMContentLoaded", function () { const monthNames = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ]; const today = new Date(); const todayYear = today.getFullYear(); const todayMonth = today.getMonth(); const todayDate = today.getDate(); const postsByMonth = {}; const archiveContainer = document.getElementById("ArchiveList"); if (archiveContainer) { const links = archiveContainer.querySelectorAll("a"); links.forEach(link => { const href = link.getAttribute("href") || ""; const dailyArchiveMatch = href.match(/\/(\d{4})_(\d{2})_(\d{2})_archive\.html/); const postMatch = href.match(/\/(\d{4})\/(\d{2})\//); let y = null, m = null, d = null; if (dailyArchiveMatch) { y = parseInt(dailyArchiveMatch[1], 10); m = parseInt(dailyArchiveMatch[2], 10) - 1; d = parseInt(dailyArchiveMatch[3], 10); } else if (postMatch) { y = parseInt(postMatch[1], 10); m = parseInt(postMatch[2], 10) - 1; const text = (link.innerText || "").trim(); const dayMatch = text.match(/\b([1-9]|[12]\d|3[01])\b/); if (dayMatch) { d = parseInt(dayMatch[1], 10); } } if (y !== null && m !== null && d !== null) { const monthKey = `${y}-${String(m).padStart(2, "0")}`; if (!postsByMonth[monthKey]) { postsByMonth[monthKey] = { year: y, month: m, days: {} }; } if (!postsByMonth[monthKey].days[d]) { postsByMonth[monthKey].days[d] = href; } } }); } const availableMonths = Object.keys(postsByMonth).sort(); if (availableMonths.length === 0) { const defaultKey = `${todayYear}-${String(todayMonth).padStart(2, "0")}`; postsByMonth[defaultKey] = { year: todayYear, month: todayMonth, days: {} }; availableMonths.push(defaultKey); } const currentMonthKey = `${todayYear}-${String(todayMonth).padStart(2, "0")}`; let activeIndex = availableMonths.indexOf(currentMonthKey); if (activeIndex === -1) { activeIndex = availableMonths.length - 1; } const monthYearEl = document.getElementById("calMonthYear"); const grid = document.getElementById("calDaysGrid"); const prevBtn = document.getElementById("calPrevBtn"); const nextBtn = document.getElementById("calNextBtn"); function renderCalendar(index) { const key = availableMonths[index]; const data = postsByMonth[key]; const year = data.year; const month = data.month; monthYearEl.textContent = `${monthNames[month]} ${year}`; prevBtn.disabled = (index <= 0); nextBtn.disabled = (index >= availableMonths.length - 1); grid.innerHTML = ""; const firstDayIndex = new Date(year, month, 1).getDay(); const totalDays = new Date(year, month + 1, 0).getDate(); for (let i = 0; i < firstDayIndex; i++) { const emptyCell = document.createElement("div"); emptyCell.className = "cal-day-cell empty"; grid.appendChild(emptyCell); } for (let day = 1; day <= totalDays; day++) { const hasPost = data.days[day] !== undefined; const isToday = (year === todayYear && month === todayMonth && day === todayDate); const cell = document.createElement(hasPost ? "a" : "div"); cell.className = "cal-day-cell"; if (isToday) cell.classList.add("today"); if (hasPost) { cell.classList.add("has-post"); cell.setAttribute("href", data.days[day]); cell.setAttribute("title", `View post from ${monthNames[month]} ${day}, ${year}`); } const numSpan = document.createElement("span"); numSpan.textContent = day; cell.appendChild(numSpan); if (hasPost) { const heart = document.createElement("span"); heart.className = "cal-heart"; heart.innerHTML = "♥"; cell.appendChild(heart); } grid.appendChild(cell); } } prevBtn.addEventListener("click", function () { if (activeIndex > 0) { activeIndex--; renderCalendar(activeIndex); } }); nextBtn.addEventListener("click", function () { if (activeIndex < availableMonths.length - 1) { activeIndex++; renderCalendar(activeIndex); } }); renderCalendar(activeIndex); }); //]]> </script> </div> </b:includable> <b:includable id='flat'> <b:include data='{ buttonClass: "flat-button", items: data:this.data, itemSet: "data", itemsMarkup: "super.flat" }' name='extendableItems'/> </b:includable> <b:includable id='hierarchy'> <b:include data='{ buttonClass: "flat-button", limit: 1, items: data:this.data, itemSet: "data", itemsMarkup: "super.hierarchy" }' name='extendableItems'/> </b:includable> <b:includable id='interval' var='intervals'> <ul class='hierarchy'> <b:loop values='data:intervals' var='interval'> <li class='archivedate'> <div class='hierarchy-title'> <a class='post-count-link' expr:href='data:interval.url'> <data:interval.name/> <span class='post-count'><data:interval.post-count/></span> </a> </div> <div class='hierarchy-content'> <b:include cond='data:interval.data' data='interval.data' name='interval'/> <b:include cond='data:interval.posts' data='interval.posts' name='posts'/> </div> </li> </b:loop> </ul> </b:includable> <b:includable id='posts' var='posts'> <ul class='posts hierarchy'> <b:loop values='data:posts' var='post'> <li> <a expr:href='data:post.url'><data:post.title/></a> </li> </b:loop> </ul> </b:includable> </b:widget> - Click Save
- Visit your blog and check your sidebar to see your new calendar archive in action!
Step 3: Quick Customization Tips
Want to match the calendar to your blog's specific branding? Look inside the <style> block in the code you just pasted:
- Change the Accent Color: Locate
#ff4d6d(the pink accent) and#e63946(the heart icon color) and replace them with your blog's primary theme hex codes. - Swap the Heart Icon: If you prefer something other than a heart, locate:
and change it to a simple bullet point (heart.innerHTML = "♥";●), star (★), or any custom character:heart.innerHTML = "●";
Since someone previously mentioned that they have strict rules about using only two colors in their theme, I wanted to clarify that you can still customize them to match your theme. If you need to make any adjustments, you can either change its value to inherit or remove the color property altogether.
Final Thoughts
Adding this easy-to-navigate calendar widget to your blog gives it a more polished, customized look without requiring external libraries or bulky plugins.
Lightweight and fast, this calendar offers readers an intuitive way to explore your older content. Even better, the script automatically skips months without published posts, so visitors won't encounter empty calendars or frustrating dead ends.
If you run into any problems or errors, or if you'd like to customize the calendar further but aren't sure where to start, feel free to let me know. I'd be happy to help!