feat: molzi3d.de v1.0.0 — Komplettes Redesign mit Next.js 16
- WordPress durch Next.js 16 + Tailwind CSS v4 + Framer Motion ersetzt - 44 Guides + 15 Seiten aus WordPress migriert (HTML -> Markdown) - Emerald Design-System mit Light/Dark Mode Toggle - Sidebar-First Navigation (Dokumentations-Stil) - Difficulty-Badges, Lesezeit, verwandte Guides - Statischer Export fuer Plesk-Hosting - WordPress-DB Backup gesichert (6.2 MB) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
27
app/src/components/ui/DifficultyBadge.tsx
Normal file
27
app/src/components/ui/DifficultyBadge.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
interface DifficultyBadgeProps {
|
||||
level: "einsteiger" | "fortgeschritten" | "experte";
|
||||
}
|
||||
|
||||
const config = {
|
||||
einsteiger: {
|
||||
label: "Einsteiger",
|
||||
className: "bg-emerald-100 text-emerald-800 dark:bg-emerald-900/30 dark:text-emerald-400",
|
||||
},
|
||||
fortgeschritten: {
|
||||
label: "Fortgeschritten",
|
||||
className: "bg-amber-100 text-amber-800 dark:bg-amber-900/30 dark:text-amber-400",
|
||||
},
|
||||
experte: {
|
||||
label: "Experte",
|
||||
className: "bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400",
|
||||
},
|
||||
};
|
||||
|
||||
export const DifficultyBadge = ({ level }: DifficultyBadgeProps) => {
|
||||
const { label, className } = config[level];
|
||||
return (
|
||||
<span className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${className}`}>
|
||||
{label}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
20
app/src/components/ui/SearchButton.tsx
Normal file
20
app/src/components/ui/SearchButton.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
"use client";
|
||||
|
||||
export const SearchButton = () => (
|
||||
<button
|
||||
className="flex items-center gap-2 px-3 py-1.5 rounded-md border border-border text-sm text-muted-foreground hover:text-foreground hover:border-accent/50 transition-colors"
|
||||
onClick={() => {
|
||||
// TODO: Search-Modal oeffnen
|
||||
document.dispatchEvent(new CustomEvent("open-search"));
|
||||
}}
|
||||
>
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<circle cx="11" cy="11" r="8" />
|
||||
<path d="M21 21l-4.35-4.35" />
|
||||
</svg>
|
||||
<span className="hidden sm:inline">Suche...</span>
|
||||
<kbd className="hidden sm:inline-flex items-center text-xs border border-border rounded px-1.5 py-0.5 ml-2">
|
||||
⌘K
|
||||
</kbd>
|
||||
</button>
|
||||
);
|
||||
25
app/src/components/ui/ThemeToggle.tsx
Normal file
25
app/src/components/ui/ThemeToggle.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
"use client";
|
||||
|
||||
interface ThemeToggleProps {
|
||||
theme: "light" | "dark";
|
||||
onToggle: () => void;
|
||||
}
|
||||
|
||||
export const ThemeToggle = ({ theme, onToggle }: ThemeToggleProps) => (
|
||||
<button
|
||||
onClick={onToggle}
|
||||
className="p-2 rounded-md text-muted-foreground hover:text-foreground hover:bg-surface-hover transition-colors"
|
||||
aria-label={`Wechsle zu ${theme === "light" ? "Dark" : "Light"} Mode`}
|
||||
>
|
||||
{theme === "light" ? (
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
|
||||
</svg>
|
||||
) : (
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<circle cx="12" cy="12" r="5" />
|
||||
<path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42" />
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
Reference in New Issue
Block a user