Layout
Controls how the viewer adapts to narrow containers, and where the tool strip and navigation bar sit. The measurement is the container, not the browser window: the viewer measures its own element whenever it resizes and resolves a size class from that, so a split pane, a phone web view and a desktop sidebar all get the layout they deserve without the host patching anything from outside.
All of the types below are exported from the package, alongside LayoutBreakpoints and the Resolved* mirrors, so a host can type its own configuration. Two things remain the host's responsibility, because the SDK cannot set them: the viewport meta tag and sizing the container in dvh — see Quick start and the container.
Example
// Top toolbar on desktop, bottom toolbar on phones. new PDFViewer(container, doc, { layout: { toolbarPosition: 'top', toolbarPositionCompact: 'bottom' }, });
Options 15
'auto' measures the container; a LayoutSize ('comfortable' | 'compact' | 'narrow' | 'tiny') pins one. The classes are listed in the table below.narrow. Breakpoints are normalized so tiny <= narrow <= compact always holds, whatever you pass; a class whose threshold equals the one above it is never selected.new PDFViewer(container, doc, { layout: { breakpoints: { compact: 1024, narrow: 700, tiny: 520 }, }, });
'top' or 'bottom'. A bottom tool strip sits within thumb reach, which is the conventional place for a primary action bar on a touch device. Tool state and focus survive a runtime flip. The More menu flips to open upward when the toolbar is at the bottom.'top'Default. The tool strip above the document.'bottom'The tool strip below the document, within thumb reach.
toolbarPosition once the layout goes narrow; falls back to toolbarPosition when unset. Ignored while the phone shell is on, because the strip owns the bottom (see phone).layout: { toolbarPosition: 'top', toolbarPositionCompact: 'bottom' }
With 'menu' the bar keeps the tools that fit and moves the rest into a More popover, so every tool stays reachable at any width. The button carries a count of what is in the menu, so it is obvious the product did not shrink — the window did. Which tools stay is decided by use first and importance second: the four tools used most recently always keep their place on the bar, and a tool picked from the menu joins them on the bar at once, displacing the least important. Beyond those four, the content tools (Edit Text, Add Text, Sign, Draw) outrank the mode tools (Pan, Select), and the Outline / Attachments / Signatures panel toggles are the first chrome to move into the menu. Menu contents are listed in declared order, so they do not reshuffle as the container resizes.
Set 'scroll' to keep a single horizontally scrolling strip instead.
'menu'Default. Tools that do not fit collapse into a More popover, ranked by use and importance.'scroll'A single horizontally scrolling strip.
'auto' pushes the document aside at comfortable and switches to an overlay drawer at compact and below. The sidebar — one panel, with Pages, Outline, Attachments, Signatures and Comments as tabs — then floats over the document, and a backdrop appears that dismisses it when tapped. Force either behavior with 'push' or 'overlay'. On a tablet the sidebar always docks beside the page.'auto'Default. Push atcomfortable, overlay atcompactand below.'push'Always push the document aside.'overlay'Always float over the document with a tap-to-dismiss backdrop.
false to keep those mouse-sized on a touch device; the pointer is still reported as coarse, so the on-page handles, the options bars' chips, panels and dialogs keep their touch sizes.The viewer lays itself out for a pointer as well as a width. 'auto' asks the device through (pointer: coarse) and follows the answer if it changes — a tablet gaining a trackpad, a convertible folded into a slate. The result is published as data-ks-pointer="coarse" or "fine" on the viewer element, and on every dialog, sheet, toast and popover the SDK places outside it, so one rule reaches them all. Like data-ks-size, it is a supported styling hook.
Pin 'coarse' to see the touch layout without a touchscreen. 'fine' is for the other case: a kiosk with a touchscreen that is driven by a mouse. On a coarse pointer every field the SDK draws is at least 16px, so iOS does not zoom the page when one is focused; fields on the page itself keep the size the PDF gives them.
'auto'Default. Ask the device, and follow the answer if it changes.'coarse'The touch layout: 44px targets, no hover-only controls, 16px fields.'fine'The mouse layout, for a touchscreen driven by a mouse.
// 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' } });
In a tiny container the bar layout composes itself for a phone. The tool section — the tool groups and the More button — moves into a strip at the bottom of the screen, labeled, five across, with the four most recently used tools kept and the rest behind More, which opens upward. Above the strip an inspector sheet carries the active tool's options: the same options bar the tool mounts at the top of the document on a desktop, laid out as the sheet's content, under a head that names the tool and offers a close. A pill handle folds the options away without leaving the tool. A tool with no options shows its name above the strip and nothing more. The top bar keeps the chrome: the panel toggles, Select / Pan, undo and redo, Print and Download and the host's actions; toolbarPositionCompact is ignored while the shell is on, because the strip owns the bottom.
phone picks one of four placements; every one takes the bar's tool section as a strip and gives the active tool the sheet's body for its options, and they differ in where the two sit. Held sideways — wider than tall and no taller than 500px, which the viewer publishes as data-ks-height="short" at any width — every placement but 'none' becomes the side rails below the comfortable width: the tools down the left, the options down the right, the pages between.
'sheet'Default. The strip along the bottom, the options rising above it in the inspector sheet.'dock'The strip along the bottom, the options as one row of chips above it: the document keeps the most room, and a chip opens its popover as it would above a desktop bar.'split'The tools as a row of labeled chips under the top bar, the options in a sheet at the bottom.'float'Document first: a button in the bottom-right corner unfolds into a column of labeled tools; choosing one folds it away and the button wears that tool. The options rise in a sheet; the page navigator keeps left, clear of the button.'none'Keep the one-bar composition on phones.
// Keep the one-bar composition on phones instead. new PDFViewer(container, doc, { layout: { phone: 'none' } });
'rail' puts the tools down the left edge in 56 × 52px cells, docks the active tool's options down the right at 268px, and docks the sidebar at 300px beside the page rather than sliding it over. The top bar keeps the chrome. 'none' keeps the desktop composition with the touch sizes. A host that wants the rail on every screen, mouse or not, asks for it as the toolbar layout: toolbar: { layout: 'rail' }. On a phone the phone placement still wins.'rail'Default. Tools down the left, the active tool's options down the right, the sidebar docked beside the page.'none'The desktop composition with the touch sizes.
'auto'Default. Sheet on anarrowortinycontainer, or any coarse pointer. Inline everywhere else.'inline'Always type on the page.'sheet'Always use the bottom sheet, at any size.
new PDFViewer(container, doc, { layout: { textEditing: 'auto', // 'auto' | 'inline' | 'sheet' }, });
Size classes
| Class | Default container width | Behavior |
|---|---|---|
comfortable | above 900px | Labels under every icon; side panels push the document aside. |
compact | 900px and below | Icon-only toolbar; side panels become overlay drawers with a tap-to-dismiss backdrop. |
narrow | 640px and below | Tools that do not fit collapse into a More menu; a tighter, smaller bar. |
tiny | 480px and below | Minimum chrome; separators hidden; modals go full-bleed. |
Handled by the SDK — remove host-side workarounds
| If your app currently does this | The SDK now does it |
|---|---|
| Renders its own bottom tool strip with a "More tools" menu | Tools that do not fit collapse into a More popover, ranked by importance. Place the strip with layout.toolbarPosition / toolbarPositionCompact. |
| Opens a custom popup for editing text on phones, with its own font controls | layout.textEditing opens a built-in bottom sheet carrying the real font, size, bold, italic, color, delete, Save & Close and Cancel controls. |
| Shows its own "double-tap or press and hold" hint over text blocks | The tool shows that hint itself on a single tap, and accepts both gestures. |
| Works around Manage Pages actions being clipped or unreachable | The dialog goes full-bleed and its action row wraps in a narrow browser window; reordering works by touch through a grip handle. |
| Forces side panels closed, or narrows them, on small screens | Panels become mutually exclusive overlay drawers with a tap-to-dismiss backdrop below the compact breakpoint. |
Adds touch-action or gesture shims so drawing and selection work on touch | Drag tools claim the gesture themselves while active, and release it the moment they deactivate. |
| Pads the bottom of the viewer for the home indicator | Bottom bars and full-screen dialogs use the safe-area insets, given viewport-fit=cover. |
Styling hook: data attributes on the viewer element
| Attribute | Value |
|---|---|
data-ks-theme | light or dark; absent for auto. |
data-ks-size | The resolved size class. |
data-ks-pointer | coarse or fine; also on every dialog, sheet, toast and popover placed outside the viewer. |
data-ks-height | short when the viewer is wider than tall and no taller than 500px, at any width. |
data-ks-toolbar | The toolbar placement. |
data-ks-nav | The navigation bar placement. |
data-ks-panels | The panel display mode. |
data-ks-touch | Touch targets. |
data-ks-text-edit | The text editing surface. |
data-ks-phone | The phone placement, or rails, while the phone shell is on. |
data-ks-rails | on while the rails are. |
data-ks-tablet | on while the rails are a tablet's. |
data-ks-tool | The active tool's name; absent when none is. |
data-ks-touch-drag | on while a drag tool is active and claims finger drags. |
data-ks-print | on while a print is prepared. |
Notes
Styling hook. The attributes in the table are supported API for host overrides, for example .ks-viewer[data-ks-size='tiny'] .my-custom-action { display: none; }. See Theme tokens for what else a stylesheet may target.
The phone shell's sheet. In the sheet the bars lay their controls out inline rather than as chips that open popovers: Draw shows a Stroke / Fill switch over the color panel and the weight panel under it, Highlight and Mark text show the color panel with the highlighter inks first, Erase its size slider across the row, Stamp its panel of stamps, Edit Text its font and size over Bold, Italic, Color, Delete and Done. The sheet's head also carries the live readout — an object's size while it is resized, its angle while it is turned — that a desktop shows beside the object, because on a phone the finger doing the dragging covers that spot. A text selection puts its bar in the sheet too, a row of pills above the strip, with its overflow unfolding beneath. 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.
Your own chrome on a phone. A host that draws its own chrome (toolbar.layout: 'none') has no strip to move, and gets the rest on a phone: in a tiny container the active tool's options still rise in the inspector sheet — the same inline layout, head, close and readout — rather than staying a desktop bar wrapped over the page; while no tool runs, the sheet is gone. layout.phone keeps its meaning for the sheet ('dock' gives the row of chips, 'none' keeps the desktop bar) and the sideways rails dock the sheet down the right with no rail on the left. A tablet keeps the desktop composition the host laid its chrome around. The sheet rises from the bottom edge of the SDK's container: a bottom bar of your own belongs below the container, not over it, and the sheet publishes its height on --ks-shell-inset-bottom as ever.
The tablet. On a touch screen the drawing tools' options carry an Input row: Finger (the default) draws with anything; Stylus only ignores a finger, so a hand can rest on the glass while a pen draws and a finger still scrolls the page. One setting, shared by every tool that draws, kept for the session. Manage Pages opens at 800px with the pages four to a row and a 24px check on each selected one; Security opens at 720px with its two jobs side by side; every dialog's controls are 44px (these two dialog sizes follow the browser window, not the container). Crossing into or out of the phone width closes the active tool. The ribbon layout has no phone composition and keeps its own behavior at every width.
Below 480px the dialogs are the screen — title along the top, body scrolling between, actions pinned above the home indicator — and so are Manage Pages, the Security dialog and the sidebar, which becomes "Contents" with its tabs as a row beneath. A toast sits above the strip and the sheet: the viewer tells every portal how tall they are through --ks-portal-inset-bottom. A host's own bottom-anchored surface inside the viewer can read the same height from --ks-shell-inset-bottom on .ks-viewer.
Touch targets on the page. On a coarse pointer the on-page controls change shape as well as reach. A selected object's grips draw at 13px and its rotate handle as a 26px button on a longer stalk, each with a 44px hit box; its action bar sits above the object rather than on it, with 44px actions, and stays inside the page. A text selection gets two round handles under the line, draggable to extend it. Note pins and links take 44px hit boxes; a tap on a link in the document first shows where it goes, with an Open button, and only a second tap — or Open — follows it (links.pill: false turns this off). The erase brush's "Will remove" label rides above the ring instead of under the thumb. Thumbnails show their Rotate and menu on every row, and the search bar's arrows grow to 44px; in a tiny container the bar is a sheet across the top of the document with the field taking the row.
Form fields on touch. Form fields set their text at 16px and scale the element to fit its box, so iOS does not zoom the page into a focused field. While a field has focus a stepper along the bottom of the viewer — Previous, Next, "Field 3 of 11" with the field's name, Done — replaces Tab, which a phone has no key for; it follows the visual viewport up when the keyboard opens and scrolls the focused field clear of it. Read-only and hidden fields are skipped; Done closes the keyboard. forms.stepper: false leaves only the fields.
How a text block is opened on touch. A single tap selects the block and shows a short hint — _"Double-tap or press and hold to edit this text"_ — because a tap that only selects otherwise looks like nothing happened. Either a double-tap or a press-and-hold of about 0.6 seconds then opens the sheet. Holding is canceled the moment the finger moves, so it never steals a drag of the block. The sheet carries the same font, size, bold, italic, color and delete controls as the desktop options bar, plus Save & Close and Cancel. Typing in the sheet edits the text exactly as typing inline does, so font substitution and what is written to the file are identical.
Touch behavior. Drag tools claim the gesture: freehand, line, arrow, rectangle, ellipse, highlight, erase, link, note, pan and both text-selection tools bind their drag to the page; while one is active the viewer sets data-ks-touch-drag="on" and the pages stop handing finger drags to the scroller, and the flag is removed the moment the tool deactivates. A system swipe or an incoming call (pointercancel) calls off the drag in progress and removes its preview, so the next drag starts clean. The outline panel's rename / delete buttons, hover-revealed on a desktop, are always visible and sized for a fingertip on a coarse pointer; the attachments panel shows download on every row at any pointer, and remove too where attachment editing is on. Edit Text watches visualViewport and scrolls the block being edited above the keyboard. Double-tap-to-zoom is suppressed on the viewer (touch-action: manipulation), which removes the browser's ~300ms click delay; panning is unaffected. A bottom toolbar and the floating navigation bar clear the home indicator, and full-screen dialogs clear the notch, provided the host sets viewport-fit=cover.
Two fingers zoom the document. A pinch on the pages zooms the document about the point between the fingers, and the document follows them as they move together; the browser no longer zooms your whole page for it. A second finger landing while the first is drawing turns the gesture into a pinch: the stroke, selection or drag the first finger began is called off and nothing is written, and the finger left behind draws nothing until it lifts. navigation.pinchZoom: false leaves pinching to the browser.