About this animation
A habit heatmap with a light wave and rolling streak count, with configurable history, streak, colors, and fonts.
Why this one
Completing a day sends a brief pulse through the whole history instead of merely repainting a cell. The breathing outline makes today easy to find; a magnetic glass tooltip follows day inspection. The Aurora demo presents a complete meditation habit screen.
How it works
- Measure the container with
onLayout; fit 18 columns of seven square cells into its width. - Draw every cell in one Skia Canvas. A UI thread entrance clock reveals diagonal groups with 6 ms spacing.
- Share one completion progress between today's color, the drawn confirmation ring, and rolling number columns. Separate values handle pressing and the transient wave.
- Offset each cell's 290 ms light pulse by its Euclidean distance from today, at 8 ms per cell. The full celebration settles within 600 ms.
- Compose native tap and pan gestures for inspection, and tap and 600 ms long press for completion. Spring the tooltip toward the inspected cell.
- Reset with a 220 ms fade. Reduced motion removes entrance staggering, wave, breathing, and spatial travel.
Install
npx expo install @shopify/react-native-skia expo-blur expo-linear-gradient react-native-gesture-handler react-native-reanimated react-native-workletsUse a GestureHandlerRootView in the host app. In web projects, load CanvasKit before mounting the component, using Skia's web loader. The catalog host must register this demo with its withSkia loader. Fonts are supplied by the host; the reusable component has no theme imports.
Usage
import HabitHeatmapGrid from './habit-heatmap-grid';
// Provide 126 consecutive days, in chronological order. The last day is today.
<HabitHeatmapGrid days={history} />;const habit = useRef<HabitHeatmapGridRef>(null);
<HabitHeatmapGrid
ref={habit}
days={history}
title="Evening practice"
streak={12}
targetMinutes={15}
monthPercent={80}
bestStreak={30}
totalHours={24}
waveDelay={10}
onCompleteChange={saveCompletion}
/>;
// habit.current?.complete(), reset(), enter()Props
| Prop | Type | Default | Purpose |
|---|---|---|---|
days | readonly HabitDay[] | Required | Chronological history. The latest 126 days are shown; the last is today. |
title | string | 'Daily practice' | Habit title. |
streak | number | 0 | Baseline streak before completing today. |
targetMinutes | number | 10 | Today's target in minutes. |
monthPercent | number | 0 | Percentage achieved this month. |
bestStreak | number | 0 | Longest streak in days. |
totalHours | number | 0 | Total practice hours. |
colors | readonly [ string, string, string, string, string, ] | LEVELS | Five activity colors, from inactive to strongest. |
waveDelay | number | 8 | Wave travel time per cell of distance in milliseconds. |
fontFamily | string | undefined | UI font family. |
titleFontFamily | string | undefined | Editorial title font family. |
monoFontFamily | string | undefined | Tabular number font family. |
onCompleteChange | ( completed: boolean, ) => void | undefined | Called when today's completion changes. |
onDayInspect | (day: HabitDay) => void | undefined | Called when a day is inspected. |
accessibilityLabel | string | 'Habit history' | Grid label for assistive technology. |
style | StyleProp<ViewStyle> | undefined | Container style. |
testID | string | undefined | Test identifier. |
duration | number | 1700 | Heatmap breathing half-cycle in milliseconds. Spring physics and reduced-motion timing are unchanged. |
The component owns today's temporary completion state; call reset() to undo. enter() replays the entrance independently. Month and weekday labels derive from your date range. Supply whole, nonnegative numbers for statistics.
Compatibility
Targets Expo SDK 58, React Native 0.88.0, Reanimated 4.7.1, worklets 0.13.0, and Skia 2.14.0. Uses the existing Expo Go compatible stack. Supports iOS, Android, and web. The demo is designed for 320×694 and 390×844 portrait containers.
Accessibility
The completion button exposes a button role, selection state, and activation action. Tap is an alternative to holding. The history exposes adjustable actions to inspect adjacent days without dragging. Status updates use a polite live region; touching outside the grid dismisses details. Reduced motion displays all cells immediately and completes without a traveling wave or spatial number animation.
Icons: Lucide (ISC), copied from the repository kit. Aurora's sparkles tile and palette also come from the kit.




