UILoaders and Feedbackno dependencies

MoonPhaseLoader

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 props
72
2.4

Installation

Copies the component source and its CSS Module into your project.

npx motionfoundry add moon-phase-loader

Writes 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

PropTypeDefault
size

Moon diameter in pixels.

number72
duration

Seconds for one full cycle of phases.

number2.4
color

Lit moon color.

string'#ff8a1f'
shadowColor

Shadow disc color — match your background.

string'#0b0a09'
glow

Soft glow around the moon.

booleantrue
clip

Clip the shadow to the moon so only the lit shape shows.

booleanfalse
label

Accessible loading label.

string'Loading'
className

Merged onto the root.

string''
style

Merged onto the root.

React.CSSPropertiesundefined

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}