1

<svg style="z-index: 0;" width="800" height="400" viewBox="1400 -1300 700 200">
  <g
    tabindex="0"
    role="button"
  >
    <defs>
      <marker
        id="red-arrowhead"
        viewBox="0 0 15 15"
        refX="3"
        refY="5"
        markerUnits="strokeWidth"
        markerWidth="6"
        markerHeight="6"
        orient="auto"
      >
        <path
          d="M 0 0.5 L 8 5 L 0 10 Z"
          stroke="grey"
          stroke-width="1"
          fill="grey"
          stroke-linecap="round"
          stroke-linejoin="round"
        ></path>
      </marker>
    </defs>
    <path
      d="M2002.7598312618293,-1234.5329730096134 C1731.0960939101396,-1234.5329730096134 1731.0960939101396,-1188.6076247061148 1459.4323565584502,-1188.6076247061148"
      stroke-width="20"
      stroke="transparent"
      fill="none"
      style="pointer-events: all;"
    ></path>
    <path
      id="text"
      d="M2002.7598312618293,-1234.5329730096134 C1731.0960939101396,-1234.5329730096134 1731.0960939101396,-1188.6076247061148 1459.4323565584502,-1188.6076247061148"
      stroke="grey"
      fill="transparent"
      marker-end="url(#red-arrowhead)"
      style="stroke-width: 6px; pointer-events: none;"
    ></path>
    <text dy="-10px" fill="black" font-size="14">
      <textPath
        startOffset="50%"
        text-anchor="center"
        href="#text"
      >
        text
      </textPath>
    </text>
  </g>
</svg>

I want text to be readable. How can I do it? I only need css solution. I tried rotateY, scale, matrix but unlucky

1
  • Please update your question to show your attempt with those transforms. Commented Sep 8 at 14:21

4 Answers 4

3

Change path drawing direction

Instead of a transform you should consider reversing the path direction.

This way you can still update attributes/properties such as start-offset without recalculating a transform origin – which becomes way more complicated with asymmetric text path geometries.

svg {
  display: inline-block;
  width: 20%;
  height: auto;
  outline: 1px solid #ccc;
  overflow: visible;
  padding: 5em;
  margin-right: 1em;
}
<h3>Symmetric text path</h3>
<svg viewBox="0 0 100 100">
    <path id="textPath" d="M 0 50 Q 25 0 50 50 T 100 50" fill="none" stroke="#ccc" />
    <circle cx="50" cy="50" r="2" fill="red"/>

    <text>
      <textPath href="#textPath" >
        text
      </textPath>
    </text>
</svg>


<svg viewBox="0 0 100 100">
    <path id="textPath2" d="M 0 50 Q 25 0 50 50 T 100 50" fill="none" stroke="#ccc" />
    <circle cx="50" cy="50" r="2" fill="red"/>

    <text style="transform: scale(-1, -1); transform-origin: 50px 50px" >
      <textPath href="#textPath2" >
        text
      </textPath>
    </text>
</svg>


<h3>Asymmetric text path</h3>
<svg viewBox="0 0 100 100">
    <path id="textPath3" d="M 0 50 Q 75 0 50 50 T 100 25" fill="none" stroke="#ccc" />
    <circle cx="50" cy="50" r="2" fill="red"/>
    <text>
      <textPath href="#textPath3" >
        text
      </textPath>
    </text>
</svg>


<svg viewBox="0 0 100 100">
    <path id="textPath5" d="M 0 50 Q 75 0 50 50 T 100 25" fill="none" stroke="#ccc" stroke-dasharray="1 1" style="transform: scale(-1, -1); transform-origin: 50px 50px"  />
  
    <path id="textPath4" d="M 0 50 Q 75 0 50 50 T 100 25" fill="none" stroke="#ccc"  />
  
  <circle cx="50" cy="50" r="2" fill="red"/>

    <text style="transform: scale(-1, -1); transform-origin: 50px 50px" >
      <textPath href="#textPath4" >
        text
      </textPath>
    </text>
</svg>

You can use a tool like svg-commander to reverse the path data.

<svg style="z-index: 0;" width="800" height="400" viewBox="1400 -1300 700 200">
  <g tabindex="0" role="button">
    <defs>
      <marker
        id="red-arrowhead"
        viewBox="0 0 15 15"
        refX="3"
        refY="5"
        markerUnits="strokeWidth"
        markerWidth="6"
        markerHeight="6"
        orient="180deg"
      >
        <path
          d="M 0 0.5 L 8 5 L 0 10 Z"
          stroke="grey"
          stroke-width="1"
          fill="grey"
          stroke-linecap="round"
          stroke-linejoin="round"
        ></path>
      </marker>
    </defs>
    
      <path id="textPath"
      d="M1459.43-1188.61c271.67 0 271.67-45.92 543.33-45.92" 
      stroke-width="20"
      stroke="grey"
      fill="transparent"
      marker-start="url(#red-arrowhead)"
      style="stroke-width: 6px; pointer-events: none;" />
    

    <text dy="-10px" fill="black" font-size="14">
      <textPath
        startOffset="50%"
        text-anchor="center"
        href="#textPath"
      >
        text
      </textPath>
    </text>
  </g>
</svg>

Benefits from refactoring path

When we're already going to refactor the <textPath> geometry we can also apply some other optimizations such as:

  • converting commands to all relative
  • reducing the floating point accuracy

By doing so, we can significantly reduce the size of the final SVG markup but most importantly make it slightly more "readable" (admittedly very relative=).

While viewBox offsets or transforms can be helpful – if viewbox offset and transforms are combined, things get quite confusing (where is visual my starting point?)

Foremost, we can simplify multiple "origin-point" references by panning the shape to it's actual starting point coordinates:

Once all commands are relative we can simply move the first M command x/y coordinate pair to the actual positions by subtracting adding the viewBox x/y offsets.

New ViewBox

viewBox="0 0 700 200"

New Textpath

M59.43 112.61 c271.67 0 271.67-45.92 543.33-45.92

svg {
  display: block;
  outline: 1px solid #ccc;
  overflow: visible;
}
    <svg style="z-index: 0;" width="800" height="400" viewBox="0 0 700 200">
      <g tabindex="0" role="button">
        <defs>
          <marker
            id="red-arrowhead"
            viewBox="0 0 15 15"
            refX="3"
            refY="5"
            markerUnits="strokeWidth"
            markerWidth="6"
            markerHeight="6"
            orient="180deg"
          >
            <path
              d="M 0 0.5 L 8 5 L 0 10 Z"
              stroke="grey"
              stroke-width="1"
              fill="grey"
              stroke-linecap="round"
              stroke-linejoin="round"
            ></path>
          </marker>
        </defs>
        
          <path id="textPath"
          d="M59.43 112.61 c271.67 0 271.67-45.92 543.33-45.92" 
          stroke-width="20"
          stroke="grey"
          fill="transparent"
          marker-start="url(#red-arrowhead)"
          style="stroke-width: 6px; pointer-events: none;" />

        <text dy="-10px" fill="black" font-size="14">
          <textPath
            startOffset="50%"
            text-anchor="center"
            href="#textPath"
          >
            text
          </textPath>
        </text>
      </g>
    </svg>

Implications for SVG <marker> elements

Since we're changing the intrinsic drawing direction we also need to adjust the marker orientation. But we can easily do so by tweaking the orient attribute like so:

<marker
        id="red-arrowhead"
        viewBox="0 0 15 15"
        refX="3"
        refY="5"
        markerUnits="strokeWidth"
        markerWidth="6"
        markerHeight="6"
        orient="180deg"
      >
        <path
          d="M 0 0.5 L 8 5 L 0 10 Z"
          stroke="grey"
          stroke-width="1"
          fill="grey"
          stroke-linecap="round"
          stroke-linejoin="round"
        ></path>
      </marker>

<textPath> related side attribute

Theoretically, you could also apply the side with values right or left attribute to change alignment of text to the inside or outside – this would be the most straight-forward solution.

Unfortunately, it is currently (2025) only supported by Firefox – so we can't really use it for a robust cross-browser solution. Hopefully, we see side implemented by other vendors in the next years.

See also: "How can I get SVG text to align with the outer arc of a circle segment?"

Sign up to request clarification or add additional context in comments.

Comments

1

This version should work.

<svg style="z-index: 0;" width="800" height="400" viewBox="1400 -1300 700 200">
  <g
    tabindex="0"
    role="button"
  >
    <defs>
      <marker
        id="red-arrowhead"
        viewBox="0 0 15 15"
        refX="3"
        refY="5"
        markerUnits="strokeWidth"
        markerWidth="6"
        markerHeight="6"
        orient="auto"
      >
        <path
          d="M 0 0.5 L 8 5 L 0 10 Z"
          stroke="grey"
          stroke-width="1"
          fill="grey"
          stroke-linecap="round"
          stroke-linejoin="round"
        ></path>
      </marker>
    </defs>
    <path
      d="M2002.7598312618293,-1234.5329730096134 C1731.0960939101396,-1234.5329730096134 1731.0960939101396,-1188.6076247061148 1459.4323565584502,-1188.6076247061148"
      stroke-width="20"
      stroke="transparent"
      fill="none"
      style="pointer-events: all;"
    ></path>
    <path
      id="text"
      d="M2002.7598312618293,-1234.5329730096134 C1731.0960939101396,-1234.5329730096134 1731.0960939101396,-1188.6076247061148 1459.4323565584502,-1188.6076247061148"
      stroke="grey"
      fill="transparent"
      marker-end="url(#red-arrowhead)"
      style="stroke-width: 6px; pointer-events: none;"
    ></path>
      
     <text font-size="24" fill="black"  transform="rotate(180, 1731, -1211)">
      <textPath href="#text" startOffset="50%" text-anchor="middle">
        <tspan dy="-10" >Text</tspan>
      </textPath>
    </text>
    
  </g>
</svg>

1 Comment

Good answers include some explanation, not just code
0

add the transform "...transform="rotate(180 1731 -1211)"" in your exsisting text dy next to font size. That should solve the issue.


Comments

0
<svg style="z-index: 0;" width="800" height="400" viewBox="1400 -1300 700 200">
  <g
    tabindex="0"
    role="button"
  >
    <defs>
      <marker
        id="red-arrowhead"
        viewBox="0 0 15 15"
        refX="3"
        refY="5"
        markerUnits="strokeWidth"
        markerWidth="6"
        markerHeight="6"
        orient="auto"
      >
        <path
          d="M 0 0.5 L 8 5 L 0 10 Z"
          stroke="grey"
          stroke-width="1"
          fill="grey"
          stroke-linecap="round"
          stroke-linejoin="round"
        ></path>
      </marker>
    </defs>
    <path
      d="M2002.7598312618293,-1234.5329730096134 C1731.0960939101396,-1234.5329730096134 1731.0960939101396,-1188.6076247061148 1459.4323565584502,-1188.6076247061148"
      stroke-width="20"
      stroke="transparent"
      fill="none"
      style="pointer-events: all;"
    ></path>
    <path
      id="text"
      d="M2002.7598312618293,-1234.5329730096134 C1731.0960939101396,-1234.5329730096134 1731.0960939101396,-1188.6076247061148 1459.4323565584502,-1188.6076247061148"
      stroke="grey"
      fill="transparent"
      marker-end="url(#red-arrowhead)"
      style="stroke-width: 6px; pointer-events: none;"
    ></path>
    <text dy="-10px" fill="black" font-size="14" style="transform: scale(-1, -1); transform-origin: 1700px -1190px;">
      <textPath
        startOffset="50%"
        text-anchor="center"
        href="#text"
      >
        text
      </textPath>
    </text>
  </g>
</svg>

This worked for me. I just scaled the text so that it's "mirrored", by adding style parameters inside the text tag.

<text dy="-10px" fill="black" font-size="14" style="transform: scale(-1, -1); transform-origin: 1700px -1190px;">

However, i had to fine-tune the transform origin for the text to appear on screen.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.