Back to Top for Elementor

“Back to Top” button with progressive fill based on scroll.
Automatically uses Elementor’s main color.

Simply copy paste the code inside a php file and place it inside your mu-plugins folder.

				
					<?php
/**
 * Plugin Name: Wycan Back to Top
 * Description: Bouton "Back to Top" avec remplissage progressif basé sur le scroll. Utilise automatiquement la couleur principale d'Elementor.
 * Version: 1.0.0
 * Author: Wycan
 * Author URI: https://wycan.fr
 * License: GPL-2.0+
 *
 */

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class Wycan_Back_To_Top {

    public function __construct() {
        add_action( 'wp_footer', [ $this, 'render_button' ] );
        add_action( 'wp_footer', [ $this, 'inline_styles' ] );
        add_action( 'wp_footer', [ $this, 'inline_scripts' ] );
    }

    /**
     * Récupère la couleur principale définie dans Elementor.
     */
    private function get_primary_color(): string {
        $default = '#000000';

        if ( ! did_action( 'elementor/loaded' ) ) {
            return $default;
        }

        // Elementor stocke le kit (Global Colors) dans un post type dédié.
        $kit = \Elementor\Plugin::$instance->kits_manager->get_active_kit_for_frontend();

        if ( ! $kit ) {
            return $default;
        }

        $settings = $kit->get_settings();

        // Les couleurs globales sont dans system_colors.
        if ( ! empty( $settings['system_colors'] ) && is_array( $settings['system_colors'] ) ) {
            foreach ( $settings['system_colors'] as $color ) {
                if ( isset( $color['_id'] ) && $color['_id'] === 'primary' && ! empty( $color['color'] ) ) {
                    return $color['color'];
                }
            }
        }

        // Fallback : custom_colors si la couleur primaire a été définie là.
        if ( ! empty( $settings['custom_colors'] ) && is_array( $settings['custom_colors'] ) ) {
            foreach ( $settings['custom_colors'] as $color ) {
                if ( isset( $color['_id'] ) && $color['_id'] === 'primary' && ! empty( $color['color'] ) ) {
                    return $color['color'];
                }
            }
        }

        return $default;
    }

    /**
     * Rendu HTML du bouton.
     */
    public function render_button(): void {
        ?>
        <div id="wycan-btt" class="wycan-btt" aria-label="<?php esc_attr_e( 'Retour en haut', 'wycan' ); ?>" role="button" tabindex="0">
            <svg class="wycan-btt-progress" viewBox="0 0 100 100">
                <circle class="wycan-btt-track" cx="50" cy="50" r="46"></circle>
                <circle class="wycan-btt-fill" cx="50" cy="50" r="46"></circle>
            </svg>
            <span class="wycan-btt-icon">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
                    <polyline points="17 11 12 6 7 11"></polyline>
                    <polyline points="17 18 12 13 7 18"></polyline>
                </svg>
            </span>
        </div>
        <?php
    }

    /**
     * Styles inline (aucun fichier externe requis).
     */
    public function inline_styles(): void {
        $color = $this->get_primary_color();
        ?>
        <style id="wycan-btt-css">
            .wycan-btt {
                --wycan-btt-color: <?php echo esc_attr( $color ); ?>;
                --wycan-btt-size: 48px;

                position: fixed;
                bottom: 30px;
                right: 30px;
                width: var(--wycan-btt-size);
                height: var(--wycan-btt-size);
                cursor: pointer;
                z-index: 9999;
                opacity: 0;
                visibility: hidden;
                transform: translateY(12px);
                transition: opacity .3s ease, visibility .3s ease, transform .3s ease;
            }

            .wycan-btt.is-visible {
                opacity: 1;
                visibility: visible;
                transform: translateY(0);
            }

            /* Cercle SVG de progression */
            .wycan-btt-progress {
                position: absolute;
                inset: 0;
                width: 100%;
                height: 100%;
                transform: rotate(-90deg);
            }

            .wycan-btt-track {
                fill: #ffffff;
                stroke: #e0e0e0;
                stroke-width: 4;
                filter: drop-shadow(0 2px 6px rgba(0, 0, 0, .08));
            }

            .wycan-btt-fill {
                fill: none;
                stroke: var(--wycan-btt-color);
                stroke-width: 4;
                stroke-dasharray: 289.027;
                stroke-dashoffset: 289.027;
                stroke-linecap: round;
                transition: stroke-dashoffset .1s linear;
            }

            /* Icône centrale */
            .wycan-btt-icon {
                position: absolute;
                inset: 0;
                display: flex;
                align-items: center;
                justify-content: center;
                color: var(--wycan-btt-color);
            }

            .wycan-btt-icon svg {
                width: 20px;
                height: 20px;
            }

            /* Hover */
            .wycan-btt:hover .wycan-btt-track {
                fill: var(--wycan-btt-color);
                transition: fill .25s ease;
            }

            .wycan-btt:hover .wycan-btt-icon {
                color: #ffffff;
                transition: color .25s ease;
            }

            .wycan-btt:hover .wycan-btt-fill {
                stroke: #ffffff;
            }

            /* Responsive */
            @media (max-width: 768px) {
                .wycan-btt {
                    --wycan-btt-size: 42px;
                    bottom: 20px;
                    right: 20px;
                }

                .wycan-btt-icon svg {
                    width: 17px;
                    height: 17px;
                }
            }
        </style>
        <?php
    }

    /**
     * JavaScript inline pour le scroll tracking et le smooth scroll.
     */
    public function inline_scripts(): void {
        ?>
        <script id="wycan-btt-js">
        (function () {
            var btn = document.getElementById('wycan-btt');
            if (!btn) return;

            var fill = btn.querySelector('.wycan-btt-fill');
            var circumference = 2 * Math.PI * 46; // r = 46
            var showThreshold = 300; // px avant d'afficher le bouton
            var ticking = false;

            function updateProgress() {
                var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
                var docHeight = document.documentElement.scrollHeight - document.documentElement.clientHeight;
                var progress = docHeight > 0 ? Math.min(scrollTop / docHeight, 1) : 0;

                // Remplissage du cercle
                fill.style.strokeDashoffset = circumference * (1 - progress);

                // Afficher / masquer
                if (scrollTop > showThreshold) {
                    btn.classList.add('is-visible');
                } else {
                    btn.classList.remove('is-visible');
                }

                ticking = false;
            }

            window.addEventListener('scroll', function () {
                if (!ticking) {
                    window.requestAnimationFrame(updateProgress);
                    ticking = true;
                }
            }, { passive: true });

            // Scroll to top au clic
            btn.addEventListener('click', function () {
                window.scrollTo({ top: 0, behavior: 'smooth' });
            });

            // Accessibilité clavier
            btn.addEventListener('keydown', function (e) {
                if (e.key === 'Enter' || e.key === ' ') {
                    e.preventDefault();
                    window.scrollTo({ top: 0, behavior: 'smooth' });
                }
            });

            // État initial
            updateProgress();
        })();
        </script>
        <?php
    }
}

new Wycan_Back_To_Top();

				
			
Want to leave a comment ?

Leave a Reply

Your email address will not be published. Required fields are marked *

Want to see more code snippets?

Back to Top for Elementor

“Back to Top” button with progressive fill based on scroll. Automatically uses Elementor's main color.

WooCommerce Gallery Hover for Elementor Loop

Displays the first image in the gallery when hovering over the featured image in Elementor loops.

Free shipping method with WooCommerce coupon

Offer free shipping… but only for the shipping method you choose.

WooCommerce Product Buyers List

Added a tab in WooCommerce to display buyers of a specific product with date filter.

Additional Emails per Products for WooCommerce

Adds a custom tab in WooCommerce products to write an email specific to each product. This will be sent in a dedicated email.

Create a custom gallery with Elementor

With this code we gonna add a custom gallery to an elementor page
Let's talk ?

A question about WordPress?
A web project to be outsourced to a freelancer?
I am your man!