Ink Toggle
A dark mode switch that pours the theme, a drop of ink falls and floods the screen.
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-cssInstall
npx @rslab/cli add ink-toggleUsage
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.setColorSchemeunderneath. The liquid shapes are drawn withblendMode="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
Blurand alpha-sharpeningColorMatrixlayer. 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
measureInWindowand 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_Rand thestretchkeyframes: the teardrop shapeBlur blur={8}andGOO_MATRIX: stickiness of the liquid