CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
Personal GitHub Pages site (Jekyll) for Maykell Sanchez Romero. Bilingual (Spanish/English) technical blog with an ongoing Elixir tutorial series. Theme: Beautiful Jekyll v5. Deploys automatically on push to master.
Build and Serve Commands
# Install dependencies
bundle install
# Local development server (http://localhost:4000)
bundle exec jekyll serve
# Build for production (output: _site/)
bundle exec jekyll build
No test suite, linter, or custom build scripts. Ruby 2.7+ required (see .ruby-version).
Architecture
Jekyll site using Beautiful Jekyll theme via gemspec. Kramdown (GFM) for Markdown, Rouge for syntax highlighting.
Key directories:
_posts/- Blog articles. Naming:YYYY-MM-DD-slug-title.md. Drafts prefixed with underscore._includes/- Jekyll partials.bilingual_header.htmlis the bilingual system entry point._templates/- Templates for new content. Copybilingual_article_template.mdfor new bilingual posts._ideas/- Planning files.todo_lecciones_elixir.mdtracks Elixir lesson progress.app/- Experimental SPAs (placeholder, not actively developed).assets/css/,assets/js/- Static assets includingbilingual.cssandbilingual.js.
Bilingual System
All new articles use the bilingual infrastructure. The system is self-contained in _includes/bilingual_header.html (embedded CSS+JS) with standalone reference files in assets/.
Structure for bilingual posts:
---
layout: post
title: "Titulo en Espanol | English Title"
description: "Descripcion | Description"
tags: [Topic, Bilingual, Tutorial]
---
<!-- Bilingual Article Header -->
<!-- Include this in bilingual articles for language switching functionality -->
<style>
/* Bilingual Article Styles */
.language-selector {
display: flex;
gap: 10px;
margin-bottom: 20px;
padding: 10px;
background-color: #f8f9fa;
border-radius: 8px;
justify-content: center;
flex-wrap: wrap;
}
.lang-btn {
background-color: #ffffff;
border: 2px solid #e9ecef;
color: #495057;
padding: 8px 16px;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.3s ease;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 6px;
}
.lang-btn:hover {
background-color: #e9ecef;
border-color: #dee2e6;
transform: translateY(-1px);
}
.lang-btn.active {
background-color: #007bff;
color: white;
border-color: #007bff;
}
.lang-btn.active:hover {
background-color: #0056b3;
border-color: #0056b3;
}
.lang-content {
transition: opacity 0.3s ease;
}
.lang-content.hidden {
display: none;
}
/* Responsive design */
@media (max-width: 768px) {
.language-selector {
padding: 8px;
}
.lang-btn {
padding: 6px 12px;
font-size: 13px;
}
}
</style>
<div class="language-selector">
<button class="lang-btn active" onclick="switchLanguage('es')" data-lang="es">🇪🇸 Español</button>
<button class="lang-btn" onclick="switchLanguage('en')" data-lang="en">🇺🇸 English</button>
</div>
<script>
// Bilingual Article Functionality
function switchLanguage(lang) {
// Hide all language content
document.querySelectorAll('.lang-content').forEach(content => {
content.classList.add('hidden');
});
// Show selected language content
const selectedContent = document.getElementById('lang-' + lang);
if (selectedContent) {
selectedContent.classList.remove('hidden');
}
// Update button states
document.querySelectorAll('.lang-btn').forEach(btn => {
btn.classList.remove('active');
});
// Activate selected button
const selectedBtn = document.querySelector(`[data-lang="${lang}"]`);
if (selectedBtn) {
selectedBtn.classList.add('active');
}
// Save language preference
localStorage.setItem('preferredLanguage', lang);
// Update URL hash without scrolling
if (history.replaceState) {
history.replaceState(null, null, '#' + lang);
}
}
// Initialize language on page load
document.addEventListener('DOMContentLoaded', function() {
// Get language preference from URL hash, localStorage, or default to Spanish
let lang = 'es'; // Default to Spanish
// Check URL hash first
if (window.location.hash) {
const hashLang = window.location.hash.substring(1);
if (hashLang === 'es' || hashLang === 'en') {
lang = hashLang;
}
} else {
// Check localStorage
const savedLang = localStorage.getItem('preferredLanguage');
if (savedLang && (savedLang === 'es' || savedLang === 'en')) {
lang = savedLang;
}
}
// Apply the language
switchLanguage(lang);
});
// Handle browser back/forward navigation
window.addEventListener('hashchange', function() {
if (window.location.hash) {
const hashLang = window.location.hash.substring(1);
if (hashLang === 'es' || hashLang === 'en') {
switchLanguage(hashLang);
}
}
});
</script>
<div class="lang-content" id="lang-es" markdown="1">
Spanish content...
</div>
<div class="lang-content hidden" id="lang-en" markdown="1">
English content...
</div>
The markdown="1" attribute on content divs is required for Jekyll to render Markdown inside HTML tags. Do not modify _includes/bilingual_header.html without understanding its impact on all bilingual posts.
Elixir Lessons Series
47-lesson bilingual tutorial series. Check _ideas/todo_lecciones_elixir.md for current progress and next lesson. Reference the completed Lesson 01 (_posts/2025-01-09-elixir-lessons-01-intro-to-elixir-and-beam.md) as the canonical example for style and structure.
Lesson structure requirements
- Front matter with bilingual title/description using
|separator - Introduction and objectives
- Theoretical concepts with examples
- Practical examples (progressive difficulty)
- Best practices section
- Exercises: minimum 5 theoretical + 10 practical
- Complete answers for all exercises
- References and links
- Next lesson preview (mark unreleased as (proximamente) / (coming soon))
Exercise rules
- Continuous numbering across the entire article - never restart numbering in subsections
- Use inline difficulty labels:
1. Question *(Basico)*(not bold subsection headers) - Difficulty levels: (Basico), (Intermedio), (Avanzado)
Git Conventions
- Main branch:
master - Always include co-authorship:
Co-Authored-By: Maykell <kellsaro@gmail.com> - Permalink format:
/:year-:month-:day-:title/