Re: Support viewBox in CSS

Previous Topic Next Topic
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view

Re: Support viewBox in CSS

Amelia Bellamy-Royds
[Copying the SVG mailing list, since this discussion affects them most of all.  For www-svg subscribers, you may want to start by reviewing Jake Archibald's message from 28 February "object-fit/view-box for element content", archived here*:
*I think. The archives are currently down, or too slow for my very slow connection.]

I am also strongly supportive of converting the SVG viewBox attribute into a property controllable by CSS.  I also like Jake's proposal to extend it to the CSS box model as a way of creating aspect ratio control and scale-to-fit behavior for HTML content.

It's taken me this long to reply because I wanted to carefully outline some of the issues that need to be considered.  This is also effectively an explanation of why we haven't done this yet in SVG.

It's getting a little late to spec this in time for SVG 2.  However, if there is support from the CSS WG to also adopt this feature for CSS box layout, then it probably makes sense to have it as its own module, anyway.

(Aside: I agree that the CSS-ified term should be `view-box`. I'm using `viewBox` here out of habit. I'd assume it would continue to be the standard spelling for the presentation attribute.)

First, a summary of what the SVG viewBox and preserveAspectRatio attributes do, translated into CSS terms:

- viewBox defines an intrinsic aspect ratio for the element.  This intrinsic aspect ratio is used to resolve `auto` height or width width proportional to a fixed value in the other dimension.

- viewBox defines default height and width in pixels for the element, although this currently only has an effect when specified on the root element of an SVG file that is embedded as an image.

- viewBox + preserveAspectRatio together create a scale + translate transformation for the element's _child_ content.  They do not affect the position or scale of the layout box for the `<svg>` itself.

- viewBox (in most but not all cases) establishes the basis for 100% width and 100% height for SVG layout of all child elements.  More generally, it defines the px-to-percentage ratio.

The scale transformation is not defined as an explicitly magnification factor, like in transform: scale(s).  Instead, it's always a scale-to-fit, with the preserveAspectRatio attribute defining what "fit" means when the `<svg>` layout box is constrained to a different aspect ratio than that declared by the viewBox.  preserveAspectRatio therefore has much the same function as CSS object-fit and object-position combined (albeit with more limited options).

The translate transformation is a combination of an implicit transformation from the preserveAspectRatio (the corner of the viewBox won't always fit up against the corner of the layout box) and an explicit transformation defined by the xMin and yMin parameters of the viewBox.  These are often left as 0 (no explicit translation), but they are very useful in some cases, such as creating a centered coordinate system.

So, if I was going to write up a proper spec for viewBox in CSS (as I keep telling people I intend to do), I would make the x- and y-offset parameters optional, and I would replace preserveAspectRatio with object-fit and object-position.  (The mapping of attribute values to those properties could be defined via user stylesheets.)  Currently, these object-* properties are not well defined for `<svg>` elements anyway, and interact poorly with preserveAspectRatio.  It makes sense to create a single cohesive definition, with all the extra options that object-* properties provide.  Jake's proposal introduces a new property, but I don't think it's necessary.  

Benefits for SVG:

- Allow viewBox to be animated via CSS animations & transitions.  This is an important part of making declarative animation via CSS a comprehensive alternative to SMIL.

- Allow viewBox to be controlled by media queries.  This is essential for creating responsive SVG designs.  SVG 2 geometry properties and CSS Transforms allow the layout of shapes to be adjusted for different media, but that is of limited use unless the overall dimensions of the graphic can also be adjusted.

- Allow a common viewBox to be specified once for many different `<svg>` elements within a document (e.g., for SVG icons in an HTML file), reducing markup and repetition.

Complications for SVG:

- When an SVG file is an embedded document (e.g., `<img>` or `<object>`), the viewBox is often used to determine how much space in the main document it should consume (by defining the intrinsic aspect ratio & size).  This in turn affects the "media" for all media queries defined within the SVG file.  If those media queries can alter the viewBox, you could generate infinite loops & there would need to be clear rules on how to resolve them.

- Only certain elements currently accept the viewBox attribute (svg, symbol, pattern, marker).  Only some of these (svg, symbol) are "viewport elements" that re-define the px-to-percent ratio. 

  If viewBox became a generic property that you could specify on any element (by setting it to a value other than `auto`), it would need to create a consistent behavior as a layout container for child content. 
  I personally think it's one of the biggest mistakes in SVG that patterns & markers _don't_ reset the meaning of percentage lengths (it makes percentage lengths pretty much useless within pattern and marker content).  There has been some talk of changing this in the SVG 2 spec, but it would be a breaking change & we haven't made that resolution.  I would also like to see viewBox available on `<clipPath>` and `<mask>`.  I don't mind allowing it on generic SVG container elements (`<g>` and `<a>`), effectively giving them the same layout model as a nested `<svg>`.  The other parts of the layout model, `x`, `y`, `width`, and `height`, have all been made geometry properties in SVG 2, although they currently would not have any effect if declared on a `<g>` element.
  A nested `<svg>` element, even without a `viewBox` attribute, redefines the coordinate system for child elements.  In other words, it resets the percentage-to-px ratio and creates a translation based on the value of `x` and `y`. Therefore, there would need to be both a `view-box: auto` value (default for SVG elements that normally take a `viewBox` attribute) and a `view-box: none` value (initial value for the property and default for all other SVG container elements).
  These changes would require coordinating changes to SVG DOM properties that allow you to access the nearest ancestor viewport element for any SVG element.  They need to either (a) be redefined to retrieve the nearest ancestor `<svg>` or `<symbol>` element, independent of any notion of viewBox or the basis of 100% or (b) be redefined to find the nearest ancestor whose computed value for `view-box` is not `none` (more useful, but more work for the implementation). Implementations would also need to update how they resolve percentage lengths.
- The `<use>` element has special rules for how width & height values on that element interact with width, height, and viewBox on a re-used `<svg>` or `<symbol>` element.  These rules would need to be updated & generalized to any element with a non-none computed value of `view-box`, and implementations updated correspondingly.
- SVG currently has other ways to set the viewBox on an `<svg>` element, the `<view>` element and the `#svgView()` fragment, which are not easy to represent in CSS.  When a view is in effect, the effective `viewBox` and `preserveAspectRatio` values for the `<svg>` element are over-ridden by those specified in the view.  This shouldn't be a deal-breaker, though, since you can also specify `transform` via those means, so any implementation is already going to have to propagate transform styles from the view to the SVG.  It's just an extra headache & confusion to be aware of.

- Although it's not currently specced, a common request for SVG has been the ability to automatically generate a viewBox based on the bounding box (fill-box or stroke-box) of its child contents.  This would scale whatever graphic you have to fill the available space, without the author needing to calculate the exact bounds of the graphic.  This is _different_ from the `auto` value I've described previously (the current behavior of an SVG if you don't specify `viewBox` attribute), which creates a view-box based on the width and height in pixels, and therefore does not apply a scaling effect.

Benefits for CSS box model:

The main benefit as noted in Jake's proposal would be aspect ratio control, for videos, for images that haven't been downloaded yet, and for complex graphical layouts.

There have been a number of different proposals on www-style lately for aspect ratio control.  Currently, the "padding hack" is the only way to create a scalable container with a fixed aspect ratio (and it can only be used to scale height to match width and not the reverse).

The scale-to-fit effect of viewBox would be also useful for big text headers and for embedded `<iframe>` (e.g., the little scaled-down preview iframes used on CodePen).

Complications for CSS box model:

The `view-box` approach would not be quite as flexible as other proposals for aspect ratio control, since it can't separate the aspect ratio from the scale effect.  Most of the use cases (video, image, and big-text headers) are all scale-to-fit.  But there could be other cases, such as text-in-a-shape layouts, where you would want the box to maintain a certain aspect ratio but not scale the text.

The xMin and yMin offset parameters are less useful for CSS (at least, until polar coordinate layout gets adopted).  If we make them optional that isn't a big problem, but would need to define the behavior if they are used (e.g., the impact on the interpretation of left/top/etc in absolute positioning, on transform-origin, and so on).

All in all, I do think it's a useful proposal, but there are a lot of details to sort out.  I would be happy to help spec it, but I probably won't have much time to work on it in the next few months as we try to get SVG 2 and the SVG accessibility specs finalized.

~Amelia Bellamy-Royds

On 4 April 2016 at 20:24, Sarah Drasner <[hidden email]> wrote:
I would like to support Jake Archibald's proposal, to move the viewBox, or view-box, as stated in the proposal into the CSS spec. Currently if you need to adjust the viewBox for responsive development, which is a very useful use-case, you have to use JavaScript for the behavior of a simple media query. Using a CSS media query would be nice in a multitude of implementations, including but not limited to SVG sprites or Infographics. I've written an article for a few ways of working with it here:

Thanks for your time and attention.
Best wishes,