Extradimensional Haunted Mansion Helper

by MegaScience

HTML

<div class="map-grid">
    <div class="panel bottom middle">
        <h3>General</h3>
        <label><input id="general1" type="checkbox" /> <span class="unsafe">Field of View grows (Leave ASAP)</span></label>
        <label><input id="general2" type="checkbox" /> <span class="unsafe">Field of View shrinks (Leave ASAP)</span></label>
        <label><input id="general3" type="checkbox" /> <span title="Ignore message and keep trying to exit left.">Floor entirely lit; Keep trying to exit left</span></label>
        <label><input id="general4" type="checkbox" /> <span class="safe" title="You can jump on the wall trim where&#13;the note desk used to be to get to&#13;the left exit.">Light goes out briefly and sinkhole appears</span></label>
        <label><input id="general5" type="checkbox" /> <span>Floor numbers on upper level incorrect</span></label>
        <label><input id="general6" type="checkbox" /> <span title="Item is named Fearful Masquerade Mask.">Sad mask on face</span></label>
        <label><input id="general7" type="checkbox" /> <span title="Item is named Scary Masquerade Mask">Angry mask on face</span></label>
    </div>
    <div class="panel bottom left">
        <h3>Bottom-Left</h3>
        <label><input id="bottomleft1" type="checkbox" /> <span class="task" title="Click notes and exit left">Notes don't have Lightbulb Notifier</span></label>
        <label><input id="bottomleft2" type="checkbox" /> <span>No Toy Soldier right of ladder</span></label>
        <label><input id="bottomleft3" type="checkbox" /> <span class="safe task" title="Only anomaly where you don't leave via left portal.">Sophilia guides to a portal. Use it to exit</span></label>
    </div>
    <div class="panel top left">
        <h3>Top-Left</h3>
        <label><input id="topleft1" type="checkbox" /> <span>Extra picture frames around window</span></label>
        <label><input id="topleft2" type="checkbox" /> <span>Upside-down statues on ceiling</span></label>
...

CSS

@counter-style anomalyType {
    system: fixed;
    symbols: "\200B" "🛡️" "⚠️" "📝";
}

:root {
    --primary-text-color: rgb(187, 187, 187);
    --label-border-width: 2px;
    --label-negative-border-width: calc(-1 * var(--label-border-width));
    --prefix-unset: 1;
    --prefix-safe: 2;
    --prefix-unsafe: 3;
    --prefix-task: 4;
}

html, body {
    margin: 0;
    padding: 0;
}

body {
    display: flex;
    flex-direction: column;
    background-color: black;
    color: var(--primary-text-color);
    font-family: Arial, Helvetica, sans-serif;
    counter-reset: anomaliesFound;
}

.map-grid {
    --gap: 3px;
    --columns: 3;
    --rows: 2;

    display: grid;
    grid-template-columns: repeat(var(--columns), 1fr);
    grid-template-rows: repeat(var(--rows), 1fr);
    width: 100vw;
    max-width: 100%;
    aspect-ratio: var(--columns) / var(--rows);

    margin: var(--gap);
    gap: var(--gap);

    .top    { grid-row:    1; }
    .bottom { grid-row:    2; }
    .left   { grid-column: 1; }
    .middle { grid-column: 2; }
    .right  { grid-column: 3; }
}

h3 {
    margin: 0;
    text-decoration: underline white;
}

.panel {
    background-color: rgb(63, 63, 63);
    border: 2px ridge black;
    border-radius: 15px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    padding: 2px;
}

label {
    display: flex;
    align-items: flex-start;
    width: 100%;
    counter-reset: isSafe var(--prefix-unset) isTask var(--prefix-unset);

    input[type="checkbox"] {
        flex-shrink: 0;
    }

    input:checked + span {
        color: rgb(0, 180, 0);
        text-decoration: line-through black 2px;
        -webkit-text-stroke: black 2px;
        paint-order: stroke fill;
        counter-increment: anomaliesFound;
    }

    span {
        flex: 1;

        &.unsafe { counter-set:   isSafe var(--prefix-unsafe); }

       ...

JavaScript

class checkboxControllerClass {
    static storageName = 'maplestory2025EHM'
    static checkboxes = document.querySelectorAll('label input[id][type="checkbox"]')
    static checkboxStorage = {}

    static loadCheckboxes() {
        const data = window.localStorage.getItem(this.storageName)
        if (data !== null) {
            this.checkboxStorage = JSON.parse(data)
            for (const checkbox of this.checkboxes) checkbox.checked = this.checkboxStorage[checkbox.id]
        }
    }

    static saveCheckboxes() {
        for (const checkbox of this.checkboxes) this.checkboxStorage[checkbox.id] = checkbox.checked
        window.localStorage.setItem(this.storageName, JSON.stringify(this.checkboxStorage))
    }

    static init() {
        this.loadCheckboxes()
        for (const checkbox of this.checkboxes) checkbox.addEventListener('input', () => this.saveCheckboxes())
    }
}

checkboxControllerClass.init()