Skip to content

Commit 21721c3

Browse files
authored
Add MCP Integration and Technical SEO Service Pages
- Created mcp-integration.astro page detailing Model Context Protocol services, including FAQs, problem and solution sections, and service offerings. - Created technical-seo.astro page outlining Technical SEO and Generative Engine Optimization (GEO) services, featuring FAQs, problem and solution sections, and comprehensive service details.
1 parent a738fe0 commit 21721c3

9 files changed

Lines changed: 1244 additions & 42 deletions

File tree

src/components/HeaderNav.astro

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,19 @@ const { background = "", currentPath = Astro.url.pathname } =
1515
1616
const navLinks: NavLink[] = [
1717
{ label: "Home", href: "/" },
18-
{ label: "Services", href: "/services/" },
18+
{
19+
label: "Services",
20+
href: "/services/",
21+
children: [
22+
{ label: "AI Automation", href: "/services/ai-automation/" },
23+
{ label: "MCP Integration", href: "/services/mcp-integration/" },
24+
{ label: "Technical SEO & GEO", href: "/services/technical-seo/" },
25+
{
26+
label: "Full Stack Development",
27+
href: "/services/fullstack-development/",
28+
},
29+
],
30+
},
1931
{ label: "Portfolio", href: "/portfolio/" },
2032
{ label: "About", href: "/about/" },
2133
{ label: "Blog", href: "/blog/" },

src/components/solid/Navigation.tsx

Lines changed: 142 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export interface NavLink {
55
label: string;
66
href: string;
77
isActive?: boolean;
8+
children?: NavLink[];
89
}
910

1011
interface NavigationProps {
@@ -14,6 +15,7 @@ interface NavigationProps {
1415

1516
const Navigation: Component<NavigationProps> = (props) => {
1617
const [isOpen, setIsOpen] = createSignal(false);
18+
const [openDropdown, setOpenDropdown] = createSignal<string | null>(null);
1719

1820
const isActive = (href: string): boolean => {
1921
if (href === "/") {
@@ -22,6 +24,11 @@ const Navigation: Component<NavigationProps> = (props) => {
2224
return props.currentPath.startsWith(href);
2325
};
2426

27+
const hasActiveChild = (link: NavLink): boolean => {
28+
if (!link.children) return false;
29+
return link.children.some((child) => isActive(child.href));
30+
};
31+
2532
onMount(() => {
2633
if (typeof window === "undefined") return;
2734

@@ -60,15 +67,76 @@ const Navigation: Component<NavigationProps> = (props) => {
6067
<ul class="hidden md:flex items-center gap-1 rounded-xl border border-[var(--border-strong)] bg-[color-mix(in_srgb,var(--surface-panel)_84%,transparent)] px-1.5 py-1 shadow-[0_10px_24px_rgba(15,23,42,0.08)]">
6168
<For each={props.links}>
6269
{(link) => (
63-
<li>
64-
<a
65-
class={`${baseClass} ${isActive(link.href) ? activeClass : ""}`}
66-
href={link.href}
67-
data-track={`nav_desktop_${toTrackToken(link.label)}`}
68-
aria-current={isActive(link.href) ? "page" : undefined}
70+
<li class="relative">
71+
<Show
72+
when={link.children && link.children.length > 0}
73+
fallback={
74+
<a
75+
class={`${baseClass} ${isActive(link.href) ? activeClass : ""}`}
76+
href={link.href}
77+
data-track={`nav_desktop_${toTrackToken(link.label)}`}
78+
aria-current={isActive(link.href) ? "page" : undefined}
79+
>
80+
{link.label}
81+
</a>
82+
}
6983
>
70-
{link.label}
71-
</a>
84+
<button
85+
type="button"
86+
class={`${baseClass} ${isActive(link.href) || hasActiveChild(link) ? activeClass : ""} gap-1`}
87+
onClick={() =>
88+
setOpenDropdown(
89+
openDropdown() === link.label ? null : link.label,
90+
)
91+
}
92+
onMouseEnter={() => setOpenDropdown(link.label)}
93+
aria-expanded={openDropdown() === link.label}
94+
aria-haspopup="true"
95+
>
96+
{link.label}
97+
<svg
98+
class={`h-3.5 w-3.5 transition-transform ${openDropdown() === link.label ? "rotate-180" : ""}`}
99+
viewBox="0 0 24 24"
100+
fill="none"
101+
stroke="currentColor"
102+
stroke-width="2.5"
103+
stroke-linecap="round"
104+
stroke-linejoin="round"
105+
aria-hidden="true"
106+
>
107+
<polyline points="6 9 12 15 18 9" />
108+
</svg>
109+
</button>
110+
<Show when={openDropdown() === link.label}>
111+
<div
112+
class="absolute left-0 top-full z-50 mt-1 min-w-[220px] rounded-xl border border-[var(--border-strong)] bg-[var(--surface-panel)] p-1.5 shadow-lg"
113+
onMouseLeave={() => setOpenDropdown(null)}
114+
role="menu"
115+
>
116+
<For each={link.children}>
117+
{(child) => (
118+
<a
119+
class="block rounded-lg px-4 py-2.5 text-sm font-medium text-[var(--text-muted)] transition-colors hover:bg-[var(--surface-elevated)] hover:text-[var(--text-strong)]"
120+
href={child.href}
121+
data-track={`nav_desktop_${toTrackToken(link.label)}_${toTrackToken(child.label)}`}
122+
onClick={() => setOpenDropdown(null)}
123+
>
124+
{child.label}
125+
</a>
126+
)}
127+
</For>
128+
<div class="my-1.5 border-t border-[var(--border-strong)]" />
129+
<a
130+
class="block rounded-lg px-4 py-2.5 text-sm font-semibold text-[var(--accent-strong)] transition-colors hover:bg-[var(--surface-elevated)]"
131+
href={link.href}
132+
data-track={`nav_desktop_${toTrackToken(link.label)}_all`}
133+
onClick={() => setOpenDropdown(null)}
134+
>
135+
View All Services →
136+
</a>
137+
</div>
138+
</Show>
139+
</Show>
72140
</li>
73141
)}
74142
</For>
@@ -162,19 +230,76 @@ const Navigation: Component<NavigationProps> = (props) => {
162230
</svg>
163231
</button>
164232
</div>
165-
<ul class="py-2">
233+
<ul class="py-2 overflow-y-auto max-h-[calc(100vh-180px)]">
166234
<For each={props.links}>
167235
{(link) => (
168236
<li>
169-
<a
170-
class={`${mobileBaseClass} ${isActive(link.href) ? mobileActiveClass : ""}`}
171-
href={link.href}
172-
data-track={`nav_mobile_${toTrackToken(link.label)}`}
173-
onClick={() => setIsOpen(false)}
174-
aria-current={isActive(link.href) ? "page" : undefined}
237+
<Show
238+
when={link.children && link.children.length > 0}
239+
fallback={
240+
<a
241+
class={`${mobileBaseClass} ${isActive(link.href) ? mobileActiveClass : ""}`}
242+
href={link.href}
243+
data-track={`nav_mobile_${toTrackToken(link.label)}`}
244+
onClick={() => setIsOpen(false)}
245+
aria-current={isActive(link.href) ? "page" : undefined}
246+
>
247+
{link.label}
248+
</a>
249+
}
175250
>
176-
{link.label}
177-
</a>
251+
<button
252+
type="button"
253+
class={`${mobileBaseClass} flex items-center justify-between ${isActive(link.href) || hasActiveChild(link) ? mobileActiveClass : ""}`}
254+
onClick={() =>
255+
setOpenDropdown(
256+
openDropdown() === link.label ? null : link.label,
257+
)
258+
}
259+
>
260+
{link.label}
261+
<svg
262+
class={`h-4 w-4 transition-transform ${openDropdown() === link.label ? "rotate-180" : ""}`}
263+
viewBox="0 0 24 24"
264+
fill="none"
265+
stroke="currentColor"
266+
stroke-width="2"
267+
stroke-linecap="round"
268+
stroke-linejoin="round"
269+
aria-hidden="true"
270+
>
271+
<polyline points="6 9 12 15 18 9" />
272+
</svg>
273+
</button>
274+
<Show when={openDropdown() === link.label}>
275+
<ul class="bg-[var(--surface-panel)] border-t border-[var(--border-strong)]">
276+
<For each={link.children}>
277+
{(child) => (
278+
<li>
279+
<a
280+
class="block px-8 py-2.5 text-sm text-[var(--text-muted)] hover:bg-[var(--surface-elevated)] hover:text-[var(--text-strong)]"
281+
href={child.href}
282+
data-track={`nav_mobile_${toTrackToken(link.label)}_${toTrackToken(child.label)}`}
283+
onClick={() => setIsOpen(false)}
284+
>
285+
{child.label}
286+
</a>
287+
</li>
288+
)}
289+
</For>
290+
<li>
291+
<a
292+
class="block px-8 py-2.5 text-sm font-semibold text-[var(--accent-strong)] hover:bg-[var(--surface-elevated)]"
293+
href={link.href}
294+
data-track={`nav_mobile_${toTrackToken(link.label)}_all`}
295+
onClick={() => setIsOpen(false)}
296+
>
297+
View All Services →
298+
</a>
299+
</li>
300+
</ul>
301+
</Show>
302+
</Show>
178303
</li>
179304
)}
180305
</For>

0 commit comments

Comments
 (0)