My css snippets for outline, inspired by logseq bullet threading plugin

outline

inspired by logseq plugin, and now it’s part of my theme, and here I extract it as a snippet

body {
  --outline-guideline-width: var(--size-2-1);
  --outline-guideline-color: var(--accent-active);
  --outline-item-height: calc(var(--nav-item-size) * 1.8);
  /*active color*/
  --accent-active: hsl(var(--accent-h),
      var(--accent-s),
      calc(var(--accent-l) + 4%));
}

.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item {
  position: relative;
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item-self {
  position: relative;
  margin-bottom: 0;
  white-space: nowrap;
  margin-top: -1px !important;
  /* fix item gap */
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item-self .tree-item-inner {
  padding-left: var(--size-4-4);
  overflow: hidden;
  text-overflow: ellipsis;
  height: var(--outline-item-height);
  line-height: var(--outline-item-height);
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item-self .tree-item-inner::before {
  content: "";
  width: var(--size-4-1);
  height: var(--size-4-1);
  border: var(--size-2-1) solid var(--outline-guideline-color);
  border-radius: 50%;
  position: absolute;
  left: 7px;
  top: 50%;
  transform: translateY(-50%);
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item-self .tree-item-icon ~ .tree-item-inner {
  padding-left: var(--size-4-1);
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item-self .tree-item-icon ~ .tree-item-inner::before {
  content: none;
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item.is-collapsed .tree-item-icon::before {
  box-shadow: 0 0 0 var(--size-4-1) var(--background-modifier-hover);
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item::after {
  content: "";
  width: var(--outline-guideline-width);
  position: absolute;
  background-color: transparent;
  top: calc(var(--outline-item-height) / 2 * -1);
  left: -10px;
  height: calc(100% - var(--outline-item-height) + var(--size-4-8));
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item-icon {
  cursor: pointer;
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item-icon::before {
  width: var(--size-4-2);
  height: var(--size-4-2);
  background-color: var(--outline-guideline-color);
  border-radius: 50%;
  position: absolute;
  left: 7px;
  top: 50%;
  transform: translateY(-50%);
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item-icon svg path {
  display: none;
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item:hover > .tree-item-children > .tree-item::after {
  background-color: var(--outline-guideline-color);
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item:hover > .tree-item-self:hover + .tree-item-children .tree-item::after {
  background-color: transparent;
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item:hover > .tree-item-children > .tree-item:hover::after, .workspace-leaf-content[data-type=outline] .view-content .outline .tree-item:hover > .tree-item-children > .tree-item:hover ~ .tree-item::after {
  background-color: transparent;
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item:hover > .tree-item-children > .tree-item:hover::before {
  content: "";
  position: absolute;
  top: calc(var(--outline-item-height) / 2 * -1);
  left: -10px;
  bottom: calc(100% - (var(--outline-item-height) + var(--size-4-2)) / 2 - 1px);
  width: var(--size-4-4);
  border-bottom-left-radius: var(--radius-m);
  border-bottom: var(--outline-guideline-width) solid var(--outline-guideline-color);
  border-left: var(--outline-guideline-width) solid var(--outline-guideline-color);
}
.workspace-leaf-content[data-type=outline] .view-content .outline :is(.tree-item-children, .tree-item-self .tree-item-self) {
  padding-left: 0;
  margin-left: var(--size-4-5);
  border-left: none;
}

In addition: waiting for official electron version update to 21+ for :has() selector!

5 Likes

Had to change this to var(--size-4-4); to get the top level headers gap to look right in my theme but other than that it looks really impressive! Thanks for sharing.

2 Likes

this seems very interesting. but i don’t get it. how can i test it? u only share the css here and i feel that’s only part of it. how do i apply it?

Create a file eg. outline.css and paste this example into it. Then save the file in your vaults .obsidian/snippets folder. Then in your settings under Appearance - CSS Snippets, you need to enable the snippet to apply it.

For me the bullet threading does not seem to work. I’ve also tried with a new vault without any other themes/plugins activated. I saved the code as described an enabled it. I used LF linebreaks and UTF-8.

I am using Obsidian 1.0.3 on Windows if that helps.

Any help would be appreciated, thanks!

When you paste the code into the file outline.css use ctrl+shift+v rather than ctrl+v.
Try that and see if it works.

Unfortunately still no effect is shown. Do you have another idea? Thanks!

I am using Obsidian 1.0.3 on Windows btw.

I’m on windows 10 and it works ok for me. Only thing I can think of is your theme might be overriding it? If you can, post a screenshot of your outline panel so we can see what is showing.

My outline looks like this:
grafik
I am with my cursor on test1, in dark mode and in live mode. I tried reading mode as well.

I am on the default theme and have the snippet enabled:

The contents of outline.css is the code copied from @subframe7536. Do you need anything else?

Thank you!

Yeah that does not look right. The only thing I changed was the line I posted above as the lines were not lining up for me, so you can try that. Also restart Obsidian if you have not already. Otherwise delete the file and recreate it making sure to use ctrl+shift+v to paste it in (code copied from the forum will not format properly with normal ctrl+v pasting).

Here is my snippet with the one change, try this one.

/* Outline CSS Snippet
https://forum.obsidian.md/t/my-css-snippets-for-outline-inspired-by-logseq-bullet-threading-plugin/48558
*/
body {
  --outline-guideline-width: var(--size-2-1);
  --outline-guideline-color: var(--accent-active);
  --outline-item-height: calc(var(--nav-item-size) * 1.8);
  /*active color*/
  --accent-active: hsl(var(--accent-h),
      var(--accent-s),
      calc(var(--accent-l) + 4%));
}

.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item {
  position: relative;
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item-self {
  position: relative;
  margin-bottom: 0;
  white-space: nowrap;
  margin-top: -5px !important;
  /* fix item gap */
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item-self .tree-item-inner {
  padding-left: var(--size-4-4);
  overflow: hidden;
  text-overflow: ellipsis;
  height: var(--outline-item-height);
  line-height: var(--outline-item-height);
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item-self .tree-item-inner::before {
  content: "";
  width: var(--size-4-1);
  height: var(--size-4-1);
  border: var(--size-2-1) solid var(--outline-guideline-color);
  border-radius: 50%;
  position: absolute;
  left: 7px;
  top: 50%;
  transform: translateY(-50%);
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item-self .tree-item-icon ~ .tree-item-inner {
  padding-left: var(--size-4-4); /* Changed from 'var(--size-4-1);' now looks right */
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item-self .tree-item-icon ~ .tree-item-inner::before {
  content: none;
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item.is-collapsed .tree-item-icon::before {
  box-shadow: 0 0 0 var(--size-4-1) var(--background-modifier-hover);
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item::after {
  content: "";
  width: var(--outline-guideline-width);
  position: absolute;
  background-color: transparent;
  top: calc(var(--outline-item-height) / 2 * -1);
  left: -10px;
  height: calc(100% - var(--outline-item-height) + var(--size-4-8));
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item-icon {
  cursor: pointer;
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item-icon::before {
  width: var(--size-4-2);
  height: var(--size-4-2);
  background-color: var(--outline-guideline-color);
  border-radius: 50%;
  position: absolute;
  left: 7px;
  top: 50%;
  transform: translateY(-50%);
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item-icon svg path {
  display: none;
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item:hover > .tree-item-children > .tree-item::after {
  background-color: var(--outline-guideline-color);
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item:hover > .tree-item-self:hover + .tree-item-children .tree-item::after {
  background-color: transparent;
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item:hover > .tree-item-children > .tree-item:hover::after, .workspace-leaf-content[data-type=outline] .view-content .outline .tree-item:hover > .tree-item-children > .tree-item:hover ~ .tree-item::after {
  background-color: transparent;
}
.workspace-leaf-content[data-type=outline] .view-content .outline .tree-item:hover > .tree-item-children > .tree-item:hover::before {
  content: "";
  position: absolute;
  top: calc(var(--outline-item-height) / 2 * -1);
  left: -10px;
  bottom: calc(100% - (var(--outline-item-height) + var(--size-4-2)) / 2 - 1px);
  width: var(--size-4-4);
  border-bottom-left-radius: var(--radius-m);
  border-bottom: var(--outline-guideline-width) solid var(--outline-guideline-color);
  border-left: var(--outline-guideline-width) solid var(--outline-guideline-color);
}
.workspace-leaf-content[data-type=outline] .view-content .outline :is(.tree-item-children, .tree-item-self .tree-item-self) {
  padding-left: 0;
  margin-left: var(--size-4-5);
  border-left: none;
}

Been playing around with this some more and I just noticed that in my theme Blue Topaz it still shows a bit of the drop down arrow? Tried this in 10 different themes as well as the default theme and it works great in all of them except for ITS Theme which is completely messed up and, like I said Blue Topaz which looks good other than a bit of the drop down arrow is still showing (Have not been able to get rid of that).

Thank you for the advice. Unfortunately, still no bullet threading is shown for me.

I took the following steps:

  1. Open Obsidian Sandbox vault (Help → Sandbox vault)
  2. Go to Settings → Appearance → CSS snippets and open the folder
  3. Create a new file called outline.css
  4. Open the file with VS Code
  5. Copy the snippet from your post, paste it with Ctrl+Shift+V and save the file
  6. Go back to Obsidian → Appearance and enable the snippet outline

This did not change anything for me. I tried both light and dark theme as well as live mode and read mode.
I then installed Blue Topaz and tried it again with light and dark theme as well as live mode and read mode without success:

I don’t even see anything messed up, as if the snippet is not enabled.
Did you enable anything else is the settings? Maybe you could try the steps above as well to see if it still works for you in a new/different vault.

@willasm I figured it out!

Apparently I misunderstood what this snippet was for. I was under the impression that bullet threading would work for any list whereas this snippet is for the outline view (on the left panel) only.

In the outline view it works great! Thank you for your help.

Do you know if something similar is possible for a list within a note?

Sorry, not that I’m aware of. I have several themes installed and none of them do anything to lists.

I see, thank you for your support @willasm!

sorry, but it can’t until the version of electron update to 21+ officially :smiling_face_with_tear:.there is no hierarchy in the dom tree of editor area, so i can’t get the preceding node without :has selector. this selector is only supported in electron 21+. so currently it only effect in outline panel