CSS fallback for OpenType small caps
Asked Answered
G

4

9

I'm working on a site where small caps are important: setting the text of the Bible. In the Old Testament the name of God is transliterated as Lord but in small caps—not LORD. However, the state of OpenType small caps support at the moment is… less than optimal. Safari (even up through Safari 8 on Yosemite, from which I am typing this) still doesn't support the -webkit-font-feature-settings: 'smcp' option, and a lot of the hits for this website will be coming from mobile.

Unfortunately, "graceful degradation" is problematic here: if you specify both font-variant: small-caps and font-feature-settings: 'smcp' in a browser that supports the latter (e.g. Chrome), the font-variant declaration overrides it, so the horribly ugly old-style version still comes into play. (Note: this is as it should be per the spec: the font-variant declaration has a higher priority than the font-feature-settings declaration). Given the current implementations of font-variant: small-caps, though—shrunken capitals rather than actual small capitals—the result is that using font-variant: small-caps realists in not-so-gracefully degrading everyone's reading experience.

In the past, I have exported the small caps as a distinct webfont and specified them directly; see this post for a simple example: the first line of each paragraph is specified that way.

While I can do the same thing here (and at least in theory could deliver a pretty small typeface, since I really only need three characters: o, r, and d), I'd prefer simply to enable sane fallbacks. As noted above, however, that's not possible. I am open to but would very much prefer to avoid server-side solutions (browser detection, etc.) as a point of complexity that is better to minimize, especially given how rapidly browsers change. How else might one solve this problem, and especially are there existing solutions for it?

Edit: clarifying based on comments—in the future, font-variant: small-caps will handle this nicely, as per the spec it should display a small-capitals-variant of the typeface if the typeface supplies it. However, at present, no browser supports this (at least, none that I can find!). This means that instead, they all render fake small capitals simply by scaling down actual capitals. The result is typographically unpleasant, and unacceptable on this project.

Girish answered 20/7, 2014 at 0:32 Comment(5)
What exactly is wrong with just using font-variant?Alabama
font-variant: small-caps should use the font's baked in small caps, if present, so you shouldn't need to turn on the smcp feature, basic CSS should already do the right thing.Limpet
@user1795832, using font-variant has the downside at present that it doesn't use actual small capitals; it just downscales the capitals. From a typography perspective, this is obnoxious and always has been. @Mike'Pomax'Kamermans, that's true in theory, but it doesn't work on any browser I've tested, and the MDN pages report the same—no support for it. Vendors appear to be supporting only the low-level handles (e.g. 'smcp') until the CSS3 spec is finalized, not supporting high-level declarations (so no font-variant-ligatures, either, and so on).Girish
Thanks for this question! Could you maybe describe breifly the method you used to create a seperate small-caps Gentium version?Hoxsie
@LeoKoppelkamm I've updated the answer below with details about using FontSquirrel to create a small-caps version.Girish
G
15

Last updated 2016/02/28.

I spent a considerable amount of time researching this and wrestling with it. After digging around as best I could, the top solutions for now are:

@supports

Take advantage of the @supports rule in browsers. This is what I initially opted to do on this project.[1] You use the rule this way:

.some-class {
    font-variant: small-caps;
}

@supports(font-feature-settings: 'smcp') {
    .some-class {
        font-variant: normal;
        font-feature-settings: 'smcp';
    }
}

I've simplified by leaving out the prefixed versions; you'll need to add the -webkit- and -moz- prefixes to get this actually working. Update, 2012/02/28: you no longer need the -moz- prefix, and this will work in Safari in the next release (iOS 9.3 and OS X Safari 9.1).

This has the advantage that support for real small caps and support for the @supports rule are very similar:

This isn't perfect: since IE10/11 don't implement @supports, you miss one browser. (Edit, 2015/09/31: IE proper doesn't have @supports, but Edge 12+ does, and that should increasingly cover all consumer users of the site.) Still, this gets you most of the way there, and it should be future-facing: this should progressively enhance the site nicely. The normal (bad, but functional) small caps are displayed in the meantime, and when browsers eventually get around to using OpenType small caps by default for font-variant: small-caps, this will continue to work just fine. It's "progressive enhancement" and it'll work nicely for most purposes.[2]

Typeface subsetting

As mentioned in the question, one can create a subset of the typeface that includes only small capitals. This is what I have done for the small caps on my own website; see the first line of the first paragraph in this post for an example.

To pull this off, you'll need to start by subsetting the typeface. You can do this manually with a font tool, or (the simpler way) you can use FontSquirrel's custom subsetting tool in their webfont generator. (Note: You must check the license and confirm that the typeface in question allows this kind of modification. See below.) In the web font generator, first upload the file you wish to modify. Then choose the Expert radio button. Most of the settings you can leave as they are; they're good sane defaults. Midway down the page you'll see OpenType Flattening options. Here, select only "Small Caps". Run the generator. The result will be a complete replacement of the normal lowercase letters with the small caps set.[3]

In that case, you can simply apply a style to the elements you want to have small capitals, e.g.:

.divine-name {
    font-family: 'my_typeface_smcp', 'my_typeface', serif;
}

The major advantage to this approach is consistency: that typeface is going to display on every browser out there, back to IE5.5, as long as you deliver it correctly using the various hooks required by @font-face.

There are a few disadvantages to this approach, though:

  1. It means delivering another font file. In my case, this would be an acceeptably low size (since I actually only need four characters), but it's still something to consider in general. It is in any case another HTTP request, which is going to further slow the page load time or at least give you some flash of unstyled text when it reloads.

  2. It may violate the licenses of the typefaces in question. For at least one of the fonts I am using on this project, it does: the license explicitly forbids rebuilding the font using tools like FontSquirrel. (FontSquirrel was the tool I used for this approach before, and it works quite well.) This is a make-or-break issue for using a subset of a typeface to accomplish the goal. That being said, if you have a good reason to do it, you may be able to get support from the vendor (especially if they're a small shop). For the project that prompted this question, I was able to do just that with a nice email—the designer is a great guy.

The other major reason not to do it this way is that it has a significantly higher maintenance cost. If at any point you need to change or update the typeface, you have to go through the subsetting process all over again. By contrast, the first option will simply work, though admittedly not as pleasantly as one might hope, and will not only continue to work but will actually improve over time as browsers increase their implementation of the CSS3 standard.


Notes

  1. For various reasons (especially see note 2 below), I actually opted for the second approach outlined here, which is the same approach I was trying to avoid. Alas.

  2. Issues remain: even in the latest Chrome (38 as of the time of this edit), using the font-feature-settings: 'smcp' approach has some issues. For example, if you turn on letter-spacing (a fairly common recommendation for small caps), the small caps will revert to normal lowercase letters. Fixed in Chrome 48. HT to answerer below.

  3. From the FontSquirrel blog post that introduced the feature:

    If you have a font with OpenType features, you can now flatten some of them into your webfont. For instance, some fonts have small caps built in, but they are completely inaccessible in a web browser. By selecting the "Small Cap" option, the Generator will replace all the lowercase glyphs with the small cap variants, giving you a small cap font. Please note that not all OpenType features are supported and if the font lacks OpenType features, using these options won't create them.

Girish answered 7/8, 2014 at 1:39 Comment(0)
B
1

Support for font-feature-settings:"smcp" has improved in recent browsers. Chrome 48 fixes the letter-spacing bug. And according to caniuse.com, upcoming versions of Safari for both iOS and Desktop will support it. The prefixed version is still required for -webkit, though.

Breadroot answered 28/2, 2016 at 0:34 Comment(0)
C
0

You already mentioned it here, graceful degradation.

Use the font-feature setting and if a browser does not support opentype features then the user will receive all the content still, with the worst-case scenario being full-size capitals; nothing is lost. I'd say that is a graceful degradation.

Aa an alternative you might want to consider using Typogruby (http://avdgaag.github.io/typogruby/), or similar, or manually adding a class to that word which you can target directly in your html, faking it with CSS2 e.g.:

.caps {font-size: .83em; letter-spacing: .25em; text-transform: uppercase;}

Edit: Of course, with the above method you can also specify a different font for the .caps class, one with full caps that will work well at small sizes in place of true small caps.

Cordiform answered 20/7, 2014 at 12:53 Comment(2)
Alas: "graceful degradation" doesn't work here. You can't transform all-caps to small caps successfully: LORD will under no circumstances come out right: if you write it that way and use small caps, all the capitals will become small capitals, including the first. And Lord is not a viable option, for various reasons having to do with the translation issue driving the whole thing in the first place. I'm familiar with the classing approach, and you'll note that I essentially mentioned it above. I'm looking for any known other solutions.Girish
Note that "progressive enhancement" will work… at whatever point the CSS3 spec finally gets finalized. Whenever that is. :pGirish
F
-2

Stop thinking OpenType for a moment and think content: you need different styling for semantically different data (in this case, smallcaps styling for a special religious word) That's literally what HTML markup was invented for. Just auto-wrap those words in <span class="smallcapped"> markup with javascript, and have its class use a dedicated small-caps font. This is very easy to do, and even allows you to find the best suite smallcaps look independent of the main text typeface.

Simply run some code every time a section is DOM-loaded (emphatically not after your entire content is done, unless you're only ever showing short stretches of text):

function wrapSmallcaps(element) {
  var content = element.innerHTML;
  ["LORD"].forEach(function(term) {
    var search = new RegExp("    W?"+term+"  W?",'g');
    content = content.replace(search, function(a,b) {
      return a.replace(term, "<span class='smallcapped'>"+term+"</span>");
    });
  });
  element.innerHTML = content;
}

ex: http://jsbin.com/cowumaxe/1/edit

We could solve this by trying to make use of OpenType fonts, but you'd be tying yourself to one font, rather than one for for each style, making it extremely hard to improve the looks later on without violating font licenses when a better running text typeface or smallcaps typeface is found, or user feedback prompts you to improve the looks.

(and of course, this code is literally universal for any browser since IE8 -- http://caniuse.com/#search=queryselector shows zero "no support" or even "partial support" for all browsers people actually use)

Fascicle answered 21/7, 2014 at 14:54 Comment(7)
Yes, this will work, though all the Javascript is unnecessary as I have the classes in the source. In any case, note that I noted this as a one possible solution in my question. I was looking for alternatives. :)Girish
If you already have classes in the source, then it's even easier. Consider this answer also a remark on the fact that even if you're looking for alternatives, that doesn't mean the alternatives are better. Even when browsers fully support all of OpenType, having marked up source changes which parts of OpenType you need: some OpenType features like smallcaps (as opposed to ligatures etc.) only make sense in typesetting contexts where there is no explicit markup available (indesign, word, etc). If there is, controlling the style through markup-linked styling is infinitely better.Limpet
As I would think the question strongly indicates: this is a browser context, not some place where explicit markup is available. Even there, active use of OpenType features is the standard; the existence of distinct small caps is hardly an innovation driven by browsers: it is a centuries-old practice made available through OpenType.Girish
Eh? Browser context is literally where explicit markup is available in your source. You said you already have classes in your source, so to me that means you have [...] the <span class='lord'>lord</span> said [...]. Yes, OpenType is a standard on the web, and I love OpenType, but smallcaps features is one of those things that doesn't make sense as a blind feature on the web (unlike old style numbers or swashes etc) because it literally swaps typeface. A separate OpenType font for the smallcaps look, applied to the markup for your "these words need a different typeface", is much better.Limpet
That's… not how OpenType small caps work, and OpenType as a standard is much broader than the web (and wasn't invented for the web). It works exactly like ligatures does: it substitutes other characters as appropriate in a given context. It uses the same typeface, just making appropriate transformations. Perhaps we're just talking past each other?Girish
we might be. I know how OpenType works, I've written parsers for them and articles about them; the argument I was making was that relying on small capital substitution in a medium that allows styling based on semantic markup makes less sense than marking up the data that needs small capital styling and simply using a dedicated small caps font, as smallcaps are just another stylistic set: an alternate typeface encoded in the same font file. So, using markup with a smallcap font, instead of using smallcaps GSUB, makes your content supported in virtually all browsers, past and futureLimpet
I figured we had to be. :p As noted in the original post, the best solution I've come up with to date relies heavily on marking up that way (or otherwise accessing via CSS rules—e.g. p:first-line) and substituting a typeface directly. In this case, it appears that's the best solution: a typeface that replaces the normal characters with their small cap equivalents and supplies that typeface to the corresponding files. (That's what I do on my own website currently, so I know it works reliably.) Thanks for the discussion!Girish

© 2022 - 2024 — McMap. All rights reserved.