Tag Styling

I had the idea to add a data-tag-name attribute to tags that are created so that #my-tag would look like this: <span class="sb-hashtag" data-tag-name="my-tag">#my-tag</span>.

My Initial use case was to be able to easily add distinct coloring/styling to a given tag via space-style (ie. [data-tag-name="my-tag"] {background-color: limegreen}).

I decided using the data-* attribute over just adding a CSS class of tag-my-tag because I thought it would also have the added benefit of providing a handle to a tag that could potentially be used with the new schema additions, possibly facilitating the ability to add an attribute schema to a tag.

I am open to hearing what others think about this approach so feel free to chime in.

In the mean time, I have not been able to figure out where SB is handling the insertion of that <span...>#my-tag</span> when you add a tag to the editor.

I found this code in hide-marks.ts where I thought that was being handled and modified it like this:

// FILE: web/cm_plugins/hide_marks.ts
...
      if (line === "#") {
          // Empty header, potentially a tag, style it as such
          widgets.push(
            Decoration.mark({
              tagName: "span",
              class: "sb-hashtag test-class",
              attributes: {
                "data-tag-name": "test" // hardcoded just to prove functionality for now
              },
            }).range(from, from + 1),
          );

However, this did not add the attribute or the css class that I added. Additionally I quickly realized that this code was for handling the case where a line starts with a # and was not triggered if the tag was not the first thing on the line, so I continued looking…

I found this code that handles where rendered tags are created (my understanding is this handles where tags are rendered in a page a query or include, please do correct me if this assumption is wrong too btw), modified it as follows, and this does work as I expected, adding the attribute and allowing for targeted styling:

// FILE: plugs/markdown/markdown-render.ts
...
    case "Hashtag": {
      const tagText: string = t.children![0].text!;
      return {
        name: "span",
        attrs: {
          class: "hashtag",
          "data-tag-name": tagText.replace("#", ""),
        },
        body: tagText,
      };
    }
...

… and finally I tried modifying the parser to see if that would make any change to the <span> inserted around a tag:

// FILE: common/markdown_parser/parser.ts
const Hashtag = regexParser({
  firstCharCode: 35, // #
  regex: new RegExp(`^${tagRegex.source}`),
  nodeType: "Hashtag",
  className: "sb-test",
  tag: ct.HashtagTag,
});

… Still no luck…

So I am hoping that someone can help clarify my understanding about how the insertion of the elements around a tag work… I would guess my limited understanding of how codemirror works could be part of the problem but my understanding was that this was facilitated through codemirror’s Decorations via the mark() function in the first example. I intend to dive more into how codemirror works and then, more specifically into the lang-markdown plugin but figured I would post this in the mean time to get input from the community as well.

Thanks in advance for any responses!

Indeed, this is just to handle an edge case where it’s ambiguous whether you’re going to type a header or a hashtag, you can ignore it for the purposes of what you’re trying to do.

Yes, this code handles rendering of markdown with live preview, so indeed in templates and query renderings for instance. It’s one of the places where you’d want to make your change.

Yes, you cannot really make styling changes here. In style.ts on line 52, you see:

{ tag: ct.HashtagTag, class: "sb-hashtag" },

But unfortunately you cannot add content-based styling this way, just additional fixed style attributes.

To do what you want to do, you unfortunately would have to create a codemirror plugin along the lines of web/cm_plugins/link.ts (which replaces the rendering a link with a custom <a href> element). What you can do is create a copy of this plugin, load that in clean.ts, but let it act on “Hashtag” nodes instead of “Link” nodes, and work from there. Basically you’d want it to render as a custom span tag, and attach the data attributes there.

Hopefully this is enough of a pointer to help, I could dive deeper but quickly I’d end up giving you the actual implementation, which would be cheating :wink:

1 Like

Thanks! I think this should get me going, my intent was to address #938 at the same time too fwiw.