Skip to content

Instantly share code, notes, and snippets.

@joeberkovitz
Last active November 12, 2021 19:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joeberkovitz/72895c28b18b3ef583d21a67455e0ccd to your computer and use it in GitHub Desktop.
Save joeberkovitz/72895c28b18b3ef583d21a67455e0ccd to your computer and use it in GitHub Desktop.
Draft issue proposal for styling

Preamble

The idea of MNX style properties and classes was first presented in the original draft spec. That proposal was rejected some time ago, and is not part of 1.0. It relied on CSS syntax, it was overcomplicated, and it didn't anticipate <system-layout> or <score>.

However, there's still a need for style properties as evidenced by the fact that styles and classes have been proposed multiple times in the CG to solve problems with text, dynamics, graphics, repeats. Each proposal has been a little different. It makes sense to revisit styles and address them in a way that's more uniform. That way we can have a single useful tool that can be rolled out repeatedly when we need it. This proposal tries to put forth a new vision of styles and how they fit into MNX 1.0.

This proposal is big enough to need a fair amount of up-front rationales and definitions before getting into the meat of it all, so thanks for your patience in reading. If you just want to look at examples, please skip straight to the end.

What do style properties control?

We'll not debate the full set of style properties here. Those can be broken out as separate issues once the mechanism is clarified.

That said, here are some possible examples that illustrate the variety of possible styles:

  • color
  • note head size
  • music or text font family
  • fine-grained X/Y offsets
  • visibility / audibility
  • enharmonic spelling variants

Design Goals

  • A set of style properties can be computed to control each element's rendering. A set of optional property values must be available to control the presentation and performance of each element. We speak of these properties as "styling" the element in question.
  • Straightforward computation of effective properties. The computation of the effective set of properties for an element must be straightforward and easy for encoders/decoders to understand and implement.
  • No special style syntax beyond XML. No full-on CSS syntax parsing. However property value types e.g. RGBA colors or dimensions may require simple parsing.
  • Simple, explicit styles can be encoded directly on an element. Example: change the color of one note; set the music font of the entire document.
  • Some style properties can be inherited from an ancestor element. Example: apply a different note spacing to all events in one specific measure. Set the music font family for the entire score.
  • Some style properties can be applied by a layout. Example: alter the music font size for a single-part layout of a score vs. a conductor score.
  • Some style properties can be applied by a score. Example: alter the music font size for large-print edition of a single-part score, vs. the regular-print edition (both use the same layout).
  • Style computation is independent of semantics. The determination of style properties should be driven by a single mechanism that works the same way for all notation elements.
  • Style properties may be semantics-dependent. It's to be expected that not all style properties apply to every element. Example: an italic font style does not affect the appearance of noteheads.
  • Styles can affect both graphical and performance rendering. Example of a performance style: fine-tune the dynamics of some events for a particular interpretation of cresc.

Architecture

In this section we describe the concepts used by styles and how they relate to each other.

Style computation

Any element can serve as a "style source". This means that it defines a particular set of style properties, which in turn affect a set of "target elements".

Target elements are things you can see and/or hear. For example, a <note>.

A target element can always be a style source for itself. A note might specify that its color is blue, for example. However, other elements can also be style sources for the same note. For example, its parent <event> might specify that all its notes are blue. Or a system layout that's in effect, might specify that all noteheads in some part are shown smaller than normal. Or the document as a whole might specify a uniform music font to be used everywhere.

Style sources can include elements living in <global>, <part>, <system-layout> and <score>. To control how these many sources work on each target element, we need a well-defined algorithm. Therefore, the set of style properties for any single target element are derived from a number of style sources, consulted in the following order, highest priority first:

  • an applicable score element, followed by its ancestors
  • an applicable layout element, followed by its ancestors
  • the target element itself
  • the ancestors of the target element, up to its containing <measure> or <part>
  • within <global>, any <measure> to which the target element belongs
  • the <global> element

Much of the utility of styles lies in this priority order. It establishes a useful scheme, in which specific values take precedence over basic defaults.

Sources, properties, and classes

A style source can supply a target element's properties directly. To do this, it defines some name/value pairs of style properties, attached directly to the style source in XML. When the style source is consulted as part of style computation, these properties are simply applied to the target (if they aren't already defined with a higher priority).

However, target elements can also name "style classes" to which they belongs. A style class lets the target element say, "I am a certain kind of thing", without declaring any style properties. In turn, other elements like layouts or scores can say, "Here are the style properties for that kind of thing", by supplying a definition of that same style class.

This lets semantic elements be very specific about their differences from other elements, while leaving the details up to <score> or <system-layout> elements. For example a <note> might say it belongs to a class named small. One layout might specify that a small note is 0.75 the size of a regular note; another layout might instead specify 0.85 because it just looks better in that layout.

Specifying attributes in a style source

A basic choice confronting us is how to specify a set of property values in MNX for some style source. Here are several approaches that could be taken; they all work at a mechanical level. For simplicity we stick to one example: changing the color of all elements in a sequence to make them blue, and also making all notes in the sequence smaller than normal. Thus, we are supplying style properties for a sequence (which itself is invisible), to be inherited by its descendants (which are things we can see like notes and directions).

Note that more than one of these could be allowed... not that that is necessarily a good thing.

1. Direct attributes

In this approach all style properties are represented as XML attribute-value pairs on an element being styled:

<sequence color="#0000FF" note-size="0.75"/>
  [...affected elements...]
</sequence> 

Notes:

  • Most compact representation, compatible with existing attributes like color on <note>.
  • This approach allows any style property of any possible descendant to be used as an XML attribute (in this case note-size is inherited by all <note> elements below <sequence>). So it may have the effect of requiring a huge style-attribute group that applies to almost every element in the schema.

2. Style element attributes

<sequence>
  <style color="#0000FF" note-size="0.75"/>
  [...affected elements...]
</sequence> 

Notes:

  • Not as compact.
  • Segregates all style definitions in a distinct element, which must be able to carry any style attributes.
  • Fewer XSD side-effects.

3. Style element children

<sequence>
    <style name="color" value="#0000FF"/>
    <style name="note-size" value="0.75"/>
    [...affected elements...]
</sequence>

Notes:

  • Extremely verbose
  • No effect at all on XSD schema

For compactness, only choice 1 (Direct attributes) will be used in remaining examples.

Declaring style classes

Target elements may list the classes they belong to using the class attribute, which takes a whitespace-delimited list of class identifiers:

<event value="1/8">
    <note pitch="C4"/>
    <note class="emphasis" pitch="E4"/>
    <note class="emphasis small" pitch="G4"/>
</event>

Target elements can declare more than one class. Style properties declared by a target element are applied in the order in which they were named, allowing subsequent class names to override earlier ones.

Defining style classes

Style sources may include one or more <style-class> children, each of which defines a set of properties for some class name; here a system layout defines what the classes emphasis and small mean for that particular layout:

<system-layout id="editorial">
    <style-class name="emphasis" color="#0000FF"/>
    <style-class name="small" note-size="0.75"/>
</system-layout>

Class definitions are always optional; if a style source doesn't supply one, then the class's properties are unaffected by that source.

Defining element classes

Target elements are considered to automatically belong to a special "element class" determined by the target element's name alone. This permits style sources to control the appearance of different elements by name.

Element-name-based classes can be defined in a style source as follows. This example lets the <score> element define the default fonts for instruction text and expression text:

<score>
    <style-class element="instruction" font-family="Palatino" font-style="italic" font-size="12"/>
    <style-class element="expression" font-family="Palatino" font-weight="bold" font-size="14"/>
</score>

A target's element class is always processed first, before iterating through the explicitly named classes. This means that element classes supply default values that are overridden by named classes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment