Develop and iterate on IWSDK UI panels efficiently. Use when working on PanelUI components, debugging UI layout, or improving UI design in IWSDK applications.
64
76%
Does it follow best practices?
Impact
—
No eval scenarios have been run
Passed
No known issues
Optimize this skill with Tessl
npx tessl skill review --optimize ./.claude/skills/iwsdk-ui-panel/SKILL.mdThis skill teaches the efficient workflow for developing UI panels in IWSDK applications using temporary ScreenSpace positioning and backdrop techniques.
When working on a UI panel, follow these steps for rapid iteration:
Temporarily add the ScreenSpace component to your PanelUI entity to make it fill the 2D screen during development:
import { ScreenSpace } from '@iwsdk/core';
world
.createTransformEntity(panelHolder)
.addComponent(PanelUI, {
config: '/ui/your-panel.json',
maxWidth: 1.0,
maxHeight: 0.5,
})
.addComponent(ScreenSpace, {
width: '90vw', // Fill 90% of viewport width
height: '90vh', // Fill 90% of viewport height
top: '5vh', // Center with 5% margins
left: '5vw',
});Important: This is temporary for development only. Remove before production.
Create a solid color backdrop far from your gameplay area for clean UI visibility:
const backdrop = new Mesh(
new BoxGeometry(20, 20, 0.1),
new MeshBasicMaterial({ color: 0x1a1a2e }),
);
backdrop.position.set(0, 0, -50); // Far from gameplay
scene.add(backdrop);Move the camera very close to the backdrop (within 0.5m) to eliminate background distractions:
// Position camera very close to backdrop for clean UI development
camera.position.set(0, 0, -49.5); // Just 0.5m from backdrop at z=-50
camera.lookAt(0, 0, -50);Why close? The backdrop must fill the entire field of view to block out the 3D scene. Being far away (50m) won't work - you'll still see the environment around the edges.
Now you can rapidly iterate on your UI:
.uikitml fileThe ScreenSpace component makes the panel "follow" the camera, so it appears as a 2D overlay on your backdrop.
When you enter VR mode:
This dual-mode behavior is handled automatically by the ScreenSpaceUISystem.
UIKit components expose size information through signals. Log these to debug layout issues:
const document = PanelDocument.data.document[entity.index];
console.log('computedSize:', document.computedSize); // Intrinsic size in cm
console.log('targetSize:', document.targetSize); // Target size in meters
console.log('rootElement.size.value:', document.rootElement?.size?.value);
console.log('document.scale:', document.scale); // Applied scaleUnderstanding the output:
computedSize: UIKit's rendered size in centimeters (based on your CSS)targetSize: The requested size in meters (from PanelUI maxWidth/maxHeight or ScreenSpace constraints)document.scale: Uniform scale factor applied to fit target while preserving aspect ratioExample output:
computedSize: { width: 100, height: 50 } // 100cm × 50cm
targetSize: { width: 0.274, height: 0.168 } // 0.274m × 0.168m
document.scale: { x: 0.274, y: 0.274, z: 0.274 } // Scaled down by 0.274xThe ScreenSpace component positions panels using CSS-like properties:
.addComponent(ScreenSpace, {
width: '90vw', // CSS size: px, vw, vh, %, auto
height: '90vh', // CSS size: px, vw, vh, %, auto
top: '5vh', // CSS position: px, %, vh, auto
bottom: 'auto', // CSS position: px, %, vh, auto
left: '5vw', // CSS position: px, %, vw, auto
right: 'auto', // CSS position: px, %, vw, auto
zOffset: 0.2, // Distance in meters from camera (default: 0.2m)
});How it works:
zOffset distance, using CSS layoutScreenSpaceUISystemUse flexbox in your UIKitML for centered layouts:
.container {
display: flex;
flex-direction: column; /* Stack vertically */
justify-content: center; /* Center vertically */
align-items: center; /* Center horizontally */
}Set border-radius: 0 for square edges that align with grid systems:
.panel {
border-radius: 0; /* Square edges */
border-width: 0.15;
border-color: #27272a;
}Remember: UIKit uses centimeters for sizing, world space uses meters:
width: 100 in UIKitML = 100cm = 1.0mmaxWidth: 1.0 in PanelUI = 1.0 meterBefore committing or going to production:
The panel will remain at its world space position defined by the entity's transform.
// 1. Enable spatialUI feature
World.create(container, {
features: { spatialUI: true },
}).then((world) => {
const { scene, camera } = world;
// 2. Create backdrop for UI development
const backdrop = new Mesh(
new BoxGeometry(20, 20, 0.1),
new MeshBasicMaterial({ color: 0x1a1a2e }),
);
backdrop.position.set(0, 0, -50);
scene.add(backdrop);
// 3. Position camera close to backdrop
camera.position.set(0, 0, -49.5);
camera.lookAt(0, 0, -50);
// 4. Create your UI panel with ScreenSpace
const panelHolder = new Group();
panelHolder.position.set(0, 1.5, -1.0); // World space position for VR
scene.add(panelHolder);
world
.createTransformEntity(panelHolder)
.addComponent(PanelUI, {
config: '/ui/my-panel.json',
maxWidth: 1.0,
maxHeight: 0.5,
})
.addComponent(ScreenSpace, {
// TEMPORARY for development
width: '90vw',
height: '90vh',
top: '5vh',
left: '5vw',
});
});Panel not filling screen:
Background still visible:
Panel doesn't return to world space in VR:
spatialUI: true in World.create featuresSize signals showing unexpected values:
b3d1162
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.