Inject HTML into a page from a content script

Yes, that's possible. Use chrome.runtime.getURL to get an absolute URL for the resource. For example:

Step 1 (standard JavaScript):

fetch(chrome.runtime.getURL('/template.html')).then(r => r.text()).then(html => {
  document.body.insertAdjacentHTML('beforeend', html);
  // not using innerHTML as it would break js event listeners of the page
});

Step 1 (jQuery):

$.get(chrome.runtime.getURL('/template.html'), function(data) {
    $(data).appendTo('body');
    // Or if you're using jQuery 1.8+:
    // $($.parseHTML(data)).appendTo('body');
});

Step 2:

Register the resource in the manifest.json under web_accessible_resources:

  "web_accessible_resources": [
    "template.html",
    "foo.jpg"
  ]

Another way of doing it is to use new Fetch API:

If the file's name is modal.html - update manifest.json accordingly

    "web_accessible_resources": [
        "modal.html",
    ],

and inject it like this:

    fetch(chrome.runtime.getURL('/modal.html'))
        .then(response => response.text())
        .then(data => {
            document.getElementById('inject-container').innerHTML = data;
            // other code
            // eg update injected elements,
            // add event listeners or logic to connect to other parts of the app
        }).catch(err => {
            // handle error
        });

This is my approach using a synchronous XHR:

var xmlHttp = null;

xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", chrome.runtime.getURL ("src/inject/inject.html"), false );
xmlHttp.send( null );
    
var inject  = document.createElement("div");
inject.innerHTML = xmlHttp.responseText
document.body.insertBefore (inject, document.body.firstChild);

Without jQuery etc.