Adding custom shortcode to page leads to page without styling

Shortcodes need to return their values, but the_content() echoes the post content.

So you have two options. First is to capture all output and then return it at the end:

ob_start(); // Start capturing output;

while( $loop->have_posts() ) {
   $loop->the_post();
   the_content();
};

wp_reset_postdata();

return ob_get_clean(); // Return captured output;

That will be the cleanest way if you want to capture HTML and the output of template tags or partials.

The other option is to get the content into a variable. The ‘get’ equivalent of the_content() is get_the_content(), but it tries weird stuff with the ‘Read More’ tag etc. so the easiest way is to get the raw $post->post_content with the the_content filters applied:

global $post;

// etc. etc.

while( $loop->have_posts() ) {
   $loop->the_post();
   $content = apply_filters( 'the_content', $post->post_content );
};

wp_reset_postdata();

return $content;

Also note how instead of returning inside the loop I put the value in a variable and returned it at the end. This is because if you return earlier in the function then wp_reset_postdata() will never run, which could cause trouble with other loops on the page.