commit
f8bd1d0438
@ -0,0 +1,598 @@
|
||||
/*
|
||||
* DjangoBlog 主题切换样式
|
||||
* theme.css - 深色/浅色主题支持
|
||||
* 适配 DjangoBlog 实际类名
|
||||
*/
|
||||
|
||||
:root {
|
||||
/* ====== 浅色主题变量 ====== */
|
||||
--bg-primary: #ffffff;
|
||||
--bg-secondary: #f8f9fa;
|
||||
--bg-tertiary: #e9ecef;
|
||||
--bg-card: #ffffff;
|
||||
--text-primary: #333333;
|
||||
--text-secondary: #666666;
|
||||
--text-muted: #999999;
|
||||
--border-color: #dee2e6;
|
||||
--link-color: #007bff;
|
||||
--link-hover-color: #0056b3;
|
||||
--code-bg: #f8f9fa;
|
||||
--blockquote-border: #e9ecef;
|
||||
--shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
--transition: all 0.3s ease;
|
||||
|
||||
/* 导航栏 */
|
||||
--nav-bg: #f8f9fa;
|
||||
--nav-text: #333333;
|
||||
--nav-border: #dee2e6;
|
||||
|
||||
/* 按钮 */
|
||||
--btn-primary-bg: #007bff;
|
||||
--btn-primary-text: #ffffff;
|
||||
--btn-secondary-bg: #6c757d;
|
||||
--btn-secondary-text: #ffffff;
|
||||
|
||||
/* 输入框 */
|
||||
--input-bg: #ffffff;
|
||||
--input-text: #333333;
|
||||
--input-border: #ced4da;
|
||||
--input-focus-border: #007bff;
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
/* ====== 深色主题变量 ====== */
|
||||
--bg-primary: #1a1a1a;
|
||||
--bg-secondary: #2d2d2d;
|
||||
--bg-tertiary: #404040;
|
||||
--bg-card: #2d2d2d;
|
||||
--text-primary: #e9ecef;
|
||||
--text-secondary: #adb5bd;
|
||||
--text-muted: #6c757d;
|
||||
--border-color: #495057;
|
||||
--link-color: #4dabf7;
|
||||
--link-hover-color: #74c0fc;
|
||||
--code-bg: #2d2d2d;
|
||||
--blockquote-border: #495057;
|
||||
--shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
|
||||
/* 导航栏 */
|
||||
--nav-bg: #2d2d2d;
|
||||
--nav-text: #e9ecef;
|
||||
--nav-border: #495057;
|
||||
|
||||
/* 按钮 */
|
||||
--btn-primary-bg: #4dabf7;
|
||||
--btn-primary-text: #ffffff;
|
||||
--btn-secondary-bg: #6c757d;
|
||||
--btn-secondary-text: #ffffff;
|
||||
|
||||
/* 输入框 */
|
||||
--input-bg: #2d2d2d;
|
||||
--input-text: #e9ecef;
|
||||
--input-border: #495057;
|
||||
--input-focus-border: #4dabf7;
|
||||
}
|
||||
|
||||
/* ====== 基础元素样式 ====== */
|
||||
body {
|
||||
background-color: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
transition: var(--transition);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* ====== DjangoBlog 特定样式适配 ====== */
|
||||
|
||||
/* 站点头部 */
|
||||
.site-header {
|
||||
background-color: var(--nav-bg) !important;
|
||||
border-bottom: 1px solid var(--nav-border);
|
||||
}
|
||||
|
||||
.site-title a,
|
||||
.site-description {
|
||||
color: var(--nav-text) !important;
|
||||
}
|
||||
|
||||
/* 主导航 */
|
||||
.main-navigation {
|
||||
background-color: var(--nav-bg);
|
||||
border-color: var(--nav-border);
|
||||
}
|
||||
|
||||
.main-navigation a {
|
||||
color: var(--nav-text) !important;
|
||||
}
|
||||
|
||||
.main-navigation a:hover {
|
||||
color: var(--link-hover-color) !important;
|
||||
}
|
||||
|
||||
/* 主要内容区域 */
|
||||
#page {
|
||||
background-color: var(--bg-primary);
|
||||
}
|
||||
|
||||
#main {
|
||||
background-color: var(--bg-primary);
|
||||
}
|
||||
|
||||
/* 文章和卡片样式 */
|
||||
.entry-content,
|
||||
.entry-header,
|
||||
.entry-summary,
|
||||
.type-post,
|
||||
.widget,
|
||||
.comment-list,
|
||||
.comment-body {
|
||||
background-color: var(--bg-card);
|
||||
color: var(--text-primary);
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
/* 文章标题 */
|
||||
.entry-title,
|
||||
.entry-title a {
|
||||
color: var(--text-primary) !important;
|
||||
}
|
||||
|
||||
.entry-title a:hover {
|
||||
color: var(--link-hover-color) !important;
|
||||
}
|
||||
|
||||
/* 元信息 */
|
||||
.entry-meta,
|
||||
.comment-metadata {
|
||||
color: var(--text-muted) !important;
|
||||
}
|
||||
|
||||
/* 小工具 */
|
||||
.widget-title {
|
||||
color: var(--text-primary);
|
||||
border-bottom-color: var(--border-color);
|
||||
}
|
||||
|
||||
/* 链接 */
|
||||
a {
|
||||
color: var(--link-color);
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--link-hover-color);
|
||||
}
|
||||
|
||||
/* 代码块 */
|
||||
pre, code {
|
||||
background-color: var(--code-bg) !important;
|
||||
color: var(--text-primary) !important;
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
/* 引用 */
|
||||
blockquote {
|
||||
border-left-color: var(--blockquote-border);
|
||||
background-color: var(--bg-secondary);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* 表格 */
|
||||
table {
|
||||
background-color: var(--bg-card);
|
||||
color: var(--text-primary);
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
th, td {
|
||||
border-color: var(--border-color);
|
||||
background-color: var(--bg-card);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
/* 表单 */
|
||||
input, textarea, select {
|
||||
background-color: var(--input-bg);
|
||||
border-color: var(--input-border);
|
||||
color: var(--input-text);
|
||||
}
|
||||
|
||||
input:focus, textarea:focus, select:focus {
|
||||
background-color: var(--input-bg);
|
||||
border-color: var(--input-focus-border);
|
||||
color: var(--input-text);
|
||||
}
|
||||
|
||||
/* 按钮 */
|
||||
button, .button, input[type="submit"], input[type="button"] {
|
||||
background-color: var(--btn-primary-bg);
|
||||
color: var(--btn-primary-text);
|
||||
border-color: var(--btn-primary-bg);
|
||||
}
|
||||
|
||||
/* 页脚 */
|
||||
.site-footer {
|
||||
background-color: var(--bg-secondary) !important;
|
||||
border-top-color: var(--border-color);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* ====== 主题切换按钮样式 ====== */
|
||||
.theme-toggle-btn {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.2rem;
|
||||
transition: var(--transition);
|
||||
color: var(--text-primary);
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.theme-toggle-btn:hover {
|
||||
transform: scale(1.1);
|
||||
background-color: var(--bg-tertiary);
|
||||
border-color: var(--link-color);
|
||||
}
|
||||
|
||||
.theme-toggle-btn:focus {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 2px var(--link-color);
|
||||
}
|
||||
|
||||
/* ====== 导航栏集成样式 ====== */
|
||||
.nav-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.theme-toggle-container {
|
||||
margin: 0 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.user-menu {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.user-dropdown {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.username {
|
||||
padding: 8px 15px;
|
||||
color: var(--text-primary);
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
background: var(--bg-secondary);
|
||||
transition: var(--transition);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.username:hover {
|
||||
background: var(--bg-tertiary);
|
||||
}
|
||||
|
||||
.dropdown-content {
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
background-color: var(--bg-card);
|
||||
min-width: 160px;
|
||||
box-shadow: var(--shadow);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.dropdown-content a {
|
||||
color: var(--text-primary);
|
||||
padding: 12px 16px;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
transition: var(--transition);
|
||||
border: none;
|
||||
}
|
||||
|
||||
.dropdown-content a:hover {
|
||||
background-color: var(--bg-secondary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.user-dropdown:hover .dropdown-content {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dropdown-divider {
|
||||
height: 1px;
|
||||
background-color: var(--border-color);
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.login-link {
|
||||
padding: 8px 15px;
|
||||
color: var(--text-primary);
|
||||
text-decoration: none;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
background: var(--bg-secondary);
|
||||
transition: var(--transition);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.login-link:hover {
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--link-hover-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* ====== 平滑过渡效果 ====== */
|
||||
body,
|
||||
.site-header,
|
||||
.main-navigation,
|
||||
.entry-content,
|
||||
.entry-header,
|
||||
.widget,
|
||||
.comment-list,
|
||||
.site-footer,
|
||||
a,
|
||||
button,
|
||||
input,
|
||||
textarea,
|
||||
select,
|
||||
table,
|
||||
pre,
|
||||
code {
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
/* 图片过渡 */
|
||||
img {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
[data-theme="dark"] img {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
[data-theme="dark"] img:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* ====== 响应式调整 ====== */
|
||||
@media (max-width: 768px) {
|
||||
.theme-toggle-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.nav-container {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.theme-toggle-container {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.user-menu {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
/* ====== 导航栏集成优化样式 ====== */
|
||||
|
||||
/* 导航容器布局 */
|
||||
.nav-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.primary-nav {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.nav-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
/* 主题切换按钮优化 */
|
||||
.theme-toggle-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.theme-toggle-btn {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.2rem;
|
||||
transition: var(--transition);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.theme-toggle-btn:hover {
|
||||
transform: scale(1.1);
|
||||
background-color: var(--bg-tertiary);
|
||||
border-color: var(--link-color);
|
||||
}
|
||||
|
||||
/* 用户菜单优化 */
|
||||
.user-menu {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.user-dropdown {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.username {
|
||||
padding: 8px 15px;
|
||||
color: var(--text-primary);
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 20px;
|
||||
background: var(--bg-secondary);
|
||||
transition: var(--transition);
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.username:hover {
|
||||
background: var(--bg-tertiary);
|
||||
}
|
||||
|
||||
.user-icon {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.dropdown-content {
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 100%;
|
||||
background-color: var(--bg-card);
|
||||
min-width: 140px;
|
||||
box-shadow: var(--shadow);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
z-index: 1000;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.dropdown-content a {
|
||||
color: var(--text-primary);
|
||||
padding: 10px 15px;
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
transition: var(--transition);
|
||||
border: none;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.dropdown-content a:hover {
|
||||
background-color: var(--bg-secondary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.user-dropdown:hover .dropdown-content {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dropdown-divider {
|
||||
height: 1px;
|
||||
background-color: var(--border-color);
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.login-link {
|
||||
padding: 8px 15px;
|
||||
color: var(--text-primary);
|
||||
text-decoration: none;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 20px;
|
||||
background: var(--bg-secondary);
|
||||
transition: var(--transition);
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.login-link:hover {
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--link-hover-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.nav-container {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.nav-actions {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.theme-toggle-btn {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.primary-nav {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
/* 确保原有导航样式兼容 */
|
||||
.main-navigation {
|
||||
background-color: var(--nav-bg);
|
||||
border-bottom: 1px solid var(--nav-border);
|
||||
transition: var(--transition);
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.main-navigation div {
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.main-navigation a {
|
||||
color: var(--nav-text) !important;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.main-navigation a:hover {
|
||||
color: var(--link-hover-color) !important;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
/* 修复可能的分层问题 */
|
||||
.site-header {
|
||||
position: relative;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.main-navigation {
|
||||
position: relative;
|
||||
z-index: 99;
|
||||
}
|
||||
@ -0,0 +1,132 @@
|
||||
// blog/static/blog/js/theme-switcher.js
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
class ThemeSwitcher {
|
||||
constructor() {
|
||||
this.themeToggle = null;
|
||||
this.currentTheme = this.getPreferredTheme();
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
console.log('初始化主题切换器,当前主题:', this.currentTheme);
|
||||
this.setTheme(this.currentTheme);
|
||||
this.bindEvents();
|
||||
this.watchSystemTheme();
|
||||
}
|
||||
|
||||
getPreferredTheme() {
|
||||
try {
|
||||
const storedTheme = localStorage.getItem('theme');
|
||||
if (storedTheme) {
|
||||
return storedTheme;
|
||||
}
|
||||
|
||||
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||
return 'dark';
|
||||
}
|
||||
return 'light';
|
||||
} catch (error) {
|
||||
console.warn('获取主题偏好失败:', error);
|
||||
return 'light';
|
||||
}
|
||||
}
|
||||
|
||||
setTheme(theme) {
|
||||
try {
|
||||
document.documentElement.setAttribute('data-theme', theme);
|
||||
localStorage.setItem('theme', theme);
|
||||
this.currentTheme = theme;
|
||||
this.updateToggleIcon();
|
||||
|
||||
console.log('主题已设置为:', theme);
|
||||
|
||||
const event = new CustomEvent('themeChanged', {
|
||||
detail: { theme: theme }
|
||||
});
|
||||
document.dispatchEvent(event);
|
||||
|
||||
} catch (error) {
|
||||
console.error('设置主题失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
toggleTheme() {
|
||||
const newTheme = this.currentTheme === 'light' ? 'dark' : 'light';
|
||||
console.log('切换主题:', this.currentTheme, '->', newTheme);
|
||||
this.setTheme(newTheme);
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
this.themeToggle = document.getElementById('theme-toggle');
|
||||
|
||||
if (this.themeToggle) {
|
||||
this.themeToggle.addEventListener('click', () => {
|
||||
this.toggleTheme();
|
||||
});
|
||||
this.updateToggleIcon();
|
||||
} else {
|
||||
console.warn('未找到主题切换按钮 #theme-toggle');
|
||||
|
||||
setTimeout(() => {
|
||||
this.themeToggle = document.getElementById('theme-toggle');
|
||||
if (this.themeToggle) {
|
||||
this.themeToggle.addEventListener('click', () => {
|
||||
this.toggleTheme();
|
||||
});
|
||||
this.updateToggleIcon();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
updateToggleIcon() {
|
||||
if (!this.themeToggle) return;
|
||||
|
||||
const iconElement = this.themeToggle.querySelector('.theme-icon');
|
||||
if (iconElement) {
|
||||
const icon = this.currentTheme === 'light' ? '🌙' : '☀️';
|
||||
const title = this.currentTheme === 'light' ? '切换到深色模式' : '切换到浅色模式';
|
||||
|
||||
iconElement.textContent = icon;
|
||||
this.themeToggle.title = title;
|
||||
this.themeToggle.setAttribute('aria-label', title);
|
||||
}
|
||||
}
|
||||
|
||||
watchSystemTheme() {
|
||||
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
|
||||
const handleSystemThemeChange = (e) => {
|
||||
if (!localStorage.getItem('theme')) {
|
||||
const newTheme = e.matches ? 'dark' : 'light';
|
||||
console.log('系统主题变化,自动切换至:', newTheme);
|
||||
this.setTheme(newTheme);
|
||||
}
|
||||
};
|
||||
|
||||
if (mediaQuery.addEventListener) {
|
||||
mediaQuery.addEventListener('change', handleSystemThemeChange);
|
||||
} else {
|
||||
mediaQuery.addListener(handleSystemThemeChange);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
window.themeSwitcher = new ThemeSwitcher();
|
||||
});
|
||||
} else {
|
||||
window.themeSwitcher = new ThemeSwitcher();
|
||||
}
|
||||
|
||||
window.toggleTheme = function() {
|
||||
if (window.themeSwitcher) {
|
||||
window.themeSwitcher.toggleTheme();
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
Loading…
Reference in new issue