Detent web components
By Shane Logsdon

Media & effects

Focus Mode

Implemented

Atmospheric UI isolation dimming background distractions without layout shifts

detent-focus-mode

Port status
Catalog status
implemented
Origin
upstream
Phase
4
Wave
phase-4-pointer
Group
Media & effects

7.8 KB gzip for the class module

Variants

Select a card's focus control (or its "Focus View" action) to isolate it: the other cards dim to dim-opacity without moving the grid, the banner names the isolated card and Escape or Exit clears it. This instance uses a documentation dataset that shows category, metric and delta; every value is rendered as text and the selection is reported through detent-focus-change.

Recorded states

  • closed recorded state; no setter
  • selected recorded state; no setter
  • reduced motion recorded state; no setter
  • active recorded state; no setter

Usage

Import

import '/components/focus-mode/define.js';

// or register it yourself
import { DetentFocusMode } from '/components/focus-mode/focus-mode.js';

Attributes

  • dim-opacity
  • focused-id

Properties

  • items
  • focusedId
  • dimOpacity
  • onFocusChange

Slots

No slots are recorded for this component.

Events

  • detent-focus-change — {}

Example

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

<detent-focus-mode
  dim-opacity="0.2"
  focused-id
></detent-focus-mode>

Property-only values

const element = document.querySelector('detent-focus-mode');
if (element !== null) {
  // items: FocusModeItem[]
  element.items = [];
  // onFocusChange: (id: string | null) => void
  element.onFocusChange = () => {};
}

Source

The pinned upstream source for this row:

  • src/components/ui/FocusMode.tsx sha256 9cb1bbcfcdf9…

Native files

The native module and its artifacts:

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

API

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

Attributes

Attributes
AttributePropertyTypeDefaultNotes
dim-opacitydimOpacitynumber0.2Kebab-case attribute; camelCase property.
focused-idfocusedIdstring | nullnullKebab-case attribute; camelCase property.

Properties

Properties
PropertyTypeDefaultNotes
itemsFocusModeItem[][...]Property-only: assign from script; not accepted as an attribute.
focusedIdstring | nullnullProperty-only: assign from script; not accepted as an attribute.
dimOpacitynumber0.2Property-only: assign from script; not accepted as an attribute.
onFocusChange(id: string | null) => voidundefinedProperty-only: assign from script; not accepted as an attribute.

Events

Events
EventUpstream propPayload typeNotes
detent-focus-changeonFocusChange(id: string | null) => voidComposed, bubbles; fires only for a user action.

Slots

No slots are recorded for this component.

CSS customisation

  • React callback props become typed composed CustomEvents; programmatic state assignment emits none.
  • Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
  • `lucide-react` icon components replaced with local SVG assets (ISC notice retained).
  • React state/hooks replaced by element-owned state with pre-upgrade property preservation.
  • FocusModeItem.content is rendered as text, not a ReactNode; the default items carry the written copy of the upstream demo content.
  • While a card is selected the other cards are also inert, not only pointer-events:none, so keyboard focus cannot land on content dimmed to dim-opacity.
  • Card and control borders use #6b6b6b instead of the upstream #1F1F1F hairline to reach the WCAG 1.4.11 3:1 non-text ratio.
  • dimOpacity clamps to 0..1 and warns once on non-numeric input; a selection whose item disappears is cleared without an event.

What it does

An inline dashboard focus mode: selecting one card dims every other card to dim-opacity without moving the grid, raises a banner that names the isolated card and offers an exit control, and listens for Escape.

Upstream behaviour
  • Subtle background opacity dampening bringing selected components into focus
  • Zero layout shift architecture keeping existing dashboard grid geometry intact
  • Tactile spring scaling and border elevation on active focused target
  • Keyboard Escape key listener and explicit exit controls
  • Reduced motion support with instant opacity transitions
Differences from upstream
  • React callback props become typed composed CustomEvents; programmatic state assignment emits none.
  • Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
  • `lucide-react` icon components replaced with local SVG assets (ISC notice retained).
  • React state/hooks replaced by element-owned state with pre-upgrade property preservation.
  • FocusModeItem.content is rendered as text, not a ReactNode; the default items carry the written copy of the upstream demo content.
  • While a card is selected the other cards are also inert, not only pointer-events:none, so keyboard focus cannot land on content dimmed to dim-opacity.
  • Card and control borders use #6b6b6b instead of the upstream #1F1F1F hairline to reach the WCAG 1.4.11 3:1 non-text ratio.
  • dimOpacity clamps to 0..1 and warns once on non-numeric input; a selection whose item disappears is cleared without an event.
Previous: Action Dock Next: Form

Docs

Components

Project