Feedback & status
Status Island
Continuous physical morphing island with spring layout physics
detent-status-island
Port status
- Catalog status
- implemented
- Origin
- upstream
- Phase
- 3
- Wave
- phase-3-overlays
- Group
- Feedback & status
9.6 KB gzip for the class module · inlines the shared spring
Variants
The pill morphs between collapsed, expanded, profile and share. Tab walks the controls, Escape collapses from any open state, and an outside press collapses it too.
No state change yet.
Recorded states
closedrecorded state; no setterexpandedrecorded state; no settercollapsedrecorded state; no setterdark themerecorded state; no setterreduced motionrecorded state; no setterprofilerecorded state; no settersharerecorded state; no setter
Usage
Import
import '/components/status-island/define.js';
// or register it yourself
import { DetentStatusIsland } from '/components/status-island/status-island.js';
Attributes
avataravatar-altdefault-statedescriptiongreetingid-prefixnamejob-titlestatestatus-text
Properties
metadataonStateChangesocials
Slots
defaultprofile-contentshare-content
Events
detent-state-change— {}
Example
Runnable example: site/examples/status-island.ts. The snippet below is generated from the catalogued attributes.
<detent-status-island
avatar
avatar-alt
default-state="collapsed"
description="Building thoughtful interfaces..."
greeting="Hello, I am [name]"
id-prefix
name="Suraj Maurya"
job-title="Frontend Developer"
state
status-text="Available for work"
></detent-status-island>
Property-only values
const element = document.querySelector('detent-status-island');
if (element !== null) {
// metadata: DynamicIslandMetadataItem[]
element.metadata = [];
// socials: DynamicIslandSocials
element.socials = undefined;
// onStateChange: (state: DynamicIslandState) => void
element.onStateChange = () => {};
}
Source
The pinned upstream source for this row:
src/components/ui/DynamicIsland.tsxsha256 87cc23102b95…
Native files
The native module and its artifacts:
src/components/status-island/status-island.tssrc/components/status-island/styles.csssrc/components/status-island/define.tsdocs/components/status-island.mdsite/examples/status-island.tstests/browser/status-island.spec.tsevidence/components/status-island.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 |
|---|---|---|---|---|
avatar | avatar | string | undefined | Kebab-case attribute; camelCase property. |
avatar-alt | avatarAlt | string | undefined | Kebab-case attribute; camelCase property. |
default-state | defaultState | 'collapsed' | 'expanded' | 'profile' | 'share' | 'collapsed' | Kebab-case attribute; camelCase property. |
description | description | string | 'Building thoughtful interfaces...' | Kebab-case attribute; camelCase property. |
greeting | greeting | string | 'Hello, I am [name]' | Kebab-case attribute; camelCase property. |
id-prefix | — | — | — | Recorded in the catalog as an attribute. |
name | name | string | 'Suraj Maurya' | Kebab-case attribute; camelCase property. |
job-title | jobTitle | string | 'Frontend Developer' | Kebab-case attribute; camelCase property. |
state | state | 'collapsed' | 'expanded' | 'profile' | 'share' | undefined | Kebab-case attribute; camelCase property. |
status-text | statusText | string | 'Available for work' | Kebab-case attribute; camelCase property. |
Properties
| Property | Type | Default | Notes |
|---|---|---|---|
metadata | DynamicIslandMetadataItem[] | undefined | Property-only: assign from script; not accepted as an attribute. |
onStateChange | (state: DynamicIslandState) => void | undefined | Property-only: assign from script; not accepted as an attribute. |
socials | DynamicIslandSocials | 5 default links | Property-only: assign from script; not accepted as an attribute. |
Events
| Event | Upstream prop | Payload type | Notes |
|---|---|---|---|
detent-state-change | onStateChange | (state: DynamicIslandState) => void | Composed, bubbles; fires only for a user action. |
Slots
| Slot | Notes |
|---|---|
default | Consumer content; rendered through a slot, never re-created. |
profile-content | Consumer content; rendered through a slot, never re-created. |
share-content | Consumer content; rendered through a slot, never re-created. |
CSS customisation
- React `children`/`ReactNode` props become DOM slots; caller content stays light DOM.
- `className` prop is replaced by documented CSS parts/custom properties; no Tailwind in consumers.
- 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.
- React state/hooks replaced by element-owned state with pre-upgrade property preservation.
- state is the observable value. Upstream's controlled/uncontrolled split (state vs defaultState) becomes "state reflects the current state, default-state seeds it", so all four catalogued states stay inspectable and styleable without a second attribute (PC-3.3).
- A polite live region announces the state change. Upstream had none; the port announces each user transition once and never for programmatic writes (PC-12.5).
- Focus is moved deliberately after a transition. Upstream replaced the view without moving focus, leaving focus on a removed node; the port focuses the new view's first control (PC-5.1, PC-12.6).
- The social dock validates URLs. Upstream assigned href unvalidated; a refused URL now becomes an inert element instead of a link (R-3.2).
- No layoutId avatar projection. The avatar is rendered per view; the surface morph is a spring on width/height/radius, which keeps the pill geometry continuous without shared-layout projection (PC-10.2).
- className is replaced by documented parts and custom properties; icon (a React node per social item) is replaced by type-driven local SVGs (PC-11.5).
- The status pulse is disabled under reduced motion, and the dot uses the text token so it is perceivable in both schemes (PC-10.5, WCAG 1.4.11).
- Slots replace React nodes: children → the default slot, profileContent → profile-content, shareContent → share-content (PC-11.2).
- The upstream `role` prop is ported as `job-title`/`jobTitle`: `role` is a reserved global ARIA attribute, so using it for the person's job title put an invalid ARIA role on the host element (axe reports it as critical).
What it does
id-prefix seeds the shadow ids (<prefix>-island, <prefix>-surface, <prefix>-live).
Upstream behaviour
- Continuous physical morphing between collapsed, expanded, profile, and share states
- Spring-based layout-aware geometry and radius interpolation with no abrupt snapping
- Persistent spatial continuity for the avatar across collapsed and profile states
- Fully accessible keyboard navigation with Escape key dismissal and click-outside collapse
- Configurable social dock supporting predefined platform detection and custom links
- Light and dark mode compatibility adhering strictly to the EasyUI design token system
- Respects prefers-reduced-motion media query with instant fallback states
Differences from upstream
- React `children`/`ReactNode` props become DOM slots; caller content stays light DOM.
- `className` prop is replaced by documented CSS parts/custom properties; no Tailwind in consumers.
- 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.
- React state/hooks replaced by element-owned state with pre-upgrade property preservation.
- state is the observable value. Upstream's controlled/uncontrolled split (state vs defaultState) becomes "state reflects the current state, default-state seeds it", so all four catalogued states stay inspectable and styleable without a second attribute (PC-3.3).
- A polite live region announces the state change. Upstream had none; the port announces each user transition once and never for programmatic writes (PC-12.5).
- Focus is moved deliberately after a transition. Upstream replaced the view without moving focus, leaving focus on a removed node; the port focuses the new view's first control (PC-5.1, PC-12.6).
- The social dock validates URLs. Upstream assigned href unvalidated; a refused URL now becomes an inert element instead of a link (R-3.2).
- No layoutId avatar projection. The avatar is rendered per view; the surface morph is a spring on width/height/radius, which keeps the pill geometry continuous without shared-layout projection (PC-10.2).
- className is replaced by documented parts and custom properties; icon (a React node per social item) is replaced by type-driven local SVGs (PC-11.5).
- The status pulse is disabled under reduced motion, and the dot uses the text token so it is perceivable in both schemes (PC-10.5, WCAG 1.4.11).
- Slots replace React nodes: children → the default slot, profileContent → profile-content, shareContent → share-content (PC-11.2).
- The upstream `role` prop is ported as `job-title`/`jobTitle`: `role` is a reserved global ARIA attribute, so using it for the person's job title put an invalid ARIA role on the host element (axe reports it as critical).