Table data filtering and search capabilities in Markdown enable dynamic content discovery that transforms static tabular information into interactive, searchable interfaces allowing users to efficiently navigate large datasets, locate specific information, and customize their viewing experience. While standard Markdown tables provide basic data presentation, advanced filtering and search implementations using JavaScript integration, responsive design patterns, and progressive enhancement create sophisticated data exploration tools that maintain accessibility while delivering powerful user interactions.

Interactive table capabilities provide essential benefits for content navigation:

  • Information Discovery: Enable users to quickly locate specific data points within large datasets
  • Content Customization: Allow filtering based on user preferences and requirements
  • Improved Usability: Reduce cognitive load by presenting only relevant information
  • Enhanced Performance: Display manageable subsets of data without overwhelming users
  • Accessibility: Maintain keyboard navigation and screen reader compatibility

Foundation JavaScript Integration

Basic Search Implementation

Setting up fundamental search functionality for Markdown tables:

// table-search.js - Basic table search implementation
class MarkdownTableSearch {
    constructor(tableSelector, options = {}) {
        this.table = document.querySelector(tableSelector);
        this.options = {
            searchInputId: 'table-search',
            highlightClass: 'search-highlight',
            noResultsMessage: 'No matching records found',
            debounceDelay: 300,
            caseSensitive: false,
            searchColumns: 'all', // 'all' or array of column indices
            ...options
        };
        
        this.searchInput = null;
        this.originalRows = [];
        this.debounceTimer = null;
        
        this.init();
    }
    
    init() {
        if (!this.table) {
            console.error('Table not found:', this.tableSelector);
            return;
        }
        
        this.createSearchInput();
        this.cacheOriginalRows();
        this.bindEvents();
    }
    
    createSearchInput() {
        // Create search container
        const searchContainer = document.createElement('div');
        searchContainer.className = 'table-search-container';
        
        // Create search input
        this.searchInput = document.createElement('input');
        this.searchInput.type = 'text';
        this.searchInput.id = this.options.searchInputId;
        this.searchInput.className = 'table-search-input';
        this.searchInput.placeholder = 'Search table data...';
        this.searchInput.setAttribute('aria-label', 'Search table content');
        
        // Create search icon
        const searchIcon = document.createElement('span');
        searchIcon.className = 'search-icon';
        searchIcon.innerHTML = '🔍';
        searchIcon.setAttribute('aria-hidden', 'true');
        
        // Create clear button
        const clearButton = document.createElement('button');
        clearButton.type = 'button';
        clearButton.className = 'search-clear';
        clearButton.innerHTML = '';
        clearButton.setAttribute('aria-label', 'Clear search');
        clearButton.style.display = 'none';
        
        // Assemble search container
        searchContainer.appendChild(searchIcon);
        searchContainer.appendChild(this.searchInput);
        searchContainer.appendChild(clearButton);
        
        // Insert before table
        this.table.parentNode.insertBefore(searchContainer, this.table);
        
        this.clearButton = clearButton;
    }
    
    cacheOriginalRows() {
        const tbody = this.table.querySelector('tbody');
        if (tbody) {
            this.originalRows = Array.from(tbody.querySelectorAll('tr')).map(row => ({
                element: row,
                data: this.extractRowData(row),
                visible: true
            }));
        }
    }
    
    extractRowData(row) {
        return Array.from(row.querySelectorAll('td, th')).map(cell => ({
            text: cell.textContent.toLowerCase(),
            html: cell.innerHTML
        }));
    }
    
    bindEvents() {
        // Search input events
        this.searchInput.addEventListener('input', (e) => {
            this.handleSearch(e.target.value);
        });
        
        this.searchInput.addEventListener('keydown', (e) => {
            if (e.key === 'Escape') {
                this.clearSearch();
            }
        });
        
        // Clear button event
        this.clearButton.addEventListener('click', () => {
            this.clearSearch();
        });
        
        // Show/hide clear button based on input
        this.searchInput.addEventListener('input', (e) => {
            this.clearButton.style.display = e.target.value ? 'block' : 'none';
        });
    }
    
    handleSearch(query) {
        // Clear previous debounce timer
        if (this.debounceTimer) {
            clearTimeout(this.debounceTimer);
        }
        
        // Debounce search execution
        this.debounceTimer = setTimeout(() => {
            this.performSearch(query);
        }, this.options.debounceDelay);
    }
    
    performSearch(query) {
        const searchTerm = this.options.caseSensitive ? query : query.toLowerCase();
        
        if (!searchTerm.trim()) {
            this.showAllRows();
            this.removeNoResultsMessage();
            this.clearHighlights();
            return;
        }
        
        let visibleCount = 0;
        
        this.originalRows.forEach(rowData => {
            const matches = this.searchInRow(rowData, searchTerm);
            
            if (matches.found) {
                this.showRow(rowData.element);
                this.highlightMatches(rowData.element, matches.positions);
                visibleCount++;
                rowData.visible = true;
            } else {
                this.hideRow(rowData.element);
                rowData.visible = false;
            }
        });
        
        // Handle no results
        if (visibleCount === 0) {
            this.showNoResultsMessage();
        } else {
            this.removeNoResultsMessage();
        }
        
        // Emit search event
        this.emitSearchEvent(searchTerm, visibleCount);
    }
    
    searchInRow(rowData, searchTerm) {
        const matches = {
            found: false,
            positions: []
        };
        
        const columnsToSearch = this.getSearchColumns();
        
        rowData.data.forEach((cell, columnIndex) => {
            if (columnsToSearch.includes(columnIndex)) {
                const cellText = cell.text;
                const matchIndex = cellText.indexOf(searchTerm);
                
                if (matchIndex !== -1) {
                    matches.found = true;
                    matches.positions.push({
                        columnIndex,
                        startIndex: matchIndex,
                        endIndex: matchIndex + searchTerm.length,
                        originalText: cellText
                    });
                }
            }
        });
        
        return matches;
    }
    
    getSearchColumns() {
        if (this.options.searchColumns === 'all') {
            return Array.from({length: this.originalRows[0]?.data.length || 0}, (_, i) => i);
        }
        return this.options.searchColumns;
    }
    
    highlightMatches(row, positions) {
        this.clearHighlights(row);
        
        positions.forEach(position => {
            const cell = row.children[position.columnIndex];
            if (cell) {
                const originalHTML = this.originalRows.find(r => r.element === row)
                    .data[position.columnIndex].html;
                
                const highlightedHTML = this.addHighlight(
                    originalHTML,
                    position.startIndex,
                    position.endIndex
                );
                
                cell.innerHTML = highlightedHTML;
            }
        });
    }
    
    addHighlight(html, startIndex, endIndex) {
        // Simple text highlighting - in production, use a proper HTML parser
        const text = html;
        return text.substring(0, startIndex) +
               `<mark class="${this.options.highlightClass}">` +
               text.substring(startIndex, endIndex) +
               '</mark>' +
               text.substring(endIndex);
    }
    
    clearHighlights(specificRow = null) {
        const rows = specificRow ? [specificRow] : this.originalRows.map(r => r.element);
        
        rows.forEach(row => {
            const highlights = row.querySelectorAll(`.${this.options.highlightClass}`);
            highlights.forEach(highlight => {
                const parent = highlight.parentNode;
                parent.replaceChild(
                    document.createTextNode(highlight.textContent),
                    highlight
                );
                parent.normalize();
            });
        });
    }
    
    showRow(row) {
        row.style.display = '';
        row.removeAttribute('aria-hidden');
    }
    
    hideRow(row) {
        row.style.display = 'none';
        row.setAttribute('aria-hidden', 'true');
    }
    
    showAllRows() {
        this.originalRows.forEach(rowData => {
            this.showRow(rowData.element);
            rowData.visible = true;
        });
    }
    
    showNoResultsMessage() {
        this.removeNoResultsMessage();
        
        const tbody = this.table.querySelector('tbody');
        const noResultsRow = document.createElement('tr');
        noResultsRow.className = 'no-results-row';
        
        const noResultsCell = document.createElement('td');
        noResultsCell.setAttribute('colspan', this.getColumnCount());
        noResultsCell.className = 'no-results-message';
        noResultsCell.textContent = this.options.noResultsMessage;
        
        noResultsRow.appendChild(noResultsCell);
        tbody.appendChild(noResultsRow);
    }
    
    removeNoResultsMessage() {
        const noResultsRow = this.table.querySelector('.no-results-row');
        if (noResultsRow) {
            noResultsRow.remove();
        }
    }
    
    getColumnCount() {
        const firstRow = this.table.querySelector('tr');
        return firstRow ? firstRow.children.length : 1;
    }
    
    clearSearch() {
        this.searchInput.value = '';
        this.clearButton.style.display = 'none';
        this.showAllRows();
        this.removeNoResultsMessage();
        this.clearHighlights();
        this.searchInput.focus();
        
        this.emitSearchEvent('', this.originalRows.length);
    }
    
    emitSearchEvent(query, resultCount) {
        const event = new CustomEvent('tableSearch', {
            detail: {
                query,
                resultCount,
                totalRows: this.originalRows.length
            }
        });
        
        this.table.dispatchEvent(event);
    }
    
    // Public API methods
    search(query) {
        this.searchInput.value = query;
        this.performSearch(query);
    }
    
    getVisibleRows() {
        return this.originalRows.filter(row => row.visible);
    }
    
    getSearchStats() {
        const visible = this.getVisibleRows().length;
        return {
            total: this.originalRows.length,
            visible: visible,
            hidden: this.originalRows.length - visible,
            searchTerm: this.searchInput.value
        };
    }
}

// Initialize search for all tables with search capability
document.addEventListener('DOMContentLoaded', function() {
    // Auto-initialize tables with data-searchable attribute
    const searchableTables = document.querySelectorAll('table[data-searchable]');
    
    searchableTables.forEach((table, index) => {
        const searchId = `table-search-${index}`;
        new MarkdownTableSearch(`#${table.id || table.className}`, {
            searchInputId: searchId
        });
    });
});

Advanced Filtering System

Implementing comprehensive filtering capabilities with multiple criteria:

// table-filter.js - Advanced filtering system
class MarkdownTableFilter {
    constructor(tableSelector, options = {}) {
        this.table = document.querySelector(tableSelector);
        this.options = {
            filterContainerId: 'table-filters',
            enableColumnFilters: true,
            enableGlobalSearch: true,
            enableDateFiltering: true,
            enableNumberFiltering: true,
            enableCategoryFiltering: true,
            filterPosition: 'top', // 'top' or 'bottom'
            ...options
        };
        
        this.filters = new Map();
        this.activeFilters = new Map();
        this.originalRows = [];
        this.columnTypes = new Map();
        
        this.init();
    }
    
    init() {
        if (!this.table) {
            console.error('Table not found');
            return;
        }
        
        this.analyzeTableStructure();
        this.cacheOriginalData();
        this.createFilterInterface();
        this.bindEvents();
    }
    
    analyzeTableStructure() {
        const headerRow = this.table.querySelector('thead tr, tr');
        if (!headerRow) return;
        
        const headers = Array.from(headerRow.querySelectorAll('th, td'));
        const firstDataRow = this.table.querySelector('tbody tr, tr:nth-child(2)');
        
        headers.forEach((header, index) => {
            const columnName = header.textContent.trim();
            const sampleData = firstDataRow?.children[index]?.textContent.trim() || '';
            const columnType = this.detectColumnType(sampleData, columnName);
            
            this.columnTypes.set(index, {
                name: columnName,
                type: columnType,
                index: index
            });
        });
    }
    
    detectColumnType(sampleData, columnName) {
        const name = columnName.toLowerCase();
        
        // Date detection
        if (name.includes('date') || name.includes('time') || /^\d{4}-\d{2}-\d{2}/.test(sampleData)) {
            return 'date';
        }
        
        // Number detection
        if (name.includes('price') || name.includes('amount') || name.includes('count') ||
            /^[\$£€]?[\d,]+\.?\d*$/.test(sampleData.replace(/[^\d.,\$£€]/g, ''))) {
            return 'number';
        }
        
        // Status/Category detection
        if (name.includes('status') || name.includes('category') || name.includes('type')) {
            return 'category';
        }
        
        // Boolean detection
        if (['yes', 'no', 'true', 'false', 'active', 'inactive'].includes(sampleData.toLowerCase())) {
            return 'boolean';
        }
        
        return 'text';
    }
    
    cacheOriginalData() {
        const tbody = this.table.querySelector('tbody');
        const rows = tbody ? tbody.querySelectorAll('tr') : this.table.querySelectorAll('tr:not(:first-child)');
        
        this.originalRows = Array.from(rows).map(row => ({
            element: row,
            data: this.extractRowData(row),
            visible: true,
            matchesFilters: true
        }));
    }
    
    extractRowData(row) {
        return Array.from(row.children).map((cell, index) => {
            const text = cell.textContent.trim();
            const columnInfo = this.columnTypes.get(index);
            
            return {
                raw: text,
                processed: this.processDataByType(text, columnInfo?.type || 'text'),
                type: columnInfo?.type || 'text'
            };
        });
    }
    
    processDataByType(value, type) {
        switch (type) {
            case 'number':
                const numValue = parseFloat(value.replace(/[^\d.-]/g, ''));
                return isNaN(numValue) ? 0 : numValue;
            
            case 'date':
                const dateValue = new Date(value);
                return isNaN(dateValue.getTime()) ? null : dateValue;
            
            case 'boolean':
                return ['yes', 'true', 'active', '1'].includes(value.toLowerCase());
            
            default:
                return value.toLowerCase();
        }
    }
    
    createFilterInterface() {
        const filterContainer = this.createFilterContainer();
        
        if (this.options.enableGlobalSearch) {
            this.createGlobalSearchFilter(filterContainer);
        }
        
        if (this.options.enableColumnFilters) {
            this.createColumnFilters(filterContainer);
        }
        
        this.createFilterControls(filterContainer);
        
        // Position filter container
        if (this.options.filterPosition === 'bottom') {
            this.table.parentNode.insertBefore(filterContainer, this.table.nextSibling);
        } else {
            this.table.parentNode.insertBefore(filterContainer, this.table);
        }
    }
    
    createFilterContainer() {
        const container = document.createElement('div');
        container.id = this.options.filterContainerId;
        container.className = 'table-filter-container';
        container.setAttribute('role', 'search');
        container.setAttribute('aria-label', 'Table filtering controls');
        
        return container;
    }
    
    createGlobalSearchFilter(container) {
        const searchSection = document.createElement('div');
        searchSection.className = 'filter-section global-search';
        
        const searchLabel = document.createElement('label');
        searchLabel.textContent = 'Search: ';
        searchLabel.className = 'filter-label';
        
        const searchInput = document.createElement('input');
        searchInput.type = 'text';
        searchInput.className = 'filter-input global-search-input';
        searchInput.placeholder = 'Search all columns...';
        searchInput.setAttribute('aria-label', 'Search table content');
        
        searchLabel.appendChild(searchInput);
        searchSection.appendChild(searchLabel);
        container.appendChild(searchSection);
        
        // Store filter reference
        this.filters.set('global-search', {
            type: 'global-search',
            element: searchInput,
            value: ''
        });
        
        // Bind search event
        let searchTimeout;
        searchInput.addEventListener('input', (e) => {
            clearTimeout(searchTimeout);
            searchTimeout = setTimeout(() => {
                this.updateFilter('global-search', e.target.value);
            }, 300);
        });
    }
    
    createColumnFilters(container) {
        const filtersSection = document.createElement('div');
        filtersSection.className = 'filter-section column-filters';
        
        const filtersTitle = document.createElement('h4');
        filtersTitle.textContent = 'Column Filters:';
        filtersTitle.className = 'filters-title';
        filtersSection.appendChild(filtersTitle);
        
        const filtersGrid = document.createElement('div');
        filtersGrid.className = 'filters-grid';
        
        this.columnTypes.forEach((columnInfo, index) => {
            const filterElement = this.createColumnFilter(columnInfo);
            if (filterElement) {
                filtersGrid.appendChild(filterElement);
            }
        });
        
        filtersSection.appendChild(filtersGrid);
        container.appendChild(filtersSection);
    }
    
    createColumnFilter(columnInfo) {
        const { name, type, index } = columnInfo;
        const filterWrapper = document.createElement('div');
        filterWrapper.className = `filter-column filter-${type}`;
        
        const label = document.createElement('label');
        label.textContent = name + ': ';
        label.className = 'filter-label';
        
        let filterElement;
        
        switch (type) {
            case 'category':
                filterElement = this.createCategoryFilter(index);
                break;
            case 'number':
                filterElement = this.createNumberRangeFilter(index);
                break;
            case 'date':
                filterElement = this.createDateRangeFilter(index);
                break;
            case 'boolean':
                filterElement = this.createBooleanFilter(index);
                break;
            default:
                filterElement = this.createTextFilter(index);
        }
        
        if (filterElement) {
            if (Array.isArray(filterElement)) {
                filterElement.forEach(el => label.appendChild(el));
            } else {
                label.appendChild(filterElement);
            }
            filterWrapper.appendChild(label);
        }
        
        return filterElement ? filterWrapper : null;
    }
    
    createCategoryFilter(columnIndex) {
        const uniqueValues = this.getUniqueValuesForColumn(columnIndex);
        
        const select = document.createElement('select');
        select.className = 'filter-input category-filter';
        select.setAttribute('aria-label', `Filter by ${this.columnTypes.get(columnIndex).name}`);
        
        // Add default option
        const defaultOption = document.createElement('option');
        defaultOption.value = '';
        defaultOption.textContent = 'All';
        select.appendChild(defaultOption);
        
        // Add unique values
        uniqueValues.forEach(value => {
            const option = document.createElement('option');
            option.value = value;
            option.textContent = value;
            select.appendChild(option);
        });
        
        // Store filter reference
        this.filters.set(`column-${columnIndex}`, {
            type: 'category',
            element: select,
            columnIndex: columnIndex,
            value: ''
        });
        
        // Bind change event
        select.addEventListener('change', (e) => {
            this.updateFilter(`column-${columnIndex}`, e.target.value);
        });
        
        return select;
    }
    
    createNumberRangeFilter(columnIndex) {
        const { min, max } = this.getNumericRangeForColumn(columnIndex);
        
        const container = document.createElement('div');
        container.className = 'range-filter-container';
        
        const minInput = document.createElement('input');
        minInput.type = 'number';
        minInput.className = 'filter-input range-min';
        minInput.placeholder = `Min (${min})`;
        minInput.min = min;
        minInput.max = max;
        minInput.step = this.getStepValue(min, max);
        
        const maxInput = document.createElement('input');
        maxInput.type = 'number';
        maxInput.className = 'filter-input range-max';
        maxInput.placeholder = `Max (${max})`;
        maxInput.min = min;
        maxInput.max = max;
        maxInput.step = this.getStepValue(min, max);
        
        container.appendChild(minInput);
        container.appendChild(document.createTextNode(' - '));
        container.appendChild(maxInput);
        
        // Store filter reference
        this.filters.set(`column-${columnIndex}`, {
            type: 'number-range',
            element: { min: minInput, max: maxInput },
            columnIndex: columnIndex,
            value: { min: null, max: null }
        });
        
        // Bind change events
        const updateRange = () => {
            const minVal = minInput.value ? parseFloat(minInput.value) : null;
            const maxVal = maxInput.value ? parseFloat(maxInput.value) : null;
            this.updateFilter(`column-${columnIndex}`, { min: minVal, max: maxVal });
        };
        
        minInput.addEventListener('input', updateRange);
        maxInput.addEventListener('input', updateRange);
        
        return container;
    }
    
    createDateRangeFilter(columnIndex) {
        const container = document.createElement('div');
        container.className = 'date-filter-container';
        
        const fromInput = document.createElement('input');
        fromInput.type = 'date';
        fromInput.className = 'filter-input date-from';
        
        const toInput = document.createElement('input');
        toInput.type = 'date';
        toInput.className = 'filter-input date-to';
        
        container.appendChild(fromInput);
        container.appendChild(document.createTextNode(' to '));
        container.appendChild(toInput);
        
        // Store filter reference
        this.filters.set(`column-${columnIndex}`, {
            type: 'date-range',
            element: { from: fromInput, to: toInput },
            columnIndex: columnIndex,
            value: { from: null, to: null }
        });
        
        // Bind change events
        const updateDateRange = () => {
            const fromVal = fromInput.value ? new Date(fromInput.value) : null;
            const toVal = toInput.value ? new Date(toInput.value) : null;
            this.updateFilter(`column-${columnIndex}`, { from: fromVal, to: toVal });
        };
        
        fromInput.addEventListener('change', updateDateRange);
        toInput.addEventListener('change', updateDateRange);
        
        return container;
    }
    
    createBooleanFilter(columnIndex) {
        const select = document.createElement('select');
        select.className = 'filter-input boolean-filter';
        
        const options = [
            { value: '', text: 'All' },
            { value: 'true', text: 'Yes/True/Active' },
            { value: 'false', text: 'No/False/Inactive' }
        ];
        
        options.forEach(option => {
            const optionElement = document.createElement('option');
            optionElement.value = option.value;
            optionElement.textContent = option.text;
            select.appendChild(optionElement);
        });
        
        // Store filter reference
        this.filters.set(`column-${columnIndex}`, {
            type: 'boolean',
            element: select,
            columnIndex: columnIndex,
            value: ''
        });
        
        // Bind change event
        select.addEventListener('change', (e) => {
            this.updateFilter(`column-${columnIndex}`, e.target.value);
        });
        
        return select;
    }
    
    createTextFilter(columnIndex) {
        const input = document.createElement('input');
        input.type = 'text';
        input.className = 'filter-input text-filter';
        input.placeholder = 'Filter text...';
        input.setAttribute('aria-label', `Filter ${this.columnTypes.get(columnIndex).name}`);
        
        // Store filter reference
        this.filters.set(`column-${columnIndex}`, {
            type: 'text',
            element: input,
            columnIndex: columnIndex,
            value: ''
        });
        
        // Bind input event with debounce
        let textTimeout;
        input.addEventListener('input', (e) => {
            clearTimeout(textTimeout);
            textTimeout = setTimeout(() => {
                this.updateFilter(`column-${columnIndex}`, e.target.value);
            }, 300);
        });
        
        return input;
    }
    
    createFilterControls(container) {
        const controlsSection = document.createElement('div');
        controlsSection.className = 'filter-controls';
        
        const clearButton = document.createElement('button');
        clearButton.type = 'button';
        clearButton.className = 'filter-button clear-filters';
        clearButton.textContent = 'Clear All Filters';
        clearButton.addEventListener('click', () => this.clearAllFilters());
        
        const resultCount = document.createElement('span');
        resultCount.className = 'filter-result-count';
        resultCount.setAttribute('aria-live', 'polite');
        
        controlsSection.appendChild(clearButton);
        controlsSection.appendChild(resultCount);
        container.appendChild(controlsSection);
        
        this.resultCountElement = resultCount;
        this.updateResultCount();
    }
    
    getUniqueValuesForColumn(columnIndex) {
        const values = new Set();
        this.originalRows.forEach(row => {
            const cellValue = row.data[columnIndex]?.raw || '';
            if (cellValue) {
                values.add(cellValue);
            }
        });
        return Array.from(values).sort();
    }
    
    getNumericRangeForColumn(columnIndex) {
        let min = Infinity;
        let max = -Infinity;
        
        this.originalRows.forEach(row => {
            const cellValue = row.data[columnIndex]?.processed;
            if (typeof cellValue === 'number' && !isNaN(cellValue)) {
                min = Math.min(min, cellValue);
                max = Math.max(max, cellValue);
            }
        });
        
        return {
            min: min === Infinity ? 0 : min,
            max: max === -Infinity ? 100 : max
        };
    }
    
    getStepValue(min, max) {
        const range = max - min;
        if (range > 1000) return 10;
        if (range > 100) return 1;
        if (range > 10) return 0.1;
        return 0.01;
    }
    
    updateFilter(filterKey, value) {
        this.activeFilters.set(filterKey, value);
        this.applyFilters();
    }
    
    applyFilters() {
        let visibleCount = 0;
        
        this.originalRows.forEach(row => {
            const matches = this.rowMatchesAllFilters(row);
            
            if (matches) {
                this.showRow(row.element);
                row.visible = true;
                row.matchesFilters = true;
                visibleCount++;
            } else {
                this.hideRow(row.element);
                row.visible = false;
                row.matchesFilters = false;
            }
        });
        
        this.updateResultCount();
        this.emitFilterEvent(visibleCount);
    }
    
    rowMatchesAllFilters(row) {
        for (const [filterKey, filterValue] of this.activeFilters.entries()) {
            if (!filterValue && filterValue !== 0) continue; // Skip empty filters
            
            if (!this.rowMatchesFilter(row, filterKey, filterValue)) {
                return false;
            }
        }
        return true;
    }
    
    rowMatchesFilter(row, filterKey, filterValue) {
        if (filterKey === 'global-search') {
            return this.rowMatchesGlobalSearch(row, filterValue);
        }
        
        const match = filterKey.match(/column-(\d+)/);
        if (!match) return true;
        
        const columnIndex = parseInt(match[1]);
        const cellData = row.data[columnIndex];
        const filter = this.filters.get(filterKey);
        
        switch (filter.type) {
            case 'category':
                return cellData.raw === filterValue;
            
            case 'text':
                return cellData.processed.includes(filterValue.toLowerCase());
            
            case 'number-range':
                const numValue = cellData.processed;
                const { min, max } = filterValue;
                return (!min || numValue >= min) && (!max || numValue <= max);
            
            case 'date-range':
                const dateValue = cellData.processed;
                if (!dateValue) return false;
                const { from, to } = filterValue;
                return (!from || dateValue >= from) && (!to || dateValue <= to);
            
            case 'boolean':
                const boolValue = cellData.processed;
                return filterValue === '' || boolValue === (filterValue === 'true');
            
            default:
                return true;
        }
    }
    
    rowMatchesGlobalSearch(row, searchTerm) {
        const lowerSearchTerm = searchTerm.toLowerCase();
        return row.data.some(cell => 
            cell.raw.toLowerCase().includes(lowerSearchTerm)
        );
    }
    
    showRow(row) {
        row.style.display = '';
        row.removeAttribute('aria-hidden');
    }
    
    hideRow(row) {
        row.style.display = 'none';
        row.setAttribute('aria-hidden', 'true');
    }
    
    clearAllFilters() {
        this.activeFilters.clear();
        
        // Reset all filter inputs
        this.filters.forEach(filter => {
            switch (filter.type) {
                case 'global-search':
                case 'text':
                case 'category':
                case 'boolean':
                    filter.element.value = '';
                    break;
                case 'number-range':
                    filter.element.min.value = '';
                    filter.element.max.value = '';
                    break;
                case 'date-range':
                    filter.element.from.value = '';
                    filter.element.to.value = '';
                    break;
            }
        });
        
        this.applyFilters();
    }
    
    updateResultCount() {
        const visibleRows = this.originalRows.filter(row => row.visible).length;
        const totalRows = this.originalRows.length;
        
        this.resultCountElement.textContent = 
            `Showing ${visibleRows} of ${totalRows} rows`;
    }
    
    emitFilterEvent(visibleCount) {
        const event = new CustomEvent('tableFilter', {
            detail: {
                visibleCount,
                totalCount: this.originalRows.length,
                activeFilters: Object.fromEntries(this.activeFilters)
            }
        });
        
        this.table.dispatchEvent(event);
    }
    
    // Public API
    getFilteredRows() {
        return this.originalRows.filter(row => row.visible);
    }
    
    exportFilteredData() {
        const headers = Array.from(this.columnTypes.values()).map(col => col.name);
        const rows = this.getFilteredRows().map(row => 
            row.data.map(cell => cell.raw)
        );
        
        return { headers, rows };
    }
    
    setFilter(filterKey, value) {
        const filter = this.filters.get(filterKey);
        if (filter) {
            switch (filter.type) {
                case 'category':
                case 'boolean':
                    filter.element.value = value;
                    break;
                case 'text':
                case 'global-search':
                    filter.element.value = value;
                    break;
                case 'number-range':
                    if (value.min !== undefined) filter.element.min.value = value.min;
                    if (value.max !== undefined) filter.element.max.value = value.max;
                    break;
                case 'date-range':
                    if (value.from) filter.element.from.value = value.from;
                    if (value.to) filter.element.to.value = value.to;
                    break;
            }
            this.updateFilter(filterKey, value);
        }
    }
}

// Auto-initialize filterable tables
document.addEventListener('DOMContentLoaded', function() {
    const filterableTables = document.querySelectorAll('table[data-filterable]');
    
    filterableTables.forEach((table, index) => {
        new MarkdownTableFilter(`#${table.id || 'table-' + index}`);
    });
});

Responsive Search Interface

Mobile-Optimized Search Controls

/* responsive-table-search.css - Mobile-first search styling */

/* Base search styling */
.table-search-container {
    position: relative;
    margin-bottom: 1rem;
    background: #f8f9fa;
    padding: 12px;
    border-radius: 8px;
    border: 1px solid #e9ecef;
}

.table-search-input {
    width: 100%;
    padding: 12px 40px 12px 40px;
    border: 2px solid #e9ecef;
    border-radius: 6px;
    font-size: 16px;
    font-family: inherit;
    background: white;
    transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.table-search-input:focus {
    outline: none;
    border-color: #007bff;
    box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1);
}

.search-icon {
    position: absolute;
    left: 24px;
    top: 50%;
    transform: translateY(-50%);
    color: #6c757d;
    font-size: 16px;
    pointer-events: none;
}

.search-clear {
    position: absolute;
    right: 24px;
    top: 50%;
    transform: translateY(-50%);
    background: none;
    border: none;
    color: #6c757d;
    cursor: pointer;
    font-size: 18px;
    width: 24px;
    height: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    transition: background-color 0.2s ease;
}

.search-clear:hover {
    background-color: #e9ecef;
    color: #495057;
}

.search-clear:focus {
    outline: 2px solid #007bff;
    outline-offset: 2px;
}

/* Filter container styling */
.table-filter-container {
    background: white;
    border: 1px solid #e9ecef;
    border-radius: 8px;
    padding: 16px;
    margin-bottom: 1rem;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.filter-section {
    margin-bottom: 16px;
}

.filter-section:last-child {
    margin-bottom: 0;
}

.filters-title {
    margin: 0 0 12px 0;
    font-size: 16px;
    font-weight: 600;
    color: #495057;
}

.filters-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 12px;
}

.filter-column {
    display: flex;
    flex-direction: column;
}

.filter-label {
    font-weight: 500;
    margin-bottom: 4px;
    color: #495057;
    font-size: 14px;
}

.filter-input {
    padding: 8px 12px;
    border: 1px solid #ced4da;
    border-radius: 4px;
    font-size: 14px;
    background: white;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.filter-input:focus {
    outline: none;
    border-color: #007bff;
    box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.1);
}

/* Range filter styling */
.range-filter-container,
.date-filter-container {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.range-filter-container .filter-input,
.date-filter-container .filter-input {
    flex: 1;
    min-width: 100px;
}

/* Filter controls */
.filter-controls {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 16px;
    padding-top: 16px;
    border-top: 1px solid #e9ecef;
    flex-wrap: wrap;
    gap: 12px;
}

.filter-button {
    padding: 8px 16px;
    background: #007bff;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 14px;
    font-weight: 500;
    transition: background-color 0.2s ease;
}

.filter-button:hover {
    background: #0056b3;
}

.filter-button:focus {
    outline: 2px solid #007bff;
    outline-offset: 2px;
}

.filter-button.clear-filters {
    background: #6c757d;
}

.filter-button.clear-filters:hover {
    background: #545b62;
}

.filter-result-count {
    font-size: 14px;
    color: #6c757d;
    font-weight: 500;
}

/* Search highlighting */
.search-highlight {
    background: #fff3cd;
    color: #856404;
    padding: 1px 2px;
    border-radius: 2px;
    font-weight: 500;
}

/* No results message */
.no-results-row {
    background: #f8f9fa;
}

.no-results-message {
    text-align: center;
    padding: 24px;
    color: #6c757d;
    font-style: italic;
}

/* Responsive design */
@media (min-width: 576px) {
    .filters-grid {
        grid-template-columns: repeat(2, 1fr);
    }
    
    .filter-column {
        flex-direction: row;
        align-items: center;
        gap: 8px;
    }
    
    .filter-label {
        margin-bottom: 0;
        min-width: 120px;
    }
}

@media (min-width: 768px) {
    .filters-grid {
        grid-template-columns: repeat(3, 1fr);
    }
    
    .range-filter-container,
    .date-filter-container {
        flex-wrap: nowrap;
    }
}

@media (min-width: 992px) {
    .filters-grid {
        grid-template-columns: repeat(4, 1fr);
    }
    
    .table-search-input {
        font-size: 14px;
    }
}

/* High contrast mode */
@media (prefers-contrast: high) {
    .table-search-container,
    .table-filter-container {
        border: 2px solid #000;
        background: #fff;
    }
    
    .filter-input,
    .table-search-input {
        border: 2px solid #000;
    }
    
    .filter-input:focus,
    .table-search-input:focus {
        box-shadow: 0 0 0 3px #ffff00;
        border-color: #000;
    }
    
    .search-highlight {
        background: #ffff00;
        color: #000;
    }
}

/* Reduced motion preferences */
@media (prefers-reduced-motion: reduce) {
    .filter-input,
    .table-search-input,
    .filter-button,
    .search-clear {
        transition: none;
    }
}

/* Focus management for keyboard navigation */
.filter-container:focus-within {
    outline: 2px solid #007bff;
    outline-offset: 4px;
}

/* Print styles */
@media print {
    .table-search-container,
    .table-filter-container {
        display: none;
    }
}

Progressive Enhancement

<!-- progressive-table-enhancement.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Enhanced Markdown Table Example</title>
    <link rel="stylesheet" href="responsive-table-search.css">
</head>
<body>
    <!-- Basic Markdown table that works without JavaScript -->
    <table id="product-table" class="markdown-table" data-searchable data-filterable>
        <thead>
            <tr>
                <th>Product Name</th>
                <th>Category</th>
                <th>Price</th>
                <th>Stock</th>
                <th>Status</th>
                <th>Release Date</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>Wireless Headphones Pro</td>
                <td>Electronics</td>
                <td>$199.99</td>
                <td>45</td>
                <td>Active</td>
                <td>2024-01-15</td>
            </tr>
            <tr>
                <td>Office Chair Deluxe</td>
                <td>Furniture</td>
                <td>$399.00</td>
                <td>12</td>
                <td>Limited</td>
                <td>2023-11-22</td>
            </tr>
            <tr>
                <td>Laptop Stand Adjustable</td>
                <td>Accessories</td>
                <td>$89.95</td>
                <td>0</td>
                <td>Inactive</td>
                <td>2024-03-08</td>
            </tr>
            <tr>
                <td>Desk Organizer Premium</td>
                <td>Office Supplies</td>
                <td>$49.99</td>
                <td>28</td>
                <td>Active</td>
                <td>2024-05-12</td>
            </tr>
            <tr>
                <td>Monitor Arm Dual</td>
                <td>Accessories</td>
                <td>$159.95</td>
                <td>18</td>
                <td>Active</td>
                <td>2024-02-29</td>
            </tr>
            <tr>
                <td>Keyboard Mechanical RGB</td>
                <td>Electronics</td>
                <td>$129.99</td>
                <td>35</td>
                <td>Active</td>
                <td>2023-12-10</td>
            </tr>
        </tbody>
    </table>

    <!-- Enhanced features loaded progressively -->
    <script>
        // Feature detection and progressive enhancement
        if ('querySelector' in document && 'addEventListener' in window) {
            // Load enhanced functionality
            Promise.all([
                import('./table-search.js'),
                import('./table-filter.js')
            ]).then(([SearchModule, FilterModule]) => {
                // Initialize search functionality
                new SearchModule.MarkdownTableSearch('#product-table', {
                    highlightClass: 'search-highlight',
                    debounceDelay: 250
                });

                // Initialize filtering functionality  
                new FilterModule.MarkdownTableFilter('#product-table', {
                    enableGlobalSearch: false, // Using separate search
                    enableColumnFilters: true,
                    filterPosition: 'top'
                });

                // Add export functionality
                addExportFeatures();
            }).catch(error => {
                console.log('Enhanced features not available:', error);
                // Fallback to basic functionality
            });
        }

        function addExportFeatures() {
            const table = document.getElementById('product-table');
            const filterContainer = document.querySelector('.table-filter-container');
            
            if (!filterContainer) return;

            const exportButton = document.createElement('button');
            exportButton.textContent = 'Export Filtered Data';
            exportButton.className = 'filter-button export-button';
            exportButton.addEventListener('click', exportFilteredData);

            const controls = filterContainer.querySelector('.filter-controls');
            if (controls) {
                controls.appendChild(exportButton);
            }
        }

        function exportFilteredData() {
            // Get the filter instance
            const table = document.getElementById('product-table');
            const filterEvent = new CustomEvent('getFilteredData');
            table.dispatchEvent(filterEvent);

            // This is a simplified export - in production, use a proper library
            const visibleRows = Array.from(table.querySelectorAll('tbody tr'))
                .filter(row => row.style.display !== 'none');

            const csvContent = generateCSV(visibleRows);
            downloadCSV(csvContent, 'filtered-table-data.csv');
        }

        function generateCSV(rows) {
            const headers = Array.from(document.querySelectorAll('#product-table thead th'))
                .map(th => `"${th.textContent.trim()}"`);

            const csvRows = [headers.join(',')];

            rows.forEach(row => {
                const cells = Array.from(row.querySelectorAll('td'))
                    .map(td => `"${td.textContent.trim().replace(/"/g, '""')}"`);
                csvRows.push(cells.join(','));
            });

            return csvRows.join('\n');
        }

        function downloadCSV(content, filename) {
            const blob = new Blob([content], { type: 'text/csv;charset=utf-8;' });
            const link = document.createElement('a');
            
            if (link.download !== undefined) {
                const url = URL.createObjectURL(blob);
                link.setAttribute('href', url);
                link.setAttribute('download', filename);
                link.style.visibility = 'hidden';
                document.body.appendChild(link);
                link.click();
                document.body.removeChild(link);
            }
        }

        // Accessibility enhancements
        document.addEventListener('DOMContentLoaded', function() {
            // Add skip link for keyboard users
            const skipLink = document.createElement('a');
            skipLink.href = '#product-table';
            skipLink.textContent = 'Skip to table';
            skipLink.className = 'skip-link';
            skipLink.style.cssText = `
                position: absolute;
                left: -9999px;
                background: #000;
                color: #fff;
                padding: 8px;
                text-decoration: none;
                z-index: 1000;
            `;
            
            skipLink.addEventListener('focus', () => {
                skipLink.style.left = '0';
            });
            
            skipLink.addEventListener('blur', () => {
                skipLink.style.left = '-9999px';
            });
            
            document.body.insertBefore(skipLink, document.body.firstChild);

            // Add table caption for screen readers
            const table = document.getElementById('product-table');
            const caption = document.createElement('caption');
            caption.textContent = 'Product inventory with search and filtering capabilities';
            caption.style.cssText = `
                position: absolute;
                left: -9999px;
                width: 1px;
                height: 1px;
                overflow: hidden;
            `;
            table.insertBefore(caption, table.firstChild);
        });
    </script>
</body>
</html>

Advanced Search Features

Fuzzy Search Implementation

// fuzzy-search.js - Advanced fuzzy search capabilities
class FuzzyTableSearch {
    constructor(tableSelector, options = {}) {
        this.table = document.querySelector(tableSelector);
        this.options = {
            threshold: 0.6, // Similarity threshold (0-1)
            maxDistance: 3, // Maximum edit distance
            enableSoundex: true, // Phonetic matching
            enableSynonyms: false,
            synonyms: new Map(), // Word synonyms
            ...options
        };
        
        this.searchIndex = new Map();
        this.originalRows = [];
        
        this.init();
    }
    
    init() {
        if (!this.table) return;
        
        this.buildSearchIndex();
        this.createSearchInterface();
    }
    
    buildSearchIndex() {
        const rows = this.table.querySelectorAll('tbody tr');
        
        rows.forEach((row, rowIndex) => {
            const cells = Array.from(row.querySelectorAll('td'));
            const searchableText = cells.map(cell => cell.textContent.trim()).join(' ');
            
            this.originalRows.push({
                element: row,
                text: searchableText,
                tokens: this.tokenize(searchableText),
                soundex: this.options.enableSoundex ? this.soundex(searchableText) : null
            });
        });
    }
    
    createSearchInterface() {
        const searchContainer = document.createElement('div');
        searchContainer.className = 'fuzzy-search-container';
        
        const searchInput = document.createElement('input');
        searchInput.type = 'text';
        searchInput.className = 'fuzzy-search-input';
        searchInput.placeholder = 'Fuzzy search (try typos!)...';
        
        const resultsInfo = document.createElement('div');
        resultsInfo.className = 'search-results-info';
        
        const suggestionsList = document.createElement('div');
        suggestionsList.className = 'search-suggestions';
        suggestionsList.style.display = 'none';
        
        searchContainer.appendChild(searchInput);
        searchContainer.appendChild(suggestionsList);
        searchContainer.appendChild(resultsInfo);
        
        this.table.parentNode.insertBefore(searchContainer, this.table);
        
        this.searchInput = searchInput;
        this.resultsInfo = resultsInfo;
        this.suggestionsList = suggestionsList;
        
        this.bindSearchEvents();
        this.updateResultsInfo();
    }
    
    bindSearchEvents() {
        let searchTimeout;
        
        this.searchInput.addEventListener('input', (e) => {
            clearTimeout(searchTimeout);
            searchTimeout = setTimeout(() => {
                this.performFuzzySearch(e.target.value);
            }, 300);
        });
        
        this.searchInput.addEventListener('keydown', (e) => {
            if (e.key === 'ArrowDown') {
                this.navigateSuggestions(1);
                e.preventDefault();
            } else if (e.key === 'ArrowUp') {
                this.navigateSuggestions(-1);
                e.preventDefault();
            } else if (e.key === 'Enter') {
                this.selectActiveSuggestion();
                e.preventDefault();
            } else if (e.key === 'Escape') {
                this.hideSuggestions();
            }
        });
    }
    
    performFuzzySearch(query) {
        if (!query.trim()) {
            this.showAllRows();
            this.hideSuggestions();
            this.updateResultsInfo();
            return;
        }
        
        const results = this.fuzzyMatch(query);
        this.displayResults(results);
        this.showSuggestions(query, results);
        this.updateResultsInfo(results.length, query);
    }
    
    fuzzyMatch(query) {
        const queryTokens = this.tokenize(query);
        const querySoundex = this.options.enableSoundex ? this.soundex(query) : null;
        
        const results = this.originalRows.map((row, index) => {
            const scores = [];
            
            // Token-based matching
            const tokenScore = this.calculateTokenScore(queryTokens, row.tokens);
            scores.push({ score: tokenScore, type: 'token' });
            
            // Levenshtein distance
            const editScore = this.calculateEditScore(query, row.text);
            scores.push({ score: editScore, type: 'edit' });
            
            // Soundex matching
            if (querySoundex && row.soundex) {
                const soundexScore = querySoundex === row.soundex ? 1 : 0;
                scores.push({ score: soundexScore, type: 'soundex' });
            }
            
            // Substring matching
            const substringScore = this.calculateSubstringScore(query, row.text);
            scores.push({ score: substringScore, type: 'substring' });
            
            // Calculate weighted average
            const finalScore = this.calculateWeightedScore(scores);
            
            return {
                row: row,
                score: finalScore,
                index: index,
                details: scores
            };
        }).filter(result => result.score >= this.options.threshold)
          .sort((a, b) => b.score - a.score);
        
        return results;
    }
    
    calculateTokenScore(queryTokens, rowTokens) {
        if (queryTokens.length === 0 || rowTokens.length === 0) return 0;
        
        let matchCount = 0;
        queryTokens.forEach(qToken => {
            const bestMatch = Math.max(...rowTokens.map(rToken => 
                this.stringSimilarity(qToken, rToken)
            ));
            if (bestMatch > 0.7) matchCount++;
        });
        
        return matchCount / queryTokens.length;
    }
    
    calculateEditScore(query, text) {
        const distance = this.levenshteinDistance(
            query.toLowerCase(), 
            text.toLowerCase().substring(0, query.length + this.options.maxDistance)
        );
        
        const maxLength = Math.max(query.length, text.length);
        return 1 - (distance / maxLength);
    }
    
    calculateSubstringScore(query, text) {
        const lowerQuery = query.toLowerCase();
        const lowerText = text.toLowerCase();
        
        if (lowerText.includes(lowerQuery)) {
            // Bonus for exact substring match
            return 0.9;
        }
        
        // Partial word matching
        const words = lowerText.split(/\s+/);
        const matchingWords = words.filter(word => 
            word.includes(lowerQuery) || lowerQuery.includes(word)
        );
        
        return matchingWords.length / words.length;
    }
    
    calculateWeightedScore(scores) {
        const weights = {
            token: 0.4,
            edit: 0.3,
            substring: 0.2,
            soundex: 0.1
        };
        
        let weightedSum = 0;
        let totalWeight = 0;
        
        scores.forEach(({ score, type }) => {
            const weight = weights[type] || 0.1;
            weightedSum += score * weight;
            totalWeight += weight;
        });
        
        return totalWeight > 0 ? weightedSum / totalWeight : 0;
    }
    
    tokenize(text) {
        return text.toLowerCase()
                  .replace(/[^\w\s]/g, ' ')
                  .split(/\s+/)
                  .filter(token => token.length > 0);
    }
    
    soundex(text) {
        // Simplified soundex algorithm
        const tokens = this.tokenize(text);
        return tokens.map(token => {
            let soundex = token.charAt(0).toUpperCase();
            
            const mapping = {
                'bfpv': '1', 'cgjkqsxz': '2', 'dt': '3',
                'l': '4', 'mn': '5', 'r': '6'
            };
            
            for (let i = 1; i < token.length; i++) {
                const char = token.charAt(i).toLowerCase();
                for (const [chars, code] of Object.entries(mapping)) {
                    if (chars.includes(char)) {
                        if (soundex.slice(-1) !== code) {
                            soundex += code;
                        }
                        break;
                    }
                }
                if (soundex.length >= 4) break;
            }
            
            return (soundex + '000').substring(0, 4);
        }).join('');
    }
    
    levenshteinDistance(str1, str2) {
        const matrix = Array(str2.length + 1).fill().map(() => 
            Array(str1.length + 1).fill(0)
        );
        
        for (let i = 0; i <= str1.length; i++) matrix[0][i] = i;
        for (let j = 0; j <= str2.length; j++) matrix[j][0] = j;
        
        for (let j = 1; j <= str2.length; j++) {
            for (let i = 1; i <= str1.length; i++) {
                const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
                matrix[j][i] = Math.min(
                    matrix[j - 1][i] + 1,     // deletion
                    matrix[j][i - 1] + 1,     // insertion
                    matrix[j - 1][i - 1] + cost // substitution
                );
            }
        }
        
        return matrix[str2.length][str1.length];
    }
    
    stringSimilarity(str1, str2) {
        const longer = str1.length > str2.length ? str1 : str2;
        const shorter = str1.length > str2.length ? str2 : str1;
        
        if (longer.length === 0) return 1.0;
        
        const editDistance = this.levenshteinDistance(longer, shorter);
        return (longer.length - editDistance) / longer.length;
    }
    
    displayResults(results) {
        this.originalRows.forEach((row, index) => {
            const isVisible = results.some(result => result.index === index);
            
            if (isVisible) {
                row.element.style.display = '';
                row.element.removeAttribute('aria-hidden');
            } else {
                row.element.style.display = 'none';
                row.element.setAttribute('aria-hidden', 'true');
            }
        });
    }
    
    showSuggestions(query, results) {
        if (results.length === 0 || results.length === this.originalRows.length) {
            this.hideSuggestions();
            return;
        }
        
        // Generate search suggestions based on partial matches
        const suggestions = this.generateSuggestions(query, results);
        
        if (suggestions.length > 0) {
            this.renderSuggestions(suggestions);
            this.suggestionsList.style.display = 'block';
        } else {
            this.hideSuggestions();
        }
    }
    
    generateSuggestions(query, results) {
        const suggestions = new Set();
        
        results.slice(0, 5).forEach(result => {
            // Extract words that might be good suggestions
            const words = result.row.tokens;
            words.forEach(word => {
                if (word.length > 2 && 
                    this.stringSimilarity(query, word) > 0.5 &&
                    word !== query.toLowerCase()) {
                    suggestions.add(word);
                }
            });
        });
        
        return Array.from(suggestions).slice(0, 5);
    }
    
    renderSuggestions(suggestions) {
        this.suggestionsList.innerHTML = '';
        
        suggestions.forEach((suggestion, index) => {
            const suggestionElement = document.createElement('div');
            suggestionElement.className = 'search-suggestion';
            suggestionElement.textContent = suggestion;
            suggestionElement.setAttribute('role', 'option');
            suggestionElement.setAttribute('tabindex', '-1');
            
            suggestionElement.addEventListener('click', () => {
                this.selectSuggestion(suggestion);
            });
            
            this.suggestionsList.appendChild(suggestionElement);
        });
        
        this.activeSuggestionIndex = -1;
    }
    
    navigateSuggestions(direction) {
        const suggestions = this.suggestionsList.querySelectorAll('.search-suggestion');
        if (suggestions.length === 0) return;
        
        // Remove previous active state
        if (this.activeSuggestionIndex >= 0) {
            suggestions[this.activeSuggestionIndex].classList.remove('active');
        }
        
        // Update index
        this.activeSuggestionIndex += direction;
        
        if (this.activeSuggestionIndex < 0) {
            this.activeSuggestionIndex = suggestions.length - 1;
        } else if (this.activeSuggestionIndex >= suggestions.length) {
            this.activeSuggestionIndex = 0;
        }
        
        // Add active state
        suggestions[this.activeSuggestionIndex].classList.add('active');
        suggestions[this.activeSuggestionIndex].scrollIntoView({
            block: 'nearest'
        });
    }
    
    selectActiveSuggestion() {
        const suggestions = this.suggestionsList.querySelectorAll('.search-suggestion');
        if (this.activeSuggestionIndex >= 0 && 
            this.activeSuggestionIndex < suggestions.length) {
            const suggestion = suggestions[this.activeSuggestionIndex].textContent;
            this.selectSuggestion(suggestion);
        }
    }
    
    selectSuggestion(suggestion) {
        this.searchInput.value = suggestion;
        this.hideSuggestions();
        this.performFuzzySearch(suggestion);
        this.searchInput.focus();
    }
    
    hideSuggestions() {
        this.suggestionsList.style.display = 'none';
        this.activeSuggestionIndex = -1;
    }
    
    showAllRows() {
        this.originalRows.forEach(row => {
            row.element.style.display = '';
            row.element.removeAttribute('aria-hidden');
        });
    }
    
    updateResultsInfo(visibleCount = null, query = '') {
        const count = visibleCount !== null ? visibleCount : this.originalRows.length;
        const total = this.originalRows.length;
        
        if (query) {
            this.resultsInfo.textContent = 
                `Found ${count} of ${total} rows matching "${query}"`;
        } else {
            this.resultsInfo.textContent = `Showing all ${total} rows`;
        }
        
        this.resultsInfo.setAttribute('aria-live', 'polite');
    }
}

// Enhanced CSS for fuzzy search
const fuzzySearchStyles = `
.fuzzy-search-container {
    position: relative;
    margin-bottom: 1rem;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}

.fuzzy-search-input {
    width: 100%;
    padding: 12px 16px;
    border: 2px solid #e9ecef;
    border-radius: 8px;
    font-size: 16px;
    background: white;
    transition: border-color 0.2s ease;
}

.fuzzy-search-input:focus {
    outline: none;
    border-color: #007bff;
    box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1);
}

.search-suggestions {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: white;
    border: 1px solid #e9ecef;
    border-radius: 0 0 8px 8px;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
    z-index: 1000;
    max-height: 200px;
    overflow-y: auto;
}

.search-suggestion {
    padding: 12px 16px;
    cursor: pointer;
    border-bottom: 1px solid #f8f9fa;
    transition: background-color 0.15s ease;
}

.search-suggestion:hover,
.search-suggestion.active {
    background-color: #f8f9fa;
}

.search-suggestion:last-child {
    border-bottom: none;
}

.search-results-info {
    margin-top: 8px;
    font-size: 14px;
    color: #6c757d;
    font-weight: 500;
}

@media (max-width: 768px) {
    .fuzzy-search-input {
        font-size: 16px; /* Prevent zoom on iOS */
    }
}
`;

// Inject CSS
if (typeof document !== 'undefined') {
    const style = document.createElement('style');
    style.textContent = fuzzySearchStyles;
    document.head.appendChild(style);
}

// Auto-initialize fuzzy search
document.addEventListener('DOMContentLoaded', function() {
    const fuzzySearchTables = document.querySelectorAll('table[data-fuzzy-search]');
    
    fuzzySearchTables.forEach(table => {
        new FuzzyTableSearch(`#${table.id}`, {
            threshold: parseFloat(table.dataset.threshold) || 0.6,
            maxDistance: parseInt(table.dataset.maxDistance) || 3
        });
    });
});

Integration with Modern Documentation

Table data filtering and search capabilities integrate seamlessly with comprehensive Markdown workflows. When combined with table responsive design techniques, interactive search functionality maintains usability across all device sizes while providing consistent filtering experiences that adapt to different screen constraints.

For complex data presentation requirements, filtering systems work effectively with table column alignment and width controls to create sophisticated data exploration interfaces that maintain visual organization while enabling dynamic content manipulation.

When building comprehensive documentation systems, table search capabilities complement automation workflows by enabling automated index generation, search optimization, and content discovery features that scale effectively with growing datasets.

Performance Optimization

Virtual Scrolling for Large Datasets

// virtual-table-search.js - Performance optimization for large datasets
class VirtualTableSearch {
    constructor(tableSelector, data, options = {}) {
        this.container = document.querySelector(tableSelector);
        this.data = data;
        this.filteredData = [...data];
        this.options = {
            rowHeight: 40,
            visibleRows: 20,
            bufferRows: 5,
            searchDebounce: 300,
            ...options
        };
        
        this.scrollTop = 0;
        this.startIndex = 0;
        this.endIndex = 0;
        this.searchQuery = '';
        
        this.init();
    }
    
    init() {
        this.createVirtualTable();
        this.createSearchInterface();
        this.updateDisplay();
    }
    
    createVirtualTable() {
        this.container.innerHTML = '';
        this.container.className = 'virtual-table-container';
        
        // Create table wrapper
        this.tableWrapper = document.createElement('div');
        this.tableWrapper.className = 'virtual-table-wrapper';
        this.tableWrapper.style.cssText = `
            height: ${this.options.visibleRows * this.options.rowHeight}px;
            overflow: auto;
            position: relative;
        `;
        
        // Create virtual table
        this.table = document.createElement('table');
        this.table.className = 'virtual-table';
        
        // Create header (always visible)
        this.createHeader();
        
        // Create body container
        this.tbody = document.createElement('tbody');
        this.table.appendChild(this.tbody);
        
        // Create spacer for scrolling
        this.spacer = document.createElement('div');
        this.updateSpacerHeight();
        
        this.tableWrapper.appendChild(this.table);
        this.tableWrapper.appendChild(this.spacer);
        this.container.appendChild(this.tableWrapper);
        
        // Bind scroll event
        this.tableWrapper.addEventListener('scroll', () => {
            this.handleScroll();
        });
    }
    
    createHeader() {
        const thead = document.createElement('thead');
        const headerRow = document.createElement('tr');
        
        // Assuming first data row contains headers
        if (this.data.length > 0) {
            Object.keys(this.data[0]).forEach(key => {
                const th = document.createElement('th');
                th.textContent = key;
                th.style.cssText = `
                    position: sticky;
                    top: 0;
                    background: #f8f9fa;
                    z-index: 10;
                    padding: 8px 12px;
                    border-bottom: 2px solid #dee2e6;
                `;
                headerRow.appendChild(th);
            });
        }
        
        thead.appendChild(headerRow);
        this.table.appendChild(thead);
    }
    
    createSearchInterface() {
        const searchContainer = document.createElement('div');
        searchContainer.className = 'virtual-search-container';
        
        const searchInput = document.createElement('input');
        searchInput.type = 'text';
        searchInput.className = 'virtual-search-input';
        searchInput.placeholder = `Search ${this.data.length} records...`;
        
        const resultCount = document.createElement('span');
        resultCount.className = 'virtual-search-count';
        this.updateSearchCount(resultCount);
        
        searchContainer.appendChild(searchInput);
        searchContainer.appendChild(resultCount);
        
        this.container.insertBefore(searchContainer, this.tableWrapper);
        
        // Bind search events
        let searchTimeout;
        searchInput.addEventListener('input', (e) => {
            clearTimeout(searchTimeout);
            searchTimeout = setTimeout(() => {
                this.performSearch(e.target.value);
            }, this.options.searchDebounce);
        });
        
        this.searchInput = searchInput;
        this.resultCount = resultCount;
    }
    
    performSearch(query) {
        this.searchQuery = query.toLowerCase();
        
        if (!this.searchQuery) {
            this.filteredData = [...this.data];
        } else {
            this.filteredData = this.data.filter(row => {
                return Object.values(row).some(value => 
                    String(value).toLowerCase().includes(this.searchQuery)
                );
            });
        }
        
        // Reset scroll position
        this.tableWrapper.scrollTop = 0;
        this.scrollTop = 0;
        
        this.updateSpacerHeight();
        this.updateDisplay();
        this.updateSearchCount();
    }
    
    handleScroll() {
        this.scrollTop = this.tableWrapper.scrollTop;
        
        // Use requestAnimationFrame for smooth scrolling
        requestAnimationFrame(() => {
            this.updateDisplay();
        });
    }
    
    updateDisplay() {
        const containerHeight = this.tableWrapper.clientHeight;
        const totalRows = this.filteredData.length;
        
        // Calculate visible range with buffer
        this.startIndex = Math.max(0, Math.floor(this.scrollTop / this.options.rowHeight) - this.options.bufferRows);
        this.endIndex = Math.min(totalRows, this.startIndex + this.options.visibleRows + (this.options.bufferRows * 2));
        
        // Clear current rows
        this.tbody.innerHTML = '';
        
        // Render visible rows
        for (let i = this.startIndex; i < this.endIndex; i++) {
            if (i < this.filteredData.length) {
                const row = this.createRow(this.filteredData[i], i);
                this.tbody.appendChild(row);
            }
        }
        
        // Position table to create virtual scrolling effect
        const offsetY = this.startIndex * this.options.rowHeight;
        this.table.style.transform = `translateY(${offsetY}px)`;
    }
    
    createRow(rowData, index) {
        const tr = document.createElement('tr');
        tr.style.height = `${this.options.rowHeight}px`;
        tr.setAttribute('data-index', index);
        
        Object.entries(rowData).forEach(([key, value]) => {
            const td = document.createElement('td');
            td.textContent = value;
            td.style.padding = '8px 12px';
            
            // Highlight search matches
            if (this.searchQuery && String(value).toLowerCase().includes(this.searchQuery)) {
                this.highlightText(td, String(value), this.searchQuery);
            }
            
            tr.appendChild(td);
        });
        
        return tr;
    }
    
    highlightText(element, text, query) {
        const regex = new RegExp(`(${query})`, 'gi');
        element.innerHTML = text.replace(regex, '<mark class="search-highlight">$1</mark>');
    }
    
    updateSpacerHeight() {
        const totalHeight = this.filteredData.length * this.options.rowHeight;
        this.spacer.style.height = `${totalHeight}px`;
    }
    
    updateSearchCount() {
        this.resultCount.textContent = 
            `${this.filteredData.length} of ${this.data.length} records`;
    }
    
    // Public API
    setData(newData) {
        this.data = newData;
        this.performSearch(this.searchQuery);
    }
    
    getFilteredData() {
        return this.filteredData;
    }
    
    scrollToIndex(index) {
        const targetScrollTop = index * this.options.rowHeight;
        this.tableWrapper.scrollTop = targetScrollTop;
    }
}

// Performance monitoring
class TablePerformanceMonitor {
    constructor() {
        this.metrics = new Map();
        this.observers = new Map();
    }
    
    startTiming(operation) {
        this.metrics.set(operation, performance.now());
    }
    
    endTiming(operation) {
        const startTime = this.metrics.get(operation);
        if (startTime) {
            const duration = performance.now() - startTime;
            console.log(`${operation}: ${duration.toFixed(2)}ms`);
            
            // Emit performance event
            const event = new CustomEvent('tablePerformance', {
                detail: { operation, duration }
            });
            document.dispatchEvent(event);
            
            this.metrics.delete(operation);
            return duration;
        }
    }
    
    observeTable(tableElement, callback) {
        if ('IntersectionObserver' in window) {
            const observer = new IntersectionObserver((entries) => {
                entries.forEach(entry => {
                    if (callback) {
                        callback(entry.isIntersecting, entry.intersectionRatio);
                    }
                });
            }, {
                threshold: [0, 0.25, 0.5, 0.75, 1]
            });
            
            observer.observe(tableElement);
            this.observers.set(tableElement, observer);
        }
    }
    
    measureMemoryUsage() {
        if ('memory' in performance) {
            return {
                used: performance.memory.usedJSHeapSize,
                total: performance.memory.totalJSHeapSize,
                limit: performance.memory.jsHeapSizeLimit
            };
        }
        return null;
    }
    
    cleanup(tableElement) {
        const observer = this.observers.get(tableElement);
        if (observer) {
            observer.disconnect();
            this.observers.delete(tableElement);
        }
    }
}

// Global performance monitor instance
window.tablePerformanceMonitor = new TablePerformanceMonitor();

Accessibility Implementation

Screen Reader Optimization

// accessible-table-search.js - Accessibility-focused implementation
class AccessibleTableSearch {
    constructor(tableSelector, options = {}) {
        this.table = document.querySelector(tableSelector);
        this.options = {
            announceResults: true,
            liveRegionDelay: 500,
            keyboardNavigation: true,
            focusManagement: true,
            ...options
        };
        
        this.searchInput = null;
        this.liveRegion = null;
        this.originalRows = [];
        this.currentFocusIndex = -1;
        
        this.init();
    }
    
    init() {
        if (!this.table) return;
        
        this.enhanceTableAccessibility();
        this.createAccessibleSearchInterface();
        this.bindAccessibilityEvents();
        this.cacheTableData();
    }
    
    enhanceTableAccessibility() {
        // Ensure table has proper ARIA attributes
        if (!this.table.getAttribute('role')) {
            this.table.setAttribute('role', 'table');
        }
        
        // Add table caption if not present
        if (!this.table.querySelector('caption')) {
            const caption = document.createElement('caption');
            caption.textContent = 'Data table with search functionality';
            caption.className = 'sr-only'; // Screen reader only
            this.table.insertBefore(caption, this.table.firstChild);
        }
        
        // Enhance header cells
        const headers = this.table.querySelectorAll('thead th');
        headers.forEach((header, index) => {
            if (!header.id) {
                header.id = `col-header-${index}`;
            }
            header.setAttribute('scope', 'col');
        });
        
        // Enhance data cells
        const rows = this.table.querySelectorAll('tbody tr');
        rows.forEach((row, rowIndex) => {
            row.setAttribute('role', 'row');
            
            const cells = row.querySelectorAll('td');
            cells.forEach((cell, cellIndex) => {
                cell.setAttribute('role', 'cell');
                
                // Associate with column header
                const headerId = headers[cellIndex]?.id;
                if (headerId) {
                    const existingHeaders = cell.getAttribute('headers') || '';
                    const newHeaders = existingHeaders ? 
                        `${existingHeaders} ${headerId}` : headerId;
                    cell.setAttribute('headers', newHeaders);
                }
            });
        });
    }
    
    createAccessibleSearchInterface() {
        const searchContainer = document.createElement('div');
        searchContainer.className = 'accessible-search-container';
        searchContainer.setAttribute('role', 'search');
        searchContainer.setAttribute('aria-label', 'Table search controls');
        
        // Search label and input
        const searchLabel = document.createElement('label');
        searchLabel.textContent = 'Search table data: ';
        searchLabel.className = 'search-label';
        searchLabel.htmlFor = 'accessible-table-search';
        
        this.searchInput = document.createElement('input');
        this.searchInput.type = 'text';
        this.searchInput.id = 'accessible-table-search';
        this.searchInput.className = 'accessible-search-input';
        this.searchInput.setAttribute('aria-describedby', 'search-instructions search-results');
        this.searchInput.setAttribute('autocomplete', 'off');
        this.searchInput.setAttribute('spellcheck', 'false');
        
        // Search instructions
        const instructions = document.createElement('div');
        instructions.id = 'search-instructions';
        instructions.className = 'search-instructions';
        instructions.textContent = 'Type to filter table rows. Use arrow keys to navigate results.';
        
        // Clear button
        const clearButton = document.createElement('button');
        clearButton.type = 'button';
        clearButton.className = 'search-clear-button';
        clearButton.textContent = 'Clear search';
        clearButton.setAttribute('aria-label', 'Clear search and show all results');
        
        // Search results summary
        const resultsDiv = document.createElement('div');
        resultsDiv.id = 'search-results';
        resultsDiv.className = 'search-results-summary';
        resultsDiv.setAttribute('aria-live', 'polite');
        resultsDiv.setAttribute('aria-atomic', 'true');
        
        // Live region for announcements
        this.liveRegion = document.createElement('div');
        this.liveRegion.className = 'sr-only';
        this.liveRegion.setAttribute('aria-live', 'assertive');
        this.liveRegion.setAttribute('aria-atomic', 'true');
        
        // Assemble interface
        searchContainer.appendChild(searchLabel);
        searchContainer.appendChild(this.searchInput);
        searchContainer.appendChild(clearButton);
        searchContainer.appendChild(instructions);
        searchContainer.appendChild(resultsDiv);
        searchContainer.appendChild(this.liveRegion);
        
        this.table.parentNode.insertBefore(searchContainer, this.table);
        
        this.clearButton = clearButton;
        this.resultsDiv = resultsDiv;
        
        // Initial results summary
        this.updateResultsSummary();
    }
    
    bindAccessibilityEvents() {
        let searchTimeout;
        let announceTimeout;
        
        // Search input events
        this.searchInput.addEventListener('input', (e) => {
            clearTimeout(searchTimeout);
            clearTimeout(announceTimeout);
            
            searchTimeout = setTimeout(() => {
                this.performAccessibleSearch(e.target.value);
                
                // Delayed announcement for better UX
                if (this.options.announceResults) {
                    announceTimeout = setTimeout(() => {
                        this.announceSearchResults(e.target.value);
                    }, this.options.liveRegionDelay);
                }
            }, 300);
        });
        
        // Keyboard navigation
        if (this.options.keyboardNavigation) {
            this.searchInput.addEventListener('keydown', (e) => {
                this.handleSearchKeyboard(e);
            });
            
            // Table navigation
            this.table.addEventListener('keydown', (e) => {
                this.handleTableKeyboard(e);
            });
        }
        
        // Clear button
        this.clearButton.addEventListener('click', () => {
            this.clearSearch();
        });
        
        // Focus management
        if (this.options.focusManagement) {
            this.searchInput.addEventListener('focus', () => {
                this.currentFocusIndex = -1;
            });
        }
    }
    
    handleSearchKeyboard(e) {
        const visibleRows = this.getVisibleRows();
        
        switch (e.key) {
            case 'ArrowDown':
                e.preventDefault();
                if (visibleRows.length > 0) {
                    this.focusTableRow(0);
                }
                break;
                
            case 'Escape':
                e.preventDefault();
                this.clearSearch();
                break;
                
            case 'Enter':
                e.preventDefault();
                if (visibleRows.length === 1) {
                    this.focusTableRow(0);
                }
                break;
        }
    }
    
    handleTableKeyboard(e) {
        const visibleRows = this.getVisibleRows();
        
        switch (e.key) {
            case 'ArrowUp':
                e.preventDefault();
                if (this.currentFocusIndex <= 0) {
                    this.searchInput.focus();
                    this.currentFocusIndex = -1;
                } else {
                    this.focusTableRow(this.currentFocusIndex - 1);
                }
                break;
                
            case 'ArrowDown':
                e.preventDefault();
                if (this.currentFocusIndex < visibleRows.length - 1) {
                    this.focusTableRow(this.currentFocusIndex + 1);
                }
                break;
                
            case 'Home':
                e.preventDefault();
                this.focusTableRow(0);
                break;
                
            case 'End':
                e.preventDefault();
                this.focusTableRow(visibleRows.length - 1);
                break;
                
            case 'Escape':
                e.preventDefault();
                this.searchInput.focus();
                this.currentFocusIndex = -1;
                break;
        }
    }
    
    focusTableRow(index) {
        const visibleRows = this.getVisibleRows();
        
        if (index >= 0 && index < visibleRows.length) {
            this.currentFocusIndex = index;
            const row = visibleRows[index];
            
            // Make row focusable temporarily
            row.setAttribute('tabindex', '-1');
            row.focus();
            
            // Announce row content
            this.announceRowContent(row, index + 1, visibleRows.length);
            
            // Remove tabindex from other rows
            visibleRows.forEach((otherRow, otherIndex) => {
                if (otherIndex !== index) {
                    otherRow.removeAttribute('tabindex');
                }
            });
        }
    }
    
    announceRowContent(row, position, total) {
        const cells = Array.from(row.querySelectorAll('td'));
        const content = cells.map(cell => cell.textContent.trim()).join(', ');
        
        this.liveRegion.textContent = 
            `Row ${position} of ${total}: ${content}`;
    }
    
    performAccessibleSearch(query) {
        if (!query.trim()) {
            this.showAllRows();
            this.updateResultsSummary();
            return;
        }
        
        const lowerQuery = query.toLowerCase();
        let visibleCount = 0;
        
        this.originalRows.forEach(rowData => {
            const matches = rowData.searchableText.includes(lowerQuery);
            
            if (matches) {
                this.showRow(rowData.element);
                visibleCount++;
            } else {
                this.hideRow(rowData.element);
            }
        });
        
        this.updateResultsSummary(visibleCount, query);
        
        // Reset focus index when search changes
        this.currentFocusIndex = -1;
    }
    
    showRow(row) {
        row.style.display = '';
        row.removeAttribute('aria-hidden');
    }
    
    hideRow(row) {
        row.style.display = 'none';
        row.setAttribute('aria-hidden', 'true');
    }
    
    showAllRows() {
        this.originalRows.forEach(rowData => {
            this.showRow(rowData.element);
        });
    }
    
    getVisibleRows() {
        return Array.from(this.table.querySelectorAll('tbody tr'))
            .filter(row => row.style.display !== 'none');
    }
    
    cacheTableData() {
        const rows = this.table.querySelectorAll('tbody tr');
        
        this.originalRows = Array.from(rows).map(row => {
            const cells = Array.from(row.querySelectorAll('td'));
            const searchableText = cells
                .map(cell => cell.textContent.trim())
                .join(' ')
                .toLowerCase();
            
            return {
                element: row,
                searchableText: searchableText
            };
        });
    }
    
    updateResultsSummary(visibleCount = null, query = '') {
        const count = visibleCount !== null ? visibleCount : this.originalRows.length;
        const total = this.originalRows.length;
        
        let summaryText;
        if (query) {
            summaryText = `${count} of ${total} rows match "${query}"`;
        } else {
            summaryText = `Showing all ${total} rows`;
        }
        
        this.resultsDiv.textContent = summaryText;
    }
    
    announceSearchResults(query) {
        const visibleRows = this.getVisibleRows();
        const count = visibleRows.length;
        const total = this.originalRows.length;
        
        let announcement;
        if (!query) {
            announcement = `Showing all ${total} rows`;
        } else if (count === 0) {
            announcement = `No results found for "${query}"`;
        } else if (count === 1) {
            announcement = `1 result found for "${query}"`;
        } else {
            announcement = `${count} results found for "${query}"`;
        }
        
        this.liveRegion.textContent = announcement;
    }
    
    clearSearch() {
        this.searchInput.value = '';
        this.showAllRows();
        this.updateResultsSummary();
        this.currentFocusIndex = -1;
        this.searchInput.focus();
        
        this.liveRegion.textContent = 'Search cleared, showing all rows';
    }
    
    // Public API
    setFocus() {
        this.searchInput.focus();
    }
    
    announceMessage(message) {
        this.liveRegion.textContent = message;
    }
}

// CSS for accessibility features
const accessibilityStyles = `
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.accessible-search-container {
    margin-bottom: 1rem;
    padding: 16px;
    background: #f8f9fa;
    border-radius: 8px;
    border: 1px solid #e9ecef;
}

.search-label {
    display: block;
    font-weight: 600;
    margin-bottom: 8px;
    color: #495057;
}

.accessible-search-input {
    width: 100%;
    max-width: 400px;
    padding: 12px;
    border: 2px solid #ced4da;
    border-radius: 4px;
    font-size: 16px;
    margin-bottom: 8px;
}

.accessible-search-input:focus {
    outline: none;
    border-color: #007bff;
    box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.25);
}

.search-clear-button {
    margin-left: 8px;
    padding: 12px 16px;
    background: #6c757d;
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 14px;
}

.search-clear-button:hover {
    background: #545b62;
}

.search-clear-button:focus {
    outline: 2px solid #007bff;
    outline-offset: 2px;
}

.search-instructions {
    font-size: 14px;
    color: #6c757d;
    margin: 8px 0;
}

.search-results-summary {
    font-weight: 500;
    color: #495057;
    margin-top: 8px;
}

/* High contrast mode */
@media (prefers-contrast: high) {
    .accessible-search-container {
        border: 2px solid #000;
        background: #fff;
    }
    
    .accessible-search-input {
        border: 2px solid #000;
    }
    
    .accessible-search-input:focus {
        box-shadow: 0 0 0 3px #ffff00;
        border-color: #000;
    }
}

/* Focus indicators for keyboard navigation */
tbody tr[tabindex]:focus {
    outline: 2px solid #007bff;
    outline-offset: -2px;
    background-color: rgba(0, 123, 255, 0.1);
}

/* Reduced motion preferences */
@media (prefers-reduced-motion: reduce) {
    .accessible-search-input,
    .search-clear-button {
        transition: none;
    }
}
`;

// Inject CSS
if (typeof document !== 'undefined') {
    const style = document.createElement('style');
    style.textContent = accessibilityStyles;
    document.head.appendChild(style);
}

Conclusion

Table data filtering and search capabilities transform static Markdown tables into dynamic, interactive content discovery systems that enhance user experience while maintaining accessibility and performance standards. Through progressive enhancement, intelligent search algorithms, and comprehensive filtering options, documentation creators can build sophisticated data exploration tools that scale effectively with content growth while serving diverse user needs.

The key to successful implementation lies in balancing functionality with usability, ensuring that enhanced features improve rather than complicate the user experience. Whether you’re building product catalogs, documentation indexes, or data dashboards, the techniques covered in this guide provide the foundation for creating searchable, filterable table systems that engage users while maintaining the simplicity and portability that make Markdown an ideal format for structured content.

Remember to implement features progressively, prioritize keyboard accessibility, and optimize performance for large datasets. With proper table filtering and search implementation, your Markdown documentation becomes a powerful platform for content discovery that serves both casual browsers and power users seeking specific information within comprehensive datasets.