Home > Software engineering >  Scale SVG to height of an adjacent element
Scale SVG to height of an adjacent element

Time:12-23

I'm trying to scale an SVG with a fixed aspect ratio to match the height of an adjacent element (a div containing a range of different elements).

For demonstration purposes, here I have simply set the height of the SVG (#triangle) to 10rem, which just happens to match the height of the div (#text) on the right. But if you resize the window, everything changes. (see code snippet at the end of this question)

Screenshot showing a triangle SVG whose height matches the text section adjacent to it

What I would like to achieve is to be able to resize the window and as the text wraps (changing its height), the SVG's height adjusts to match:

Screenshot showing the same triangle SVG, and adjacent text section. The text has wrapped, making it taller. The height of the SVG is adjusted to match the text section

I assumed that there would be a CSS grid or Flexbox solution to this, but I haven't found one so far.

(I'd also be very happy with a CSS solution instead of the SVG, eg. that uses clip-path or some other styled div, but the key issue is that it should have a fixed aspect ratio)

Creating a Javascript solution would be ok, but it is a little trickier than it first appears.

The difficulty is that the available space that determines whether #text wraps (and thus determines the height of #text) is dependent on the width of #triangle. #triangle has a fixed aspect ratio so its width is dependent on its height, which would be dependent on the height of #text...

A practical note: Obviously this kind of layout doesn't provide a huge amount of flexibility, and could only work with a particular type of content — a narrow SVG, and reasonably short text elements. On desktop screens there would be plenty of empty space on the right hand side, with no wrapping, whereas on mobile it would take up the full width, with some wrapped text.

#container {
  border: 1px goldenrod solid;
  padding: 1em;
  display: flex;
  flex-direction: row;
  justify-content: flex-start;
  gap: 1em;
}

#triangle {
  border: 1px steelblue solid;
  height: 10rem;
}

#text {
  border: 1px steelblue solid;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
}
<div id="container">

  <svg id="triangle" 
       viewBox="0 0 6 8">
    <polygon points="0,8 3,0 6,8"
             fill="tomato"/>
  </svg>


  <div id="text">
    <h1>
      Triangle
    </h1>
    <p>
      A polygon with three edges and three vertices.
    </p>
    <a href="https://en.wikipedia.org/wiki/Triangle">
    Triangle on Wikipedia
    </a>      
  </div>


<div>

</body>

CodePudding user response:

This is not the perfect solution, but maybe somebody else can extend it.

The main trick is that the SVG (svg) has the height of 0, but will grow (flex-grow: 1;) according to the parent element (.triangle).

The problem in this solution is that the share between .triangle and .text is fixed (grid-template-columns: 1fr 4fr;). This share can be adjusted, but if the use cases differ a lot in the text height it will be a problem.

#container {
  border: 1px goldenrod solid;
  padding: 1em;
  display: grid;
  grid-template-columns: 1fr 4fr;
  grid-column-gap: 1em;
}

.triangle {
  display: flex;
  align-items: stretch;
  flex-direction: column;
}

.triangle svg {
  border: 1px steelblue solid;
  flex-grow: 1;
  height: 0;
}

.text {
  border: 1px steelblue solid;
}
<div id="container">
  <div >
    <svg viewBox="0 0 6 8">
      <polygon points="0,8 3,0 6,8" fill="tomato"/>
    </svg>
  </div>
  <div >
    <h1>Triangle</h1>
    <p>A polygon with three edges and three vertices.</p>
    <a href="https://en.wikipedia.org/wiki/Triangle">
    Triangle on Wikipedia
    </a>
  </div>
</div>

  • Related