Merge pull request 'docs/future-enhancement-plan' (#4) from docs/future-enhancement-plan into dev
Enterprise AI Code Review / ai-review (pull_request) Successful in 43s

Reviewed-on: #4
This commit was merged in pull request #4.
This commit is contained in:
2026-01-01 14:04:01 +00:00
13 changed files with 6943 additions and 541 deletions
+590
View File
@@ -0,0 +1,590 @@
# Cozy Den - Future Enhancement Plan
## Executive Summary
This plan outlines potential future enhancements for the Cozy Den landing page (hiddenden.cafe). The current site is well-built with a solid foundation - clean Astro architecture, cozy coffee-themed aesthetic, Docker deployment, and strong accessibility. This document organizes future possibilities by priority and complexity.
---
## Current State Assessment
**Strengths:**
- ✅ Clean, minimal Astro structure with proper TypeScript configuration
- ✅ Strong accessibility (ARIA labels, semantic HTML, keyboard navigation)
- ✅ Production-ready Docker deployment with Nginx
- ✅ Responsive design with mobile support
- ✅ Custom 404 page with themed styling
- ✅ SEO foundations (sitemap, robots.txt, meta tags)
- ✅ Privacy-first (no tracking, no external dependencies)
- ✅ Well-documented (CLAUDE.md, DEVELOPMENT.md, PROJECT_CONTEXT.md)
**Current Limitations:**
- Single-page site (only 2 routes: index, 404)
- No dynamic content (all hardcoded)
- No reusable components (empty components directory)
- Limited animations (only fade-in on load)
- No image optimization system
- Static service list with "More Coming Soon" placeholder
- Missing OG image for social sharing
- No form handling for contact/donations
---
## Phase 1: Quick Wins (Low Effort, High Impact)
### 1.1 Visual Polish
**Effort:** 1-2 hours | **Impact:** Medium
- **Add social sharing image** - Create og-image.png (1200x630px) with coffee theme
- **Favicon variants** - Add apple-touch-icon.png, favicon-16x16.png, favicon-32x32.png
- **Scroll animations** - Implement Intersection Observer for sections to fade/slide in on scroll
- **Hover effects** - Add subtle transitions for links, buttons, and cards
- **Background texture** - Subtle coffee stain or paper texture overlay on background
**Files to modify:**
- `public/` (add new image assets)
- `src/layouts/BaseLayout.astro` (add meta tags for OG image, favicon variants)
- `src/pages/index.astro` (add scroll animation JavaScript)
### 1.2 Content Updates
**Effort:** 30 minutes | **Impact:** High
- **Update service list** - Replace "More Coming Soon" with actual services
- **Add donation/payment links** - Ko-fi, Liberapay, or crypto addresses
- **Personalize About Me** - Review and update personal section
- **Add social links** - Mastodon, GitHub, etc. if desired
**Files to modify:**
- `src/pages/index.astro` (update content in Services and Support sections)
### 1.3 Component Extraction
**Effort:** 2-3 hours | **Impact:** Medium (code quality)
Extract repeated patterns into reusable Astro components:
- **ServiceItem.astro** - Reusable service card component
- **SupportItem.astro** - Reusable support/help card component
- **Card.astro** - Generic card wrapper component
- **Section.astro** - Section wrapper with consistent spacing
**Files to create:**
- `src/components/ServiceItem.astro`
- `src/components/SupportItem.astro`
- `src/components/Card.astro`
- `src/components/Section.astro`
**Files to modify:**
- `src/pages/index.astro` (replace hardcoded HTML with component calls)
---
## Phase 2: Medium Complexity Enhancements
### 2.1 Blog Implementation
**Effort:** 1-2 days | **Impact:** High
Implement a blog using Astro Content Collections for project updates, tutorials, and thoughts.
**Features:**
- Markdown-based blog posts with frontmatter (title, date, tags, description)
- Blog listing page with pagination
- Individual post pages with reading time estimate
- Tag filtering system
- RSS feed generation
- Code syntax highlighting with Shiki
- Consistent coffee theme styling
**Files to create:**
- `src/content/config.ts` (Content Collections schema)
- `src/content/blog/*.md` (blog posts)
- `src/pages/blog/index.astro` (blog listing)
- `src/pages/blog/[slug].astro` (individual post template)
- `src/pages/blog/tags/[tag].astro` (tag filtering)
- `src/pages/rss.xml.js` (RSS feed)
- `src/components/BlogCard.astro` (blog post preview card)
**Files to modify:**
- `astro.config.mjs` (enable Content Collections)
- `package.json` (add @astrojs/rss if needed)
- `src/pages/index.astro` (add link to blog section)
### 2.2 Projects Showcase
**Effort:** 2-3 days | **Impact:** High
Create a projects page showcasing your work, optionally pulling from Gitea API.
**Features:**
- Projects grid/list with filtering by language/topic
- Project cards with description, tech stack, links
- Option to manually curate or pull from Gitea API
- Search functionality
- "Featured projects" section
**Implementation Options:**
1. **Static** - Manually maintain project list in JSON/frontmatter
2. **Dynamic** - Fetch from Gitea API at build time (SSG)
3. **Hybrid** - Curated featured projects + API for rest
**Files to create:**
- `src/pages/projects.astro` (projects listing)
- `src/components/ProjectCard.astro` (project preview)
- `src/utils/gitea.ts` (Gitea API integration if dynamic)
- `src/data/projects.json` (static project data if manual)
**Files to modify:**
- `src/pages/index.astro` (add link to projects page)
### 2.3 Contact Form
**Effort:** 2-4 days | **Impact:** Medium
Self-hosted contact form with server-side handling (no third-party services).
**Implementation Options:**
1. **Astro SSR + Email** - Use Astro SSR mode with nodemailer
2. **External API** - Simple Node.js/Python microservice on separate port
3. **Static form service** - Self-hosted FormSpree alternative
**Features:**
- Name, email, message fields with validation
- Spam protection (honeypot field, rate limiting)
- Email notification to your address
- Success/error feedback to user
- Maintains privacy-first approach (no tracking)
**Files to create:**
- `src/pages/contact.astro` (contact page)
- `src/pages/api/contact.ts` (API endpoint if using SSR)
- Or separate microservice in `/contact-service/`
**Configuration changes:**
- May need to switch Astro to SSR mode or hybrid mode
- Docker setup updates for email service
### 2.4 Theme Toggle
**Effort:** 1-2 days | **Impact:** Medium
Add light/dark theme toggle or alternate color schemes.
**Implementation:**
- Toggle button in header/footer
- LocalStorage persistence
- CSS custom property switching
- Smooth transition animations
- Respect `prefers-color-scheme` system preference
- Alternate schemes: light mode, extra dark, seasonal themes
**Files to modify:**
- `src/layouts/BaseLayout.astro` (add theme toggle script, alternate color variables)
- `src/pages/index.astro` (add toggle button)
- Consider creating `src/utils/theme.ts` for theme management
---
## Phase 3: Advanced Features
### 3.1 Service Status Dashboard
**Effort:** 3-5 days | **Impact:** Medium-High
Dashboard showing status of self-hosted services with uptime monitoring.
**Features:**
- Real-time or periodic status checks
- Uptime percentage display
- Incident history
- Visual indicators (green/yellow/red)
- Manual status override capability
**Implementation:**
- Build-time health checks for SSG
- Or client-side status checks (fetch API)
- Or separate monitoring service (self-hosted Uptime Kuma integration)
**Files to create:**
- `src/pages/status.astro` (status dashboard)
- `src/components/ServiceStatus.astro` (status indicator)
- `src/utils/healthCheck.ts` (service health checking)
### 3.2 Art/Badge Gallery
**Effort:** 2-4 days | **Impact:** Medium
Showcase furry art, badges, stickers, or commission work.
**Features:**
- Grid layout with lightbox modal
- Categories/tags for filtering
- Artist attribution and links
- Image optimization with Astro Image component
- Lazy loading for performance
- Alt text for accessibility
**Files to create:**
- `src/pages/gallery.astro` (gallery page)
- `src/components/Gallery.astro` (grid component)
- `src/components/Lightbox.astro` (modal viewer)
- `public/gallery/` (image storage)
**Configuration:**
- Add @astrojs/image integration
- Optimize build for image processing
### 3.3 Interactive Hero Section
**Effort:** 2-3 days | **Impact:** Medium
Enhanced hero section with animated coffee cup, steam, or other cozy elements.
**Features:**
- Animated SVG coffee cup with steam
- Parallax scrolling effect
- Subtle particle effects (coffee beans, steam wisps)
- Time-based greeting (morning/evening)
- Optional: cursor trail effect (paw prints)
**Implementation:**
- SVG animations with CSS or GSAP
- Minimal JavaScript for interactivity
- Maintain performance (no heavy libraries)
**Files to modify:**
- `src/pages/index.astro` (enhance hero section)
- `public/` (add animated SVG assets)
### 3.4 Guest Book / Community Section
**Effort:** 4-7 days | **Impact:** Medium
Community guest book for visitors to leave messages.
**Features:**
- Message submission form
- Moderation queue (approve before publishing)
- Optional: user avatars or default furry icons
- Pagination
- Rate limiting and spam protection
- Self-hosted storage (SQLite or JSON files)
**Implementation:**
- Requires backend (Astro SSR or separate microservice)
- Database: SQLite, PostgreSQL, or JSON files
- Admin interface for moderation
- Consider: WebMentions integration for federated comments
**Files to create:**
- `src/pages/guestbook.astro` (guest book display)
- `src/pages/api/guestbook.ts` (API endpoints)
- `src/pages/admin/guestbook.astro` (moderation interface)
- Database schema and migration scripts
### 3.5 Privacy-Friendly Analytics
**Effort:** 2-4 days | **Impact:** Low-Medium
Self-hosted analytics to understand traffic without compromising privacy.
**Options:**
1. **Plausible Analytics** (self-hosted)
2. **GoAccess** (log analysis)
3. **Umami** (self-hosted, lightweight)
4. **Custom solution** (minimal logging in Nginx)
**Features:**
- Page view counts
- Referrer tracking
- No cookies or personal data
- Public dashboard option
- No IP address storage
**Implementation:**
- Deploy separate analytics service in Docker
- Add tracking script to BaseLayout
- Create dashboard page or embed analytics UI
---
## Phase 4: Long-Term Vision
### 4.1 Self-Hosting Hub
**Effort:** Ongoing | **Impact:** High (community value)
Transform site into a resource hub for self-hosting enthusiasts.
**Features:**
- Tutorial blog posts (Docker, Nginx, services)
- Docker Compose examples repository
- Service recommendations and reviews
- Troubleshooting guides
- Link directory of self-hosting resources
**Content needed:**
- Write tutorials based on your experience
- Document your homelab setup
- Create reusable Docker configurations
- Build community through shared knowledge
### 4.2 Multilingual Support (i18n)
**Effort:** 3-5 days + translation time | **Impact:** Medium
Add support for multiple languages.
**Implementation:**
- Use Astro's i18n routing
- Translation files (JSON or YAML)
- Language switcher component
- SEO considerations (hreflang tags)
**Languages to consider:**
- English (current)
- Additional languages based on audience
### 4.3 Furry Community Features
**Effort:** Variable | **Impact:** Medium (niche audience)
Features specifically for furry community engagement.
**Ideas:**
- Fursona information page
- Commission status/prices page
- Convention schedule/attendance
- Furry resource links
- Badge collection showcase
- Art trades or collaborations section
### 4.4 Seasonal Themes
**Effort:** 2-3 days per theme | **Impact:** Low-Medium (delight factor)
Automatic theme changes based on season or holidays.
**Themes:**
- Fall: Orange/brown palette, falling leaves animation
- Winter: Cool tones, snow particles
- Spring: Pastel colors, flower accents
- Summer: Bright colors, sunny vibes
- Special: Pride month, Halloween, winter holidays
**Implementation:**
- CSS custom property switching
- JavaScript date detection
- Optional manual theme selector
- Maintain core cozy aesthetic across all themes
### 4.5 API Integrations
**Effort:** Variable | **Impact:** Medium
Integrate with various services you self-host.
**Possible integrations:**
- **Gitea**: Recent commits, repository stats, contribution graph
- **Mastodon**: Recent toots, follower count (if you run instance)
- **Media server**: Recently watched/listened
- **RSS reader**: Shared articles or reading list
- **Bookmarks**: Public bookmark collection
---
## Phase 5: Performance & Code Quality
### 5.1 Performance Optimization
**Effort:** 2-3 days | **Impact:** Medium
Optimize for speed and efficiency.
**Tasks:**
- Implement lazy loading for images (when added)
- Add preload/prefetch hints for critical resources
- Optimize SVG assets (SVGO)
- Implement service worker for offline support
- Critical CSS inlining
- Font loading optimization (if custom fonts added)
- Reduce bundle size analysis
**Tools:**
- Lighthouse audits
- WebPageTest
- Bundle analyzer
### 5.2 Testing Infrastructure
**Effort:** 3-5 days | **Impact:** Medium (quality)
Add automated testing as site grows.
**Testing types:**
- **Unit tests**: Component logic (Vitest)
- **Integration tests**: Page rendering
- **E2E tests**: User flows (Playwright)
- **Accessibility tests**: axe-core automated checks
- **Visual regression**: Screenshot comparisons
**Files to create:**
- `tests/` directory structure
- `vitest.config.ts` or `playwright.config.ts`
- CI/CD pipeline configuration
### 5.3 Documentation Expansion
**Effort:** Ongoing | **Impact:** Medium
Enhance documentation for contributors and future maintainability.
**Documentation needs:**
- Inline code comments for complex logic
- API documentation (if backend added)
- Component usage examples
- Deployment troubleshooting guide
- Contributing guidelines
- Architecture decision records (ADRs)
---
## Implementation Priority Matrix
### High Priority, Quick Wins
1. ✅ Add social sharing OG image
2. ✅ Update service list content
3. ✅ Add donation/payment links
4. ✅ Implement scroll animations
5. ✅ Add favicon variants
### High Priority, Medium Effort
1. ✅ Blog implementation (Content Collections)
2. ✅ Projects showcase page
3. ✅ Component extraction (ServiceItem, Card, etc.)
### Medium Priority, Nice to Have
1. ✅ Contact form
2. ✅ Theme toggle
3. ✅ Service status dashboard
4. ✅ Art/badge gallery
### Low Priority, Future Exploration
1. ✅ Guest book / community features
2. ✅ Privacy-friendly analytics
3. ✅ Multilingual support
4. ✅ Seasonal themes
---
## Technical Considerations
### Astro Mode Decision
Current: **Static Site Generation (SSG)**
Consider switching to **Hybrid** or **Server (SSR)** if adding:
- Contact forms
- Guest book
- Real-time service status
- User authentication
**Trade-offs:**
- SSG: Fast, cheap hosting, better privacy, limited interactivity
- SSR: Dynamic features, server costs, more complexity
- Hybrid: Best of both (some pages static, some dynamic)
### Database Decisions
If dynamic features are added:
**Options:**
- **SQLite**: Simple, file-based, good for small-medium traffic
- **PostgreSQL**: Robust, scalable, self-hostable
- **JSON files**: Simplest, version-controllable, limited scale
- **Redis**: Fast, good for caching and simple data
**Recommendation:** Start with SQLite or JSON, migrate to PostgreSQL if traffic grows.
### Deployment Architecture Evolution
**Current:** Static site → Nginx container
**Future options:**
- **Hybrid**: Astro SSR + Nginx reverse proxy
- **Microservices**: Static site + separate API services
- **Monolith**: Astro SSR handling everything
**Recommendation:** Stick with static as long as possible, add microservices for specific dynamic needs.
---
## Resource Estimates
### Time Investment by Phase
- **Phase 1 (Quick Wins):** 3-5 hours total
- **Phase 2 (Medium):** 1-2 weeks part-time
- **Phase 3 (Advanced):** 2-4 weeks part-time
- **Phase 4 (Long-term):** Ongoing, months
- **Phase 5 (Quality):** 1 week part-time
### Infrastructure Costs
Current: Minimal (static hosting)
Potential additions:
- Blog: No additional cost (static)
- Contact form: May need email service (self-hosted = free)
- Database: SQLite = free, PostgreSQL = minimal RAM
- Analytics: Self-hosted = minimal resources
- Guest book: Requires backend (modest VPS resources)
---
## Next Steps & Recommendations
### Immediate Actions (This Week)
1. **Create OG image** - Design 1200x630px social sharing image with coffee theme
2. **Update service list** - Replace placeholder with actual services
3. **Add donation links** - Set up Ko-fi/Liberapay and add to Support section
4. **Scroll animations** - Implement Intersection Observer for section reveals
### Short Term (This Month)
1. **Extract components** - Refactor ServiceItem and SupportItem into reusable components
2. **Plan blog structure** - Decide on categories, write first 2-3 posts
3. **Implement blog** - Set up Content Collections and blog pages
4. **Favicon variants** - Generate and add multiple favicon sizes
### Medium Term (Next 3 Months)
1. **Projects showcase** - Decide on Gitea API integration or manual curation
2. **Contact form** - Evaluate SSR vs microservice approach
3. **Theme toggle** - Implement light/dark mode switching
4. **Service status** - Add health checks for self-hosted services
### Long Term (6-12 Months)
1. **Community features** - Guest book or comment system if desired
2. **Analytics** - Self-hosted Plausible or Umami instance
3. **Content creation** - Regular blog posts, tutorials, documentation
4. **Seasonal themes** - Create at least 2 alternate seasonal palettes
---
## Success Metrics
Track progress with these goals:
**Technical:**
- ✅ Lighthouse score >95 (all categories)
- ✅ Zero accessibility violations (axe-core)
- ✅ Build time <30 seconds
- ✅ Page load time <2 seconds
**Content:**
- ✅ 10+ blog posts published
- ✅ 5+ projects showcased
- ✅ All services documented
- ✅ Complete personal information
**Community (if applicable):**
- ✅ Guest book messages
- ✅ GitHub stars/forks
- ✅ Visitor traffic (privacy-respecting measurement)
- ✅ Positive feedback
---
## Conclusion
Cozy Den has a solid foundation and many exciting possibilities for growth. The beauty of this project is its flexibility - you can implement features at your own pace based on your needs and available time.
**Core Philosophy to Maintain:**
- ✅ Cozy, warm aesthetic (coffee theme)
- ✅ Privacy-first (no tracking, self-hosted)
- ✅ Lightweight (fast loading, minimal bloat)
- ✅ Accessible (WCAG compliance)
- ✅ Personal (authentic, not corporate)
**Recommended Approach:**
Start with Phase 1 quick wins to polish the existing site, then move to Phase 2 based on what excites you most (blog for writing, projects for showcasing work, contact form for engagement). Don't feel pressured to implement everything - pick what adds value to your goals.
This is your cozy corner of the internet. Build it in a way that makes you happy! ☕🦊
+5506
View File
File diff suppressed because it is too large Load Diff
+51
View File
@@ -0,0 +1,51 @@
# Favicon Generation Instructions
The following favicon files are referenced in the site but need to be generated:
- `favicon-16x16.png` - 16x16px PNG favicon
- `favicon-32x32.png` - 32x32px PNG favicon
- `apple-touch-icon.png` - 180x180px PNG for Apple devices
- `og-image.png` - 1200x630px PNG for social media sharing (optional, currently using SVG)
## How to Generate
### Option 1: Use an Online Tool
Visit https://realfavicongenerator.net/ and upload your source image (coffee emoji or custom design).
### Option 2: Use ImageMagick (if available)
If you have a source PNG image:
```bash
# 16x16
convert source.png -resize 16x16 favicon-16x16.png
# 32x32
convert source.png -resize 32x32 favicon-32x32.png
# Apple touch icon
convert source.png -resize 180x180 apple-touch-icon.png
# OG image
convert source.png -resize 1200x630 og-image.png
```
### Option 3: Use the SVG favicon
The current `favicon.svg` (coffee emoji) works in modern browsers. The PNG variants are fallbacks for older browsers and specific platforms.
## Current Status
-`favicon.svg` - Exists (coffee emoji)
-`favicon-16x16.png` - Needs to be created
-`favicon-32x32.png` - Needs to be created
-`apple-touch-icon.png` - Needs to be created
-`og-image.svg` - Created (SVG placeholder)
- ⚠️ `og-image.png` - Optional (browsers support SVG, but PNG is more compatible)
## Design Recommendations
Use the cozy coffee theme colors:
- Background: `#1a1410` (dark coffee)
- Accent: `#e8bf8e` (light coffee/cream)
- Text: `#f4e9d8` (cream)
The favicon should be simple and recognizable at small sizes. The coffee emoji ☕ is perfect!
+2 -1
View File
@@ -1,4 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<rect width="100" height="100" fill="#1a1410"/> <rect width="100" height="100" fill="#1a1410"/>
<text x="50" y="70" font-size="60" text-anchor="middle" fill="#d4a574"></text> <circle cx="50" cy="50" r="35" fill="#d4a574"/>
<circle cx="50" cy="50" r="25" fill="#2a1f18"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 204 B

After

Width:  |  Height:  |  Size: 220 B

+30
View File
@@ -0,0 +1,30 @@
<svg width="1200" height="630" xmlns="http://www.w3.org/2000/svg">
<!-- Background gradient -->
<defs>
<linearGradient id="bgGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#1a1410;stop-opacity:1" />
<stop offset="100%" style="stop-color:#2a1f18;stop-opacity:1" />
</linearGradient>
</defs>
<!-- Background -->
<rect width="1200" height="630" fill="url(#bgGradient)"/>
<!-- Coffee cup emoji (approximate) -->
<text x="600" y="250" font-size="120" text-anchor="middle"></text>
<!-- Title -->
<text x="600" y="380" font-family="system-ui, sans-serif" font-size="72" font-weight="bold" fill="#e8bf8e" text-anchor="middle">
Hidden Den Cafe
</text>
<!-- Subtitle -->
<text x="600" y="450" font-family="system-ui, sans-serif" font-size="36" fill="#c4b5a0" text-anchor="middle">
A cozy corner of the internet
</text>
<!-- Bottom text -->
<text x="600" y="550" font-family="system-ui, sans-serif" font-size="24" fill="#8b6f47" text-anchor="middle">
Self-hosted • Privacy-focused • Furry-friendly
</text>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+32
View File
@@ -0,0 +1,32 @@
{
"name": "Hidden Den Cafe",
"short_name": "Hidden Den",
"description": "A cozy, self-hosted corner of the internet",
"icons": [
{
"src": "/favicon.svg",
"sizes": "any",
"type": "image/svg+xml"
},
{
"src": "/favicon-16x16.png",
"sizes": "16x16",
"type": "image/png"
},
{
"src": "/favicon-32x32.png",
"sizes": "32x32",
"type": "image/png"
},
{
"src": "/apple-touch-icon.png",
"sizes": "180x180",
"type": "image/png"
}
],
"theme_color": "#d4a574",
"background_color": "#1a1410",
"display": "standalone",
"start_url": "/",
"orientation": "portrait"
}
+74
View File
@@ -0,0 +1,74 @@
---
interface Props {
class?: string;
}
const { class: className } = Astro.props;
---
<div class:list={["card", "scroll-animate", className]}>
<slot />
</div>
<style>
.card {
background: var(--color-bg-light);
border-radius: 12px;
padding: var(--space-lg);
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
border: 1px solid rgba(212, 165, 116, 0.1);
transition: box-shadow 0.3s ease;
}
.card:hover {
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.4);
}
.card :global(h2) {
font-size: 2rem;
margin-bottom: var(--space-md);
}
.card :global(p) {
margin-bottom: var(--space-md);
color: var(--color-text);
}
.card :global(p:last-child) {
margin-bottom: 0;
}
/* Scroll animation states */
.scroll-animate {
opacity: 0;
transform: translateY(30px);
transition: opacity 0.6s ease-out, transform 0.6s ease-out;
}
.scroll-animate.visible {
opacity: 1;
transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
.card {
transition: none;
}
.card:hover {
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
}
.scroll-animate {
opacity: 1;
transform: none;
transition: none;
}
}
@media (max-width: 768px) {
.card :global(h2) {
font-size: 1.5rem;
}
}
</style>
+27
View File
@@ -0,0 +1,27 @@
---
interface Props {
class?: string;
ariaLabelledby?: string;
}
const { class: className, ariaLabelledby } = Astro.props;
---
<section class:list={["section", className]} aria-labelledby={ariaLabelledby}>
<div class="container">
<slot />
</div>
</section>
<style>
.section {
padding: var(--space-lg) 0;
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 0 var(--space-md);
width: 100%;
}
</style>
+68
View File
@@ -0,0 +1,68 @@
---
interface Props {
title: string;
description: string;
url?: string;
comingSoon?: boolean;
}
const { title, description, url, comingSoon = false } = Astro.props;
---
<div class="service-item" class:list={{ "coming-soon": comingSoon }} role="listitem">
<h3>
{url ? (
<a
href={url}
target="_blank"
rel="noopener noreferrer"
aria-label={`Visit ${title}`}
>
{title}
</a>
) : (
title
)}
</h3>
<p>{description}</p>
</div>
<style>
.service-item {
background: var(--color-bg);
padding: var(--space-md);
border-radius: 8px;
border-left: 4px solid var(--color-accent);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.service-item:hover {
transform: translateX(4px);
box-shadow: 0 4px 8px rgba(212, 165, 116, 0.2);
}
.service-item.coming-soon {
border-left-color: var(--color-warm);
opacity: 0.8;
}
.service-item h3 {
margin-bottom: var(--space-xs);
font-size: 1.25rem;
}
.service-item p {
color: var(--color-text-dim);
margin: 0;
}
@media (prefers-reduced-motion: reduce) {
.service-item {
transition: none;
}
.service-item:hover {
transform: none;
}
}
</style>
+58
View File
@@ -0,0 +1,58 @@
---
interface Props {
title: string;
description: string;
comingSoonNote?: string;
}
const { title, description, comingSoonNote } = Astro.props;
---
<div class="support-item" role="listitem">
<h3>{title}</h3>
<p>
{description}
{comingSoonNote && (
<span class="coming-soon-text">({comingSoonNote})</span>
)}
</p>
</div>
<style>
.support-item {
background: var(--color-bg);
padding: var(--space-md);
border-radius: 8px;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.support-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(212, 165, 116, 0.15);
}
.support-item h3 {
margin-bottom: var(--space-xs);
font-size: 1.25rem;
}
.support-item p {
color: var(--color-text-dim);
margin: 0;
}
.coming-soon-text {
font-style: italic;
color: var(--color-warm);
}
@media (prefers-reduced-motion: reduce) {
.support-item {
transition: none;
}
.support-item:hover {
transform: none;
}
}
</style>
+33 -6
View File
@@ -10,7 +10,7 @@ const {
title, title,
description = "Hidden Den Cafe - A cozy, self-hosted corner of the internet. Privacy-focused, furry-friendly, and built with love.", description = "Hidden Den Cafe - A cozy, self-hosted corner of the internet. Privacy-focused, furry-friendly, and built with love.",
ogImage = "/og-image.png", ogImage = "/og-image.png",
canonicalURL = Astro.url.pathname canonicalURL = Astro.url.pathname,
} = Astro.props; } = Astro.props;
const fullCanonicalURL = new URL(canonicalURL, Astro.site).href; const fullCanonicalURL = new URL(canonicalURL, Astro.site).href;
@@ -48,13 +48,33 @@ const fullOgImage = new URL(ogImage, Astro.site).href;
<!-- Additional Meta Tags --> <!-- Additional Meta Tags -->
<meta name="author" content="Latte" /> <meta name="author" content="Latte" />
<meta name="keywords" content="self-hosted, privacy, open-source, furry, developer, cozy, hidden den" /> <meta
name="keywords"
content="self-hosted, privacy, open-source, furry, developer, cozy, hidden den"
/>
<meta name="theme-color" content="#d4a574" /> <meta name="theme-color" content="#d4a574" />
<meta name="color-scheme" content="dark" /> <meta name="color-scheme" content="dark" />
<!-- Favicon --> <!-- Favicons -->
<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="apple-touch-icon" href="/favicon.svg" /> <link
rel="icon"
type="image/png"
sizes="32x32"
href="/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/favicon-16x16.png"
/>
<link
rel="apple-touch-icon"
sizes="180x180"
href="/apple-touch-icon.png"
/>
<link rel="manifest" href="/site.webmanifest" />
<!-- Preconnect for performance (if needed for future external resources) --> <!-- Preconnect for performance (if needed for future external resources) -->
<!-- <link rel="preconnect" href="https://example.com" crossorigin /> --> <!-- <link rel="preconnect" href="https://example.com" crossorigin /> -->
@@ -83,7 +103,9 @@ const fullOgImage = new URL(ogImage, Astro.site).href;
--space-xl: 3rem; --space-xl: 3rem;
/* Typography */ /* Typography */
--font-body: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; --font-body:
system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
sans-serif;
} }
* { * {
@@ -104,7 +126,12 @@ const fullOgImage = new URL(ogImage, Astro.site).href;
line-height: 1.6; line-height: 1.6;
} }
h1, h2, h3, h4, h5, h6 { h1,
h2,
h3,
h4,
h5,
h6 {
color: var(--color-accent-bright); color: var(--color-accent-bright);
line-height: 1.2; line-height: 1.2;
} }
+12 -8
View File
@@ -1,5 +1,5 @@
--- ---
import BaseLayout from '../layouts/BaseLayout.astro'; import BaseLayout from "../layouts/BaseLayout.astro";
--- ---
<BaseLayout <BaseLayout
@@ -13,15 +13,19 @@ import BaseLayout from '../layouts/BaseLayout.astro';
<h1 class="error-code">404</h1> <h1 class="error-code">404</h1>
<h2 class="error-title">Lost in the Den?</h2> <h2 class="error-title">Lost in the Den?</h2>
<p class="error-message"> <p class="error-message">
Oops! This cozy corner doesn't seem to exist. Maybe it's still being built, Oops! This cozy corner doesn't seem to exist. Maybe it's
or perhaps you've wandered into a part of the den that hasn't been opened yet. still being built, or perhaps you've wandered into a
part of the den that hasn't been opened yet.
</p> </p>
<div class="actions"> <div class="actions">
<a href="/" class="btn-primary"> <a href="/" class="btn-primary"> Back to Home </a>
<span aria-hidden="true">🏡</span> Back to Home <a
</a> href="https://git.hiddenden.cafe"
<a href="https://git.hiddenden.cafe" class="btn-secondary" target="_blank" rel="noopener noreferrer"> class="btn-secondary"
<span aria-hidden="true">📦</span> Visit Gitea target="_blank"
rel="noopener noreferrer"
>
Visit Gitea
</a> </a>
</div> </div>
</div> </div>
+112 -178
View File
@@ -1,5 +1,9 @@
--- ---
import BaseLayout from '../layouts/BaseLayout.astro'; import BaseLayout from "../layouts/BaseLayout.astro";
import Card from "../components/Card.astro";
import Section from "../components/Section.astro";
import ServiceItem from "../components/ServiceItem.astro";
import SupportItem from "../components/SupportItem.astro";
--- ---
<BaseLayout <BaseLayout
@@ -11,135 +15,145 @@ import BaseLayout from '../layouts/BaseLayout.astro';
<section class="hero" aria-labelledby="hero-title"> <section class="hero" aria-labelledby="hero-title">
<div class="container"> <div class="container">
<div class="hero-content fade-in"> <div class="hero-content fade-in">
<h1 id="hero-title" class="hero-title"> <h1 id="hero-title" class="hero-title">Hidden Den Cafe</h1>
<span aria-hidden="true">🏡</span> Hidden Den Cafe
</h1>
<p class="hero-subtitle">A cozy corner of the internet</p> <p class="hero-subtitle">A cozy corner of the internet</p>
</div> </div>
</div> </div>
</section> </section>
<!-- About Hidden Den Section --> <!-- About Hidden Den Section -->
<section class="section about-den" aria-labelledby="about-den-heading"> <Section class="about-den" ariaLabelledby="about-den-heading">
<div class="container"> <Card>
<div class="card fade-in"> <h2 id="about-den-heading">About Hidden Den</h2>
<h2 id="about-den-heading"><span aria-hidden="true">☕</span> About Hidden Den</h2>
<p> <p>
Welcome to Hidden Den Cafe - a warm, self-hosted space where technology Welcome to Hidden Den Cafe - a warm, self-hosted space where
meets comfort. This is a personal corner of the internet built with love, technology meets comfort. This is a personal corner of the
care, and a strong belief in privacy and open-source values. internet built with love, care, and a strong belief in
privacy and open-source values.
</p> </p>
<p> <p>
Everything here runs on self-hosted infrastructure, giving complete control Everything here runs on self-hosted infrastructure, giving
over data and services. No cloud dependencies, no tracking, just a cozy complete control over data and services. No cloud
digital home. dependencies, no tracking, just a cozy digital home.
</p> </p>
</div> </Card>
</div> </Section>
</section>
<!-- About Latte Section --> <!-- About Latte Section -->
<section class="section about-me" aria-labelledby="about-me-heading"> <Section class="about-me" ariaLabelledby="about-me-heading">
<div class="container"> <Card>
<div class="card fade-in"> <h2 id="about-me-heading">About Me</h2>
<h2 id="about-me-heading"><span aria-hidden="true">🦊</span> About Me</h2>
<p> <p>
Hey there! I'm Latte, a gay furry developer who loves building things Hey there! I'm Latte, a gay furry developer who loves
and being part of the warm, welcoming furry community. I'm passionate building things and being part of the warm, welcoming furry
about self-hosting, open-source software, and creating cozy spaces community. I'm passionate about self-hosting, open-source
both online and off. software, and creating cozy spaces both online and off.
</p> </p>
<p> <p>
I work primarily with Python and Flask, though I'm always learning new I work primarily with Python and Flask, though I'm always
things (currently exploring JavaScript and the C stack). When I get those learning new things (currently exploring JavaScript and the
surges of creative energy, I love diving into new projects - from Reddit C stack). When I get those surges of creative energy, I love
downloaders to Telegram sticker tools, and everything in between. diving into new projects - from Reddit downloaders to
Telegram sticker tools, and everything in between.
</p> </p>
<p> <p>Coffee enthusiast • Linux lover • Self-hosting advocate</p>
Coffee enthusiast ☕ • Linux lover 🐧 • Self-hosting advocate 🏠 </Card>
</p> </Section>
</div>
</div>
</section>
<!-- Services Section --> <!-- Services Section -->
<section class="section services" aria-labelledby="services-heading"> <Section class="services" ariaLabelledby="services-heading">
<div class="container"> <Card>
<div class="card fade-in"> <h2 id="services-heading">Services</h2>
<h2 id="services-heading"><span aria-hidden="true">🛠️</span> Services</h2>
<p>Here are the services currently running in the den:</p> <p>Here are the services currently running in the den:</p>
<div class="service-list" role="list"> <div class="service-list" role="list">
<div class="service-item" role="listitem"> <ServiceItem
<h3> title="Gitea"
<a href="https://git.hiddenden.cafe" target="_blank" rel="noopener noreferrer" aria-label="Visit Gitea - Self-hosted Git service"> description="Self-hosted Git service for all my projects and code repositories."
<span aria-hidden="true">📦</span> Gitea url="https://git.hiddenden.cafe"
</a> />
</h3> <ServiceItem
<p>Self-hosted Git service for all my projects and code repositories.</p> title="More Coming Soon"
description="The den is always growing! More services will be added as they're developed and deployed."
comingSoon={true}
/>
</div> </div>
</Card>
<div class="service-item coming-soon" role="listitem"> </Section>
<h3><span aria-hidden="true">🔜</span> More Coming Soon</h3>
<p>The den is always growing! More services will be added as they're developed and deployed.</p>
</div>
</div>
</div>
</div>
</section>
<!-- Support Section --> <!-- Support Section -->
<section class="section support" aria-labelledby="support-heading"> <Section class="support" ariaLabelledby="support-heading">
<div class="container"> <Card>
<div class="card fade-in"> <h2 id="support-heading">How to Help Out</h2>
<h2 id="support-heading"><span aria-hidden="true">💝</span> How to Help Out</h2>
<p> <p>
If you'd like to support the Hidden Den and help keep the lights on, If you'd like to support the Hidden Den and help keep the
here are some ways you can contribute: lights on, here are some ways you can contribute:
</p> </p>
<div class="support-list" role="list"> <div class="support-list" role="list">
<div class="support-item" role="listitem"> <SupportItem
<h3><span aria-hidden="true">🌟</span> Share & Spread the Word</h3> title="Share & Spread the Word"
<p>Tell others about the projects and services hosted here!</p> description="Tell others about the projects and services hosted here!"
/>
<SupportItem
title="Report Issues"
description="Found a bug or have a suggestion? Let me know!"
/>
<SupportItem
title="Buy Me a Coffee"
description="Donations help cover server costs and keep the den cozy."
comingSoonNote="Payment links coming soon!"
/>
<SupportItem
title="Contribute"
description="Check out the projects on Gitea - contributions are always welcome!"
/>
</div> </div>
</Card>
<div class="support-item" role="listitem"> </Section>
<h3><span aria-hidden="true">🐛</span> Report Issues</h3>
<p>Found a bug or have a suggestion? Let me know!</p>
</div>
<div class="support-item" role="listitem">
<h3><span aria-hidden="true">☕</span> Buy Me a Coffee</h3>
<p>
Donations help cover server costs and keep the den cozy.
<span class="coming-soon-text">(Payment links coming soon!)</span>
</p>
</div>
<div class="support-item" role="listitem">
<h3><span aria-hidden="true">🤝</span> Contribute</h3>
<p>Check out the projects on Gitea - contributions are always welcome!</p>
</div>
</div>
</div>
</div>
</section>
<!-- Footer --> <!-- Footer -->
<footer class="footer" role="contentinfo"> <footer class="footer" role="contentinfo">
<div class="container"> <div class="container">
<p>Made with <span aria-hidden="true">💖</span><span class="sr-only">love</span> by Latte</p> <p>Made with love by Latte</p>
<nav aria-label="Footer navigation"> <nav aria-label="Footer navigation">
<p class="footer-links"> <p class="footer-links">
<a href="https://git.hiddenden.cafe" target="_blank" rel="noopener noreferrer">Gitea</a> <a
<span class="separator" aria-hidden="true">•</span> href="https://git.hiddenden.cafe"
<a href="https://dmush.cloud" target="_blank" rel="noopener noreferrer">dmush.cloud</a> target="_blank"
rel="noopener noreferrer">Gitea</a
>
</p> </p>
</nav> </nav>
</div> </div>
</footer> </footer>
</main> </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> </BaseLayout>
<style> <style>
@@ -160,7 +174,11 @@ import BaseLayout from '../layouts/BaseLayout.astro';
.hero { .hero {
padding: var(--space-xl) 0; padding: var(--space-xl) 0;
text-align: center; text-align: center;
background: linear-gradient(135deg, var(--color-bg) 0%, var(--color-bg-light) 100%); background: linear-gradient(
135deg,
var(--color-bg) 0%,
var(--color-bg-light) 100%
);
} }
.hero-title { .hero-title {
@@ -174,91 +192,15 @@ import BaseLayout from '../layouts/BaseLayout.astro';
color: var(--color-text-dim); color: var(--color-text-dim);
} }
/* Sections */ /* Service and Support Lists */
.section { .service-list,
padding: var(--space-lg) 0; .support-list {
}
.card {
background: var(--color-bg-light);
border-radius: 12px;
padding: var(--space-lg);
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
border: 1px solid rgba(212, 165, 116, 0.1);
}
.card h2 {
font-size: 2rem;
margin-bottom: var(--space-md);
}
.card p {
margin-bottom: var(--space-md);
color: var(--color-text);
}
.card p:last-child {
margin-bottom: 0;
}
/* Services Section */
.service-list {
margin-top: var(--space-md); margin-top: var(--space-md);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--space-md); gap: var(--space-md);
} }
.service-item {
background: var(--color-bg);
padding: var(--space-md);
border-radius: 8px;
border-left: 4px solid var(--color-accent);
}
.service-item h3 {
margin-bottom: var(--space-xs);
font-size: 1.25rem;
}
.service-item p {
color: var(--color-text-dim);
margin: 0;
}
.service-item.coming-soon {
border-left-color: var(--color-warm);
opacity: 0.8;
}
/* Support Section */
.support-list {
margin-top: var(--space-md);
display: grid;
gap: var(--space-md);
}
.support-item {
background: var(--color-bg);
padding: var(--space-md);
border-radius: 8px;
}
.support-item h3 {
margin-bottom: var(--space-xs);
font-size: 1.25rem;
}
.support-item p {
color: var(--color-text-dim);
margin: 0;
}
.coming-soon-text {
font-style: italic;
color: var(--color-warm);
}
/* Footer */ /* Footer */
.footer { .footer {
margin-top: auto; margin-top: auto;
@@ -280,10 +222,6 @@ import BaseLayout from '../layouts/BaseLayout.astro';
gap: var(--space-sm); gap: var(--space-sm);
} }
.separator {
color: var(--color-warm);
}
/* Responsive */ /* Responsive */
@media (max-width: 768px) { @media (max-width: 768px) {
.hero-title { .hero-title {
@@ -293,9 +231,5 @@ import BaseLayout from '../layouts/BaseLayout.astro';
.hero-subtitle { .hero-subtitle {
font-size: 1rem; font-size: 1rem;
} }
.card h2 {
font-size: 1.5rem;
}
} }
</style> </style>