# provider-orientation

Declarative device orientation / compass — request a heading, read the
normalized bearing from attributes/state.

## Features

- **Attribute-driven** Request + read a compass heading through attributes
- **Normalized bearing** `0`–`360°` from magnetic north on both iOS and
  Android, one shape either way
- **Throttled updates** `compass-throttle-ms` caps update frequency
  (Android can fire 60-200 Hz)
- **iOS-aware** Works with the required user-gesture permission flow

## Installation


`@excom/provider-orientation` v0.1.4

```bash
pnpm add @excom/provider-orientation
```

```bash
npm install @excom/provider-orientation
```

```bash
yarn add @excom/provider-orientation
```

### Import

```ts
import "@excom/provider-orientation";
```



## Usage

**Requires a user gesture on iOS.** `DeviceOrientationEvent
.requestPermission()` must run synchronously inside a click handler or
Safari denies it — so set `is-paused` and invoke the `--request` command
from a button rather than relying on the connect-time auto-request (the
handler runs in a microtask of the click, inside its user activation):

```html
<button type="button" command="--request" commandfor="compass">
  Enable compass
</button>
<provider-orientation id="compass" is-paused></provider-orientation>
```

```css
compass-needle {
  transform: rotate(calc(var(--bearing, 0) * 1deg));
}
```

Android and desktop browsers with a sensor don't require permission and
will start listening as soon as the request fires; browsers with no
sensor at all simply never report a reading.

### API Reference


#### Attributes

| Name | Surface | Type | Default | Values | Description |
| --- | --- | --- | --- | --- | --- |
| `is-paused` | option | `boolean` |  |  | Skip requesting on connect. On iOS this is effectively required (a connect-time request happens outside a user gesture and will be denied) — pair with `provider-orientation-request` from a click handler instead (see class docs). |
| `compass-throttle-ms` | option | `number` | `100` |  | Minimum ms between `provider-orientation-success` updates. Android can fire `deviceorientationabsolute` at 60-200 Hz; without throttling that floods listeners and CSS/Quark bindings. |
| `is-requesting` | state | `boolean` |  |  | iOS only: the permission request is pending (between `provider-orientation-request` and the user's response). |
| `is-success` | state | `boolean` |  |  | Listening for orientation updates (permission granted where required). Stays set across updates. |
| `is-error` | state | `boolean` |  |  | The permission request was denied, or listening failed to start. |

#### Provision

| Name | Type | Description |
| --- | --- | --- |
| `provision` | `ProviderOrientationSuccess` (`{ bearing: number; alpha: number \| null; }`) | Latest reading: `{ bearing, alpha }` on success, or the error on failure. Not reflected as an attribute. |

#### Fires

| Name | Type | Description |
| --- | --- | --- |
| `provider-orientation-success` | `ProviderOrientationSuccessEvent` (`CustomEvent & { type: "provider-orientation-success"; detail: { bearing: number; alpha: number \| null; }; bubbles: true; cancelable: true; composed: true }`) | Dispatched on every compass update (throttled by `compass-throttle-ms`). `bearing` is normalized 0-360°; `alpha` is the raw `DeviceOrientationEvent.alpha` where available. |
| `provider-orientation-error` | `ProviderOrientationErrorEvent` (`CustomEvent & { type: "provider-orientation-error"; detail: string \| Error; bubbles: true; cancelable: true; composed: true }`) | Dispatched when the permission request is denied, or fails for any other reason. |

#### Commands

| Command | Action |
| --- | --- |
| `--request` | Requests permission (iOS) and starts listening for orientation updates. Invoke it from a button (`<button command="--request" commandfor="…">`) so the user activation is there. |

#### Default actions

| Event | Default behavior (unless preventDefault() is called) |
| --- | --- |
| `provider-orientation-success` | Stores `{ bearing, alpha }` in `provision` and sets `is-success` (clearing `is-requesting` / `is-error`). |
| `provider-orientation-error` | Stores the error in `provision` and sets `is-error` (clearing `is-requesting` / `is-success`). |



### Examples

#### Request on click


```html
<div>
  <button type="button" command="--request" commandfor="orient">
    Enable compass
  </button>
  <provider-orientation id="orient" is-paused>
    <output></output>
  </provider-orientation>
  <p class="status">No reading yet — click above. Requires a device
    sensor (desktop browsers usually have none).</p>
  <quark-sheet>
    provider-orientation {
      $reading: prop("provision");
      &[is-success] output {
        content: if($reading.bearing != null: "Heading #{$reading.bearing.toFixed(0)}°"; else: "Listening — no reading yet.");
      }
      &[is-error] output { content: $reading.message or $reading; }
    }
  </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
