dev-diaryvuecomponentsbrand-designaccessibilityresponsive-design

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.

godegit Development Team
godegit Development Team
12 min read
Split screen showing failing tests on left, Vue component code on right

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

  1. Tests Drive Architecture: Let failing tests guide your component design decisions
  2. Accessibility from Day One: Build inclusive components from the start, not as an afterthought
  3. Brand as Code: Translate design systems into testable, reusable patterns
  4. Composition Over Complexity: Build small, focused components that do one thing well
  5. 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

Tagged with

dev-diaryvuecomponentsbrand-designaccessibilityresponsive-design

Share this post

godegit Development Team

godegit Development Team

The engineering team behind godegit, sharing real-world experiences building maintainable, tested Vue applications.

Related Posts