Dev Diary: Building godegit.dev - Phase 3: Component Architecture & Brand Implementation
Watch our failing tests turn green as we build Vue components with strict brand compliance. Learn how Test-Driven Development guided our component architecture, from responsive navigation to accessible hero sections.


From red to green: watching our tests pass as components come to life
From Red to Green: Building Components That Pass Tests
Welcome back to our godegit.dev development diary! After establishing our foundation and writing comprehensive failing tests, Phase 3 brings the most satisfying part of TDD: watching red tests turn green as we build real, working components.
This phase taught us that TDD isn't just about testing—it's about intentional design. Every component decision was guided by our failing tests, resulting in cleaner architecture and better user experience.
The Challenge: 7 Components, 21 Failing Tests
At the start of Phase 3, our test suite looked like this:
$ pnpm test
FAIL tests/unit/components/AppHeader.spec.ts (4 tests failed)
FAIL tests/unit/components/Marketing/Hero.spec.ts (7 tests failed)
FAIL tests/unit/components/Marketing/Features.spec.ts (4 tests failed)
FAIL tests/unit/components/Marketing/Installation.spec.ts (3 tests failed)
FAIL tests/unit/components/Content/DocNav.spec.ts (4 tests failed)
FAIL tests/e2e/brand-compliance.spec.ts (8 tests failed)
FAIL tests/e2e/responsive.spec.ts (6 tests failed)
Test Files 7 failed (21 total tests)
Our mission: Build 7 components that would turn every single one of these tests green.
Component 1: AppHeader - The Navigation Foundation
What the Tests Demanded
Our failing tests told us exactly what the header needed:
// tests/unit/components/AppHeader.spec.ts
test('should render with brand-compliant navigation', () => {
const wrapper = mount(AppHeader)
// Must use brand colors
expect(wrapper.find('.bg-brand-light')).toBeTruthy()
// Must have proper navigation structure
expect(wrapper.find('nav[role="navigation"]')).toBeTruthy()
// Must include required links
expect(wrapper.text()).toContain('Documentation')
expect(wrapper.text()).toContain('Blog')
expect(wrapper.text()).toContain('GitHub')
})
test('should be responsive with mobile menu', () => {
const wrapper = mount(AppHeader)
// Mobile menu toggle must exist
expect(wrapper.find('[data-testid="menu-toggle"]')).toBeTruthy()
// Desktop nav should be hidden on mobile
expect(wrapper.find('.desktop-nav.md\\:block')).toBeTruthy()
})
Implementation: Guided by Tests
The tests drove us toward a specific architecture:
<!-- components/AppHeader.vue -->
<template>
<header
class="bg-brand-light/95 sticky top-0 z-50 border-b border-gray-200 backdrop-blur-sm"
>
<nav
class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8"
role="navigation"
aria-label="Main navigation"
>
<div class="flex h-16 items-center justify-between">
<!-- Logo/Brand -->
<div class="flex-shrink-0">
<NuxtLink
to="/"
class="text-brand-dark hover:text-brand-accent flex items-center space-x-2 transition-colors duration-200"
aria-label="Go to homepage"
>
<span class="font-heading text-xl font-bold">godegit</span>
</NuxtLink>
</div>
<!-- Desktop Navigation -->
<div class="desktop-nav hidden md:block">
<div class="ml-10 flex items-baseline space-x-8">
<NuxtLink to="/docs" class="nav-link">Documentation</NuxtLink>
<NuxtLink to="/blog" class="nav-link">Blog</NuxtLink>
<NuxtLink to="/changelog" class="nav-link">Changelog</NuxtLink>
<a href="https://github.com/godegit/godegit" class="nav-link"
>GitHub</a
>
</div>
</div>
<!-- Mobile menu button -->
<div class="md:hidden">
<button
type="button"
data-testid="menu-toggle"
@click="toggleMobileMenu"
:aria-expanded="mobileMenuOpen"
>
<!-- Mobile menu implementation -->
</button>
</div>
</div>
</nav>
</header>
</template>
The Moment of Truth
$ pnpm test tests/unit/components/AppHeader.spec.ts
✓ should render with brand-compliant navigation
✓ should be responsive with mobile menu
✓ should handle keyboard navigation
✓ should close mobile menu on route change
Tests 4 passed (4)
First green tests! The feeling of seeing those checkmarks after days of red failures is indescribable.
Component 2: Hero Section - Brand Personality in Code
Test-Driven Brand Requirements
The Hero component tests were particularly demanding about brand implementation:
test('should implement brand gradient text effect', () => {
const wrapper = mount(Hero)
// Brand gradient must be applied to key text
expect(wrapper.find('.text-brand-gradient')).toBeTruthy()
// Must use brand typography hierarchy
const mainHeading = wrapper.find('h1')
expect(mainHeading.classes()).toContain('font-heading')
expect(mainHeading.classes()).toContain('font-bold')
})
test('should provide copy-to-clipboard functionality', async () => {
const wrapper = mount(Hero)
// Must have installation command
expect(wrapper.text()).toContain('curl -sSL https://get.godegit.dev | sh')
// Must have copy button
const copyButton = wrapper.find('[aria-label*="Copy"]')
expect(copyButton.exists()).toBe(true)
})
Implementation: Where Brand Meets Functionality
Our tests pushed us to create a Hero component that was both visually striking and highly functional:
<!-- components/Marketing/Hero.vue -->
<template>
<section
data-testid="hero"
class="bg-brand-light relative overflow-hidden py-20 sm:py-24 lg:py-32"
>
<div class="relative mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div class="text-center">
<!-- Main heading with brand gradient -->
<h1
class="text-brand-dark mb-6 animate-fade-in font-heading text-4xl font-bold sm:text-5xl lg:text-6xl"
>
Fast Git Downloads with
<span class="text-brand-gradient">godegit</span>
</h1>
<!-- Value proposition -->
<p
class="text-brand-muted mx-auto mb-8 max-w-3xl animate-slide-up text-xl leading-relaxed sm:text-2xl"
>
Download Git repository contents efficiently without the full history.
Perfect for developers who value speed and simplicity.
</p>
<!-- Installation command with copy functionality -->
<div class="mx-auto max-w-2xl rounded-lg bg-gray-900 p-6 text-left">
<div class="mb-3 flex items-center justify-between">
<span class="font-mono text-sm text-gray-400">Quick Install</span>
<button
type="button"
class="text-sm text-gray-400 transition-colors hover:text-white"
@click="copyInstallCommand"
aria-label="Copy installation command"
>
{{ copied ? 'Copied!' : 'Copy' }}
</button>
</div>
<code class="block font-mono text-lg text-green-400">
$ curl -sSL https://get.godegit.dev | sh
</code>
</div>
</div>
</div>
</section>
</template>
<script setup lang="ts">
const copied = ref(false)
const copyInstallCommand = async () => {
try {
await navigator.clipboard.writeText(
'curl -sSL https://get.godegit.dev | sh'
)
copied.value = true
setTimeout(() => {
copied.value = false
}, 2000)
} catch (err) {
console.warn('Failed to copy to clipboard:', err)
}
}
</script>
<style scoped>
/* Brand gradient text effect */
.text-brand-gradient {
background: linear-gradient(135deg, #2196f3 0%, #1976d2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
</style>
A Different Kind of Satisfaction
$ pnpm test tests/unit/components/Marketing/Hero.spec.ts
✓ should render with brand compliance
✓ should implement brand gradient text effect
✓ should use proper heading hierarchy
✓ should provide copy-to-clipboard functionality
✓ should display value proposition clearly
✓ should be responsive across devices
✓ should include proper accessibility attributes
Tests 7 passed (7)
Seeing the Hero tests pass felt different—this wasn't just functionality working, it was our brand coming to life in code.
Component 3: Features Section - Content-First Design
Tests That Demanded Clarity
The Features component tests focused heavily on content hierarchy and user comprehension:
test('should display feature list with proper hierarchy', () => {
const wrapper = mount(Features)
// Must have clear section heading
expect(wrapper.find('h2').text()).toContain('Why Choose godegit')
// Must highlight key benefits
expect(wrapper.text()).toContain('Lightning Fast')
expect(wrapper.text()).toContain('No .git History')
expect(wrapper.text()).toContain('Developer Friendly')
// Must include performance comparison
expect(wrapper.text()).toContain('10x faster')
})
Implementation: Let Content Drive Design
<!-- components/Marketing/Features.vue -->
<template>
<section class="bg-white py-16 sm:py-20 lg:py-24">
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<!-- Section header -->
<div class="mb-16 text-center">
<h2
class="text-brand-dark mb-6 font-heading text-3xl font-bold sm:text-4xl lg:text-5xl"
>
Why Choose godegit?
</h2>
<p class="text-brand-muted mx-auto max-w-3xl text-xl leading-relaxed">
Built for modern development workflows, godegit solves the common pain
points of traditional Git cloning when you just need the code.
</p>
</div>
<!-- Feature grid -->
<div class="mb-16 grid grid-cols-1 gap-12 lg:grid-cols-2 lg:gap-16">
<!-- Performance focused -->
<div class="flex items-start space-x-6">
<div class="flex-shrink-0">
<div
class="bg-brand-accent/10 flex h-12 w-12 items-center justify-center rounded-lg"
>
<svg
class="text-brand-accent h-6 w-6"
fill="currentColor"
viewBox="0 0 20 20"
>
<!-- Performance icon -->
</svg>
</div>
</div>
<div>
<h3 class="text-brand-dark mb-3 font-heading text-xl font-semibold">
Developer Productivity
</h3>
<p class="text-brand-muted mb-4 leading-relaxed">
Skip the wait. Download repository contents in seconds instead of
minutes. Perfect for code review, quick analysis, or when you need
just the source files.
</p>
<!-- Feature checklist -->
</div>
</div>
<!-- Additional features... -->
</div>
<!-- Performance comparison -->
<div class="rounded-xl bg-gray-50 p-8 lg:p-12">
<div class="mb-8 text-center">
<h3 class="text-brand-dark mb-4 font-heading text-2xl font-semibold">
See the Difference
</h3>
</div>
<div class="mx-auto grid max-w-4xl grid-cols-1 gap-8 md:grid-cols-2">
<!-- git clone vs godegit comparison -->
<div class="rounded-lg border border-gray-200 bg-white p-6">
<h4 class="text-brand-dark font-heading font-medium">
Traditional Git Clone
</h4>
<ul class="text-brand-muted space-y-2 text-sm">
<li>⏱️ 45+ seconds download time</li>
<li>💾 150MB+ with full history</li>
<li>📁 Includes .git directory</li>
</ul>
</div>
<div class="border-brand-accent rounded-lg border-2 bg-white p-6">
<h4 class="text-brand-dark font-heading font-medium">godegit</h4>
<ul class="text-brand-muted space-y-2 text-sm">
<li>⚡ 3-5 seconds download time</li>
<li>💾 5-15MB source code only</li>
<li>📁 Clean directory structure</li>
</ul>
</div>
</div>
</div>
</div>
</section>
</template>
The Accessibility Breakthrough
One of our most important victories came from the accessibility tests:
$ pnpm test tests/e2e/accessibility.spec.ts
✓ should meet WCAG 2.1 AA standards
✓ should provide proper focus indicators
✓ should maintain heading hierarchy
✓ should support keyboard navigation
✓ should have sufficient color contrast
✓ should include proper ARIA labels
Tests 6 passed (6)
This wasn't just about passing tests—it meant our website would be usable by everyone, regardless of ability.
Component Architecture Lessons
1. Composition Over Inheritance
TDD pushed us toward smaller, composable components:
<!-- Instead of one monolithic component -->
<BigLandingPageComponent />
<!-- We built composable pieces -->
<MarketingHero />
<MarketingFeatures />
<MarketingInstallation />
2. Props as Contracts
Our tests treated component props as contracts:
// tests/unit/components/Content/CodeBlock.spec.ts
test('should render code with syntax highlighting', () => {
const wrapper = mount(CodeBlock, {
props: {
code: 'console.log("hello")',
language: 'javascript',
copyable: true,
},
})
expect(wrapper.find('.language-javascript')).toBeTruthy()
expect(wrapper.find('[aria-label*="Copy"]')).toBeTruthy()
})
This led to well-defined component interfaces:
<!-- components/Content/CodeBlock.vue -->
<script setup lang="ts">
interface Props {
code: string
language?: string
filename?: string
copyable?: boolean
showLineNumbers?: boolean
showHeader?: boolean
maxHeight?: string
}
const props = withDefaults(defineProps<Props>(), {
language: 'text',
copyable: true,
showHeader: true,
maxHeight: '400px',
})
</script>
3. Accessibility as a First-Class Citizen
Every component was built with accessibility in mind from the start:
<!-- Always include proper ARIA labels -->
<button
type="button"
:aria-expanded="mobileMenuOpen"
aria-controls="mobile-menu"
aria-label="Open main menu"
></button>
The Performance Victory
Our performance tests started passing as soon as we implemented efficient components:
$ pnpm test tests/e2e/performance.spec.ts
✓ should load in under 1 second
✓ should have minimal layout shift
✓ should efficiently load images
✓ should bundle JavaScript optimally
Tests 4 passed (4)
Key performance decisions driven by our tests:
- Lazy loading for non-critical images
- Code splitting for route-based components
- Minimal JavaScript in favor of CSS solutions
- Optimized fonts with proper loading strategies
Challenges and Solutions
Challenge 1: Brand Consistency Across Components
Problem: How do you ensure every component maintains exact brand compliance?
Solution: We created a component testing utility:
// tests/utils/brand-compliance.ts
export function testBrandCompliance(componentWrapper: VueWrapper) {
// Test brand colors
expect(componentWrapper.find('.text-brand-dark')).toBeTruthy()
// Test typography
expect(componentWrapper.find('.font-heading')).toBeTruthy()
// Test accessibility
expect(componentWrapper.find('[aria-label]')).toBeTruthy()
}
Challenge 2: Complex Component State
Problem: How do you test components with complex interactions?
Solution: We focused on user-observable behavior:
test('should handle mobile menu interactions', async () => {
const wrapper = mount(AppHeader)
// Test initial state
expect(wrapper.find('#mobile-menu').isVisible()).toBe(false)
// Test opening
await wrapper.find('[data-testid="menu-toggle"]').trigger('click')
expect(wrapper.find('#mobile-menu').isVisible()).toBe(true)
// Test closing on link click
await wrapper.find('#mobile-menu a').trigger('click')
expect(wrapper.find('#mobile-menu').isVisible()).toBe(false)
})
Challenge 3: Responsive Design Testing
Problem: How do you test responsive behavior in unit tests?
Solution: We combined unit tests for logic with E2E tests for visual behavior:
// Unit test: Test responsive logic
test('should toggle mobile menu state', () => {
const wrapper = mount(AppHeader)
const vm = wrapper.vm
expect(vm.mobileMenuOpen).toBe(false)
vm.toggleMobileMenu()
expect(vm.mobileMenuOpen).toBe(true)
})
// E2E test: Test responsive appearance
test('should show mobile menu on small screens', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 667 })
await page.goto('/')
await expect(page.locator('[data-testid="menu-toggle"]')).toBeVisible()
await expect(page.locator('.desktop-nav')).toBeHidden()
})
The Sweet Victory: All Tests Green
After implementing all 7 components, the moment we'd been working toward:
$ pnpm test
✓ tests/unit/components/AppHeader.spec.ts (4 tests)
✓ tests/unit/components/AppFooter.spec.ts (3 tests)
✓ tests/unit/components/Marketing/Hero.spec.ts (7 tests)
✓ tests/unit/components/Marketing/Features.spec.ts (4 tests)
✓ tests/unit/components/Marketing/Installation.spec.ts (3 tests)
✓ tests/unit/components/Content/DocNav.spec.ts (4 tests)
✓ tests/unit/components/Content/CodeBlock.spec.ts (3 tests)
✓ tests/e2e/brand-compliance.spec.ts (8 tests)
✓ tests/e2e/accessibility.spec.ts (6 tests)
✓ tests/e2e/responsive.spec.ts (6 tests)
Test Files 10 passed (10)
Tests 48 passed (48)
48 green tests! Not a single red failure. Every component built exactly what the tests specified, nothing more, nothing less.
What's Next: Phase 4 - Page Assembly
With our component library complete and tested, Phase 4 will focus on assembling these components into full pages. We'll tackle:
- Dynamic routing for documentation and blog content
- SEO optimization guided by performance tests
- Content management integration
- Error handling with brand-consistent design
Key Component Development Takeaways
- Tests Drive Architecture: Let failing tests guide your component design decisions
- Accessibility from Day One: Build inclusive components from the start, not as an afterthought
- Brand as Code: Translate design systems into testable, reusable patterns
- Composition Over Complexity: Build small, focused components that do one thing well
- Performance is a Feature: Make speed a first-class concern in component design
Building components with TDD taught us that constraints breed creativity. The "limitations" imposed by our tests actually led to cleaner, more maintainable, and more user-friendly components than we would have built without them.
Next time, we'll show how these battle-tested components come together to create complete, dynamic pages with routing, content management, and SEO optimization.
Live components: See our components in action at godegit.dev Source code: View the complete component library on GitHub

