Heya @left! I’ll try to illuminate as best I can. This is just to satiate curiosity (and writing it all out is a good way for me verify I’ve checked everything I can think of). There’s no pressure to get all this.
First a big picture:
CSS is one of a trio of technologies that combine to make up Web pages. The other two are HTML and JavaScript. These Web technologies apply here because Obsidian is built on https://www.electronjs.org/, and so its user interface is basically a Web page running in a stripped down Web browser.
The three technologies work together to enforce a separation of concerns:
- When you want to deal with what is on a Web page, then you work with HTML.
- When you want to deal with what a Web page looks like, then you work with CSS.
- When you want to deal with what a Web page does, then you work with JavaScript.
(We’re not interested in JavaScript, but I mention it to ensure that the big picture is complete.)
HTML is used to describe a https://en.wikipedia.org/wiki/Tree_structure that makes up what is on a Web page. Here’s a sample I banged together (I put it in an image so we can have pretty colors that make things easier to look at):
That HTML corresponds to this tree:
We call the things in the tree “elements”, and we can speak of each elements’ parent, children, and siblings, since the whole thing is close enough to a family tree style of thing.
Once we have a tree like this, then we can use CSS to specify what things should look like. Here’s a sample:
This CSS specifies three rules. Each rule has two parts; a selector, and a list of style attributes.
The selector tells the Web browser which things the rule applies to. For example, the selector nav ul li
tells the Web browser to style all the li
list elements that are in ul
unordered lists, that are in turn in nav
navigation sections.
So this CSS would cause the Web browser to render the paragraphs of text in red, the navigation links in green, and the article header in blue. The navigation links’ text would be 1.5 times as big as the text in the paragraphs, and the article header’s text would be twice as big.
A Web developer almost always adds extra information to elements in an HTML tree to give CSS the ability to ask for more specific things. One of these pieces of information is the class
. A class is a name the developer specifies, and that name can then be applied to as many elements in the HTML tree as the developer likes. And a single element can also have as many different classes as the developer likes, too.
So a developer could write a selector like p.lead-paragraph
to style all the <p class="lead-paragraph">
s with slightly larger text. Then a Web page could have as many lead paragraphs as it needs, and each one would be made slightly larger.
So now we have a big picture. Obsidian’s interface is a giant tree. Most things in the tree have various classes to help describe what they are, and the CSS in Obsidian’s themes can specify which elements with which classes in which parts of the tree should look like what.
This is why CSS for other things–other apps or other Web pages–rarely work when you put them in your theme. CSS from elsewhere uses selectors that assume a different tree structure and different classes than the ones Obsidian uses.
Second, why we can’t get numbering to work:
CSS provides a handful of style attributes to set up numbering. One of them–counter-reset
–is used to start counting. Another, counter-increment
, is used to actually count something.
Both of these attributes work when you specify which counter they apply to. You can have as many counters as you like, and each one has its own name that you choose. Then, in other CSS, you can specify where to display the value of the counter by using its name.
The tricky part is this: We, the writers of the CSS, can’t know in advance how many counters we will need. One Web page might have five separate lists that each need their own counter. And a list could have sub lists, which will each need their own counter, and they could have sub lists too. And a different Web page on our Web site may have a different number of lists.
So in order for all this to work, we have to rely on CSS to handle setting up actual counters for us. All we do is specify patterns in the HTML tree that match when to make a counter.
There are basically two kinds of patterns we can specify: flat ones and nested ones. Which one we use depends on the tree structure we have to work with.
The flat structure is the simplest one, where all the headers are just children of the same parent element, one after another. In this case, we just make six counters (one for each level of header), and the counters are reset each time we go past a header of the next highest level.
The outline pane, though, uses a nested tree structure. Each header has its child headers contained within it.
If you look in the CSS we’re using for the outline, you’ll find counters for each of the six levels of header you can use in your notes, named from h1
to h6
. That CSS sets up a counter for the h1
s (the biggest headers), and then sets up one counter for each h1
to count the h2
s that are inside it. And then, for each of those h2
s, it sets up one counter to count the h3
s that are inside. And so on down to h6
.
This works because CSS is relying on the tree structure of the outline. Each h1
contains h2
s, each of which contains h3
s. So the CSS works by creating a new counter each time it goes down a level in the tree.
The problem with the preview is that it doesn’t use either of these two structures. It’s a little bit of both:
The preview is made up of a series of div.markdown-preview-section
elements, each one of which contains the interesting stuff (e.g. a header, or a paragraph). (div
is short for “division”–it’s a general purpose element).
We can’t use a flat approach in the CSS because the only things we can count are the markdown-preview-sections
. Nothing distinguishes one from any other.
And we can’t use a nested approach, because the headers aren’t children of each other. Whenever we write CSS that descends into each .markdown-preview-section
, that CSS sets up counters that only work inside that one single .markdown-preview-section
. That’s what we want in a nested tree structure, but it doesn’t work here.
This is frustrating, of course, because we as humans can easily see how to count things ourselves. Our problem is that CSS, as a language, doesn’t offer the vocabulary we need to express how to do that.
In order to get counting to work properly, either the preview needs to fully adhere to one structure or the other, or we need to use a tool more sophisticated than CSS (namely, JavaScript).
My first suggestion was to add information to each .markdown-preview-section
saying what kind of header it was. That info would be added to each div
's class
attribute like you see in the screenshot. The CSS could pick up on that, and so we would have a flat structure.
But that’s actually not a good solution, because then information is duplicated, and duplicated information is a good source of bugs. If each .markdown-preview-section
says what kind of header it contains, then if then if the header changes from, say, an h1
to an h2
, the developers have to make sure that not only does the header itself get changed, but so does the information on the containing markdown-preview-section
. This is a great place for someone to make a mistake.
So the thing to do is hold out for the plugin API (application programming interface–its just like a user interface, except that programs use it instead of users). Then hopefully that API will make it possible to write a plugin which does the numbering.
Don’t worry if this doesn’t all make sense. I’m not very good at explanation, and the thing I just tried to explain covers a lot of ground at both a high and low level. But if you picked up just a few separate little things from it, then it’s still a win!
As for what @Silver says about optimization and scroll syncing, I simply can’t comment. Those are topics where only a working knowledge of Obsidian’s code and design can help, and I don’t have that.