Use html inside shortcode function

There are several things you need to know, after which the problems become obvious:

  • you join strings together using . for example 'hello' . 'world' is the same as 'helloworld'
  • You end a statement/action/call with ;, this is you telling PHP that you have finished saying something, and you are going to say something new next, like a fullstop in a sentence
  • shortcodes must return their content as a string, you can’t use functions that echo or output directly to the browser, and you can’t use . to attach them to a string. the_title is one of those functions
  • you don’t have to put it all on a single line, it makes it very difficult to read. What are you going to do when it says the problem is on line 5 but line 5 has tonnes of stuff on it? ¯\_(ツ)_/¯ it’s like asking for directions and just being told “Somewhere north”
  • $string .= 'stuff'; is short hand for $string = $string . 'stuff';
  • you can’t just pull stuff out of thin air, e.g. $string, you ask PHP to append stuff to it but where did it come from? What is $string? You need to give things initial values. PHP is going to go “wtf is $string, they just asked us to add stuff to it but I’ve never heard of it, I’ll just create a new variable and pretend it’s "" and put a note in the PHP error log, hopefully this doesn’t break anything 🤞”. Think of it like being asked to go give Mary her coffee, who’s Mary? You’ve never been introduced to Mary, PHP is basically the same as you just making up an imaginary friend called Mary on the spot so it still makes sense.

Thankfully there isn’t anything in your HTML snippet that actually requires a shortcode other than automatically getting the title.

The above bullet points should make it clear what went wrong and how to fix your shortcode, but for the future, here’s a much easier method, output buffers.

function myshortcode( $atts ) : string {
    ob_start();
    ?>
    <p>Your HTML goes here, and it'll get intercepted and returned as a string variable when `ob_get_clean` is called.</p>
    <?php
    return ob_get_clean();
}

Now you can work the same way as ordinary PHP template files in your shortcode, and it even has type hinting so if you return something other than a string it’ll complain and tell you exactly what went wrong.