-
Notifications
You must be signed in to change notification settings - Fork 202
Expand file tree
/
Copy pathBottomNav.tsx
More file actions
92 lines (87 loc) · 2.96 KB
/
Copy pathBottomNav.tsx
File metadata and controls
92 lines (87 loc) · 2.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import { Link, useLocation } from 'react-router-dom';
import type { LucideIcon } from 'lucide-react';
import {
LayoutDashboard,
ArrowLeftRight,
CreditCard,
FilePlus,
Gavel,
} from 'lucide-react';
import { cx } from '../utils/classnames';
import './BottomNav.css';
/**
* Navigation item configuration for the bottom nav bar.
* Each item maps to a route in the application.
*/
interface BottomNavItem {
/** Unique identifier for the nav item. */
id: string;
/** Display label — visually hidden on very narrow viewports. */
label: string;
/** Route path the item navigates to. */
to: string;
/** Lucide icon component. */
Icon: LucideIcon;
/** Whether the route match must be exact (e.g. for `/`). */
exact?: boolean;
}
const NAV_ITEMS: BottomNavItem[] = [
{ id: 'nav-dashboard', label: 'Dashboard', to: '/', Icon: LayoutDashboard, exact: true },
{ id: 'nav-transactions', label: 'Transactions', to: '/transactions', Icon: ArrowLeftRight },
{ id: 'nav-credit-lines', label: 'Credit Lines', to: '/credit-lines', Icon: CreditCard },
{ id: 'nav-open-credit', label: 'Open Credit', to: '/open-credit', Icon: FilePlus },
{ id: 'nav-auctions', label: 'Auctions', to: '/dutch-auctions', Icon: Gavel },
];
/**
* Mobile-first bottom navigation bar.
*
* Visible only on viewports ≤ 768 px (`--bp-mobile`). Provides icon + label
* links to the primary application routes. The active route is visually
* distinguished with the accent colour and a top-border indicator.
*
* Accessibility (WCAG 2.1 AA):
* - Every item is a semantic `<a>` via React Router's `NavLink`.
* - Touch targets are ≥ 44 × 44 px (WCAG 2.5.5).
* - `aria-current="page"` is set automatically by `NavLink` on the active item.
* - Icons are marked `aria-hidden="true"` so screen readers announce only the label.
* - Focus indicators use the shared focus-ring pattern.
*
* Motion:
* - Entrance animation (`bottom-nav--enter`) under normal conditions.
* - Suppressed when `prefers-reduced-motion: reduce` is active.
*
* @example
* <BottomNav />
*/
export function BottomNav() {
const location = useLocation();
return (
<nav
className="bottom-nav"
aria-label="Primary navigation"
role="navigation"
>
{NAV_ITEMS.map((item) => {
const isActive = item.exact
? location.pathname === item.to
: location.pathname.startsWith(item.to);
return (
<Link
key={item.id}
to={item.to}
className={cx('bottom-nav__link', { 'bottom-nav__link--active': isActive })}
aria-current={isActive ? 'page' : undefined}
>
<item.Icon
className="bottom-nav__icon"
size={22}
strokeWidth={isActive ? 2.5 : 1.75}
aria-hidden="true"
/>
<span className="bottom-nav__label">{item.label}</span>
</Link>
);
})}
</nav>
);
}