# TCBA Website Modernization Guide

> A comprehensive guide for converting legacy Word/Excel-generated HTML pages to modern, accessible, and maintainable web pages.

## Table of Contents

1. [Overview](#overview)
2. [HTML Structure Standards](#html-structure-standards)
3. [CSS Architecture](#css-architecture)
4. [Theme System](#theme-system)
5. [Extraction Process](#extraction-process)
6. [Common Pitfalls](#common-pitfalls)
7. [Available Scripts](#available-scripts)

---

## Overview

The TCBA (Twenty-First Century Baseball Association) website contains extensive historical baseball statistics dating back to 1902. Many pages were originally created using Microsoft Word or Excel, resulting in bloated HTML with Office-specific markup (xmlns:o, xmlns:w, xmlns:m).

### Modernization Goals

- **Reduce file size**: From 800+ lines to 100-300 lines
- **Improve accessibility**: WCAG-compliant contrast, keyboard navigation
- **Enable theming**: Night/Day mode with user preference persistence
- **Ensure responsiveness**: Mobile-friendly layouts
- **Maintain data integrity**: All original content preserved

---

## HTML Structure Standards

### Required DOCTYPE and HTML Tag

```html
<!DOCTYPE html>
<html lang="en" data-theme="night">
```

- Always use `data-theme="night"` as default
- The theme can be toggled by user and persists via localStorage

### Required Meta Tags

```html
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TCBA - [Page Title]</title>
    <link rel="stylesheet" href="tcba-modern.css">
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
</head>
```

### Required Body Elements - Unified Header Bar

All modernized pages use a unified header bar with:
- **Left side**: Back button (wrapped in `.header-left`)
- **Right side**: Hamburger menu button + Pill-style theme toggle (in `.header-right`)

```html
<body>
    <!-- Site Header with transparent background -->
    <header class="site-header">
        <div class="header-left">
            <a href="index.htm" class="back-btn"><span class="arrow">←</span> BACK</a>
        </div>
        <div class="header-right">
            <button class="menu-trigger" id="menuTrigger" aria-label="Open navigation menu" aria-expanded="false">
                <span></span><span></span><span></span>
            </button>
            <button class="game-toggle" id="gameToggle" aria-label="Switch theme">
                <span class="toggle-track">
                    <span class="toggle-thumb"></span>
                </span>
                <span class="toggle-text">NIGHT</span>
            </button>
        </div>
    </header>

    <!-- Slide-out Navigation Menu (STANDARD TEMPLATE) -->
    <nav class="slide-menu" id="slideMenu" aria-hidden="true">
        <div class="slide-menu-header">
            <span class="menu-title">TCBA NAVIGATION</span>
            <button class="menu-close" id="menuClose" aria-label="Close menu">&times;</button>
        </div>
        <ul class="menu-links">
            <li><a href="Stats-Lineup.htm">📊 Statistics & Records</a></li>
            <li><a href="Team_Franchise Link.htm">⚾ Teams & Franchises</a></li>
            <li><a href="Hall of Fame Entrance.htm">🏆 Hall of Fame</a></li>
            <li><a href="LeagueChamps.htm">🎖️ League Champions</a></li>
            <li><a href="Tcba Today_Contents.htm">📰 TCBA Today</a></li>
            <li><a href="Yearbook/YB275 Origins.htm">📖 Origins Yearbook</a></li>
            <li><a href="Constitution_Today.htm">📜 Constitution</a></li>
            <li><a href="photogallery/Photogallery.html">📷 Photo Gallery</a></li>
        </ul>
        <div class="menu-footer">
            <p>Season <span class="origins-season">--</span> Origins • Season <span class="today-season">--</span> Today</p>
        </div>
    </nav>
    <div class="menu-overlay" id="menuOverlay"></div>

    <!-- Page content here -->

    <!-- Required JavaScript at end of body -->
</body>
```

### Slide Menu Template for Subdirectory Pages

For pages in subdirectories (e.g., `Yearbook/`, `photogallery/`), use `../` prefix on all hrefs:

```html
<nav class="slide-menu" id="slideMenu" aria-hidden="true">
    <div class="slide-menu-header">
        <span class="menu-title">TCBA NAVIGATION</span>
        <button class="menu-close" id="menuClose" aria-label="Close menu">&times;</button>
    </div>
    <ul class="menu-links">
        <li><a href="../Stats-Lineup.htm">📊 Statistics & Records</a></li>
        <li><a href="../Team_Franchise Link.htm">⚾ Teams & Franchises</a></li>
        <li><a href="../Hall of Fame Entrance.htm">🏆 Hall of Fame</a></li>
        <li><a href="../LeagueChamps.htm">🎖️ League Champions</a></li>
        <li><a href="../Tcba Today_Contents.htm">📰 TCBA Today</a></li>
        <li><a href="YB275 Origins.htm">📖 Origins Yearbook</a></li>
        <li><a href="../Constitution_Today.htm">📜 Constitution</a></li>
        <li><a href="../photogallery/Photogallery.html">📷 Photo Gallery</a></li>
    </ul>
    <div class="menu-footer">
        <p>Season <span class="origins-season">--</span> Origins • Season <span class="today-season">--</span> Today</p>
    </div>
</nav>
<div class="menu-overlay" id="menuOverlay"></div>
```

### Required JavaScript for Theme Toggle and Menu

```javascript
// Theme and Menu Setup
(function() {
    const html = document.documentElement;
    const saved = localStorage.getItem('tcba-theme');
    if (saved) html.setAttribute('data-theme', saved);

    function updateToggleText() {
        const toggleText = document.querySelector('.toggle-text');
        if (toggleText) {
            const mode = html.getAttribute('data-theme') || 'night';
            toggleText.textContent = mode.toUpperCase();
        }
    }

    document.addEventListener('DOMContentLoaded', function() {
        // Theme toggle
        const toggle = document.getElementById('gameToggle');
        if (toggle) {
            updateToggleText();
            toggle.addEventListener('click', function() {
                const current = html.getAttribute('data-theme') || 'night';
                const next = current === 'night' ? 'day' : 'night';
                html.setAttribute('data-theme', next);
                localStorage.setItem('tcba-theme', next);
                updateToggleText();
            });
        }

        // Menu functionality
        const menuTrigger = document.getElementById('menuTrigger');
        const menuClose = document.getElementById('menuClose');
        const slideMenu = document.getElementById('slideMenu');
        const menuOverlay = document.getElementById('menuOverlay');

        function openMenu() {
            slideMenu.classList.add('open');
            menuOverlay.classList.add('show');
            menuTrigger.setAttribute('aria-expanded', 'true');
        }
        function closeMenu() {
            slideMenu.classList.remove('open');
            menuOverlay.classList.remove('show');
            menuTrigger.setAttribute('aria-expanded', 'false');
        }

        if (menuTrigger) menuTrigger.addEventListener('click', openMenu);
        if (menuClose) menuClose.addEventListener('click', closeMenu);
        if (menuOverlay) menuOverlay.addEventListener('click', closeMenu);
        document.addEventListener('keydown', (e) => {
            if (e.key === 'Escape' && slideMenu.classList.contains('open')) closeMenu();
        });

        // Update season numbers in menu footer
        document.querySelectorAll('.origins-season').forEach(el => el.textContent = '121');
        document.querySelectorAll('.today-season').forEach(el => el.textContent = '120');
    });
})();
```

---

## CSS Architecture

### Using tcba-modern.css

Link the shared stylesheet for consistent styling:

```html
<link rel="stylesheet" href="tcba-modern.css">
```

### CSS Custom Properties (Design Tokens)

The stylesheet provides these CSS variables for theming:

| Variable | Night Mode | Day Mode | Usage |
|----------|------------|----------|-------|
| `--bg-primary` | `#0a1628` | `#f5f0e6` | Page background |
| `--bg-secondary` | `#132238` | `#fff9f0` | Section background |
| `--bg-card` | `rgba(255,255,255,0.05)` | `rgba(0,0,0,0.03)` | Card background |
| `--text-primary` | `#ffffff` | `#1a1a1a` | Main text |
| `--text-secondary` | `rgba(255,255,255,0.85)` | `rgba(0,0,0,0.8)` | Secondary text |
| `--text-muted` | `rgba(255,255,255,0.6)` | `rgba(0,0,0,0.55)` | Muted text |
| `--accent` | `#ffaa00` | `#c45500` | Accent/highlight color |
| `--border-color` | `rgba(255,255,255,0.1)` | `rgba(0,0,0,0.1)` | Borders |
| `--font-display` | `'Bebas Neue'` | `'Bebas Neue'` | Headers |
| `--font-body` | `'Inter'` | `'Inter'` | Body text |

### Typography Scale

```css
--text-sm: 0.95rem;
--text-base: 1.1rem;
--text-lg: 1.35rem;
--text-xl: 1.75rem;
--text-2xl: 2.25rem;
--text-3xl: 3rem;
```

### Spacing Scale

```css
--space-xs: 0.25rem;
--space-sm: 0.5rem;
--space-md: 1rem;
--space-lg: 1.5rem;
--space-xl: 2rem;
--space-2xl: 3rem;
```

---

## Theme System

### How It Works

1. **Default**: Pages load with `data-theme="night"` on the `<html>` element
2. **Toggle**: User clicks theme button to switch between night/day
3. **Persistence**: Preference stored in `localStorage` key `tcba-theme`
4. **Initialization**: On page load, saved preference is applied before render

### Theme Toggle CSS (if not using tcba-modern.css)

```css
.theme-toggle {
    position: fixed;
    top: 1rem;
    right: 1rem;
    background: var(--bg-card);
    border: 1px solid var(--border-color);
    color: var(--text-primary);
    padding: 0.5rem 1rem;
    border-radius: 6px;
    cursor: pointer;
    font-family: var(--font-display);
    font-size: 0.9rem;
    letter-spacing: 0.1em;
    transition: all 0.2s ease;
    z-index: 1000;
}
.theme-toggle:hover {
    border-color: var(--accent);
    color: var(--accent);
}
```

### Day Mode Override

```css
[data-theme="day"] {
    --bg-primary: #f5f0e6;
    --bg-secondary: #fff9f0;
    /* ... other day mode values ... */
}
```

---

## Unified Header Bar

All modernized pages include a transparent fixed header bar with navigation elements.

### Header Structure

The header consists of:
- **Left**: Back button (← BACK) linking to index.htm
- **Right**: Hamburger menu trigger + Pill-style theme toggle (NIGHT/DAY)

See "Required Body Elements - Unified Header Bar" section above for the full HTML template.

### Critical CSS Classes

| Class | Purpose |
|-------|---------|
| `.site-header` | Transparent fixed header bar |
| `.back-link` | Back button link |
| `.menu-trigger` | Hamburger icon button (3 bars) |
| `.theme-toggle` | Pill-style toggle with icon and text |
| `.slide-menu` | Slide-out navigation panel |
| `.slide-menu.open` | Active state for slide menu |
| `.menu-overlay` | Dark overlay behind menu |
| `.menu-overlay.show` | Active state for overlay |
| `.slide-menu-header` | Menu header with title and close button |
| `.menu-links` | Navigation link list |
| `.menu-footer` | Season info at bottom of menu |

### JavaScript Class Requirements

**Important**: The menu uses these specific class toggles:
- Menu open: `.slide-menu.open` (NOT `.active`)
- Overlay show: `.menu-overlay.show` (NOT `.active`)

---

## Extraction Process

### Source File Characteristics

Legacy files typically have:
- `xmlns:o`, `xmlns:w`, `xmlns:m` namespaces (Microsoft Office)
- Inline styles with specific fonts (Times New Roman, etc.)
- Table-based layouts with complex cell spanning
- Windows-1252 encoding (not UTF-8)
- 500-3000+ lines of HTML for simple content

### Extraction Scripts

#### extract_team.py

Extracts structured data from Word-generated team pages:

```python
from extract_team import extract_team_data

data = extract_team_data('TeamName.htm')
# Returns dict with: team_name, years, gm, special, franchise,
#                    history, wins, losses, batting_html, batting_excel,
#                    pitching_html, pitching_excel, seasons
```

#### Key Regex Patterns

**Team Name** (from h3 header):
```python
re.search(r'<h3[^>]*>.*?["\']>([A-Z][^<\d]+?)\s*\d{4}', content, re.DOTALL | re.IGNORECASE)
```

**Years Range** (handles multiple formats and dash types):
```python
re.search(r'>\s*[-–—]\s*(\d{4}[-–—\d&;ampltp\s]+(?:\d{4}|[Pp]resent))', content)
```

**History Brief** (multi-span text):
```python
re.search(r'History\s*Brief:?\s*</span>(.*?)</p>', content, re.DOTALL | re.IGNORECASE)
```

**Season Records** (table rows with 4-digit year):
```python
if re.match(r'^(19|20)\d{2}$', first_cell_value):
    # This is a season record row
```

### modernize_team.py

Generates modern HTML from extracted data and batch processes:

```bash
# Single file
python modernize_team.py TeamName.htm

# Batch process from list
python modernize_team.py --batch team_pages_list.txt

# Force re-process (use -OLD backup as source)
python modernize_team.py --batch team_pages_list.txt --force
```

---

## Common Pitfalls

### 1. Encoding Issues

**Problem**: Windows-1252 characters display as garbled text
**Solution**: Always read with Windows-1252, write with UTF-8

```python
content = Path(filepath).read_text(encoding='windows-1252')
Path(filepath).write_text(html, encoding='utf-8')
```

### 2. Curly Apostrophes in JavaScript

**Problem**: Curly apostrophes (') break JavaScript strings
**Solution**: Use double quotes for strings containing apostrophes

```javascript
// BAD
'Hamilton EH's': 'Hamilton.htm'

// GOOD
"Hamilton EH's": 'Hamilton.htm'
```

### 3. Multi-Span Text Extraction

**Problem**: Text split across multiple `<span>` tags
**Solution**: Capture entire section, then strip ALL tags

```python
# Strip all HTML tags without adding spaces
hist = re.sub(r'<[^>]+>', '', section_text)
hist = re.sub(r'\s+', ' ', hist).strip()
```

### 4. Trailing Dashes in Names

**Problem**: Team names extracted with trailing dashes ("Jersey –")
**Solution**: Strip trailing punctuation

```python
team_name = re.sub(r'\s*[-–—]\s*$', '', team_name).strip()
```

### 5. Season Table Detection

**Problem**: Table headers not consistently formatted
**Solution**: Find rows by detecting 4-digit year in first cell

```python
if values[0] and re.match(r'^(19|20)\d{2}$', values[0]):
    # Valid season row
```

---

## Available Scripts

| Script | Purpose | Usage |
|--------|---------|-------|
| `extract_team.py` | Extract data from Word-generated team pages | `python extract_team.py TeamName.htm` |
| `modernize_team.py` | Generate modern HTML from extracted data | `python modernize_team.py --batch` |
| `add_theme_toggle.py` | Add theme toggle to existing modern pages | `python add_theme_toggle.py` |
| `add_back_nav.py` | Add floating back navigation to modern pages | `python add_back_nav.py` |

---

## File Naming Conventions

- **Team pages**: `CityName.htm` or `Nickname.htm` (e.g., `Long Island.htm`, `Scranton.htm`)
- **Backup files**: `OriginalName-OLD.htm` (preserved for data recovery)
- **Modern templates**: Usually same name, overwriting original after backup
- **Stats files**: `Career Batting XX.htm`, `Career Pitching by Team XX.htm` (XX = abbreviation)

---

## Backup Procedure

Before modernizing any file:

1. Create backup with `-OLD` suffix
2. Extract data from original
3. Generate modern HTML
4. Write to original filename
5. Verify extraction was successful

```python
backup_path = filepath.with_name(filepath.stem + '-OLD' + filepath.suffix)
if not backup_path.exists():
    shutil.copy(filepath, backup_path)
```

---

## Era Designation System

Teams are tagged with era indicators showing when they played:

| Tag | Era | Color | Hex |
|-----|-----|-------|-----|
| Y | Yesterday | Medium Purple | `#7B68EE` |
| G | Gold | Peru/Tan | `#CD853F` |
| O | Origins | Slate Gray | `#708090` |
| T | Today | Steel Blue | `#4682B4` |

---

## Next Steps

For detailed extraction patterns, see: [EXTRACTION-PROCESS.md](./EXTRACTION-PROCESS.md)

For technical CSS/JS reference, see: [Technical Reference Page](./technical-reference.htm)

