Hook de WordPress para agregar nombres descriptivos a los enlaces y mejorar la accesibilidad

Este hook para WordPress soluciona el problema de accesibilidad de los enlaces de varias maneras:

Características principales:

  1. Detección automática: Identifica enlaces que solo contienen imágenes o íconos sin texto descriptivo
  2. Generación de descripciones: Crea automáticamente descripciones basadas en:
    • Patrones de URL (servicios, contacto, redes sociales, etc.)
    • Contenido interno del enlace (alt text de imágenes)
    • Clases CSS del elemento
  3. Atributo aria-label: Agrega automáticamente el atributo aria-label con texto descriptivo
  4. Estilos de accesibilidad: Incluye CSS para mejorar el foco visual y marcar enlaces externos

Cómo usar:

  1. Instalación: Copia el código al functions.php de tu child theme
  2. Personalización: Modifica el array $url_patterns para agregar más patrones específicos de tu sitio
  3. Shortcode: Usa [accessible_link] para crear enlaces accesibles manualmente:
    1. [accessible_link url="/servicios" description="Ver todos nuestros servicios"]
          <img src="arrow.png" alt="flecha">
      [/accessible_link]

       

  4. Función PHP: Usa accessible_link() en templates:
    1. echo accessible_link('/contacto', '<i class="icon-arrow"></i>', 'Ir a página de contacto');

      El hook procesará automáticamente el contenido y mejorará la accesibilidad de enlaces existentes sin modificar tu HTML actual, solucionando el problema que muestras en la imagen de la auditoría de accesibilidad.

Orden de prioridad para generar nombres:

  1. Texto visible del enlace – Si hay texto real en el enlace, lo usa directamente
  2. Alt text de imágenes – Si contiene una imagen con alt descriptivo
  3. Atributo title – De cualquier elemento interno
  4. Atributos data-* – Como data-title, data-label, data-text
  5. Patrones de URL – Solo como respaldo para URLs conocidas
  6. Slug de la URL – Convierte el slug en texto legible (ej: «peritaje-informatico» → «Peritaje Informático»)

La función extract_link_text() es especialmente inteligente porque:

  • Extrae texto real de spans, divs, párrafos dentro del enlace
  • Ignora elementos vacíos o que solo contienen símbolos
  • Busca texto en atributos como placeholder y value
  • Como último recurso, extrae cualquier texto visible

Y extract_text_from_url() convierte URLs como /analisis-forense/ en «Análisis Forense» de forma automática.

/**
 * Hook para mejorar la accesibilidad de los enlaces agregando nombres descriptivos
 * Agregar al functions.php del child theme
 */

// Hook para procesar el contenido y mejorar los enlaces
add_filter('the_content', 'improve_link_accessibility');
add_filter('widget_text', 'improve_link_accessibility');

function improve_link_accessibility($content) {
    // Procesar enlaces que solo contienen imágenes o íconos
    $content = preg_replace_callback(
        '/<a([^>]*href="([^"]*)"[^>]*)>(\s*<[^>]*>\s*)*<\/a>/i',
        'add_accessible_link_text',
        $content
    );
    
    return $content;
}

function add_accessible_link_text($matches) {
    $full_link = $matches[0];
    $link_attributes = $matches[1];
    $url = $matches[2];
    $inner_content = $matches[3];
    
    // Si el enlace ya tiene texto descriptivo, no modificarlo
    if (preg_match('/[a-zA-Z]{3,}/', strip_tags($inner_content))) {
        return $full_link;
    }
    
    // Si ya tiene aria-label o title descriptivo, no modificarlo
    if (preg_match('/aria-label\s*=\s*["\'][^"\']{10,}["\']/', $link_attributes) || 
        preg_match('/title\s*=\s*["\'][^"\']{10,}["\']/', $link_attributes)) {
        return $full_link;
    }
    
    // Generar texto descriptivo basado en la URL o contexto
    $descriptive_text = generate_link_description($url, $inner_content);
    
    if ($descriptive_text) {
        // Agregar aria-label con el texto descriptivo
        $new_attributes = $link_attributes . ' aria-label="' . esc_attr($descriptive_text) . '"';
        return '<a' . $new_attributes . '>' . $inner_content . '</a>';
    }
    
    return $full_link;
}

function generate_link_description($url, $inner_content = '') {
    if (!empty($inner_content)) {
        // 1. Primero intentar extraer texto visible del enlace
        $text_content = extract_link_text($inner_content);
        if (!empty($text_content)) {
            return trim($text_content);
        }
        
        // 2. Si contiene una imagen, usar el alt text
        if (preg_match('/<img[^>]*alt\s*=\s*["\']([^"\']+)["\'][^>]*>/i', $inner_content, $alt_match)) {
            $alt_text = trim($alt_match[1]);
            if (!empty($alt_text) && $alt_text !== 'image' && strlen($alt_text) > 2) {
                return $alt_text;
            }
        }
        
        // 3. Buscar title en elementos internos
        if (preg_match('/title\s*=\s*["\']([^"\']{3,})["\']/', $inner_content, $title_match)) {
            return trim($title_match[1]);
        }
        
        // 4. Buscar data-* attributes que puedan tener descripción
        if (preg_match('/data-(?:title|label|text)\s*=\s*["\']([^"\']{3,})["\']/', $inner_content, $data_match)) {
            return trim($data_match[1]);
        }
    }
    
    // 5. Como último recurso, usar patrones de URL
    $url_patterns = array(
        '/peritaje-informatico/i' => 'Peritaje Informático',
        '/analisis-forense/i' => 'Análisis Forense',
        '/contacto/i' => 'Contacto',
        '/servicios/i' => 'Servicios',
        '/blog/i' => 'Blog',
        '/portfolio/i' => 'Portfolio',
        '/sobre-nosotros|about/i' => 'Sobre Nosotros',
        '/telefono|phone|tel:/i' => 'Teléfono',
        '/email|mailto:/i' => 'Email',
        '/facebook\.com/i' => 'Facebook',
        '/twitter\.com|x\.com/i' => 'Twitter',
        '/linkedin\.com/i' => 'LinkedIn',
        '/instagram\.com/i' => 'Instagram',
        '/youtube\.com/i' => 'YouTube',
        '/whatsapp/i' => 'WhatsApp',
    );
    
    foreach ($url_patterns as $pattern => $description) {
        if (preg_match($pattern, $url)) {
            return $description;
        }
    }
    
    // 6. Extraer texto del slug de la URL
    $url_text = extract_text_from_url($url);
    if (!empty($url_text)) {
        return $url_text;
    }
    
    return 'Enlace';
}

// Función auxiliar para extraer texto visible del contenido del enlace
function extract_link_text($content) {
    // Remover scripts y estilos
    $content = preg_replace('/<(script|style)[^>]*>.*?<\/\1>/si', '', $content);
    
    // Extraer texto de elementos con contenido textual
    $text_elements = array();
    
    // Buscar spans, divs, p, etc. con texto
    if (preg_match_all('/>([^<]{3,})</i', $content, $matches)) {
        foreach ($matches[1] as $text) {
            $cleaned_text = trim(strip_tags($text));
            if (strlen($cleaned_text) > 2 && !preg_match('/^[\s\W]*$/', $cleaned_text)) {
                $text_elements[] = $cleaned_text;
            }
        }
    }
    
    // Buscar texto en atributos placeholder, value, etc.
    if (preg_match('/(?:placeholder|value)\s*=\s*["\']([^"\']{3,})["\']/', $content, $attr_match)) {
        $text_elements[] = trim($attr_match[1]);
    }
    
    // Devolver el primer texto válido encontrado
    if (!empty($text_elements)) {
        return $text_elements[0];
    }
    
    // Como último recurso, extraer cualquier texto visible
    $plain_text = trim(strip_tags($content));
    if (strlen($plain_text) > 2 && !preg_match('/^[\s\W]*$/', $plain_text)) {
        return $plain_text;
    }
    
    return '';
}

// Función auxiliar para extraer texto legible de una URL
function extract_text_from_url($url) {
    $parsed_url = parse_url($url);
    $path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
    
    // Extraer la última parte del path (slug)
    $path_parts = array_filter(explode('/', $path));
    if (!empty($path_parts)) {
        $slug = end($path_parts);
        
        // Limpiar el slug y convertirlo en texto legible
        $slug = preg_replace('/\.[^.]+$/', '', $slug); // remover extensión
        $slug = str_replace(array('-', '_'), ' ', $slug);
        $slug = ucwords(trim($slug));
        
        if (strlen($slug) > 2) {
            return $slug;
        }
    }
    
    return '';
}

// Hook adicional para enlaces específicos en el tema
add_action('wp_head', 'add_custom_link_styles');

function add_custom_link_styles() {
    ?>
    <style>
    /* Mejorar visualmente los enlaces para accesibilidad */
    a:focus {
        outline: 2px solid #005fcc;
        outline-offset: 2px;
    }
    
    /* Indicador visual para enlaces externos */
    a[href^="http"]:not([href*="<?php echo home_url(); ?>"])::after {
        content: " ↗";
        font-size: 0.8em;
        opacity: 0.7;
    }
    </style>
    <?php
}

// Función para agregar manualmente aria-label a enlaces específicos
function accessible_link($url, $content, $description = '', $class = '') {
    $aria_label = $description ? 'aria-label="' . esc_attr($description) . '"' : '';
    $class_attr = $class ? 'class="' . esc_attr($class) . '"' : '';
    
    return '<a href="' . esc_url($url) . '" ' . $aria_label . ' ' . $class_attr . '>' . $content . '</a>';
}

// Shortcode para crear enlaces accesibles
add_shortcode('accessible_link', 'accessible_link_shortcode');

function accessible_link_shortcode($atts, $content = null) {
    $atts = shortcode_atts(array(
        'url' => '#',
        'description' => '',
        'class' => '',
        'target' => ''
    ), $atts);
    
    $target_attr = $atts['target'] ? 'target="' . esc_attr($atts['target']) . '"' : '';
    $aria_label = $atts['description'] ? 'aria-label="' . esc_attr($atts['description']) . '"' : '';
    $class_attr = $atts['class'] ? 'class="' . esc_attr($atts['class']) . '"' : '';
    
    return '<a href="' . esc_url($atts['url']) . '" ' . $aria_label . ' ' . $class_attr . ' ' . $target_attr . '>' . do_shortcode($content) . '</a>';
}

/**
 * Uso del shortcode:
 * [accessible_link url="https://ejemplo.com" description="Visitar página de servicios"]Contenido del enlace[/accessible_link]
 */