The Svelte 5 SSG Migration #1

Merged
mifi merged 14 commits from feature/svelte-ssg into main 2026-02-01 05:50:42 +00:00
14 changed files with 618 additions and 292 deletions
Showing only changes of commit 263bf9d2d3 - Show all commits

View File

@@ -476,6 +476,55 @@ a {
} }
} }
/* ========================================
Nav Item and Footer Links Common Styles
======================================== */
.footer-links,
.nav-item {
display: flex;
gap: var(--space-lg);
justify-content: center;
align-items: center;
@media (max-width: 480px) {
gap: var(--space-md);
}
& a {
font-size: var(--font-size-medium);
font-weight: var(--font-weight-medium);
color: var(--color-text-secondary);
text-decoration: none;
transition: color var(--transition-fast);
border-bottom: 1px solid transparent;
padding: var(--space-xs) var(--space-sm);
margin: calc(-1 * var(--space-xs)) calc(-1 * var(--space-sm));
min-height: 44px;
display: inline-flex;
align-items: center;
&:hover {
color: var(--color-primary);
border-bottom-color: var(--color-primary);
}
&:focus-visible {
outline: 4px solid var(--color-focus);
outline-offset: 4px;
border-bottom-color: transparent;
box-shadow: 0 0 0 8px var(--color-focus-outline);
border-radius: 3px;
}
@media print {
&:after {
content: none;
}
}
}
}
/* ======================================== /* ========================================
Responsive Design Responsive Design
======================================== */ ======================================== */

View File

@@ -35,48 +35,4 @@
color: var(--color-text-tertiary); color: var(--color-text-tertiary);
max-width: 100%; max-width: 100%;
} }
.footer-links {
display: flex;
gap: var(--space-lg);
justify-content: center;
align-items: center;
@media (max-width: 480px) {
gap: var(--space-md);
}
& a {
font-size: var(--font-size-medium);
font-weight: var(--font-weight-medium);
color: var(--color-text-secondary);
text-decoration: none;
transition: color var(--transition-fast);
border-bottom: 1px solid transparent;
padding: var(--space-xs) var(--space-sm);
margin: calc(-1 * var(--space-xs)) calc(-1 * var(--space-sm));
min-height: 44px;
display: inline-flex;
align-items: center;
&:hover {
color: var(--color-primary);
border-bottom-color: var(--color-primary);
}
&:focus-visible {
outline: 4px solid var(--color-focus);
outline-offset: 4px;
border-bottom-color: transparent;
box-shadow: 0 0 0 8px var(--color-focus-outline);
border-radius: 3px;
}
@media print {
&:after {
content: none;
}
}
}
}
</style> </style>

View File

@@ -0,0 +1,319 @@
<script lang="ts">
import Wordmark from './Wordmark.svelte';
</script>
<nav id="nav" class="nav" aria-label="Main navigation">
<input
type="checkbox"
id="nav-toggle"
class="nav-toggle-input"
aria-hidden="true"
hidden
/>
<div class="mobile-nav-header">
<span class="mobile nav-header-logo">
<Wordmark />
</span>
<label for="nav-toggle" class="nav-toggle" aria-label="Toggle navigation">
<span class="nav-toggle-inner">
<span class="nav-toggle-line"></span>
<span class="nav-toggle-line"></span>
<span class="nav-toggle-line"></span>
</span>
</label>
</div>
<div class="nav-menu container">
<span class="nav-header-logo desktop">
<Wordmark />
</span>
<ul class="nav-list">
<li class="nav-item">
<a href="#what-we-do" class="nav-link">Services</a>
</li>
<li class="nav-item">
<a href="#impact" class="nav-link">Impact</a>
</li>
<li class="nav-item">
<a href="#how-we-work" class="nav-link">Process</a>
</li>
<li class="nav-item">
<a href="#schedule" class="nav-link">Contact</a>
</li>
</ul>
<div class="nav-item nav-back-to-top">
<a href="#header" class="nav-link">Back to top</a>
</div>
</div>
</nav>
<style>
.nav {
background-color: var(--color-bg);
background-color: var(--color-bg);
border-bottom: 1px solid var(--color-border);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.6);
padding: var(--space-md) 0;
position: sticky;
text-align: center;
top: 0;
z-index: 100;
@media (max-width: 768px) {
display: flex;
flex-direction: column;
align-items: stretch;
}
}
.container,
.nav-menu {
display: grid;
grid-template-columns: 1fr auto 1fr;
justify-content: space-between;
align-items: center;
}
.mobile-nav-header {
anchor-name: --mobile-nav-header;
display: none;
justify-content: space-between;
align-items: center;
@media (max-width: 768px) {
display: flex;
}
}
.nav-header-logo {
color: var(--color-text);
display: inline-block;
max-width: 250px;
padding-left: var(--space-md);
&.mobile {
display: none;
}
@media (max-width: 768px) {
&.desktop {
display: none;
}
&.mobile {
display: inline-block;
}
}
}
/* Hamburger toggle: mobile only, animates to X when open */
.nav-toggle-input {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
&:checked {
& ~ .mobile-nav-header .nav-toggle .nav-toggle-line {
&:nth-child(1) {
transform: translateY(8px) rotate(45deg);
}
&:nth-child(2) {
opacity: 0;
}
&:nth-child(3) {
transform: translateY(-8px) rotate(-45deg);
}
}
& ~ .nav-menu {
max-height: 80vh;
opacity: 1;
padding-top: var(--space-md);
padding-bottom: var(--space-md);
border-top: 1px solid var(--color-border);
}
}
}
.nav-toggle {
display: none;
align-items: center;
justify-content: flex-end;
flex: 0 0 auto;
padding: var(--space-sm) var(--space-md);
width: calc(24px + var(--space-md) + var(--space-md));
height: calc(31px + var(--space-sm) + var(--space-sm));
cursor: pointer;
color: var(--color-text);
background: transparent;
border: none;
border-radius: var(--space-xs);
transition:
color 0.2s ease,
background-color 0.2s ease;
@media (max-width: 768px) {
display: flex;
}
&:hover {
color: var(--color-primary);
}
&:focus-visible {
outline: 4px solid var(--color-focus);
outline-offset: 4px;
}
}
.nav-toggle-inner {
display: flex;
flex-direction: column;
justify-content: space-between;
width: 24px;
height: 18px;
}
.nav-toggle-line {
display: block;
width: 100%;
height: 2px;
background-color: currentColor;
border-radius: 1px;
transform-origin: center;
transition:
transform 0.25s ease,
opacity 0.2s ease;
@media (prefers-reduced-motion: reduce) {
transition-duration: 0.01ms;
}
}
.nav-list {
display: flex;
justify-content: center;
align-items: center;
list-style: none;
padding: 0;
margin: 0;
}
.nav-item {
margin: 0 var(--space-md);
}
/* Back to top + mobile nav logo: hidden until page is scrolled (CSS scroll-driven animation) */
.nav-back-to-top,
.nav-header-logo {
/* Fallback when scroll-driven animations arent supported: always visible */
opacity: 1;
visibility: visible;
}
/* Mobile: show toggle, collapse menu until opened; menu overlays content via anchor */
@media (max-width: 768px) {
.nav-menu {
display: flex;
flex-direction: column;
align-items: stretch;
max-height: 0;
overflow: hidden;
opacity: 0;
padding-top: 0;
padding-bottom: 0;
border-top: none;
transition:
max-height 0.3s ease,
opacity 0.25s ease,
padding 0.25s ease;
& .nav-list,
& .nav-item {
width: 100%;
}
& .nav-list {
flex-direction: column;
gap: 0;
}
& .nav-item {
margin: 0;
text-align: center;
& a:hover {
border-bottom-color: transparent;
}
}
& .nav-item a,
& .nav-back-to-top a {
display: block;
padding: var(--space-md);
}
@supports (top: anchor(bottom)) {
position: fixed;
position-anchor: --mobile-nav-header;
top: anchor(--mobile-nav-header bottom);
left: anchor(--mobile-nav-header left);
right: anchor(--mobile-nav-header right);
margin: 0;
overflow-y: auto;
background-color: var(--color-bg);
border-bottom: 1px solid var(--color-border);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
z-index: 99;
}
@media (prefers-reduced-motion: reduce) {
transition-duration: 0.01ms;
}
}
}
@supports (animation-timeline: scroll()) {
.nav-back-to-top,
.nav-header-logo {
opacity: 0;
visibility: hidden;
pointer-events: none;
animation: nav-reveal-on-scroll linear;
animation-timeline: scroll(root block);
animation-range: 300px 400px;
animation-fill-mode: both;
}
@keyframes nav-reveal-on-scroll {
from {
opacity: 0;
visibility: hidden;
pointer-events: none;
}
to {
opacity: 1;
visibility: visible;
pointer-events: auto;
}
}
@media (prefers-reduced-motion: reduce) {
.nav-back-to-top,
.nav-header-logo {
animation: none;
opacity: 1;
visibility: visible;
pointer-events: auto;
}
}
}
</style>

View File

@@ -2,7 +2,7 @@ import { describe, it, expect } from 'vitest';
import { getCurrentYear } from './copyright-year'; import { getCurrentYear } from './copyright-year';
describe('getCurrentYear', () => { describe('getCurrentYear', () => {
it('returns the current calendar year', () => { it('returns the current calendar year', () => {
expect(getCurrentYear()).toBe(new Date().getFullYear()); expect(getCurrentYear()).toBe(new Date().getFullYear());
}); });
}); });

View File

@@ -3,5 +3,5 @@
* The client-side footer year is updated by static/copyright-year.js. * The client-side footer year is updated by static/copyright-year.js.
*/ */
export function getCurrentYear(): number { export function getCurrentYear(): number {
return new Date().getFullYear(); return new Date().getFullYear();
} }

View File

@@ -1,26 +1,26 @@
export const whatWeDoItems = [ export const whatWeDoItems = [
'Product-focused frontend and UI architecture for modern web applications, with an emphasis on clarity, scalability, and long-term maintainability.', 'Product-focused frontend and UI architecture for modern web applications, with an emphasis on clarity, scalability, and long-term maintainability.',
'Greenfield product builds and early-stage foundations, getting new projects off the ground quickly with structures designed to grow, not be rewritten.', 'Greenfield product builds and early-stage foundations, getting new projects off the ground quickly with structures designed to grow, not be rewritten.',
'Performance, Core Web Vitals, rendering strategy, and technical SEO optimization focused on real-world user journeys—not just lab scores.', 'Performance, Core Web Vitals, rendering strategy, and technical SEO optimization focused on real-world user journeys—not just lab scores.',
'Accessibility-first engineering, ensuring WCAG-compliant interfaces with semantic markup, keyboard parity, and inclusive interaction patterns.', 'Accessibility-first engineering, ensuring WCAG-compliant interfaces with semantic markup, keyboard parity, and inclusive interaction patterns.',
'Modernization and stabilization of existing systems, including refactors, framework upgrades, and untangling overgrown frontend codebases.', 'Modernization and stabilization of existing systems, including refactors, framework upgrades, and untangling overgrown frontend codebases.',
'End-to-end feature delivery with clear ownership and documentation, spanning frontend and supporting backend work without unnecessary complexity.', 'End-to-end feature delivery with clear ownership and documentation, spanning frontend and supporting backend work without unnecessary complexity.',
]; ];
export const impactItems = [ export const impactItems = [
'Get new products off the ground quickly by establishing durable frontend and platform foundations—clean architecture, clear patterns, and pragmatic defaults designed to scale with teams and traffic.', 'Get new products off the ground quickly by establishing durable frontend and platform foundations—clean architecture, clear patterns, and pragmatic defaults designed to scale with teams and traffic.',
'Improve performance, Core Web Vitals, and technical SEO on high-traffic user journeys through rendering strategy, bundle discipline, and careful attention to real-world loading behavior.', 'Improve performance, Core Web Vitals, and technical SEO on high-traffic user journeys through rendering strategy, bundle discipline, and careful attention to real-world loading behavior.',
'Build accessibility into core UI systems, not as a retrofit—semantic markup, keyboard parity, and screen reader support baked into reusable components and design patterns.', 'Build accessibility into core UI systems, not as a retrofit—semantic markup, keyboard parity, and screen reader support baked into reusable components and design patterns.',
'Bring order to complex or aging codebases by simplifying structure, reducing duplication, and clarifying ownership, enabling teams to ship confidently without over-engineering.', 'Bring order to complex or aging codebases by simplifying structure, reducing duplication, and clarifying ownership, enabling teams to ship confidently without over-engineering.',
'Design and evolve shared component libraries and UI systems that improve consistency, velocity, and long-term maintainability across multiple teams.', 'Design and evolve shared component libraries and UI systems that improve consistency, velocity, and long-term maintainability across multiple teams.',
'Partner closely with product, design, and engineering leadership (including marketing teams and non-technical organizations) to translate goals into shippable systems, balancing speed, quality, and technical risk.', 'Partner closely with product, design, and engineering leadership (including marketing teams and non-technical organizations) to translate goals into shippable systems, balancing speed, quality, and technical risk.',
]; ];
export const howWeWorkItems = [ export const howWeWorkItems = [
'Engagements are consulting-led and senior-driven. I work directly with founders, product leaders, marketing teams, and engineering teams—including organizations without in-house technical staff—to establish direction and deliver solutions with a high degree of autonomy.', 'Engagements are consulting-led and senior-driven. I work directly with founders, product leaders, marketing teams, and engineering teams—including organizations without in-house technical staff—to establish direction and deliver solutions with a high degree of autonomy.',
'Focused, pragmatic scope. Work is scoped to deliver real progress quickly, with an emphasis on building the right foundation rather than over-engineering for hypothetical futures.', 'Focused, pragmatic scope. Work is scoped to deliver real progress quickly, with an emphasis on building the right foundation rather than over-engineering for hypothetical futures.',
'Async-friendly, low-friction communication. Clear written updates, documented decisions, and scheduled calls when they add value—not meetings for their own sake.', 'Async-friendly, low-friction communication. Clear written updates, documented decisions, and scheduled calls when they add value—not meetings for their own sake.',
'Quality as a default. Accessibility, performance, and maintainability are built into the work from the start, not added later as cleanup.', 'Quality as a default. Accessibility, performance, and maintainability are built into the work from the start, not added later as cleanup.',
"Flexible engagement models. Hourly or fixed-scope work depending on clarity and needs; longer-term engagements welcome when there's ongoing product momentum.", "Flexible engagement models. Hourly or fixed-scope work depending on clarity and needs; longer-term engagements welcome when there's ongoing product momentum.",
'Clean handoff. Code, documentation, and context are left in a state where internal teams—or future vendors—can confidently extend the work without dependency.', 'Clean handoff. Code, documentation, and context are left in a state where internal teams—or future vendors—can confidently extend the work without dependency.',
]; ];

View File

@@ -1,27 +1,27 @@
export const engagements = [ export const engagements = [
{ {
title: 'Atlassian — Senior UI Engineer (Enterprise SaaS)', title: 'Atlassian — Senior UI Engineer (Enterprise SaaS)',
description: description:
'Frontend architecture and feature delivery for Confluence integrations, including React 18 migration work and standardizing end-to-end testing practices.', 'Frontend architecture and feature delivery for Confluence integrations, including React 18 migration work and standardizing end-to-end testing practices.',
}, },
{ {
title: 'CarGurus — Principal UI Engineer (Consumer Marketplace)', title: 'CarGurus — Principal UI Engineer (Consumer Marketplace)',
description: description:
'Built and maintained high-traffic frontend systems, improved Core Web Vitals and technical SEO, and developed shared UI platforms used across teams.', 'Built and maintained high-traffic frontend systems, improved Core Web Vitals and technical SEO, and developed shared UI platforms used across teams.',
}, },
{ {
title: 'The TJX Companies (TJ Maxx) — UI Engineer (Enterprise Retail)', title: 'The TJX Companies (TJ Maxx) — UI Engineer (Enterprise Retail)',
description: description:
'Delivered UX improvements for large-scale e-commerce experiences in close partnership with design, QA, and product teams.', 'Delivered UX improvements for large-scale e-commerce experiences in close partnership with design, QA, and product teams.',
}, },
{ {
title: 'Timberland — Senior Interactive Developer (Global Ecommerce)', title: 'Timberland — Senior Interactive Developer (Global Ecommerce)',
description: description:
'Led global web initiatives across brand and e-commerce platforms, acting as a technical bridge between marketing, design, and engineering.', 'Led global web initiatives across brand and e-commerce platforms, acting as a technical bridge between marketing, design, and engineering.',
}, },
{ {
title: 'MFA Boston — Pro Bono Technical Lead (Nonprofit / Fundraising)', title: 'MFA Boston — Pro Bono Technical Lead (Nonprofit / Fundraising)',
description: description:
"Designed and built a custom auction application for the MFA's annual Young Patrons fundraiser; subsequently iterated on and supported the platform over multiple years as the event grew, until it concluded during the pandemic.", "Designed and built a custom auction application for the MFA's annual Young Patrons fundraiser; subsequently iterated on and supported the platform over multiple years as the event grew, until it concluded during the pandemic.",
}, },
]; ];

View File

@@ -1,26 +1,26 @@
export const experienceLogos = [ export const experienceLogos = [
{ src: '/assets/logos/atlassian.svg', alt: 'Atlassian', width: 2500, height: 2500 }, { src: '/assets/logos/atlassian.svg', alt: 'Atlassian', width: 2500, height: 2500 },
{ {
src: '/assets/logos/tjx.svg', src: '/assets/logos/tjx.svg',
alt: 'TJ Maxx (The TJX Companies)', alt: 'TJ Maxx (The TJX Companies)',
width: 2500, width: 2500,
height: 621, height: 621,
}, },
{ src: '/assets/logos/cargurus.svg', alt: 'CarGurus', width: 2500, height: 398 }, { src: '/assets/logos/cargurus.svg', alt: 'CarGurus', width: 2500, height: 398 },
{ src: '/assets/logos/timberland.svg', alt: 'Timberland', width: 190, height: 35 }, { src: '/assets/logos/timberland.svg', alt: 'Timberland', width: 190, height: 35 },
{ src: '/assets/logos/vf.svg', alt: 'VF Corporation', width: 190, height: 155 }, { src: '/assets/logos/vf.svg', alt: 'VF Corporation', width: 190, height: 155 },
{ {
src: '/assets/logos/bottomline.svg', src: '/assets/logos/bottomline.svg',
alt: 'Bottomline Technologies', alt: 'Bottomline Technologies',
width: 2702, width: 2702,
height: 571, height: 571,
}, },
{ {
src: '/assets/logos/mfa-boston.svg', src: '/assets/logos/mfa-boston.svg',
alt: 'Museum of Fine Arts Boston', alt: 'Museum of Fine Arts Boston',
width: 572, width: 572,
height: 88, height: 88,
}, },
] as const; ] as const;
export const experienceTextList = experienceLogos.map((l) => l.alt); export const experienceTextList = experienceLogos.map((l) => l.alt);

View File

@@ -2,8 +2,8 @@ import type { PageMeta } from '$lib/seo';
import { defaultJsonLdGraph } from './json-ld'; import { defaultJsonLdGraph } from './json-ld';
export const homeMeta: PageMeta = { export const homeMeta: PageMeta = {
title: 'mifi Ventures — Software Engineering Consulting | Boston, MA', title: 'mifi Ventures — Software Engineering Consulting | Boston, MA',
description: description:
'Boston-based software engineering consulting. Mike Fitzpatrick helps teams build reliable, accessible, high-performance web applications. Specializing in frontend architecture, performance optimization, and modern web development.', 'Boston-based software engineering consulting. Mike Fitzpatrick helps teams build reliable, accessible, high-performance web applications. Specializing in frontend architecture, performance optimization, and modern web development.',
jsonLd: defaultJsonLdGraph, jsonLd: defaultJsonLdGraph,
}; };

View File

@@ -6,145 +6,145 @@
const BASE = 'https://mifi.ventures'; const BASE = 'https://mifi.ventures';
export const defaultJsonLdGraph: Record<string, unknown>[] = [ export const defaultJsonLdGraph: Record<string, unknown>[] = [
{ {
'@type': 'Organization', '@type': 'Organization',
'@id': `${BASE}/#organization`, '@id': `${BASE}/#organization`,
name: 'mifi Ventures, LLC', name: 'mifi Ventures, LLC',
legalName: 'mifi Ventures, LLC', legalName: 'mifi Ventures, LLC',
url: `${BASE}/`, url: `${BASE}/`,
logo: { '@type': 'ImageObject', url: `${BASE}/favicon.svg` }, logo: { '@type': 'ImageObject', url: `${BASE}/favicon.svg` },
description: description:
'Software engineering consulting specializing in product-focused frontend architecture, performance optimization, and accessibility-first engineering.', 'Software engineering consulting specializing in product-focused frontend architecture, performance optimization, and accessibility-first engineering.',
founder: { '@id': `${BASE}/#principal` }, founder: { '@id': `${BASE}/#principal` },
address: { address: {
'@type': 'PostalAddress', '@type': 'PostalAddress',
addressLocality: 'Boston', addressLocality: 'Boston',
addressRegion: 'MA', addressRegion: 'MA',
addressCountry: 'US', addressCountry: 'US',
},
geo: { '@type': 'GeoCoordinates', latitude: 42.360082, longitude: -71.05888 },
areaServed: { '@type': 'Country', name: 'United States' },
hasOfferCatalog: { '@id': `${BASE}/#services` },
sameAs: ['https://www.linkedin.com/in/the-mifi', 'https://github.com/the-mifi'],
}, },
geo: { '@type': 'GeoCoordinates', latitude: 42.360082, longitude: -71.05888 }, {
areaServed: { '@type': 'Country', name: 'United States' }, '@type': 'Person',
hasOfferCatalog: { '@id': `${BASE}/#services` }, '@id': `${BASE}/#principal`,
sameAs: ['https://www.linkedin.com/in/the-mifi', 'https://github.com/the-mifi'], name: 'Mike Fitzpatrick',
}, jobTitle: 'Principal Software Engineer and Architect',
{ description:
'@type': 'Person', 'Senior full-stack engineer and architect helping teams ship reliable, accessible, high-performance web products.',
'@id': `${BASE}/#principal`, url: `${BASE}/`,
name: 'Mike Fitzpatrick', worksFor: { '@id': `${BASE}/#organization` },
jobTitle: 'Principal Software Engineer and Architect', knowsAbout: [
description: 'Frontend Architecture',
'Senior full-stack engineer and architect helping teams ship reliable, accessible, high-performance web products.', 'UI Architecture',
url: `${BASE}/`, 'React Development',
worksFor: { '@id': `${BASE}/#organization` }, 'Web Performance Optimization',
knowsAbout: [ 'Core Web Vitals',
'Frontend Architecture', 'Technical SEO',
'UI Architecture', 'Web Accessibility (WCAG)',
'React Development', 'Component Libraries',
'Web Performance Optimization', 'Design Systems',
'Core Web Vitals', 'JavaScript',
'Technical SEO', 'TypeScript',
'Web Accessibility (WCAG)', 'Modern Web Development',
'Component Libraries', 'Greenfield Product Development',
'Design Systems', 'Legacy System Modernization',
'JavaScript', 'Code Refactoring',
'TypeScript', ],
'Modern Web Development', sameAs: ['https://www.linkedin.com/in/the-mifi', 'https://github.com/the-mifi'],
'Greenfield Product Development',
'Legacy System Modernization',
'Code Refactoring',
],
sameAs: ['https://www.linkedin.com/in/the-mifi', 'https://github.com/the-mifi'],
},
{
'@type': 'WebSite',
'@id': `${BASE}/#website`,
url: `${BASE}/`,
name: 'mifi Ventures',
description: 'Software Engineering Consulting — Boston, MA',
publisher: { '@id': `${BASE}/#organization` },
potentialAction: {
'@type': 'ReserveAction',
target: {
'@type': 'EntryPoint',
urlTemplate: 'https://cal.mifi.ventures/the-mifi',
},
name: 'Schedule a 30-minute intro call',
}, },
}, {
{ '@type': 'WebSite',
'@type': 'WebPage', '@id': `${BASE}/#website`,
'@id': `${BASE}/#webpage`, url: `${BASE}/`,
url: `${BASE}/`, name: 'mifi Ventures',
name: 'mifi Ventures — Software Engineering Consulting | Boston, MA', description: 'Software Engineering Consulting Boston, MA',
description: publisher: { '@id': `${BASE}/#organization` },
'Boston-based software engineering consulting. Mike Fitzpatrick helps teams build reliable, accessible, high-performance web applications.', potentialAction: {
isPartOf: { '@id': `${BASE}/#website` }, '@type': 'ReserveAction',
about: { '@id': `${BASE}/#organization` }, target: {
mainEntity: { '@id': `${BASE}/#organization` }, '@type': 'EntryPoint',
primaryImageOfPage: { '@type': 'ImageObject', url: `${BASE}/favicon.svg` }, urlTemplate: 'https://cal.mifi.ventures/the-mifi',
inLanguage: 'en-US', },
}, name: 'Schedule a 30-minute intro call',
{
'@type': 'OfferCatalog',
'@id': `${BASE}/#services`,
name: 'Software Engineering Consulting Services',
description: 'Consulting services offered by mifi Ventures',
numberOfItems: 6,
itemListElement: [
{
'@type': 'Offer',
itemOffered: {
'@type': 'Service',
name: 'Frontend and UI Architecture',
description:
'Product-focused frontend and UI architecture for modern web applications, with an emphasis on clarity, scalability, and long-term maintainability.',
}, },
}, },
{ {
'@type': 'Offer', '@type': 'WebPage',
itemOffered: { '@id': `${BASE}/#webpage`,
'@type': 'Service', url: `${BASE}/`,
name: 'Greenfield Product Development', name: 'mifi Ventures — Software Engineering Consulting | Boston, MA',
description: description:
'Greenfield product builds and early-stage foundations, getting new projects off the ground quickly with structures designed to grow, not be rewritten.', 'Boston-based software engineering consulting. Mike Fitzpatrick helps teams build reliable, accessible, high-performance web applications.',
}, isPartOf: { '@id': `${BASE}/#website` },
}, about: { '@id': `${BASE}/#organization` },
{ mainEntity: { '@id': `${BASE}/#organization` },
'@type': 'Offer', primaryImageOfPage: { '@type': 'ImageObject', url: `${BASE}/favicon.svg` },
itemOffered: { inLanguage: 'en-US',
'@type': 'Service', },
name: 'Performance Optimization', {
description: '@type': 'OfferCatalog',
'Performance, Core Web Vitals, rendering strategy, and technical SEO optimization focused on real-world user journeys.', '@id': `${BASE}/#services`,
}, name: 'Software Engineering Consulting Services',
}, description: 'Consulting services offered by mifi Ventures',
{ numberOfItems: 6,
'@type': 'Offer', itemListElement: [
itemOffered: { {
'@type': 'Service', '@type': 'Offer',
name: 'Accessibility Engineering', itemOffered: {
description: '@type': 'Service',
'Accessibility-first engineering, ensuring WCAG-compliant interfaces with semantic markup, keyboard parity, and inclusive interaction patterns.', name: 'Frontend and UI Architecture',
}, description:
}, 'Product-focused frontend and UI architecture for modern web applications, with an emphasis on clarity, scalability, and long-term maintainability.',
{ },
'@type': 'Offer', },
itemOffered: { {
'@type': 'Service', '@type': 'Offer',
name: 'System Modernization', itemOffered: {
description: '@type': 'Service',
'Modernization and stabilization of existing systems, including refactors, framework upgrades, and untangling overgrown frontend codebases.', name: 'Greenfield Product Development',
}, description:
}, 'Greenfield product builds and early-stage foundations, getting new projects off the ground quickly with structures designed to grow, not be rewritten.',
{ },
'@type': 'Offer', },
itemOffered: { {
'@type': 'Service', '@type': 'Offer',
name: 'End-to-End Feature Delivery', itemOffered: {
description: '@type': 'Service',
'End-to-end feature delivery with clear ownership and documentation, spanning frontend and supporting backend work without unnecessary complexity.', name: 'Performance Optimization',
}, description:
}, 'Performance, Core Web Vitals, rendering strategy, and technical SEO optimization focused on real-world user journeys.',
], },
}, },
{
'@type': 'Offer',
itemOffered: {
'@type': 'Service',
name: 'Accessibility Engineering',
description:
'Accessibility-first engineering, ensuring WCAG-compliant interfaces with semantic markup, keyboard parity, and inclusive interaction patterns.',
},
},
{
'@type': 'Offer',
itemOffered: {
'@type': 'Service',
name: 'System Modernization',
description:
'Modernization and stabilization of existing systems, including refactors, framework upgrades, and untangling overgrown frontend codebases.',
},
},
{
'@type': 'Offer',
itemOffered: {
'@type': 'Service',
name: 'End-to-End Feature Delivery',
description:
'End-to-end feature delivery with clear ownership and documentation, spanning frontend and supporting backend work without unnecessary complexity.',
},
},
],
},
]; ];

View File

@@ -4,50 +4,50 @@
*/ */
export const SEO_DEFAULTS = { export const SEO_DEFAULTS = {
siteName: 'mifi Ventures', siteName: 'mifi Ventures',
baseUrl: 'https://mifi.ventures', baseUrl: 'https://mifi.ventures',
defaultOgImage: '/assets/og-image.png', defaultOgImage: '/assets/og-image.png',
ogImageWidth: 1200, ogImageWidth: 1200,
ogImageHeight: 630, ogImageHeight: 630,
locale: 'en_US', locale: 'en_US',
twitterCard: 'summary_large_image' as const, twitterCard: 'summary_large_image' as const,
themeColorLight: '#0052cc', themeColorLight: '#0052cc',
themeColorDark: '#4da6ff', themeColorDark: '#4da6ff',
} as const; } as const;
export interface PageMeta { export interface PageMeta {
title: string; title: string;
description?: string; description?: string;
canonical?: string; canonical?: string;
ogImage?: string; ogImage?: string;
ogType?: string; ogType?: string;
twitterTitle?: string; twitterTitle?: string;
twitterDescription?: string; twitterDescription?: string;
/** JSON-LD graph nodes (merged with defaults in layout) */ /** JSON-LD graph nodes (merged with defaults in layout) */
jsonLd?: Record<string, unknown>[]; jsonLd?: Record<string, unknown>[];
} }
export interface MergedMeta extends PageMeta { export interface MergedMeta extends PageMeta {
canonical: string; canonical: string;
ogImage: string; ogImage: string;
ogImageAlt: string; ogImageAlt: string;
jsonLdGraph: Record<string, unknown>[]; jsonLdGraph: Record<string, unknown>[];
} }
/** Merge page meta with site defaults for rendering. */ /** Merge page meta with site defaults for rendering. */
export function mergeMeta(meta: PageMeta, path: string = '/'): MergedMeta { export function mergeMeta(meta: PageMeta, path: string = '/'): MergedMeta {
const baseUrl = SEO_DEFAULTS.baseUrl; const baseUrl = SEO_DEFAULTS.baseUrl;
const canonical = meta.canonical ?? `${baseUrl}${path === '/' ? '' : path}`; const canonical = meta.canonical ?? `${baseUrl}${path === '/' ? '' : path}`;
const ogImage = meta.ogImage?.startsWith('http') const ogImage = meta.ogImage?.startsWith('http')
? meta.ogImage ? meta.ogImage
: `${baseUrl}${meta.ogImage?.startsWith('/') ? meta.ogImage : SEO_DEFAULTS.defaultOgImage}`; : `${baseUrl}${meta.ogImage?.startsWith('/') ? meta.ogImage : SEO_DEFAULTS.defaultOgImage}`;
const ogImageAlt = meta.title; const ogImageAlt = meta.title;
const jsonLdGraph = meta.jsonLd ?? []; const jsonLdGraph = meta.jsonLd ?? [];
return { return {
...meta, ...meta,
canonical, canonical,
ogImage, ogImage,
ogImageAlt, ogImageAlt,
jsonLdGraph, jsonLdGraph,
}; };
} }

View File

@@ -1,4 +1,5 @@
<script lang="ts"> <script lang="ts">
import Navigation from '$lib/components/Navigation.svelte';
import Hero from '$lib/components/Hero.svelte'; import Hero from '$lib/components/Hero.svelte';
import ExperienceSection from '$lib/components/ExperienceSection.svelte'; import ExperienceSection from '$lib/components/ExperienceSection.svelte';
import WhatWeDo from '$lib/components/WhatWeDo.svelte'; import WhatWeDo from '$lib/components/WhatWeDo.svelte';
@@ -9,6 +10,7 @@
import Footer from '$lib/components/Footer.svelte'; import Footer from '$lib/components/Footer.svelte';
</script> </script>
<Navigation />
<Hero /> <Hero />
<main id="main"> <main id="main">
<ExperienceSection /> <ExperienceSection />

View File

@@ -2,5 +2,5 @@ import type { PageLoad } from './$types';
import { homeMeta } from '$lib/data/home-meta'; import { homeMeta } from '$lib/data/home-meta';
export const load: PageLoad = () => { export const load: PageLoad = () => {
return { meta: homeMeta }; return { meta: homeMeta };
}; };

Binary file not shown.

Before

Width:  |  Height:  |  Size: 587 KiB

After

Width:  |  Height:  |  Size: 591 KiB