tamer-app-shell
App chrome (AppBar / TabBar / Screen / SafeArea) plus a Material 3 component set (Button, Card, FAB, NavigationDrawer, NavigationRail, …) for Lynx. Used by tamer-router Stack / Tab layouts and standalone in any Lynx page.
Install
Run t4l link after installing.
Usage
Each export drops in directly — most pages need only a few. Imports first:
Button
variant: 'filled' (default) | 'outlined' | 'text' | 'elevated' | 'tonal'
size: 'xs' | 'sm' | 'md' (default) | 'lg' | 'xl'
shape: 'round' (default) | 'square'
ButtonGroup
Card
variant: 'elevated' (default) | 'filled' | 'outlined'
Fab
size: 'small' | 'regular' (default) | 'large'
ExtendedFab
FabMenu
FloatingFabContainer
NavigationDrawer
NavigationRail
ScreenScopedOverlay
level: SCREEN_OVERLAY_LEVEL_FLOATING (10) for menus/FABs, SCREEN_OVERLAY_LEVEL_DRAWER (20) for drawers.
Theme tokens
The token set tracks the active light / dark palette from tamer-system-ui, so theme switches propagate without per-component plumbing.
The packages/example app at pages/m3/ exercises every component above — see Example Anatomy.
API
App chrome
<AppBar>
AppBarAction: { icon: string; set?: IconSet; onTap: () => void }. If leftAction is undefined and canGoBack(), shows the default back button.
<TabBar>
TabItem: { icon: string; set?: IconSet; label?: string; path?: string; onTap?: () => void }. Uses AppShellRouterContext.replace(path, { tab: true }) when path is set.
<Content>
Scrollable view. Accepts children and optional style.
<Screen> / <SafeArea>
Re-exported from tamer-screen. Screen is a full-screen flex column. SafeArea accepts edges?: ('top'|'right'|'bottom'|'left')[] plus children.
<AppShellProvider> / useAppShellContext()
useAppShellContext() returns { showAppBar, showTabBar, barHeight }.
useAppShellRouter()
Returns AppShellRouterContextValue | null: back, canGoBack, replace(route, options?). Provided by FileRouter; usable directly with manual coordinators.
Material 3 components
<Button>
<ButtonGroup>
ButtonGroupProps accepts items: ButtonGroupItem[], selectedId, onSelect. Connected button row.
<Card variant>
'elevated' | 'filled' | 'outlined'.
<Fab> / <ExtendedFab> / <FabMenu>
FabSize: 'small' | 'regular' | 'large'. FabMenu takes FabMenuItem[] (id, icon, label, onTap).
<FloatingFabContainer> / useFloatingFabOffsets()
Wraps a FAB at the screen edge accounting for tab bar + insets. The hook returns { bottom, right, … } in px if you want the math without the wrapper.
<ScreenScopedOverlay>
Mounts an overlay (sheet, drawer, dialog) above the current screen — sits below pushed routes. Use level={SCREEN_OVERLAY_LEVEL_FLOATING} (10) for menus / FABs and level={SCREEN_OVERLAY_LEVEL_DRAWER} (20) for drawers.
<NavigationDrawer>
Side drawer; takes DrawerSection[] of DrawerItems, open, onDismiss, selectedId, onSelect.
<NavigationRail>
Vertical rail; takes NavRailItem[], selectedId, onSelect.
useM3ThemeTokens()
Returns the M3 token set (primary, onPrimary, surface, surfaceContainer*, outline, outlineVariant, …) derived from the active palette in tamer-system-ui.
px(...values)
Helper for converting a list of numbers to a px string.
How it works
- App chrome is a thin layer over plain Lynx
<view>/<text>— no native module.AppBar/TabBarread insets from tamer-insets and palette from tamer-system-ui.Screen/SafeAreacome from tamer-screen. AppShellRouterContextis populated byFileRouter(or your own coordinator). Components that show a back button (AppBar,NavigationDrawerheaders) consume it viauseAppShellRouter().- M3 components consume
useM3ThemeTokens()directly, so any theme change propagates without re-rendering the whole tree manually. ScreenScopedOverlaymounts into the active screen's overlay layer, not document root — pushed routes hide existing overlays automatically and overlays don't bleed across stack entries.FloatingFabContainersubscribes touseInsets()+ a constant tab-bar visual height (80px);useFloatingFabOffsets()exposes the same math.
