feat: implement dark mode support
- Introduce CSS variables for theming in `global.css` - Add `ThemeIcon` component for toggling light/dark modes - Integrate theme initialization script in `BaseLayout` to prevent FOUC - Update navigation, buttons, and tag styles for dark mode compatibility - Configure Shiki for dual-theme syntax highlighting in `astro.config.mjs` - Adjust link colors to accessible blue shades for both themes
This commit is contained in:
parent
e357f2ed68
commit
62aa324e16
11 changed files with 218 additions and 61 deletions
|
|
@ -6,5 +6,14 @@ import preact from "@astrojs/preact";
|
|||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
site: "https://blog.n-daisuke897.com/",
|
||||
integrations: [preact()]
|
||||
integrations: [preact()],
|
||||
markdown: {
|
||||
shikiConfig: {
|
||||
themes: {
|
||||
light: 'github-light',
|
||||
dark: 'github-dark',
|
||||
},
|
||||
wrap: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
@ -12,11 +12,19 @@ const { title, url, datetime } = Astro.props;
|
|||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
:global(.dark) .new-article-date {
|
||||
color: #94a3b8;
|
||||
}
|
||||
.new-article-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #333f;
|
||||
color: var(--text-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
.new-article-title:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
/* Removed specific dark mode color to inherit var(--text-color) */
|
||||
</style>
|
||||
<div class="new-article">
|
||||
<div class="new-article-date">{datetime}</div>
|
||||
|
|
|
|||
|
|
@ -9,3 +9,11 @@ import Navigation from "./Navigation.astro";
|
|||
<Navigation />
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<style>
|
||||
nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
---
|
||||
|
||||
import ThemeIcon from "./ThemeIcon.astro";
|
||||
---
|
||||
|
||||
<div class="nav-links">
|
||||
<ThemeIcon />
|
||||
<a href="/">Home</a>
|
||||
<a href="/about/">About</a>
|
||||
<a href="/blog/">Articles</a>
|
||||
|
|
|
|||
36
src/components/ThemeIcon.astro
Normal file
36
src/components/ThemeIcon.astro
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
---
|
||||
---
|
||||
<button id="themeToggle">
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path class="sun" d="M6.76 4.84l-1.8-1.79-1.41 1.41 1.79 1.79 1.42-1.41zM4 10.5H1v2h3v-2zm9-9.95h-2V3.5h2V.55zm7.45 3.91l-1.41-1.41-1.79 1.79 1.41 1.41 1.79-1.79zm-3.21 13.7l1.79 1.8 1.41-1.41-1.8-1.79-1.4 1.4zM20 10.5v2h3v-2h-3zm-8-5c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6-2.69-6-6-6zm-1 16.95h2V19.5h-2v2.95zm-7.45-3.91l1.41 1.41 1.79-1.8-1.41-1.41-1.79 1.8z" />
|
||||
<path class="moon" d="M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9 9-4.03 9-9c0-.46-.04-.92-.1-1.36-.98 1.37-2.58 2.27-4.4 2.27-3.03 0-5.5-2.47-5.5-5.5 0-1.82.89-3.42 2.26-4.4-.44-.06-.9-.1-1.36-.1z" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<style>
|
||||
#themeToggle {
|
||||
border: 0;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.sun { fill: transparent; }
|
||||
.moon { fill: transparent; }
|
||||
|
||||
:global(.dark) .moon { fill: white; }
|
||||
:global(html:not(.dark)) .sun { fill: black; }
|
||||
</style>
|
||||
|
||||
<script is:inline>
|
||||
const handleToggleClick = () => {
|
||||
const element = document.documentElement;
|
||||
element.classList.toggle("dark");
|
||||
|
||||
const isDark = element.classList.contains("dark");
|
||||
localStorage.setItem("theme", isDark ? "dark" : "light");
|
||||
}
|
||||
|
||||
document.getElementById("themeToggle").addEventListener("click", handleToggleClick);
|
||||
</script>
|
||||
|
|
@ -12,6 +12,22 @@ const { pageTitle } = Astro.props;
|
|||
<meta name="viewport" content="width=device-width" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<title>{pageTitle + " | Naputo"}</title>
|
||||
<script is:inline>
|
||||
const theme = (() => {
|
||||
if (typeof localStorage !== 'undefined' && localStorage.getItem('theme')) {
|
||||
return localStorage.getItem('theme');
|
||||
}
|
||||
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||
return 'dark';
|
||||
}
|
||||
return 'light';
|
||||
})();
|
||||
if (theme === 'dark') {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<Header />
|
||||
|
|
|
|||
|
|
@ -30,7 +30,11 @@ const optionsForDate = {
|
|||
|
||||
<style>
|
||||
a {
|
||||
color: #00539f;
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
:global(.dark) a {
|
||||
color: #60a5fa;
|
||||
}
|
||||
|
||||
.tags {
|
||||
|
|
@ -47,6 +51,11 @@ const optionsForDate = {
|
|||
background-color: #f8fcfd;
|
||||
}
|
||||
|
||||
:global(.dark) .tag {
|
||||
background-color: #1e293b;
|
||||
border-color: #475569;
|
||||
}
|
||||
|
||||
.markdown-content {
|
||||
font-family: sans-serif;
|
||||
font-size: 1.1rem;
|
||||
|
|
|
|||
|
|
@ -77,15 +77,28 @@ const pageTitle = "Articles";
|
|||
text-decoration: none;
|
||||
transition: background-color 0.3s ease, transform 0.3s ease;
|
||||
}
|
||||
:global(.dark) nav.pagination a,
|
||||
:global(.dark) nav.pagination span {
|
||||
background-color: #1e293b;
|
||||
color: #f8fafc;
|
||||
border: 1px solid #334155;
|
||||
}
|
||||
nav.pagination a:hover {
|
||||
background-color: #cacaca;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
:global(.dark) nav.pagination a:hover {
|
||||
background-color: #334155;
|
||||
}
|
||||
nav.pagination a.active {
|
||||
background-color: #cacaca;
|
||||
font-weight: bold;
|
||||
pointer-events: none;
|
||||
}
|
||||
:global(.dark) nav.pagination a.active {
|
||||
background-color: #334155;
|
||||
color: #f8fafc;
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
nav.pagination {
|
||||
flex-direction: column;
|
||||
|
|
|
|||
|
|
@ -34,9 +34,17 @@ const sortedPosts = allPosts.sort((a, b) => b.data.pubDate.valueOf() - a.data.pu
|
|||
transition: background-color 0.3s ease, transform 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
:global(.dark) .list-of-articles-button {
|
||||
background-color: #1e293b;
|
||||
color: #f8fafc;
|
||||
border: 1px solid #334155;
|
||||
}
|
||||
.list-of-articles-button:hover {
|
||||
background-color: #cacaca;
|
||||
}
|
||||
:global(.dark) .list-of-articles-button:hover {
|
||||
background-color: #334155;
|
||||
}
|
||||
</style>
|
||||
<BaseLayout pageTitle={pageTitle}>
|
||||
<h3 class="header-new-posts">New Posts</h3>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ const pageTitle = "Tag List";
|
|||
|
||||
<style>
|
||||
a {
|
||||
color: #00539f;
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
.tags {
|
||||
|
|
@ -36,4 +36,13 @@ const pageTitle = "Tag List";
|
|||
font-size: 1.15em;
|
||||
background-color: #f8fcfd;
|
||||
}
|
||||
|
||||
:global(.dark) .tag {
|
||||
background-color: #1e293b;
|
||||
border-color: #475569;
|
||||
}
|
||||
|
||||
:global(.dark) .tag a {
|
||||
color: #f8fafc;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,19 @@
|
|||
/* Dark Mode support */
|
||||
:root {
|
||||
--bg-color: #f1f5f9;
|
||||
--text-color: #333333;
|
||||
--nav-hover: #e0e0e0;
|
||||
}
|
||||
|
||||
html.dark {
|
||||
--bg-color: #0f172a;
|
||||
--text-color: #f8fafc;
|
||||
--nav-hover: #1e293b;
|
||||
}
|
||||
|
||||
html {
|
||||
background-color: #f1f5f9;
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-color);
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
|
|
@ -21,6 +35,14 @@ h1 {
|
|||
}
|
||||
|
||||
/* nav styles */
|
||||
a {
|
||||
color: #3b82f6; /* Blue 500 - vivid but readable on light */
|
||||
}
|
||||
|
||||
html.dark a {
|
||||
color: #60a5fa; /* Blue 400 - lighter and softer for dark mode */
|
||||
}
|
||||
|
||||
.hamburger {
|
||||
padding-right: 20px;
|
||||
cursor: pointer;
|
||||
|
|
@ -31,17 +53,22 @@ h1 {
|
|||
width: 32px;
|
||||
height: 4px;
|
||||
margin-bottom: 8px;
|
||||
background-color: #333333;
|
||||
background-color: var(--text-color);
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
display: none;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.nav-links.expanded {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.nav-links a {
|
||||
display: block;
|
||||
text-align: center;
|
||||
|
|
@ -51,12 +78,12 @@ h1 {
|
|||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
border-radius: 4px;
|
||||
color: #333333;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.nav-links a:hover,
|
||||
.nav-links a:focus {
|
||||
background-color: #e0e0e0;
|
||||
background-color: var(--nav-hover);
|
||||
}
|
||||
|
||||
.nav-links img {
|
||||
|
|
@ -66,14 +93,16 @@ h1 {
|
|||
display: inline-block;
|
||||
}
|
||||
|
||||
.expanded {
|
||||
display: unset;
|
||||
html.dark .nav-links img {
|
||||
filter: invert(1) hue-rotate(180deg);
|
||||
}
|
||||
|
||||
/* .expanded removed as it is handled by .nav-links.expanded */
|
||||
|
||||
@media screen and (min-width: 636px) {
|
||||
.nav-links {
|
||||
margin-left: 5em;
|
||||
display: block;
|
||||
display: flex;
|
||||
position: static;
|
||||
width: auto;
|
||||
background: none;
|
||||
|
|
@ -94,3 +123,14 @@ pre,
|
|||
code {
|
||||
font-family: "JetBrains Mono", "Fira Code", "Menlo", "Consolas", monospace;
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 1rem;
|
||||
border-radius: 0.5rem;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
html.dark pre {
|
||||
background-color: #1e293b !important;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue