← banjotuna

Add grid view mode for chords - show all keys in a table

f86e0d0b0b212ab2d8e25b40603fc520356355fb · Claude · Feb 1, 2026 23:24

- Add view mode toggle (Single Key / All Keys Grid) in chord controls
- In grid mode, display all 12 keys as rows with chord types as columns
- Hide key selector when grid mode is active (all keys shown)
- Add CSS styles for the grid table with sticky headers
- Support print styles for grid view

https://claude.ai/code/session_01VSHtwaMJKL1ZgH33JDcwbY

+226 −2 · 1 file changed

  • index.html +226 −2
diff --git a/index.html b/index.html
index 70c7a5f..ce75fa1 100644
--- a/index.html
+++ b/index.html
@@ -875,6 +875,124 @@
             border-color: var(--g-family);
         }
 
+        .view-mode-buttons {
+            display: flex;
+            gap: 5px;
+        }
+
+        .view-mode-btn {
+            padding: 6px 12px;
+            border: 2px solid var(--border-color);
+            background: white;
+            cursor: pointer;
+            font-size: 12px;
+            border-radius: 4px;
+            transition: all 0.2s;
+        }
+
+        .view-mode-btn:hover {
+            background: #f0f0f0;
+        }
+
+        .view-mode-btn.active {
+            background: var(--d-family);
+            color: white;
+            border-color: var(--d-family);
+        }
+
+        /* All Keys Grid View */
+        .chord-keys-grid-container {
+            overflow-x: auto;
+            padding: 10px;
+        }
+
+        .chord-keys-grid {
+            border-collapse: collapse;
+            width: 100%;
+            background: white;
+            border: 2px solid var(--border-color);
+        }
+
+        .chord-keys-grid th,
+        .chord-keys-grid td {
+            border: 1px solid var(--border-color);
+            padding: 8px;
+            text-align: center;
+            vertical-align: top;
+        }
+
+        .chord-keys-grid th {
+            background: var(--bg-dark);
+            color: white;
+            font-size: 13px;
+            font-weight: bold;
+            position: sticky;
+            top: 0;
+            z-index: 1;
+        }
+
+        .chord-keys-grid th.key-header {
+            background: var(--g-family);
+            min-width: 60px;
+        }
+
+        .chord-keys-grid td.key-cell {
+            background: var(--g-family-light);
+            font-weight: bold;
+            font-size: 16px;
+            vertical-align: middle;
+            min-width: 50px;
+        }
+
+        .chord-keys-grid .grid-chord-cell {
+            min-width: 150px;
+            padding: 5px;
+        }
+
+        .chord-keys-grid .grid-chord-diagram {
+            background: #fafafa;
+            border-radius: 4px;
+            padding: 5px;
+        }
+
+        .chord-keys-grid .grid-chord-title {
+            font-size: 14px;
+            font-weight: bold;
+            margin-bottom: 3px;
+        }
+
+        .chord-keys-grid .grid-chord-notes {
+            font-size: 10px;
+            color: var(--text-muted);
+            margin-top: 3px;
+        }
+
+        .chord-keys-grid .grid-no-voicing {
+            color: var(--text-muted);
+            font-style: italic;
+            font-size: 11px;
+            padding: 20px 5px;
+        }
+
+        @media print {
+            .chord-keys-grid-container {
+                overflow: visible;
+            }
+
+            .chord-keys-grid {
+                font-size: 10px;
+            }
+
+            .chord-keys-grid th,
+            .chord-keys-grid td {
+                padding: 4px;
+            }
+
+            .chord-keys-grid .grid-chord-cell {
+                min-width: 100px;
+            }
+        }
+
         .chord-grid {
             display: grid;
             grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
@@ -1086,6 +1204,14 @@
                 </div>
 
                 <div class="chord-control-group">
+                    <label>View Mode</label>
+                    <div class="view-mode-buttons" id="viewModeButtons">
+                        <button class="view-mode-btn active" data-mode="single">Single Key</button>
+                        <button class="view-mode-btn" data-mode="grid">All Keys Grid</button>
+                    </div>
+                </div>
+
+                <div class="chord-control-group" id="keyButtonsContainer">
                     <label>Key</label>
                     <div class="key-buttons" id="keyButtons">
                         <!-- Keys populated by JS -->
@@ -2004,7 +2130,8 @@
             chordTuning: 'Open G',
             chordKey: 'G',
             chordTypeFilter: 'all',
-            fingerNotation: 'numbers'
+            fingerNotation: 'numbers',
+            chordViewMode: 'single'  // 'single' or 'grid' (all keys)
         };
 
         function loadState() {
@@ -2572,7 +2699,8 @@
                 chordTuning: 'Open G',
                 chordKey: 'G',
                 chordTypeFilter: 'all',
-                fingerNotation: 'numbers'
+                fingerNotation: 'numbers',
+                chordViewMode: 'single'
             };
             saveState();
             applyStateToUI();
@@ -2684,6 +2812,35 @@
             });
         }
 
+        function populateViewModeButtons() {
+            const container = document.getElementById('viewModeButtons');
+            if (!container) return;
+
+            container.innerHTML = `
+                <button class="view-mode-btn ${state.chordViewMode === 'single' ? 'active' : ''}" data-mode="single">Single Key</button>
+                <button class="view-mode-btn ${state.chordViewMode === 'grid' ? 'active' : ''}" data-mode="grid">All Keys Grid</button>
+            `;
+
+            // Add click handlers
+            container.querySelectorAll('.view-mode-btn').forEach(btn => {
+                btn.addEventListener('click', () => {
+                    state.chordViewMode = btn.dataset.mode;
+                    saveState();
+                    populateViewModeButtons();
+                    updateKeyButtonsVisibility();
+                    renderChords();
+                });
+            });
+        }
+
+        function updateKeyButtonsVisibility() {
+            const keyButtonsContainer = document.getElementById('keyButtonsContainer');
+            if (keyButtonsContainer) {
+                // Hide key buttons in grid mode (since all keys are shown)
+                keyButtonsContainer.style.display = state.chordViewMode === 'grid' ? 'none' : 'flex';
+            }
+        }
+
         function renderChords() {
             const container = document.getElementById('chordContainer');
             if (!container) return;
@@ -2696,6 +2853,15 @@
 
             const fingerNotation = document.querySelector('input[name="fingerNotation"]:checked')?.value || 'numbers';
 
+            // Check view mode
+            if (state.chordViewMode === 'grid') {
+                renderChordsGrid(container, tuningData, fingerNotation);
+            } else {
+                renderChordsSingle(container, tuningData, fingerNotation);
+            }
+        }
+
+        function renderChordsSingle(container, tuningData, fingerNotation) {
             // Determine which chord types to show
             const typesToShow = state.chordTypeFilter === 'all'
                 ? Object.keys(CHORD_TYPES)
@@ -2734,9 +2900,65 @@
                 }
             });
 
+            container.className = 'chord-grid';
             container.innerHTML = html;
         }
 
+        function renderChordsGrid(container, tuningData, fingerNotation) {
+            // Determine which chord types to show as columns
+            const typesToShow = state.chordTypeFilter === 'all'
+                ? Object.keys(CHORD_TYPES)
+                : [state.chordTypeFilter];
+
+            // Build table header with chord types
+            let headerHtml = '<tr><th class="key-header">Key</th>';
+            typesToShow.forEach(chordType => {
+                const type = CHORD_TYPES[chordType];
+                headerHtml += `<th>${type.name}</th>`;
+            });
+            headerHtml += '</tr>';
+
+            // Build table rows for each key
+            let rowsHtml = '';
+            CHORD_KEYS.forEach(key => {
+                rowsHtml += `<tr><td class="key-cell">${key.display}</td>`;
+
+                typesToShow.forEach(chordType => {
+                    const type = CHORD_TYPES[chordType];
+                    const voicing = findChordVoicing(tuningData, key.root, chordType, 12);
+
+                    if (voicing) {
+                        const svg = generateChordDiagramSVG(voicing, `${key.root}${type.symbol}`, state.chordTuning, fingerNotation);
+                        rowsHtml += `
+                            <td class="grid-chord-cell">
+                                <div class="grid-chord-diagram">
+                                    <div class="grid-chord-title">${displayNote(key.root)}${type.symbol}</div>
+                                    ${svg}
+                                    <div class="grid-chord-notes">${voicing.chordNotes.join(' - ')}</div>
+                                </div>
+                            </td>
+                        `;
+                    } else {
+                        rowsHtml += `
+                            <td class="grid-chord-cell">
+                                <div class="grid-no-voicing">Not available</div>
+                            </td>
+                        `;
+                    }
+                });
+
+                rowsHtml += '</tr>';
+            });
+
+            container.className = 'chord-keys-grid-container';
+            container.innerHTML = `
+                <table class="chord-keys-grid">
+                    <thead>${headerHtml}</thead>
+                    <tbody>${rowsHtml}</tbody>
+                </table>
+            `;
+        }
+
         function setupChordEventListeners() {
             // Tuning selector
             const tuningSelect = document.getElementById('chordTuningSelect');
@@ -2760,9 +2982,11 @@
 
         function initChordTab() {
             populateChordTuningSelect();
+            populateViewModeButtons();
             populateKeyButtons();
             populateChordTypeButtons();
             setupChordEventListeners();
+            updateKeyButtonsVisibility();
             renderChords();
         }