Skip to content
Native Animation
Moon Phase Scroll animation preview

This preview uses fictional demo content. See the props below for the customization options available in this component.

npx nativeanimation add moon-phase-scroll
Skia

Moon Phase Scroll

A scroll-driven lunar sleep program with changing moon phases, with configurable chapters, program title, fonts, and accent color.

$9 for this animation

Add to your project

Copy the component and its assets into your app with the Native Animation CLI, or paste the agent prompt into Claude Code, Codex or Cursor once the MCP server is connected.

About this animation

A scroll-driven lunar sleep program with changing moon phases, with configurable chapters, program title, fonts, and accent color.

Why this one

Instead of swapping phase icons, a single spherical moon continuously changes its illuminated face. Earthshine keeps the dark face visible, and the lake becomes a path of light at full moon. The scroll acts as both a story and a calendar.

How it works

  1. onLayout measures the container. One progress value, p = scrollY / height, drives all five chapters and the lunar calendar.
  2. A Skia runtime shader projects a sphere against a changing light direction. Its terminator is an ellipse with minor axis cos(angle) × radius, softened across approximately 6 points. Fractal noise and 18 soft crater depressions supply local texture.
  3. Pose interpolation rests for the first and last 20% of each chapter interval. The moon follows a shallow arc and rotates 8 degrees across the cycle.
  4. The sky, 160 deterministic stars, full-moon halo, segmented lake reflection, and a shooting star respond to lunar illumination. Reading veils are part of the shader.
  5. Headings have separate entrance and exit windows with a gap between adjacent chapters. The night counter rolls only during the last quarter of each integer change; tens roll only on carry.
  6. Native scrolling snaps to chapters. Web scrolling remains continuous. The final action belongs to the scroll content. Demo autoplay uses the ref to move the actual scroll, then fades to black to restart.

Install

npx expo install @shopify/react-native-skia react-native-reanimated react-native-worklets

Copy this folder into your project; demo/ is optional. Load CanvasKit before mounting on web, following Skia's web setup. Expo configures the Reanimated Babel plugin automatically.

Usage

import { MoonPhaseScroll } from './moon-phase-scroll';

export function SleepProgram() {
  return <MoonPhaseScroll onStart={() => openProgram()} />;
}

Supply your loaded fonts and use the ref for external navigation:

import { useRef } from 'react';
import { MoonPhaseScroll, type MoonPhaseScrollRef } from './moon-phase-scroll';

export function LunarJourney() {
  const moon = useRef<MoonPhaseScrollRef>(null);
  return (
    <MoonPhaseScroll
      ref={moon}
      fontFamily="YourSans"
      titleFontFamily="YourSerif"
      italicFontFamily="YourSerifItalic"
      monoFontFamily="YourMono"
      accentColor="#C4B5FD"
      moonRatio={0.62}
      haloIntensity={1.2}
      onStart={() => openProgram()}
      onChapterChange={(chapter) => trackChapter(chapter)}
    />
  );
}

The examples use host-provided openProgram and trackChapter callbacks. scrollToChapter(chapter, animated = true) accepts fractional chapters from 0 through 4. reset() fades to black, returns to chapter 0, and fades back in. Navigation can always be interrupted by touch or wheel input.

Props

PropTypeDefaultDescription
chaptersTuple of five MoonChapter objectsLunar sleep chaptersTitles, subtitles, eyebrows, descriptions, and sessions.
headerReactNode—Host brand content above the program title.
programTitlestringSleep with the moonSmall program heading.
actionLabelstringStart the 29-night programFinal button label and accessibility label.
onStart() => void—Final action callback.
onChapterChange(chapter: number) => void—Nearest chapter changed; called on the JS thread.
onInteraction() => void—User interrupts external playback.
fontFamilystringSystemInterface text.
titleFontFamilystringSystemFirst heading line.
italicFontFamilystringSystemEditorial second line.
monoFontFamilystringSystemCounters and eyebrows.
accentColorstring#C4B5FDValid Skia color for text, halo, reflection, and action.
moonRationumber0.62Moon diameter divided by container width; clamped to 0.3–0.75.
haloIntensitynumber1Full-moon halo multiplier; clamped to 0–2.
transitionDurationnumber2200Imperative scroll animation duration in milliseconds.
snapToChaptersbooleantrueChapter snapping on native platforms only.
styleStyleProp<ViewStyle>—Root container style.
testIDstring—Root test identifier.

Compatibility

Expo SDK 58, React Native 0.88.0, Reanimated 4.7.1, Worklets 0.13.0, and Skia 2.14.0. Dependencies are included in Expo Go. iOS, Android, and web are compilation targets. No network assets or additional native dependencies are required.

Accessibility

Chapter navigation and the first-screen scroll hint are labeled buttons with targets of at least 44 points. Each scroll chapter exposes a header block containing its complete copy. Inactive visual headings are hidden from screen readers. The final action has a button role and uses actionLabel as its label.

Reduced motion freezes the moon's pose, rotation, ripples, and hint, removes the meteor, and replaces continuous phase changes with 200 ms scene fades. Chapter text fades in over 200 ms without moving or overlapping another heading. Chapter buttons provide a tap alternative to scrolling.

Icons: Lucide (ISC). The demo uses the Aurora brand from the project kit; production content and font names are supplied by the host.