Skip to content

Instantly share code, notes, and snippets.

@OndraZizka
Created May 6, 2025 10:42
Show Gist options
  • Save OndraZizka/325eee26a3902d4be02d35cc206269f8 to your computer and use it in GitHub Desktop.
Save OndraZizka/325eee26a3902d4be02d35cc206269f8 to your computer and use it in GitHub Desktop.
A page with a custom dynamic favicon and title. Can be used for Firefox Tree Style Tab as a group designator.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Loading...</title>
<link id="favicon" rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="> <style>
body {
font-family: sans-serif;
padding: 20px;
line-height: 1.6;
}
code {
background-color: #f0f0f0;
padding: 2px 5px;
border-radius: 3px;
}
.param-display {
margin-top: 20px;
border: 1px solid #ccc;
padding: 15px;
border-radius: 5px;
background-color: #f9f9f9;
}
.color-preview {
display: inline-block;
width: 20px;
height: 20px;
border: 1px solid #555;
vertical-align: middle;
margin-left: 5px;
}
label {
margin-right: 5px;
font-weight: bold;
}
input[type="color"] {
vertical-align: middle;
margin-left: 5px;
cursor: pointer;
border: 1px solid #ccc;
height: 30px; /* Make picker slightly larger */
padding: 2px; /* Add slight padding */
}
.setting {
margin-bottom: 10px;
}
</style>
</head>
<body>
<h1>Dynamic Page Title and Favicon</h1>
<p>This page's title and favicon color are set based on URL parameters.</p>
<p>Try appending <code>?name=YourName&color=YourColor</code> to the URL, or use the color picker below.</p>
<p>Examples:</p>
<ul>
<li><code>?name=Projects&color=blue</code></li>
<li><code>?name=Urgent&color=%23ff0000</code> (URL encoded #ff0000 for red)</li>
<li><code>?name=Info&color=lightgreen</code></li>
</ul>
<div class="param-display">
<h2>Current Settings:</h2>
<div class="setting">
Page Title set to: <strong id="display-name"></strong>
</div>
<div class="setting">
Favicon Color set to: <strong id="display-color"></strong>
<span id="color-preview" class="color-preview"></span>
</div>
<div class="setting">
<label for="colorPicker">Change Color:</label>
<input type="color" id="colorPicker" name="colorPicker">
</div>
</div>
<script>
// Store the current name parameter to reuse it when color changes
let currentName = 'Default Page';
const defaultColor = '#808080'; // Grey in hex format
// --- DOM Elements ---
const faviconLink = document.getElementById('favicon');
const displayNameElement = document.getElementById('display-name');
const displayColorElement = document.getElementById('display-color');
const colorPreviewElement = document.getElementById('color-preview');
const colorPickerElement = document.getElementById('colorPicker');
/**
* Generates a square favicon as a Data URL with the specified color.
* @param {string} color - A valid CSS color string.
* @param {number} size - The size of the square favicon. Defaults to 32.
* @returns {string} - A Data URL string (PNG format).
*/
function generateFaviconDataUrl(color, size = 32) {
const canvas = document.createElement('canvas');
canvas.width = size;
canvas.height = size;
const ctx = canvas.getContext('2d');
try {
ctx.fillStyle = color;
ctx.fillRect(0, 0, size, size);
return canvas.toDataURL('image/png');
} catch (error) {
console.warn(`Invalid color "${color}" for favicon, using default.`, error);
ctx.fillStyle = defaultColor; // Fallback color
ctx.fillRect(0, 0, size, size);
return canvas.toDataURL('image/png');
}
}
/**
* Updates the visual elements (title, favicon, text displays, color picker value).
* @param {string} name - The name for the title.
* @param {string} color - The CSS color string.
*/
function applyStyling(name, color) {
try {
// 1. Set Document Title
document.title = name;
// 2. Generate and Set Favicon
const faviconDataUrl = generateFaviconDataUrl(color);
faviconLink.href = faviconDataUrl;
// 3. Update Display Information on Page
displayNameElement.textContent = name;
displayColorElement.textContent = color;
colorPreviewElement.style.backgroundColor = color;
// 4. Update Color Picker Value
// Input type=color expects hex format (#rrggbb).
// If the input 'color' isn't hex, the browser might handle it,
// or reset to black. For robustness, we ideally convert.
// However, the picker itself *outputs* hex, simplifying updates *from* the picker.
// For initial load, we'll try setting directly.
// Use a temporary element to try and get the computed hex value for better initial sync
const tempDiv = document.createElement('div');
tempDiv.style.color = color;
document.body.appendChild(tempDiv); // Must be in DOM to compute style
const computedColorStyle = window.getComputedStyle(tempDiv).color;
// computedColorStyle is often "rgb(r, g, b)". Convert to hex.
const rgbMatch = computedColorStyle.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
let hexColor = defaultColor; // Default if conversion fails
if (rgbMatch) {
hexColor = "#" + (+rgbMatch[1]).toString(16).padStart(2, '0') +
(+rgbMatch[2]).toString(16).padStart(2, '0') +
(+rgbMatch[3]).toString(16).padStart(2, '0');
} else if (/^#[0-9a-f]{6}$/i.test(color)) {
hexColor = color; // Already hex
} else {
console.warn(`Could not reliably convert initial color "${color}" to hex for color picker. Using default.`);
}
document.body.removeChild(tempDiv); // Clean up temp element
colorPickerElement.value = hexColor;
} catch (error) {
console.error("Error applying styling:", error);
// Fallback display in case of severe errors
displayNameElement.textContent = name + " (Error)";
displayColorElement.textContent = color + " (Invalid)";
colorPreviewElement.style.backgroundColor = defaultColor;
faviconLink.href = generateFaviconDataUrl(defaultColor);
colorPickerElement.value = defaultColor;
}
}
/**
* Updates the URL's query parameters without reloading the page.
* @param {string} name - The name parameter value.
* @param {string} color - The color parameter value.
*/
function updateUrl(name, color) {
const urlParams = new URLSearchParams(window.location.search);
urlParams.set('name', name);
urlParams.set('color', color);
const newUrl = window.location.pathname + '?' + urlParams.toString() + window.location.hash; // Preserve hash
// Use replaceState to avoid polluting browser history for simple style changes
history.replaceState(null, '', newUrl);
}
/**
* Initializes the page based on URL parameters on load.
*/
function initializePage() {
const urlParams = new URLSearchParams(window.location.search);
const nameParam = urlParams.get('name') || 'Default Page';
const colorParam = urlParams.get('color') || defaultColor; // Use hex default
currentName = nameParam; // Store the initial name
applyStyling(nameParam, colorParam);
}
// --- Event Listeners ---
// Initialize on load
document.addEventListener('DOMContentLoaded', initializePage);
// Update when color picker value changes
colorPickerElement.addEventListener('input', (event) => {
const newColor = event.target.value; // This is always in #rrggbb hex format
// Apply styling using the *current* name and the *new* color
applyStyling(currentName, newColor);
// Update the URL to reflect the change
updateUrl(currentName, newColor);
});
</script>
</body>
</html>
@OndraZizka
Copy link
Author

Screenshot of how it looks in Firefox with Tree Style Tabs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment