Add CSS class for each level in a nested tag, to enable fine-grained CSS selection in edit and preview modes

Use case or problem

I’d like to add context-sensitive color to nested tags in both preview and edit mode.

It is possible to style non-nested tags by using a CSS selector such as this: (for a tag #project)

.tag[href^="#project"],
.cm-s-obsidian span.cm-hashtag-begin.cm-tag-project, .cm-s-obsidian span.cm-hashtag-end.cm-tag-project

This works in both edit and preview mode.

HOWEVER this breaks down when nested tags are used. The nested tag #project/test-project-a is NOT colored by the above rule.

In preview mode the above renders as:

<a href="#project/test-project-a" class="tag" target="_blank" rel="noopener">#project/test-project-a</a>

This is easy to create a CSS selector for because we can just target the beginning of the string:

.tag[href^="#project"]

But we can’t do this in edit mode due to the way edit mode constructs the CSS classes.

This is because the editor embeds the entire tag name as a CSS class in edit mode. Typing the above tag in edit mode renders as the following in edit mode:

<span class="cm-hashtag cm-meta cm-hashtag-end cm-tag-projecttest-project-a">project/test-project-a</span>

As you can see, the only way to color this tag is to modify the CSS snippet every time a new project is created, to add a selector for cm-tag-projecttest-project-a.

Proposed solution

Add each component of a nested tag as a separate CSS class.

Given tag: #foo/bar/baz

Render this in edit mode:

<span class="cm-hashtag cm-meta cm-hashtag-end cm-tag-foo cm-tag-foo-bar cm-tag-foo-bar-baz">project/test-project-a</span>

Notice the tag is split into three separate classes, one for each nesting level. This allows easy selector definition for defining scoped colors in edit mode.

And in preview mode:

<a href="#project/test-project-a" class="tag tag-foo tag-foo-bar tag-foo-bar-baz" target="_blank" rel="noopener">#project/test-project-a</a>

Current workaround (optional)

None.

Related feature requests (optional)

This question has come up several times in various places, for example:

Would something like this work for you in editor mode?

.cm-hashtag[class*="cm-tag-project"]

No but after a small bit of tinkering I DID get this to work.

It just needed a tiny smidge more specificity even with !important. ¯\_(ツ)_/¯

.tag[href^="#project"],
span.cm-hashtag[class*="cm-tag-project"]
{
  background-color: var(--project-tag-background) !important;
}

:point_up_2:This now colors tags in both preview AND edit mode, with a single declaration covering both #project and #project/anything-here/and-more-here-is-fine-too!

I had completely forgotten about the wildcard selector, thanks!! :smiley: :beers:

1 Like

I am trying to create the pill tag in the note. Using the code above, I get this in edit mode, which is good thing:
image

But, once I clicked elsewhere in the note, the tag is displayed as such. Notice that the hash itself is in a separate pill tag:

image

Any insights will be much appreciated!

This is the code I am using:

.tag, div:not(.CodeMirror-activeline) > .CodeMirror-line span.cm-hashtag-end {
  background-color: var(--text-accent);
  border: none;
  color: white !important;
  font-size: 16px; var(--font-size-tag);
  padding: 1px 8px;
  text-align: center;
  text-decoration: none !important;
  display: inline-block;
  margin: 0px 0px;
  cursor: pointer;
  border-radius: 14px;
}
/* Tag color in preview & edit mode */
.tag[href^="#obsidian"],
span.cm-hashtag[class*="cm-tag-obsidian"] {
  background-color: #4d3ca6 !important;
  color: white !important;
}
.tag[href^="#important"] {
  background-color: red;
}
.tag[href^="#complete"] {
  background-color: green;
}
.tag[href^="#inprogress"] {
  background-color: orange;
}

Did you ever resolve this?

What about non-latin tags? Are there rule for naming css classes for these?