rslabdocs

Ink Toggle

A dark mode switch that pours the theme, a drop of ink falls and floods the screen.

ReanimatedSkiaNativeWindSource

Prerequisites

This animation is built on the following libraries. expo install pins the versions that match your SDK, and the CLI tells you exactly which ones are missing when you add the component.

npx expo install react-native-reanimated @shopify/react-native-skia expo-haptics expo-status-bar expo-symbols react-native-view-shot react-native-css

Install

npx @rslab/cli add ink-toggle

Usage

Mount it at the root of a screen, it fills its parent and the canvas overlays the content while animating:

import { InkToggle } from '@/components/InkToggle';

<View className="will-change-variable flex-1 bg-neutral-50 dark:bg-neutral-950">
  {/* your content with dark: variants */}
  <InkToggle iconStyle={{ position: 'absolute', top: 64, right: 20 }} />
</View>;

Requires "userInterfaceStyle": "automatic" in app.json.

How it works

  • Screenshot reveal. On tap the component screenshots the current screen, draws it full-screen on a Skia canvas, then flips Appearance.setColorScheme underneath. The liquid shapes are drawn with blendMode="dstOut", so they erase the screenshot: the drop and the wave are holes showing the new theme.
  • Metaball teardrop. The falling drop is two circles (head plus trailing tail) in a Blur and alpha-sharpening ColorMatrix layer. The tail lags during the fall and snaps back at impact, so the drop reads as a stretched teardrop.
  • The wave. A huge circle anchored below the bottom edge, centered on the impact point. It starts tangent to the screen and grows with a soft-start bezier, surging out of the edge while the goo merges the shrinking drop into it.
  • Position agnostic. The icon measures itself with measureInWindow and the drip starts at the bottom edge of the glyph, so the night visibly spills out of the moon wherever you place the icon.

Tuning

  • Drip timings: swell 220ms, hang 280ms, fall 380ms, wave 650ms
  • HEAD_R / TAIL_R and the stretch keyframes: the teardrop shape
  • Blur blur={8} and GOO_MATRIX: stickiness of the liquid

On this page