vanta-admin 0.3.1__tar.gz → 0.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. {vanta_admin-0.3.1/vanta_admin.egg-info → vanta_admin-0.5.0}/PKG-INFO +9 -3
  2. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/README.md +8 -2
  3. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/pyproject.toml +1 -1
  4. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/static/admin/css/theme.admin.css +157 -25
  5. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/static/admin/css/theme.responsive.css +1 -1
  6. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/static/admin/css/theme.tokens.css +16 -16
  7. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/static/admin/js/sidebar.js +64 -0
  8. vanta_admin-0.5.0/vanta_admin/static/admin/js/vanta-theme.js +128 -0
  9. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/templates/admin/nav_sidebar.html +49 -11
  10. {vanta_admin-0.3.1 → vanta_admin-0.5.0/vanta_admin.egg-info}/PKG-INFO +9 -3
  11. vanta_admin-0.3.1/vanta_admin/static/admin/js/vanta-theme.js +0 -37
  12. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/LICENSE +0 -0
  13. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/MANIFEST.in +0 -0
  14. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/setup.cfg +0 -0
  15. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/__init__.py +0 -0
  16. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/apps.py +0 -0
  17. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/static/admin/css/theme.base.css +0 -0
  18. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/static/admin/css/theme.css +0 -0
  19. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/static/admin/css/theme.login.css +0 -0
  20. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/static/admin/css/two_factor.css +0 -0
  21. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/static/admin/img/favicon.png +0 -0
  22. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/static/admin/js/changelist.js +0 -0
  23. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/static/admin/js/forms.js +0 -0
  24. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/static/admin/js/messages.js +0 -0
  25. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/templates/admin/app_index.html +0 -0
  26. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/templates/admin/base.html +0 -0
  27. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/templates/admin/base_site.html +0 -0
  28. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/templates/admin/change_form.html +0 -0
  29. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/templates/admin/change_list.html +0 -0
  30. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/templates/admin/filter.html +0 -0
  31. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/templates/admin/includes/breadcrumbs.html +0 -0
  32. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/templates/admin/index.html +0 -0
  33. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/templates/admin/login.html +0 -0
  34. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/templates/two_factor/_base.html +0 -0
  35. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/templates/two_factor/_base_focus.html +0 -0
  36. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/templates/two_factor/_wizard_actions.html +0 -0
  37. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/templates/two_factor/core/login.html +0 -0
  38. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/templates/two_factor/core/setup_complete.html +0 -0
  39. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/templates/two_factor/profile/profile.html +0 -0
  40. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/templatetags/__init__.py +0 -0
  41. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin/templatetags/admin_theme.py +0 -0
  42. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin.egg-info/SOURCES.txt +0 -0
  43. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin.egg-info/dependency_links.txt +0 -0
  44. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin.egg-info/requires.txt +0 -0
  45. {vanta_admin-0.3.1 → vanta_admin-0.5.0}/vanta_admin.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vanta-admin
3
- Version: 0.3.1
3
+ Version: 0.5.0
4
4
  Summary: A sharp, dark, and portable Django admin theme forged for builders who want the admin to feel less default.
5
5
  Author: oli-dev0
6
6
  License-Expression: MIT
@@ -35,7 +35,9 @@ feels more deliberate in everyday use.
35
35
  ## What It Adds
36
36
 
37
37
  - A cleaner admin layout with a fixed sidebar that remembers how you left it.
38
+ - A sidebar navigation filter that lets users quickly narrow large admin menus by app or model name.
38
39
  - Light and dark mode, using the same calm Vanta look across the admin.
40
+ - A more settled light-mode palette with clearer search focus states and stronger changelist table headers.
39
41
  - Less visual noise on the main admin screens, login pages, cards, filters, and
40
42
  two-factor auth pages.
41
43
  - Denser dashboards, tables, filters, messages, and forms so admin work feels
@@ -47,8 +49,12 @@ feels more deliberate in everyday use.
47
49
  - Styled select controls for common admin dropdowns, while Django's native
48
50
  multi-select fields stay unchanged.
49
51
  - Built-in icons for common admin areas, models, and recent actions.
50
- - A cleaner account menu with theme controls, project links, password access,
51
- logout, and a Vanta support link.
52
+ - A cleaner account menu with display preferences, project links, password
53
+ access, logout, and a Vanta support link.
54
+ - A 12-hour and 24-hour time-format preference for changelist date/time values,
55
+ stored in the browser for each admin user.
56
+ - Shorter changelist date formatting that makes date-heavy tables easier to
57
+ scan.
52
58
  - A packaged favicon for the admin.
53
59
  - A mobile sidebar overlay that makes the admin easier to use on smaller
54
60
  screens.
@@ -10,7 +10,9 @@ feels more deliberate in everyday use.
10
10
  ## What It Adds
11
11
 
12
12
  - A cleaner admin layout with a fixed sidebar that remembers how you left it.
13
+ - A sidebar navigation filter that lets users quickly narrow large admin menus by app or model name.
13
14
  - Light and dark mode, using the same calm Vanta look across the admin.
15
+ - A more settled light-mode palette with clearer search focus states and stronger changelist table headers.
14
16
  - Less visual noise on the main admin screens, login pages, cards, filters, and
15
17
  two-factor auth pages.
16
18
  - Denser dashboards, tables, filters, messages, and forms so admin work feels
@@ -22,8 +24,12 @@ feels more deliberate in everyday use.
22
24
  - Styled select controls for common admin dropdowns, while Django's native
23
25
  multi-select fields stay unchanged.
24
26
  - Built-in icons for common admin areas, models, and recent actions.
25
- - A cleaner account menu with theme controls, project links, password access,
26
- logout, and a Vanta support link.
27
+ - A cleaner account menu with display preferences, project links, password
28
+ access, logout, and a Vanta support link.
29
+ - A 12-hour and 24-hour time-format preference for changelist date/time values,
30
+ stored in the browser for each admin user.
31
+ - Shorter changelist date formatting that makes date-heavy tables easier to
32
+ scan.
27
33
  - A packaged favicon for the admin.
28
34
  - A mobile sidebar overlay that makes the admin easier to use on smaller
29
35
  screens.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "vanta-admin"
7
- version = "0.3.1"
7
+ version = "0.5.0"
8
8
  description = "A sharp, dark, and portable Django admin theme forged for builders who want the admin to feel less default."
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -112,11 +112,66 @@ body.admin-sidebar-collapsed .admin-sidebar__collapse-arrow {
112
112
  white-space: nowrap;
113
113
  }
114
114
 
115
+ .admin-sidebar__filter {
116
+ position: relative;
117
+ display: flex;
118
+ align-items: center;
119
+ padding: 0.85rem 1rem 0;
120
+ }
121
+
122
+ .admin-sidebar__filter::before {
123
+ content: "";
124
+ position: absolute;
125
+ left: 1.72rem;
126
+ width: 0.95rem;
127
+ height: 0.95rem;
128
+ background: var(--accent);
129
+ mask: url("../img/search.svg") center / contain no-repeat;
130
+ -webkit-mask: url("../img/search.svg") center / contain no-repeat;
131
+ pointer-events: none;
132
+ }
133
+
134
+ #nav-sidebar .admin-sidebar__filter input[type="search"] {
135
+ width: 100%;
136
+ min-height: 2rem;
137
+ border: 0;
138
+ border-radius: 0.27rem;
139
+ background: var(--surface-muted);
140
+ color: var(--text);
141
+ padding: 0.32rem 0.7rem 0.32rem 2.65rem;
142
+ font-size: 0.82rem;
143
+ font-weight: 650;
144
+ outline: none;
145
+ transition:
146
+ background-color var(--admin-transition),
147
+ color var(--admin-transition);
148
+ }
149
+
150
+ #nav-sidebar .admin-sidebar__filter input[type="search"]::placeholder {
151
+ color: var(--admin-muted);
152
+ opacity: 0.82;
153
+ }
154
+
155
+ #nav-sidebar .admin-sidebar__filter input[type="search"]:focus,
156
+ #nav-sidebar .admin-sidebar__filter input[type="search"]:focus-visible {
157
+ background: var(--surface);
158
+ box-shadow: none;
159
+ }
160
+
161
+ #nav-sidebar .admin-sidebar__filter input[type="search"]::-webkit-search-decoration {
162
+ appearance: none;
163
+ }
164
+
165
+ html[data-theme="light"] #nav-sidebar .admin-sidebar__filter input[type="search"]:focus,
166
+ html[data-theme="light"] #nav-sidebar .admin-sidebar__filter input[type="search"]:focus-visible {
167
+ background: #e8f1fa;
168
+ }
169
+
115
170
  .admin-sidebar__groups {
116
171
  flex: 1 1 auto;
117
172
  min-height: 0;
118
173
  overflow-y: auto;
119
- padding: 0.9rem 1rem 8.25rem;
174
+ padding: 0.65rem 1rem 8.25rem;
120
175
  scrollbar-color: var(--border-strong) transparent;
121
176
  scrollbar-width: thin;
122
177
  }
@@ -332,8 +387,8 @@ body.admin-sidebar-collapsed .admin-sidebar__collapse-arrow {
332
387
  position: absolute;
333
388
  bottom: -0.2rem;
334
389
  left: 10.45rem;
335
- width: 12.7rem;
336
- padding: 0.72rem;
390
+ width: 16.6rem;
391
+ padding: 0.42rem 0.72rem;
337
392
  border: 1px solid var(--border-strong);
338
393
  border-radius: var(--radius-md);
339
394
  background: var(--surface-strong);
@@ -341,23 +396,71 @@ body.admin-sidebar-collapsed .admin-sidebar__collapse-arrow {
341
396
  color: var(--text);
342
397
  }
343
398
 
344
- .admin-sidebar__theme-controls {
399
+ .admin-sidebar__account-section {
400
+ padding: 0.64rem 0;
401
+ }
402
+
403
+ .admin-sidebar__account-section + .admin-sidebar__account-section {
404
+ border-top: 1px solid var(--border);
405
+ }
406
+
407
+ .admin-sidebar__section-title {
408
+ margin: 0 0 0.24rem;
409
+ color: var(--subtle);
410
+ font-size: 0.72rem;
411
+ font-weight: 760;
412
+ }
413
+
414
+ .admin-sidebar__display-section {
415
+ display: grid;
416
+ gap: 0.16rem;
417
+ }
418
+
419
+ .admin-sidebar__display-row {
420
+ display: grid;
421
+ grid-template-columns: minmax(0, 1fr) auto;
422
+ align-items: center;
423
+ gap: 0.28rem;
424
+ min-height: 2.15rem;
425
+ }
426
+
427
+ .admin-sidebar__display-label {
428
+ display: inline-flex;
429
+ align-items: center;
430
+ min-width: 0;
431
+ gap: 0.18rem;
432
+ color: var(--text);
433
+ font-size: 0.82rem;
434
+ font-weight: 680;
435
+ }
436
+
437
+ .admin-sidebar__display-label svg {
438
+ width: 0.95rem;
439
+ height: 0.95rem;
440
+ flex: 0 0 auto;
441
+ color: var(--muted);
442
+ transform: translateX(-0.08rem);
443
+ }
444
+
445
+ .admin-sidebar__theme-controls,
446
+ .admin-sidebar__time-controls {
345
447
  display: grid;
346
448
  grid-template-columns: 1fr 1fr;
347
- gap: 0.32rem;
348
- padding: 0.25rem;
349
- margin-bottom: 0.64rem;
449
+ gap: 0.24rem;
450
+ padding: 0.18rem;
451
+ width: 4.75rem;
350
452
  border-radius: 0.34rem;
351
453
  background: var(--surface-muted);
352
454
  }
353
455
 
354
- .admin-sidebar__theme-button {
456
+ .admin-sidebar__theme-button,
457
+ .admin-sidebar__time-button {
355
458
  display: inline-flex;
356
459
  align-items: center;
357
460
  justify-content: center;
358
461
  gap: 0.42rem;
359
- min-height: 2.05rem;
360
- padding: 0 0.58rem;
462
+ min-height: 1.62rem;
463
+ padding: 0 0.38rem;
361
464
  border: 0;
362
465
  border-radius: 0.27rem;
363
466
  background: transparent;
@@ -365,15 +468,21 @@ body.admin-sidebar-collapsed .admin-sidebar__collapse-arrow {
365
468
  cursor: pointer;
366
469
  font-size: 0.82rem;
367
470
  font-weight: 720;
471
+ white-space: nowrap;
472
+ }
473
+
474
+ .admin-sidebar__time-button {
475
+ letter-spacing: 0;
368
476
  }
369
477
 
370
478
  .admin-sidebar__theme-button svg {
371
- width: 0.95rem;
372
- height: 0.95rem;
479
+ width: 0.92rem;
480
+ height: 0.92rem;
373
481
  }
374
482
 
375
483
  html[data-theme="dark"] .admin-sidebar__theme-button--dark,
376
- html[data-theme="light"] .admin-sidebar__theme-button--light {
484
+ html[data-theme="light"] .admin-sidebar__theme-button--light,
485
+ .admin-sidebar__time-button[aria-pressed="true"] {
377
486
  color: var(--accent-strong);
378
487
  background: var(--accent-soft);
379
488
  }
@@ -382,13 +491,18 @@ html[data-theme="light"] .admin-sidebar__theme-button--light {
382
491
  background: var(--accent-softer);
383
492
  }
384
493
 
494
+ html[data-theme="light"] .admin-sidebar__time-button[aria-pressed="true"] {
495
+ background: var(--accent-softer);
496
+ }
497
+
385
498
  .admin-sidebar__account-links {
386
499
  display: grid;
387
500
  gap: 0.1rem;
388
501
  }
389
502
 
390
503
  .admin-sidebar__account-links a,
391
- .admin-sidebar__account-links button {
504
+ .admin-sidebar__account-links button,
505
+ .admin-sidebar__logout-section button {
392
506
  display: flex;
393
507
  align-items: center;
394
508
  gap: 0.68rem;
@@ -409,7 +523,8 @@ html[data-theme="light"] .admin-sidebar__theme-button--light {
409
523
  transform 130ms var(--ease);
410
524
  }
411
525
 
412
- .admin-sidebar__account-links svg {
526
+ .admin-sidebar__account-links svg,
527
+ .admin-sidebar__logout-section svg {
413
528
  width: 1rem;
414
529
  height: 1rem;
415
530
  color: var(--muted);
@@ -486,13 +601,15 @@ html[data-theme="light"] .admin-sidebar__theme-button--light {
486
601
  .admin-sidebar__account-links .admin-sidebar__external-icon {
487
602
  width: 0.86rem;
488
603
  height: 0.86rem;
489
- margin-left: auto;
604
+ margin-left: 0.58rem;
490
605
  }
491
606
 
492
607
  .admin-sidebar__account-links a:hover,
493
608
  .admin-sidebar__account-links a:focus,
494
609
  .admin-sidebar__account-links button:hover,
495
- .admin-sidebar__account-links button:focus {
610
+ .admin-sidebar__account-links button:focus,
611
+ .admin-sidebar__logout-section button:hover,
612
+ .admin-sidebar__logout-section button:focus {
496
613
  color: var(--accent-strong);
497
614
  background: var(--accent-softer);
498
615
  transform: translateX(1px);
@@ -501,17 +618,12 @@ html[data-theme="light"] .admin-sidebar__theme-button--light {
501
618
  .admin-sidebar__account-links a:hover svg,
502
619
  .admin-sidebar__account-links a:focus svg,
503
620
  .admin-sidebar__account-links button:hover svg,
504
- .admin-sidebar__account-links button:focus svg {
621
+ .admin-sidebar__account-links button:focus svg,
622
+ .admin-sidebar__logout-section button:hover svg,
623
+ .admin-sidebar__logout-section button:focus svg {
505
624
  color: var(--accent);
506
625
  }
507
626
 
508
- .admin-sidebar__account-divider {
509
- display: block;
510
- height: 1px;
511
- margin: 0.42rem 0.24rem;
512
- background: var(--border);
513
- }
514
-
515
627
  @keyframes donation-steam {
516
628
  0%,
517
629
  100% {
@@ -534,6 +646,7 @@ body.admin-sidebar-collapsed .admin-sidebar {
534
646
  }
535
647
 
536
648
  body.admin-sidebar-collapsed .admin-sidebar__brand,
649
+ body.admin-sidebar-collapsed .admin-sidebar__filter,
537
650
  body.admin-sidebar-collapsed .admin-sidebar__groups,
538
651
  body.admin-sidebar-collapsed .admin-sidebar__account {
539
652
  display: none;
@@ -1075,6 +1188,11 @@ select,
1075
1188
  max-width: 16.8rem;
1076
1189
  }
1077
1190
 
1191
+ html[data-theme="light"] #changelist-search input[type="text"]:focus,
1192
+ html[data-theme="light"] #changelist input[type="search"]:focus {
1193
+ background: #e8f1fa;
1194
+ }
1195
+
1078
1196
  #changelist .actions select {
1079
1197
  appearance: none;
1080
1198
  min-height: 1.5rem;
@@ -1519,6 +1637,10 @@ input[type="checkbox"]:focus-visible {
1519
1637
  vertical-align: middle;
1520
1638
  }
1521
1639
 
1640
+ html[data-theme="light"] #changelist table thead th {
1641
+ background: #d5e3f1;
1642
+ }
1643
+
1522
1644
  #changelist table thead th:first-child {
1523
1645
  border-radius: var(--admin-radius-sm) 0 0 var(--admin-radius-sm);
1524
1646
  }
@@ -1563,6 +1685,11 @@ input[type="checkbox"]:focus-visible {
1563
1685
  transform: translateY(-2px);
1564
1686
  }
1565
1687
 
1688
+ html[data-theme="light"] #changelist table thead th a:hover,
1689
+ html[data-theme="light"] #changelist table thead th a:focus {
1690
+ background: #c7d9eb;
1691
+ }
1692
+
1566
1693
  #changelist table thead th a:focus-visible {
1567
1694
  outline: 2px solid var(--admin-accent);
1568
1695
  outline-offset: 2px;
@@ -1581,6 +1708,11 @@ input[type="checkbox"]:focus-visible {
1581
1708
  vertical-align: middle;
1582
1709
  }
1583
1710
 
1711
+ html[data-theme="light"] #changelist table thead th.sorted .text,
1712
+ html[data-theme="light"] #changelist table thead th.sorted .sortoptions {
1713
+ background: #c7d9eb !important;
1714
+ }
1715
+
1584
1716
  #changelist table thead th.sorted .text {
1585
1717
  float: none !important;
1586
1718
  padding: 0 !important;
@@ -182,7 +182,7 @@
182
182
  right: 0;
183
183
  left: auto;
184
184
  bottom: calc(100% + 0.55rem);
185
- width: min(12.7rem, calc(100vw - 2.4rem));
185
+ width: min(16.6rem, calc(100vw - 2.4rem));
186
186
  }
187
187
 
188
188
  .admin-model-row {
@@ -93,15 +93,15 @@ body[data-theme="light"],
93
93
  body.theme-light,
94
94
  .theme-light {
95
95
  color-scheme: light;
96
- --page-bg: #ffffff;
97
- --shell-bg: #ffffff;
98
- --sidebar-bg: #f6faff;
99
- --surface: #f6faff;
100
- --surface-strong: #ffffff;
101
- --surface-muted: #f1f7ff;
102
- --surface-hover: #eaf4ff;
103
- --row-bg: #ffffff;
104
- --row-bg-alt: #f4f9ff;
96
+ --page-bg: #f3f7fb;
97
+ --shell-bg: #f3f7fb;
98
+ --sidebar-bg: #eef5fb;
99
+ --surface: #f7fbff;
100
+ --surface-strong: #fbfdff;
101
+ --surface-muted: #dfeaf5;
102
+ --surface-hover: #e4f0fb;
103
+ --row-bg: #fbfdff;
104
+ --row-bg-alt: #f2f8fe;
105
105
  --border: #d7e6f4;
106
106
  --border-strong: #bcd3e8;
107
107
  --text: #102033;
@@ -114,12 +114,12 @@ body.theme-light,
114
114
  --accent-softer: color-mix(in srgb, var(--accent) 8%, transparent);
115
115
  --shadow: none;
116
116
  --shadow-soft: none;
117
- --input-bg: #ffffff;
118
- --admin-bg: #ffffff;
119
- --admin-sidebar-bg: #f6faff;
120
- --admin-surface: #ffffff;
121
- --admin-surface-muted: #f6faff;
122
- --admin-surface-raised: #f1f7ff;
117
+ --input-bg: #dfeaf5;
118
+ --admin-bg: #f3f7fb;
119
+ --admin-sidebar-bg: #eef5fb;
120
+ --admin-surface: #fbfdff;
121
+ --admin-surface-muted: #dfeaf5;
122
+ --admin-surface-raised: #f7fbff;
123
123
  --admin-text: #102033;
124
124
  --admin-muted: #4f647a;
125
125
  --admin-muted-soft: #71879d;
@@ -146,7 +146,7 @@ body.theme-light,
146
146
  --admin-warning-bg: color-mix(in srgb, var(--admin-warning) 14%, transparent);
147
147
  --admin-link: color-mix(in srgb, var(--admin-accent) 78%, var(--admin-muted));
148
148
  --admin-link-strong: color-mix(in srgb, var(--admin-accent) 86%, var(--admin-text));
149
- --admin-nav-item-active-bg: #eaf4ff;
149
+ --admin-nav-item-active-bg: #dbe9f7;
150
150
  --admin-nav-item-active-border: color-mix(in srgb, var(--admin-accent) 34%, var(--admin-border));
151
151
  --admin-button-text: #ffffff;
152
152
  --admin-shadow-soft: none;
@@ -10,8 +10,10 @@
10
10
  const collapseButton = document.getElementById('admin-sidebar-collapse');
11
11
  const menuButton = document.getElementById('admin-sidebar-menu-button');
12
12
  const accountDetails = document.querySelector('.admin-sidebar__account');
13
+ const navFilter = document.getElementById('admin-nav-filter');
13
14
  const mobileQuery = window.matchMedia('(max-width: 1024px)');
14
15
  const navSections = Array.from(document.querySelectorAll('[data-admin-nav-section]'));
16
+ let isFilteringNavigation = false;
15
17
 
16
18
  function setCollapsed(isCollapsed, shouldPersist) {
17
19
  body.classList.toggle('admin-sidebar-collapsed', isCollapsed);
@@ -75,6 +77,10 @@
75
77
  }
76
78
 
77
79
  function persistOpenSections() {
80
+ if (isFilteringNavigation) {
81
+ return;
82
+ }
83
+
78
84
  const openSections = navSections
79
85
  .filter(isSectionOpen)
80
86
  .map(getSectionKey)
@@ -83,6 +89,60 @@
83
89
  localStorage.setItem(navSectionsStateKey, JSON.stringify(openSections));
84
90
  }
85
91
 
92
+ function normalizeFilterValue(value) {
93
+ return value.trim().toLowerCase();
94
+ }
95
+
96
+ function getNavItemText(element) {
97
+ return element.textContent.trim().toLowerCase();
98
+ }
99
+
100
+ function filterNavigation() {
101
+ if (!navFilter) {
102
+ return;
103
+ }
104
+
105
+ const query = normalizeFilterValue(navFilter.value);
106
+ const hasQuery = query.length > 0;
107
+
108
+ isFilteringNavigation = true;
109
+
110
+ navSections.forEach((section) => {
111
+ const sectionTitle = section.querySelector('.admin-sidebar__group-title span');
112
+ const modelItems = Array.from(section.querySelectorAll('.admin-sidebar__model'));
113
+ const matchesSection = sectionTitle && getNavItemText(sectionTitle).includes(query);
114
+ let hasVisibleModel = false;
115
+
116
+ modelItems.forEach((item) => {
117
+ const matchesModel = getNavItemText(item).includes(query);
118
+ const isVisible = !hasQuery || matchesSection || matchesModel;
119
+
120
+ item.hidden = !isVisible;
121
+ hasVisibleModel = hasVisibleModel || isVisible;
122
+ });
123
+
124
+ section.hidden = hasQuery && !matchesSection && !hasVisibleModel;
125
+
126
+ if (hasQuery && !section.hidden) {
127
+ section.open = true;
128
+ }
129
+
130
+ syncSectionAria(section);
131
+ });
132
+
133
+ if (!hasQuery) {
134
+ applyStoredOpenSections();
135
+ navSections.forEach((section) => {
136
+ section.hidden = false;
137
+ section.querySelectorAll('.admin-sidebar__model').forEach((item) => {
138
+ item.hidden = false;
139
+ });
140
+ });
141
+ }
142
+
143
+ isFilteringNavigation = false;
144
+ }
145
+
86
146
  function applyStoredOpenSections() {
87
147
  const storedOpenSections = getStoredOpenSections();
88
148
 
@@ -134,6 +194,10 @@
134
194
  });
135
195
  });
136
196
 
197
+ if (navFilter) {
198
+ navFilter.addEventListener('input', filterNavigation);
199
+ }
200
+
137
201
  document.addEventListener('keydown', (event) => {
138
202
  if (event.key === 'Escape') {
139
203
  setMobileOpen(false);
@@ -0,0 +1,128 @@
1
+ 'use strict';
2
+
3
+ {
4
+ const validThemeValues = new Set(['light', 'dark']);
5
+ const validTimeFormatValues = new Set(['12', '24']);
6
+ const timeFormatStorageKey = 'django.admin.theme.timeFormat';
7
+ const root = document.documentElement;
8
+ const themeButtons = document.querySelectorAll('[data-admin-theme-value]');
9
+ const timeFormatButtons = document.querySelectorAll('[data-admin-time-format-value]');
10
+
11
+ function updateThemeButtons(theme) {
12
+ themeButtons.forEach((button) => {
13
+ button.setAttribute('aria-pressed', String(button.dataset.adminThemeValue === theme));
14
+ });
15
+ }
16
+
17
+ function applyTheme(theme) {
18
+ if (!validThemeValues.has(theme)) {
19
+ return;
20
+ }
21
+
22
+ root.dataset.theme = theme;
23
+ localStorage.setItem('theme', theme);
24
+ updateThemeButtons(theme);
25
+ }
26
+
27
+ function updateTimeFormatButtons(timeFormat) {
28
+ timeFormatButtons.forEach((button) => {
29
+ button.setAttribute('aria-pressed', String(button.dataset.adminTimeFormatValue === timeFormat));
30
+ });
31
+ }
32
+
33
+ function formatHour(hour, meridiem) {
34
+ let formattedHour = Number(hour);
35
+
36
+ if (meridiem === 'p.m.' && formattedHour !== 12) {
37
+ formattedHour += 12;
38
+ } else if (meridiem === 'a.m.' && formattedHour === 12) {
39
+ formattedHour = 0;
40
+ }
41
+
42
+ return String(formattedHour).padStart(2, '0');
43
+ }
44
+
45
+ function abbreviateMonth(text) {
46
+ return text.replace(
47
+ /\b(January|February|March|April|May|June|July|August|September|October|November|December)\b/g,
48
+ (month) => month.slice(0, 3),
49
+ );
50
+ }
51
+
52
+ function formatDateOrder(text) {
53
+ return text.replace(
54
+ /\b(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d{1,2}), (\d{4}), ([^,]+)/g,
55
+ (match, month, day, year, time) => `${day} ${month} ${year} ${time}`,
56
+ );
57
+ }
58
+
59
+ function formatTableDateText(text) {
60
+ return formatDateOrder(abbreviateMonth(text));
61
+ }
62
+
63
+ function formatTableTimeText(text) {
64
+ const formattedMinuteTime = text.replace(
65
+ /(\b[A-Z][a-z]+ \d{1,2}, \d{4}, )(\d{1,2}):(\d{2}) (a\.m\.|p\.m\.)(?=$|[^\w])/g,
66
+ (match, datePrefix, hour, minute, meridiem) => `${datePrefix}${formatHour(hour, meridiem)}:${minute}`,
67
+ );
68
+
69
+ const formattedTime = formattedMinuteTime.replace(
70
+ /(\b[A-Z][a-z]+ \d{1,2}, \d{4}, )(\d{1,2}) (a\.m\.|p\.m\.)(?=$|[^\w])/g,
71
+ (match, datePrefix, hour, meridiem) => `${datePrefix}${Number(formatHour(hour, meridiem))}h`,
72
+ );
73
+
74
+ return formatTableDateText(formattedTime);
75
+ }
76
+
77
+ function updateTableTimeFormat(timeFormat) {
78
+ document.querySelectorAll('#changelist table tbody th, #changelist table tbody td').forEach((cell) => {
79
+ if (cell.childElementCount !== 0) {
80
+ return;
81
+ }
82
+
83
+ if (!cell.dataset.adminOriginalTimeText) {
84
+ cell.dataset.adminOriginalTimeText = cell.textContent;
85
+ }
86
+
87
+ const originalText = cell.dataset.adminOriginalTimeText;
88
+ const formattedText = timeFormat === '24' ? formatTableTimeText(originalText) : formatTableDateText(originalText);
89
+
90
+ cell.textContent = formattedText;
91
+ });
92
+ }
93
+
94
+ function applyTimeFormat(timeFormat) {
95
+ if (!validTimeFormatValues.has(timeFormat)) {
96
+ return;
97
+ }
98
+
99
+ localStorage.setItem(timeFormatStorageKey, timeFormat);
100
+ updateTimeFormatButtons(timeFormat);
101
+ updateTableTimeFormat(timeFormat);
102
+ }
103
+
104
+ const storedTheme = localStorage.getItem('theme');
105
+ if (validThemeValues.has(storedTheme)) {
106
+ root.dataset.theme = storedTheme;
107
+ updateThemeButtons(storedTheme);
108
+ } else {
109
+ updateThemeButtons(root.dataset.theme);
110
+ }
111
+
112
+ themeButtons.forEach((button) => {
113
+ button.addEventListener('click', () => {
114
+ applyTheme(button.dataset.adminThemeValue);
115
+ });
116
+ });
117
+
118
+ const storedTimeFormat = localStorage.getItem(timeFormatStorageKey);
119
+ const initialTimeFormat = validTimeFormatValues.has(storedTimeFormat) ? storedTimeFormat : '12';
120
+ updateTimeFormatButtons(initialTimeFormat);
121
+ updateTableTimeFormat(initialTimeFormat);
122
+
123
+ timeFormatButtons.forEach((button) => {
124
+ button.addEventListener('click', () => {
125
+ applyTimeFormat(button.dataset.adminTimeFormatValue);
126
+ });
127
+ });
128
+ }
@@ -52,6 +52,19 @@
52
52
  </div>
53
53
  </div>
54
54
 
55
+ {% if available_apps %}
56
+ <div class="admin-sidebar__filter">
57
+ <label class="visually-hidden" for="admin-nav-filter">Filter navigation items</label>
58
+ <input
59
+ type="search"
60
+ id="admin-nav-filter"
61
+ autocomplete="off"
62
+ spellcheck="false"
63
+ placeholder="Start typing to filter..."
64
+ >
65
+ </div>
66
+ {% endif %}
67
+
55
68
  <div class="admin-sidebar__groups">
56
69
  {% if available_apps %}
57
70
  {% grouped_admin_nav_apps available_apps as nav_apps %}
@@ -103,18 +116,41 @@
103
116
  </summary>
104
117
 
105
118
  <div class="admin-sidebar__account-menu">
106
- <div class="admin-sidebar__theme-controls" aria-label="Theme controls">
107
- <button class="admin-sidebar__theme-button admin-sidebar__theme-button--light" type="button" data-admin-theme-value="light">
108
- <svg aria-hidden="true"><use href="#admin-icon-sun"></use></svg>
109
- <span>Light</span>
110
- </button>
111
- <button class="admin-sidebar__theme-button admin-sidebar__theme-button--dark" type="button" data-admin-theme-value="dark">
112
- <svg aria-hidden="true"><use href="#admin-icon-moon"></use></svg>
113
- <span>Dark</span>
114
- </button>
119
+ <div class="admin-sidebar__account-section admin-sidebar__display-section">
120
+ <div class="admin-sidebar__section-title">Display</div>
121
+ <div class="admin-sidebar__display-row">
122
+ <span class="admin-sidebar__display-label">
123
+ <svg aria-hidden="true"><use href="#admin-icon-sun"></use></svg>
124
+ <span>Theme</span>
125
+ </span>
126
+ <span class="admin-sidebar__theme-controls" aria-label="Theme controls">
127
+ <button class="admin-sidebar__theme-button admin-sidebar__theme-button--light" type="button" data-admin-theme-value="light">
128
+ <svg aria-hidden="true"><use href="#admin-icon-sun"></use></svg>
129
+ <span class="visually-hidden">Light</span>
130
+ </button>
131
+ <button class="admin-sidebar__theme-button admin-sidebar__theme-button--dark" type="button" data-admin-theme-value="dark">
132
+ <svg aria-hidden="true"><use href="#admin-icon-moon"></use></svg>
133
+ <span class="visually-hidden">Dark</span>
134
+ </button>
135
+ </span>
136
+ </div>
137
+ <div class="admin-sidebar__display-row">
138
+ <span class="admin-sidebar__display-label">
139
+ <svg aria-hidden="true"><use href="#admin-icon-clock"></use></svg>
140
+ <span>Time format</span>
141
+ </span>
142
+ <span class="admin-sidebar__time-controls" aria-label="Time format controls">
143
+ <button class="admin-sidebar__time-button admin-sidebar__time-button--12" type="button" data-admin-time-format-value="12">
144
+ <span>12h</span>
145
+ </button>
146
+ <button class="admin-sidebar__time-button admin-sidebar__time-button--24" type="button" data-admin-time-format-value="24">
147
+ <span>24h</span>
148
+ </button>
149
+ </span>
150
+ </div>
115
151
  </div>
116
152
 
117
- <div class="admin-sidebar__account-links">
153
+ <div class="admin-sidebar__account-section admin-sidebar__account-links">
118
154
  {% if site_url %}
119
155
  <a href="{{ site_url }}">
120
156
  <svg aria-hidden="true"><use href="#admin-icon-external"></use></svg>
@@ -134,7 +170,9 @@
134
170
  <span class="donation-button__label">Buy me a coffee</span>
135
171
  <svg class="admin-sidebar__external-icon" aria-hidden="true"><use href="#admin-icon-external"></use></svg>
136
172
  </a>
137
- <span class="admin-sidebar__account-divider" aria-hidden="true"></span>
173
+ </div>
174
+
175
+ <div class="admin-sidebar__account-section admin-sidebar__logout-section">
138
176
  <form method="post" action="{% url 'admin:logout' %}">
139
177
  {% csrf_token %}
140
178
  <button type="submit">
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vanta-admin
3
- Version: 0.3.1
3
+ Version: 0.5.0
4
4
  Summary: A sharp, dark, and portable Django admin theme forged for builders who want the admin to feel less default.
5
5
  Author: oli-dev0
6
6
  License-Expression: MIT
@@ -35,7 +35,9 @@ feels more deliberate in everyday use.
35
35
  ## What It Adds
36
36
 
37
37
  - A cleaner admin layout with a fixed sidebar that remembers how you left it.
38
+ - A sidebar navigation filter that lets users quickly narrow large admin menus by app or model name.
38
39
  - Light and dark mode, using the same calm Vanta look across the admin.
40
+ - A more settled light-mode palette with clearer search focus states and stronger changelist table headers.
39
41
  - Less visual noise on the main admin screens, login pages, cards, filters, and
40
42
  two-factor auth pages.
41
43
  - Denser dashboards, tables, filters, messages, and forms so admin work feels
@@ -47,8 +49,12 @@ feels more deliberate in everyday use.
47
49
  - Styled select controls for common admin dropdowns, while Django's native
48
50
  multi-select fields stay unchanged.
49
51
  - Built-in icons for common admin areas, models, and recent actions.
50
- - A cleaner account menu with theme controls, project links, password access,
51
- logout, and a Vanta support link.
52
+ - A cleaner account menu with display preferences, project links, password
53
+ access, logout, and a Vanta support link.
54
+ - A 12-hour and 24-hour time-format preference for changelist date/time values,
55
+ stored in the browser for each admin user.
56
+ - Shorter changelist date formatting that makes date-heavy tables easier to
57
+ scan.
52
58
  - A packaged favicon for the admin.
53
59
  - A mobile sidebar overlay that makes the admin easier to use on smaller
54
60
  screens.
@@ -1,37 +0,0 @@
1
- 'use strict';
2
-
3
- {
4
- const validThemeValues = new Set(['light', 'dark']);
5
- const root = document.documentElement;
6
- const themeButtons = document.querySelectorAll('[data-admin-theme-value]');
7
-
8
- function updateThemeButtons(theme) {
9
- themeButtons.forEach((button) => {
10
- button.setAttribute('aria-pressed', String(button.dataset.adminThemeValue === theme));
11
- });
12
- }
13
-
14
- function applyTheme(theme) {
15
- if (!validThemeValues.has(theme)) {
16
- return;
17
- }
18
-
19
- root.dataset.theme = theme;
20
- localStorage.setItem('theme', theme);
21
- updateThemeButtons(theme);
22
- }
23
-
24
- const storedTheme = localStorage.getItem('theme');
25
- if (validThemeValues.has(storedTheme)) {
26
- root.dataset.theme = storedTheme;
27
- updateThemeButtons(storedTheme);
28
- } else {
29
- updateThemeButtons(root.dataset.theme);
30
- }
31
-
32
- themeButtons.forEach((button) => {
33
- button.addEventListener('click', () => {
34
- applyTheme(button.dataset.adminThemeValue);
35
- });
36
- });
37
- }
File without changes
File without changes
File without changes