Skip to main content
The Tracker component displays activity or time-series data in a compact grid format, similar to GitHub’s contribution graph. Each item represents a data point with optional color coding and tooltips.

Basic Usage

{% load unfold %}

{% component "unfold/components/tracker.html" with data=tracker_data %}
{% endcomponent %}

Props

data
list
required
List of tracker items. Each item should be a dictionary with optional color and tooltip keys.
data[].color
string
Tailwind CSS background color class for the item (e.g., bg-green-600).
data[].tooltip
string
Tooltip text displayed on hover.
class
string
Additional CSS classes for the tracker container.

Examples

Simple Activity Tracker

# In view or context
tracker_data = [
    {'color': 'bg-green-600', 'tooltip': 'March 1: 15 commits'},
    {'color': 'bg-green-400', 'tooltip': 'March 2: 8 commits'},
    {'color': 'bg-base-300', 'tooltip': 'March 3: No activity'},
    {'color': 'bg-green-600', 'tooltip': 'March 4: 12 commits'},
    # ... more days
]
{% component "unfold/components/tracker.html" with data=tracker_data %}
{% endcomponent %}

Tracker in Card

{% component "unfold/components/card.html" with title="Activity This Month" %}
  {% component "unfold/components/tracker.html" with data=activity_tracker %}
  {% endcomponent %}
{% endcomponent %}

Complete Example: User Activity Tracker

# views.py
from django.views.generic import TemplateView
from unfold.views import UnfoldModelAdminViewMixin
from django.utils import timezone
from datetime import timedelta
from django.db.models import Count

class UserActivityView(UnfoldModelAdminViewMixin, TemplateView):
    title = "User Activity"
    template_name = "admin/user_activity.html"
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        
        # Generate last 90 days of activity
        today = timezone.now().date()
        activity_data = []
        
        # Get activity counts per day
        activities = Activity.objects.filter(
            created_at__gte=today - timedelta(days=89)
        ).values('created_at__date').annotate(
            count=Count('id')
        )
        
        activity_by_date = {
            item['created_at__date']: item['count'] 
            for item in activities
        }
        
        # Generate tracker data
        for i in range(89, -1, -1):
            date = today - timedelta(days=i)
            count = activity_by_date.get(date, 0)
            
            # Color based on activity level
            if count == 0:
                color = 'bg-base-300 dark:bg-base-400'
            elif count < 5:
                color = 'bg-green-300 dark:bg-green-400'
            elif count < 10:
                color = 'bg-green-500 dark:bg-green-600'
            else:
                color = 'bg-green-700 dark:bg-green-800'
            
            activity_data.append({
                'color': color,
                'tooltip': f"{date.strftime('%b %d, %Y')}: {count} activities"
            })
        
        context['activity_tracker'] = activity_data
        return context
{# templates/admin/user_activity.html #}
{% extends "unfold/layouts/base.html" %}
{% load unfold %}

{% block content %}
<div class="container mx-auto">
  {% component "unfold/components/card.html" with title="Activity Overview" %}
    <div class="mb-4">
      <p class="text-sm text-gray-600 dark:text-gray-400">Last 90 days</p>
    </div>
    
    {% component "unfold/components/tracker.html" with data=activity_tracker class="max-w-4xl" %}
    {% endcomponent %}
    
    <div class="mt-4 flex items-center gap-4 text-sm">
      <span class="text-gray-600 dark:text-gray-400">Less</span>
      <div class="flex gap-1">
        <div class="w-4 h-4 bg-base-300 dark:bg-base-400 rounded"></div>
        <div class="w-4 h-4 bg-green-300 dark:bg-green-400 rounded"></div>
        <div class="w-4 h-4 bg-green-500 dark:bg-green-600 rounded"></div>
        <div class="w-4 h-4 bg-green-700 dark:bg-green-800 rounded"></div>
      </div>
      <span class="text-gray-600 dark:text-gray-400">More</span>
    </div>
  {% endcomponent %}
</div>
{% endblock %}

Use Cases

GitHub-Style Contribution Graph

def generate_contribution_graph(user, days=365):
    """Generate a year of contribution data"""
    today = timezone.now().date()
    contributions = Contribution.objects.filter(
        user=user,
        created_at__gte=today - timedelta(days=days-1)
    ).values('created_at__date').annotate(count=Count('id'))
    
    contrib_by_date = {item['created_at__date']: item['count'] for item in contributions}
    
    tracker_data = []
    for i in range(days-1, -1, -1):
        date = today - timedelta(days=i)
        count = contrib_by_date.get(date, 0)
        
        # Color intensity based on contribution count
        colors = [
            'bg-base-300 dark:bg-base-600',
            'bg-green-200 dark:bg-green-900',
            'bg-green-400 dark:bg-green-700',
            'bg-green-600 dark:bg-green-500',
            'bg-green-800 dark:bg-green-300',
        ]
        
        level = min(count, 4)  # Cap at 4 levels
        
        tracker_data.append({
            'color': colors[level],
            'tooltip': f"{date.strftime('%a, %b %d, %Y')}: {count} contributions"
        })
    
    return tracker_data

Server Uptime Tracker

def generate_uptime_tracker(server, days=30):
    """Generate uptime status for last 30 days"""
    today = timezone.now().date()
    uptime_data = []
    
    for i in range(days-1, -1, -1):
        date = today - timedelta(days=i)
        
        # Check if server was up on this day
        was_up = ServerStatus.objects.filter(
            server=server,
            date=date,
            status='up'
        ).exists()
        
        uptime_data.append({
            'color': 'bg-green-600' if was_up else 'bg-red-600',
            'tooltip': f"{date.strftime('%b %d')}: {'Up' if was_up else 'Down'}"
        })
    
    return uptime_data
{% component "unfold/components/card.html" with title="Server Uptime" %}
  {% component "unfold/components/tracker.html" with data=uptime_tracker %}
  {% endcomponent %}
  
  {% slot footer %}
    <div class="flex items-center gap-4 text-sm">
      <div class="flex items-center gap-2">
        <div class="w-3 h-3 bg-green-600 rounded"></div>
        <span>Online</span>
      </div>
      <div class="flex items-center gap-2">
        <div class="w-3 h-3 bg-red-600 rounded"></div>
        <span>Offline</span>
      </div>
    </div>
  {% endslot %}
{% endcomponent %}

Custom Status Tracker

status_tracker = [
    {'color': 'bg-blue-600', 'tooltip': 'Planned'},
    {'color': 'bg-blue-600', 'tooltip': 'Planned'},
    {'color': 'bg-yellow-600', 'tooltip': 'In Progress'},
    {'color': 'bg-yellow-600', 'tooltip': 'In Progress'},
    {'color': 'bg-green-600', 'tooltip': 'Completed'},
    {'color': 'bg-green-600', 'tooltip': 'Completed'},
    {'color': 'bg-red-600', 'tooltip': 'Failed'},
    {'color': 'bg-base-300', 'tooltip': 'No data'},
]

Styling

Default tracker styles:
  • Container: flex flex-row gap-0.5 overflow-hidden rounded-default
  • Items: h-8 px-px size-full
  • Default color: bg-base-300 dark:bg-base-400
  • Hover effect: hover:opacity-50

Custom Layouts

{# Larger squares #}
{% component "unfold/components/tracker.html" with data=tracker_data class="[&>li]:h-12" %}
{% endcomponent %}

{# Smaller gaps #}
{% component "unfold/components/tracker.html" with data=tracker_data class="gap-1" %}
{% endcomponent %}

{# Square items #}
{% component "unfold/components/tracker.html" with data=tracker_data class="[&>li]:w-8" %}
{% endcomponent %}

Accessibility

  • Tooltips provide context for each tracker item
  • Use semantic colors (green for success, red for error, etc.)
  • Include a legend when color coding is complex

Best Practices

  1. Consistent Data: Ensure all days/periods are represented
  2. Color Meaning: Use intuitive color schemes
  3. Tooltips: Provide detailed information in tooltips
  4. Legend: Include a legend for complex color coding
  5. Performance: Limit to reasonable time periods (e.g., 365 days max)
  • Progress - Linear progress indicators
  • Card - Wrap trackers in cards
  • Chart - More detailed time-series visualizations

Source

Template: src/unfold/templates/unfold/components/tracker.html:1

Build docs developers (and LLMs) love