Hello, I am somewhat new to Twine, and I’m currently working on a project in Twine 2 Harlawe 3. I’m trying to implement a dark mode toggle feature that saves the user’s preference to local storage, i am trying to implement something that lasts through hard game restarts.
I’ve been experimenting with JavaScript, but I’m still figuring out how to effectively use it within Twine. If anyone has experience with this or could point me in the right direction, I would really appreciate your guidance!
Maybe you can share the code you have so far so I can be more helpful.
You can save to localStorage using setItem. You can retrieve what you saved with getItem.
// saving the player preference
localStorage.setItem('darkMode', userPrefs)
// getting the player preference from local storage
let userPrefs = localStorage.getItem('darkMode')
This is what I said earlier:
Try this macro.
(set: $darkMode = false)
And then you can use the state of the toggle button to turn dark mode on or off.
Could you share the dark mode related code in your story? That would allow me to be more helpful. What I tried to share probably wouldn’t work in isolation.
I shared a long post at first because I didn’t want to make any assumptions about the code you had already written.
Well, I could also be wrong about the macro in particular. I’m more familiar with JavaScript than Twine.
The issue could be the macro. Maybe try
(set: $userPrefs = false)
and use that to store the player dark mode preferences? I think something else is probably going on though. I will wait for someone else more knowledgeable to answer!
This is my code the localstorage part does not work tho
// Initialize dark mode preference on page load
document.addEventListener("DOMContentLoaded", function () {
// Retrieve preference from localStorage
const savedPreference = localStorage.getItem("toggle");
// Define the $toggle variable as false if it doesn't exist in the story
if (typeof State.variables.toggle === "undefined") {
State.variables.toggle = savedPreference === "true";
}
// Apply dark mode based on the saved or current toggle state
if (State.variables.toggle) {
enableDarkMode();
} else {
disableDarkMode();
}
// Set the toggle switch position
const toggleSwitch = document.getElementById("darkModeToggle");
if (toggleSwitch) {
toggleSwitch.checked = State.variables.toggle;
}
});
// Enable dark mode
function enableDarkMode() {
document.body.classList.add("dark-mode");
localStorage.setItem("toggle", "true"); // Save preference in localStorage
State.variables.toggle = true;
}
// Disable dark mode
function disableDarkMode() {
document.body.classList.remove("dark-mode");
localStorage.setItem("toggle", "false"); // Save preference in localStorage
State.variables.toggle = false;
}
// Toggle dark mode on switch interaction
window.switch1 = function (element) {
if (!element) return;
if (element.checked) {
enableDarkMode();
} else {
disableDarkMode();
}
};
Harlowe has been deliberately designed to limit an Author’s ability to use JavaScript to extend the functionality of their project or of the Harlowe runtime engine itself. And Harlowe doesn’t have any JavaScript APIs for accessing the features of its runtime engine.
Your JavaScript code is making reference to a State.variables property, which is part of SugarCube’s State API, which is why it’s not working in Harlowe.
Recent versions of Harlowe 3.x do support access Story & Temporary variables from JavaScript that is executed within a <script> HTML element that is placed within the contents of a Passage…
(set: $thing to "abc")
thing: before: (print: $thing) (should be abc)
<script>
$thing = "def";
</script>
thing: after: (print: $thing) (should now be def)
…but the Story or Temporary variable does need to be initialised some time before it is referenced in JavaScript code.
Depending on the Operating System, the Brand of web-browser, and if Privacy Mode is being used, there are times when localStorage may not be accessible. So if you intend to use it then you will likely need to write additional code to handle any access/authorisation errors that may occur.
And if your Story HTML file is to be run locally from the end-user’s drive, then you may want to add an unique identifier to the Key Name you’re using, so you don’t interfere with data being stored in localStorage be any other such run HTML file.
Harlowe itself does this with the Key Names it uses to store its saves. It adds the unique IFID of the Twine Project to the Key Name. Twine stores the IFID in attributes of the <tw-storydata> element contained within the HTML file.