Skip to content

Commit 92ee17e

Browse files
mattstrattonclaude
andcommitted
Add devopsdays branding and modern admin theme
Public site: - devopsdays brand colors (#0082AB blue, gray gradient) - Brain logo in navbar and footer - Roboto font (matching current site) - Gradient masthead on homepage and event pages - Bold uppercase tracking-wider headings (devopsdays style) Admin: - django-unfold admin theme with custom sidebar navigation - devopsdays primary color applied to admin UI - Organized sidebar: Events, Sponsors, Speakers, Content, Users Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent f0fe5cc commit 92ee17e

File tree

12 files changed

+197
-102
lines changed

12 files changed

+197
-102
lines changed

apps/events/admin.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,30 @@
11
from django.contrib import admin
2+
from unfold.admin import ModelAdmin, TabularInline
23

34
from .models import City, Event, EventPage, TeamMember, ProgramEntry
45

56

67
@admin.register(City)
7-
class CityAdmin(admin.ModelAdmin):
8+
class CityAdmin(ModelAdmin):
89
list_display = ["display_name", "slug", "country"]
910
search_fields = ["display_name", "slug", "country"]
1011
prepopulated_fields = {"slug": ("display_name",)}
1112

1213

13-
class EventPageInline(admin.TabularInline):
14+
class EventPageInline(TabularInline):
1415
model = EventPage
1516
extra = 0
1617
fields = ["page_type", "title", "slug", "is_visible", "sort_order"]
1718

1819

19-
class TeamMemberInline(admin.TabularInline):
20+
class TeamMemberInline(TabularInline):
2021
model = TeamMember
2122
extra = 0
2223
fields = ["name", "employer", "twitter", "github", "linkedin", "sort_order"]
2324

2425

2526
@admin.register(Event)
26-
class EventAdmin(admin.ModelAdmin):
27+
class EventAdmin(ModelAdmin):
2728
list_display = ["__str__", "year", "city", "status", "start_date", "sponsors_accepted"]
2829
list_filter = ["status", "year", "sponsors_accepted"]
2930
search_fields = ["slug", "city__display_name", "city__slug"]
@@ -64,22 +65,22 @@ class EventAdmin(admin.ModelAdmin):
6465

6566

6667
@admin.register(EventPage)
67-
class EventPageAdmin(admin.ModelAdmin):
68+
class EventPageAdmin(ModelAdmin):
6869
list_display = ["event", "page_type", "title", "is_visible", "sort_order"]
6970
list_filter = ["page_type", "is_visible"]
7071
search_fields = ["title", "event__slug"]
7172
autocomplete_fields = ["event"]
7273

7374

7475
@admin.register(TeamMember)
75-
class TeamMemberAdmin(admin.ModelAdmin):
76+
class TeamMemberAdmin(ModelAdmin):
7677
list_display = ["name", "event", "employer"]
7778
search_fields = ["name", "event__slug"]
7879
autocomplete_fields = ["event"]
7980

8081

8182
@admin.register(ProgramEntry)
82-
class ProgramEntryAdmin(admin.ModelAdmin):
83+
class ProgramEntryAdmin(ModelAdmin):
8384
list_display = ["display_title", "event", "date", "start_time", "end_time", "entry_type"]
8485
list_filter = ["entry_type", "date"]
8586
search_fields = ["custom_title", "event__slug"]

apps/pages/admin.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
from django.contrib import admin
2+
from unfold.admin import ModelAdmin
23

34
from .models import BlogPost, StaticPage
45

56

67
@admin.register(BlogPost)
7-
class BlogPostAdmin(admin.ModelAdmin):
8+
class BlogPostAdmin(ModelAdmin):
89
list_display = ["title", "author", "published_at", "is_published"]
910
list_filter = ["published_at"]
1011
search_fields = ["title", "author"]
1112
prepopulated_fields = {"slug": ("title",)}
1213

1314

1415
@admin.register(StaticPage)
15-
class StaticPageAdmin(admin.ModelAdmin):
16+
class StaticPageAdmin(ModelAdmin):
1617
list_display = ["title", "slug", "updated_at"]
1718
search_fields = ["title", "slug"]

apps/people/admin.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
from django.contrib import admin
2+
from unfold.admin import ModelAdmin
23

34
from .models import Speaker, Talk
45

56

67
@admin.register(Speaker)
7-
class SpeakerAdmin(admin.ModelAdmin):
8+
class SpeakerAdmin(ModelAdmin):
89
list_display = ["name", "event"]
910
search_fields = ["name", "slug", "event__slug"]
1011
autocomplete_fields = ["event"]
1112

1213

1314
@admin.register(Talk)
14-
class TalkAdmin(admin.ModelAdmin):
15+
class TalkAdmin(ModelAdmin):
1516
list_display = ["title", "event", "talk_type"]
1617
list_filter = ["talk_type", "event__year"]
1718
search_fields = ["title", "slug", "event__slug"]

apps/sponsors/admin.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
11
from django.contrib import admin
2+
from unfold.admin import ModelAdmin
23

34
from .models import Sponsor, SponsorLevel, EventSponsor
45

56

67
@admin.register(Sponsor)
7-
class SponsorAdmin(admin.ModelAdmin):
8+
class SponsorAdmin(ModelAdmin):
89
list_display = ["name", "slug", "url"]
910
search_fields = ["name", "slug"]
1011
prepopulated_fields = {"slug": ("name",)}
1112

1213

1314
@admin.register(SponsorLevel)
14-
class SponsorLevelAdmin(admin.ModelAdmin):
15+
class SponsorLevelAdmin(ModelAdmin):
1516
list_display = ["label", "event", "slug", "max_sponsors", "sort_order"]
1617
list_filter = ["event__year"]
1718
search_fields = ["label", "event__slug"]
1819
autocomplete_fields = ["event"]
1920

2021

2122
@admin.register(EventSponsor)
22-
class EventSponsorAdmin(admin.ModelAdmin):
23+
class EventSponsorAdmin(ModelAdmin):
2324
list_display = ["sponsor", "event", "level"]
2425
list_filter = ["level__label", "event__year"]
2526
search_fields = ["sponsor__name", "event__slug"]

apps/users/admin.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
from django.contrib import admin
22
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
3+
from unfold.admin import ModelAdmin, TabularInline
34

45
from .models import User, EventPermission
56

67

7-
class EventPermissionInline(admin.TabularInline):
8+
class EventPermissionInline(TabularInline):
89
model = EventPermission
910
fk_name = "user"
1011
extra = 1
1112
autocomplete_fields = ["event"]
1213

1314

1415
@admin.register(User)
15-
class UserAdmin(BaseUserAdmin):
16+
class UserAdmin(BaseUserAdmin, ModelAdmin):
1617
list_display = ["email", "username", "is_core_team", "is_active", "last_login"]
1718
list_filter = ["is_core_team", "is_active", "is_staff"]
1819
search_fields = ["email", "username", "first_name", "last_name"]
@@ -23,7 +24,7 @@ class UserAdmin(BaseUserAdmin):
2324

2425

2526
@admin.register(EventPermission)
26-
class EventPermissionAdmin(admin.ModelAdmin):
27+
class EventPermissionAdmin(ModelAdmin):
2728
list_display = ["user", "event", "role", "granted_by", "created_at"]
2829
list_filter = ["role"]
2930
autocomplete_fields = ["user", "event", "granted_by"]

config/settings.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
ALLOWED_HOSTS = env("DJANGO_ALLOWED_HOSTS")
2222

2323
INSTALLED_APPS = [
24+
"unfold",
2425
"django.contrib.admin",
2526
"django.contrib.auth",
2627
"django.contrib.contenttypes",
@@ -168,6 +169,77 @@
168169

169170
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
170171

172+
# Django Unfold admin theme
173+
UNFOLD = {
174+
"SITE_TITLE": "devopsdays admin",
175+
"SITE_HEADER": "devopsdays",
176+
"SITE_SYMBOL": "calendar_month",
177+
"COLORS": {
178+
"primary": {
179+
"50": "#e6f5fa",
180+
"100": "#b3e0ed",
181+
"200": "#80cce0",
182+
"300": "#4db7d3",
183+
"400": "#26a8c9",
184+
"500": "#0082AB",
185+
"600": "#006d8f",
186+
"700": "#005873",
187+
"800": "#004357",
188+
"900": "#002e3b",
189+
"950": "#001a22",
190+
},
191+
},
192+
"SIDEBAR": {
193+
"navigation": [
194+
{
195+
"title": "Events",
196+
"icon": "calendar_month",
197+
"items": [
198+
{"title": "Events", "link": "/admin/events/event/", "icon": "event"},
199+
{"title": "Cities", "link": "/admin/events/city/", "icon": "location_city"},
200+
{"title": "Event Pages", "link": "/admin/events/eventpage/", "icon": "article"},
201+
{"title": "Team Members", "link": "/admin/events/teammember/", "icon": "group"},
202+
{"title": "Program Entries", "link": "/admin/events/programentry/", "icon": "schedule"},
203+
],
204+
},
205+
{
206+
"title": "Sponsors",
207+
"icon": "handshake",
208+
"items": [
209+
{"title": "Sponsors", "link": "/admin/sponsors/sponsor/", "icon": "business"},
210+
{"title": "Sponsor Levels", "link": "/admin/sponsors/sponsorlevel/", "icon": "star"},
211+
{"title": "Event Sponsors", "link": "/admin/sponsors/eventsponsor/", "icon": "link"},
212+
],
213+
},
214+
{
215+
"title": "Speakers & Talks",
216+
"icon": "mic",
217+
"items": [
218+
{"title": "Speakers", "link": "/admin/people/speaker/", "icon": "person"},
219+
{"title": "Talks", "link": "/admin/people/talk/", "icon": "podium"},
220+
],
221+
},
222+
{
223+
"title": "Content",
224+
"icon": "edit_note",
225+
"items": [
226+
{"title": "Blog Posts", "link": "/admin/pages/blogpost/", "icon": "rss_feed"},
227+
{"title": "Static Pages", "link": "/admin/pages/staticpage/", "icon": "description"},
228+
],
229+
},
230+
{
231+
"title": "Users & Auth",
232+
"icon": "admin_panel_settings",
233+
"items": [
234+
{"title": "Users", "link": "/admin/users/user/", "icon": "person"},
235+
{"title": "Event Permissions", "link": "/admin/users/eventpermission/", "icon": "key"},
236+
{"title": "Groups", "link": "/admin/auth/group/", "icon": "groups"},
237+
],
238+
},
239+
],
240+
},
241+
}
242+
171243
# Production security settings
172244
if not DEBUG:
173245
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ inertia-django==1.2.0
1010
Pillow==11.1.0
1111
markdown==3.7
1212
whitenoise==6.8.2
13+
django-unfold==0.44.0
1314
PyYAML==6.0.2

static/img/devopsdays-brain.png

22.4 KB
Loading

templates/public/base.html

Lines changed: 46 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{% load static %}
12
<!DOCTYPE html>
23
<html lang="en" class="h-full">
34
<head>
@@ -6,54 +7,64 @@
67
<title>{% block title %}devopsdays{% endblock %}</title>
78
<meta name="description" content="{% block meta_description %}devopsdays is a worldwide series of community-run conferences covering topics of software development, IT infrastructure operations, and the intersection between them.{% endblock %}">
89

9-
<!-- Tailwind via CDN for the POC — will be built via Vite in production -->
1010
<script src="https://cdn.tailwindcss.com"></script>
1111
<script>
1212
tailwind.config = {
1313
theme: {
1414
extend: {
1515
colors: {
16-
'dod-blue': '#1a4e8a',
17-
'dod-green': '#39b54a',
18-
'dod-dark': '#1a1a2e',
16+
'dod': {
17+
'50': '#e6f5fa',
18+
'100': '#b3e0ed',
19+
'200': '#80cce0',
20+
'300': '#4db7d3',
21+
'400': '#26a8c9',
22+
'500': '#0082AB',
23+
'600': '#006d8f',
24+
'700': '#005873',
25+
'800': '#004357',
26+
'900': '#002e3b',
27+
},
28+
'dod-gray': '#bfbfc1',
29+
'dod-ignite': '#00C342',
30+
'dod-openspace': '#FF8300',
1931
},
2032
fontFamily: {
21-
sans: ['Inter', 'system-ui', 'sans-serif'],
22-
mono: ['JetBrains Mono', 'monospace'],
33+
sans: ['Roboto', 'system-ui', 'sans-serif'],
2334
},
2435
}
2536
}
2637
}
2738
</script>
2839
<link rel="preconnect" href="https://fonts.googleapis.com">
29-
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
40+
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700;900&display=swap" rel="stylesheet">
3041

3142
{% block extra_head %}{% endblock %}
3243
</head>
33-
<body class="h-full bg-gray-50 font-sans text-gray-900">
44+
<body class="h-full bg-white font-sans text-gray-800">
3445

3546
<!-- Navigation -->
36-
<nav class="bg-dod-dark text-white shadow-lg">
47+
<nav class="bg-white border-b-2 border-dod-500">
3748
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
3849
<div class="flex items-center justify-between h-16">
3950
<div class="flex items-center space-x-8">
40-
<a href="/" class="text-xl font-bold tracking-tight">
41-
<span class="text-dod-green">devopsdays</span>
51+
<a href="/" class="flex items-center space-x-2">
52+
<img src="{% static 'img/devopsdays-brain.png' %}" alt="" class="h-8">
53+
<span class="text-xl font-black uppercase tracking-wide text-dod-500">devopsdays</span>
4254
</a>
4355
<div class="hidden md:flex space-x-6">
44-
<a href="/events/" class="text-gray-300 hover:text-white transition text-sm font-medium">Events</a>
45-
<a href="/blog/" class="text-gray-300 hover:text-white transition text-sm font-medium">Blog</a>
46-
<a href="/about/" class="text-gray-300 hover:text-white transition text-sm font-medium">About</a>
47-
<a href="/organizing/" class="text-gray-300 hover:text-white transition text-sm font-medium">Organizing</a>
48-
<a href="/speaking/" class="text-gray-300 hover:text-white transition text-sm font-medium">Speaking</a>
56+
<a href="/events/" class="text-sm font-bold uppercase tracking-wider text-dod-500 hover:text-dod-700 transition">Events</a>
57+
<a href="/blog/" class="text-sm font-bold uppercase tracking-wider text-dod-500 hover:text-dod-700 transition">Blog</a>
58+
<a href="/about/" class="text-sm font-bold uppercase tracking-wider text-dod-500 hover:text-dod-700 transition">About</a>
59+
<a href="/organizing/" class="text-sm font-bold uppercase tracking-wider text-dod-500 hover:text-dod-700 transition">Organizing</a>
60+
<a href="/speaking/" class="text-sm font-bold uppercase tracking-wider text-dod-500 hover:text-dod-700 transition">Speaking</a>
4961
</div>
5062
</div>
5163
<div class="flex items-center space-x-4">
5264
{% if user.is_authenticated %}
53-
<a href="/manage/" class="text-sm text-gray-300 hover:text-white transition">Manage Events</a>
54-
<a href="/accounts/logout/" class="text-sm text-gray-400 hover:text-white transition">Logout</a>
65+
<a href="/manage/" class="text-sm font-medium text-dod-500 hover:text-dod-700 transition">Manage Events</a>
5566
{% else %}
56-
<a href="/accounts/login/" class="text-sm bg-dod-green text-white px-4 py-2 rounded-md hover:bg-green-600 transition font-medium">Sign In</a>
67+
<a href="/accounts/login/" class="text-sm bg-dod-500 text-white px-4 py-2 rounded hover:bg-dod-600 transition font-bold uppercase tracking-wider">Sign In</a>
5768
{% endif %}
5869
</div>
5970
</div>
@@ -66,32 +77,35 @@
6677
</main>
6778

6879
<!-- Footer -->
69-
<footer class="bg-dod-dark text-gray-400 mt-16">
80+
<footer class="bg-gray-50 border-t-2 border-dod-500 mt-16">
7081
<div class="max-w-7xl mx-auto px-4 py-12 sm:px-6 lg:px-8">
7182
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
7283
<div>
73-
<h3 class="text-white font-semibold mb-3">devopsdays</h3>
74-
<p class="text-sm">A worldwide series of community-run conferences covering DevOps, software development, IT infrastructure operations, and the intersection between them.</p>
84+
<div class="flex items-center space-x-2 mb-3">
85+
<img src="{% static 'img/devopsdays-brain.png' %}" alt="" class="h-6">
86+
<span class="font-black uppercase tracking-wide text-dod-500">devopsdays</span>
87+
</div>
88+
<p class="text-sm text-gray-600">A worldwide series of community-run conferences covering DevOps, software development, IT infrastructure operations, and the intersection between them.</p>
7589
</div>
7690
<div>
77-
<h3 class="text-white font-semibold mb-3">Links</h3>
91+
<h3 class="font-bold uppercase tracking-wider text-dod-500 text-sm mb-3">Links</h3>
7892
<ul class="space-y-2 text-sm">
79-
<li><a href="/about/" class="hover:text-white transition">About</a></li>
80-
<li><a href="/organizing/" class="hover:text-white transition">Organizing Guide</a></li>
81-
<li><a href="/conduct/" class="hover:text-white transition">Code of Conduct</a></li>
82-
<li><a href="/speaking/" class="hover:text-white transition">Speaking</a></li>
93+
<li><a href="/about/" class="text-gray-600 hover:text-dod-500 transition">About</a></li>
94+
<li><a href="/organizing/" class="text-gray-600 hover:text-dod-500 transition">Organizing Guide</a></li>
95+
<li><a href="/conduct/" class="text-gray-600 hover:text-dod-500 transition">Code of Conduct</a></li>
96+
<li><a href="/speaking/" class="text-gray-600 hover:text-dod-500 transition">Speaking</a></li>
8397
</ul>
8498
</div>
8599
<div>
86-
<h3 class="text-white font-semibold mb-3">Community</h3>
100+
<h3 class="font-bold uppercase tracking-wider text-dod-500 text-sm mb-3">Community</h3>
87101
<ul class="space-y-2 text-sm">
88-
<li><a href="https://github.com/devopsdays" class="hover:text-white transition">GitHub</a></li>
89-
<li><a href="https://social.devopsdays.org" class="hover:text-white transition">Mastodon</a></li>
102+
<li><a href="https://github.com/devopsdays" class="text-gray-600 hover:text-dod-500 transition">GitHub</a></li>
103+
<li><a href="https://social.devopsdays.org" class="text-gray-600 hover:text-dod-500 transition">Mastodon</a></li>
90104
</ul>
91105
</div>
92106
</div>
93-
<div class="border-t border-gray-700 mt-8 pt-8 text-sm text-center">
94-
<p>&copy; devopsdays a community conference series since 2009</p>
107+
<div class="border-t border-gray-200 mt-8 pt-8 text-sm text-center text-gray-500">
108+
<p>&copy; devopsdays &mdash; a community conference series since 2009</p>
95109
</div>
96110
</div>
97111
</footer>

0 commit comments

Comments
 (0)