# detect-browser

Drop-in tag provides browser, OS, device, and language metadata — target Safari, standalone mode, mobile devices, etc. Helpful in creating device-specific UX.


```html
<div>
  <detect-browser></detect-browser>
  <style>
    #demo-detect-browser-simple> :first-child {
      detect-browser::before { content: "Rendered with CSS: "; }
      detect-browser::after {
        content:
        "browser: " attr(browser-name) " " attr(browser-version)
        " · engine: " attr(browser-engine)
        " · os: " attr(operating-system)
        " · device: " attr(device-type);
      }
    }
  </style>
</div>
```


## Features

- **Attribute metadata** Set once on connect for CSS / Quark targeting
- **Standalone / PWA detection** `is-standalone` reflects installed mode
- **Language detection** `language-id` reflects system-selected language
- **Device class** `device-type` (`mobile` / `desktop`) for coarse targeting
- **Full metadata** `.provision` exposes user agent, languages, hardware hints

## Installation


`@excom/detect-browser` v0.1.4

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

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

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

### Import

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



## Usage

Drop it anywhere and select on its attributes with plain CSS.

```html
<detect-browser></detect-browser>
```

```css
html:has(detect-browser[browser-name="safari"]) .safari-only {
  display: block;
}
```

Live touch / pointer detection is `detect-media media-query="(pointer: coarse)"` — this element reports what the user agent says once, not what the input is now.

### API Reference


#### Attributes

| Name | Surface | Type | Default | Values | Description |
| --- | --- | --- | --- | --- | --- |
| `browser-name` | state | `string` |  | `"chrome"` \| `"firefox"` \| `"safari"` \| `"edge"` \| `"opera"` \| `internet explorer` | Detected browser family. |
| `browser-version` | state | `string` |  |  | Browser version, as reported by the user agent string. |
| `browser-engine` | state | `string` |  | `"blink"` \| `"gecko"` \| `"webkit"` \| `"trident"` | Rendering engine. |
| `browser-vendor` | state | `string` |  |  | `navigator.vendor`, lowercased. |
| `operating-system` | state | `string` |  | `windows 10+` \| `windows 8.1` \| `windows 8` \| `windows 7` \| `macos <version>` \| `"android"` \| `"ios"` \| `"linux"` | Detected OS and, where available, version. |
| `operating-platform` | state | `string` |  |  | `navigator.userAgentData.platform` (falls back to `navigator.platform`), lowercased. |
| `device-type` | state | `string` |  | `"mobile"` \| `"desktop"` | Coarse device class inferred from the user agent string. |
| `is-standalone` | state | `boolean` |  |  | Present when running in standalone / installed PWA mode. |
| `language-id` | state | `string` |  |  | `navigator.language`, lowercased. |
| `cookies-enabled` | state | `boolean` |  |  | Present when `navigator.cookieEnabled` is `true`. |
| `do-not-track` | state | `string` |  |  | `navigator.doNotTrack`, as reported by the browser. |

#### Provision

| Name | Type | Description |
| --- | --- | --- |
| `provision` | `BrowserInfo` (`{ browserName: string \| null; browserVersion: string \| null; browserEngine: string \| null; browserVendor: string \| null; operatingSystem: string \| null; operatingPlatform: string \| null; deviceType: string \| null; isStandalone: boolean \| null; languageId: string \| null; cookiesEnabled: boolean \| null; doNotTrack: string \| null; userAgent: string \| null; languageIds: string[] \| null; hardwareConcurrency: number \| null; deviceMemory: number \| null; }`) | Every field above, plus values with no CSS-friendly attribute form: full user agent string, `navigator.languages`, hardware concurrency, and device memory (GB, where supported). Not reflected as an attribute. |



### Examples

#### Language

Showing a greeting in your system language (Only Spanish or English for this example). `language-id` mirrors `navigator.language` (lowercased). Spanish prefixes show `Hola`; everything else shows `Hello`. Change the browser language (or override it in DevTools) and reload to flip it.


```html
<div>
  <detect-browser>
    <span lang="en">Hello</span>
    <span lang="es">Hola</span>
  </detect-browser>
  <style>
    #demo-detect-browser-language > :first-child {
      detect-browser {
        [lang="en"],
        [lang="es"] { display: none; }
        &[language-id^="es"] [lang="es"],
        &:not([language-id^="es"]) [lang="en"] { display: inline; }
      }
    }
  </style>
</div>
```


#### Safari-only install hint

iOS Safari has no install prompt, so the sheet renders "Add to Home Screen" steps there, and not once the app is installed. Spoof an iOS Safari user agent in DevTools and reload to trigger it.


```html
<div>
  <small role="note">Spoof an iOS Safari user agent in DevTools and reload.</small>
  <detect-browser></detect-browser>
  <include-content>
    <template>
      <p>Install this app: tap <strong>Share</strong>, then
        <strong>Add to Home Screen</strong>.</p>
    </template>
  </include-content>
  <quark-sheet>
    detect-browser[browser-name="safari"][operating-system="ios"]:not([is-standalone]) + include-content {
      is-active: "";
    }
  </quark-sheet>
</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
