https://github.com/mdrv/wbh
π¨ Restrict access to your website from web browsers without specific features.
https://github.com/mdrv/wbh
browsers caniuse support target webdev
Last synced: 19 days ago
JSON representation
π¨ Restrict access to your website from web browsers without specific features.
- Host: GitHub
- URL: https://github.com/mdrv/wbh
- Owner: mdrv
- License: gpl-3.0
- Created: 2025-06-20T14:07:54.000Z (12 months ago)
- Default Branch: main
- Last Pushed: 2026-02-25T02:58:17.000Z (4 months ago)
- Last Synced: 2026-02-25T07:54:50.199Z (4 months ago)
- Topics: browsers, caniuse, support, target, webdev
- Language: TypeScript
- Homepage:
- Size: 137 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# π¨ Web Browser Hard (WBH)

Restrict access to your website from web browsers without specific features.
> [!CAUTION]
> The API of this module is **rapidly changing** and is not intended for production or commercial use.
>
> To suit your specific needs, it's necessary to fork this project (with attribution). You can expect `v265`+ to be **more stable**. π
## π Features
- 𧬠**Modular**: Use it on any framework (Svelte, Vue, etc.)
- π **Versatile**: Pick from [70+ browser features](https://github.com/mdrv/wbh/wiki/feature-list) to check
- ποΈ **Customizable**: Set metadata per-feature with levels, scores, and wisdom text
- π² **Tree-shakeable**: Unused imports stay out of bundle
- π **Easy to use**: Comes with TypeScript types out of the box
- π¦ **Presets**: Ready-made profiles for common app types (modern web apps, SPAs, e-commerce, etc.)
- π **Version inference**: Infer minimum browser versions required by your feature set
Any question? Read [Q&A](https://github.com/mdrv/wbh/wiki/Q&A) on the wiki.
## π§ Example Code
### Basic usage
```ts
import {
WBH,
WBHLevel as L,
avif,
fileSystemApi,
cssAspectRatio,
} from '@mdrv/wbh'
const wbh = new WBH([
{
feat: avif,
level: L.CRITICAL,
score: 50,
wisdom: 'Most images are optimized AVIF.',
},
{
feat: fileSystemApi,
level: L.IMPORTANT,
score: 50,
wisdom: 'Needed for local binary storage.',
},
{
feat: cssAspectRatio,
level: L.OPTIONAL,
score: 49,
wisdom: 'Some layouts depend on aspect ratio.',
},
])
const result = await wbh.getResultAsync()
if (result.score >= 0) {
// Render your app
} else {
// Browser missing features β show fallback UI
}
```
### Using presets
```ts
import { WBH, presets } from '@mdrv/wbh'
const wbh = new WBH(presets.modernWebApp())
const result = await wbh.getResultAsync()
console.log(result.unsupported.map((f) => f.name))
```
### Browser version inference
```ts
const wbh = new WBH(presets.modernWebApp())
await wbh.getResultAsync()
const versions = wbh.inferMinimumVersions()
// { chrome: "120", firefox: "121", safari: "17", edge: "120" }
const comparison = wbh.compareBrowser('firefox', 115)
// { browser: "firefox", version: 115, status: "outdated", gap: 6, ... }
```
### Options
```ts
new WBH(features, {
cache: false, // opt-in: cache detection results (default off)
mockUnsupported: false, // simulate all features unsupported (for testing)
})
```
## π Release Info
This package implements **Gregorian YYM-based** semver notation.
| Version | Date |
| ---------- | ------------ |
| `v257.x.x` | July 2025 |
| `v260.x.x` | OctβDec 2025 |
| `v265.x.x` | May 2026 |
See [CHANGELOG](https://github.com/mdrv/wbh/wiki/changelog) for full history.
---
v265 Changelog (click to expand)
### Breaking Changes
- **Renamed** `forceFail` option β `mockUnsupported`
- **Removed** built-in CSS (`css.ts`) β handle your own fallback UI
- **Removed** `is-mobile` dependency
- **Updated** `Result` type: now includes `timestamp` and `durationMs` fields
- **Updated** feature data: each feature now includes `safari` and `edge` version fields
### New Features
- **Presets system** (`src/presets.ts`) β 7 ready-made profiles:
- `modernWebApp()` β CSS nesting, grid, container queries, dialog, view transitions
- `spa()` β Single-page app essentials (view transitions, navigation API, promises)
- `contentSite()` β Content-focused sites (text-wrap, viewport units, typography)
- `dataHeavy()` β Data-intensive apps (IndexedDB, WebSockets, structuredClone)
- `cuttingEdge()` β Latest APIs (anchor positioning, scroll timeline, declarative shadow DOM)
- `minimal()` β Bare minimum for basic compatibility
- `ecommerce()` β E-commerce features (dialog modals, payment API, popover)
- **`inferMinimumVersions()`** β Returns min Chrome/Firefox/Safari/Edge versions needed
- **`compareBrowser(browser, version)`** β Compare a browser version against requirements; returns `current`, `outdated`, or `unsupported` status
- **Opt-in cache** via `{ cache: true }` option (default off to avoid stale results)
- **Enriched Result** β `timestamp` (Date.now()) and `durationMs` (performance.now())
### New Feature Detections (+20)
| Feature | Level | Notes |
| ---------------------- | --------- | --------------------------------- |
| `htmlDialog` | CRITICAL | `` + showModal |
| `viewTransitions` | CRITICAL | View Transitions API |
| `cssNesting` | CRITICAL | CSS nesting syntax |
| `cssHasSelector` | CRITICAL | `:has()` relational selector |
| `structuredClone` | CRITICAL | Deep cloning without hacks |
| `promiseWithResolvers` | CRITICAL | Promise.withResolvers() |
| `arrayAt` | IMPORTANT | Array.at() negative indexing |
| `arrayWith` | IMPORTANT | Array.with() immutable mutation |
| `cssColorMix` | IMPORTANT | color-mix() function |
| `viewportUnits` | IMPORTANT | svh/lvh/dvh units |
| `cssFocusVisible` | IMPORTANT | :focus-visible pseudo-class |
| `navigationApi` | IMPORTANT | Navigation API (Chrome/Edge only) |
| `urlPattern` | IMPORTANT | URLPattern API |
| `cssAnchorPositioning` | OPTIONAL | Anchor positioning (Chrome only) |
| `declarativeShadowDom` | OPTIONAL | Declarative Shadow DOM |
| `promiseTry` | OPTIONAL | Promise.try() |
| `regexpEscape` | OPTIONAL | RegExp.escape() |
| `importAttributes` | OPTIONAL | Import attributes |
### Internal Improvements
- **gen.ts** now extracts Safari and Edge version data from MDN BCD + caniuse-db
- **types.ts** cleaned up: `caniuse` is `readonly string[]`, added `reason?`, `BrowserVersionMap`, `BrowserComparison`
- **dynamicImport** no longer references removed `is-mobile` dep
- Test suite expanded from 4 β 10 tests covering all new functionality
---
## π Thank You
This project proudly uses:
- π§© [**es-toolkit**](https://github.com/toss/es-toolkit) (modern JS utilities with TypeScript support)
- π₯ [**Bun.js**](https://github.com/oven-sh/bun) (blazing fast server-side JS runtime)
- π Additional data: [**MDN**](https://github.com/mdn/browser-compat-data) and [**Can I use...**](https://github.com/Fyrd/caniuse)
Β© 2025 MEDRIVIA οΌ Umar Alfarouk