feat: dark mode

This commit is contained in:
JamesFlare1212
2025-05-13 16:54:39 -04:00
parent de78a2b508
commit 8c11180e8c
3 changed files with 302 additions and 64 deletions

View File

@@ -1,10 +1,13 @@
---
// No props needed for this simple header
// src/components/Header.astro
import ThemeToggler from './ThemeToggler.astro';
---
<header>
<h1>DSAS CCA Viewer</h1>
<nav>
<a href="/">Clubs</a>
<a href="/staff">Staff</a>
<ThemeToggler/>
</nav>
</header>

View File

@@ -0,0 +1,108 @@
---
// src/components/ThemeToggler.astro
---
<style>
:root {
--toggle-bg: #e2e8f0;
--toggle-bg-dark: #334155;
--knob-bg: #ffffff;
--icon-color: #facc15; /* sun / moon colour */
}
.theme-toggle-button{
position:relative;
width:3rem;
height:1.5rem;
border-radius:9999px;
background:var(--toggle-bg,#e2e8f0);
border:none;
cursor:pointer;
padding:0;
display:flex;
align-items:center;
transition:background .3s ease;
}
.theme-toggle-button .knob{
position:absolute;
top:0.125rem;
left:0.125rem;
width:1.25rem;
height:1.25rem;
border-radius:9999px;
background:var(--knob-bg,#fff);
display:flex;
align-items:center;
justify-content:center;
transition:transform .3s ease, background .3s ease;
box-shadow:0 1px 3px rgba(0,0,0,0.2);
}
body.dark-mode .theme-toggle-button{
background:var(--toggle-bg-dark,#334155);
}
body.dark-mode .theme-toggle-button .knob{
transform:translateX(1.5rem);
}
.theme-toggle-button svg{
width:0.75rem;
height:0.75rem;
color:var(--icon-color,#facc15);
}
body.dark-mode .theme-toggle-button svg.sun{display:none;}
body:not(.dark-mode) .theme-toggle-button svg.moon{display:none;}
</style>
<button id="themeToggleBtn" class="theme-toggle-button" aria-label="Toggle theme" aria-pressed="false">
<div class="knob">
<!-- Sun icon -->
<svg class="sun" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 18a6 6 0 110-12 6 6 0 010 12z"/>
<path d="M12 2v2M12 20v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M2 12h2M20 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/>
</svg>
<!-- Moon icon -->
<svg class="moon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/>
</svg>
</div>
</button>
<script>
const themeToggleBtn = document.getElementById('themeToggleBtn');
const body = document.body;
const preferDarkQuery = '(prefers-color-scheme: dark)';
function applyTheme(theme){
if(theme==='dark'){
body.classList.add('dark-mode');
themeToggleBtn?.setAttribute('aria-pressed','true');
}else{
body.classList.remove('dark-mode');
themeToggleBtn?.setAttribute('aria-pressed','false');
}
}
function toggleTheme(){
const isDarkMode = body.classList.contains('dark-mode');
const newTheme = isDarkMode ? 'light' : 'dark';
localStorage.setItem('theme', newTheme);
applyTheme(newTheme);
}
function loadTheme(){
const savedTheme = localStorage.getItem('theme');
const systemPrefersDark = window.matchMedia(preferDarkQuery).matches;
if(savedTheme){
applyTheme(savedTheme);
}else if(systemPrefersDark){
applyTheme('dark');
}else{
applyTheme('light');
}
}
loadTheme();
themeToggleBtn?.addEventListener('click', toggleTheme);
window.matchMedia(preferDarkQuery).addEventListener('change', (e)=>{
if(!localStorage.getItem('theme')){
applyTheme(e.matches ? 'dark' : 'light');
}
});
</script>

View File

@@ -1,20 +1,84 @@
/* src/styles/global.css */
/* Basic Reset & Defaults */
:root {
/* Light Mode Variables (Default) */
--bg-color: #f4f7f6;
--text-color: #333;
--header-bg: #2c3e50;
--header-text: white;
--header-link-hover: #1abc9c;
--footer-bg: #34495e;
--footer-text: #ecf0f1;
--card-bg: white;
--card-box-shadow: rgba(0,0,0,0.1);
--card-box-shadow-hover: rgba(0,0,0,0.15);
--card-border: #eee; /* For borders like in card-details-section */
--search-bar-bg: #ffffff;
--search-bar-border: #ddd;
--search-bar-text: #333; /* Text inside input fields */
--button-bg: #1abc9c;
--button-text: white;
--button-bg-hover: #16a085;
--placeholder-text: #888;
--modal-content-bg: white;
--modal-overlay-bg: rgba(0, 0, 0, 0.6);
--modal-header-border: #eee;
--modal-close-button-color: #888;
--modal-close-button-hover-color: #333;
--accent-color: #1abc9c; /* For things like modal h3 */
--strong-text-color: #555;
--link-color: #1abc9c; /* General link color if needed */
}
body.dark-mode {
/* Dark Mode Variable Overrides */
--bg-color: #1a1a1a; /* Darker background */
--text-color: #e0e0e0; /* Lighter text */
--header-bg: #1f2937; /* Slightly different dark header */
--header-text: #e0e0e0;
--header-link-hover: #58c1ac; /* Lighter accent for hover */
--footer-bg: #111827; /* Darker footer */
--footer-text: #c7c7c7;
--card-bg: #2d2d2d; /* Dark card background */
--card-box-shadow: rgba(255,255,255,0.05); /* Lighter shadow for dark mode */
--card-box-shadow-hover: rgba(255,255,255,0.1);
--card-border: #444; /* Darker border */
--search-bar-bg: #2d2d2d;
--search-bar-border: #555;
--search-bar-text: #e0e0e0; /* Text inside input fields */
--button-bg: #58c1ac; /* Lighter accent for buttons */
--button-text: #1a1a1a; /* Darker text on light buttons */
--button-bg-hover: #4aa08c;
--placeholder-text: #777;
--modal-content-bg: #2d2d2d;
--modal-overlay-bg: rgba(0, 0, 0, 0.8); /* Darker overlay */
--modal-header-border: #444;
--modal-close-button-color: #aaa;
--modal-close-button-hover-color: #e0e0e0;
--accent-color: #58c1ac;
--strong-text-color: #bbb;
--link-color: #58c1ac;
}
body {
font-family: sans-serif;
margin: 0;
background-color: #f4f7f6;
color: #333;
background-color: var(--bg-color); /* USE VAR */
color: var(--text-color); /* USE VAR */
line-height: 1.6;
box-sizing: border-box; /* Add to body and inherit */
box-sizing: border-box;
transition: background-color 0.3s ease, color 0.3s ease; /* Smooth transition */
}
*, *::before, *::after {
box-sizing: inherit; /* Ensure all elements use the same box model */
box-sizing: inherit;
}
header {
background-color: #2c3e50;
color: white;
background-color: var(--header-bg); /* USE VAR */
color: var(--header-text); /* USE VAR */
padding: 1rem 2rem;
display: flex;
justify-content: space-between;
@@ -23,19 +87,40 @@ header {
header h1 {
margin: 0;
font-size: 1.8rem;
font-size: 1.66rem;
}
header nav{
display:flex; /* put the links and the toggle in one row */
align-items:center; /* vertically centre them */
gap:1.5rem; /* space between the links and the toggle */
}
header nav a {
color: white;
color: var(--header-text); /* USE VAR */
text-decoration: none;
margin-left: 1.5rem;
font-size: 1.1rem;
margin-left: 0;
font-size: 1.33rem;
transition: color 0.2s ease-in-out;
}
header nav a:hover {
color: #1abc9c;
color: var(--header-link-hover); /* USE VAR */
}
/* Theme Toggle Button Style */
.theme-toggle-button {
background: none;
border: 1px solid var(--header-text);
color: var(--header-text);
padding: 0.4rem 0.8rem;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
margin-left: 0;
}
body.dark-mode .theme-toggle-button {
border-color: var(--header-text); /* Or a specific dark mode border color */
}
main {
@@ -47,46 +132,57 @@ main {
footer {
text-align: center;
padding: 1.5rem;
background-color: #34495e;
color: #ecf0f1;
background-color: var(--footer-bg); /* USE VAR */
color: var(--footer-text); /* USE VAR */
margin-top: 2rem;
}
/* Search Bar Styles - Desktop First */
.search-bar-container {
background-color: #ffffff;
background-color: var(--search-bar-bg); /* USE VAR */
padding: 1.5rem;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
box-shadow: 0 2px 10px var(--card-box-shadow); /* USE VAR for consistency with cards */
margin-bottom: 2rem;
display: flex;
flex-wrap: wrap;
gap: 1rem;
align-items: center; /* Aligns items vertically if they have different heights on one line */
align-items: center;
}
.search-bar-container input[type="text"],
.search-bar-container input[type="number"],
.search-bar-container select {
background-color: var(--bg-color); /* Match page bg or use specific input bg */
color: var(--search-bar-text); /* USE VAR */
padding: 0.75rem;
border: 1px solid #ddd;
border: 1px solid var(--search-bar-border); /* USE VAR */
border-radius: 4px;
font-size: 1rem;
line-height: 1.4; /* Ensure consistent line height */
line-height: 1.4;
flex-grow: 1;
flex-shrink: 1;
/* min-width helps with wrapping on desktop */
}
/* Handle select dropdown arrow color for dark mode (can be tricky cross-browser) */
/* For modern browsers, accent-color can influence form controls */
.search-bar-container select {
accent-color: var(--accent-color);
}
/* Placeholder color for inputs */
.search-bar-container input::placeholder {
color: var(--placeholder-text);
opacity: 0.7;
}
.search-bar-container input[type="text"] {
flex-basis: 300px; /* Ideal width on desktop */
min-width: 250px; /* Minimum it should shrink to on desktop before wrapping */
flex-basis: 300px;
min-width: 250px;
}
.search-bar-container select,
.search-bar-container input[type="number"] {
flex-basis: 200px; /* Ideal width for these filters on desktop */
min-width: 150px; /* Minimum they should shrink to */
flex-basis: 200px;
min-width: 150px;
}
@@ -98,34 +194,41 @@ footer {
}
header nav {
margin-top: 0.5rem;
width: 100%; /* Ensure nav takes full width for button alignment */
display: flex;
justify-content: space-between; /* Align nav items and toggle button */
align-items: center;
}
header nav div { /* Assuming nav links are wrapped in a div */
display: flex;
}
header nav a {
margin-left: 0;
margin-right: 1rem;
}
.theme-toggle-button {
margin-left: auto; /* Push toggle to the right */
}
/* === Search Bar Mobile Styles === */
.search-bar-container {
flex-direction: column; /* Stack items vertically */
align-items: stretch; /* Make items take up full available width */
padding: 1rem; /* Adjust padding for mobile */
gap: 0.75rem; /* Adjust gap between stacked items */
flex-direction: column;
align-items: stretch;
padding: 1rem;
gap: 0.75rem;
}
.search-bar-container input[type="text"],
.search-bar-container input[type="number"],
.search-bar-container select {
width: 100%; /* Make each item take full width */
font-size: 0.9rem; /* Slightly smaller font for better fit */
padding: 0.65rem 0.75rem; /* Adjust padding (vertical, horizontal) */
/* Remove flex-basis, min-width for mobile as width: 100% takes precedence */
width: 100%;
font-size: 0.9rem;
padding: 0.65rem 0.75rem;
flex-basis: auto;
min-width: 0;
flex-grow: 0; /* Not needed when width is 100% in a column */
flex-shrink: 0; /* Not needed when width is 100% in a column */
flex-grow: 0;
flex-shrink: 0;
}
/* No specific overrides needed for input[type="text"] or input[type="number"] here
as they will follow the general rule above for mobile. */
}
@@ -138,27 +241,32 @@ footer {
/* Card Styles */
.card {
background-color: white;
background-color: var(--card-bg); /* USE VAR */
color: var(--text-color); /* Inherit text color from body or set specific */
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
box-shadow: 0 4px 6px var(--card-box-shadow); /* USE VAR */
overflow: hidden;
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out, background-color 0.3s ease, color 0.3s ease;
display: flex;
flex-direction: column;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 12px rgba(0,0,0,0.15);
box-shadow: 0 8px 12px var(--card-box-shadow-hover); /* USE VAR */
}
.card-photo-container {
width: 100%;
aspect-ratio: 16 / 9;
background-color: #e0e0e0;
background-color: var(--placeholder-text); /* Or a darker placeholder for dark mode */
overflow: hidden;
display: block;
}
body.dark-mode .card-photo-container {
background-color: #4a4a4a; /* Darker placeholder */
}
.card-photo {
display: block;
@@ -178,7 +286,7 @@ footer {
margin-top: 0;
margin-bottom: 0.33rem;
font-size: 1.33rem;
color: #333;
color: var(--text-color); /* USE VAR */
}
.card-content p {
@@ -188,19 +296,24 @@ footer {
margin-right: 0;
font-size: 1rem;
line-height: 1.5;
color: var(--text-color); /* USE VAR */
}
.card-content p strong {
color: var(--strong-text-color); /* USE VAR for strong tags inside paragraphs */
}
.card-info-placeholder {
font-style: italic;
color: #888;
color: var(--placeholder-text); /* USE VAR */
font-size: 0.9rem;
min-height: 1.2em;
}
.card-details-section {
.card-details-section { /* This class is not currently used in ClubCard.tsx for the modal */
margin-top: 0.75rem;
padding-top: 0.75rem;
border-top: 1px solid #eee;
border-top: 1px solid var(--card-border); /* USE VAR */
}
.card-details-section p {
@@ -209,12 +322,12 @@ footer {
}
.card-details-section strong {
color: #555;
color: var(--strong-text-color); /* USE VAR */
}
.card-button {
background-color: #1abc9c;
color: white;
background-color: var(--button-bg); /* USE VAR */
color: var(--button-text); /* USE VAR */
border: none;
padding: 0.6rem 1rem;
border-radius: 4px;
@@ -226,7 +339,7 @@ footer {
}
.card-button:hover {
background-color: #16a085;
background-color: var(--button-bg-hover); /* USE VAR */
}
/* Loading and Error Messages */
@@ -234,24 +347,33 @@ footer {
text-align: center;
font-size: 1.2rem;
padding: 2rem;
color: #555;
color: var(--strong-text-color); /* USE VAR */
}
.error-message {
color: #e74c3c;
color: #e74c3c; /* Keep specific error color, or make it a variable too */
}
body.dark-mode .error-message {
color: #ff6b6b; /* Lighter red for dark mode */
}
/* Staff Card Specifics */
.staff-card {
.staff-card { /* Staff card inherits .card styles, so it gets dark mode too */
padding: 1.5rem;
}
.staff-card h3 {
font-size: 1.2rem;
color: var(--text-color); /* USE VAR */
}
.staff-card p {
font-size: 1rem;
color: #555;
color: var(--text-color); /* USE VAR */
}
.staff-card p strong {
color: var(--strong-text-color);
}
/* Modal Styles */
.modal-overlay {
@@ -260,7 +382,7 @@ footer {
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.6);
background-color: var(--modal-overlay-bg); /* USE VAR */
display: flex;
align-items: center;
justify-content: center;
@@ -269,10 +391,11 @@ footer {
}
.modal-content {
background-color: white;
background-color: var(--modal-content-bg); /* USE VAR */
color: var(--text-color); /* USE VAR */
padding: 2rem;
border-radius: 8px;
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
box-shadow: 0 5px 15px var(--card-box-shadow); /* USE VAR */
width: 90%;
max-width: 700px;
max-height: 90vh;
@@ -287,18 +410,18 @@ footer {
padding-right: 1rem;
}
.modal-content h2 {
.modal-content h2 { /* Main title in modal */
margin-top: 0;
margin-bottom: 1rem;
color: #333;
color: var(--text-color); /* USE VAR */
padding-bottom: 1rem;
border-bottom: 1px solid #eee;
border-bottom: 1px solid var(--modal-header-border); /* USE VAR */
}
.modal-content h3 {
.modal-content h3 { /* Subheadings like "Meta Information" in modal */
margin-top: 1rem;
margin-bottom: 0.5rem;
color: #1abc9c;
color: var(--accent-color); /* USE VAR */
}
.modal-content p, .modal-content li {
margin-top: 0.33rem;
@@ -307,6 +430,10 @@ footer {
margin-right: 0;
font-size: 1rem;
line-height: 1.5;
color: var(--text-color); /* USE VAR */
}
.modal-content p strong {
color: var(--strong-text-color);
}
.modal-content ul {
padding-left: 20px;
@@ -328,10 +455,10 @@ footer {
border: none;
font-size: 2rem;
font-weight: bold;
color: #888;
color: var(--modal-close-button-color); /* USE VAR */
cursor: pointer;
line-height: 1;
}
.modal-close-button:hover {
color: #333;
color: var(--modal-close-button-hover-color); /* USE VAR */
}