I’m not sure what the exact use case was, but Sam Stephenson recently asked me:
@chriscoyier What's the best way to reference the OS X system font in CSS (to get Lucida Grande or Helvetica, as appropriate)?
— Sam Stephenson (@sstephenson) November 20, 2014
My immediate thought was to use the old trick where you put the User Agent in a data-attribute on the root element that you can select off of.
var doc = document.documentElement;
doc.setAttribute('data-useragent', navigator.userAgent);
Then set the fonts as needed. In this case, setting Lucida Grande for OS X but Helvetica Neue for the latest version.
html {
font-family: sans-serif;
}
html[data-useragent*='Mac OS X'] {
font-family: "Lucida Grande","Lucida Sans Unicode", Tahoma, sans-serif;
}
html[data-useragent*='Mac OS X 10_10'] {
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
font-weight: 300;
}
Which basically works. It’s UA-sniffing which is bad, but we aren’t doing anything particularly mission critical here such that we would put our site or the web at risk.
Sam tried another path and got pretty fancy. Here, he creates an iframe
(so there is no interfering styles), inserts a button
element (which should have the system font on it by default), and tests the styles on it.
(function() {
window.addEventListener("DOMContentLoaded", function() {
getSystemFontFamily(function(systemFontFamily) {
var cssText = ".system-font { font-family: " + systemFontFamily + " }";
var element = createStyleElement(cssText);
document.head.insertBefore(element, document.head.firstChild);
});
});
function getSystemFontFamily(callback) {
withHiddenFrame(function(document) {
var button = document.createElement("button");
document.body.appendChild(button);
callback(window.getComputedStyle(button).fontFamily);
});
}
function withHiddenFrame(callback) {
var frame = document.createElement("iframe");
frame.style.cssText = "width: 0; height: 0; visibility: hidden";
frame.onload = function() {
callback(frame.contentDocument);
document.body.removeChild(frame);
}
document.body.appendChild(frame);
}
function createStyleElement(cssText) {
var element = document.createElement("style");
element.type = "text/css";
if (element.styleSheet) {
element.styleSheet.cssText = cssText;
} else {
element.appendChild(document.createTextNode(cssText));
}
return element;
}
})();
On Yosemite (10.10.1) you’ll get:
.system-font {
font-family: '.HelveticaNeueDeskInterface-Regular';
}
Weird name, but it works.
In 10.9 you get:
.system-font {
font-family: '.LucidaGrandeUI';
}
Again, weird name, but works.
Tab Atkins suggested that CSS should be able to do this with essentially font-family: sans-serif;
, but it just doesn’t work that way unfortunately. You’d get Helvetica in 10.9, not Lucida Grande.
Tab also suggested trying @font-face. By setting multiple local()
values for the src
property, it works!
@font-face {
font-family: "MacSystem";
src:
local(".HelveticaNeueDeskInterface-Regular"),
local(".LucidaGrandeUI");
}
body {
font-family: "MacSystem", sans-serif;
}
The trick there is to pick the “rarest” one first. As I type this on Yosemite 10.10.1, it font-family: ".LucidaGrandeUI";
works, so it has to be second in the list for this to work.
You could extend this idea to Linux and Windows and stuff too, I’m sure, and make a big ol’ @font-face
declaration that covers all the ground. If anyone is so inclined, feel free to post it and I’ll link it up here.
Update September 2016
Lots of sites are starting to use a local font stack that tries to use whatever the “system font” is. That font stack looks like:
Used by GitHub:
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}
Used by WordPress admin and Medium interface:
body {
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
}
Will the iframe/button return the default system font or the one the user defined as default on its system? Actually I never looked at whether browsers respected that setting. If the goal is to get this ser defined font the @font-face may not work.
Isn’t that what the generic “sans-serif” setting is for?
Yeah – I mentioned above that’s what Tab Atkins said too. The trouble is it just doesn’t work in the use case for OS X 10.9 and down. The system font there is Lucida Grande, and what you get on a Mac browser is Helvetica.
What am I missing here? Can’t all this be accomplished with a complex font tag?
The issue would be that you could have “HelveticaNeue-Light” on an OS X 10.9 machine, and then you’d get that, not the true system font. As I said at the top, I’m not sure exactly what the use case is and I’m sure it’s not super common, but that was the goal and the goal was met. CSS Tricks, if you will.
Cool tips …. by the ways love the new theme
I’m not sure what the browser/OS support is for this, but according to: http://www.sitepoint.com/css-system-styles/
you could use:
Nice, Kemie.
This appears to work on MacOSX after a quick dev tools trial, anyone else care to try this out?
Nice! This looks far more appropriate for the occasion instead of the user agent, although that’s also a neat way to do it.
Using this for harnessing the OS system fonts seems to be covered on CSS-Tricks already too:
I’m now confused on which source of information is more ideal for the topic haha.
I thought from the headline you would be discussing how to get OS specific fonts of multiple systems… but apparently by ‘OS Specific Fonts’ you mean ‘Mac Specific Fonts’….
As I said at the bottom, the concepts used here to figure it all out could be extended to any system or any combination of system. The use case isn’t my own, so I didn’t dig super deep into it, but if you feel like doing that definitely go for it.
90 % of world population got a worse problem, that we do not use just basic ISO characters, but UTF characters, and e.g. if i would like to write something with “ěščřžýáíé” characters, it may look unreadable on one computer and OK on the other, as one font name might support these chars on one computer, but do not support them on the other.
So it would need some detection of caron or acute above the defined characters for the defined font on user browser, or it would always be easier and certain for me to simple define a custom webfont.
Browsers will use a fallback font (even one not specified in the CSS font stack) if the glyph they need is not in the specified font.
I’d guess most modern OSes come with fonts covering a wide range of characters, so the worst you’re likely to get is some characters rendering in a different font to the surrounding text (I had this problem on my blog when writing about Maltese places).
This technique might also be particularly useful in cases where a web font is served, but said font renders poorly under such-and-such OS.
For example, imagine a situation where a client’s established branding requires the use of some non-web-optimized Georgia clone called “Foobar”. Perhaps under Windows “Foobar” looks fine, but under OS X the weight is so thin as to be illegible. Using this technique, we could serve Georgia to OS X users instead.
I’m pretty sure this is webkit only (I haven’t tested it anywhere else) but setting the value of
font-family
tosystem-font
will actually give you whatever the system font is:I actually found this by viewing the source on Safari’s “You’re Not Connected To The Internet Connection” page.
I just use font anti-aliased and font-family: sans-serif, for the least problems.