This document is a draft specification for the creation and positioning of notes in both continuous and paged media. This specification was written as part of the Pushing Forward for CSS Print project, funded through the NGI0 Commons Fund, established by NLnet. It’s a joint initiative from core contributors of Paged.js (@julientaq, @JulieBlanc) and the WeasyPrint team (@grewn0uille, @liZe). The result of this work can be found on CSS Print Lab’s github.
This draft is based on:
note()
value, that you can use in the content property to declare elements as notes;element()
value to place the element removed from the flow (i.e. the note) in a specific place of the same document,@note-area
at-rule that can be used to display notes in a page (for paged media only);Originally published here : https://github.com/w3c/css-print/issues/3
Discussed in the W3C CSS Working Group repository: https://github.com/w3c/csswg-drafts/issues/12472
For now, the W3C specifications only allow the creation of footnotes in paged media, as described in the CSS Generated Content for Paged Media Module (css-gcpm-3). But we know that designers and editors like to be able like to make use of all kinds of notes. You can find some examples here: https://www.w3.org/XML/2015/02/footnote-examples/. To name a few:
Types of notes | |
---|---|
Footnotes | notes placed at the bottom of the pages (paged media only) |
Side notes | notes related to the page content, grouped in the left or in the right of the page or content, alongside the text area |
End notes | notes grouped together in one place at the end of a document or at the end of a section of the document |
Marginal notes | notes that are placed to one side of a page (or document) or both sides at the exact vertical position of the note reference number |
Column footnotes | footnotes that can be placed according to the column in which they are located |
Multiple notes area | not for pages that share footnotes, margin notes, column footnote, etc. |
A part of the CSS Generated Content for Paged Media Module (css-gcpm-3) is dedicated to footnotes. The first section defines the terms of the footnote objects. These definitions can be applied to all types of notes, thus we’ll keep the same terms but removing the prefix “foot” for the rest of this issue discussion:
Note element: The element containing the content of the note, which will be removed from the flow and displayed as a note.
Note marker: (also known as note number): A number or symbol adjacent to the note body, identifying the particular note. The note marker should use the same number or symbol as the corresponding note call, although the marker may contain additional punctuation.
Note body : The note marker is placed before the note element, and together they represent the note body, which will be placed in the note area.
Note callout (also known as note reference): A number or symbol, found in the main text, which points to the note body.
Note area: The page or document area used to display notes.
Note rule: (also known as note separator): A horizontal rule is often used to separate the note area from the rest of the page. The separator (and the entire note area) cannot be rendered on a page with no notes.
The specification also describes how to create footnotes in a page with the following code:
@page {
@footnote {
float: bottom;
/* Style of footnote area */
}
}
span.footnote {
/* Display span as footnote */
float: footnote;
}
This code also creates the special footnote counter and special pseudo-elements for footnote calls (::footnote-call
) and footnote markers (::footnote-marker
). The position, size and styles of the footnote area are defined by the @footnote
declaration. The specification does not set precise limits, only a couple of paragraphs and a lot of issues and unanswered questions. The advantage is that we can do many proposals without conflicting with the current specifications.
In css-gcpm-3, there is this issue:
Issue: Why is
float: bottom
used with the footnote area? Floating footnotes to the footnote area, and then floating the footnote area itself, seems overly complex, given that implementations don’t allow the footnote area to float anywhere else. Note that some implementations do allow the footnote area to be absolutely positioned. (https://www.w3.org/TR/css-gcpm-3/)
We agree that using double float declaration is complex. But a mechanism is needed to move the note element into a specific area of the page or the document. To do this, we can take inspiration from a mechanism already present in the draft of the paged media specifications, the running element: by adding the position: running()
declaration, we can remove an element from the flow to reuse it in multiple places, perfect for the running heads of a book for example.
For the notes, we’d like to propose a new way to create and place notes, based on new values (notes()
and element()
) for properties already in the CSS specifications (position
and content
).
Notes are always dependent on a principal flow, but they are out-of-flow elements, such as an aside. This is ancillary content that may be moved to the bottom of the page, to its margins, or to another section of the document. A note is created when the content is moved to a specific area of the document, leaving a reference indicator behind.
For all types of notes, we can see a pattern to build them:
The W3C doesn't provide a dedicated way to tag notes in HTML. Some discussions in the mailing list of the W3C or WHATWG, or in blog articles, suggest different ways but none of them seem to have unanimous agreement.
In the meantime, there are two most commonly used methods of adding notes:
href
attrbute pointing to sections of a page with fragmented URLs. This mechanism provides some issues pointed out by Greg Lowney in terms of accessibility. This also poses problems with more complex layouts. If the notes elements are presented in a list, it becomes necessary to transform the DOM to place them in the manner of a side note, end notes or footnotes (both for continuous and/or paged media).<span>
elements directly in paragraphs to encapsulate note elements in the place where they appear. This is the method used in examples in the css-gcpm-3 draft.David MacDonald and Shane McCarron propose specifics <note>
, <notegroup>
and <refnote>
elements in some unofficial specifications. One part of the document presents uses cases and a lot of requirements that are very useful. However, we believe that many of the HTML syntaxes proposed in the document can be best achieved through CSS mechanisms.
Therefore, we propose to use only one note
element.
A note
element represents a note, e.g. a secondary content that is related to other content in the document. It’s a self-contained node that gives the information about where a note starts and where it ends. The note
element must be placed where the note appears in the content flow. This is a new type of HTML element which has the following properties:
<note>
and closing </note>
tags. Any content between those two tags is part of the content of the note.note
element can be the child of any grouping content element or heading element.note
element accepts other block or inline elements such as paragraphs, table, images, list, etc.note
element can be a child of another note
element.<note>
element accepts all the global attributes.<note>
element to read it when they want.The following example is a conforming HTML fragment:
This potential new HTML element is easy to use and allows a note to be always attached to the content it adds details to. This proposal is aligned to the way HTML works (a node mechanism) without adding an HTML element that would depend on another one (to create note references for example).
Until this element exists in HTML, a span
element will be used with a class named “note” for illustrative purposes.
A mechanism is needed to move the note element into a specific area of the page or of the document, and leaving a reference indicator in its place. To do this, we add the note()
value to the position property and the element()
value to the content property.
The note()
function removes the element (and associated ::before
and ::after
pseudo-elements) from the principal flow, and makes it available to place in a page margin-box, a page note-area @note-area
or a ::note-area
pseudo element using element()
. The element inherits from its original position in the document, but is not rendered there, instead a ::note-call
pseudo-element is created and inserted in the original position of the note. The elements keep their defined styles.
A custom identifier is required: note(<custom-ident>)
. If there is no element()
value corresponding to the custom identifier of the note()
value, the elements are not removed from the flow and are shown as inline note
elements.
To place the elements removed from the flow in a specific place on the document or page, we can use the function element()
already present in the specifications and usable in a content
property.
The
element()
value of the content property places an element (which has been removed from the normal flow viarunning()
) in a page margin box. Whenever the value of the element changes, the value ofelement()
is updated.
Just as withstring()
,element()
takes an optional keyword to describe which value should be used in the case of multiple assignments on a page. User agents must be able to recall many values, aselement()
can return past, current, or future values of the assignment. https://www.w3.org/TR/css-gcpm-3/#element-syntax
A custom identifier is required (same as the corresponding note()
function): element(<custom-ident>)
.
all-once
To make element()
work with note()
, a specific behaviour needs to be added that can be declared via an optional keyword all-once
.
With the keyword all-once
, the value of all the assignments of the document or the page are used; i.e. all the note elements (which have been removed from the normal flow via note()
) are displayed in the new area where they're assigned. Each element from note()
value is displayed in the same order of the flow and has only one assignment in the document or in the page (the elements are not repeated). The value may be empty if there is no note element on the page.
element() = string(<custom-ident>, all-once)
@note-area
The element()
function can be used in new page area @note-area
(see Page note area).
Using the element()
function, the margin-boxes can now receive the content of the note
elements.
The dimensions and (default) properties of a margin box work as described in CSS-page-3. The size of the note area does not affect the content page area.
ISSUE: In css-gcpm-3, the default value of the second argument of the element()
function is first
. However, when notes are created from the note()
function, this should be the value all-once
by default. How do you indicate this?
ISSUE: If the other arguments of the function element()
are declared (first
, first-except
, last
, start
), what does that do for the note elements?
This section and follows contains some specifications of css-gcpm-3.
The note counter is a predefined counter associated with the note element. Its value is the number or symbol used to identify the note. This value is used in both the note call and the note marker. It should be incremented for each note.
By default, note counters use a predefined counter with the same name (<custom-ident>
) as the one used in note()
. You don’t need to declare counter-reset
or counter-increment
unless you want to customize their behavior.
The note counter, like other counters, may use any counter style. Notes often use a sequence of symbols.
::note-call { content: counter(footnote, symbols('*', '†', '‡', '§')); }
::note-marker { content: counter(footnote, symbols('*', '†', '‡', '§')) '. '; }
The note counter can be reset on each page:
@page {
counter-reset: note;
@note-area { … }
}
Note that the value of the note counter should depend on the position of the note element in the document tree, not where it is eventually placed. A note element may sometimes be placed on the page after the note call, but the same counter value must be used for both.
::note-call
pseudo elementA ::note-call
pseudo-element is inserted in place of the note
element when the latter is removed from the flow. By default, the content of this pseudo-element is the value of the note counter, styled as a superscripted number. It must act like an anchor.
::note-call {
content: counter(note);
vertical-align: baseline;
font-size: 100%;
line-height: inherit;
font-variant-position: super;
}
::note-marker
pseudo elementThe ::note-marker
pseudo-element represents the note element’s marker, the number or symbol that identifies each note. This pseudo-element behaves like a ::marker
pseudo-element, as defined in [CSS3LIST]. It is placed at the beginning of the parent’s content, and is inline by default. The ::note-marker
can be styled just as other ::marker
elements can be. The default style should include list-style-position: inside
, or be set as any other list.
::note-marker {
content: counter(note) '. ';
}
The ::note-callback
pseudo-element represents the note element's call back, e.g. a navigation back from a note that returns to the correct associated ::note-call
. It is placed at the end of the superior parent’s content, and is inline by default. By default, the content of this pseudo-element is the Leftwards Arrow with Hook Unicode Character (“↩” U+21A9). It must act like a link.
::note-callback {
content: '↩';
}
ISSUE Mostly useful for continuous media. Should we mention that by default it doesn’t show up in paged media? But, paged media isn’t always meant for print… Thoughts?
Notes call and maker can be used either at the parent element level or at the note element level, allowing different types of notes within the same section.
@note-area
)The @note-area
at-rule creates special page areas that can be used to display notes elements via the element()
function. Those areas, called “page note areas”, are created within the page context, which is the declaration block of the @page rule. This rule defines a box that, if used, will contain the corresponding note elements that appear on that page and move by generated content properties for notes. Since note areas are in the context page, note boxes are contained in the content area of a page box.
The @note-area
may be following by a custom identifier.
The properties of a note box are determined by properties declared in the note rules. These properties can be used to define:
If the content of a note area overflows from the box, it will go to the next page, in the same note area (see Notes policy).
Any of the CSS layout facilities can be used to create, position and size note areas: float, absolute positioning, grid, exclusion, etc.
Default values of properties for @note-area
:
@note-area {
float: bottom;
float-reference: page;
width: 100%;
max-height: 80%;
}
float
and float-reference
)CSS Page float adds some values to the float
property to position elements in a page context, and proposes float-reference
property to indicate the “reference container” for a floated element. We will make extensive use of these properties in the following examples.
float-reference
propertyName:
float-reference
Value: inline | column | region | page (...)
inline
The float reference is the line box of the float anchor. (...)
column
The float reference is the column in a multi column environment in which the float anchor is placed.
region
The float reference is the region in a region-chain within which the float anchor is placed. (...)
page
The float reference of the float is the page within which the float anchor is placed. (...)
ISSUE: We need to specify the algorithm that avoids collisions. Also, what if the marginal note overflows the page, how do we indicate that it can be moved up?
float
propertyCSS Page float propose some extentions of the float
property.
Name:
float
Value: left | right | top | bottom
Values can be added together:
float: top right;
ISSUE: Further definitions and examples are required to block-start | block-end | inline-start | inline-end | snap-block | snap-inline | none
ISSUE: Does the clear
property can work with this proposal?
In a multi-column layout, note elements may have to be displayed at the bottom of each column. This means that multiple note areas might have to be created for the same fragmented flow. This layout creates some issues:
CSS Page float proposes the float-reference
property to indicate the “reference container” for a floated element. The value column
indicates that the float reference is the column in which the floated element is placed in a multi column environment.
We can use this reference to indicate the creation of note areas in the columns of the page where the note appears. As many boxes as necessary are created on each column. All the note areas have the same properties and can be targeted by one @note-area
rule only.
There are already a lot of use cases in critical editions where you can find multiple kinds of notes (bibliographical references, explanations, etc.) The @note-area
at-rule declaration make multiple notes easier by mixing multiple note areas in the same page context.
css-gcpm-3 defines a special@footnote
rule. This rule can be kept in this specification as a specific @note-area
with predefined positioning scheme. It behaves like a floated bottom page element. No positioning scheme designed by the user is taken into account in this @footnote
rule, and only one footnote box can be created on a page.
Notes in continuous media can follow the same general model as notes in paged media. The section Create notes with CSS applies to both types of media.
However, in continuous media, an explicit mechanism is needed to define where notes should appear, since there is no implicit page structure to anchor note areas. This requires associating a parent element with the note container.
We outline three syntactic proposals currently under discussion. All aim to allow endnotes, margin notes, or sidenotes for any block-level element, without requiring additional HTML markup.
element()
with ::after
This approach uses the standard ::after
pseudo-element and the element()
function to insert the content of the note.
note {
position: note(sidenotes);
}
section::after {
content: element(sidenotes);
}
Considerations
::after
is already widely used by authors and cannot be duplicated, which may conflict with other content or styles.::after
pseudo-element is inline by default and only supports textual content
in most current implementations.::after
declaration is allowed, making it impossible to define multiple note areas.::note-area
pseudo elementThe ::note-area
pseudo-element represents a dedicated block-level container for notes generated via the note()
/ element()
mechanism. It dynamically inserts note content at the end of the selected element.
::note-area
creates a pseudo-element that is the last-child of the selected element or the penultimate if an ::after
pseudo-element is displayed. It only accepts content: element(<ident>)
values and is block-level by default.
The ::note-area
only receives notes originating from its parent, it does not aggregate notes from elsewhere in the document.
note {
position: note(sidenotes);
}
section::note-area {
content: element(sidenotes);
}
Considerations
inline
, though that can be overridden (display: block
can be assumed here).::note-area
pseudo-element is possible per element; multiple note streams require more complex handling or alternative syntax.@note-area
This approach introduces @note-area
at-rule nested inside the CSS rule of the parent element. It provides explicit mapping between the element and the note stream.
note {
position: note(sidenotes);
}
section {
@note-area {
content: element(sidenotes);
}
}
Considerations
@note-area
block can be defined within a single element, supporting multiple note streams.In all three proposals, once the note is mapped to its container, authors can use existing CSS layout mechanisms to position and size the note content, such as: float, absolute positioning, grid, exclusion, etc.
Note styling is defined in the note element and applies identically across all three models.
All three proposals also support inheritance: nested elements can override or inherit note behavior from their parents.
For example, marginal notes can be achieved in all three models using similar layout logic. Here’s an example using ::note-area
.
ISSUE A dedicated issue is open to track these discussions and ensure a clear path forward for note handling in continuous media: https://github.com/css-print-lab/css-print-lab.github.io/issues/10
In paged media, it can happen at the end of the page that the last note overflows from the note area. As a general rule, overflow content can be moved to the next page in the note area or the margin box that corresponds to the previous one. However, rendering notes can be complex. Like the footnote-policy
property in the css-gcpm-3, we need a note-policy
that gives authors some control over the rendering. We propose to update the current specifications.
The note-policy
must be declared in the global context of the note area or the margin box.
The values accepted in note-policy
are auto
, line
or block
.
note-policy: auto
The user agent chooses how to render notes, and may place some of the note body on a later page than the note reference. A note body must never be placed on a page before the note reference.
If the entire note doesn’t fit in the note area or the margin box, due to the lack of space, the user agent displays the note line by line. If the addition of a new line implies the disappearance in the page of the line of text containing the note call, the user agent stops the rendering of the note and passes the content of the remaining note in the corresponding note area on the next page. Note that the user agent must honor widows and orphans settings when doing this, and may need to apply line
value on certain notes to do so.
note-policy: line
If a given note body cannot be placed on the current page due to lack of space, the user agent introduces a forced page break before the line containing the note reference, so that both the reference and the note body end up on the next page. Note that the user agent must honor widows and orphans settings when doing this, and may need to insert the page break on an earlier line.
note-policy: block
As with line, except that the forced page break is introduced before the paragraph that contains the note.