the scenes
O for a Muse of fire, that would ascend
The brightest heaven of invention,
A kingdom for a stage, princes to act
And monarchs to behold the swelling scene!
Henry V
1 logical location
The scenes of the plays each resides in its own location. I call them “play sections” most of the time in the code. I’m not 100% sure why. I guess it’s because there are some play sections that are not quite like scenes (particularly prologues and epilogues, for example.) But I think it’s really just that “play section” emphasizes that a play-section is nothing more than that: a sectioning of a play, which you can easily imagine being done another way. Whereas “scenes” are scenes. You have an independent sense of what a scene is or is supposed to be like.
<route path="/plays/{play}/{section}" to="play_section" />
2 flow
There’s a discussion somewhere about the wish to preserve continuity in the representation; this remark belongs there (if anywhere). The part of that which explains why we put scenes on separate pages, belongs here.
The scenes within each act were played continuously without pause.
—Sir Sidney Lee, Shakespeare and the Modern Stage
3 changes
#play-{play} <class add="in-scene" />
#play-{play}-entryway-layer <class remove="active" />
html <class remove="not-scroll-layer" />
html <class add="scroll-layer" />
3.1 document title
For scenes, the document title should be the play’s title plus the scene.
<title><play-title /> <eval>section</eval></title>
3.2 meta description
See equivalent in play.
meta[charset="utf-8"] <after>
<xslt name="render_scene_meta_description"
section="{section}"
input="/static/doc/plays/{play}.xml" />
</after>
<<get scene name>>
<xsl:param name="section" />
<xsl:template match="play">
<xsl:variable
name="play-title"
select="title/@unique_title" />
<xsl:variable name="scene-name">
<xsl:call-template name="get-scene-name">
<xsl:with-param name="section-key" select="$section" />
</xsl:call-template>
</xsl:variable>
<meta
name="description"
content="Text of {$scene-name} ({$section}) of Shakespeare's play {$play-title}, with marginal notes, annotations, illustrations, and audio recordings." />
</xsl:template>
4 material / container
// variables used by both scene text and stage
@import play // for play background color
@import paper // for paper color
// An invariant... (unless the display itself is narrower, in which case we must
// go into a different mode.)
$sceneTextWidth = 30rem
$sceneTextMargin = 2rem
$sceneTextBackgroundColor = $playBackgroundColor
Rename this element. It’s now only used for the unbounded (i.e. flow-positioned, i.e. scrollable) document content of plays (i.e. scenes, but could apply to, e.g. a role page). It’s in the body/main scrolling container.
@import play // because of play background color
@import scene
@import transition-timings
.play
position relative
z-index $playSheetsLayer + 1
<<the play styles>>
The play document is waiting stage right until summoned.
transform translateX(100vw)
When it hears its cue (that is, a scene is opened), the play document comes into view.
&.in-scene
transform translateX(0)
default-transition('transform')
So the idea is that the text is “off to the right,” and it slides into view when a scene is open. A scene must always be open when we’re looking at this. Now, some scenes may happen to be shorter than the screen. But this element will always extend at least to the top and bottom of the screen, showing the “material” on which the scene text is written.
min-height 100vh
min-height 101vh // this was for iOS hacking, but I think it's not working
background $playBackgroundColor
box-shadow 0 0 1em #111
Why? Because we want to present the text as a continuous column. There may be moments during scene transitions when the new text hasn’t loaded yet, and since the content of this element governs its height, we don’t want it to suddenly drop down to nothing. That would be ugly and jarring.
As a refinement to that, we could say that the play itself is semi-transparent where a scene is not present, showing the solid color only where one is:
background rgba($playBackgroundColor, .8)
//.scene-text
.scene-heading
.scene
background $playBackgroundColor
This would perhaps be less confusing where the scene is shorter than the screen, but would also give a less stark blankness during the moment when scene text is loading.
The width of the scene text is specified where it can be used elsewhere. We don’t apply the padding here because some elements in the document (e.g. the header) may not want it.
width $sceneTextWidth + 2 * $sceneTextMargin
Center the sheet horizontally (within its container, which is flush horizontally to the viewport). This ensures that the scene text never goes out of view, while space is distributed evently to marginal content.
margin-left auto
margin-right auto
…except that if we totally run out of space, we make it fit, because we never allow it to clip.
max-width 100%
To make the transition between scenes smoother, the scene text fades in and out.
DISABLED: this is not necessarily problematic, but it causes an extra paint of the scene text at the end of the transition, probably as the .scene is merged back into the parent layer (the animation being done).
UPDATE: I don’t know know if that’s true or not anymore.
@import transition-timings
.scene
animation fade-text $defaultTransitionDuration $defaultTransitionTiming
@keyframes fade-text
from
opacity 0
to
opacity 1
5 add scene text
#scene-text <append>
<xslt name="render_play_heading_if_first_scene"
section="{section}"
input="/static/doc/plays/{play}.xml" />
<header class="scene-nav">
<scene-link say="back to" to="previous" />
<scene-link say="skip to" to="next"/>
</header>
<xslt name="render_scene_heading"
play="{play}"
section="{section}"
input="/static/doc/plays/{play}.xml" />
<xslt name="render_scene_text"
play-key="{play}"
section-key="{section}"
input="/static/doc/plays/text/{play}-{section}.xml" />
<div class="scene-bottom">
<xslt name="render_scene_heading"
play="{play}"
section="{section}"
input="/static/doc/plays/{play}.xml" />
</div>
<footer class="scene-nav">
<scene-link to="previous"/>
<scene-link say="on to" to="next"/>
</footer>
</append>
5.1 getting out of a scene
I find that when I’m in a scene, I tend to want to get back to the main play sheet by touching it. So when a scene is open, a “screen” sits in front of the main sheet. It’s just a link back to the main sheet.
The element itself was added in the plays, and I’m leaving it there just to avoid an extra DOM operation.
@import docking
.play-sheet-screen
hug-parent()
pointer-events none
.in-scene &
pointer-events auto
The use of pointer-events
instead of, say display
, depends on the fact that the
link is completely invisible (not to mention support for pointer-events
). It’s
motivated by the wish to minimize layout changes during transitions.
For that to work, you have to let the screen (or some container of it) know that a scene is open.
#play-{play}-choices <class add="in-scene" />
Of course, this isn’t discoverable, either.
6 next and previous scene links
Belongs under flow?
You’ll notice that there are “next” and “previous” links at the beginning and end of the scene text. I call these “proximate” scene links because if this whole web thing doesn’t work out, maybe I can get a job at the state department.
6.1 scene links
Here’s a little macro that we use above to create the scene link calls.
<xsl:template match="scene-link">
<xslt name="render-play-section-link"
input="/static/doc/plays/{{play}}.xml"
relation="{@to}" prefix="{@say}"
section-key="{{section}}" />
</xsl:template>
And here’s the transform that renders them. Remember, we can’t know the key of the next or previous scene without opening the play document (or an index of it).
<xsl:param name="section-key" />
<xsl:param name="relation" />
<xsl:param name="prefix" select="$relation" />
<xsl:variable name="play-key" select="/play/@key"/>
<xsl:template match="/play">
<xsl:variable name="section" select="sections/section[@key=$section-key]"/>
<xsl:choose>
<xsl:when test="$relation='previous'">
<xsl:apply-templates mode="section-link" select="$section/preceding-sibling::section[1]" />
</xsl:when>
<xsl:when test="$relation='next'">
<xsl:apply-templates mode="section-link" select="$section/following-sibling::section[1]" />
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template mode="section-link" match="section">
<a href="/plays/{$play-key}/{@key}#top-of-scene"
class="{$relation} proximate-scene-link">
<span class="text">
<span class="prefix">
<xsl:value-of select="$prefix"/>
</span>
<xsl:text> </xsl:text>
<xsl:value-of select="@key"/>
</span>
</a>
</xsl:template>
Each scene has a header and footer for navigational links to the adjacent scenes.
@import thumb-metrics
$scale = 1.5
$size = $thumbRems * $scale
.scene-nav .proximate-scene-link
display block
position relative
This would require that the links end up in the same place when you get to the next one, which doesn’t quite make sense for the bottom one. It might make more sense if they are in-page links that go to the top or bottom.
The links are signs that point up or down. They are both included in both places so that they can be used in succession to skip in one direction through a number of scenes.
@import signs
@import arrows
@import centering
@import paper
@import colors
@import pointing
.proximate-scene-link
text-align center
sign-text-color()
height $size * .6
overflow hidden
&:before
content ''
width $size
height @width
background $worksColor
position absolute
left 50%
transform-origin center
transform translateX(-50%) rotate(45deg)
box-shadow 0 0 1em -.5em #111
&.previous:before
top 50%
&.next:before
bottom 50%
header &.previous
footer &.next
font-size $scale * 100%
header &.next
footer &.previous
display none
+user_pointing_at()
background rgba(black, .1)
&:before
background $contextColor
In the scene text, the proximate scene links support linear navigation through the plays. They should look like signs and not completely block the “paper” behind it, in order that the scene might look continuous. The “previous” scene link in particular serves to signify that the page is part of a series, for visitors arriving at the scene from a direct external link. The “next” scene link also serves as a call to action when reaching the end of a scene by scrolling.
.scene-text .proximate-scene-link
<<proximate scene link rules>>
The proximate scene links in the scene text are large, but still secondary to the current scene.
> .text
line-height 1
font-weight 200
horizontally-center()
.prefix
display block
font-size 50%
font-weight bold
&.previous > .text
top 50%
margin-top .2em
&.previous > .text
bottom 50%
margin-bottom -.2em
7 headings
7.1 play heading at the beginning
<xsl:param name="section" />
<xsl:template match="/play">
<xsl:variable name="play" select="@key" />
<xsl:if test="sections/section[1]/@key = $section">
<h1 class="play-heading">
<a class="play-heading-link" href="/plays/{$play}">
<i>
<xsl:value-of select="title" />
</i>
</a>
</h1>
</xsl:if>
</xsl:template>
@import exit-links
@import fonts
@import paper
@import scene
@import typesetting
.play-heading // the h1 inside play header
book-font()
margin 0 // beat h1 or h2 default
white-space pre-line
// This bubbles to the container, meaning you get some space between the
// scene material and the top of the viewport. This only applies on the
// first scene; otherwise the scene is always flush to the top.
margin-top 1em
padding-top .5em
font-weight normal // beat h1 or h2 default default
letter-spacing -.046em
color #222
background $paperColor
.play-heading // the h1 inside play header
font-size 300% // beat h1 default
.play-heading-link
display block
paper-font()
text-align center
padding 0 $sceneTextMargin // so it will match other text
> i
font-style normal // beat i default
7.2 scene heading every time
The “choices” anchor isn’t working here.
<xsl:param name="play" />
<xsl:param name="section" />
<<get scene name>>
<xsl:template match="/">
<h1 class="scene-heading">
<a href="/plays/{$play}#back-to-{$play}-choices" class="scene-play-link">
<span class="scene-play-link-hint"></span>
<xsl:value-of select="/play/title/@unique_title" />
<xsl:if test="$section != (//section)[1]/@key">
<xsl:text> </xsl:text>
<br/>
</xsl:if>
</a>
<span class="tty"> </span>
<a href="/plays/{$play}/{$section}#top-of-scene"
class="scene-heading-link">
<xsl:call-template name="get-scene-name">
<xsl:with-param name="section-key" select="$section" />
</xsl:call-template>
</a>
</h1>
</xsl:template>
.scene-heading
has-exit-link()
text-align center
paper-font()
font-size 200% // beat h1 default
font-weight normal // beat h1 default
margin 0 // beat h1 default
z-index 1
.scene-heading-link
display block
padding-top .25em
padding-bottom .5em
.scene-bottom &
display none
The heading first contains the title of the play, which serves as a link back to the play’s main page—basically closing the scene text. Since the scene text appears “from the right,” dismissing it can then be thought of as a leftward move. A dedicated element is used for that.
@import colors
@import pointing
@import centering
@import arrows
@import signs
@import paper
.scene-play-link
display block
position relative
z-index 2
background $worksColor
box-shadow 0 0 1em -.5em #111
color $signTextColor
color rgba(white, .6)
+arrow-before-pointing(left, size: 1rem, color: $signTextColor)
position absolute
left 0
vertically-center()
+user_pointing_at()
background $contextColor
So…. this is to “paper over” the shadow that’s put on .scene so that the anchor and resource lanes look like they’re behind the scene. The “proper” fix would be to make these elements actually behind the element that makes the shadow, but that raises other problems.
.scene-heading
@require docking
background inherit
&:after
content ''
dock-bottom()
height .75em
margin-bottom (@height / -2)
background inherit
z-index 1
.scene-bottom .scene-heading:after
display none
8 scene names
Most of the time, willshake refers to scenes by their short identifiers, which are mostly numeric. For example, the last scene of Hamlet is 5.2. Having short, canonical identifiers for the scenes is absolutely crucial to the way that willshake works. And for people who are familiar with that convention, it’s the most efficient way to communicate a location.
But these short names are not self-documenting. The longer, more familiar format, like “Act 5, Scene 2,” communicates what it is. It’s not just a scene reference, but it carries the “metadata” (in the form of the words “act” and “scene”) that tell you what it is. This is friendlier. So in places where space permits, the longer form is used.
<xsl:template name="get-scene-name">
<xsl:param name="section" select="$section" />
<xsl:variable name="act" select="substring-before(concat($section, '.'), '.')"/>
<xsl:variable name="scene" select="substring-after($section, '.')"/>
<xsl:if test="number($act) > 0">Act </xsl:if>
<xsl:value-of select="$act" />
<xsl:choose>
<xsl:when test="number($scene) > 0">
<xsl:value-of select="concat(', Scene ', $scene)"/>
</xsl:when>
<xsl:when test="$scene">
<xsl:value-of select="concat(', ', $scene)" />
</xsl:when>
</xsl:choose>
</xsl:template>
This template is used by a couple of placeholders. Why is it duplicated in
those transforms, rather than imported by them? Because it turns out that I
can’t rely on xsl:import
on the client. Just another harbinger of the need that
I’ll have to drop client-side XSLT (which will mean dropping XSLT altogether).
In the meantime, I’m just avoiding imports.1
9 composition
The scene text is composed of several elements. To make dealing with it more tractable, these elements are handled separately (to the extent possible).
Merge this into typesetting the plays.
This is where we turn our play document into HTML markup.
: $(TRANSFORMS)/render_scene_text.list/* \
|> ^o bundle scene rendering transforms ^ \
echo "<?xml version='1.0' encoding='utf-8'?>" > %o ; \
echo "<xsl:transform version='1.0'" >> %o; \
echo " xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>" >> %o; \
echo "<xsl:output omit-xml-declaration='yes' />" >> %o; \
list=`echo %f | tr ' ' '\n' | sort`; \
cat $list >> %o; \
echo "</xsl:transform>" >> %o; \
|> $(TRANSFORMS)/render_scene_text.xsl
The principal mode in this transform is “scene”.
The input to this transform is a standalone play section document.
<xsl:param name="section-key" />
<!-- Maybe the section documents should have this -->
<xsl:param name="play-key" select="/*/@play"/>
<xsl:template match="/">
<xsl:apply-templates
mode="scene"
select="(//section[@key = $section-key])[1]"/>
</xsl:template>
<!-- Don't render scene location -->
<xsl:template mode="scene" match="location" />
<<scene-template>>
9.1 scene
We only render one scene at a time.
<xsl:template mode="scene" match="section">
<div data-scene="{$play-key}-{$section-key}" class="scene">
<xsl:apply-templates mode="scene" />
</div>
</xsl:template>
9.2 finis
Most of the original texts end with the word “FINIS.” Not to waste any paper, the editors add a little flourish.
It’s a nice touch. And we don’t even have to worry about dead trees, so let’s do it.
The above image is extracted from a facsimile of the last page of “The Merchant of Venice” in a First Folio. See lib/paper for a reference to the source image.
Just as the play’s title is only rendered at the beginning of the first scene, this is only rendered at the end of the last one.
.scene <after>
<xslt name="render_finis_if_last_scene"
input="/static/doc/plays/{play}.xml"
section="{section}" />
</after>
<xsl:param name="section" />
<xsl:template match="/play">
<xsl:if test="$section = sections/section[last()]/@key">
<p class="scene-finis">finis.</p>
</xsl:if>
</xsl:template>
@import fonts
.scene-finis
paper-font()
<<finis styles>>
In the folio, the ‘finis’ is a large, centered heading.
font-size 400%
text-align center
Like many headings in the folio, the ‘finis’ is also in all caps. That looks
good, but I happen to like the fi
ligature you get in lowercase IM Fell.
Just as the top of the first scene leaves some space at the top of the screen (by way of a bubbling margin), this leaves some space at the bottom. In both cases, a special case is signified.
margin-bottom 2em // beat p default
Actually, you get 1em
by default from a paragraph, but this increases it and
also makes the intention explicit.
This helps blend the text with the “paper” background.
color rgba(#111, .8)
We also add the flourish image.
&:after
<<finis flourish>>
Create a block to work with.
content ''
display block
opacity .8
The slight translusency helps to blend the image with the background.
Reset the font size from parent. For some unknown reason, without this the entire image is scaled.
font-size 1rem
Give it some arbitrary height. The goal here is simply to avoid too much upscaling.
height 20em
The drawing is added as a background image.
background-repeat no-repeat
background-position 50% // center horizontally
Ensure that the image is flush on all edges, so that the “blenders” below will work.
background-size cover
These “blenders” help integrate it with the setting.
@import paper
background-image \
linear-gradient(rgba($paperColor, 1), rgba($paperColor, 0) 3rem),
linear-gradient(to left, rgba($paperColor, 1), rgba($paperColor, 0) 4rem),
linear-gradient(to right, rgba($paperColor, 1), rgba($paperColor, 0) 4rem),
linear-gradient(to top, rgba($paperColor, 1), rgba($paperColor, 0) 4rem),
url('/static/images/F1-SCETI-0846@crop.jpg')
10 stage directions
<xsl:template mode="scene" match="sd">
<!-- Can't use "p" here for same reason as above. -->
<div>
<xsl:attribute name="class">
<xsl:text>sd</xsl:text>
<xsl:if test="../section and not(preceding-sibling::speech)"> osd</xsl:if>
</xsl:attribute>
<a class="a" id="{@a}" />
<i class="text">
<xsl:apply-templates mode="scene" />
</i>
<br class="tty" />
</div>
</xsl:template>
<xsl:template mode="scene" match="sd/text()">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template mode="scene" match="enter|exit|manent">
<xsl:choose>
<xsl:when test="@role">
<a>
<xsl:value-of select="."/>
</a>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
11 quotes about scenes
Behold, the heavens do ope,
The gods look down, and this unnatural scene
They laugh at.
Coriolanus
Thus with imagined wing our swift scene flies
In motion of no less celerity
Than that of thought.
Henry V
Your patience this allowing,
I turn my glass and give my scene such growing
As you had slept between.
The Winter’s Tale
How many ages hence
Shall this our lofty scene be acted over
In states unborn and accents yet unknown!
Julius Caesar
Footnotes:
See the post “xsl:import and xsl:include in stylesheets don’t
work on Google Chrome.” As the answer promises, xsl:import
does in fact work in
Chrome now (although document()
doesn’t…), but it’s still an issue in Safari.
See also (the visible part of) the post “Safari’s transformToDocument() and
xsl:import” on the hyphenated site. My experience is the same: the transform
method (transformToFragment
) returns null
.