7

So, here's my problem: I'm creating a website where I've some posts. In those posts, I put a "::first-letter" highlighting to make it bigger, and it works perfectly.

But, when I'm going to load a post with first letter as a Unicode Emoticon that is a UTF-8 mb4 (2 Unicode Chars), it fails, by trying to load the single char as 2 separated, so the result is something strange.

This is a screenshot:

Error with unicode and ::first-letter

How can you see, there's a bigger letter and one smaller that are unknown, and then the same emoticon visible, because I created a post with the same emoticons wrote down 2 times.

.first_letter_post::first-letter {
  float: left;
  padding-right: 20px;
  padding-top: 0px;
  margin-bottom: -15px;
  margin-top: -10px;
  font-size: 50px;
  font-weight: bold;
  text-transform: uppercase;
}
<p class="first_letter_post">foobar</p>

This is the character: , and I'm using Google Chrome.

I hope someone can help me with this.

shipshape
  • 1,682
  • 2
  • 17
  • 33
Davide I.
  • 302
  • 1
  • 10
  • Can you also post a sample of the character itself, and what browser this is in? Are you sure it's an issue of CSS styling and not the character output itself? – deceze Aug 19 '16 at 15:33
  • I added the character and the browser used. I'm sure about it because if I add a lot of more of that icon after the first one it show without any problem. – Davide I. Aug 19 '16 at 15:35
  • 1
    I've converted this to a live example for you. Indeed, it breaks in Chrome. Works fine in Safari. – deceze Aug 19 '16 at 15:40
  • Hahaha, Chrome. You and your broken emoji support once again. – BoltClock Aug 19 '16 at 15:40
  • Thanks for the Live example. Do you have any idea? – Davide I. Aug 19 '16 at 15:42
  • 3
    On Firefox, ::first-letter does not match the emoji. On IE and Microsoft Edge, ::first-letter *does* match the emoji. I haven't gone through the css-text and css-pseudo-4 specs in depth, but based on what I know Firefox's behavior is conformant to spec, since an emoji technically isn't a letter according to the Unicode spec. – BoltClock Aug 19 '16 at 15:44
  • 1
    I believe there was an explicit remark in the CSS specs somewhere that `::first-letter` doesn't necessary succeed for non-letters. That said, I like IE's result best. – Mr Lister Aug 19 '16 at 16:02
  • 1
    (`utf8mb4` applies only to MySQL; removing from tags.) – Rick James Aug 19 '16 at 18:17
  • @Rick James: :O You have 22,222 rep! – BoltClock Aug 19 '16 at 18:19
  • @BoltClock -- sorta like the odometer rolling over! – Rick James Aug 19 '16 at 18:22
  • @RickJames Thanks, added only to let people understand better what type of emoticon give the problem. – Davide I. Aug 20 '16 at 09:52
  • The "black diamond" could be another issue... See http://stackoverflow.com/a/38363567/1766831 – Rick James Aug 20 '16 at 15:24

1 Answers1

2

Chrome has a long know history of problems with unicode [bug]. This issues is a combination of those problems:

  1. Failing to correctly recognize symbols consisting of more than 3 bytes.
  2. Styling symbols regardless of being a letter unit

This results in Chrome tearing a single symbol apart.

IE is correctly recognizing unicode symbols consisting of multiple codepoints and applies the styling regardless of the spec stating that ::first-letter should be applied to typographic letter units only.

Firefox behaves very strict to the spec, not applying styles to non-letter units. I could not determine whether the Alphanumeric Supplement Space should be treated as letter as well, but Firefox is not treating them as such.

This means, that you should refrain from using ::first-letter when you are heavily relying on it and know that those characters might occur.

A possible solution I could think of, is manually detecting the first character via javascript and wrapping it in a tag and then apply the styles. My solution is a bit messy due to the hard coded hex value, but it might be sufficient.

// manually wrapping the "first character"
Array.prototype.forEach.call(document.querySelectorAll("div"),function(el){wrapFirstChar(el)});

function wrapFirstChar(div){
  let content = div.innerHTML,chars=content.charCodeAt(0) >= 55349?2:1;
  div.innerHTML = "<span>"+content.substring(0,chars)+"</span>"+content.substring(chars);
}

// this is what javascript sees at the first two positions of the string
//Array.prototype.forEach.call(document.querySelectorAll("p"),(e)=>console.log(e.innerHTML.charCodeAt(0)+"+"+e.innerHTML.charCodeAt(1)));
p::first-letter {
  font-weight: bold;
  color:red;
}
span {
  font-weight: bold;
  color:blue;
}
p{
margin:0;
}
<h2>using ::first-letter</h2>
<p> 4 bytes symbol</p>
<p> Enclosed Alphanumeric Supplement 1F170</p>
<p> Mathematical Alphanumeric Symbols 1D7B9</p>
<p> Arabic Mathematical Alphabetic Symbols 1EE00</p>
<p>a normal character (1 byte)</p>

<h2>manually replaced</h2>
<div> 4 bytes symbol</div>
<div> Enclosed Alphanumeric Supplement 1F170</div>
<div> Mathematical Alphanumeric Symbols 1D7B9</div>
<div> Arabic Mathematical Alphabetic Symbols 1EE00</div>
<div>a normal character (1 byte)</div>
Christoph
  • 50,121
  • 21
  • 99
  • 128
  • Firefox is not "failing" here - it's conforming to spec. Emoji are neither considered typographic letter units by css-text's definition, nor punctuation (as Unicode classifies them as Symbols) and therefore ::first-letter will not match *anything* if the first formatted line begins with an emoji. I suspect by "correctly" you meant "as I would expect" (in a similar vein to Mr Lister's comment on the question), since IE's behavior of treating emoji as typographic letter units is *incorrect*. – BoltClock Mar 02 '17 at 03:04
  • Thanks for your comment. I corrected/clarified my answer accordingly. – Christoph Mar 02 '17 at 17:14