Exclusifbêta
Reading Ruler <mv-reading-ruler>
Guide de lecture pour les textes longs (conditions, articles, documentation) qui aide les lecteurs dyslexiques, atteints de TDAH ou malvoyants à ne pas perdre leur ligne : une bande d’exactement une ligne de haut se cale sur les vraies boîtes de ligne du texte sous le pointeur, le reste étant légèrement assombri (bande, masque ou soulignement). Contrairement aux guides et aux extensions qui ne suivent que le pointeur, il suit aussi la lecture au clavier (le curseur de texte, le lien qui a le focus, ou Haut/Bas et J/K ligne par ligne), et il ne bloque jamais les clics, la sélection ni le curseur système.
| Catégorie | Curseurs |
|---|---|
| Type | Web Component (<mv-reading-ruler>) |
| Statut | bêta |
| Keywords | exclusive, light, reading, ruler, reading-guide, line-focus, dyslexia, adhd, low-vision, accessibility, article, legal, docs |
When to use
- Terms of service, policies or contracts are long and dense, and readers need help keeping their place
- An article or docs page wants an accessibility option for readers with dyslexia, ADHD or low vision
- A reading preference toggle should be remembered across visits without an account or a browser extension
Avoid when
- Readers need to see the article structure and jump between sections → use Reading Map instead
- The goal is a decorative highlight that follows the pointer over cards or buttons → use Cursor Snap instead
- The text is short or in an editor; the ruler is for reading, not for typing
Installation
node scripts/add.mjs reading-ruler --out ./src/marvelousAgent IA avec le serveur MCP de Marvelous UI : install_components({ slugs: ["reading-ruler"], target_dir: "<absolute path>/src/marvelous", framework: "react" }).
Fichiers copiés (dépendances comprises) : tokens/tokens.css, core/base.css, core/dom.js, core/element.js, core/motion.js, components/reading-ruler/reading-ruler.js, components/reading-ruler/reading-ruler.css.
Utilisation
Démarrage rapide, le balisage minimal qui fonctionne :
<button data-ruler-toggle>Reading ruler</button>
<mv-reading-ruler><article>…</article></mv-reading-ruler>Balisage de référence : partez de celui-ci et personnalisez-le avec les attributs, data-* et les variables CSS :
<div id="mv-reading-ruler-demo" style="width:min(100%,44rem);margin-inline:auto;display:grid;gap:.75rem">
<div style="display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:.75rem">
<label class="mv-choice" style="gap:.6rem">
<input type="checkbox" role="switch" class="mv-switch" data-ruler-toggle="rr-terms" checked>
<span class="mv-choice-text"><span class="mv-choice-title">Reading ruler</span></span>
</label>
<div style="display:flex;align-items:center;gap:.5rem;font-size:.8rem;color:var(--mv-fg-muted)">
<label for="rr-mode">Style</label>
<select class="mv-select" id="rr-mode" style="width:auto">
<option value="band">Band</option>
<option value="mask">Mask</option>
<option value="underline">Underline</option>
</select>
</div>
</div>
<mv-reading-ruler id="rr-terms" enabled style="padding:1.25rem 1.5rem;border:1px solid var(--mv-border);border-radius:var(--mv-radius-lg);background:var(--mv-bg)">
<article style="font-size:.95rem;line-height:1.7;color:var(--mv-fg)">
<p style="margin:0 0 .25rem;font-size:.75rem;color:var(--mv-fg-muted)">Last updated September 24, 2026</p>
<h3 style="margin:0 0 .5rem;font-size:1.15rem">4. Subscriptions and billing</h3>
<p style="margin:0 0 .75rem">Your plan renews automatically at the end of each billing period at the price shown on your <a href="#" style="color:var(--mv-accent-fg)">account page</a>, currently $12.00 per month or $120.00 per year. We will email you at least 14 days before any price change, and the new price applies from the next period only.</p>
<p style="margin:0 0 .75rem">You can cancel at any time from Settings › Billing. Cancellation takes effect at the end of the current period; you keep access until then, and no partial refunds are issued except where the law of your country requires them.</p>
<h3 style="margin:0 0 .5rem;font-size:1.15rem">5. Your content</h3>
<p style="margin:0">You keep every right to the documents you upload. You grant us a limited license to store, process and display them only to run the service for you and the people you choose to share them with.</p>
</article>
</mv-reading-ruler>
<p style="margin:0;font-size:.8rem;color:var(--mv-fg-muted)">Move the pointer over the text, or focus it and press <kbd class="mv-kbd">↓</kbd> <kbd class="mv-kbd">↑</kbd> (or <kbd class="mv-kbd">J</kbd> <kbd class="mv-kbd">K</kbd>) to step line by line.</p>
<script type="module">
const root = document.getElementById("mv-reading-ruler-demo");
root.querySelector("#rr-mode").addEventListener("change", (e) => {
root.querySelector("#rr-terms").setAttribute("mode", e.target.value);
});
</script>
</div>API
Attributes
| Name | Type | Default | Description |
|---|---|---|---|
enabled | boolean | Turns the ruler on. Toggles set it for you; without a toggle, set it yourself. | |
mode | band | mask | underline | band | band: tinted band and gently dimmed text around it. mask: only the line stays at full strength (stronger dim). underline: an accent line under the current line, nothing dimmed. |
storage-key | string | Opt-in: remembers the reader's on/off choice in localStorage under this key (stored as "on" | "off"; only choices made with a toggle are saved). | |
data-ruler-toggle (on your control) | (empty) | ruler id | Any button or checkbox anywhere on the page. Empty: controls the ruler it sits in, else the first ruler of the page; with a value: the ruler with that id. Buttons get aria-pressed, checkboxes get checked, both get aria-controls. | |
data-stepped | boolean | Set by the component after a keyboard step, until the text loses focus (hides the hint). |
Properties
| Name | Type | Description |
|---|---|---|
strings | { label, hint } | Accessible name of the text while the ruler is on ("Text with reading ruler") and keyboard hint ("↑ ↓ or J K: move line by line"). English defaults, each overridable. |
Methods
| Name | Description |
|---|---|
refresh() | Re-measures the line boxes. Automatic on resize, content changes and web font loads. |
Events
| Name | Description |
|---|---|
mv-toggle | A toggle is about to turn the ruler on or off. detail = { enabled }. Cancelable: preventDefault() keeps the current state (the toggle is reset). |
CSS classes
| Name | Description |
|---|---|
mv-reading-ruler-layer | Overlay (aria-hidden, pointer-events: none, user-select: none); data-placed once positioned. |
mv-reading-ruler-band | The band, one line tall (line box plus padding). |
mv-reading-ruler-hint | Keyboard hint that rides the band, shown on keyboard focus until the first step. |
CSS variables
| Name | Default | Description |
|---|---|---|
--mv-reading-ruler-color | accent at 9% | Band tint (band mode). |
--mv-reading-ruler-edge | accent at 38% | Hairlines above and below the band. |
--mv-reading-ruler-dim | --mv-bg at 38% (mask: 68%) | Veil over the rest of the text. Match it to the text's background color. |
--mv-reading-ruler-padding | 0.2em | Extra height above and below the line box. |
--mv-reading-ruler-underline | var(--mv-accent) | Underline color (underline mode). |
--mv-reading-ruler-thickness | 2px | Underline thickness. |
Accessibility
The overlay is aria-hidden with pointer-events: none and user-select: none, so links, buttons and text selection work exactly as without it, and the system cursor is never hidden or replaced. Pointer following is on only under (hover: hover) and (pointer: fine); on touch a tap places the ruler on that line. While on, the text becomes a focusable region (tabindex=0, role=region, aria-label "Text with reading ruler", aria-keyshortcuts) whose description is the keyboard hint, also shown on keyboard focus: ArrowUp/ArrowDown or J/K step line by line and scroll the line into view (ignored with Ctrl, Alt or Meta, inside fields and editable text). Focusing a link or button inside moves the ruler to its line, and so does moving the caret (caret browsing, keyboard selection). Toggles you place get aria-pressed or checked and aria-controls; the choice is remembered only with storage-key (wrapped in try/catch). Reduced motion (OS or data-motion="reduce"): the band jumps without easing and scrolling is instant. Forced colors: no dimming, the band becomes a 2px Highlight outline. Nothing is announced; the attributes set by the component are removed when it is turned off.