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!