Media & effects
Focus Mode
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
closedrecorded state; no setterselectedrecorded state; no setterreduced motionrecorded state; no setteractiverecorded 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-opacityfocused-id
Properties
itemsfocusedIddimOpacityonFocusChange
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.tsxsha256 9cb1bbcfcdf9…
Native files
The native module and its artifacts:
src/components/focus-mode/focus-mode.tssrc/components/focus-mode/styles.csssrc/components/focus-mode/define.tsdocs/components/focus-mode.mdsite/examples/focus-mode.tstests/browser/focus-mode.spec.tsevidence/components/focus-mode.md
API
Boolean attributes follow HTML presence semantics: present means true, absent means false, and disabled="false" still means disabled.
Attributes
| Attribute | Property | Type | Default | Notes |
|---|---|---|---|---|
dim-opacity | dimOpacity | number | 0.2 | Kebab-case attribute; camelCase property. |
focused-id | focusedId | string | null | null | Kebab-case attribute; camelCase property. |
Properties
| Property | Type | Default | Notes |
|---|---|---|---|
items | FocusModeItem[] | [...] | Property-only: assign from script; not accepted as an attribute. |
focusedId | string | null | null | Property-only: assign from script; not accepted as an attribute. |
dimOpacity | number | 0.2 | Property-only: assign from script; not accepted as an attribute. |
onFocusChange | (id: string | null) => void | undefined | Property-only: assign from script; not accepted as an attribute. |
Events
| Event | Upstream prop | Payload type | Notes |
|---|---|---|---|
detent-focus-change | onFocusChange | (id: string | null) => void | Composed, 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.