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.

all-the-plays.svg

It is the one place where we are strongly compelled to show all the plays together.

The plays page has two main purposes:

  1. Navigation to a particular play
  2. 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.

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.

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:

2

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.

about willshake

Project “willshake” is an ongoing effort to bring the beauty and pleasure of Shakespeare to new media.

Please report problems on the issue tracker. For anything else, public@gavinpc.com

Willshake is an experiment in literate programming—not because it’s about literature, but because the program is written for a human audience.

Following is a visualization of the system. Each circle represents a document that is responsible for some part of the system. You can open the documents by touching the circles.

Starting with the project philosophy as a foundation, the layers are built up (or down, as it were): the programming system, the platform, the framework, the features, and so on. Everything that you see in the site is put there by these documents—even this message.

Again, this is an experiment. The documents contain a lot of “thinking out loud” and a lot of old thinking. The goal is not to make it perfect, but to maintain a reflective process that supports its own evolution.

graph of the program

about

Shakespeare

An edition of the plays and poems of Shakespeare.

the works