2

I'm using Thymeleaf 3.1 fragments to compose a site and I'm running into some issues with including and styling an element that's passed in as a fragment itself. Specifically, I'm trying to set the size of an SVG fragment that's passed in as an argument to another fragment.

I have a fragment like this:

<!-- tiles.html -->

<div th:fragment="tile(svgElement)">
  <svg th:replace="${svgElement}"></svg>
</div>

When I use this fragment, the svgElement argument is another fragment, like this:

<!-- page.html -->

<div th:replace="~{/tiles :: tile(~{/svg :: drawing})}"></div>

Here I am passing in ~{/svg :: drawing} to the tile fragment as an argument. drawing is SVG code that lives in another HTML file, for instance, something like this:

<!-- svg.html -->

<svg
  th:fragment="drawing"
  width="24"
  height="24"
  viewBox="0 0 24 24"
  fill="none"
  xmlns="http://www.w3.org/2000/svg"
>
  <path
    fill-rule="evenodd"
    clip-rule="evenodd"
    d="M13.5 2H10.5V10.5H2V13.5H10.5V22H13.5V13.5H22V10.5H13.5V2Z"
    fill="black"
  />
</svg>

The tile fragment takes the drawing and replaces the placeholder svg element with drawing. That said, I want to be able to add a class to the svg element. Something like this:

<!-- tiles.html -->

<div th:fragment="tile(svgElement)">
  <svg th:replace="${svgElement}" class="w-16 h-16 p-6"></svg>
</div>

But the way I'm doing it here, since I am using th:replace the class="w-16 h-16 p-6" is lost. I've been looking for a way to add a class to the element that will replace the placeholder or alternatives that don't involve defining external CSS (I'm using tailwind).

What I've Tried

I'm using tailwindcss to keep all my styling in the templates. My current solution is adding a class name to the tile and then using a CSS selector to select the child svg elements:

<!-- tiles.html -->

<div class="tile" th:fragment="tile(svgElement)">
  <svg th:replace="${svgElement}"></svg>
</div>
.tile svg {
  height: 4rem;
  width: 4rem;
}

This works but the point of using tailwind is to eliminate the need to define external CSS like this. So ideally I'd like to find a way to make this work.

The other thing I've tried is to use th:include and make the browser clean up the nested <svg><svg>...</svg></svg> that would result. I've tried this and adding classes to the outer <svg> tag, and also going in and adding svg particular attributes:

<!-- tiles.html -->
<div class="tile" th:fragment="tile(svgElement)">
  <svg 
    th:include="${svgElement}"
    th:classappend="${'h-16 w-16 p-6'}"
  ></svg>
</div>

<!-- or -->

<div class="tile" th:fragment="tile(svgElement)">
  <svg 
    th:include="${svgElement}"
    th:width="64"
    th:height="64"
    th:attr="viewBox=${'0 0 64 64'}"
    xmlns="http://www.w3.org/2000/svg"></svg>
</div>

But these results in wonky results even though the nested SVG does seem to be taken care of. I've been trying this with larger SVGs that I need to shrink down. It seems like if it starts out nested, the inner paths aren't resized along with the bounding svg element. So I'm left with the right-sized element, but the underlying drawing hasn't been resized, so I only see a piece of it.


I'll also add that ideally, I don't add a classNames parameter to all my SVG fragments. There are a lot of them!

Sebastian Kaczmarek
  • 8,120
  • 4
  • 20
  • 38
iansedano
  • 6,169
  • 2
  • 12
  • 24
  • I don't have a solution but I would love for Thymeleaf to have something for this that you can maybe wrap an SVG with something else and you could add some attributes on the wrapped thing. Maybe create an issue in the Thymeleaf issue tracker to request if they could support this? – Wim Deblauwe Feb 22 '23 at 13:30

0 Answers0