NavBuddy
View SourceAdvanced 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:
def deps do
[
{:navbuddy, "~> 0.4.0"}
]
endInstallation & Setup
Choose your integration approach:
🚀 Native DaisyUI Integration (Recommended for Phoenix 1.8+)
For projects with Tailwind CSS + DaisyUI already set up:
# Add to mix.exs
{:navbuddy, "~> 0.4.0"}Then use native components:
<NavBuddy.NativeComponent.native_nav
items={@nav_items}
brand={%{name: "MyApp", href: "/"}}
theme="light"
/>With DaisyUI (Recommended)
If you're using DaisyUI in your Phoenix project, NavBuddy will automatically integrate with your existing themes and styling:
<!-- 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:
<link rel="stylesheet" href="/assets/navbuddy.css" />
<script src="/assets/navbuddy.js"></script>Use in your LiveView:
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
endNavigation 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 itemlabel(string) - Display text for the navigation item
Optional fields:
navigate(string) - Path to navigate to (required for clickable items)icon(string) - Icon class or identifierdropdown(list) - List of child navigation itemsmega_menu(map) - Mega menu configurationactive(boolean) - Whether the item is currently activedisabled(boolean) - Whether the item is disabledpermissions(string | list) - Required permissions to see the item
Convenience Functions
NavBuddy provides helper functions to create navigation items easily:
# 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:
# 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
%{id: "home", label: "Home", navigate: "/", icon: "home"}Single-Layer Dropdowns
%{
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
%{
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
%{
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
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):
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:
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:
# 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
# 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
permissionskey to everyone - Shows items with
permissions: nilto 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:
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:
<.nav_menu items={@navigation_items} config={config} current_path={@current_path} />LiveView Integration
NavBuddy includes comprehensive LiveView helpers:
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", ...)
endMobile Optimization
Drawer Mode (Default)
Slide-out navigation drawer on mobile:
%NavBuddy.Config{mobile_behavior: :drawer}Collapse Mode
Collapsible menu sections:
%NavBuddy.Config{mobile_behavior: :collapse}Overlay Mode
Full-screen navigation overlay:
%NavBuddy.Config{mobile_behavior: :overlay}Theming
Automatic Theme Detection
%NavBuddy.Config{theme: :auto} # Follows system preferenceManual Theme Selection
%NavBuddy.Config{theme: :light} # or :darkCustom Themes
Override CSS custom properties:
: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:
<.nav_breadcrumbs
items={@navigation_items}
current_path={@current_path}
/>Advanced Features
Smart Positioning
Automatically adjusts dropdown and mega menu positions to stay within viewport:
%NavBuddy.Config{smart_positioning: true}Touch Gestures
Mobile-optimized touch interactions:
%NavBuddy.Config{touch_gestures: true}Analytics Integration
Built-in event tracking:
%NavBuddy.Config{analytics: true}
# Events emitted:
# navbuddy:item_click
# navbuddy:dropdown_open
# navbuddy:dropdown_close
# navbuddy:mobile_toggleAnimation Control
Control animations:
%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:
// 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:
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
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - 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 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