Acting on individual characters with shortcode

I want to make a shortcode, call it “bellcurve” that takes a string and renders it, character-by-character in a small font that GROWS IN THE MIDDLE and is small again at the end.

So, for example, this…

{{< bellcurve "fiver" >}}

…might render as…

<span class="small">f</span><span class="medium">i</span><span class="large">v</span><span class="medium">e</span><span class="small">r</span>

After much reading of the Hugo docs and Googling, two questions remain:

First, how can I split the param, “fiver” into individual runes?

Note: If this isn’t possible, I suppose I could have the shortcode take an array of strings and then have them typed one character at a time, but this seems messy.

{{< bellcurve the text to shape >}}

…compared to…

{{< bellcurve t h e " " t e x t " " t o " " s h a p e >}}

Second, is it possible to traverse an array with an index in order to calculate the size class based on a character’s position in the string? The goal would be to ensure that the peak is always near the center.


The goal is to have the peak font size always hit in the middle of the shortcode param.

Am I asking too much of Hugo templates, or is this doable?


Perhaps try split using an empty string "" as delimiter?

range is your friend here.

countrunes might assist, also.

Thanks @pointyfar and @maiki! Those three suggestions helped a lot. For what it’s worth, here’s the end result of my font-size experiment using inline styles rather than classes.

This is my first stab at making a “real” custom shortcode, so there’s probably all kinds of ways this could have been done more eloquently, or more in line with Hugo conventions. Anyone reading this: please don’t hesitate to school me and I’ll happily post revisions.

Oh, and one last thing: I changed the shortcode name from bellcurve to swell. It’s a better description for the behavior and for how I feel about the results! :wink:

Sample Input:

<div style="line-height: 1.6rem">
<div>012{{< swell "345" >}}678</div>
<div>012{{< swell "3456" >}}789</div>
<div>012{{< swell "34567" >}}890</div>
<div>012{{< swell "345678" >}}901</div>
<div>012{{< swell "3456789" >}}012</div>
<div>012{{< swell "34567890" >}}123</div>
<div>012{{< swell "345678901" >}}234</div>
<div>012{{< swell "345678901234567" >}}890</div>
<div>abc{{< swell "def" >}}ghi</div>
<div>abc{{< swell "defg" >}}hij</div>
<div>abc{{< swell "defgh" >}}ijk</div>
<div>abc{{< swell "defghi" >}}jkl</div>
<div>abc{{< swell "defghij" >}}klm</div>
<div>abc{{< swell "defghijk" >}}lmn</div>
<div>abc{{< swell "defghijkl" >}}mno</div>
<div>abc{{< swell "defghijklmnopqrs" >}}tuv</div>

Sample Output


     Usage: {\{< swell "some text" >}\}
     Result: A clean set of nested spans that make the text grow then shrink.

     Custom handling is given for odd-length and even-length strings in order to ensure that the first and last characters are always the "normal" size, i.e., the same size as the surrounding text.

     Caveat Coder: It was really hard to get this code to be both formatted for readability AND to avoid whitespace leaks. All changes you might make require careful testing to make sure nothing's failing.

     Testing: Copy the text below into a Hugo template. Add in extra {'s to make the shortcodes work.

     <div>012{< swell "123456789" >}}012</div>
     <div>901{< swell "12345678" >}}901</div>

     <div>jkl{< swell "abcdefghi" >}}jkl</div>
     <div>ijk{< swell "abcdefgh" >}}ijk</div>

     If all goes well, there should be no spaces within the divs.
{{- /* Values */ -}}
{{- $stringToWriggle := .Get 0 -}}
{{- $chars := split $stringToWriggle "" -}}
{{- $count := countrunes $stringToWriggle -}}
{{- $isEven := modBool $count 2 -}}
{{- $halfLength := (div $count 2) -}}
{{- $scratch := .Scratch -}}

<!-- Loop over every character -->
{{- range $elem_index, $elem_val := $chars -}}

    <!-- Leave beginning and end marks at "normal" size -->
    {{- if (or (eq $elem_index 0) (eq $elem_index $count)) -}}
        {{- $elem_val -}}

    <!-- For even-length strings, the 2nd central character _doesn't_ grow… -->
    {{- else if (and $isEven (eq $elem_index $halfLength)) -}}
        {{- $elem_val -}}

    <!-- Left-half text -->
    {{- else if (le $elem_index $halfLength) -}}
        <span style="font-size:larger;">{{- $elem_val -}}

        <!-- Save the matching </span> for the end -->
        {{- $scratch.Add "closingSpans" "</span>" -}}

    <!-- Right-half text -->
    {{- else -}}
        <span style="font-size:smaller;">{{- $elem_val -}}

        <!-- Save the matching </span> for the end -->
        {{- $scratch.Add "closingSpans" "</span>" -}}
    {{- end -}}
{{- end -}}
{{- $scratch.Get "closingSpans" | safeHTML -}}
1 Like