feat(header): add UtilityBar + Marquee + Navigation from v6-nav
- UtilityBar.astro (v6-utility): phone, clock, date, email from site settings Clock updated by fxClock() in src/lib/fx-animations.ts - Marquee.astro (v6-marquee): log ticker with 4 entries (animated horizontally) Content duplicated for seamless loop - Navigation.astro (v6-nav): REPLACED legacy. Adds 'บริการ' dropdown (4 services) + 'บทความ' link per plan round 2. Click-to-toggle on mobile. - src/data/nav.ts: single source of truth for mainLinks + servicesDropdown - fx-system.css: +27 lines for dropdown styles + active link underline (v6-nav originally had no dropdown — we added per spec) Refs: .hermes/plans/2026-06-13_124000-moreminimore-v7-5-migration.md Task 2.1-2.4
This commit is contained in:
30
src/components/Marquee.astro
Normal file
30
src/components/Marquee.astro
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
/**
|
||||||
|
* MOREMINIMORE - Marquee (from v6-marquee)
|
||||||
|
* Extracted from Desktop/moreminomore-mockup-v7-5.html lines 590-609
|
||||||
|
*
|
||||||
|
* Log ticker — animates horizontally (marquee 40s linear infinite in fx-system.css)
|
||||||
|
* Content is hardcoded ticker entries (not dynamic) — same as v7-5 design
|
||||||
|
*
|
||||||
|
* Duplicate content to achieve seamless loop (track is rendered twice)
|
||||||
|
*/
|
||||||
|
const tickerEntries = [
|
||||||
|
{ ts: '[2026-06-13]', text: '<span class="cmd">$</span> build/deploy <span class="ok">✓</span>' },
|
||||||
|
{ ts: '[log]', text: 'Dataroot +373% Impression <span class="ok">✓</span>' },
|
||||||
|
{ ts: '[2026-06-13]', text: '<span class="cmd">$</span> contact/free <em>30 min</em> <span class="ok">✓</span>' },
|
||||||
|
{ ts: '[log]', text: '9 case studies, 0 fabricated <span class="ok">✓</span>' },
|
||||||
|
];
|
||||||
|
---
|
||||||
|
|
||||||
|
<div id="v6-marquee-inner" class="fx-marquee">
|
||||||
|
<div class="fx-marquee-track">
|
||||||
|
{/* First copy */}
|
||||||
|
{tickerEntries.map((entry) => (
|
||||||
|
<span><span class="ts">{entry.ts}</span> <Fragment set:html={entry.text} /></span>
|
||||||
|
))}
|
||||||
|
{/* Second copy (duplicated for seamless loop) */}
|
||||||
|
{tickerEntries.map((entry) => (
|
||||||
|
<span><span class="ts">{entry.ts}</span> <Fragment set:html={entry.text} /></span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -1,529 +1,82 @@
|
|||||||
---
|
---
|
||||||
/**
|
/**
|
||||||
* MOREMINIMORE - NAVIGATION COMPONENT (LIGHT THEME)
|
* MOREMINIMORE - NAVIGATION (from v6-nav, enhanced)
|
||||||
* Light bg + dark text nav links + white logo banner
|
* Extracted from Desktop/moreminomore-mockup-v7-5.html lines 662-684
|
||||||
|
*
|
||||||
|
* Per plan 2026-06-13 round 2:
|
||||||
|
* - v6-nav structure: logo + menu + CTA
|
||||||
|
* - Enhancement A: dropdown 'บริการ' (4 sub-items from servicesDropdown)
|
||||||
|
* - Enhancement C: add 'บทความ' link (blog) — not in original v6-nav
|
||||||
|
* - Social icons: not in v6-nav itself; they live in UtilityBar (per plan)
|
||||||
|
*
|
||||||
|
* Props:
|
||||||
|
* - currentPath: string — for active link highlighting (optional, defaults to Astro.url.pathname)
|
||||||
*/
|
*/
|
||||||
|
import { mainLinks, servicesDropdown } from '../data/nav';
|
||||||
|
|
||||||
const services = [
|
interface Props {
|
||||||
{ label: 'Website Development', href: '/services/webdev' },
|
currentPath?: string;
|
||||||
{ label: 'Marketing Automation', href: '/services/marketing' },
|
}
|
||||||
{ label: 'AI Automation', href: '/services/automation' },
|
|
||||||
{ label: 'Tech Consult', href: '/services/ai-consult' },
|
const { currentPath = Astro.url.pathname } = Astro.props;
|
||||||
];
|
const isActive = (href: string) => {
|
||||||
|
if (href === '/') return currentPath === '/';
|
||||||
|
return currentPath.startsWith(href);
|
||||||
|
};
|
||||||
---
|
---
|
||||||
|
|
||||||
<header class="nav" id="nav">
|
<nav id="v6-nav-inner" class="fx-nav">
|
||||||
<div class="nav-container container">
|
<div class="fx-nav-inner">
|
||||||
<!-- Logo: white-bg banner with the black-text logo (works on light/yellow) -->
|
<a href="/" class="fx-nav-logo">
|
||||||
<a href="/" class="nav-logo">
|
<span class="fx-nav-logo-text">MOREMI<em>ni</em>MORE</span>
|
||||||
<div class="logo-banner">
|
|
||||||
<img src="/images/logo-long-black.png" alt="MoreminiMore" class="logo-img" />
|
|
||||||
</div>
|
|
||||||
</a>
|
</a>
|
||||||
|
<ul class="fx-nav-menu">
|
||||||
<!-- Desktop Navigation -->
|
{mainLinks.map((link) => (
|
||||||
<nav class="nav-desktop">
|
<li class={link.hasDropdown ? 'fx-nav-has-dropdown' : ''}>
|
||||||
<a href="/" class="nav-link" data-magnetic>หน้าแรก</a>
|
{link.hasDropdown ? (
|
||||||
|
<>
|
||||||
<!-- Services with Submenu -->
|
<a href={link.href} class:list={[isActive(link.href) && 'active']}>
|
||||||
<div class="nav-dropdown">
|
{link.label}
|
||||||
<a href="/services" class="nav-link nav-dropdown-trigger" data-magnetic>
|
</a>
|
||||||
บริการ
|
<ul class="fx-nav-dropdown">
|
||||||
<svg class="dropdown-arrow" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
{servicesDropdown.map((service) => (
|
||||||
<path d="M6 9l6 6 6-6"/>
|
<li>
|
||||||
</svg>
|
<a href={service.href} class:list={[isActive(service.href) && 'active']}>
|
||||||
</a>
|
{service.label}
|
||||||
<div class="nav-dropdown-menu">
|
</a>
|
||||||
{services.map(service => (
|
</li>
|
||||||
<a href={service.href} class="dropdown-item">
|
))}
|
||||||
{service.label}
|
</ul>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<a href={link.href} class:list={[isActive(link.href) && 'active']}>
|
||||||
|
{link.label}
|
||||||
</a>
|
</a>
|
||||||
))}
|
)}
|
||||||
</div>
|
</li>
|
||||||
</div>
|
))}
|
||||||
|
</ul>
|
||||||
<a href="/portfolio" class="nav-link" data-magnetic>ผลงาน</a>
|
<a href="/contact" class="fx-nav-cta">ปรึกษาฟรี →</a>
|
||||||
<a href="/blog" class="nav-link" data-magnetic>บทความ</a>
|
|
||||||
<a href="/about" class="nav-link" data-magnetic>เกี่ยวกับเรา</a>
|
|
||||||
<a href="/contact" class="nav-link nav-cta" data-magnetic>ติดต่อเรา</a>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<!-- Mobile Menu Toggle (DARK icons on light bg) -->
|
|
||||||
<button class="nav-toggle" id="nav-toggle" aria-label="Toggle menu">
|
|
||||||
<span class="toggle-line"></span>
|
|
||||||
<span class="toggle-line"></span>
|
|
||||||
<span class="toggle-line"></span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
</nav>
|
||||||
<!-- Mobile Navigation Overlay (LIGHT bg) -->
|
|
||||||
<div class="nav-mobile-overlay" id="nav-mobile">
|
|
||||||
<nav class="nav-mobile-content">
|
|
||||||
<a href="/" class="mobile-link">หน้าแรก</a>
|
|
||||||
|
|
||||||
<!-- Mobile Services with Submenu -->
|
|
||||||
<div class="mobile-dropdown">
|
|
||||||
<button class="mobile-dropdown-trigger">
|
|
||||||
บริการ
|
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
||||||
<path d="M6 9l6 6 6-6"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<div class="mobile-dropdown-content">
|
|
||||||
{services.map(service => (
|
|
||||||
<a href={service.href} class="mobile-link mobile-sublink">{service.label}</a>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a href="/portfolio" class="mobile-link">ผลงาน</a>
|
|
||||||
<a href="/blog" class="mobile-link">บทความ</a>
|
|
||||||
<a href="/about" class="mobile-link">เกี่ยวกับเรา</a>
|
|
||||||
<a href="/contact" class="mobile-link mobile-cta">ติดต่อเรา</a>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<!-- Scroll Progress Bar (yellow) -->
|
|
||||||
<div class="scroll-progress" id="scroll-progress"></div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
/* ============================================
|
|
||||||
NAVIGATION BASE — LIGHT THEME
|
|
||||||
============================================ */
|
|
||||||
|
|
||||||
.nav {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
z-index: 1000;
|
|
||||||
padding: 16px 0;
|
|
||||||
background: var(--color-white);
|
|
||||||
border-bottom: 1px solid var(--color-gray-200);
|
|
||||||
transition: all 0.3s var(--ease-out-expo);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav.scrolled {
|
|
||||||
padding: 12px 0;
|
|
||||||
background: rgba(255, 255, 255, 0.95);
|
|
||||||
backdrop-filter: blur(20px);
|
|
||||||
-webkit-backdrop-filter: blur(20px);
|
|
||||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.06);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
CONTAINER
|
|
||||||
============================================ */
|
|
||||||
|
|
||||||
.nav-container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
LOGO BANNER
|
|
||||||
============================================ */
|
|
||||||
|
|
||||||
.nav-logo {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
z-index: 1001;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo-banner {
|
|
||||||
background: var(--color-white);
|
|
||||||
border-radius: 0 0 12px 12px;
|
|
||||||
padding: 8px 16px;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo-banner:hover {
|
|
||||||
transform: scale(1.02);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav.scrolled .logo-banner {
|
|
||||||
border-radius: 0 0 8px 8px;
|
|
||||||
padding: 6px 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo-img {
|
|
||||||
height: 36px;
|
|
||||||
width: auto;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav.scrolled .logo-img {
|
|
||||||
height: 28px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
DESKTOP NAVIGATION
|
|
||||||
============================================ */
|
|
||||||
|
|
||||||
.nav-desktop {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-link {
|
|
||||||
position: relative;
|
|
||||||
padding: 12px 16px;
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 600;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 2px;
|
|
||||||
color: var(--color-black);
|
|
||||||
transition: color 0.3s ease;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-link:hover,
|
|
||||||
.nav-link.active {
|
|
||||||
color: var(--color-primary-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-cta {
|
|
||||||
background: var(--color-primary);
|
|
||||||
color: var(--color-black) !important;
|
|
||||||
border: 2px solid var(--color-primary);
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
margin-left: 16px;
|
|
||||||
padding: 12px 24px;
|
|
||||||
transition: all 0.3s var(--ease-out-expo);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-cta:hover {
|
|
||||||
background: var(--color-primary-dark);
|
|
||||||
border-color: var(--color-primary-dark);
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 8px 20px rgba(254, 212, 0, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-cta::after { display: none; }
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
DROPDOWN — LIGHT CARD ON WHITE BG
|
|
||||||
============================================ */
|
|
||||||
|
|
||||||
.nav-dropdown {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-dropdown-trigger {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-arrow {
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
transition: transform 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-dropdown:hover .dropdown-arrow {
|
|
||||||
transform: rotate(180deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-dropdown-menu {
|
|
||||||
position: absolute;
|
|
||||||
top: 100%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%) translateY(10px);
|
|
||||||
min-width: 220px;
|
|
||||||
background: var(--color-white);
|
|
||||||
border: 1px solid var(--color-gray-200);
|
|
||||||
border-radius: var(--radius-lg);
|
|
||||||
padding: 12px 0;
|
|
||||||
opacity: 0;
|
|
||||||
visibility: hidden;
|
|
||||||
transition: all 0.3s var(--ease-out-expo);
|
|
||||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-dropdown:hover .nav-dropdown-menu {
|
|
||||||
opacity: 1;
|
|
||||||
visibility: visible;
|
|
||||||
transform: translateX(-50%) translateY(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-item {
|
|
||||||
display: block;
|
|
||||||
padding: 12px 24px;
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--color-gray-700);
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-item:hover {
|
|
||||||
background: var(--color-bg-alt);
|
|
||||||
color: var(--color-primary-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
MOBILE TOGGLE — DARK lines (since nav is light)
|
|
||||||
============================================ */
|
|
||||||
|
|
||||||
.nav-toggle {
|
|
||||||
display: none;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 5px;
|
|
||||||
width: 44px;
|
|
||||||
height: 44px;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
z-index: 1001;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggle-line {
|
|
||||||
width: 24px;
|
|
||||||
height: 2px;
|
|
||||||
background: var(--color-black);
|
|
||||||
border-radius: 2px;
|
|
||||||
transition: all 0.3s var(--ease-out-expo);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-toggle.active .toggle-line:nth-child(1) {
|
|
||||||
transform: rotate(45deg) translate(5px, 5px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-toggle.active .toggle-line:nth-child(2) {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateX(-10px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-toggle.active .toggle-line:nth-child(3) {
|
|
||||||
transform: rotate(-45deg) translate(5px, -5px);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
MOBILE OVERLAY — LIGHT THEME
|
|
||||||
============================================ */
|
|
||||||
|
|
||||||
.nav-mobile-overlay {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background: var(--color-white);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
opacity: 0;
|
|
||||||
visibility: hidden;
|
|
||||||
transition: all 0.4s var(--ease-out-expo);
|
|
||||||
z-index: 999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-mobile-overlay.active {
|
|
||||||
opacity: 1;
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-mobile-content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
padding: 40px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile-link {
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: 28px;
|
|
||||||
font-weight: 800;
|
|
||||||
color: var(--color-black);
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 2px;
|
|
||||||
padding: 14px 32px;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(20px);
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-mobile-overlay.active .mobile-link {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-mobile-overlay.active .mobile-link:nth-child(1) { transition-delay: 0.1s; }
|
|
||||||
.nav-mobile-overlay.active .mobile-link:nth-child(2) { transition-delay: 0.15s; }
|
|
||||||
.nav-mobile-overlay.active .mobile-link:nth-child(3) { transition-delay: 0.2s; }
|
|
||||||
.nav-mobile-overlay.active .mobile-link:nth-child(4) { transition-delay: 0.25s; }
|
|
||||||
.nav-mobile-overlay.active .mobile-link:nth-child(5) { transition-delay: 0.3s; }
|
|
||||||
.nav-mobile-overlay.active .mobile-link:nth-child(6) { transition-delay: 0.35s; }
|
|
||||||
|
|
||||||
.mobile-link:hover {
|
|
||||||
color: var(--color-primary-dark);
|
|
||||||
transform: translateX(10px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile-cta {
|
|
||||||
background: var(--color-primary);
|
|
||||||
color: var(--color-black) !important;
|
|
||||||
border: 2px solid var(--color-primary);
|
|
||||||
border-radius: var(--radius-lg);
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
MOBILE DROPDOWN
|
|
||||||
============================================ */
|
|
||||||
|
|
||||||
.mobile-dropdown {
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile-dropdown-trigger {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 8px;
|
|
||||||
width: 100%;
|
|
||||||
padding: 14px 32px;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
font-family: var(--font-display);
|
|
||||||
font-size: 28px;
|
|
||||||
font-weight: 800;
|
|
||||||
color: var(--color-black);
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 2px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: color 0.3s ease;
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(20px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile-dropdown-trigger svg {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
transition: transform 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile-dropdown.open .mobile-dropdown-trigger {
|
|
||||||
color: var(--color-primary-dark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile-dropdown.open .mobile-dropdown-trigger svg {
|
|
||||||
transform: rotate(180deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile-dropdown-content {
|
|
||||||
max-height: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
transition: max-height 0.4s var(--ease-out-expo);
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile-dropdown.open .mobile-dropdown-content {
|
|
||||||
max-height: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile-sublink {
|
|
||||||
font-size: 20px !important;
|
|
||||||
padding: 10px 32px !important;
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mobile-sublink:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
SCROLL PROGRESS
|
|
||||||
============================================ */
|
|
||||||
|
|
||||||
.scroll-progress {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
height: 3px;
|
|
||||||
background: linear-gradient(90deg, var(--color-primary), #ffe066);
|
|
||||||
width: 0;
|
|
||||||
z-index: 1001;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
|
||||||
RESPONSIVE
|
|
||||||
============================================ */
|
|
||||||
|
|
||||||
@media (max-width: 1024px) {
|
|
||||||
.nav-desktop {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-toggle {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
|
||||||
.mobile-link {
|
|
||||||
font-size: 24px;
|
|
||||||
padding: 12px 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Navigation scroll effect
|
// Mobile menu toggle — only relevant on small screens
|
||||||
const nav = document.getElementById('nav');
|
// Desktop uses hover for dropdown (handled by CSS)
|
||||||
const toggle = document.getElementById('nav-toggle');
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
const mobileNav = document.getElementById('nav-mobile');
|
const nav = document.getElementById('v6-nav-inner');
|
||||||
const scrollProgress = document.getElementById('scroll-progress');
|
if (!nav) return;
|
||||||
|
|
||||||
// Scroll effect
|
// Find all items with dropdowns and add click-to-toggle for mobile
|
||||||
window.addEventListener('scroll', () => {
|
const dropdownItems = nav.querySelectorAll('.fx-nav-has-dropdown > a');
|
||||||
const scrollY = window.scrollY;
|
dropdownItems.forEach((trigger) => {
|
||||||
|
trigger.addEventListener('click', (e) => {
|
||||||
if (scrollY > 80) {
|
if (window.innerWidth <= 768) {
|
||||||
nav?.classList.add('scrolled');
|
e.preventDefault();
|
||||||
} else {
|
const parent = trigger.parentElement;
|
||||||
nav?.classList.remove('scrolled');
|
if (parent) parent.classList.toggle('fx-nav-dropdown-open');
|
||||||
}
|
}
|
||||||
|
});
|
||||||
// Scroll progress
|
|
||||||
const docHeight = document.documentElement.scrollHeight - window.innerHeight;
|
|
||||||
const progress = (scrollY / docHeight) * 100;
|
|
||||||
if (scrollProgress) {
|
|
||||||
scrollProgress.style.width = `${progress}%`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Mobile menu toggle
|
|
||||||
toggle?.addEventListener('click', () => {
|
|
||||||
toggle.classList.toggle('active');
|
|
||||||
mobileNav?.classList.toggle('active');
|
|
||||||
document.body.style.overflow = mobileNav?.classList.contains('active') ? 'hidden' : '';
|
|
||||||
});
|
|
||||||
|
|
||||||
// Mobile dropdown toggle
|
|
||||||
document.querySelectorAll('.mobile-dropdown-trigger').forEach(btn => {
|
|
||||||
btn.addEventListener('click', () => {
|
|
||||||
btn.parentElement?.classList.toggle('open');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Close mobile nav on link click
|
|
||||||
document.querySelectorAll('.nav-mobile-overlay .mobile-link').forEach(link => {
|
|
||||||
link.addEventListener('click', () => {
|
|
||||||
toggle?.classList.remove('active');
|
|
||||||
mobileNav?.classList.remove('active');
|
|
||||||
document.body.style.overflow = '';
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
30
src/components/UtilityBar.astro
Normal file
30
src/components/UtilityBar.astro
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
/**
|
||||||
|
* MOREMINIMORE - UtilityBar (from v6-utility)
|
||||||
|
* Extracted from Desktop/moreminomore-mockup-v7-5.html lines 571-589
|
||||||
|
*
|
||||||
|
* Top info bar — phone + clock + date + mode indicator + email
|
||||||
|
* Phone/email pulled from src/content/settings/site.md (single source of truth)
|
||||||
|
*
|
||||||
|
* Clock/date are updated by fx-animations.ts → fxClock() (id="fx-time", id="fx-date")
|
||||||
|
*/
|
||||||
|
import { getEntry } from 'astro:content';
|
||||||
|
import type { CollectionEntry } from 'astro:content';
|
||||||
|
|
||||||
|
const site = (await getEntry('settings', 'site')) as CollectionEntry<'settings'>;
|
||||||
|
const phone = site?.data?.phone ?? '080-995-5945';
|
||||||
|
const email = site?.data?.email ?? 'contact@moreminimore.com';
|
||||||
|
---
|
||||||
|
|
||||||
|
<div id="v6-utility-inner" class="fx-utility-bar">
|
||||||
|
<div class="fx-utility-bar-left">
|
||||||
|
<span class="fx-utility-item">📞 <strong>{phone}</strong></span>
|
||||||
|
<span class="fx-utility-item" id="fx-time">⏱ — : — : —</span>
|
||||||
|
<span class="fx-utility-item" id="fx-date">📅 — — —</span>
|
||||||
|
</div>
|
||||||
|
<div class="fx-utility-bar-right">
|
||||||
|
<span class="fx-mode-indicator">system</span>
|
||||||
|
<span class="fx-theme-toggle" id="fx-theme-toggle">◐ auto</span>
|
||||||
|
<a href={`mailto:${email}`} class="fx-utility-item" style="text-decoration:none">✉ {email}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
43
src/data/nav.ts
Normal file
43
src/data/nav.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* MOREMINIMORE - Nav data (single source of truth)
|
||||||
|
* Used by Navigation.astro and Footer.astro
|
||||||
|
*
|
||||||
|
* Per plan 2026-06-13 round 2:
|
||||||
|
* - Main menu: 7 items including 'บทความ' (blog) + 'FAQ' + 'ติดต่อ'
|
||||||
|
* - Services dropdown: 4 services (matched to content collection slugs)
|
||||||
|
* - Social: facebook, line, linkedin (from settings collection at runtime)
|
||||||
|
*
|
||||||
|
* Slugs match src/content/services/*-new.mdx:
|
||||||
|
* - ai-consult-new → /services/ai-consult
|
||||||
|
* - marketing-new → /services/marketing
|
||||||
|
* - automation-new → /services/automation
|
||||||
|
* - webdev-new → /services/webdev
|
||||||
|
*
|
||||||
|
* Href uses /services/{slug-without-new} to match the [slug].astro route.
|
||||||
|
*/
|
||||||
|
export const mainLinks = [
|
||||||
|
{ label: 'หน้าแรก', href: '/' },
|
||||||
|
{ label: 'บริการ', href: '/services', hasDropdown: true },
|
||||||
|
{ label: 'ผลงาน', href: '/portfolio' },
|
||||||
|
{ label: 'บทความ', href: '/blog' },
|
||||||
|
{ label: 'เกี่ยวกับ', href: '/about' },
|
||||||
|
{ label: 'FAQ', href: '/faq' },
|
||||||
|
{ label: 'ติดต่อ', href: '/contact' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const servicesDropdown = [
|
||||||
|
{ label: 'AI Consult', href: '/services/ai-consult' },
|
||||||
|
{ label: 'Marketing Automation', href: '/services/marketing' },
|
||||||
|
{ label: 'AI Automation', href: '/services/automation' },
|
||||||
|
{ label: 'Website Development', href: '/services/webdev' },
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Social links are passed as props to the component
|
||||||
|
* (because they come from the settings collection which is loaded async)
|
||||||
|
*/
|
||||||
|
export interface SocialLinks {
|
||||||
|
facebook?: string;
|
||||||
|
line?: string;
|
||||||
|
linkedin?: string;
|
||||||
|
}
|
||||||
@@ -483,3 +483,29 @@ img{max-width:100%;display:block}
|
|||||||
.fx-marquee-track{animation:none}
|
.fx-marquee-track{animation:none}
|
||||||
.fx-faq-a::after{display:none}
|
.fx-faq-a::after{display:none}
|
||||||
.fx-hero::after{display:none}
|
.fx-hero::after{display:none}
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
NAV DROPDOWN ENHANCEMENT (added 2026-06-13, plan round 2)
|
||||||
|
v6-nav didn't have a dropdown originally; we added
|
||||||
|
'บริการ' dropdown per user spec. Active link styling too.
|
||||||
|
============================================ */
|
||||||
|
.fx-nav-menu{position:relative}
|
||||||
|
.fx-nav-menu li{position:relative}
|
||||||
|
.fx-nav-dropdown{position:absolute;top:100%;left:0;min-width:240px;background:var(--paper);border:1.5px solid var(--ink);box-shadow:4px 4px 0 var(--ink);list-style:none;padding:8px 0;margin:0;opacity:0;visibility:hidden;transform:translateY(-4px);transition:opacity 0.18s,transform 0.18s,visibility 0.18s;z-index:60}
|
||||||
|
.fx-nav-has-dropdown:hover>.fx-nav-dropdown,
|
||||||
|
.fx-nav-dropdown-open>.fx-nav-dropdown{opacity:1;visibility:visible;transform:translateY(0)}
|
||||||
|
.fx-nav-dropdown li{margin:0}
|
||||||
|
.fx-nav-dropdown a{display:block;padding:10px 18px;font:600 12px/1.3 'JetBrains Mono',monospace;text-transform:uppercase;letter-spacing:0.3px;color:var(--ink);text-decoration:none;transition:background 0.15s,color 0.15s}
|
||||||
|
.fx-nav-dropdown a:hover{background:var(--brand-yellow);color:var(--ink)}
|
||||||
|
.fx-nav-dropdown a.active{background:var(--coral);color:#FAFAFA}
|
||||||
|
.fx-nav-menu a.active{color:var(--coral)}
|
||||||
|
.fx-nav-menu a.active::after{content:'';display:block;height:2px;background:var(--coral);margin-top:4px}
|
||||||
|
@media (max-width:768px){
|
||||||
|
.fx-nav-inner{flex-direction:column;align-items:flex-start;padding:12px 16px}
|
||||||
|
.fx-nav-menu{flex-direction:column;width:100%;gap:0}
|
||||||
|
.fx-nav-menu>li{width:100%;border-bottom:1px solid var(--line-2)}
|
||||||
|
.fx-nav-menu>li>a{display:block;padding:12px 0}
|
||||||
|
.fx-nav-dropdown{position:static;box-shadow:none;border:none;background:var(--paper-2);opacity:1;visibility:visible;transform:none;max-height:0;overflow:hidden;padding:0;transition:max-height 0.25s,padding 0.25s}
|
||||||
|
.fx-nav-dropdown-open>.fx-nav-dropdown{max-height:400px;padding:8px 0}
|
||||||
|
.fx-nav-cta{margin-top:12px;align-self:flex-start}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user