Bullet Threading in obsidian

Logseq Bullet Threading in Obsidian

Create Logseq Like Bullet Threading in Obsidian

Logseq has this cool plugin called bullet threading. It adds bullet threading to active blocks in logseq. If you are missing this feature in Obsidian or you want to use the bullet threading feature in Obsidian, this article is for you. 

This solution that I provide in this article will allow you to have the same connection that is in the Logseq app for bullets. And not just for bullets, but also for outlines. 

It will be helpful if you work with a lot of bullets and indentations. 

For people thinking is it only for aesthetic purposes? Yes, it is. 

It is for aesthetic purposes if you work with smaller outlines. However, as the size of the outline increases, the utility of this feature increases dramatically. 

It helps you stay on track. When you are working with large outlines and many indention levels, you will never get lost. You can easily see what is indented under what. 

So, let’s dive in. 

To enable this feature in Obsidian, we’ll need to use a CSS snippet. 

Here’s how to add a Custom CSS in your Obsidian vault:

  • Go to settings
  • Go to AppearanceCSS snippets, select Open snippets folder (folder icon)
  • In the snippets either create a new CSS file or paste the file you have downloaded

Okay, let’s talk about getting the bullet threading CSS. 

There are three parts to this.

Outliner Threading

This CSS snippet will add bullet threading to your outline only. When you open the outliner in the sidebar, you will see it in action. Here is the source of the CSS snippet. And here is the CSS Code:

Remember this only works on the outliner pane. To get the threading feature for bullets, try the next steps.

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;
}

Bullet Threading in Editing Mode

This was recently shared in the Obsidian Forum and it works flawlessly. But it only works while in live preview mode. 

Here’s the code that will add threading to bullet lists like this.

image 10

Here’s the code:

.HyperMD-list-line-1:not(:has(~ .HyperMD-list-line-1 ~ .HyperMD-list-line:hover)):has(~ :is(.HyperMD-list-line-6, .HyperMD-list-line-5, .HyperMD-list-line-4, .HyperMD-list-line-3, .HyperMD-list-line-2):hover)::after,
.HyperMD-list-line-1:not(:has(~ .HyperMD-list-line-1 ~ .HyperMD-list-line:hover)) ~ .HyperMD-list-line:has(~ .HyperMD-list-line-2:hover, ~ .HyperMD-list-line-2 ~ :is(.HyperMD-list-line-3, .HyperMD-list-line-4, .HyperMD-list-line-5, .HyperMD-list-line-6):hover)::before,
.HyperMD-list-line-2:not(:has(~ .HyperMD-list-line-2 ~ .HyperMD-list-line:hover)):is(:hover, :has(~ :is(.HyperMD-list-line-3, .HyperMD-list-line-4, .HyperMD-list-line-5, .HyperMD-list-line-6):hover))::before {
--list-threading-color: hsl(23, 100%, 45%, 0.3);
--list-threading-offset: calc(4px + 0.15em);
}

.HyperMD-list-line-2:not(:has(~ .HyperMD-list-line-2 ~ .HyperMD-list-line:hover)):has(~ :is(.HyperMD-list-line-6, .HyperMD-list-line-5, .HyperMD-list-line-4, .HyperMD-list-line-3):hover)::after,
.HyperMD-list-line-2:not(:has(~ .HyperMD-list-line-2 ~ .HyperMD-list-line:hover)) ~ .HyperMD-list-line:has(~ .HyperMD-list-line-3:hover, ~ .HyperMD-list-line-3 ~ :is(.HyperMD-list-line-4, .HyperMD-list-line-5, .HyperMD-list-line-6):hover)::before,
.HyperMD-list-line-3:not(:has(~ .HyperMD-list-line-3 ~ .HyperMD-list-line:hover)):is(:hover, :has(~ :is(.HyperMD-list-line-4, .HyperMD-list-line-5, .HyperMD-list-line-6):hover))::before {
--list-threading-color: hsl(46, 100%, 45%, 0.3);
--list-threading-offset: calc(4px + 0.15em + 2em);
}

.HyperMD-list-line-3:not(:has(~ .HyperMD-list-line-3 ~ .HyperMD-list-line:hover)):has(~ :is(.HyperMD-list-line-6, .HyperMD-list-line-5, .HyperMD-list-line-4):hover)::after,
.HyperMD-list-line-3:not(:has(~ .HyperMD-list-line-3 ~ .HyperMD-list-line:hover)) ~ .HyperMD-list-line:has(~ .HyperMD-list-line-4:hover, ~ .HyperMD-list-line-4 ~ :is(.HyperMD-list-line-5, .HyperMD-list-line-6):hover)::before,
.HyperMD-list-line-4:not(:has(~ .HyperMD-list-line-4 ~ .HyperMD-list-line:hover)):is(:hover, :has(~ :is(.HyperMD-list-line-5, .HyperMD-list-line-6):hover))::before {
--list-threading-color: hsl(70, 100%, 45%, 0.3);
--list-threading-offset: calc(4px + 0.15em + 2 * 2em);
}

.HyperMD-list-line-4:not(:has(~ .HyperMD-list-line-4 ~ .HyperMD-list-line:hover)):has(~ :is(.HyperMD-list-line-6, .HyperMD-list-line-5):hover)::after,
.HyperMD-list-line-4:not(:has(~ .HyperMD-list-line-4 ~ .HyperMD-list-line:hover)) ~ .HyperMD-list-line:has(~ .HyperMD-list-line-5:hover, ~ .HyperMD-list-line-5 ~ :is(.HyperMD-list-line-6):hover)::before,
.HyperMD-list-line-5:not(:has(~ .HyperMD-list-line-5 ~ .HyperMD-list-line:hover)):is(:hover, :has(~ :is(.HyperMD-list-line-6):hover))::before {
--list-threading-color: hsl(105, 100%, 45%, 0.3);
--list-threading-offset: calc(4px + 0.15em + 3 * 2em);
}

.HyperMD-list-line-5:not(:has(~ .HyperMD-list-line-5 ~ .HyperMD-list-line:hover)):has(~ :is(.HyperMD-list-line-6):hover)::after,
.HyperMD-list-line-5:not(:has(~ .HyperMD-list-line-5 ~ .HyperMD-list-line:hover)) ~ .HyperMD-list-line:has(~ .HyperMD-list-line-6:hover)::before,
.HyperMD-list-line-6:not(:has(~ .HyperMD-list-line-6 ~ .HyperMD-list-line:hover)):is(:hover)::before {
--list-threading-color: hsl(187, 100%, 45%, 0.3);
--list-threading-offset: calc(4px + 0.15em + 4 * 2em);
}

/* tails */
.HyperMD-list-line-1:not(:has(~ .HyperMD-list-line-1 ~ .HyperMD-list-line:hover)):has(~ :is(.HyperMD-list-line-6, .HyperMD-list-line-5, .HyperMD-list-line-4, .HyperMD-list-line-3, .HyperMD-list-line-2):hover)::after,
.HyperMD-list-line-2:not(:has(~ .HyperMD-list-line-2 ~ .HyperMD-list-line:hover)):has(~ :is(.HyperMD-list-line-6, .HyperMD-list-line-5, .HyperMD-list-line-4, .HyperMD-list-line-3):hover)::after,
.HyperMD-list-line-3:not(:has(~ .HyperMD-list-line-3 ~ .HyperMD-list-line:hover)):has(~ :is(.HyperMD-list-line-6, .HyperMD-list-line-5, .HyperMD-list-line-4):hover)::after,
.HyperMD-list-line-4:not(:has(~ .HyperMD-list-line-4 ~ .HyperMD-list-line:hover)):has(~ :is(.HyperMD-list-line-6, .HyperMD-list-line-5):hover)::after,
.HyperMD-list-line-5:not(:has(~ .HyperMD-list-line-5 ~ .HyperMD-list-line:hover)):has(~ :is(.HyperMD-list-line-6):hover)::after {
content: "";
position: absolute;
left: var(--list-threading-offset);
bottom: 0;
height: 0.8em;
width: 2px;
background-color: var(--list-threading-color);
}
.HyperMD-list-line.HyperMD-task-line::after {
max-height: 0.275em;
}

/* in-between lines */
.HyperMD-list-line-1:not(:has(~ .HyperMD-list-line-1 ~ .HyperMD-list-line:hover)) ~ .HyperMD-list-line:has(~ .HyperMD-list-line-2:hover, ~ .HyperMD-list-line-2 ~ :is(.HyperMD-list-line-6, .HyperMD-list-line-5, .HyperMD-list-line-4, .HyperMD-list-line-3):hover)::before,
.HyperMD-list-line-2:not(:has(~ .HyperMD-list-line-2 ~ .HyperMD-list-line:hover)) ~ .HyperMD-list-line:has(~ .HyperMD-list-line-3:hover, ~ .HyperMD-list-line-3 ~ :is(.HyperMD-list-line-6, .HyperMD-list-line-5, .HyperMD-list-line-4):hover)::before,
.HyperMD-list-line-3:not(:has(~ .HyperMD-list-line-3 ~ .HyperMD-list-line:hover)) ~ .HyperMD-list-line:has(~ .HyperMD-list-line-4:hover, ~ .HyperMD-list-line-4 ~ :is(.HyperMD-list-line-6, .HyperMD-list-line-5):hover)::before,
.HyperMD-list-line-4:not(:has(~ .HyperMD-list-line-4 ~ .HyperMD-list-line:hover)) ~ .HyperMD-list-line:has(~ .HyperMD-list-line-5:hover, ~ .HyperMD-list-line-5 ~ :is(.HyperMD-list-line-6):hover)::before,
.HyperMD-list-line-5:not(:has(~ .HyperMD-list-line-5 ~ .HyperMD-list-line:hover)) ~ .HyperMD-list-line:has(~ .HyperMD-list-line-6:hover)::before {
content: "";
position: absolute;
left: var(--list-threading-offset);
top: 0;
height: 100%;
width: 2px;
background-color: var(--list-threading-color);
}

/* elbows */
.HyperMD-list-line-2:not(:has(~ .HyperMD-list-line-2 ~ .HyperMD-list-line:hover)):is(:hover, :has(~ :is(.HyperMD-list-line-6, .HyperMD-list-line-5, .HyperMD-list-line-4, .HyperMD-list-line-3):hover))::before,
.HyperMD-list-line-3:not(:has(~ .HyperMD-list-line-3 ~ .HyperMD-list-line:hover)):is(:hover, :has(~ :is(.HyperMD-list-line-6, .HyperMD-list-line-5, .HyperMD-list-line-4):hover))::before,
.HyperMD-list-line-4:not(:has(~ .HyperMD-list-line-4 ~ .HyperMD-list-line:hover)):is(:hover, :has(~ :is(.HyperMD-list-line-6, .HyperMD-list-line-5):hover))::before,
.HyperMD-list-line-5:not(:has(~ .HyperMD-list-line-5 ~ .HyperMD-list-line:hover)):is(:hover, :has(~ :is(.HyperMD-list-line-6):hover))::before,
.HyperMD-list-line-6:not(:has(~ .HyperMD-list-line-6 ~ .HyperMD-list-line:hover)):is(:hover)::before {
content: "";
position: absolute;
left: var(--list-threading-offset);
width: 2em;
top: 0;
height: calc(100% - 0.825em - var(--outline-guideline-width) / 2);
border-bottom-left-radius: var(--radius-m);
border-bottom: var(--outline-guideline-width) solid var(--list-threading-color);
border-left: var(--outline-guideline-width) solid var(--list-threading-color);
}
.HyperMD-list-line.HyperMD-task-line::before {
max-width: calc(2em - 0.35em);
}

Add Bullet Threading in Reading Mode

This CSS snippet was shared by a FirelsGood on Discord group. And this threading works well with reading mode. When you hover over a bullet, you can get an overview of what is under what indentation. 

image 3

Here’s the code:

body {
--outline-guideline-width: var(--size-2-1);
--outline-guideline-color: var(--color-accent);
--outline-item-height: calc(var(--nav-item-size) * 1.8);
}

li {
position: relative;
}

/* In-between items */

li:hover > ul > li:has(~li:hover)::before {
content: "";
width: var(--outline-guideline-width);
position: absolute;
background-color: var(--outline-guideline-color);
top: calc(var(--outline-item-height) / 2 * -1);
left: calc(-2px - 2em - var(--size-4-4));
height: calc(100% - var(--outline-item-height) + var(--size-4-8) + 2px);
}

/* Elbows items */

li:hover > ul > li:hover::before {
content: "";
position: absolute;
top: calc(var(--outline-item-height) / 2 * -1);
left: calc(-2px - 2em - var(--size-4-4));
bottom: calc(100% - (var(--outline-item-height) + var(--size-4-2)) / 2 + 1px);
width: calc(1em + var(--size-4-4) - 2px);
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);
}

A combination of All

I combined the outline threading and bullet threading CSS snippet and changed some settings to be used in my Vault. This gives me a look like this. 

image 11

You can get this CSS snippet along with all other snippets I’ve compiled in my Obsidian CSS vault

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.