meyerweb.com

Skip to: site navigation/presentation
Skip to: Thoughts From Eric

Archive: 'Browsers' Category

Scaling SVG Clipping Paths for CSS Use

I’ve been working a lot with the clip-path property recently, as I write the chapter on filters, blends, clipping, and masking for CSS: The Definitive Guide’s long-delayed 4th edition (available now in early-release format!).  One of the nifty things you can do with clipping paths is define them with percentage-based coordinates.  For example, here’s a hexagon-like clipping path:

clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);

That starts at the center top (50% 0), goes to the right edge, quarter-down (100% 25%), and so on.

When I got to SVG-based clipping, which you can use with the CSS pattern clip-path: url(#pathID), I tried and failed with this:

<clipPath id="hexlike">
  <polygon points="50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%" />
</clipPath>

It didn’t work because, as I discovered to my horror, SVG does not allow percentage coordinates.  I could just strip all the percent signs out, but that would be the same as saying this in CSS:

clip-path: polygon(50px 0, 100px 25px, 100px 75px, 50px 100px, 0 75px, 0 25px);

I didn’t want pixels, though.  I want percentages, darn it all!

So I asked around on Twitter, and Markus Stange pointed me to the solution: converting all the SVG coordinates to the range 0–1 and using the clipPathUnits attribute.  The working version looks like this:

<clipPath id="hexlike" clipPathUnits="objectBoundingBox">
  <polygon points="0.5 0, 1 0.25, 1 0.75, 0.5 1, 0 0.75, 0 0.25"/>
</clipPath>`
A hexlike clipping path.

That yields the same result as the polygon() CSS shape with the percentages I showed before.

All that is great if you’re writing your own SVG shapes and can make sure you set it up properly, but what if someone hands you a shape to be used as a clip path and it’s in absolute coordinates like 100 75?  If you’re really lucky, the shape has a viewbox of 0 0 100 100 (or all the coordinate points are in that range) and you can just divide all the coordinate points by 100 to get the proper values.  But that’s really tedious for any but the simplest of shapes, and besides, what if it has some other viewbox?  That’s where the transform attribute saves the day.

For example, suppose you get an SVG file that looks like this (with the actual path coordinates removed because there are a lot of them):

<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
  xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 329.6667 88">
  <clipPath id="cloud02">
    <path d="…(coordinates go here)…"/>
  </clipPath>
</svg>

First, add the clipPathUnits attribute to the <clipPath> element:

<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
 xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 329.6667 86">
  <clipPath id="cloud02" clipPathUnits="objectBoundingBox">
    <path d="…(coordinates go here)…"/>
  </clipPath>
</svg>

Next, look at the viewBox attribute on the <svg> element itself.  The value there is 329.6667 86.  That means 329.6667 coordinate units horizontally, and 86 units vertically.  So all you need to do now is divide all the horizontal values by 329.6667, and the vertical values by 86.  Which would be super tedious, except we have scaling transforms at our disposal:

<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
  xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 329.6667 86">
  <clipPath id="cloud02" clipPathUnits="objectBoundingBox"
   transform="scale(0.003033 0.0116279)">
    <path d="…(coordinates go here)…"/>
  </clipPath>
</svg>

Those two values are 1/329.6667 and 1/86, respectively, and they effectively scale every point in the d attribute to fit into the needed 0–1 range.  (That’s not precisely what happens, but the outcome is the same.)  Thus we have an SVG clipping path that scales with the element and fits to its dimensions!

This works just as well for other markup patterns.  To return to the hexlike path from before, assume it was written like this:

<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
 xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100">
  <clipPath id="hexlike">
    <polygon points="50 0, 100 25, 100 75, 50 100, 0 75, 0 25" />
  </clipPath>
</svg>

If that were applied as-is, via clip-path: url(#hexlike), it would create a hex-like clipping path that fits a 100px by 100px box, positioned in the top left of the element (in left-to-right languages, I presume).  The quick fix:

<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
 xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100">
  <clipPath id="hexlike" clipPathUnits="objectBoundingBox"
   transform="scale(0.01)">
    <polygon points="50 0, 100 25, 100 75, 50 100, 0 75, 0 25" />
  </clipPath>
</svg>

Bingo bango bongo, it will now scale to the element’s dimensions, whatever those turn out to be.

Of course, if you apply that to something like a short paragraph, it will be verrrrry stretched out, but the same would be true with a percentage-based polygon() shape.  The beauty here is that you can scale any coordinate set, so if you have a tiny path that you want to blow up, or a huge path you want to shrink down, you can transform it without using clipPathUnits to stretch it over the bounding box.  Something like this:

<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
 xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100">
  <clipPath id="hexlike" transform="scale(4)">
    <polygon points="50 0, 100 25, 100 75, 50 100, 0 75, 0 25" />
  </clipPath>
</svg>

That gets you a hexlike shape that fits a 400px by 400px box, for example.

Now all CSS needs is the ability to size and position clipping paths in a manner similar background images, and we’ll be in clover.  I hope such properties are in the WG’s mind, because I’d love to be able to just point an at SVG clipping path, and then say something like clip-path-position: center; clip-path-size: contain;.  That would remove the need for fiddling with SVG attributes altogether.

Thanks to Markus Stange for the technique and his invaluable assistance in proofreading this article.

Element Dragging in Web Inspectors

Yesterday, I was looking at an existing page, wondering if it would be improved by rearranging some of the elements.  I was about to fire up the git engine (spawn a branch, check it out, do edits, preview them, commit changes, etc., etc.) when I got a weird thought: could I just drag elements around in the Web Inspector in my browser of choice, Firefox Nightly, so as to quickly try out various changes without having to open an editor?  Turns out the answer is yes, as demonstrated in this video!
Youtube: “Dragging elements in Firefox Nightly’s Web Inspector”
Since I recorded the video, I’ve learned that this same capability exists in public-release Firefox, and has been in Chrome for a while.  It’s probably been in Firefox for a while, too.  What I was surprised to find was how many other people were similarly surprised that this is possible, which is why I made the video.  It’s probably easier to understand to video if it’s full screen, or at least expanded, but I think the basic idea gets across even in small-screen format.  Share and enjoy!

Workshopping

I’m criminally behind in sharing this with everyone, so I’m jumping straight to the bottom line here: I’m teaching a workshop on advanced CSS layout techniques in October, and co-teaching another workshop on CSS animation in November with the inestimable Val Head.  Both are courtesy O’Reilly & Associates, and will be conducted at their offices in Boston.

A few more details:

  • New CSS Layout (October 17-18) is two days of deep diving into flexbox, multicolumn, grid, and related technologies.  There will be a heavy emphasis on Things You Can Use Today, including bugs and how to handle them, with a keen focus on using everything in a progressively enhancing way.  In other words, you should walk away knowing how to use new technologies right away, without leaving behind users of older browsers, and have a good sense of what you’ll be able to do in the next 6-12 months.  This will be hands-on, interactive, and very much a dialogue with technical instruction.  If you’re looking for two days of watching me drone in front of a slide show, this is not that.  I’m not even sure I’ll have any slides at all—I’ll probably spend the entire time in BBEdit and a browser instead.  The class size is limited to 40 people.
  • CSS Animation (November 17-18) is another two days of diving deep into the topic.  For this one, I’ll spend the first day going through every last piece of CSS transition and animation syntax, with generous helping of transform.  On the second day, Val will show how to put that syntax to use in a way that serves and strengthens your design, instead of undermining it.  It’s basically a day of learning how the tools work, and a day of learning how to properly use the tools.  Again, class size of 40; and again, very much hands-on and interactive.

So that’s what’s up.  Looking for ways to seriously expand your skills in layout or animation or both?  Come, join us!

Subgrids Considered Essential

Grid layout is a pretty darned fantastic thing.  I’ve been digging into it as I write the grid layout chapter of CSS:TDG4e, and there are more things in here than have been dreamt of in our layout philosophies.  One of the things I’ve recently come to realize is the power and necessity of subgrids.

To understand why, let’s explore a use case, which you can see in various forms on this test page.  I cribbed this use case pretty directly from Fantasai, but I’m going to be analyzing it a little differently.  The basis is a form with the various fields and labels in unordered list items, to help with accessibility and to be basically readable and usable if the CSS somehow fails to be applied.  The markup looks like this:

<form method="post" action="script.cgi">
  <ul>
    <li><label for=name>Name:</label>
        <input id=name name=name></li>
    <li><label for=email>Email:</label>
        <input id=email name=email type=email></li>
    <li><label for=tel>Telephone:</label> 
        <input id=tel name=tel type=tel></li>
    <li><label for=url>Homepage:</label>
        <input id=url name=url type=url></li>
    <li><label for=bday>Birthday:</label>
        <input id=bday name=bday type=date></li>
  </ul>
</form>

The basic form with no grids.

Ideally, we’d like the form fields to all line up into a column, and the labels to be in another column.  Furthermore, we’d like the label column to be only as wide as the widest label’s element box, and no more; the rest of the grid can be taken up by the input column.

This seems like a case tailor-made for grid layout, but there’s a problem.  Because grid items are only ever the child elements of a grid container, we can’t just apply a grid to the ul element and go from there.  Instead, we have to make each li element a grid container, in order to make each label and input element a grid item.  But that means each list item’s grid affects its children, with no reference to the other list items’ grids.  We can write the template we want, like so:

form ul li {display: grid;
  grid-template-columns: [start label] max-content [input] 1fr [end];}

Each list item a grid, but to what end?

That will get us a result almost precisely as if we’d applied no grids at all.  The only difference is that the input elements will all be as wide as their columns, according to the CSS Grid specification.  Chrome fails to do this last bit correctly, whereas Firefox Nightly gets it right, but otherwise the layout is essentially the same.  You can see this in the #form1 example on the test page.  (Remember, you have to have a current grid-supporting browser for the examples to correspond to what I’m talking about here.)

We can get closer to our goal by experimenting with a fixed-width grid column for the labels, figuring out the width of the widest label, and then just making all the label columns be that wide.  That would be written something like this:

form ul li {display: grid;
  grid-template-columns: [start label] 7em [input] 1fr [end];}

Using fixed-width columns to simulate a single column.

That works pretty well so long as none of the labels ever change—if a label is added (or edited) to be wider, it will wrap to multiple lines.  Alternatively, if the longest label is dropped or edited to be shorter, the label column won’t resize down.  It will just stay the same dumb fixed width until someone hand-adjusts it.  And honestly, at that point I may as well be using flexbox, which would do this version of the layout just as well, and would be more widely supported for the near to intermediate future.  At any rate, you can see the grid version of this in the #form2 example on the test page.

But what if we could set a grid on the ul element, and then make all the li elements grids that use their parents’ grid for the layout of their children?  That’s exactly what subgrids do.  This is the solution we’ve been seeking, in basic form:

form ul {display: grid;
  grid-template-columns: [start label] max-content [input] 1fr [end];}
form ul li {display: grid; grid: subgrid; grid-column: start / end;}

Here the list items establish grid containers, thus making the label and input elements into grid items like before, but are stretched across the two columns of the ul while using those very grid lines for laying out their child elements, plus those children influence the placement of their grandparent’s grid lines.  Thus, we can specify things like max-content for the label column size and have it Just Work™.

Or it would Just Work™, except that as I write this, none of the grid implementations have subgrid support.  Authors who want to create the kind of layout we’re after have to compromise in one way or another—either by faking the content-sizing with a fixed-width column, or by stripping down the markup until there’s barely anything left—thus sacrificing accessibility, progressive enhancement, and general best practices, as Fantasai illustrated in her article.

You can probably see a lot of other ways in which subgrids would be useful.  Take defining a full-page grid, the kind with a bunch of regularly repeating grid lines onto which various elements can be attached, like this one or this one.  In this scenario, being able to designate each section of the page a subgrid would let you have all the pieces inside that section participate in, and lay out in relation to, the overall page grid.  Without subgrids, you’d either have to make every element you want to lay out a child of the body element (or whatever you used to create the page grid), or you’d have to recreate segments of the page grid in each nested grid, and give up any hope of columns that can flex with the contents of multiple page sections.  Neither solution is appealing.

This is why I’ve come to the same conclusion other grid experts (like Rachel) already have: subgrids are a major component of grid layout, and should be a part of any grid layout implementation when it emerges from developer-preview status.  If that means delaying the emergence of grids, I think it’s worth it.

I say that because our field has a tendency to glom onto the first iteration of a technology, learn it inside out, hack around its limitations, and then ignore any future improvements unless somehow forced to do so.  If grid layout is released without subgrid support, we’re risking shoving subgrids into the back of the author-practices cupboard for a long time to come.  And along with it, potentially, grids themselves.  The frustration of trying to build layouts without subgrids will quickly become overwhelming for all but the simplest cases, leading authors to try and then discard grids as a serious tool.

First impressions matter.  CSS itself suffered for years from the initial impressions designers formed of “boring, boxy” layouts, and it still suffers from the handicap of being a presentation system without a layout engine at its core.  Grid layout is the first serious candidate to fill that hole in the past two decades, and I don’t want to see them hamstrung from the outset.  Subgrids are essential to the adoption of grids.  I hope they’ll be implemented as soon as possible, and before grids are pushed into public release channels.

Essential Tool: Firefox’s screenshot Command

Everyone has their own idiosyncratic collection of tools they can’t work without, and I’ve recently been using one of mine as I produce figures for CSS: The Definitve Guide, Fourth Edition (CSS:TDG4e).  It’s Firefox’s command-line screenshot utility.

To get access to screenshot, you first have to hit ⇧F2 for the Developer Toolbar, not ⌥⌘K for the Web Console.  (I know, two command lines—who thought that was a good idea?  Moving on.)  Once you’re in the Developer Toolbar, you can type s and then hit Tab to autocomplete screenshot.  Then type a filename for your screenshot, if you want to define it, either with or without the file extension; otherwise you’ll get whatever naming convention your computer uses for screen captures.  For example, mine does something like Screen Shot 2015-10-22 at 10.05.51.png by default.  If you hit [return] (or equivalent) at this point, it’ll save the screenshot to your Downloads folder (or equivalent).  Done!

Except, don’t do that yet, because what really makes screenshot great is its options; in my case, they’re what elevate screenshot from useful to essential, and what set it apart from any screen-capture addon I’ve ever seen.

The option I use a lot, particularly when grabbing images of web sites for my talks, is --fullpage.  That option captures absolutely everything on the page, even the parts you can’t see in the browser window.  See, by default, when you use screenshot, it only shows you the portion of the page visible in the browser window.  In many cases, that’s all you want or need, but for the times you want it all, --fullpage is there for you.  Any time you see me do a long scroll of a web page in a talk, like I did right at the ten-minute mark of my talk at Fluent 2015, it was thanks to --fullpage.

If you want the browser --chrome to show around your screenshot, though, you can’t capture the --fullpage.  Firefox will just ignore the -fullpage option if you invoke --chrome, and give you the visible portion of the page surrounded by your browser chrome, including all your addon icons and unread tabs.  Which makes some sense, I admit, but part of me wishes someone had gone to the effort of adding code to redraw the chrome all the way around a --fullpage capture if you asked for it.

Now, for the purposes of CSS:TDG4e’s figures, there are two screenshot options that I cannot live without.

A screen capture of Facebook’s “Trending” panel.
I captured this using screenshot fb-trend --selector '#u_0_l'.  That saved exactly what you see to fb-trend.png.

The first is --selector, which lets you supply a CSS selector to an element—at which point, Firefox will capture just that element and its descendants.  The only, and quite understandable, limitation is that the selector you supply must match a single element.  For me, that’s usually just --selector 'body', since every figure I create is a single page, and there’s nothing in the body except what I want to include in the figure.  So instead of trying to drag-select a region of the screen with ⇧⌘4, or (worse) trying to precisely size the browser window to show just the body element and not one pixel more, I can enter something like screenshot fig047 --selector 'body' and get precisely what I need.

That might seem like a lot to type every time, but the thing is, I don’t have to: not only does the Web Toolbar have full tab-autocomplete, the Toolbar also offers up-arrow history.  So once I’ve tab-completed the command to capture my first figure, I just use the up arrow to bring the command back and change the file name.  Quick, simple, efficient.

The second essential option for me is --dpr, which defines a device pixel ratio.  Let’s say I want to capture something at four times the usual resolution.  --dpr 4 makes it happen.  Since all my figures are meant to go to print as well as ebooks, I can capture at print-worthy resolutions without having to use ⌘+ to blow up the content, or fiddle with using CSS to make everything bigger.  Also if I want to go the other way and capture a deliberately pixellated version of a page, I can use something like --dpr 0.33.

I have used this occasionally to size down an image online: I “View Image” to get it in its own window, then use screenshot with a fractional DPR value to shrink it.  Yes, this is a rare use case, even for me, but hey—the option exists!  I haven’t used the DPR option for my talks, but given the growing use of HD 16:9 projectors—something we’ve been using at An Event Apart for a while now, actually—I’m starting to lean toward using --dpr 2 to get sharper images.

(Aside: it turns out this option is only present in very recent versions of Firefox, such as Developer Edition 43 and the current Nightlies.  So if you need DPR, grab a Nightly and go crazy!)

A closeup of text on a test page.
A snippet of an image I captured using --dpr 5.  On-screen, the page was at 100% zoom, 16-pixel (browser default) text sizing.  The resulting capture was 4000×2403 pixels.

And that’s not all!  You can set a --delay in seconds, to make sure a popup menu or other bit of interaction is visible before the capture happens.  If you want to take your captured image straight into another program before saving it, there’s --clipboard.  And there’s an option to upload straight to --imgur, though I confess I haven’t figured out how that one works.  I suspect you have to be logged into imgur first.  If anyone knows, please leave a comment so the rest of us know how to use it!

The one thing that irks me a little bit about screenshot is that the file name must come before the options.  When I’m producing a bunch of figures in a row, having to drag-select just the file name for replacement is a touch tedious; I wish I could put the file name at the end of the command, so I could quickly drag-select it with a rightward wrist-flick.  But all things considered, this is a pretty minor gripe.  Well, shut my mouth and paint me red—it turns out you can put the filename after the options.  Either that wasn’t possible at some point and I never retested the assertion, or it was always possible and I just messed up.  Either way, this irk is irksome no more!

The other thing I wish screenshot could do is let me define a precise width or height in pixels—or, since I’m dreaming, a value using any valid CSS length unit—and scale the result to that measure.  This isn’t really useful for the CSS:TDG4e figures, but it could come in pretty handy for creating talk slides.  No, I have no idea how that would interact with the DPR option, but I’d certainly be willing to find out.

So that’s one of my “unusual but essential” tools.  What’s yours?

Undoing oncut/oncopy/onpaste Falsities

Inspired by Ryan Joy’s excellent and deservedly popular tweet, I wrote a small, not-terribly-smart Javascript function to undo cut/copy/paste blocking in HTML.

function fixCCP() {
   var elems = document.getElementsByTagName('*');
   var attrs = ['onpaste','oncopy','oncut'];
   for (i = 0; i < elems.length; i++) {
      for (j = 0; j < attrs.length; j++) {
         if (elems[i].getAttribute(attrs[j])) {
            elems[i].setAttribute(attrs[j],elems[i]
            .getAttribute(attrs[j])
            .replace("return false","return true"));
         }
      }
   }
}

Here it is as a bookmarklet, if you still roll that way (as I do): fixCCP.  Thanks to the Bookmarklet Maker at bookmarklets.org for helping me out with that!

If there are obvious improvements to be made to its functionality, let me know and I’ll throw it up on Github.

Ciao, Camino

It’s been obvious for a long time that Camino was withering away, but they’ve formally called an end to the project:

After a decade-long run, Camino is no longer being developed, and we encourage all users to upgrade to a more modern browser. Camino is increasingly lagging behind the fast pace of changes on the web, and more importantly it is not receiving security updates, making it increasingly unsafe to use.

I used Camino for a long time, and only left when it had lagged much too far behind the rest of the browser market.  (Camino used Gecko embedding, which was disabled a couple of years ago.  That change effectively froze Camino’s rendering engine at the level of Firefox 3.6.)

When I migrated away from Camino, I tried a few alternatives and eventually settled on Firefox because its UI was the least unlike Camino’s.  (We like best what we know best.)  There were still some things I sorely missed, though, like simple Flash blocking and whitelisting, the multi-row Bookmarks bar, the keyboard bookmark-activation shortcuts, and the truly great downloads manager.  If you miss those (or would like to experience them) too, here’s how I got them back in Firefox:

  • Flashblock — though its UI isn’t quite as easy as Camino’s Flash preferences, Flashblock works well and allows whitelisting.
  • Multirow Bookmarks Toolbar Plus — the layout of this has gotten a bit wonky under recent Firefox Nightly builds, but still works just fine, and you can even set it to auto-hide itself.
  • Bookmark Shortcut Keys — built for me in Jetpack by Jeff Balogh, this lets you define which keyboard shortcuts trigger the first nine bookmarks in the Bookmarks bar.  I use this all the time, just as I did in Camino.  I’ve defined my shortcuts to be ⌘1 through ⌘9, but you can pick whichever modifier keys you like.  Thanks, Jeff!
  • Download Manage Tweak — adds controls to show a file in the OS, delete the file, remove the file from the list without deleting it, and so on.  As with Flashblock, it isn’t quite as smooth as Camino’s UI, but it does the same job and a bit more besides.

And one more: New Tab Homepage, which makes certain that Firefox loads your Home page, and not the dashboard, whenever you open a new tab.

Oh, all right, one more one more: RSS Icon in Awesombar.  If you still roll that way.

So if you still pine a bit for Camino’s UI features, there’s how you can recreate most of the experience in Firefox.  If you don’t, then peace be with you, as with the entire Camino team.  Thank you all for everything you did to bring OS X a great browser that just felt right.

Helvetial

Maybe all the cool kids already know this, but I didn’t, so I’ll document it for the rest of us:  in Windows, Helvetica is not Helvetica: it’s Arial.  It’s Arial even if you explicitly ask for Helvetica and fall back to a non-sans-serif font family and allow for no other choices—but it’s not Helvetica if you try to get to it indirectly.

To see what I mean, you can load up my testcase in any Windows browser—IE, Firefox, Chrome, whatever—assuming that you haven’t installed Helvetica on your Windows machine.  (If you have, then I’d love to know what results you get.)  Given that you haven’t installed Helvetica, you should see that three of the four bottom-bordered spans are using Arial.  This can be determined due to the shapes of the “GR” characters—they are notably different between Helvetica and Arial.  Here’s what I apply to the first test list item:

#l01 .s01 {font-family: Helvetica, monospace;}
#l01 .s02 {font-family: Arial, monospace;}

My result is that they use exactly the same face, and that face is Arial, which should not have happened.  If Helvetica is not present, the first span should be rendered using a monospace font face.  If it is present, then the first span should have different letterforms than the second.

But it’s the second line where things get really interesting.  There, I assigned local copies of Helvetica and Arial (if they exist) to the invented family names “H” and “A”.  Then I apply this to the second test list item:

#l02 .s01 {font-family: H, monospace;}
#l02 .s02 {font-family: A, monospace;}

The result should be the same as the first line, but it isn’t: the first span gets a fallback font face, and the second span gets Arial.  So while the system redirects requests for Helvetica to Arial, it doesn’t do so in such a way that the invented family name “H” resolves to Arial, even though it was assigned Helvetica (or perhaps I should say “Helvetica”) as its source.

I’d be interested to know if there’s something I’ve overlooked or misunderstood here, because these waters are deep and I suspect my understanding of them is somewhat shallow.

February 2017
SMTWTFS
January  
 1234
567891011
12131415161718
19202122232425
262728  

Archives

Feeds

Extras