Studiohni News Slider

PHP
<?php
/**
 * Shortcode: [news-slider]
 * In functions.php einfügen oder als kleines Plugin verwenden.
 */

function news_slider_shortcode() {
    $args = [
        'post_type'           => 'post',
        'post_status'         => 'publish',
        'posts_per_page'      => -1,
        'ignore_sticky_posts' => false,
        'orderby'             => 'date',
        'order'               => 'DESC',
    ];

    $query = new WP_Query( $args );

    if ( ! $query->have_posts() ) {
        return '';
    }

    // Sticky zuerst sortieren
    $sticky_ids = get_option( 'sticky_posts' );
    $stickies   = [];
    $regulars   = [];

    while ( $query->have_posts() ) {
        $query->the_post();
        $post = get_post();
        if ( in_array( $post->ID, $sticky_ids ) ) {
            $stickies[] = $post;
        } else {
            $regulars[] = $post;
        }
    }
    wp_reset_postdata();

    $posts = array_merge( $stickies, $regulars );
    $total = count( $posts );

    // Gutenberg Block-Rendering vorbereiten
    if ( ! function_exists( 'render_block' ) ) {
        require_once ABSPATH . 'wp-includes/blocks.php';
    }

    $svg_next = '<svg width="100%" height="100%" viewBox="0 0 100 177" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><g transform="matrix(-1.93606,0,0,1.93606,235.82,-113.022)"><circle cx="115.274" cy="62.93" r="4.455" style="fill:white;"></circle></g><g transform="matrix(-1.93606,0,0,1.93606,255.26,-94.242)"><circle cx="115.274" cy="62.93" r="4.455" style="fill:white;"></circle></g><g transform="matrix(-1.93606,0,0,1.93606,275.661,-73.4043)"><circle cx="115.274" cy="62.93" r="4.455" style="fill:white;"></circle></g><g transform="matrix(-1.93606,0,0,1.93606,295.039,-53.9325)"><circle cx="115.274" cy="62.93" r="4.455" style="fill:white;"></circle></g><g transform="matrix(-1.93606,0,0,1.93606,314.553,-34.7509)"><circle cx="115.274" cy="62.93" r="4.455" style="fill:white;"></circle></g><g transform="matrix(-1.93606,0,0,1.93606,293.866,-14.2368)"><circle cx="115.274" cy="62.93" r="4.455" style="fill:white;"></circle></g><g transform="matrix(-1.93606,0,0,1.93606,273.823,5.27794)"><circle cx="115.274" cy="62.93" r="4.455" style="fill:white;"></circle></g><g transform="matrix(-1.93606,0,0,1.93606,252.835,25.6766)"><circle cx="115.274" cy="62.93" r="4.455" style="fill:white;"></circle></g><g transform="matrix(-1.93606,0,0,1.93606,231.803,46.5381)"><circle cx="115.274" cy="62.93" r="4.455" style="fill:white;"></circle></g></svg>';

    $svg_prev = '<svg width="100%" height="100%" viewBox="0 0 100 177" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><g transform="matrix(1.93606,0,0,1.93606,-135.819,-113.022)"><circle cx="115.274" cy="62.93" r="4.455" style="fill:white;"></circle></g><g transform="matrix(1.93606,0,0,1.93606,-155.259,-94.242)"><circle cx="115.274" cy="62.93" r="4.455" style="fill:white;"></circle></g><g transform="matrix(1.93606,0,0,1.93606,-175.66,-73.4043)"><circle cx="115.274" cy="62.93" r="4.455" style="fill:white;"></circle></g><g transform="matrix(1.93606,0,0,1.93606,-195.038,-53.9325)"><circle cx="115.274" cy="62.93" r="4.455" style="fill:white;"></circle></g><g transform="matrix(1.93606,0,0,1.93606,-214.552,-34.7509)"><circle cx="115.274" cy="62.93" r="4.455" style="fill:white;"></circle></g><g transform="matrix(1.93606,0,0,1.93606,-193.865,-14.2368)"><circle cx="115.274" cy="62.93" r="4.455" style="fill:white;"></circle></g><g transform="matrix(1.93606,0,0,1.93606,-173.822,5.27794)"><circle cx="115.274" cy="62.93" r="4.455" style="fill:white;"></circle></g><g transform="matrix(1.93606,0,0,1.93606,-152.834,25.6766)"><circle cx="115.274" cy="62.93" r="4.455" style="fill:white;"></circle></g><g transform="matrix(1.93606,0,0,1.93606,-131.802,46.5381)"><circle cx="115.274" cy="62.93" r="4.455" style="fill:white;"></circle></g></svg>';

    $uid = 'ns-' . uniqid();

    ob_start();
    ?>
    <div class="news-slider" id="<?php echo esc_attr( $uid ); ?>" data-total="<?php echo $total; ?>">
        <div class="news-slider__track">
            <?php foreach ( $posts as $i => $post ) :
                $GLOBALS['post'] = $post;
                setup_postdata( $post );

                $thumb_id  = get_post_thumbnail_id( $post->ID );
                $thumb_url = $thumb_id ? wp_get_attachment_image_url( $thumb_id, 'large' ) : '';
                $content   = apply_filters( 'the_content', $post->post_content );
                $title     = get_the_title( $post->ID );
            ?>
            <div class="news-slider__slide<?php echo $i === 0 ? ' is-active' : ''; ?>" aria-hidden="<?php echo $i === 0 ? 'false' : 'true'; ?>">
                <div class="news-slider__image" style="<?php echo $thumb_url ? 'background-image:url(' . esc_url( $thumb_url ) . ');' : ''; ?>"></div>
                <div class="news-slider__content">
                    <h2 class="news-slider__title"><?php echo esc_html( $title ); ?></h2>
                    <div class="news-slider__body"><?php echo $content; ?></div>
                </div>
            </div>
            <?php endforeach;
            wp_reset_postdata(); ?>
        </div>

        <?php if ( $total > 1 ) : ?>
        <button class="news-slider__btn news-slider__btn--prev" aria-label="Vorheriger Beitrag">
            <?php echo $svg_prev; ?>
        </button>
        <button class="news-slider__btn news-slider__btn--next" aria-label="Nächster Beitrag">
            <?php echo $svg_next; ?>
        </button>

        <div class="news-slider__pagination">
            <?php for ( $i = 0; $i < $total; $i++ ) : ?>
            <button class="news-slider__dot<?php echo $i === 0 ? ' is-active' : ''; ?>" data-index="<?php echo $i; ?>" aria-label="Beitrag <?php echo $i + 1; ?>"></button>
            <?php endfor; ?>
        </div>
        <?php endif; ?>
    </div>

    <style>
    .news-slider {
        position: relative;
        overflow: hidden;
    }
    .news-slider__track {
        position: relative;
    }
    .news-slider__slide {
        display: none;
        align-items: stretch;
        min-height: 450px;
    }
    .news-slider__slide.is-active {
        display: flex;
    }
    .news-slider__image {
        flex: 0 0 30%;
        background-size: cover;
        background-position: center;
        background-repeat: no-repeat;
    }
    .news-slider__content {
        flex: 0 0 70%;
        padding: 32px 48px;
        overflow: auto;
        background: #a89968;
    }
    .news-slider__title {
        margin-top: 0;
        margin-bottom: 0.75rem;
        color: #fff;
        line-height: 1.2 !important;
    }
    .news-slider__body {
        color: #fff;
    }

    .news-slider__body p {
        color: #fff;
        line-height: 1.2 !important;
    }
    .news-slider__btn {
        position: absolute;
        top: 50%;
        transform: translateY(-50%);
        width: 40px;
        height: 80px;
        background: transparent;
        border: none;
        cursor: pointer;
        padding: 0;
        z-index: 10;
    }
    .news-slider__btn--prev { left: 16px; }
    .news-slider__btn--next { right: 16px; }
    .news-slider__pagination {
        display: flex;
        justify-content: center;
        gap: 8px;
        margin-top: 1rem;
    }
    .news-slider__btn:hover,
    .news-slider__btn:focus,
    .news-slider__btn:focus-visible {
        background: transparent;
        box-shadow: none;
    }
    .news-slider__dot {
        width: 12px;
        height: 12px;
        border-radius: 50%;
        border: 2px solid currentColor;
        background: transparent;
        cursor: pointer;
        padding: 0;
    }
    .news-slider__dot.is-active {
        background: currentColor;
    }
    @media all and (max-width: 768px) {
        .news-slider__slide {
            flex-direction: column;
        }
        .news-slider__image {
            flex: 0 0 250px;
            height: 250px;
        }
        .news-slider__content {
            padding: 24px;
        }
        .news-slider__btn {
            position: absolute;
            top: 50%;
            transform: translateY(-50%);
            width: 16px;
            height: 36px;
            background: transparent;
            border: none;
            cursor: pointer;
            padding: 0;
            z-index: 10;
        }   
        .news-slider__btn--prev { left: 0; }
        .news-slider__btn--next { right: 0; }
    }
    </style>

    <script>
    (function() {
        const slider = document.getElementById('<?php echo esc_js( $uid ); ?>');
        if (!slider) return;

        const slides = slider.querySelectorAll('.news-slider__slide');
        const dots   = slider.querySelectorAll('.news-slider__dot');
        let current  = 0;
        const total  = slides.length;

        function goTo(index) {
            slides[current].classList.remove('is-active');
            slides[current].setAttribute('aria-hidden', 'true');
            dots[current].classList.remove('is-active');

            current = (index + total) % total;

            slides[current].classList.add('is-active');
            slides[current].setAttribute('aria-hidden', 'false');
            dots[current].classList.add('is-active');
        }

        slider.querySelector('.news-slider__btn--prev').addEventListener('click', () => goTo(current - 1));
        slider.querySelector('.news-slider__btn--next').addEventListener('click', () => goTo(current + 1));
        dots.forEach(dot => dot.addEventListener('click', () => goTo(+dot.dataset.index)));
    })();
    </script>
    <?php
    return ob_get_clean();
}
add_shortcode( 'news-slider', 'news_slider_shortcode' );