HTML Ruby Markup Extensions

Unofficial Draft 25 February 2013

Latest editor's draft:
http://darobin.github.com/html-ruby/
Editor:
Robin Berjon, W3C

Abstract

The ruby markup model currently described in the HTML specification is limited in its support for a number of features, notably jukugo and double-sided ruby, as well as inline ruby. This specification addresses these issues by introducing new elements and changing the ruby processing model. Specific care has been taken to ensure that authoring remains as simple as possible.

This document was largely developed to address the shortcomings listed in Use Cases & Exploratory Approaches for Ruby Markup.

Status of This Document

This document is merely a public working draft of a potential specification. It has no official standing of any kind and does not represent the support or consensus of any standards organisation.

This specification is a proposed extension specification to HTML and has no official standing whatsoever.

Table of Contents

1. Changes compared to the current ruby model

The following changes are made to HTML by this document:

2. The ruby element

Categories:
Flow content.
Phrasing content.
Palpable content.
Contexts in which this element can be used:
Where phrasing content is expected.
Content model:
See prose.
Content attributes:
Global attributes
DOM interface:
Uses HTMLElement.

The ruby element allows one or more spans of phrasing content to be marked with ruby annotations. Ruby annotations are short runs of text presented alongside base text, primarily used in East Asian typography as a guide for pronunciation or to include other annotations. In Japanese, this form of typography is also known as furigana.

The content model of ruby elements consists of one or more of the following sequences:

  1. One or more phrasing content nodes or rb elements.
  2. One or the other of the following:
    • One or more rt or rtc elements
    • An rp element followed by one or more rt or rtc elements, followed by an rp element.

The ruby, rb, rtc, and rt elements can be used for a variety of kinds of annotations, including in particular (though by no means limited to) those described below. For more details on Japanese Ruby in particular, and how to render Ruby for Japanese, see Requirements for Japanese Text Layout. [JLREQ]

Note

At the time of writing, CSS does not yet provide a way to fully control the rendering of the HTML ruby element. It is hoped that CSS will be extended to support the styles described below in due course.

Mono-ruby for individual base characters

Annotations (the ruby text) are associated individually with each ideographic character (the base text). In Japanese this is typically hiragana or katakana characters used to provide readings of kanji characters.

<ruby>B<rt>annotation</ruby>

When no rb element is used, the base is implied, as above. But you can also make it explicit. This can be useful notably for styling, or when consecutive bases are to be treated as a group, as in the jukugo ruby example further down.

<ruby><rb>base<rt>annotation</ruby>

In this example, notice how each annotation corresponds to a single base character.

<ruby>日<rt>に</rt></ruby><ruby>本<rt>ほん</rt></ruby>
<ruby>語<rt>ご</rt></ruby>で<ruby>書<rt>か</rt></ruby>
いた<ruby>作<rt>さく</rt></ruby><ruby>文<rt>ぶん</rt></ruby>です。

Ruby text interspersed in regular text provides structure akin to the following image:

An example of ruby text mixed up with regular text.

This example can also be written as follows, using one ruby element with two segments of base text and two annotations (one for each) rather than two back-to-back ruby elements each with one base text segment and annotation (as in the markup above):

<ruby>日<rt>に</rt>本<rt>ほん</rt>語<rt>ご</rt></ruby>で<ruby>書<rt>か</rt></ruby>
 いた<ruby>作<rt>さく</rt>文<rt>ぶん</rt></ruby>です。
Group ruby

Group ruby is often used where phonetic annotations don't map to discreet base characters, or for semantic glosses that span the whole base text. For example, the word "today" is written with the characters 今日, literally "this day". But it's pronounced きょう (kyou), which can't be broken down into a "this" part and a "day" part. In typical rendering, you can't split text that is annotated with group ruby; it has to wrap as a single unit onto the next line. When a ruby text annotation maps to a base that is comprised of more than one character, then that base is grouped.

Group ruby example with きょう annotating 今日

Which can be marked up as follows:

<ruby>今日<rt>きょう</ruby>
Jukugo ruby

Jukugo refers to a Japanese compound noun, ie. a word made up of more than one kanji character. Jukugo ruby is a term that is used not to describe ruby annotations over jukugo text, but rather to describe ruby with a behaviour slightly different from mono or group ruby. Jukugo ruby is similar to mono ruby, in that there is a strong association between ruby text and individual base characters, but the ruby text is typically rendered as grouped together over multiple ideographs when they are on the same line.

The distinction is captured in this example:

Example of jukugo ruby

Which can be marked up as follows:

<ruby>法<rb>華<rb>経<rt>ほ<rt>け<rt>きょう</ruby>

In this example, each rt element is paired with its respective rb element, the difference with an interleaved rb/rt approach being that the sequences of both base text and ruby annotations are implicitly placed in common containers so that the grouping information is captured.

Note

For more details on Jukugo Ruby rendering, see Appendix F in the Requirements for Japanese Text Layout [JLREQ] and Use Case C: Jukugo ruby in the Use Cases & Exploratory Approaches for Ruby Markup [RUBY-UC].

Inline ruby

In some contexts, for instance when the font size or line height are too small for ruby to be readable, it is desirable to inline the ruby annotation such that it appears in parentheses after the text it annotates. This also provides a convenient fallback strategy for user agents that do not support rendering ruby annotations.

Inlining takes grouping into account. For example, Tokyo is written with two kanji characters, 東, which is pronounced とう, and 京, which is pronounced きょう. Each base character should be annotated individually, but the fallback should be 東京(とうきょう) not 東(とう)京(きょう). This can be marked up as follows:

<ruby>東<rb>京<rt>とう<rt>きょう</ruby>

Note that the above markup will enable the usage of parentheses when inlining for browsers that support ruby layout, but for those that don't it will fail to provide parenthetical fallback. This is where the rp element is useful. It can be inserted into the above example to provide the appropriate fallback when ruby layout is not supported:

<ruby>東<rb>京<rp>(</rp><rt>とう<rt>きょう<rp>)</rp></ruby>
Text with both phonetic and semantic annotations (double-sided ruby)

Sometimes, ruby can be used to annotate a base twice.

In the following example, the Chinese word for San Francisco (旧金山, i.e. “old gold mountain”) is annotated both using pinyin to give the pronunciation, and with the original English.

San Francisco in Chinese, with both pinyin and the original English as annotations.

Which is marked up as follows:

<ruby><rb>旧<rb>金<rb>山<rt>jiù<rt>jīn<rt>shān<rtc>San Francisco</ruby>

In this example, a single base run of three base characters is annotated with three pinyin ruby text segments in a first (implicit) container, and an rtc element is introduced in order to provide a second single ruby text annotation being the city's English name.

We can also revisit our jukugo example above with 上手 ("skill") to show how it can be annotation in both kana and romaji phonetics while at the same time maintaining the pairing to bases and annotation grouping information.

上手 ("skill") annotated in both kana and romaji, shown in both jukugo and mono styles.

Which is marked up as follows:

<ruby><rb>上<rb>手<rt>じよう<rt>ず<rtc><rt>jou<rt>zu</ruby>

Text that is a direct child of the rtc element implicitly produces a ruby text segment as if it were contained in an rt element. In this contrived example, this is shown with some symbols that are given names in English and French with annotations intended to appear on either side of the base symbol.

<ruby>
  ♥<rt>Heart<rtc lang=fr>Cœur</rtc>
  ☘<rt>Shamrock<rtc lang=fr>Trèfle</rtc>
  ✶<rt>Star<rtc lang=fr>Étoile
</ruby>

Similarly, text directly inside a ruby element implicitly produces a ruby base as if it were contained in an rb element, and rt children of ruby are implicitly contained in an rtc container. In effect, the above example is equivalent (in meaning, though not in the DOM it produces) to the following:

<ruby>
  <rb>♥</rb><rtc><rt>Heart</rt></rtc><rtc lang=fr><rt>Cœur</rt></rtc>
  <rb>☘</rb><rtc><rt>Shamrock</rt></rtc><rtc lang=fr><rt>Trèfle</rt></rtc>
  <rb>✶</rb><rtc><rt>Star</rt></rtc><rtc lang=fr><rt>Étoile</rt></rtc>
</ruby>

Within a ruby element, content is parcelled into a series of ruby segments. Each ruby segment is described by:

Each ruby text container is described by zero or more ruby text annotations each of which is a DOM range that may contain text nodes or an rt element, and an annotations range that is a range including all the annotations for that container.

Furthermore, a ruby element contains ignored segments. Ignored segments do not form part of the document's semantics (they consist of some inter-element whitespace and rp elements, the latter of which are used for legacy user agents that do not support ruby at all).

Each ruby text annotation in each ruby text container of a ruby segment is paired with the base at the same offset. If there are more bases than ruby text annotations for a given container, then its last annotation spans the remaining bases. If there are more ruby text annotations than there are bases, they are not paired.

Informally, the segmentation and categorisation algorithm below performs a simple set of tasks. First it processes adjacent rb elements, text nodes, and non-ruby elemtns into a list of bases. Then it processes up to two rtc elements or sequences of rt elements that are considered to automatically map to an "anonymous rtc". Put together these data items form a ruby segment as detailed in the data model above. It will continue to produce such segments until it reaches the end of the content of a given ruby element. The complexity of the algorithm below compared to this informal description stems from the need to support an author-friendly syntax and being mindful of inter-element white space.

At any particular time, the segmentation and categorisation of content of a ruby element is the result that would be obtained from running the following algorithm:

  1. Let root be the ruby element for which the algorithm is being run.
  2. Let index be 0.
  3. Let ruby segments be an empty list.
  4. Let current bases be an empty list of DOM ranges.
  5. Let current bases range be null.
  6. Let current bases range start be null.
  7. Let current annotations be an empty list of DOM ranges.
  8. Let current annotations range be null.
  9. Let current annotations range start be null.
  10. Let current annotation containers be an empty list.
  11. Let current automatic base nodes be an empty list of DOM Nodes.
  12. Let current automatic base range start be null.
  13. If root has a ruby element ancestor, then abort these steps.
  14. Process a ruby child: If index is equal to or greater than the number of child nodes in root, then run the steps to commit a ruby segment, return ruby segments, and abort these steps.
  15. Let current child be the indexth node in root.
  16. If current child is not a Text node and is not an Element node, then increment index by one and jump to the step labelled process a ruby child.
  17. If current child is an rp element, then increment index by one and jump to the step labelled process a ruby child.
  18. If current child is an rt element, then run these substeps:
    1. Run the steps to commit an automatic base.
    2. Run the steps to commit the base range.
    3. If current annotation containers contains two elements, then run the steps to commit a ruby segment.
    4. If current annotations is empty, set current annotations range start to the value of index.
    5. Create a new DOM range whose start is the boundary point (root, index) and whose end is the boundary point (root, index plus one), and append it at the end of current annotations.
    6. Increment index by one and jump to the step labelled process a ruby child.
  19. If current child is an rtc element, then run these substeps:
    1. Run the steps to commit an automatic base.
    2. Run the steps to commit the base range.
    3. Run the steps to commit current annotations.
    4. If current annotation containers contains two elements, then run the steps to commit a ruby segment.
    5. Create a new annotation container. It is described by the list of annotations returned by running the steps to process an rtc element and a DOM range whose start is the boundary point (root, index) and whose end is the boundary point (root, index plus one). Append this new annotation container at the end of current annotation containers.
    6. If current annotation containers contains two elements, then run the steps to commit a ruby segment.
    7. Increment index by one and jump to the step labelled process a ruby child.
  20. If current child is a Text node and is inter-element whitespace, then run these substeps:
    1. If current annotations is not empty, increment index by one and jump to the step labelled process a ruby child.
    2. If current annotation containers does not contain two elements, then run the following substeps:
      1. Let lookahead index be set to the value of index.
      2. Peek ahead: Increment lookahead index by one.
      3. If lookahead index is equal to or greater than the number of child nodes in root, then abort these substeps.
      4. Let peek child be the lookahead indexth node in root.
      5. If peek child is a Text node and is inter-element whitespace, then jump to the step labelled peek ahead.
      6. If peek child is an rt element, an rtc element, or an rp element, then set index to the value of lookahead index and jump to the step labelled process a ruby child.
  21. If current annotations is not empty or if current annotation containers is not empty, then run the steps to commit a ruby segment.
  22. If current child is an rb element, then run these substeps:
    1. Run the steps to commit an automatic base.
    2. If current bases is empty, then set current bases range start to the value of index.
    3. Create a new DOM range whose start is the boundary point (root, index) and whose end is the boundary point (root, index plus one), and append it at the end of current bases.
    4. Increment index by one and jump to the step labelled process a ruby child.
  23. If current automatic base nodes is empty, set current automatic base range start to the value of index.
  24. Append current child at the end of current automatic base nodes.
  25. Increment index by one and jump to the step labelled process a ruby child.

When the steps above say to commit a ruby segment, it means to run the following steps at that point in the algorithm:

  1. Run the steps to commit an automatic base.
  2. If current bases, current annotations, and current annotation containers are all empty, abort these steps.
  3. Run the steps to commit the base range.
  4. Run the steps to commit current annotations.
  5. Create a new ruby segment. It is described by a list of bases set to current bases, a base DOM range set to current bases range, and a first and second annotation containers that are respectively the first and second items of the current annotation containers list. Append this new ruby segment at the end of ruby segments.
  6. Let current bases be an empty list.
  7. Let current bases range be null.
  8. Let current bases range start be null.
  9. Let current annotation containers be an empty list.

When the steps above say to commit the base range, it means to run the following steps at that point in the algorithm:

  1. If current bases is empty, abort these steps.
  2. If current bases range is not null, abort these steps.
  3. Let current bases range be a DOM range whose start is the boundary point (root, current bases range start) and whose end is the boundary point (root, index).

When the steps above say to commit current annotations, it means to run the following steps at that point in the algorithm:

  1. If current annotations is not empty and current annotations range is null let current annotations range be a DOM range whose start is the boundary point (root, current annotations range start) and whose end is the boundary point (root, index).
  2. If current annotations is not empty, create a new annotation container. It is described by an annotations list set to current annotations and a range set to current annotations range. Append this new annotation container at the end of current annotation containers.
  3. Let current annotations be an empty list of DOM ranges.
  4. Let current annotations range be null.
  5. Let current annotations range start be null.

When the steps above say to commit an automatic base, it means to run the following steps at that point in the algorithm:

  1. If current automatic base nodes is empty, abort these steps.
  2. If current automatic base nodes contains nodes that are not Text nodes, or Text nodes that are not inter-element whitespace, then run these substeps:
    1. It current bases is empty, set current bases range start to the value of current automatic base range start.
    2. Create a new DOM range whose start is the boundary point (root, current automatic base range start) and whose end is the boundary point (root, index), and append it at the end of current bases.
  3. Let current automatic base nodes be an empty list of DOM Nodes.
  4. Let current automatic base range start be null.

3. The rb element

Categories:
None.
Contexts in which this element can be used:
As a child of a ruby element.
Content model:
Phrasing content.
Content attributes:
Global attributes
DOM interface:
Uses HTMLElement.

The rb element marks the base text component of a ruby annotation. When it is the child of a ruby element, it doesn't represent anything itself, but its parent ruby element uses it as part of determining what it represents.

An rb element that is not a child of a ruby element represents the same thing as its children.

4. The rt element

Categories:
None.
Contexts in which this element can be used:
As a child of a ruby or of an rtc element.
Content model:
Phrasing content.
Content attributes:
Global attributes
DOM interface:
Uses HTMLElement.

The rt element marks the ruby text component of a ruby annotation. When it is the child of a ruby element or of an rtc element that is itself the child of a ruby element, it doesn't represent anything itself, but its ancestor ruby element uses it as part of determining what it represents.

An rt element that is not a child of a ruby element or of an rtc element that is itself the child of a ruby element represents the same thing as its children.

5. The rtc element

Categories:
None.
Contexts in which this element can be used:
As a child of a ruby element.
Content model:
Phrasing content.
Content attributes:
Global attributes
DOM interface:
Uses HTMLElement.

The rtc element marks a ruby text container for ruby text components in a ruby annotation. When it is the child of a ruby element it doesn't represent anything itself, but its parent ruby element uses it as part of determining what it represents.

An rtc element that is not a child of a ruby element represents the same thing as its children.

When an rtc element is processed as part of the segmentation and categorisation of content for a ruby element, the following algorithm defines how to process an rtc element:

  1. Let root be the rtc element for which the algorithm is being run.
  2. Let index be 0.
  3. Let annotations be an empty list of DOM ranges.
  4. Let current automatic annotation nodes be an empty list of DOM nodes.
  5. Let current automatic annotation range start be null.
  6. Process an rtc child: If index is equal to or greater than the number of child nodes in root, then run the steps to commit an automatic annotation, return annotations, and abort these steps.
  7. Let current child be the indexth node in root.
  8. If current child is an rt element, then run these substeps:
    1. Run the steps to commit an automatic annotation.
    2. Create a new DOM range whose start is the boundary point (root, index) and whose end is the boundary point (root, index plus one), and append it at the end of annotations.
    3. Increment index by one and jump to the step labelled process an rtc child.
  9. If current automatic annotation nodes is empty, set current automatic annotation range start to the value of index.
  10. Append current child at the end of current automatic annotation nodes.
  11. Increment index by one and jump to the step labelled process an rtc child.

When the steps above say to commit an automatic annotation, it means to run the following steps at that point in the algorithm:

  1. If current automatic annotation nodes is empty, abort these steps.
  2. If current automatic annotation nodes contains nodes that are not Text nodes, or Text nodes that are not inter-element whitespace, then create a new DOM range whose start is the boundary point (root, current automatic annotation range start) and whose end is the boundary point (root, index), and append it at the end of annotations.
  3. Let current automatic annotation nodes be an empty list of DOM nodes.
  4. Let current automatic annotation range start be null.

6. The rp element

Categories:
None.
Contexts in which this element can be used:
As a child of a ruby element, either immediately before or immediately after an rt or rtc element.
Content model:
Phrasing content.
Content attributes:
Global attributes
DOM interface:
Uses HTMLElement.

The rp element can be used to provide parentheses around a ruby text component of a ruby annotation, to be shown by user agents that don't support ruby annotations.

An rp element that is a child of a ruby element represents nothing. An rp element whose parent element is not a ruby element represents its children.

The example above, in which each ideograph in the text 漢字 is annotated with its phonetic reading, could be expanded to use rp so that in legacy user agents the readings are in parentheses (please note that white space has been introduced into this example in order to make it readable):

...
    <ruby>
      漢
      <rb>字</rb>
      <rp> (</rp>
      <rt>かん</rt>
      <rt>じ</rt>
      <rp>) </rp>
    </ruby>
...

In conforming user agents the rendering would be as above, but in user agents that do not support ruby, the rendering would be:

... 漢字 (かんじ) ...

When there are multiple annotations for a segment, rp elements can also be placed between the annotations. Here is another copy of an earlier contrived example showing some symbols with names given in English and French using double-sided annotations, but this time with rp elements as well:

<ruby>
♥<rp>: </rp><rt>Heart</rt><rp>, </rp><rtc><rt lang=fr>Cœur</rt></rtc><rp>.</rp>
☘<rp>: </rp><rt>Shamrock</rt><rp>, </rp><rtc><rt lang=fr>Trèfle</rt></rtc><rp>.</rp>
✶<rp>: </rp><rt>Star</rt><rp>, </rp><rtc><rt lang=fr>Étoile</rt></rtc><rp>.</rp>
</ruby>

This would make the example render as follows in non-ruby-capable user agents:

♥: Heart, Cœur. ☘: Shamrock, Trèfle. ✶: Star, Étoile.

7. Miscellaneous Changes

In Usage summary, add rb and rtc to the table.

Changes are required to [WEBVTT] in order to match this model.

In the parsing algorithm, “A start tag whose tag name is one of: "rp", "rt"” needs to include "rb" and "rtc". Closing tag handling for rt and rp need be complemented with the new elements, and they need to be added to Closing elements that have implied end tags. There are several other occurrences of "rp" and "rt" in the parsing algorithm that need to see these elements added.

The CSS for Phrasing content needs to include style for rb and rtc, and the following prose needs to take them into account. Note that the CSS Ruby Module [CSS3RUBY] is currently in sore need of fixing such that any reliance on it is hazardous at best.

rb needs to be removed from Non-conforming features.

The Elements appendix needs updating to include the new elements, as will the Element interfaces appendix.

8. Acknowledgements

Much of this document is deeply indebted to Fantasai and Richard Ishida.

9. Issues

Issue 1

Indicate document conformance problems better (e.g. more than two rtcs).

Issue 2

Take text-level elements into account rather than just text nodes.

A. References

A.1 Normative references

[RUBY-UC]
Richard Ishida et al. Use Cases & Exploratory Approaches for Ruby Markup. 10 July 2012. W3C Working Draft. URL: http://www.w3.org/TR/ruby-use-cases/

A.2 Informative references

[CSS3RUBY]
Michel Suignard. CSS3 Ruby Module. 14 May 2003. W3C Candidate Recommendation. URL: http://www.w3.org/TR/2003/CR-css3-ruby-20030514
[WEBVTT]
Ian Hickson. WebVTT. 21 August 2012. W3C Working Draft. URL: http://dev.w3.org/html5/webvtt/