# detect-features

Feature detection — gate content on real browser support with
plain CSS.


```html
<div>
  <detect-features></detect-features>
  <p>Geolocation supported in this browser.</p>
  <p role="alert">Geolocation is not supported in this browser.</p>
  <small role="note">See "Live document" tab below to see the <code>full-support</code> and <code>no-support</code> attributes populated with all of your browser's features.</small>
  <style>
    #demo-detect-features-simple {
      &:has(detect-features[full-support~="geolocation"]) p[role="alert"] {
        display: none;
      }
      &:has(detect-features:not([full-support~="geolocation"])) p:not([role="alert"]) {
        display: none;
      }
    }
  </style>
</div>
```


## Features

- **Support tokens** Set once on connect for CSS / Quark targeting
- **CSS-selectable** Gate content with attribute selectors, no polyfills
- **JS / Quark data** `.provision` exposes `fullSupport` / `noSupport` arrays
- **Common Web APIs** Geolocation, share, bluetooth, clipboard,
  media-devices, service-worker, payment-request, and many more

## Installation


`@excom/detect-features` v0.1.4

```bash
pnpm add @excom/detect-features
```

```bash
npm install @excom/detect-features
```

```bash
yarn add @excom/detect-features
```

### Import

```ts
import "@excom/detect-features";
```



## Usage

Check `full-support` / `no-support` for a space-separated feature name.

```html
<detect-features></detect-features>
```

Use CSS or Quark to react to the feature detection:

```css
detect-features:not([full-support~="geolocation"]) ~ .needs-location {
  display: none;
}
```

### API Reference


#### Attributes

| Name | Surface | Type | Default | Values | Description |
| --- | --- | --- | --- | --- | --- |
| `full-support` | state | `tokenlist` |  | `"geolocation"` \| `"vibrate"` \| `"bluetooth"` \| `"usb"` \| `"serial"` \| `"hid"` \| `"share"` \| `"clipboard"` \| `"credentials"` \| `"media-devices"` \| `"service-worker"` \| `"storage"` \| `"wake-lock"` \| `"gpu"` \| `"locks"` \| `"permissions"` \| `"connection"` \| `"user-agent-data"` \| `"battery"` \| `"midi"` \| `"webxr"` \| `"virtual-keyboard"` \| `"window-controls-overlay"` \| `"contacts"` \| `"local-storage"` \| `"session-storage"` \| `"indexed-db"` \| `"notification"` \| `"payment-request"` \| `"web-socket"` \| `"worker"` \| `"shared-worker"` \| `"broadcast-channel"` \| `"intersection-observer"` \| `"resize-observer"` \| `"speech-synthesis"` \| `"eye-dropper"` \| `"barcode-detector"` \| `"file-system-access"` \| `"webrtc"` \| `"dialog"` \| `"view-transitions"` \| `"cookie-store"` \| `"offscreen-canvas"` | Space-separated feature names fully supported by this browser. |
| `partial-support` | state | `tokenlist` |  |  | Reserved for features with partial / conditional support. Not populated yet — see `INTERNAL.md`. |
| `no-support` | state | `tokenlist` |  | `"geolocation"` \| `"vibrate"` \| `"bluetooth"` \| `"usb"` \| `"serial"` \| `"hid"` \| `"share"` \| `"clipboard"` \| `"credentials"` \| `"media-devices"` \| `"service-worker"` \| `"storage"` \| `"wake-lock"` \| `"gpu"` \| `"locks"` \| `"permissions"` \| `"connection"` \| `"user-agent-data"` \| `"battery"` \| `"midi"` \| `"webxr"` \| `"virtual-keyboard"` \| `"window-controls-overlay"` \| `"contacts"` \| `"local-storage"` \| `"session-storage"` \| `"indexed-db"` \| `"notification"` \| `"payment-request"` \| `"web-socket"` \| `"worker"` \| `"shared-worker"` \| `"broadcast-channel"` \| `"intersection-observer"` \| `"resize-observer"` \| `"speech-synthesis"` \| `"eye-dropper"` \| `"barcode-detector"` \| `"file-system-access"` \| `"webrtc"` \| `"dialog"` \| `"view-transitions"` \| `"cookie-store"` \| `"offscreen-canvas"` | Space-separated feature names with no support in this browser. |
| `granted-permissions` | state | `tokenlist` |  |  | Reserved for features that require an explicit permission grant. Not populated yet — see `INTERNAL.md`. |

#### Provision

| Name | Type | Description |
| --- | --- | --- |
| `provision` | `FeatureInfo` (`{ fullSupport: keyof typeof FEATURE_CHECKS[]; noSupport: keyof typeof FEATURE_CHECKS[]; }`) | Same partitions as the token attributes, as string arrays for JS / Quark (`fullSupport`, `noSupport`). Not reflected as an attribute. |



### Examples

#### Gate a whole section

`full-support` lists every feature this browser supports; `no-support`
lists the rest. Wrap a whole section instead of one element by pairing
`:has()` with the same attribute selector:

```css
body:has(detect-features:not([full-support~="geolocation"])) #store-locator {
  display: none;
}
```

#### Web Share progressive UI

Show a share affordance only when `share` is listed in `full-support`.
Browsers without Web Share keep the fallback copy instead.


```html
<div>
  <detect-features></detect-features>
  <p>Web Share is available in this browser.</p>
  <p role="status">
    Web Share is not available — offer a copy-link fallback instead.
  </p>
  <small role="note">See "Live document" tab below to see the <code>full-support</code> and <code>no-support</code> attributes populated with all of your browser's features.</small>
  <style>
    #demo-detect-features-share {
      &:has(detect-features[full-support~="share"]) p[role="status"] {
        display: none;
      }
      &:has(detect-features:not([full-support~="share"])) p:not([role="status"]) {
        display: none;
      }
    }
  </style>
</div>
```

## Release notes

### 0.1.1 (2026-09-23)

- Ship only dist (and declared extras) in the npm tarball; drop build logs, tests and sources
