Please disable your ad blocker.
The only ads on this site are discreet (sidebar on desktop, below the content on mobile).They do not interfere with reading and may even offer you benefits (discounts, free months…).
HTML/CSS/JS: create an option to change text size on the fly
Difficulty
This accessible tutorial will allow you to copy/paste the code and customize it according to your needs.
Having a personal or professional blog/website is great, but having one that’s readable by everyone is even better. Unfortunately, most of the web still builds sites with static text like 12 or 14px. The result: on 4K screens, or if the visitor has vision issues, you have to zoom and distort the site’s design just to be able to read something.
I’m offering you a guide to fix that once and for all, just like I did on my own website, as you can see in the Site options at the top right of every page.
Small bonus: my approach is persistent and respectful of privacy.
We’ll go through the HTML, CSS, and JavaScript needed to set up this control.
The HTML
For the HTML code, I chose not to use an <input type="range"> for the simple reason that they tend to move far too quickly. Since this option is meant to improve accessibility, that would be counterproductive.
<div id="fontsize-control">
<div class="fontsize-control-wrapper">
<span aria-label="Text size" class="fa-solid fa-text-height"></span>
<label for="customfontsize">Change text size: </label>
<input aria-describedby="fontsize-help" id="customfontsize" inputmode="decimal" min="1" max="2" step="0.1" type="text" value="1.2"><span class="unit">rem.</span>
<button aria-label="Reset size" aria-hidden="true" class="fa-solid fa-rotate-left" id="reset-fontsize" title="Reset size"></button>
<span aria-label="Help about text size" class="tooltip-wrapper" tabindex="0">
<span aria-hidden="true" class="fa-regular fa-circle-question help-icon"></span>
<span aria-live="polite" class="tooltip-content" role="tooltip">Enter a value between 1 and 2rem, then press <kbd>Enter</kbd>.<br>
You can also use the <kbd>↑</kbd> / <kbd>↓</kbd> arrows in the text field.</span>
</div>
</div>
HTML Code Explanation
<div id="fontsize-control">: Main container for the text size control.<div class="fontsize-control-wrapper">: Sub-container that aligns elements (icon, label, field, unit, help).<span aria-label="Taille du texte" class="fa-solid fa-text-height"></span>: Visual icon (Font Awesome) representing text size, with a description for screen readers.<label for="customfontsize">Modifier la taille du texte : </label>: Label associated with the text field, linked via theforattribute.<input ... id="customfontsize" ... type="text" value="1.2">: Text field where the user enters the size in rem.aria-describedby: reference to the help text.inputmode="decimal": numeric keyboard on mobile.min: minimum allowed value.max: maximum allowed value.step: increment between min and max.value="1.2": default value.
<span class="unit">rem.</span>: Displays the unit right after the field for clarity.<button ... id="reset-fontsize" title="Réinitialiser la taille"></button><button ...>: Interactive element used to reset the text size to its default value.aria-label="Réinitialiser la taille": Provides a clear description for screen readers so the button is understandable even without the icon.aria-hidden="true": Hides the decorative icon from screen readers, since the useful information is already provided byaria-label.class="fa-solid fa-rotate-left": Applies the Font Awesome icon representing a circular arrow (“reset”).id="reset-fontsize": Unique identifier used to bind the button to the JavaScript script.title="Réinitialiser la taille": Shows a tooltip on hover, useful for sighted users.
<span aria-label="Aide sur la taille du texte" class="tooltip-wrapper" tabindex="0">: Tooltip container, keyboard-accessible thanks totabindex="0".<span aria-hidden="true" class="fa-regular fa-circle-question help-icon"></span>: “?” icon that triggers the tooltip, hidden from screen readers because the help is provided elsewhere.<span aria-live="polite" class="tooltip-content" role="tooltip"> ... </span>: Help text displayed on hover or focus.role="tooltip": explicit role.aria-live="polite": polite announcement for screen readers.- Contains keyboard instructions (Enter, ↑, ↓) highlighted with
<kbd>.
The CSS
In this CSS, we first define variables for text size. --customfontsize is the only one that can be modified by the visitor. Then we define --font-size-large for emphasis, --font-size-normal for “normal” content, and --font-size-small for “small” content such as notes.
They help keep consistency across the entire site.
Then we apply these variables to the body, to the .important class, and to the .note class.
Finally, we style the text size control (#fontsize-control) and its tooltip so it’s clear, accessible, and pleasant to use.
/* ===========================
General variables
=========================== */
:root {
--customfontsize: 1.2rem; /* default value editable by the user */
--font-size-large: calc(var(--customfontsize) + 0.3rem); /* emphasized text */
--font-size-normal: var(--customfontsize); /* standard text */
--font-size-small: calc(var(--customfontsize) - 0.2rem); /* smaller text (notes) */
}
/* ===========================
Applying variables
=========================== */
/* Default site text size */
body {
font-size: var(--font-size-normal);
}
/* Emphasized text */
.important {
font-size: var(--font-size-large);
}
/* Secondary text / notes */
.note {
font-size: var(--font-size-small);
}
/* ===========================
Text size control
=========================== */
#fontsize-control,
#fontsize-control:hover {
padding: 0 20px; /* constant inner spacing */
}
#fontsize-control:hover {
background-color: #1e1e1e; /* hover contrast */
color: #eeeeee;
}
#fontsize-control {
display: block;
width: 100%;
}
#fontsize-control * {
width: auto; /* prevents children from inheriting a fixed width */
}
#fontsize-control label {
margin-left: .3em; /* small space between icon and label */
}
#fontsize-control input {
box-sizing: border-box; /* includes padding/border in width */
color: #101010; /* forced color for readability */
cursor: text;
line-height: 1.5;
margin-left: .3em;
padding: 0;
text-align: center; /* centered value in the field */
width: 5em; /* fixed width for input */
}
/* Wrapper to align icon, label and input */
.fontsize-control-wrapper {
align-items: center;
display: inline-flex;
height: 60px;
width: 100%;
}
.fontsize-control-wrapper .unit {
color: #eeeeee;
font-size: var(--font-size-normal);
margin-left: .3em;
}
#reset-fontsize {
cursor: pointer;
}
/* ===========================
Help tooltip
=========================== */
#fontsize-control .tooltip-wrapper {
display: inline-block;
margin-left: .4em;
position: relative; /* required to position the content */
}
#fontsize-control .help-icon {
cursor: help; /* “?” icon with help cursor */
}
/* Show tooltip on focus or hover */
#fontsize-control .tooltip-wrapper:focus .tooltip-content,
#fontsize-control .tooltip-wrapper:hover .tooltip-content {
display: block;
opacity: 1;
transform: translateY(0);
visibility: visible;
}
/* Tooltip content styling */
#fontsize-control .tooltip-content {
bottom: 125%; /* position above the icon */
background-color: #1e1e1e;
border-radius: 16px;
box-shadow: 8px 8px 8px rgba(255, 0, 0, 0.5),
-8px -8px 8px rgba(255, 0, 0, 0.5),
8px -8px 8px rgba(255, 0, 0, 0.5),
-8px 8px 8px rgba(255, 0, 0, 0.5); /* red glow effect */
color: #eeeeee;
font-size: var(--font-size-small);
opacity: 0; /* hidden by default */
padding: .6em;
position: absolute;
right: -.5em;
transform: translateY(10px); /* upward animation */
transition: opacity .3s ease, transform .3s ease; /* smooth effect */
visibility: hidden;
white-space: normal;
width: max-content;
z-index: 10; /* above everything else */
}
The JS
This JavaScript script manages the text size control.
It initializes the value from local storage (localStorage), updates the CSS variable --customfontsize, and allows the user to change the size via the text field.
Validation happens with the Enter key, the ↑ / ↓ arrows, or when leaving the field (blur).
The reset button returns to the default size, and I also associated the keyboard shortcut ALT+R with it.
document.addEventListener('DOMContentLoaded', () => {
const inputNumber = document.getElementById('customfontsize');
const defaultSize = 1.2;
// init
const saved = localStorage.getItem('customfontsize');
const initial = saved ? parseFloat(saved) : defaultSize;
const resetBtn = document.getElementById('reset-fontsize');
const root = document.documentElement;
inputNumber.value = initial;
root.style.setProperty('--customfontsize', initial + 'rem');
// shared function
function applyFontsize(value) {
root.style.setProperty('--customfontsize', value + 'rem');
localStorage.setItem('customfontsize', value);
inputNumber.value = value;
}
// reset button
resetBtn.addEventListener('click', () => {
applyFontsize(defaultSize);
});
// global shortcut Alt+R
document.addEventListener('keydown', (e) => {
if (e.altKey && e.key.toLowerCase() === 'r') {
e.preventDefault();
applyFontsize(defaultSize);
}
});
// text field: don't force during typing
inputNumber.addEventListener('input', () => {
// let the user type freely
});
// validation with Enter + arrow keys
inputNumber.addEventListener('keydown', (e) => {
let current = parseFloat(inputNumber.value.replace(',', '.')) || defaultSize;
if (e.key === 'Enter') {
e.preventDefault();
let raw = inputNumber.value.replace(',', '.');
let value = parseFloat(raw);
if (!isNaN(value) && value >= 1 && value <= 2) {
applyFontsize(value);
} else {
inputNumber.value = localStorage.getItem('customfontsize') || defaultSize;
}
}
if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
e.preventDefault();
let step = e.key === 'ArrowUp' ? 0.1 : -0.1;
let next = Math.min(2, Math.max(1, current + step));
applyFontsize(Number(next.toFixed(1)));
}
});
// final validation on blur
inputNumber.addEventListener('blur', () => {
let raw = inputNumber.value.replace(',', '.');
let value = parseFloat(raw);
if (!isNaN(value) && value >= 1 && value <= 2) {
applyFontsize(value);
} else {
// if invalid → restore last known value
inputNumber.value = localStorage.getItem('customfontsize') || defaultSize;
}
});
});
JavaScript Explanation
DOMContentLoaded: waits until the page is loaded before executing the script.const root = document.documentElement: targets the root<html>element to update the CSS variable--customfontsize.localStorage.getItem(): retrieves the last value saved by the user.applyFontsize(): shared function that applies the size to the site and saves it.const resetBtn: retrieves the reset button to attach behavior to it.resetBtn.addEventListener('click'): resets the size to the default value when clicking the button.document.addEventListener('keydown'): adds a global Alt+R keyboard shortcut to reset the size.input: allows the user to type freely without immediate validation.keydown:- Enter : validates the value if it’s between 1 and 2.
- ↑ / ↓ : increases or decreases the size in 0.1 steps, rounded to one decimal place.
blur: when leaving the field, checks the value and restores the last known value if invalid.- Comma handling:
replace(',', '.')allows inputs using a comma as decimal separator.
Comments