- Footer.astro (v6-footer): REPLACED legacy 439-line version. 4-col sitemap bound to site settings (phone, email, line, facebook, linkedin) + servicesDropdown + company links. Logo uses /images/logo-long-black.png (local, was hardcoded to dataroot CDN in v7-5). - Base.astro: mount <UtilityBar /> + <Marquee /> + <Navigation /> + <Footer /> around <slot />. Nav receives currentPath for active link highlight. Animation init now runs BOTH initAnimations (legacy bento) and fxInit (v7-5). - SWEEP: removed duplicate <Navigation /> / <Footer /> + their imports from 11 page files. Idempotent script via execute_code. Verified: all 9 pages return 200, header/footer render exactly once each. Refs: .hermes/plans/2026-06-13_124000-moreminimore-v7-5-migration.md Task 3.1-3.2
274 lines
9.2 KiB
Plaintext
274 lines
9.2 KiB
Plaintext
---
|
|
import Base from '../../layouts/Base.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">
|
|
<PageHero
|
|
badge="บทความ"
|
|
title="ความรู้ด้านดิจิทัล"
|
|
subtitle="เทคนิคและความรู้ใหม่ๆ สำหรับธุรกิจไทย — อ่านจบใน 5 นาที เน้นลงมือทำ ไม่ใช่ทฤษฎี"
|
|
/>
|
|
|
|
{sortedPosts.length > 0 && (
|
|
<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 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>
|
|
|
|
{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>
|
|
|
|
<section class="section section-yellow cta-section">
|
|
<div class="container">
|
|
<div class="cta-content reveal">
|
|
<h2 class="cta-title">ต้องการความช่วยเหลือ?</h2>
|
|
<p class="cta-desc">ปรึกษาฟรี! เราพร้อมช่วยวิเคราะห์และให้คำแนะนำ</p>
|
|
<div class="cta-actions">
|
|
<a href="/contact" class="btn btn-dark btn-lg">ติดต่อเรา →</a>
|
|
<a href="tel:0809955945" class="btn btn-outline-dark btn-lg">080-995-5945</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</Base>
|
|
|
|
<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); }
|
|
|
|
/* 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 {
|
|
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-aside {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 16px;
|
|
}
|
|
.featured-excerpt {
|
|
font-size: 16px;
|
|
line-height: 1.7;
|
|
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;
|
|
align-items: center;
|
|
gap: 8px;
|
|
color: var(--color-black);
|
|
font-family: var(--font-display);
|
|
font-weight: 800;
|
|
font-size: 14px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 1px;
|
|
}
|
|
.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 {
|
|
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); }
|
|
|
|
/* CTA */
|
|
.cta-content { text-align: center; max-width: 700px; margin: 0 auto; }
|
|
.cta-title {
|
|
font-family: var(--font-display);
|
|
font-size: clamp(28px, 4vw, 44px);
|
|
font-weight: 900;
|
|
color: var(--color-black);
|
|
margin-bottom: 16px;
|
|
}
|
|
.cta-desc {
|
|
font-size: 18px;
|
|
color: rgba(0, 0, 0, 0.7);
|
|
margin-bottom: 32px;
|
|
}
|
|
.cta-actions {
|
|
display: flex;
|
|
gap: 16px;
|
|
justify-content: center;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
/* 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) {
|
|
.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>
|