# Phones and touch, without fighting the browser

Meet the two host-side requirements the SDK cannot do for you, pick a phone and tablet composition, open at the right zoom, and keep pinch, the form stepper and the touch sizes the SDK provides.

- Level: Mobile
- Uses: `viewport meta`, `100dvh`, `layout.phone`, `layout.tablet`, `layout.pointer`, `layout.touchTargets`, `navigation.pinchZoom`, `navigation.defaultZoom`, `forms.stepper`

The measurement is the container, not the browser window: the viewer observes its own element with a `ResizeObserver` and resolves a size class from that (`comfortable`, `compact` at 900px and below, `narrow` at 640px and below, `tiny` at 480px and below). It lays itself out for a pointer as well, through `(pointer: coarse)`. Two things are outside the SDK's control and must come from the page.

## 1. The viewport meta tag is mandatory

Without it a mobile browser lays the page out at ~980px and reports that width to the SDK, so the viewer picks its desktop layout on a phone and every responsive rule stays dormant. `viewport-fit=cover` is what lets the safe-area handling reach the notch and home-indicator insets. Do not add `user-scalable=no`: a two-finger pinch on the document zooms the document itself (`navigation.pinchZoom`), and the rest of your page keeps the browser's own pinch-zoom only without it.

`index.html`

```html
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
```

## 2. Size the container with dvh, not vh

On mobile `100vh` is the viewport without the browser's collapsible address bar, so a `100vh` container is taller than the screen and the bottom of the viewer sits under the chrome. Do not set `overflow`: the SDK manages its own scrolling.

`app.css`

```css
#pdf-container {
  width: 100%;
  height: 100dvh; /* not 100vh */
}
```

## 3. Pick the phone and tablet composition

In a `tiny` container the bar layout composes itself for a phone: the tool section moves into a strip at the bottom, five across, with the active tool's options in an inspector sheet above it. `layout.phone` picks one of four placements (`'sheet'`, the default; `'dock'`; `'split'`; `'float'`) or `'none'` to keep the one-bar composition. A touch screen wider than a phone gets `layout.tablet: 'rail'` (the default): the tools down the left edge, the active tool's options docked down the right, the sidebar docked beside the page; `'none'` keeps the desktop composition with the touch sizes. `touchTargets: true` (the default) grows the toolbar, navigation bar and search bar to 44px hit areas on a coarse pointer.

`viewer.ts`

```ts
new PDFViewer(container, doc, {
  layout: {
    phone: 'dock', // the strip along the bottom, the options as one row of chips above it
    tablet: 'rail',
    touchTargets: true,
  },
  navigation: {
    pinchZoom: true, // two fingers zoom the document, about the point between them
    defaultZoom: 'fit-width',
  },
  forms: { stepper: true }, // Previous / Next / Done bar while a field has focus
});

// Everything a phone would get — 44px targets, no hover-only controls,
// 16px fields — on a desktop, for checking a design.
new PDFViewer(container, doc, { layout: { pointer: 'coarse' } });
```

> `navigation.defaultZoom` takes `'fit-width'` (the default), `'fit-page'` or a number. A number is also a ceiling: a page that would overflow the width at it opens at fit-width instead, on any screen, and a phone-sized layout (narrow/tiny) fits it again when turned or shrunk to that size. While the zoom is a fit mode the PDF re-fits when the container's width changes.

## 4. Let the SDK handle the gestures

Drag tools claim the gesture: while one is active the viewer sets `data-ks-touch-drag="on"` and the pages stop handing finger drags to the scroller, so the drag draws instead of scrolling; the flag is removed the moment the tool deactivates. Double-tap-to-zoom is suppressed on the viewer (`touch-action: manipulation`). A second finger landing while the first is drawing turns the gesture into a pinch and the stroke is called off. Form fields set their text at 16px so iOS does not zoom the page into a focused field, and the stepper follows the visual viewport up when the keyboard opens.

`app.css`

```css
/* The data attributes are supported hooks for host overrides. */
.ks-viewer[data-ks-size='tiny'] .my-custom-action {
  display: none;
}

/* A bottom-anchored surface of your own inside the viewer clears the strip and sheet. */
.ks-viewer .my-banner {
  bottom: var(--ks-shell-inset-bottom);
}
```

## Gotchas

- Crossing into or out of the phone width closes the active tool, since its options are mounted where the old composition put them. The ribbon layout has no phone composition yet and keeps its own behavior at every width.
- `toolbarPositionCompact` is ignored while the phone shell is on, because the strip owns the bottom.
- Held sideways (wider than tall and no taller than 500px, published as `data-ks-height="short"`), every placement but `'none'` becomes the side rails below the `comfortable` width.
- The SDK holds no file name and no save state, on a phone as on a desktop: the document's name and "saved just now" belong to your own header above the viewer.
- Manage Pages opens at 800px and Security at 720px; these two dialog sizes follow the browser window, not the container. Below 480px the dialogs are the screen.

## Related

- [Quick start and the container](https://kernstra.com/docs/web-sdk/guides/container)
- [Layout](https://kernstra.com/docs/web-sdk/config/layout/phone)
- [Navigation bar](https://kernstra.com/docs/web-sdk/config/navigation/pinchzoom)
- [Forms](https://kernstra.com/docs/web-sdk/config/forms/stepper)
- [Theme tokens](https://kernstra.com/docs/web-sdk/guides/theme-tokens)

Web page: https://kernstra.com/docs/web-sdk/recipes/phones-touch
