Created
May 6, 2025 10:42
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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> |
Author
OndraZizka
commented
May 6, 2025
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment