Detent web components
By Shane Logsdon

Media & effects

Density Lens

Implemented

Cursor-tracking magnification & detail inspection lens

detent-density-lens

Port status
Catalog status
implemented
Origin
upstream
Phase
4
Wave
phase-4-pointer
Group
Media & effects
circle lens Hover to inspect the demo build summary. The magnified readout comes from renderLensContent.

4.5 KB gzip for the class module · inlines the shared spring

Variants

Hover (or tap, on touch) any panel on this page: the lens follows the pointer with the pinned spring (stiffness 450, damping 28, mass 0.2) and eases from opacity 0 / scale 0.8 to opacity 1 / scale 1. The three recorded lensShape variants only change the corner geometry: circle is a 50 % radius, oval 40 px and square 16 px on the same square box. Nothing is captured and no layout moves.

oval lens, 2.5× The same panel with a 40 px corner radius and a different zoom factor.
square lens A 16 px corner radius, the third recorded variant.
glass only No renderer: the lens is the documented glass highlight over the region under the pointer.

The square lens opts out of the border through the property (showBorder = false). With prefers-reduced-motion: reduce the lens appears at the pointer in its final state and schedules no frame.

Recorded states

  • hover recorded state; no setter

Usage

Import

import '/components/density-lens/define.js';

// or register it yourself
import { DetentDensityLens } from '/components/density-lens/density-lens.js';

Attributes

  • lens-shape
  • lens-size
  • show-border
  • zoom-scale

Properties

  • renderLensContent

Slots

  • default

Events

No events are recorded for this component.

Example

Runnable example: site/examples/density-lens.ts. The snippet below is generated from the catalogued attributes.

<detent-density-lens
  lens-shape="circle"
  lens-size="150"
  show-border
  zoom-scale="2"
></detent-density-lens>

Property-only values

const element = document.querySelector('detent-density-lens');
if (element !== null) {
  // renderLensContent: (pos: { x: number, y: number, scale: number }) => ReactNode
  element.renderLensContent = () => {};
}

Source

The pinned upstream source for this row:

  • src/components/ui/DensityLens.tsx sha256 9090e41248b8…

Native files

The native module and its artifacts:

  • src/components/density-lens/density-lens.ts
  • src/components/density-lens/styles.css
  • src/components/density-lens/define.ts
  • docs/components/density-lens.md
  • site/examples/density-lens.ts
  • tests/browser/density-lens.spec.ts
  • evidence/components/density-lens.md

API

Boolean attributes follow HTML presence semantics: present means true, absent means false, and disabled="false" still means disabled.

Attributes

Attributes
AttributePropertyTypeDefaultNotes
lens-shapelensShape'circle' | 'oval' | 'square''circle'Kebab-case attribute; camelCase property.
lens-sizelensSizenumber150Kebab-case attribute; camelCase property.
show-bordershowBorderbooleantruePresence semantics: present means true, absent means false.
zoom-scalezoomScalenumber2Kebab-case attribute; camelCase property.

Properties

Properties
PropertyTypeDefaultNotes
renderLensContent(pos: { x: number, y: number, scale: number }) => ReactNodeundefinedProperty-only: assign from script; not accepted as an attribute.

Events

No custom events are recorded for this component.

Slots

Slots
SlotNotes
defaultConsumer content; rendered through a slot, never re-created.

CSS customisation

  • React `children`/`ReactNode` props become DOM slots; caller content stays light DOM.
  • Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
  • React state/hooks replaced by element-owned state with pre-upgrade property preservation.
  • A custom element may not clone or re-serialise slotted nodes (PC-11.2), so the lens does not duplicate children: the default path is the documented glass highlight and the 2x magnified view comes from the recorded renderLensContent property ({ x, y, scale }).
  • Lens border and ring colours were corrected from #1f1f1f / #4a4a4a (1.05:1 and 1.8:1 on white) to #6b7280 (accessibility correction).
  • Touch inspects directly on pointerdown; upstream listened to mouse events only, leaving the recorded touch fallback unimplemented.

What it does

A hover lens that follows the pointer, showing a consumer-supplied magnified rendering or a glass highlight.

Upstream behaviour
  • Subtle spring lag cursor tracking for realistic tactile lens momentum
  • Automatic coordinate interpolation with 2x magnification or custom overlay renderers
  • Configurable lens geometry (circle, oval, rounded square), border glow, and zoom scale
  • Zero layout shifts and smooth GPU-accelerated backdrop blur
Differences from upstream
  • React `children`/`ReactNode` props become DOM slots; caller content stays light DOM.
  • Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
  • React state/hooks replaced by element-owned state with pre-upgrade property preservation.
  • A custom element may not clone or re-serialise slotted nodes (PC-11.2), so the lens does not duplicate children: the default path is the documented glass highlight and the 2x magnified view comes from the recorded renderLensContent property ({ x, y, scale }).
  • Lens border and ring colours were corrected from #1f1f1f / #4a4a4a (1.05:1 and 1.8:1 on white) to #6b7280 (accessibility correction).
  • Touch inspects directly on pointerdown; upstream listened to mouse events only, leaving the recorded touch fallback unimplemented.
Previous: Date Picker Next: Dependency Trace

Docs

Components

Project