3

I'm trying to build a little system which highlights combining characters in a different color than regular characters. Take the following example:

* { font-size: 72px }
b { font-weight: normal; color: red }
Te&#x301;st A&#x334; B&#x353; <br/>
Te<b>&#x301;</b>st A<b>&#x334;</b> B<b>&#x353;</b>

I'd like the three combining characters (acute accent, tilde overlay, and x below) to be highlighted in red, but remain precisely where they were if in the original text. The problem is, when I wrap a combining character in an HTML element, it is no longer 'attached' to the base character and instead inline with the rest of the text.

Is there any way to accomplish this with HTML / CSS?

Note: I have reviewed the answers here and here, but they all seem to only attack the problem 'geometrically'—that is they highlight the part of a character within a certain region. This question is specifically about highlight the 'typographical' aspects of the combining characters.

Community
  • 1
  • 1
p.s.w.g
  • 146,324
  • 30
  • 291
  • 331
  • possible duplicate of [Is it possible to apply CSS to half of a character?](http://stackoverflow.com/questions/23569441/is-it-possible-to-apply-css-to-half-of-a-character) – Paulie_D Oct 16 '14 at 15:15
  • 2
    @Paulie_D Not quite a duplicate. That question is about dividing the character 'geometrically'. I'm interested more in the 'typography'. So even in cases where the combining character crosses over the base character (overlay) it would only apply it to the combining character. – p.s.w.g Oct 16 '14 at 15:19
  • 1
    I think this is a duplicate of http://stackoverflow.com/questions/23537441/how-to-display-accents-over-words-with-different-colors-in-html-css – Jukka K. Korpela Oct 16 '14 at 17:14

4 Answers4

1

I am not confident I have completely understood your issue, but here goes. If you simply need the accent to return to the correct position now it is styled separately, you could apply the something similar to:

* { font-size: 72px }
b { font-weight: normal; color: red; position: absolute; top: 52%;}
Te&#x0301;st <br/>
Te<b>&#x0301;</b>st
Martyn0627
  • 741
  • 1
  • 4
  • 17
  • Using `position: absolute` means that the actual position of the acute depends on the *container*. Click the 'Full page' link in the snippet to see what I mean. You could use `position: relative`, but you'd have to tweak that for every character. – p.s.w.g Oct 16 '14 at 16:04
1

Okay I think I've found a partial solution, but it's a bit tricky. Basically, I have to render the entire character, with combining characters, then hide it with another character on top of it without any combining characters:

* { font-size: 72px }
b { font-weight: normal; color: red; width: 0px; overflow: visible; display: inline-block; }
i { font-style: normal; color: black; }
Te&#x0301;st <br/>
T<b>e&#x0301;</b><i>e</i>st

Unfortunately, this gives a very slight red outline to the base character when anti-aliased.

enter image description here

And it won't work for certain overlay characters. In this example, the red bar should be on black d:

* { font-size: 72px }
b { font-weight: normal; color: red; width: 0px; overflow: visible; display: inline-block; }
i { font-style: normal; color: black; }
d&#x336; <br/>
<b>d&#x336;</b><i>d</i>
p.s.w.g
  • 146,324
  • 30
  • 291
  • 331
  • Have you come across a better solution than this? It's very disappointing to me there doesn't seem to be anything. – jcuenod Sep 26 '15 at 03:52
  • @jcuenod I just posted a new answer based on SVG that you might be interested. – p.s.w.g Oct 01 '15 at 04:47
1

I'm revisting this question almost a year later, and I've figured out a more satisfactory (though far more verbose) solution using SVG. Basically, this is the similar to my previous HTML/CSS-based version, but SVG gives you the power to do clip / mask out the anti-aliased edges of the underlying base character.

The only really problem remaining is how to handle overlay characters (where the combining character is rendered directly on top of the underlying character). In this case you'd either need to render the base character on top of the overlay (not preferable in my use case) or render the overlay on an placeholder white space character which will not necessarily perfectly match the width of the base character. Here's a demonstration:

svg text {
  x: 50px;
  y: 50px;
  alignment-baseline: middle;
  text-anchor: middle;
  font-size: 55px;
}
svg .backdrop {
  x: 1px;
  y: 1px;
  rx: 15px;
  ry: 15px;
  width: 98px;
  height: 98px;
  fill: url(#grad);
}
svg .cc-above { fill: #F00; }
svg .cc-below { fill: #00F; }
svg .cc-overlay { fill: #0FF; }
svg .cc-base { fill: #000; }
svg .cc-mask { stroke: #000; stroke-width: 3px; }
.sample { float: left; }
.caption { display: block; text-align: center; }
<div class="sample">
  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="100px" height="100px" viewBox="0 0 100 100">
    <defs>
      <text id="base" x="50" y="50">e</text>
      <linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="100%">
        <stop offset="0%" style="stop-color:#DDD;stop-opacity:1" />
        <stop offset="100%" style="stop-color:#888;stop-opacity:1" />
      </linearGradient>
    </defs>
    <rect class="backdrop" />
    <mask id="mask1">
      <rect x="0" y="0" width="100%" height="100%" fill="#fff" />
      <use xlink:href="#base" class="cc-mask" />
    </mask>
    <text class="cc-above" x="50%" y="50%" mask="url(#mask1)">e&#x0300;</text>
    <use xlink:href="#base" class="cc-base" />
  </svg>
  <div class="caption">Above</div>
</div>

<div class="sample">
  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="100px" height="100px" viewBox="0 0 100 100">
    <defs>
      <text id="base" x="50" y="50">e</text>
      <linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="100%">
        <stop offset="0%" style="stop-color:#DDD;stop-opacity:1" />
        <stop offset="100%" style="stop-color:#888;stop-opacity:1" />
      </linearGradient>
    </defs>
    <rect class="backdrop" />
    <mask id="mask1">
      <rect x="0" y="0" width="100%" height="100%" fill="#fff" />
      <use xlink:href="#base" class="cc-mask" />
    </mask>
    <text class="cc-below" x="50%" y="50%" mask="url(#mask1)">e&#x031F;</text>
    <use xlink:href="#base" class="cc-base" />
  </svg>
  <div class="caption">Below</div>
</div>

<div class="sample">
  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="100px" height="100px" viewBox="0 0 100 100">
    <defs>
      <text id="base" x="50" y="50">e</text>
      <linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="100%">
        <stop offset="0%" style="stop-color:#DDD;stop-opacity:1" />
        <stop offset="100%" style="stop-color:#888;stop-opacity:1" />
      </linearGradient>
    </defs>
    <rect class="backdrop" />
    <mask id="mask1">
      <rect x="0" y="0" width="100%" height="100%" fill="#fff" />
      <use xlink:href="#base" class="cc-mask" />
    </mask>
    <use xlink:href="#base" class="cc-base" />
    <text class="cc-overlay" x="50%" y="50%">&#x2002;&#x0334;</text>
  </svg>
  <div class="caption">Overlay</div>
</div>

<div class="sample">
  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="100px" height="100px" viewBox="0 0 100 100">
    <defs>
      <text id="base" x="50" y="50">e</text>
      <linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="100%">
        <stop offset="0%" style="stop-color:#DDD;stop-opacity:1" />
        <stop offset="100%" style="stop-color:#888;stop-opacity:1" />
      </linearGradient>
    </defs>
    <rect class="backdrop" />
    <mask id="mask1">
      <rect x="0" y="0" width="100%" height="100%" fill="#fff" />
      <use xlink:href="#base" class="cc-mask" />
    </mask>
    <text class="cc-above" x="50%" y="50%" mask="url(#mask1)">e&#x0300;</text>
    <text class="cc-below" x="50%" y="50%" mask="url(#mask1)">e&#x031F;</text>
    <use xlink:href="#base" class="cc-base" />
    <text class="cc-overlay" x="50%" y="50%">&#x2002;&#x0334;</text>
  </svg>
  <div class="caption">All</div>
</div>

I don't have all that much experience with SVG, and someone that does may be able to find a way to improve upon this solution further.

p.s.w.g
  • 146,324
  • 30
  • 291
  • 331
0

This is a hack but what about using text-indent:-1ex to move the accent back 1 character width. http://jsfiddle.net/1tm1Lrrp/4/

In any case none of these suggestions would work with an i because the dot would still be there.

mdf
  • 186
  • 4