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 =...