Initial commit: Crumbforest Architecture Refinement v1 (Clean)
This commit is contained in:
697
HOME_TEMPLATE_PLAN.md
Normal file
697
HOME_TEMPLATE_PLAN.md
Normal file
@@ -0,0 +1,697 @@
|
||||
# 🏠 Home Template System - Implementation Plan
|
||||
|
||||
## 🎯 Ziel
|
||||
|
||||
**Flexibles Home Template pro Container-Deployment:**
|
||||
- Verwendet **Pico CSS** (statt Tailwind)
|
||||
- **Deployment Config** (JSON) pro Container
|
||||
- **Branko.de Content** als Basis
|
||||
- **Multilingual** (de/en/fr)
|
||||
- **Einfach austauschbar** bei neuem Container
|
||||
|
||||
---
|
||||
|
||||
## 📁 Struktur
|
||||
|
||||
```
|
||||
compose/
|
||||
deployment_config.json # Pro Container anpassbar!
|
||||
|
||||
app/
|
||||
routers/
|
||||
home.py # Home routes (/, /about, /crew, etc.)
|
||||
|
||||
templates/
|
||||
home/
|
||||
base_home.html # Base für Home (ohne Auth)
|
||||
index.html # Landing Page
|
||||
about.html # Mission / Wurzeln
|
||||
crew.html # Character Cards
|
||||
hardware.html # Hardware Info
|
||||
software.html # Software Info
|
||||
contact.html # Kontakt / Impressum
|
||||
|
||||
static/
|
||||
css/
|
||||
home_default.css # Standard Home Theme
|
||||
home_forest.css # Wald Theme (dunkel/grün)
|
||||
home_light.css # Hell Theme
|
||||
|
||||
data/
|
||||
testimonials.de.json # Testimonials Deutsch
|
||||
testimonials.en.json # English
|
||||
testimonials.fr.json # Français
|
||||
characters.json # Character Definitions
|
||||
|
||||
assets/
|
||||
logo.png
|
||||
hero_bg.jpg
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 1. Deployment Config
|
||||
|
||||
**`compose/deployment_config.json`**
|
||||
|
||||
```json
|
||||
{
|
||||
"deployment_id": "crumbforest_main",
|
||||
"deployment_name": "Crumbforest Main",
|
||||
"base_url": "https://branko.de",
|
||||
|
||||
"home": {
|
||||
"enabled": true,
|
||||
"theme": "forest",
|
||||
"default_lang": "de",
|
||||
"languages": ["de", "en", "fr"],
|
||||
|
||||
"hero": {
|
||||
"title": "🌳 Crumbforest",
|
||||
"subtitle": "Wo Fragen wachsen. Und jeder Krümel zählt.",
|
||||
"cta_text": "Den Wald entdecken",
|
||||
"cta_link": "#explore"
|
||||
},
|
||||
|
||||
"mission": {
|
||||
"title": "🌲 Unsere Wurzeln",
|
||||
"description": "Crumbforest ist ein offenes Lern-Ökosystem mit Kindern, Maschinen und Natur.",
|
||||
"values": [
|
||||
{
|
||||
"icon": "🦉",
|
||||
"title": "Fragen",
|
||||
"text": "Jedes Kind darf fragen. Wir schützen dieses Recht in jedem Terminal."
|
||||
},
|
||||
{
|
||||
"icon": "🛠️",
|
||||
"title": "Bauen",
|
||||
"text": "Hands-on Lernen mit Raspberry Pi, Bash, Blockly und mehr."
|
||||
},
|
||||
{
|
||||
"icon": "🌐",
|
||||
"title": "Verbinden",
|
||||
"text": "Unsere Rollen und APIs bilden ein Resonanz-Netz."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"sections": {
|
||||
"testimonials": true,
|
||||
"crew": true,
|
||||
"hardware": true,
|
||||
"software": true,
|
||||
"contact": true
|
||||
},
|
||||
|
||||
"navigation": [
|
||||
{"label": "Home", "url": "/", "icon": "🏠"},
|
||||
{"label": "Mission", "url": "/about", "icon": "🌲"},
|
||||
{"label": "Crew", "url": "/crew", "icon": "🌟"},
|
||||
{"label": "Hardware", "url": "/hardware", "icon": "🔧"},
|
||||
{"label": "Software", "url": "/software", "icon": "💻"},
|
||||
{"label": "Login", "url": "/de/login", "icon": "🔐"}
|
||||
],
|
||||
|
||||
"footer": {
|
||||
"tagline": "Made with 💚 in the Crumbforest",
|
||||
"links": [
|
||||
{"label": "Impressum", "url": "/impressum"},
|
||||
{"label": "Datenschutz", "url": "/datenschutz"}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
"features": {
|
||||
"rag_system": true,
|
||||
"diary_system": true,
|
||||
"document_search": true,
|
||||
"roles_web": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 2. Pico CSS Home Theme
|
||||
|
||||
**`app/static/css/home_forest.css`**
|
||||
|
||||
```css
|
||||
/* Crumbforest Forest Theme - Dark & Green */
|
||||
:root {
|
||||
--pico-font-family: "Inter", system-ui, sans-serif;
|
||||
|
||||
/* Forest Colors */
|
||||
--pico-primary: #10b981; /* Emerald */
|
||||
--pico-primary-hover: #059669;
|
||||
--pico-primary-focus: rgba(16, 185, 129, 0.125);
|
||||
|
||||
--pico-background-color: #0f172a; /* Dark Blue-Gray */
|
||||
--pico-color: #e2e8f0; /* Light Gray */
|
||||
|
||||
/* Gradients */
|
||||
--hero-gradient: linear-gradient(135deg, #064e3b 0%, #10b981 100%);
|
||||
--section-accent: #1e293b;
|
||||
}
|
||||
|
||||
/* Hero Section */
|
||||
.hero {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--hero-gradient);
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-size: 4rem;
|
||||
margin-bottom: 1rem;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.hero p {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
max-width: 600px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.hero .cta-button {
|
||||
background: white;
|
||||
color: black;
|
||||
padding: 1rem 2rem;
|
||||
border-radius: 2rem;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.hero .cta-button:hover {
|
||||
background: #fbbf24; /* Yellow */
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
/* Language Switcher */
|
||||
.lang-switcher {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
justify-content: center;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.lang-switcher a {
|
||||
background: white;
|
||||
color: black;
|
||||
padding: 0.5rem 1.5rem;
|
||||
border-radius: 2rem;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Mission Section */
|
||||
.mission {
|
||||
padding: 4rem 2rem;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mission h2 {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.values-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 2rem;
|
||||
margin-top: 2rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.value-card {
|
||||
background: var(--section-accent);
|
||||
padding: 2rem;
|
||||
border-radius: 1rem;
|
||||
}
|
||||
|
||||
.value-card h3 {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
/* Character Cards */
|
||||
.crew-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 1.5rem;
|
||||
padding: 2rem;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.character-card {
|
||||
background: var(--section-accent);
|
||||
padding: 1.5rem;
|
||||
border-radius: 1rem;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.character-card:hover {
|
||||
background: var(--pico-primary);
|
||||
transform: translateY(-5px);
|
||||
border-color: var(--pico-primary-hover);
|
||||
}
|
||||
|
||||
.character-card .icon {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
/* Modal */
|
||||
dialog {
|
||||
border-radius: 1rem;
|
||||
border: none;
|
||||
padding: 2rem;
|
||||
max-width: 600px;
|
||||
background: var(--pico-background-color);
|
||||
}
|
||||
|
||||
dialog::backdrop {
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
/* Testimonials */
|
||||
.testimonials {
|
||||
background: #7c3aed; /* Purple */
|
||||
padding: 4rem 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.testimonial-slide {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.testimonial-slide p {
|
||||
font-size: 1.25rem;
|
||||
font-style: italic;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.hero h1 {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
.hero p {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.values-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧩 3. Base Home Template
|
||||
|
||||
**`app/templates/home/base_home.html`**
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ lang }}" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title>{% block title %}{{ deployment.home.hero.title }}{% endblock %}</title>
|
||||
|
||||
<!-- SEO -->
|
||||
<meta name="description" content="{% block description %}{{ deployment.home.mission.description }}{% endblock %}">
|
||||
<meta name="keywords" content="Crumbforest, Kinderfragen, Lernen, Terminal, Raspberry Pi, Open Source">
|
||||
<meta name="author" content="Die Crumbforest-Crew">
|
||||
<meta name="robots" content="index, follow">
|
||||
|
||||
<!-- Open Graph -->
|
||||
<meta property="og:title" content="{% block og_title %}{{ deployment.home.hero.title }}{% endblock %}">
|
||||
<meta property="og:description" content="{{ deployment.home.hero.subtitle }}">
|
||||
<meta property="og:url" content="{{ deployment.base_url }}">
|
||||
|
||||
<!-- CSS -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
|
||||
<link rel="stylesheet" href="/static/css/home_{{ deployment.home.theme }}.css">
|
||||
|
||||
{% block extra_css %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<!-- Navigation -->
|
||||
<nav class="container-fluid">
|
||||
<ul>
|
||||
<li><strong>{{ deployment.home.hero.title }}</strong></li>
|
||||
</ul>
|
||||
<ul>
|
||||
{% for nav_item in deployment.home.navigation %}
|
||||
<li><a href="{{ nav_item.url }}">{{ nav_item.icon }} {{ nav_item.label }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<!-- Main Content -->
|
||||
{% block content %}{% endblock %}
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="container">
|
||||
<small>
|
||||
{{ deployment.home.footer.tagline }}
|
||||
{% for link in deployment.home.footer.links %}
|
||||
| <a href="{{ link.url }}">{{ link.label }}</a>
|
||||
{% endfor %}
|
||||
</small>
|
||||
</footer>
|
||||
|
||||
{% block extra_js %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🏠 4. Home Index Template
|
||||
|
||||
**`app/templates/home/index.html`**
|
||||
|
||||
```html
|
||||
{% extends "home/base_home.html" %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Hero Section -->
|
||||
<section class="hero">
|
||||
<div>
|
||||
<h1>{{ deployment.home.hero.title }}</h1>
|
||||
<p>{{ deployment.home.hero.subtitle }}</p>
|
||||
|
||||
<a href="{{ deployment.home.hero.cta_link }}" class="cta-button">
|
||||
{{ deployment.home.hero.cta_text }}
|
||||
</a>
|
||||
|
||||
<!-- Language Switcher -->
|
||||
<div class="lang-switcher">
|
||||
{% for lang_code in deployment.home.languages %}
|
||||
<a href="?lang={{ lang_code }}">{{ lang_code.upper() }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Mission Section -->
|
||||
<section id="explore" class="mission">
|
||||
<h2>{{ deployment.home.mission.title }}</h2>
|
||||
<p>{{ deployment.home.mission.description }}</p>
|
||||
|
||||
<div class="values-grid">
|
||||
{% for value in deployment.home.mission.values %}
|
||||
<div class="value-card">
|
||||
<h3>{{ value.icon }} {{ value.title }}</h3>
|
||||
<p>{{ value.text }}</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% if deployment.home.sections.testimonials %}
|
||||
<!-- Testimonials -->
|
||||
<section class="testimonials">
|
||||
<h2>💬 Stimmen aus dem Crumbforest</h2>
|
||||
|
||||
<div class="testimonial-slide" id="testimonial-container">
|
||||
<p id="testimonial-text"></p>
|
||||
<small id="testimonial-author"></small>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; justify-content: center; gap: 2rem; margin-top: 2rem;">
|
||||
<button onclick="prevTestimonial()">⬅️</button>
|
||||
<button onclick="nextTestimonial()">➡️</button>
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
{% if deployment.home.sections.crew %}
|
||||
<!-- Character Preview -->
|
||||
<section class="container">
|
||||
<h2 style="text-align: center;">🌟 Lerne die Crew kennen</h2>
|
||||
<div style="text-align: center; margin: 2rem 0;">
|
||||
<a href="/crew" role="button">Alle Characters entdecken</a>
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
<!-- Access Section -->
|
||||
<section class="container">
|
||||
<h2 style="text-align: center;">🌐 Zugang zum Wald</h2>
|
||||
<div style="display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap;">
|
||||
{% if deployment.features.rag_system %}
|
||||
<a href="/de/login" role="button">RAG System</a>
|
||||
{% endif %}
|
||||
{% if deployment.features.document_search %}
|
||||
<a href="/de/login" role="button">Document Search</a>
|
||||
{% endif %}
|
||||
<a href="{{ deployment.base_url }}/hardware" role="button" class="outline">Hardware Info</a>
|
||||
<a href="{{ deployment.base_url }}/software" role="button" class="outline">Software Info</a>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script>
|
||||
let testimonials = [];
|
||||
let currentIndex = 0;
|
||||
|
||||
fetch('/static/data/testimonials.{{ lang }}.json')
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
testimonials = data;
|
||||
showTestimonial(0);
|
||||
setInterval(nextTestimonial, 8000);
|
||||
});
|
||||
|
||||
function showTestimonial(index) {
|
||||
const t = testimonials[index];
|
||||
document.getElementById('testimonial-text').textContent = `"${t.message}"`;
|
||||
document.getElementById('testimonial-author').textContent = `– ${t.author}, ${t.role}`;
|
||||
}
|
||||
|
||||
function nextTestimonial() {
|
||||
currentIndex = (currentIndex + 1) % testimonials.length;
|
||||
showTestimonial(currentIndex);
|
||||
}
|
||||
|
||||
function prevTestimonial() {
|
||||
currentIndex = (currentIndex - 1 + testimonials.length) % testimonials.length;
|
||||
showTestimonial(currentIndex);
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🦉 5. Crew Page Template
|
||||
|
||||
**`app/templates/home/crew.html`**
|
||||
|
||||
```html
|
||||
{% extends "home/base_home.html" %}
|
||||
|
||||
{% block title %}Crew - {{ deployment.home.hero.title }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main class="container">
|
||||
<hgroup>
|
||||
<h1>🌟 Die Crumbforest Crew</h1>
|
||||
<p>Lerne unsere Characters kennen!</p>
|
||||
</hgroup>
|
||||
|
||||
<div class="crew-grid">
|
||||
{% for character in characters %}
|
||||
<div class="character-card" onclick="showCharacter('{{ character.id }}')">
|
||||
<div class="icon">{{ character.icon }}</div>
|
||||
<h3>{{ character.name }}</h3>
|
||||
<p>{{ character.short }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Dialog for this character -->
|
||||
<dialog id="dialog-{{ character.id }}">
|
||||
<article>
|
||||
<header>
|
||||
<button aria-label="Close" rel="prev" onclick="closeCharacter('{{ character.id }}')"></button>
|
||||
<h2>{{ character.icon }} {{ character.name }}</h2>
|
||||
</header>
|
||||
<p>{{ character.description }}</p>
|
||||
</article>
|
||||
</dialog>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script>
|
||||
function showCharacter(id) {
|
||||
document.getElementById('dialog-' + id).showModal();
|
||||
}
|
||||
|
||||
function closeCharacter(id) {
|
||||
document.getElementById('dialog-' + id).close();
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔌 6. FastAPI Router
|
||||
|
||||
**`app/routers/home.py`**
|
||||
|
||||
```python
|
||||
from fastapi import APIRouter, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
import json
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
# Load deployment config
|
||||
with open('compose/deployment_config.json') as f:
|
||||
deployment_config = json.load(f)
|
||||
|
||||
# Load characters
|
||||
with open('app/static/data/characters.json') as f:
|
||||
characters_data = json.load(f)
|
||||
|
||||
@router.get("/", response_class=HTMLResponse)
|
||||
async def home_index(req: Request, lang: str = "de"):
|
||||
"""
|
||||
Public home page - no auth required.
|
||||
"""
|
||||
req.session["lang"] = lang
|
||||
|
||||
return req.app.state.render(
|
||||
req,
|
||||
"home/index.html",
|
||||
deployment=deployment_config,
|
||||
lang=lang
|
||||
)
|
||||
|
||||
@router.get("/about", response_class=HTMLResponse)
|
||||
async def home_about(req: Request):
|
||||
"""
|
||||
About / Mission page.
|
||||
"""
|
||||
lang = req.session.get("lang", "de")
|
||||
|
||||
return req.app.state.render(
|
||||
req,
|
||||
"home/about.html",
|
||||
deployment=deployment_config,
|
||||
lang=lang
|
||||
)
|
||||
|
||||
@router.get("/crew", response_class=HTMLResponse)
|
||||
async def home_crew(req: Request):
|
||||
"""
|
||||
Crew / Characters page.
|
||||
"""
|
||||
lang = req.session.get("lang", "de")
|
||||
|
||||
return req.app.state.render(
|
||||
req,
|
||||
"home/crew.html",
|
||||
deployment=deployment_config,
|
||||
characters=characters_data,
|
||||
lang=lang
|
||||
)
|
||||
|
||||
@router.get("/hardware", response_class=HTMLResponse)
|
||||
async def home_hardware(req: Request):
|
||||
lang = req.session.get("lang", "de")
|
||||
|
||||
return req.app.state.render(
|
||||
req,
|
||||
"home/hardware.html",
|
||||
deployment=deployment_config,
|
||||
lang=lang
|
||||
)
|
||||
|
||||
@router.get("/software", response_class=HTMLResponse)
|
||||
async def home_software(req: Request):
|
||||
lang = req.session.get("lang", "de")
|
||||
|
||||
return req.app.state.render(
|
||||
req,
|
||||
"home/software.html",
|
||||
deployment=deployment_config,
|
||||
lang=lang
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 7. Implementation Steps
|
||||
|
||||
### Phase 1: Foundation (2-3 Stunden)
|
||||
- [ ] `deployment_config.json` erstellen
|
||||
- [ ] `home_forest.css` Pico Theme
|
||||
- [ ] `base_home.html` Template
|
||||
- [ ] `/routers/home.py` Router
|
||||
|
||||
### Phase 2: Content (2-3 Stunden)
|
||||
- [ ] `index.html` Landing Page
|
||||
- [ ] `crew.html` Character Page
|
||||
- [ ] `characters.json` & `testimonials.*.json` Daten
|
||||
- [ ] Branko.de Texte übernehmen
|
||||
|
||||
### Phase 3: Testing (1 Stunde)
|
||||
- [ ] Multilingual testen (de/en/fr)
|
||||
- [ ] Mobile Responsive prüfen
|
||||
- [ ] Navigation Flow
|
||||
- [ ] Character Modals
|
||||
|
||||
### Phase 4: Deployment (30 Min)
|
||||
- [ ] Docker Rebuild
|
||||
- [ ] Config per Container anpassbar
|
||||
- [ ] Verify Home vs Admin Separation
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Vorteile
|
||||
|
||||
✅ **Pico CSS** - Konsistent mit dem Rest des Systems
|
||||
✅ **Deployment Config** - Neues Design = neue JSON
|
||||
✅ **Kein Auth** - Home ist öffentlich, Login für Features
|
||||
✅ **Multilingual** - de/en/fr Support
|
||||
✅ **Flexibel** - Jeder Container kann eigenes Design haben
|
||||
✅ **Characters** - Branko.de Crew integriert
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Next Step?
|
||||
|
||||
Soll ich mit **Phase 1** starten?
|
||||
- deployment_config.json
|
||||
- home_forest.css (Pico-basiert)
|
||||
- base_home.html
|
||||
- /routers/home.py
|
||||
|
||||
Dann hast du ein lauffähiges Home Template! 🌲
|
||||
Reference in New Issue
Block a user