Back to Blog
Software Engineering Astro Web Performance SEO

Building labitcode: A Modern Engineering Lab

A deep dive into the architecture of labitcode.com using Astro 5, Tailwind CSS v4, and a zero-React runtime policy. Learn how we built dynamic search, theme synchronization without FOUC, and optimized core performance metrics for a perfect Lighthouse score.

AG
Alfonso Garcia
· · 5 min read
Building labitcode: A Modern Engineering Lab

Every software engineer needs a lab — a sandbox to build, break, document, and share engineering discoveries. labitcode is exactly that: a portfolio and technical blog designed as a converging point for human software engineering and AI-powered innovation.

When building the platform, we wanted to avoid the bloat of modern single-page application (SPA) frameworks. Why load megabytes of JavaScript, manage heavy hydration processes, and sacrifice SEO indexing for a content-driven developer site?

In this post, we’ll deep-dive into the architectural decisions that power labitcode, exploring the static content layer, zero-React runtime components, and our SEO-first optimization pipeline.


1. Type-Safe Content Layer with Astro 5

Astro 5 introduces a revised Content Layer API that decouples content files from routing and loads them into a fast, local database at build time. This allows us to query markdown (.md) and MDX (.mdx) files as if they were rows in a database.

To ensure consistency, we use Zod schemas to validate content fields statically. If an author forgets to add a description or types an incorrect publish date, the build fails immediately.

Here is our content schema configuration:

// src/content.config.ts
import { defineCollection, z } from "astro:content";
import { glob } from "astro/loaders";

const blog = defineCollection({
  loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/content/blog" }),
  schema: z.object({
    title: z.string(),
    description: z.string(),
    pubDate: z.coerce.date(),
    updatedDate: z.coerce.date().optional(),
    heroImage: z.string().optional(),
    tags: z.array(z.string()),
    author: z.enum(["Alfonso Garcia", "AI"]),
    draft: z.boolean().default(false),
  }),
});

const projects = defineCollection({
  loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/content/projects" }),
  schema: z.object({
    title: z.string(),
    description: z.string(),
    stack: z.array(z.string()),
    url: z.string().optional(),
    repo: z.string().optional(),
    heroImage: z.string().optional(),
    lastUpdated: z.coerce.date(),
    creator: z.string(),
    status: z.enum(["active", "archived", "in-progress"]),
  }),
});

export const collections = { blog, projects };

Using this loader schema, we can safely write pages like this:

---
// src/pages/blog/index.astro
import { getCollection } from "astro:content";
import BlogCard from "@/components/BlogCard.astro";

const posts = (await getCollection("blog"))
  .filter((post) => !post.data.draft)
  .sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
---

<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
  {posts.map((post) => <BlogCard {...post.data} slug={post.id} />)}
</div>

2. The “Zero-React” Runtime Strategy

One of our primary design goals was to load zero framework JavaScript at runtime. We wanted to see what was possible with pure Vanilla JS and Web APIs.

By avoiding React, Vue, or Svelte, we avoid:

  • Hydration blocking: The main thread remains free to paint the page instantly.
  • Bundle overhead: We don’t download a framework library just to handle basic interactivity.

Here are the details of how we built two interactive features using this philosophy.

To support instant searching without external databases or heavyweight client-side libraries (like Fuse.js), we implemented a custom fuzzy search.

The search operates in three steps:

  1. Build-Time Indexing: An Astro route compiles all blog and project meta-information into a single static JSON index /search-index.json.
  2. Lazy Loading: The search script does not download the JSON index on initial page load. Instead, it waits until the user clicks the search button or presses Cmd+K.
  3. Fuzzy Search Algorithm: A custom algorithm checks if characters of the query appear in order within titles or descriptions.

Here is the fuzzy search logic:

// src/components/Search.astro (script excerpt)
function fuzzyMatch(text: string, query: string): boolean {
  const lowerText = text.toLowerCase();
  const lowerQuery = query.toLowerCase();

  let textIdx = 0;
  for (let qIdx = 0; qIdx < lowerQuery.length; qIdx++) {
    const char = lowerQuery[qIdx];
    const found = lowerText.indexOf(char, textIdx);
    if (found === -1) return false;
    textIdx = found + 1; // Move search pointer forward
  }
  return true;
}

This fuzzy match is extremely light and matches queries like “speed” to “Pagespeed” or “Performance” without importing a heavy dependency.

B. Anti-FOUC Theme Synchronizer

Implementing dark mode toggle in static sites often causes a FOUC (Flash of Unstyled Content): the browser renders the default light theme first, parses JavaScript late, and then snaps to dark theme, creating a jarring white flash.

To prevent this, we placed a tiny script in the <head> of our layout. Because it is placed before <body> starts rendering, it blocks layout painting just long enough to apply the correct theme class, resulting in a seamless load:

<!-- src/layouts/BaseLayout.astro -->
<script is:inline>
  (function () {
    const stored = localStorage.getItem("theme");
    const preferred = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
    document.documentElement.setAttribute("data-theme", stored || preferred);
  })();

  // Re-run theme check when Astro View Transitions swaps pages
  document.addEventListener("astro:after-swap", function () {
    const stored = localStorage.getItem("theme");
    const preferred = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
    document.documentElement.setAttribute("data-theme", stored || preferred);
  });
</script>

3. CSS-First Styling with Tailwind CSS v4

We chose Tailwind CSS v4 due to its CSS-first configuration model. Instead of relying on a complex JavaScript tailwind.config.js file, Tailwind v4 is integrated directly through Vite using @tailwindcss/vite and configured directly inside the main CSS file using CSS variables:

/* src/styles/global.css */
@import "tailwindcss";

@theme {
  --color-accent: #dc2626;
  --color-accent-light: #ef4444;
  --color-accent-dark: #b91c1c;
  --color-navy: #0a1628;
  --color-navy-light: #1e293b;
  --font-sans: "Inter", ui-sans-serif, system-ui;
}

This compiles color variables into standard CSS properties, meaning the styles are resolved natively by the browser quickly.


4. The 100/100 Lighthouse Optimization Checklist

To ensure extreme performance, we designed a checklist of best practices which we applied systematically across the site:

A. Critical Stylesheet Inlining

Normally, stylesheet links block rendering. In Astro, we inline all page-specific CSS directly into the HTML to eliminate the network request round-trip and satisfy the “Eliminate render-blocking resources” audit:

// astro.config.mjs
export default defineConfig({
  build: {
    inlineStylesheets: "always", // Inlines all CSS in the output HTML
  },
});

B. Responsive Retina Image Assets

Instead of serving raw large images, we convert all images to WebP, size them to exactly double the CSS display width (providing 2x Retina sharpness), and declare explicit width and height dimensions to reserve layout space and prevent Cumulative Layout Shift (CLS):

<img
  src="/images/project.webp"
  alt="Project Screenshot"
  width="640"
  height="360"
  loading="lazy"
  decoding="async"
/>

C. Contrast Ratio Adjustments

To guarantee accessibility (A11y), all text must have a minimum contrast ratio of 4.5:1 against its background. We systematically tested our links and footers, modifying our light mode links from #DC2626 (which had a borderline 4.46:1 contrast ratio on #FDF4F4) to #B91C1C, achieving an accessible contrast ratio of 5.64:1.


Summary of Results

Our real-world production test results reflect the value of these decisions:

Audit CategoryLocal ScoreProduction Score (Mobile)
Performance100/10093/100
Accessibility100/100100/100
Best Practices100/100100/100
SEO100/100100/100

Note: The slight difference between local performance (100) and production performance (93) is due to network latency, the location of CDN nodes, and loading the Vercel Speed Insights telemetry script.

Building labitcode has proven that static-first rendering, native CSS compilation, and minimal JavaScript are the keys to a fast, clean, and developer-friendly site.


Built with precision by Alfonso Garcia and the labitcode AI entity.

Join the conversation

Have thoughts on this post? Share them on social media or reach out directly.