How to scale SVGs that use real world units - css

I need to generate SVGs using real world units (e.g. inches) and have them scale proportionately to fit inside a div with fixed dimensions. Is this possible?
As a simple example, this SVG is overflowing the div's boundaries:
<div style="width: 600px; height: 400px;">
<svg width="60in" height="15in" viewBox="0 0 60 15" style="border: 1px solid black;">
<line x1="30" y1="0" x2="30" y2="15" style="stroke:rgb(0,0,0);stroke-width:2px" vector-effect="non-scaling-stroke" />
</svg>
</div>

Remove the width and height attributes from your SVG (they're not necessary). Then your SVG will scale automatically to your DIV's size. (Keep the viewBox since that will remind you that your dimensions are probably in inches)

Related

How exactly does the fontSize of SVG foreignObject work

New to SVG, I was trying to use foreignObject to put a custom HTML component inside SVG, and I'm very confused about the fontSize.
It seems like the usual CSS like font-size: 10px doesn't work the same way here, when I add that style, the font I got is not exactly 10px. Besides, I noticed that the font size changes as the screen width change.
My question is, which factors are affecting the font size here? and how do I get a static style like 14px?
Here's my current code:
<div style="width: 100%">
<svg class="ProgressBar" viewBox="0 0 120 10">
<foreignObject height="30" width="50" x="0" y="0">
<div style="font-size: 10px; color: rgb(135, 144, 162);">
<div>Text</div>
</div>
</foreignObject>
</svg>
</div>
Like any other SVG element, <foreignObject> elements are subject to any scaling that the SVG undergoes.
Your SVG has a viewBox, so the contents will be scaled. That includes the foreignObject and the text within it.
If you don't want scaling, then remove the viewBox. Or set the SVG width and height so that the SVG doesn't scale (ie. has 1:1 scale).
For example, the text in the following snippet has a size of 10px. This is because the width has been set to the same value as the viewBox width value (120).
<div style="width: 120px">
<svg class="ProgressBar" viewBox="0 0 120 10">
<foreignObject height="30" width="50" x="0" y="0">
<div style="font-size: 10px; color: rgb(135, 144, 162);">
<div>Text</div>
</div>
</foreignObject>
</svg>
</div>

SVG viewBox="0 0 100 100" wont scale my path to 100%?

I currently have an svg path that only shows about 50%. How can I get it to show 100%?
Here is my code:
<div className="svgPathContainer">
<svg width="100%" height="100%" viewBox="0 0 100 100" preserveAspectRatio="none">
<path d="M82 88L0 0H123V170H0L82 88Z" fill="#F9B300" />
</svg>
</div>
I have className="svgPath" set as:
.svgPathContainer{
width: 100%;
height: 100%;
}
Here is also an example of the svg only showing at 50%:
https://codesandbox.io/s/svg-scaling-fff1q?file=/src/App.js:93-357
The problem: Why wont the svg path scale to 100% of the .svgPathContainer?
The viewBox is like a canvas size. So drawings outside of this view box will be clipped. Like css overflow: hidden. And your drawing has a size of width 123px and height 170px. So you have to change the view box to this value. Check our some docs.
If you want to keep the viewbox of 100 x 100 px, you need to change your drawing element size (path).
The view box has nothing to do with the scale. It's just the canvas size. A rect clip with width, height and offset (x y w h) for the SVG elements inside. And the SVG tag's width and height attributes are for scale the rendered image.
<div className="svgPathContainer">
<svg width="100%" height="100%" viewbox="0 0 123 170" preserveAspectRatio="none">
<path d="M82 88L0 0H123V170H0L82 88Z" fill="#F9B300" />
</svg>
</div>

SVG with height 100% will never shrink below 150px

I'm trying to create a SVG which stretches (in height) to a paragraph with variable height next to it.
Currently I have the following working example code which works as long if the paragraph is bigger then 150px. As soon as the paragraph is less in height, the SVG stops shrinking.
<div style="display: flex">
<div>
<svg style="display:block;width: 40px;height:100%">
<line x1="20" y1="0" x2="20" y2="100%" stroke-width="1" stroke="black"></line>
</svg>
</div>
<div style="height: 300px;border:1px solid">
This paragraph is 300px, the svg will stretch accordingly
</div>
</div>
<br><br><br><br>
<div style="display: flex">
<div>
<svg style="width: 40px;height:100%">
<line x1="20" y1="0" x2="20" y2="100%" stroke-width="1" stroke="black"></line>
</svg>
</div>
<div style="height: 50px;border:1px solid">
This paragraph is 50px, the svg will not shrink below 150px
</div>
</div>
This can't be solved with javascript, it should be responsive. the SVG is in practice a lot more complicated and cannot be replaced with a simple border-left: 1px solid black
SVGs like other replaced elements have a default size of 300x150. If they don't have enough information to calculate their width or height, they will report their default width (300) or height (150)
For your first example div, when the browser is trying to calculate its height, it will ask the first child what its height is. It will report a default of 150px (from the SVG). The second child will report 300px. So the browser will set that first div to 300px. This is the greater of its two children.
In the second div, the same process will be followed. This time the max height will be the default height of the SVG (150px) since that is larger than the other child (50px). So that section will end up at a height of 150px.
To fix this, you don't have much choice other than giving the SVG a specific height, rather than using a percentage.
<svg style="width: 40px; height:50px">
Demo:
<div style="display: flex">
<div>
<svg style="display:block;width: 40px;height:100%">
<line x1="20" y1="0" x2="20" y2="100%" stroke-width="1" stroke="black"></line>
</svg>
</div>
<div style="height: 300px;border:1px solid">
This paragraph is 300px, the svg will stretch accordingly
</div>
</div>
<br><br><br><br>
<div style="display: flex">
<div>
<svg style="width: 40px; height:50px">
<line x1="20" y1="0" x2="20" y2="100%" stroke-width="1" stroke="black"></line>
</svg>
</div>
<div style="height: 50px;border:1px solid">
This paragraph is 50px, the svg will not shrink below 150px
</div>
</div>
Set your viewBox accordingly, I do not know the userSpaceOnUse (min x min y width height) of your SVG, let's say viewBox="0 0 100 100".
Remove width and height attributes.
Set the style attribute to 100% width and 100% height.
override the default xMidYMid value by setting preserveAspectRatio="none"
Resulting svg should look like this:
<svg preserveAspectRatio="none" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="width:100%;height:100%;">

How to make an svg element responsive to it's parent container?

I'm trying to acheive this:
Rendering an svg square (w:h = 1:1 rectangular) inside of div,
when the div's width is greater than height, the square should fit into the container by the height(red box is the div, green box is the svg square):
When the div's height is greater than width, the squire should fit into the container by the width:
It can be easily achieved by specifying the size of the svg view port.But if I remove the size of the svg view port, instead when I add the size to the parent div(red box), it refuses to look at the height of the container, the image turns to be:
Is there a way we can make the square responsive to the container height?
here is my code:
#main {
width: 400px;
height: 100px;
border: 4px solid red;
}
<div id="main">
<svg viewBox="0 0 100 100" preserveAspectRatio="xMidYMid Meet">
<rect x="0" y="0" width="100" height="100" fill="green"/>
</svg>
</div>
Just set the width and height attributes of the SVG to "100%". Either in the SVG, or via CSS.
Secondly, fix your other attributes:
viewBox values should not have commas.
it is preserveAspectRatio, not preserveAspect, and meet, not Meet
#main {
width: 400px;
height: 100px;
border: 4px solid red;
}
<div id="main">
<svg width="100%" height="100%" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid meet">
<rect x="0" y="0" width="100" height="100" fill="green"/>
</svg>
</div>

Targeting Inline SVG from stylesheet

I'm surprised to be having this problem, but there must be some funniness about SVG + CSS I'm not quite getting.
Short version, this doesn't work:
HTML
<div class="svg-container>
<svg class="mybox">...</svg>
</div>
CSS from stylesheet
.mybox { max-height: 150px; }
Long version
I have some SVG "widgets", some which are 2:1 width:height ratio, others which are 1:1 width:height, and need the flexibility for anything between and beyond.
The "widgets" will be shown in a gallery, each item having a width of 318px and a height of 150px.
However, the gallery is not the only (or even primary) display of these "widgets", they will be used elsewhere, and need to scale, so adding an inline SVG style block of max-height: 150px is not an option. Each SVG is sitting in a container, 'svg-container'.
Repeat: Inline SVG styles are not an option. SVG itself probably has to be inline, as we're passing data to the SVG, so linking to the SVG as an image, etc, not an option.
Here is a CodePen (yes, it's ugly, proof of concept)
Note: the arrow inside resizes to the max-height of 150px, however, the SVG loses its aspect ratio, as you can see from the border.
The trick was to set an height on the .svg-container. This implies that the height of the SVG element is 25vw (viewport width unit) but not more than 150px at max. Since the height of the element is now known, the width is set based on the viewBox specified on the SVG. The actual value (25vw in this case) is just a random value and can be modified as necessary.
The text-align: center on the container (as you would have guessed) is to center the SVG element horizontally within the container.
.svg-container {
text-align: center;
height: 25vw;
/* This is the key. I have used vw units for responsiveness */
margin-bottom: 10px;
border: 1px solid;
}
.mybox {
max-height: 150px;
height: 100%;
}
<div class="svg-container">
<svg class="mybox" style="border: solid" x="0" y="0" viewBox="0, 0, 500, 500">
<polygon fill="orange" points="256,512 512,256 352,256 352,0.001 160,0 160,256 0,256 "></polygon>
<text text-anchor="middle" x="250" y="250" style="font-size: 100px;" stroke="black" fill="black">000</text>
</svg>
</div>
<div class="svg-container">
<svg class="mybox" style="border: solid" x="0" y="0" viewBox="0, 0, 250, 500">
<polygon fill="orange" points="128,512 256,256 176,256 176,0.001 80,0 80,256 0,256 "></polygon>
<text text-anchor="middle" x="125" y="250" style="font-size: 50px;" stroke="black" fill="black">000</text>
</svg>
</div>

Resources