diff --git a/index.html b/index.html
index a38d208..f71584d 100644
--- a/index.html
+++ b/index.html
@@ -8,10 +8,10 @@
-
-
+
+
@@ -55,6 +63,7 @@
+
diff --git a/public/images/immich.png b/public/images/immich-dark.png
similarity index 100%
rename from public/images/immich.png
rename to public/images/immich-dark.png
diff --git a/public/images/immich-light.png b/public/images/immich-light.png
new file mode 100644
index 0000000..35a6152
Binary files /dev/null and b/public/images/immich-light.png differ
diff --git a/public/images/logo-dark.svg b/public/images/logo-dark.svg
new file mode 100644
index 0000000..a70a58d
--- /dev/null
+++ b/public/images/logo-dark.svg
@@ -0,0 +1,27 @@
+
diff --git a/public/images/nextcloud.png b/public/images/nextcloud-dark.png
similarity index 100%
rename from public/images/nextcloud.png
rename to public/images/nextcloud-dark.png
diff --git a/public/images/nextcloud-light.png b/public/images/nextcloud-light.png
new file mode 100644
index 0000000..5b06c90
Binary files /dev/null and b/public/images/nextcloud-light.png differ
diff --git a/public/images/vaultwarden-dark.png b/public/images/vaultwarden-dark.png
new file mode 100644
index 0000000..19164ae
Binary files /dev/null and b/public/images/vaultwarden-dark.png differ
diff --git a/public/images/vaultwarden.png b/public/images/vaultwarden-light.png
similarity index 100%
rename from public/images/vaultwarden.png
rename to public/images/vaultwarden-light.png
diff --git a/public/images/zipline.png b/public/images/zipline-dark.png
similarity index 100%
rename from public/images/zipline.png
rename to public/images/zipline-dark.png
diff --git a/public/images/zipline-light.png b/public/images/zipline-light.png
new file mode 100644
index 0000000..c52ca4f
Binary files /dev/null and b/public/images/zipline-light.png differ
diff --git a/public/js/cards.js b/public/js/cards.js
index bec49d3..00a6776 100644
--- a/public/js/cards.js
+++ b/public/js/cards.js
@@ -31,10 +31,14 @@ const cards = [
];
const services = document.getElementById("services");
-window.onload = function () {
+function renderCards() {
+ const theme = document.getElementById("body").classList[0];
+ services.innerHTML = "";
for (let index = 0; index < cards.length; index++) {
+ const image_name = cards[index].image.split(".")[0];
+ const image_ext = cards[index].image.split(".")[1];
const template = `
-
+
${cards[index].name}
@@ -65,4 +69,5 @@ window.onload = function () {
services.appendChild(element);
}
-};
+}
+window.onload = renderCards();
diff --git a/public/js/theme.js b/public/js/theme.js
new file mode 100644
index 0000000..d5d705f
--- /dev/null
+++ b/public/js/theme.js
@@ -0,0 +1,36 @@
+const bodyElement = document.getElementById("body");
+const logo = document.getElementById("logo");
+const themeToggle = document.getElementById("theme-toggle");
+
+const setTheme = (theme) => {
+ bodyElement.classList.remove(theme == "light" ? "dark" : "light");
+ bodyElement.classList.add(theme == "light" ? "light" : "dark");
+ theme == "light"
+ ? (themeToggle.checked = false)
+ : (themeToggle.checked = true);
+ logo.src = `/public/images/logo-${theme}.svg`;
+ localStorage.setItem("theme", theme);
+};
+
+function updateTheme() {
+ let theme = localStorage.getItem("theme");
+ console.log("updatingTheme", theme);
+ if (theme) {
+ setTheme(theme);
+ } else {
+ setTheme("light");
+ }
+}
+
+themeToggle.addEventListener("click", () => {
+ let theme = localStorage.getItem("theme");
+ if (theme == "light") {
+ setTheme("dark");
+ renderCards();
+ } else {
+ setTheme("light");
+ renderCards();
+ }
+});
+
+updateTheme();
diff --git a/public/output.css b/public/output.css
index 4b1a3c7..30d9a4d 100644
--- a/public/output.css
+++ b/public/output.css
@@ -1053,6 +1053,18 @@ input:checked + .toggle-bg {
}
}
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border-width: 0;
+}
+
.visible {
visibility: visible;
}
@@ -1170,6 +1182,18 @@ input:checked + .toggle-bg {
margin-top: 1.5rem;
}
+.ms-3 {
+ margin-inline-start: 0.75rem;
+}
+
+.mr-2 {
+ margin-right: 0.5rem;
+}
+
+.mr-4 {
+ margin-right: 1rem;
+}
+
.block {
display: block;
}
@@ -1250,20 +1274,8 @@ input:checked + .toggle-bg {
width: 100%;
}
-.w-auto {
- width: auto;
-}
-
-.w-\[25\%\] {
- width: 25%;
-}
-
-.w-\[50\%\] {
- width: 50%;
-}
-
-.w-\[100\%\] {
- width: 100%;
+.w-11 {
+ width: 2.75rem;
}
.max-w-\[1440px\] {
@@ -1392,6 +1404,14 @@ input:checked + .toggle-bg {
gap: 0.5rem;
}
+.gap-4 {
+ gap: 1rem;
+}
+
+.gap-6 {
+ gap: 1.5rem;
+}
+
.space-x-2 > :not([hidden]) ~ :not([hidden]) {
--tw-space-x-reverse: 0;
margin-right: calc(0.5rem * var(--tw-space-x-reverse));
@@ -1406,14 +1426,14 @@ input:checked + .toggle-bg {
white-space: pre;
}
-.rounded-lg {
- border-radius: 0.5rem;
-}
-
.rounded-full {
border-radius: 9999px;
}
+.rounded-lg {
+ border-radius: 0.5rem;
+}
+
.rounded-e-lg {
border-start-end-radius: 0.5rem;
border-end-end-radius: 0.5rem;
@@ -1496,15 +1516,6 @@ input:checked + .toggle-bg {
background-color: rgb(17 24 39 / 0.5);
}
-.bg-white {
- --tw-bg-opacity: 1;
- background-color: rgb(255 255 255 / var(--tw-bg-opacity));
-}
-
-.bg-white\/50 {
- background-color: rgb(255 255 255 / 0.5);
-}
-
.bg-green-500 {
--tw-bg-opacity: 1;
background-color: rgb(14 159 110 / var(--tw-bg-opacity));
@@ -1515,21 +1526,25 @@ input:checked + .toggle-bg {
background-color: rgb(224 36 36 / var(--tw-bg-opacity));
}
-.bg-yellow-400 {
- --tw-bg-opacity: 1;
- background-color: rgb(227 160 8 / var(--tw-bg-opacity));
-}
-
-.bg-slate-200 {
- --tw-bg-opacity: 1;
- background-color: rgb(226 232 240 / var(--tw-bg-opacity));
-}
-
.bg-slate-50 {
--tw-bg-opacity: 1;
background-color: rgb(248 250 252 / var(--tw-bg-opacity));
}
+.bg-white {
+ --tw-bg-opacity: 1;
+ background-color: rgb(255 255 255 / var(--tw-bg-opacity));
+}
+
+.bg-white\/50 {
+ background-color: rgb(255 255 255 / 0.5);
+}
+
+.bg-yellow-400 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(227 160 8 / var(--tw-bg-opacity));
+}
+
.object-cover {
-o-object-fit: cover;
object-fit: cover;
@@ -1560,6 +1575,11 @@ input:checked + .toggle-bg {
padding-right: 0.5rem;
}
+.px-4 {
+ padding-left: 1rem;
+ padding-right: 1rem;
+}
+
.px-5 {
padding-left: 1.25rem;
padding-right: 1.25rem;
@@ -1580,11 +1600,6 @@ input:checked + .toggle-bg {
padding-bottom: 0.75rem;
}
-.px-4 {
- padding-left: 1rem;
- padding-right: 1rem;
-}
-
.py-4 {
padding-top: 1rem;
padding-bottom: 1rem;
@@ -1693,11 +1708,6 @@ input:checked + .toggle-bg {
color: rgb(255 255 255 / var(--tw-text-opacity));
}
-.text-gray-600 {
- --tw-text-opacity: 1;
- color: rgb(75 85 99 / var(--tw-text-opacity));
-}
-
.opacity-0 {
opacity: 0;
}
@@ -1755,6 +1765,65 @@ input:checked + .toggle-bg {
transition-timing-function: cubic-bezier(0, 0, 0.2, 1);
}
+.after\:absolute::after {
+ content: var(--tw-content);
+ position: absolute;
+}
+
+.after\:start-\[2px\]::after {
+ content: var(--tw-content);
+ inset-inline-start: 2px;
+}
+
+.after\:top-\[2px\]::after {
+ content: var(--tw-content);
+ top: 2px;
+}
+
+.after\:h-5::after {
+ content: var(--tw-content);
+ height: 1.25rem;
+}
+
+.after\:w-5::after {
+ content: var(--tw-content);
+ width: 1.25rem;
+}
+
+.after\:rounded-full::after {
+ content: var(--tw-content);
+ border-radius: 9999px;
+}
+
+.after\:border::after {
+ content: var(--tw-content);
+ border-width: 1px;
+}
+
+.after\:border-gray-300::after {
+ content: var(--tw-content);
+ --tw-border-opacity: 1;
+ border-color: rgb(209 213 219 / var(--tw-border-opacity));
+}
+
+.after\:bg-white::after {
+ content: var(--tw-content);
+ --tw-bg-opacity: 1;
+ background-color: rgb(255 255 255 / var(--tw-bg-opacity));
+}
+
+.after\:transition-all::after {
+ content: var(--tw-content);
+ transition-property: all;
+ transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
+ transition-duration: 150ms;
+}
+
+.after\:content-\[\'\'\]::after {
+ --tw-content: '';
+ content: var(--tw-content);
+}
+
.hover\:border-gray-300:hover {
--tw-border-opacity: 1;
border-color: rgb(209 213 219 / var(--tw-border-opacity));
@@ -1835,132 +1904,40 @@ input:checked + .toggle-bg {
--tw-ring-color: rgb(229 231 235 / var(--tw-ring-opacity));
}
-.dark\:border-blue-500:is(.dark *) {
- --tw-border-opacity: 1;
- border-color: rgb(63 131 248 / var(--tw-border-opacity));
-}
-
-.dark\:border-gray-600:is(.dark *) {
- --tw-border-opacity: 1;
- border-color: rgb(75 85 99 / var(--tw-border-opacity));
-}
-
-.dark\:border-gray-700:is(.dark *) {
- --tw-border-opacity: 1;
- border-color: rgb(55 65 81 / var(--tw-border-opacity));
-}
-
-.dark\:border-transparent:is(.dark *) {
- border-color: transparent;
-}
-
-.dark\:bg-blue-600:is(.dark *) {
+.peer:checked ~ .peer-checked\:bg-blue-600 {
--tw-bg-opacity: 1;
background-color: rgb(28 100 242 / var(--tw-bg-opacity));
}
-.dark\:bg-gray-600:is(.dark *) {
- --tw-bg-opacity: 1;
- background-color: rgb(75 85 99 / var(--tw-bg-opacity));
+.peer:checked ~ .peer-checked\:after\:translate-x-full::after {
+ content: var(--tw-content);
+ --tw-translate-x: 100%;
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
-.dark\:bg-gray-700:is(.dark *) {
- --tw-bg-opacity: 1;
- background-color: rgb(55 65 81 / var(--tw-bg-opacity));
+.peer:checked ~ .peer-checked\:after\:border-white::after {
+ content: var(--tw-content);
+ --tw-border-opacity: 1;
+ border-color: rgb(255 255 255 / var(--tw-border-opacity));
}
-.dark\:bg-gray-800:is(.dark *) {
- --tw-bg-opacity: 1;
- background-color: rgb(31 41 55 / var(--tw-bg-opacity));
+.peer:focus ~ .peer-focus\:outline-none {
+ outline: 2px solid transparent;
+ outline-offset: 2px;
}
-.dark\:bg-gray-800\/50:is(.dark *) {
- background-color: rgb(31 41 55 / 0.5);
+.peer:focus ~ .peer-focus\:ring-4 {
+ --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);
+ box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
}
-.dark\:bg-gray-900\/80:is(.dark *) {
- background-color: rgb(17 24 39 / 0.8);
-}
-
-.dark\:text-blue-500:is(.dark *) {
- --tw-text-opacity: 1;
- color: rgb(63 131 248 / var(--tw-text-opacity));
-}
-
-.dark\:text-gray-400:is(.dark *) {
- --tw-text-opacity: 1;
- color: rgb(156 163 175 / var(--tw-text-opacity));
-}
-
-.dark\:text-white:is(.dark *) {
- --tw-text-opacity: 1;
- color: rgb(255 255 255 / var(--tw-text-opacity));
-}
-
-.dark\:hover\:bg-blue-700:hover:is(.dark *) {
- --tw-bg-opacity: 1;
- background-color: rgb(26 86 219 / var(--tw-bg-opacity));
-}
-
-.dark\:hover\:bg-gray-600:hover:is(.dark *) {
- --tw-bg-opacity: 1;
- background-color: rgb(75 85 99 / var(--tw-bg-opacity));
-}
-
-.dark\:hover\:bg-gray-700:hover:is(.dark *) {
- --tw-bg-opacity: 1;
- background-color: rgb(55 65 81 / var(--tw-bg-opacity));
-}
-
-.dark\:hover\:bg-gray-800:hover:is(.dark *) {
- --tw-bg-opacity: 1;
- background-color: rgb(31 41 55 / var(--tw-bg-opacity));
-}
-
-.dark\:hover\:text-blue-500:hover:is(.dark *) {
- --tw-text-opacity: 1;
- color: rgb(63 131 248 / var(--tw-text-opacity));
-}
-
-.dark\:hover\:text-gray-300:hover:is(.dark *) {
- --tw-text-opacity: 1;
- color: rgb(209 213 219 / var(--tw-text-opacity));
-}
-
-.dark\:hover\:text-white:hover:is(.dark *) {
- --tw-text-opacity: 1;
- color: rgb(255 255 255 / var(--tw-text-opacity));
-}
-
-.dark\:focus\:ring-blue-800:focus:is(.dark *) {
+.peer:focus ~ .peer-focus\:ring-blue-300 {
--tw-ring-opacity: 1;
- --tw-ring-color: rgb(30 66 159 / var(--tw-ring-opacity));
-}
-
-.dark\:focus\:ring-gray-700:focus:is(.dark *) {
- --tw-ring-opacity: 1;
- --tw-ring-color: rgb(55 65 81 / var(--tw-ring-opacity));
-}
-
-@media (min-width: 768px) {
- .md\:w-auto {
- width: auto;
- }
-
- .md\:flex-nowrap {
- flex-wrap: nowrap;
- }
-
- .md\:justify-start {
- justify-content: flex-start;
- }
+ --tw-ring-color: rgb(164 202 254 / var(--tw-ring-opacity));
}
@media (min-width: 1024px) {
- .lg\:w-auto {
- width: auto;
- }
-
.lg\:flex-nowrap {
flex-wrap: nowrap;
}
@@ -1970,16 +1947,6 @@ input:checked + .toggle-bg {
}
}
-@media (min-width: 1280px) {
- .xl\:w-auto {
- width: auto;
- }
-
- .xl\:flex-nowrap {
- flex-wrap: nowrap;
- }
-}
-
.rtl\:rotate-180:where([dir="rtl"], [dir="rtl"] *) {
--tw-rotate: 180deg;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
@@ -1988,3 +1955,131 @@ input:checked + .toggle-bg {
.rtl\:space-x-reverse:where([dir="rtl"], [dir="rtl"] *) > :not([hidden]) ~ :not([hidden]) {
--tw-space-x-reverse: 1;
}
+
+.peer:checked ~ .rtl\:peer-checked\:after\:-translate-x-full:where([dir="rtl"], [dir="rtl"] *)::after {
+ content: var(--tw-content);
+ --tw-translate-x: -100%;
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
+}
+
+.dark\:border-blue-500:where(.dark, .dark *) {
+ --tw-border-opacity: 1;
+ border-color: rgb(63 131 248 / var(--tw-border-opacity));
+}
+
+.dark\:border-gray-600:where(.dark, .dark *) {
+ --tw-border-opacity: 1;
+ border-color: rgb(75 85 99 / var(--tw-border-opacity));
+}
+
+.dark\:border-gray-700:where(.dark, .dark *) {
+ --tw-border-opacity: 1;
+ border-color: rgb(55 65 81 / var(--tw-border-opacity));
+}
+
+.dark\:border-transparent:where(.dark, .dark *) {
+ border-color: transparent;
+}
+
+.dark\:bg-blue-600:where(.dark, .dark *) {
+ --tw-bg-opacity: 1;
+ background-color: rgb(28 100 242 / var(--tw-bg-opacity));
+}
+
+.dark\:bg-gray-600:where(.dark, .dark *) {
+ --tw-bg-opacity: 1;
+ background-color: rgb(75 85 99 / var(--tw-bg-opacity));
+}
+
+.dark\:bg-gray-700:where(.dark, .dark *) {
+ --tw-bg-opacity: 1;
+ background-color: rgb(55 65 81 / var(--tw-bg-opacity));
+}
+
+.dark\:bg-gray-800:where(.dark, .dark *) {
+ --tw-bg-opacity: 1;
+ background-color: rgb(31 41 55 / var(--tw-bg-opacity));
+}
+
+.dark\:bg-gray-800\/50:where(.dark, .dark *) {
+ background-color: rgb(31 41 55 / 0.5);
+}
+
+.dark\:bg-gray-900\/80:where(.dark, .dark *) {
+ background-color: rgb(17 24 39 / 0.8);
+}
+
+.dark\:bg-slate-800:where(.dark, .dark *) {
+ --tw-bg-opacity: 1;
+ background-color: rgb(30 41 59 / var(--tw-bg-opacity));
+}
+
+.dark\:text-blue-500:where(.dark, .dark *) {
+ --tw-text-opacity: 1;
+ color: rgb(63 131 248 / var(--tw-text-opacity));
+}
+
+.dark\:text-gray-400:where(.dark, .dark *) {
+ --tw-text-opacity: 1;
+ color: rgb(156 163 175 / var(--tw-text-opacity));
+}
+
+.dark\:text-white:where(.dark, .dark *) {
+ --tw-text-opacity: 1;
+ color: rgb(255 255 255 / var(--tw-text-opacity));
+}
+
+.dark\:text-gray-300:where(.dark, .dark *) {
+ --tw-text-opacity: 1;
+ color: rgb(209 213 219 / var(--tw-text-opacity));
+}
+
+.dark\:hover\:bg-blue-700:hover:where(.dark, .dark *) {
+ --tw-bg-opacity: 1;
+ background-color: rgb(26 86 219 / var(--tw-bg-opacity));
+}
+
+.dark\:hover\:bg-gray-600:hover:where(.dark, .dark *) {
+ --tw-bg-opacity: 1;
+ background-color: rgb(75 85 99 / var(--tw-bg-opacity));
+}
+
+.dark\:hover\:bg-gray-700:hover:where(.dark, .dark *) {
+ --tw-bg-opacity: 1;
+ background-color: rgb(55 65 81 / var(--tw-bg-opacity));
+}
+
+.dark\:hover\:bg-gray-800:hover:where(.dark, .dark *) {
+ --tw-bg-opacity: 1;
+ background-color: rgb(31 41 55 / var(--tw-bg-opacity));
+}
+
+.dark\:hover\:text-blue-500:hover:where(.dark, .dark *) {
+ --tw-text-opacity: 1;
+ color: rgb(63 131 248 / var(--tw-text-opacity));
+}
+
+.dark\:hover\:text-gray-300:hover:where(.dark, .dark *) {
+ --tw-text-opacity: 1;
+ color: rgb(209 213 219 / var(--tw-text-opacity));
+}
+
+.dark\:hover\:text-white:hover:where(.dark, .dark *) {
+ --tw-text-opacity: 1;
+ color: rgb(255 255 255 / var(--tw-text-opacity));
+}
+
+.dark\:focus\:ring-blue-800:focus:where(.dark, .dark *) {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(30 66 159 / var(--tw-ring-opacity));
+}
+
+.dark\:focus\:ring-gray-700:focus:where(.dark, .dark *) {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(55 65 81 / var(--tw-ring-opacity));
+}
+
+.peer:focus ~ .dark\:peer-focus\:ring-blue-800:where(.dark, .dark *) {
+ --tw-ring-opacity: 1;
+ --tw-ring-color: rgb(30 66 159 / var(--tw-ring-opacity));
+}
diff --git a/tailwind.config.js b/tailwind.config.js
index 3b0ae21..8390d14 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -1,5 +1,6 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
+ darkMode: "selector",
content: [
"./index.html",
"./src/**/*.{html,js}",