How to use posts as tabs in a custom page?

For me, I offer you a solution composed of three parts:

1 – Create a CTP.

2 – Create the necessary javascript and css scripts.

3 – Create a dedicated template in the root of your theme archive-$posttype.php, in our example will be archive-product.php

I created a repo in my git where you can download the Plugin here is the link: Custom CPT

The source code for the archive-product.php page is here: archive-product

So you can download them directly to test and adapt them to your needs.
I used the twentynineeen theme, you have to create a physical page named: archive-product.php and its code is indicated above in the gist.
The JS and CSS code is quite basic, so improve it for your needs.

The main file :

/**
* Plugin Name:     Dro Cpt
* Plugin URI:      PLUGIN SITE HERE
* Description:     PLUGIN DESCRIPTION HERE
* Author:          YOUR NAME HERE
* Author URI:      YOUR SITE HERE
* Text Domain:     dro-cpt
* Domain Path:     /languages
* Version:         0.1.0
*
* @package         Dro_Cpt
*/ 
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}
// Hook our Plugin 
  add_action( 'plugins_loaded', 'dro_cpt' );

  function dro_cpt() {

/**
 * Create our CPT
*/
   add_action( 'init', function() {
    $labels = array(
        'name' => _x('Products', 'post type general name'),
        'singular_name' => _x('Product', 'post type singular name'),
        'add_new' => _x('Add New', 'book'),
        'add_new_item' => __('Add New Product'),
        'edit_item' => __('Edit Product'),
        'new_item' => __('New Product'),
        'all_items' => __('All Products'),
        'view_item' => __('View Product'),
        'search_items' => __('Search Products'),
        'not_found' => __('No products found'),
        'not_found_in_trash' => __('No products found in the Trash'),
        'parent_item_colon' => '',
        'menu_name' => 'Products'
    );
    $args = array(
        'labels' => $labels,
        'description' => 'Holds our products and product specific data',
        'public' => true,
        'menu_position' => 5,
        'supports' => array('title', 'editor', 'thumbnail', 'excerpt'),
        'has_archive' => true, // Enabling a post type archive at http://yoursite.com/product
    );
    register_post_type( 'product', $args ); // thanks  Daniel Pataki
} );
}

/**
* Enqueue the js and css juste for our CPT .
*/
add_action( 'template_redirect', function() {
global $wp_query;
// Load the assets juste for our CPT archive page.
if ( $wp_query->is_post_type_archive('product') ) {

    add_action( 'wp_enqueue_scripts', 'dro_cpt_scripts' );

    function dro_cpt_scripts() {
        wp_enqueue_script( 'dro-cpt-js', plugin_dir_url(__FILE__) . 'assets/js/dro-cpt.js', array('jquery'), '1.0.0', TRUE );
        wp_enqueue_style( 'dro-cpt-css', plugin_dir_url(__FILE__) . 'assets/css/dro-cpt.css' );
    }

}
});

The archive-product.php (twentynineteen/archive-product.php) :

<?php
/**
 * The template for displaying archive pages
 *
 * @link https://developer.wordpress.org/themes/basics/template-hierarchy/
 *
 * @package WordPress
 * @subpackage Twenty_Nineteen
 * @since 1.0.0
 */

get_header();
?>

    <section id="primary" class="content-area">
        <main id="main" class="site-main">
                    <div class="dro-cpt">
        <?php if ( have_posts() ) : ?>

            <header class="page-header">
                <?php
                    the_archive_title( '<h1 class="page-title">', '</h1>' );
                ?>
            </header><!-- .page-header -->

            <?php
            // Start the Loop.
            while ( have_posts() ) :
                the_post();

                                the_title('<a class="dro-cpt-tab" href="#" data-id="'.get_the_ID().'">','</a>',TRUE);
                                echo '<div class="dro-cpt-description" id= "description-'.get_the_ID().'">';
                                the_post_thumbnail( 'thumbnail' );
                                echo '<div class=" single-description " >'.the_content().'</div>';
                                echo "</div>";
                // End the loop.
            endwhile;

            // Previous/next page navigation.
            twentynineteen_the_posts_navigation();

            // If no content, include the "No posts found" template.
        else :
            get_template_part( 'template-parts/content/content', 'none' );

        endif;
        ?>
                        <div class="dro-cpt-display"></div>
                    </div><!-- .dro-cpt -->
        </main><!-- #main -->
    </section><!-- #primary -->

<?php
get_footer();

The js ( plugins/dro-cpt/assets/dro-cpt.js) :

(function($){

    // show the first element
    var firstDescr =  $('div.dro-cpt-description').first().html();
    $('div.dro-cpt-display').html(firstDescr);

    $('a.dro-cpt-tab').on('click',function(e){
        e.preventDefault();
        var id = $(this).data('id');
        var desc = $('#description-'+id).html();
        console.log(desc);
        $('div.dro-cpt-display').html(desc);
    });
})(jQuery);

The CSS (plugins/dro-cpt/assets/dro-cpt.css):

div.dro-cpt{

    max-width: 800px;
    margin: 0 auto;
    border: 1px solid #ccc;
}
div.dro-cpt a{
    text-decoration: none;
    color: #000;
    font-size: 1rem;
    border: 1px solid #ccc;
    padding: 5px 10px;
    border-radius: 5px;
    margin-right: 5px;
}
div.dro-cpt a:hover{
    background-color: #f5f5f5;
}
.dro-cpt-description{
    display: none;
}
div.dro-cpt-display{
    margin-top: 10px;
}