/** * FLO — Betheme Child Theme * ------------------------------------------------------------------ * Arquitectura (según instrucciones del proyecto): * - CSS/JS globales encolados aquí (no en Theme Options de Betheme). * - Secciones del homepage servidas como HTML crudo vía shortcode * [flo_section name='01-hero'] → carga sections/01-hero.php. * Esto evita que el editor de Betheme sobreescriba nuestras funciones. * - Nav y footer inyectados con wp_body_open / wp_footer (PHP real, * menús WP y carrito en vivo de WooCommerce). * - Todas las clases con prefijo flo- para no chocar con .section, * .price, .cart, etc. de Betheme y WooCommerce. * - Imágenes servidas desde assets/ (no base64) para performance/SEO. * * IMPORTANTE sobre Muffin Builder: * Hay 2 formas de insertar HTML en Betheme: * (a) "Text/Column" dentro del editor de Betheme → Betheme puede * envolver/override el markup y romper nuestros hooks. * (b) "Code / Raw HTML" item de Muffin Builder → respeta el markup. * Usamos (b): en Muffin Builder se coloca UNA sola línea inerte * [flo_section name='01-hero'] * y el PHP carga el HTML real desde el archivo. Así el contenido * pesado vive en el theme (versionable) y Muffin sólo referencia. * ------------------------------------------------------------------ */ if ( ! defined( 'ABSPATH' ) ) { exit; } define( 'FLO_VER', '1.0.0' ); define( 'FLO_DIR', get_stylesheet_directory() ); define( 'FLO_URI', get_stylesheet_directory_uri() ); define( 'FLO_ASSETS', FLO_URI . '/assets' ); /* ================================================================== * 1. ENCOLADO DE CSS / JS GLOBALES * ================================================================== */ add_action( 'wp_enqueue_scripts', 'flo_enqueue_assets', 999 ); function flo_enqueue_assets() { // Versionado por filemtime → invalida caché al editar los archivos. $css_file = FLO_DIR . '/assets/css/flo-global.css'; $js_file = FLO_DIR . '/assets/js/flo-global.js'; $css_ver = file_exists( $css_file ) ? filemtime( $css_file ) : FLO_VER; $js_ver = file_exists( $js_file ) ? filemtime( $js_file ) : FLO_VER; // style.css del child (header) — SIN dependencias frágiles. wp_enqueue_style( 'flo-child', get_stylesheet_uri(), array(), FLO_VER ); // CSS global de FLO (todos los estilos del homepage). // SIN dependencia de handles de Betheme que podrían no existir. wp_enqueue_style( 'flo-global', FLO_ASSETS . '/css/flo-global.css', array(), $css_ver ); // Tipografías del brand (Michroma display + Inter body). wp_enqueue_style( 'flo-fonts', 'https://fonts.googleapis.com/css2?family=Michroma&family=Inter:wght@300;400;500;600&display=swap', array(), null ); // GSAP + ScrollTrigger — vendored localmente (sin CDN) por las // instrucciones: mantener archivos una vez aprobados. wp_enqueue_script( 'flo-gsap', FLO_ASSETS . '/js/gsap.min.js', array(), '3.12.5', true ); wp_enqueue_script( 'flo-scrolltrigger', FLO_ASSETS . '/js/ScrollTrigger.min.js', array( 'flo-gsap' ), '3.12.5', true ); // JS global de FLO (hero player, turntable, configuradores, bicolor). wp_enqueue_script( 'flo-global', FLO_ASSETS . '/js/flo-global.js', array( 'flo-gsap', 'flo-scrolltrigger' ), $js_ver, true ); // Pasa rutas de assets al JS (los frames y fotos se cargan por URL, // no embebidos, para no inflar el DOM). wp_localize_script( 'flo-global', 'FLO_CFG', array( 'assets' => FLO_ASSETS, 'bikes' => FLO_ASSETS . '/bikes', 'video' => FLO_ASSETS . '/video', 'heroFrames' => 75, // hero0000..hero0074.jpg 'ttFrames' => 97, // tt0000..tt0096.jpg 'ajax' => admin_url( 'admin-ajax.php' ), 'shop' => function_exists( 'wc_get_page_permalink' ) ? wc_get_page_permalink( 'shop' ) : home_url( '/tienda' ), ) ); } /* ================================================================== * 2. SHORTCODE DE SECCIÓN — [flo_section name='01-hero'] * Carga sections/01-hero.php como HTML crudo. * ================================================================== */ add_shortcode( 'flo_section', 'flo_render_section' ); function flo_render_section( $atts ) { $atts = shortcode_atts( array( 'name' => '' ), $atts, 'flo_section' ); $name = sanitize_file_name( $atts['name'] ); if ( ! $name ) { return ''; } $file = FLO_DIR . '/sections/' . $name . '.php'; if ( ! file_exists( $file ) ) { return current_user_can( 'manage_options' ) ? '' : ''; } ob_start(); include $file; // el PHP puede usar shortcodes de WooCommerce dentro return ob_get_clean(); } /* ================================================================== * 3. INYECTORES DE NAV Y FOOTER (wp_body_open / wp_footer) * PHP real: menús WP + carrito en vivo de WooCommerce. * ================================================================== */ add_action( 'wp_body_open', 'flo_inject_prelude', 1 ); function flo_inject_prelude() { $file = FLO_DIR . '/inc/prelude.php'; if ( file_exists( $file ) ) { include $file; } } add_action( 'wp_body_open', 'flo_inject_nav', 5 ); function flo_inject_nav() { $file = FLO_DIR . '/inc/nav.php'; if ( file_exists( $file ) ) { include $file; } } add_action( 'wp_footer', 'flo_inject_footer', 5 ); function flo_inject_footer() { $file = FLO_DIR . '/inc/footer.php'; if ( file_exists( $file ) ) { include $file; } // Contenedor de filtros SVG (liquid glass) — una sola vez, global. $svg = FLO_DIR . '/inc/glass-filters.php'; if ( file_exists( $svg ) ) { include $svg; } } /* ================================================================== * 4. MENÚS Y SOPORTE DE TEMA * ================================================================== */ add_action( 'after_setup_theme', 'flo_theme_setup' ); function flo_theme_setup() { register_nav_menus( array( 'flo_primary' => 'FLO — Menú principal', 'flo_footer' => 'FLO — Menú footer', ) ); add_theme_support( 'title-tag' ); add_theme_support( 'post-thumbnails' ); add_theme_support( 'woocommerce' ); } /* ================================================================== * 5. HELPERS WOOCOMMERCE PARA DATOS DINÁMICOS * Shortcodes propios que envuelven datos de producto para usar * dentro de las secciones (precio, nombre, imagen destacada…). * ================================================================== */ // [flo_product_price id="123"] → precio formateado del producto. add_shortcode( 'flo_product_price', function( $atts ) { $atts = shortcode_atts( array( 'id' => 0 ), $atts ); if ( ! function_exists( 'wc_get_product' ) ) { return ''; } $p = wc_get_product( (int) $atts['id'] ); return $p ? $p->get_price_html() : ''; } ); // [flo_product_name id="123"] add_shortcode( 'flo_product_name', function( $atts ) { $atts = shortcode_atts( array( 'id' => 0 ), $atts ); if ( ! function_exists( 'wc_get_product' ) ) { return ''; } $p = wc_get_product( (int) $atts['id'] ); return $p ? esc_html( $p->get_name() ) : ''; } ); // [flo_cart_count] → número de items en el carrito (para el nav). add_shortcode( 'flo_cart_count', function() { if ( ! function_exists( 'WC' ) || is_null( WC()->cart ) ) { return '0'; } return (string) WC()->cart->get_cart_contents_count(); } ); /* ================================================================== * 6. FRAGMENTO AJAX DEL CARRITO (contador en vivo del nav) * ================================================================== */ add_filter( 'woocommerce_add_to_cart_fragments', 'flo_cart_fragment' ); function flo_cart_fragment( $fragments ) { $count = ( function_exists( 'WC' ) && ! is_null( WC()->cart ) ) ? WC()->cart->get_cart_contents_count() : 0; ob_start(); echo '' . esc_html( $count ) . ''; $fragments['.flo-cart-count'] = ob_get_clean(); return $fragments; } /* ================================================================== * 7. DIAGNÓSTICO Y FALLBACK DE ASSETS * Si por alguna razón (modo Performance de Betheme, plugin de caché * agresivo, etc.) los assets encolados no aparecen, este bloque: * a) imprime un aviso en el admin si faltan los archivos, y * b) inyecta CSS/JS críticos directamente como respaldo. * ================================================================== */ // (a) Aviso en el admin si los archivos no existen. add_action( 'admin_notices', 'flo_assets_notice' ); function flo_assets_notice() { if ( ! current_user_can( 'manage_options' ) ) { return; } $css = FLO_DIR . '/assets/css/flo-global.css'; $js = FLO_DIR . '/assets/js/flo-global.js'; $missing = array(); if ( ! file_exists( $css ) ) { $missing[] = 'assets/css/flo-global.css'; } if ( ! file_exists( $js ) ) { $missing[] = 'assets/js/flo-global.js'; } if ( $missing ) { echo '
FLO: faltan archivos del tema: '
. esc_html( implode( ', ', $missing ) )
. '. Revisa que la carpeta assets/ se haya subido completa.
Something big is brewing! Our store is in the works and will be launching soon!