feat(pages): Bento Grid redesign across all pages
Apply Bento Grid + decorative parallax orb system from about.astro to all remaining pages (home, services, portfolio, faq, contact, terms, privacy, blog list, blog detail, services detail). Layout changes (consistent across all pages): - Main content sections use <BentoGrid> + <BentoTile> - 2-3 <DecoOrb> per page for decorative parallax (no parallax blobs behind text — orbs are pure decoration, z-index: 0, pointer-events: none) - Surface variants: white, soft, yellow, purple-soft, teal, mint, dark (rotated across pages for visual variety) - Asymmetric span strategy (8+4, 7+5, 6+6) instead of flat grids - Process sections use clean 4x1 grid - Pull quote + yellow CTA kept as-is (standalone sections) Content rules preserved: - All Thai content kept verbatim - No fabricated statistics - No emoji icons (use text numerals 01 02 03 04) - All design tokens from global.css (no hardcoded hex) - Existing global classes (.container, .section, .btn, .section-badge, .section-title, .cta-section, etc.) reused — no design system break Build verified: 22 pages built in 1.80s, no errors. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,9 @@ import Base from '../../layouts/Base.astro';
|
||||
import Navigation from '../../components/Navigation.astro';
|
||||
import Footer from '../../components/Footer.astro';
|
||||
import PageHero from '../../components/PageHero.astro';
|
||||
import BentoGrid from '../../components/BentoGrid.astro';
|
||||
import BentoTile from '../../components/BentoTile.astro';
|
||||
import DecoOrb from '../../components/DecoOrb.astro';
|
||||
import { getCollection, render } from 'astro:content';
|
||||
|
||||
const { slug } = Astro.params;
|
||||
@@ -33,6 +36,12 @@ const formattedDate = post.data.date.toLocaleDateString('th-TH', {
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
});
|
||||
|
||||
// Surface rotation for related cards
|
||||
const surfaces = ['soft', 'yellow', 'mint'] as const;
|
||||
function surfaceFor(i: number) {
|
||||
return surfaces[i % surfaces.length];
|
||||
}
|
||||
---
|
||||
|
||||
<Base title={`${post.data.title} | MoreminiMore`}>
|
||||
@@ -45,104 +54,143 @@ const formattedDate = post.data.date.toLocaleDateString('th-TH', {
|
||||
/>
|
||||
|
||||
{post.data.image && (
|
||||
<div class="article-image">
|
||||
<div class="container">
|
||||
<img src={post.data.image} alt={post.data.title} />
|
||||
<section class="section section-bento article-image-section">
|
||||
<DecoOrb color="soft" size="500px" speed={0.3} position={{ top: '-100px', right: '-150px' }} opacity={0.4} blur="80px" />
|
||||
<DecoOrb color="yellow" size="350px" speed={0.3} position={{ bottom: '-150px', left: '-100px' }} opacity={0.2} blur="80px" />
|
||||
<div class="container" style="position: relative; z-index: 1;">
|
||||
<div class="article-image">
|
||||
<img src={post.data.image} alt={post.data.title} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
<section class="section article-section">
|
||||
<div class="container">
|
||||
<div class="article-grid reveal">
|
||||
<article class="article-content">
|
||||
<section class="section section-bento article-section">
|
||||
<DecoOrb color="purple" size="400px" speed={0.3} position={{ top: '5%', left: '-150px' }} opacity={0.2} blur="80px" />
|
||||
<DecoOrb color="mint" size="350px" speed={0.3} position={{ bottom: '10%', right: '-100px' }} opacity={0.2} blur="80px" />
|
||||
<div class="container" style="position: relative; z-index: 1;">
|
||||
<BentoGrid>
|
||||
<!-- Main article body — full width bento tile -->
|
||||
<BentoTile span={8} surface="white" eyebrow={post.data.category} title={post.data.title}>
|
||||
<div class="article-meta">
|
||||
<span class="article-date">{formattedDate}</span>
|
||||
</div>
|
||||
<div class="article-body">
|
||||
<Content />
|
||||
</div>
|
||||
</article>
|
||||
</BentoTile>
|
||||
|
||||
<aside class="article-sidebar">
|
||||
<div class="sidebar-card">
|
||||
<h3 class="sidebar-title">เกี่ยวกับ MoreminiMore</h3>
|
||||
<p class="sidebar-text">
|
||||
ดิจิทัลเอเจนซี่ที่ช่วยให้ธุรกิจไทยเติบโตด้วยเทคโนโลยีสมัยใหม่
|
||||
</p>
|
||||
<a href="/about" class="btn btn-outline-dark btn-sm">ดูเพิ่มเติม</a>
|
||||
</div>
|
||||
<!-- Sidebar tile: about + contact stack -->
|
||||
<BentoTile span={4} surface="soft" eyebrow="เกี่ยวกับเรา" title="MoreminiMore">
|
||||
<p>ดิจิทัลเอเจนซี่ที่ช่วยให้ธุรกิจไทยเติบโตด้วยเทคโนโลยีสมัยใหม่</p>
|
||||
<a href="/about" class="btn btn-outline-dark btn-sm">ดูเพิ่มเติม</a>
|
||||
|
||||
<div class="sidebar-card">
|
||||
<h3 class="sidebar-title">สนใจบริการ?</h3>
|
||||
<p class="sidebar-text">ติดต่อเราได้เลย ปรึกษาฟรี!</p>
|
||||
<div class="sidebar-divider"></div>
|
||||
|
||||
<span class="tile-eyebrow-sm">สนใจบริการ?</span>
|
||||
<p style="margin-top: 8px;">ติดต่อเราได้เลย ปรึกษาฟรี!</p>
|
||||
<div class="sidebar-actions">
|
||||
<a href="/contact" class="btn btn-primary btn-sm">ติดต่อเรา</a>
|
||||
<a href="tel:0809955945" class="btn btn-outline-dark btn-sm" style="margin-top: 8px;">080-995-5945</a>
|
||||
<a href="tel:0809955945" class="btn btn-outline-dark btn-sm">080-995-5945</a>
|
||||
</div>
|
||||
|
||||
{related.length > 0 && (
|
||||
<div class="sidebar-card">
|
||||
<h3 class="sidebar-title">บทความที่เกี่ยวข้อง</h3>
|
||||
<div class="related-list">
|
||||
{related.map(r => (
|
||||
<a href={`/blog/${r.id}`} class="related-item">
|
||||
<img src={r.data.image} alt={r.data.title} />
|
||||
<span>{r.data.title}</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</aside>
|
||||
</div>
|
||||
</BentoTile>
|
||||
</BentoGrid>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{related.length > 0 && (
|
||||
<section class="section section-bento related-section">
|
||||
<DecoOrb color="yellow" size="400px" speed={0.3} position={{ top: '10%', right: '-100px' }} opacity={0.25} blur="80px" />
|
||||
<DecoOrb color="soft" size="350px" speed={0.3} position={{ bottom: '-100px', left: '-100px' }} opacity={0.4} blur="80px" />
|
||||
<div class="container" style="position: relative; z-index: 1;">
|
||||
<div class="section-header reveal">
|
||||
<span class="section-badge">บทความที่เกี่ยวข้อง</span>
|
||||
<h2 class="section-title">อ่านต่อ <span class="highlight">เนื้อหาใกล้เคียง</span></h2>
|
||||
</div>
|
||||
|
||||
<BentoGrid>
|
||||
{related.map((r, i) => (
|
||||
<a href={`/blog/${r.id}`} class="related-link">
|
||||
<BentoTile span={4} surface={surfaceFor(i)} eyebrow={r.data.category} title={r.data.title}>
|
||||
<div class="related-card-body">
|
||||
{r.data.image && (
|
||||
<div class="related-image">
|
||||
<img src={r.data.image} alt={r.data.title} loading="lazy" />
|
||||
</div>
|
||||
)}
|
||||
<span class="related-date">
|
||||
{new Date(r.data.date).toLocaleDateString('th-TH', { year: 'numeric', month: 'long', day: 'numeric' })}
|
||||
</span>
|
||||
</div>
|
||||
</BentoTile>
|
||||
</a>
|
||||
))}
|
||||
</BentoGrid>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
<Footer />
|
||||
</Base>
|
||||
|
||||
<style>
|
||||
.article-image-section { background: var(--color-white); padding-top: 40px; padding-bottom: 40px; }
|
||||
.article-section { background: var(--color-white); }
|
||||
.related-section { background: var(--color-bg-alt); }
|
||||
.section-bento { position: relative; overflow: hidden; }
|
||||
|
||||
/* Article image (hero) */
|
||||
.article-image {
|
||||
padding: 40px 0;
|
||||
background: var(--color-bg-alt);
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
border-radius: var(--radius-xl);
|
||||
background: var(--color-bg-soft);
|
||||
}
|
||||
.article-image img {
|
||||
width: 100%;
|
||||
max-height: 500px;
|
||||
object-fit: cover;
|
||||
border-radius: var(--radius-xl);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.article-section { background: var(--color-white); }
|
||||
|
||||
.article-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 360px;
|
||||
gap: 60px;
|
||||
/* Main article body */
|
||||
.article-meta {
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid var(--color-gray-200);
|
||||
}
|
||||
.article-date {
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: var(--color-gray-600);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.article-body {
|
||||
font-size: 18px;
|
||||
font-size: 17px;
|
||||
line-height: 1.8;
|
||||
color: var(--color-gray-700);
|
||||
}
|
||||
.article-body :global(h2) {
|
||||
font-family: var(--font-display);
|
||||
font-size: 28px;
|
||||
font-size: 26px;
|
||||
font-weight: 800;
|
||||
color: var(--color-black);
|
||||
margin: 40px 0 20px;
|
||||
margin: 36px 0 18px;
|
||||
}
|
||||
.article-body :global(h3) {
|
||||
font-family: var(--font-display);
|
||||
font-size: 22px;
|
||||
font-size: 20px;
|
||||
font-weight: 800;
|
||||
color: var(--color-black);
|
||||
margin: 32px 0 16px;
|
||||
margin: 28px 0 14px;
|
||||
}
|
||||
.article-body :global(p) { margin-bottom: 20px; }
|
||||
.article-body :global(p) { margin-bottom: 18px; }
|
||||
.article-body :global(ul), .article-body :global(ol) {
|
||||
margin: 20px 0;
|
||||
margin: 16px 0;
|
||||
padding-left: 24px;
|
||||
}
|
||||
.article-body :global(li) { margin-bottom: 12px; }
|
||||
.article-body :global(li) { margin-bottom: 10px; }
|
||||
.article-body :global(a) {
|
||||
color: var(--color-primary-dark);
|
||||
font-weight: 600;
|
||||
@@ -150,40 +198,37 @@ const formattedDate = post.data.date.toLocaleDateString('th-TH', {
|
||||
.article-body :global(a:hover) { text-decoration: underline; }
|
||||
.article-body :global(blockquote) {
|
||||
border-left: 4px solid var(--color-primary);
|
||||
padding-left: 24px;
|
||||
margin: 32px 0;
|
||||
padding-left: 20px;
|
||||
margin: 28px 0;
|
||||
font-style: italic;
|
||||
color: var(--color-gray-700);
|
||||
}
|
||||
.article-body :global(img) {
|
||||
border-radius: var(--radius-md);
|
||||
margin: 20px 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
.article-body :global(strong) { color: var(--color-black); font-weight: 800; }
|
||||
|
||||
/* Sidebar (soft tile) */
|
||||
.sidebar-divider {
|
||||
height: 1px;
|
||||
background: var(--color-gray-200);
|
||||
margin: 24px 0;
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
.article-sidebar {
|
||||
.tile-eyebrow-sm {
|
||||
font-size: 11px;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
opacity: 0.7;
|
||||
display: block;
|
||||
}
|
||||
.sidebar-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
}
|
||||
.sidebar-card {
|
||||
background: var(--color-bg-alt);
|
||||
border: 1px solid var(--color-gray-200);
|
||||
border-radius: var(--radius-xl);
|
||||
padding: 28px;
|
||||
}
|
||||
.sidebar-title {
|
||||
font-family: var(--font-display);
|
||||
font-size: 16px;
|
||||
font-weight: 800;
|
||||
color: var(--color-black);
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.sidebar-text {
|
||||
font-size: 14px;
|
||||
color: var(--color-gray-600);
|
||||
line-height: 1.6;
|
||||
margin-bottom: 16px;
|
||||
gap: 8px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
.btn-sm {
|
||||
padding: 10px 20px;
|
||||
@@ -197,39 +242,75 @@ const formattedDate = post.data.date.toLocaleDateString('th-TH', {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.related-list {
|
||||
/* Section header */
|
||||
.section-header { text-align: center; margin-bottom: 48px; }
|
||||
.section-badge {
|
||||
display: inline-block;
|
||||
background: var(--color-primary);
|
||||
color: var(--color-black);
|
||||
padding: 8px 20px;
|
||||
border-radius: var(--radius-full);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.section-title {
|
||||
font-family: var(--font-display);
|
||||
font-size: clamp(28px, 4vw, 40px);
|
||||
font-weight: 900;
|
||||
color: var(--color-black);
|
||||
}
|
||||
.section-title .highlight { color: var(--color-primary-dark); }
|
||||
|
||||
/* Related cards */
|
||||
.related-link {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
.related-card-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
.related-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 8px;
|
||||
background: var(--color-white);
|
||||
.related-image {
|
||||
aspect-ratio: 16/10;
|
||||
overflow: hidden;
|
||||
border-radius: var(--radius-md);
|
||||
transition: all 0.2s ease;
|
||||
background: var(--color-bg-soft);
|
||||
margin: -8px -8px 4px;
|
||||
}
|
||||
.related-item:hover { transform: translateX(4px); }
|
||||
.related-item img {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
.related-image img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: var(--radius-sm);
|
||||
flex-shrink: 0;
|
||||
display: block;
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
.related-item span {
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: var(--color-black);
|
||||
line-height: 1.3;
|
||||
.related-link:hover .related-image img { transform: scale(1.06); }
|
||||
.related-date {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
opacity: 0.65;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.article-grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.article-body { font-size: 16px; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// Parallax orbs (use data-parallax-speed from DecoOrb)
|
||||
const parallaxEls = document.querySelectorAll('[data-parallax-speed]');
|
||||
function updateParallax() {
|
||||
const scrolled = window.scrollY;
|
||||
parallaxEls.forEach(el => {
|
||||
const speed = parseFloat(el.getAttribute('data-parallax-speed') || '0.4');
|
||||
const ty = scrolled * speed * -0.3;
|
||||
el.style.transform = `translate3d(0, ${ty}px, 0)`;
|
||||
});
|
||||
}
|
||||
window.addEventListener('scroll', () => requestAnimationFrame(updateParallax), { passive: true });
|
||||
</script>
|
||||
|
||||
@@ -3,10 +3,19 @@ import Base from '../../layouts/Base.astro';
|
||||
import Navigation from '../../components/Navigation.astro';
|
||||
import Footer from '../../components/Footer.astro';
|
||||
import PageHero from '../../components/PageHero.astro';
|
||||
import BentoGrid from '../../components/BentoGrid.astro';
|
||||
import BentoTile from '../../components/BentoTile.astro';
|
||||
import DecoOrb from '../../components/DecoOrb.astro';
|
||||
import { getCollection } from 'astro:content';
|
||||
|
||||
const blogPosts = await getCollection('blog');
|
||||
const sortedPosts = blogPosts.sort((a, b) => b.data.date.valueOf() - a.data.date.valueOf());
|
||||
|
||||
// Surface color rotation for variety across the post grid
|
||||
const surfaces = ['white', 'soft', 'yellow', 'mint', 'purple-soft', 'teal'] as const;
|
||||
function surfaceFor(i: number) {
|
||||
return surfaces[i % surfaces.length];
|
||||
}
|
||||
---
|
||||
|
||||
<Base title="บทความ | MoreminiMore - รับทำเว็บไซต์ SEO AI Chatbot">
|
||||
@@ -19,57 +28,74 @@ const sortedPosts = blogPosts.sort((a, b) => b.data.date.valueOf() - a.data.date
|
||||
/>
|
||||
|
||||
{sortedPosts.length > 0 && (
|
||||
<section class="featured-section section-soft">
|
||||
<div class="container">
|
||||
<a href={`/blog/${sortedPosts[0].id}`} class="featured-card">
|
||||
<div class="featured-image">
|
||||
{sortedPosts[0].data.image && (
|
||||
<img src={sortedPosts[0].data.image} alt={sortedPosts[0].data.title} loading="eager" />
|
||||
)}
|
||||
<span class="featured-badge">บทความล่าสุด</span>
|
||||
</div>
|
||||
<div class="featured-content">
|
||||
<span class="category-badge">{sortedPosts[0].data.category}</span>
|
||||
<h2 class="featured-title">{sortedPosts[0].data.title}</h2>
|
||||
<p class="featured-excerpt">{sortedPosts[0].data.excerpt}</p>
|
||||
<span class="read-more">
|
||||
อ่านต่อ
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M5 12h14M12 5l7 7-7 7"/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<section class="section section-bento featured-section">
|
||||
<DecoOrb color="yellow" size="450px" speed={0.3} position={{ top: '-100px', right: '-100px' }} opacity={0.3} blur="80px" />
|
||||
<DecoOrb color="soft" size="400px" speed={0.3} position={{ bottom: '-150px', left: '-100px' }} opacity={0.4} blur="80px" />
|
||||
<div class="container" style="position: relative; z-index: 1;">
|
||||
<a href={`/blog/${sortedPosts[0].id}`} class="featured-tile-link">
|
||||
<BentoGrid>
|
||||
<BentoTile span={7} surface="white" eyebrow="บทความล่าสุด" title={sortedPosts[0].data.title}>
|
||||
<div class="featured-content">
|
||||
<div class="featured-image">
|
||||
{sortedPosts[0].data.image && (
|
||||
<img src={sortedPosts[0].data.image} alt={sortedPosts[0].data.title} loading="eager" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</BentoTile>
|
||||
|
||||
<BentoTile span={5} surface="yellow" eyebrow={sortedPosts[0].data.category} title="อ่านบทความเต็ม">
|
||||
<div class="featured-aside">
|
||||
<p class="featured-excerpt">{sortedPosts[0].data.excerpt}</p>
|
||||
<div class="featured-meta">
|
||||
<span class="featured-date">
|
||||
{new Date(sortedPosts[0].data.date).toLocaleDateString('th-TH', { year: 'numeric', month: 'long', day: 'numeric' })}
|
||||
</span>
|
||||
</div>
|
||||
<span class="read-more">
|
||||
อ่านต่อ
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M5 12h14M12 5l7 7-7 7"/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</BentoTile>
|
||||
</BentoGrid>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
<section class="section blog-section">
|
||||
<div class="container">
|
||||
<div class="section-header">
|
||||
<section class="section section-bento blog-section">
|
||||
<DecoOrb color="mint" size="400px" speed={0.3} position={{ top: '5%', right: '-150px' }} opacity={0.2} blur="80px" />
|
||||
<DecoOrb color="purple" size="350px" speed={0.3} position={{ bottom: '-100px', left: '-100px' }} opacity={0.2} blur="80px" />
|
||||
<div class="container" style="position: relative; z-index: 1;">
|
||||
<div class="section-header reveal">
|
||||
<span class="section-badge">บทความทั้งหมด</span>
|
||||
<h2 class="section-title">บทความ <span class="highlight">ล่าสุด</span></h2>
|
||||
</div>
|
||||
|
||||
<div class="blog-grid">
|
||||
{sortedPosts.slice(1).map((post, i) => (
|
||||
<a href={`/blog/${post.id}`} class="blog-card" style={`--delay: ${i * 0.1}s`}>
|
||||
<div class="blog-image">
|
||||
{post.data.image && (
|
||||
<img src={post.data.image} alt={post.data.title} loading="lazy" />
|
||||
)}
|
||||
</div>
|
||||
<div class="blog-content">
|
||||
<span class="blog-category">{post.data.category}</span>
|
||||
<h3 class="blog-title">{post.data.title}</h3>
|
||||
<p class="blog-excerpt">{post.data.excerpt}</p>
|
||||
<span class="blog-date">
|
||||
{new Date(post.data.date).toLocaleDateString('th-TH', { year: 'numeric', month: 'long', day: 'numeric' })}
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
{sortedPosts.length > 1 && (
|
||||
<BentoGrid>
|
||||
{sortedPosts.slice(1).map((post, i) => (
|
||||
<a href={`/blog/${post.id}`} class="post-tile-link">
|
||||
<BentoTile span={4} surface={surfaceFor(i)} eyebrow={post.data.category} title={post.data.title} reveal={true}>
|
||||
<div class="post-card-body">
|
||||
{post.data.image && (
|
||||
<div class="post-image">
|
||||
<img src={post.data.image} alt={post.data.title} loading="lazy" />
|
||||
</div>
|
||||
)}
|
||||
<p class="post-excerpt">{post.data.excerpt}</p>
|
||||
<span class="post-date">
|
||||
{new Date(post.data.date).toLocaleDateString('th-TH', { year: 'numeric', month: 'long', day: 'numeric' })}
|
||||
</span>
|
||||
</div>
|
||||
</BentoTile>
|
||||
</a>
|
||||
))}
|
||||
</BentoGrid>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -92,77 +118,46 @@ const sortedPosts = blogPosts.sort((a, b) => b.data.date.valueOf() - a.data.date
|
||||
<style>
|
||||
.section-soft { background: var(--color-bg-alt); }
|
||||
.section-yellow { background: var(--color-primary); }
|
||||
.section-bento { position: relative; overflow: hidden; }
|
||||
.featured-section { background: var(--color-bg-alt); padding-top: 60px; padding-bottom: 60px; }
|
||||
.blog-section { background: var(--color-white); }
|
||||
|
||||
/* Featured */
|
||||
.featured-section { padding: 60px 0; }
|
||||
.featured-card {
|
||||
display: grid;
|
||||
grid-template-columns: 1.5fr 1fr;
|
||||
background: var(--color-white);
|
||||
border: 1px solid var(--color-gray-200);
|
||||
border-radius: var(--radius-xl);
|
||||
overflow: hidden;
|
||||
transition: all 0.4s ease;
|
||||
}
|
||||
.featured-card:hover {
|
||||
transform: scale(1.01);
|
||||
box-shadow: var(--shadow-md);
|
||||
border-color: var(--color-primary);
|
||||
/* Make the whole featured tile clickable without breaking inner link */
|
||||
.featured-tile-link {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.featured-content { display: flex; flex-direction: column; gap: 16px; }
|
||||
.featured-image {
|
||||
position: relative;
|
||||
aspect-ratio: 16/10;
|
||||
overflow: hidden;
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-bg-soft);
|
||||
}
|
||||
.featured-image img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
.featured-badge {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
left: 16px;
|
||||
background: var(--color-primary);
|
||||
color: var(--color-black);
|
||||
padding: 6px 14px;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.featured-content {
|
||||
padding: 40px;
|
||||
|
||||
.featured-aside {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
.category-badge {
|
||||
display: inline-block;
|
||||
background: var(--color-bg-alt);
|
||||
color: var(--color-gray-600);
|
||||
padding: 4px 12px;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
align-self: flex-start;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.featured-title {
|
||||
font-family: var(--font-display);
|
||||
font-size: clamp(22px, 3vw, 28px);
|
||||
font-weight: 800;
|
||||
color: var(--color-black);
|
||||
margin-bottom: 12px;
|
||||
line-height: 1.3;
|
||||
gap: 16px;
|
||||
}
|
||||
.featured-excerpt {
|
||||
font-size: 14px;
|
||||
color: var(--color-gray-600);
|
||||
font-size: 16px;
|
||||
line-height: 1.7;
|
||||
margin-bottom: 20px;
|
||||
color: var(--color-black);
|
||||
}
|
||||
.featured-meta { font-size: 13px; }
|
||||
.featured-date {
|
||||
font-weight: 700;
|
||||
color: var(--color-black);
|
||||
opacity: 0.7;
|
||||
}
|
||||
.read-more {
|
||||
display: inline-flex;
|
||||
@@ -175,9 +170,48 @@ const sortedPosts = blogPosts.sort((a, b) => b.data.date.valueOf() - a.data.date
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
.read-more:hover { color: var(--color-primary-dark); }
|
||||
.read-more svg { width: 18px; height: 18px; }
|
||||
|
||||
/* Post grid cards */
|
||||
.post-tile-link {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
.post-card-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
height: 100%;
|
||||
}
|
||||
.post-image {
|
||||
aspect-ratio: 16/10;
|
||||
overflow: hidden;
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-bg-soft);
|
||||
margin: -8px -8px 4px;
|
||||
}
|
||||
.post-image img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
.post-tile-link:hover .post-image img { transform: scale(1.06); }
|
||||
.post-excerpt {
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
color: inherit;
|
||||
opacity: 0.85;
|
||||
}
|
||||
.post-date {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
opacity: 0.6;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
/* Section header */
|
||||
.section-header { text-align: center; margin-bottom: 48px; }
|
||||
.section-badge {
|
||||
@@ -200,65 +234,6 @@ const sortedPosts = blogPosts.sort((a, b) => b.data.date.valueOf() - a.data.date
|
||||
}
|
||||
.section-title .highlight { color: var(--color-primary-dark); }
|
||||
|
||||
/* Blog grid */
|
||||
.blog-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 32px;
|
||||
}
|
||||
.blog-card {
|
||||
display: block;
|
||||
background: var(--color-white);
|
||||
border: 1px solid var(--color-gray-200);
|
||||
border-radius: var(--radius-xl);
|
||||
overflow: hidden;
|
||||
transition: all 0.4s ease;
|
||||
}
|
||||
.blog-card:hover {
|
||||
transform: translateY(-8px);
|
||||
box-shadow: var(--shadow-md);
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
.blog-image { aspect-ratio: 16/10; overflow: hidden; background: var(--color-bg-soft); }
|
||||
.blog-image img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
.blog-card:hover .blog-image img { transform: scale(1.08); }
|
||||
.blog-content { padding: 24px; }
|
||||
.blog-category {
|
||||
display: inline-block;
|
||||
background: var(--color-primary);
|
||||
color: var(--color-black);
|
||||
padding: 4px 12px;
|
||||
border-radius: var(--radius-sm);
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.blog-title {
|
||||
font-family: var(--font-display);
|
||||
font-size: 18px;
|
||||
font-weight: 800;
|
||||
color: var(--color-black);
|
||||
margin-bottom: 12px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
.blog-excerpt {
|
||||
font-size: 14px;
|
||||
color: var(--color-gray-600);
|
||||
line-height: 1.6;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.blog-date {
|
||||
font-size: 12px;
|
||||
color: var(--color-gray-500);
|
||||
}
|
||||
|
||||
/* CTA */
|
||||
.cta-content { text-align: center; max-width: 700px; margin: 0 auto; }
|
||||
.cta-title {
|
||||
@@ -280,13 +255,25 @@ const sortedPosts = blogPosts.sort((a, b) => b.data.date.valueOf() - a.data.date
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.featured-card { grid-template-columns: 1fr; }
|
||||
.blog-grid { grid-template-columns: repeat(2, 1fr); }
|
||||
}
|
||||
/* Reduce padding on bento tiles that are link wrappers so the image breathes */
|
||||
.post-tile-link :global(.bento-tile) { padding: 16px; }
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.blog-grid { grid-template-columns: 1fr; }
|
||||
.cta-actions { flex-direction: column; }
|
||||
.cta-actions .btn { width: 100%; justify-content: center; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// Parallax orbs (use data-parallax-speed from DecoOrb)
|
||||
const parallaxEls = document.querySelectorAll('[data-parallax-speed]');
|
||||
function updateParallax() {
|
||||
const scrolled = window.scrollY;
|
||||
parallaxEls.forEach(el => {
|
||||
const speed = parseFloat(el.getAttribute('data-parallax-speed') || '0.4');
|
||||
const ty = scrolled * speed * -0.3;
|
||||
el.style.transform = `translate3d(0, ${ty}px, 0)`;
|
||||
});
|
||||
}
|
||||
window.addEventListener('scroll', () => requestAnimationFrame(updateParallax), { passive: true });
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user