Merge pull request 'dev' (#5) from dev into main
Reviewed-on: #5
This commit was merged in pull request #5.
This commit is contained in:
@@ -1,17 +1,36 @@
|
|||||||
name: AI Chat (Bartender)
|
name: AI Chat (Bartender)
|
||||||
|
|
||||||
|
# WORKFLOW ROUTING:
|
||||||
|
# This workflow handles FREE-FORM questions/chat (no specific command)
|
||||||
|
# Other workflows: ai-issue-triage.yml (@codebot triage), ai-comment-reply.yml (specific commands)
|
||||||
|
# This is the FALLBACK for any @codebot mention that isn't a known command
|
||||||
|
|
||||||
on:
|
on:
|
||||||
issue_comment:
|
issue_comment:
|
||||||
types: [created]
|
types: [created]
|
||||||
|
|
||||||
# CUSTOMIZE YOUR BOT NAME:
|
# CUSTOMIZE YOUR BOT NAME:
|
||||||
# Change '@ai-bot' below to match your config.yml mention_prefix
|
# Change '@codebot' in all conditions below to match your config.yml mention_prefix
|
||||||
# Examples: '@bartender', '@uni', '@joey', '@codebot'
|
# Examples: '@bartender', '@uni', '@joey', '@codebot'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ai-chat:
|
ai-chat:
|
||||||
# Only run if comment mentions the bot
|
# Only run if comment mentions the bot but NOT a specific command
|
||||||
if: contains(github.event.comment.body, '@codebot') # <-- Change this to your bot name
|
# This prevents duplicate runs with ai-comment-reply.yml and ai-issue-triage.yml
|
||||||
|
# CRITICAL: Ignore bot's own comments to prevent infinite loops (bot username: Bartender)
|
||||||
|
if: |
|
||||||
|
github.event.comment.user.login != 'Bartender' &&
|
||||||
|
contains(github.event.comment.body, '@codebot') &&
|
||||||
|
!contains(github.event.comment.body, '@codebot triage') &&
|
||||||
|
!contains(github.event.comment.body, '@codebot help') &&
|
||||||
|
!contains(github.event.comment.body, '@codebot explain') &&
|
||||||
|
!contains(github.event.comment.body, '@codebot suggest') &&
|
||||||
|
!contains(github.event.comment.body, '@codebot security') &&
|
||||||
|
!contains(github.event.comment.body, '@codebot summarize') &&
|
||||||
|
!contains(github.event.comment.body, '@codebot changelog') &&
|
||||||
|
!contains(github.event.comment.body, '@codebot explain-diff') &&
|
||||||
|
!contains(github.event.comment.body, '@codebot review-again') &&
|
||||||
|
!contains(github.event.comment.body, '@codebot setup-labels')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|||||||
@@ -1,17 +1,34 @@
|
|||||||
name: AI Comment Reply
|
name: AI Comment Reply
|
||||||
|
|
||||||
|
# WORKFLOW ROUTING:
|
||||||
|
# This workflow handles SPECIFIC commands: help, explain, suggest, security, summarize, changelog, explain-diff, review-again, setup-labels
|
||||||
|
# Other workflows: ai-issue-triage.yml (@codebot triage), ai-chat.yml (free-form questions)
|
||||||
|
|
||||||
on:
|
on:
|
||||||
issue_comment:
|
issue_comment:
|
||||||
types: [created]
|
types: [created]
|
||||||
|
|
||||||
# CUSTOMIZE YOUR BOT NAME:
|
# CUSTOMIZE YOUR BOT NAME:
|
||||||
# Change '@ai-bot' below to match your config.yml mention_prefix
|
# Change '@codebot' in the 'if' condition below to match your config.yml mention_prefix
|
||||||
# Examples: '@bartender', '@uni', '@joey', '@codebot'
|
# Examples: '@bartender', '@uni', '@joey', '@codebot'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ai-reply:
|
ai-reply:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: contains(github.event.comment.body, '@codebot') # <-- Change this to your bot name
|
# Only run for specific commands (not free-form chat or triage)
|
||||||
|
# This prevents duplicate runs with ai-chat.yml and ai-issue-triage.yml
|
||||||
|
# CRITICAL: Ignore bot's own comments to prevent infinite loops (bot username: Bartender)
|
||||||
|
if: |
|
||||||
|
github.event.comment.user.login != 'Bartender' &&
|
||||||
|
(contains(github.event.comment.body, '@codebot help') ||
|
||||||
|
contains(github.event.comment.body, '@codebot explain') ||
|
||||||
|
contains(github.event.comment.body, '@codebot suggest') ||
|
||||||
|
contains(github.event.comment.body, '@codebot security') ||
|
||||||
|
contains(github.event.comment.body, '@codebot summarize') ||
|
||||||
|
contains(github.event.comment.body, '@codebot changelog') ||
|
||||||
|
contains(github.event.comment.body, '@codebot explain-diff') ||
|
||||||
|
contains(github.event.comment.body, '@codebot review-again') ||
|
||||||
|
contains(github.event.comment.body, '@codebot setup-labels'))
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
@@ -30,12 +47,52 @@ jobs:
|
|||||||
- name: Run AI Comment Response
|
- name: Run AI Comment Response
|
||||||
env:
|
env:
|
||||||
AI_REVIEW_TOKEN: ${{ secrets.AI_REVIEW_TOKEN }}
|
AI_REVIEW_TOKEN: ${{ secrets.AI_REVIEW_TOKEN }}
|
||||||
AI_REVIEW_REPO: ${{ gitea.repository }}
|
|
||||||
AI_REVIEW_API_URL: https://git.hiddenden.cafe/api/v1
|
AI_REVIEW_API_URL: https://git.hiddenden.cafe/api/v1
|
||||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||||
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
|
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
|
||||||
OLLAMA_HOST: ${{ secrets.OLLAMA_HOST }}
|
OLLAMA_HOST: ${{ secrets.OLLAMA_HOST }}
|
||||||
run: |
|
run: |
|
||||||
cd .ai-review/tools/ai-review
|
cd .ai-review/tools/ai-review
|
||||||
python main.py comment ${{ gitea.repository }} ${{ gitea.event.issue.number }} \
|
|
||||||
"${{ gitea.event.comment.body }}"
|
# Determine if this is a PR or issue comment
|
||||||
|
IS_PR="${{ gitea.event.issue.pull_request != null }}"
|
||||||
|
REPO="${{ gitea.repository }}"
|
||||||
|
ISSUE_NUMBER="${{ gitea.event.issue.number }}"
|
||||||
|
|
||||||
|
# Validate inputs
|
||||||
|
if [ -z "$REPO" ] || [ -z "$ISSUE_NUMBER" ]; then
|
||||||
|
echo "Error: Missing required parameters"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validate repository format (owner/repo)
|
||||||
|
if ! echo "$REPO" | grep -qE '^[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+$'; then
|
||||||
|
echo "Error: Invalid repository format: $REPO"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$IS_PR" = "true" ]; then
|
||||||
|
# This is a PR comment - use safe dispatch with minimal event data
|
||||||
|
# Build minimal event payload (does not include sensitive user data)
|
||||||
|
EVENT_DATA=$(cat <<EOF
|
||||||
|
{
|
||||||
|
"action": "created",
|
||||||
|
"issue": {
|
||||||
|
"number": ${{ gitea.event.issue.number }},
|
||||||
|
"pull_request": {}
|
||||||
|
},
|
||||||
|
"comment": {
|
||||||
|
"id": ${{ gitea.event.comment.id }},
|
||||||
|
"body": $(echo '${{ gitea.event.comment.body }}' | jq -Rs .)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
# Use safe dispatch utility
|
||||||
|
python utils/safe_dispatch.py issue_comment "$REPO" "$EVENT_DATA"
|
||||||
|
else
|
||||||
|
# This is an issue comment - use the comment command
|
||||||
|
COMMENT_BODY='${{ gitea.event.comment.body }}'
|
||||||
|
python main.py comment "$REPO" "$ISSUE_NUMBER" "$COMMENT_BODY"
|
||||||
|
fi
|
||||||
|
|||||||
@@ -1,12 +1,21 @@
|
|||||||
name: AI Issue Triage
|
name: AI Issue Triage
|
||||||
|
|
||||||
|
# WORKFLOW ROUTING:
|
||||||
|
# This workflow handles ONLY the 'triage' command
|
||||||
|
# Other workflows: ai-comment-reply.yml (specific commands), ai-chat.yml (free-form questions)
|
||||||
|
|
||||||
on:
|
on:
|
||||||
issues:
|
issue_comment:
|
||||||
types: [opened, labeled]
|
types: [created]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ai-triage:
|
ai-triage:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
# Only run if comment contains @codebot triage
|
||||||
|
# CRITICAL: Ignore bot's own comments to prevent infinite loops (bot username: Bartender)
|
||||||
|
if: |
|
||||||
|
github.event.comment.user.login != 'Bartender' &&
|
||||||
|
contains(github.event.comment.body, '@codebot triage')
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
@@ -32,5 +41,4 @@ jobs:
|
|||||||
OLLAMA_HOST: ${{ secrets.OLLAMA_HOST }}
|
OLLAMA_HOST: ${{ secrets.OLLAMA_HOST }}
|
||||||
run: |
|
run: |
|
||||||
cd .ai-review/tools/ai-review
|
cd .ai-review/tools/ai-review
|
||||||
python main.py issue ${{ gitea.repository }} ${{ gitea.event.issue.number }} \
|
python main.py issue ${{ gitea.repository }} ${{ gitea.event.issue.number }}
|
||||||
--title "${{ gitea.event.issue.title }}"
|
|
||||||
|
|||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
# Build output
|
||||||
|
dist/
|
||||||
|
.astro/
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.production
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Package lock files (keep package-lock.json but ignore others)
|
||||||
|
yarn.lock
|
||||||
|
pnpm-lock.yaml
|
||||||
|
|
||||||
|
# Local development
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Build artifacts
|
||||||
|
*.tsbuildinfo
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
---
|
|
||||||
interface Props {
|
|
||||||
title: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { title } = Astro.props;
|
|
||||||
---
|
|
||||||
|
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<meta name="description" content="Hidden Den Cafe - A cozy corner of the internet" />
|
|
||||||
<meta name="viewport" content="width=device-width" />
|
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
||||||
<meta name="generator" content={Astro.generator} />
|
|
||||||
<title>{title}</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<slot />
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
<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;
|
|
||||||
|
|
||||||
/* Spacing */
|
|
||||||
--space-xs: 0.5rem;
|
|
||||||
--space-sm: 1rem;
|
|
||||||
--space-md: 1.5rem;
|
|
||||||
--space-lg: 2rem;
|
|
||||||
--space-xl: 3rem;
|
|
||||||
|
|
||||||
/* Typography */
|
|
||||||
--font-body: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
|
||||||
font-family: var(--font-body);
|
|
||||||
background: var(--color-bg);
|
|
||||||
color: var(--color-text);
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
min-height: 100vh;
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
|
||||||
color: var(--color-accent-bright);
|
|
||||||
line-height: 1.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: var(--color-accent-bright);
|
|
||||||
text-decoration: none;
|
|
||||||
transition: color 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
color: var(--color-accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Smooth animations */
|
|
||||||
@keyframes fadeIn {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(20px);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.fade-in {
|
|
||||||
animation: fadeIn 0.6s ease-out;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,236 +0,0 @@
|
|||||||
# CLAUDE.MD - AI Assistant Guide
|
|
||||||
|
|
||||||
This file provides guidance for Claude Code and other AI assistants when working with the Cozy Den project.
|
|
||||||
|
|
||||||
## Project Quick Reference
|
|
||||||
|
|
||||||
**Project:** Cozy Den - Personal landing page for hiddenden.cafe
|
|
||||||
**Owner:** Latte (gay furry developer, values self-hosting and privacy)
|
|
||||||
**Tech Stack:** Astro 4.x, TypeScript, Vanilla CSS, Docker + Nginx
|
|
||||||
**Aesthetic:** Warm coffee/cappuccino theme, cozy hidden den vibes
|
|
||||||
**Deployment:** Docker containers pushed to Gitea registry at git.hiddenden.cafe
|
|
||||||
|
|
||||||
## Core Design Principles
|
|
||||||
|
|
||||||
1. **Cozy Aesthetic** - Warm colors, coffee/cappuccino theme, hidden den vibes
|
|
||||||
2. **Self-Hosted** - Everything runs on personal infrastructure (homelab/VPS)
|
|
||||||
3. **Privacy First** - No tracking, no external dependencies
|
|
||||||
4. **Lightweight** - Static HTML/CSS, minimal JavaScript
|
|
||||||
5. **Docker-Ready** - Easy deployment via containers
|
|
||||||
|
|
||||||
## File Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
src/
|
|
||||||
├── layouts/
|
|
||||||
│ └── BaseLayout.astro # Base HTML layout + global styles + CSS variables
|
|
||||||
├── pages/
|
|
||||||
│ └── index.astro # Main landing page (all sections here)
|
|
||||||
└── components/ # Empty - ready for future components
|
|
||||||
public/
|
|
||||||
└── favicon.svg # Coffee emoji favicon
|
|
||||||
```
|
|
||||||
|
|
||||||
**Key Files:**
|
|
||||||
- `src/layouts/BaseLayout.astro` - CSS variables, global styles, base HTML structure
|
|
||||||
- `src/pages/index.astro` - All page content and section-specific styles
|
|
||||||
- `astro.config.mjs` - Astro configuration
|
|
||||||
- `Dockerfile` - Multi-stage build (Node builder + Nginx server)
|
|
||||||
- `docker-compose.yml` - Local Docker Compose setup
|
|
||||||
- `nginx.conf` - Production Nginx configuration
|
|
||||||
|
|
||||||
## Color System
|
|
||||||
|
|
||||||
All colors use CSS custom properties in `src/layouts/BaseLayout.astro`:
|
|
||||||
|
|
||||||
```css
|
|
||||||
--color-bg: #1a1410 /* Dark background (deep coffee) */
|
|
||||||
--color-bg-light: #2a1f18 /* Lighter background for cards */
|
|
||||||
--color-text: #f4e9d8 /* Cream text */
|
|
||||||
--color-text-dim: #c4b5a0 /* Dimmed text */
|
|
||||||
--color-accent: #d4a574 /* Warm accent (coffee with cream) */
|
|
||||||
--color-accent-bright: #e8bf8e /* Brighter accent for highlights */
|
|
||||||
--color-warm: #8b6f47 /* Warm brown for borders/accents */
|
|
||||||
```
|
|
||||||
|
|
||||||
**To change theme:** Edit these variables. All components update automatically.
|
|
||||||
|
|
||||||
## Common Modification Patterns
|
|
||||||
|
|
||||||
### Adding a Section
|
|
||||||
```astro
|
|
||||||
<section class="section new-section">
|
|
||||||
<div class="container">
|
|
||||||
<div class="card fade-in">
|
|
||||||
<h2>Section Title</h2>
|
|
||||||
<p>Content</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Adding a Service
|
|
||||||
```astro
|
|
||||||
<div class="service-item">
|
|
||||||
<h3><a href="https://service.hiddenden.cafe">🔧 Service Name</a></h3>
|
|
||||||
<p>Description of the service</p>
|
|
||||||
</div>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Adding a New Page
|
|
||||||
Create `src/pages/newpage.astro`:
|
|
||||||
```astro
|
|
||||||
---
|
|
||||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
|
||||||
---
|
|
||||||
|
|
||||||
<BaseLayout title="New Page">
|
|
||||||
<div class="container">
|
|
||||||
<h1>New Page</h1>
|
|
||||||
</div>
|
|
||||||
</BaseLayout>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Development Workflow
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm install # Install dependencies
|
|
||||||
npm run dev # Start dev server (http://localhost:4321)
|
|
||||||
npm run build # Build for production (output to dist/)
|
|
||||||
npm run preview # Preview production build
|
|
||||||
```
|
|
||||||
|
|
||||||
## Docker Workflow
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Build image
|
|
||||||
docker build -t cozy-den .
|
|
||||||
|
|
||||||
# Run locally
|
|
||||||
docker run -d -p 3000:80 --name cozy-den cozy-den
|
|
||||||
|
|
||||||
# Or use Docker Compose
|
|
||||||
docker-compose up -d
|
|
||||||
|
|
||||||
# Tag for Gitea registry
|
|
||||||
docker tag cozy-den git.hiddenden.cafe/mats/cozy-den:latest
|
|
||||||
|
|
||||||
# Push to Gitea
|
|
||||||
docker login git.hiddenden.cafe
|
|
||||||
docker push git.hiddenden.cafe/mats/cozy-den:latest
|
|
||||||
```
|
|
||||||
|
|
||||||
## Important Implementation Guidelines
|
|
||||||
|
|
||||||
### DO:
|
|
||||||
- Maintain the cozy, warm aesthetic (coffee theme)
|
|
||||||
- Keep the site lightweight and fast (static HTML/CSS)
|
|
||||||
- Use CSS custom properties for all colors
|
|
||||||
- Add `.fade-in` class for animations
|
|
||||||
- Test both dev and production builds
|
|
||||||
- Verify Docker build works after changes
|
|
||||||
- Use semantic HTML with consistent `.card` class styling
|
|
||||||
- Ensure responsive design works on mobile
|
|
||||||
- Be warm and friendly in communication (matches site vibe)
|
|
||||||
- Focus on practical implementation
|
|
||||||
- Respect the furry community context
|
|
||||||
|
|
||||||
### DON'T:
|
|
||||||
- Add tracking or external dependencies
|
|
||||||
- Make the site heavy or complex
|
|
||||||
- Use JavaScript unless absolutely necessary (site is pure HTML/CSS)
|
|
||||||
- Create sterile or corporate design elements
|
|
||||||
- Add features not explicitly requested
|
|
||||||
- Break the coffee/warm color theme
|
|
||||||
- Ignore accessibility considerations
|
|
||||||
|
|
||||||
## Astro-Specific Notes
|
|
||||||
|
|
||||||
- **File Extensions:** `.astro` for components, `.mjs` for config
|
|
||||||
- **Frontmatter:** Code between `---` runs at build time
|
|
||||||
- **Styling:** `<style>` tags are scoped by default, use `<style is:global>` for global styles
|
|
||||||
- **Static Generation:** Astro generates static HTML at build time
|
|
||||||
- **No Runtime:** This site outputs pure HTML/CSS with no JavaScript runtime needed
|
|
||||||
|
|
||||||
## Current Project Status
|
|
||||||
|
|
||||||
**Completed:**
|
|
||||||
- Landing page with hero section
|
|
||||||
- About Hidden Den section
|
|
||||||
- About Me section (Latte)
|
|
||||||
- Services section (Gitea linked)
|
|
||||||
- Support section
|
|
||||||
- Docker deployment setup
|
|
||||||
- Responsive design
|
|
||||||
|
|
||||||
**Future Possibilities:**
|
|
||||||
- Blog section using Astro Content Collections
|
|
||||||
- More self-hosted services
|
|
||||||
- Payment/donation links
|
|
||||||
- Project showcase pulling from Gitea API
|
|
||||||
- Custom 404 page
|
|
||||||
- Theme toggle
|
|
||||||
- Contact form
|
|
||||||
- RSS feed
|
|
||||||
|
|
||||||
## Owner Preferences
|
|
||||||
|
|
||||||
Latte typically:
|
|
||||||
- Works in bursts of creative energy
|
|
||||||
- Uses Docker for all deployments
|
|
||||||
- Pushes to personal Gitea at git.hiddenden.cafe
|
|
||||||
- Values complete control over hosting
|
|
||||||
- Prefers warm, personal styling over corporate design
|
|
||||||
- Is learning Astro (normally uses Python/Flask)
|
|
||||||
|
|
||||||
## Testing Checklist
|
|
||||||
|
|
||||||
Before deploying changes:
|
|
||||||
- [ ] `npm run dev` - Check locally
|
|
||||||
- [ ] `npm run build` - Ensure build succeeds
|
|
||||||
- [ ] `docker build -t cozy-den .` - Verify Docker build
|
|
||||||
- [ ] Test on mobile viewport
|
|
||||||
- [ ] Check all links work
|
|
||||||
- [ ] Verify color contrast for accessibility
|
|
||||||
|
|
||||||
## Success Criteria
|
|
||||||
|
|
||||||
The site should:
|
|
||||||
- Load fast (static HTML)
|
|
||||||
- Feel warm and welcoming
|
|
||||||
- Accurately represent Latte and Hidden Den
|
|
||||||
- Work on all screen sizes
|
|
||||||
- Be easy to deploy via Docker
|
|
||||||
- Require minimal maintenance
|
|
||||||
|
|
||||||
## Troubleshooting Quick Reference
|
|
||||||
|
|
||||||
**Build fails:** Check TypeScript config, ensure Node 18+
|
|
||||||
**Styles not applying:** Check if you need `is:global`, verify CSS variables are in BaseLayout
|
|
||||||
**Docker build fails:** Ensure package.json and package-lock.json are present
|
|
||||||
**Changes not showing:** Hard refresh (Ctrl+Shift+R), restart dev server, or clear `.astro` cache
|
|
||||||
|
|
||||||
## Documentation Files
|
|
||||||
|
|
||||||
- **CLAUDE.MD** (this file) - AI assistant guide
|
|
||||||
- **PROJECT_CONTEXT.md** - Project context and design principles
|
|
||||||
- **DEVELOPMENT.md** - Developer documentation and architecture
|
|
||||||
- **TODO.md** - Current tasks and future features
|
|
||||||
- **README.md** - User-facing documentation
|
|
||||||
|
|
||||||
## Communication Style
|
|
||||||
|
|
||||||
When working with this project:
|
|
||||||
- Be warm and friendly (matches the site vibe)
|
|
||||||
- Use clear, direct language
|
|
||||||
- Respect the furry community context
|
|
||||||
- Focus on practical implementation
|
|
||||||
- Acknowledge this is a learning project with Astro
|
|
||||||
- Personal and authentic over polished and corporate
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Last Updated:** 2025-12-23
|
|
||||||
**Project Version:** Initial release
|
|
||||||
**Astro Version:** 4.x
|
|
||||||
**Node Version:** 18+
|
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Project Quick Reference
|
||||||
|
|
||||||
|
**Project:** Cozy Den - Personal landing page for hiddenden.cafe
|
||||||
|
**Owner:** Latte (gay furry developer, values self-hosting and privacy)
|
||||||
|
**Tech Stack:** Astro 4.x, TypeScript, Vanilla CSS, Docker + Nginx
|
||||||
|
**Aesthetic:** Warm coffee/cappuccino theme, cozy hidden den vibes
|
||||||
|
**Deployment:** Docker containers pushed to Gitea registry at git.hiddenden.cafe
|
||||||
|
|
||||||
|
## Core Design Principles
|
||||||
|
|
||||||
|
1. **Cozy Aesthetic** - Warm colors, coffee/cappuccino theme, hidden den vibes
|
||||||
|
2. **Self-Hosted** - Everything runs on personal infrastructure (homelab/VPS)
|
||||||
|
3. **Privacy First** - No tracking, no external dependencies
|
||||||
|
4. **Lightweight** - Static HTML/CSS, minimal JavaScript
|
||||||
|
5. **Docker-Ready** - Easy deployment via containers
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
cozy-den/
|
||||||
|
├── src/
|
||||||
|
│ ├── layouts/
|
||||||
|
│ │ └── BaseLayout.astro # Base layout + global styles
|
||||||
|
│ └── pages/
|
||||||
|
│ ├── index.astro # Main landing page
|
||||||
|
│ └── 404.astro # Custom 404 page
|
||||||
|
├── public/
|
||||||
|
│ ├── favicon.svg # Coffee emoji favicon
|
||||||
|
│ └── robots.txt # Search engine directives
|
||||||
|
├── astro.config.mjs # Astro config with sitemap
|
||||||
|
├── package.json # Dependencies (Astro 4.x, @astrojs/sitemap)
|
||||||
|
├── Dockerfile # Multi-stage: Node builder + Nginx
|
||||||
|
├── docker-compose.yml # Local container orchestration
|
||||||
|
└── nginx.conf # Production web server config
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture Notes
|
||||||
|
|
||||||
|
This is a simple static site following standard Astro conventions:
|
||||||
|
- Layouts in `src/layouts/` for reusable page templates
|
||||||
|
- Pages in `src/pages/` (routes automatically based on filename)
|
||||||
|
- All content is on a single page (`index.astro`) with multiple sections
|
||||||
|
- Custom 404 page with cozy theming
|
||||||
|
- No client-side JavaScript - pure static HTML/CSS output
|
||||||
|
- CSS custom properties centralized in `BaseLayout.astro` for theming
|
||||||
|
- Accessibility improvements with ARIA labels and semantic HTML
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Development
|
||||||
|
npm install # Install dependencies
|
||||||
|
npm run dev # Start dev server at http://localhost:4321
|
||||||
|
npm run build # Build for production (runs astro check + astro build)
|
||||||
|
npm run preview # Preview production build
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
docker build -t cozy-den .
|
||||||
|
docker run -d -p 3000:80 --name cozy-den cozy-den
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
# Deployment to Gitea registry
|
||||||
|
docker tag cozy-den git.hiddenden.cafe/mats/cozy-den:latest
|
||||||
|
docker login git.hiddenden.cafe
|
||||||
|
docker push git.hiddenden.cafe/mats/cozy-den:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
## Color System
|
||||||
|
|
||||||
|
All colors use CSS custom properties in `BaseLayout.astro`:
|
||||||
|
|
||||||
|
```css
|
||||||
|
--color-bg: #1a1410 /* Dark background (deep coffee) */
|
||||||
|
--color-bg-light: #2a1f18 /* Lighter background for cards */
|
||||||
|
--color-text: #f4e9d8 /* Cream text */
|
||||||
|
--color-text-dim: #c4b5a0 /* Dimmed text */
|
||||||
|
--color-accent: #d4a574 /* Warm accent (coffee with cream) */
|
||||||
|
--color-accent-bright: #e8bf8e /* Brighter accent for highlights */
|
||||||
|
--color-warm: #8b6f47 /* Warm brown for borders/accents */
|
||||||
|
```
|
||||||
|
|
||||||
|
**To change theme:** Edit these variables. All components update automatically.
|
||||||
|
|
||||||
|
## Common Modification Patterns
|
||||||
|
|
||||||
|
### Adding a Section
|
||||||
|
```astro
|
||||||
|
<section class="section new-section">
|
||||||
|
<div class="container">
|
||||||
|
<div class="card fade-in">
|
||||||
|
<h2>Section Title</h2>
|
||||||
|
<p>Content</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adding a Service
|
||||||
|
```astro
|
||||||
|
<div class="service-item">
|
||||||
|
<h3><a href="https://service.hiddenden.cafe">🔧 Service Name</a></h3>
|
||||||
|
<p>Description of the service</p>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adding a New Page
|
||||||
|
Create new `.astro` file in `src/pages/`:
|
||||||
|
```astro
|
||||||
|
---
|
||||||
|
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||||
|
---
|
||||||
|
|
||||||
|
<BaseLayout title="New Page">
|
||||||
|
<div class="container">
|
||||||
|
<h1>New Page</h1>
|
||||||
|
</div>
|
||||||
|
</BaseLayout>
|
||||||
|
```
|
||||||
|
Note: Pages route based on filename (e.g., `about.astro` → `/about`)
|
||||||
|
|
||||||
|
## Implementation Guidelines
|
||||||
|
|
||||||
|
**DO:**
|
||||||
|
- Maintain cozy, warm aesthetic (coffee/cappuccino theme)
|
||||||
|
- Keep site lightweight - static HTML/CSS only, no JavaScript runtime
|
||||||
|
- Use CSS custom properties for all colors (defined in `src/layouts/BaseLayout.astro`)
|
||||||
|
- Use `.fade-in` class for animations, `.card` class for consistent card styling
|
||||||
|
- Test production builds and Docker builds after changes
|
||||||
|
- Ensure responsive design works on mobile
|
||||||
|
- Follow standard Astro structure (layouts in `src/layouts/`, pages in `src/pages/`)
|
||||||
|
|
||||||
|
**DON'T:**
|
||||||
|
- Add tracking or external dependencies (privacy-first approach)
|
||||||
|
- Add client-side JavaScript unless absolutely necessary
|
||||||
|
- Break the coffee/warm color theme
|
||||||
|
- Create sterile or corporate design elements
|
||||||
|
|
||||||
|
## Astro-Specific Notes
|
||||||
|
|
||||||
|
- Frontmatter (code between `---`) runs at build time only
|
||||||
|
- `<style>` tags are scoped by default; use `<style is:global>` for global styles (see `src/layouts/BaseLayout.astro`)
|
||||||
|
- Site generates static HTML at build time - no JavaScript runtime
|
||||||
|
- Sitemap integration configured in `astro.config.mjs` via `@astrojs/sitemap`
|
||||||
|
- Custom 404 page at `src/pages/404.astro` with warm, themed styling
|
||||||
|
|
||||||
|
## Context & Preferences
|
||||||
|
|
||||||
|
- **Owner:** Latte (gay furry developer who values self-hosting, privacy, and open-source)
|
||||||
|
- **Deployment:** All deployments via Docker to personal Gitea registry (git.hiddenden.cafe)
|
||||||
|
- **Design Philosophy:** Warm, personal, cozy aesthetic over corporate/sterile design
|
||||||
|
- **Technical Background:** Owner typically uses Python/Flask, learning Astro with this project
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
**Build fails:** Check TypeScript config, ensure Node 18+, run `astro check`
|
||||||
|
**Styles not applying:** Verify CSS variables are in `BaseLayout.astro`, check if you need `is:global`
|
||||||
|
**Docker build fails:** Ensure `package.json` and `package-lock.json` exist
|
||||||
|
**Changes not showing:** Hard refresh browser, restart dev server, or clear `.astro` cache
|
||||||
|
|
||||||
|
## Related Documentation
|
||||||
|
|
||||||
|
- **PROJECT_CONTEXT.md** - Design principles and project philosophy
|
||||||
|
- **DEVELOPMENT.md** - Detailed developer documentation
|
||||||
|
- **TODO.md** - Current tasks and future feature ideas
|
||||||
|
- **README.md** - User-facing setup and deployment guide
|
||||||
+13
-3
@@ -19,11 +19,13 @@ cozy-den/
|
|||||||
│ ├── layouts/
|
│ ├── layouts/
|
||||||
│ │ └── BaseLayout.astro # Base HTML layout with global styles
|
│ │ └── BaseLayout.astro # Base HTML layout with global styles
|
||||||
│ ├── pages/
|
│ ├── pages/
|
||||||
│ │ └── index.astro # Main landing page
|
│ │ ├── index.astro # Main landing page
|
||||||
|
│ │ └── 404.astro # Custom 404 error page
|
||||||
│ └── components/ # (Empty - ready for future components)
|
│ └── components/ # (Empty - ready for future components)
|
||||||
├── public/
|
├── public/
|
||||||
│ └── favicon.svg # Site favicon (coffee emoji)
|
│ ├── favicon.svg # Site favicon (coffee emoji)
|
||||||
├── astro.config.mjs # Astro configuration
|
│ └── robots.txt # Search engine directives
|
||||||
|
├── astro.config.mjs # Astro configuration with sitemap
|
||||||
├── package.json # Node dependencies
|
├── package.json # Node dependencies
|
||||||
├── tsconfig.json # TypeScript configuration
|
├── tsconfig.json # TypeScript configuration
|
||||||
├── Dockerfile # Multi-stage Docker build
|
├── Dockerfile # Multi-stage Docker build
|
||||||
@@ -67,6 +69,14 @@ The main page includes these sections:
|
|||||||
5. **Support** - Ways to help/contribute
|
5. **Support** - Ways to help/contribute
|
||||||
6. **Footer** - Links and credits
|
6. **Footer** - Links and credits
|
||||||
|
|
||||||
|
### 404.astro
|
||||||
|
|
||||||
|
Custom error page with:
|
||||||
|
- Themed styling matching the cozy aesthetic
|
||||||
|
- Clear error message ("Lost in the Den?")
|
||||||
|
- Action buttons to return home or visit Gitea
|
||||||
|
- Responsive design for all devices
|
||||||
|
|
||||||
## Development Workflow
|
## Development Workflow
|
||||||
|
|
||||||
### Local Development
|
### Local Development
|
||||||
|
|||||||
+590
@@ -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! ☕🦊
|
||||||
+6
-2
@@ -29,6 +29,10 @@ The site currently has:
|
|||||||
- ✅ Support section
|
- ✅ Support section
|
||||||
- ✅ Docker deployment setup
|
- ✅ Docker deployment setup
|
||||||
- ✅ Responsive design
|
- ✅ Responsive design
|
||||||
|
- ✅ Custom 404 page with themed styling
|
||||||
|
- ✅ Sitemap integration for SEO
|
||||||
|
- ✅ robots.txt for search engines
|
||||||
|
- ✅ Accessibility improvements (ARIA labels, semantic HTML)
|
||||||
|
|
||||||
## What Might Be Added Later
|
## What Might Be Added Later
|
||||||
|
|
||||||
@@ -55,9 +59,9 @@ Main content is in `src/pages/index.astro`. Each section is wrapped in semantic
|
|||||||
|
|
||||||
### File Organization
|
### File Organization
|
||||||
- `src/layouts/` - Reusable layouts (currently just BaseLayout)
|
- `src/layouts/` - Reusable layouts (currently just BaseLayout)
|
||||||
- `src/pages/` - Routes (index.astro = homepage)
|
- `src/pages/` - Routes (index.astro = homepage, 404.astro = error page)
|
||||||
- `src/components/` - Reusable components (empty, ready for future use)
|
- `src/components/` - Reusable components (empty, ready for future use)
|
||||||
- `public/` - Static assets (favicon, images)
|
- `public/` - Static assets (favicon.svg, robots.txt)
|
||||||
|
|
||||||
## Communication Style
|
## Communication Style
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
# Cozy Den - Tasks & TODO
|
# Cozy Den - Tasks & TODO
|
||||||
|
|
||||||
## Current Status
|
## Current Status
|
||||||
✅ Initial project structure created
|
✅ Initial project structure created (proper src/ directory)
|
||||||
✅ Landing page with all main sections
|
✅ Landing page with all main sections
|
||||||
✅ Docker deployment setup
|
✅ Docker deployment setup
|
||||||
✅ Responsive design
|
✅ Responsive design
|
||||||
|
✅ Custom 404 page
|
||||||
|
✅ Sitemap integration
|
||||||
|
✅ robots.txt
|
||||||
|
✅ Accessibility improvements (ARIA labels, semantic HTML)
|
||||||
|
|
||||||
## Immediate Next Steps
|
## Immediate Next Steps
|
||||||
|
|
||||||
@@ -17,7 +21,7 @@
|
|||||||
### Optional Enhancements
|
### Optional Enhancements
|
||||||
|
|
||||||
#### Short Term
|
#### Short Term
|
||||||
- [ ] Add a custom 404 page
|
- [x] Add a custom 404 page
|
||||||
- [ ] Add favicon variants for different platforms (apple-touch-icon, etc.)
|
- [ ] Add favicon variants for different platforms (apple-touch-icon, etc.)
|
||||||
- [ ] Add Open Graph meta tags for social media sharing
|
- [ ] Add Open Graph meta tags for social media sharing
|
||||||
- [ ] Add animation on scroll for sections
|
- [ ] Add animation on scroll for sections
|
||||||
@@ -90,9 +94,9 @@ Pull repository data from Gitea API and display:
|
|||||||
- [ ] Consider breaking large components into smaller ones
|
- [ ] Consider breaking large components into smaller ones
|
||||||
- [ ] Add comments for complex CSS
|
- [ ] Add comments for complex CSS
|
||||||
- [ ] Set up automated testing if site grows
|
- [ ] Set up automated testing if site grows
|
||||||
- [ ] Add accessibility audit
|
- [x] Add accessibility audit
|
||||||
- [ ] Optimize images if any are added
|
- [ ] Optimize images if any are added
|
||||||
- [ ] Consider adding a sitemap.xml
|
- [x] Consider adding a sitemap.xml
|
||||||
|
|
||||||
## Performance Optimization
|
## Performance Optimization
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
import { defineConfig } from 'astro/config';
|
import { defineConfig } from 'astro/config';
|
||||||
|
import sitemap from '@astrojs/sitemap';
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
site: 'https://hiddenden.cafe',
|
site: 'https://hiddenden.cafe',
|
||||||
|
integrations: [
|
||||||
|
sitemap({
|
||||||
|
changefreq: 'weekly',
|
||||||
|
priority: 0.7,
|
||||||
|
lastmod: new Date(),
|
||||||
|
}),
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|||||||
-294
@@ -1,294 +0,0 @@
|
|||||||
---
|
|
||||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
|
||||||
---
|
|
||||||
|
|
||||||
<BaseLayout title="Hidden Den Cafe">
|
|
||||||
<main>
|
|
||||||
<!-- Hero Section -->
|
|
||||||
<section class="hero">
|
|
||||||
<div class="container">
|
|
||||||
<div class="hero-content fade-in">
|
|
||||||
<h1 class="hero-title">🏡 Hidden Den Cafe</h1>
|
|
||||||
<p class="hero-subtitle">A cozy corner of the internet</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- About Hidden Den Section -->
|
|
||||||
<section class="section about-den">
|
|
||||||
<div class="container">
|
|
||||||
<div class="card fade-in">
|
|
||||||
<h2>☕ 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>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- About Latte Section -->
|
|
||||||
<section class="section about-me">
|
|
||||||
<div class="container">
|
|
||||||
<div class="card fade-in">
|
|
||||||
<h2>🦊 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>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Services Section -->
|
|
||||||
<section class="section services">
|
|
||||||
<div class="container">
|
|
||||||
<div class="card fade-in">
|
|
||||||
<h2>🛠️ Services</h2>
|
|
||||||
<p>Here are the services currently running in the den:</p>
|
|
||||||
|
|
||||||
<div class="service-list">
|
|
||||||
<div class="service-item">
|
|
||||||
<h3>
|
|
||||||
<a href="https://git.hiddenden.cafe" target="_blank" rel="noopener noreferrer">
|
|
||||||
📦 Gitea
|
|
||||||
</a>
|
|
||||||
</h3>
|
|
||||||
<p>Self-hosted Git service for all my projects and code repositories.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="service-item coming-soon">
|
|
||||||
<h3>🔜 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 -->
|
|
||||||
<section class="section support">
|
|
||||||
<div class="container">
|
|
||||||
<div class="card fade-in">
|
|
||||||
<h2>💝 How to Help Out</h2>
|
|
||||||
<p>
|
|
||||||
If you'd like to support the Hidden Den and help keep the lights on,
|
|
||||||
here are some ways you can contribute:
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="support-list">
|
|
||||||
<div class="support-item">
|
|
||||||
<h3>🌟 Share & Spread the Word</h3>
|
|
||||||
<p>Tell others about the projects and services hosted here!</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="support-item">
|
|
||||||
<h3>🐛 Report Issues</h3>
|
|
||||||
<p>Found a bug or have a suggestion? Let me know!</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="support-item">
|
|
||||||
<h3>☕ 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">
|
|
||||||
<h3>🤝 Contribute</h3>
|
|
||||||
<p>Check out the projects on Gitea - contributions are always welcome!</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Footer -->
|
|
||||||
<footer class="footer">
|
|
||||||
<div class="container">
|
|
||||||
<p>Made with 💖 by Latte</p>
|
|
||||||
<p class="footer-links">
|
|
||||||
<a href="https://git.hiddenden.cafe" target="_blank" rel="noopener noreferrer">Gitea</a>
|
|
||||||
<span class="separator">•</span>
|
|
||||||
<a href="https://dmush.cloud" target="_blank" rel="noopener noreferrer">dmush.cloud</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
</main>
|
|
||||||
</BaseLayout>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
main {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
max-width: 800px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 0 var(--space-md);
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Hero Section */
|
|
||||||
.hero {
|
|
||||||
padding: var(--space-xl) 0;
|
|
||||||
text-align: center;
|
|
||||||
background: linear-gradient(135deg, var(--color-bg) 0%, var(--color-bg-light) 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-title {
|
|
||||||
font-size: 3rem;
|
|
||||||
margin-bottom: var(--space-sm);
|
|
||||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-subtitle {
|
|
||||||
font-size: 1.25rem;
|
|
||||||
color: var(--color-text-dim);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sections */
|
|
||||||
.section {
|
|
||||||
padding: var(--space-lg) 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
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 {
|
|
||||||
margin-top: auto;
|
|
||||||
padding: var(--space-lg) 0;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer-links {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
.separator {
|
|
||||||
color: var(--color-warm);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Responsive */
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.hero-title {
|
|
||||||
font-size: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hero-subtitle {
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card h2 {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
Generated
+5506
File diff suppressed because it is too large
Load Diff
+2
-1
@@ -10,6 +10,7 @@
|
|||||||
"astro": "astro"
|
"astro": "astro"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"astro": "^4.16.18"
|
"astro": "^4.16.18",
|
||||||
|
"@astrojs/sitemap": "^3.2.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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!
|
||||||
@@ -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 |
@@ -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 |
@@ -0,0 +1,16 @@
|
|||||||
|
# Hidden Den Cafe - Robots.txt
|
||||||
|
User-agent: *
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
# Sitemap
|
||||||
|
Sitemap: https://hiddenden.cafe/sitemap-index.xml
|
||||||
|
|
||||||
|
# Crawl-delay for polite crawlers
|
||||||
|
Crawl-delay: 1
|
||||||
|
|
||||||
|
# Block aggressive bots (optional - uncomment if needed)
|
||||||
|
# User-agent: AhrefsBot
|
||||||
|
# Disallow: /
|
||||||
|
|
||||||
|
# User-agent: SemrushBot
|
||||||
|
# Disallow: /
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
---
|
||||||
|
interface Props {
|
||||||
|
title: string;
|
||||||
|
description?: string;
|
||||||
|
ogImage?: string;
|
||||||
|
canonicalURL?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
title,
|
||||||
|
description = "Hidden Den Cafe - A cozy, self-hosted corner of the internet. Privacy-focused, furry-friendly, and built with love.",
|
||||||
|
ogImage = "/og-image.png",
|
||||||
|
canonicalURL = Astro.url.pathname,
|
||||||
|
} = Astro.props;
|
||||||
|
|
||||||
|
const fullCanonicalURL = new URL(canonicalURL, Astro.site).href;
|
||||||
|
const fullOgImage = new URL(ogImage, Astro.site).href;
|
||||||
|
---
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta name="generator" content={Astro.generator} />
|
||||||
|
|
||||||
|
<!-- Primary Meta Tags -->
|
||||||
|
<title>{title}</title>
|
||||||
|
<meta name="title" content={title} />
|
||||||
|
<meta name="description" content={description} />
|
||||||
|
<link rel="canonical" href={fullCanonicalURL} />
|
||||||
|
|
||||||
|
<!-- Open Graph / Facebook -->
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:url" content={fullCanonicalURL} />
|
||||||
|
<meta property="og:title" content={title} />
|
||||||
|
<meta property="og:description" content={description} />
|
||||||
|
<meta property="og:image" content={fullOgImage} />
|
||||||
|
<meta property="og:site_name" content="Hidden Den Cafe" />
|
||||||
|
<meta property="og:locale" content="en_US" />
|
||||||
|
|
||||||
|
<!-- Twitter Card -->
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<meta name="twitter:url" content={fullCanonicalURL} />
|
||||||
|
<meta name="twitter:title" content={title} />
|
||||||
|
<meta name="twitter:description" content={description} />
|
||||||
|
<meta name="twitter:image" content={fullOgImage} />
|
||||||
|
|
||||||
|
<!-- Additional Meta Tags -->
|
||||||
|
<meta name="author" content="Latte" />
|
||||||
|
<meta
|
||||||
|
name="keywords"
|
||||||
|
content="self-hosted, privacy, open-source, furry, developer, cozy, hidden den"
|
||||||
|
/>
|
||||||
|
<meta name="theme-color" content="#d4a574" />
|
||||||
|
<meta name="color-scheme" content="dark" />
|
||||||
|
|
||||||
|
<!-- Favicons -->
|
||||||
|
<link rel="icon" type="image/svg+xml" 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) -->
|
||||||
|
<!-- <link rel="preconnect" href="https://example.com" crossorigin /> -->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<slot />
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
<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;
|
||||||
|
|
||||||
|
/* Spacing */
|
||||||
|
--space-xs: 0.5rem;
|
||||||
|
--space-sm: 1rem;
|
||||||
|
--space-md: 1.5rem;
|
||||||
|
--space-lg: 2rem;
|
||||||
|
--space-xl: 3rem;
|
||||||
|
|
||||||
|
/* Typography */
|
||||||
|
--font-body:
|
||||||
|
system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
||||||
|
sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-family: var(--font-body);
|
||||||
|
background: var(--color-bg);
|
||||||
|
color: var(--color-text);
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
min-height: 100vh;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
color: var(--color-accent-bright);
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--color-accent-bright);
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: var(--color-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:focus-visible {
|
||||||
|
outline: 2px solid var(--color-accent);
|
||||||
|
outline-offset: 2px;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Smooth animations */
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-in {
|
||||||
|
animation: fadeIn 0.6s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Respect user's motion preferences */
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
animation-duration: 0.01ms !important;
|
||||||
|
animation-iteration-count: 1 !important;
|
||||||
|
transition-duration: 0.01ms !important;
|
||||||
|
scroll-behavior: auto !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Improve focus visibility for keyboard navigation */
|
||||||
|
*:focus-visible {
|
||||||
|
outline: 2px solid var(--color-accent);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Screen reader only content */
|
||||||
|
.sr-only {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
padding: 0;
|
||||||
|
margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
white-space: nowrap;
|
||||||
|
border-width: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
---
|
||||||
|
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."
|
||||||
|
>
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</BaseLayout>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.not-found-page {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: var(--space-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 600px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: var(--color-bg-light);
|
||||||
|
border-radius: 12px;
|
||||||
|
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-weight: bold;
|
||||||
|
color: var(--color-accent);
|
||||||
|
line-height: 1;
|
||||||
|
margin: 0;
|
||||||
|
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-title {
|
||||||
|
font-size: 2rem;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--color-accent-bright);
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: var(--color-text-dim);
|
||||||
|
line-height: 1.6;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
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;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
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) {
|
||||||
|
.error-code {
|
||||||
|
font-size: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary,
|
||||||
|
.btn-secondary {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,235 @@
|
|||||||
|
---
|
||||||
|
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
|
||||||
|
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."
|
||||||
|
>
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</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>
|
||||||
|
|
||||||
|
<!-- 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>
|
||||||
|
|
||||||
|
<!-- 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="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}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
<!-- Support Section -->
|
||||||
|
<Section class="support" ariaLabelledby="support-heading">
|
||||||
|
<Card>
|
||||||
|
<h2 id="support-heading">How to Help Out</h2>
|
||||||
|
<p>
|
||||||
|
If you'd like to support the Hidden Den and help keep the
|
||||||
|
lights on, here are some ways you can contribute:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="support-list" role="list">
|
||||||
|
<SupportItem
|
||||||
|
title="Share & Spread the Word"
|
||||||
|
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>
|
||||||
|
</Card>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<footer class="footer" role="contentinfo">
|
||||||
|
<div class="container">
|
||||||
|
<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>
|
||||||
|
</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;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 var(--space-md);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hero Section */
|
||||||
|
.hero {
|
||||||
|
padding: var(--space-xl) 0;
|
||||||
|
text-align: center;
|
||||||
|
background: linear-gradient(
|
||||||
|
135deg,
|
||||||
|
var(--color-bg) 0%,
|
||||||
|
var(--color-bg-light) 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-title {
|
||||||
|
font-size: 3rem;
|
||||||
|
margin-bottom: var(--space-sm);
|
||||||
|
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-subtitle {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
color: var(--color-text-dim);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Service and Support Lists */
|
||||||
|
.service-list,
|
||||||
|
.support-list {
|
||||||
|
margin-top: var(--space-md);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer */
|
||||||
|
.footer {
|
||||||
|
margin-top: auto;
|
||||||
|
padding: var(--space-lg) 0;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-links {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.hero-title {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-subtitle {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user