Skip to content

Commit 0403243

Browse files
committed
feat: visual polish - logo, accent glow, light/dark mode, animations, rich footer
Add repo logo in hero section from plugin.logo. Add accent-colored top border and glow shadow on section cards. Add light/dark mode with OS preference detection, manual toggle, localStorage. Add smooth height animation on section expand/collapse. Richer footer with social links, build date, version, directory link. Add build_date to template context in build_site.py. Made-with: Cursor
1 parent a63472d commit 0403243

File tree

2 files changed

+147
-13
lines changed

2 files changed

+147
-13
lines changed

site-template/build_site.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"""
77

88
import argparse
9+
import datetime
910
import json
1011
import re
1112
import shutil
@@ -195,6 +196,7 @@ def main():
195196
"mcp_tools": mcp_tools,
196197
"mcp_tool_count": len(mcp_tools),
197198
"mcp_grouped": mcp_grouped,
199+
"build_date": datetime.date.today().isoformat(),
198200
}
199201

200202
env = Environment(

site-template/template.html.j2

Lines changed: 145 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
<meta property="og:type" content="website" />
1111
{% if site.ogImage %}<meta property="og:image" content="{{ site.ogImage }}" />{% endif %}
1212
{% if site.favicon %}<link rel="icon" href="{{ site.favicon }}" />{% endif %}
13+
<script>
14+
(function(){var t=localStorage.getItem('theme');if(t==='light')document.documentElement.setAttribute('data-theme','light');else if(t==='dark')document.documentElement.setAttribute('data-theme','dark');})();
15+
</script>
1316
<style>
1417
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 400; font-display: swap; src: url('fonts/inter-regular.woff2') format('woff2'); }
1518
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 500; font-display: swap; src: url('fonts/inter-medium.woff2') format('woff2'); }
@@ -27,30 +30,64 @@
2730
--border: #30363d;
2831
--text: #e6edf3;
2932
--text-dim: #8b949e;
33+
--nav-bg: rgba(13,17,23,0.85);
3034
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
3135
--font-mono: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
3236
--hero-from: {{ site.heroGradientFrom | default('#0d1117') }};
3337
--hero-to: {{ site.heroGradientTo | default('#161b22') }};
3438
}
3539
40+
/* LIGHT THEME (OS preference, no explicit data-theme) */
41+
@media (prefers-color-scheme: light) {
42+
html:not([data-theme="dark"]) {
43+
--bg: #f6f8fa; --bg2: #ffffff; --bg3: #f0f2f5;
44+
--border: #d0d7de; --text: #1f2328; --text-dim: #656d76;
45+
--nav-bg: rgba(255,255,255,0.88);
46+
--hero-from: #f0f2f5; --hero-to: #f6f8fa;
47+
}
48+
html:not([data-theme="dark"]) a { color: var(--accent); }
49+
html:not([data-theme="dark"]) a:hover { color: var(--accent-light); }
50+
html:not([data-theme="dark"]) .hero h1 { background: linear-gradient(135deg, var(--text), var(--accent)); -webkit-background-clip: text; background-clip: text; }
51+
html:not([data-theme="dark"]) .data-table tr:hover td { background: rgba(0,0,0,0.03); }
52+
html:not([data-theme="dark"]) .search-input { background: var(--bg3); }
53+
html:not([data-theme="dark"]) .install-steps code { background: var(--bg3); }
54+
}
55+
/* LIGHT THEME (explicit toggle) */
56+
[data-theme="light"] {
57+
--bg: #f6f8fa; --bg2: #ffffff; --bg3: #f0f2f5;
58+
--border: #d0d7de; --text: #1f2328; --text-dim: #656d76;
59+
--nav-bg: rgba(255,255,255,0.88);
60+
--hero-from: #f0f2f5; --hero-to: #f6f8fa;
61+
}
62+
[data-theme="light"] a { color: var(--accent); }
63+
[data-theme="light"] a:hover { color: var(--accent-light); }
64+
[data-theme="light"] .hero h1 { background: linear-gradient(135deg, var(--text), var(--accent)); -webkit-background-clip: text; background-clip: text; }
65+
[data-theme="light"] .data-table tr:hover td { background: rgba(0,0,0,0.03); }
66+
[data-theme="light"] .search-input { background: var(--bg3); }
67+
[data-theme="light"] .install-steps code { background: var(--bg3); }
68+
3669
html { scroll-behavior: smooth; }
3770
body { font-family: var(--font-sans); background: var(--bg); color: var(--text); line-height: 1.6; min-height: 100vh; }
3871
a { color: var(--accent-light); text-decoration: none; transition: color 0.2s; }
3972
a:hover { color: #fff; }
4073
4174
/* NAV */
42-
.nav { position: sticky; top: 0; z-index: 100; background: rgba(13,17,23,0.85); backdrop-filter: blur(12px); border-bottom: 1px solid var(--border); padding: 0 1.5rem; }
75+
.nav { position: sticky; top: 0; z-index: 100; background: var(--nav-bg); backdrop-filter: blur(12px); border-bottom: 1px solid var(--border); padding: 0 1.5rem; }
4376
.nav-inner { max-width: 1100px; margin: 0 auto; display: flex; align-items: center; justify-content: space-between; height: 56px; }
4477
.nav-brand { font-weight: 700; font-size: 1rem; color: var(--text); white-space: nowrap; }
45-
.nav-links { display: flex; gap: 1.5rem; list-style: none; }
78+
.nav-links { display: flex; gap: 1.5rem; list-style: none; align-items: center; }
4679
.nav-links a { color: var(--text-dim); font-size: 0.875rem; font-weight: 500; transition: color 0.2s; }
4780
.nav-links a:hover, .nav-links a.active { color: var(--accent-light); }
4881
.nav-toggle { display: none; background: none; border: none; color: var(--text); cursor: pointer; padding: 0.5rem; }
4982
.nav-toggle svg { width: 24px; height: 24px; }
83+
.theme-toggle { background: none; border: 1px solid var(--border); border-radius: 6px; color: var(--text-dim); cursor: pointer; padding: 0.25rem 0.5rem; font-size: 0.8125rem; transition: color 0.2s, border-color 0.2s; display: inline-flex; align-items: center; gap: 0.3rem; font-family: var(--font-sans); }
84+
.theme-toggle:hover { color: var(--accent-light); border-color: var(--accent); }
85+
.theme-toggle svg { width: 14px; height: 14px; }
5086
5187
/* HERO */
52-
.hero { text-align: center; padding: 5rem 1.5rem 4.5rem; background: linear-gradient(180deg, var(--hero-from) 0%, var(--hero-to) 70%, var(--bg) 100%); position: relative; }
88+
.hero { text-align: center; padding: 4rem 1.5rem 4.5rem; background: linear-gradient(180deg, var(--hero-from) 0%, var(--hero-to) 70%, var(--bg) 100%); position: relative; }
5389
.hero-inner { max-width: 720px; margin: 0 auto; }
90+
.hero-logo { width: 80px; height: 80px; border-radius: 50%; object-fit: cover; margin-bottom: 1.25rem; box-shadow: 0 4px 24px rgba(0,0,0,0.3); border: 2px solid var(--border); }
5491
.hero h1 { font-size: 2.75rem; font-weight: 700; margin-bottom: 1rem; background: linear-gradient(135deg, var(--text), var(--accent-light)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
5592
.hero p { font-size: 1.125rem; color: var(--text-dim); margin-bottom: 2rem; max-width: 600px; margin-left: auto; margin-right: auto; }
5693
.stats { display: flex; justify-content: center; gap: 2.5rem; flex-wrap: wrap; margin-bottom: 2rem; }
@@ -66,7 +103,7 @@
66103
.content-area { max-width: 1040px; margin: 0 auto; padding: 2rem 1.5rem 0; }
67104
68105
/* SECTIONS */
69-
.section { background: var(--bg2); border: 1px solid var(--border); border-radius: 12px; padding: 1.75rem 2rem; margin-bottom: 1.25rem; }
106+
.section { background: var(--bg2); border: 1px solid var(--border); border-top: 2px solid var(--accent); border-radius: 12px; padding: 1.75rem 2rem; margin-bottom: 1.25rem; box-shadow: 0 -2px 20px color-mix(in srgb, var(--accent) 8%, transparent); }
70107
.section-header { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 1rem; padding-bottom: 0.625rem; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
71108
.section-header h2 { font-size: 1.25rem; font-weight: 700; margin: 0; }
72109
.count { font-size: 0.8125rem; font-weight: 500; color: var(--text-dim); background: var(--bg3); padding: 0.15rem 0.6rem; border-radius: 12px; }
@@ -92,7 +129,10 @@
92129
details.cat-group[open] > summary::before { transform: rotate(90deg); }
93130
details.cat-group > summary:hover { background: var(--bg3); }
94131
details.cat-group > summary .cat-count { font-weight: 400; color: var(--text-dim); font-size: 0.75rem; margin-left: auto; }
95-
details.cat-group > .cat-body { padding: 0.25rem 0 0.5rem; }
132+
details.cat-group > .cat-body { overflow: hidden; }
133+
134+
/* EXPAND ANIMATION */
135+
.cat-body-anim { transition: max-height 0.25s ease, opacity 0.2s ease; overflow: hidden; }
96136
97137
/* TABLE */
98138
.data-table { width: 100%; border-collapse: collapse; font-size: 0.9375rem; }
@@ -122,12 +162,22 @@
122162
.back-to-top svg { width: 20px; height: 20px; }
123163
124164
/* FOOTER */
125-
footer { text-align: center; padding: 2.5rem 1.5rem; color: var(--text-dim); font-size: 0.8125rem; margin-top: 1rem; }
165+
footer { text-align: center; padding: 2rem 1.5rem 2.5rem; color: var(--text-dim); font-size: 0.8125rem; margin-top: 1rem; }
126166
footer a { color: var(--accent-light); }
167+
.footer-inner { max-width: 1040px; margin: 0 auto; }
168+
.footer-links { display: flex; justify-content: center; gap: 1.25rem; margin-bottom: 1rem; flex-wrap: wrap; }
169+
.footer-links a { display: inline-flex; align-items: center; gap: 0.35rem; color: var(--text-dim); transition: color 0.2s; }
170+
.footer-links a:hover { color: var(--accent-light); }
171+
.footer-links svg { width: 16px; height: 16px; }
172+
.footer-divider { width: 60px; height: 2px; background: var(--accent); margin: 0 auto 1rem; border-radius: 1px; opacity: 0.4; }
173+
.footer-meta { display: flex; justify-content: center; gap: 0.5rem; flex-wrap: wrap; font-size: 0.75rem; }
174+
.footer-meta span { opacity: 0.7; }
175+
.footer-directory { margin-top: 0.75rem; font-size: 0.6875rem; opacity: 0.5; }
127176
128177
/* RESPONSIVE */
129178
@media (max-width: 768px) {
130179
.hero h1 { font-size: 2rem; }
180+
.hero-logo { width: 64px; height: 64px; }
131181
.stats { gap: 1.25rem; }
132182
.stat-val { font-size: 1.5rem; }
133183
.nav-links { gap: 0.75rem; }
@@ -136,7 +186,7 @@
136186
}
137187
@media (max-width: 640px) {
138188
.nav-toggle { display: block; }
139-
.nav-links { display: none; position: absolute; top: 56px; left: 0; right: 0; flex-direction: column; background: rgba(13,17,23,0.97); border-bottom: 1px solid var(--border); padding: 1rem 1.5rem; gap: 0.75rem; }
189+
.nav-links { display: none; position: absolute; top: 56px; left: 0; right: 0; flex-direction: column; background: var(--nav-bg); backdrop-filter: blur(12px); border-bottom: 1px solid var(--border); padding: 1rem 1.5rem; gap: 0.75rem; }
140190
.nav-links.open { display: flex; }
141191
.hero { padding: 3rem 1rem 2.5rem; }
142192
.hero h1 { font-size: 1.5rem; }
@@ -172,13 +222,19 @@
172222
{% if mcp_tools %}<li><a href="#mcp-tools">MCP Tools</a></li>{% endif %}
173223
<li><a href="#install">Install</a></li>
174224
<li><a href="{{ site.links.github | default(plugin.repository) }}">GitHub</a></li>
225+
<li>
226+
<button class="theme-toggle" id="themeToggle" aria-label="Toggle theme">
227+
<svg id="themeIcon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>
228+
</button>
229+
</li>
175230
</ul>
176231
</div>
177232
</nav>
178233

179234
<!-- Hero -->
180235
<section class="hero">
181236
<div class="hero-inner">
237+
{% if plugin.logo %}<img class="hero-logo" src="{{ plugin.logo }}" alt="{{ plugin.displayName }} logo" />{% endif %}
182238
<h1>{{ plugin.displayName }}</h1>
183239
<p>{{ plugin.description }}</p>
184240
<div class="stats">
@@ -342,11 +398,35 @@
342398

343399
<!-- Footer -->
344400
<footer>
345-
<p>
346-
Built by <a href="{{ plugin.author.url | default('https://github.com/TMHSDigital') }}">{{ plugin.author.name | default('TMHSDigital') }}</a>
347-
&middot; {{ plugin.license | default('CC-BY-NC-ND-4.0') }}
348-
&middot; v{{ plugin.version | default('0.0.0') }}
349-
</p>
401+
<div class="footer-inner">
402+
<div class="footer-links">
403+
<a href="{{ site.links.github | default(plugin.repository) }}">
404+
<svg viewBox="0 0 16 16" fill="currentColor"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg>
405+
GitHub
406+
</a>
407+
{% if site.links.npm %}
408+
<a href="{{ site.links.npm }}">
409+
<svg viewBox="0 0 16 16" fill="currentColor"><path d="M0 0v16h16V0H0zm13 13H8V5H5v8H3V3h10v10z"/></svg>
410+
npm
411+
</a>
412+
{% endif %}
413+
<a href="https://tmhsdigital.github.io/Developer-Tools-Directory/">
414+
<svg viewBox="0 0 16 16" fill="currentColor"><path d="M1.5 1.75V13.5h13.05l.001-11.75H1.5zM0 1.75C0 .784.784 0 1.75 0h12.5C15.216 0 16 .784 16 1.75v11.5A1.75 1.75 0 0114.25 15H1.75A1.75 1.75 0 010 13.25V1.75zM9.22 3.72a.75.75 0 000 1.06L10.69 6.25H6.75a.75.75 0 000 1.5h3.94l-1.47 1.47a.75.75 0 101.06 1.06l2.75-2.75a.75.75 0 000-1.06L10.28 3.72a.75.75 0 00-1.06 0z"/></svg>
415+
All Tools
416+
</a>
417+
</div>
418+
<div class="footer-divider"></div>
419+
<div class="footer-meta">
420+
<span>v{{ plugin.version | default('0.0.0') }}</span>
421+
<span>&middot;</span>
422+
<span>{{ plugin.license | default('CC-BY-NC-ND-4.0') }}</span>
423+
<span>&middot;</span>
424+
<span>Built {{ build_date }}</span>
425+
</div>
426+
<div class="footer-directory">
427+
Part of <a href="https://tmhsdigital.github.io/Developer-Tools-Directory/">TMHSDigital Developer Tools</a>
428+
</div>
429+
</div>
350430
</footer>
351431

352432
<!-- Toast -->
@@ -370,6 +450,37 @@
370450
};
371451
})();
372452
453+
/* Theme toggle (dark / light / auto) */
454+
(function () {
455+
var btn = document.getElementById('themeToggle');
456+
var icon = document.getElementById('themeIcon');
457+
var sunSvg = '<circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>';
458+
var moonSvg = '<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>';
459+
var autoSvg = '<circle cx="12" cy="12" r="10"/><path d="M12 2a10 10 0 0 1 0 20V2z"/>';
460+
var states = ['dark', 'light', 'auto'];
461+
function getState() { return localStorage.getItem('theme') || 'auto'; }
462+
function apply(state) {
463+
if (state === 'light') { document.documentElement.setAttribute('data-theme', 'light'); }
464+
else if (state === 'dark') { document.documentElement.setAttribute('data-theme', 'dark'); }
465+
else { document.documentElement.removeAttribute('data-theme'); }
466+
if (state === 'auto') localStorage.removeItem('theme');
467+
else localStorage.setItem('theme', state);
468+
updateIcon(state);
469+
}
470+
function updateIcon(state) {
471+
if (state === 'light') icon.innerHTML = sunSvg;
472+
else if (state === 'dark') icon.innerHTML = moonSvg;
473+
else icon.innerHTML = autoSvg;
474+
btn.title = 'Theme: ' + state;
475+
}
476+
btn.addEventListener('click', function () {
477+
var cur = getState();
478+
var next = states[(states.indexOf(cur) + 1) % states.length];
479+
apply(next);
480+
});
481+
updateIcon(getState());
482+
})();
483+
373484
/* Animated stat counters */
374485
(function () {
375486
var prefersReduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
@@ -459,7 +570,7 @@
459570
});
460571
})();
461572
462-
/* Section collapse/expand toggles (Skills, Rules) */
573+
/* Section collapse/expand toggles */
463574
(function () {
464575
document.querySelectorAll('.toggle-section').forEach(function (btn) {
465576
var targetId = btn.getAttribute('data-target');
@@ -471,6 +582,27 @@
471582
});
472583
})();
473584
585+
/* Smooth expand/collapse animation */
586+
(function () {
587+
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
588+
document.querySelectorAll('details.cat-group').forEach(function (det) {
589+
var body = det.querySelector('.cat-body');
590+
if (!body) return;
591+
det.addEventListener('toggle', function () {
592+
if (det.open) {
593+
body.style.maxHeight = '0';
594+
body.style.opacity = '0';
595+
body.classList.add('cat-body-anim');
596+
requestAnimationFrame(function () {
597+
body.style.maxHeight = body.scrollHeight + 'px';
598+
body.style.opacity = '1';
599+
});
600+
setTimeout(function () { body.style.maxHeight = 'none'; body.classList.remove('cat-body-anim'); }, 260);
601+
}
602+
});
603+
});
604+
})();
605+
474606
/* Scroll spy */
475607
(function () {
476608
var links = document.querySelectorAll('.nav-links a[href^="#"]');

0 commit comments

Comments
 (0)