API

nsh/device

The device API tells the app about the device and its environment. Rust code calls the functions of nsh_core::ui::prelude (window(), dark(), font_scale(), locale(), os(), idiom(), pixel_ratio(), set_color_scheme(), …); TSX imports nsh/device. The window size, the color scheme, the font scale, the pixel ratio and the locale are reactive: read them in a binding (a closure passed to an element in Rust, JSX in TSX), computed or effect, and what reads them updates when the platform reports a change (rotation, a window resize, switching to dark mode, changing the text size or the language, moving to a screen of another density).

use nsh_core::{
    platform::{Appearance, Idiom, Os},
    ui::prelude::*, // window, dark, font_scale, locale, os, idiom, pixel_ratio, set_color_scheme, ...
};

See Device and environment for the guide.

#windowSize

const windowSize: Accessor<Size>;

The size of the surface in points: the window, or the embedded NshAppView / NshView. Reactive. Rust: window() -> Size<Points>.

let wide = move || window().width >= px(600.0);

view().style_when(wide, |s| s.flex_row())

#colorScheme

const colorScheme: Accessor<"light" | "dark">;

The appearance shown: the system’s, unless the app set its own (nsh.json "appearance", setColorScheme). Reactive. Rust: dark() -> bool; theme colors (AdaptiveColor) and the .dark(…) style variant follow it without reading it. Tailwind’s dark: variant follows it too (see Tailwind).

view().flex_1().bg(move || if dark() { black() } else { white() })

#fontScale

const fontScale: Accessor<number>;

The user’s preferred text size relative to the default (1 is the default size; Dynamic Type on iOS, font size on Android), or the text size the embedding app sets in its place. Reactive. Rust: font_scale() -> f32. Text already scales with it unless allowFontScaling={false}; read it when other sizes should follow, such as icons next to text.

#locale

const locale: Accessor<string>;

The user’s locale as a BCP 47 tag ("en-US", "zh-Hans-CN"), or the locale the embedding app sets in its place. Reactive. Rust: locale() -> Locale (locale().as_str() is the tag). Intl formats for it by default; read it to choose the strings of your own translations (Device and environment).

#setColorScheme

function setColorScheme(scheme: "light" | "dark" | "system"): void;

Shows the app in scheme whatever the system’s appearance, at once: colorScheme(), dark: classes, the components’ theme and the platform’s controls (alerts, the status bar’s default style) follow it; "system" follows the system again. nsh.json "appearance" sets it from the start. Rust: set_color_scheme(Appearance::Dark) (Appearance::Light, Appearance::System).

#platform

const platform: Platform;

interface Platform {
  readonly os: "ios" | "android" | "macos" | "windows" | "linux" | "web";
  readonly version: string;
  readonly idiom: "phone" | "pad" | "mac" | "tv" | "desktop";
  readonly catalyst: boolean;
  select<T>(spec: PlatformSelectSpec<T>): T | undefined;
}

The platform the app runs on, constant while it runs.

MemberRustDescription
osos() -> Os (os().name() is the TS string)The operating system. iOS, the iOS simulator and Mac Catalyst report "ios", browsers "web".
versionos_version() -> (u16, u16)iOS and desktops: the OS version ("17.5"); Android: the API level ("35"); the web: "0.0". Rust gives the major and minor version, or the API level and 0 on Android.
idiomidiom() -> Idiom; is_desktop() for "desktop"The kind of device: "phone", "pad" (an iPad, an Android tablet), "mac" (Mac Catalyst in the Mac idiom), "tv" (Apple TV, Android TV) or "desktop", where desktop behavior applies: macOS, Windows, Linux, and browsers with a mouse and a wide window.
catalystidiom() == Idiom::MacMac Catalyst in the Mac idiom.
select(spec)match os() { … }The value for this platform (see below).
let columns = if is_desktop() { 3 } else { 1 };

#platform.select

interface PlatformSelectSpec<T> {
  ios?: T;
  android?: T;
  macos?: T;
  windows?: T;
  linux?: T;
  web?: T;
  mobile?: T;   // iOS, Android, and phone and tablet browsers
  desktop?: T;  // macOS, Windows, Linux, and desktop browsers
  native?: T;   // every platform but the web
  default?: T;
}

Returns the value for the current OS (ios, android, web, …), else mobile / desktop, else native (every platform but the web), else default, else undefined. In a browser, mobile or desktop follows the idiom. Rust code matches on os() (and is_desktop() for mobile / desktop).

use nsh_core::platform::Os;

let card = view().p_4();
let card = match os() {
    Os::Ios => card.shadow_color(black()).shadow_opacity(0.15).shadow_radius(px(8.0)),
    Os::Android => card.elevation(px(4.0)),
    _ => card,
};

#pixelRatio

const pixelRatio: Accessor<number>;

Device pixels per point (e.g. 3 on most iPhones). Reactive: it changes when the window moves to a screen of another density (on Mac Catalyst, the desktops, or an external display). Rust: pixel_ratio() -> f32, and round_to_nearest_pixel(points) for a length on whole device pixels; in TSX that length is Math.round(x * pixelRatio()) / pixelRatio().

let hairline = move || px(1.0 / pixel_ratio());

#Layout direction

The layout direction follows the user’s language: in a right-to-left language (Arabic, Hebrew, …) the app lays out right to left, rows run from the right, and left / right style properties (marginLeft, paddingRight, …) mean start and end. Changes apply at once, with no restart.

A view’s direction style ("ltr", "rtl" or "inherit", the default) sets the direction of its own subtree; in Rust, .direction(LayoutDirection::..).

view().direction(LayoutDirection::Rtl).flex_row().gap_2().child(text("First")).child(text("Second"))
  • Safe area insets (Rust safe_area(); TSX safeArea(), safeAreaInsets()) and the software keyboard (Rust keyboard_height(); TSX keyboardFrame()) are part of nsh/base.
  • Screen reader and reduced motion settings are in nsh/accessibility.