# Sheet

> A modal component that slides in from the side of the screen, commonly used for navigation menus, filters, and detail views.

**BNA UI** — a React Native / Expo component library.
These components render through `react-native`, not the DOM: there are no HTML
elements, no Tailwind classes and no Radix primitives. Source is copied into your
project and imported through `@/components/ui/*`, `@/components/charts/*`,
`@/hooks/*` and `@/theme/*`. Colours come from the `useColor` hook rather than
hardcoded hex; sizing tokens (`HEIGHT`, `FONT_SIZE`, `BORDER_RADIUS`, `CORNERS`)
come from `@/theme/globals`.

- Docs: https://ui.ahmedbna.com/docs/components/sheet
- Markdown: https://ui.ahmedbna.com/docs/components/sheet.md
- Structured JSON (props, usage, source, examples): https://ui.ahmedbna.com/r/ai/sheet.json
- Install payload (source plus every file it imports): https://ui.ahmedbna.com/r/sheet.json
- Install: `npx bna-ui add sheet`
- npm dependencies: `expo-haptics`, `lucide-react-native`, `react-native-reanimated`, `react-native-safe-area-context`, `react-native-svg`, `react-native-worklets`
- Registry dependencies: `mode-provider`, `useColorScheme`, `colors`, `useColor`, `globals`, `useHaptics`, `text`, `view`, `icon`, `spinner`, `button`
- Preview recording: https://demo.ahmedbna.com/0268-sheet-demo.MP4

---

**Example:** A basic sheet that slides in from the right side

```tsx
// components/demo/sheet/sheet-demo.tsx
import { Button } from '@/components/ui/button';
import {
  Sheet,
  SheetContent,
  SheetDescription,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from '@/components/ui/sheet';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import React, { useState } from 'react';

export function SheetDemo() {
  const [open, setOpen] = useState(false);

  return (
    <Sheet open={open} onOpenChange={setOpen}>
      <SheetTrigger>Open Sheet</SheetTrigger>
      <SheetContent>
        <SheetHeader>
          <SheetTitle>Welcome to the Sheet</SheetTitle>
          <SheetDescription>
            This is a basic sheet component that slides in from the right side
            of the screen.
          </SheetDescription>
        </SheetHeader>
        <View style={{ padding: 24, gap: 16 }}>
          <Text>
            This sheet can contain any content you need. It's perfect for
            navigation menus, forms, settings, or detailed information.
          </Text>
          <Button onPress={() => setOpen(false)}>Close Sheet</Button>
        </View>
      </SheetContent>
    </Sheet>
  );
}
```

## Installation

### CLI

```bash
npx bna-ui add sheet
```

### Manual

**1.** Install the following dependencies:

```bash
npx expo install lucide-react-native
```

**2.** Copy and paste the following code into your project.

```tsx
// components/ui/sheet.tsx
import { Button } from '@/components/ui/button';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import { useColor } from '@/hooks/useColor';
import { BORDER_RADIUS, FONT_SIZE } from '@/theme/globals';
import { X } from 'lucide-react-native';
import React, { useEffect } from 'react';
import {
  Modal,
  Platform,
  Pressable,
  StyleSheet,
  TouchableOpacity,
  useWindowDimensions,
  ViewStyle,
} from 'react-native';
import Animated, {
  Easing,
  interpolate,
  runOnJS,
  useAnimatedStyle,
  useSharedValue,
  withTiming,
} from 'react-native-reanimated';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

type SheetSide = 'left' | 'right';

interface SheetProps {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  side?: SheetSide;
  children: React.ReactNode;
}

interface SheetContentProps {
  children: React.ReactNode;
  style?: ViewStyle;
}

interface SheetHeaderProps {
  children: React.ReactNode;
  style?: ViewStyle;
}

interface SheetTitleProps {
  children: React.ReactNode;
}

interface SheetDescriptionProps {
  children: React.ReactNode;
}

interface SheetTriggerProps {
  children: React.ReactNode;
  asChild?: boolean;
}

interface SheetContextValue {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  side: SheetSide;
}

const SheetContext = React.createContext<SheetContextValue | null>(null);

const useSheet = () => {
  const context = React.useContext(SheetContext);
  if (!context) {
    throw new Error('Sheet components must be used within a Sheet');
  }
  return context;
};

export function Sheet({
  open,
  onOpenChange,
  side = 'right',
  children,
}: SheetProps) {
  return (
    <SheetContext.Provider value={{ open, onOpenChange, side }}>
      {children}
    </SheetContext.Provider>
  );
}

export function SheetTrigger({ children, asChild }: SheetTriggerProps) {
  const context = React.useContext(SheetContext);

  const handlePress = () => {
    if (context) {
      context.onOpenChange(true);
    }
  };

  if (asChild && React.isValidElement(children)) {
    return React.cloneElement(children as React.ReactElement<any>, {
      onPress: handlePress,
    });
  }

  return <Button onPress={handlePress}>{children}</Button>;
}

export function SheetContent({ children, style }: SheetContentProps) {
  const { open, onOpenChange, side } = useSheet();
  const { width: screenWidth } = useWindowDimensions();
  const insets = useSafeAreaInsets();
  const sheetWidth = Math.min(screenWidth * 0.8, 400);
  const [isVisible, setIsVisible] = React.useState(open);

  const backgroundColor = useColor('background');
  const borderColor = useColor('border');
  const iconColor = useColor('text');

  // Animation values using Reanimated's useSharedValue
  const initialPosition = side === 'left' ? -sheetWidth : sheetWidth;
  const translateX = useSharedValue(initialPosition);
  const overlayOpacity = useSharedValue(0);

  // Effect to handle the animation based on the `open` prop
  useEffect(() => {
    // Reset position if side changes while closed
    if (open && !isVisible) {
      translateX.value = side === 'left' ? -sheetWidth : sheetWidth;
    }

    if (open) {
      setIsVisible(true); // Mount the modal
      // Animate in
      translateX.value = withTiming(0, {
        duration: 300,
        easing: Easing.out(Easing.quad),
      });
      overlayOpacity.value = withTiming(1, { duration: 300 });
    } else if (isVisible) {
      // Animate out, then hide modal in the callback
      translateX.value = withTiming(
        initialPosition,
        { duration: 250 },
        (finished) => {
          if (finished) {
            // Use runOnJS to update React state from the UI thread
            runOnJS(setIsVisible)(false);
          }
        }
      );
      overlayOpacity.value = withTiming(0, { duration: 250 });
    }
  }, [open, side, sheetWidth]); // Rerun if these change

  // Animated style for the sheet content
  const animatedSheetStyle = useAnimatedStyle(() => {
    return {
      transform: [{ translateX: translateX.value }],
    };
  });

  // Animated style for the overlay
  const animatedOverlayStyle = useAnimatedStyle(() => {
    return {
      opacity: interpolate(overlayOpacity.value, [0, 1], [0, 0.3]),
    };
  });

  const handleClose = () => {
    onOpenChange(false);
  };

  if (!isVisible) {
    return null;
  }

  return (
    <Modal
      visible={isVisible}
      transparent={true}
      animationType='none'
      onRequestClose={handleClose}
      statusBarTranslucent={true}
    >
      <View style={styles.modalContainer}>
        {/* Semi-transparent overlay */}
        <Animated.View style={[styles.overlay, animatedOverlayStyle]}>
          <Pressable style={styles.overlayPressable} onPress={handleClose} />
        </Animated.View>

        {/* Sheet */}
        <Animated.View
          style={[
            styles.sheet,
            {
              borderRadius: BORDER_RADIUS,
              backgroundColor,
              borderColor,
              width: sheetWidth,
              [side]: 0,
            },
            animatedSheetStyle, // Apply the animated style
            style,
          ]}
          accessibilityViewIsModal
        >
          {/* Close button */}
          <TouchableOpacity
            style={[
              styles.closeButton,
              {
                backgroundColor: backgroundColor,
                top: insets.top + 10,
                [side === 'left' ? 'right' : 'left']: 16,
              },
            ]}
            onPress={handleClose}
            hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
            accessibilityRole='button'
            accessibilityLabel='Close'
          >
            <X size={20} color={iconColor} />
          </TouchableOpacity>

          {/* Content */}
          <View style={styles.contentContainer}>{children}</View>
        </Animated.View>
      </View>
    </Modal>
  );
}

// Unchanged components below

export function SheetHeader({ children, style }: SheetHeaderProps) {
  const insets = useSafeAreaInsets();

  return (
    <View style={[styles.header, { paddingTop: insets.top + 50 }, style]}>
      {children}
    </View>
  );
}

export function SheetTitle({ children }: SheetTitleProps) {
  return (
    <Text variant='title' style={styles.title}>
      {children}
    </Text>
  );
}

export function SheetDescription({ children }: SheetDescriptionProps) {
  const mutedColor = useColor('textMuted');

  return (
    <Text style={[styles.description, { color: mutedColor }]}>{children}</Text>
  );
}

const styles = StyleSheet.create({
  modalContainer: {
    flex: 1,
  },
  overlay: {
    ...StyleSheet.absoluteFill,
    backgroundColor: 'rgba(0, 0, 0, 1)', // Opacity is controlled by animation
  },
  overlayPressable: {
    flex: 1,
  },
  sheet: {
    position: 'absolute',
    top: 0,
    bottom: 0,
    borderLeftWidth: 1,
    borderRightWidth: 1,
    ...Platform.select({
      ios: {
        shadowColor: '#000',
        shadowOffset: { width: 0, height: 4 },
        shadowOpacity: 0.25,
        shadowRadius: 8,
      },
      android: {
        elevation: 10,
      },
    }),
  },
  closeButton: {
    position: 'absolute',
    zIndex: 1,
    borderRadius: 999, // Make it circular
    width: 32,
    height: 32,
    alignItems: 'center',
    justifyContent: 'center',
  },
  contentContainer: {
    flex: 1,
  },
  header: {
    paddingHorizontal: 24,
    paddingBottom: 16,
  },
  title: {
    marginBottom: 8,
  },
  description: {
    fontSize: FONT_SIZE,
    lineHeight: 20,
  },
});
```

**3.** Update the import paths to match your project setup.

## Usage

```tsx
import {
  Sheet,
  SheetContent,
  SheetDescription,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from '@/components/ui/sheet';
```

```tsx
<Sheet>
  <SheetTrigger>
    <Button>Open Sheet</Button>
  </SheetTrigger>
  <SheetContent>
    <SheetHeader>
      <SheetTitle>Sheet Title</SheetTitle>
      <SheetDescription>
        This is a description of the sheet content.
      </SheetDescription>
    </SheetHeader>
    {/* Your content here */}
  </SheetContent>
</Sheet>
```

## Examples

#### Default

**Example:** A basic sheet that slides in from the right side

```tsx
// components/demo/sheet/sheet-demo.tsx
import { Button } from '@/components/ui/button';
import {
  Sheet,
  SheetContent,
  SheetDescription,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from '@/components/ui/sheet';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import React, { useState } from 'react';

export function SheetDemo() {
  const [open, setOpen] = useState(false);

  return (
    <Sheet open={open} onOpenChange={setOpen}>
      <SheetTrigger>Open Sheet</SheetTrigger>
      <SheetContent>
        <SheetHeader>
          <SheetTitle>Welcome to the Sheet</SheetTitle>
          <SheetDescription>
            This is a basic sheet component that slides in from the right side
            of the screen.
          </SheetDescription>
        </SheetHeader>
        <View style={{ padding: 24, gap: 16 }}>
          <Text>
            This sheet can contain any content you need. It's perfect for
            navigation menus, forms, settings, or detailed information.
          </Text>
          <Button onPress={() => setOpen(false)}>Close Sheet</Button>
        </View>
      </SheetContent>
    </Sheet>
  );
}
```

#### Left Side

**Example:** A sheet that slides in from the left side

```tsx
// components/demo/sheet/sheet-left.tsx
import { Button } from '@/components/ui/button';
import {
  Sheet,
  SheetContent,
  SheetDescription,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from '@/components/ui/sheet';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import React, { useState } from 'react';

export function SheetLeft() {
  const [open, setOpen] = useState(false);

  return (
    <Sheet open={open} onOpenChange={setOpen} side='left'>
      <SheetTrigger asChild>
        <Button>Open Left Sheet</Button>
      </SheetTrigger>
      <SheetContent>
        <SheetHeader>
          <SheetTitle>Left Side Sheet</SheetTitle>
          <SheetDescription>
            This sheet slides in from the left side of the screen.
          </SheetDescription>
        </SheetHeader>
        <View style={{ padding: 24, gap: 16 }}>
          <Text>
            Left-side sheets are commonly used for navigation menus and primary
            actions that need to be easily accessible.
          </Text>
          <Button onPress={() => setOpen(false)}>Close Sheet</Button>
        </View>
      </SheetContent>
    </Sheet>
  );
}
```

#### Navigation Menu

**Example:** A sheet used as a navigation menu with links

```tsx
// components/demo/sheet/sheet-navigation.tsx
import { Button } from '@/components/ui/button';
import { Icon } from '@/components/ui/icon';
import {
  Sheet,
  SheetContent,
  SheetDescription,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from '@/components/ui/sheet';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import { useColor } from '@/hooks/useColor';
import { Bell, Home, Mail, Search, Settings, User } from 'lucide-react-native';
import React, { useState } from 'react';
import { StyleSheet, TouchableOpacity } from 'react-native';

export function SheetNavigation() {
  const [open, setOpen] = useState(false);
  const [activeItem, setActiveItem] = useState('home');

  const textColor = useColor('text');
  const mutedColor = useColor('textMuted');
  const borderColor = useColor('border');

  const navigationItems = [
    { id: 'home', label: 'Home', icon: Home },
    { id: 'profile', label: 'Profile', icon: User },
    { id: 'messages', label: 'Messages', icon: Mail },
    { id: 'search', label: 'Search', icon: Search },
    { id: 'notifications', label: 'Notifications', icon: Bell },
    { id: 'settings', label: 'Settings', icon: Settings },
  ];

  const handleItemPress = (itemId: string) => {
    setActiveItem(itemId);
    setOpen(false);
  };

  return (
    <Sheet open={open} onOpenChange={setOpen} side='left'>
      <SheetTrigger asChild>
        <Button>Open Navigation</Button>
      </SheetTrigger>
      <SheetContent>
        <SheetHeader>
          <SheetTitle>Navigation Menu</SheetTitle>
          <SheetDescription>
            Navigate to different sections of the app.
          </SheetDescription>
        </SheetHeader>
        <View style={styles.navigationContainer}>
          {navigationItems.map((item) => {
            const name = item.icon;
            const isActive = activeItem === item.id;

            return (
              <TouchableOpacity
                key={item.id}
                style={[
                  styles.navigationItem,
                  {
                    backgroundColor: isActive
                      ? `${textColor}10`
                      : 'transparent',
                    borderColor,
                  },
                ]}
                onPress={() => handleItemPress(item.id)}
              >
                <Icon
                  name={name}
                  size={20}
                  color={isActive ? textColor : mutedColor}
                />
                <Text
                  style={[
                    styles.navigationText,
                    { color: isActive ? textColor : mutedColor },
                  ]}
                >
                  {item.label}
                </Text>
              </TouchableOpacity>
            );
          })}
        </View>
      </SheetContent>
    </Sheet>
  );
}

const styles = StyleSheet.create({
  navigationContainer: {
    padding: 16,
    gap: 8,
  },
  navigationItem: {
    flexDirection: 'row',
    alignItems: 'center',
    gap: 12,
    padding: 12,
    borderRadius: 8,
    borderWidth: 1,
    borderColor: 'transparent',
  },
  navigationText: {
    fontSize: 16,
    fontWeight: '500',
  },
});
```

#### Form Sheet

**Example:** A sheet containing a form with input fields

```tsx
// components/demo/sheet/sheet-form.tsx
import { Button } from '@/components/ui/button';
import {
  Sheet,
  SheetContent,
  SheetDescription,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from '@/components/ui/sheet';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import { useColor } from '@/hooks/useColor';
import React, { useState } from 'react';
import { Alert, StyleSheet, TextInput } from 'react-native';

export function SheetForm() {
  const [open, setOpen] = useState(false);
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    message: '',
  });

  const textColor = useColor('text');
  const backgroundColor = useColor('background');
  const borderColor = useColor('border');
  const mutedColor = useColor('textMuted');

  const handleSubmit = () => {
    if (!formData.name || !formData.email || !formData.message) {
      Alert.alert('Error', 'Please fill in all fields');
      return;
    }

    Alert.alert('Success', 'Form submitted successfully!');
    setFormData({ name: '', email: '', message: '' });
    setOpen(false);
  };

  const handleReset = () => {
    setFormData({ name: '', email: '', message: '' });
  };

  return (
    <Sheet open={open} onOpenChange={setOpen}>
      <SheetTrigger asChild>
        <Button>Open Contact Form</Button>
      </SheetTrigger>
      <SheetContent>
        <SheetHeader>
          <SheetTitle>Contact Us</SheetTitle>
          <SheetDescription>
            Fill out the form below and we'll get back to you soon.
          </SheetDescription>
        </SheetHeader>
        <View style={styles.formContainer}>
          <View style={styles.fieldContainer}>
            <Text style={[styles.label, { color: textColor }]}>Name</Text>
            <TextInput
              style={[
                styles.input,
                {
                  borderColor,
                  backgroundColor,
                  color: textColor,
                },
              ]}
              value={formData.name}
              onChangeText={(text) =>
                setFormData((prev) => ({ ...prev, name: text }))
              }
              placeholder='Enter your name'
              placeholderTextColor={mutedColor}
            />
          </View>

          <View style={styles.fieldContainer}>
            <Text style={[styles.label, { color: textColor }]}>Email</Text>
            <TextInput
              style={[
                styles.input,
                {
                  borderColor,
                  backgroundColor,
                  color: textColor,
                },
              ]}
              value={formData.email}
              onChangeText={(text) =>
                setFormData((prev) => ({ ...prev, email: text }))
              }
              placeholder='Enter your email'
              placeholderTextColor={mutedColor}
              keyboardType='email-address'
              autoCapitalize='none'
            />
          </View>

          <View style={styles.fieldContainer}>
            <Text style={[styles.label, { color: textColor }]}>Message</Text>
            <TextInput
              style={[
                styles.input,
                styles.textArea,
                {
                  borderColor,
                  backgroundColor,
                  color: textColor,
                },
              ]}
              value={formData.message}
              onChangeText={(text) =>
                setFormData((prev) => ({ ...prev, message: text }))
              }
              placeholder='Enter your message'
              placeholderTextColor={mutedColor}
              multiline
              numberOfLines={4}
              textAlignVertical='top'
            />
          </View>

          <View style={styles.buttonContainer}>
            <Button style={styles.button} onPress={handleSubmit}>
              Submit
            </Button>
            <Button
              variant='outline'
              style={styles.button}
              onPress={handleReset}
            >
              Reset
            </Button>
          </View>
        </View>
      </SheetContent>
    </Sheet>
  );
}

const styles = StyleSheet.create({
  formContainer: {
    padding: 24,
    gap: 20,
  },
  fieldContainer: {
    gap: 8,
  },
  label: {
    fontSize: 16,
    fontWeight: '500',
  },
  input: {
    borderWidth: 1,
    borderRadius: 8,
    padding: 12,
    fontSize: 16,
  },
  textArea: {
    height: 100,
  },
  buttonContainer: {
    flexDirection: 'row',
    gap: 12,
    marginTop: 12,
  },
  button: {
    flex: 1,
  },
});
```

#### Filter Sheet

**Example:** A sheet used for filtering options

```tsx
// components/demo/sheet/sheet-filter.tsx
import { Button } from '@/components/ui/button';
import {
  Sheet,
  SheetContent,
  SheetDescription,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from '@/components/ui/sheet';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import { useColor } from '@/hooks/useColor';
import { Filter } from 'lucide-react-native';
import React, { useState } from 'react';
import { StyleSheet, TouchableOpacity } from 'react-native';

export function SheetFilter() {
  const [open, setOpen] = useState(false);
  const [filters, setFilters] = useState({
    category: 'all',
    price: 'all',
    rating: 'all',
    brand: 'all',
  });

  const textColor = useColor('text');
  const mutedColor = useColor('textMuted');
  const borderColor = useColor('border');

  const filterOptions = {
    category: [
      { value: 'all', label: 'All Categories' },
      { value: 'electronics', label: 'Electronics' },
      { value: 'clothing', label: 'Clothing' },
      { value: 'books', label: 'Books' },
      { value: 'home', label: 'Home & Garden' },
    ],
    price: [
      { value: 'all', label: 'Any Price' },
      { value: 'under-25', label: 'Under $25' },
      { value: '25-50', label: '$25 - $50' },
      { value: '50-100', label: '$50 - $100' },
      { value: 'over-100', label: 'Over $100' },
    ],
    rating: [
      { value: 'all', label: 'Any Rating' },
      { value: '4-plus', label: '4+ Stars' },
      { value: '3-plus', label: '3+ Stars' },
      { value: '2-plus', label: '2+ Stars' },
    ],
    brand: [
      { value: 'all', label: 'All Brands' },
      { value: 'apple', label: 'Apple' },
      { value: 'samsung', label: 'Samsung' },
      { value: 'nike', label: 'Nike' },
      { value: 'adidas', label: 'Adidas' },
    ],
  };

  const handleFilterChange = (
    filterType: keyof typeof filters,
    value: string
  ) => {
    setFilters((prev) => ({ ...prev, [filterType]: value }));
  };

  const handleApplyFilters = () => {
    // Apply filters logic here
    console.log('Applied filters:', filters);
    setOpen(false);
  };

  const handleClearFilters = () => {
    setFilters({
      category: 'all',
      price: 'all',
      rating: 'all',
      brand: 'all',
    });
  };

  const renderFilterSection = (
    title: string,
    filterType: keyof typeof filters,
    options: { value: string; label: string }[]
  ) => (
    <View style={styles.filterSection}>
      <Text style={[styles.sectionTitle, { color: textColor }]}>{title}</Text>
      <View style={styles.optionsContainer}>
        {options.map((option) => (
          <TouchableOpacity
            key={option.value}
            style={[
              styles.option,
              {
                borderColor,
                backgroundColor:
                  filters[filterType] === option.value
                    ? `${textColor}10`
                    : 'transparent',
              },
            ]}
            onPress={() => handleFilterChange(filterType, option.value)}
          >
            <Text
              style={[
                styles.optionText,
                {
                  color:
                    filters[filterType] === option.value
                      ? textColor
                      : mutedColor,
                },
              ]}
            >
              {option.label}
            </Text>
          </TouchableOpacity>
        ))}
      </View>
    </View>
  );

  return (
    <Sheet open={open} onOpenChange={setOpen}>
      <SheetTrigger asChild>
        <Button icon={Filter}>Filter</Button>
      </SheetTrigger>
      <SheetContent>
        <SheetHeader>
          <SheetTitle>Filter Products</SheetTitle>
          <SheetDescription>
            Refine your search results using the filters below.
          </SheetDescription>
        </SheetHeader>
        <View style={styles.filterContainer}>
          {renderFilterSection('Category', 'category', filterOptions.category)}
          {renderFilterSection('Price Range', 'price', filterOptions.price)}
          {renderFilterSection('Rating', 'rating', filterOptions.rating)}
          {renderFilterSection('Brand', 'brand', filterOptions.brand)}

          <View style={styles.buttonContainer}>
            <Button style={styles.button} onPress={handleApplyFilters}>
              Apply Filters
            </Button>
            <Button
              variant='outline'
              style={styles.button}
              onPress={handleClearFilters}
            >
              Clear All
            </Button>
          </View>
        </View>
      </SheetContent>
    </Sheet>
  );
}

const styles = StyleSheet.create({
  filterContainer: {
    padding: 16,
    gap: 24,
  },
  filterSection: {
    gap: 12,
  },
  sectionTitle: {
    fontSize: 18,
    fontWeight: '600',
  },
  optionsContainer: {
    gap: 8,
  },
  option: {
    padding: 12,
    borderRadius: 8,
    borderWidth: 1,
  },
  optionText: {
    fontSize: 16,
  },
  buttonContainer: {
    flexDirection: 'row',
    gap: 12,
    marginTop: 12,
  },
  button: {
    flex: 1,
  },
});
```

## API Reference

### Sheet

The root component that manages the sheet state and provides context to child components.

| Prop           | Type                      | Default   | Description                                   |
| -------------- | ------------------------- | --------- | --------------------------------------------- |
| `open`         | `boolean`                 | -         | Controls whether the sheet is open or closed. |
| `onOpenChange` | `(open: boolean) => void` | -         | Callback fired when the sheet state changes.  |
| `side`         | `'left' \| 'right'`       | `'right'` | The side from which the sheet slides in.      |
| `children`     | `ReactNode`               | -         | The sheet trigger and content components.     |

### SheetTrigger

The trigger component that opens the sheet when pressed.

| Prop       | Type        | Default | Description                                     |
| ---------- | ----------- | ------- | ----------------------------------------------- |
| `children` | `ReactNode` | -       | The trigger content (usually a button or text). |
| `asChild`  | `boolean`   | `false` | Whether to render as a child component.         |

### SheetContent

The main content container that slides in from the specified side.

| Prop       | Type        | Description                                          |
| ---------- | ----------- | ---------------------------------------------------- |
| `children` | `ReactNode` | The content to display inside the sheet.             |
| `style`    | `ViewStyle` | Additional styles to apply to the content container. |

### SheetHeader

A header component that provides consistent spacing and layout for the sheet title and description.

| Prop       | Type        | Description                                 |
| ---------- | ----------- | ------------------------------------------- |
| `children` | `ReactNode` | The header content (title and description). |
| `style`    | `ViewStyle` | Additional styles to apply to the header.   |

### SheetTitle

The title component for the sheet header.

| Prop       | Type        | Description                |
| ---------- | ----------- | -------------------------- |
| `children` | `ReactNode` | The title text or content. |

### SheetDescription

The description component for the sheet header.

| Prop       | Type        | Description                      |
| ---------- | ----------- | -------------------------------- |
| `children` | `ReactNode` | The description text or content. |

## Accessibility

The Sheet component is built with accessibility in mind:

- Uses native Modal component for proper focus management
- Includes proper ARIA attributes for screen readers
- Supports keyboard navigation and dismissal
- Close button is positioned for easy access
- Overlay can be pressed to dismiss the sheet
- Proper hit areas for touch targets

## Animation

The Sheet component includes smooth animations:

- Slides in from the specified side (left or right)
- Fades in the overlay backdrop
- Animates out when dismissed
- Uses native animations for optimal performance

## Notes

- Maximum width is set to 80% of screen width or 400px, whichever is smaller
- Includes platform-specific shadow styling for iOS and Android
- Close button position adapts based on the sheet side
