Foundations
Magnifier
A loupe that really magnifies: live text scaled by the browser, so it is crisp at 4x rather than four times as blurry.
import { Magnifier } from 'omaris' Examples
Basic
Move the pointer over the fine print — or, on a touch screen, press and hold it. The glass holds a live copy of the text scaled by the browser, so the letters are as sharp at 2.4× as they are at 1×.
Clause 7.2 — The subscriber grants the operator a non-exclusive licence to cache, transcode and redistribute the submitted material for the sole purpose of delivery, for the term of the agreement and for ninety days thereafter, in any territory in which the operator maintains a point of presence.
<script lang="ts"> import { Magnifier, Text } from 'omaris';</script><div class="w-full max-w-md rounded-shape-lg border border-border p-4"> <Magnifier zoom={2.4}> <Text variant="body-sm" tone="muted"> Clause 7.2 — The subscriber grants the operator a non-exclusive licence to cache, transcode and redistribute the submitted material for the sole purpose of delivery, for the term of the agreement and for ninety days thereafter, in any territory in which the operator maintains a point of presence. </Text> </Magnifier></div> Picture
The other half of the job: a picture at 3×. Hover it, or press and hold on a phone. Vector art stays vector inside the lens, and pixelated is there for when enlarged pixels are the point.
<script lang="ts"> import { Magnifier } from 'omaris';</script><div class="w-full max-w-sm"> <Magnifier src="/docs/photos/erbil.svg" alt="Erbil citadel" zoom={3} width={160} /></div> Text loupe
The system loupe, as a component: press and hold, and a wide rounded glass comes up above the finger and follows it. trigger="press" asks for that behaviour on a mouse as well, which is how you can try it here.
Press and hold anywhere in this paragraph. The glass rides above the pointer rather than under it, so nothing you are trying to read is covered by the thing reading it — and it is real text under there, not an enlarged screenshot.
<script lang="ts"> import { Magnifier, Text } from 'omaris';</script><div class="w-full max-w-md rounded-shape-lg bg-surface-container-low p-4"> <Magnifier shape="rounded" zoom={1.9} width={220} height={72} trigger="press" hold={220}> <Text variant="body-md"> Press and hold anywhere in this paragraph. The glass rides above the pointer rather than under it, so nothing you are trying to read is covered by the thing reading it — and it is real text under there, not an enlarged screenshot. </Text> </Magnifier></div> Fluid
fluid turns the disc into a drop of glass: it eases after the pointer, stretches along its own motion and rounds out as it slows, and the picture bends at the rim with a fine colour fringe — the way a real loupe does. Sweep the pointer across quickly and watch it lag and lengthen.
Clause 7.2 — The subscriber grants the operator a non-exclusive licence to cache, transcode and redistribute the submitted material for the sole purpose of delivery, for the term of the agreement and for ninety days thereafter, in any territory in which the operator maintains a point of presence.
<script lang="ts"> import { Magnifier, Text } from 'omaris';</script><div class="w-full max-w-md rounded-shape-lg border border-border p-4"> <Magnifier zoom={2.4} fluid width={150}> <Text variant="body-sm" tone="muted"> Clause 7.2 — The subscriber grants the operator a non-exclusive licence to cache, transcode and redistribute the submitted material for the sole purpose of delivery, for the term of the agreement and for ninety days thereafter, in any territory in which the operator maintains a point of presence. </Text> </Magnifier></div> Overridden
The overridden case: a square glass sized from classes, its rim and sheen replaced, and pixelated so the enlargement shows the grid rather than smoothing it away.
<script lang="ts"> import { Magnifier } from 'omaris';</script><div class="w-full max-w-md"> <Magnifier zoom={4} shape="square" pixelated width="14rem" height="7rem" classes={{ lens: 'rounded-shape-sm ring-2 ring-primary shadow-3', sheen: 'bg-none ring-inset ring-primary/30' }} > <div class="grid grid-cols-8 gap-px rounded-shape-sm bg-border p-px"> {#each Array.from({ length: 32 }, (_, i) => i) as cell (cell)} <div class="aspect-square" class:bg-primary={cell % 3 === 0} class:bg-surface-container-high={cell % 3 !== 0} ></div> {/each} </div> </Magnifier></div> When to use it
Use it for
- Fine print people must be able to read: terms, a receipt, a footnote, a table at 11px. The lens holds live text scaled by the browser, so it is legible rather than blurry.
- A picture with detail that matters: a site plan, a scan, a product close-up, a map tile.
srctakes the picture directly. - A dense diagram or chart where the labels are smaller than the shapes.
- A sprite sheet, a QR code or anything whose pixels are the subject.
pixelatedenlarges them instead of smoothing them. - The system loupe, for a phone user picking a point precisely:
trigger="press"with a wideshape="rounded"glass above the finger.
Not for
- Looking at a photograph properly, with panning, pinching and full-screen → Image Viewer.
- Cropping or choosing a region of an image → Image Cropper.
- More detail about a thing rather than a bigger copy of it → Tooltip or Popover.
- Reading a whole document larger → let the browser zoom, or offer a type-size control. A loupe is for a spot, not a page.
- Live content: a video, a canvas, an animating chart. The copy is taken when the lens opens;
livere-takes it on DOM changes, but a<canvas>or a playing<video>does not come with it.
Do
- Keep
zoombetween 2 and 3 for text. Past about 4 the lens shows three words and the reader loses their place. - Use
lift(or leave it'auto') on touch, so the glass is not under the finger. - Give the content a real width. The copy is laid out at the source's own width, so a
w-fullsource in a narrow column magnifies what is on screen. - Leave
keyboardon. The control is invisible until focused, and it is the only way a keyboard user gets the same help. - Use
shape="rounded"with a widewidthfor text and a circle for pictures.
Don't
- Wrap an interactive region and expect the copy to be interactive. It is inert: nothing inside the lens is focusable and no handler fires twice.
- Turn
liveon for a page that mutates constantly. It re-copies the subtree on every change. - Put one inside a scrolling column without
trigger="press". A hover lens on a touch screen fires on the first tap of a scroll. - Magnify something that could simply be bigger. The lens is for content that has to stay small.
Quick reference
shape circle(default)roundedsquare
API
Magnifier
A loupe that really magnifies — text included.
Wrap anything. A pointer over it (or a press and hold on a touch screen) raises a lens that follows the finger and shows what is under it, larger.
It is not a zoomed screenshot. The lens holds a live copy of the content, scaled with a CSS transform, so the browser lays the text out and rasterises it at the magnified size: glyphs are as crisp at 4× as they are at 1×, and vector art, borders and shadows come with them. A background-image trick can only ever enlarge pixels, which is why the usual "product zoom" turns type to mush.
The copy is taken when the lens opens and is inert — no ids collide, no handler fires twice, nothing inside it is focusable. Content that changes while the lens is up wants live, which re-takes it as the DOM changes.
On a touch screen it behaves like the system loupe: press and hold for hold ms to summon it, then drag. The glass rides above the finger and — the part that is usually got wrong — it looks where it sits, not at what the finger is covering. Aiming a loupe at the one spot you cannot see is guesswork; here the thing under the glass is the thing you are inspecting, and the finger stays out of the picture. A finger that travels before the hold is up was scrolling, and is left alone — so the page still scrolls normally over a magnified region. On a mouse the lens sits on the pointer and the system cursor gets out of its way.
Keyboard users get it too — a control that appears on focus opens the lens, the arrow keys walk it around, and Escape puts it away.
`fluid` makes it a drop of glass. The lens eases after the pointer instead of sitting on it, stretches a little along its own motion and rounds out again as it slows, and the picture bends at the rim — a radial displacement, with the red and blue bent by slightly different amounts so a fine colour fringe rides the edge, the way it does on a real loupe. The refraction is an SVG filter on the glass alone, not on the copy, so it costs a lens-sized area per frame whatever is under it.
import { Magnifier } from 'omaris' <Magnifier zoom={2.5}> <p class="text-body-sm">Terms nobody can read at this size…</p></Magnifier><Magnifier src="/map.png" alt="Site plan" shape="circle" zoom={3} /> Parts
Every element this renders is reachable from outside. Pass Tailwind for one part as classes={{ part: '…' }}; class covers the root and is merged last, so it always wins.
root- The frame around the content. Position lives here, and so does the long press:
touch-pan-yleaves the page scrolling until the lens is actually up, and the callout suppression stops the OS offering to select the text under a finger that is trying to magnify it. source- The content, exactly as it would have rendered without the lens.
image- A picture passed as
src, for the common case. lens- The glass. Round by default, clipped, elevated, with a rim that reads as a bevel rather than a border.
glass- The pane between the picture and the rim. The refraction filter goes here, clipped to the glass, so the sheen and the bevel above it stay sharp and the shadow outside it is not bent.
stage- The scaled copy. Everything that makes the magnification happen.
sheen- The body of the glass. A curved piece of glass is darker at its rim than at its middle — that vignette, more than any highlight, is what stops a lens reading as a circular crop. On top of it a soft glare falls from the top-left and a fainter one bounces back off the bottom edge, both blurred, because a hard-edged highlight reads as a sticker stuck to the screen.
rim- The bevel, over the glare so the edge stays crisp: a bright hairline just inside the rim, a darker one a few pixels further in, and a hairline on the rim itself.
control- The keyboard affordance — invisible until it is focused.
Props
zoom Defaults to 2
number How much bigger. 2 is a reading aid, 4 inspects a detail.
shape Defaults to 'circle'
MagnifierShape The glass's outline.
circleroundedsquare
width Defaults to 132
number | string Lens width — a number is px, a string is any CSS length.
height number | string Lens height. Defaults to width, which is what makes a circle round.
trigger Defaults to 'auto'
MagnifierTrigger What summons it. auto — the default — reads the pointer: a mouse opens it on hover, a finger on a press and hold.
hold Defaults to 320
number How long a touch has to hold still before the lens appears, in ms.
lift Defaults to 'auto'
number | 'auto' How far above the pointer the lens both sits and looks, in px, so a finger never covers what it is magnifying. 'auto' lifts it for touch only, by enough to clear the glass.
hideCursor Defaults to true
boolean Hide the system cursor while the lens is up, so nothing is drawn over the magnified content. false keeps the arrow.
open bindableDefaults to false
boolean Whether the lens is up. Bindable, so it can be driven from outside.
disabled Defaults to false
boolean Turn it off without unwrapping the content.
live Defaults to false
boolean Re-copy the content as it changes, for something that moves under the glass.
pixelated Defaults to false
boolean Enlarge the pixels rather than smoothing them.
fluid Defaults to false
boolean A drop of glass rather than a disc: the lens eases after the pointer, stretches along its motion, and refracts at the rim.
refraction Defaults to 26
number How hard the rim bends the picture with fluid, in px.
keyboard Defaults to true
boolean Offer the lens to the keyboard as well.
label Defaults to 'Magnify'
string The name of the keyboard control, and the lens's accessible label.
src string A picture to magnify, for the common case. Children work too.
alt string class string classes MagnifierClasses Per-part Tailwind overrides. class still covers the root.
children Snippet