Remove custom CSS from main user templates

Remove <style> tags from analytics, bookshelf, conflicts, and dashboard
templates. Convert custom CSS classes (.stat-card, .shelf, .book-item,
.conflict-card, etc.) to TailwindCSS utility classes. Rely on
centralized theme CSS variables in input.css.
This commit is contained in:
2026-02-02 10:27:53 -05:00
parent 605beb31e5
commit fa16a3d78a
4 changed files with 36 additions and 339 deletions
+4 -13
View File
@@ -12,15 +12,6 @@ templ Analytics(user User) {
<link href="/static/style.css" rel="stylesheet">
<script src="/static/theme.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
.stat-card {
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.stat-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
</style>
</head>
<body class="theme-{ user.Theme }">
@Header(user, "/analytics")
@@ -54,19 +45,19 @@ templ Analytics(user User) {
<!-- Stats Cards -->
<div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8" id="stats-container">
<div class="stat-card card p-6 rounded-lg border" style="background-color: var(--bg-secondary); border-color: var(--border);">
<div class="card p-6 rounded-lg border transition-all duration-200 ease hover:-translate-y-0.5 hover:shadow-lg" style="background-color: var(--bg-secondary); border-color: var(--border);">
<h3 class="text-lg font-semibold mb-2" style="color: var(--text-secondary);">Books Read</h3>
<p id="total-books" class="text-3xl font-bold" style="color: var(--text-primary);">-</p>
</div>
<div class="stat-card card p-6 rounded-lg border" style="background-color: var(--bg-secondary); border-color: var(--border);">
<div class="card p-6 rounded-lg border transition-all duration-200 ease hover:-translate-y-0.5 hover:shadow-lg" style="background-color: var(--bg-secondary); border-color: var(--border);">
<h3 class="text-lg font-semibold mb-2" style="color: var(--text-secondary);">Pages Read</h3>
<p id="total-pages" class="text-3xl font-bold" style="color: var(--text-primary);">-</p>
</div>
<div class="stat-card card p-6 rounded-lg border" style="background-color: var(--bg-secondary); border-color: var(--border);">
<div class="card p-6 rounded-lg border transition-all duration-200 ease hover:-translate-y-0.5 hover:shadow-lg" style="background-color: var(--bg-secondary); border-color: var(--border);">
<h3 class="text-lg font-semibold mb-2" style="color: var(--text-secondary);">Reading Time</h3>
<p id="reading-time" class="text-3xl font-bold" style="color: var(--text-primary);">-</p>
</div>
<div class="stat-card card p-6 rounded-lg border" style="background-color: var(--bg-secondary); border-color: var(--border);">
<div class="card p-6 rounded-lg border transition-all duration-200 ease hover:-translate-y-0.5 hover:shadow-lg" style="background-color: var(--bg-secondary); border-color: var(--border);">
<h3 class="text-lg font-semibold mb-2" style="color: var(--text-secondary);">Completion Rate</h3>
<p id="completion-rate" class="text-3xl font-bold" style="color: var(--text-primary);">-</p>
</div>
+9 -211
View File
@@ -12,216 +12,12 @@ templ BookShelf(user User) {
<script src="/static/header.js"></script>
<script src="/static/search.js"></script>
<link href="/static/style.css" rel="stylesheet">
<style>
:root {
--bg-primary: #1a1b26;
--bg-secondary: #16161e;
--text-primary: #a9b1d6;
--text-secondary: #565f89;
--accent: #7aa2f7;
--border: #414868;
}
.theme-tokyo-night {
--bg-primary: #1a1b26;
--bg-secondary: #16161e;
--text-primary: #a9b1d6;
--text-secondary: #565f89;
--accent: #7aa2f7;
--border: #414868;
}
.theme-dracula {
--bg-primary: #282a36;
--bg-secondary: #21222c;
--text-primary: #f8f8f2;
--text-secondary: #6272a4;
--accent: #bd93f9;
--border: #44475a;
}
.theme-nord {
--bg-primary: #2e3440;
--bg-secondary: #3b4252;
--text-primary: #eceff4;
--text-secondary: #5e81ac;
--accent: #88c0d0;
--border: #4c566a;
}
.theme-solarized-dark {
--bg-primary: #002b36;
--bg-secondary: #073642;
--text-primary: #93a1a1;
--text-secondary: #586e75;
--accent: #2aa198;
--border: #586e75;
}
.theme-monokai {
--bg-primary: #272822;
--bg-secondary: #3e3d32;
--text-primary: #f8f8f2;
--text-secondary: #75715e;
--accent: #a6e22e;
--border: #49483e;
}
.theme-one-dark-pro {
--bg-primary: #282c34;
--bg-secondary: #21252b;
--text-primary: #abb2bf;
--text-secondary: #5c6370;
--accent: #61dafb;
--border: #3e4451;
}
.theme-material-dark {
--bg-primary: #263238;
--bg-secondary: #37474f;
--text-primary: #eeffff;
--text-secondary: #546e7a;
--accent: #80cbc4;
--border: #455a64;
}
.theme-catppuccin-mocha {
--bg-primary: #1e1e2e;
--bg-secondary: #181825;
--text-primary: #cdd6f4;
--text-secondary: #bac2de;
--accent: #f38ba8;
--border: #313244;
}
.theme-catppuccin-macchiato {
--bg-primary: #24273a;
--bg-secondary: #1e2030;
--text-primary: #cad3f5;
--text-secondary: #b8c0e0;
--accent: #f0c6c6;
--border: #363a4f;
}
.theme-catppuccin-frappe {
--bg-primary: #303446;
--bg-secondary: #292c3c;
--text-primary: #c6d0f5;
--text-secondary: #b5bfe2;
--accent: #f2d5cf;
--border: #414559;
}
.theme-catppuccin-latte {
--bg-primary: #eff1f5;
--bg-secondary: #e6e9ef;
--text-primary: #4c4f69;
--text-secondary: #5c5f77;
--accent: #d20f39;
--border: #bcc0cc;
}
/* Wood themes */
.theme-wood-light {
--bg-primary: rgba(222, 184, 135, 0.9);
--bg-secondary: rgba(210, 166, 121, 0.95);
--text-primary: #2c1810;
--text-secondary: #5a3a2a;
--accent: #8b5a2b;
--border: #6b4423;
}
.theme-wood-dark {
--bg-primary: rgba(139, 115, 85, 0.9);
--bg-secondary: rgba(107, 83, 68, 0.95);
--text-primary: #f5e6d3;
--text-secondary: #d4c4b0;
--accent: #deb887;
--border: #5a4636;
}
.theme-wood-mahogany {
--bg-primary: rgba(160, 82, 45, 0.9);
--bg-secondary: rgba(139, 69, 19, 0.95);
--text-primary: #ffe4c4;
--text-secondary: #daa520;
--accent: #f4a460;
--border: #8b4513;
}
body {
background-color: var(--bg-primary);
color: var(--text-primary);
min-height: 100vh;
transition: background 0.3s ease;
}
.card {
background-color: var(--bg-secondary);
border-color: var(--border);
}
.btn-primary {
background-color: var(--accent);
color: var(--bg-primary);
}
.shelf {
position: relative;
background: linear-gradient(to bottom, transparent 0%, rgba(0, 0, 0, 0.1) 100%);
padding: 2rem 1rem;
margin-bottom: 1rem;
border-radius: 0.5rem;
}
.shelf::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 12px;
background: linear-gradient(to bottom,
rgba(107, 68, 35, 0.3) 0%,
rgba(107, 68, 35, 0.5) 50%,
rgba(107, 68, 35, 0.3) 100%);
border-radius: 0 0 0.5rem 0.5rem;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.book-item {
position: relative;
transition: transform 0.2s ease, box-shadow 0.2s ease;
cursor: pointer;
}
.book-item:hover {
transform: translateY(-8px) rotate(-2deg);
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.3);
z-index: 10;
}
.book-cover {
aspect-ratio: 2/3;
overflow: hidden;
border-radius: 0.25rem;
box-shadow:
2px 2px 4px rgba(0, 0, 0, 0.2),
-1px -1px 2px rgba(255, 255, 255, 0.1) inset;
position: relative;
}
.book-cover::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 4px;
background: linear-gradient(to right,
rgba(0, 0, 0, 0.3) 0%,
rgba(255, 255, 255, 0.1) 50%,
transparent 100%);
}
.library-selector {
margin-bottom: 2rem;
}
.loading-spinner {
display: inline-block;
width: 40px;
height: 40px;
border: 4px solid var(--border);
border-top-color: var(--accent);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
</style>
</head>
<body class="theme-tokyo-night">
@Header(user, "/bookshelf")
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<!-- Library Selector -->
<div class="library-selector">
<div class="mb-8">
<div class="flex flex-col sm:flex-row gap-4 items-center justify-between">
<div class="flex-1 w-full">
<label class="block text-sm font-medium mb-2" style="color: var(--text-secondary)">Select Library</label>
@@ -396,10 +192,11 @@ templ BookShelf(user User) {
let html = '';
shelves.forEach((shelfBooks, index) => {
html += `<div class="shelf">
html += `<div class="relative bg-gradient-to-b from-transparent to-black/10 p-8 mb-4 rounded-lg" style="padding-bottom: 3rem;">
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-4">
${shelfBooks.map(book => renderBookCard(book)).join('')}
</div>
<div class="absolute bottom-0 left-0 right-0 h-3 rounded-b-lg" style="background: linear-gradient(to bottom, rgba(107, 68, 35, 0.3) 0%, rgba(107, 68, 35, 0.5) 50%, rgba(107, 68, 35, 0.3) 100%); box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);"></div>
</div>`;
});
@@ -409,11 +206,12 @@ templ BookShelf(user User) {
function renderBookCard(book) {
const coverUrl = book.cover_image_path || '/static/placeholder-book.svg';
const authorHtml = book.author ? `<p class="text-xs" style="color: var(--text-secondary)">${book.author}</p>` : '';
return `<div class="book-item" onclick="viewBook('${book.id}')">
<div class="book-cover">
<img src="${coverUrl}"
alt="${book.title}"
return `<div class="relative transition-all duration-200 ease hover:-translate-y-2 hover:-rotate-2 hover:shadow-2xl hover:z-10 cursor-pointer" onclick="viewBook('${book.id}')">
<div class="aspect-[2/3] overflow-hidden rounded shadow-[2px_2px_4px_rgba(0,0,0,0.2),-1px_-1px_2px_rgba(255,255,255,0.1)_inset] relative">
<div class="absolute left-0 top-0 bottom-0 w-1" style="background: linear-gradient(to right, rgba(0, 0, 0, 0.3) 0%, rgba(255, 255, 255, 0.1) 50%, transparent 100%);"></div>
<img src="${coverUrl}"
alt="${book.title}"
class="w-full h-full object-cover"
onerror="this.src='/static/placeholder-book.svg'"
>
+23 -55
View File
@@ -13,38 +13,6 @@ templ Conflicts(user User, conflicts []handlers.ConflictDetailResponse, total in
<script src="/static/toast.js"></script>
<link href="/static/style.css" rel="stylesheet">
<script src="/static/theme.js"></script>
<style>
.conflict-card {
border-left: 4px solid #f59e0b;
}
.conflict-resolved {
border-left-color: #10b981;
opacity: 0.7;
}
.progress-bar {
height: 8px;
border-radius: 4px;
background-color: var(--border);
overflow: hidden;
}
.progress-fill {
height: 100%;
background-color: var(--accent);
transition: width 0.3s ease;
}
.device-badge {
display: inline-block;
padding: 2px 8px;
border-radius: 4px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
}
.device-koreader { background-color: #3b82f6; color: white; }
.device-kobo { background-color: #8b5cf6; color: white; }
.device-web { background-color: #10b981; color: white; }
.device-mobile { background-color: #f59e0b; color: white; }
</style>
</head>
<body class="theme-{ user.Theme }">
@Header(user, "/conflicts")
@@ -127,30 +95,30 @@ templ Conflicts(user User, conflicts []handlers.ConflictDetailResponse, total in
</div>
}
for _, conflict := range conflicts {
<div class="conflict-item card p-6 rounded-lg border" data-conflict-id="{ conflict.ID }" style="background-color: var(--bg-secondary); border-color: var(--border); border-left: 4px solid #f59e0b;">
<div class="flex justify-between items-start mb-4">
<div class="flex items-start space-x-4">
<input type="checkbox" class="conflict-checkbox w-5 h-5 rounded mt-1"
data-conflict-id="{ conflict.ID }"
onchange="updateBulkActions()">
<div>
<h3 class="text-lg font-semibold" style="color: var(--text-primary)">{ conflict.MediaItemTitle }</h3>
<p class="text-sm" style="color: var(--text-secondary)">Type: { conflict.ConflictType }</p>
</div>
</div>
<button onclick="showConflictModal('{ conflict.ID }')" class="btn-primary px-4 py-2 rounded-lg">
Resolve
</button>
</div>
<div class="text-sm" style="color: var(--text-secondary)">
Created: { conflict.CreatedAt.Format("2006-01-02 15:04:05") }
if conflict.ResolvedBy != "" {
| Resolved by: { conflict.ResolvedBy }
}
for _, conflict := range conflicts {
<div class="conflict-item card p-6 rounded-lg border" data-conflict-id="{ conflict.ID }" style="background-color: var(--bg-secondary); border-color: var(--border); border-left-width: 4px; border-left-style: solid; border-left-color: rgb(245, 158, 11);">
<div class="flex justify-between items-start mb-4">
<div class="flex items-start space-x-4">
<input type="checkbox" class="conflict-checkbox w-5 h-5 rounded mt-1"
data-conflict-id="{ conflict.ID }"
onchange="updateBulkActions()">
<div>
<h3 class="text-lg font-semibold" style="color: var(--text-primary)">{ conflict.MediaItemTitle }</h3>
<p class="text-sm" style="color: var(--text-secondary)">Type: { conflict.ConflictType }</p>
</div>
</div>
</div>
}
<button onclick="showConflictModal('{ conflict.ID }')" class="btn-primary px-4 py-2 rounded-lg">
Resolve
</button>
</div>
<div class="text-sm" style="color: var(--text-secondary)">
Created: { conflict.CreatedAt.Format("2006-01-02 15:04:05") }
if conflict.ResolvedBy != "" {
| Resolved by: { conflict.ResolvedBy }
}
</div>
</div>
}
</div>
<div id="conflict-modal" class="hidden fixed inset-0 z-50 flex items-center justify-center overflow-y-auto" style="background-color: rgba(0, 0, 0, 0.7);">
-60
View File
@@ -12,66 +12,6 @@ templ Dashboard(user User) {
<script src="/static/header.js"></script>
<script src="/static/search.js"></script>
<link href="/static/style.css" rel="stylesheet">
<style>
:root {
--bg-primary: #1a1b26;
--bg-secondary: #16161e;
--text-primary: #a9b1d6;
--text-secondary: #565f89;
--accent: #7aa2f7;
--border: #414868;
}
.theme-tokyo-night {
--bg-primary: #1a1b26;
--bg-secondary: #16161e;
--text-primary: #a9b1d6;
--text-secondary: #565f89;
--accent: #7aa2f7;
--border: #414868;
}
body {
background-color: var(--bg-primary);
color: var(--text-primary);
}
.card {
background-color: var(--bg-secondary);
border-color: var(--border);
}
.btn-primary {
background-color: var(--accent);
color: var(--bg-primary);
}
.btn-primary:hover {
opacity: 0.8;
}
.rating-star {
display: inline-block;
transition: all 0.2s ease;
cursor: pointer;
position: relative;
}
.rating-star:hover {
transform: scale(1.1);
}
.half-star {
position: relative;
display: inline-block;
}
.half-star::before {
content: '★';
position: absolute;
left: 0;
top: 0;
width: 50%;
overflow: hidden;
color: #facc15; /* yellow-400 */
}
.rating-text {
font-size: 0.875rem;
margin-left: 0.5rem;
opacity: 0.8;
}
</style>
</head>
<body class="theme-tokyo-night">
@Header(user, "/dashboard")