Adding (nested) callouts using the API

My question itself is simple, but I’m going to add some context later in the post in case someone has a better idea of how to achieve my actual goal. I also asked on Discord and didn’t get an answer, so I thought I’d post here as well for a wider audience.

The Question

I would like to add a callout using the Obsidian API. Currently, I’m doing something like this to get a very barebones callout.

function makeCallout(root, title, text) {
    // root is some container the callout goes into
    let calloutProps = {cls: "callout"} 
    let callout = root.createEl('div', calloutProps);
    let calloutTitle = callout.createEl('div', {cls: "callout-title"});
    let calloutTitleInner = calloutTitle.createEl('div', {cls: "callout-title-inner", "text": title});
    let calloutContent = callout.createEl('div', {cls: "callout-content", "text": text});
    // All the other divs (icon, fold, etc.)

    return callout;

But this has problems:

  1. Overall, it’s a pain to construct callouts. I would still have to add all the other divs manually, such as callout-fold (and the folding-related attributes to the callout div itself) if it needs to fold. Other things like icons, borders, etc. need to be added manually.
  2. I’d also have to figure out the icon and all that depending on the data-callout attribute, which applies the colors but not the icon.
  3. I also think this makes it difficult to use custom callouts defined via CSS snippets and theme the callouts.

Further, this has a few issues with Live Preview and folding callouts. Something like this folds in reading view but doesn’t fold in Live Preview:

let calloutDict = {cls: "callout is-collapsible is-collapsed", attr:{"data-callout-fold": "-"}} 
let callout = root.createEl('div', calloutProps);
// Remaining stuff ...

Ultimately, the question is: is there any simple way to add callouts using the API without building the entire thing from scratch? I’m not sure using raw markdown will work for my purposes (see below). (Bonus: do the callouts fold properly in live preview?)

Context and my actual goal

I want to pull all tasks from a note and divide them into nested callouts depending on tag, subtag, etc. The names of the tags, how many of them there are, levels of nesting, etc. are note-dependent and I don’t know them beforehand.

(I am currently playing around with this in dataviewjs blocks but plan to move to a plugin eventually. This is intended as part of a set of tools to track and plan first-principles calculations in computational condensed matter physics.)

I managed to get something that looks like this by playing around with raw markdown and dv.markdownTaskList from dataviewjs.

The plan is to eventually use custom CSS to style some of these callouts (multicolumn is a priority, for example). However, using raw markdown has a huge drawback: I can’t click on a task in the callout to go to the original task in the note, and checking a task in the callout doesn’t affect the original task (and vice versa). These are all perks you get with dv.taskList, so I’m trying to create a callout object (container? not sure of terminology) to use as a container for the task list.

(As an aside, it’s possible to specify a container with dv.el as, e.g., dv.el("b", "some bold text", {container: myContainer}) and it works with my primitive callouts, but I haven’t figured out how to do that with dv.taskList yet. That’s a problem for another post.)

If you think there’s a better way to achieve my actual goal, please let me know! I’m very new to JavaScript so I’m sure there are many simpler solutions I’m unaware of.