• Forum Moderator applications are now open! If you're interested in joining an active team of moderators for one of the biggest Pokémon forums on the internet, click here for info.

celeste's hobbyist webdevlog

I hope project 5 isn’t another huge jump in complexity and you find yourself with plenty of time for Lumiose sightseeing!
i will be okay regardless, i'm actually already a little ahead, i'm just doing overkill because i really want to autism out and only have daily japanese study to do and basically nothing else for multiple days LOL i did already look at the project 5 requirements and it seems... fine...? if i have any confusion or issues i do have a class session on friday to be asking things. it's our first project with javascript classes (the programming concept, not... not class that i go to at school) and he wants us to built a little "shop" with them but i think it'll probably take less time than this. i'm hoping anyway
 
it has been OVER A MONTH since i have been able to work on personal projects. oh my god. hello

chipping away at Armastide finally!! poor Armastide... it really got away from me. it's still in a really early WIP stage.

you see, armastide's main form of navigation is going to be via clickable image maps, sort of like a click and point adventure game; for example, clicking on a mailbox to get to the contact page. i am the one handling the vast majority of the code and it is a big labor of love because... well, pages and projects across the site will look radically different from each other because it's going to be co-owned by me, my girlfriend, and my girlfriend's QPP (my friend), and it is going to be a hub for pretty much any web project we can think of that isn't better suited on a different website. this means a lot of new CSS and a lot of new scripts to handle different dynamic systems across the whole domain. it is going to be sprawling but it is going to be fun.

we actually have bits and pieces of actual content ready for the site, but we are still working on the initial necessities to get the site off the ground. my to do list to get the website up and running is as follows, more or less:
  • splash page
  • home page (with the first clickable map)
  • contact page
  • an "about me" for each of us three
  • guestbook
  • a sitemap as an alternative way of navigating if the image maps are inaccessible
all of these are at varying steps of complete. for instance, the guestbook was one of the first things i tackled a while back just because i felt like it and it's pretty much entirely up and running, aside from needing to make some more graphical assets for it, of which my friend is doing instead of me. the home page is in an early but functional state with WIP art. and the about me pages, other than the three of us thinking about what we want to put on them, haven't been started at all. etc.

well tonight i decided to tackle really finishing up the guestbook. i'm using this script that i created a few years ago to get a functioning "comment section" on a static website via hooking into google forms/sheets, though i've upgraded it for armastide with a couple sorely missing features: comment moderation (as in comments have to be approved before they show up) and administrator markers (so that people can easily tell when it's me or one of the other two admins commenting). and i've just styled the whole thing like a proper dedicated guestbook instead of a comment section.

to get to the guestbook, the user clicks on a signboard which then leads them to this page:
desktop
1764228795774.jpeg

mobile (i took this screencap on my computer but at mobile resolution lol)
1764229018160.png
forgive me for gushing but i'm really proud of this little project. every time the page is loaded, all the comments have randomized paper sizes and colors and textures, as if they were haphazardly left up on the board with spare materials. just for fun, users can pick a stamp to put onto their message just for a little bit of personal flair. i managed to really make the whole thing responsive so it looks good on any resolution. i am so so happy with it lol.

my work on this guestbook tonight was tidying up the CSS a little bit (i had somehow forgotten to set custom colors for links and set a minimum height for the signboard on incredibly short desktop windows) and then adding the administrator markers, which was as simple as adding another invisible input field into the google form and adding a setting for it into the comment script. it just checks for whether the setting for admin markers is on, and if so, it applies some special class names to any comments with a "yes" in the Admin field, which can be manually changed by simply editing the google sheet. these class names apply the special stamp and the marker next to the name via CSS. nifty!!

as you can see the graphics are still placeholder though. my friend still needs to create actual stamps for us (including the administrator exclusive stamp), a proper illustrated background that isn't just a free-to-use forest image, and a proper administrator icon to go next to admin names. these can all be easily added at a later date and then the guestbook will be fully complete. i did make the wooden siding and the vines myself though. oh, and the paper textures too.

now as for the big thing i worked on tonight: the navigation image maps. i will just call them "maps" from now on, you know what they are lol. i'm going to be honest, i knew that what i wanted to achieve would definitely be possible, but i had no idea how to pull it off when i started working on armastide. as a result, the singular map that was "done" (the artwork is not complete and most of the clickable areas don't actually go to any functional page yet) was all stuffed into one messy HTML page, internal scripts and style and all, just for testing purposes. now that i have a decent sense of how this is all going to work, i needed to start creating the actual dynamic scripting for it that i will be using on the final version of the site. i do not want to have to copy paste the same code and make changes across every single page every time i edit anything, after all.

what i ended up with is super satisfying. the home page map used to be one big messy HTML document but now the entire page looks like this:
HTML:
<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Test Home</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta charset="UTF-8">
        <link href="/general/styles/noscript.css" rel="stylesheet" type="text/css">
        <script src="/general/scripts/navmaps.js" defer></script>
    </head>
    <body>
        <!-- Handled entirely by JavaScript -->
        <noscript>
            <p><em>This website requires JavaScript to function!</em></p>
            <p>Please enable scripts in your browser.</p>
        </noscript>
    </body>
</html>
in the future, this will be just a tad longer for the sake of more meta tags in the head, but the body really can remain that empty, because the "navmaps.js" script in the head is handling building all of the HTML dynamically. for every map i want to make, i can just fill out meta information, put a noscript into the body out of courtesy, and stick that navmaps script in the head and that is quite literally it.

this is my navmaps.js:
JavaScript:
function initResources() {
    const css = document.createElement('link');
    css.type = 'text/css';
    css.rel = 'stylesheet';
    css.href = '/general/styles/navmap.css';
    document.getElementsByTagName('head')[0].appendChild(css);

    const js = document.createElement('script');
    js.src = '/general/scripts/imageMapResizer.js';
    js.type = 'text/javascript';
    js.onload = () => {
        initSitemap();
        initMap();
    }
    document.getElementsByTagName('head')[0].appendChild(js);
}
initResources();

function initSitemap() {
    const sitemap = document.createElement('div');
    sitemap.id = 'sitemap';

    const helper = document.createElement('div');
    helper.id = 'sitemap-helper';
    helper.innerHTML = `
        <p>test test test test test pretend these are sitemap links</p>
    `; // Replace with actual sitemap stuff later
   
    const edge = document.createElement('div');
    edge.id = 'sitemap-edge';
    edge.role = 'button';
    helper.appendChild(edge);

    sitemap.appendChild(helper);

    const script = document.createElement('script');
    script.innerText = `
        document.getElementById('sitemap-edge').addEventListener("click", () => {
            const div = document.getElementById('sitemap');
            if (div.classList.contains('open')) {
                div.classList.remove('open');
            } else {
                div.classList.add('open');
            }
        })
    `; // Replace with actual script later
    script.type = 'text/javascript';
    sitemap.appendChild(script);

    document.getElementsByTagName('body')[0].appendChild(sitemap);
}

function initMap() {
    const path = window.location.pathname.split('/');
    const page = path[path.length - 1].split('.html')[0];

    const clickable = document.createElement('div');
    clickable.id = 'clickable';

    const img = document.createElement('img');
    img.src = `/general/images/maps/${page}/clickable.png`;
    img.alt = "Clickable area";
    img.useMap = '#clickmap';
    clickable.appendChild(img);

    const map = document.createElement('map');
    map.name = 'clickmap';
    function makeArea(name, data) {
        const area = document.createElement('area');
        area.shape = 'poly';
        area.alt = name;
        area.href = data[0];
        area.coords = data[1];
        return area;
    }
    Object.keys(mapData[page]).forEach(e => {
        map.appendChild(makeArea(e, mapData[page][e]));
    });
    clickable.appendChild(map);

    function makeDiv(dir) {
        const div = document.createElement('div');
        div.id = dir;
        div.ariaHidden = true;
        div.style.backgroundImage = `url('/general/images/maps/${page}/${dir}.png')`;
        return div;
    }
    clickable.appendChild(makeDiv('top'));
    clickable.appendChild(makeDiv('left'));
    clickable.appendChild(makeDiv('right'));
    clickable.appendChild(makeDiv('bottom'));

    document.getElementsByTagName('body')[0].appendChild(clickable);

    const resizer = document.createElement('script');
    resizer.innerText = 'imageMapResize();';
    resizer.type = 'text/javascript';
    document.getElementsByTagName('body')[0].appendChild(resizer);
}

const mapData = {
    "home": {
        "Sitemap": ["#", "783, 452, 993, 455, 1010, 684, 764, 680"],
        "Guestbook": ["/general/guestbook.html", "1049, 593, 1208, 582, 1217, 681, 1232, 689, 1240, 787, 1137, 793, 1131, 691, 1058, 689"],
        "Contact": ["#", "16, 739, 103, 714, 224, 716, 234, 848, 169, 847, 183, 1047, 114, 1054, 100, 848, 18, 680"],
        "Cave": ["#", "305, 792, 341, 696, 335, 597, 404, 406, 541, 249, 708, 323, 774, 409, 792, 782"]
    }
}
essentially it is loading the necessary CSS and a singular script dependency to the page... yes i'm breaking my rule on not relying on any other libraries from other people. that script just resizes HTML map/area elements dynamically, it's a tiny script that barely takes up any space and i just did not feel like figuring that out on my own. i have it locally downloaded to my site so i will never lose access to it even if the original source goes down or something (thank you david J bradshaw). after that, it checks what page the user is on (in this case, "home") and uses the coordinate data i've written out in mapData for that page to build all of the necessary HTML to the page. after all of the HTML is there, it executes the image map resizer. and that's it!

now to be clear, some of this is placeholder code. while coding this tonight i was just sitting down to write a script that would give the same exact end result as the original HTML page, which wasn't actually fully done. there is a dropdown sitemap that is loaded on every map and i have the absolute bare minimum proof of concept for that at the moment because i was just focused on making it extend downwards and then go back up when the edge is clicked on. really importantly i also want to make it so clickable areas glow or otherwise have some indicator of being clickable that isn't just the mouse cursor changing to a pointer. and i also do not like how the artwork loads in at the moment, because each map is divided into 5 separate images and they pop into the screen as they load instead of all at once. being divided up like this is necessary because i want this website to be responsive for all devices, so there is a square center to every illustration that is always centered on the page and takes up as much room as possible and this is where all the clickable elements are. then the rest of the viewport is taken up by un-interactable art that serves as "bleed" in both the vertical and horizontal directions, so one above, one to the left, one to the right, and one below. my friend who is illustrating these literally has this ridiculous looking grid template to paint over in which the corners of the grid will never be visible. his canvas is a strange + shape to say the least.

here's the home page visually as of right now (the weird blue bar is the very early WIP sitemap by the way, it drops down when clicked on):
desktop
1764230303937.png


mobile (again technically this screencap is from my computer):
1764230341337.png


and here's a little bit of inspect element to show you what i meant by there being a clickable square in the center of the illustration:
1764230396321.png

moral of the story is i was born to be a backend developer but i keep making this sort of thing work on static websites instead (and without a static site generator) because i have problems. next time i will continue working on my navmaps script probably :bulbaCute:
 
Last edited:
it has once again been over a month since my last update, but i was really busy with finals and the holidays and everything so it's taken me quite a bit to get back to work on my personal projects. i'm on break and working on them now, though!

there is a dropdown sitemap that is loaded on every map and i have the absolute bare minimum proof of concept for that at the moment because i was just focused on making it extend downwards and then go back up when the edge is clicked on. really importantly i also want to make it so clickable areas glow or otherwise have some indicator of being clickable that isn't just the mouse cursor changing to a pointer. and i also do not like how the artwork loads in at the moment, because each map is divided into 5 separate images and they pop into the screen as they load instead of all at once.
so i've gone through and solved everything i was complaining about in my previous post! i will go through all of the points in order


1. the sitemap
a couple days ago i sat down and finished programming the sitemap all in one sitting. it isn't visually finished yet (as in, i still need to make assets and adjust the CSS to make it look like a scroll and not the mess it is right now) but it is fully finished being programmed otherwise so i can plug-and-play the art and colors whenever i want later.
1767454152499.png

the sitemap now builds itself from a "linkData" object which contains all the link categories and URLs, so i don't need to make changes to every single page to update the sitemap since that would be terrible. actually building the links was not the difficult part whatsoever, but i did spend more time than i expected on the sitemap because i wanted it to be properly accessible and responsive. after a lot of tinkering i was able to get the sitemap fully responsive to varying browser zoom levels and also easy to navigate by keyboard. the media queries that handle resizing/repositioning the sitemap based on screen size are now handled with rem/em units (i formed a terrible habit early on of using px for everything even when rem/em would be better so i am still learning to use them more naturally) so zooming in will eventually swap the sitemap to the "mobile" view which takes up much more screen real-estate, etc. and i made it so that when a keyboard user tabs into the page, the sitemap is the first thing that gets focused (not the links which are hidden off-screen at this time) and it glows brightly in response, and when interacted with by keyboard key instead of clicking, it drops down like normal but jumps the focused element to the first link in the list of links.

i still need to make it look visually not... like that, but that shouldn't take very long. i just need to draw the rolled up end of a scroll and a little ribbon sticking out for the bottom, and a paper-y edge for the sides. then i just need to adjust the colors so it's uh, paper colored and not blinding red haha


2. clickable area indicators
this was a weird process honestly! i set up a testing map that looks like this for the sake of... well, testing
1767457657950.png

i decided i wanted to achieve the following:
  • clickable objects need a baseline amount of glow at all times. having a clickable object only be indicated via hovering is both inaccessible and just straight up impossible for anyone using a phone to access the website
  • hovering over an object with the mouse or focusing an object with the keyboard should cause the glow to intensify, indicating that the user is about to interact with it if they want. on phones this just appears as a brief glow before the page changes but that is fine
  • it would also be nice if there was some sort of cursor effect when hovering just for more visual flair
doing all of this was a little weird because i am using HTML's built-in image maps feature (<map> and <area>) which has inherent semantic value in the HTML document instead of me needing to program a custom system that makes it clear what the purpose of the image map is. however, it doesn't come with built-in visual features like this, so those are what i had to script out myself.

the baseline amount of glow was easy; i simply asked the site's main artist @TeamCapumon (hi) to add a glow-y outline to all the interactable stuff within the art itself. simple! adding the extra glow when hovering/focusing an object was a little more complicated though. image maps utilize a single image for the map, not multiple in layers, so it's not like i could just tell the site to change the square, arrow, or diamond images individually. my solution to this problem feels a little silly, but it works great in practice; i have an <img> that perfectly overlaps the main map image with an id of "overlay" that functions as... well... an overlay. within the image directory for the map which includes the center "clickable" image as well as the top/left/right/bottom bleed illustrations, i simply save different versions of the clickable area which has all the different objects highlighted and i name them the same thing as the title of the area link within the code. then, when any area is hovered or focused, it swaps the source of the overlay image to the appropriately identically named .PNG and sets its opacity to 1. when it is un-hovered/unfocused the opacity is set back to 0. this has a nice smooth glowing effect because i set up a CSS transition to fade the opacity changes in and out. it's slightly annoying to have to save separate versions of the artwork for this, but it's not a big deal because the aforementioned site artist puts the glow on its own layer, so it's very easy to change on the fly.

as for the cursor effect, i decided on a pulsing white circle.
1767459291831.png

i didn't feel like recording a gif for this explanation so imagine that this glowy circle is pulsing slightly. don't worry, it's not fast or intense enough to cause eyestrain! anyway, the way i handled this is pretty straightforward. there's a typically hidden (AKA display: none;) div on the document called the "follower", because it follows the user's mouse. this div is a 30px sized square with a transparent background color and 50% border radius which causes it to appear as a transparent circle. i then used CSS keyframes to animate the infinite pulsing glow effect, which utilizes box-shadow, ironically. i'll share my snippet of CSS for this, it's very simplistic:
CSS:
/* Following cursor */
#follower {
    position: absolute;
    z-index: 99999;
    pointer-events: none;
    width: 30px;
    height: 30px;
    background-color: transparent;
    border-radius: 50%;
    animation: follower-glow 1.5s infinite;
}
@keyframes follower-glow {
    0% {box-shadow: 0px 0px 10px 5px rgba(255,255,255,0.9);}
    50% {box-shadow: 0px 0px 10px 10px rgba(255,255,255,0.9);}
    100% {box-shadow: 0px 0px 10px 5px rgba(255,255,255,0.9);}
}
then, when each area of the image map is generated by the navmaps script, it also applies event listeners for the mouse moving over the area, which causes the follower div to become visible and also move to the position of the mouse, offset a little bit for centering. here's the code for that as well (though i redacted anything to do with the aforementioned overlay image to make it easier to read):
JavaScript:
area.onmouseenter = () => {
    document.getElementById('follower').style.display = 'inline-block';
}
area.onmouseleave = () => {
    document.getElementById('follower').style.display = 'none';
}
area.addEventListener('mousemove', (e) => {
    const follower = document.getElementById('follower');
    const x = e.clientX - (30 / 2);
    const y = e.clientY - (30 / 2);
    follower.style.left = x + 'px';
    follower.style.top = y + 'px';
});
i'm really happy with the result of all of this; it almost feels like a video game and i keep hovering over my own maps and feeling happy with myself lol


3. pre-loading images
all of the images used in one of these map pages is already dynamically inserted by the navmaps script, so it was pretty easy to push all of them into an array and have a function that loops through the array every 200 milliseconds to see if the images are fully loaded yet. it's not the prettiest solution, but it works, and that's what matters, though i may go back and add a loading screen of some sort for slower connections so they aren't sitting on a black screen for too long without any indication of what's going on.

once all the images are loaded, the timer interval stops checking for loaded images, and the whole map is set to full opacity, which has a nice fade-in effect because of CSS transitions. if all the images in the map are already in the cache, this causes the fade-in to happen immediately upon page load, which looks pretty nice! this is also when all of the map areas are made clickable and the image map resizer is added to the page and executed.

if anything somehow goes wrong during this process, it should be noted that the sitemap is added to the page separately from all of this, so it's always there regardless of the state of the image map being loaded and can be used regardless.


that's about it i think? my solution here is really nice because the navmaps script checks the currently accessed map and loads everything dynamically for me, so the actual HTML documents are pretty bare-bones aside from a <noscript> tag to tell the user to turn on scripts if they're off for whatever reason, and meta tags in the document head. i did remember this morning that browsers caching the script would be an issue though! because uh, the sitemap would get stuck on an old version even when links are added to it and stuff like that. so i amended the embedded script on every map document to be a different script that loads the navmaps script with a dynamic timestamp appended to it, which forces the browser to reload it every time. i don't mind using this solution because the navmaps script is pretty lightweight.

i've also probably touched up some other parts of the site in the past month that i'm forgetting right now but that's alright haha. next time i need to start making those darn about me pages and put the splash page together!!
 
Please note: The thread is from 6 months ago.
Please take the age of this thread into consideration in writing your reply. Depending on what exactly you wanted to say, you may want to consider if it would be better to post a new thread instead.
Back
Top Bottom