the plays
Shakespeare’s plays are the central focus of willshake. Whatever form it takes, the plays are the “happy path”; you should fall into them, even if drunk.1
1 the plays as a whole
Although we may think of “Shakespeare’s plays” as being set apart from everything else in the world, it was not so black and white in Shakespeare’s time. The theater company of which Shakespeare was a part kept other works in their repertoire, including works of other contemporary playwrights. So the notion of an exhibit that features only Shakespeare’s works is more of a modern artifice, born out of a print tradition more than a theatrical one.
1.1 logical location of the plays
Known elsewhere as “routes.”
<route path="/plays" to="plays" />
<route path="/plays/{play}" to="play" />
1.1.1 validation of play locations
We were at some point validating the play
token against the following pattern,
to ensure that the play actually exists, and otherwise to provider better “not
found” responses when it doesn’t:
(?-i)AWW|Ant|AYL|Err|Cor|Cym|Ham|JC|[12]H4|H5|[123]H6|H8|Jn|Lr|R[23]|LLL|Mac|M[MV]|Wiv|MND|Ado|Oth|Per|Rom|Shr|Tmp|Tim|Tit|Tro|TN|TGV|WT
This constraint matching is not implemented in the current version of getflow, and it’s not clear that this wouldn’t be better handled by the server configuration (since we’d never generate internal links to invalid paths).
The validator for section was (with named groups)
(?'act'[12345]|Prologue|Epilogue|Induction)(?:\.(?'scene'(?:\d\d?|Prologue|Chorus)))
It’s also useful to have redirects for other formats, though that doesn’t belong here.
/poems/sonnets/(?i:(?'roman'[IVXLC]+)) (?'act'I|II|III|IV|V|PRO|EPI|IND)(?:\.((?'scene'chorus|PRO|[IVX]+)))?
1.2 as a location in the exhibit
“The plays” is an interesting and special location in willshake. As things stand, “the plays” is just the sign that nominally stands between the entryway and the individual plays, and is of much less interest per se than either. But it is the jumping off point for almost forty featured paths.
It is the one place where we are strongly compelled to show all the plays together.
The plays page has two main purposes:
- Navigation to a particular play
- Help understand the plays as a whole in various aspects.
1.3 structural changes
Currently, the /plays
path is what you might call a “no-op.” It doesn’t do
anything. The only thing that changes is the title.
<title>Shakespeare’s plays</title>
Otherwise, it’s identical to its parent page (also known as the “home page”).
It’s odd to have a “pointless” path like this, but there are a number of things
to consider. The existence of /plays
is driven largely by the logical
structure: it does make sense to have the plays under a dedicated “directory.”
And it’s not hard to imagine how that might translate into a spatial structure—indeed, it may be too easy to think of what the “plays” part of an exhibit might look like. But ultimately, the important function
2 individual plays
Lots more happens when you go from /plays
into a particular play. You’ve chosen
something; we have a much more specific context now.
Following are the changes that we make when visiting a play. Order matters, but this is not the order of the resulting structure—it’s the order in which we make the changes, both when pre-rendered by the server and when transitioning on the client.
When thinking about how to model the play, we have two presiding metaphors. The play is
- a document
- a part of the main exhibit
Both metaphors are operative.
The “entryway” of the play is where we bridge between the exhibit model and the document model.
@import paper
$playBackgroundColor = $paperColor
2.1 the basics
2.1.1 flip the play sign
The “play signs” in the works area are the canonical ways of getting to the plays. Since each play is like “another world,” it’s worth taking measures to bridge the two places. One way of doing this is by connecting the play sign from the main entryway to the play title shown in the play’s entryway. They’re two completely different elements, but they are visually similar.
#play-{play}-sign <class add="open" />
When opened, the play sign will “flip out"—fly towards you while turning over.
@import docking
@import paper
@import colors
.play-sign
transition transform .6s cubic-bezier(0.230, 1.000, 0.320, 1.000)
transform-origin center -100%
// On the way back, wait for the background fade first
transition-delay .5s
&.open
// !important because the z-indexes are assigned by id
z-index 50 !important // get in front of all other signs
transform rotateX(270deg) perspective(10px) translate3d(0, 200%, 4px)
// On the way out, start right away
transition-delay 0s
&
&:before
transform-style preserve-3d
backface-visibility hidden
&:before
content ''
hug-parent()
background #111
transform rotateY(180deg)
There’s a tradeoff here between how much work is done at the moment the sign is touched, and how much memory is reserved in advance. Deferring most of those changes until as late as possible can prevent the creation of over a hundred compositor layers (at least in Safari). The price is that the sign must be converted to a compositor layer at the very moment when it needs to move.
Credit for the idea of using a rotated pseudo-element for the backface: http://stackoverflow.com/a/17533399
TODO flipping the play sign doesn’t work a lot of the time
And often when it does, it’s not smooth. Can the above be simplified to make it better?
2.1.2 play title
The title of the HTML “document” is important for indicating what the document is about. We simply set it to the title of the play.
<title><play-title /></title>
The play-title
referenced there is a macro, which we’ll need in several other
places:
<xsl:template match="play-title">
<xslt name="play-title"
input="/static/doc/plays/{{play}}.xml"/>
</xsl:template>
The play’s title is pulled from its “index” document.
<xsl:template match="play">
<xsl:value-of select="title/@unique_title" />
</xsl:template>
Again, this transform will be used everywhere that the play-title
macro is used.
2.1.3 meta description
The user doesn’t usually see the page’s “meta description,” but it’s important for indexing by search engines. Each page should have a distinct value for this, and it is liable to be used on the “SERP” pages as the preview text. So this can affect “click-through rate.” If you’re into that kind of thing.
getflow server doesn’t support meta[charset]
alone, which is what I’d rather
here.
meta[charset="utf-8"] <after>
<xslt name="render_play_meta_description"
input="/static/doc/plays/{play}.xml" />
</after>
<xsl:template match="play">
<meta
name="description"
content="The full text of Shakespeare's play {title/@unique_title}, with marginal notes, annotations, illustrations, and audio recordings." />
</xsl:template>
2.1.4 play description
We need at least a brief paragraph stating what this location is about. Here we just indicate the title of the play, its approximate date of composition, and Shakespeare’s age at the time.
<xsl:template match="play">
This is the willshake edition of <a href="/plays/{@key}"><i><xsl:value-of
select="title/@unique_title"/></i></a>, a <a href="/plays">play</a> written by
<a>William Shakespeare</a>, probably some time
<xsl:for-each select="composition">
<xsl:choose>
<xsl:when test="@earliest = @latest">
around <xsl:value-of select="@earliest"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('between ', @earliest, ' and ', @latest)" />
</xsl:otherwise>
</xsl:choose>
<xsl:text>, when he was </xsl:text>
<xsl:if test="@earliest != @latest">about </xsl:if>
<xsl:value-of select="@earliest - 1564"/>
<xsl:text> years old.</xsl:text>
</xsl:for-each>
</xsl:template>
2.1.5 “start reading” link
Now that we know where we are, we need a call to action. The main thing you can do with the play is read it. That still hasn’t changed.
The simplest way to make reading the play a “fall into” type of action would be just to include the text here. But we break the text into sections, for reasons discussed elsewhere.
So the next best thing is to provide a simple “start reading” link pointing to the first section of the play.
<xslt name="render-play-first-section-link"
input="/static/doc/plays/{play}.xml"
text="start" />
The first section is usually 1.1
, but not always: there are prologues,
inductions, and so on. We have to open the play’s index to get the key of the
first section.
<xsl:param name="text" />
<xsl:template match="/play">
<a class="play-start-sign"
href="/plays/{@key}/{(sections/section)[1]/@key}">
<span class="text">
<xsl:value-of select="$text"/>
</span>
</a>
</xsl:template>
2.2 the play document
The play resides in the document layer. The document layer is a layer separate from the main exhibit.
#document-layer <append>
<article id="play-{play}" class="play" data-play="{play}">
<div id="scene-text" class="scene-text">
<a id="top-of-scene" />
</div>
</article>
<<play entryway>>
</append>
Note that the play entryway layer is in the document layer, when you might
expect it to be right under body
with the other layers. This is because (as
ought to be discussed in layers), there’s only one way for a full-screen,
gesture-scrollable but partly-transparent layer to allow click-through to
another layer—and that is for it to be the default overflow container,
i.e. the document element.
2.3 the play entryway
When you visit a play, the experience is in many ways like the main entryway. Just like the main entryway, there’s a large backdrop establishing the space, a large sign pointing to the main content, and one or more “sheets” serving as an overview of that content.
<div id="play-{play}-entryway-layer" class="play-entryway-layer scroll-layer">
<site_beacon to="the_works" />
<div class="play-entryway-content">
<a id="top-of-{play}" />
<<play entryway sign>>
<div class="play-entryway-pane">
<a id="play-{play}-top" />
<<play sheets>>
</div>
</div>
</div>
(There’s also an anchor that lets you link to the top of the entryway.)
Also like the main entryway, this one is a definite size. There’s one screen’s worth of “open” space at the beginning and the end. In the middle is a compact “sheet” representing the play as a document. This will be two screens high, so the whole thing together will be four screens high.
.play-entryway-pane
dock-top()
height 100vh
transform translate3d(0, 0, 0)
.play-entryway-layer.active &
height 400vh
transform translate3d(0, 100vh, 0)
Now, we have to get ahead of ourselves a bit here. When you actually enter the
play—that is, go into a scene—the arrangement changes a bit. The entryway
layer stops being scrollable, showing only the top “sheet” of the play, as a
reference. Thus, the open space at the bottom and top cannot be reached
directly. That’s why we have different rules above based on whether or not the
entryway is considered a scroll-layer
.
There’s a bit more discussion of this change in the section play “sheets”.
It’s difficult to do this transition seamlessly. The present solution is a compromise that prioritizes the most common cases.
When you go from the play entryway into a scene, two main things happen: the scene text slides in from “stage right”, and the play’s main sheet “docks” into the window, and the entryway layer is no longer scrollable. Usually, the latter change is not noticeable, since the main sheet will already be “snapped” into view when the provided links (such as the entryway sign) are used. If not, the sheet does “jump” into place, but it’s usually not by much. Besides, there’s so much else going on, that the transition would not reliably be smooth anyway.
Remind me what the “careful choice” is for making this work in Chrome? It looks the way I’d expect it to be.
But when you leave a scene and return to the main sheet, the transition can be
distractingly jumpy. This is because the entryway layer is suddenly scrollable
again, and the sheet is “pushed down” again. And because the provided links
have anchors pointing to the main sheet, it will be scrolled back into place,
adding to the vertigo of the effect. As it stands, the existing transition is
carefully chosen to avoid a bug in Chrome for Android, where the overflow height
of the play-entryway-layer
is simply not recalculated at the end of the change,
leaving it in an invalid state.
The simplest way to avoid this “jump” is to apply all changes—the style
changes and the scrolling—at once. getflow
allows you to bypass scroll
animation for a target by setting a special attribute.
<a id="back-to-{play}-choices" getflow-scroll-behavior="auto" />
So for links that are coming back to the play from within a scene, this anchor
should be used instead of #play-{play}-choices
, which is otherwise the same
place. See getflow for the rationale behind this approach.
The following section elaborates a bit more on this structure, particularly the
need for a site_beacon
.
2.3.1 let the play come in front of the beacon
The beacon is (initially) a top-level fixture. That should be discussed in the context of “space” (along with the attendant downsides, cf Hacker News comments), and referenced when the beacon is introduced. That will be a useful term here, because that’s what’s changing: it’s no longer going to be topmost.
We describe the beacon as something that’s visible from everywhere in the space. That’s the whole point of it.
But the plays are like little worlds of their own. Once you’re into a play, you’re getting pretty far from home. Whatever space is available should be devoted to the play itself. So it would be useful if the play “materials” could be in front of the beacon when they come into view. In other words, this only applies once you’ve moved down a bit, into the play “sheets.” At the beginning of the entryway, the beacon should still be on top.
So, why not simply put it between the document background layer and the document layer (where the play resides)? I mean, the entrance is mostly empty—just a sign—so the beacon is not obscured at all. The problem is, yes, you can see it, but you can’t touch it. That’s because the document layer, which is in front of it, is like a transparent panel covering the whole screen (as we discuss in layers). And this is necessary so that the play entryway can be scrolled normally (i.e. with gestures) even where it has no visible content. And there’s just no way to interpose an element from one stacking context into the middle of another. (That’s in effect the definition of a stacking context).
So, one way or another, we need a hack.2 What we end up doing is the least unpalatable of the alternatives we tried. In fact, it’s not altogether bad.
What we do is just create another copy of the beacon, placed inside of the entryway layer. The rest of the entryway content (defined above), goes into another element.
$playSheetsLayer = 1
With that, we can get the stacking order that we want:
@import docking
@import play
.play-entryway-content
transform translateZ(0)
z-index $playSheetsLayer
pointer-events none
.play-entryway-pane
.play-entryway-sign
pointer-events auto
.play-entryway-layer > .site-beacon
z-index 0
transform translateZ(0)
Note that for this to work, both elements must have explicit z-indexes, as well
as the “transform hack.” In fact, .play-entryway-content
is only needed as a
wrapper element to prevent paint-on-scroll behavior in Chrome. Normally, that
would be achieved by using the transform hack on the scrolling layer itself.
But that can’t be used in this case, because any any element with a transform
automatically becomes the positioning parent of fixed-position elements inside
of it. This would cause the beacon to behave as absolutely positioned, instead
of fixed, defeating the whole purpose. This is just what the spec says.
Having both beacons in the same place won’t really hurt anything. But, since they are semi-transparent, it does make a difference. Let’s hide the other one, just for good measure.
#willshake-beacon <class add="invisible" />
#willshake-beacon.invisible
visibility hidden
And now, the beacon recedes into the scenery.
BUG beacon is not affordable at bottom of play page
2.3.2 the entryway sign
We put a giant sign at the entryway to the play. This and the “beacon” are the only things visible over the play’s background images at the entryway.
<a id="play-{play}-entryway-sign"
href="/plays/{play}#play-{play}-choices"
class="play-entryway-sign"
data-play="{play}">
<play-title />
</a>
And what does that look like?
@import exit-links
@import fonts
@import colors
@import docking
@import paper
@import thumb-metrics
@import transition-timings
@import pointing
@import arrows
.play-entryway-sign
paper-font()
scale($threshold, $scale)
@media screen and (min-width $threshold)
font-size $scale
scale(25em, 125%)
scale(30em, 150%)
scale(35em, 175%)
scale(40em, 200%)
scale(50em, 250%)
scale(60em, 300%)
scale(70em, 350%)
scale(80em, 400%)
position absolute
top 66vh
left 33%
transform translate3d(-50%, 0, 0)
padding-top .5em
padding-bottom .5em
padding-right 2.5em // room for arrow
padding-left .5em
// Kind of a hack to keep the play sign from becoming visible when you enter
// a scene. Otherwise it would come into view as the entryway layer is
// reduced to one viewport high.
.play-entryway-layer:not(.active) &
display none
// UNDECIDED on the translucency
//background rgba($paperColor, .95)
background $paperColor
// Trying to darken the corner here, but it's not very convincing
//background radial-gradient(ellipse 60% 220% at 58% 85%, transparent 96%, black), $paperColor
// A bit of "curl"
border-radius 5em 0 0 0 / 10em 10em 1em 10em
border-left 1px solid rgba(black, .5)
+user_pointing_at()
background $worksLightColor
// The inset shadow gives a bit of discolored paper effect
box-shadow .125em .125em 1em, inset .5em .5em 1em -.75em #111, inset .75em 0 1em -.25em rgba(#222, .5)
// FYI, you can fine-tune these
// &[data-play="Ham"]
// top 60vh
// IF THIS REALLY needs to be position abs, why are we taking decault left
// and top?
+arrow-after-pointing(down, size: 1em, color: rgba(white, .7))
position absolute
default-transition('transform')
// Part of the "flip play sign" effect
transform-origin top center
animation turn-in 1s
@keyframes turn-in
from
transform translateX(-50%) rotateX(90deg)
to
transform translateX(-50%) rotateX(0deg)
2.4 play “sheets”
We think of the play loosely as a document, comprising a number of sheets.
The notion of sheets is introduced as an “expansion joint,” though—something that can be used by other feature that come later. To start with, there is only one sheet.
<section id="play-{play}-sheets" class="play-sheets">
<<play main sheet>>
</section>
2.4.1 the set of sheets
The collection of sheets has certain properties.
.play-sheets
<<play sheets styles>>
The play sheets use all of the available width.
position absolute
width 100%
When some other scroll layer is active (the play timeline or the play text), then we want this docked to the top of the viewport with only the first sheet visible.
overflow hidden
height 100vh
The trick to this “expansion joint” idea is that you’ll have to be agnostic of this height somehow, because you won’t know how many sheets there are. I don’t suppose this can be made to work with flow positioning?
Whereas, when the entryway is the active scrolling layer, the whole element is positioned below the fold, and all sheets are available, with one viewport devoted to each. We also reserve an extra screen at the bottom. At the very least, we can use this for identifying the backdrop. Right now, it’s empty.
.play-entryway-layer.active &
height 200vh
2.4.2 sheets generally
The idea of the sheets is to balance between features that aren’t known yet, and a common way of presenting material that has known good properties. These “known good” properties are defined here, and can be used by features added later.
layout
A sheet is one viewport high. So it can’t show very much. Instead it should point to the place that is being added. At the same time, a sheet is transluscent, since it would otherwise block out all context. This way, you can still see the play’s background image.
@import colors
.play-sheet
position relative
height 100vh
box-shadow 0 0 1em
background rgba($worksLightColor, .8)
display flex
flex-direction column
The space not used by the sheet heading should be available to its “body” content. That’s why it uses “flex” positioning.
.play-sheet-heading
flex 0 1 auto
.play-sheet-body
flex 1 1 auto
position relative
This way, if the heading shrinks or grows, the body will adjust automatically to use the remaining space.
sheet heading
A sheet will also have some heading telling what it is. These should look the same. The title above the main sheet in particular serves to tell you what play you’re in at all times, since it doesn’t go anywhere, and doesn’t get covered up until the screen is quite small.
@import typesetting
@import signs
.play-sheet-heading
clip-text()
margin-top 0 // beat h* default
margin-bottom 0 // beat h* default
font-weight normal // beat h* default
sign-text-color()
letter-spacing -.04em
@media (max-width 20rem)
font-size 150%
@media (min-width 35rem)
font-size 225%
@media (min-width 40rem)
font-size 250%
@media (min-width 45rem)
font-size 300%
.play-sheet-heading-link
display block // so that it occupies the whole heading
dismissing sheets
Although you can still see that there’s something behind the sheets, they generally occupy the full screen. So it’s not necessarily clear that you can actually leave. That’s what the “dismiss” link is for. It’s kind of like an exit link, but with a different icon (and it just scrolls up on the page, rather than going to a parent path). The main sheet’s dismiss link, takes you back to the top of the play.
@import signs
@import colors
@import arrows
@import centering
@import pointing
.play-sheet-dismiss-link
float left
circle(1.2em)
margin-right .125em
box-shadow 0 0 4px -2px // #222
position relative
+arrow-after-pointing(up, size: .4em)
top -.1em
horizontally-center()
background rgba(black, .3)
+user_pointing_at()
background $contextColor
2.4.3 main sheet
The “main” play sheet is the one that pertains to the play proper, that is, everything directly coming from the text or otherwise provided to help you find your way around it.
<section id="play-{play}-choices" class="play-sheet play-main-sheet">
<<top of main sheet>>
<h1 class="play-sheet-heading">
<a href="/plays/{play}#top-of-{play}" class="play-sheet-dismiss-link"></a>
<a href="/plays/{play}#play-{play}-choices" class="play-sheet-heading-link">
<play-title />
</a>
</h1>
<div id="play-{play}-main-sheet-body"
class="play-sheet-body play-main-sheet-body play-entryway-signs">
<a href="/plays/{play}/#back-to-{play}-choices" class="play-sheet-screen" />
<header class="site-play-description">
<xslt name="render_play_description"
input="/static/doc/plays/{play}.xml" />
</header>
<<start reading link>>
</div>
</section>
And now for the contents of the play main sheet.
@import centering
@import colors
@import signs
.play-entryway-signs
z-index 1
> a
@media (min-width 35em)
font-size 150%
color $signTextColor
> .text
font-size 200%
font-weight 200
.play-start-sign
display block
text-align center
centered-block()
circle(width: $thumbEms)
box-shadow 0 0 .5em #111
background $worksColor
+user_pointing_at()
background $contextColor
position relative
.text
full-center()
2.5 adjust layers
I’m not 100% sure what this is for.
Shelved. See above where this was being set. This was in set_layer_state
.
#Shakespeare <class remove="plays-path" />
On #exhibit
, this is just for the keyboard hack. See load script. scroll-layer
is also primarily for scrolling to anchors.
#play-{play}-entryway-layer <class add="active" />
#exhibit <class remove="active" />
#document-background-layer <class add="active" />
#document-layer <class add="active" />
When we open a document, we have to explicitly indicate the change in scrolling layer.
In principle, the main document should be marked as not being the scroll layer at this point.
html <class add="not-scroll-layer" />
But that is not done, because of a wierd (actually very basic) bug in getflow.
See, the html
element already has the not-scroll-layer
class from the beginning,
thanks to the exhibit in the entryway. This would cause the class to be removed
when reversing this rule (i.e. returning to the entryway from a play), which
would result in two scrollbars being displayed. I ought to fix that bug; for
now I’m just removing that rule.
#play-{play}-entryway-layer <class add="active scroll-layer" />
2.6 ship the plays
Of course, the plays have to be available for use on the web site.
In order to use the plays on the web site, we have to copy the text there.
# The first set is just indexes.
: foreach $(PLAYS)/*.xml |> !copy_to |> $(SITE_PLAYS)/%b
These indexes of the plays may change. The best way to make sure that they’re up-to-date is to version them, as described in the web server’s discussion about coherence.
: foreach $(SITE_PLAYS)/* | $(PROGRAM)/<hash> \
|> ^o hash %b ^ %<hash> %f > %o \
|> $(ROOT)/hashed/%b
Instead of copying each section individually, I’ll just symlink the entire directory.
: $(ROOT)/Tupfile.ini |> ln --symbolic --relative $(PLAYS)/sections %o |> $(SITE_PLAYS)/text
I’m not versioning the sections, because there are 900 of them. It wouldn’t be worth adding these to the prologue, since they shouldn’t have any breaking changes. But the indexes might.
2.7 what do they look like?
/ play sections : the container in the play page for play section overview and, / on the section’s page, its full content
@import colors
@import docking
@import paper
@import play
@import scene
@import transition-timings
Layers.
The play resides in the document layer, which is completely dedicated to whatever document is open. Except the “beacon,” nothing else will be visible when a play is open.
The play is a document comprising various sheets.
The play area contains three top-level layers: the entryway, the timeline, and the document.
The entryway is a fixed-size area where you can see the main ways that you can go from here. All of the substantive areas are in front the entryway.
The timeline layer is a navigational underlay which is also independently browsable in a dedicated path.
The document contains the text of the play as well as its marginal annotations.
@import layers
.play-entryway-layer
// Regarding the compositor argument, see note in the "beacon" section
// below.
scroll-layer(compositor: false)
This is just for establishing layers. The details of the play entryway layer
are in a dedicated stylesheet, play-rules--entryway.styl
.
The play description.
This is like the region messages in #Shakespeare
and upstream/downstream.
@import centering
@import scene
@import responsive
.site-play-description
color rgba(#111, .9)
text-align center
centered-block(width: 20em)
max-width 100%
padding 2em
box-sizing border-box
//border 4px solid yellow
+wide-screen()
font-size 200%
p
// Ensure that the scene text covers this when it's open.
max-width $sceneTextWidth
3 services
The plays are where all the action is. The purpose of this document (and the scenes) is not to do every possible interesting thing. On the contrary, the purpose is to do the “minimum viable” thing, while serving as a foundation (or platform) on which other interesting things can be built.
3.1 play-specific styles
One convenient way to add features is through stylesheets. You’d be surprised how much you mileage you can get out of generated CSS. There’s already a pipeline for adding stylesheets to the site, but that’s really for generic styles. I’m talking about styles specific to each play.
head <append>
<link rel="stylesheet" href="/static/style/{play}.css" />
</append>
It should be trivial for features to get styles into that file. With the
following rule, you can write stylesheets in the form {play}--*
to the css/plays
directory and have them automatically bundled.
: foreach $(PLAYS)/* \
| $(CSS)/plays/<all> \
|> ^o (alternate) bundle play styles %B ^ \
cat $(CSS)/plays/%B--* > %o \
|> $(SITE_STYLE)/%B.css
That will probably break if you remove all of the features that use it (i.e. write play styles to be bundled), because the shell will take the glob pattern literally, producing an error. But the advantage of the above method over scanning the whole group list is that Tup will only rebuild the necessary styles when a play’s source changes, rather than all of them.
These styles are loaded at the entryway for each play, although they are not necessarily used there. Assuming that movement within a play is more common than movement between the plays, it’s worth the extra front-loading. But be smart about their size. CSS can gzip very well, especially if you keep the 32K window in mind.
3.2 index the roles
Should this be under “the roles”?
We’ll count the lines with a little Python program:
"""Reads the given play document and writes roles section with line counts."""
import sys
from lxml import etree
index = {}
playDoc = etree.parse(sys.argv[1])
play = playDoc.getroot()
for speech in play.findall('.//speech'):
lines = len(speech.findall('.//line'))
for speaker in speech.findall('speaker'):
role = speaker.get('role')
index[role] = index.get(role, 0) + lines
for role in play.findall('.//role'):
role.set('line-count', str(index.get(role.get('key'), 0)))
roles = play.find('roles')
roles.set('play', play.get('key'))
sys.stdout.write(etree.tostring(roles, pretty_print=True).decode('utf-8'))
Then we’ll call that for all of the plays:
: foreach $(PLAYS)/* \
| $(PROGRAM)/index-roles \
$(ROOT)/collated/plays/<all> \
|> ^o index roles for %B^ \
$(PROGRAM)/index-roles $(ROOT)/collated/plays/%b > %o \
|> $(SITE_PLAYS)/%B-roles.xml
4 individual scenes
See the scenes.
Footnotes:
“The User Is Drunk.” https://news.ycombinator.com/item?id=7655281
By “hack,” I mean a kludge, a compromise, a whatever-it-takes solution. Something which goes out of the way of the intended usage of a system.