Files
roi-theme/wp-content/themes/apus-theme/inc/critical-css.php
FrankZamora a0633f4000 Fix: Consolidar funciones sanitizadoras para resolver error fatal de redeclaración
Resuelve issue #21 y sub-issue #22

PROBLEMA:
- La función apus_sanitize_checkbox() estaba definida en 4 archivos diferentes
- Causaba error fatal: "Cannot redeclare apus_sanitize_checkbox()"
- Impedía activación del tema en staging

SOLUCIÓN:
1. Crear inc/sanitize-functions.php con funciones centralizadas
2. Incluir sanitize-functions.php al inicio de functions.php
3. Eliminar definiciones duplicadas en:
   - inc/customizer-fonts.php (líneas 83-93)
   - inc/adsense-delay.php (líneas 161-163)
   - inc/admin/options-api.php (líneas 240-242)
   - inc/critical-css.php (líneas 361-363)

ARCHIVOS MODIFICADOS:
- inc/sanitize-functions.php (nuevo)
- functions.php (incluir sanitize-functions.php)
- inc/customizer-fonts.php (eliminar duplicados)
- inc/adsense-delay.php (eliminar duplicados)
- inc/admin/options-api.php (eliminar duplicados)
- inc/critical-css.php (eliminar duplicados)

FUNCIONES CONSOLIDADAS:
- apus_sanitize_checkbox($input): Sanitiza valores boolean
- apus_sanitize_select($input, $setting): Sanitiza valores select

VERIFICACIÓN:
 Sintaxis PHP correcta en todos los archivos
 No hay redeclaraciones de funciones
 Funciones protegidas con function_exists()

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 10:41:18 -06:00

368 lines
11 KiB
PHP

<?php
/**
* Critical CSS Generator and Inline Loader
*
* This file provides functionality to inline critical CSS for above-the-fold content,
* improving First Contentful Paint (FCP) and Largest Contentful Paint (LCP).
*
* IMPORTANT: This feature is DISABLED by default. Enable it via Customizer.
*
* How it works:
* 1. When enabled, critical CSS is inlined in the <head>
* 2. Main stylesheet is loaded asynchronously after page load
* 3. Improves Core Web Vitals by reducing render-blocking CSS
*
* @package Apus_Theme
* @since 1.0.0
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Check if Critical CSS is enabled
*
* @since 1.0.0
* @return bool
*/
function apus_is_critical_css_enabled() {
return get_theme_mod( 'apus_enable_critical_css', false );
}
/**
* Get Critical CSS Content
*
* Returns the critical CSS for the current page type.
* You can customize this based on page types (home, single, archive, etc.)
*
* @since 1.0.0
* @return string Critical CSS content
*/
function apus_get_critical_css() {
// Define critical CSS based on page type
$critical_css = '';
// Get transient to cache critical CSS
$transient_key = 'apus_critical_css_' . apus_get_page_type();
$cached_css = get_transient( $transient_key );
if ( false !== $cached_css ) {
return $cached_css;
}
// Generate critical CSS based on page type
if ( is_front_page() || is_home() ) {
$critical_css = apus_get_home_critical_css();
} elseif ( is_single() ) {
$critical_css = apus_get_single_critical_css();
} elseif ( is_archive() || is_category() || is_tag() ) {
$critical_css = apus_get_archive_critical_css();
} else {
$critical_css = apus_get_default_critical_css();
}
// Cache for 24 hours
set_transient( $transient_key, $critical_css, DAY_IN_SECONDS );
return $critical_css;
}
/**
* Get current page type for caching
*
* @since 1.0.0
* @return string Page type identifier
*/
function apus_get_page_type() {
if ( is_front_page() ) {
return 'home';
} elseif ( is_single() ) {
return 'single';
} elseif ( is_archive() ) {
return 'archive';
} elseif ( is_search() ) {
return 'search';
} elseif ( is_404() ) {
return '404';
} else {
return 'page';
}
}
/**
* Critical CSS for Homepage
*
* @since 1.0.0
* @return string
*/
function apus_get_home_critical_css() {
return '
/* Reset and Base */
*,*::before,*::after{box-sizing:border-box}
body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;line-height:1.6;color:#333;background:#fff}
img{max-width:100%;height:auto;display:block}
a{color:#0066cc;text-decoration:none}
/* Header */
.site-header{background:#fff;border-bottom:1px solid #e5e5e5;position:sticky;top:0;z-index:1000}
.site-header .container{max-width:1200px;margin:0 auto;padding:1rem}
.site-branding{display:flex;align-items:center}
.site-title{margin:0;font-size:1.5rem;font-weight:700}
/* Hero Section */
.hero-section{padding:3rem 1rem;text-align:center;background:#f8f9fa}
.hero-title{font-size:2.5rem;margin:0 0 1rem;font-weight:700;line-height:1.2}
.hero-description{font-size:1.125rem;color:#666;max-width:600px;margin:0 auto}
/* Container */
.container{max-width:1200px;margin:0 auto;padding:0 1rem}
/* Featured Posts Grid */
.featured-posts{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:2rem;margin:2rem 0}
.post-card{background:#fff;border:1px solid #e5e5e5;border-radius:8px;overflow:hidden;transition:transform .2s}
.post-card:hover{transform:translateY(-4px)}
/* Skip to content */
.skip-link{position:absolute;left:-999px;top:0;background:#0066cc;color:#fff;padding:.5rem 1rem;text-decoration:none}
.skip-link:focus{left:0}
';
}
/**
* Critical CSS for Single Post/Page
*
* @since 1.0.0
* @return string
*/
function apus_get_single_critical_css() {
return '
/* Reset and Base */
*,*::before,*::after{box-sizing:border-box}
body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;line-height:1.6;color:#333;background:#fff}
img{max-width:100%;height:auto;display:block}
a{color:#0066cc;text-decoration:none}
h1,h2,h3,h4,h5,h6{margin:1.5rem 0 1rem;font-weight:700;line-height:1.3}
h1{font-size:2.5rem}
h2{font-size:2rem}
h3{font-size:1.5rem}
p{margin:0 0 1.5rem}
/* Header */
.site-header{background:#fff;border-bottom:1px solid #e5e5e5;position:sticky;top:0;z-index:1000}
.site-header .container{max-width:1200px;margin:0 auto;padding:1rem}
/* Article */
.entry-header{margin:2rem 0}
.entry-title{font-size:2.5rem;margin:0 0 1rem;line-height:1.2}
.entry-meta{color:#666;font-size:.875rem}
.entry-content{max-width:800px;margin:0 auto;font-size:1.125rem;line-height:1.8}
.featured-image{margin:2rem 0}
/* Container */
.container{max-width:1200px;margin:0 auto;padding:0 1rem}
/* Skip to content */
.skip-link{position:absolute;left:-999px;top:0;background:#0066cc;color:#fff;padding:.5rem 1rem;text-decoration:none}
.skip-link:focus{left:0}
';
}
/**
* Critical CSS for Archive Pages
*
* @since 1.0.0
* @return string
*/
function apus_get_archive_critical_css() {
return '
/* Reset and Base */
*,*::before,*::after{box-sizing:border-box}
body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;line-height:1.6;color:#333;background:#fff}
img{max-width:100%;height:auto;display:block}
a{color:#0066cc;text-decoration:none}
/* Header */
.site-header{background:#fff;border-bottom:1px solid #e5e5e5;position:sticky;top:0;z-index:1000}
.site-header .container{max-width:1200px;margin:0 auto;padding:1rem}
/* Archive Header */
.archive-header{padding:2rem 1rem;background:#f8f9fa;border-bottom:1px solid #e5e5e5}
.archive-title{margin:0;font-size:2rem;font-weight:700}
.archive-description{margin:1rem 0 0;color:#666;font-size:1.125rem}
/* Posts Grid */
.posts-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:2rem;margin:2rem 0}
.post-card{background:#fff;border:1px solid #e5e5e5;border-radius:8px;overflow:hidden}
/* Container */
.container{max-width:1200px;margin:0 auto;padding:0 1rem}
/* Skip to content */
.skip-link{position:absolute;left:-999px;top:0;background:#0066cc;color:#fff;padding:.5rem 1rem;text-decoration:none}
.skip-link:focus{left:0}
';
}
/**
* Critical CSS for Default Pages
*
* @since 1.0.0
* @return string
*/
function apus_get_default_critical_css() {
return '
/* Reset and Base */
*,*::before,*::after{box-sizing:border-box}
body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;line-height:1.6;color:#333;background:#fff}
img{max-width:100%;height:auto;display:block}
a{color:#0066cc;text-decoration:none}
/* Header */
.site-header{background:#fff;border-bottom:1px solid #e5e5e5;position:sticky;top:0;z-index:1000}
.site-header .container{max-width:1200px;margin:0 auto;padding:1rem}
/* Container */
.container{max-width:1200px;margin:0 auto;padding:0 1rem}
/* Content */
.site-content{min-height:50vh;padding:2rem 0}
/* Skip to content */
.skip-link{position:absolute;left:-999px;top:0;background:#0066cc;color:#fff;padding:.5rem 1rem;text-decoration:none}
.skip-link:focus{left:0}
';
}
/**
* Output Critical CSS inline in head
*
* @since 1.0.0
*/
function apus_output_critical_css() {
if ( ! apus_is_critical_css_enabled() ) {
return;
}
$critical_css = apus_get_critical_css();
if ( empty( $critical_css ) ) {
return;
}
// Minify CSS (remove extra whitespace and newlines)
$critical_css = preg_replace( '/\s+/', ' ', $critical_css );
$critical_css = trim( $critical_css );
// Output inline critical CSS
echo '<style id="apus-critical-css">' . $critical_css . '</style>' . "\n";
}
add_action( 'wp_head', 'apus_output_critical_css', 1 );
/**
* Load main stylesheet asynchronously when critical CSS is enabled
*
* @since 1.0.0
*/
function apus_async_main_stylesheet() {
if ( ! apus_is_critical_css_enabled() ) {
return;
}
// Dequeue main stylesheet to prevent render-blocking
wp_dequeue_style( 'apus-theme-style' );
// Enqueue with media="print" and onload to load asynchronously
wp_enqueue_style(
'apus-theme-style-async',
get_stylesheet_uri(),
array(),
APUS_VERSION,
'print'
);
// Add onload attribute to switch media to "all"
add_filter( 'style_loader_tag', 'apus_add_async_attribute', 10, 2 );
}
add_action( 'wp_enqueue_scripts', 'apus_async_main_stylesheet', 999 );
/**
* Add async loading attributes to stylesheet
*
* @since 1.0.0
* @param string $html The link tag for the enqueued style.
* @param string $handle The style's registered handle.
* @return string Modified link tag
*/
function apus_add_async_attribute( $html, $handle ) {
if ( 'apus-theme-style-async' !== $handle ) {
return $html;
}
// Add onload attribute to switch media to "all"
$html = str_replace(
"media='print'",
"media='print' onload=\"this.media='all'\"",
$html
);
// Add noscript fallback
$html .= '<noscript><link rel="stylesheet" href="' . get_stylesheet_uri() . '"></noscript>';
return $html;
}
/**
* Add Customizer setting for Critical CSS
*
* @since 1.0.0
* @param WP_Customize_Manager $wp_customize Theme Customizer object.
*/
function apus_critical_css_customizer( $wp_customize ) {
// Add Performance section
$wp_customize->add_section(
'apus_performance',
array(
'title' => __( 'Performance Optimization', 'apus-theme' ),
'priority' => 130,
)
);
// Critical CSS Enable/Disable
$wp_customize->add_setting(
'apus_enable_critical_css',
array(
'default' => false,
'sanitize_callback' => 'apus_sanitize_checkbox',
'transport' => 'refresh',
)
);
$wp_customize->add_control(
'apus_enable_critical_css',
array(
'label' => __( 'Enable Critical CSS', 'apus-theme' ),
'description' => __( 'Inline critical CSS and load main stylesheet asynchronously. This can improve Core Web Vitals but may cause a flash of unstyled content (FOUC). Test thoroughly before enabling in production.', 'apus-theme' ),
'section' => 'apus_performance',
'type' => 'checkbox',
)
);
}
add_action( 'customize_register', 'apus_critical_css_customizer' );
/**
* Clear critical CSS cache when theme is updated
*
* @since 1.0.0
*/
function apus_clear_critical_css_cache() {
$page_types = array( 'home', 'single', 'archive', 'search', '404', 'page' );
foreach ( $page_types as $type ) {
delete_transient( 'apus_critical_css_' . $type );
}
}
add_action( 'after_switch_theme', 'apus_clear_critical_css_cache' );
add_action( 'customize_save_after', 'apus_clear_critical_css_cache' );