About this animation
An expanding metric tile dashboard with animated light and configurable metrics, colors, copy, and fonts.
Why this one
All tiles share an opaque charcoal glass surface, a quiet corner glow, and one reading layout. Color identifies the metric only in its chart and corner light. The demo uses a coordinated lime, mint, cyan, and amber palette, with a restrained text-only Aurora identity.
How it works
onLayoutmeasures the container; up to six metrics form a two-column grid, capped at 520 pt, with a compact final row. Below 694 pt tall, the dashboard scrolls.- Every tile owns its opaque surface and clipped Skia chart. The selected tile moves above its siblings, so their text never shows through the detail.
- Titles and subtitles share one position. The numeral and its smaller unit sit together on a baseline; all summary charts use a 28 pt band with the status below it.
- One shared expansion progress drives bounds, numeral size, chart geometry, and sibling recession (scale 0.96, opacity 0.35). Opening fills the visible container, even in a short embed. Closing returns to the original tile rectangle and restores dashboard scroll position.
- Gesture Handler handles taps and downward dragging with an interruptible spring and explicit mass. A sibling 48 pt back button supplies the tap alternative. Reduced motion changes detail state immediately, without geometry travel or ambient drift.
- Numerals count over 900 ms; an animated reaction sends only changed integers to React through
scheduleOnRN. The demo opens Daylight at 1.2 seconds, closes at 4.2 seconds, then opens Calm Index at 5.1 seconds and closes at 8.1 seconds. Physical interaction cancels autoplay.
Install
npx expo install @shopify/react-native-skia expo-blur react-native-gesture-handler react-native-reanimated react-native-workletsMount your application inside GestureHandlerRootView. Configure Reanimated/worklets for your Expo app. For web, initialize Skia's CanvasKit before mounting this component, following your host application's Skia loader setup. Fonts are supplied by the host; the component does not import catalog code.
Usage
import { AuraMetricTiles } from './aura-metric-tiles';
const metrics = [{
title: 'Daylight', subtitle: 'Daily exposure', value: 85, unit: '%',
palette: ['#15151A', '#E5F23C', '#C2EE72'] as const,
detail: 'wave' as const, status: 'Steady',
}];
<AuraMetricTiles metrics={metrics} title="Today's light" />;const ref = useRef<AuraMetricTilesRef>(null);
<AuraMetricTiles
ref={ref}
metrics={metrics}
fontFamily="YourMediumFont"
numberFontFamily="YourMonoFont"
auraIntensity={0.6}
countDuration={700}
onOpen={(index) => trackMetric(index)}
onControlPress={(index) => openInput(index)}
/>;
// From an event handler:
ref.current?.open(0);
ref.current?.close();Props
| Prop | Type | Default | Purpose |
|---|---|---|---|
| metrics | readonly AuraMetric[] | required | Up to six metrics; optional normalized hourly samples |
| title | string | Your metrics | Dashboard heading |
| brand | ReactNode | none | Host-supplied identity |
| fontFamily / numberFontFamily / headingFontFamily | string | system / system / body | Typography |
| theme | complete palette | dark, white ink, lime accent | Background, ink, muted, border, accent, optional surface (#15151A); all tiles share the surface |
| detailLabel | string | Past 24 hours | Chart heading |
| timeLabels | three strings | 12 AM, 12 PM, 12 AM | Hourly chart axis |
| closeLabel | string | Back to metrics | Accessible back control |
| controlLabels | three strings | Keyboard, Voice, Camera | Accessible dock controls |
| auraIntensity | number | 1 | Corner glow drift strength; 0 pauses ambient clock |
| countDuration | number | 900 | Count-up milliseconds |
| onOpen / onClose | callbacks | none | Transition-start notifications |
| onInteraction | callback | none | Stops host autoplay on physical input |
| onControlPress | (index: number) => void | opens related metric | Connect keyboard, voice, camera to host features |
| style / testID | ViewStyle / string | none / aura-metric-tiles | Container and stable control identifiers |
AuraMetricTilesRef exposes open(index) and close(). Invalid indices are ignored. A metric contains title, subtitle, value, unit, palette (legacy base plus primary and secondary accents; the primary colors the chart and glow), detail (wave, ruler, ring, bars, dots), status, and optional samples. Without samples the chart is illustrative, not measured sensor data. The legacy base and secondary accent remain accepted for API compatibility. Tile ink, muted text, borders, and the shared surface come from theme; supply theme.surface alongside matching ink when using a light theme.
Layout and integration
The root fills its parent. The grid is capped at 520 pt wide and 860 pt tall (centered on taller hosts) and uses an internal scrolling layout below 694 pt tall, including landscape and 300×400 embeds. The detail fills the visible container; scrolling position is restored after closing. Full-screen hosts should apply safe-area insets themselves. All dock and back targets are at least 48 pt.
Compatibility
Designed for Expo SDK 58, React Native 0.88.0, Reanimated 4.7.1, worklets 0.13.0, and Skia 2.14.0. Uses installed Expo Go-compatible modules; no new native dependency is introduced. Supports iOS, Android, and web. Demo input controls open related metrics unless connected to real host input; no microphone or camera capture is claimed.
Accessibility
Metric tiles, dock buttons, and back controls expose button roles, labels, activation actions, and stable test IDs. Back is the tap alternative to dragging down. Reduced motion freezes the auras, displays final readings immediately, and switches detail state immediately without travel. Hidden detail controls are excluded from accessibility. Physical and accessibility interaction stop demo autoplay.
Icons: Lucide (ISC), copied from the project kit. Aurora is a fictional wellness identity presented as a wordmark without a badge.




