Vectojs responsive layout
Skill vectojs/vectojs-skills/skills/vectojs-responsive-layout
Use when designing adaptive VectoJS canvas layouts with browser resize or zoom, Stack, Flow, Card, ScrollView, VirtualList, responsive panels, dashboards, forms, or layout reflow.From its SKILL.md
npx -y skills add vectojs/vectojs-skills --skill vectojs-responsive-layoutAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
10.8 KB, ~1.5k tokens by cl100k_base, as published. Nobody here has run it
VectoJS Responsive Layout
Use this skill when a VectoJS UI must adapt to viewport size, browser zoom, density, content length, or resizable panels.
Layout workflow
- Define layout state in logical CSS pixels, not backing-store pixels.
- Resize the
Scenefrom the containing element, not blindly fromwindow, when embedding in an app shell. - Compose with
Stack,Flow,Card,ScrollView,VirtualList,TreeView, and resizable panels. - After direct child size changes, call the parent layout method and mark the scene dirty.
- Keep one scroll owner per region; avoid nested wheel handlers without clear boundaries.
- Use role labels and semantic regions even for canvas-rendered layout containers.
Read references/layout-recipes.md for copyable patterns.
For OS/browser/zoom/DPR consistency and text-selection alignment (embedded
scene.resize() bridge, Firefox Range recalibration, web-font race, the
DPR-1-vs-2 test matrix), read references/cross-environment.md.
Design rules
- Use
scene.resize(width, height)for logical layout dimensions. - Let VectoJS/Canvas handle high-DPI backing stores; do not multiply layout coordinates by DPR.
- Make breakpoint decisions from available logical width/height.
- Prefer reflowing existing entities to destroying and recreating the full tree.
- Reserve virtualization for long lists/tables/trees; do not render thousands of offscreen rows as regular children.
- Treat browser zoom as a layout-input multiplier only when product requirements explicitly need density changes beyond normal CSS pixel behavior.
Common mistakes
| Mistake | Correction |
|---|---|
Reading canvas.width for layout | Use container CSS size or scene.width / scene.height. |
| Recreating every component on resize | Reposition/reconfigure existing entities, then scene.markDirty(). |
| ScrollView inside another wheel-capturing region | Give each wheel gesture one owner or define escape behavior. |
| Fixed desktop-only coordinates | Add breakpoint functions and test narrow, wide, and zoomed layouts. |
| Changing child size without relayout | Call stack.layout() / equivalent and mark dirty. |
Hand-syncing content.width = panel.width after every PanelGroup drag/resize | Panel.setContent(content, fit?) / Card.setContent(content, fit?) (>= @vectojs/[email protected]) track the container's box automatically, fit: true by default. Pass fit: false for self-sizing content (bare Text with no maxWidth) — the default would overwrite its self-computed box every frame; wrap it in a Stack first if it needs to fill the viewport instead. |
| Growing ScrollView content without re-measuring | scrollView.add() measures automatically, but mutating an existing child's size needs scrollView.updateContentSize() or the max-scroll clamp goes stale. |
Guessing estimatedRowHeight for fixed-height VirtualList rows | Set it to the exact row height — the estimate only exists for variable rows (measured heights are cached per index); setItems() resets scroll and that cache. |
Passing canvas.width/canvas.height into a layout function | Those are the DPR-scaled backing store (2× at retina). Pass window.innerWidth/scene.width — the logical size — or panels double in width. |
Expecting Tabs to stay legible with many tabs | Tabs (>= @vectojs/[email protected]) keeps a fixed tabWidth (floor minTabWidth) and scrolls horizontally; pass closable: true + onClose for per-tab × close. Since 1.9.4 surplus bar width stays empty (never stretches past tabWidth). |
Wanting no tab bar while only one tab exists (Vim showtabline=1) | Tabs (>= @vectojs/[email protected]) autoHideTabBar: true — bar + hit region vanish below two tabs, content takes full height; read the live effectiveTabBarHeight getter (not tabHeight) when laying out siblings around the bar. |
Drag handler reading localX deltas from the dragged handle | PanelResizeHandle (>= 1.1.3) uses sceneX/sceneY — a coordinate space that does not move with the handle. Any custom drag must do the same or it lags the cursor. |
| Selection highlights drift after zoom (Firefox), canvas looks fine | The app owns sizing but never calls scene.resize() — it is the Range-metric recalibration hook. Bridge the container via ResizeObserver (references/cross-environment.md). |
| Hit/selection tests pass headless, fail on real laptops | Headless runs DPR 1; real machines are DPR 2 — run pointer/selection tests at deviceScaleFactor: 2 too. Offset proportional to distance from origin ⇒ DPR bug. |
| Text renders in a different font than it was measured with | Web-font race: construct text after await document.fonts.ready, re-measure from document.fonts.onloadingdone for lazy fonts. |
Scroll owners and RTL
One scroll owner per region. ScrollView, VirtualList, TreeView, and a
virtualized Table (viewportHeight) each own their region's scroll. Nesting two
owners, or adding your own wheel/drag handler on top of one, produces
double-scrolling. All four also drag-to-scroll on touch (the content follows
the finger 1:1), so don't add a touch handler either.
Sizing notes that bite:
VirtualListneeds a saneestimatedRowHeight; a wrong estimate shows as a scrollbar that jumps as real heights are measured.Tablevirtualizes only when you give itviewportHeight. Without it the whole grid mounts.TreeViewfires its toggle onpointerupand only within ~6px of the pointerdown, so a drag scrolls instead of expanding a row.
RTL is a layout concern, not just a text one. An RTL paragraph right-aligns,
and selection anchors at the visual origin. For the a11y layer, set
readingDirection: 'rtl' on the Scene so tab order reverses inline within
each visual row — tab order follows where things are drawn, not the order they
were added, so an RTL UI that skips this tabs in the wrong direction.
Verification
Check at least:
- narrow mobile width;
- desktop width;
- browser zoom 125% and 150%;
- keyboard navigation/focus order;
- an RTL pass (
readingDirection: 'rtl') if the app is ever localized; - role-based automation for interactive controls.
What ships with it: 3 files
4.6 KB alongside SKILL.md
agents/
- openai.yaml207 B
references/
- cross-environment.md2.8 KB
- layout-recipes.md1.6 KB