Sidebar Component¶
Version: 0.6.0
Last Updated: January 3, 2026
Location: src/templates/partials/sidebar.html
The sidebar provides the primary navigation structure for Arctyk ITSM. It displays main application sections (Dashboard, Tickets, Projects, Users, Inventory) with active state indication and collapsible sections.
Overview¶
The sidebar includes:
- Main navigation menu items
- Active state highlighting
- Icon indicators
- Optional submenu items
- Responsive collapse on mobile
- User profile section
Basic Structure¶
<aside class="sidebar">
<nav class="navbar-vertical navbar-expand-lg">
<!-- Sidebar Header -->
<div class="navbar-brand">
<a href="/"><strong>Arctyk</strong> ITSM</a>
</div>
<!-- Navigation Items -->
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link active" href="/dashboard/">
<i class="bi bi-speedometer2"></i>
<span>Dashboard</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/tickets/">
<i class="bi bi-ticket"></i>
<span>Tickets</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/projects/">
<i class="bi bi-folder"></i>
<span>Projects</span>
</a>
</li>
</ul>
<!-- User Section -->
<div class="sidebar-footer">
<div class="d-flex align-items-center">
<img
src="{{ user.profile.avatar.url }}"
alt="Avatar"
class="rounded-circle"
/>
<div class="ms-2">
<div class="fw-bold">{{ user.first_name }}</div>
<small class="text-muted">{{ user.get_role_display }}</small>
</div>
</div>
</div>
</nav>
</aside>
Active States¶
Implementation¶
from django.views.generic import TemplateView
class DashboardView(TemplateView):
template_name = 'dashboard/dashboard.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['active_nav'] = 'dashboard'
return context
Template Usage¶
<li class="nav-item">
<a class="nav-link {% if active_nav == 'dashboard' %}active{% endif %}" href="{% url 'dashboard' %}">
<i class="bi bi-speedometer2"></i>
<span>Dashboard</span>
</a>
</li>
Custom Template Tag¶
# src/config/templatetags/nav_tags.py
from django import template
register = template.Library()
@register.simple_tag
def nav_active(request, app_label):
if request.resolver_match and request.resolver_match.app_name == app_label:
return 'active'
return ''
Usage:
<a class="nav-link {% nav_active request 'tickets' %}" href="{% url 'tickets:ticket_list' %}">
Tickets
</a>
Collapsible Sections¶
Multi-Level Navigation¶
<li class="nav-item">
<a class="nav-link collapsed" href="#submenu" data-bs-toggle="collapse">
<i class="bi bi-gear"></i>
<span>Settings</span>
<i class="bi bi-chevron-down ms-auto"></i>
</a>
<div class="collapse" id="submenu">
<ul class="nav flex-column ms-3">
<li class="nav-item">
<a class="nav-link" href="{% url 'config:settings' %}"> General </a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'users:user_list' %}"> Users </a>
</li>
</ul>
</div>
</li>
Styling¶
CSS Classes¶
.sidebar {
background-color: #f8f9fa;
border-right: 1px solid #dee2e6;
min-height: 100vh;
padding: 1rem 0;
}
.nav-link {
color: #495057;
padding: 0.75rem 1rem;
transition: all 0.2s ease;
}
.nav-link:hover {
background-color: #e9ecef;
color: #212529;
}
.nav-link.active {
background-color: #0d6efd;
color: #fff;
border-left: 3px solid #0d6efd;
}
Custom Styling¶
// SCSS example
.sidebar {
$bg-color: #f8f9fa;
$active-color: #0d6efd;
$hover-color: #e9ecef;
background-color: $bg-color;
.nav-link {
&:hover {
background-color: $hover-color;
}
&.active {
background-color: $active-color;
color: white;
}
}
}
Responsive Behavior¶
Mobile Toggle¶
<button
class="navbar-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarVertical"
aria-controls="navbarVertical"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarVertical">
<!-- Sidebar content -->
</div>
CSS Media Queries¶
@media (max-width: 768px) {
.sidebar {
position: fixed;
left: 0;
top: 0;
height: 100%;
width: 250px;
z-index: 1000;
transform: translateX(-100%);
transition: transform 0.3s ease;
}
.sidebar.show {
transform: translateX(0);
}
}
Testing¶
Unit Tests¶
from django.test import TestCase, Client
from django.contrib.auth.models import User
class SidebarNavigationTest(TestCase):
def setUp(self):
self.user = User.objects.create_user(
username='testuser',
password='testpass'
)
self.client = Client()
def test_sidebar_renders(self):
self.client.login(username='testuser', password='testpass')
response = self.client.get('/dashboard/')
self.assertContains(response, 'sidebar')
self.assertContains(response, 'Dashboard')
def test_active_state(self):
self.client.login(username='testuser', password='testpass')
response = self.client.get('/tickets/')
self.assertContains(response, 'nav-link active')
Accessibility¶
<nav class="sidebar" aria-label="Main navigation">
<ul class="nav">
<li class="nav-item">
<a class="nav-link" href="/dashboard/" aria-current="page">
<i class="bi bi-speedometer2" aria-hidden="true"></i>
<span>Dashboard</span>
</a>
</li>
</ul>
</nav>
Best Practices¶
✅ Use semantic HTML (<nav>, <ul>, <li>)
✅ Indicate active page with aria-current="page"
✅ Include icons with aria-hidden="true"
✅ Provide clear visual active state
✅ Support keyboard navigation
✅ Collapse on mobile automatically
❌ Don't use nested links
❌ Don't make sidebar too wide (>250px recommended)
❌ Don't hide navigation without toggle
Resources¶
Implementation¶
See Partials Guide for details.