Creating P2P replicas of 9gag / Instagram / Pinterest / Tumblr / Youtube / …

Hi,

First of all I would like to say that I’ve hated TiddlyWiki since day 1, but then I got held captive in a forest by a madman called @papiche and he forced me into developing his stupid project of copying the web using TW and IPFS.

So I looked into it a little bit and started with 9gag. By creating a custom stylesheet, I managed to have TW look like 9gag :
(demo) (loading media might takes a few seconds)

Here is the corresponding stylesheet :

corresponding CSS

body.tc-body,
.nc-sidebar, 
.nc-bar.nc-topbar {
    background-color: hsl(0, 0%, 7.8%);
}



/**
 * Sidebar
 */



.nc-sidebar {
    border: none;
    scrollbar-width: none;
}




/**
 * Topbar
 */



.nc-bar.nc-topbar {
    border-bottom: 0.0675rem solid hsl(0,0%,30%);
    max-width: 100%;
}

@media (min-width: 960px) {
    .nc-topbar-wrapper {
      left: 0;
    }
    .nc-sidebar {
        top: 52px;
    }
}

.nc-topbar .left {
    display: flex;
    flex-grow: 1;
}
.nc-topbar .left > .tc-keyboard {
    flex-basis: 100%;
}

.nc-bar input[type="search"],
.nc-bar input[type="search"]:focus {
    width: calc(100% - 20px);
}

.nc-bar .tc-page-controls button[arial-label="recherche avancée"] {
    /* padding-left: 1rem;*/
}

.nc-topbar .right {
    flex-basis: content;
    padding-left: 20px;
}

.nc-topbar .right .tc-page-controls > *:last-child{
    margin-right: 0;
}


.nc-topbar .tc-reveal.tc-popup {
  left: unset !important;
  right: 0;
}


/**
 * Tiddler (inc. contenu)
 */



.tc-story-river {
    
    max-width: 640px !important;
}

div.tc-tiddler-frame {
    
    position: relative;
    display: flex;
    flex-direction: column;
    /*padding-top: 2rem;*/
    padding: 0;
    border-radius: 0;
    
    /*background: black;*/
    /*margin-bottom: 3rem;*/
    background: transparent;
    
    border: 0 !important;
    border-bottom: 0.0675rem solid hsl(0,0%,20%) !important;
    margin-bottom: 3rem;
    padding-bottom: 3rem;
}


.tc-tiddler-frame > .tc-tiddler-body {
    /* contents */
    order: 3;
    color: hsl(38.8, 5%, 90%);
}

.tc-tiddler-frame .tc-tiddler-body > * {
    
    width: 90%;
    margin: auto;
}

.tc-tiddler-frame .tc-tiddler-body > img, 
.tc-tiddler-frame .tc-tiddler-body > video,
.tc-tiddler-frame .tc-tiddler-body > svg, 
.tc-tiddler-frame .tc-tiddler-body > canvas, 
.tc-tiddler-frame .tc-tiddler-body > embed, 
.tc-tiddler-frame .tc-tiddler-body > iframe {
    
    width: 100%;
}





/**
 * Titre et contrôles d'édition
 */


.tc-tiddler-frame > .tc-tiddler-title {
    /* titre et contrôles */
    background: transparent;
    
    order: 2;
    float: right;
    position: absolute;
    
    /*top: 1.5rem;
    right: 2rem;*/
    top: 0;
    right: 0;
}


.tc-tiddler-title:not(.tc-tiddler-edit-title) > .tc-titlebar > span:not(.tc-tiddler-controls) {
    /* titre */
    display: none;
}

div.tc-tiddler-frame .tc-tiddler-title:not(.tc-tiddler-edit-title) > .tc-titlebar > .tc-tiddler-controls > button {
    
    /*box-sizing: content-box;*/
    margin: 0;
    width: 2rem;
    padding-left: 0.5rem;
    padding-right: 0.5rem;
    margin-left: 0.25rem;
    margin-right: 0.25rem;
    border-radius: 0.125rem;
}

div.tc-tiddler-frame .tc-tiddler-title > .tc-titlebar > .tc-tiddler-controls > button:last-of-type {
    
    margin-right: 0;
}

div.tc-tiddler-frame .tc-tiddler-title > .tc-titlebar > .tc-tiddler-controls > button > * {
    
    width: 0.75rem;
}


div.tc-tiddler-frame:not([data-tags*="$:/isAttachment"]) .tc-tiddler-title > .tc-titlebar > .tc-tiddler-controls > button[aria-label="export attachment to IPFS"],
div.tc-tiddler-frame[data-tags*="$:/isAttachment"] .tc-tiddler-title > .tc-titlebar > .tc-tiddler-controls > button[aria-label="export content to IPFS"],
div.tc-tiddler-frame[data-tags*="$:/isAttachment"]:not(.tc-tiddler-system) .tc-tiddler-title > .tc-titlebar > .tc-tiddler-controls > button[aria-label="export to IPFS"], 
div.tc-tiddler-frame.tc-tiddler-system .tc-tiddler-title > .tc-titlebar > .tc-tiddler-controls > button[aria-label="export content to IPFS"], 
div.tc-tiddler-frame:not(.tc-tiddler-system) .tc-tiddler-title > .tc-titlebar > .tc-tiddler-controls > button[aria-label="export to IPFS"], 
div.tc-tiddler-frame.tc-tiddler-system:not([data-tags*="$:/tags/Stylesheet"]):not([data-tiddler-title^="$:/plugins/"][data-tiddler-title$=".js"]) .tc-tiddler-title > .tc-titlebar > .tc-tiddler-controls > button[aria-label="export to IPFS"],
div.tc-tiddler-frame.tc-tiddler-system .tc-tiddler-title > .tc-titlebar > .tc-tiddler-controls > button[aria-label="export content to IPFS"],
div.tc-tiddler-frame.tc-tiddler-system:not([data-tags*="$:/tags/Image"]) .tc-tiddler-title > .tc-titlebar > .tc-tiddler-controls > button[aria-label="export attachment to IPFS"]
{
    
    display: none;
}

div.tc-tiddler-frame[data-tags*="$:/isAttachment"]:not([data-tags*="$:/isIpfs"]) .tc-tiddler-title > .tc-titlebar > .tc-tiddler-controls > button[aria-label="export attachment to IPFS"] {
    
    background: hsl(0,50%,50%);
}


div.tc-tiddler-frame:not([data-tags*="$:/isAttachment"]):not([data-tags*="$:/isIpfs"]) .tc-tiddler-title > .tc-titlebar > .tc-tiddler-controls > button[aria-label="export content to IPFS"],
div.tc-tiddler-frame:not([data-tags*="$:/isAttachment"]):not([data-tags*="$:/isIpfs"]) .tc-tiddler-title > .tc-titlebar > .tc-tiddler-controls > button[aria-label="export to IPFS"] {
    
    background: hsl(0,50%,50%) !important;
}

div.tc-tiddler-frame:not([data-tags*="$:/isAttachment"]):not([data-tags*="$:/isIpfs"]) .tc-tiddler-title > .tc-titlebar > .tc-tiddler-controls > button[aria-label="export content to IPFS"] svg,
div.tc-tiddler-frame:not([data-tags*="$:/isAttachment"]):not([data-tags*="$:/isIpfs"]) .tc-tiddler-title > .tc-titlebar > .tc-tiddler-controls > button[aria-label="export to IPFS"] svg {
    
    fill: white !important;
}


/**
 * Nom d'utilisateur
 */



.tc-subtitle > a.tc-tiddlylink {
  font-style: normal;
  color: white;
  font-weight: bold;
}


/**
 * Date
 */



.tc-tiddler-frame > :nth-child(3) {
    /* date */
    order: 1;
    text-align: left;
    position: relative;
    top: 0.5rem;
}

.tc-tiddler-frame > :nth-child(3) .tc-subtitle {
    /* date date */
    color: hsl(38.8, 5%, 45%);
    padding-bottom: 1rem;
}




/**
 * Tags
 */



.tc-tiddler-frame > :nth-child(4) {
    /* tags */
    order: 7;
}

[data-tag-title^="$"] {
    display: none;
}

.tc-tiddler-frame > :nth-child(4) > .tc-tags-wrapper {
    
    text-align: left !important;
    margin: 0;
    margin-right: 0.5rem;
}

.tc-tiddler-frame > :nth-child(4) > .tc-tags-wrapper > .tc-tag-list-item {
    
    margin-top: 0.5rem;
}

button.tc-tag-label, 
span.tc-tag-label {
    border-style: solid;
    
    /*
    background-color: transparent;
    border-width: 0.125rem;
    border-color: hsl(38.8, 100%, 70%);
    border-color: hsl(38.8, 30%, 30%);
    color: hsl(38.8, 30%, 30%) !important;
    */
    
    line-height: 2rem;
    font-size: 0.875rem;
    font-weight: 700;
    border-radius: 0.25rem;
    border-color: transparent;
    background-color: hsl(0,0%,20%);
    color: white !important;
    padding: 0 0.25rem !important;
}

button.tc-tag-label:hover, 
span.tc-tag-label:hover {
    background-color: hsl(0,0%,30%);
    /*
    background-color: hsl(38.8, 100%, 70%);
    border-color: hsl(38.8, 100%, 70%);
    color: hsl(38.8, 5%, 5%) !important;
    */
    
}



/**
 * Alertes
 */



.tc-alerts {
    
    top: unset;
    bottom: 0.5rem;
    display: flex;
    flex-direction: column-reverse;
    max-width: 25vw;
}

.tc-alert {
    margin: 0;
    margin-top: 0.5rem;
    margin-left: 0.5rem;
    background-color: hsl(38.8, 5%, 5%);
    border-color: hsl(38.8, 15%, 15%);
    border-radius: 0.25rem;
}

.tc-alert-subtitle {
    
    color: hsl(38.8, 5%, 35%);
}

.tc-alert-toolbar svg {
    fill: hsl(38.8, 25%, 35%);
}



/**
 * Paramètres
 */


.tc-icon-ipfs-wrapper {
  width: auto;
}

(it’s just a rough draft ; I haven’t worked on responsiveness yet)

I also switched default tiddlers to:

[app[6gag]] +[!nsort[created]]
[tag[Fun]] +[!nsort[created]]

What now?

adding other 9gagS, InstaramS, PinterestS, YoutubeS

Now I would like to be able to add other “accounts” to my tiddlywiki, and that I and other people be able to switch between different accounts, each having a different type of content. For instance, with the Youtube replica: I don’t want to see funny videos when I’m learning code, and I don’t want to see coding videos when I’m trying to unwind.

I had to work hard to configure my Youtube account in order not to be polluted by suggestions:
(screenshot)

(yeah, I know, I could use Unhook – and I do – but it’s not enough)

switching between “virtual tiddlywikis”

I need to be able to switch between what I would call “virtual tiddlywikis”.

Virtual TW have :

  1. a specific type of content
  2. a specific theme

For example, I could have :

  • two 9gags
  • nine youtubes
  • etc…

Switching between virtual TW ought be possible both :

  1. via a menu bar (I saw there already exists a menu bar plugin)
  2. via a specific URL

default values for tags and custom fields for new tiddlers via attachment imports

I think I need this to switch between virtual TWs.

Could someone point me in the right direction for where to look in the doc to do that?

switching stylesheets

I need to be able to switch between themes or stylesheets, but so far I have close to nothing:

let displayStylesheets = function () {
	
	console.log(document.styleSheets)
}

window.addEventListener('load', function () {
	
	setTimeout(displayStylesheets, 3000)

	console.log(document.URL)
});

changing TW behavior when clicking on a tag

I would also like to be able reproduce the behavior of 9gag (and every major platform I can think of) when clicking on a tag.

Instead of having a dropdown menu listing tiddlers tagged with a label, I would like the interface to display tiddlers with that specific tag (sorted by recency).

infinite scroll pagination + lazy load

I would also like some kind pagination of tiddlers (infinite scroll, ideally) or maybe lazy load of media stored on IPFS, because it seems to me that it would diminish load time.


Thanks in advance for the help.

1 Like

Welcome to TiddlyWiki @bogoris – I hope you’ll come to enjoy it :smiley:

3 Likes

Look into Alternative Layouts, you can import specific style sheets into your custom layout tid instead of tagging them so they won’t be global styles.

3 Likes

@Brian_Radspinner > Thanks! I’ve looked into it and I think I’ll use layouts for users to be able to switch between two modes: visitor and editor.

@jeremyruston > Yeah, I guess TW is something that grows on you ; I hate it less and less.


Ok, so I’ve created a plugin called astroport/univers and two themes called 6gag (9gag clone) and tutube (Youtube clone).

“virtual tiddlywikis” are now called “universes” (it’s really similar to a Youtube channel, or a Wordpress category feed ; I just prefer the term “universe”, because we head “this artist has a universe … blabla…”, and I feel we all are multi-faceted.

I’ve managed to rip-off the theme switching mechanics to make my universe-switching mechanics kinda work.

  • A universe is created via the creation of a new tiddler (which title can be something like “Fun”, “Poems”, “Video clips”, “Music parodies”, “Swipe file”…)
  • Each universe has the tag $:/tag/univers, plus other tags that will update the default tags of a new tiddler via $:/config/NewTiddler/Tags
  • Switching universe should also update the default tiddlers displayed in the feed via updating the $:/DefaultTiddlersand this is where I struggle.

I’ve looked into concatenation but I fail to make it wok.

Here is the current code for the dropdown menu that is displayed when clicking on a Button I created, that is displayed in the controls section of the topbar:

$:/plugins/astroport/universes/Buttons/Télécommande
(button code)

\whitespace trim
<span class="tc-popup-keep">
<$button popup=<<qualify "$:/state/popup/theme">> tooltip={{$:/language/Buttons/Theme/Hint}} aria-label={{$:/language/Buttons/Theme/Caption}} class=<<tv-config-toolbar-class>> selectedClass="tc-selected">
<$list filter="[<tv-config-toolbar-icons>match[yes]]">
{{$:/core/images/preview-open}}
</$list>
<$list filter="[<tv-config-toolbar-text>match[yes]]">
<span class="tc-btn-text"><$text text={{$:/language/Buttons/Theme/Caption}}/></span>
</$list>
</$button>
</span>
<$reveal state=<<qualify "$:/state/popup/theme">> type="popup" position="below" animate="yes">
<div class="tc-drop-down">
<$linkcatcher to="$:/plugins/astroport/universes/chaine_allumee">
{{$:/plugins/astroport/universes/snippets/telecommande}}
</$linkcatcher>
</div>
</$reveal>

$:/plugins/astroport/universes/snippets/telecommande
(dropdown menu code)

\whitespace trim
\define concat(a, b, c) $a$$b$$c$

\define change-universe()
<$set name="universeTags" tiddler=<<navigateTo>> field="tags" filter="-[prefix[$:/]]">
<$action-setfield $tiddler="$:/config/NewTiddler/Tags" $field="text" $value=<<universeTags>>/>
<$set name="defaultFilter" tiddler="$:/config/NewTiddler/Tags" filter="">
<$macrocall $name="concat" a="[all[tiddlers]tag[" b=<<universeTags>> c="]]" />
<$action-setfield $tiddler="$:/DefaultTiddlers" $field="text" $value="" />
<$/set>
<$/set>
<$action-setfield $tiddler="$:/plugins/astroport/universes/chaine_allumee" $field="text" $value=<<navigateTo>>/>
\end

<div class="tc-chooser">
<$list filter="[tag[$:/tags/univers]]">
<$set name="cls" filter="[all[current]field:title{$:/plugins/astroport/universes/chaine_allumee}] [[$:/plugins/astroport/universes/chaine_allumee]field:title<currentTiddler>] +[limit[1]]" value="tc-chooser-item tc-chosen" emptyValue="tc-chooser-item">
<div class=<<cls>>>
<$set name="chaine" value={{$:/plugins/astroport/universes/chaine_allumee}}>
<$set name="filtre" value="[field:chaine[<<chaine>>]]">
<$linkcatcher actions=<<change-universe>>><$link to={{!!title}}>[{{!!app}}]&nbsp;<$view field="title" format="text"/></$link></$linkcatcher> &nbsp; <$link to={{!!title}}>(éditer)</$link>
</$set>
</$set>
</div>
</$set>
</$list>
</div>

I can’t figure out how I need to edit this part:

<$action-setfield $tiddler="$:/DefaultTiddlers" $field="text" $value="" />

to update $:/DefaultTiddlers with a value looking like this:

[all[tiddlers]tag[Fun]tag[music]]

(for a “universe” displaying parodies of musical videos)

I think I would need something like an implode function to convert a list of tags into the tag[Fun]tag[music] format.

Can anyone point me in the right direction?

Thanks!

This is correct - BUT action widgets (i.e the $action-setfield) must be inside a trggering widget like e.g a <$button> widget or similar, see: https://tiddlywiki.com/prerelease/#TriggeringWidgets

What do you mean with “convert a list of tags”? Could you elaborate on what it is you have and what you want.

Please also explain what you hate/hated with TW. It is obviously very unusual to have people who do not like TW here on the boards and we wish to make TW appealing, perhaps especially for people like you who are clearly versed in coding and tech, so what is not good about it and how ought it be instead? Your input is very valuable.

Try to replace these 2 lines:

<$macrocall $name="concat" a="[all[tiddlers]tag[" b=<<universeTags>> c="]]" />
<$action-setfield $tiddler="$:/DefaultTiddlers" $field="text" $value="" />

with:

<$let
  intro="[all[tiddlers]"
  outro="]"
  prefix="tag["
  suffix="]"
  filterString={{{[<universeTags>enlist-input[]] :map[addprefix<prefix>addsuffix<suffix>] +[join[]addprefix<intro>addsuffix<outro>]}}}
>
<$action-setfield $tiddler="$:/DefaultTiddlers" $field="text" $value=<<filterString>> />
</$let>

It should work… maybe… :sweat_smile:

Fred

Hi @twMat,

Here are examples of what I want to do:

a universe tiddler (eg: "music parodies")
→ a list of tags (music, Fun)
→ [all[tiddler]tag[music]tag[Fun]
another universe tiddler (eg: "geek humor")
→ a list of tags (dev, Fun)
→ [all[tiddler]tag[dev]tag[Fun]

I manage setting $:/config/NewTiddler/Tags with that line:

<$set name="universeTags" tiddler=<<navigateTo>> field="tags" filter="-[prefix[$:/]]">

(with the action change-universe() called with the <$linkcatcher />)

but I fail to update $:/DefaultTiddlers with a dynamic value derived from <> (which is a universe tiddler title which tags are the ones I want to use for the filter of the default timeline)

On the using end:

  • I find the non-linear navigation confusing
  • I find confusing having the ability to edit tiddlers when I am visiting a TW

On the developing end:

  • It took me quite some time to understand when a filter is evaluated directly or when I have to put it inside something like a widget like <$list filter="[all[tiddlers]tag[Fun] />
  • At first I hadn’t seen the “Contents” tab on tiddlywiki.com
  • It took me some time to understand the mechanics of shadow tiddlers and the fact that naming a tiddler with the same name of a tiddler shadow would override its contents and that I could package it into my own plugin.

Actually, I’m a Luddite ; I’ve read Roy Lewis’ novel “How I ate my father” and I think technology has been a mistake since the invention of fire. My sole goal here is to prove to @papiche that it is impossible to recreate social networks using TiddlyWiki and IPFS.


@tw-FRed > Your answer popped when I was writting… Wow, thanks! This is really near from what I need. I just miss filtering out $:/tags/univers tags to have all the posts listed in the timeline (at the moment I only have the universe tiddler, which is logic). I have seen in the Filter Expression tiddler that I could use something like +[!prefix[$:/]] or +[!is[system]]. I’ll look into it tomorrow and keep you posted.

Thanks again to you both. @papiche was right when telling me people are nice here :slight_smile:

Yes, it is an acquired taste. I’ll return to your full post more properly, probably in the weekend, but just regarding this particular gripe, I just the other day created ScrollBack. This might mitigate at least one of the confusing aspects with the non-linear navigation. (In case you don’t know how to install a foreign tiddler/plugin into your wiki: You go to the site and click-hold the tiddler link there and drag it over to your wikis browser tab and drop it onto your wiki.)

1 Like

Ok, so I’ve managed to find a workaround to my problem: I won’t use the $:/tags/univers anymore. Instead, I’ll use a custom field called univers on “universe tiddlers”, and check if that field exists to filter out “universe tiddlers”.

Here is the code:

button

\whitespace trim
<span class="tc-popup-keep">
<$button popup=<<qualify "$:/state/popup/theme">> tooltip={{$:/language/Buttons/Theme/Hint}} aria-label={{$:/language/Buttons/Theme/Caption}} class=<<tv-config-toolbar-class>> selectedClass="tc-selected">
<$list filter="[<tv-config-toolbar-icons>match[yes]]">
{{$:/core/images/preview-open}}
</$list>
<$list filter="[<tv-config-toolbar-text>match[yes]]">
<span class="tc-btn-text"><$text text={{$:/language/Buttons/Theme/Caption}}/></span>
</$list>
</$button>
</span>
<$reveal state=<<qualify "$:/state/popup/theme">> type="popup" position="below" animate="yes">
<div class="tc-drop-down">
{{$:/plugins/astroport/universes/snippets/telecommande}}
</div>
</$reveal>

(I had a useless <$linkcatcher /> in it ; don’t know what it was doing here)

dropdown menu

\whitespace trim

\define change-universe()
<$action-setfield $tiddler="$:/plugins/astroport/universes/chaine_allumee" $field="text" $value=<<navigateTo>>/>
<$set name="universeDefaultTitle" tiddler=<<navigateTo>> field="titre">
<$action-setfield $tiddler="$:/language/DefaultNewTiddlerTitle" $field="text" $value=<<universeDefaultTitle>> />
</$set>
<$set name="universeTags" tiddler=<<navigateTo>> field="tags">
<$action-setfield $tiddler="$:/config/NewTiddler/Tags" $field="text" $value=<<universeTags>>/>
<$let
  intro="[all[tiddlers]"
  outro="]-[has:field[univers]]+[!nsort[created]]+[limit[10]]"
  prefix="tag["
  suffix="]"
  astroportDefaultTiddlersFilter={{{[<universeTags>enlist-input[]] :map[addprefix<prefix>addsuffix<suffix>] +[join[]addprefix<intro>addsuffix<outro>]}}}
>
<$action-setfield $tiddler="$:/DefaultTiddlers" $field="text" $value=<<astroportDefaultTiddlersFilter>> />
<$action-sendmessage $message="tm-home" />
<$/let>
</$let>
<$/set>
\end

<div class="tc-chooser">
<$list filter="[all[tiddlers]has:field[univers]]">
<$set name="cls" filter="[all[current]field:title{$:/plugins/astroport/universes/chaine_allumee}] [[$:/plugins/astroport/universes/chaine_allumee]field:title<currentTiddler>] +[limit[1]]" value="tc-chooser-item tc-chosen" emptyValue="tc-chooser-item">
<div class=<<cls>>>
<$set name="chaine" value={{$:/plugins/astroport/universes/chaine_allumee}}>
<$set name="filtre" value="[field:chaine[<<chaine>>]]">
<$linkcatcher actions=<<change-universe>>><$link to={{!!title}}>[{{!!app}}]&nbsp;<$view field="title" format="text"/></$link></$linkcatcher><$link to={{!!title}}>(éditer)</$link>
</$set>
</$set>
</div>
</$set>
</$list>
</div>

Problem: default tags when importing

Now I’m looking into having default tags apply when importing a file. At the moment, only system tags $:/tags/isEmbedded and $:/tags/isAttachment are displayed :thinking:

Problem: changing behavior of tag pills

I also want to delete the dropdown menu on tag pills and instead have the click refresh the view to list most recent tiddlers with said tag.

I’ve started playing with override of $:/core/ui/TagTemplate, but have not succeeded in getting what I’m looking for yet.

After exploring the system tiddlers, I have found that the macro called tag-pill-body is defined in $:/core/macros/tag. I’m studying it to see it I can adapt the code to my needs.

And after playing a few more half hours, I’ve discovered that to refresh the view, I need to update the list field of $:/StoryList.

So here is what I’ve got so far for the $:/core/ui/TagTemplate tiddler:

\whitespace trim
<span class="tc-tag-list-item" data-tag-title=<<currentTiddler>>>
<$set name="tiddlersToOpen" value={{{ [all[tiddlers]tag[<currentTiddler>] }}}>
<$let 
before="setTitle='$:/StoryList' setField='list' setTo='"
middle={{{ <tiddlersToOpen> }}} 
after=" dragFilter='[all[current]tagging[]]' tag='span'"
elementAttributes={{{ <before><middle><after> }}}
>
<$macrocall $name="tag-pill-body" 
tag=<<currentTiddler>> 
icon={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerIconFilter]!is[draft]get[text]] }}} 
colour={{{ [<currentTiddler>] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerColourFilter]!is[draft]get[text]] }}} 
palette={{$:/palette}} 
element-tag="""$button""" 
element-attributes={{{ <elementAttributes> }}} />
</$let>
</$set>
</span>

Once again I struggle with understanding when variables will or won’t be evaluated when put inside brackets, (triple) curly brackets, chevrons and double quotes :face_with_monocle: