Merge pull request 'feat: redesign site as personal page with Catppuccin theme' (#6) from feat/personal-site-redesign into main
AI Codebase Quality Review / ai-codebase-review (push) Successful in 20s

Reviewed-on: #6
This commit was merged in pull request #6.
This commit is contained in:
2026-01-03 12:05:00 +00:00
3 changed files with 349 additions and 255 deletions
+14 -12
View File
@@ -52,7 +52,7 @@ const fullOgImage = new URL(ogImage, Astro.site).href;
name="keywords"
content="self-hosted, privacy, open-source, furry, developer, cozy, hidden den"
/>
<meta name="theme-color" content="#d4a574" />
<meta name="theme-color" content="#cba6f7" />
<meta name="color-scheme" content="dark" />
<!-- Favicons -->
@@ -86,14 +86,18 @@ const fullOgImage = new URL(ogImage, Astro.site).href;
<style is:global>
:root {
/* Cozy Den Color Palette */
--color-bg: #1a1410;
--color-bg-light: #2a1f18;
--color-text: #f4e9d8;
--color-text-dim: #c4b5a0;
--color-accent: #d4a574;
--color-accent-bright: #e8bf8e;
--color-warm: #8b6f47;
/* Catppuccin Mocha Color Palette */
--color-bg: #1e1e2e;
--color-bg-light: #313244;
--color-surface: #45475a;
--color-text: #cdd6f4;
--color-text-dim: #a6adc8;
--color-accent: #cba6f7;
--color-accent-bright: #f5c2e7;
--color-warm: #f38ba8;
--color-blue: #89b4fa;
--color-green: #a6e3a1;
--color-peach: #fab387;
/* Spacing */
--space-xs: 0.5rem;
@@ -103,9 +107,7 @@ const fullOgImage = new URL(ogImage, Astro.site).href;
--space-xl: 3rem;
/* Typography */
--font-body:
system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
sans-serif;
--font-body: "JetBrains Mono", "Fira Code", "SF Mono", Consolas, monospace;
}
* {
+68 -92
View File
@@ -4,30 +4,29 @@ import BaseLayout from "../layouts/BaseLayout.astro";
<BaseLayout
title="404 - Page Not Found | Hidden Den Cafe"
description="This page doesn't exist in the Hidden Den. Let's get you back to somewhere cozy."
description="This page doesn't exist in the Hidden Den."
>
<div class="matrix-bg" aria-hidden="true"></div>
<main class="not-found-page">
<div class="container">
<div class="card fade-in">
<div class="not-found-content">
<h1 class="error-code">404</h1>
<h2 class="error-title">Lost in the Den?</h2>
<p class="error-message">
Oops! This cozy corner doesn't seem to exist. Maybe it's
still being built, or perhaps you've wandered into a
part of the den that hasn't been opened yet.
</p>
<div class="actions">
<a href="/" class="btn-primary"> Back to Home </a>
<a
href="https://git.hiddenden.cafe"
class="btn-secondary"
target="_blank"
rel="noopener noreferrer"
>
Visit Gitea
</a>
</div>
<div class="content fade-in">
<h1 class="error-code">404</h1>
<div class="divider">══════════════════════════════</div>
<h2 class="error-title">Lost in the Den?</h2>
<p class="error-message">
This corner doesn't exist yet.
</p>
<div class="actions">
<a href="/" class="btn">[Back to Home]</a>
<a
href="https://git.hiddenden.cafe"
class="btn"
target="_blank"
rel="noopener noreferrer"
>
[Gitea]
</a>
</div>
</div>
</div>
@@ -35,6 +34,26 @@ import BaseLayout from "../layouts/BaseLayout.astro";
</BaseLayout>
<style>
.matrix-bg {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
opacity: 0.03;
background:
linear-gradient(var(--color-accent) 1px, transparent 1px),
linear-gradient(90deg, var(--color-accent) 1px, transparent 1px);
background-size: 50px 50px;
animation: grid-move 20s linear infinite;
}
@keyframes grid-move {
0% { transform: translate(0, 0); }
100% { transform: translate(50px, 50px); }
}
.not-found-page {
display: flex;
align-items: center;
@@ -44,45 +63,40 @@ import BaseLayout from "../layouts/BaseLayout.astro";
}
.container {
max-width: 600px;
max-width: 500px;
width: 100%;
}
.card {
background: var(--color-bg-light);
border-radius: 12px;
background: rgba(30, 30, 46, 0.8);
backdrop-filter: blur(10px);
border: 1px solid var(--color-surface);
border-radius: 8px;
padding: var(--space-xl);
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
border: 1px solid rgba(212, 165, 116, 0.1);
text-align: center;
}
.not-found-content {
display: flex;
flex-direction: column;
gap: var(--space-md);
}
.error-code {
font-size: 6rem;
font-size: 5rem;
font-weight: bold;
color: var(--color-accent);
color: var(--color-warm);
line-height: 1;
margin: 0;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
}
.divider {
color: var(--color-surface);
font-size: 0.75rem;
margin: var(--space-md) 0;
user-select: none;
}
.error-title {
font-size: 2rem;
margin: 0;
color: var(--color-accent-bright);
font-size: 1.25rem;
margin: 0 0 var(--space-sm) 0;
color: var(--color-accent);
}
.error-message {
font-size: 1.1rem;
color: var(--color-text-dim);
line-height: 1.6;
margin: 0;
margin: 0 0 var(--space-lg) 0;
}
.actions {
@@ -90,65 +104,27 @@ import BaseLayout from "../layouts/BaseLayout.astro";
gap: var(--space-md);
justify-content: center;
flex-wrap: wrap;
margin-top: var(--space-md);
}
.btn-primary,
.btn-secondary {
display: inline-flex;
align-items: center;
gap: var(--space-xs);
padding: var(--space-sm) var(--space-lg);
border-radius: 8px;
font-weight: 500;
transition: all 0.2s ease;
.btn {
color: var(--color-blue);
text-decoration: none;
transition: color 0.2s ease;
}
.btn-primary {
background: var(--color-accent);
color: var(--color-bg);
}
.btn-primary:hover {
background: var(--color-accent-bright);
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(212, 165, 116, 0.3);
}
.btn-secondary {
background: transparent;
.btn:hover {
color: var(--color-accent-bright);
border: 2px solid var(--color-accent);
}
.btn-secondary:hover {
background: rgba(212, 165, 116, 0.1);
border-color: var(--color-accent-bright);
transform: translateY(-2px);
}
@media (max-width: 768px) {
@media (max-width: 600px) {
.error-code {
font-size: 4rem;
font-size: 3rem;
}
}
.error-title {
font-size: 1.5rem;
}
.error-message {
font-size: 1rem;
}
.actions {
flex-direction: column;
}
.btn-primary,
.btn-secondary {
width: 100%;
justify-content: center;
@media (prefers-reduced-motion: reduce) {
.matrix-bg {
animation: none;
}
}
</style>
+267 -151
View File
@@ -1,203 +1,319 @@
---
import BaseLayout from "../layouts/BaseLayout.astro";
import Card from "../components/Card.astro";
import Section from "../components/Section.astro";
import ServiceItem from "../components/ServiceItem.astro";
---
<BaseLayout
title="Hidden Den Cafe - Cozy Self-Hosted Services"
description="Welcome to Hidden Den Cafe - a warm, self-hosted space where technology meets comfort. Privacy-focused, furry-friendly, and built with love by Latte."
title="Hidden Den Cafe"
description="Latte's cozy corner of the internet. Gay furry wizard, IT enthusiast, and self-hosting advocate."
>
<main id="main-content">
<!-- Hero Section -->
<section class="hero" aria-labelledby="hero-title">
<div class="container">
<div class="hero-content fade-in">
<h1 id="hero-title" class="hero-title">Hidden Den Cafe</h1>
<p class="hero-subtitle">A cozy corner of the internet</p>
<div class="matrix-bg" aria-hidden="true"></div>
<main class="main">
<div class="container">
<!-- Header -->
<header class="header fade-in">
<h1 class="title">Hidden Den Cafe</h1>
<div class="divider">══════════════════════════════</div>
</header>
<!-- About -->
<section class="section fade-in">
<p class="intro">
Hey, I'm <span class="highlight">Latte</span> (he/him).
</p>
<p class="tagline">gay furry wizard</p>
<p class="desc">
IT wizard with a homelab. I believe in self-hosting, privacy,
and keeping control of my own data. Companies don't get to sell
or misuse what's mine.
</p>
</section>
<div class="divider">══════════════════════════════</div>
<!-- Games -->
<section class="section fade-in">
<h2>Games</h2>
<p class="games">
Minecraft, No Man's Sky, Warframe, Cyberpunk 2077
</p>
</section>
<div class="divider">══════════════════════════════</div>
<!-- Projects -->
<section class="section fade-in">
<h2>Projects</h2>
<div class="project">
<h3><a href="https://git.hiddenden.cafe/Hiddenden/openrabbit" target="_blank" rel="noopener noreferrer">OpenRabbit</a></h3>
<p>AI code review system for Gitea with automated PR review, issue triage, interactive chat, and codebase analysis.</p>
</div>
</div>
</section>
<p class="note">More projects coming soon...</p>
</section>
<!-- About Hidden Den Section -->
<Section class="about-den" ariaLabelledby="about-den-heading">
<Card>
<h2 id="about-den-heading">About Hidden Den</h2>
<p>
Welcome to Hidden Den Cafe - a warm, self-hosted space where
technology meets comfort. This is a personal corner of the
internet built with love, care, and a strong belief in
privacy and open-source values.
</p>
<p>
Everything here runs on self-hosted infrastructure, giving
complete control over data and services. No cloud
dependencies, no tracking, just a cozy digital home.
</p>
</Card>
</Section>
<div class="divider">══════════════════════════════</div>
<!-- About Latte Section -->
<Section class="about-me" ariaLabelledby="about-me-heading">
<Card>
<h2 id="about-me-heading">About Me</h2>
<p>
Hey there! I'm Latte, a gay furry developer who loves
building things and being part of the warm, welcoming furry
community. I'm passionate about self-hosting, open-source
software, and creating cozy spaces both online and off.
</p>
<p>
I work primarily with Python and Flask, though I'm always
learning new things (currently exploring JavaScript and the
C stack). When I get those surges of creative energy, I love
diving into new projects - from Reddit downloaders to
Telegram sticker tools, and everything in between.
</p>
<p>Coffee enthusiast • Linux lover • Self-hosting advocate</p>
</Card>
</Section>
<!-- Links -->
<section class="section fade-in">
<h2>Links</h2>
<ul class="links">
<li><a href="https://git.hiddenden.cafe" target="_blank" rel="noopener noreferrer">Gitea</a> — Self-hosted git server</li>
</ul>
</section>
<!-- Services Section -->
<Section class="services" ariaLabelledby="services-heading">
<Card>
<h2 id="services-heading">Services</h2>
<p>Here are the services currently running in the den:</p>
<div class="divider">══════════════════════════════</div>
<div class="service-list" role="list">
<ServiceItem
title="Gitea"
description="Self-hosted Git service for all my projects and code repositories."
url="https://git.hiddenden.cafe"
/>
<ServiceItem
title="More Coming Soon"
description="The den is always growing! More services will be added as they're developed and deployed."
comingSoon={true}
/>
<!-- Donate -->
<section class="section fade-in">
<h2>Support</h2>
<div class="donate">
<div class="crypto">
<span class="crypto-label">[XMR]</span>
<code class="crypto-addr">41uiUeBru8jhtzjQz3M5CKV1uFpern7juStdfveNQS52LQ9aw3mNkdbc8akM81YnxuE2RT9K2Cmyp9cfyi1osrbVBjBbzQ3</code>
</div>
<div class="crypto">
<span class="crypto-label">[ETH]</span>
<code class="crypto-addr">0x3Dfc92458267b91BFa6bF8f6c86bAE809Ab76Cb4</code>
</div>
</div>
</Card>
</Section>
</section>
<!-- Footer -->
<footer class="footer" role="contentinfo">
<div class="container">
<!-- Footer -->
<footer class="footer fade-in">
<div class="divider">══════════════════════════════</div>
<p>Made with love by Latte</p>
<nav aria-label="Footer navigation">
<p class="footer-links">
<a
href="https://git.hiddenden.cafe"
target="_blank"
rel="noopener noreferrer">Gitea</a
>
</p>
</nav>
</div>
</footer>
</footer>
</div>
</main>
<!-- Scroll Animation Script -->
<script>
// Intersection Observer for scroll animations
const observerOptions = {
threshold: 0.1,
rootMargin: "0px 0px -50px 0px",
};
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add("visible");
// Optionally unobserve after animation
observer.unobserve(entry.target);
}
});
}, observerOptions);
// Observe all elements with scroll-animate class
document.addEventListener("DOMContentLoaded", () => {
const animateElements =
document.querySelectorAll(".scroll-animate");
animateElements.forEach((el) => observer.observe(el));
});
</script>
</BaseLayout>
<style>
main {
display: flex;
flex-direction: column;
/* Matrix-style animated background */
.matrix-bg {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
opacity: 0.03;
background:
linear-gradient(var(--color-accent) 1px, transparent 1px),
linear-gradient(90deg, var(--color-accent) 1px, transparent 1px);
background-size: 50px 50px;
animation: grid-move 20s linear infinite;
}
@keyframes grid-move {
0% { transform: translate(0, 0); }
100% { transform: translate(50px, 50px); }
}
.main {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: var(--space-lg) var(--space-md);
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 0 var(--space-md);
max-width: 700px;
width: 100%;
background: rgba(30, 30, 46, 0.8);
backdrop-filter: blur(10px);
border: 1px solid var(--color-surface);
border-radius: 8px;
padding: var(--space-xl);
}
/* Hero Section */
.hero {
padding: var(--space-xl) 0;
.header {
text-align: center;
background: linear-gradient(
135deg,
var(--color-bg) 0%,
var(--color-bg-light) 100%
);
margin-bottom: var(--space-lg);
}
.hero-title {
font-size: 3rem;
.title {
font-size: 2rem;
font-weight: 700;
letter-spacing: 2px;
}
.divider {
color: var(--color-surface);
text-align: center;
font-size: 0.75rem;
margin: var(--space-md) 0;
user-select: none;
}
.section {
margin: var(--space-md) 0;
}
.section h2 {
font-size: 1rem;
text-transform: uppercase;
letter-spacing: 2px;
margin-bottom: var(--space-sm);
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
color: var(--color-accent);
}
.hero-subtitle {
font-size: 1.25rem;
.intro {
font-size: 1.1rem;
margin-bottom: var(--space-xs);
}
.highlight {
color: var(--color-accent-bright);
font-weight: 700;
}
.tagline {
color: var(--color-accent);
font-style: italic;
margin-bottom: var(--space-sm);
}
.desc {
color: var(--color-text-dim);
line-height: 1.7;
}
.games {
color: var(--color-text-dim);
}
/* Service List */
.service-list {
margin-top: var(--space-md);
display: flex;
flex-direction: column;
gap: var(--space-md);
.project {
margin-bottom: var(--space-sm);
}
.project h3 {
font-size: 1rem;
margin-bottom: var(--space-xs);
}
.project h3 a {
color: var(--color-blue);
}
.project h3 a:hover {
color: var(--color-accent-bright);
}
.project p {
color: var(--color-text-dim);
font-size: 0.9rem;
}
.note {
color: var(--color-surface);
font-style: italic;
font-size: 0.85rem;
}
.links {
list-style: none;
}
.links li {
margin-bottom: var(--space-xs);
color: var(--color-text-dim);
}
.links a {
color: var(--color-blue);
}
.links a:hover {
color: var(--color-accent-bright);
}
.donate {
display: flex;
flex-direction: column;
gap: var(--space-sm);
}
.crypto {
display: flex;
flex-wrap: wrap;
align-items: baseline;
gap: var(--space-xs);
}
.crypto-label {
color: var(--color-peach);
font-weight: 700;
font-size: 0.85rem;
}
.crypto-addr {
font-size: 0.7rem;
color: var(--color-text-dim);
word-break: break-all;
background: var(--color-bg);
padding: 2px 6px;
border-radius: 4px;
}
/* Footer */
.footer {
margin-top: auto;
padding: var(--space-lg) 0;
margin-top: var(--space-lg);
text-align: center;
border-top: 1px solid rgba(212, 165, 116, 0.2);
background: var(--color-bg-light);
}
.footer p {
color: var(--color-text-dim);
margin-bottom: var(--space-xs);
font-size: 0.85rem;
}
.footer-links {
display: flex;
justify-content: center;
align-items: center;
gap: var(--space-sm);
/* Animations */
.fade-in {
animation: fadeIn 0.6s ease-out forwards;
opacity: 0;
}
.fade-in:nth-child(1) { animation-delay: 0.1s; }
.fade-in:nth-child(2) { animation-delay: 0.2s; }
.fade-in:nth-child(3) { animation-delay: 0.3s; }
.fade-in:nth-child(4) { animation-delay: 0.4s; }
.fade-in:nth-child(5) { animation-delay: 0.5s; }
.fade-in:nth-child(6) { animation-delay: 0.6s; }
.fade-in:nth-child(7) { animation-delay: 0.7s; }
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Responsive */
@media (max-width: 768px) {
.hero-title {
font-size: 2rem;
@media (max-width: 600px) {
.container {
padding: var(--space-md);
}
.hero-subtitle {
font-size: 1rem;
.title {
font-size: 1.5rem;
}
.divider {
font-size: 0.6rem;
}
}
@media (prefers-reduced-motion: reduce) {
.matrix-bg {
animation: none;
}
.fade-in {
animation: none;
opacity: 1;
}
}
</style>