Media & effects
Hanging Charm
Pointer-driven hanging charm with layered spring physics
detent-hanging-charm
Port status
- Catalog status
- implemented
- Origin
- upstream
- Phase
- 4
- Wave
- phase-4-pointer
- Group
- Media & effects
6.1 KB gzip for the class module · inlines the shared spring
Variants
Move the pointer across a charm: the chillies, lemon and trailing strings swing by their own fractions of the
pointer offset, and they ease back to rest when the pointer leaves. This charm uses a stiffer, heavily damped spring
with a wider rotation range. Both this and the preview expose a single role="img" whose name is the
caption; every decorative layer is aria-hidden. This is a decorative demonstration — no
data is involved.
Recorded states
reduced motionrecorded state; no settercompletedrecorded state; no setter
Usage
Import
import '/components/hanging-charm/define.js';
// or register it yourself
import { DetentHangingCharm } from '/components/hanging-charm/hanging-charm.js';
Attributes
captiondampingmax-rotationstiffness-xstiffness-y
Properties
No properties are recorded for this component.
Slots
No slots are recorded for this component.
Events
No events are recorded for this component.
Example
Runnable example: site/examples/hanging-charm.ts. The snippet below is generated from the catalogued attributes.
<detent-hanging-charm
caption
damping="9"
max-rotation="13"
stiffness-x="55"
stiffness-y="45"
></detent-hanging-charm>
Source
The pinned upstream source for this row:
src/components/ui/NimbuMirchi.tsxsha256 ab7ba88dab2e…
Native files
The native module and its artifacts:
src/components/hanging-charm/hanging-charm.tssrc/components/hanging-charm/styles.csssrc/components/hanging-charm/define.tsdocs/components/hanging-charm.mdsite/examples/hanging-charm.tstests/browser/hanging-charm.spec.tsevidence/components/hanging-charm.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 |
|---|---|---|---|---|
caption | caption | string | undefined | Kebab-case attribute; camelCase property. |
damping | damping | number | 9 | Kebab-case attribute; camelCase property. |
max-rotation | maxRotation | number | 13 | Kebab-case attribute; camelCase property. |
stiffness-x | stiffnessX | number | 55 | Kebab-case attribute; camelCase property. |
stiffness-y | stiffnessY | number | 45 | Kebab-case attribute; camelCase property. |
Properties
No property-only values are recorded for this component.
Events
No custom events are recorded for this component.
Slots
No slots are recorded for this component.
CSS customisation
- `className` prop is replaced by documented CSS parts/custom properties; no Tailwind in consumers.
- React state/hooks replaced by element-owned state with pre-upgrade property preservation.
- The sr-only caption span is not rendered; the caption is exposed exactly once as the role=img aria-label.
- The host reflects data-motion (moving|rest) so the recorded completed state is observable.
- Spring tuning attributes apply from the next pointer interaction because a spring keeps the constants it was created with.
What it does
A hanging lemon-and-chilli charm painted as one SVG, suspended from a ceiling line, that swings with layered spring physics as the pointer moves across it.
Upstream behaviour
- Pointer position drives a normalized MotionValue pipeline (no React renders per frame)
- Layered secondary motion — chillies, lemon, and trailing strings each swing independently
- Configurable spring stiffness, damping, and max rotation for tuning the physical feel
- Geometry-aware normalization that adapts to the actual container size
- Respects prefers-reduced-motion with a clean static rest pose
- Graceful degradation when pointer events are unavailable
Differences from upstream
- `className` prop is replaced by documented CSS parts/custom properties; no Tailwind in consumers.
- React state/hooks replaced by element-owned state with pre-upgrade property preservation.
- The sr-only caption span is not rendered; the caption is exposed exactly once as the role=img aria-label.
- The host reflects data-motion (moving|rest) so the recorded completed state is observable.
- Spring tuning attributes apply from the next pointer interaction because a spring keeps the constants it was created with.