{"_id":"@bramus/sticky-observer","name":"@bramus/sticky-observer","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@bramus/sticky-observer","version":"1.0.0","description":"Observe CSS `position: sticky` elements getting stuck or unstuck","type":"module","module":"dist/index.js","types":"dist/index.d.ts","scripts":{"start":"npm run clean && esbuild src/index.ts --sourcemap --bundle --outfile=demo/sticky-observer.js --platform=browser --format=esm --target=es6 --watch --servedir=demo","lint":"prettier --check '{src,test,cli}/**/*.{ts,tsx,js,jsx,html,css}'","format":"prettier --write '{src,test,cli}/**/*.{ts,tsx,js,jsx,html,css}'","clean":"rm -rf dist","build":"npm run clean && esbuild src/index.ts --sourcemap --bundle --outfile=dist/index.js --platform=browser --format=esm --target=es6 && tsc --emitDeclarationOnly --outDir dist","prepack":"npm run prevent-dirty-tree && npm run build","prevent-dirty-tree":"exit $(git status --porcelain | wc -l)"},"keywords":["sticky","observer","intersectionobserver"],"author":{"name":"Bramus Van Damme","email":"bramus@bram.us","url":"https://www.bram.us/"},"license":"MIT","devDependencies":{"prettier":"^3.3.3","typescript":"^5.0.0","esbuild":"^0.19.0"},"gitHead":"86b34e1ee6a94039c0a223cde539887975f1f4a1","_id":"@bramus/sticky-observer@1.0.0","_nodeVersion":"20.17.0","_npmVersion":"11.6.3","dist":{"integrity":"sha512-mm4VGx04uFJNcoon+X0EYAGBuRByWPJDRXYgU0/D1HkFz2Tf7pRJ9YUw/T2rXOKhGcfUSMu+5fNXW0a+ZouYBw==","shasum":"3ee47bfc32b1c8bd5b4b7fa7a9869b258eb2e348","tarball":"https://registry.npmjs.org/@bramus/sticky-observer/-/sticky-observer-1.0.0.tgz","fileCount":9,"unpackedSize":22279,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIBkQsJ30TRNSGKON9dreH1fbZlAAIaEtzxxMQqIE5dYzAiAYlwBJVO1Pf7i+Z48OOIGrfTAPQLB4515OxDlejbJCpw=="}]},"_npmUser":{"name":"bramus","email":"bramus@bram.us"},"directories":{},"maintainers":[{"name":"bramus","email":"bramus@bram.us"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sticky-observer_1.0.0_1764271848904_0.4188044976585452"},"_hasShrinkwrap":false}},"time":{"created":"2025-11-27T19:30:48.774Z","1.0.0":"2025-11-27T19:30:49.082Z","modified":"2025-11-27T19:30:49.540Z"},"maintainers":[{"name":"bramus","email":"bramus@bram.us"}],"description":"Observe CSS `position: sticky` elements getting stuck or unstuck","keywords":["sticky","observer","intersectionobserver"],"author":{"name":"Bramus Van Damme","email":"bramus@bram.us","url":"https://www.bram.us/"},"license":"MIT","readme":"# Sticky Observer\n\nObserve CSS `position: sticky` elements getting stuck or unstuck.\n\nIt implements [the \"Sticky Events\" pattern using sentinels and `IntersectionObserver`](https://developer.chrome.com/docs/css-ui/sticky-headers), allowing you to react to changes in the sticky state of an element.\n\n## Installation\n\n```bash\nnpm install @bramus/sticky-observer\n```\n\n## Usage\n\n### Basic Usage\n\n1. Import `StickyObserver` in your project.\n2. Call `StickyObserver.observe()` with a CSS selector for the elements you want to watch.\n3. Listen for the `sticky-change` event on the observer instance.\n\n```typescript\nimport { StickyObserver } from '@bramus/sticky-observer';\n\n// Initialize and observe elements matching the selector\nconst observer = StickyObserver.observe('h2');\n\n// Listen for state changes\nobserver.addEventListener('sticky-change', (e: Event) => {\n  const { target, stuck } = e.detail;\n\n  console.log(`Element is now ${stuck ? 'stuck' : 'unstuck'}:`, target);\n});\n```\n\n### CSS Requirement\n\nFor the observer to work correctly, the parent container of your sticky element **must be positioned** (e.g., `position: relative`).\n\nIf the parent is statically positioned, `StickyObserver` will automatically set `position: relative` on it and log a warning to the console.\n\n## API\n\n### `StickyObserver.observe(selector, options?)`\n\nStatic method to create a new observer instance.\n\n- **`selector`** (`string`): CSS selector for the element(s) to observe.\n- **`options`** (`StickyObserverOptions`): Optional configuration object.\n\nReturns a `StickyObserver` instance.\n\n### Options (`StickyObserverOptions`)\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `debug` | `boolean` | `false` | If `true`, shows visual outlines on the target and sentinels, and enables console logging. |\n| `container` | `HTMLElement` | `null` | Optional scroll container to use as the `IntersectionObserver` root. When omitted, it uses the document viewport |\n| `remainStickyBeyondStickyEdge` | `boolean` | `false` | If `true`, the element reports as \"stuck\" even when it has exited the scrollport beyond its sticky edge (normally it would unstick). |\n\n### Events\n\nThe `StickyObserver` instance extends `EventTarget` and dispatches the following event:\n\n#### `sticky-change`\n\nFired when the sticky state of an observed element changes.\n\n- **Event Type**: `CustomEvent<StickyChangeDetail>`\n- **`detail` property**:\n  - `target`: The `HTMLElement` that changed state.\n  - `stuck`: `boolean` indicating if the element is currently stuck.\n\n### Instance Methods\n\n#### `disconnect()`\n\nStops observing all elements and disconnects the internal `IntersectionObserver`s.\n\n```typescript\nobserver.disconnect();\n```\n\n## How it works\n\nThis library injects two \"sentinel\" elements into the parent of the sticky element:\n\n1. A **Top Sentinel** placed before the element (at the start of the parent).\n2. A **Bottom Sentinel** placed after the element (at the end of the parent).\n\nIt uses `IntersectionObserver` to track when these sentinels intersect with the viewport (or container). Based on the intersection logic, it determines whether the element is currently in a \"stuck\" state.\n\nReference: [An event for CSS `position:sticky`](https://developer.chrome.com/docs/css-ui/sticky-headers)\n\n## License\n\n[MIT](./LICENSE)\n","readmeFilename":"README.md","_rev":"1-f3182acd9f695e55410a4fbb1db1156d"}