The web app gives one answer to the question of what kind of screen it runs on, and every surface reads that answer. webapp/js/device-class.js defines three device classes and the thresholds between them. This page explains the decision, what each class changes, and how a class change is handled while the app runs. The home screen and dock are on home screen, dock, and launcher.
In short¶
- There are three classes:
pc,tablet, andphone(webapp/js/device-class.js:22-26). pcmeans the pointer can hover and is fine, at any window width. Otherwise a window up to 640 px wide isphoneand a wider one istablet(webapp/js/device-class.js:16-33).- The dock mounts only on
pcwith a window at least 900 px wide (webapp/js/device-class.js:17-19,webapp/js/device-class.js:35-36). - Each class has its own home grid and its own saved layout in the user account (
webapp/js/device-class.js:22-26).
How it works¶
The decision¶
The flowchart shows how the class and the dock are decided.
flowchart TD
start["deviceClass"] --> fine{"matchMedia hover hover and pointer fine"}
fine -->|"yes"| pc["pc: 8 x 5 board"]
fine -->|"no"| width{"innerWidth up to 640"}
width -->|"yes"| phone["phone: 4 x 5 board"]
width -->|"no"| tablet["tablet: 6 x 5 board"]
pc --> wide{"innerWidth at least 900 and not framed"}
wide -->|"yes"| dock["dock mounts"]
wide -->|"no"| float["floating button only"]
phone --> float
tablet --> floatIn words:
hasFinePointer()tests the media query(hover: hover) and (pointer: fine)(webapp/js/device-class.js:16,webapp/js/device-class.js:28).- If it matches, the class is
pc(webapp/js/device-class.js:31). - Otherwise the class is
phonewhenwindow.innerWidthis at mostPHONE_MAX_WIDTH(640), elsetablet(webapp/js/device-class.js:20,webapp/js/device-class.js:32). isPcPointer()adds the width testPC_MIN_WIDTH(900). The dock mounts only when it is true and the page is not a duplicated tile (webapp/js/device-class.js:19,webapp/js/device-class.js:36,webapp/js/app-wheel.js:87-93).- Split screen (the duplicate-home +) exists on the
pcclass only:tilesAllowed()tests the class,syncDupHomehides the button and collapses tiles on the other classes and on every class change (webapp/js/tiles.js:60-64,webapp/js/tiles.js:130-137,webapp/js/tiles.js:148).
What each class sets¶
| Class | Board columns by rows | Server layout key | Local cache key | Dock possible | Split screen | Source |
|---|---|---|---|---|---|---|
pc |
8 by 5 | springboard |
cluster_springboard |
yes, from 900 px | yes | webapp/js/device-class.js:23, webapp/js/tiles.js:61 |
tablet |
6 by 5 | springboard_tablet |
cluster_springboard_tablet |
no | no | webapp/js/device-class.js:24, webapp/js/tiles.js:61 |
phone |
4 by 5 | springboard_phone |
cluster_springboard_phone |
no | no | webapp/js/device-class.js:25, webapp/js/tiles.js:61 |
applyBoardProfile(cls) copies the columns, rows, and keys into the home screen and publishes them to CSS as --board-cols, --board-rows, and data-board-class (webapp/js/home.js:65-79).
Other effects of the class:
- The floating button is hidden only when the user docked it and
isPcPointer()is true. On phones, tablets, and narrow PC windows it always stays, so an open app always has a way home (webapp/js/home.js:1040-1047). - Compact row density applies only on fine-pointer devices (
webapp/js/prefs.js:79-83). - On touch, a tile must be held for 450 ms before it can be dragged; a finger that moves earlier scrolls (
webapp/js/home.js:77-82,webapp/js/touch-gate.js:1-22).
A class change at run time¶
onDeviceClassChange(cb) listens to resize and to the media query, waits 120 ms for the events to settle, and calls back only when the class really changed (webapp/js/device-class.js:38-64). The home screen then applies the new profile, reconciles, renders, and reads the layout for the new class from the server (webapp/js/home.js:4481-4492).
First use of a class¶
When the server holds no layout for the current class and the browser has no cached one, the home screen starts from the PC layout, reflows it onto the smaller board, and saves it as the class's own layout (webapp/js/home.js:4590-4612). The PC layout itself is never changed by another class (webapp/js/device-class.js:6-14).
Why it is built this way¶
- The comment in
webapp/js/device-class.js:1-4states that one module answers the question so that no surface can disagree with another. - The comment in
webapp/js/home.js:52-56states that an eight-column layout squeezed into a phone was unreadable, so each class has its own arrangement. - The comment in
webapp/js/home.js:1040-1043states that the button stays outside a dock so that an open app is never left without a way home.
Rules and boundaries¶
- The project's written engineering rules state that the PC experience is the launcher, detected by hover, a fine pointer, and at least 900 px, and that the mobile and tablet design is defined separately.
- Browser tests:
web-client/_e2e_mobile_home.mjs,web-client/_e2e_mobile_app.mjs,web-client/_e2e_touch_sweep.mjs,web-client/_e2e_app_wheel.mjs.
Interaction with other parts¶
- The saved layouts are UI-state keys; see user UI state.
- Drag surfaces in app bundles use the touch gate from the platform context; see gated app bundles.
Limits and open points¶
- The class uses the window's own width, so a split pane narrower than 640 px on a touch screen is a
phonepane. The dock is never mounted inside a pane (webapp/js/app-wheel.js:93). - No server setting or user preference overrides the class. A search for other writers of
boardClassinwebapp/js/found onlyapplyBoardProfile.
Terms introduced¶
| Term | Code identifier | Definition | Turkish term | Source |
|---|---|---|---|---|
| device class | deviceClass |
The web app's classification of the screen as pc, tablet, or phone. |
cihaz sınıfı | webapp/js/device-class.js:30 |
| board profile | BOARD_PROFILES |
The grid size and layout keys that belong to one device class. | tahta profili | webapp/js/device-class.js:22 |
| PC pointer | isPcPointer |
The test that a fine, hovering pointer has room for the dock. | PC işaretçisi | webapp/js/device-class.js:36 |
Related pages¶
Verified against commit c0d3217 on 2026-11-14.