Trick to convert the core images to SVG image for CSS background

,

If we want to add an svg icon to a link with css, as far as I know we currently have a few options :

  1. using a font with icons like font awesome : allows to change the color but limited to the font awesome icons
  2. referencing an external SVG file in a background url : the color must be set in the svg itself
  3. transcluding a custom tiddler and use it as an URI in a background url : the color must be set in the svg itself

The issue is addressed here :

https://groups.google.com/g/tiddlywiki/c/YznqeuiZyqQ/m/GtXDxe0SBgAJ

Turns out there actually IS a straightforward way to convert the core images.
Here’s the trick : svg balise can be contained inside another svg balise !

Using this fact, we can generate a SVG URI with a custom color, using filter and some widgets.

This all can be done in only one tiddler :

\rules only filteredtranscludeinline transcludeinline macrodef macrocallinline html
<pre>

.icon-svg:before{
    content: " ";
    display: inline-block;
    height:22pt;
    width:22pt;
    background: center / contain no-repeat var(--url);
}

.icon-svg {
<$vars svg={{$:/core/images/tip}} fill=yellow> {{!!url}} </$vars>
}

.icon-svg.github {
<$vars svg={{$:/core/images/github}} fill=blue> {{!!url}} </$vars>
}

In the field url :

`--`url:url(<$text text={{{[['data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" fill="]][<fill>][[" width="22pt" height="22pt">]][<svg>][[</svg>']]+[join[]]}}} />);

Here’s a live demo :

https://telumire.github.io/TW-tips/index.html#Trick%20to%20use%20system%20svg%20with%20css%20and%20allow%20to%20change%20the%20color

Trick to use system svg with css and allow to change the color.json (792 Bytes)


EDIT: I just found out that hexadecimal color values doesn’t work in an url, but luckily there is an easy workaround ! We just have to replace # with %23 :

fill={{{ [{$:/palette}getindex[external-link-foreground]search-replace[#],[%23]] }}}

We can even add a fallback in case the color specified isnt in the current color palette and upgrade the filter to only require the name of the color (replace the content of the url field with the following):

<$vars 
svg={{{[<svg>get[text]else<svg>] }}}
fill={{{[<fill>]:reduce[{$:/palette}getindex<currentTiddler>match[inherit]then[foreground]else<currentTiddler>]:reduce[{$:/palette}getindex<currentTiddler>else<currentTiddler>]:reduce[<currentTiddler>is[blank]then[$:/palettes/Vanilla]getindex[foreground]else<currentTiddler>]+[search-replace[#],[%23]]}}}>  

`--`url:url(<$text text={{{[['data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" fill="]][<fill>][[" width="22pt" height="22pt">]][<svg>][[</svg>']]+[join[]]}}} />);

</$vars>

Now we can write something like <$vars svg={{$:/core/images/open-window}} fill="background" > {{!!url}} or even <$vars svg={{$:/core/images/open-window}} fill="#FF0000" > {{!!url}} without issues !

If fill is blank, the color default to <<color foreground>>.


EDIT2 : Here’s another demo, demonstrating how to style external links :

https://Telumire.github.io/TiddlyTweaks/index.html#:[[$:/ThemeTweaks/svg/style/external_link]]

Futures improvements (if any) will be made to this url instead of the previous one.

1 Like

I had a look at @telumire wiki other day and I found quite interesting tips and tricks! I hope @telumire find some time to share some of them here :wink:

1 Like

VERY useful post!

FYI, you might find it helpful to read the (ongoing, slightly slow) discussion about TW-Icon harmonization on Github (i.e . the 40,000 additional icons provided via morosanuae’s huge work) .

Best wishes
TT

2 Likes

@telumire I am very interested in this result, but not 100% sure how to make use of it. Can you please provide an instruction for us dummies?

Eg; how could I go about changing the color of an icon in a core or custom button when naming the image?

The following seems to contain a spurious “]”

{{$:/core/images/tip[}}

It is interesting!

I think @telumire illustrates that our general idea that TW = JS + HTML + CSS is slightly understated.

I do think that, more likely, TW = SVG + JS + HTML + CSS is closer to it’s functional reality. Certainly the degree of integration of SVG into the TW UI is pretty exceptional.

Just a comment
TT

1 Like

The following seems to contain a spurious “]”

Nice catch ! Yes it should read

<$vars svg={{$:/core/images/tip}} fill=yellow> {{!!url}}

Eg; how could I go about changing the color of an icon in a core or custom button when naming the image?

Do you mean changing the color of an existing button with CSS only ?

This is a bit tricky … using this method, you would target the element with CSS, then hide the SVG inside the button and use a pseudo-element after or before to display your custom SVG as the background image of the pseudo-element. I can try my hand at it tomorrow if you want :slight_smile:

The way I make use of it is for external links. If you want to display an icon to external links you would type :

.tc-tiddler-body a.tc-tiddlylink-external:after, a[target=_blank]:after{
    content: " ";
    margin-left:.5ch;
    display: inline-block;
    height:1em;
    width:1em;
   <$vars svg={{$:/core/images/open-window}} fill={{{ [{$:/palette}getindex[external-link-foreground]] }}} > {{!!url}}</$vars>
    background: center / contain no-repeat var(--url);
}

.tc-tiddler-body a.tc-tiddlylink-external:visited:after { <$vars svg={{$:/core/images/open-window}} fill={{{ [{$:/palette}getindex[external-link-foreground-visited]] }}} > {{!!url}}</$vars>}

.tc-tiddler-body a.tc-tiddlylink-external:hover:after { <$vars svg={{$:/core/images/open-window}} fill={{{ [{$:/palette}getindex[foreground]] }}} > {{!!url}}</$vars>}

Without forgetting to fill the url field :

`--`url:url(<$text text={{{[['data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" fill="]][<fill>][[" width="22pt" height="22pt">]][<svg>][[</svg>']]+[join[]]}}} />);

Result :

image

1 Like

SVG is part of the HTML spec but yes, it’s easy to forget how well all these specs are intermingled !

TBH I wasn’t aware SVG was a subsidiary of HTML. Partly I think because in high end vector graphic software like Adobe Illustrator (which I use a lot) it is treated as a thing in itself independent of HTML applications.

Regarding the use of SVG in TW I thought your OP very useful. Given the degree to which SVG is used to form the UI in TW — I do think forays into optimizing its use, i.e. better understanding how to optimise its presenting, is well worth the journey.

1 Like

Well to be exact it is XML-based : It can be viewed stand-alone, mixed with HTML content, or embedded using XML namespaces within other XML languages so yes it is part of the HTML spec but it is also it’s own thing :slight_smile:

Btw I’ve found a great article about how to set a color for SVG in CSS backgrounds : https://codepen.io/noahblon/post/coloring-svgs-in-css-background-images. A macro to find out the correct hue rotation / saturation / brightness would probably be more performant than generating a whole new svg each time we want to use a custom color but I’m not sure how to go about that…

2 Likes

Updated to support dark mode

Here’s another use for this trick : dynamic SVG favicon for your wiki ! You can make it so the color of the favicon change according to the current palette, or if the darkmode is enabled.

In a tiddler with the tag $:/tags/AboveStory :

<$vars palette={{{[{$:/info/darkmode}match[yes]then{!!dark}else{!!light}]}}} >
<$vars
svg={{$:/core/images/theme-button}}
fill={{{[<palette>getindex{SVG favicon!!color}match[inherit]then[foreground]else{SVG favicon!!color}]:reduce[<palette>getindex<currentTiddler>else<currentTiddler>]:reduce[<currentTiddler>is[blank]then[$:/palettes/Vanilla]getindex[foreground]else<currentTiddler>]+[search-replace[#],[%23]]}}}
>
<link 
rel="icon" 
type="image/svg+xml"
href={{{[[data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" fill="]][<fill>][[" width="22pt" height="22pt">]][<svg>][[</svg>]]+[join[]]}}}
/>
</$vars>
</$vars>

With the fields

dark:$:/palettes/GruvboxDark
light:$:/palettes/Blanca

You set the color of the favicon with a field named “color”. Tiddlywiki will automatically provide a color picker, too. Neat !

Here I use the $:/core/images/theme-button, but you can of course use any svg you like.

It’s also possible to use emoji :

svg='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><text x="50%" y="50%" font-size="12" dominant-baseline="central" text-anchor="middle">😎</text></svg>'

and SVG animations :

svg='
<svg width="22pt" height="22pt" viewBox="0 0 128 128">
  <rect x="10" y="10" width="100" height="100">
    <animate attributeType="XML" attributeName="x" from="-100" to="120" dur="10s" repeatCount="indefinite"/>
  </rect>
</svg>
'

svg animation

Online demo : TiddlyTweaks — Small tweaks for TiddlyWiki

Note : This demo only support firefox (for now), to support the other major browser see Are you using SVG favicons yet? A guide for modern browsers. | by Antoine Boulanger | The Startup | Medium

EDIT : it does in fact works on chrome as well ! The issue was that chrome only accept favicons in the head tag.

3 Likes

Hi, That’s a nice idea, but I don’t have a dark theme set to my browser, so the favicon is almost invisible.

The demo currenty doesnt use the darkmode, it’s more of a proof of concept … but I will add it soon (tomorrow, probably) ! For now you can use the switch palette icon to switch to the light palette (see bellow), this should improve the contrast :

image

Here’s the result :

EDIT: It should be fixed now !

2 Likes