@objectifthunes/royal-games-ui
v1.3.1
Published
Hand-authored React graphics library for Capacitor games with Enamel and Gloss themes.
Maintainers
Readme
@objectifthunes/royal-games-ui
A hand-authored React graphics library for Capacitor mobile games. The package provides the Midnight Court visual system as accessible React components with two complete themes:
- Enamel uses dark contours, gold frames, inset tracks, and material faces.
- Gloss uses borderless extrusion, brighter highlights, and deeper shadows.
Both themes use the same component APIs and semantic CSS tokens. Theme changes never replace component structure or behavior.
Install
pnpm add @objectifthunes/royal-games-ui react react-domReact and React DOM 19 or newer are peer dependencies. The package has no runtime dependencies.
Import the public tokens once at the application entry point:
import "@objectifthunes/royal-games-ui/tokens.css";Component modules retain their own CSS imports. A downstream bundler can remove unrelated component JavaScript and styles.
Application shell
import { useState } from "react";
import {
Body,
BottomStack,
Button,
Chrome,
ChromeRow,
Dock,
Screen,
ScreenName,
SkinProvider,
Tab,
TabBar,
} from "@objectifthunes/royal-games-ui";
export function StoreScreen() {
const [section, setSection] = useState("store");
return (
<SkinProvider skin="enamel">
<Screen aria-label="Royal store">
<Chrome>
<ChromeRow>Host-owned identity and resources</ChromeRow>
<ScreenName>Store</ScreenName>
</Chrome>
<Body layout="commerce">Host-owned store content</Body>
<BottomStack>
<Dock>
<Button>Continue</Button>
</Dock>
<TabBar aria-label="Main navigation" value={section} onValueChange={setSection}>
<Tab value="home">Home</Tab>
<Tab value="store">Store</Tab>
<Tab value="profile">Profile</Tab>
</TabBar>
</BottomStack>
</Screen>
</SkinProvider>
);
}Screen is the sole owner of design scaling, the usable content rectangle, container
classification, orientation adjustments, and safe-area resolution. Chrome, Body, BottomStack,
Dock, FloatChrome, and TabBar consume that geometry instead of guessing the current device.
Screen classification is measured from the component itself, not the browser viewport, so an
embedded portrait game surface does not accidentally receive landscape placement.
Body provides structural dashboard, commerce, form, list, and result layouts in addition
to its neutral flow default. In portrait, BottomStack occupies a real lower-shell slot and the
Body scrollport ends exactly above it. In short landscape geometry, Screen deliberately moves the
lower shell onto the scene plane and preserves a measured scroll reserve. Hosts should not add
screen-specific padding or absolute footer offsets around these primitives.
The documentation site publishes complete, executable recipes for dashboard, shop, settings, map, victory, and leaderboard screens at Royal Games UI screens. The source shown on each recipe page is read from the same TSX file as its live preview; abbreviated pseudo-code is not used.
Switching themes
SkinProvider supports controlled and uncontrolled operation:
function ThemeButton() {
const { skin, setSkin } = useSkin();
return (
<Button onClick={() => setSkin(skin === "enamel" ? "gloss" : "enamel")}>
Use {skin === "enamel" ? "Gloss" : "Enamel"}
</Button>
);
}
<SkinProvider defaultSkin="enamel">
<ThemeButton />
</SkinProvider>;Nested providers create independent theme scopes. In controlled mode, setSkin requests a change
through onSkinChange and does not invent local state.
Native controls
Actions and form components keep native browser behavior:
Buttonis a native button, defaults totype="button", and uses a stable geometry width preset instead of measuring its label. Usewidth="compact|standard|wide|extended|full"for layout intent.ToggleandCheckboxare native checkbox inputs.Slideris a native range input.Fieldforwards its ref and standard props to its native input.Segmentedimplements radio-group semantics and roving focus.TabBarandTabimplement context registration and the tab keyboard pattern without DOM queries.
Accessible names are required where visible content cannot provide one. Interactive targets have a 3rem minimum hit box and visible focus treatment.
Design units and placement
All component geometry derives from --rg-u. It is 1px for standalone components and scales from
the 332-wide reference container inside a screen. Scaling caps at the reviewed 410-wide geometry on
large and tablet surfaces; a Screen whose own measured geometry is landscape uses its shorter axis
so controls do not become oversized or collide with the lower shell.
import { MapPin, u } from "@objectifthunes/royal-games-ui";
<MapPin
variant="button"
state="current"
label="Open destination seven"
onPress={openDestination}
style={{ insetInlineStart: u(70), insetBlockStart: u(190) }}
>
7
</MapPin>;Hosts can override the documented safe-inset tokens in deterministic WebView or browser fixtures.
Descendants must not apply env(safe-area-inset-*) again.
Artwork and product state
The host supplies player identity, text, images, SVG artwork, prices, purchase callbacks, navigation, rankings, rewards, and all game state. The library does not ship binary artwork, emoji placeholders, payment logic, progression rules, timers, or mock product content.
Components do not assume a router, state library, network layer, localization system, or game rule
model. Polymorphic components such as MapPin and SettingsRow expose discriminated native roots
and forward the appropriate HTML attributes, refs, data-* hooks, and test identifiers. Spacing
presets use geometry names such as compact, roomy, and tight; they never infer what a host
puts inside a component.
Gameplay-specific boards, keyboards, letter tiles, joysticks, and rule-driven controls are intentionally outside this package.
Motion
Entrance choreography is separate from interaction feedback. Interaction states have no fixed delay.
useEntranceMotion measures the actual finite Web Animations attached to an element and settles
React entrance state when they finish. Reduced-motion preferences disable nonessential animation.
Development
pnpm install --frozen-lockfile
pnpm check
pnpm test:browser
pnpm test:packageThe checks validate formatting, strict TypeScript and negative type contracts, source authorship rules, unit and accessibility integration flows, the production build, packed Vite and Next.js consumers, and browser interaction. Visual baselines cover both themes in Chromium and WebKit across the required compact, reference, large, tablet, and landscape fixtures.
