Twine: Click image to reveal text

I’m in Sugar Cube 2.31.1.

I have a passage with 3 images.

When the user clicks on each image, it reveals text “beneath” it.

I don’t think I can do this with the link macro? Is there another way?

Many thanks, Ben

Yup, you can use the <<link>> macro for that. Here’s how:

<<link [img[example.png]]>>
	<<replace "#txt1">>\
<span id="txt1"></span>

If you want the image to be visible even when you launch it from Twine, take a look at this “Displaying images in Twine” sample code. (And you can click “Jump to Start” in the UI bar to see other sample code there.)

Have fun! :smiley:

You guys are amazing! I had to tweak it slightly so that the new text emerged from behind the image (as opposed to below it):

<span id="txt1"><<link [img[Images/myimage.jpg]]>></span>
	<<replace "#txt1" t8n>>New Text.

Two follow on questions:

  1. With the above, is there any way to get the outgoing image to dissolve? The transition effect sees to only effect the incoming text…

  2. Just so I have my bases covered: How would this work with a CSS tag/ID for the image, which automatically bounds the size, etc?

Thanks again! Ben

The code you posted is broken and shouldn’t work. Any HTML elements and SugarCube macros which have a “head” and “tail” part can’t cross over each other, they can only be entirely inside or entirely outside one another.

If you want the image to be completely replaced by the text, then you’d do that like this:

<span id="txt1">\
	<<link [img[example.png]]>>
		<<replace "#txt1">>\

Note that the indentation used, where you indent in one more level after each “head” and indent out again at the “tail”, helps prevent you from making mistakes where one element or macro crosses over another. The “\” at the end of the lines prevents an extra line break, making it easy for you to use that kind of indentation.

However, if you want the image to slowly vanish to reveal the text behind it when clicked on, then you’d need to do that a different way. In your passage you’d want something like this:

<div class="imageblock">\
	<span class="txt">Text</span>\
	<<link [img[setup.ImagePath + "example.png"]]>>
		<<run $(".imageblock img").css({ opacity: 0, cursor: "default", "z-index": 1 })>>

That will cause the image to fade out and reveal the text after the image is clicked on, without shifting around any content which was below that on the screen. (See the jQuery .css() method for details.)

Also, in your Stylesheet section you’d need this:

.imageblock {
	width: fit-content;
	display: flex;
	align-items: center;
	justify-content: center;
.imageblock a {
	display: flex;
.imageblock img {
	transition: opacity 0.5s linear;
	z-index: 5;
.txt {
	position: absolute;
	z-index: 3;

(NOTE: You should run that through Autoprefixer (with browser list set to “last 400 version”) if you want to make that more compatible with older browsers.)

That CSS will make sure that the text is centered within the image’s area, and cause the change in opacity to take half a second. Additionally, along with the jQuery code, that will also cause the text to pop to the front when the image is clicked on, by changing the CSS z-index property.

Hope that helps! :smiley:

That works really well! Thank you. Though it’s raised one initial question, and slightly larger one:

  1. Is there a way to implement this across multiple images covering multiple texts—so you click one image at a time—without creating multiple iterations of the “.imageblock” tag (eg “.imageblock”; “.imageblock2”; “imageblock3”…etc), which all contain the same variables, but can be called independently? The multiple iterations works, I’m just wondering if there’s a simpler method?

  2. This maybe should be the subject of its own post: What’s the best way to build all this so that it scales across different devices? I’m aware of the @media tag, and plan on using it. My question is where should I place the crucial information that I need to scale within the Twine hierarchy, and what is the best code to use for it? Right now, this is my base style. Note the max-height and max-width: Those are set to my mobile phone’s screen size. When I load Twine on my phone and run the story, it all fits within the screen, with no need to scroll. I would like to achieve this across any device.

#ui-bar {
  display: none;

#story {
margin: 1em;


body {  
  background-color: #FFFAFA;
  color: #2F4F4F;
  font-family: georgia;
  font-size: 115%;
  text-shadow: #888 0 0 0.05em;
  text-align: left;
  max-width: 411px;
  max-height: 731px;

The question of the images-as-links, the subject of this thread, ties into this.

Using the method above, I will need to define the image size before it loads into Twine (for example, I can use an external editor to make each image = 411px, the max-width, so it spans the screen). But before I go down that road I want to make sure that those images will also scale across multiple devices, and not remain at their fixed dimensions (so a user on a tablet doesn’t end up with an image out of proportion to the rest of the story). Before I start editing multiple images down to size, I want to make this isn’t going to cause me more problems further down the line.

I hope that makes sense! Any thoughts would be most welcome.

All the best, Ben

As you mentioned, the @media tag will do what you’re looking for in regards to the various devices.

Forgive me if you already understand how it works, but in case that’s what you’re confused on: to use the @media tag, first you define your CSS like normal for all instances, and then you place @media tags with overrides for when the screen is at a certain width.

Like this:

/* default assignment */
.imageblock {
	width: 411px;

/* override for 800 width layout */
@media (max-width: 800px) {
	body {
		font-size: 90%;
	.imageblock {
		width: 300px;

/* override for 640 width layout */
@media (max-width: 640px) {
	.imageblock {
		width: 200px;

In the above example, the 640 width screens will not only have the images set to 200px, but the font size will also be set to 90% because 640 is also less than 800, so the overrides for 800 also apply. You can use @media (min-width: XX) and (max-width: XX) to specify a more specific range if that’s an issue.

Hopefully that answers question #2.

This is fantastic. Thank you! There’s one thing I can’t make work: the image itself isn’t responsive to the changes in screen size. The image stays its original size, though the text that appears is responsive. What would be the best way to make the image, which is also a link, responsive as well? Many thanks, Ben

I actually messed up my example and forgot the img part. So if you change the lines with .imageblock to .imageblock img, hopefully it’ll work.

Hey, That’s not quite working on my end. As a test, I’m working with one image on my laptop that should scale down as the screen gets smaller (originally 1000px wide), and one that should scale up as it gets larger (originally 411px wide). Intriguingly, the images change position according to padding instructions in each the @media tag, but they won’t scale in size. I’ve additionally tried tagging the initial image in the .imageblock img. However, if I set it to width: 100% it defaults the size to 411px. Would this be related to the original .body settings? Thanks again! Ben

Just tested changing the body to max-width: 100% also with .imageblock img: 100% and that seems to have done the trick for the large image scaling down! (But not the small one scaling up). (I think). This helps solve my workflow problem, namely what size to make my original images (answer: big). Thanks again for your help. Let me know if there’s anything else I need to keep in mind! Cheers! Ben