Getting Twine "if" & CSS to play nice together

(Not super familiar with either Twine nor HTML)

Twine Version: 2.3.9 (web)
Story Format: Harlowe 3.1.0

(the Story Java Script is empty)

Story Style sheet
img{
	max-width: 100%;
  	max-height: 100%;
}

#mapDiv{
  float: right;
  position: relative;
}

#pos1{
    left: 12.67%;
  	top: 79%;
    position:absolute; 
}

#pos2{
    left: 40%;
  	top: 55%;
    position:absolute; 
}
Starting passage
- [[Number 1]]
(if: $progress >= 2)[- [[Number 2]]]
(if: $progress >= 3)[- [[Number 3]]]
<div id="mapDiv">
<img src="http://placekitten.com/300/300">
<img src="http://placekitten.com/30/30" style="left: 87%; top: 12%; position:absolute;">
<img src="http://placekitten.com/30/30" id="pos1">
(if: $progress >= 2)[<img src="http://placekitten.com/30/30">]
(if: $progress >= 3)[<img src="http://placekitten.com/30/30" style="left: 33%; top: 33%; position:absolute;">]
(if: $progress >= 4)[<img src="http://placekitten.com/30/30" id="pos2">]
</div>
Passage "Number 1"
(if: $progress < 2)[(set: $progress to 2)]
[[Return->Start]]
Passage "Number 2"
(if: $progress < 3)[(set: $progress to 3)]
[[Return->Start]]
Passage "Number 3"
(if: $progress < 4)[(set: $progress to 4)]
[[Return->Start]]

Here’s a hub passage with a number of sub passages which you can go back and forth between. A new passage is revealed each time you visit the latest one, along with a small image of a cat. This is done by incrementing the variable “$progress” on passage visit and then using its value to determine what gets revealed.

The problem is, when I’m trying to combine Twine “if”-checks with CSS, either trough direct element styling or trough an “id” link to the stylesheet, the image in question doesn’t get revealed. It works fine if it’s either just a styled cat image or an unstyled if-revealed cat image (the first four “img” elements all works as expected). How can I fix this?

1 Like

The outcome you are seeing is due to a number of technical issues:

1: Due to how Harlowe’s Passage Transition Effect is implemented any block based standard HTML element (like <div> and <img>) you include within a Passage will be displayed using a different ‘delay’ than the story format’s own custom HTML elements. To overcome this visual effect issue you need change the standard HTML element to be displayed as inline-block instead.

The following CSS demonstrates how to do this for the <div> element…

div {
    display: inline-block;
    width: 100%;
}

…and to do the same for any other block based element (like <p>) you want to use simply append that element’s type to the above CSS selector using a coma as a separator…

div, p {
    display: inline-block;
    width: 100%;
}

2: Each line-break you add to the contents of a Passage automatically gets converted to a HTML line-break (<br>) element, and unfortunately line-breaks can effect the positioning of visual elements like <img>. To overcome this issue you can use Harlowe’s Collapsing White-space markup to suppress the automatically generated <br> within the ‘image’ section of content.

{
<div id="mapDiv">
... content of the div element...
</div>
}

3: The CSS position:absolute; property is relies on the parent-child nature of the page’s HTML structure to work, and unfortunately the custom HTML elements Harlowe is injecting into the output is effecting the CSS of the <img> elements you are conditionally displaying including in the output.

Your static <img> with that has inline position:absolute; works because that element is a direct child of the <div> element, as shown by the following HTML structure which I obtained by used my web-browser’s Web Developer Tools to Inspect the page.

<tw-collapsed>
<div id="mapDiv" data-raw="">
   <img src="http://placekitten.com/300/300" data-raw="">
   <img src="http://placekitten.com/30/30" style="left: 87%; top: 12%; position:absolute;" data-raw="">
   ...
</div>		
</tw-collapsed>

…however if I Inspect the HTML elements being generate when the condition <img> with a inline position:absolute; is display you will notice that that <img> element is not a direct child of the <div> element…

<tw-collapsed>
<div id="mapDiv" data-raw="">
   ...
   <tw-hook>
      <img src="http://placekitten.com/30/30" style="left: 33%; top: 33%; position:absolute;" data-raw="">
   </tw-hook>
</div>		
</tw-collapsed>

…there is a custom <tw-hook> element (that is associated with the (if:) macro call) in-between the <div> and the <img> elements, and this causing the position:absolute; of the <img> to be relative to the <tw-hook> element instead of the <div>. And due to its nature this issue is harder to overcome than the previous two.

The following solution replaces the <div> with a Named Hook, and uses the related (append:) macro to conditionally inject the other <img> elements as needed into the defined area.

- [[Number 1]]
(if: $progress >= 2)[- [[Number 2]]]
(if: $progress >= 3)[- [[Number 3]]]
{
	|map>[
		<img src="http://placekitten.com/300/300">
		<img src="http://placekitten.com/30/30" style="left: 87%; top: 12%; position:absolute;">
		<img src="http://placekitten.com/30/30" id="pos1">
	]
	(if: $progress >= 2)[(append: ?map)[<img src="http://placekitten.com/30/30">]]
	(if: $progress >= 3)[(append: ?map)[<img src="http://placekitten.com/30/30" style="left: 33%; top: 33%; position:absolute;">]]
	(if: $progress >= 4)[(append: ?map)[<img src="http://placekitten.com/30/30" id="pos2">]]
}

…the above requires CSS like that describe in point 1, except its selector needs to target the Named Hook instead…

tw-hook[name="map"] {
    display: inline-block;
    width: 100%;
}

Using the above Named Hook technique results in a HTML structure like the following…

<tw-collapsed>
   <tw-hook name="map">
      <img src="http://placekitten.com/300/300" data-raw="">
      <img src="http://placekitten.com/30/30" style="left: 87%; top: 12%; position:absolute;" data-raw="">
      ...
      <img src="http://placekitten.com/30/30" style="left: 33%; top: 33%; position:absolute;" data-raw="">
      ...
   </tw-hook>
</tw-collapsed>

…where both the static and the conditionally added <img> elements are all direct children of the Name Hook’s <tw-hook> element.

1 Like

Whew, thanks, I’ll give your solutions a try. I was going to ask about white-space formatting after the big issue was resolved, nice of you to preempt that =) (otherwise I might have just mashed the img elements together with the div without any spaces and called it a day)

Edit: Just so people know, the above solution seem to have missed the CSS from “#mapDiv”, but including its contents in “tw-hook[name=“map”]” worked just fine