RAPHA - Engenharia de Dados & Software RAPHA - Engenharia de Dados & Software
100%
Voltar aos Posts
8 min de leitura

Construindo um Blog Moderno: Do Conceito à Implementação

Como desenvolvi um blog pessoal usando Astro, Tailwind CSS e técnicas modernas de desenvolvimento, com auxílio de IA para otimização e testes

terça-feira
8 min
de leitura

Há algumas semanas, decidi criar um novo blog pessoal. Não apenas como uma vitrine para meus projetos, mas como um laboratório para experimentar tecnologias modernas e documentar minha jornada no desenvolvimento. O resultado foi muito além do que imaginei inicialmente.

Motivação e Objetivos

Por que um novo blog?

O projeto nasceu de duas necessidades principais:

  1. Posicionamento profissional: Queria um espaço para compartilhar conhecimentos sobre dados, engenharia de software e telemetria de forma mais estruturada
  2. Desenvolvimento pessoal: Precisava de um projeto paralelo interessante para experimentar novas tecnologias e manter-me atualizado

Objetivos técnicos

  • Performance excepcional (Core Web Vitals otimizados)
  • Design moderno e responsivo
  • Experiência de desenvolvimento fluida
  • SEO otimizado para melhor visibilidade
  • Facilidade de manutenção e extensibilidade

Stack Tecnológica Escolhida

Astro como Foundation

Optei pelo Astro v4.16.19 como gerador de sites estáticos por várias razões técnicas:

// astro.config.mjs
export default defineConfig({
  integrations: [
    tailwind(),
    mdx(),
    sitemap()
  ],
  markdown: {
    shikiConfig: {
      theme: 'github-dark',
      wrap: true
    }
  },
  site: 'https://meublog.dev'
});

Vantagens do Astro:

  • Islands Architecture: JavaScript hidratado apenas onde necessário
  • Zero JS por padrão: Performance superior com loading mínimo
  • Flexibilidade: Suporte a React, Vue, Svelte quando necessário
  • Built-in optimizations: Lazy loading, code splitting automático

Tailwind CSS para Design System

/* tailwind.config.cjs */
module.exports = {
  theme: {
    extend: {
      colors: {
        primary: {
          50: '#eff6ff',
          600: '#2563eb',
          // Palette personalizada indigo/purple
        }
      },
      typography: {
        DEFAULT: {
          css: {
            maxWidth: 'none',
            // Customizações para melhor legibilidade
          }
        }
      }
    }
  }
}

Por que Tailwind:

  • Consistência: Design system coeso em toda aplicação
  • Performance: CSS otimizado com purging automático
  • Experiência de Desenvolvimento: Autocomplete excelente no VS Code
  • Customização: Fácil extensão do tema base

TypeScript para Type Safety

// src/lib/posts.ts
interface PostFrontmatter {
  title: string;
  description?: string;
  date: string;
  category: string;
  tags?: string[];
  featured?: boolean;
  readingTime?: string;
}

export function getAllPosts(): Array<{
  url: string;
  frontmatter: PostFrontmatter;
}> {
  // Implementação type-safe para coleta de posts
}

Arquitetura e Estrutura

Organização de Arquivos

src/
├── components/          # Componentes reutilizáveis
│   ├── AuthorBio.astro
│   ├── CategoryBadge.astro
│   └── ThemeToggle.astro
├── layouts/            # Layouts base
│   ├── BaseLayout.astro
│   └── PostLayout.astro
├── pages/              # Rotas da aplicação
│   ├── index.astro
│   ├── posts.astro
│   └── [slug].astro
├── content/            # Conteúdo em MDX
│   ├── posts/
│   └── notes/
├── lib/                # Utilities e helpers
└── styles/             # Estilos globais

Sistema de Componentes

Desenvolvi componentes modulares e reutilizáveis:

---
// CategoryBadge.astro
export interface Props {
  name: string;
  size?: 'sm' | 'md' | 'lg';
  variant?: 'default' | 'outlined';
}

const { name, size = 'md', variant = 'default' } = Astro.props;
---

<span 
  class={`
    inline-flex items-center rounded-full font-medium
    ${size === 'sm' ? 'px-2 py-1 text-xs' : 'px-3 py-1.5 text-sm'}
    ${variant === 'outlined' 
      ? 'border border-indigo-200 text-indigo-700' 
      : 'bg-indigo-100 text-indigo-800'
    }
  `}
>
  {name}
</span>

Funcionalidades Implementadas

Sistema de Busca e Filtros

Implementei um sistema de busca client-side otimizado:

// Busca em tempo real com debounce
function setupSearch() {
  const searchInput = document.getElementById('search');
  const articles = Array.from(document.querySelectorAll('[data-searchable]'));
  
  let debounceTimer;
  
  searchInput.addEventListener('input', (e) => {
    clearTimeout(debounceTimer);
    debounceTimer = setTimeout(() => {
      filterArticles(e.target.value, articles);
    }, 300);
  });
}

function filterArticles(term, articles) {
  const normalizedTerm = term.toLowerCase().trim();
  
  articles.forEach(article => {
    const title = article.dataset.title || '';
    const tags = article.dataset.tags || '';
    const category = article.dataset.category || '';
    
    const matches = [title, tags, category].some(field => 
      field.toLowerCase().includes(normalizedTerm)
    );
    
    article.style.display = matches ? '' : 'none';
  });
}

Dark Mode com Persistência

// Theme toggle com sistema de preferência
class ThemeManager {
  constructor() {
    this.theme = this.getStoredTheme() || this.getPreferredTheme();
    this.applyTheme(this.theme);
  }
  
  getStoredTheme() {
    return localStorage.getItem('theme');
  }
  
  getPreferredTheme() {
    return window.matchMedia('(prefers-color-scheme: dark)').matches 
      ? 'dark' : 'light';
  }
  
  toggle() {
    this.theme = this.theme === 'dark' ? 'light' : 'dark';
    this.applyTheme(this.theme);
    localStorage.setItem('theme', this.theme);
  }
}

SEO e Performance

---
// BaseLayout.astro - Meta tags otimizadas
const { title, description, image } = Astro.props;
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
---

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  
  <!-- SEO Essentials -->
  <title>{title}</title>
  <meta name="description" content={description} />
  <link rel="canonical" href={canonicalURL} />
  
  <!-- Open Graph -->
  <meta property="og:title" content={title} />
  <meta property="og:description" content={description} />
  <meta property="og:image" content={image} />
  
  <!-- Performance hints -->
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="dns-prefetch" href="//fonts.gstatic.com">
</head>

O Papel da IA no Desenvolvimento

Autocomplete Inteligente

Utilizei muito o GitHub Copilot e Cursor AI para:

// Exemplo de código gerado com assistência de IA
export function generateReadingTime(content: string): string {
  // IA sugeriu automaticamente esta implementação otimizada
  const wordsPerMinute = 200;
  const wordCount = content
    .replace(/[^\w\s]/g, '')
    .split(/\s+/)
    .filter(word => word.length > 0).length;
  
  const minutes = Math.ceil(wordCount / wordsPerMinute);
  return `${minutes} min`;
}

Testes Automatizados

A IA me ajudou a escrever testes abrangentes:

// Testes gerados com assistência de IA
describe('Search Functionality', () => {
  beforeEach(() => {
    // Setup DOM elements
    document.body.innerHTML = `
      <input id="search" type="text" />
      <div data-searchable data-title="typescript guide"></div>
    `;
  });

  test('should filter articles based on search term', () => {
    const searchInput = document.getElementById('search');
    const article = document.querySelector('[data-searchable]');
    
    // Simulate search
    searchInput.value = 'typescript';
    searchInput.dispatchEvent(new Event('input'));
    
    expect(article.style.display).not.toBe('none');
  });
});

Otimizações de Performance

A IA sugeriu várias otimizações:

// Lazy loading de imagens
const imageObserver = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      img.classList.remove('lazy');
      imageObserver.unobserve(img);
    }
  });
});

// Debounce para eventos frequentes
function debounce(func, wait) {
  let timeout;
  return function executedFunction(...args) {
    const later = () => {
      clearTimeout(timeout);
      func(...args);
    };
    clearTimeout(timeout);
    timeout = setTimeout(later, wait);
  };
}

Processo de Design e UX

Design System Coeso

Criei um sistema de design baseado em:

/* Design tokens */
:root {
  --color-primary-50: #eff6ff;
  --color-primary-600: #2563eb;
  --space-xs: 0.25rem;
  --space-sm: 0.5rem;
  --space-md: 1rem;
  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --border-radius: 0.5rem;
}

/* Componente base */
.btn {
  @apply px-4 py-2 rounded-lg font-medium transition-all duration-200;
  @apply focus:outline-none focus:ring-2 focus:ring-offset-2;
}

.btn-primary {
  @apply bg-indigo-600 text-white hover:bg-indigo-700;
  @apply focus:ring-indigo-500;
}

Micro-interações

Implementei animações sutis para melhorar UX:

/* Hover effects suaves */
.card {
  @apply transition-all duration-300;
  @apply hover:shadow-xl hover:-translate-y-1;
}

/* Loading states */
.skeleton {
  @apply animate-pulse bg-gray-200 dark:bg-gray-700;
}

/* Focus states acessíveis */
.focus-ring {
  @apply focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2;
  @apply dark:focus:ring-offset-gray-900;
}

Challenges e Soluções

Performance de Build

Problema: Build times crescendo com mais conteúdo

Solução: Implementei build incremental e lazy loading:

// astro.config.mjs
export default defineConfig({
  experimental: {
    contentCollectionCache: true
  },
  vite: {
    build: {
      rollupOptions: {
        output: {
          manualChunks: {
            vendor: ['react', 'react-dom'],
            utils: ['lodash', 'date-fns']
          }
        }
      }
    }
  }
});

SEO para SPA-like Navigation

Problema: Manter SEO com interações dinâmicas

Solução: Hidratação seletiva com Astro Islands:

---
// Apenas componentes interativos são hidratados
---

<SearchBar client:load />
<StaticContent />
<FilterButtons client:idle />

Métricas e Resultados

Performance Metrics

Após otimizações, consegui:

  • Lighthouse Score: 98/100
  • First Contentful Paint: < 1.2s
  • Largest Contentful Paint: < 2.5s
  • Cumulative Layout Shift: < 0.1
  • Bundle Size: ~45KB (gzipped)

Developer Experience

  • Build Time: ~3s para builds incrementais
  • Hot Reload: < 500ms
  • Type Safety: 100% TypeScript coverage
  • Code Quality: ESLint + Prettier configurados

Lições Aprendidas

Tecnológicas

  1. Astro é excelente para content-heavy sites: Zero JS por padrão resulta em performance superior
  2. Tailwind + TypeScript: Combinação poderosa para DX e maintainability
  3. AI-assisted development: Aumenta significativamente a velocidade de desenvolvimento

Processo

  1. Start simple, iterate fast: Comecei com MVP e adicionei features incrementalmente
  2. Design system first: Investir tempo em tokens e componentes base acelera desenvolvimento posterior
  3. Performance budget: Definir limites de performance desde o início previne regressões

Próximos Passos

Features Planejadas

  • Analytics privacy-focused: Implementar tracking sem cookies
  • Newsletter integration: Sistema de assinatura para updates
  • Comment system: Usando GitHub Discussions API
  • PWA capabilities: Service worker para offline reading

Otimizações Técnicas

  • Edge deployment: Migrar para Vercel Edge Functions
  • Image optimization: Implementar responsive images com diferentes formatos
  • Internationalization: Suporte a múltiplos idiomas

Conclusão

Este projeto foi muito além de um simples blog. Tornou-se um laboratório para experimentar tecnologias modernas, praticar boas práticas de desenvolvimento e documentar minha jornada profissional.

A combinação de Astro + Tailwind + TypeScript provou ser extremamente produtiva, especialmente com o auxílio de ferramentas de IA para acelerar o desenvolvimento e garantir qualidade do código.

Principais takeaways:

  • Performance importa: Users percebem a diferença
  • Developer Experience: Ferramentas certas fazem toda diferença
  • AI as a pair programmer: Aumenta velocidade sem comprometer qualidade
  • Iterative development: Melhor que big bang releases

O código completo está disponível no GitHub, e continuo documentando novas funcionalidades e otimizações conforme implemento.


Este artigo foi escrito enquanto continuo desenvolvendo e melhorando o blog. Se você tem sugestões ou quer discutir algum aspecto técnico, fique à vontade para entrar em contato!