Skip to content

Commit 7744d20

Browse files
authored
docs: UI defaults follow-ups (review of #76) (#77)
* docs: correct UI defaults follow-ups on top of #76 Review follow-ups verified against @dcl/react-ecs on the merged branch. - build-ui/SKILL: an incomplete virtual size does not warn. There is no such log in react-ecs; the only console.log is the mobile 16:9 override. Since the skill tells an agent to trust that warning, the wrong claim is worse here than in prose. - build-ui/SKILL: add the scene-wide consequence of an incomplete size -- setUiRenderer wins the arbitration if it mentions either dimension, so it disables the virtual screen for the whole scene and discards a valid size passed to addUiRenderer, silently. - ui-components: UiCanvasInformation width/height are RAW canvas pixels, not "virtual/scaled units when a virtual size is set". The SDK derives the scale factor from them, so they cannot already be scaled. This matters for the slider helper, which divides by them. - build-ui/SKILL, advanced-input/SKILL: the "~3x scaling for mobile" rule of thumb survived in both files, one of them in a paragraph this branch rewrote. With dpr out of the scale factor most of that 3x is already applied, and the 1600x720 mobile virtual screen adds ~1.2x more. - build-ui/SKILL, ui-components: 'interactable' is recommended for whole-UI application without a client floor. It needs an explorer that reports the area: mobile client 1.12.1 onwards, which is also the release that normalizes the 'device' area between Android and iOS. Older clients report no margins and the inset silently does nothing. The 7.26.0 version gate is left as written -- confirmed as the release number. * fix: the incomplete-size case does log, the <= 0 opt-out is the silent one My earlier correction was verified against the cherry-picked branch, not against what merged. Commit 2c1128ff added isPartialVirtualSize() and a once-per-size console.log, so main carries two logs in @dcl/react-ecs and the original wording was right for this case. The distinction is still worth stating, inverted: a value <= 0 is the documented opt-out and stays silent; a half-given size is treated as a mistake and is reported. Both disable the virtual screen either way.
1 parent 4777da7 commit 7744d20

3 files changed

Lines changed: 12 additions & 6 deletions

File tree

advanced-input/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ Key facts from the mobile docs expansion (commit `17ca7be`):
236236
- **Touch-only input** -- no mouse hover states, keyboard shortcuts, or right-click.
237237
- **`borderRadius` unsupported on mobile UI** -- avoid rounded corners in mobile-targeting scenes.
238238
- **Static HUD** -- the mobile client has fixed on-screen controls (joystick, action buttons) that cannot be repositioned or customized from the scene.
239-
- **UI designed for desktop needs ~3x scaling for mobile readability.**
239+
- **The old "~3x scaling for mobile" rule no longer applies as written.** On SDK 7.26.0+ pixel-sized UI is already ~2–3× larger on a phone than before (`devicePixelRatio` was removed from the UI scale factor), and the mobile virtual screen (`1600x720` vs desktop's `1920x1080`) adds ~1.2× more. Start from the desktop sizes, measure on a device, and scale up only what comes up short. See [[build-ui]].
240240
- **UI is kept inside the device's safe area (notch, home indicator) automatically on SDK 7.26.0+** — the renderer's `screenInset` option defaults to `'device'`. Only pass `screenInset: 'none'` if you want the UI over the whole screen; the `ScreenInsetArea` component is then available to inset individual subtrees. Below 7.26.0, wrap the UI in `ScreenInsetArea` (from `@dcl/sdk/react-ecs`) yourself. See [[build-ui]].
241241
- **SDK features not yet on mobile:** ParticleSystem, scene dynamic lights (PBPointLight), AudioAnalysis, nine-slice UI tile mode. Check the docs for the latest feature parity tracker.
242242

build-ui/SKILL.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,17 @@ The options argument is optional at the API level. **On SDK 7.26.0+, omitting it
3636
| No virtual size passed, mobile | `1600x720` |
3737
| A 16:9 size passed (e.g. `1920x1080`), mobile | overridden to `1600x720`, logged once to console |
3838
| A non-16:9 size passed | used as-is on every platform |
39-
| A size with any value `<= 0` | virtual screen **disabled** — raw canvas pixels, no scaling |
40-
| Only one of the two dimensions passed | also **disabled** (both are required), and warns once to the console |
39+
| A size with any value `<= 0` | virtual screen **disabled** — raw canvas pixels, no scaling. Silent: this is the documented opt-out |
40+
| Only one of the two dimensions passed | also **disabled** (both are required), and logged once per size — it is treated as a mistake, not an opt-out |
4141

4242
So on 7.26.0+, `{ virtualWidth: 0, virtualHeight: 0 }` — not omitting the options — is how you opt into raw-pixel layout. Only do that if the user explicitly asks for it. **Below 7.26.0 there are no defaults: omitting the options is what disables scaling.** See the version gate below.
4343

4444
Because the default rule above has you pass the size explicitly either way, generated code behaves identically on both sides of that boundary — which is a second reason to always pass it.
4545

4646
The virtual size is scene-wide, resolved as: the size on `setUiRenderer` wins → else the first `addUiRenderer` that passed one → else the platform default. Options carrying only a `screenInset` don't count as a passed size.
4747

48+
Note that `setUiRenderer` wins the arbitration if it mentions *either* dimension, even when the size is incomplete and therefore invalid. So `setUiRenderer(ui, { virtualWidth: 1920 })` disables the virtual screen for the whole scene and discards a valid size passed to any `addUiRenderer`. The SDK logs it once per size, but the scene still loses its virtual screen. Never emit a single dimension.
49+
4850
API (verified against `@dcl/react-ecs`, file `dist/system.d.ts`):
4951

5052
```ts
@@ -118,9 +120,9 @@ export function setupUi() {
118120
119121
**Dropdown** — Selection dropdown. Key props: `options` (string[]), `selectedIndex`, `onChange`, `fontSize`, `uiTransform`, `disabled`.
120122
121-
**ScreenInsetArea** — Wrapper that keeps children inside the device's hardware-reserved margins (notch, status bar, home indicator, rounded corners). **Usually unnecessary now: `screenInset` defaults to `'device'`, which already does this for the whole renderer.** Reach for the component only when the renderer opted out with `screenInset: 'none'` and you want to protect just one subtree — wrapping on top of the default double-applies the inset. On mobile it positions itself absolutely using the insets the device reports; on desktop the insets are `(0,0,0,0)`, so it's a no-op. It owns its own `positionType` and `position`; any values you pass for those in `uiTransform` are ignored. All other `uiTransform` props (`padding`, `flexDirection`, `alignItems`, …) and components (`uiBackground`, `onMouseDown`, …) work as usual. A child sized `width: '100%', height: '100%'` fills the safe area exactly. Distinct from the *Decentraland system HUD* reserved zones (joystick, chat, profile, interaction button) — those still need to be avoided manually. UI designed for desktop typically needs sizes scaled ~3× for mobile readability.
123+
**ScreenInsetArea** — Wrapper that keeps children inside the device's hardware-reserved margins (notch, status bar, home indicator, rounded corners). **Usually unnecessary now: `screenInset` defaults to `'device'`, which already does this for the whole renderer.** Reach for the component only when the renderer opted out with `screenInset: 'none'` and you want to protect just one subtree — wrapping on top of the default double-applies the inset. On mobile it positions itself absolutely using the insets the device reports; on desktop the insets are `(0,0,0,0)`, so it's a no-op. It owns its own `positionType` and `position`; any values you pass for those in `uiTransform` are ignored. All other `uiTransform` props (`padding`, `flexDirection`, `alignItems`, …) and components (`uiBackground`, `onMouseDown`, …) work as usual. A child sized `width: '100%', height: '100%'` fills the safe area exactly. Distinct from the *Decentraland system HUD* reserved zones (joystick, chat, profile, interaction button) — avoid those with `screenInset: 'interactable'` or by hand. Do **not** apply the old "scale sizes ~3× for mobile" rule of thumb on 7.26.0+: with `devicePixelRatio` out of the scale factor, pixel-sized UI is already ~2–3× larger on a phone than it used to be, and the `1600x720` mobile virtual screen adds ~1.2× on top. Start from the desktop sizes and only scale up what actually measures too small on a device.
122124
123-
**InteractableArea** — Wrapper that keeps children inside the renderer-reported *interactable area* — the part of the screen NOT covered by the client's own UI (minimap, chat window, platform overlays). Reads `UiCanvasInformation.interactableArea` and constrains children via absolute positioning; on the Unity desktop client the left ~25% of the screen is reserved, so children fill the remaining ~75%. **Prefer `screenInset: 'interactable'` on the renderer for a whole-UI application**; use the component for a single subtree, or when the renderer uses a different inset. Like `ScreenInsetArea`, it owns `positionType`/`position` (values you pass are ignored) and falls back to zero insets (no-op) when unavailable. Import from `@dcl/sdk/react-ecs`; usage `<InteractableArea><MyHud /></InteractableArea>`. Distinct from `ScreenInsetArea` (which avoids *device* hardware margins, not client UI). See `{baseDir}/references/ui-components.md` → InteractableArea.
125+
**InteractableArea** — Wrapper that keeps children inside the renderer-reported *interactable area* — the part of the screen NOT covered by the client's own UI (minimap, chat window, platform overlays). Reads `UiCanvasInformation.interactableArea` and constrains children via absolute positioning; on the Unity desktop client the left ~25% of the screen is reserved, so children fill the remaining ~75%. **Prefer `screenInset: 'interactable'` on the renderer for a whole-UI application**; use the component for a single subtree, or when the renderer uses a different inset. Either form needs an explorer that reports the area: it works on desktop, and on mobile from client `1.12.1` onwards — older mobile clients report no margins and the inset silently does nothing. Like `ScreenInsetArea`, it owns `positionType`/`position` (values you pass are ignored) and falls back to zero insets (no-op) when unavailable. Import from `@dcl/sdk/react-ecs`; usage `<InteractableArea><MyHud /></InteractableArea>`. Distinct from `ScreenInsetArea` (which avoids *device* hardware margins, not client UI). See `{baseDir}/references/ui-components.md` → InteractableArea.
124126
125127
## Adding Independent UI Renderers (addUiRenderer)
126128

build-ui/references/ui-components.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,8 @@ Wraps children so they stay inside the renderer-reported **interactable area**
244244

245245
**For a whole UI, prefer `screenInset: 'interactable'` on the renderer.** Use the component to inset a single subtree, or when the renderer sits in a different area.
246246

247+
⚠️ **Client support.** Either form needs an explorer that reports the area. It works on desktop, and on mobile from client version `1.12.1` onwards — older mobile clients report no margins, so the area falls back to the whole screen and the inset silently does nothing. Note that `1.12.1` is also the release that normalizes the `'device'` area between Android and iOS, so treat it as the floor for any inset-sensitive mobile layout.
248+
247249
```tsx
248250
import ReactEcs, { ReactEcsRenderer, UiEntity, InteractableArea } from '@dcl/sdk/react-ecs'
249251

@@ -370,7 +372,9 @@ const Modal = () => {
370372

371373
## UiCanvasInformation (Responsive Design)
372374

373-
Fields: `width`, `height`, `devicePixelRatio` (all numbers, in virtual/scaled units when a virtual size is set), plus `screenInsetArea` and `interactableArea` (`BorderRect``top`/`bottom`/`left`/`right` in canvas pixels). `devicePixelRatio` is a display-density hint, useful for picking a 1x/2x/3x texture.
375+
Fields: `width`, `height`, `devicePixelRatio` (all numbers), plus `screenInsetArea` and `interactableArea` (`BorderRect``top`/`bottom`/`left`/`right`). `devicePixelRatio` is a display-density hint, useful for picking a 1x/2x/3x texture; it does not take part in UI layout.
376+
377+
⚠️ **`width` and `height` are RAW canvas pixels, not virtual/scaled units** — the SDK derives the UI scale factor from them (`Math.min(width / virtualWidth, height / virtualHeight)`), so they cannot already be scaled. The two `BorderRect`s are raw canvas pixels too. They are the right input for *decisions* (which layout, which texture resolution), not for computing sizes — the renderer already scales pixel values for you.
374378

375379
```typescript
376380
import { UiCanvasInformation, engine } from '@dcl/sdk/ecs'

0 commit comments

Comments
 (0)