# Cover header

> Drawer panel with a dark cover (large avatar, name, View profile, a bell with a count, an overflow button and a round + action), icon items active in Accent Colour text, a divider and text-only secondary items.

Block · navigation. Registry item `drawer-03`, shown in the vega Style. JSON: https://nativecn.dev/r/styles/vega/drawer-03.json

## Installation

```sh
npx nativecn-cli@latest add drawer-03
```

Also installs: [drawer](https://nativecn.dev/docs/components/drawer), [avatar](https://nativecn.dev/docs/components/avatar), [badge](https://nativecn.dev/docs/components/badge), [button](https://nativecn.dev/docs/components/button), [pressable](https://nativecn.dev/docs/primitives/pressable), [separator](https://nativecn.dev/docs/components/separator), [text](https://nativecn.dev/docs/components/text), [theme](https://nativecn.dev/docs/theme/theme).

Packages: `expo-router`, `lucide-react-native@^1.51.0`, `react-native-svg`.

## What makes it different

A consumer or commerce app drawer: a dark profile cover that stays dark in both Schemes (with a light status bar over it while the drawer is open), and items that show the current route by Accent Colour text and icon, with no background.

## Block Variants

- [drawer-01](https://nativecn.dev/docs/blocks/drawer-01) – Profile header + grouped sections: The familiar Gmail/Slack drawer: who is signed in on top, items grouped under small headings, Log out at the bottom; the active item sits on a muted background.
- [drawer-02](https://nativecn.dev/docs/blocks/drawer-02) – SaaS workspace: A product workspace: an Accent-tinted onboarding Checklist on top, a second group pushed to the bottom, and a team switcher and account button in the footer instead of a profile header.
- **drawer-03** (this page) – Cover header: A consumer or commerce app drawer: a dark profile cover that stays dark in both Schemes (with a light status bar over it while the drawer is open), and items that show the current route by Accent Colour text and icon, with no background.

## Accessibility

- Each item is a DrawerItem: role link with aria-selected while its route is active, or role button for an action without href.
- An item's visible label is its accessible name.
- Items are 48 high in Vega and 40 in Nova, with the tap area extended to 48.
- The bell is an icon-only Button named "Notifications, <count> unread"; its count Badge is hidden from screen readers.
- The overflow and + buttons are icon-only Buttons named "More" and "Create".
- View profile has role link; the initials Avatar is decorative.

## Notes

In app/(drawer)/_layout.tsx, with Expo Router's drawer from expo-router/drawer (never @react-navigation/drawer or the npm package expo-drawer): <Drawer drawerContent={(props) => <Drawer03 {...props} />} />. Spread every drawerContent prop in, so items can close the drawer. Tapping an item navigates and closes the drawer; the backdrop and the system back gesture also close it. Names, avatars, counts, items and routes are placeholders: rename them and point each href at a route in your drawer Layout. Item heights follow the drawer.item Style Slot (Vega 48, Nova 40), and the panel keeps clear of the status bar and home indicator.

## Source

### {components}/drawer-03/index.tsx

```tsx
// drawer-03: cover header. The panel of a drawer Layout, built from the drawer Components on
// Expo Router's own drawer (`expo-router/drawer`; never install `@react-navigation/drawer` or
// the unrelated npm package `expo-drawer`).
//
// In app/(drawer)/_layout.tsx:
//   <Drawer
//     drawerContent={(props) => (
//       <Drawer03 {...props} onNotificationsPress={openInbox} onCreatePress={create} />
//     )}
//     screenOptions={{ drawerStyle: { width: 330 } }}
//   />
//
// The cover is dark in both Schemes: it renders the dark Scheme's Colour Roles through a nested
// ThemeProvider. The name, counts, items and routes are placeholders: rename them to your app's.
import { router } from "expo-router";
import { getDrawerStatusFromState } from "expo-router/drawer";
import {
  Bell,
  EllipsisVertical,
  History,
  House,
  Plus,
  ShoppingCart,
  Store,
} from "lucide-react-native";
import { StatusBar, View } from "react-native";

import { Avatar } from "@/components/avatar";
import { Badge } from "@/components/badge";
import { Button } from "@/components/button";
import {
  DrawerContent,
  DrawerHeader,
  DrawerItem,
  DrawerSection,
  type DrawerContentProps,
} from "@/components/drawer";
import { Pressable } from "@/components/primitives/pressable";
import { Separator } from "@/components/separator";
import { Text } from "@/components/text";
import { ThemeProvider, colors, createStyles } from "@/theme";

export type Drawer03Props = DrawerContentProps & {
  /** Runs when the bell in the cover is pressed. */
  onNotificationsPress?: () => void;
  /** Runs when the overflow (⋮) button in the cover is pressed. */
  onMorePress?: () => void;
  /** Runs when the round + action in the cover is pressed. */
  onCreatePress?: () => void;
};

/** Placeholder unread count shown on the bell. */
const UNREAD = 45;

export function Drawer03({
  onNotificationsPress,
  onMorePress,
  onCreatePress,
  ...props
}: Drawer03Props) {
  const styles = useStyles();
  const open = props.state ? getDrawerStatusFromState(props.state) === "open" : false;
  const viewProfile = () => {
    router.navigate("/profile");
    props.navigation?.closeDrawer();
  };

  return (
    <DrawerContent {...props}>
      {/* The cover is dark in both Schemes, so the status bar over it is light while it's open. */}
      {open ? <StatusBar barStyle="light-content" /> : null}
      <DrawerHeader style={styles.cover}>
        <ThemeProvider scheme="dark">
          <Cover
            onViewProfile={viewProfile}
            onNotificationsPress={onNotificationsPress}
            onMorePress={onMorePress}
            onCreatePress={onCreatePress}
          />
        </ThemeProvider>
      </DrawerHeader>

      <DrawerSection>
        <DrawerItem label="Home" icon={House} href="/" variant="text" />
        <DrawerItem label="Shop" icon={Store} href="/shop" variant="text" />
        <DrawerItem label="Shopping cart" icon={ShoppingCart} href="/cart" variant="text" />
        <DrawerItem label="Same-day delivery" icon={History} href="/delivery" variant="text" />
      </DrawerSection>

      <Separator />

      <DrawerSection>
        <DrawerItem label="Invite friends" href="/invite" variant="text" />
        <DrawerItem label="Find friends" href="/friends" variant="text" />
        <DrawerItem label="Account" href="/account" variant="text" />
        <DrawerItem label="Settings" href="/settings" variant="text" />
      </DrawerSection>
    </DrawerContent>
  );
}

type CoverProps = {
  onViewProfile: () => void;
  onNotificationsPress?: () => void;
  onMorePress?: () => void;
  onCreatePress?: () => void;
};

/** The cover's content, rendered inside a dark ThemeProvider so every Colour Role is the dark one. */
function Cover({ onViewProfile, onNotificationsPress, onMorePress, onCreatePress }: CoverProps) {
  const styles = useCoverStyles();
  return (
    <View style={styles.root}>
      <View style={styles.top}>
        <Avatar fallback="JD" size="lg" style={styles.avatar} />
        <View style={styles.actions}>
          <View>
            <Button
              variant="ghost"
              icon={Bell}
              aria-label={`Notifications, ${UNREAD} unread`}
              onPress={onNotificationsPress}
            />
            <Badge
              label={String(UNREAD)}
              aria-hidden
              importantForAccessibility="no-hide-descendants"
              style={styles.count}
            />
          </View>
          <Button variant="ghost" icon={EllipsisVertical} aria-label="More" onPress={onMorePress} />
        </View>
      </View>

      <View style={styles.identity}>
        <Text variant="h4" numberOfLines={1}>
          Jane Doe
        </Text>
        <Pressable role="link" onPress={onViewProfile} style={styles.link}>
          <Text variant="small" color="mutedForeground">
            View profile
          </Text>
        </Pressable>
      </View>

      <Button
        icon={Plus}
        size="lg"
        aria-label="Create"
        onPress={onCreatePress}
        style={styles.create}
      />
    </View>
  );
}

const useStyles = createStyles(() => ({
  // The dark Scheme's surface in both Schemes, so the cover always reads as dark.
  cover: { backgroundColor: colors.dark.muted },
}));

const useCoverStyles = createStyles((t) => ({
  root: { gap: t.spacing[3] },
  top: { flexDirection: "row", alignItems: "flex-start", justifyContent: "space-between" },
  avatar: { backgroundColor: t.colors.background },
  actions: { flexDirection: "row", alignItems: "center" },
  count: { position: "absolute", top: 0, end: 0, pointerEvents: "none" },
  identity: { gap: t.spacing[0.5], paddingEnd: t.spacing[16] },
  link: { alignSelf: "flex-start" },
  create: { position: "absolute", end: 0, bottom: 0, borderRadius: t.radius.full },
}));
```
