JSFiddle - React, Tailwind, and code Playground
HTML
<h1>Original</h1>
<div>
<p>色(いろ)は匂(にほ)へど<br>
散(ち)りぬるを。</p>
<p>我(わ)が世(よ)誰(たれ)ぞ<br>
常(つね)ならむ。</p>
<p>有為(うゐ)の奥山(おくやま)<br>
今日(けふ)越(こ)えて<br>
浅(あさ)き夢(ゆめ)見(み)じ<br>
酔(ゑ)ひもせず。</p>
</div>
<h1>Original with furigana</h1>
<div class="furigana horizontal">
<p>色(いろ)は匂(にほ)へど<br>
散(ち)りぬるを。</p>
<p>我(わ)が世(よ)誰(たれ)ぞ<br>
常(つね)ならむ。</p>
<p>有為(うゐ)の奥山(おくやま)<br>
今日(けふ)越(こ)えて<br>
浅(あさ)き夢(ゆめ)見(み)じ<br>
酔(ゑ)ひもせず。</p>
</div>
<h1>Vertically typeset</h1>
<div class='furigana vertical'>
<p>色(いろ)は匂(にほ)へど<br>
散(ち)りぬるを。</p>
<p>我(わ)が世(よ)誰(たれ)ぞ<br>
常(つね)ならむ。</p>
<p>有為(うゐ)の奥山(おくやま)<br>
今日(けふ)越(こ)えて<br>
浅(あさ)き夢(ゆめ)見(み)じ<br>
酔(ゑ)ひもせず。</p>
</div>
CSS
div {
margin: 0px;
padding: 0px;
background-color: #CCC;
}
div.vertical {
background-color: #E5B07D;
}
.ruby {
display: inline-table;
/* doesn't work in Opera, because vertical-align
has been broken for years */
vertical-align: bottom;
}
.rb {
display: table-row;
}
.rt {
display: table-row;
font-size: 60%;
text-align: center;
}
.rotated {
display: inline-block;
position: relative;
width: 1em;
height: 1em;
text-align: center;
}
.clockwise {
transform: rotate(90deg);
-moz-transform: rotate(90deg);
-webkit-transform: rotate(90deg);
-o-transform: rotate(90deg);
-ms-transform: rotate(90deg);
}
.cclockwise {
transform: rotate(-90deg);
-moz-transform: rotate(-90deg);
-webkit-transform: rotate(-90deg);
-o-transform: rotate(-90deg);
-ms-transform: rotate(-90deg);
}
.tlorigin {
transform-origin: 0% 0%;
-moz-transform-origin: 0% 0%;
-webkit-transform-origin: 0% 0%;
-o-transform-origin: 0% 0%;
-ms-transform-origin: 0% 0%;
}
.horigin {
transform-origin: 50% 50%;
-moz-transform-origin: 50% 50%;
-webkit-transform-origin: 50% 50%;
-o-transform-origin: 50% 50%;
-ms-transform-origin: 50% 50%;
}
JavaScript
/**
Maybe browsers will be able to typeset text vertically (with opentype
features applied to fonts) in 2012. It's unlikely, so in the mean time
let's "fake" vertical writing with rotations. LOTS of rotations.
It's a good thing clients have JavaScript. If we had to form this
HTML at the server it would be a truly ridiculous amount of data
to send back to the client.
- Mike "Pomax" Kamermans
nihongoresources.com
**/
/**
* turn 日本語(にほんご) into characters with
* guide text on top.
*/
function typesetFurigana(text)
{
return text.replace(/([\u4E00-\u9FFF]+)\(([^\)]+)\)/g,"<span class='ruby'><span class='rt'>$2</span><span class='rb'>$1</span></span>");
}
/**
* Special function to replace certain characters that actually
* change glyph in vert/vrt2 mode.
*/
function replaceVerticalCJKChars(text)
{
text = text.replace(/「/g,'﹁');
text = text.replace(/」/g,'﹂');
text = text.replace(/、/g, '︑');
text = text.replace(/。/g,'︒');
text = text.replace(/…/g,'⁝');
return text;
}
/**
* Run through all downstream nodes for some element,
* and rotate all text node letters by 90 degrees
* (individually!)
*/
function rotateAllText(element)
{
var children=element.childNodes, e=children.length, c, child, text;
// snapshoot children, because childNodes is a live list!
var nc = [];
for(c=0; c<e; c++) { nc[c] = children[c]; }
children = nc;
for(c=0; c<e; c++) {
child = children[c];
if(child.nodeType !== 3) {
// not a textnode -> recurse
rotateAllText(child);
} else {
// text node -> split and rotate every letter individually
text = child.nodeValue.trim();
// tokenise on উ, which cannot be verticalised
text = text.replace(/([\u0020-\u9FFF])/g,"$1উ");
text = text.split("উ");
for(var t=0, et=text.length; t<et; t++) {
if(text[t].trim()=="") continue;
span =...