What is the suggested way of creating persistent UI elements in Snowman?

To me it seems like the most practical way to add persistent buttons or other UI elements to a story using Snowman is actually to fork Snowman and edit the index.ejs file. But surely that can’t be the preferred way. What is the recommended way to go about adding e.g. a save/load button at the top of the screen independent of whatever passage is currently being displayed?

On a related note, is there a way to extend _.template? I was pretty drawn to Snowman at first but the more I look into it the more I think macros exist in other story formats for more than just simplicity of programming, and Snowman not even having the ability to create macros seems very unfortunate.

1 Like

I don’t miss macros at all, but having a way of extending the UI would be cool. Would this recipe help you?


I’d have to add that to every single passage I wanted to have them on, no? While having the ability to change the UI for certain passages definitely seems useful, I’m not too keen on the code duplication and the fact that everything remains in <tw-passage> (which makes generally implementing something like this quite a bit more difficult) (Unless this works with nesting… i.e. if Header had the content <div#passagecontent> and Footer the content </div>… but I don’t know if underscore template would like that)

I guess ideally whether or not the UI elements show up (and exactly which UI elements show up) would be determined by passage metadata:

:: Normal Passage
This is a normal passage that shows the UI elements.

:: Special Passage {"ui": "hidden"}
This passage has no UI because of its metadata. Ideally this would be checked by a function I'd write which hooks Snowman's rendering or templating.

Ah, looks like I can just do this!

const Passage = Object.getPrototypeOf(window.story.passages[1]);
const oldRender = Passage.render;
Passage.render = function(src) {
    src = src || this.source;
    if (!this.tags.includes("noui")) {
        src = "the ui<br><div#ui>" + src + "</div>";
    return oldRender.call(this, src);

How about that.
Not the cleanest but works perfectly fine.

1 Like

My second suggestion would be to add the UI around tw-passage with jQuery. But your idea is good imo.