A loader where a shadow disc sweeps across a glowing moon, cycling crescent → new → crescent; optionally clipped to the moon for a crisp phase silhouette.
Preview
Customize
7 props72
2.4
Installation
Copies the component source and its CSS Module into your project.
npx motionfoundry add moon-phase-loaderWrites components/ui/moon-phase-loader.tsx and components/styles/moon-phase-loader.module.css.
Usage
Updates live as you change the controls — only props that differ from the defaults are included.
example.tsx
1import { MoonPhaseLoader } from "@motionfoundry/ui";2 3export function Example() {4 return (5 <MoonPhaseLoader />6 );7}Props
Accessibility
Renders role="status" with an aria-label (default "Loading"); the moon itself is decorative and aria-hidden. Pure CSS animation that stops under prefers-reduced-motion, leaving a static crescent.
Toggle prefers-reduced-motion in your OS or browser devtools (Rendering → Emulate CSS media feature) to verify the reduced-motion state.
Source
components/ui/moon-phase-loader.tsx
1"use client";2 3import * as React from "react";4import styles from "../styles/moon-phase-loader.module.css";5 6export interface MoonPhaseLoaderProps {7 /** Moon diameter in pixels */8 size?: number;9 /** Seconds for one full cycle of phases */10 duration?: number;11 /** Lit moon color */12 color?: string;13 /** Shadow disc color (match your background) */14 shadowColor?: string;15 /** Soft glow around the lit moon */16 glow?: boolean;17 /** Clip the shadow to the moon so only the lit shape shows */18 clip?: boolean;19 /** Accessible loading label */20 label?: string;21 /** Optional className merged onto the root */22 className?: string;23 /** Optional inline styles merged onto the root */24 style?: React.CSSProperties;25}26 27export function MoonPhaseLoader({28 size = 72,29 duration = 2.4,30 color = "#ff8a1f",31 shadowColor = "#0b0a09",32 glow = true,33 clip = false,34 label = "Loading",35 className = "",36 style,37}: MoonPhaseLoaderProps) {38 return (39 <div40 role="status"41 aria-label={label}42 className={`${styles.root} ${clip ? styles.clip : ""} ${glow ? styles.glow : ""} ${className}`.trim()}43 style={44 {45 "--mf-mp-size": `${size}px`,46 "--mf-mp-duration": `${Math.max(0.4, duration)}s`,47 "--mf-mp-color": color,48 "--mf-mp-shadow": shadowColor,49 ...style,50 } as React.CSSProperties51 }52 >53 <span className={styles.moon} aria-hidden="true">54 <span className={styles.shadow} />55 </span>56 </div>57 );58}