App
npx @vyui/cli add appOverview
VyApp is the recommended root wrapper for a Vy UI app. It owns the dark-mode class-toggle + remount contract that useColorMode requires of an app root, mounts the shared overlay host so teleporting surfaces work with zero setup, and reports the root's size whenever it changes.
<script setup lang="ts">
import { VyApp } from '@vyui/kit/app'
</script>
<template>
<VyApp>
<!-- your screens -->
</VyApp>
</template>
import { createApp } from '@lynx-js/vue'
import { VyUI } from '@vyui/kit'
import App from './App.vue'
createApp(App).use(VyUI).mount('#app')
VyApp once, at the top of the tree. Everything it wires up (the dark ramp, the overlay host, the viewport signal) applies to the whole app below it.Usage
Dark mode
VyApp reads useColorMode's mode / isDark and applies both pieces of the app-root contract: the .dark class and a :key="mode" remount. Lynx native only applies a class change to freshly mounted nodes, so toggling .dark on an already-mounted tree would not re-skin it, so keying on mode remounts the subtree on every change, turning that platform quirk into the mechanism. You get this for free just by wrapping your app; nothing further to wire up.
Overlay host
By default VyApp mounts OverlayRoot (from @vyui/core) as its first child, so Modal, Tray, Toast, and other teleporting components work without any hand-wiring. Set overlays to false and place your own <OverlayRoot /> if you need control over where it sits in the tree.
<template>
<VyApp :overlays="false">
<OverlayRoot />
<!-- your screens -->
</VyApp>
</template>
Radius token
Pass radius to set --ui-radius (in rem) on the root, overriding the default shipped in @vyui/kit/style.css.
<VyApp :radius="0.75">
<!-- your screens -->
</VyApp>
Safe area
VyApp snapshots the container's safe-area insets (notch, Dynamic Island, home indicator) and provides them to the whole tree, so any descendant's useSafeArea() resolves without wiring. Set safe-area="false" to force zeros, as a fullscreen surface drawing its own chrome would want. The insets come from global props the host injects; Sparkling handles this automatically for most apps. If you need to build your own host, see native host integration.
Viewport size
VyApp emits viewport-change with { width, height } on mount and on every resize (rotation, split-screen), sourced from the background-thread layoutchange event. main-thread-* attrs do not reliably attach to this root, so this is the supported way to observe root size.
<VyApp @viewport-change="(size) => console.log(size.width, size.height)">
<!-- your screens -->
</VyApp>
Reading color mode from the slot
The default slot receives { mode, isDark }, the same values useColorMode() returns, so a descendant can react to color mode without importing the composable itself.
<VyApp v-slot="{ mode, isDark }">
<text>Current mode: {{ mode }} ({{ isDark ? 'dark' : 'light' }})</text>
</VyApp>
Features and behavior
- Applies the
.darkclass and a:key="mode"remount together, so color mode toggles correctly on Lynx native and on web. - Mounts
OverlayRootas the first child by default; opt out withoverlays="false"to place it yourself. radiussets--ui-radiusinline, in rem.- Provides safe-area insets tree-wide (
useSafeArea());safe-area="false"opts the app out with zeros. - Emits
viewport-changeon mount and resize, from the background-threadlayoutchangeevent. - The default slot exposes
{ mode, isDark }for consumers that want color mode without callinguseColorMode()directly.
Props
| Prop | Default | Type |
|---|---|---|
overlays | true | boolean | undefinedMount the overlay host (`OverlayRoot`) as the first child of the root so teleported surfaces (Modal, Tray, Toast, …) work without hand-wiring. Opt out when placing `<OverlayRoot />` yourself. |
radius | — | number | undefinedCorner radius in rem — sets `--ui-radius` on the root. Omit to keep the `@vyui/kit/style.css` default. |
safeArea | — | boolean | undefinedProvide app-wide safe-area insets to the whole tree (`useSafeArea()`). `false` opts the app out with zeros; omit to use the container's reported insets as-is. A subtree can still override with `provideSafeAreaInsets`. |
ui | — | Partial<Record<"root", ClassNameValue>> | undefined |
Emits
| Event | Payload |
|---|---|
viewport-change | [size: { width: number; height: number; }] |
Slots
| Slot | Bindings |
|---|---|
default | { mode: ColorMode; isDark: boolean; } |
Styling and theming
Override globally through appConfig.ui.app or locally with the ui prop.
| UI slot | Purpose |
|---|---|
root | The app-root element. Paints the bg-default surface token and fills the viewport (w-full h-full). |
Platform notes
- The dark-mode remount relies on Lynx native applying class changes only to freshly mounted nodes, and
:key="mode"turns that into the toggle mechanism. SeeuseColorModefor the full rationale. viewport-changeis sourced from the background-threadlayoutchangeevent rather than amain-thread-*binding, because those attrs do not reliably fall through onto this root.- Components that teleport (
Modal,Tray,Toast, …) need anOverlayRootsomewhere in the tree to render at all, andVyAppprovides one by default so this is rarely something you need to think about.
Related components
Trayfor a bottom sheet mounted through the overlay hostVyAppprovides.Modalfor a centered blocking dialog, same overlay host.- Theming → Dark Mode and Theming → Overrides for runtime theme overrides.