# NavBuddy
[](https://hex.pm/packages/navbuddy)
[](https://hexdocs.pm/navbuddy)
[](https://github.com/SangRJ/navbuddy/blob/main/LICENSE)
**Advanced Configurable Navigation Component for Phoenix LiveView**
NavBuddy provides comprehensive navigation components for Phoenix LiveView applications, featuring dropdowns, mega menus, mobile-responsive design, native DaisyUI integration, and full accessibility support.
## Features
- 🎯 **Advanced Menu System**: Single-layer dropdowns, multi-layer dropdowns, and mega menus
- 📱 **Mobile-Responsive**: Drawer, collapse, and overlay modes for mobile devices
- 🎨 **Native DaisyUI Integration**: True Tailwind CSS + DaisyUI support with theme compatibility
- 🎨 **Standalone DaisyUI**: Built-in compatibility with DaisyUI components and themes
- ♿ **Accessibility First**: WCAG 2.1 AA compliant with screen reader support
- ⌨️ **Keyboard Navigation**: Full arrow key navigation with focus management
- 🎨 **Modern Theming**: DaisyUI theme tokens with light/dark/auto theme support
- 🔧 **Highly Configurable**: Extensive configuration options
- 🚀 **Phoenix 1.8+ Ready**: Enhanced for Phoenix 1.8+ and LiveView 1.1+
- 📊 **Analytics Ready**: Built-in event tracking support
## Quick Start
Add to your `mix.exs`:
```elixir
def deps do
[
{:navbuddy, "~> 0.4.0"}
]
end
```
## Installation & Setup
Choose your integration approach:
### 🚀 Native DaisyUI Integration (Recommended for Phoenix 1.8+)
For projects with Tailwind CSS + DaisyUI already set up:
```elixir
# Add to mix.exs
{:navbuddy, "~> 0.4.0"}
```
Then use native components:
```heex
<NavBuddy.NativeComponent.native_nav
items={@nav_items}
brand={%{name: "MyApp", href: "/"}}
theme="light"
/>
```
**📖 [Complete Native Setup Guide](NATIVE_DAISYUI_GUIDE.md)**
### With DaisyUI (Recommended)
If you're using DaisyUI in your Phoenix project, NavBuddy will automatically integrate with your existing themes and styling:
```html
<!-- NavBuddy CSS will work seamlessly with your DaisyUI setup -->
<link rel="stylesheet" href="/assets/navbuddy.css" />
<script src="/assets/navbuddy.js"></script>
```
### Standalone (Without DaisyUI)
Include the CSS and JavaScript in your layout:
```html
<link rel="stylesheet" href="/assets/navbuddy.css" />
<script src="/assets/navbuddy.js"></script>
```
Use in your LiveView:
```elixir
defmodule MyAppWeb.PageLive do
use MyAppWeb, :live_view
import NavBuddy.Component
def mount(_params, _session, socket) do
socket = assign(socket, :navigation_items, navigation_items())
{:ok, socket}
end
def render(assigns) do
~H"""
<.nav_menu items={@navigation_items} current_path={@current_path} />
"""
end
defp navigation_items do
[
%{id: "home", label: "Home", navigate: "/"},
%{id: "about", label: "About", navigate: "/about"},
%{
id: "products",
label: "Products",
navigate: "/products",
dropdown: [
%{id: "web", label: "Web Apps", navigate: "/products/web"},
%{id: "mobile", label: "Mobile Apps", navigate: "/products/mobile"}
]
}
]
end
end
```
## Navigation Structure
NavBuddy supports various navigation structures with comprehensive validation and helpful error messages.
### Required vs Optional Fields
**Required fields:**
- `id` (string) - Unique identifier for the navigation item
- `label` (string) - Display text for the navigation item
**Optional fields:**
- `navigate` (string) - Path to navigate to (required for clickable items)
- `icon` (string) - Icon class or identifier
- `dropdown` (list) - List of child navigation items
- `mega_menu` (map) - Mega menu configuration
- `active` (boolean) - Whether the item is currently active
- `disabled` (boolean) - Whether the item is disabled
- `permissions` (string | list) - Required permissions to see the item
### Convenience Functions
NavBuddy provides helper functions to create navigation items easily:
```elixir
# Simple navigation item
NavBuddy.nav_item("home", "Home", "/")
# => %{id: "home", label: "Home", navigate: "/"}
# With additional options
NavBuddy.nav_item("admin", "Admin", "/admin", permissions: "admin", icon: "admin-icon")
# => %{id: "admin", label: "Admin", navigate: "/admin", permissions: "admin", icon: "admin-icon"}
# Dropdown item
dropdown_items = [
NavBuddy.nav_item("web", "Web Apps", "/products/web"),
NavBuddy.nav_item("mobile", "Mobile Apps", "/products/mobile")
]
NavBuddy.dropdown_item("products", "Products", dropdown_items)
# Mega menu item
categories = %{
"Frontend" => [NavBuddy.nav_item("react", "React", "/tools/react")],
"Backend" => [NavBuddy.nav_item("elixir", "Elixir", "/tools/elixir")]
}
NavBuddy.mega_menu_item("tools", "Tools", categories)
```
### Validation and Error Handling
NavBuddy provides comprehensive validation with helpful error messages:
```elixir
# Invalid item - missing required fields
NavBuddy.validate_nav_item(%{label: "Home"})
# => {:error, "Navigation item missing required field 'id'. Required fields: id (string), label (string). Optional fields: navigate, icon, dropdown, mega_menu, active, disabled, permissions"}
# Invalid field type
NavBuddy.validate_nav_item(%{id: "home", label: "Home", active: "yes"})
# => {:error, "Field 'active' must be a boolean, got: \"yes\""}
# Conflicting navigation types
NavBuddy.validate_nav_item(%{id: "item", label: "Item", dropdown: [], mega_menu: %{}})
# => {:error, "Navigation item cannot have both 'dropdown' and 'mega_menu'. Choose one or the other."}
```
### Simple Links
```elixir
%{id: "home", label: "Home", navigate: "/", icon: "home"}
```
### Single-Layer Dropdowns
```elixir
%{
id: "products",
label: "Products",
navigate: "/products",
icon: "package",
dropdown: [
%{id: "web", label: "Web Development", navigate: "/products/web"},
%{id: "mobile", label: "Mobile Apps", navigate: "/products/mobile"}
]
}
```
### Multi-Layer Dropdowns
```elixir
%{
id: "services",
label: "Services",
dropdown: [
%{
id: "development",
label: "Development",
dropdown: [
%{id: "frontend", label: "Frontend", navigate: "/services/frontend"},
%{id: "backend", label: "Backend", navigate: "/services/backend"}
]
}
]
}
```
### Mega Menus
```elixir
%{
id: "solutions",
label: "Solutions",
mega_menu: %{
title: "Our Solutions",
description: "Comprehensive business solutions",
sections: [
%{
title: "For Small Business",
description: "Perfect for startups",
items: [
%{id: "starter", label: "Starter Package", navigate: "/solutions/starter"},
%{id: "growth", label: "Growth Package", navigate: "/solutions/growth"}
]
},
%{
title: "For Enterprise",
description: "Scalable solutions",
items: [
%{id: "enterprise", label: "Enterprise Suite", navigate: "/solutions/enterprise"},
%{id: "custom", label: "Custom Solutions", navigate: "/solutions/custom"}
]
}
]
}
}
```
## Permissions-Based Navigation
NavBuddy supports permissions-based filtering to control which navigation items are visible to users. Add a `permissions` key to any navigation item to require specific permissions.
### Basic Permissions
```elixir
navigation_items = [
%{id: "home", label: "Home", navigate: "/"},
%{id: "dashboard", label: "Dashboard", navigate: "/dashboard", permissions: "user"},
%{id: "admin", label: "Admin Panel", navigate: "/admin", permissions: "admin"}
]
```
### Multiple Required Permissions
Use a list to require multiple permissions (user must have ALL permissions):
```elixir
navigation_items = [
%{
id: "sensitive",
label: "Sensitive Data",
navigate: "/sensitive",
permissions: ["admin", "data_access"] # Requires BOTH permissions
}
]
```
### Nested Structure Permissions
Permissions work with dropdowns and mega-menus:
```elixir
navigation_items = [
%{
id: "reports",
label: "Reports",
dropdown: [
%{id: "basic_reports", label: "Basic Reports", navigate: "/reports/basic"},
%{id: "admin_reports", label: "Admin Reports", navigate: "/reports/admin", permissions: "admin"},
%{id: "analytics", label: "Analytics", navigate: "/reports/analytics", permissions: ["analyst", "premium"]}
]
}
]
```
### Using in LiveView
Pass user permissions to the component:
```elixir
# In your LiveView mount/3 or assign
def mount(_params, session, socket) do
current_user = get_current_user(session)
user_permissions = get_user_permissions(current_user)
socket = assign(socket, user_permissions: user_permissions)
{:ok, socket}
end
# In your template
<NavBuddy.Component.nav_menu
items={@navigation_items}
user_permissions={@user_permissions}
config={@nav_config}
current_path={@current_path} />
```
### Permission Examples
```elixir
# String permission - user needs "admin" permission
%{id: "admin", label: "Admin", navigate: "/admin", permissions: "admin"}
# List of permissions - user needs ALL listed permissions
%{id: "sensitive", label: "Sensitive", navigate: "/sensitive", permissions: ["admin", "security"]}
# No permissions - visible to everyone
%{id: "home", label: "Home", navigate: "/"}
# nil permissions - same as no permissions key
%{id: "public", label: "Public", navigate: "/public", permissions: nil}
```
The filtering automatically:
- Shows items with no `permissions` key to everyone
- Shows items with `permissions: nil` to everyone
- Requires exact string match for string permissions
- Requires ALL permissions in list for list permissions
- Recursively filters dropdown and mega-menu items
- Removes parent items if all children are filtered out
## Configuration
Create a configuration struct:
```elixir
config = %NavBuddy.Config{
orientation: :horizontal, # :horizontal | :vertical
theme: :auto, # :light | :dark | :auto
dropdown_trigger: :hover, # :hover | :click
mobile_behavior: :drawer, # :drawer | :collapse | :overlay
animations: true, # boolean
accessibility: true, # boolean
mobile_breakpoint: 768, # pixels
max_dropdown_depth: 5, # levels
auto_close_delay: 300, # milliseconds
touch_gestures: true, # boolean
analytics: false # boolean
}
```
Use with your navigation:
```elixir
<.nav_menu items={@navigation_items} config={config} current_path={@current_path} />
```
## LiveView Integration
NavBuddy includes comprehensive LiveView helpers:
```elixir
defmodule MyAppWeb.PageLive do
use MyAppWeb, :live_view
use NavBuddy.LiveHelpers # Adds event handlers
def mount(_params, _session, socket) do
socket =
socket
|> assign_navbuddy_state()
|> assign(:navigation_items, navigation_items())
{:ok, socket}
end
# Event handlers are automatically available:
# handle_event("navbuddy:item_click", ...)
# handle_event("navbuddy:dropdown_toggle", ...)
# handle_event("navbuddy:mobile_toggle", ...)
end
```
## Mobile Optimization
### Drawer Mode (Default)
Slide-out navigation drawer on mobile:
```elixir
%NavBuddy.Config{mobile_behavior: :drawer}
```
### Collapse Mode
Collapsible menu sections:
```elixir
%NavBuddy.Config{mobile_behavior: :collapse}
```
### Overlay Mode
Full-screen navigation overlay:
```elixir
%NavBuddy.Config{mobile_behavior: :overlay}
```
## Theming
### Automatic Theme Detection
```elixir
%NavBuddy.Config{theme: :auto} # Follows system preference
```
### Manual Theme Selection
```elixir
%NavBuddy.Config{theme: :light} # or :dark
```
### Custom Themes
Override CSS custom properties:
```css
:root {
--navbuddy-bg-primary: #your-color;
--navbuddy-text-primary: #your-color;
--navbuddy-accent-primary: #your-color;
}
```
## Accessibility Features
NavBuddy is built with accessibility in mind:
- **ARIA Support**: Proper ARIA labels, roles, and states
- **Keyboard Navigation**: Full keyboard support with arrow keys
- **Screen Readers**: Compatible with screen reader software
- **Focus Management**: Intelligent focus handling
- **High Contrast**: Support for high contrast themes
- **Reduced Motion**: Respects `prefers-reduced-motion`
### Keyboard Navigation
- **Arrow Keys**: Navigate between menu items
- **Enter/Space**: Activate menu items
- **Escape**: Close menus and return focus
- **Home/End**: Jump to first/last items
- **Tab**: Standard tab navigation
## Breadcrumbs
Automatic breadcrumb generation:
```elixir
<.nav_breadcrumbs
items={@navigation_items}
current_path={@current_path}
/>
```
## Advanced Features
### Smart Positioning
Automatically adjusts dropdown and mega menu positions to stay within viewport:
```elixir
%NavBuddy.Config{smart_positioning: true}
```
### Touch Gestures
Mobile-optimized touch interactions:
```elixir
%NavBuddy.Config{touch_gestures: true}
```
### Analytics Integration
Built-in event tracking:
```elixir
%NavBuddy.Config{analytics: true}
# Events emitted:
# navbuddy:item_click
# navbuddy:dropdown_open
# navbuddy:dropdown_close
# navbuddy:mobile_toggle
```
### Animation Control
Control animations:
```elixir
%NavBuddy.Config{animations: true, auto_close_delay: 300}
```
## CSS Classes
NavBuddy provides semantic CSS classes for customization:
- `.navbuddy-nav` - Main navigation container
- `.navbuddy-nav-item` - Navigation item wrapper
- `.navbuddy-nav-link` - Navigation links
- `.navbuddy-dropdown` - Dropdown menus
- `.navbuddy-mega-menu` - Mega menus
- `.navbuddy-mobile-toggle` - Mobile menu toggle
- `.navbuddy-breadcrumbs` - Breadcrumb navigation
## JavaScript API
Access the JavaScript API directly:
```javascript
// Initialize manually
const nav = new NavBuddy(document.querySelector(".navbuddy-nav"), {
smartPositioning: true,
keyboardNavigation: true,
analytics: true,
});
// Methods
nav.openDropdown("products");
nav.closeMegaMenu("solutions");
nav.toggleMobileNav();
nav.closeAllMenus();
// Events
nav.trackEvent("custom_event", { custom: "data" });
// State
const state = nav.getState();
```
## Browser Support
- Chrome 90+
- Firefox 88+
- Safari 14+
- Edge 90+
## Examples
Check out the included example navigation structure:
```elixir
NavBuddy.example_navigation()
```
This returns a comprehensive navigation structure showcasing all features:
- Simple links with icons
- Single and multi-level dropdowns
- Mega menus with grid layouts
- Mixed navigation types
## Contributing
1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request
## Changelog
### v0.1.0 (2025-01-25)
- Initial release
- Advanced dropdown and mega menu support
- Mobile-responsive design
- Full accessibility implementation
- Comprehensive theming system
- LiveView integration
- Touch gesture support
- Analytics integration
## License
MIT License - see [LICENSE](LICENSE) for details.
## Acknowledgments
- Phoenix LiveView team for the excellent framework
- Tailwind CSS for design inspiration
- Accessibility guidelines from WCAG 2.1
---
**Made with ❤️ for the Phoenix community**