Custom Rem-level formatting with tags

There were many Discord Suggestions asking for improved formatting options

While these are important features for RemNote 1.0 they are tedious to design and implement.

Adding the (normalized) tag names of a rem as a CSS class is easy to implement and provides a flexible solution for rem-level customizability. Example use cases include:

Demos

Basic text styling
  • Font-size
  • font colors
  • more background colors
  • text align (left, right, center was asked 3 times - especially useful for latex equations)
  • strike-through
TODO: Todo customization
  • #todo - bold
  • #todo #urgent - bold red on yellow background
  • #important - red, larger font
  • #quote - cursive, quoted on a grey background
Adaptive Headers

Column Layout

Table Layout

Focus Sidebar

Pros and Cons List

Basic Widgets: Rating

Advanced Widgets: Person Template

Sidenote/Comment

Requested

Legend:

  • :white_check_mark: implemented
  • :running_man:‍♂ in progress/partially implemented
  • :red_circle: missing

Most of this got implemented in the November 2020 update. There are still a few things QoL things that I’d like to see. This list is WIP. I’ll add things when I find them.

Allow to add and remove tags via the API.

This way we can connect a style to a shortcut.
Martin said that they are considering adding tag management as builtin shortcuts. This would be much better of cause. Still this needs to be an API feature at some point.
For now you can use a text expander/shortcut ulitity like Auto(hot)key/Keyboard Maestro to paste tags.

✅ Move tags to the top of the Rem

This is because CSS selectors only can go down the tree.
This makes it much, much (!) more powerful because it allows styling a whole subtree and not just a single rem.

🏃‍♂️ (Semantic) CSS Classes

Make sure every part of the UI can be targeted with a CSS class. I don’t know places where this is not the case (there could be though) but these two imporant ones:

  • .tree-node-container > #Pane... should be a .parent-node and
  • :white_check_mark: .tree-node-container > #TreeNodeChildrenPane... should be .children-nodes (currently .TreeNode which works too but could be renamed/augmented).
  • More classes when editing a rem.
    • :white_check_mark: E.g. :: should get a class.
    • And things before and after it as well. (See also point 4).

Update: @ognsya pointed out below that we also need classes for structures that are not described by tags:

  • CSS classes for Rem Types: Concept, Descriptor, Question, Slot need to be annoted in top level in .tree-node-container. (We have .rem-container--*-rem-type, but it needs to be top level.)
  • Filled in slots in template instances get an extra data tag because they would be just identified by a reference and not a tag. This already works, but only as long as you leave the little x for the TypeParent/TypeChildren relationship. There should also be a way to reset this relation. I would also like it to not be a rem-data-tag but a rem-data-slot attribute to have the semantics better expressed.
✅ Page/Document Tags

Similar to 1 add tags also page/document. This allows documents to be styled differently, e.g. have a special style for Daily Documents, meeting notes, workout log, … (infinite possibilities here, multiplies with 1.!) or implement features like enabing a sidebar on some pages.

🏃‍♂️ Static DOM

Update: This is hard. When you hover over a rem it gets transformed to the slate editor instead of static markup. This way it is easy to place the cursor when clicking.

Have the markup change as little as possible on hover/focus. Only apply display: none/visibility: hidden instead of removing it from DOM. This makes user styles more flexible. For example you can always have the toggle button. I’m not an expert for React performance, but I feel like manipulating the DOM/mounting components is equally costly as a leaving extra elements in the DOM (Stackoverflow: Conditional Rendering vs. Hiding, Stackoverflow: visibility: hidden vs display: none;). (My intuition is use Conditional Rendering if it is done only once. If it is often shown on hover use visibility: hidden because no DOM reflow. display: none removes it completly from the rendering.

Less Tag Normalization

Not necessary, more of a QoL. Currently it is quite aggressive. When a rem with the text Rating: 5! is used as a tag the data attribute is just rating. A less aggressive algorithm would be:

  • lowercase
  • strip everything except a-z, 0-9, _ and whitespace
  • turn sequences of whitespace to -
Markup Improvements

It is a kind of technical dept and later changes are harder to do because more things (Mobile App, Desktop App, Web, User Themes) depend on it. It is not a well defined task where compromises have to be made. Changing to much will break current user themes (I pledge to help fixing them if required.) I also understand completely that this is no priority right now because it has no obvious value for the average user. Maybe we can leave it as it is, but it triggers my OCD :stuck_out_tongue: Here are a few things I have found - the more that get fixed, the better:

  • There are a bunch of .*_rem_type > span[data-slate-zero-width="z"][data-slate-length="0"] which are hard to work around. They should be removed.
  • Useless .rem > div. This does not seem to serve a purpose. It could be merged with the parent tag.
  • #hierarchy-editor-references and .hierarchy-editor__tag-bar should be outside .rem-text.
  • One useless span parent of .rem-bullet__container. (One is used for the hidden children search box.)
  • Different naming conventions .EditorContainer (CamelCase) vs. rem-container--default, .bullet-point__container (BEM)
  • Use classes instead of IDs (not really important, but can technically be improved), like #hierarchy-editor-references which exist multiple times. These seem to be artifacts from early RemNote versions, i.e. pre two pane layout.
  • Generally select using the smallest possible selector. (Especially time intensive…) This makes overwriting styles easier. E.g. one does not have to throw in a random #id because it is also in the builtin stylesheet or an !important.
✅ Allow indented and hidden code blocks on Custom CSS.

You can nest CSS blocks under headings and fold those headings.

Added after November 2020 Udate

🏃‍♂️ More concistency

This is a future investment.

🔴 .parent class
🔴 Eaiser to use data-document-tags (.document-content) class

More semantic/concise/robust than

#document[data-document-tags~=specialdocument] #hierarchy-editor-list__inner 
> div:first-child 
> div:nth-child(2)
🔴 .data-rem-container-tags when opening the rem

You can target the children of a tagged rem both when the rem is opened as or inside a page, but it is not semantic at all:

/* Option 1: almost the same as an unopened rem */
[data-rem-container-tags~=todo] 
> :nth-child(2) /* Children of the page. The first one being the .root-tree-node-content for the page itself where hidden rem of the page might be displayed */
.rem-bullet__container {
  background: blue;
}
🔴 Handle SlateJS zero width spaces


In the above example I can not select with .separator-symbol. IIRC I read in a Github issue that those can’t be removed, but maybe somehow worked around to not receive a css class?

  • rem id as data attribute (I just thought of that. This is not necessary for styling, but useful for a browser extension.)
  • easily selectable document root document root
  • .TreeNodeLeftBorder is a sibling of .tree-node-container which makes it impossible to use :nth-child reliably. I assume for now this setting is disabled.
  • add data tags to tag buttons
    • This makes it possible to hide them (unless focused)

this would be super cool to have! I think it fits much more in the Remnote workflow than this text editor “basic” features

2 Likes

Turns out we already have that, oops :sweat_smile:

Why did I not know this?!? We should make documentation.

9 Likes

Have you tried font-size?

1 Like

FONTSIZE?? What do you mean??? :laughing:

12 Likes

Hahah. That was amazing. Good job!

you took it to the next level!

Would you mind sharing the Css code?

5 Likes

Haha, that’s amazing! how do you do that?

For those wondering: I made these by injecting some JavaScript into the page fixing the markup. You can do this e.g. with https://violentmonkey.github.io/, but it does not work well with Single-Page-Applications so we need a native implementation. Or Native Plugins (Custom JavaScript) for that matter :smiley:

Also bumping this to the top of Latest :smile:

5 Likes

Great job as always! Goes much beyond simple “formatting” - that’s full on “styling”!

Would be great if this could be coupled with the existing templates functionality. That way, if I tag a Rem with #person, for example, not only I get the slots for that (Name, Picture, Phone, Address etc.), but also these slots appear in a certain layout (for example, picture on left, info on right).

I think RemNote would greatly benefit from allowing content to be structured like that (after all, slots are already like variables), and endlessly leveraged to do all kinds of things. Especially once queries get more powerful.

Not long ago I got really into Tiddlywiki, which allows a lot of customization and complex queries. Too bad it has very limited/awkward editing/formatting experience. RemNote is the opposite.

Anyway, I digress. Great experiments there! Keep it up!

4 Likes

Thanks for the comment. This is a great idea! I updated point 2 above. Turns out that RemNote already records TypeParent-TypeChildren relation in rem-data-tags. I’m pretty sure now that this is what they are for in the first place, since tags are RemNotes typing/inheritance mechanism. I makes sense now that tags are applied transitively as well, i.e. having Book #Publication and How to Take Smart Notes #Book lists the latter as #Publication as well. It is buggy though.

@ognsya Could you please post a person template of how you would like to have it styled? I would like to try that. I am taking from your comment that you had it customized in TiddlyWiki already?

3 Likes

@hannesfrank I did implement some stuff in Tiddlywiki, but I don’t have it anymore…

I don’t really have any specific layout in mind for this… I just used #person as a basic example of the benefits of styling. Different data structures could benefit from other layouts, of course. Ideally RemNote will allow any user to create their own, via some simple scripting/styling language (possibly via the plugin system, I guess).

Your Pros/Cons proof-of-concept is a great example of that - it’s simple, but very helpful, since it provides such a specific functionality.

2 Likes

Well, here is a pure CSS person template for RemNote:

6 Likes

Nice! I hope RemNote devs are keeping an eye on everything you’ve been doing.

PS: so you’re from 2099. That explains a lot

7 Likes

Holy crud! That’s awesome!:exploding_head: I’m actually starting to become interested in coding because of posts like these.

5 Likes

Wow, these examples are incredible @hannesfrank!

For “Allow to add and remove tags via the API”, what flow did you have in mind for allowing you to add tags via a shortcut? The only flow I can see here is 1) trigger a popup plugin with a keyboard shortcut 2) the plugin adds a tag 3) the plugin closes itself.
What do you think about providing an option to directly link keyboard shortcuts with tags, instead?

We’ve implemented these changes so far - am I missing any particularly important ones?

  • HTML structure simplifications for easier Custom CSS (#hierarchy-editor-references and .hierarchy-editor__tag-bar should be outside .rem-text )
  • Added data-document-tags (document level) and data-rem-container-tags (subtree level) selectors to augment data-rem-tags.
  • Less tag normalization.
  • Added a “separator-symbol” class.
  • Semantic CSS classes at the subtree level (new tree-node-container-* classes)
  • Allow indented and hidden code blocks on Custom CSS.
5 Likes

Yes, that is exactly how I imagined a plugin could work. A native implementation to add/toggle tags using a shortcuts would be even better of course because it eliminates the loading time. I would still love having more things exposed for programmatic access. I wrote a bit about that too and maybe post this later (think: everything is a remcommand, like in modern text editors).
For example there might be cases where you want to cycle through 3 tags with a single shortcut.

For now one can also use auto(hot)key or something similar to associate a tag with a shortcut. So it is not super high priority on my list. You can not remove the tag again though.

Yes, I think you got the highest value/time ones! Thank you very much!

Having the normal and focused markup play nicely together is hard I guess because it is created by the slate editor. That would be my next wish. Maybe you could at least leave the hover markup unchanged for now?

Here are some more wishes which make styling for active recall practice easier:

  • Wrap key/name and content (front and back of a card) in an extra element. I think we have this for unfocused rems, but not for focused/hovered ones. (Again, IDK anything about slate.)
  • Add data tags for practice status, i.e corrects in a row, last time wrong, due/in queue.

Semantic CSS addendum:

  • *--focused (and *--hovered) on all ancestor nodes. Sometimes I also want to disable styling of a group of rem if one of them is focused. I don’t know if this is practical for performance reasons (probably yes because hierarchies are not that deep).

I’ll report back when I find more stuff that can be improved.

6 Likes

Thanks @Martin for replying!

@hannesfrank for president of Remmunity 2020 :sparkler:

6 Likes

@hannesfrank This looks amazing. Thank you!
But missing the part that how do i implement it?

I’ll release it as copy&paste and/or tutorial when Martin rolls out the changes described here.