feat(blog): restore typography ratios and implement smooth filter animations
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 6s
Build & Deploy / 🧪 QA (push) Failing after 1m13s
Build & Deploy / 🏗️ Build (push) Failing after 10m57s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🩺 Health Check (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 1s
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 6s
Build & Deploy / 🧪 QA (push) Failing after 1m13s
Build & Deploy / 🏗️ Build (push) Failing after 10m57s
Build & Deploy / 🚀 Deploy (push) Has been skipped
Build & Deploy / 🩺 Health Check (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 1s
This commit is contained in:
@@ -83,7 +83,7 @@ export default async function BlogPostPage({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Section containerVariant="wide" className="pt-0 md:pt-0">
|
<Section containerVariant="wide" className="pt-0 md:pt-0">
|
||||||
<div className="max-w-3xl mx-auto px-5 md:px-0">
|
<div className="max-w-4xl mx-auto px-5 md:px-0">
|
||||||
<Reveal delay={0.4} width="100%">
|
<Reveal delay={0.4} width="100%">
|
||||||
{post.tags && post.tags.length > 0 && (
|
{post.tags && post.tags.length > 0 && (
|
||||||
<div className="flex flex-wrap gap-2 mb-10 md:mb-12">
|
<div className="flex flex-wrap gap-2 mb-10 md:mb-12">
|
||||||
@@ -98,7 +98,7 @@ export default async function BlogPostPage({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="max-w-none">
|
<div className="article-content max-w-none">
|
||||||
<MDXRemote
|
<MDXRemote
|
||||||
source={post.body.raw.replace(/^---[\s\S]*?\n---\s*/, '')}
|
source={post.body.raw.replace(/^---[\s\S]*?\n---\s*/, '')}
|
||||||
components={MDXComponents}
|
components={MDXComponents}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import { Section } from "../../src/components/Section";
|
|||||||
import { AbstractCircuit, GradientMesh } from "../../src/components/Effects";
|
import { AbstractCircuit, GradientMesh } from "../../src/components/Effects";
|
||||||
import { useAnalytics } from "../../src/components/analytics/useAnalytics";
|
import { useAnalytics } from "../../src/components/analytics/useAnalytics";
|
||||||
|
|
||||||
|
import { motion, AnimatePresence } from "framer-motion";
|
||||||
|
|
||||||
export default function BlogPage() {
|
export default function BlogPage() {
|
||||||
const [searchQuery, setSearchQuery] = useState("");
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
const [activeTags, setActiveTags] = useState<string[]>([]);
|
const [activeTags, setActiveTags] = useState<string[]>([]);
|
||||||
@@ -133,35 +135,55 @@ export default function BlogPage() {
|
|||||||
<div className="space-y-12 relative z-10 p-4 md:p-0">
|
<div className="space-y-12 relative z-10 p-4 md:p-0">
|
||||||
{/* Posts List (Vertical & Minimal) */}
|
{/* Posts List (Vertical & Minimal) */}
|
||||||
<div id="posts-container" className="space-y-12">
|
<div id="posts-container" className="space-y-12">
|
||||||
{postsToShow.length === 0 ? (
|
<AnimatePresence mode="popLayout" initial={false}>
|
||||||
<div className="py-24 text-center border border-dashed border-slate-200 rounded-3xl bg-white/50">
|
{postsToShow.length === 0 ? (
|
||||||
<p className="text-slate-400 font-mono text-sm uppercase tracking-widest">
|
<motion.div
|
||||||
Keine Beiträge gefunden.
|
key="no-results"
|
||||||
</p>
|
initial={{ opacity: 0, y: 20 }}
|
||||||
<button
|
animate={{ opacity: 1, y: 0 }}
|
||||||
onClick={() => {
|
exit={{ opacity: 0, scale: 0.95 }}
|
||||||
setSearchQuery("");
|
className="py-24 text-center border border-dashed border-slate-200 rounded-3xl bg-white/50"
|
||||||
setActiveTags([]);
|
|
||||||
}}
|
|
||||||
className="mt-4 text-xs font-bold text-slate-900 underline underline-offset-4 hover:text-slate-600"
|
|
||||||
>
|
>
|
||||||
Filter zurücksetzen
|
<p className="text-slate-400 font-mono text-sm uppercase tracking-widest">
|
||||||
</button>
|
Keine Beiträge gefunden.
|
||||||
</div>
|
</p>
|
||||||
) : (
|
<button
|
||||||
<div className="grid grid-cols-1 gap-6 w-full">
|
onClick={() => {
|
||||||
{postsToShow.map((post, i) => (
|
setSearchQuery("");
|
||||||
<Reveal
|
setActiveTags([]);
|
||||||
key={post.slug}
|
}}
|
||||||
delay={0.05 * i}
|
className="mt-4 text-xs font-bold text-slate-900 underline underline-offset-4 hover:text-slate-600"
|
||||||
width="100%"
|
|
||||||
viewport={{ once: true, margin: "20% 0px" }} // Added more margin for better back-navigation detection
|
|
||||||
>
|
>
|
||||||
<MediumCard post={post} />
|
Filter zurücksetzen
|
||||||
</Reveal>
|
</button>
|
||||||
))}
|
</motion.div>
|
||||||
</div>
|
) : (
|
||||||
)}
|
<motion.div
|
||||||
|
key="post-grid"
|
||||||
|
layout
|
||||||
|
className="grid grid-cols-1 gap-6 w-full"
|
||||||
|
>
|
||||||
|
<AnimatePresence mode="popLayout">
|
||||||
|
{postsToShow.map((post, i) => (
|
||||||
|
<motion.div
|
||||||
|
key={post.slug}
|
||||||
|
layout
|
||||||
|
initial={{ opacity: 0, scale: 0.9 }}
|
||||||
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
|
exit={{ opacity: 0, scale: 0.9 }}
|
||||||
|
transition={{
|
||||||
|
duration: 0.4,
|
||||||
|
delay: i < 8 ? i * 0.05 : 0,
|
||||||
|
ease: [0.16, 1, 0.3, 1]
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MediumCard post={post} />
|
||||||
|
</motion.div>
|
||||||
|
))}
|
||||||
|
</AnimatePresence>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
|
||||||
{/* Pagination */}
|
{/* Pagination */}
|
||||||
{hasMore && (
|
{hasMore && (
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
@apply mb-4 text-sm md:text-base leading-relaxed text-slate-700;
|
@apply mb-6 text-base leading-relaxed text-slate-700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lead {
|
.lead {
|
||||||
@@ -86,6 +86,7 @@
|
|||||||
|
|
||||||
/* Components - Tailwind utility classes */
|
/* Components - Tailwind utility classes */
|
||||||
@layer components {
|
@layer components {
|
||||||
|
|
||||||
/* Legacy hooks required by tests */
|
/* Legacy hooks required by tests */
|
||||||
.file-example {
|
.file-example {
|
||||||
@apply w-full;
|
@apply w-full;
|
||||||
@@ -149,32 +150,36 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.article-content {
|
.article-content {
|
||||||
@apply text-lg leading-relaxed;
|
@apply font-serif antialiased text-slate-700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-content p {
|
.article-content p {
|
||||||
@apply mb-5;
|
@apply text-lg md:text-xl leading-relaxed mb-6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-content h1 {
|
||||||
|
@apply text-3xl md:text-5xl font-bold text-slate-900 mb-8 mt-12 tracking-tight leading-[1.1] font-sans;
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-content h2 {
|
.article-content h2 {
|
||||||
@apply text-2xl font-bold mt-8 mb-3;
|
@apply text-2xl md:text-4xl font-bold text-slate-900 mb-6 mt-10 tracking-tight leading-tight font-sans;
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-content h3 {
|
.article-content h3 {
|
||||||
@apply text-xl font-bold mt-6 mb-2;
|
@apply text-xl md:text-2xl font-bold text-slate-900 mb-4 mt-8 tracking-tight leading-snug font-sans;
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-content ul,
|
.article-content ul,
|
||||||
.article-content ol {
|
.article-content ol {
|
||||||
@apply ml-6 mb-5;
|
@apply ml-6 mb-6 text-lg md:text-xl;
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-content li {
|
.article-content li {
|
||||||
@apply mb-1;
|
@apply mb-2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.article-content blockquote {
|
.article-content blockquote {
|
||||||
@apply border-l-2 border-slate-400 pl-4 italic text-slate-600 my-5 text-lg;
|
@apply border-l-4 border-slate-900 pl-6 italic text-slate-700 my-10 text-xl md:text-2xl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Buttons */
|
/* Buttons */
|
||||||
@@ -286,6 +291,7 @@
|
|||||||
|
|
||||||
/* Print styles */
|
/* Print styles */
|
||||||
@media print {
|
@media print {
|
||||||
|
|
||||||
.floating-back-to-top,
|
.floating-back-to-top,
|
||||||
.reading-progress-bar {
|
.reading-progress-bar {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
@@ -344,38 +350,30 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.highlighter-yellow {
|
.highlighter-yellow {
|
||||||
background: linear-gradient(
|
background: linear-gradient(135deg,
|
||||||
135deg,
|
rgba(255, 235, 59, 0.95) 0%,
|
||||||
rgba(255, 235, 59, 0.95) 0%,
|
rgba(255, 213, 79, 0.95) 100%);
|
||||||
rgba(255, 213, 79, 0.95) 100%
|
|
||||||
);
|
|
||||||
color: #3f2f00;
|
color: #3f2f00;
|
||||||
}
|
}
|
||||||
|
|
||||||
.highlighter-pink {
|
.highlighter-pink {
|
||||||
background: linear-gradient(
|
background: linear-gradient(135deg,
|
||||||
135deg,
|
rgba(255, 167, 209, 0.95) 0%,
|
||||||
rgba(255, 167, 209, 0.95) 0%,
|
rgba(255, 122, 175, 0.95) 100%);
|
||||||
rgba(255, 122, 175, 0.95) 100%
|
|
||||||
);
|
|
||||||
color: #3f0018;
|
color: #3f0018;
|
||||||
}
|
}
|
||||||
|
|
||||||
.highlighter-green {
|
.highlighter-green {
|
||||||
background: linear-gradient(
|
background: linear-gradient(135deg,
|
||||||
135deg,
|
rgba(129, 199, 132, 0.95) 0%,
|
||||||
rgba(129, 199, 132, 0.95) 0%,
|
rgba(102, 187, 106, 0.95) 100%);
|
||||||
rgba(102, 187, 106, 0.95) 100%
|
|
||||||
);
|
|
||||||
color: #002f0a;
|
color: #002f0a;
|
||||||
}
|
}
|
||||||
|
|
||||||
.highlighter-blue {
|
.highlighter-blue {
|
||||||
background: linear-gradient(
|
background: linear-gradient(135deg,
|
||||||
135deg,
|
rgba(226, 232, 240, 0.95) 0%,
|
||||||
rgba(226, 232, 240, 0.95) 0%,
|
rgba(203, 213, 225, 0.95) 100%);
|
||||||
rgba(203, 213, 225, 0.95) 100%
|
|
||||||
);
|
|
||||||
color: #0f172a;
|
color: #0f172a;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,17 +409,14 @@
|
|||||||
border-radius: 0.18em;
|
border-radius: 0.18em;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
|
|
||||||
background: linear-gradient(
|
background: linear-gradient(180deg,
|
||||||
180deg,
|
rgba(255, 255, 255, 0) 0%,
|
||||||
rgba(255, 255, 255, 0) 0%,
|
rgba(255, 255, 255, 0) 20%,
|
||||||
rgba(255, 255, 255, 0) 20%,
|
rgba(253, 230, 138, 0.7) 20%,
|
||||||
rgba(253, 230, 138, 0.7) 20%,
|
rgba(253, 230, 138, 0.7) 100%);
|
||||||
rgba(253, 230, 138, 0.7) 100%
|
|
||||||
);
|
|
||||||
|
|
||||||
transform-origin: left center;
|
transform-origin: left center;
|
||||||
transform: rotate(calc((var(--marker-seed, 0) - 3) * 0.45deg))
|
transform: rotate(calc((var(--marker-seed, 0) - 3) * 0.45deg)) skewX(calc((var(--marker-seed, 0) - 3) * -0.25deg));
|
||||||
skewX(calc((var(--marker-seed, 0) - 3) * -0.25deg));
|
|
||||||
|
|
||||||
filter: saturate(1.05);
|
filter: saturate(1.05);
|
||||||
}
|
}
|
||||||
@@ -436,28 +431,24 @@
|
|||||||
border-radius: 0.18em;
|
border-radius: 0.18em;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
|
|
||||||
background: linear-gradient(
|
background: linear-gradient(90deg,
|
||||||
90deg,
|
rgba(253, 230, 138, 0) 0%,
|
||||||
rgba(253, 230, 138, 0) 0%,
|
rgba(253, 230, 138, 0.6) 8%,
|
||||||
rgba(253, 230, 138, 0.6) 8%,
|
rgba(253, 230, 138, 0.55) 60%,
|
||||||
rgba(253, 230, 138, 0.55) 60%,
|
rgba(253, 230, 138, 0.35) 100%);
|
||||||
rgba(253, 230, 138, 0.35) 100%
|
|
||||||
);
|
|
||||||
|
|
||||||
opacity: 0.75;
|
opacity: 0.75;
|
||||||
mix-blend-mode: multiply;
|
mix-blend-mode: multiply;
|
||||||
transform: rotate(calc((var(--marker-seed, 0) - 3) * 0.45deg))
|
transform: rotate(calc((var(--marker-seed, 0) - 3) * 0.45deg)) translateY(0.02em);
|
||||||
translateY(0.02em);
|
|
||||||
|
|
||||||
mask-image: linear-gradient(
|
mask-image: linear-gradient(180deg,
|
||||||
180deg,
|
rgba(0, 0, 0, 0) 0%,
|
||||||
rgba(0, 0, 0, 0) 0%,
|
rgba(0, 0, 0, 1) 20%,
|
||||||
rgba(0, 0, 0, 1) 20%,
|
rgba(0, 0, 0, 1) 100%);
|
||||||
rgba(0, 0, 0, 1) 100%
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
|
||||||
.post-link:hover .marker-title::before,
|
.post-link:hover .marker-title::before,
|
||||||
.post-link:hover .marker-title::after {
|
.post-link:hover .marker-title::after {
|
||||||
filter: saturate(1.08) contrast(1.02);
|
filter: saturate(1.08) contrast(1.02);
|
||||||
@@ -720,6 +711,7 @@ pre:has(code[class*="language-"]) {
|
|||||||
|
|
||||||
/* Gradient Mesh Blob Animations */
|
/* Gradient Mesh Blob Animations */
|
||||||
@keyframes gradient-blob-1 {
|
@keyframes gradient-blob-1 {
|
||||||
|
|
||||||
0%,
|
0%,
|
||||||
100% {
|
100% {
|
||||||
transform: translate(0, 0) scale(1);
|
transform: translate(0, 0) scale(1);
|
||||||
@@ -739,6 +731,7 @@ pre:has(code[class*="language-"]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes gradient-blob-2 {
|
@keyframes gradient-blob-2 {
|
||||||
|
|
||||||
0%,
|
0%,
|
||||||
100% {
|
100% {
|
||||||
transform: translate(0, 0) scale(1);
|
transform: translate(0, 0) scale(1);
|
||||||
@@ -754,6 +747,7 @@ pre:has(code[class*="language-"]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes gradient-blob-3 {
|
@keyframes gradient-blob-3 {
|
||||||
|
|
||||||
0%,
|
0%,
|
||||||
100% {
|
100% {
|
||||||
transform: translate(0, 0) scale(1);
|
transform: translate(0, 0) scale(1);
|
||||||
@@ -789,6 +783,7 @@ pre:has(code[class*="language-"]) {
|
|||||||
|
|
||||||
/* Circuit Pulse (used for node glow effects) */
|
/* Circuit Pulse (used for node glow effects) */
|
||||||
@keyframes circuit-pulse {
|
@keyframes circuit-pulse {
|
||||||
|
|
||||||
0%,
|
0%,
|
||||||
100% {
|
100% {
|
||||||
opacity: 0.2;
|
opacity: 0.2;
|
||||||
@@ -892,14 +887,12 @@ pre:has(code[class*="language-"]) {
|
|||||||
inset: 0;
|
inset: 0;
|
||||||
border-radius: inherit;
|
border-radius: inherit;
|
||||||
padding: 1px;
|
padding: 1px;
|
||||||
background: linear-gradient(
|
background: linear-gradient(90deg,
|
||||||
90deg,
|
transparent 0%,
|
||||||
transparent 0%,
|
rgba(148, 163, 184, 0.3) 25%,
|
||||||
rgba(148, 163, 184, 0.3) 25%,
|
rgba(191, 206, 228, 0.2) 50%,
|
||||||
rgba(191, 206, 228, 0.2) 50%,
|
rgba(148, 163, 184, 0.3) 75%,
|
||||||
rgba(148, 163, 184, 0.3) 75%,
|
transparent 100%);
|
||||||
transparent 100%
|
|
||||||
);
|
|
||||||
background-size: 200% 100%;
|
background-size: 200% 100%;
|
||||||
animation: border-trace 4s linear infinite;
|
animation: border-trace 4s linear infinite;
|
||||||
-webkit-mask:
|
-webkit-mask:
|
||||||
@@ -969,6 +962,7 @@ pre:has(code[class*="language-"]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@keyframes junctionGlow {
|
@keyframes junctionGlow {
|
||||||
|
|
||||||
0%,
|
0%,
|
||||||
100% {
|
100% {
|
||||||
opacity: 0.1;
|
opacity: 0.1;
|
||||||
@@ -977,4 +971,4 @@ pre:has(code[class*="language-"]) {
|
|||||||
50% {
|
50% {
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -20,7 +20,7 @@ interface BlockquoteProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ArticleBlockquote: React.FC<BlockquoteProps> = ({ children, className = '' }) => (
|
export const ArticleBlockquote: React.FC<BlockquoteProps> = ({ children, className = '' }) => (
|
||||||
<blockquote className={`not-prose border-l-4 border-slate-900 pl-6 italic text-slate-700 my-8 text-xl md:text-2xl font-serif ${className}`}>
|
<blockquote className={`not-prose border-l-4 border-slate-900 pl-6 italic text-slate-700 my-10 text-xl md:text-2xl font-serif ${className}`}>
|
||||||
{children}
|
{children}
|
||||||
</blockquote>
|
</blockquote>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ interface HeadingProps {
|
|||||||
|
|
||||||
export const H1: React.FC<HeadingProps> = ({ children, className = "" }) => (
|
export const H1: React.FC<HeadingProps> = ({ children, className = "" }) => (
|
||||||
<h1
|
<h1
|
||||||
className={`not-prose text-4xl md:text-5xl font-bold text-slate-900 mb-6 mt-8 leading-[1.1] tracking-tight ${className}`}
|
className={`not-prose text-3xl md:text-5xl font-bold text-slate-900 mb-8 mt-12 leading-[1.1] tracking-tight font-sans ${className}`}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</h1>
|
</h1>
|
||||||
@@ -15,7 +15,7 @@ export const H1: React.FC<HeadingProps> = ({ children, className = "" }) => (
|
|||||||
|
|
||||||
export const H2: React.FC<HeadingProps> = ({ children, className = "" }) => (
|
export const H2: React.FC<HeadingProps> = ({ children, className = "" }) => (
|
||||||
<h2
|
<h2
|
||||||
className={`not-prose text-3xl md:text-4xl font-bold text-slate-900 mb-4 mt-10 leading-[1.2] tracking-tight ${className}`}
|
className={`not-prose text-2xl md:text-4xl font-bold text-slate-900 mb-6 mt-10 leading-tight tracking-tight font-sans ${className}`}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</h2>
|
</h2>
|
||||||
@@ -23,7 +23,7 @@ export const H2: React.FC<HeadingProps> = ({ children, className = "" }) => (
|
|||||||
|
|
||||||
export const H3: React.FC<HeadingProps> = ({ children, className = "" }) => (
|
export const H3: React.FC<HeadingProps> = ({ children, className = "" }) => (
|
||||||
<h3
|
<h3
|
||||||
className={`not-prose text-2xl md:text-3xl font-bold text-slate-900 mb-3 mt-8 leading-[1.3] tracking-tight ${className}`}
|
className={`not-prose text-xl md:text-2xl font-bold text-slate-900 mb-4 mt-8 leading-snug tracking-tight font-sans ${className}`}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</h3>
|
</h3>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export const Paragraph: React.FC<ParagraphProps> = ({
|
|||||||
className = "",
|
className = "",
|
||||||
}) => (
|
}) => (
|
||||||
<div
|
<div
|
||||||
className={`not-prose text-slate-700 font-serif text-lg md:text-xl leading-[1.6] mb-4 [&_p]:mb-4 last:[&_p]:mb-0 ${className}`}
|
className={`not-prose text-slate-700 font-serif text-lg md:text-xl leading-[1.6] mb-8 ${className}`}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
@@ -21,7 +21,7 @@ export const LeadParagraph: React.FC<ParagraphProps> = ({
|
|||||||
className = "",
|
className = "",
|
||||||
}) => (
|
}) => (
|
||||||
<div
|
<div
|
||||||
className={`not-prose text-xl md:text-2xl text-slate-700 font-serif italic leading-snug mb-6 [&_p]:mb-6 last:[&_p]:mb-0 ${className}`}
|
className={`not-prose text-xl md:text-2xl text-slate-700 font-serif italic leading-snug mb-10 ${className}`}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
/* eslint-disable react/prop-types */
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
import { motion, AnimatePresence } from "framer-motion";
|
||||||
import { cn } from "../../utils/cn";
|
import { cn } from "../../utils/cn";
|
||||||
import { Search } from "lucide-react";
|
import { Search } from "lucide-react";
|
||||||
|
|
||||||
interface BlogCommandBarProps {
|
interface BlogCommandBarProps {
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
onSearchChange: (value: string) => void;
|
onSearchChange: (_value: string) => void;
|
||||||
tags: string[];
|
tags: string[];
|
||||||
activeTags: string[];
|
activeTags: string[];
|
||||||
onTagToggle: (tag: string) => void;
|
onTagToggle: (_tag: string) => void;
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,37 +46,55 @@ export const BlogCommandBar: React.FC<BlogCommandBarProps> = ({
|
|||||||
placeholder="Beiträge suchen..."
|
placeholder="Beiträge suchen..."
|
||||||
className="w-full bg-transparent px-3 md:px-4 py-2 md:py-3 text-base md:text-lg text-slate-900 placeholder:text-slate-300 outline-none font-bold"
|
className="w-full bg-transparent px-3 md:px-4 py-2 md:py-3 text-base md:text-lg text-slate-900 placeholder:text-slate-300 outline-none font-bold"
|
||||||
/>
|
/>
|
||||||
{searchQuery && (
|
<AnimatePresence>
|
||||||
<button
|
{searchQuery && (
|
||||||
onClick={() => onSearchChange("")}
|
<motion.button
|
||||||
className="mr-2 px-2.5 py-1 md:px-3 md:py-1.5 bg-slate-100 hover:bg-slate-200 rounded-lg text-[9px] md:text-[10px] font-bold uppercase tracking-wider text-slate-500 hover:text-slate-900 transition-colors"
|
initial={{ opacity: 0, scale: 0.8, x: 10 }}
|
||||||
>
|
animate={{ opacity: 1, scale: 1, x: 0 }}
|
||||||
Leeren
|
exit={{ opacity: 0, scale: 0.8, x: 10 }}
|
||||||
</button>
|
onClick={() => onSearchChange("")}
|
||||||
)}
|
className="mr-2 px-2.5 py-1 md:px-3 md:py-1.5 bg-slate-100 hover:bg-slate-200 rounded-lg text-[9px] md:text-[10px] font-bold uppercase tracking-wider text-slate-500 hover:text-slate-900 transition-colors"
|
||||||
|
>
|
||||||
|
Leeren
|
||||||
|
</motion.button>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Tag Command Row */}
|
{/* Tag Command Row */}
|
||||||
<div className="flex flex-wrap items-center justify-center gap-1.5 md:gap-2 px-4 md:px-0">
|
<motion.div
|
||||||
|
layout
|
||||||
|
className="flex flex-wrap items-center justify-center gap-1.5 md:gap-2 px-4 md:px-0"
|
||||||
|
>
|
||||||
{tags.map((tag) => {
|
{tags.map((tag) => {
|
||||||
const isActive = activeTags.includes(tag);
|
const isActive = activeTags.includes(tag);
|
||||||
return (
|
return (
|
||||||
<button
|
<motion.button
|
||||||
key={tag}
|
key={tag}
|
||||||
|
layout
|
||||||
onClick={() => onTagToggle(tag)}
|
onClick={() => onTagToggle(tag)}
|
||||||
className={cn(
|
className={cn(
|
||||||
"px-2.5 py-1 md:px-3 md:py-1.5 rounded-lg text-[9px] md:text-[10px] font-mono uppercase tracking-wider border transition-all duration-200 select-none",
|
"relative px-2.5 py-1 md:px-3 md:py-1.5 rounded-lg text-[9px] md:text-[10px] font-mono uppercase tracking-wider border transition-all duration-200 select-none overflow-hidden",
|
||||||
isActive
|
isActive
|
||||||
? "bg-slate-900 text-white border-slate-900 shadow-md transform scale-105"
|
? "text-white border-slate-900 shadow-md"
|
||||||
: "bg-white text-slate-500 border-slate-200 hover:border-slate-400 hover:text-slate-900 hover:bg-slate-50",
|
: "bg-white text-slate-500 border-slate-200 hover:border-slate-400 hover:text-slate-900 hover:bg-slate-50",
|
||||||
)}
|
)}
|
||||||
|
whileHover={{ scale: 1.02 }}
|
||||||
|
whileTap={{ scale: 0.98 }}
|
||||||
>
|
>
|
||||||
#{tag}
|
<span className="relative z-10">#{tag}</span>
|
||||||
</button>
|
{isActive && (
|
||||||
|
<motion.div
|
||||||
|
layoutId="activeTag"
|
||||||
|
className="absolute inset-0 bg-slate-900 z-0"
|
||||||
|
transition={{ type: "spring", bounce: 0.2, duration: 0.6 }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</motion.button>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
import { LeadParagraph } from '../components/ArticleParagraph';
|
import { LeadParagraph } from '../components/ArticleParagraph';
|
||||||
import { H2, H3 } from '../components/ArticleHeading';
|
import { H1, H2, H3 } from '../components/ArticleHeading';
|
||||||
import { Paragraph } from '../components/ArticleParagraph';
|
import { Paragraph } from '../components/ArticleParagraph';
|
||||||
import { ArticleBlockquote } from '../components/ArticleBlockquote';
|
import { ArticleBlockquote } from '../components/ArticleBlockquote';
|
||||||
import { Marker } from '../components/Marker';
|
import { Marker } from '../components/Marker';
|
||||||
@@ -18,7 +18,9 @@ import { Section } from '../components/Section';
|
|||||||
import { Reveal } from '../components/Reveal';
|
import { Reveal } from '../components/Reveal';
|
||||||
|
|
||||||
export const mdxComponents = {
|
export const mdxComponents = {
|
||||||
|
// Named exports for explicit MDX usage
|
||||||
LeadParagraph,
|
LeadParagraph,
|
||||||
|
H1,
|
||||||
H2,
|
H2,
|
||||||
H3,
|
H3,
|
||||||
Paragraph,
|
Paragraph,
|
||||||
|
|||||||
Reference in New Issue
Block a user