Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

clickable "Hint"-button in almost every passage (SugarCube 2)

Hey everybody,

I am in need for orientation for the following problem:

I want to have a "hint" button in almost every passage which opens a <div> with a hint on how to solve the passage's task. The text for the hint is different for each passage and will be stored in a string within each passage.

I figured the PassageHeader-passage in combination with some <<button>> and <<replace>> macros within a custom widget would do the job. But isn't there a more elegant, maybe pure CSS/jQuery solution?



  • More elegant than what? You haven't shown what you've come up with. There are several ways to accomplish what you've described, even with the components you mentioned.
  • Sorry, you caught me lazy.

    My go looks like this: Every passage which can show a hint holds
    <<set _hint = "I am a hint.">>

    In the "PassageHeader" special passage I put:
    <<button "need Help?">>
    		<<replace "#passageHint">>
    			<<print _hint>>
    			<<button "ok, got it.">>
    				<<replace "#passageHint">><</replace>>
    <span id="passageHint"></span>

    This works as I expected it to, I just wasn't sure, if there wasn't a more elegant way to achieve the same result.

    What I am struggling with is when I want to exclude the button from certain passages:
    <<if passage() neq "nothere">>
    works, but
    <<if passage() neq "nothere" or passage() neq "nothereeither">>
    doesn't. Could you give me a hint on what I am overlooking?

  • You're using the wrong logical operator. You want logical-AND in this case. For example:
    <<if passage() neq "nothere" and passage() neq "nothereeither">>

    That said, if you have many passages where the hint button should not be shown, then you're better off tagging each of those passages—e.g. with a tag like nohint—and checking the passage's tags. I'd also suggest a simplification by making the hint spoiler-like, rather than injecting it.

    For example, in the PassageHeader special passage:
    <<if not tags().includes("nohint")>>
    \<<button "Toggle Help">><<toggleclass "#passageHint" "show">><</button>>
    <span id="passageHint">_hint</span>
    You'll also need the following CSS: (Twine 2: goes in Story Stylesheet; Twine 1: goes in stylesheet-tagged passage)
    #passageHint {
    	display: none;
    } {
    	display: inline;
    SEE: tags() function and <<toggleclass>> macro.
  • Wait. My previous suggestion won't work as-is since you're setting the hint after the PassageHeader special passage has been executed. Mea culpa. You'll probably need to keep doing the injection bit in one form or another.

    Actually, if you inject it into the top of the passage via the PassageDone special passage, then you could trigger the whole thing off of the existence of the _hint variable itself—meaning you wouldn't have to check for passage names, tags, or anything else.

    For example. Put something like the following in the PassageDone special passage. It prepends the hint spoiler to the top of the passage if and only if you've set the _hint temporary variable within the passage:
    <<if _hint>>
    \<<prepend ".passage">>
    \<<button "Toggle Help">><<toggleclass "#passageHint" "show">><</button>>
    <div id="passageHint">_hint</div>
    You'll also need the following CSS: (slightly changed from the previous version)
    #passageHint {
    	display: none;
    } {
    	display: block;
    SEE: <<prepend>> macro.
  • Cheers, that is indeed a very elegant solution. Is there a way to change the button text from within a second <<toggleclass>>-macro?

    I tried
    <div id="passageHint">_hint</div>

    <<if _hint>>
    .hintButton::after {
        content: "Show hint";
    .hintbuttonhidden::after {
        content: "Hide hint";

    It works as I wanted it to, but the text appears outside of the button.

  • There are a couple of ways to do what you want.

    Continuing with your current method. First problem, you aren't selecting the button, so obviously the text will be outside. You'd need something like the following to target the button:
    .hintButton > button::after { ... }
    .hintButton.hintbuttonhidden > button::after { ... }
    I believe that will do what you need.

    That said, I'd probably recommend using a short ID, rather than a class, and a shorter toggled class. For example:
    #hint > button::after { ... }
    #hint.toggled > button::after { ... }

    PS: Sent from my smartphone, so this is a little light.
  • Hey TME,

    can I bug you again? I want a div to appear on the very top of all passages, so I put
    <div class="logo">
    <img style="height:50px;"  src="xxxxxxxxx">

    in PassageHeader.

    I expected it to appear on top of every passage BEFORE the hint button, but the button actually appears first, messing up my layout.

    I guess it has something to do with the task object order of execution, but I can't figure it out.

  • What does this have to do with the task objects? You're using the PassageDone special passage to prepend the hint button to the passage element are you not?

    I'll labor under that assumption for now. As PassageDone is executed last among the Passage* special passages, when it prepends the hint button to the passage element, then it, naturally, becomes the first thing within it.

    You could resolve that dilemma in a couple of different ways. Here's one example: (based on your examples past and present)
    <div id="passageHint">_hint</div>
Sign In or Register to comment.