Tartan ist ein gemusterter Stoff, der typischerweise mit Schottland assoziiert wird, insbesondere mit deren modischen Kilts. Auf tartanify.com haben wir über 5.000 Tartanmuster (als SVG- und PNG-Dateien) gesammelt und darauf geachtet, alle Muster herauszufiltern, die ausdrückliche Nutzungsbeschränkungen haben.
Die Idee wurde während unseres Sommerurlaubs in Schottland von Sylvain Guizard erdacht. Ganz am Anfang dachten wir daran, die Musterbibliothek manuell in einer Grafiksoftware wie Adobe Illustrator oder Sketch zu erstellen. Aber das war, bevor wir entdeckten, dass es tausende von Tartan-Mustern gibt. Wir fühlten uns überwältigt und gaben auf... bis ich herausfand, dass Tartans eine spezifische Anatomie haben und durch einfache Zeichenfolgen von Fadenanzahlen und Farbcodes referenziert werden.

Tartan-Anatomie und SVG
Tartan besteht aus abwechselnden Bändern aus farbigen Fäden, die rechtwinklig und parallel zueinander gewebt sind. Die vertikalen und horizontalen Bänder folgen dem gleichen Muster aus Farben und Breiten. Die rechteckigen Bereiche, in denen sich die horizontalen und vertikalen Bänder kreuzen, erwecken durch das Mischen der ursprünglichen Farben den Anschein neuer Farben. Darüber hinaus werden Tartans mit einer speziellen Technik namens Köperbindung gewebt, die sichtbare diagonale Linien erzeugt. Ich habe versucht, die Technik mit SVG-Rechtecken als Fäden hier nachzubilden.
Analysieren wir die folgende SVG-Struktur.
<svg viewBox="0 0 280 280" width="280" height="280" x="0" y="0" xmlns="http://www.w3.org/2000/svg">
<defs>
<mask id="grating" x="0" y="0" width="1" height="1">
<rect x="0" y="0" width="100%" height="100%" fill="url(#diagonalStripes)"/>
</mask>
</defs>
<g id="horizontalStripes">
<rect fill="#FF8A00" height="40" width="100%" x="0" y="0"/>
<rect fill="#E52E71" height="10" width="100%" x="0" y="40"/>
<rect fill="#FFFFFF" height="10" width="100%" x="0" y="50"/>
<rect fill="#E52E71" height="70" width="100%" x="0" y="60"/>
<rect fill="#100E17" height="20" width="100%" x="0" y="130"/>
<rect fill="#E52E71" height="70" width="100%" x="0" y="150"/>
<rect fill="#FFFFFF" height="10" width="100%" x="0" y="220"/>
<rect fill="#E52E71" height="10" width="100%" x="0" y="230"/>
<rect fill="#FF8A00" height="40" width="100%" x="0" y="240"/>
</g>
<g id="verticalStripes" mask="url(#grating)">
<rect fill="#FF8A00" width="40" height="100%" x="0" y="0" />
<rect fill="#E52E71" width="10" height="100%" x="40" y="0" />
<rect fill="#FFFFFF" width="10" height="100%" x="50" y="0" />
<rect fill="#E52E71" width="70" height="100%" x="60" y="0" />
<rect fill="#100E17" width="20" height="100%" x="130" y="0" />
<rect fill="#E52E71" width="70" height="100%" x="150" y="0" />
<rect fill="#FFFFFF" width="10" height="100%" x="220" y="0" />
<rect fill="#E52E71" width="10" height="100%" x="230" y="0" />
<rect fill="#FF8A00" width="40" height="100%" x="240" y="0" />
</g>
</svg>
Die Gruppe horizontalStripes erstellt ein 280×280 Quadrat mit horizontalen Streifen. Die Gruppe verticalStripes erstellt dasselbe Quadrat, aber um 90 Grad gedreht. Beide Quadrate beginnen bei den Koordinaten (0,0). Das bedeutet, dass die horizontalStripes vollständig von den verticalStripes überdeckt werden; es sei denn, wir wenden eine Maske auf die obere an.
<defs>
<mask id="grating" x="0" y="0" width="1" height="1">
<rect x="0" y="0" width="100%" height="100%" fill="url(#diagonalStripes)"/>
</mask>
</defs>
Das SVG-Element mask definiert eine Alpha-Maske. Standardmäßig ist das Koordinatensystem für seine Attribute x, y, width und height das objectBoundingBox. Das Festlegen von width und height auf 1 (oder 100%) bedeutet, dass die Maske die verticalStripes abdeckt, was dazu führt, dass nur die weißen Teile innerhalb der Maske vollständig sichtbar sind.
Können wir unsere Maske mit einem Muster füllen? Ja, das können wir! Spiegeln wir die Tartan-Webtechnik mit einer Musterkachel, so:

In der Musterdefinition ändern wir die patternUnits von der Standardeinstellung objectBoundingBox zu userSpaceOnUse, so dass nun Breite und Höhe in Pixeln definiert sind.
<svg width="0" height="0">
<defs>
<pattern id="diagonalStripes" x="0" y="0" patternUnits="userSpaceOnUse" width="8" height="8">
<polygon points="0,4 0,8 8,0 4,0" fill="white"/>
<polygon points="4,8 8,8 8,4" fill="white"/>
</pattern>
</defs>
</svg>
Verwendung von React für Tartan-Weberei
Wir haben gerade gesehen, wie wir eine manuelle "Weberei" mit SVG erstellen können. Nun wollen wir diesen Prozess mit React automatisieren.
Die Komponente SvgDefs ist unkompliziert – sie gibt die defs-Markierung zurück.
const SvgDefs = () => {
return (
<defs>
<pattern
id="diagonalStripes"
x="0"
y="0"
width="8"
height="8"
patternUnits="userSpaceOnUse"
>
<polygon points="0,4 0,8 8,0 4,0" fill="#ffffff" />
<polygon points="4,8 8,8 8,4" fill="#ffffff" />
</pattern>
<mask id="grating" x="0" y="0" width="1" height="1">
<rect
x="0"
y="0"
width="100%"
height="100%"
fill="url(#diagonalStripes)"
/>
</mask>
</defs>
)
}
Wir werden einen Tartan als Array von Streifen darstellen. Jeder Streifen ist ein Objekt mit zwei Eigenschaften: fill (eine Hex-Farbe) und size (eine Zahl).
const tartan = [
{ fill: "#FF8A00", size: 40 },
{ fill: "#E52E71", size: 10 },
{ fill: "#FFFFFF", size: 10 },
{ fill: "#E52E71", size: 70 },
{ fill: "#100E17", size: 20 },
{ fill: "#E52E71", size: 70 },
{ fill: "#FFFFFF", size: 10 },
{ fill: "#E52E71", size: 10 },
{ fill: "#FF8A00", size: 40 },
]
Tartan-Daten sind oft als Paar von Zeichenfolgen verfügbar: Palette und Threadcount, die so aussehen könnten:
// Palette
O#FF8A00 P#E52E71 W#FFFFFF K#100E17
// Threadcount
O/40 P10 W10 P70 K/10.
Ich werde nicht darauf eingehen, wie man diese Zeichenfolgen-Darstellung in das Streifen-Array umwandelt, aber wenn Sie interessiert sind, finden Sie meine Methode in diesem Gist.
Die Komponente SvgTile nimmt das tartan-Array als Props und gibt eine SVG-Struktur zurück.
const SvgTile = ({ tartan }) => {
// We need to calculate the starting position of each stripe and the total size of the tile
const cumulativeSizes = tartan
.map(el => el.size)
.reduce(function(r, a) {
if (r.length > 0) a += r[r.length - 1]
r.push(a)
return r
}, [])
// The tile size
const size = cumulativeSizes[cumulativeSizes.length - 1]
return (
<svg
viewBox={`0 0 ${size} ${size}`}
width={size}
height={size}
x="0"
y="0"
xmlns="http://www.w3.org/2000/svg"
>
<SvgDefs />
<g id="horizontalStripes">
{tartan.map((el, index) => {
return (
<rect
fill={el.fill}
width="100%"
height={el.size}
x="0"
y={cumulativeSizes[index - 1] || 0}
/>
)
})}
</g>
<g id="verticalStripes" mask="url(#grating)">
{tartan.map((el, index) => {
return (
<rect
fill={el.fill}
width={el.size}
height="100%"
x={cumulativeSizes[index - 1] || 0}
y="0"
/>
)
})}
</g>
</svg>
)
}
Verwendung einer Tartan-SVG-Kachel als Hintergrundbild
Auf tartanify.com wird jeder einzelne Tartan als Hintergrundbild für ein Vollbild-Element verwendet. Dies erfordert einige zusätzliche Manipulationen, da wir unsere Tartan-Musterkachel nicht als SVG-Bild haben. Wir können auch kein Inline-SVG direkt in der Eigenschaft background-image verwenden.
Glücklicherweise funktioniert das Kodieren des SVG als Hintergrundbild.
.bg-element {
background-image: url('data:image/svg+xml;charset=utf-8,<svg>...</svg>');
}
Erstellen wir nun eine SvgBg-Komponente. Sie nimmt das tartan-Array als Props und gibt ein Vollbild-Div mit dem Tartan-Muster als Hintergrund zurück.
Wir müssen das SvgTile-React-Objekt in eine Zeichenfolge konvertieren. Das Objekt ReactDOMServer ermöglicht es uns, Komponenten in statische Markups zu rendern. Seine Methode renderToStaticMarkup ist sowohl im Browser als auch auf dem Node-Server verfügbar. Letzteres ist wichtig, da wir später die Tartan-Seiten mit Gatsby serverseitig rendern werden.
const tartanStr = ReactDOMServer.renderToStaticMarkup(<SvgTile tartan={tartan} />)
Unsere SVG-Zeichenfolge enthält Hex-Farbcode, die mit dem #-Symbol beginnen. Gleichzeitig beginnt # einen Fragmentbezeichner in einer URL. Das bedeutet, unser Code wird fehlschlagen, es sei denn, wir escapen alle diese Instanzen. Hier kommt die integrierte JavaScript-Funktion encodeURIComponent ins Spiel.
const SvgBg = ({ tartan }) => {
const tartanStr = ReactDOMServer.renderToStaticMarkup(<SvgTile tartan={tartan} />)
const tartanData = encodeURIComponent(tartanStr)
return (
<div
style={{
width: "100%",
height: "100vh",
backgroundImage: `url("data:image/svg+xml;utf8,${tartanData}")`,
}}
/>
)
}
SVG-Tartan-Kachel zum Download machen
Laden wir nun unser SVG-Bild herunter.
Die Komponente SvgDownloadLink nimmt svgData (die bereits kodierte SVG-Zeichenfolge) und fileName als Props entgegen und erstellt ein Anker-Element (<a>). Das Attribut download fordert den Benutzer auf, die verlinkte URL zu speichern, anstatt zu ihr zu navigieren. Wenn es mit einem Wert verwendet wird, schlägt es den Namen der Zieldatei vor.
const SvgDownloadLink = ({ svgData, fileName = "file" }) => {
return (
<a
download={`${fileName}.svg`}
href={`data:image/svg+xml;utf8,${svgData}`}
>
Download as SVG
</a>
)
}
Konvertierung einer SVG-Tartan-Kachel in eine hochauflösende PNG-Bilddatei
Was ist mit Benutzern, die das PNG-Bildformat gegenüber SVG bevorzugen? Können wir ihnen hochauflösende PNGs anbieten?
Die Komponente PngDownloadLink erstellt, genau wie SvgDownloadLink, ein Anker-Tag und nimmt tartanData und fileName als Props entgegen. In diesem Fall müssen wir jedoch auch die Tartan-Kachelgröße angeben, da wir die Canvas-Dimensionen festlegen müssen.
const Tile = SvgTile({tartan})
// Tartan tiles are always square
const tartanSize = Tile.props.width
Im Browser zeichnen wir, sobald die Komponente fertig ist, die SVG-Kachel auf ein <canvas> Element. Wir verwenden die Methode toDataUrl() des Canvas, die das Bild als Daten-URI zurückgibt. Schließlich setzen wir den Daten-URI als href-Attribut unseres Anker-Tags.
Beachten Sie, dass wir doppelte Dimensionen für das Canvas verwenden und den ctx skalieren. So erzeugen wir ein PNG, das doppelt so groß ist, was für hochauflösende Anwendungen großartig ist.
const PngDownloadLink = ({ svgData, width, height, fileName = "file" }) => {
const aEl = React.createRef()
React.useEffect(() => {
const canvas = document.createElement("canvas")
canvas.width = 2 * width
canvas.height = 2 * height
const ctx = canvas.getContext("2d")
ctx.scale(2, 2)
let img = new Image()
img.src = `data:image/svg+xml, ${svgData}`
img.onload = () => {
ctx.drawImage(img, 0, 0)
const href = canvas.toDataURL("image/png")
aEl.current.setAttribute("href", href)
}
}, [])
return (
<a
ref={aEl}
download={`${fileName}.png`}
>
Download as PNG
</a>
)
}
Für diese Demo hätte ich den useEffect-Hook von React überspringen können und der Code wäre fehlerfrei gelaufen. Nichtsdestotrotz wird unser Code dank Gatsby sowohl im Server als auch im Browser ausgeführt. Bevor wir mit der Erstellung des Canvas beginnen, müssen wir sicherstellen, dass wir uns in einem Browser befinden. Wir sollten auch sicherstellen, dass das Anker-Element "bereit" ist, bevor wir sein Attribut ändern.
Erstellen einer statischen Website aus CSV mit Gatsby
Wenn Sie noch nichts von Gatsby gehört haben, ist es ein kostenloses Open-Source-Framework, mit dem Sie Daten von fast überall abrufen und statische Websites erstellen können, die von React angetrieben werden.
Tartanify.com ist eine Gatsby-Website, die von mir programmiert und von Sylvain entworfen wurde. Zu Beginn des Projekts hatten wir nur eine riesige CSV-Datei (ernsthaft, 5.495 Zeilen), eine Methode zur Umwandlung der Palette und Threadcount-Zeichenfolgen in die Tartan-SVG-Struktur und das Ziel, Gatsby auszuprobieren.

Um eine CSV-Datei als Datenquelle zu verwenden, benötigen wir zwei Gatsby-Plugins: gatsby-transformer-csv und gatsby-source-filesystem. Im Hintergrund liest das Quell-Plugin die Dateien im Ordner /src/data (wo wir die Datei tartans.csv abgelegt haben), dann parst das Transformer-Plugin die CSV-Datei in JSON-Arrays.
// gatsby-config.js
module.exports = {
/* ... */
plugins: [
'gatsby-transformer-csv',
{
resolve: 'gatsby-source-filesystem',
options: {
path: `${__dirname}/src/data`,
name: 'data',
},
},
],
}
Nun sehen wir uns an, was in der Datei gatsby-node.js passiert. Die Datei wird während des Website-Erstellungsprozesses ausgeführt. Dort können wir zwei Gatsby Node APIs verwenden: createPages und onCreateNode. onCreateNode wird aufgerufen, wenn ein neuer Knoten erstellt wird. Wir werden einem Tartan-Knoten zwei zusätzliche Felder hinzufügen: seinen eindeutigen Slug und einen eindeutigen Namen. Dies ist notwendig, da die CSV-Datei eine Reihe von Tartan-Varianten enthält, die unter demselben Namen gespeichert sind.
// gatsby-node.js
// We add slugs here and use this array to check if a slug is already in use
let slugs = []
// Then, if needed, we append a number
let i = 1
exports.onCreateNode = ({ node, actions }) => {
if (node.internal.type === 'TartansCsv') {
// This transforms any string into slug
let slug = slugify(node.Name)
let uniqueName = node.Name
// If the slug is already in use, we will attach a number to it and the uniqueName
if (slugs.indexOf(slug) !== -1) {
slug += `-${i}`
uniqueName += ` ${i}`
i++
} else {
i = 1
}
slugs.push(slug)
// Adding fields to the node happen here
actions.createNodeField({
name: 'slug',
node,
value: slug,
})
actions.createNodeField({
name: 'Unique_Name',
node,
value: uniqueName,
})
}
}
Als Nächstes erstellen wir Seiten für jeden *einzelnen* Tartan. Wir möchten Zugriff auf seine Geschwister haben, damit wir leicht navigieren können. Wir werden die vorherigen und nächsten Kanten abfragen und das Ergebnis dem Kontext der Tartan-Seite hinzufügen.
// gatsby-node.js
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const allTartans = await graphql(`
query {
allTartansCsv {
edges {
node {
id
fields {
slug
}
}
previous {
fields {
slug
Unique_Name
}
}
next {
fields {
slug
Unique_Name
}
}
}
}
}
`)
if (allTartans.errors) {
throw allTartans.errors
}
allTartans.data.allTartansCsv.edges.forEach(
({ node, next, previous }) => {
createPage({
path: `/tartan/${node.fields.slug}`,
component: path.resolve(`./src/templates/tartan.js`),
context: {
id: node.id,
previous,
next,
},
})
}
)
}
Wir haben uns entschieden, Tartans nach Buchstaben zu indizieren und paginierte Buchstaben-Seiten zu erstellen. Diese Seiten listen Tartans mit Links zu ihren einzelnen Seiten auf. Wir zeigen maximal 60 Tartans pro Seite an, und die Anzahl der Seiten pro Buchstabe variiert. Zum Beispiel hat der Buchstabe "a" vier Seiten: tartans/a, tartans/a/2, tartans/a/3 und tartans/a/4. Die höchste Seitenzahl (15) gehört zu "m" aufgrund einer hohen Anzahl traditioneller Namen, die mit "Mac" beginnen.
Die Seite tartans/a/4 sollte auf tartans/b als nächste Seite verweisen, und tartans/b sollte auf tartans/a/4 als vorherige Seite verweisen.
Wir durchlaufen mit einer for of-Schleife das Buchstaben-Array ["a", "b", ... , "z"] und fragen alle Tartans ab, die mit einem bestimmten Buchstaben beginnen. Dies kann mit der Filter- und Regex-Operator erfolgen.
allTartansCsv(filter: { Name: { regex: "/^${letter}/i" } })
Die Variable previousLetterLastIndex wird am Ende jeder Schleife aktualisiert und speichert die Anzahl der Seiten pro Buchstabe. Die Seite /tartans/b muss die Anzahl der 'a'-Seiten (4) kennen, da ihr vorheriger Link tartans/a/4 sein sollte.
// gatsby-node.js
const letters = "abcdefghijklmnopqrstuvwxyz".split("")
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
// etc.
let previousLetterLastIndex = 1
for (const letter of letters) {
const allTartansByLetter = await graphql(`
query {
allTartansCsv(filter: {Name: {regex: "/^${letter}/i"}}) {
nodes {
Palette
fields {
slug
Unique_Name
}
}
totalCount
}
}
`)
if (allTartansByLetter.errors) {
throw allTartansByLetter.errors
}
const nodes = allTartansByLetter.data.allTartansCsv.nodes
const totalCountByLetter = allTartansByLetter.data.allTartansCsv.totalCount
const paginatedNodes = paginateNodes(nodes, pageLength)
paginatedNodes.forEach((group, index, groups) => {
createPage({
path:
index > 0 ? `/tartans/${letter}/${index + 1}` : `/tartans/${letter}`,
component: path.resolve(`./src/templates/tartans.js`),
context: {
group,
index,
last: index === groups.length - 1,
pageCount: groups.length,
letter,
previousLetterLastIndex,
},
})
})
previousLetterLastIndex = Math.ceil(totalCountByLetter / pageLength)
}
}
Die Funktion paginateNode gibt ein Array zurück, in dem die anfänglichen Elemente nach pageLength gruppiert sind.
const paginateNodes = (array, pageLength) => {
const result = Array()
for (let i = 0; i < Math.ceil(array.length / pageLength); i++) {
result.push(array.slice(i * pageLength, (i + 1) * pageLength))
}
return result
}
Schauen wir uns nun die Tartan-Vorlage an. Da Gatsby eine React-Anwendung ist, können wir die Komponenten verwenden, die wir im ersten Teil dieses Artikels erstellt haben.
// ./src/templates/tartan.js
import React from "react"
import { graphql } from "gatsby"
import Layout from "../components/layout"
import SvgTile from "../components/svgtile"
import SvgBg from "../components/svgbg"
import svgAsString from "../components/svgasstring"
import SvgDownloadLink from "../components/svgdownloadlink"
import PngDownloadLink from "../components/pngdownloadlink"
export const query = graphql`
query($id: String!) {
tartansCsv(id: { eq: $id }) {
Palette
Threadcount
Origin_URL
fields {
slug
Unique_Name
}
}
}
`
const TartanTemplate = props => {
const { fields, Palette, Threadcount } = props.data.tartansCsv
const {slug} = fields
const svg = SvgTile({
palette: Palette,
threadcount: Threadcount,
})
const svgData = svgAsString(svg)
const svgSize = svg.props.width
return (
<Layout>
<SvgBg svg={svg} />
{/* title and navigation component comes here */}
<div className="downloads">
<SvgDownloadLink svgData={svgData} fileName={slug} />
<PngDownloadLink svgData={svgData} size={svgSize} fileName={slug} />
</div>
</Layout>
)
}
export default TartanTemplate
Konzentrieren wir uns schließlich auf die Tartan-Indexseiten (die Buchstaben-Seiten).
// ./src/templates/tartans.js
import React from "react"
import Layout from "../components/layout"
import {Link} from "gatsby"
import TartansNavigation from "../components/tartansnavigation"
const TartansTemplate = ({ pageContext }) => {
const {
group,
index,
last,
pageCount,
letter,
previousLetterLastIndex,
} = pageContext
return (
<Layout>
<header>
<h1>{letter}</h1>
</header>
<ul>
{group.map(node => {
return (
<li key={node.fields.slug}>
<Link to={`/tartan/${node.fields.slug}`}>
<span>{node.fields.Unique_Name}</span>
</Link>
</li>
)
})}
</ul>
<TartansNavigation
letter={letter}
index={index}
last={last}
previousLetterLastIndex={previousLetterLastIndex}
/>
</Layout>
)
}
export default TartansTemplate
Die Komponente TartansNavigation fügt die Vorwärts- und Rückwärtsnavigation zwischen den Indexseiten hinzu.
// ./src/components/tartansnavigation.js
import React from "react"
import {Link} from "gatsby"
const letters = "abcdefghijklmnopqrstuvwxyz".split("")
const TartansNavigation = ({
className,
letter,
index,
last,
previousLetterLastIndex,
}) => {
const first = index === 0
const letterIndex = letters.indexOf(letter)
const previousLetter = letterIndex > 0 ? letters[letterIndex - 1] : ""
const nextLetter =
letterIndex < letters.length - 1 ? letters[letterIndex + 1] : ""
let previousUrl = null, nextUrl = null
// Check if previousUrl exists and create it
if (index === 0 && previousLetter) {
// First page of each new letter except "a"
// If the previous letter had more than one page we need to attach the number
const linkFragment =
previousLetterLastIndex === 1 ? "" : `/${previousLetterLastIndex}`
previousUrl = `/tartans/${previousLetter}${linkFragment}`
} else if (index === 1) {
// The second page for a letter
previousUrl = `/tartans/${letter}`
} else if (index > 1) {
// Third and beyond
previousUrl = `/tartans/${letter}/${index}`
}
// Check if `nextUrl` exists and create it
if (last && nextLetter) {
// Last page of any letter except "z"
nextUrl = `/tartans/${nextLetter}`
} else if (!last) {
nextUrl = `/tartans/${letter}/${(index + 2).toString()}`
}
return (
<nav>
{previousUrl && (
<Link to={previousUrl} aria-label="Go to Previous Page" />
)}
{nextUrl && (
<Link to={nextUrl} aria-label="Go to Next Page" />
)}
</nav>
)
}
export default TartansNavigation
Abschließende Gedanken
Hier stoppen wir. Ich habe versucht, alle wichtigen Aspekte dieses Projekts abzudecken. Sie finden den gesamten Code von tartanify.com auf GitHub. Die Struktur dieses Artikels spiegelt meine persönliche Reise wider – das Verständnis der Besonderheiten von Tartans, ihre Übersetzung in SVG, die Automatisierung des Prozesses, die Generierung von Bildversionen und die Entdeckung von Gatsby, um eine benutzerfreundliche Website zu erstellen. Es war vielleicht nicht so lustig wie unsere schottische Reise selbst 😉, aber ich habe es wirklich genossen. Wieder einmal erwies sich ein Nebenprojekt als der beste Weg, um sich in neue Technologien einzuarbeiten.
Sehr cool. Tolle Arbeit!
Danke!
Liebe es!
Vielen Dank!
Ich liebe diese Idee! Was für eine schöne Seite!
Danke, ich werde die Nachricht an den Designer weiterleiten :)
Ich habe etwas sehr Ähnliches gemacht, indem ich SVGs (automatisch als React-Komponenten von SVGR geladen) in eine Daten-URL konvertiert habe, hauptsächlich um Symbole als Hintergrundbilder für benutzerdefinierte Formularelemente in einem aktuellen Projekt zu verwenden. Es war gut, meine Vorgehensweise durch die Bestätigung zu sehen, dass jemand anderes zu der gleichen Lösung gekommen ist :)
Großartig! Danke! :)