import { StyleSheet } from 'react-native'; import { cva, type VariantProps } from 'class-variance-authority'; /** * Toast root styles * * @note ANIMATED PROPERTIES (cannot be set via className): * The following properties are animated and cannot be overridden using Tailwind classes: * - `opacity` - Animated for visibility transitions when toasts are pushed beyond visible stack limits * - `transform` (translateY) - Animated for vertical position transitions when toasts are stacked, and for swipe-to-dismiss gestures * - `transform` (scale) - Animated for size scaling transitions when toasts are stacked (toasts behind active one are scaled down) * - `height` - Animated for height transitions when toast content changes * * To customize these properties, use the `animation` prop on `Toast.Root`: * ```tsx * * ``` * * To completely disable animated styles and apply your own via className or style prop, * set `isAnimatedStyleActive={false}` on `Toast.Root`. */ export const toastRootVariants = cva( [ // `overflow-hidden` was intentionally removed: on web it clips the drop // shadow and clips the child's transform mid-transition, producing a // visual "cut off" during enter/exit. Consumers that need clipping for // custom child content (e.g. a background image) can add it themselves. 'flex-row items-center gap-2 rounded-[var(--border-radius-default)] min-h-12 px-4 py-2 shadow-lg bg-surface-primary border border-stroke-secondary', 'native:overflow-hidden', ], { variants: { color: { neutral: '', info: '', success: '', warning: '', danger: '', }, }, defaultVariants: { color: 'neutral', }, }, ); /** * Toast wrapper variants * * Applied to the outer `Animated.View` that carries `data-state="entering|open|closed"`. * On web the CSS engine drives the enter/exit animation via `data-state` transitions — * fade + short translate — replacing the Reanimated layout animations that ran on * the same element on native. Native ignores these classes (no `web:` prefix hits). * * `placement` picks the slide direction so top-placed toasts translate up on close and * bottom-placed toasts translate down. The unprefixed `-translate-y-2` / `translate-y-2` * on the `top-*` / `bottom-*` variants also seeds the entering-state offset synchronously * — CSS transitions to `translate-y-0` when `data-state` flips to `open`. * * `motion-reduce:transition-none` collapses the transition for users with * `prefers-reduced-motion: reduce`. In that case `transitionend` may never fire, so * the Toast component's safety timeout handles removal. */ export const toastWrapperVariants = cva( [ 'absolute', 'web:transition-[opacity,transform] web:duration-200 web:ease-out', 'web:data-[state=entering]:opacity-0', 'web:data-[state=open]:opacity-100', 'web:data-[state=closed]:opacity-0 web:data-[state=closed]:duration-150', 'web:motion-reduce:transition-none web:motion-reduce:duration-0', ], { variants: { placement: { 'top-start': [ 'top-0 native:left-0 native:right-0', 'web:left-0 web:max-w-[460px]', 'web:data-[state=entering]:-translate-y-2', 'web:data-[state=closed]:-translate-y-2', ], top: [ 'top-0 native:left-0 native:right-0', 'web:left-1/2 web:-translate-x-1/2 web:max-w-[460px]', 'web:data-[state=entering]:-translate-y-2', 'web:data-[state=closed]:-translate-y-2', ], 'top-end': [ 'top-0 native:left-0 native:right-0', 'web:right-0 web:max-w-[460px]', 'web:data-[state=entering]:-translate-y-2', 'web:data-[state=closed]:-translate-y-2', ], 'bottom-start': [ 'bottom-0 native:left-0 native:right-0', 'web:left-0 web:max-w-[460px]', 'web:data-[state=entering]:translate-y-2', 'web:data-[state=closed]:translate-y-2', ], bottom: [ 'bottom-0 native:left-0 native:right-0', 'web:left-1/2 web:-translate-x-1/2 web:max-w-[460px]', 'web:data-[state=entering]:translate-y-2', 'web:data-[state=closed]:translate-y-2', ], 'bottom-end': [ 'bottom-0 native:left-0 native:right-0', 'web:right-0 web:max-w-[460px]', 'web:data-[state=entering]:translate-y-2', 'web:data-[state=closed]:translate-y-2', ], }, }, defaultVariants: { placement: 'bottom', }, }, ); export const toastLabelVariants = cva(['text-base font-sans-medium'], { variants: { color: { neutral: 'text-content-primary', info: 'text-content-info', success: 'text-content-success', warning: 'text-content-warning', danger: 'text-content-danger', }, }, defaultVariants: { color: 'neutral', }, }); export const toastDescriptionVariants = cva(['text-sm text-content-primary'], { variants: { color: { neutral: 'text-content-primary', info: 'text-content-info', success: 'text-content-success', warning: 'text-content-warning', danger: 'text-content-danger', }, }, defaultVariants: { color: 'neutral', }, }); export const toastIconVariants = cva(['size-5'], { variants: { color: { neutral: 'text-content-primary', info: 'text-content-info', success: 'text-content-success', warning: 'text-content-warning', danger: 'text-content-danger', }, }, defaultVariants: { color: 'neutral', }, }); export const toastStyleSheet = StyleSheet.create({ root: { borderCurve: 'continuous', }, }); export type ToastColorProps = VariantProps;