Skip to content

Commit df88c28

Browse files
committed
feat: Add a dark theme with a toggle
1 parent 0c203f9 commit df88c28

6 files changed

Lines changed: 187 additions & 38 deletions

File tree

dome/static/css/base.css

Lines changed: 118 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,12 @@
66
--font-sans: 'Sora', sans-serif;
77
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
88
"Courier New", monospace;
9+
--color-red-100: oklch(93.6% 0.032 17.717);
910
--color-red-600: oklch(57.7% 0.245 27.325);
11+
--color-red-700: oklch(50.5% 0.213 27.518);
1012
--color-teal-100: oklch(95.3% 0.051 180.801);
1113
--color-teal-300: oklch(85.5% 0.138 181.071);
1214
--color-teal-700: oklch(51.1% 0.096 186.391);
13-
--color-blue-400: oklch(70.7% 0.165 254.624);
14-
--color-blue-500: oklch(62.3% 0.214 259.815);
15-
--color-blue-600: oklch(54.6% 0.245 262.881);
16-
--color-blue-800: oklch(42.4% 0.199 265.638);
17-
--color-pink-600: oklch(59.2% 0.249 0.584);
18-
--color-pink-800: oklch(45.9% 0.187 3.815);
1915
--color-gray-50: oklch(98.5% 0.002 247.839);
2016
--color-gray-100: oklch(96.7% 0.003 264.542);
2117
--color-gray-200: oklch(92.8% 0.006 264.531);
@@ -25,12 +21,16 @@
2521
--color-gray-600: oklch(44.6% 0.03 256.802);
2622
--color-gray-700: oklch(37.3% 0.034 259.733);
2723
--color-neutral-200: oklch(92.2% 0 0);
24+
--color-neutral-700: oklch(37.1% 0 0);
25+
--color-neutral-800: oklch(26.9% 0 0);
26+
--color-neutral-900: oklch(20.5% 0 0);
27+
--color-stone-50: oklch(98.5% 0.001 106.423);
28+
--color-stone-100: oklch(97% 0.001 106.424);
2829
--color-black: #000;
2930
--color-white: #fff;
3031
--spacing: 0.25rem;
3132
--container-md: 28rem;
3233
--container-lg: 32rem;
33-
--container-4xl: 56rem;
3434
--container-5xl: 64rem;
3535
--text-xs: 0.75rem;
3636
--text-xs--line-height: calc(1 / 0.75);
@@ -49,13 +49,12 @@
4949
--font-weight-normal: 400;
5050
--font-weight-medium: 500;
5151
--font-weight-bold: 700;
52-
--tracking-widest: 0.1em;
5352
--ease-out: cubic-bezier(0, 0, 0.2, 1);
5453
--default-transition-duration: 150ms;
5554
--default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
5655
--default-font-family: var(--font-sans);
5756
--default-mono-font-family: var(--font-mono);
58-
--color-neo-yellow: #FFE500;
57+
--color-neo-yellow: #EFD400;
5958
--color-neo-pink: #FF006E;
6059
--color-neo-green: #06FF00;
6160
--color-neo-purple: #8338EC;
@@ -450,9 +449,6 @@
450449
.w-full {
451450
width: 100%;
452451
}
453-
.max-w-4xl {
454-
max-width: var(--container-4xl);
455-
}
456452
.max-w-5xl {
457453
max-width: var(--container-5xl);
458454
}
@@ -517,6 +513,12 @@
517513
.resize {
518514
resize: both;
519515
}
516+
.list-inside {
517+
list-style-position: inside;
518+
}
519+
.list-disc {
520+
list-style-type: disc;
521+
}
520522
.grid-cols-1 {
521523
grid-template-columns: repeat(1, minmax(0, 1fr));
522524
}
@@ -692,9 +694,18 @@
692694
.bg-neo-yellow {
693695
background-color: var(--color-neo-yellow);
694696
}
697+
.bg-neo-yellow\! {
698+
background-color: var(--color-neo-yellow) !important;
699+
}
695700
.bg-neutral-200 {
696701
background-color: var(--color-neutral-200);
697702
}
703+
.bg-red-100 {
704+
background-color: var(--color-red-100);
705+
}
706+
.bg-stone-100 {
707+
background-color: var(--color-stone-100);
708+
}
698709
.bg-teal-100 {
699710
background-color: var(--color-teal-100);
700711
}
@@ -719,9 +730,15 @@
719730
.p-1 {
720731
padding: calc(var(--spacing) * 1);
721732
}
733+
.p-1\! {
734+
padding: calc(var(--spacing) * 1) !important;
735+
}
722736
.p-2 {
723737
padding: calc(var(--spacing) * 2);
724738
}
739+
.p-2\! {
740+
padding: calc(var(--spacing) * 2) !important;
741+
}
725742
.p-3 {
726743
padding: calc(var(--spacing) * 3);
727744
}
@@ -832,12 +849,12 @@
832849
.text-gray-700 {
833850
color: var(--color-gray-700);
834851
}
835-
.text-neo-pink {
836-
color: var(--color-neo-pink);
837-
}
838852
.text-red-600 {
839853
color: var(--color-red-600);
840854
}
855+
.text-red-700 {
856+
color: var(--color-red-700);
857+
}
841858
.text-teal-700 {
842859
color: var(--color-teal-700);
843860
}
@@ -876,6 +893,15 @@
876893
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
877894
transition-duration: var(--tw-duration, var(--default-transition-duration));
878895
}
896+
.transition-colors {
897+
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to;
898+
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
899+
transition-duration: var(--tw-duration, var(--default-transition-duration));
900+
}
901+
.duration-150 {
902+
--tw-duration: 150ms;
903+
transition-duration: 150ms;
904+
}
879905
.duration-300 {
880906
--tw-duration: 300ms;
881907
transition-duration: 300ms;
@@ -941,10 +967,10 @@
941967
}
942968
}
943969
}
944-
.hover\:text-pink-800 {
970+
.hover\:bg-stone-50 {
945971
&:hover {
946972
@media (hover: hover) {
947-
color: var(--color-pink-800);
973+
background-color: var(--color-stone-50);
948974
}
949975
}
950976
}
@@ -997,11 +1023,6 @@
9971023
width: calc(var(--spacing) * 64);
9981024
}
9991025
}
1000-
.md\:flex-1 {
1001-
@media (width >= 48rem) {
1002-
flex: 1;
1003-
}
1004-
}
10051026
.md\:grid-cols-2 {
10061027
@media (width >= 48rem) {
10071028
grid-template-columns: repeat(2, minmax(0, 1fr));
@@ -1022,11 +1043,6 @@
10221043
flex-direction: row;
10231044
}
10241045
}
1025-
.md\:items-start {
1026-
@media (width >= 48rem) {
1027-
align-items: flex-start;
1028-
}
1029-
}
10301046
.md\:gap-4 {
10311047
@media (width >= 48rem) {
10321048
gap: calc(var(--spacing) * 4);
@@ -1052,6 +1068,81 @@
10521068
grid-template-columns: repeat(6, minmax(0, 1fr));
10531069
}
10541070
}
1071+
.dark\:border-neutral-700 {
1072+
&:where(.dark, .dark *) {
1073+
border-color: var(--color-neutral-700);
1074+
}
1075+
}
1076+
.dark\:bg-neutral-800 {
1077+
&:where(.dark, .dark *) {
1078+
background-color: var(--color-neutral-800);
1079+
}
1080+
}
1081+
.dark\:bg-neutral-800\! {
1082+
&:where(.dark, .dark *) {
1083+
background-color: var(--color-neutral-800) !important;
1084+
}
1085+
}
1086+
.dark\:bg-neutral-900 {
1087+
&:where(.dark, .dark *) {
1088+
background-color: var(--color-neutral-900);
1089+
}
1090+
}
1091+
.dark\:text-gray-300 {
1092+
&:where(.dark, .dark *) {
1093+
color: var(--color-gray-300);
1094+
}
1095+
}
1096+
.dark\:text-white {
1097+
&:where(.dark, .dark *) {
1098+
color: var(--color-white);
1099+
}
1100+
}
1101+
.dark\:text-white\! {
1102+
&:where(.dark, .dark *) {
1103+
color: var(--color-white) !important;
1104+
}
1105+
}
1106+
.dark\:hover\:bg-neutral-800\/70 {
1107+
&:where(.dark, .dark *) {
1108+
&:hover {
1109+
@media (hover: hover) {
1110+
background-color: color-mix(in srgb, oklch(26.9% 0 0) 70%, transparent);
1111+
@supports (color: color-mix(in lab, red, red)) {
1112+
background-color: color-mix(in oklab, var(--color-neutral-800) 70%, transparent);
1113+
}
1114+
}
1115+
}
1116+
}
1117+
}
1118+
.dark\:\[\&_\.bg-neo-yellow\]\:bg-\[\#c9b200\] {
1119+
&:where(.dark, .dark *) {
1120+
& .bg-neo-yellow {
1121+
background-color: #c9b200;
1122+
}
1123+
}
1124+
}
1125+
.dark\:\[\&_\.bg-white\]\:bg-neutral-900 {
1126+
&:where(.dark, .dark *) {
1127+
& .bg-white {
1128+
background-color: var(--color-neutral-900);
1129+
}
1130+
}
1131+
}
1132+
.dark\:\[\&_\.border-black\]\:border-white {
1133+
&:where(.dark, .dark *) {
1134+
& .border-black {
1135+
border-color: var(--color-white);
1136+
}
1137+
}
1138+
}
1139+
.dark\:\[\&_\.text-black\]\:text-white {
1140+
&:where(.dark, .dark *) {
1141+
& .text-black {
1142+
color: var(--color-white);
1143+
}
1144+
}
1145+
}
10551146
}
10561147
.btn {
10571148
display: inline-flex;

dome/static/css/base.tailwind

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
@import "tailwindcss";
22

3+
/* AIDEV-NOTE: Use class-driven dark mode so footer toggle controls theme instead of system preference. */
4+
@custom-variant dark (&:where(.dark, .dark *));
5+
36
@theme {
47
--font-sans: 'Sora', sans-serif;
5-
--color-neo-yellow: #FFE500;
8+
--color-neo-yellow: #EFD400;
69
--color-neo-pink: #FF006E;
710
--color-neo-green: #06FF00;
811
--color-neo-purple: #8338EC;

dome/templates/base.html

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22
<!DOCTYPE html>
33
<html lang="en">
44
<head>
5+
<script>
6+
// AIDEV-NOTE: Apply persisted theme before paint to avoid flash on reload.
7+
(() => {
8+
const theme = localStorage.getItem("theme") === "dark" ? "dark" : "light";
9+
document.documentElement.classList.toggle("dark", theme === "dark");
10+
})();
11+
</script>
512
<meta charset="UTF-8">
613
<meta name="viewport" content="width=device-width, initial-scale=1.0">
714
<title>
@@ -43,7 +50,7 @@
4350
<!-- https://htmx.org/quirks/#by-default-4xx-5xx-responses-do-not-swap -->
4451
<meta name="htmx-config" content='{"responseHandling": [{"code":".*", "swap": true}]}' /> <!-- All responses are swapped -->
4552
</head>
46-
<body class="bg-gray-50">
53+
<body class="bg-gray-50 text-black transition-colors duration-150 dark:bg-neutral-900 dark:text-white dark:[&_.border-black]:border-white dark:[&_.bg-white]:bg-neutral-900 dark:[&_.text-black]:text-white dark:[&_.bg-neo-yellow]:bg-[#c9b200]">
4754
<div class="max-w-5xl mx-auto">
4855
{% include "partials/nav.html" %}
4956
<main class="container my-4 mx-auto px-2 md:px-0">
Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,62 @@
11
{% load static %}
2+
23
<footer class="border-t-3 border-black py-4 mt-8 mb-2">
3-
<div class="flex items-center justify-between px-4">
4-
<p class="text-sm font-bold">
5-
Copyright © Kamil Marut {% now "Y" %} |
6-
<a class="text-neo-pink hover:text-pink-800" href="https://github.com/exler/dome" rel="noreferrer">GitHub</a>
7-
</p>
8-
<div class="flex gap-2">
4+
<div class="flex items-center justify-between px-4 gap-3">
5+
<p class="text-sm font-bold">Copyright © Digital Dome {% now "Y" %}</p>
6+
<div class="flex items-center gap-2">
7+
<button id="theme-toggle"
8+
type="button"
9+
class="btn gap-2 bg-neo-yellow! dark:bg-neutral-800! dark:text-white!"
10+
aria-label="Toggle color theme"
11+
aria-pressed="false">
12+
<span id="theme-toggle-icon" aria-hidden="true">
13+
<svg id="theme-icon-sun"
14+
xmlns="http://www.w3.org/2000/svg"
15+
viewBox="0 0 24 24"
16+
class="w-4 h-4">
17+
<path fill="currentColor" d="M12 18a6 6 0 1 1 0-12a6 6 0 0 1 0 12m0-16a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0V3a1 1 0 0 1 1-1m0 17a1 1 0 0 1 1 1v2a1 1 0 1 1-2 0v-2a1 1 0 0 1 1-1M4.22 5.64a1 1 0 0 1 1.42 0l1.41 1.41A1 1 0 1 1 5.64 8.47L4.22 7.05a1 1 0 0 1 0-1.41m12.73 12.73a1 1 0 0 1 1.41 0l1.42 1.41a1 1 0 1 1-1.42 1.42l-1.41-1.42a1 1 0 0 1 0-1.41M2 13a1 1 0 1 1 0-2h2a1 1 0 1 1 0 2zm18 0a1 1 0 1 1 0-2h2a1 1 0 1 1 0 2zM5.64 15.53a1 1 0 0 1 1.41 1.41l-1.41 1.42a1 1 0 1 1-1.42-1.42zM18.36 4.22a1 1 0 0 1 1.42 1.42l-1.42 1.41a1 1 0 1 1-1.41-1.41z" />
18+
</svg>
19+
<svg id="theme-icon-moon"
20+
xmlns="http://www.w3.org/2000/svg"
21+
viewBox="0 0 24 24"
22+
class="w-4 h-4 hidden">
23+
<path fill="currentColor" d="M21.64 13a1 1 0 0 0-1.05-.14a8.05 8.05 0 0 1-3.37.74a8.15 8.15 0 0 1-8.14-8.14a8.6 8.6 0 0 1 .25-2A1 1 0 0 0 8 2.36A10.14 10.14 0 1 0 22 16a1 1 0 0 0-.36-3" />
24+
</svg>
25+
</span>
26+
<span id="theme-toggle-label">Light</span>
27+
</button>
928
<div class="w-6 h-6 bg-neo-yellow border-2 border-black"></div>
1029
<div class="w-6 h-6 bg-neo-pink border-2 border-black"></div>
1130
<div class="w-6 h-6 bg-black border-2 border-black"></div>
1231
</div>
1332
</div>
1433
</footer>
34+
<script>
35+
// AIDEV-NOTE: Footer owns the theme toggle UI; global theme state is persisted in localStorage.
36+
(() => {
37+
const themeToggleButton = document.getElementById("theme-toggle");
38+
const themeToggleLabel = document.getElementById("theme-toggle-label");
39+
const themeIconSun = document.getElementById("theme-icon-sun");
40+
const themeIconMoon = document.getElementById("theme-icon-moon");
41+
42+
if (!themeToggleButton || !themeToggleLabel || !themeIconSun || !themeIconMoon) return;
43+
44+
const getTheme = () => (localStorage.getItem("theme") === "dark" ? "dark" : "light");
45+
const applyTheme = (theme) => {
46+
const isDark = theme === "dark";
47+
document.documentElement.classList.toggle("dark", isDark);
48+
themeToggleButton.setAttribute("aria-pressed", String(isDark));
49+
themeToggleLabel.textContent = isDark ? "Dark" : "Light";
50+
themeIconSun.classList.toggle("hidden", isDark);
51+
themeIconMoon.classList.toggle("hidden", !isDark);
52+
};
53+
54+
applyTheme(getTheme());
55+
56+
themeToggleButton.addEventListener("click", () => {
57+
const nextTheme = getTheme() === "dark" ? "light" : "dark";
58+
localStorage.setItem("theme", nextTheme);
59+
applyTheme(nextTheme);
60+
});
61+
})();
62+
</script>

entities/templates/entities/entities_detail.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ <h3 class="font-bold text-xs uppercase mb-2">Could not save changes</h3>
262262
<!-- Description -->
263263
<div class="mb-6">
264264
<h2 class="text-base font-bold uppercase mb-3">Description</h2>
265-
<p class="text-sm text-gray-700">{{ object.description|default:"No description available." }}</p>
265+
<p class="text-sm text-gray-700 dark:text-gray-300">{{ object.description|default:"No description available." }}</p>
266266
</div>
267267

268268
<!-- Metadata Fields Grid -->

tracking/templates/tracking/tracking_list.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ <h2 class="text-2xl font-bold uppercase">{{ entity_type|title }}</h2>
9494

9595
{# List view #}
9696
<div class="border-3 border-black bg-white" style="box-shadow: 4px 4px 0px 0px #000;">
97-
<div class="grid grid-cols-[3rem_4rem_1fr_8rem_5rem] p-2 bg-gray-100 border-b-2 border-black font-bold text-xs uppercase">
97+
<div class="grid grid-cols-[3rem_4rem_1fr_8rem_5rem] p-2 bg-stone-100 dark:bg-neutral-800 border-b-2 border-black font-bold text-xs uppercase">
9898
<div class="text-center">#</div>
9999
<div></div>
100100
<div class="text-left">Title</div>
@@ -104,7 +104,7 @@ <h2 class="text-2xl font-bold uppercase">{{ entity_type|title }}</h2>
104104

105105
{% for object in page_obj %}
106106
<a href="{% url 'entities:entities-detail' entity_type object.object_id %}"
107-
class="grid grid-cols-[3rem_4rem_1fr_8rem_5rem] p-2 hover:bg-gray-50 border-b border-gray-200 items-center">
107+
class="grid grid-cols-[3rem_4rem_1fr_8rem_5rem] p-2 hover:bg-stone-50 dark:hover:bg-neutral-800/70 border-b border-gray-200 dark:border-neutral-700 items-center">
108108
<div class="text-center text-sm font-bold">{{ forloop.counter0|add:page_obj.start_index }}</div>
109109
<div>
110110
<img src="{{ object.content_object.image|imageurl }}"

0 commit comments

Comments
 (0)