<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Mátyás Mustoha</title>
    <description>The latest articles on Forem by Mátyás Mustoha (@mmatyas).</description>
    <link>https://forem.com/mmatyas</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1092518%2F57dc1a88-3b12-4f80-ae66-570213f5a23a.jpeg</url>
      <title>Forem: Mátyás Mustoha</title>
      <link>https://forem.com/mmatyas</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/mmatyas"/>
    <language>en</language>
    <item>
      <title>Pretty &lt;ruby&gt; for CJK languages</title>
      <dc:creator>Mátyás Mustoha</dc:creator>
      <pubDate>Sun, 04 Jun 2023 16:39:36 +0000</pubDate>
      <link>https://forem.com/mmatyas/pretty-for-cjk-languages-4k97</link>
      <guid>https://forem.com/mmatyas/pretty-for-cjk-languages-4k97</guid>
      <description>&lt;p&gt;Recently, I've been experimenting with East Asian typography and with creating print-quality output using HTML and CSS. However, it didn't take long and I noticed something: &lt;em&gt;rubies are ugly!&lt;/em&gt; I haven't really found articles about the topic in English, so here's my attempt at one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wait, what?
&lt;/h2&gt;

&lt;p&gt;If you're not familiar with the name “ruby”, they are small characters above the text, usually for providing pronunciation hints. For example, they can show &lt;em&gt;furigana&lt;/em&gt; for Japanese, or &lt;em&gt;bopomofo&lt;/em&gt; for Chinese, but also Latin letters as well.&lt;/p&gt;

&lt;p&gt;The ruby element consists of a ruby &lt;em&gt;base&lt;/em&gt;, and the ruby &lt;em&gt;text&lt;/em&gt;, that most often sits on its top:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7tZaSpuT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nla8g5g6rwhlmfj688pn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7tZaSpuT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nla8g5g6rwhlmfj688pn.png" alt="Ruby base and ruby text" width="600" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In HTML, we can use the &lt;code&gt;&amp;lt;ruby&amp;gt;&lt;/code&gt; tag to define a whole group, in which &lt;code&gt;&amp;lt;rb&amp;gt;&lt;/code&gt;&lt;sup id="fnref1"&gt;1&lt;/sup&gt; defines the ruby &lt;em&gt;base&lt;/em&gt;, and &lt;code&gt;&amp;lt;rt&amp;gt;&lt;/code&gt; the ruby &lt;em&gt;text&lt;/em&gt;.&lt;sup id="fnref2"&gt;2&lt;/sup&gt; (Spaces added below for readability.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ruby&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ja"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;rb&amp;gt;&lt;/span&gt;東京&lt;span class="nt"&gt;&amp;lt;/rb&amp;gt;&lt;/span&gt;  &lt;span class="nt"&gt;&amp;lt;rt&amp;gt;&lt;/span&gt;とうきょう&lt;span class="nt"&gt;&amp;lt;/rt&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;/ruby&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ruby&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"zh"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;rb&amp;gt;&lt;/span&gt;北京&lt;span class="nt"&gt;&amp;lt;/rb&amp;gt;&lt;/span&gt;  &lt;span class="nt"&gt;&amp;lt;rt&amp;gt;&lt;/span&gt;Běijīng&lt;span class="nt"&gt;&amp;lt;/rt&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;/ruby&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;ruby&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"vi"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;rb&amp;gt;&lt;/span&gt;河內&lt;span class="nt"&gt;&amp;lt;/rb&amp;gt;&lt;/span&gt;  &lt;span class="nt"&gt;&amp;lt;rt&amp;gt;&lt;/span&gt;Hà Nội&lt;span class="nt"&gt;&amp;lt;/rt&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;/ruby&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The naive approach
&lt;/h2&gt;

&lt;p&gt;Now what happens when the ruby text is wider than the ruby base? By default, &lt;code&gt;&amp;lt;ruby&amp;gt;&lt;/code&gt; acts sort of like a single block of text:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7hzFu2V1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4l6x27y35lru9x0iryky.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7hzFu2V1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4l6x27y35lru9x0iryky.png" alt="Default ruby style" width="398" height="75"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In Japanese typography, however, it often looks more pleasant to spread the text over the neighboring characters, without any spacing&lt;sup id="fnref3"&gt;3&lt;/sup&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OrnI0pyC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5p309826onrfw1w0lhca.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OrnI0pyC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5p309826onrfw1w0lhca.png" alt="Pretty ruby style" width="362" height="67"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This could be solved with a little CSS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;take the ruby text out of the regular text flow with &lt;code&gt;position: absolute&lt;/code&gt;, then&lt;/li&gt;
&lt;li&gt;align it horizontally to the center of its parent, with something like &lt;code&gt;left: 50%; transform: translateX(-50%)&lt;/code&gt;, and&lt;/li&gt;
&lt;li&gt;move it to the top with &lt;code&gt;bottom: 100%&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;ruby&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;ruby&lt;/span&gt; &lt;span class="nt"&gt;rt&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-50%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nl"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this works perfectly fine in Firefox, producing the earlier image.&lt;/p&gt;

&lt;p&gt;Unfortunately, the implementation in Chrome and Safari &lt;a href="https://wpt.fyi/results/css/css-ruby"&gt;lags behind&lt;/a&gt; at the moment, and the &lt;code&gt;position&lt;/code&gt; attribute &lt;a href="https://bugs.chromium.org/p/chromium/issues/detail?id=847743"&gt;does not seem to work at all&lt;/a&gt; there.&lt;/p&gt;

&lt;h2&gt;
  
  
  An alternative
&lt;/h2&gt;

&lt;p&gt;If we cannot use the built-in &lt;code&gt;&amp;lt;rt&amp;gt;&lt;/code&gt; element, we could try to replace it with the CSS pseudo-element &lt;code&gt;::before&lt;/code&gt;. If, instead of&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ruby&amp;gt;&lt;/span&gt;東京&lt;span class="nt"&gt;&amp;lt;rt&amp;gt;&lt;/span&gt;とうきょう&lt;span class="nt"&gt;&amp;lt;/rt&amp;gt;&amp;lt;/ruby&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we write&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ruby&lt;/span&gt; &lt;span class="na"&gt;data-rt=&lt;/span&gt;&lt;span class="s"&gt;"とうきょう"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;東京&lt;span class="nt"&gt;&amp;lt;/ruby&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;this stores the ruby text as a custom attribute, which we can access from CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-rt&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nd"&gt;::before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data-rt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and, in addition to the very first styling attempt, to make it look like the original &lt;code&gt;&amp;lt;rt&amp;gt;&lt;/code&gt; tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;ruby&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-rt&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="nd"&gt;::before&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;.5em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result looks visually the same as our first attempt!&lt;/p&gt;

&lt;h2&gt;
  
  
  Sidenotes
&lt;/h2&gt;

&lt;p&gt;The above approach should work for most cases, including vertical writing. Corner cases might appear however if you try to build on top of if. As usual, most of these can be solved with a hint JavaScript code.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shorter text:&lt;/strong&gt; You might also want to spread out the characters if the ruby base is wider than the ruby text. An approach for that is to split the text to individual characters with JavaScript, then spread them with flexbox styling (&lt;code&gt;justify-content: space-around&lt;/code&gt; for example happens to match the Japanese styling specification). However, you cannot target CSS pseudo-elements with JS, so you might need to manually construct a child element for your &lt;code&gt;&amp;lt;ruby&amp;gt;&lt;/code&gt;es.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Body overflow:&lt;/strong&gt; If you want to be very precise, you might want to handle ruby texts flowing out of the body text area, i.e. make the text align to one of the sides.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Overlaps:&lt;/strong&gt; The ruby texts might overlap or touch, though in practice the chance for that shouldn't be too high. If this becomes an issue, you can detect such cases using &lt;code&gt;getBoundingClientRect()&lt;/code&gt;, and add some padding if necessary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compound words:&lt;/strong&gt; If you want to use multiple ruby texts in one single ruby element (eg. per-character pronunciation), you might need to split the ruby elements. If the ruby base can break eg. at line ends, the ruby texts should probably follow that too.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You might also need to do some preprocessing, based on your source text:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;From HTML:&lt;/strong&gt; If your text is in HTML and already uses &lt;code&gt;&amp;lt;ruby&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;rt&amp;gt;&lt;/code&gt;, you can use JavaScript to query all ruby elements, and move the text content from the &lt;code&gt;&amp;lt;rt&amp;gt;&lt;/code&gt; into the data property of the &lt;code&gt;&amp;lt;ruby&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;From Markdown:&lt;/strong&gt; If your text is in Markdown or similar, a common ruby pattern is like this: &lt;code&gt;{東京|とう|きょう}&lt;/code&gt;, that is, &lt;code&gt;{base|text1|text2|...|textN}&lt;/code&gt;, where each &lt;code&gt;text&lt;/code&gt; segment is the reading of a base character.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;From plain text:&lt;/strong&gt; If you have plain text, where the reading is next to the word (eg. &lt;code&gt;東京《とうきょう》&lt;/code&gt;), you can always just replace them with a regular expression, as long as the writing is consistent.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;A nicely typeset page pleases the eye, and often requires just a tiny bit of additional care. If you happen to work with East Asian text a lot, I hope this will help to make your content look even better.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;rb&amp;gt;&lt;/code&gt; tag is actually unnecessary now (you can directly write the text there), but in this example shows the element structure more clearly. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;For a long time, &lt;code&gt;&amp;lt;ruby&amp;gt;&lt;/code&gt; wasn't well supported, so people also used “creative” solutions, like tables for alignment. You might still run into those on some sites. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;See &lt;a href="https://www.w3.org/TR/jlreq/?lang=en"&gt;https://www.w3.org/TR/jlreq/?lang=en&lt;/a&gt; for the whole specification. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>css</category>
      <category>html</category>
      <category>typography</category>
      <category>design</category>
    </item>
  </channel>
</rss>
