Altering colours in SVG image by means of a macro

I am trying to liven up a custom button on each tiddler toolbar to have a colour dependent on an integer value [ 1 to 100 ] stored in a field on the tiddler.

I am copying the very cool feature in the Journal tool icon where a macro call inside the SVG file is parsed by Tiddlywiki and ends up substituing the integer value returned by the macro before the SVG is parsed by the browser ( that’s how the Journal icon displays the day of the month )

RatingSVG is the name of the macro - returns value 1 to 100, here the number will appear within the icon when it is displayed as a button.

Here is the SVG - the text statement and call to RatingSVG are very near the end of this script.

<svg width="22pt" height="22pt" class="tc-image-bts-phone-landscape-fill tc-image-button" viewBox="0 0 16 16"><path
     d="m 2,12.5 c -1.1045695,0 -2,-0.895431 -2,-2 v -6 c 0,-1.1045695 0.8954305,-2 2,-2 h 12 c 1.104569,0 2,0.8954305 2,2 v 6 c 0,1.104569 -0.895431,2 -2,2 z"
     id="path4120"
     sodipodi:nodetypes="sssssssss"
     style="fill:#206ee1; fill-opacity:0.3" /><text  class="tc-fill-background" font-family="Helvetica" font-size="8" font-weight="bold" ><tspan x="8" y="10" text-anchor="middle"><<RatingSVG>></tspan></text></svg>

That works fine! The button is displayed as an icon with an integer value displayed on the button.

What I would like to do now is to play around with altering the colour of the button according to the value of the rating [ 1 to 100 ] so I could for instance replace

style="fill:#206ee1 ..."

With


style="fill:hsl(<<RatingSVG>>, 100%, 50%) ..."

However the macro does not seem to be evaluated when it is ‘called’ from with quotes, when I use my browsers developer tools to view the final parsed SVG I still see the macro name-call rather than the integer value expected to be returned from that call.

Any ideas how to successfully inject macro returned values inside quotes in this situation? I don’t know for sure that the problem is caused by the quotes but it seems likely to me as the other call that labels the button with the macro return does work.

Thanks!

This won’t work, as far as I know, but I can’t really explain the mechanism behind it.

Macro can be used directly as a value of an attribute, e.g. <svg style=<<RatingSVGStyle>> > will work, but placing the macro inside a quoted string, as you did, will not work. I know two workarounds for such a situation in general:

1. Use classes instead of inline css, because there is no problem with using macros in a stylesheet tiddler or in-between <class> tags. You can assign a class to the svg <svg class="my-class"> and then define styling of this class like this:

<style>
svg.my-class {
    fill:hsl(<<RatingSVG>>, 100%, 50%);
}
</style>

Even better would be to place the style definition (from in-between <style> tags) in a separate tiddler tagged with $:/tags/Stylesheet, this way if you display multiple buttons at once, the style portion won’t be unnecessarily repeated multiple times in the final parsed code.

2. The second method might be easier to apply in your use case. Construct the whole inline style string using a second macro and use this macro directly as an attribute value, e.g. <svg style=<<RatingSVGStyle>> >.

Until recently the most common way to do this was to have a macro that included the whole style value, containing the macro and providing that to the style attribute style=<<mystyle>> you can (still) use the define pragma and use substitution;

\define mystyle() fill:hsl($(RatingSVG)$, 100%, 50%) ...

style=<<mystyle>>

however now you can use the “Substituted Attribute Values” method or the version 5.2.2 feature added support for directly specifying style properties on HTML elements (for example, <div style.color={{!!color}}>)

Or in your case style.fill=<<fillparameter>>

I am only hinting at the methods and have not tested them for you now, but others may use this as a reference.

Thanks - I am getting a little further with this after your help

Thanks TW_Tones - I am getting a little further with this after your help

I’m not able to put time into full solution, but please note formations like this can help you build calculation-responsive svg styles:

<svg viewBox="0 0 350 250">
  <circle cx="125" cy="85" r="80" fill={{{ [get[blue-value]addprefix[RGB(0,0,]addsuffix[)]] }}} opacity="45%"/>
</svg>

This simple version specifies only the blue-value within an RGB(R,G,B) color rubric by consulting a field (the blue-value field here), but whatever you can do within a filter can serve to tweak your fill values, so long as you build the filter carefully. Remember not to put quotes around a fill value that is specified this way. :wink:

1 Like

So, to follow up:

This part is easy! But then you go on to talk about a custom macro, rather than a value that is stored in a field…

Might you reconsider whether you need a macro?

If what your macro is doing can be built into a filter expression, your svg can just work directly with that filter. Using the field-based version as a reference…

<svg viewBox="0 0 50 50">
  <circle cx="25" cy="25" r="20" fill={{{ hsl( [{!!rating}] =, 90% =, 50% ) +[join[]] }}} />
</svg>

… tweak as needed. As long as you can articulate the desired value as a filter expression, you can get your SVG to calculate the color with HSL (which is very convenient for values mapped onto the 0-100 range) inline.

1 Like

Elaborating a little on @TW_Tones answer, if your TW version is 5.3.1 then you can use Substituted Attribute Values like this:

\define RatingSVG() 124

<svg width="22pt" height="22pt" class="tc-image-bts-phone-landscape-fill tc-image-button" viewBox="0 0 16 16"><path
     d="m 2,12.5 c -1.1045695,0 -2,-0.895431 -2,-2 v -6 c 0,-1.1045695 0.8954305,-2 2,-2 h 12 c 1.104569,0 2,0.8954305 2,2 v 6 c 0,1.104569 -0.895431,2 -2,2 z"
     id="path4120"
     sodipodi:nodetypes="sssssssss"
     style=`fill:hsl($(RatingSVG)$, 100%, 50%)` /><text  class="tc-fill-background" font-family="Helvetica" font-size="8" font-weight="bold" ><tspan x="8" y="10" text-anchor="middle"><<RatingSVG>></tspan></text></svg>

The important part is:

style=`fill:hsl($(RatingSVG)$, 100%, 50%)`

where the attribute value is surrounded by single backticks, and the variable name is surrounded by $( and )$.

I did not check that it works with a more complicated variable definition than in the example above, but it should.

Fred

2 Likes

Thanks Springer, yes maybe I should drop the macro, :grinning: I kind of went that way to have flexibility in how a value in the range [1 … 100] might be used to alter a colour for instance I might end up wanting to do some maths but maybe I am making life for complicated for myself and take the simplest path first.

I will give it a try and report back here in case anyone else wants something similar one day.

Background to my question

I find the numbers on the buttons a useful guide to an associated numeric value but being able to also give a color indicator makes at ‘a glance’ decisions easier - imaginary analogy - a button that displays red for hot and blue for cold on a cooking appliance but also shows temperature in degrees for precision as well.

So far I have managed to get it working with a class statement but of course that meant all instances of the button ( it appears on each tiddler ) changed colour as the value for one tiddler was changed.

There are some solutions suggested that look promising so I will dive in and see what happens -thanks everyone

Hi Fred, that looked like the simplest way to go I tried first defining RatingSVG1 and assigning it the value 80 very much like your code.

I eventually dropped down to hard coding a numeric value just try and work up to complex from simple…

style=`fill:hsl($(RatingSVG)$, 100%, 50%)`

And that still got chewed up by the browser - web developer tools reveals this as the parsed result which is then rejected - the browser parses it to give this…so it rejected everything after the first arg and added double quotes.

style="`fill:hsl(123,"

It’s hard to know the “mind of the browser” simply from developer tools but it seems that somewhere between the Tiddlywiki parsing and the browser parsing SVG script there is something that coughs on this syntax.

The impression I get at the moment messing around is that the SVG format requires double quotes in this situation and Tiddlywiki whilst it does seem to parse SVG script it does seem to treat anything in quotes as a literal string that does not require evaluating further - I could be wrong but that seems to be the pattern.

Hi @jonnie45

Sorry that it doesn’t work for you.
I checked again the code in a new tiddler on tiddlywiki.com and here is the result:

You can see the substitution happens correctly.
Could you please confirm the same test works for you?

Some points might make a difference:

  • The tiddler type should be empty or text/vnd.tiddlywiki for the substitution to work.
  • TW version must be 5.3.0 or newer. Please check your wiki version.

Also, I don’t think the browser is implied because the substitution is computed by the TW core during the rendering of wikitext to html/svg/js/whatever, and only then is the browser making its magic.

Don’t give up, we’ll find a solution! :slightly_smiling_face:

Fred

Hi Fred,

Sorry I missed the important version note - TW 5.3 !!

Thanks for your help. I now have something rough and ready running now…here is a shot of new tiddlers, the button appears in different colours according to the tiddler field value [1 to 100] the grey button indicates that the owning tiddler has not been given a value, clicking on the grey button would create the field with a default value and also cause the increment and decrement buttons to appear which can then be used to change the value of the field and so the button changes colour as I click away on the inc/dec buttons.

I probably haven’t chosen particularly good colours - it’s all about getting the principle working for now.

The macro for the tiddler button includes the following line which now uses a different name RatingSVG1 as I want to play around with adding a baseline value of say 100 before adding the value from the range [1…100] - rating3 is the tiddler field name that contains a value in the range [1 to 100]

<$set name=“RatingSVG1” value={{{ [{!!rating3}add[100]abs[]] }}}>

Now that the value is being processed and getting to where it is needed in the SVG script I can play around and see what other modifications of the appearance of the button might make sense for giving a quick glance indicator of the numeric value - shape? colour? Time to play !

Thanks to all :grinning: