fix blog page
This commit is contained in:
276
src/components/sections/BlogPostFooter.tsx
Normal file
276
src/components/sections/BlogPostFooter.tsx
Normal file
@@ -0,0 +1,276 @@
|
||||
import React from "react";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { ScrollReveal } from "@/animations/Reveal";
|
||||
import { getAdjacentPosts, getRelatedPosts } from "@/data/blog";
|
||||
|
||||
export default function BlogPostFooter({ slug }: { slug: string }) {
|
||||
const { prev, next } = getAdjacentPosts(slug);
|
||||
const related = getRelatedPosts(slug, 3);
|
||||
|
||||
return (
|
||||
<section className="dm-blog-footer" aria-label="More articles">
|
||||
<style dangerouslySetInnerHTML={{ __html: STYLES }} />
|
||||
|
||||
<div className="dm-blog-footer-inner">
|
||||
{/* Previous / Next */}
|
||||
{(prev || next) && (
|
||||
<nav className="dm-prevnext" aria-label="Article navigation">
|
||||
{prev ? (
|
||||
<Link href={`/blog/${prev.slug}`} className="dm-prevnext-card dm-prevnext-prev">
|
||||
<span className="dm-prevnext-thumb">
|
||||
<Image src={prev.image} alt={prev.title} fill sizes="80px" style={{ objectFit: "cover" }} />
|
||||
</span>
|
||||
<span className="dm-prevnext-text">
|
||||
<span className="dm-prevnext-label">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
||||
<line x1="19" y1="12" x2="5" y2="12" /><polyline points="12 19 5 12 12 5" />
|
||||
</svg>
|
||||
Previous
|
||||
</span>
|
||||
<span className="dm-prevnext-cat">{prev.category}</span>
|
||||
<span className="dm-prevnext-title">{prev.title}</span>
|
||||
</span>
|
||||
</Link>
|
||||
) : (
|
||||
<span className="dm-prevnext-placeholder" />
|
||||
)}
|
||||
{next ? (
|
||||
<Link href={`/blog/${next.slug}`} className="dm-prevnext-card dm-prevnext-next">
|
||||
<span className="dm-prevnext-text">
|
||||
<span className="dm-prevnext-label">
|
||||
Next
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
||||
<line x1="5" y1="12" x2="19" y2="12" /><polyline points="12 5 19 12 12 19" />
|
||||
</svg>
|
||||
</span>
|
||||
<span className="dm-prevnext-cat">{next.category}</span>
|
||||
<span className="dm-prevnext-title">{next.title}</span>
|
||||
</span>
|
||||
<span className="dm-prevnext-thumb">
|
||||
<Image src={next.image} alt={next.title} fill sizes="80px" style={{ objectFit: "cover" }} />
|
||||
</span>
|
||||
</Link>
|
||||
) : (
|
||||
<span className="dm-prevnext-placeholder" />
|
||||
)}
|
||||
</nav>
|
||||
)}
|
||||
|
||||
{/* Related Articles */}
|
||||
{related.length > 0 && (
|
||||
<div className="dm-related">
|
||||
<h2 className="dm-related-heading">Related Articles</h2>
|
||||
<div className="dm-related-grid">
|
||||
{related.map((post, i) => (
|
||||
<ScrollReveal key={post.slug} delay={i * 0.08} duration={0.7} yOffset={30}>
|
||||
<Link href={`/blog/${post.slug}`} className="dm-related-card">
|
||||
<div className="dm-related-img">
|
||||
<Image
|
||||
src={post.image}
|
||||
alt={post.title}
|
||||
fill
|
||||
sizes="(max-width: 700px) 100vw, (max-width: 1024px) 50vw, 33vw"
|
||||
style={{ objectFit: "cover" }}
|
||||
/>
|
||||
<span className="dm-related-badge">{post.category}</span>
|
||||
</div>
|
||||
<div className="dm-related-body">
|
||||
<h3 className="dm-related-card-title">{post.title}</h3>
|
||||
<p className="dm-related-card-excerpt">{post.excerpt}</p>
|
||||
<span className="dm-related-readmore">
|
||||
Read More
|
||||
<svg className="dm-related-readmore-arrow" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
||||
<line x1="5" y1="12" x2="19" y2="12" /><polyline points="12 5 19 12 12 19" />
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
</ScrollReveal>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Contact CTA banner */}
|
||||
<div className="dm-blog-contact-cta">
|
||||
<div className="dm-blog-contact-cta-content">
|
||||
<span className="dm-blog-contact-eyebrow">Let's talk logistics</span>
|
||||
<h2 className="dm-blog-contact-title">
|
||||
Ready to move smarter with Doormile?
|
||||
</h2>
|
||||
<p className="dm-blog-contact-sub">
|
||||
Tell us about your fleet and routes — we'll show you where the
|
||||
distance, vehicles and emissions are hiding.
|
||||
</p>
|
||||
</div>
|
||||
<Link href="/contact" className="dm-blog-contact-btn">
|
||||
Get in Touch
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
||||
<line x1="5" y1="12" x2="19" y2="12" /><polyline points="12 5 19 12 12 19" />
|
||||
</svg>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
const STYLES = `
|
||||
.dm-blog-footer {
|
||||
font-family: var(--font-manrope), sans-serif; --dm-red: #c01227; --dm-red-hover: #e31d32;
|
||||
/* The global theme applies 72px top/bottom section padding — strip it so the
|
||||
inner container is the single source of vertical rhythm (no double gap). */
|
||||
padding: 0 !important;
|
||||
}
|
||||
/* Neutralize the global theme's 120/80/60px UPPERCASE heading rules */
|
||||
.dm-blog-footer :where(h1, h2, h3, h4, h5, h6) {
|
||||
font-family: var(--font-manrope), sans-serif !important;
|
||||
text-transform: none !important;
|
||||
font-style: normal !important;
|
||||
font-weight: 800;
|
||||
}
|
||||
/* Neutralize the theme's .elementor-kit-5 a (red color + underline) */
|
||||
.dm-blog-footer a { text-decoration: none !important; }
|
||||
/* Shared content container — mirrors SingleBlog's .dm-blog-wrap (same 1280px
|
||||
max-width + 20→40px horizontal padding) so Prev/Next, Related and the CTA
|
||||
align to the exact same left/right edges as the article body above.
|
||||
Vertical rhythm: ~64px from the article end to the Prev/Next divider, then a
|
||||
consistent ~64–72px section→section gap (no 120px+ voids). */
|
||||
.dm-blog-footer-inner {
|
||||
max-width: 1280px; margin: 0 auto;
|
||||
/* Compact vertical rhythm on an 8px system. Top padding sets the
|
||||
article→Prev/Next gap (~24–32px); the inter-section gap sets the
|
||||
Prev/Next→Related gap (~32–48px). No large arbitrary voids. */
|
||||
/* Minimal bottom padding — the global site footer already contributes its
|
||||
own 20px top inset, so the CTA banner sits close to it without a void. */
|
||||
padding: clamp(24px, 3vw, 32px) clamp(20px, 4vw, 40px) clamp(8px, 1.5vw, 16px);
|
||||
display: flex; flex-direction: column; gap: clamp(32px, 4vw, 48px);
|
||||
}
|
||||
|
||||
/* Prev / Next */
|
||||
.dm-prevnext {
|
||||
display: grid; grid-template-columns: 1fr 1fr; gap: 20px;
|
||||
/* Halved from 40px: tight divider→cards spacing without crowding. */
|
||||
padding-top: clamp(16px, 2vw, 24px); border-top: 1px solid rgba(15,23,42,0.08);
|
||||
}
|
||||
@media (max-width: 640px) { .dm-prevnext { grid-template-columns: 1fr; } }
|
||||
.dm-prevnext-placeholder { display: block; }
|
||||
.dm-prevnext-card {
|
||||
display: flex; gap: 16px; align-items: center; padding: 16px;
|
||||
background: #fff; border: 1px solid rgba(15,23,42,0.09); border-radius: 22px;
|
||||
text-decoration: none; transition: transform .3s ease, box-shadow .3s ease, border-color .3s ease;
|
||||
}
|
||||
.dm-prevnext-card:hover {
|
||||
transform: translateY(-4px); border-color: rgba(192,18,39,0.2);
|
||||
box-shadow: 0 16px 34px rgba(192,18,39,0.10);
|
||||
}
|
||||
.dm-prevnext-thumb {
|
||||
position: relative; flex: 0 0 80px; width: 80px; height: 80px;
|
||||
border-radius: 16px; overflow: hidden; background: #f1f5f9;
|
||||
}
|
||||
.dm-prevnext-text { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
|
||||
.dm-prevnext-next { text-align: right; }
|
||||
.dm-prevnext-next .dm-prevnext-text { align-items: flex-end; }
|
||||
.dm-prevnext-label {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: 1px; color: var(--dm-red);
|
||||
}
|
||||
.dm-prevnext-cat { font-size: 11px; font-weight: 700; color: #94a3b8; text-transform: uppercase; letter-spacing: .5px; }
|
||||
.dm-prevnext-title {
|
||||
font-size: 15.5px; font-weight: 700; color: #1e293b; line-height: 1.4;
|
||||
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
|
||||
transition: color .2s ease;
|
||||
}
|
||||
.dm-prevnext-card:hover .dm-prevnext-title { color: var(--dm-red); }
|
||||
|
||||
/* Related */
|
||||
.dm-related-heading {
|
||||
font-size: clamp(22px, 2.2vw, 28px) !important; font-weight: 850 !important; letter-spacing: -.4px !important;
|
||||
line-height: 1.25 !important; color: #0f172a !important; margin: 0 0 24px;
|
||||
}
|
||||
.dm-related-grid {
|
||||
display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px;
|
||||
}
|
||||
@media (max-width: 1024px) { .dm-related-grid { grid-template-columns: repeat(2, 1fr); } }
|
||||
@media (max-width: 700px) { .dm-related-grid { grid-template-columns: 1fr; gap: 24px; } }
|
||||
|
||||
.dm-related-card {
|
||||
display: flex; flex-direction: column; height: 100%;
|
||||
background: #fff; border: 1px solid rgba(15,23,42,0.09); border-radius: 22px;
|
||||
overflow: hidden; box-shadow: 0 4px 24px rgba(15,23,42,0.05); text-decoration: none;
|
||||
transition: transform .4s cubic-bezier(0.2,0.8,0.2,1), box-shadow .4s ease, border-color .4s ease;
|
||||
}
|
||||
.dm-related-card:hover {
|
||||
transform: translateY(-8px); box-shadow: 0 22px 44px rgba(192,18,39,0.13);
|
||||
border-color: rgba(192,18,39,0.2);
|
||||
}
|
||||
.dm-related-img {
|
||||
position: relative; width: 100%; aspect-ratio: 16 / 10; overflow: hidden; background: #f1f5f9;
|
||||
}
|
||||
.dm-related-img img { transition: transform .5s cubic-bezier(0.2,0.8,0.2,1); }
|
||||
.dm-related-card:hover .dm-related-img img { transform: scale(1.05); }
|
||||
.dm-related-badge {
|
||||
position: absolute; top: 14px; left: 14px; z-index: 5; background: var(--dm-red); color: #fff;
|
||||
font-size: 9px; font-weight: 800; text-transform: uppercase; letter-spacing: 1.2px;
|
||||
padding: 5px 11px; border-radius: 8px; box-shadow: 0 4px 12px rgba(192,18,39,0.25);
|
||||
}
|
||||
.dm-related-body { display: flex; flex-direction: column; flex: 1; padding: 22px; }
|
||||
.dm-related-card-title {
|
||||
font-size: 17px !important; font-weight: 800 !important; color: #1e293b !important; line-height: 1.4 !important;
|
||||
letter-spacing: -.2px !important; margin: 0 0 10px;
|
||||
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
|
||||
transition: color .2s ease;
|
||||
}
|
||||
.dm-related-card:hover .dm-related-card-title { color: var(--dm-red); }
|
||||
.dm-related-card-excerpt {
|
||||
font-size: 13.5px; font-weight: 500; color: #64748b; line-height: 1.6; margin: 0 0 18px;
|
||||
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
|
||||
}
|
||||
.dm-related-readmore {
|
||||
margin-top: auto; display: inline-flex; align-items: center; gap: 7px;
|
||||
font-size: 12.5px; font-weight: 800; color: var(--dm-red);
|
||||
text-transform: uppercase; letter-spacing: .6px;
|
||||
}
|
||||
.dm-related-readmore-arrow { transition: transform .3s cubic-bezier(0.2,0.8,0.2,1); }
|
||||
.dm-related-card:hover .dm-related-readmore-arrow { transform: translateX(5px); }
|
||||
|
||||
/* Contact CTA banner */
|
||||
.dm-blog-contact-cta {
|
||||
display: flex; align-items: center; justify-content: space-between; gap: 32px;
|
||||
flex-wrap: wrap;
|
||||
background: linear-gradient(135deg, #1a1a1a 0%, #2d1417 100%);
|
||||
border-radius: 30px; padding: clamp(32px, 4vw, 56px);
|
||||
position: relative; overflow: hidden;
|
||||
}
|
||||
.dm-blog-contact-cta::after {
|
||||
content: ""; position: absolute; right: -80px; top: -80px; width: 300px; height: 300px;
|
||||
background: radial-gradient(circle, rgba(192,18,39,0.40), transparent 70%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.dm-blog-contact-cta-content { position: relative; z-index: 1; max-width: 640px; }
|
||||
.dm-blog-contact-eyebrow {
|
||||
display: inline-block; font-size: 12px; font-weight: 800; text-transform: uppercase;
|
||||
letter-spacing: 1.4px; color: #ff8088; margin-bottom: 14px;
|
||||
}
|
||||
.dm-blog-contact-title {
|
||||
font-size: clamp(22px, 2.2vw, 28px) !important; font-weight: 800 !important; line-height: 1.25 !important;
|
||||
letter-spacing: -.3px !important; color: #ffffff !important; margin: 0 0 12px; text-wrap: balance;
|
||||
}
|
||||
.dm-blog-contact-sub {
|
||||
font-size: 15.5px; line-height: 1.65; color: #e2e2e2; margin: 0; font-weight: 450;
|
||||
}
|
||||
.dm-blog-contact-btn {
|
||||
position: relative; z-index: 1; flex-shrink: 0;
|
||||
display: inline-flex; align-items: center; justify-content: center; gap: 10px;
|
||||
background: var(--dm-red); color: #fff !important; font-size: 15px; font-weight: 700;
|
||||
padding: 16px 32px; border-radius: 16px; text-decoration: none;
|
||||
box-shadow: 0 10px 26px rgba(192,18,39,0.34);
|
||||
transition: background .2s ease, transform .2s ease;
|
||||
}
|
||||
.dm-blog-contact-btn:hover { background: var(--dm-red-hover); transform: translateY(-2px); }
|
||||
@media (max-width: 720px) {
|
||||
.dm-blog-contact-cta { flex-direction: column; align-items: flex-start; gap: 26px; }
|
||||
.dm-blog-contact-btn { width: 100%; }
|
||||
}
|
||||
`;
|
||||
Reference in New Issue
Block a user