{"id":30680648,"url":"https://github.com/knowledgecode/blokr","last_synced_at":"2026-05-13T08:02:15.713Z","repository":{"id":310962813,"uuid":"1041951008","full_name":"knowledgecode/blokr","owner":"knowledgecode","description":"Lightweight library to block user interactions in browsers","archived":false,"fork":false,"pushed_at":"2025-08-21T09:09:59.000Z","size":51,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-21T10:15:56.252Z","etag":null,"topics":["blocking","browser","disable","event","interaction","lock","ui"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/knowledgecode.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-08-21T08:57:42.000Z","updated_at":"2025-08-21T09:10:03.000Z","dependencies_parsed_at":"2025-08-21T10:16:02.663Z","dependency_job_id":"9cb9daa7-d155-4e72-8540-ffbd13b9e90b","html_url":"https://github.com/knowledgecode/blokr","commit_stats":null,"previous_names":["knowledgecode/blokr"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/knowledgecode/blokr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knowledgecode%2Fblokr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knowledgecode%2Fblokr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knowledgecode%2Fblokr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knowledgecode%2Fblokr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/knowledgecode","download_url":"https://codeload.github.com/knowledgecode/blokr/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knowledgecode%2Fblokr/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273158944,"owners_count":25055859,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-01T02:00:09.058Z","response_time":120,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blocking","browser","disable","event","interaction","lock","ui"],"created_at":"2025-09-01T16:51:08.999Z","updated_at":"2026-05-13T08:02:15.704Z","avatar_url":"https://github.com/knowledgecode.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Blokr\n\n[![CI](https://github.com/knowledgecode/blokr/actions/workflows/ci.yml/badge.svg)](https://github.com/knowledgecode/blokr/actions/workflows/ci.yml)\n[![npm](https://img.shields.io/npm/v/blokr)](https://www.npmjs.com/package/blokr)\n\nLightweight library to block user interactions in browsers.\n\n## Features\n\n- **Factory-based API**: Support for both global and element-specific locks\n- **No overlay elements**: Blocks interactions without adding elements to the DOM\n- **Scope filtering**: Control which events to block (`inside`, `outside`, `self`)\n- **Per-lock timeout**: Optional automatic unlock after specified time\n- **TypeScript**: Full type support included\n- **React Hook**: Built-in `useBlokr()` hook for React components\n\n## Why Blokr?\n\n### Comparison with Alternative Solutions\n\nBlokr provides a unique approach to blocking user interactions. Here's how it compares with other techniques:\n\n#### The `inert` Attribute\n\nThe HTML5 `inert` attribute marks an element as \"inert,\" preventing user interactions including keyboard navigation.\n\n#### CSS `pointer-events: none`\n\nCSS `pointer-events: none` disables mouse and touch events on elements, but cannot block keyboard events or prevent tab navigation.\n\n#### The `\u003cdialog\u003e` Element\n\nThe HTML5 `\u003cdialog\u003e` element creates a modal dialog but adds a DOM element and provides limited scope flexibility for non-modal use cases.\n\n#### Comparison Summary\n\n| Feature | Blokr | inert | pointer-events | dialog |\n|---------|-------|-------|----------------|--------|\n| Blocks keyboard events | ✅ | ✅ | ❌ | ✅ |\n| Global interaction lock | ✅ | ❌ | ❌ | ❌ |\n| Inside/outside scope | ✅ | ❌ | ❌ | ❌ |\n| Timeout protection | ✅ | ❌ | ❌ | ❌ |\n| No DOM overlay | ✅ | ✅ | ✅ | ❌ |\n| No DOM modifications | ✅ | ❌ | ✅ | ❌ |\n\n**Key differentiators:**\n\n- **Global interaction lock**: Blokr can block interactions across the entire page, not just within specific elements\n- **Inside/outside scope**: Unique ability to selectively block events inside or outside a target element\n- **Timeout protection**: Automatic unlock prevents permanent locks due to errors or forgotten cleanup\n- **No DOM modifications**: Works purely via event listeners without modifying DOM structure or attributes\n\n## What's New in v0.4.0\n\n- **React Hook support**: New `useBlokr()` hook for React applications (React 18+ required)\n\n## ⚠️ Breaking Changes in v0.4.0\n\n- **UMD format removed**: CDN usage now requires ES modules only (`blokr/dist/index.js`)\n- **No breaking changes to core API**: All v0.3.0 JavaScript APIs remain unchanged\n\nFor changes from v0.2.x, see the [Migration from v0.2.x](#migration-from-v02x) section below.\n\n**Note:** This library is under active development. Future versions may introduce additional breaking changes. Please refer to the changelog before upgrading.\n\n## Installation\n\n```bash\nnpm install blokr\n```\n\n### React Hook Support\n\nThe `useBlokr()` React Hook is included in the same package. React 18.0+ or React 19.0+ is required to use the hook:\n\n```bash\nnpm install blokr react\n```\n\nThe `react` package is an optional peer dependency. If you don't use React, you can ignore this requirement.\n\n## Usage (Vanilla)\n\n### Basic Usage\n\n```typescript\nimport blokr from 'blokr';\n\n// Global lock - blocks all user interactions\nconst instance = blokr();\n\ninstance.lock();\n\n// Check if locked\nif (instance.isLocked()) {\n  console.log('User interactions are blocked');\n}\n\n// Unlock\ninstance.unlock();\n```\n\n### Element-specific Locking\n\n```typescript\nimport blokr from 'blokr';\n\nconst container = document.querySelector('.container');\nconst instance = blokr(container);\n\n// Block events inside the container (default scope)\ninstance.lock();\n\n// Or explicitly specify scope\ninstance.lock({ scope: 'inside' });   // Block events inside container\ninstance.lock({ scope: 'outside' });  // Block events outside container\ninstance.lock({ scope: 'self' });     // Block events on the container only\n```\n\n### Auto-timeout\n\n```typescript\nimport blokr from 'blokr';\n\nconst instance = blokr();\n\n// Auto-unlock after 5 seconds\ninstance.lock({ timeout: 5000 });\n\n// Disable timeout (lock indefinitely)\ninstance.lock({ timeout: 0 });\n```\n\n### CDN Usage (ES Modules)\n\n```html\n\u003cscript type=\"module\"\u003e\n  import blokr from 'https://unpkg.com/blokr/dist/index.js';\n\n  const instance = blokr();\n  instance.lock({ timeout: 3000 });\n\u003c/script\u003e\n```\n\n## API Reference\n\n### `blokr(target?: Element): BlokrInstance`\n\nReturns a Blokr instance. If no target is specified, creates a global instance that blocks all events. If the same target is provided multiple times, returns the cached instance.\n\n**Parameters:**\n- `target` (optional): DOM element to scope the lock to\n\n**Returns:** `BlokrInstance`\n\n**Examples:**\n\n```typescript\n// Global instance (blocks all events)\nconst global = blokr();\n\n// Element-specific instance\nconst container = document.querySelector('.modal');\nconst modal = blokr(container);\n\n// Same element returns same instance\nconst modal2 = blokr(container);\nconsole.log(modal === modal2); // true\n```\n\n### `instance.lock(options?: Options): boolean`\n\nLocks user interactions. Returns `true` if lock was applied, `false` if already locked.\n\n**Parameters:**\n- `options.timeout` (optional): Auto-unlock timeout in milliseconds. Default: `0` (no timeout)\n- `options.scope` (optional): Event blocking scope. Default: `'inside'`\n  - `'inside'`: Block events inside the target element (default)\n  - `'outside'`: Block events outside the target element\n  - `'self'`: Block events on the target element only\n\n**Returns:** `true` if lock was applied, `false` if already locked\n\n**Examples:**\n\n```typescript\nconst instance = blokr();\n\n// Basic lock\ninstance.lock(); // Returns true\n\n// Already locked\ninstance.lock(); // Returns false\n\n// Lock with timeout\ninstance.lock({ timeout: 5000 });\n\n// Lock with scope (requires target element)\nconst container = document.querySelector('.panel');\nconst panelInstance = blokr(container);\npanelInstance.lock({ scope: 'inside' });\n```\n\n### `instance.unlock(): void`\n\nUnlocks user interactions and clears any pending timeout. Safe to call even when not locked.\n\n**Examples:**\n\n```typescript\nconst instance = blokr();\ninstance.lock();\ninstance.unlock();\n\n// Safe to call multiple times\ninstance.unlock();\ninstance.unlock();\n```\n\n### `instance.isLocked(): boolean`\n\nReturns `true` if user interactions are currently locked.\n\n**Returns:** `boolean`\n\n**Examples:**\n\n```typescript\nconst instance = blokr();\nconsole.log(instance.isLocked()); // false\n\ninstance.lock();\nconsole.log(instance.isLocked()); // true\n\ninstance.unlock();\nconsole.log(instance.isLocked()); // false\n```\n\n## Examples\n\n### POST Processing with Timeout\n\n```typescript\nimport blokr from 'blokr';\n\nasync function saveUserProfile(formData: FormData) {\n  const instance = blokr();\n\n  // Block all interactions with 10-second timeout\n  instance.lock({ timeout: 10000 });\n\n  try {\n    const response = await fetch('/api/profile', {\n      method: 'POST',\n      body: formData\n    });\n\n    if (response.ok) {\n      showSuccessMessage();\n    }\n  } finally {\n    instance.unlock();\n  }\n}\n```\n\n### Modal Dialog\n\n```typescript\nimport blokr from 'blokr';\n\nfunction openModal() {\n  const modal = document.querySelector('.modal');\n  const instance = blokr(modal);\n\n  modal.classList.add('visible');\n\n  // Block all interactions outside the modal\n  instance.lock({ scope: 'outside' });\n}\n\nfunction closeModal() {\n  const modal = document.querySelector('.modal');\n  const instance = blokr(modal);\n\n  modal.classList.remove('visible');\n  instance.unlock();\n}\n```\n\n### Form Panel Lock\n\n```typescript\nimport blokr from 'blokr';\n\nfunction disableFormPanel() {\n  const panel = document.querySelector('.settings-panel');\n  const instance = blokr(panel);\n\n  // Disable interactions only inside the panel\n  instance.lock({ scope: 'inside' });\n}\n\nfunction enableFormPanel() {\n  const panel = document.querySelector('.settings-panel');\n  const instance = blokr(panel);\n\n  instance.unlock();\n}\n```\n\n### Loading Overlay Alternative\n\n```typescript\nimport blokr from 'blokr';\n\nasync function loadData() {\n  const instance = blokr();\n\n  // No overlay element needed!\n  instance.lock({ timeout: 30000 });\n\n  try {\n    const data = await fetch('/api/data').then(r =\u003e r.json());\n    renderData(data);\n  } finally {\n    instance.unlock();\n  }\n}\n```\n\n## React Hook\n\nThe `useBlokr()` hook provides a React-friendly way to manage user interaction blocking. It works seamlessly with the factory-based API and manages refs automatically.\n\n### Import\n\n```typescript\nimport { useBlokr } from 'blokr/react';\n```\n\n### Basic Usage\n\n```tsx\nimport { useBlokr } from 'blokr/react';\n\nexport function PageWithLinks() {\n  const { target, lock, unlock, isLocked } = useBlokr\u003cHTMLDivElement\u003e();\n\n  const handleLock = () =\u003e {\n    lock({ timeout: 5000 }); // Auto-unlock after 5 seconds\n  };\n\n  return (\n    \u003c\u003e\n      \u003cdiv ref={target}\u003e\n        \u003ca href=\"/page1\"\u003eGo to Page 1\u003c/a\u003e\n      \u003c/div\u003e\n      \u003cbutton onClick={handleLock}\u003eLock Link\u003c/button\u003e\n      \u003cbutton onClick={unlock}\u003eUnlock\u003c/button\u003e\n    \u003c/\u003e\n  );\n}\n```\n\n### Options\n\nThe `lock()` function accepts the same options as the core API:\n\n```tsx\nconst { target, lock, unlock } = useBlokr\u003cHTMLDivElement\u003e();\n\n// With timeout (auto-unlock after 5 seconds)\nlock({ timeout: 5000 });\n\n// With scope\nlock({ scope: 'inside' });    // Block inside the element\nlock({ scope: 'outside' });   // Block outside the element\nlock({ scope: 'self' });      // Block on the element only\n\n// With both options\nlock({ scope: 'inside', timeout: 5000 });\n```\n\n### Hook API\n\n#### `useBlokr\u003cT = Element\u003e(allowGlobal?: boolean): { target: RefObject\u003cT | null\u003e; lock: (options?: Options) =\u003e boolean; unlock: () =\u003e void; isLocked: () =\u003e boolean }`\n\nReturns an object containing a ref and three control functions for managing user interaction blocking.\n\n**Type Parameters:**\n- `T` (optional): The DOM element type. Default: `Element`\n\n**Parameters:**\n- `allowGlobal` (optional): If `true`, enables global lock mode that blocks interactions across the entire page instead of a specific element. When using global lock, the `target` ref is not needed. Default: `false`\n\n**Returns:** An object with:\n- `target`: A React ref to assign to the target element (`RefObject\u003cT | null\u003e`)\n- `lock`: Function to lock user interactions on the element (`(options?: Options) =\u003e boolean`)\n- `unlock`: Function to unlock user interactions (`() =\u003e void`)\n- `isLocked`: Function to check if currently locked (`() =\u003e boolean`)\n\n**Parameters (lock function):**\n- `options.timeout` (optional): Auto-unlock timeout in milliseconds\n- `options.scope` (optional): Event blocking scope (`'inside'`, `'outside'`, or `'self'`)\n\n**Returns (lock function):** `true` if lock was applied, `false` if already locked or if the ref is not set (when using element-specific lock)\n\n### allowGlobal Parameter\n\nThe `allowGlobal` parameter enables global lock mode, which blocks user interactions across the entire page instead of scoping to a specific element.\n\n**Global Lock (`allowGlobal=true`):**\n```tsx\n// No need to destructure 'target' since we're not using element-specific locking\nconst { lock, unlock, isLocked } = useBlokr(true);\n\n// Locks all interactions across the entire page\nlock();  // Blocks all user interactions globally\n```\n\n**Element-Specific Lock (Default: `allowGlobal=false`):**\n```tsx\nconst { target, lock, unlock, isLocked } = useBlokr\u003cHTMLDivElement\u003e();\n\n// Attach target to an element\n\u003cdiv ref={target}\u003eContent\u003c/div\u003e\n\n// Lock only affects this specific element (by default, scope='inside')\nlock();  // Blocks interactions inside the div\n```\n\n## Migration from v0.2.x\n\n### API Changes\n\n| v0.2.x | v0.3.0 |\n|--------|--------|\n| `blokr.lock()` | `blokr().lock()` |\n| `blokr.unlock()` | `blokr().unlock()` |\n| `blokr.unlock(true)` | `blokr().unlock()` (always immediate) |\n| `blokr.setTimeout(ms)` | `blokr().lock({ timeout: ms })` |\n| `blokr.isLocked()` | `blokr().isLocked()` |\n| `window.Blokr` (UMD) | `window.blokr` (UMD) |\n\n### Reference Counting Removed\n\nIn v0.2.x, multiple `lock()` calls incremented a counter:\n\n```typescript\n// v0.2.x\nblokr.lock();    // Count: 1\nblokr.lock();    // Count: 2\nblokr.unlock();  // Count: 1 (still locked)\nblokr.unlock();  // Count: 0 (unlocked)\n```\n\nIn v0.3.0, `lock()` returns `false` if already locked:\n\n```typescript\n// v0.3.0\nconst instance = blokr();\ninstance.lock();    // Returns true\ninstance.lock();    // Returns false (already locked)\ninstance.unlock();  // Unlocked\n```\n\n### Element-specific Locking (New Feature)\n\n```typescript\n// v0.3.0 only - new feature not available in v0.2.x\nconst container = document.querySelector('.container');\nconst instance = blokr(container);\n\n// Block events inside container\ninstance.lock({ scope: 'inside' });\n\n// Block events outside container\ninstance.lock({ scope: 'outside' });\n\n// Block events on container itself only\ninstance.lock({ scope: 'self' });\n```\n\n## Limitations\n\n- **Event listener priority**: Event listeners are registered at the capture phase. May not work correctly when used with event delegation libraries.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fknowledgecode%2Fblokr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fknowledgecode%2Fblokr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fknowledgecode%2Fblokr/lists"}