{"id":307966,"date":"2026-05-15T21:02:09","date_gmt":"2026-05-15T21:02:09","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/sdaweb-calendar-sync-for-google-calendar\/"},"modified":"2026-05-24T21:49:49","modified_gmt":"2026-05-24T21:49:49","slug":"sdaweb-gcal-sync","status":"publish","type":"plugin","link":"https:\/\/sr.wordpress.org\/plugins\/sdaweb-gcal-sync\/","author":23453481,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"0.13.0","stable_tag":"0.13.0","tested":"7.0","requires":"6.5","requires_php":"7.4","requires_plugins":null,"header_name":"SDAweb Calendar Sync for Google Calendar","header_author":"SDAweb - Rune Stake Stavdal, Vinjar Romsvik, Christer Andvik","header_description":"Display events from one or more Google Calendars on your WordPress site. Six views, hover popovers, ICS subscribe, mobile-aware, theme-aware, accessible by default, no third-party relays.","assets_banners_color":"","last_updated":"2026-05-24 21:49:49","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"","header_author_uri":"https:\/\/sdaweb.no","rating":0,"author_block_rating":0,"active_installs":40,"downloads":564,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"0.10.0":{"tag":"0.10.0","author":"rstake","date":"2026-05-24 14:44:22"},"0.10.1":{"tag":"0.10.1","author":"rstake","date":"2026-05-24 15:02:33"},"0.11.0":{"tag":"0.11.0","author":"rstake","date":"2026-05-24 16:32:30"},"0.11.1":{"tag":"0.11.1","author":"rstake","date":"2026-05-24 16:41:10"},"0.12.0":{"tag":"0.12.0","author":"rstake","date":"2026-05-24 17:04:08"},"0.12.1":{"tag":"0.12.1","author":"rstake","date":"2026-05-24 17:45:00"},"0.12.2":{"tag":"0.12.2","author":"rstake","date":"2026-05-24 17:59:57"},"0.12.8":{"tag":"0.12.8","author":"rstake","date":"2026-05-24 19:20:36"},"0.12.9":{"tag":"0.12.9","author":"rstake","date":"2026-05-24 20:41:27"},"0.13.0":{"tag":"0.13.0","author":"rstake","date":"2026-05-24 21:49:49"},"0.8.5":{"tag":"0.8.5","author":"rstake","date":"2026-05-15 21:07:45"},"0.8.6":{"tag":"0.8.6","author":"rstake","date":"2026-05-15 21:25:16"},"0.9.0":{"tag":"0.9.0","author":"rstake","date":"2026-05-19 19:33:45"},"0.9.1":{"tag":"0.9.1","author":"rstake","date":"2026-05-19 20:17:49"},"0.9.2":{"tag":"0.9.2","author":"rstake","date":"2026-05-20 13:10:50"},"0.9.3":{"tag":"0.9.3","author":"rstake","date":"2026-05-24 12:45:14"},"0.9.4":{"tag":"0.9.4","author":"rstake","date":"2026-05-24 12:57:53"},"0.9.5":{"tag":"0.9.5","author":"rstake","date":"2026-05-24 13:23:53"}},"upgrade_notice":{"0.8.6":"<p>Two housekeeping fixes: service-account token-exchange success log gated on WP_DEBUG (was firing every ~55 min unconditionally); readme upgrade notice for 0.8.3 trimmed to meet Plugin Check character limit.<\/p>","0.8.5":"<p>Adds a per-row Delete button to Tools \u2192 Backups so individual snapshots can be removed without waiting for the retention cap to evict them.<\/p>","0.8.4":"<p>Companion fix to 0.8.3 \u2014 preserves the Auto-resolved chip text color on hover. Without this fix, hovering a Solid chip in Month view reset the title to the link\/accent color, often dropping below the WCAG contrast Auto mode just protected. No action required after upgrade.<\/p>","0.8.3":"<p>Fixes Month-view Solid chip readability for mid-luminance feed colors (greens, yellows, mints): chip text now auto-picks dark text when white fails WCAG AA. Adds a per-display Solid chip text color dropdown. No action required after upgrade.<\/p>","0.8.2":"<p>Adds three new title-appearance controls (size, weight, uppercase) to the Upcoming view so its title can match surrounding sidebar\/footer headings without site CSS overrides. Existing displays render unchanged.<\/p>","0.8.1":"<p>Fixes URL parameters bleeding across calendar instances on the same page (switching the main calendar to Month would also flip a footer widget into Month \u2014 and look awful in the narrow footer column). Recommended for everyone with multiple calendars on the same page.<\/p>","0.8.0":"<p>Month-view &quot;+N more&quot; is now interactive \u2014 a real a11y improvement (was unreachable by keyboard and screen readers). New &quot;Day overflow&quot; setting picks between an in-place popover (default) or navigating to the Day view for that date.<\/p>","0.7.4":"<p>Cards view now correctly indicates multi-day events: same-month events show a &quot;4\u20138&quot; date range; cross-month events stack the start and end dates with an arrow. With <code>card_grouping = day<\/code>, multi-day events also appear under every day they cover (matching Week and Mini view behaviour).<\/p>","0.7.3":"<p>Hotfix for 0.7.2: moves <code>CHANGELOG.md<\/code> from the plugin root into <code>docs\/<\/code> to avoid Plugin Check&#039;s <code>unexpected_markdown_file<\/code> warning. No code changes.<\/p>","0.7.1":"<p>Admin polish: Edit Display selects no longer clip behind the Live preview when the option text is long.<\/p>","0.7.0":"<p>Adds a &quot;Card grouping&quot; option (off \/ by day \/ by week \/ by month) to the Cards view. Existing displays render identically until you opt in.<\/p>","0.6.10":"<p>Fixes per-display label overrides being silently dropped by the shortcode pipeline (same root cause as 0.6.9). Adds Event color override to the Gutenberg block UI. Fixes the admin Live Preview falling back to List when a display&#039;s saved view is Upcoming. Recommended for everyone.<\/p>"},"ratings":[],"assets_icons":{"icon.svg":{"filename":"icon.svg","revision":3533428,"resolution":false,"location":"assets","locale":false}},"assets_banners":[],"assets_blueprints":{},"all_blocks":{"sdaweb-gcal\/calendar":{"$schema":"https:\/\/schemas.wp.org\/trunk\/block.json","apiVersion":3,"name":"sdaweb-gcal\/calendar","version":"0.13.0","title":"SDAweb Calendar","category":"widgets","description":"Display events from one or more Google Calendars. Choose a saved display or configure inline.","icon":"calendar-alt","keywords":["calendar","google","events","schedule"],"supports":{"html":false,"align":["wide","full"],"spacing":{"margin":true,"padding":true}},"attributes":{"displayId":{"type":"string","default":""},"calendars":{"type":"string","default":""},"view":{"type":"string","default":"list"},"days":{"type":"number","default":30},"daysBack":{"type":"number","default":0},"anchorDate":{"type":"string","default":""},"startDate":{"type":"string","default":""},"endDate":{"type":"string","default":""},"dateFormat":{"type":"string","default":""},"timeFormat":{"type":"string","default":""},"rangeMode":{"type":"string","default":"rolling"},"rangePreset":{"type":"string","default":""},"max":{"type":"number","default":50},"group":{"type":"string","default":"day"},"showEnd":{"type":"boolean","default":false},"showEventDate":{"type":"boolean","default":false},"showCalendar":{"type":"boolean","default":false},"calendarLabelMode":{"type":"string","default":""},"showFeedLegend":{"type":"boolean","default":false},"labels":{"type":"object","default":{}},"showLocation":{"type":"boolean","default":false},"showDescription":{"type":"boolean","default":false},"descriptionWords":{"type":"number","default":30},"hideAllDay":{"type":"boolean","default":false},"hideCancelled":{"type":"boolean","default":true},"hidePast":{"type":"boolean","default":true},"search":{"type":"string","default":""},"timezone":{"type":"string","default":""},"primaryColor":{"type":"string","default":""},"accentColor":{"type":"string","default":""},"darkMode":{"type":"string","default":"auto"},"linkTarget":{"type":"string","default":"same"},"viewToggle":{"type":"boolean","default":false},"viewToggleViews":{"type":"string","default":"list,month,card"},"viewTogglePosition":{"type":"string","default":"top-right"},"timeFormatMode":{"type":"string","default":"site"},"firstDayOfWeek":{"type":"string","default":"site"},"showWeekNumbers":{"type":"boolean","default":false},"recurrenceIndicator":{"type":"string","default":"icon"},"icsSubscribe":{"type":"string","default":"none"},"listWeekHeading":{"type":"string","default":"number"},"listHeadingStyle":{"type":"string","default":"relative"},"mobileDegrade":{"type":"string","default":"auto"},"miniDefaultPanel":{"type":"string","default":"today_upcoming"},"miniUpcomingMax":{"type":"number","default":30},"miniFullUrl":{"type":"string","default":""},"miniFullLabel":{"type":"string","default":""},"todayColor":{"type":"string","default":""},"linkColor":{"type":"string","default":""},"eventColorOverride":{"type":"string","default":""},"eventHover":{"type":"string","default":"rich"},"todayStyle":{"type":"string","default":"column"},"timePlacement":{"type":"string","default":"beside_date"},"chipStyle":{"type":"string","default":"linear"},"listHeadingPosition":{"type":"string","default":"inline"},"cardGrouping":{"type":"string","default":"none"},"monthOverflowAction":{"type":"string","default":"popover"},"showSearch":{"type":"boolean","default":false},"showDatePicker":{"type":"boolean","default":false},"locale":{"type":"string","default":""},"maxEvents":{"type":"number","default":5},"showTitle":{"type":"boolean","default":false},"titleText":{"type":"string","default":""},"showLinkToFullCalendar":{"type":"boolean","default":false},"linkUrl":{"type":"string","default":""},"showMeta":{"type":"boolean","default":true}},"textdomain":"sdaweb-gcal-sync","editorScript":"file:.\/index.js","editorStyle":"file:.\/editor.css","render":"file:.\/render.php"}},"tagged_versions":["0.10.0","0.10.1","0.11.0","0.11.1","0.12.0","0.12.1","0.12.2","0.12.8","0.12.9","0.13.0","0.8.5","0.8.6","0.9.0","0.9.1","0.9.2","0.9.3","0.9.4","0.9.5"],"block_files":[],"assets_screenshots":[],"screenshots":{"1":"All six views \u2014 List, Month grid, Card grid, Week, Day, Mini-month \u2014 sharing one render pipeline.","2":"Month grid with today highlighted, multi-day event ribbons spanning across cells, ISO week numbers, and the hover popover shown on a chip.","3":"Mini-month view with dot-density grid and below-grid panel showing today + upcoming events with a \"Load more\" button.","4":"ICS subscribe dropdown \u2014 one-click hand-offs to Google, Apple\/Outlook, Android, and copy-link.","5":"Block editor experience \u2014 SDAweb Calendar block with full Inspector controls and live preview.","6":"Calendars admin tab \u2014 list of registered calendars with health status, last refresh, and actions.","7":"Add calendar form, Service account flow with JSON paste field and explanatory help.","8":"Test connection result showing the next three real event titles directly in the form.","9":"Displays editor with live preview pane and theme-presets selector.","10":"Help tab showing the in-app service-account setup walkthrough."}},"plugin_section":[],"plugin_tags":[5595,416,1486,4743,4062],"plugin_category":[40],"plugin_contributors":[257114,256141],"plugin_business_model":[],"class_list":["post-307966","plugin","type-plugin","status-publish","hentry","plugin_tags-agenda","plugin_tags-calendar","plugin_tags-events","plugin_tags-google-calendar","plugin_tags-schedule","plugin_category-calendar-and-events","plugin_contributors-rstake","plugin_contributors-sdaweb","plugin_committers-rstake"],"banners":[],"icons":{"svg":"https:\/\/ps.w.org\/sdaweb-gcal-sync\/assets\/icon.svg?rev=3533428","icon":"https:\/\/ps.w.org\/sdaweb-gcal-sync\/assets\/icon.svg?rev=3533428","icon_2x":false,"generated":false},"screenshots":[],"raw_content":"<!--section=description-->\n<p>SDAweb Calendar Sync pulls events directly from the Google Calendar API and renders them on your site through a Gutenberg block, a shortcode, or a classic widget. All three share one render pipeline so output is identical regardless of how you insert a calendar.<\/p>\n\n<h4>Six views, one render pipeline<\/h4>\n\n<ul>\n<li><strong>List \/ Agenda<\/strong> \u2014 chronological, optionally grouped by day, week, or month<\/li>\n<li><strong>Month grid<\/strong> \u2014 classic 7\u00d76 calendar with multi-day event ribbons spanning across cells, today highlight (cell or whole-column style), optional ISO 8601 week-number column, and per-feed pastel chips<\/li>\n<li><strong>Card grid<\/strong> \u2014 upcoming events as styled cards, responsive<\/li>\n<li><strong>Week<\/strong> \u2014 7 day columns, today highlighted<\/li>\n<li><strong>Day<\/strong> \u2014 single-day agenda<\/li>\n<li><strong>Mini-month<\/strong> \u2014 compact dot-density grid for sidebars and widgets, with a tap-to-expand event panel showing today + next upcoming events and a \"Load more\" button<\/li>\n<\/ul>\n\n<p>All views share the same data layer, the same CSS-variable system, and the same accessibility baseline. Switch between them with one setting, or expose a visitor-facing view-toggle pill so visitors can switch themselves.<\/p>\n\n<h4>Smart UX out of the box<\/h4>\n\n<ul>\n<li><strong>Hover popover on event chips<\/strong> (Month, Week, Mini) \u2014 a floating card with date, time, location, calendar, recurrence summary, and a click-through link. Desktop hover + keyboard focus only; touch users keep direct click-through.<\/li>\n<li><strong>Mobile auto-degrade Month \u2192 Mini<\/strong> \u2014 the full grid is hidden under ~600px and replaced with the compact Mini-month view; one wrapper, two layouts, no JS swap.<\/li>\n<li><strong>ICS subscribe dropdown<\/strong> \u2014 one-click hand-offs to Google Calendar (web), Apple Calendar \/ Outlook (webcal), Android Google Calendar app (intent), and Copy-link with toast confirmation. Mobile becomes a bottom sheet.<\/li>\n<li><strong>Live search<\/strong> + <strong>jump-to-date picker<\/strong> \u2014 optional chrome controls for visitors who want to filter or navigate quickly.<\/li>\n<li><strong>Multi-day event ribbons<\/strong> \u2014 week-spanning bars with arrow indicators when an event continues beyond the visible row.<\/li>\n<li><strong>Recurring event indicator<\/strong> \u2014 small \u21bb icon with cadence summary in the tooltip (\"Repeats weekly until 31 December 2026\").<\/li>\n<li><strong>Per-display locale override<\/strong> \u2014 render the calendar in a specific language (e.g. nb_NO) even when the site language is English.<\/li>\n<\/ul>\n\n<h4>Two authentication paths<\/h4>\n\n<ul>\n<li><strong>API key<\/strong> \u2014 for calendars marked \"Make available to public\" in Google Calendar. One field, paste, done.<\/li>\n<li><strong>Service Account JSON<\/strong> \u2014 for <strong>private<\/strong> calendars without per-user OAuth. Upload the JSON, share the calendar with the service-account email, you're set.<\/li>\n<\/ul>\n\n<p>Credentials are encrypted at rest using a key derived from your site's authentication salt. They are never echoed back into the admin UI \u2014 only the masked value and (for service accounts) the public service-account email are shown.<\/p>\n\n<h4>Theme-aware out of the box<\/h4>\n\n<p>The plugin reads your active theme's <code>theme.json<\/code> color palette and uses your <code>primary<\/code>, <code>accent<\/code>, <code>foreground<\/code>, and <code>background<\/code> colors automatically. Per-display overrides let you set custom primary, accent, today-highlight, and link colors and force light\/dark mode without touching code. Every design token is exposed as a CSS custom property so a developer can fully restyle the calendar from their theme stylesheet.<\/p>\n\n<h4>Five built-in theme presets<\/h4>\n\n<p>One-click coordinated colour bundles: <strong>Default<\/strong> (clean blue), <strong>Warm earth<\/strong> (cream + deep red), <strong>High contrast<\/strong> (solid black on white for AAA-stricter sites), <strong>Forest<\/strong> (deep green), <strong>Sunset<\/strong> (warm coral + amber).<\/p>\n\n<h4>Accessibility built in<\/h4>\n\n<p>WCAG 2.2 AA baseline with several AAA touches:<\/p>\n\n<ul>\n<li>Live contrast warnings on every colour picker \u2014 the admin UI shows the WCAG ratio against both light and dark surfaces with pass\/fail markers as you choose colours<\/li>\n<li>Automatic high-contrast overlay via <code>prefers-contrast: more<\/code><\/li>\n<li>Focus-visible halo: a 4px white ring behind the primary outline so focus stays visible against any chip background<\/li>\n<li><code>prefers-reduced-motion<\/code> honored everywhere<\/li>\n<li>ARIA roles and labels on the month grid, navigation, popovers, and view-toggle<\/li>\n<li>Semantic HTML throughout<\/li>\n<li>RTL-aware via CSS logical properties<\/li>\n<li>Tabular numerals for day numbers and time labels so single- and double-digit values don't drift<\/li>\n<\/ul>\n\n<h4>Built for the WordPress.org standard<\/h4>\n\n<ul>\n<li>Block editor first-class \u2014 server-side rendered, ServerSideRender preview, all the standard <code>InspectorControls<\/code><\/li>\n<li>Shortcode and classic widget on the same render pipeline<\/li>\n<li>No bundled core libraries (no jQuery on front-end, no Guzzle, no Carbon, no Monolog)<\/li>\n<li>No third-party authentication relay \u2014 your credentials only ever talk directly to googleapis.com<\/li>\n<li>Translation ready via translate.wordpress.org once the plugin is published and indexed (no bundled <code>.po<\/code>\/<code>.mo<\/code> \u2014 WordPress auto-loads locale files into <code>wp-content\/languages\/plugins\/<\/code> per the WP Plugin Handbook), plurals via <code>_n()<\/code>, JS strings via <code>wp_localize_script<\/code><\/li>\n<li>Free and GPLv2<\/li>\n<\/ul>\n\n<h4>For developers<\/h4>\n\n<p>Extension hooks are documented in <code>docs\/hooks.md<\/code> inside the plugin folder. The first-release set includes filters for event data, event URLs, query args, cache TTL, render output, palette resolution, plus actions for refresh and uninstall lifecycle.<\/p>\n\n<h3>Third-Party Services<\/h3>\n\n<p>This plugin connects to the Google Calendar API to retrieve events from calendars you configure.<\/p>\n\n<ul>\n<li>Service: Google Calendar API v3<\/li>\n<li>Website: https:\/\/developers.google.com\/calendar<\/li>\n<li>Terms of Service: https:\/\/developers.google.com\/terms<\/li>\n<li>Privacy Policy: https:\/\/policies.google.com\/privacy<\/li>\n<\/ul>\n\n<p>Data sent: the calendar ID(s) you configure, plus either your API key or a JSON Web Token signed with your service account credentials. Event data is returned to your server and cached locally as WordPress transients. No event data is sent to any third party.<\/p>\n\n<p>The ICS subscribe feature, when enabled, links visitors directly to Google's public iCal feed (<code>calendar.google.com\/calendar\/ical\/...\/public\/basic.ics<\/code>) \u2014 the plugin does not proxy or store that data.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the plugin to your <code>\/wp-content\/plugins\/<\/code> directory, or install it from the Plugins screen in WordPress.<\/li>\n<li>Activate the plugin.<\/li>\n<li>Go to <strong>Settings \u2192 SDAweb Calendar Sync<\/strong> to add your first calendar.<\/li>\n<li>Choose API key (public calendars) or Service Account (private calendars), follow the in-app setup guide, save.<\/li>\n<li>Create a display, choose a view, and copy its shortcode \u2014 or insert the <strong>SDAweb Calendar<\/strong> block in a page.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"do%20i%20need%20a%20google%20account%20to%20use%20this%20plugin%3F\"><h3>Do I need a Google account to use this plugin?<\/h3><\/dt>\n<dd><p>You need a Google Cloud project to generate either an API key (for public calendars) or a service account (for private calendars). The plugin's Help tab walks you through both setups in 7\u20138 steps each. Setup is one-time per site.<\/p><\/dd>\n<dt id=\"does%20this%20plugin%20send%20my%20data%20anywhere%20other%20than%20google%3F\"><h3>Does this plugin send my data anywhere other than Google?<\/h3><\/dt>\n<dd><p>No. Calendar data is fetched directly from <code>googleapis.com<\/code> using the Google Calendar API. The plugin does not contact any SDAweb-controlled servers, analytics endpoints, or third-party relays. There is no telemetry.<\/p><\/dd>\n<dt id=\"can%20i%20display%20a%20private%20%28non-public%29%20calendar%3F\"><h3>Can I display a private (non-public) calendar?<\/h3><\/dt>\n<dd><p>Yes. Use the Service Account authentication option. The plugin shows you the service account email; share your private calendar with that email in Google Calendar's sharing settings, and the plugin can read it.<\/p><\/dd>\n<dt id=\"how%20often%20does%20the%20plugin%20refresh%20events%3F\"><h3>How often does the plugin refresh events?<\/h3><\/dt>\n<dd><p>A WP-Cron job refreshes registered calendars in the background every 15 minutes. Cache lifetime is configurable upward in the plugin settings (15 minutes is the enforced minimum). You can also click \"Refresh now\" on any calendar in the admin to fetch immediately.<\/p><\/dd>\n<dt id=\"does%20it%20work%20with%20caching%20plugins%3F\"><h3>Does it work with caching plugins?<\/h3><\/dt>\n<dd><p>Yes \u2014 the calendar HTML is part of the page output, so any page-cache plugin caches it like any other content. The cache will refresh on its own schedule. If you need immediate refresh after a calendar change, purge the page cache.<\/p><\/dd>\n<dt id=\"can%20i%20add%20multiple%20calendars%20to%20one%20display%3F\"><h3>Can I add multiple calendars to one display?<\/h3><\/dt>\n<dd><p>Yes. Each display picks one or more registered calendars and merges them. Events are color-coded by calendar. A multi-feed legend strip can be enabled above the events; chips can be styled solid (single-feed) or pastel (multi-feed legibility).<\/p><\/dd>\n<dt id=\"will%20it%20work%20with%20my%20theme%3F\"><h3>Will it work with my theme?<\/h3><\/dt>\n<dd><p>The plugin reads your active theme's <code>theme.json<\/code> color palette automatically, so calendar colors match the site by default with zero configuration. If you use a classic theme without <code>theme.json<\/code> (or want to override), every color is exposed as a CSS variable that you can override from your theme stylesheet. Five built-in theme presets give you coordinated palettes with one click.<\/p><\/dd>\n<dt id=\"is%20the%20plugin%20accessible%3F\"><h3>Is the plugin accessible?<\/h3><\/dt>\n<dd><p>Yes \u2014 built-in. WCAG 2.2 AA baseline includes <code>prefers-reduced-motion<\/code> and <code>prefers-contrast: more<\/code> support, <code>:focus-visible<\/code> outlines with a white halo (visible on any background), ARIA labelling on the month grid, navigation, popovers, and view-toggle, semantic HTML throughout, RTL-aware via CSS logical properties, and live contrast warnings in the colour-picker UI as you choose values.<\/p><\/dd>\n<dt id=\"can%20i%20show%20the%20calendar%20in%20a%20different%20language%20than%20the%20rest%20of%20the%20site%3F\"><h3>Can I show the calendar in a different language than the rest of the site?<\/h3><\/dt>\n<dd><p>Yes. Each display has an optional Locale override field \u2014 set it to <code>nb_NO<\/code>, <code>sv_SE<\/code>, etc. and that calendar renders weekday names, month names, and built-in labels in that language regardless of the site's language. Useful when an English site hosts Norwegian-audience content.<\/p><\/dd>\n<dt id=\"where%20are%20the%20extension%20hooks%20documented%3F\"><h3>Where are the extension hooks documented?<\/h3><\/dt>\n<dd><p>In <code>docs\/hooks.md<\/code> inside the plugin folder. The plugin commits to keeping documented hooks stable within a major version.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<p>The most recent releases are listed here. The complete history is in\n    docs\/CHANGELOG.md bundled with the plugin.<\/p>\n\n<h4>0.13.0<\/h4>\n\n<ul>\n<li>Admin form reorganised \u2014 the Display edit screen now follows a clearer 7-section flow (Identity \/ View &amp; layout \/ Date window \/ Appearance \/ Visitor experience \/ Outbound links \/ Localisation). All option keys preserved; existing displays load unchanged.<\/li>\n<li>Upcoming, Mini-month, and Label-overrides promoted out of the catch-all \"Advanced\" disclosure into top-level sections matching the List\/Card\/Month pattern.<\/li>\n<li>New \"Outbound links\" section consolidates four previously scattered link surfaces (event-link target, footer \"See all\" link, Upcoming title-row \"See all\", Mini-month full-calendar link) into one fieldset with subheadings.<\/li>\n<li>\"Visitor controls\" renamed to \"Visitor experience\"; view-toggle + instant-swap controls grouped with the other visitor-side rendering options (search, hover popover, mobile fallback). Front-end render is byte-identical \u2014 admin-only change.<\/li>\n<\/ul>\n\n<h4>0.12.9<\/h4>\n\n<ul>\n<li>Polish: admin URL placeholder examples changed from Norwegian <code>https:\/\/example.com\/kalender\/<\/code> to English <code>https:\/\/example.com\/calendar\/<\/code>. Plugin source language is English; example strings should match. Affects three input fields on the display edit form (Upcoming \"See all\" URL, Footer link URL, Mini-month full-calendar URL) plus the equivalent control in the Gutenberg block inspector. No functional change.<\/li>\n<\/ul>\n\n<h4>0.12.8<\/h4>\n\n<ul>\n<li>Fix: per-view CSS now pre-loaded on every page that hosts a swap-enabled display. Per-view stylesheets were previously conditionally enqueued only for the view being rendered on the initial request \u2014 which broke after a swap to a view whose CSS wasn't already in the document head (\"display totally messed up\" after clicking a toggle that targeted an unrendered view). The combined size of all view stylesheets is small (~6 KB), so pre-loading is the cleanest fix; avoids a runtime stylesheet-injection path in the swap module and the brief flash-of-unstyled-content that would produce. Also bundles the 0.12.7 fix for an undefined <code>$context<\/code> reference in <code>View_Nav::render()<\/code> that emitted a PHP warning on every prev\/next anchor render \u2014 invisible when <code>display_errors<\/code> is off, but if on it injected into the response body and corrupted the JSON the swap module received.<\/li>\n<\/ul>\n\n<h4>0.12.7<\/h4>\n\n<ul>\n<li>Fix: 0.12.6's page-URL plumbing in <code>View_Nav::render()<\/code> referenced an undefined <code>$context<\/code> variable (the function takes <code>$display_slug<\/code> directly, not a context object). PHP 8+ emitted a Warning per request; when <code>display_errors<\/code> is on in the host's PHP config, that warning text gets injected into the response body \u2014 corrupting the JSON returned to the swap module (\"display totally messed up\" after a swap). <code>View_Nav::render()<\/code> now takes <code>$page_url<\/code> as an explicit parameter; the four callers (Day\/Week\/Month\/Mini renderers) pass <code>$context-&gt;page_url<\/code>. No more undefined-variable reference.<\/li>\n<\/ul>\n\n<h4>0.12.6<\/h4>\n\n<ul>\n<li>Fix: view-toggle and prev\/next anchor hrefs inside the REST-rendered partial were being built against the REST endpoint URL (<code>\/wp-json\/sdaweb-gcal\/v1\/render<\/code>) instead of the page URL, because <code>add_query_arg()<\/code> with no explicit base falls back to <code>$_SERVER['REQUEST_URI']<\/code>. Invisible to JS users (the swap module parses params from the href query string and ignores the pathname), but would have broken any non-JS path through the swapped HTML and produced incorrect URLs in any context that read raw hrefs (e.g. RSS-style aggregators, accessibility tools that surface link targets). Client now sends <code>page_url=window.location.href<\/code> as a REST param; server validates same-origin and uses it as the base for all toggle\/nav anchor URLs. Per-page-URL cache partitioning added to the render cache + ETag so cached responses don't cross-contaminate across host pages.<\/li>\n<\/ul>\n\n<h4>0.12.5<\/h4>\n\n<ul>\n<li><strong>Fix: instant view-swap now works end-to-end.<\/strong> Three issues from the 0.12.4 verification: (1) <code>parseLinkIntent<\/code> was reading the saved-display slug from <code>root.dataset.sdawebGcalId<\/code>, which actually holds the synthesised DOM identifier (<code>sdaweb-gcal-&lt;hash&gt;<\/code>), not the saved-display slug \u2014 REST endpoint correctly rejected it as unknown \u2192 400 \u2192 swap fell back to native nav. Now reads <code>sdaweb_gcal_id<\/code> from the link's query string (the authoritative source) with the root dataset as a fallback. (2) Hover\/focus prefetch's <code>.catch()<\/code> was returning <code>null<\/code>, which leaked through to <code>applySwap()<\/code> and crashed on <code>data.html<\/code>. Now re-throws so swap()'s own catch handles the fallback cleanly. (3) <code>applySwap<\/code> and <code>announceToScreenReader<\/code> gained null guards as defense-in-depth so any future malformed payload bails gracefully instead of throwing. The <code>pushState<\/code> URL is now also correct (it uses <code>params.slug<\/code>, which is now the proper value).<\/li>\n<\/ul>\n\n<h4>0.12.4<\/h4>\n\n<ul>\n<li><strong>Fix: instant view-swap now actually works.<\/strong> Root cause was MIME-type \u2014 LiteSpeed (and many other hosts without <code>.mjs<\/code> in their MIME map) served the script module as <code>application\/octet-stream<\/code>, and browsers silently refuse to execute modules whose Content-Type isn't a JavaScript MIME type. No error logged (the refusal happens before any module code runs), so symptoms were \"module file present, all bytes correct, zero console errors, no click interception.\" Renamed <code>swap.mjs<\/code> \u2192 <code>swap.js<\/code>. WordPress's script-modules API adds <code>type=\"module\"<\/code> regardless of file extension; the <code>.js<\/code> extension is universally JS-MIME-mapped on every host. Diagnosed via Claude-for-Chrome inspection of the live response headers on stavdal.me.<\/li>\n<li>Also in 0.12.4 (rolled forward from a 0.12.3 test build that never shipped to wp.org): view-toggle anchors now carry <code>data-sdaweb-gcal-view-toggle=\"&lt;view-slug&gt;\"<\/code> in addition to the existing BEM class. Swap module's click delegate matches by either \u2014 defense in depth against future class refactors or theme-side class manipulation. Diagnostic logs from 0.12.2 remain gated by <code>?sdaweb_gcal_swap_debug=1<\/code>.<\/li>\n<\/ul>\n\n<h4>0.12.2<\/h4>\n\n<ul>\n<li>Diagnostic: instant view-swap module now emits gated console logs at every decision point (boot, target query, listener attachment, click match, intercept). Enable by appending <code>?sdaweb_gcal_swap_debug=1<\/code> to the URL, or by setting <code>window.SdawebGcalSwap.debug = true<\/code> before the module loads. Silent by default \u2014 no production impact. Surfaces the cause of \"module loads but clicks fall through to full-page navigation\" reports without requiring code changes for diagnosis.<\/li>\n<\/ul>\n\n<h4>0.12.1<\/h4>\n\n<ul>\n<li>Closing v0.12.0 gaps against the brief: REST response now includes <code>cache: { hit, age_seconds }<\/code> field derived from Render_Cache hit\/miss state; <code>Link: &lt;home_url&gt;; rel=\"canonical\"<\/code> header so caching layers indexing the JSON point back to the human site; <code>swap:render:abort<\/code> diagnostics event via <code>connection_aborted()<\/code> on client disconnect.<\/li>\n<li>Help tab gains \"Instant view switching\" section explaining the feature, how to enable per display, when to disable for caching-plugin compatibility, and how to debug via <code>window.SdawebGcalSwap.debug<\/code> + the Diagnostics activity log.<\/li>\n<li>Feature flag default refined to match the brief: <strong>on for new displays, off for existing<\/strong> (the v0.12.0 release set both to off uniformly). Existing displays preserve their pre-0.12.0 full-page navigation behaviour on upgrade; new displays created post-upgrade have instant view swap pre-enabled.<\/li>\n<li>New: <code>wp sdaweb-gcal rest-render &lt;slug&gt;<\/code> CLI command for dispatching the REST endpoint internally + dumping JSON response to stdout \u2014 drives the new REST snapshot tests.<\/li>\n<li>New: <code>wp sdaweb-gcal architecture-check --fixture=&lt;path&gt;<\/code> CLI command \u2014 assert REST inner-HTML normalises to shortcode-pipeline inner-HTML for the same display config. Catches future drift between the two render paths.<\/li>\n<li>New: 3 REST snapshot fixtures (<code>rest-list-grouped-by-day<\/code>, <code>rest-month-grid<\/code>, <code>rest-cards-anchor<\/code>) + shared events fixture. Test runner extended to handle both HTML and JSON snapshots and to run the architecture regression check as a final pass.<\/li>\n<li>New: <code>tests\/boot-regression.html<\/code> \u2014 browser-runnable assertion that swap.mjs's boot path attaches its click listener correctly regardless of when DOMContentLoaded fires. Catches the bug class where a deferred ES module misses the DOMContentLoaded event and silently leaves the click delegate unwired.<\/li>\n<\/ul>\n\n<h4>0.12.0<\/h4>\n\n<ul>\n<li><strong>New: Instant view switching.<\/strong> Clicking the view toggle (List\/Month\/Card\/Week\/Day\/Mini\/Upcoming) or prev\/next navigation now replaces only the calendar fragment \u2014 no page reload \u2014 with a smooth crossfade on supporting browsers via the View Transitions API. Falls back gracefully to full-page navigation when JavaScript is unavailable or the feature flag is off.<\/li>\n<li><strong>New: REST endpoint<\/strong> <code>\/wp-json\/sdaweb-gcal\/v1\/render<\/code> for fetching individual view fragments. Public read; returns JSON with inner HTML + ETag + Cache-Control headers. 304 Not Modified support saves bandwidth on repeat navigations.<\/li>\n<li><strong>New: Per-display feature flag<\/strong> \"Enable instant view swap\" in the View section. Defaults <strong>off<\/strong> for safety \u2014 admin opts in per display after confirming compatibility with the site's caching plugins. Dimmed (per v0.11.0 #6 pattern) when \"Show view toggle\" is off.<\/li>\n<li><strong>New: Hover\/focus prefetch.<\/strong> Hovering or tab-focusing a view toggle speculatively fetches the target view; the subsequent click commits against a warm cache. Respects <code>Save-Data<\/code> and runs in <code>requestIdleCallback<\/code> so it never competes with user-driven work. Per-root LRU(6).<\/li>\n<li><strong>New: Accessibility for AJAX swaps<\/strong> \u2014 <code>aria-busy<\/code> during fetch, screen-reader announcement after swap via a per-root live region, focus moves to the new view's primary heading, <code>prefers-reduced-motion<\/code> skips the transition animation.<\/li>\n<li><strong>New: Diagnostics integration.<\/strong> Every swap fires <code>swap:render:ok\/304\/fail<\/code> events into the v0.11.0 #12 ring buffer. Set <code>window.SdawebGcalSwap.debug = true<\/code> from the browser console for client-side timing logs.<\/li>\n<li><strong>Bumped: Minimum WordPress version is now 6.5<\/strong> (was 6.0) \u2014 required for the WP-native script-modules API the swap module uses.<\/li>\n<\/ul>\n\n<h4>0.11.1<\/h4>\n\n<ul>\n<li>Compliance: silence 12 Plugin Check (PCP) false-positive errors from 0.11.0 \u2014 9 OutputNotEscaped at the new Link_Target helper call sites (the helper returns a hardcoded attribute string, never visitor input \u2014 phpcs can't trace escaping across function boundaries), 1 OutputNotEscaped on the CLI render command's raw stdout echo (intentional for snapshot capture), and 2 MissingTranslatorsComment on the relative-time formatter (translator comments now sit on the line immediately preceding each <code>__()<\/code> call as the sniff requires). No runtime behavior change.<\/li>\n<\/ul>\n\n<h4>0.11.0<\/h4>\n\n<ul>\n<li><strong>New (#8): Centralised link-target helper.<\/strong> Every outbound link across every view now routes through <code>View_Footer_Link<\/code>-sibling helper <code>Link_Target::attrs()<\/code>, so the display's \"Open links in\" setting governs all link surfaces consistently. Mini-month full-link and Upcoming top-row \"See all\" now also honour the setting (previously hardcoded to same-window). Footer link remains decoupled per 0.10.1.<\/li>\n<li><strong>New (#1): Per-display render cache + cascade invalidation.<\/strong> Rendered HTML is now cached (15-minute floor TTL). Calendar \"Refresh now\" cascades into every display containing the calendar, busting their render caches; the success notice surfaces the dependent-display count. New per-display \"Refresh render cache\" row action on the Displays tab. Display form save and delete also bust automatically.<\/li>\n<li><strong>New (#6): Consistent inactive-field dimming.<\/strong> Five conditional field groups in the display edit form now visibly dim with an inline \"inactive \u2014 needs X\" badge when their master switch is off: Footer link 4 fields (gated by Show \"See all\" link), Upcoming title cluster (gated by Show title row), Upcoming \"See all\" URL (doubly gated), Mini-month link label (gated by URL non-empty), View toggle 2 fields (gated by Show view toggle). Dimmed fields stay interactive and persist values to the DB.<\/li>\n<li><strong>New (#12): Diagnostics sub-panel in the Tools tab.<\/strong> Read-only introspection: per-calendar fetch state, per-display render cache hit\/miss counters, environment summary, last-50 events ring buffer. Three action buttons: re-fetch all calendars, bust all render caches, download diagnostics JSON. Replaces the save \u2192 reload \u2192 guess debugging loop.<\/li>\n<li><strong>New (#19, partial): WP-CLI commands.<\/strong> New <code>wp sdaweb-gcal render &lt;slug&gt;<\/code> for rendering a saved display to stdout, and <code>wp sdaweb-gcal cache:bust [&lt;slug&gt;]<\/code> for cache utilities. <code>--fixture=&lt;path&gt;<\/code> and <code>--events=&lt;path&gt;<\/code> flags enable deterministic snapshot testing via the new <code>sdaweb_gcal_pre_fetch_events<\/code> filter. Test fixture + bash runner + README under <code>tests\/<\/code> (not shipped in the wp.org zip). Snapshot-test feature is intentionally focused \u2014 <code>--now<\/code> flag, more fixtures, and CI wiring deferred to v0.12.0.<\/li>\n<\/ul>\n\n<h4>0.10.1<\/h4>\n\n<ul>\n<li>Change: Footer link (\"See all \u2192\") always opens in the same window now, decoupled from the display's \"Open links in\" (<code>link_target<\/code>) setting. Footer links are sibling-site navigation; event-row links continue to honour <code>link_target<\/code> for external Google Calendar event detail pages.<\/li>\n<\/ul>\n\n<h4>0.10.0<\/h4>\n\n<ul>\n<li>New: <strong>Footer link<\/strong> (\"See all \u2192\") on the bottom of List, Upcoming, and Card views. Five new admin fields under a \"Footer link\" subsection in the View tab: show toggle, URL, label override, style (Quiet \/ Underline), and alignment (Auto \/ Start \/ Center \/ End). Auto resolves per-view: End for Upcoming, Center for List, Start for Card. Color follows the display's Link color override (single source of truth across chips, top-row \"See all\", and the new footer link). Empty URL hard-suppresses render. Coexists with the existing Upcoming top-row \"See all\" \u2014 both can be enabled.<\/li>\n<\/ul>\n\n<h4>0.9.5<\/h4>\n\n<ul>\n<li>Fix: Upcoming view \u2014 <strong>Title size<\/strong>, <strong>Title weight<\/strong>, and <strong>Uppercase title text<\/strong> settings actually take effect now. <code>Shortcode::defaults()<\/code> was missing the three keys, so <code>shortcode_atts()<\/code> was silently stripping them out of the config before it reached the view layer. The 0.9.4 patch (passing the keys through <code>Display_Renderer::build_view_context<\/code>) and the 0.9.3 CSS scoping change were both correct, but a third upstream layer needed the same keys to be added too. With the input gate now opened, all three controls work end-to-end.<\/li>\n<\/ul>\n\n<h4>0.9.4<\/h4>\n\n<ul>\n<li>Fix: Upcoming view \u2014 <strong>Title size<\/strong>, <strong>Title weight<\/strong>, and <strong>Uppercase title text<\/strong> settings now actually take effect. The display orchestrator (<code>Display_Renderer::build_view_context()<\/code>) was missing the three keys from the options array it builds for the view, so the renderer always read empty values and fell back to its built-in defaults (<code>15px<\/code> \/ <code>500<\/code> \/ no transform). The renderer itself was correct; the upstream <code>Display_Renderer<\/code> is now passing the keys through. Visible only on <code>title_uppercase<\/code> until now \u2014 <code>title_size<\/code> and <code>title_weight<\/code> defaults coincidentally matched the renderer fallbacks, hiding the bug. 0.9.3's CSS scoping change remains in place as defense-in-depth against themes that override <code>text-transform<\/code> directly on <code>&lt;span&gt;<\/code> elements.<\/li>\n<\/ul>\n\n<h4>0.9.3<\/h4>\n\n<ul>\n<li>Fix: \"Uppercase title text\" setting now applies correctly on the Upcoming view when the active theme has its own <code>text-transform<\/code> rule on <code>&lt;span&gt;<\/code> elements. Scoped the rule to <code>.sdaweb-gcal-upcoming__title-text<\/code> directly instead of relying on inheritance from the parent.<\/li>\n<\/ul>\n\n<h4>0.9.2<\/h4>\n\n<ul>\n<li>Compatibility: Tested with WordPress 7.0.<\/li>\n<li>Cleanup: Suppressed a Plugin Check warning on the <code>WP_DEBUG<\/code>-gated <code>error_log()<\/code> diagnostic added in 0.9.1 (recurrence canary for the 0.9.0 native-color-picker swap bug). No behaviour change.<\/li>\n<\/ul>\n\n<h4>0.9.1<\/h4>\n\n<ul>\n<li>Fix: Per-calendar Text color save bug introduced in 0.9.0. On the Calendars edit form, saving a Text color caused that value to land in the calendar's main Color field instead, wiping the original Color value and leaving Text color blank. Root suspected in the interaction between the new wpColorPicker Text color field and the existing native <code>&lt;input type=\"color\"&gt;<\/code> Calendar Color field (likely via the OS-level recent-colors memory the native picker exposes). 0.9.1 replaces the native picker on the Calendars tab with the same wpColorPicker control the Text color field uses \u2014 both fields now share the plumbing that the Displays-tab color fields have used for years without incident. If you saved a calendar in 0.9.0 with a Text color and find the swatch flipped to the value you picked: re-edit the calendar, restore the original Color, set the Text color again, and save.<\/li>\n<\/ul>\n\n<h4>0.9.0<\/h4>\n\n<ul>\n<li>New: <strong>Tinted<\/strong> option added to <strong>Chip text color (Solid &amp; Pastel)<\/strong> on the display edit screen. Derives a deep tint of the feed color in OKLCh that passes WCAG \u2265 4.5:1 against the actual chip background (raw feed for Solid, the pastel-tinted mix for Pastel). For light feeds (yellows, limes, cyans, light pinks), tinted text would be visually indistinguishable from black; the resolver detects that perceptual collapse and falls back to the Auto dark color automatically so authors don't end up with a fourth option that's secretly the third one. Pastel chips have more contrast headroom than Solid, so Tinted works on more feeds in Pastel.<\/li>\n<li>New: <strong>Per-calendar Text color<\/strong> picker on the Calendars tab (Color row). When set, this color is used as the chip text color for every event from that calendar, on every display, regardless of the display's Chip text color setting. Live contrast badge in the picker checks against the calendar's own background (Solid worst case); Pastel renders the same text on a softer tint and has additional headroom. Leave blank to let each display's Chip text color setting decide.<\/li>\n<li>Change: <strong>Solid chip text color<\/strong> field renamed to <strong>Chip text color (Solid &amp; Pastel)<\/strong> \u2014 Pastel chip titles now consume the same resolved text color as Solid through the shared <code>--sdaweb-gcal-event-text<\/code> custom property, so a single field governs both styles. Existing displays render unchanged when chip_text_mode is <code>auto<\/code>; Pastel chips opt in to the resolved color when chip_text_mode is <code>light<\/code>, <code>dark<\/code>, or <code>tinted<\/code>, or when the per-calendar Text color override is set.<\/li>\n<li>Fix: <code>chip_text_mode<\/code> (added in 0.8.3 as <code>Solid chip text color<\/code>) was not being copied from the saved display record into the render context, so every display rendered as if the mode were <code>auto<\/code> regardless of what the admin had selected. <code>Always light<\/code> and <code>Always dark<\/code> have been silent no-ops since 0.8.3; they now work as advertised.<\/li>\n<li>Fix: Visited chip and ribbon links lost their resolved chip text color. The global <code>.sdaweb-gcal a:visited<\/code> rule (specificity 0,2,1) out-specified the resting chip-anchor rule (0,2,0), so once a visitor clicked a chip link and returned the chip title flipped to the link color, undoing the Auto-mode WCAG choice and the new per-calendar override. Chip and ribbon anchor rules now list <code>:link<\/code> \/ <code>:visited<\/code> explicitly to bump specificity past the global rule.<\/li>\n<li>Fallback aggregator: when Tinted is selected and one or more selected feeds collapse to the Auto target, the display editor shows a single quiet note (\"N of M selected feeds fall back\u2026\") under the field after save \u2014 no per-feed wall of warnings.<\/li>\n<\/ul>\n\n<h4>0.8.6<\/h4>\n\n<ul>\n<li>Fix: Service-account token-exchange success no longer writes to the PHP error log unconditionally. The success path is now gated on <code>WP_DEBUG<\/code>; on busy sites this was logging one line every ~55 minutes even when everything was working correctly. Token-exchange failures and API errors continue to log unconditionally so real problems are always surfaced.<\/li>\n<li>Fix: Upgrade notice for 0.8.3 in <code>readme.txt<\/code> exceeded WordPress.org's 300-character limit (Plugin Check <code>upgrade_notice_limit<\/code> warning). No code change.<\/li>\n<\/ul>\n\n<h4>0.8.5<\/h4>\n\n<ul>\n<li>New: <strong>Delete<\/strong> button on each row in Tools \u2192 Backups. Snapshots could previously only be removed by capturing three new ones to push them out of the retention window \u2014 fine for routine pruning, awkward when a single test snapshot wanted to go. The Delete button now removes one specific snapshot immediately (confirm dialog first; record is gone from the <code>sdaweb_gcal_backups<\/code> option). Frees a retention slot so the next pre-import\/manual snapshot won't prune a newer one to make room.<\/li>\n<\/ul>\n\n<h4>0.8.4<\/h4>\n\n<ul>\n<li>Fix: Solid Month chips lost their auto-resolved text color on hover. The general <code>.sdaweb-gcal a:hover<\/code> rule in front.css set a hover color that cascaded into the chip's <code>&lt;a&gt;<\/code> because the more specific <code>.sdaweb-gcal-month__event a:hover<\/code> rule only set <code>text-decoration<\/code> \u2014 same specificity, no <code>color<\/code> reset, so the general rule's color property won by cascade. Result: a chip whose Auto mode picked dark text correctly would flip to the link\/accent color on hover, often dropping back below the WCAG threshold the Auto mode was protecting. Fix pins the hover color to <code>inherit<\/code> and adds a subtle <code>text-underline-offset: 2px<\/code> for breathing room. Week\/Day chips deliberately keep their primary-color hover (different chip design \u2014 surface backgrounds, hover affordance comes from the color change).<\/li>\n<\/ul>\n\n<h4>0.8.3<\/h4>\n\n<ul>\n<li>New: <strong>Solid chip text color<\/strong> option on the display edit screen (Appearance \u2192 Event chip style \u2192 Solid chip text color). Three modes: <code>Auto<\/code> (default) computes the WCAG relative-luminance contrast for white vs dark text against each chip background and picks the higher-contrast side; <code>Always light (white)<\/code> preserves the pre-0.8.3 behaviour; <code>Always dark<\/code> forces dark text. Fixes a long-standing readability issue where Solid chips with mid-luminance feed colors (greens like <code>#16a34a<\/code>, yellows, mints) rendered as white text on a too-light background and the title \"faded into\" the chip in the tiny month-grid footprint. Existing displays render unchanged on first load (Auto picks white for the same dark reds\/blues\/teals that white was already being used on); only chips whose feed color previously failed AA against white will switch to dark text.<\/li>\n<\/ul>\n\n<h4>0.8.2<\/h4>\n\n<ul>\n<li>New: Upcoming view exposes three new per-display title appearance controls \u2014 <strong>Title size<\/strong> (Small \/ Medium \/ Large \/ X-Large), <strong>Title weight<\/strong> (Normal \/ Medium \/ Semi-bold \/ Bold), and an <strong>Uppercase title<\/strong> toggle. Useful when the surrounding container's headings (sidebar widget titles, footer column headers) use a different weight\/case than the plugin's default <code>15px \/ 500<\/code>, so the \"Fra kalenderen\"-style title can be tuned to blend in without site CSS overrides. Existing displays render unchanged \u2014 defaults match the previous hard-coded values.<\/li>\n<li>Change: The Upcoming view's \"See all \u2192\" link now always opens in the same window, regardless of the display's Link target setting. The link points to the site's own calendar page; opening a same-site link in a new tab is a UX anti-pattern (breaks the back button, loses navigation context). Per-event URLs (typically off-site) continue to honour the Link target setting.<\/li>\n<\/ul>\n\n<h4>0.8.1<\/h4>\n\n<ul>\n<li>Fix: View-toggle \/ focus-date \/ \"+N more\" \/ date-jump URLs are now scoped to the calendar instance whose control was used \u2014 they no longer bleed into every other calendar on the page. Previously, switching the main calendar to Month or Day view also flipped any other calendar instance on the page (footer widgets, sidebar instances) into the same view. A footer \"Upcoming\" widget on the same page as a Month-view calendar would render as a cramped Month grid in the narrow footer column, and looked broken. Same root issue for <code>?sdaweb_gcal_focus=<\/code> \u2014 every calendar on the page jumped to the same date. Fix: every navigation URL now carries <code>sdaweb_gcal_id=&lt;display-slug&gt;<\/code> and the renderer ignores the override unless the URL's id matches the current display.<\/li>\n<\/ul>\n\n<h4>0.8.0<\/h4>\n\n<ul>\n<li>New: Month-view \"+N more\" indicator is now interactive (was a non-clickable <code>&lt;li&gt;<\/code> \u2014 a real a11y gap; keyboard users and screen readers couldn't reach it). New per-display option <strong>Day overflow (\"+N more\")<\/strong> under \"Month view options\" with two modes: <code>popover<\/code> (default) opens an in-place panel listing the day's full event list; <code>day<\/code> turns \"+N more\" into a link that loads the Day view for that date. Popover ships with focus management (Esc, click-outside, focus restoration), <code>aria-haspopup<\/code>\/<code>aria-expanded<\/code>, and <code>role=\"dialog\"<\/code>. Day-link mode ships zero JS.<\/li>\n<li>New <code>month_overflow_action<\/code> shortcode attribute \/ <code>monthOverflowAction<\/code> block attribute (values: <code>popover<\/code> | <code>day<\/code>).<\/li>\n<\/ul>\n\n<h4>0.7.4<\/h4>\n\n<ul>\n<li>Fix: Multi-day events in the Cards view now render with a date range in the date column (\"4\u20138 \/ MAI \/ man\u2013fre\" for same-month, stacked \"30 APR \u2193 5 MAI\" for cross-month) instead of showing only the start day. Previously a 5-day event like \"Class trip Monday\u2013Friday\" looked indistinguishable from a single-day event in cards.<\/li>\n<li>Fix: Cards view with <code>card_grouping = day<\/code> now expands multi-day events into every day they cover, matching Week and Mini view behaviour. Previously a Monday\u2013Friday event only appeared under Monday's section. Week and month grouping keep the simpler \"appear once at the start group\" behaviour \u2014 the new date-range badge signals the span without multiplying cards across groups.<\/li>\n<li>All-day handling honours Google Calendar's exclusive-end-date convention (a \"Mar 5\u20137\" event arriving as end=Mar 8 is correctly bucketed and labelled as Mar 5\u20137 inclusive).<\/li>\n<\/ul>\n\n<h4>0.7.3<\/h4>\n\n<ul>\n<li>Docs: Moved <code>CHANGELOG.md<\/code> from the plugin root to <code>docs\/CHANGELOG.md<\/code>. Plugin Check's <code>unexpected_markdown_file<\/code> rule (severity 9 WARNING) flags any non-readme <code>.md<\/code> file at the plugin root.<\/li>\n<li>Docs: Trimmed this Changelog section (per Plugin Check's 5000-character cap). Full history is in <code>docs\/CHANGELOG.md<\/code>.<\/li>\n<\/ul>\n\n<h4>0.7.1<\/h4>\n\n<ul>\n<li>Admin: Edit Display form fields no longer overflow under the sticky Live preview pane on the right. <code>&lt;select&gt;<\/code> elements had no width rule and auto-sized to their longest option text, clipping trailing characters behind the preview. Selects, plus <code>input[type=\"number\" | \"url\" | \"date\"]<\/code>, now share the same <code>width: 100%; max-width: 520px<\/code> cap as text inputs.<\/li>\n<\/ul>\n\n<h4>0.7.0<\/h4>\n\n<ul>\n<li>New: \"Card grouping\" display option \u2014 groups cards in the Cards view under day, ISO week, or calendar month section headings, with a per-section event count. Defaults to off; existing displays render byte-identically until you opt in. Configured per display under \"Card view options\" in the admin, or via the <code>card_grouping<\/code> shortcode attribute \/ <code>cardGrouping<\/code> block attribute.<\/li>\n<\/ul>\n\n<h4>0.6.10<\/h4>\n\n<ul>\n<li>Fix: Per-display label overrides (\"Today\", \"Tomorrow\", \"All day\", etc.) are now honored by the shortcode\/block pipeline. Same root cause as 0.6.9 \u2014 <code>Shortcode::defaults()<\/code> was missing the <code>labels<\/code> key.<\/li>\n<li>New: Gutenberg block now exposes \"Event color override\" as an Inspector control. Block users in inline mode (no saved display selected) had no UI to set the override before.<\/li>\n<li>Fix: Admin Live Preview now correctly renders the Upcoming view when configured. Was silently falling back to List.<\/li>\n<\/ul>","raw_excerpt":"Display events from one or more Google Calendars. Six views, hover popovers, ICS subscribe, mobile-aware, theme-aware, accessible.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/sr.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/307966","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sr.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/sr.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/sr.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=307966"}],"author":[{"embeddable":true,"href":"https:\/\/sr.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/rstake"}],"wp:attachment":[{"href":"https:\/\/sr.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=307966"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/sr.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=307966"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/sr.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=307966"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/sr.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=307966"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/sr.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=307966"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/sr.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=307966"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}