Some TiddlyWiki CSS questions

A small side note:
Is it possible to have version number somewhere in Periodic Table Tiddler or site title/subtitle?
I mean somewhere in front page.

Unfortunately no! If you use block of styles like <style>...</style> in one tiddler when it is viewed in storyriver all other tiddlers in storyriver have access to that block! So if you have <style>h2{color:red}</style> in one tiddler and it is open in story river, h2 for all tiddlers open in story river is red.

Question 1 answer addon

I will just add here, to @Mohammad’s responce if the content of the style tags defines a named class, and this is only applied to elements in the current tiddler, then of course it only impacts the current tiddler.

  • That is using the style tags is like adding it to a stylesheet tiddler with the $:/tags/Stylesheet tag, it becomes global,
    • and the last will win,including the inline style tags, when the CSS in the style tags is visible.
  • CSS by definition uses a sophisticated set of selectors CSS Selectors Reference and it is these that determin where it applies.
<style>
.weirdclassname {
css here
}
</style>

<span class=weirdclassname> class applied here</span> and anwhere visible you assign weirdclassname.
  • So you use selectors to ensure specificity
    • or don’t

The result is the world of “CSS is your oyster”, and tfor it to behave any other way would limit its capacity.

  • Remember also, one or more classes in a tiddlers class field will be applied to the current tiddler.

My general answer here is you can’t ask CSS to do too much, it is often very fast, after all HTML and CSS are designed for this.

In your list widget example, it is generated when the list / tiddler is viewed and thats the end of it, the performance issue is typicaly in the render process.

  • Yes if you follow the implications of the following notes.

See my answer to question 2, the issue is as I understand it, does a change force a rerender occur? This will be the perfomace issue, not changing CSS values.

  • So if your html, css selectors and table data, are renderd on screen and you make changes to a seperate stylesheet, I think no (tiddlyWiki) rerender is needed, the CSS is simply “reapplied” using very fast browser handling of CSS.
  • I stand to be corrected but think this is correct.

It is helpful to think of performance in terms of impact on render, refresh and reaction to user input.

Dynamically generated CSS is recalculated as part of the TW refresh process, so potentially on every key stroke. While this can seem like it would be prohibitive from a refresh performance point of view, in reality the calculations that take place are negligible compared to the rest of the refresh cycle.

When the output CSS of a stylesheet tiddler changes, the entire style element (consisting of the output of all CSS tiddlers) is replaced and this causes the browser to have to recalculate the CSSOM and can trigger a repaint or reflow. If this is triggered on every keystroke the performance hit can be noticeable as it can make user input (typing) lag. If it is triggered by user actions such as clicking a button, it is rarely noticeable.

The alternative in TW is to have dynamically assigned class names that change in response to state tiddlers changing, for example:

<div class={{{ [<mystatetiddler>get[text]match<this-element>then[active]] }}}>
</div>

If you only have a few such elements, using filtered attributes to assign classes in this way is usually more performant, especially considering that filters are precompiled and cached for repeated execution, as this avoids the browser needing to recalculate the entire CSSOM. However, with many elements the balance tilts in favour of dynamic CSS, despite the browser needing to recalculate the CSSOM and associated reflow.

You may also find of interest this discussion which covers some of this in more detail:

Another technique that I have found to be marginally more performant that dynamic stylesheets in limited testing is using wikitext to dynamically assign CSS variables on DOM nodes and having static CSS that responds to those variables.

The core does this for things like the sidebar based on the value of a tiddler’s text field, and I do something similar in Streams for highlighting the selected item or during drag and drop. There should be no significant difference between using separate tiddlers or many fields of a single tiddler.

However, I highly recommend to use a separate state or temp tiddler to save the state information such as groups, periods and types rather than saving them in the stylesheet tiddler. If you ever package this as a plugin, modifying the stylesheet tiddler would create a real tiddler overriding the shadow tiddler in the plugin. Then if users upgrade the plugin they would not pick benefit from the newer version of the stylesheet tiddler due to the override.

2 Likes

Great answer with the technical knowledge to back it up from @saqimtiaz

Even more on Question 1

I decided to try ChatGPT to help me illustrate the use of style tags and appropriate selectors, the full conversation, answered and editing for use in tiddlers is here Using selectors to keep style tags only impacting in specific tiddler_table.json (6.4 KB)

Wow !

However to cut a long story short consider this in a tiddler;

<style>
    /* Define CSS classes for the specific table */
    #my-table {
      border-collapse: collapse;
      width: 100%;
      max-width: 800px;
      margin: 0 auto;
      font-family: Arial, sans-serif;
    }
    #my-table th {
      background-color: #4CAF50;
      color: white;
      padding: 8px;
      text-align: left;
    }
    #my-table td {
      border: 1px solid #ddd;
      padding: 8px;
      text-align: left;
    }
    #my-table tr:nth-child(even) {
      background-color: #f2f2f2;
    }
    #my-table tr:hover {
      background-color: #ddd;
    }
  </style>

<table id="my-table">
  <thead>
    <tr>
      <th>First Name</th>
      <th>Last Name</th>
      <th>Age</th>
      <th>Country</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>John</td>
      <td>Doe</td>
      <td>30</td>
      <td>USA</td>
    </tr>
    <tr>
      <td>Jane</td>
      <td>Doe</td>
      <td>28</td>
      <td>Canada</td>
    </tr>
    <tr>
      <td>Bob</td>
      <td>Smith</td>
      <td>45</td>
      <td>Australia</td>
    </tr>
    <tr>
      <td>Alice</td>
      <td>Johnson</td>
      <td>33</td>
      <td>UK</td>
    </tr>
  </tbody>
</table>
  • notice in the above the CSS applies to the below table and no other (unless it uses table id=my-table) so other tables will not be impacted.
  • Of course, place this in a stylesheet tiddler and apply @saqimtiaz advice an hey presto.

The rule responsible for dimming the groups depends on there being an element with a hover state inside the table.

table.periodic:has(.focus-group-1:hover) .element:not(.group-1) {opacity: .25}

1 Like

The ChatGPT table included hover as well. Cool

Sure. It’s usually in the URL when I post it, but I’ll see if I can find a quick way to auto-update it on the page. I usually also post a “latest” URL, and I won’t guarantee that its accurate there, as I might update this one numerous times between versions. But the versions could have it.

1 Like

That’s what I thought, and that’s why I was really surprised by the behavior.

Of course it does. I wrote it that way. D’oh!! Somehow when I was imagining this, I wasn’t figuring on how :has works and assumed I’d have an issue. Then I wrote it, got it working well, and finally went to address the issue I believed all along I had, never noticing that the implementation fixed it.

I saw all the earlier responses when I woke up this morning, and I didn’t recheck when I logged in, so I created a demo of the “magic” and was about to post it, when I saw that there’s a new answer. Thank you very much; this was driving me crazy!

Thank you for your detailed answer. It sounds as though I’m traveling in the right direction. It’s very nice to get confirmation before I’m too heavily committed.

This community is fantastic!

Oh yes, I forgot to mention that. I was definitely expecting this to be in a $:/temp tiddler. My only question is whether I should remove that tiddler when the table tiddler was closed or persist it when they reopen the table. And that’s much more a usability question than a technical TW one. (Still any suggestions would be appreciated.)

Thank you. I was mostly worried about the “dynamic” aspect of this. I didn’t know if when a tiddler containing a stylesheet is closed, whether the CSS rules that it generated are removed from memory, or if they simply accrete and clog up the works. That is probably not a problem for typical usage, but I keep hearing people talk about leaving TiddlyWiki instances running for months and I didn’t know the implications.

I’m afraid I might run into those. I see some occasional slowdowns when generating the annotated table, never terrible, but a little worrying. I don’t actually understand shat they’re about, but wondered if somehow this regeneration of the CSS might be related. They seem to correlate, very roughly, to the time I’ve had the wiki open without a refresh. Because I’m mid-development, that isn’t often a very long period, but I do have a slight concern. Eventually, I believe the CSS will be in its own tiddler which won’t be changed much, so the problem may take care of itself. We’ll see.

Thank you for your insights!

I’ve yet to even try ChatGPT (or Bard.) That dialogue is both impressive and terrifying!

It the tiddler has the tag $:/tags/Stylesheet, then the css becomes “global” regardless of being present in the river or elsewhere.
It the tiddler does NOT have the tag $:/tags/Stylesheet, then the tiddler must be rendered somewhere (e.g. river, sidebar, wherever) for the css to be applied.

That’s me. No implications whatsoever. In fact, I begin to doubt the browser’s mastery of memory management ever before I doubt TW’s capabilities.

Sidenote:

Try to avoid using HTML element ids (#my-id) in css if you can. If you need to target a specific tiddler, preface your selector with [data-tiddler-title=my-title].

1 Like

I would be inclined to let the state persist when the table is reopened within the same wiki session (without reloading the wiki). Partly this is because I dislike tying implementation details too strongly to the default TiddlyWiki UI. Imagine that you use a hook to reset/delete the temp tiddler holding the state when the tiddler holding the periodic table is closed from the story. Well, what if the periodic table was opened in a new window, or a modal, or in a custom layout without a story? All of a sudden the intended behaviour falls apart.

If you use a $:/temp tiddler, the state will automatically reset when the wiki is reloaded as temp tiddlers are never saved. At times I have a config setting that determines whether to persist the state across wiki sessions. Under the covers the config setting just switches between using a $:/temp prefix for the tiddler holding the state, vs a different prefix such as $:/config/periodictable/state.

Note that where consistency in user experience is important, I avoid using the $:/state prefix quite often due the fact that these tiddlers are by default persisted in single file wikis but not in node.js wikis.

As an aside, I can imagine a custom layout option for the periodic table being handy in some situations. The lowest hanging fruit is giving the periodic table tiddler the tag $:/tags/Layout and the fields name and description. If you use any global macros, you will need to import those as well.

Thank you for the reply. I do understand that, and expect to eventually take advantage of it, once changes to this area are minimized. I was just worried that if something came up to prevent me from doing so (and that wouldn’t surprise me) I might have problems not so much with total memory but with too many CSS rules, even if they are merely multiple copies of the same rule.

Does this work for you?

That’s what you should get if you visit the main page, https://crosseye.github.io/TW5-Chemistry/ or the version-specific page https://crosseye.github.io/TW5-Chemistry/0.7.8/. If I’ve pushed changes but not yet a version, it’s at https://crosseye.github.io/TW5-Chemistry/latest/, and that will have, say, 0.7.8+ with the + indicating the change.

Obviously, I could update the site title or subtitle SideBarSegments instead. But when I try that, I don’t like the extra spacing it introduces between title and subtitle. I’m sure that could be fixed, but is this a reasonable alternative?

1 Like

Which also applies to this:

I had started to add the following paragraph but backspaced over it – @saqimtiaz’s point covers exactly the same ground I was intending to cover…

If you’re adding to the TW UI, you will benefit from prefacing your selector with a selector “partial” that selects the container (notionally, .my-container):

.my-container [data-tiddler-title=my-title] { ... }

For example, the following applies style only when your tiddler is present in the Sidebar:

.tc-sidebar-scrollable [data-tiddler-title=my-title] {
  ...
}

Or, perhaps, only in the More tab of the Sidebar:

.tc-sidebar-tabs-more [data-tiddler-title=my-title] {
  ...
}

I think that’s my own preference as well. But I’m not yet certain, probably because the current highlighting is so transient, that cross-tiddler loading behavior seems very far away.

I don’t have enough experience with these modes of dealing with TW to understand the consequences. But thank you. I will look at this again when it comes time to code it. Of course if I don’t try to make the behavior too transient, I guess I can just take advantage of this.

But would using qualify to name this temporary tiddler alleviate some of those concerns?

Ahh, I knew there were things that worked this way, but I didn’t know any details. $:/state prefixed? Got it. Thanks.

I haven’t yet worked with custom layouts, but I’ve read the docs a few times. I figure I’ll get to this at some point, as the default layout makes this pretty tight on smaller screens. But that’s months down the road, I imagine.

Oh, that’s a good point. I’ve been inspecting to find those classes for other reasons, but hadn’t thought about using them for this.

Of course if someone is trying to squeeze the periodic table into the sidebar, they make have other issues. :slight_smile: