Forum
The great place to discuss topics with other users
change your site appearance
'This code can be further developed, including integrating your own database and more. You can also place it anywhere you like, such as in _header.tpl.
{if $user->_logged_in}
{literal}
<style>
:root {
--unitymix-accent:#30ce7d;
--unitymix-font:"Inter",sans-serif;
--unitymix-font-size:14px;
}
/* Global */
html, body {
font-family: var(--unitymix-font) !important;
font-size: var(--unitymix-font-size) !important;
scroll-behavior: smooth;
}
/* Scrollbar */
body::-webkit-scrollbar { width:12px; height:12px; background:#e1e1e1; }
body::-webkit-scrollbar-thumb { background: var(--unitymix-accent); border-radius:6px; }
/* Panel */
#personalizationPanel { position: relative; display: inline-block; z-index: 9999; }
#appearanceBtn { padding: 8px 12px; border:1px solid #ccc; border-radius:8px; background:#fff; cursor:pointer; font-weight:500; }
#appearanceDropdown { display:none; position:absolute; top:100%; left:0; min-width:300px; max-height:400px; overflow-y:auto; background:#fff; border:1px solid #ccc; border-radius:12px; padding:12px; box-shadow:0 8px 20px rgba(0,0,0,.15); z-index:9999; }
/* Accessibility classes */
body.contrast-plus {
background-color:#fff !important;
color:#000 !important;
}
body.contrast-plus a,
body.contrast-plus h1,h2,h3,h4,h5,h6,
body.contrast-plus button,
body.contrast-plus input,
body.contrast-plus select,
body.contrast-plus textarea {
color:#000 !important;
background-color:#fff !important;
border-color:#000 !important;
}
body.smart-contrast p,
body.smart-contrast span,
body.smart-contrast li,
body.smart-contrast h1,h2,h3,h4,h5,h6,
body.smart-contrast a,
body.smart-contrast button {
color: #000 !important;
background-color: #fff !important;
}
body.text-spacing { letter-spacing:.06em; word-spacing:.16em; line-height:1.8; }
body.reduce-motion *, body.reduce-motion *::before, body.reduce-motion *::after { animation:none !important; transition:none !important; }
/* High-specificity focus outlines */
body.focus-outline a:focus,
body.focus-outline button:focus,
body.focus-outline input:focus,
body.focus-outline select:focus,
body.focus-outline textarea:focus,
body.focus-outline [tabindex]:focus {
outline: 3px solid var(--unitymix-accent) !important;
outline-offset: 2px !important;
}
/* Big cursor */
body.big-cursor, body.big-cursor * { cursor: pointer !important; }
/* Tooltips */
.tooltip-enhanced .tooltip-element:hover::after,
.tooltip-enhanced .tooltip-element:focus::after {
content: attr(data-tooltip);
position: absolute;
top:-28px; left:0;
background:#333; color:#fff; padding:2px 6px; font-size:11px; border-radius:4px; white-space:nowrap; z-index:9999;
}
/* Voice overlay */
#voiceOverlay { position:fixed; bottom:10px; right:10px; background:#222; color:#fff; padding:8px 12px; border-radius:8px; box-shadow:0 2px 8px rgba(0,0,0,.3); font-size:12px; font-family:var(--unitymix-font); display:none; z-index:10000; }
/* Collapsible details summary */
details summary { cursor:pointer; font-weight:500; }
</style>
{/literal}
<div id="personalizationPanel" aria-label="Appearance and Accessibility Settings">
<div id="appearanceBtn" role="button" aria-haspopup="true" aria-expanded="false">Appearance ▾</div>
<div id="appearanceDropdown" role="menu">
<!-- Accent Color -->
<label for="colorPicker">Accent Color</label>
<div class="color-presets" id="presetColors" role="group" aria-label="Color presets"></div>
<input type="color" id="colorPicker" value="#30ce7d" aria-label="Pick accent color">
<!-- Font & Size -->
<label for="fontSelect">Font</label>
<select id="fontSelect" aria-label="Select font">
<option value="theme-default">Theme Default</option>
<option value="Inter, sans-serif">Inter</option>
<option value="Arial, sans-serif">Arial</option>
<option value="Verdana, sans-serif">Verdana</option>
<option value="Roboto, sans-serif">Roboto</option>
<option value="Times New Roman, serif">Times New Roman</option>
</select>
<label for="fontSizeRange">Font Size</label>
<input type="range" id="fontSizeRange" min="12" max="20" value="14" aria-label="Font size slider">
<!-- Accessibility Options -->
<details style="margin-bottom:12px;">
<summary>Accessibility Options ▸</summary>
<div style="display:flex;flex-direction:column;gap:6px;margin-top:6px;">
<label><input type="checkbox" id="contrastPlusToggle"> Contrast+</label>
<label><input type="checkbox" id="smartContrastToggle"> Smart Contrast</label>
<label><input type="checkbox" id="spacingToggle"> Extra text spacing</label>
<label><input type="checkbox" id="motionToggle"> Pause animations</label>
<label><input type="checkbox" id="focusToggle"> Focus outlines</label>
<label><input type="checkbox" id="cursorToggle"> Big cursor</label>
<label><input type="checkbox" id="tooltipToggle"> Enhanced tooltips</label>
<label><input type="checkbox" id="voiceNavToggle"> Voice navigation</label>
</div>
</details>
<!-- Dyslexia-friendly Font -->
<label for="dysFont">Dyslexia-friendly Font</label>
<select id="dysFont" aria-label="Dyslexia friendly font">
<option value="">Off</option>
<option value="Arial, sans-serif">Arial</option>
<option value="Verdana, sans-serif">Verdana</option>
</select>
<hr style="margin:12px 0;">
<button id="resetAccessibility" style="width:100%;padding:8px;border-radius:8px;border:1px solid #ccc;background:#f5f5f5;font-weight:500;cursor:pointer;">
Reset All Accessibility Settings
</button>
</div>
</div>
<div id="voiceOverlay">Voice commands: scroll up/down, top, bottom, open appearance</div>
{literal}
<script>
document.addEventListener('DOMContentLoaded',()=>{
const root=document.documentElement;
const body=document.body;
const btn=document.getElementById('appearanceBtn');
const drop=document.getElementById('appearanceDropdown');
const colorPicker=document.getElementById('colorPicker');
const presetBox=document.getElementById('presetColors');
const fontSelect=document.getElementById('fontSelect');
const fontSizeRange=document.getElementById('fontSizeRange');
const contrastPlus=document.getElementById('contrastPlusToggle');
const smartContrast=document.getElementById('smartContrastToggle');
const spacing=document.getElementById('spacingToggle');
const motion=document.getElementById('motionToggle');
const focus=document.getElementById('focusToggle');
const cursor=document.getElementById('cursorToggle');
const tooltip=document.getElementById('tooltipToggle');
const voiceNav=document.getElementById('voiceNavToggle');
const dysFont=document.getElementById('dysFont');
const overlay=document.getElementById('voiceOverlay');
const resetBtn=document.getElementById('resetAccessibility');
// Color presets
const presets=["#30ce7d","#67d69f","#24a85f","#ff5a5f","#ffcc00","#0077ff","#9b59b6","#e67e22","#1abc9c","#f39c12"];
presets.forEach(c=>{
const d=document.createElement('div');
d.style.background=c;
d.addEventListener('click',()=>setColor(c,d));
presetBox.appendChild(d);
});
function setColor(c,el){
root.style.setProperty('--unitymix-accent',c);
root.style.setProperty('--scrollbar-thumb-color',c);
colorPicker.value=c;
localStorage.setItem('unitymixAccent',c);
document.querySelectorAll('.color-presets div').forEach(d=>d.classList.remove('active'));
if(el) el.classList.add('active');
}
// Toggle class with localStorage
function toggleClass(cls,key,el){ body.classList.toggle(cls,el.checked); localStorage.setItem(key,el.checked?'1':''); }
// Smart Contrast safe
function applySmartContrast(enable){
if(enable) body.classList.add('smart-contrast');
else body.classList.remove('smart-contrast');
}
// Pause animations
function toggleMotion(enable){
document.querySelectorAll('*').forEach(el=>{
el.style.animationPlayState=enable?'paused':'running';
el.style.transition=enable?'none':'';
});
}
// Tooltips
function enableTooltips(enable){
document.querySelectorAll('[title],[aria-label]').forEach(el=>{
if(enable){
const text = el.getAttribute('title') || el.getAttribute('aria-label');
if(text){ el.dataset.tooltip=text; el.classList.add('tooltip-element'); }
body.classList.add('tooltip-enhanced');
} else {
el.removeAttribute('data-tooltip'); el.classList.remove('tooltip-element'); body.classList.remove('tooltip-enhanced');
}
});
}
// Voice navigation
let recognition=null;
function startVoiceNav(){
if(!('webkitSpeechRecognition' in window || 'SpeechRecognition' in window)) return;
const SpeechRecognition=window.SpeechRecognition||window.webkitSpeechRecognition;
recognition=new SpeechRecognition();
recognition.continuous=true; recognition.lang='en-US';
recognition.onresult=e=>{
const cmd=e.results[e.results.length-1][0].transcript.trim().toLowerCase();
if(cmd.includes('scroll down')) window.scrollBy(0,200);
if(cmd.includes('scroll up')) window.scrollBy(0,-200);
if(cmd.includes('top')) window.scrollTo(0,0);
if(cmd.includes('bottom')) window.scrollTo(0,document.body.scrollHeight);
if(cmd.includes('open appearance')) drop.style.display='block';
};
recognition.start(); overlay.style.display='block';
}
function stopVoiceNav(){ if(recognition) recognition.stop(); overlay.style.display='none'; }
// Event listeners
colorPicker.oninput=e=>setColor(e.target.value,null);
fontSelect.onchange=e=>{ const v=e.target.value==='theme-default'?'Inter, sans-serif':e.target.value; root.style.setProperty('--unitymix-font',v); localStorage.setItem('unitymixFont',v); }
fontSizeRange.oninput=e=>{ root.style.setProperty('--unitymix-font-size',e.target.value+'px'); localStorage.setItem('unitymixFontSize',e.target.value); }
contrastPlus.onchange=e=>toggleClass('contrast-plus','a11yContrastPlus',contrastPlus);
smartContrast.onchange=e=>{ applySmartContrast(e.target.checked); localStorage.setItem('a11ySmartContrast',e.target.checked?'1':''); };
spacing.onchange=()=>toggleClass('text-spacing','a11ySpacing',spacing);
motion.onchange=e=>toggleMotion(e.target.checked);
focus.onchange=()=>toggleClass('focus-outline','a11yFocus',focus);
cursor.onchange=()=>toggleClass('big-cursor','a11yCursor',cursor);
tooltip.onchange=e=>enableTooltips(e.target.checked);
voiceNav.onchange=e=>{ if(e.target.checked){ startVoiceNav(); localStorage.setItem('a11yVoiceNav','1'); } else { stopVoiceNav(); localStorage.setItem('a11yVoiceNav',''); } };
dysFont.onchange=e=>{ const v=e.target.value||'Inter, sans-serif'; root.style.setProperty('--unitymix-font',v); localStorage.setItem('a11yDysFont',e.target.value); };
// Reset All
resetBtn.onclick=()=>{
body.classList.remove('contrast-plus','smart-contrast','text-spacing','reduce-motion','focus-outline','big-cursor','tooltip-enhanced');
root.style.setProperty('--unitymix-font','Inter, sans-serif'); fontSelect.value='theme-default';
root.style.setProperty('--unitymix-font-size','14px'); fontSizeRange.value=14;
setColor('#30ce7d', presetBox.querySelector('div'));
[contrastPlus, smartContrast, spacing, motion, focus, cursor, tooltip, voiceNav].forEach(cb=>cb.checked=false);
dysFont.value=''; stopVoiceNav();
localStorage.clear();
};
// Load saved settings
setColor(localStorage.getItem('unitymixAccent')||'#30ce7d', presetBox.querySelector('div'));
fontSelect.value=localStorage.getItem('unitymixFont')||'theme-default';
root.style.setProperty('--unitymix-font',localStorage.getItem('unitymixFont')||'Inter, sans-serif');
fontSizeRange.value=localStorage.getItem('unitymixFontSize')||14;
root.style.setProperty('--unitymix-font-size',fontSizeRange.value+'px');
contrastPlus.checked=localStorage.getItem('a11yContrastPlus')==='1';
smartContrast.checked=localStorage.getItem('a11ySmartContrast')==='1';
spacing.checked=localStorage.getItem('a11ySpacing')==='1';
motion.checked=localStorage.getItem('a11yMotion')==='1';
focus.checked=localStorage.getItem('a11yFocus')==='1';
cursor.checked=localStorage.getItem('a11yCursor')==='1';
tooltip.checked=localStorage.getItem('a11yTooltip')==='1';
voiceNav.checked=localStorage.getItem('a11yVoiceNav')==='1';
dysFont.value=localStorage.getItem('a11yDysFont')||'';
if(contrastPlus.checked) body.classList.add('contrast-plus');
if(smartContrast.checked) applySmartContrast(true);
if(spacing.checked) body.classList.add('text-spacing');
if(focus.checked) body.classList.add('focus-outline');
if(cursor.checked) body.classList.add('big-cursor');
if(tooltip.checked) enableTooltips(true);
if(voiceNav.checked) startVoiceNav();
if(dysFont.value) root.style.setProperty('--unitymix-font',dysFont.value);
// Dropdown toggle
btn.onclick=e=>{ e.stopPropagation(); drop.style.display=drop.style.display==='block'?'none':'block'; };
drop.onclick=e=>e.stopPropagation();
document.onclick=()=>{ drop.style.display='none'; };
});
</script>
{/literal}
{/if}