Skip to content

Instantly share code, notes, and snippets.

@nahu02
Last active February 2, 2026 19:34
Show Gist options
  • Select an option

  • Save nahu02/5219705c84ab29a9928dbf3b1b0e6b3f to your computer and use it in GitHub Desktop.

Select an option

Save nahu02/5219705c84ab29a9928dbf3b1b0e6b3f to your computer and use it in GitHub Desktop.

Google Calendar Privacy Toggle

A Tampermonkey userscript that adds a privacy toggle button to Google Calendar. It allows you to instantly hide all event text (titles, times, locations) while preserving the color-coded event blocks. Note that this is a vibe-coded solution which takes heavy inspiration from @IceCreamYou and others' prior work.

This is ideal for:

  • Sharing your screen during meetings.
  • Taking screenshots of your availability without revealing sensitive details.
  • Showing someone your availability in person without revealing sensitive details.

โœจ Features

  • Floating Toggle Button: Adds a button to the bottom-left of the interface, where it is least likely to obscure something important
  • Smart Hiding: Uses color: transparent instead of removing elements, ensuring the calendar layout (event height/width) remains exactly the same.
  • State Persistence: Remembers your preference (Hidden/Visible) even after reloading the page.
  • Security Compliant: Includes a Trusted Types policy to work correctly with Google's strict Content Security Policy (CSP).

๐Ÿ“ฅ Installation

  1. Install a userscript manager like Tampermonkey or Violentmonkey.
  2. Click the Raw button on the gcal-privacy-toggle.user.js file in this Gist.
  3. Confirm the installation in the tab that opens.
  4. Refresh Google Calendar.

๐Ÿ•น๏ธ Usage

  • Default View: Events are visible as normal.
  • To Hide: Click the Eye Icon (๐Ÿ‘๏ธ) in the bottom-left corner. It will change to a Lock Icon (๐Ÿ”’), and all text will vanish.
  • To Show: Click the Lock Icon to restore text.

๐Ÿ”ง Technical Info

The script targets the [data-eventchip] attribute, which is more stable than Google's obfuscated class names (like EiZ8Dd) which are prone to change. It injects a dynamic stylesheet that toggles based on a class added to the document body.

/* Core logic */
body.privacy-active [data-eventchip] span {
    color: transparent !important;
}
// ==UserScript==
// @name Google Calendar - Privacy Toggle Button
// @namespace http://tampermonkey.net/
// @version 2.0
// @description Adds a floating button to toggle event text visibility for privacy.
// @author https://www.perplexity.ai/search/could-you-create-a-google-cale-zHxGZXRdSN2jj64wJ2d0RQ#1
// @match https://calendar.google.com/*
// @grant none
// @run-at document-end
// ==/UserScript==
(function() {
'use strict';
// --- Configuration ---
const STORAGE_KEY = 'gcal_privacy_mode_active';
const BUTTON_ID = 'gcal-privacy-toggle-btn';
// --- CSS for Privacy Mode (Text Hiding) ---
// We target the body with a specific class to make toggling easy
const privacyStyles = `
/* When body has class 'privacy-active', hide text */
body.privacy-active [data-eventchip] span,
body.privacy-active [data-eventchip] div,
body.privacy-active [data-eventchip] {
color: transparent !important;
}
/* Optional: Hide icons */
body.privacy-active [data-eventchip] i,
body.privacy-active [data-eventchip] img,
body.privacy-active [data-eventchip] .rF3YF {
visibility: hidden !important;
}
/* Agenda view */
body.privacy-active .hv0 Rb,
body.privacy-active .jKgTF {
color: transparent !important;
}
`;
// --- CSS for the Toggle Button ---
const buttonStyles = `
#${BUTTON_ID} {
position: fixed;
bottom: 20px;
left: 20px;
z-index: 9999;
background-color: #fff;
border: 1px solid #dadce0;
border-radius: 50%;
width: 48px;
height: 48px;
box-shadow: 0 1px 3px rgba(60,64,67,0.3), 0 4px 8px 3px rgba(60,64,67,0.15);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background-color 0.2s, transform 0.2s;
font-size: 24px;
}
#${BUTTON_ID}:hover {
background-color: #f1f3f4;
transform: scale(1.05);
}
#${BUTTON_ID}.active {
background-color: #e8f0fe;
color: #1a73e8;
border-color: #1a73e8;
}
`;
// --- Helper to handle Trusted Types (Security) ---
function getPolicy() {
if (window.trustedTypes && window.trustedTypes.createPolicy) {
try {
return window.trustedTypes.createPolicy('gcal-privacy-ui-policy', {
createHTML: (string) => string
});
} catch (e) {
// Policy likely already exists
return null;
}
}
return null;
}
function injectStyles() {
const policy = getPolicy();
const style = document.createElement('style');
const combinedCss = privacyStyles + buttonStyles;
if (policy) {
style.innerHTML = policy.createHTML(combinedCss);
} else {
style.textContent = combinedCss;
}
document.head.appendChild(style);
}
// --- Main Logic ---
function init() {
injectStyles();
// Create the toggle button
const btn = document.createElement('button');
btn.id = BUTTON_ID;
btn.innerHTML = '๐Ÿ‘๏ธ'; // Default icon (Visible)
btn.title = "Toggle Event Privacy";
// Check saved state
const isPrivacyActive = localStorage.getItem(STORAGE_KEY) === 'true';
updateState(isPrivacyActive, btn);
// Click handler
btn.addEventListener('click', () => {
const currentlyActive = document.body.classList.contains('privacy-active');
updateState(!currentlyActive, btn);
});
document.body.appendChild(btn);
}
function updateState(isActive, btn) {
if (isActive) {
document.body.classList.add('privacy-active');
btn.classList.add('active');
btn.innerHTML = '๐Ÿ”’'; // Icon when private
localStorage.setItem(STORAGE_KEY, 'true');
} else {
document.body.classList.remove('privacy-active');
btn.classList.remove('active');
btn.innerHTML = '๐Ÿ‘๏ธ'; // Icon when visible
localStorage.setItem(STORAGE_KEY, 'false');
}
}
// Run initialization
init();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment