Contents, Search, SVGs and Github Mobile

Mobile search and contents, svg descriptions and other stuff

This is a bit of a bigger one, I’ve been doing a lottt of tinkering to the site instead of ya know, playtesting the game.

Mobile Contents

Well, as there was already an inbuilt scrollspy contents (one that tracks which heading you’re up to on the way down your page) for desktop inbuilt to this theme, I thought there must be a way to display that simply (enough). There was.

I started banging my head against the wall when I couldn’t easily pull .rightsidebar up from the bottom…then I realised I’d already done the hard work in my previous site https://mechanicalpencil.art. All I needed to do is reenable it and make it display the contents not the tags and categories.

I’m pretty happy with how it’s working. Currently takes up 50% of the screen, debating doing a little more, but also want it to still show enough content you can quickly judge if it’s the right info.

I haven’t decided whether the ‘Back to top’ buttons are now a bit redundant. Maybe.

Mobile Search

Well. This was certainly not something I thought I would pump out in the past two days but look, here we are. I love using the ol’ ctrl+find on a web page, but unless you go looking for it in a mobile browser, you mightn’t think to use it. I dunno. I’ve never used it on a mobile browser before, I have just checked - it’s definitely there, it is. Ah well. I made a thing that does that without having to press two extra buttons.

First up, I got it to search the entire body of the article.

Then add a highlight to all of the elements it found.

Prettify the css and styling to make sure the highlight stayed in place of where the original element was.

This included saving then replacing the article content text.

Add search for headings and lists (ul and li elements).

Added buttons to jump up and down once items were found. I decided the upper third was a good centering area.

Made sure the buttons only appeared after you’d selected the search box.

Only start highlighting once 3 or more letters have been typed.

Fix up everything that had broken when the search starts replacing elements on screen. Most notably:

  • .img
  • .svg
  • href

Made the script skip any elements that had a ‘rect’ size of 0 (e.g. hidden elements etc)

What it looks like after two days of chaos 27-08-2024

Oh and a bit more styling on the buttons and positioning with the contents button etc.

I’m pretty stoked with it. Moving on:

The code

You know you want it. Here it is, free for use though please attribute. Yes it was mostly coded by AI, but it took a fair bit of prompting to add and refine all the above things:

The code
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
document.addEventListener('DOMContentLoaded', (event) => {
    const contentElements = document.querySelectorAll('.article-content p, .article-content li, .article-content ul, .article-content h1, .article-content h2, .article-content h3, .article-content a');
    const originalTexts = [];
    let highlightedElements = [];
    let currentIndex = -1;

    // Store the original text of each content element
    contentElements.forEach((element, index) => {
        originalTexts[index] = element.innerHTML;
    });

    function findOnPageMobile() {
        let input = document.getElementById('searchInput').value.toLowerCase();
        console.log(`Search input: ${input}`);
        highlightedElements = [];
        currentIndex = -1;

        contentElements.forEach((element, index) => {
            let originalText = originalTexts[index];
            let text = originalText.toLowerCase();

            // Only highlight if input length is 3 or more characters
            if (input.length >= 3 && text.includes(input)) {
                let regex = new RegExp(`(${input})`, 'gi');
                let newText = originalText.replace(regex, (match, p1, offset, string) => {
                    // Check if the match is inside an img, svg, or href attribute
                    const beforeMatch = string.slice(0, offset);
                    const afterMatch = string.slice(offset + match.length);
                    if ((beforeMatch.includes('<img') && afterMatch.includes('>')) ||
                        (beforeMatch.includes('<svg') && afterMatch.includes('</svg>')) ||
                        (beforeMatch.includes('href="') && afterMatch.includes('"'))) {
                        return p1;
                    }
                    return `<span class="highlight">${p1}</span>`;
                });

                // Preserve the href attribute of <a> tags and wrap the inner text
                if (element.tagName.toLowerCase() === 'a') {
                    const href = element.getAttribute('href');
                    const linkText = element.innerHTML.replace(regex, `<span class="highlight">$1</span>`);
                    element.outerHTML = `<a class="link" href="${href}">${linkText}</a>`;
                } else {
                    element.innerHTML = newText;
                }

                // Collect highlighted elements
                element.querySelectorAll('.highlight').forEach(highlight => {
                    highlightedElements.push(highlight);
                });
            } else {
                element.innerHTML = originalText;
            }
        });

        // Show scroll buttons if there are highlights and input is not empty
        const scrollButtons = document.getElementById('scrollButtons');
        scrollButtons.style.display = (highlightedElements.length > 0 && input.length > 0) ? 'flex' : 'none';
    }

    document.getElementById('searchInput').onkeyup = findOnPageMobile;

    // Initially hide the scroll buttons
    document.getElementById('scrollButtons').style.display = 'none';

    // Scroll functions
    document.getElementById('scrollUp').onclick = () => {
        if (highlightedElements.length > 0) {
            do {
                currentIndex = (currentIndex - 1 + highlightedElements.length) % highlightedElements.length;
                console.log(`Scrolling up to element ${currentIndex}`);
            } while (!scrollToHighlight(highlightedElements[currentIndex]));
        }
    };

    document.getElementById('scrollDown').onclick = () => {
        if (highlightedElements.length > 0) {
            do {
                currentIndex = (currentIndex + 1) % highlightedElements.length;
                console.log(`Scrolling down to element ${currentIndex}`);
            } while (!scrollToHighlight(highlightedElements[currentIndex]));
        }
    };

    function scrollToHighlight(element) {
        const rect = element.getBoundingClientRect();
        const offset = window.innerHeight / 3; // Upper third of the screen

        // Check if the element's rect values are valid
        if (rect.top === 0 && rect.bottom === 0 && rect.left === 0 && rect.right === 0) {
            console.log(`Skipping element with invalid rect: ${element.innerHTML}`);
            return false;
        }

        const scrollPosition = window.scrollY + rect.top - offset;
        console.log(`Element rect: ${JSON.stringify(rect)}`);
        console.log(`Window scrollY: ${window.scrollY}`);
        console.log(`Calculated scroll position: ${scrollPosition} for element: ${element.innerHTML}`);
        
        window.scrollTo({
            top: scrollPosition,
            behavior: 'smooth'
        });
        return true;
    }
    
    // Hide scroll buttons when clicking outside the search box and buttons
    document.addEventListener('click', (event) => {
        const searchInput = document.getElementById('searchInput');
        const scrollButtons = document.getElementById('scrollButtons');

        if (!searchInput.contains(event.target) && !scrollButtons.contains(event.target)) {
            scrollButtons.style.display = 'none';
        }
    });

    // Show scroll buttons when clicking back onto the search box
    document.getElementById('searchInput').onfocus = () => {
        document.getElementById('scrollButtons').style.display = 'flex';
    };
});

SVG Descriptions

So I could’ve sworn I added this a while back, but then working between two laptops sometimes causes some sync issues every now and again and I’ll close a file without hitting save and/or push.

Anyway, the svg.html file now looks like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{{ $path := print "img/svgs/" (.Get 0) }}
{{ $svg := resources.Get $path }}
{{ $svgContent := $svg.Content | safeHTML }}
{{ $class := .Get 1 }}
{{ $description := .Get 2 }}

<div class="{{ $class }}">
    {{ $svgContent }}
    {{ if $description }}
        <div class="svg-description">{{ $description }}</div>
    {{ end }}
</div>

And using the shortcode to embed the svg image looks like this:

1
{{ < svg "image-title.svg" "class-name" "description" > }}

Then, as long an image with that name is in the assets/img/svgs folder, it’ll be rendered inline on the page, wrapped in the class with the attached description. I’ve got my Export For Screens from Illustrator targeting that exact folder, so can make updates pretty quick and easy.

This is great for accessibility and ease of use.

Colours

I need to fix these. Realised this when my phone went into greyscale mode before bed. It’s almost impossible to distinguish the blue and pink I’ve been using.

Should probably go like a 10, 50, 90 in greyscale for my 3 biomes, then a 30, 60 in greyscale for my playing pieces.

Then dogs can play! Or whichever animals it is that are colourblind. I swear it’s famously dogs. ANYWAY.

I’ll get back to that and properly layering them at some point.

There’s a lot of diagrams (almost 50) now.

I think they’re useful.

Github Mobile

One thing that had been bugging me about my current workflow, was that if I had an idea, I wasn’t able to quickly update a rule or write a blog post directly on my phone.

I then had the gen10u5 idea of checking whether there was a version of Github for Mobile - There is. Probably adds another potential layer of security flaws to my site, but hey, what can ya do, I just hope no one is feeling too malicious.

Github Mobile - Checking on the pages of the site Github Mobile - Edit a file

Ok, so it doesn’t have a rich code editor, I can’t copy and paste full folders/select multiple items and copy around. But adding a # for headings and a - for lists is easy enough!

There’s probably a hacky way to add this in, but it’s more for jotting down a quick note in the same place I work off my laptop. I’ll try it for a bit, may revert back to Google Keep as I have that widgetted up to type a quick note.

Oh and another reason

Well that’s it really, I love the version control I can run. Particularly to check across multiple pages and see line for line what wording I’ve changed if I’ve accidentally pushed to versions at any one point.

Keep is very impressive at updating in the blink of an eye across all devices so it’s much of a muchness there.

VS Code Extensions

Here’s a few cheeky things I’ve added onto my VS Code just to make things a little easier - I have no idea why I hadn’t tried searching Hugo before. This is the third proper site I’ve built here, these could have saved a few headaches in the past:

This has just made viewing the md files so much cleaner. I like things to be in obvious different colours, helps me stay focused on the things I need to focus on.

Other Extensions

The other main ones I use are for Markdown files and HTML. Just makes everything wayyy nicer. If anyone is ever interested I’m sure I can compile a list easily.

Licensed under CC BY-NC-SA 4.0
Last updated on Aug 27, 272712 00:00 AEST

Lava your comments here

I acknowledge the Wurundjeri Woi-wurrung people as the Traditional Owners of the lands and waterways on which this idea was brought to life, and pay my respect to the wisdom of their Elders past and present.
A boardgame designed by Alex Barnes-Keoghan
Built with Hugo, Theme Stack designed by Jimmy
Parallax stars effect by Sarazond, hexagonal background by Temani Afif
GAME TESTED BY// Ruby Benjy Amy Toby Hugh Liam Kumal Ben Sam Huon Jonathan The Melbourne Incubator Sonya