Terence Eden
Terence Eden

Reputation: 14334

CSS reference to phone's Emoji font?

I want to use this specific emoji in my web page 🔍 - 🔍

On Android, the browser recognises the Unicode glyph as an Emoji, and displays.

enter image description here

On the desktop it renders as a Unicode fallback character - a little square with numbers in.

So, using Symbola, Quivira, or any other font with the right symbols, I can fix that.

@font-face {
    font-family: 'quiviraregular';
    src: url('fonts/quivira.woff2') format('woff2');
}

body{
 font-family:sans-serif,quiviraregular;
}

However, now Android displays the symbol from the font, not the Emoji.

Is there any way to set the font-family declaration so that Android (and iPhone) will use their in-built colour Emoji and all other browsers will use the provided symbol font?

Upvotes: 12

Views: 4752

Answers (4)

Boldewyn
Boldewyn

Reputation: 82814

The problem with the sans-serif generic family is, that the system font usually has black-and-white glyphs for the usual single-code-point emojis. Especially, if those emojis are somehow related to old Wingdings symbols.

The browser will pick those up then, ignore any following U+FE0F, and happily show that glyph.

There is a new generic font family named emoji that can be used to remedy this problem and that selects the system’s color emoji font, if any is installed. Unfortunately, the support for this new value is not too good at the moment, therefore prefacing it with usually installed system emoji fonts is still a good idea.

It can then be followed by an own fallback font, that is used if no system emoji font was found. The font-family declaration would then look somewhat like this:

font-family: "Some System Font",
             "Emoji Font for other System",
             emoji,
             "quiviraregular";

A site that uses this technique is the Emojipedia.

Upvotes: 1

Thomas
Thomas

Reputation: 394

I took @ckuijjer's answer and ran with it. I found that position 16,16 was not reliable in IE11. Position 14,16 had problems in FireFox. But so far Position 12,16 has worked everywhere I tried it (IE11, Edge, FF42+, Chrome; Windows7, Windows10, OSX10.11).

export default class EmojiUtils {
  static checkEmoji(char) {
    var node = document.createElement('canvas');
    var ctx = node.getContext('2d');
    ctx.fillStyle = '#f00';
    ctx.textBaseline = 'top';
    ctx.font = '32px Arial';
    ctx.fillText(char, 0, 0);
    return ctx.getImageData(12, 16, 1, 1).data[0] !== 0;
  }

  static checkColor() {
    var node = document.createElement('canvas');
    var ctx = node.getContext('2d');
    ctx.fillStyle = '#000';
    ctx.textBaseline = 'top';
    ctx.font = '32px Arial';
    ctx.fillText('\uD83D\uDD34', 0, 0);
    var data = ctx.getImageData(16, 16, 1, 1).data;
    return data[0] > data[1];
  }

  static polyfillEmoji() {
    if (!EmojiUtils.checkColor()) {
      require('emojione/font.css');
    } else {
      if (!EmojiUtils.checkEmoji('\uD83D\uDD7A')) { //Dancing man Unicode 9.0
        require('emojione/font-versioned.css');
      }
    }
  }
}

I then simply have to call EmojiUtils.polyfillEmoji() when the page loads.

I added to my body font-family: "EmojiOneColor", "EmojiOneColor-9", sans-serif;

font.css:

@font-face {
  font-family: 'EmojiOneColor';
  font-style: normal;
  font-weight: 400;
  src: local('EmojiOneColor'), local('EmojiOne Color'), url(emojione-svg.woff);
  unicode-range: U+0020, U+0023, U+002A, U+0030-0039, U+00A9, U+00AE, U+200D, U+203C, U+2049, U+20E3, U+2122, U+2139, U+2194-2199, U+21A9-21AA, U+231A-231B, U+2328, U+23CF, U+23E9-23F3, U+23F8-23FA, U+24C2, U+25AA-25AB, U+25B6, U+25C0, U+25FB-25FE, U+2600-2604, U+260E, U+2611, U+2614-2615, U+2618, U+261D, U+2620, U+2622-2623, U+2626, U+262A, U+262E-262F, U+2638-263A, U+2648-2653, U+2660, U+2663, U+2665-2666, U+2668, U+267B, U+267F, U+2692-2694, U+2696-2697, U+2699, U+269B-269C, U+26A0-26A1, U+26AA-26AB, U+26B0-26B1, U+26BD-26BE, U+26C4-26C5, U+26C8, U+26CE-26CF, U+26D1, U+26D3-26D4, U+26E9-26EA, U+26F0-26F5, U+26F7-26FA, U+26FD, U+2702, U+2705, U+2708-270D, U+270F, U+2712, U+2714, U+2716, U+271D, U+2721, U+2728, U+2733-2734, U+2744, U+2747, U+274C, U+274E, U+2753-2755, U+2757, U+2763-2764, U+2795-2797, U+27A1, U+27B0, U+27BF, U+2934-2935, U+2B05-2B07, U+2B1B-2B1C, U+2B50, U+2B55, U+3030, U+303D, U+3297, U+3299, U+FE0F, U+1F004, U+1F0CF, U+1F170-1F171, U+1F17E-1F17F, U+1F18E, U+1F191-1F19A, U+1F1E6-1F1FF, U+1F201-1F202, U+1F21A, U+1F22F, U+1F232-1F23A, U+1F250-1F251, U+1F300-1F321, U+1F324-1F393, U+1F396-1F397, U+1F399-1F39B, U+1F39E-1F3F0, U+1F3F3-1F3F5, U+1F3F7-1F4FD, U+1F4FF-1F53D, U+1F549-1F54E, U+1F550-1F567, U+1F56F-1F570, U+1F573-1F57A, U+1F587, U+1F58A-1F58D, U+1F590, U+1F595-1F596, U+1F5A4-1F5A5, U+1F5A8, U+1F5B1-1F5B2, U+1F5BC, U+1F5C2-1F5C4, U+1F5D1-1F5D3, U+1F5DC-1F5DE, U+1F5E1, U+1F5E3, U+1F5E8, U+1F5EF, U+1F5F3, U+1F5FA-1F64F, U+1F680-1F6C5, U+1F6CB-1F6D2, U+1F6E0-1F6E5, U+1F6E9, U+1F6EB-1F6EC, U+1F6F0, U+1F6F3-1F6F6, U+1F910-1F91E, U+1F920-1F927, U+1F930, U+1F933-1F93A, U+1F93C-1F93E, U+1F940-1F945, U+1F947-1F94B, U+1F950-1F95E, U+1F980-1F991, U+1F9C0;
}

font-versioned.css:

@font-face {
  font-family: 'EmojiOneColor-9';
  font-style: normal;
  font-weight: 400;
  src: local('EmojiOneColor'), local('EmojiOne Color'), url(emojione-svg.woff);
  unicode-range: U+1F57A, U+1F5A4, U+1F6D1-1F6D2, U+1F6F4-1F6F6, U+1F919-1F91E, U+1F920-1F927, U+1F930, U+1F933-1F93A, U+1F93C-1F93E, U+1F940-1F945, U+1F947-1F94B, U+1F950-1F95E, U+1F985-1F991, U+1F57A, U+1F5A4, U+1F6D1-1F6D2, U+1F6F4-1F6F6, U+1F919-1F91E, U+1F920-1F927, U+1F930, U+1F933-1F93A, U+1F93C-1F93E, U+1F940-1F945, U+1F947-1F94B, U+1F950-1F95E, U+1F985-1F991, U+1F57A, U+1F919-1F91C, U+1F91E, U+1F926, U+1F930, U+1F933-1F939, U+1F93D-1F93E;
}

This has the benefit that if chrome natively has color emojis, it will only load the Unicode 9 characters. The loaded ones will be black and white because of limitations in chrome. On other systems with black and white native emoji it loads the whole emoji font. unicode-range on that version still makes sure the font is only loaded if emoji exist on the page (Brief tofu is possible on first load).

Upvotes: 1

ckuijjer
ckuijjer

Reputation: 13814

Modernizr has a (non core) feature detection for emoji support. If you create your own build of Modernizr with cssclasses and emoji you can use the .no-emoji class that's added to the html tag to have a different font-family.

body {
    font-family: sans-serif;
}

.no-emoji body {
    font-family: sans-serif, emojisymbols;
}

I've created a jsFiddle that shows a green background if you have emoji support, and a red background and Kenichi Kaneko's EmojiSymbols font if haven't got emoji support. Unfortunately I could only see the EmojiSymbols font work on Windows 7 + Firefox, but this might give you a start.

Update based on comment

To see that it is possible to detect a specific character is supported lets take a look at Modernizr's feature detection source code for emoji support:

define(['Modernizr', 'createElement', 'test/canvastext'], function( Modernizr, createElement ) {
  Modernizr.addTest('emoji', function() {
    if (!Modernizr.canvastext) return false;
    var node = createElement('canvas'),
    ctx = node.getContext('2d');
    ctx.fillStyle = '#f00';
    ctx.textBaseline = 'top';
    ctx.font = '32px Arial';
    ctx.fillText('\ud83d\udc28', 0, 0); // U+1F428 KOALA
    return ctx.getImageData(16, 16, 1, 1).data[0] !== 0;
  });
});

it works by creating a canvas element, rendering the 🐨 koala emoji and checking the red color channel value of a specific pixel, which I'm assuming exists in every rendition of this emoji.

You can extend this function to check if all emoji in a parameter list are supported, and if not use the emoji font fallback. I think that the best solution would be to render every emoji in the same canvas and to check if there is at least one pixel colored inside the bounding box of each emoji. I'm not sure how a unsupported emoji is rendered, it might be needed to detect this.

P.S. As I would assume that specifying the default font from which the emoji come before the fallback emoji font should be enough to solve this, am I correct in assuming that these emoji don't exist in a default font?

Upvotes: 5

SW4
SW4

Reputation: 71230

One approach would be to use media-queries to target devices with no native support (such as desktop PCs), and apply the custom font-family, e.g. (very broadly speaking, you will want to refine the below)

// target devices with 'larger than mobile' screen size...and apply the emoji 'polyfill'
@media only screen and (min-device-width: 768px){
   body{
      font-family:quiviraregular;
   }
}

Upvotes: 1

Related Questions