A frontmatter that finally supports links ! (Lila's frontmatter 🌸)

Introduction

Hi there ! :blush:

Adding support to links into the notes’ frontmatters has been one of the most active subject on this forum in recent years (hundreds of topics and answers have been written about this).

List of related topics

For those who are interested, here is a non-exhaustive list of topics related to this subject : #4673, #10052, #1144, #39851, #39295, #34123, #39319, #49841, #25650, #7232, #6994, #25814, #21863, #45676 and #5648.

But despite this huge activity, I haven’t read so far any frontmatter system that properly allowed :

  • links to be :
    • auto-completed while typing
    • clickable in both Editing and Reading modes
    • reflected in the graph view
  • frontmatter to be :
    • easily editable and readable in Editing mode
    • else hidden, folded or entirely visible in Reading mode
    • properly readable in other Markdown editors

So I’ve decided to imagine my own solution to that problem.


The Lila’s frontmatter :cherry_blossom:

Dependencies

This frontmatter relies on Dataview and Custom Classes plugins. The first provides inline fields for metadata and the second allows adding custom HTML classes to specific Markdown blocks (in our case the frontmatter block).

Integration in notes

The format is pretty lightweight and feels native since it simply consists in a Markdown list of Dataview inline-fields, prefixed by a Custom Class code block :

`class:meta`
- creation:: 2023-01-21T18:55:12
- author:: [[John Doe]]
- parents:: [[Note]], [[Another note]]
- status:: #MayBePartial

This frontmatter is built with at least clutter as possible, making metadata easy to read, modify and extend in Editing mode.

Also, it uses only syntax that is part of the original Markdown specification, which means that :

  • it will properly render as an unordered list if opened with other Markdown readers
  • links auto-completion, clickability and reflection on the graph will properly work

:tada:  Fun fact: Unlike the default Obsidian frontmatter, this metadata block is not limited to the top of the file and can be placed anywhere in your notes (e.g. at the bottom).

Rendering

Thanks to the Custom Classes plugin the above frontmatter block will render with the meta class in Reading mode :

<div class="meta">
    <ul>
        <li>creation:: 2023-01-21T18:55:12</li>
        ...
    </ul>
</div>

Styling

That appended class makes the frontmatter pretty easy to style from a CSS snippet by simply referring to it with the div.meta selector.

Here are 3 examples of styles that can be applied to reach different effects :

1# Always visible frontmatter

Here is an example of always visible frontmatter. This one comes with a delicate but reactive opacity effect, triggered when hovered, and that prevent the frontmatter from taking your visual attention when you’re not using it.

Theme
Dark Peek 2023-01-25 11-52
Light Peek 2023-01-25 11-53
CSS code 📜

To apply this style to your frontmatter simply copy/paste that code into an Obsidian CSS snippet file.

div.meta {
  display: inline-block;
  font-family: monospace;
  background-color: var(--background-secondary);
  box-shadow: -1px -1px 8px var(--color-base-30);
  border-radius: 15px;
  padding: 15px 18px 15px 15px;
  transition: opacity 300ms linear;
  opacity: 0.6;
  margin-bottom: 15px;
}

div.meta:hover {
  opacity: 1;
}

div.meta::before {
  content: "Metadata";
  display: inline-block;
  margin-left: 5px;
  font-weight: 900;
  letter-spacing: 0.8px;
  padding-bottom: 4px;
  font-size: 16px;
}

div.meta ul {
  margin: 0;
  padding-left: 28px;
}

2# Visible on-hover frontmatter

Here is a slightly modified version of the previous frontmatter. This one is even more discrete when you’re not using it since it displays the metadata list only when it is hovered.

Theme
Dark Peek 2023-01-25 12-45
Light Peek 2023-01-25 12-42
CSS code 📜

To apply this style to your frontmatter simply copy/paste that code into an Obsidian CSS snippet file.

div.meta {
  display: inline-block;
  font-family: monospace;
  background-color: var(--background-secondary);
  box-shadow: -1px -1px 8px var(--color-base-30);
  border-radius: 15px;
  padding: 15px 18px 15px 15px;
  transition: opacity 300ms linear, max-height 300ms linear, max-width 300ms linear;
  opacity: 0.6;
  margin-bottom: 15px;
  overflow: hidden;
  min-height: 52px;
  max-height: 52px;
  max-width: 120px;
}

div.meta:hover {
  opacity: 1;
  max-height: 800px;
  max-width: 800px;
}

div.meta::before {
  content: "Metadata";
  display: inline-block;
  margin-left: 5px;
  font-weight: 900;
  letter-spacing: 0.8px;
  margin-bottom: 4px;
  font-size: 16px;

  /* Add an opaque background */
  position: relative;
  background-color: var(--background-secondary);
  z-index: 10;
  border-radius: 10px;

}

div.meta ul {
  margin: 0;
  padding-left: 28px;
  margin-top: -10em;
  opacity: 0;
  transition: margin-top 300ms linear, opacity 300ms linear;
  white-space: nowrap;
  max-height: 700px;

  /* Display a clean scrollbar if the height exceeds 700px */
  overflow-y: scroll;
  margin-right: -23px;
  padding-right: 8px;
}

div.meta:hover ul {
  margin-top: 0;
  opacity: 1;
}

3# Hidden frontmatter

Finally, if you prefer to not display the frontmatter at all in Reading mode, you can achieve that with that tiny CSS code :

div.meta {
    display: none;
}

Share your own versions :green_heart:

Feel free to share your own versions of the Lila’s frontmatter by submitting answers to that topic. Don’t forget to include the CSS code you used to achieve them.

Thanks for reading that post !

32 Likes

I do similar with a custom callout (via Admonition)

> [!metadata]- **Status:** #active
> status_category:: NEW 
> status:: Just created
> short_name:: <% tp.file.title %>
> priority:: 0
> 
> **Related**:: ["[[REDACTED MOC]]","[[REDACTED]]"]

Default state:

Expanded

I dig your solution though.

edit: Fixed code formatting to be accurate to my template. The > characters were stripped out when I pasted.

6 Likes

This looks great! I will definitely give it a try.

1 Like

Hey that looks great too ! Thanks for sharing :blush:

Does admonition allow for styling specific callouts ? If that seems to work well I definitely need a more discrete solution in Reading mode (to keep my attention for the main content).

Also, the display in Edition mode contains a bit more clutter than my frontmatter, but it’s totally usable in my opinion !

1 Like

Yes, admonition allows for custom callouts.

1 Like

So as I look closer into trying this, is it correct that this solution won’t work in Live Preview mode? I guess that would make sense, as it would be difficult to have some of the formatting features that you talk about that are also editable at the same time.

@dddave - similar question to you. Does your admonition solution work in Live Preview mode? I’ve haven’t used that plug-in before.

My screenshots were from live preview mode.

Hi @cjt3 !
Yesterday I started coding a Live Preview support for the Custom Classes plugin, which would allow this frontmatter to display properly in Live Preview mode.

I’ve ended up with a first minimal working version of the update, but many things remain to improve. The main complexity comes from the fact that the Live Preview mode renders things quite differently than the Reading mode :

  • lists items are rendered grouped in Reading mode while they’re rendered individually in Live Preview,
  • lists are rendered using ul/ol and li HTML elements in Reading mode while they are simulated with div and span elements in Live Preview.

Without fixes, this implies two things :

  • Using lists to group the metadata entries will not work in Live Preview
  • CSS code written for Reading mode will not work in Live Preview

I’ve several solutions in mind to deal with those problems, I will perform testing and will release the cleanest and most performant design. That should be ready in the next few days :upside_down_face:

4 Likes

Wow, the implementation looks pretty cool and it does achieve all of the functionality I would want when linking pages in frontmatter. Plus being able to target the HTML class opens so much possibility.

By the way, sorry for the noob question, how to install the Obsidian Custom Classes plugin?

It does not seem to be available on the Official Plugins list.

2 Likes

Glad to hear that @patrick_ambrosso ! :upside_down_face:

And yeah sorry, I haven’t mentioned that the Custom Classes plugin is currently not available in the official plugins list (request has been sent but has not been reviewed yet).

But you can still install this one through the BRAT plugin :

  1. Install and enable the BRAT plugin (which is available in the official plugins list)
  2. On the BRAT settings tab :
  • Click on “Add Beta plugin” button
  • Paste that URL : https://github.com/LilaRest/obsidian-custom-classes
  • Click on “Add Plugin” button to confirm
  1. Finally, go to your plugins list (Options > Community plugins) and enable the Custom Classes plugin
3 Likes

Ah, thanks much!

1 Like

Ok so I’ve just pushed a new release of the Custom Classes plugin which introduces support for Live Preview mode !

There are definitely many things to improve and that first working version should be considered as a Beta.

Note that actually, the render of the frontmatter in Live Preview mode is slighlty different than in Reading mode, I’ll post adjusted CSS snippets in the next hours.

@cjt3 @dddave @patrick_ambrosso @Outis I would be happy to have your feedback on that release :blush:

4 Likes

hey there. can u share how the custom classes can apply to LP? i tried but it seems to just applied to reading view. am i missing sth?

screenshot on how i tried in LP, source mode and reading view

this is reading view

this is LP after i reload obsidian. missing the first bullet

this is source mode

1 Like

Sorry it seems that the latest release 1.8.0 of the Custom Classes plugin has broken things.

I’ll patch that later in the day and I’ll let you know :slight_smile:

Note that you can downgrade to 1.7.0 and this should work.

4 Likes

This is absolutely wonderful. Thank you

1 Like

A script to convert YAML frontmatter to lila’s format. Aliases are preserved in a Yaml block; tags are preserved as tags; all other entries are converted to clickable links in dataview key/value pairs

It is a horrendously amateurish and hacky.

It is supplied with Absolutely no warranty whatsoever

Use it only on a copy of your vault.

Check the results very carefully after this test.
But if it’s useful, here you are. If you want to use it yourself, you will need to change the vaultDir variable to point where you need it.

# a hacky little script to change the metadate into the kind of tagged things
# show in Lila's post
import re
from pathlib import Path
vaultDir=Path("YOUR VAULT PATH HERE")

def convertMetadata(bigstring):
    try:
        dummy,metadataBlock,contentBlock = re.split("---\n",bigstring,maxsplit = 2)
    except ValueError:
        print("Apparently no metadata block found")
        return
    metaCodeBlock = []
    metaCodeString = "" 
    yamlString = ""
    # First check: is it a Kindle thing, with no proper metadate?
    if re.search('kindle://',metadataBlock): # if so, bail
        print("Looks like a Kindle file —  bailing!")
        return
    for entry in metadataBlock.splitlines():
        valueList = []
        try:
            k,v = entry.split(":",maxsplit=1)
        except ValueError: # there is no colon — skip it
            continue
        if v.strip() == "": # if there is no value — skip it
            continue
        if re.search("ags",k): # if it's a tag, preserve its character with a hash
            for value in v.split(","):
                value="#"+value.strip(' []')
                valueList.append(value)
        elif re.search("[Aa]lias",k): # if it's an alias, put it in yaml because that's the only place it workd
            yamlString = '---\n' + entry + '\n---\n'
            continue # because we are moving this to the unavoidable frontmatter, which is the only place that aliases work
        else:
            for value in v.split(","):
                if re.search('<%',value): # it's a templater command; pass it through as is
                    valueList.append(value)
                else:
                    value="[["+value.strip(' []')+"]]"
                    valueList.append(value)
        keyword='- '+k+":: "
        keyvalue=keyword+",".join(valueList)
        metaCodeBlock.append(keyvalue)
        metaCodeString="\n".join(metaCodeBlock)
    convertedString = "`class:meta`\n" +metaCodeString+ "\n\n" + contentBlock
    if yamlString:
        convertedString = yamlString + convertedString
    return convertedString

for file in vaultDir.glob("**/*.md"):
    if file.is_file():
        f = open(file, mode="r", encoding="utf8")
        try:
            bigstring = f.read()
        except UnicodeDecodeError:
            print("Dearie Me!!\nUnicode problems with {}".format(file.name))
            continue
        f.close()
    if re.search(r"class:meta",bigstring):
        continue # we've done this one already
    try: #block needed because of idiots putting emojis in their twitter handles
        print("----\nshifting metadata in {}".format(str(file.absolute())))
    except UnicodeEncodeError: #some saved tweets have emojis in the file name, which str() barfs on
        print("----\nshifting metadata in {}".format(file.name))
    convertedString  = convertMetadata(bigstring)
    if convertedString: # maybe null if no metadata
        f = open(file, mode="w", encoding="utf8")
        f.write(convertedString)
        f.close()
6 Likes

Wow it looks great, I’ll definitely give it on old vault’s frontmatters.
Thanks for this contribution :slightly_smiling_face:

1 Like

There is now a revised version that is more selective about the YAML that it digests, and preserves tags as #tags, and aliases as aliases – which does mean that notes with aliases in them will still have an old fashoned metadata block, which can’t be hidden.

Updates about the frontmatter

Hi @here ! :blush:
I’m coming with some exciting news :

  • I’ve continued to work regularly on the Custom Classes plugin over the past 2 weeks, and I’m happy to announce that it is now stable on most of its main behaviors in both Read and Live Preview modes !
  • As a result, the CSS snippets I gave in my initial post are now rendered exactly the same in both Read and Live Preview modes (see GIFs below)
  • Also, the frontmatter seems to render properly when used with the Minimal theme by @kepano
  • Last thing, the Custom Classes plugin should be included to the official community plugins list tomorrow, so installation will be easier for non-technical people.

So this is what it looks like in Live Preview now :

Theme
Dark Peek 2023-02-06 21-48
Light Peek 2023-02-06 21-46

For newcomers, you can learn why and how to install this frontmatter by reading the initial post written about it.

4 Likes

I love this approach but am curious about two things:

  1. Why not put the tags in the frontmatter so they are cli1. ckable for a search?
  2. Does anyone know how the upcoming changes to metadata that is currently being worked on by the Obsidian developers might impact this?