celeste's hobbyist webdevlog

terashards

please watch pokemon horizons
Joined
Aug 6, 2025
Messages
780
Reaction score
1,539
Pronouns
  1. She/Her
gee celeste who let you have TWO blogs...

sooo i do web development as a hobby. i got started with neocities (a sort of spiritual successor to geocities) towards the end of 2021 and it unlocked a passion within me that i didn't know i had. i plan on making this into my career and going professional one day, but even then, the stuff i do on neocities is a lot different from my future professional work. there are a variety of modern coding languages and frameworks to make web development easier and more professional, but for my own personal enjoyment, i like to keep my hobbyist projects simple. i write vanilla HTML/CSS and JavaScript from scratch wherever possible, minimizing my use of libraries and other frameworks. i like testing the boundaries of what i'm personally capable of creating this way. additionally, i still make all my web projects on neocities, so my websites are static and don't have a proper backend. this is the one thing i'd like to change in the future so i can make even more interactive projects, but not right now.

i'm going to use this blog to talk about my personal web projects and give updates on what i'm working on.



here are all my websites at the moment:

Blue Moon Falls

https://bluemoonfalls.com/
BMF is the big one, and the first website i made on neocities with almost 0 knowledge of how to code. it's a fansite for the generation 1 and 2 pokemon games, aiming to provide modern information and resources for them because of how poor the documentation and tools for them can be. i initially set out to write a lot of articles, though i quickly found by working on the site that i love working with javascript, and so there ended up being a whole lot of interactive tools as well. there is a lot of pre-existing content on BMF already, so if you want to peruse any of our websites, this is definitely the one to look around on at the moment. any messy code you may see while looking under the hood can be blamed on me learning as i go while making this site. only one of our alters (Ayano) works on this website and it's her baby, so depending on how much she's around, updates can become infrequent, but BMF will always be around.


60minlkdt
sitebutton.gif

https://60minlkdt.neocities.org/
this is the hub site for 60minlkdt, an art/writing challenge i run for the likodot community every other saturday. a prompt is selected and people make likodot fanart and fanfiction based on that prompt. like the name implies, submissions must be completed in 60 minutes (an hour) or less. i decided to make a website for it because 60minlkdt is held on multiple social media sites and it would be a nightmare to consolidate all the entries in one place otherwise. it doesn't need actual updates to its code very often - most of my maintenance is just adding new submissions to the gallery every other week - but i still have to occasionally touch up the code for whatever reason. 60minlkdt has been running since october 2024, so there's a lot of likodot work on the site!


Virtual Observer (inactive)

https://virtualobserver.moe/
this was my own personal website, but i ended up detaching from it quite a bit in recent times. maybe still worth looking at the old thing even if only for the visuals though, because i still really like how this site looks even if it's pretty barren in terms of content. anyway, it's okay that i lost interest in this personal site, becauuuuse...


Armastide (WIP)
(the website isn't live yet)
armastide is a replacement for virtual observer as my personal site, but it's also more than that. the site is going to be co-owned between me, my girlfriend, and my girlfriend's QPP (who is also my close friend). it will be a personal site for all of us and host a variety of personal projects and creative works and really whatever we feel like. this website is my main development focus right now as i am handling pretty much all of the code, and it will be a future hub for all my random other web stuff, so i really want to get it up and running to put all that stuff somewhere! also likely what i'll talk about most in this blog for the foreseeable future.




by the way, i often get asked how to get started with neocities/making personal websites, and unfortunately... i'm maybe not the best person to ask, because i tend to learn through the deranged method of bashing my head against the wall (google/stack overflow) to get answers on how to make the things i want to make until i've successfully made them. it's a very brute-force kind of way to learn. that being said, i can provide a few tips for the newbie webdev hobbyist:
  • i've talked about neocities already, but really, use neocities! it's super beginner-friendly and straightforward and has a community of browsable sites by people who are also starting out just like you. i still use this years later after starting. (there is also nekoweb which serves a similar purpose, but i personally think neocities is less intimidating to get into, and it also has been around longer so it has more of a track record)
  • download software actually meant for editing code and do not just use a notepad file. software made for coding highlights the different parts of your code different colors which makes it easier to read and tell what's going on. most of them will also let you know where there's errors in your code. i personally use Visual Studio Code, as do many others, but if that's too overwhelming to start with, Notepad++ is incredibly simple and lightweight while still providing basic syntax highlighting and some other functionality. i heavily recommend that you do NOT use neocities' on-site editor. it's simplistic to the point of fault and it's better to prepare your website updates in advance and then upload them through whatever method you prefer instead of constantly updating your neocities site.
  • the MDN Web Docs is your best resource for looking up what different things do in HTML/CSS/JavaScript, though w3schools also exists and is on the simpler side. both sites actually have decent written HTML/CSS beginner tutorials, too!
  • there are a million different tutorials to learn the fundamentals of HTML (which is what you should learn first), and you can find a giant dump of them by googling "HTML tutorial". you should just pick something that looks interesting for you and suits your learning style, but if you'd like a personal recommendation, i think Codecademy's free HTML course does a good job of walking you through the fundamentals. it's hands-on and lets you write and test code directly into the browser as you learn and follow steps.
  • looking at other people's code to learn how they do things can also be a genuinely great learning tool. you can open inspect element (ctrl+shift+i on firefox, unsure about other browsers) to look at the code for any website. unfortunately a lot of modern websites are super bloated and hard to parse, but if you look at other people's neocities sites, it should be a lot cleaner and easier to read. there's also a lot of "layout builders" that will generate basic templates for websites which you can look through or start with, like sadgrl's here.
in any case: you really just gotta get started. crack open an HTML document and a tutorial or google and start writing!
 
Last edited:
(Chanting) Devlog! Devlog! Devlog!
Super excited to see how this goes!
i'm maybe not the best person to ask, because i tend to learn through the deranged method of bashing my head against the wall (google/stack overflow) to get answers on how to make the things i want to make until i've successfully made them. it's a very brute-force kind of way to learn.
I totally understand this LOL I've done the same... My code is extremely messy but hey, if it works it works! Rubber duck debugging is good practice, too.
 
  • Thread starter
  • Staff
  • #3
i've been working on a tool that allows you to easily look up pokemon moves for the sake of contests and it's pretty much done except for the CSS... so i've been tackling that today. had to make the input form not look ugly as all hell
still using some default browser colors that also don't accommodate for light/dark mode yet, just trying to get a feel for spacing out everything
1755041047521.png
i'm going to be honest, i've really been dreading doing the CSS work on this project because i just haven't been feeling working with CSS as much lately but it feels good to be nearing completion. here's the snippet of CSS that handles this form just because i think it's decently clean/nice to look at...
you can probably tell i've been learning to make better use of :has() recently lol
CSS:
/*
    Form at top
*/
div:has(> #pokeimghelper) {
    display: table;
    height: 200px;
    width: 200px;
    margin: auto;
    text-align: center;
}
#pokeimghelper {
    display: table-cell;
    vertical-align: middle;
}
#pokeimg {
    image-rendering: pixelated;
    transform: scale(2, 2);
}

form {
    max-width: 700px;
    width: 100%;
    margin: auto;
    padding: 10px;
    text-align: center;
    border-radius: 10px;
    background-color: darkslateblue;
}

form:has(> div) {font-size: 0;} /* Removes whitespace between inline-block inputs */
form h1 {font-size: 22px;}
form h2 {font-size: 16px;}
legend, label, input {font-size: 14px;}

form h1, form h2 {
    margin-bottom: 10px;
    color: white;
}
fieldset, div:has(> label) {
    border: none;
    border-radius: 10px;
    background-color: steelblue;
    margin: 4px;
    padding: 4px;
}

legend {
    float: left; /* Fixes default legend spacing */
    width: 100%;
}
legend, label:has(+ select) {
    font-weight: bold;
    color: white;
}
label:has(+ input:disabled) {opacity: 50%;}

select {
    display: block;
    margin: auto;
    margin-top: 4px;
}
#pokemon {width: 30%;}
#filter {width: 50%;}
@media (max-width: 700px) {
    #pokemon {width: 50%;}
    #filter {width: 90%;}
}

form div:has(> input[type="checkbox"], > input[type="radio"]) {
    display: inline-block;
    margin: 4px;
    padding: 4px;
    border-radius: 4px;
    background-color: white;
}

form hr {
    margin: 10px 0px 10px 0px;
    border: none;
    border-top: 1px dashed white;
}

i have to touch up a few things and then i actually need to lock in and do the proper light mode and dark mode colors LOL. it's going to be a lot because the contest moves are red/blue/pink/green/yellow for the cool/beauty/cute/smart/tough categories respectively (plus the BDSP contest moves which have no category) so i have design separate light/dark mode colors for... all of that

i'm probably going to throw this thing on armastide and at least hand it to the ribboning community even before armastide truly launches as a site just because there's no reason to sit on this and not let people use it... lol



...i got sniped while writing this update! that's funny
(Chanting) Devlog! Devlog! Devlog!
Super excited to see how this goes!

I totally understand this LOL I've done the same... My code is extremely messy but hey, if it works it works! Rubber duck debugging is good practice, too.
thank you! my code used to be extremely messy... i try these days to make things cleaner but it can still be a little bit of a fight not to have everything devolve into spaghetti, particularly with more complex javascript stuff LOL. i feel you so much (i actually clicked on your site in your signature last night out of curiosity but i ended up being too tired to properly look around. i'll definitely do that later!!)

anyway, i have to rip myself away from my code to eat dinner now, even though i just want to sit here and keep working lol
 
  • Thread starter
  • Staff
  • #4
so i kind of spent my entire day finishing up the contest move tool project. it's live, so you can try it out! https://armastide.net/celeste/ribbonguide/contest-widget
1755127796098.jpeg
the bulk of my time was spent messing with the light and dark mode css - i am NOT a ux designer, and i kind of had no idea what i was doing. just trial-and-errored my way into making something that i thought looked presentable lol.

somehow, i haven't worked with nested CSS before...?? it felt very applicable here, so i tried it out. there was probably some sort of solution for this i could have written in javascript that wouldn't have required as much repetition with the cool/beauty/cute/smart/tough categories, but uh... that would have taken longer for me to logic out than just simply writing the CSS plain, so...
CSS:
/*
    Light mode
*/
.light {
    background-color: var(--l-surface-10);
    color: var(--black);

    hr {border-color: var(--l-surface-50);}

    a:link, a:visited {color: var(--black);}
    a:hover, a:active {color: var(--l-surface-50);}

    form {
        background-color: var(--l-surface-0);
        border-color: var(--l-surface-50);
    }
    fieldset, div:has(> label) {background-color: var(--l-surface-30);}
    form div:has(> input[type="checkbox"], > input[type="radio"]) {background-color: var(--l-surface-0);}
    select {
        color: var(--black);
        border-color: var(--l-surface-50);
        background-color: var(--l-surface-0);
    }

    .contest {
        background-color: var(--l-surface-0);
        border-color: var(--l-surface-50);

        &.combo {
            &:has(.con-cool:nth-child(2)) {border-color: var(--cool-600);}
            &:has(.con-beauty:nth-child(2)) {border-color: var(--beauty-600);}
            &:has(.con-cute:nth-child(2)) {border-color: var(--cute-600);}
            &:has(.con-smart:nth-child(2)) {border-color: var(--smart-600);}
            &:has(.con-tough:nth-child(2)) {border-color: var(--tough-600);}
            &:has(.con-none:nth-child(2)) {border-color: var(--l-surface-50);}
        }

        [class$="-cool"] {td, th {border-color: var(--cool-600);}}
        [class$="-beauty"] {td, th {border-color: var(--beauty-600);}}
        [class$="-cute"] {td, th {border-color: var(--cute-600);}}
        [class$="-smart"] {td, th {border-color: var(--smart-600);}}
        [class$="-tough"] {td, th {border-color: var(--tough-600);}}
        [class$="-none"] {td, th {border-color: var(--l-surface-50);}}

        .con-cool {td, th {
            background-color: var(--cool-100);
            &.appeal span {color: var(--cool-500);}
        }}
        .con-beauty {td, th {
            background-color: var(--beauty-100);
            &.appeal span {color: var(--beauty-500);}
        }}
        .con-cute {td, th {
            background-color: var(--cute-100);
            &.appeal span {color: var(--cute-500);}
        }}
        .con-smart {td, th {
            background-color: var(--smart-100);
            &.appeal span {color: var(--smart-500);}
        }}
        .con-tough {td, th {
            background-color: var(--tough-100);
            &.appeal span {color: var(--tough-500);}
        }}
        .con-none {td, th {
            background-color: var(--l-surface-10);
            &.appeal span {color: var(--l-surface-50);}
        }}

        .effect-cool td {background-color: var(--cool-50);}
        .effect-beauty td {background-color: var(--beauty-50);}
        .effect-cute td {background-color: var(--cute-50);}
        .effect-smart td {background-color: var(--smart-50);}
        .effect-tough td {background-color: var(--tough-50);}
        .effect-none td {background-color: var(--l-surface-0);}

        [class^='learn-'] li {background-color: var(--l-surface-0);}
        [class^='learn-'] a:link, [class^='learn-'] a:visited {color: var(--black);}

        .learn-cool ul {
            background-color: var(--cool-600);
            a:hover, a:active {color: var(--cool-600);}
        }
        .learn-beauty ul {
            background-color: var(--beauty-600);
            a:hover, a:active {color: var(--beauty-600);}
        }
        .learn-cute ul {
            background-color: var(--cute-600);
            a:hover, a:active {color: var(--cute-600);}
        }
        .learn-smart ul {
            background-color: var(--smart-600);
            a:hover, a:active {color: var(--smart-600);}
        }
        .learn-tough ul {
            background-color: var(--tough-600);
            a:hover, a:active {color: var(--tough-600);}
        }
        .learn-none ul {
            background-color: var(--l-surface-50);
            a:hover, a:active {color: var(--l-surface-50);}
        }     
    }
}

/*
    Dark mode
*/
.dark {
    background-color: var(--d-surface-10);
    color: var(--white);

    hr {border-color: var(--d-surface-50);}

    a:link, a:visited {color: var(--white);}
    a:hover, a:active {color: var(--d-surface-50);}

    form {
        background-color: var(--d-surface-0);
        border-color: var(--d-surface-50);
    }
    fieldset, div:has(> label) {background-color: var(--d-surface-30);}
    form div:has(> input[type="checkbox"], > input[type="radio"]) {background-color: var(--d-surface-0);}
    select {
        color: var(--white);
        border-color: var(--d-surface-50);
        background-color: var(--d-surface-0);
    }

    .jam span {color: var(--black);}

    .contest {
        background-color: var(--d-surface-0);
        border-color: var(--d-surface-30);

        &.combo {
            &:has(.con-cool:nth-child(2)) {border-color: var(--cool-700);}
            &:has(.con-beauty:nth-child(2)) {border-color: var(--beauty-700);}
            &:has(.con-cute:nth-child(2)) {border-color: var(--cute-700);}
            &:has(.con-smart:nth-child(2)) {border-color: var(--smart-700);}
            &:has(.con-tough:nth-child(2)) {border-color: var(--tough-700);}
            &:has(.con-none:nth-child(2)) {border-color: var(--d-surface-10);}
        }

        [class$="-cool"] {td, th {border-color: var(--cool-600);}}
        [class$="-beauty"] {td, th {border-color: var(--beauty-600);}}
        [class$="-cute"] {td, th {border-color: var(--cute-600);}}
        [class$="-smart"] {td, th {border-color: var(--smart-600);}}
        [class$="-tough"] {td, th {border-color: var(--tough-600);}}
        [class$="-none"] {td, th {border-color: var(--d-surface-40);}}

        .con-cool {td, th {
            background-color: var(--cool-900);
            &.appeal span {color: var(--cool-200);}
        }}
        .con-beauty {td, th {
            background-color: var(--beauty-900);
            &.appeal span {color: var(--beauty-200);}
        }}
        .con-cute {td, th {
            background-color: var(--cute-900);
            &.appeal span {color: var(--cute-200);}
        }}
        .con-smart {td, th {
            background-color: var(--smart-900);
            &.appeal span {color: var(--smart-200);}
        }}
        .con-tough {td, th {
            background-color: var(--tough-900);
            &.appeal span {color: var(--tough-200);}
        }}
        .con-none {td, th {
            background-color: var(--d-surface-20);
            &.appeal span {color: var(--white);}
        }}

        .effect-cool td {background-color: var(--cool-950);}
        .effect-beauty td {background-color: var(--beauty-950);}
        .effect-cute td {background-color: var(--cute-950);}
        .effect-smart td {background-color: var(--smart-950);}
        .effect-tough td {background-color: var(--tough-950);}
        .effect-none td {background-color: var(--d-surface-0);}

        [class^='learn-'] a:link, [class^='learn-'] a:visited {color: var(--white);}
        
        .learn-cool {
            li {background-color: var(--cool-900);}
            ul {
                background-color: var(--cool-600);
                a:hover, a:active {color: var(--cool-100);}
            }
        }
        .learn-beauty {
            li {background-color: var(--beauty-900);}
            ul {
                background-color: var(--beauty-600);
                a:hover, a:active {color: var(--beauty-100);}
            }
        }
        .learn-cute {
            li {background-color: var(--cute-900);}
            ul {
                background-color: var(--cute-600);
                a:hover, a:active {color: var(--cute-100);}
            }
        }
        .learn-smart {
            li {background-color: var(--smart-900);}
            ul {
                background-color: var(--smart-600);
                a:hover, a:active {color: var(--smart-100);}
            }
        }
        .learn-tough {
            li {background-color: var(--tough-900);}
            ul {
                background-color: var(--tough-600);
                a:hover, a:active {color: var(--tough-100);}
            }
        }
        .learn-none {
            li {background-color: var(--d-surface-10);}
            ul {
                background-color: var(--d-surface-30);
                a:hover, a:active {color: var(--d-surface-50);}
            }
        }
    }
}
the list of moves can also get really long, so i opted to add a "scroll to top" button in the bottom right corner. i wanted to have the button only appear when you've actually scrolled down some, and a scroll event listener seemed like a massive waste of power because the user is expected to scroll very often, which led me to learning that IntersectionObserver is a thing. it ended up being very easy and convenient to use! when the form gets mostly out of view, it adds the .visible class to the button, which makes it CSS transition its way onto the screen.
JavaScript:
// Back to top button
const observer = new IntersectionObserver(function (e) {
    if (e[0].isIntersecting) {
        document.getElementById('backtotop').className = '';
    } else {
        document.getElementById('backtotop').className = 'visible';
    }
}, {threshold: 0.4});
observer.observe(document.getElementsByTagName('form')[0]);

function scrollToTop() {
    scrollTo({top: 0, behavior: 'smooth'})
}
i would say more, but frankly i'm tired and my brain is fried from this long day of fixation and trying to get this done. so uh, i guess that's it for this update, but if you happen to click around the tool, let me know if you run into any major issues!
 
  • Thread starter
  • Staff
  • #6
last night i sent the contest move tool to various relevant places and i got the request from toasty in the ribbon master discord to have the appeal combos sort by the appeal of their second moves (because higher number = better, usually lol)

i didn't get to it last night because i was exhausted but today i threw together a very quick bit of code on top of what i already have to do that. i will spare you the javascript that actually moves the tables into the right position in the document because it's a stupid and ugly solution, but i like the little solution for determining the highest number appeal for each combo table, at least
JavaScript:
tables.forEach(e => {
    const moves = Array.from(e.querySelectorAll("[class^='con-']"));
    moves.shift(); // Remove "First Move"
    for (i = 8; i >= 0; i--) {
        if(moves.filter(m => m.dataset.appeal == i).length > 0) {
            e.dataset.maxAppeal = i;
            i = -1;
        }
    }
});
basically it grabs all the second moves in the table and then counts backwards from 8 (the highest number base appeal a move can have) and then attempts to filter the moves by that amount. if the resulting filtered array returned any results at all, that means a move exists with that amount of appeal. then i just set the count to -1 so it stops counting down lol

now roughly the best appeal combos show up on top, like sunny day shows up here at the top because of access to overheat, which has a base 6 appeal points
1755198043129.jpeg

yay
 
Last edited:
  • Thread starter
  • Staff
  • #7
i spent a super large portion of my day updating 60minlkdt's website because well. i needed to do the usual gallery updates and whatnot. but also the contest for the avatar and site button ended (you can see the site button winner in my signature now!!) and as a result i needed to make a couple of new pages with all of the entries for both categories.

the fun part about the contest was that while there was only one winner per category, all entries would be included on the website regardless. for the site buttons it's pretty straightforward - the winner's is just the main one displayed on the "link to us" page and the rest are underneath. but for the avatars, i had the fun idea of the avatar randomizing every time the page is loaded between all the entries. 60minlkdt's social medias just use the one avatar, of course, but with the power of (incredibly basic) javascript i could use all of the entries on the website at once >:3

JavaScript:
const avatarImg = document.getElementById('logoimg');
const avatarFiles = Object.keys(avatarData);
const avatarFile = avatarFiles[Math.floor(Math.random() * avatarFiles.length)];
const avatarSrc = '/images/avatars/' + avatarFile;
avatarImg.src = avatarSrc;
it really is that shrimple.

for some reason even though i definitely could have done the majority of this in plain HTML/CSS (and frankly i might go back and do that later for my own personal satisfaction) i defaulted to writing javascript to dynamically generate the list of images with artist credits from some JSON again. i don't know why i'm always doing more javascript than i need. it's a bad habit. this website still uses javascript to generate the fanfic thumbnails in the gallery... which is fine but the thing is, it's drawing on <canvas>, not just filling in normal html/css stuff. which means i programmed word wrap. manually. and the end result doesn't even look as good on some devices because the resolution of the generated image on the <canvas> isn't as sharp as just... normal font display. maybe one day i'll go back and make a more normal person solution but i can't be bothered right now.

the only real problem with this update today is that the CSS for the new stuff is kinda nasty. i want to go back and change it later for my own personal satisfaction but i am way too sleepy. i feel like i've been at my keyboard all day managing various things (and i left the house to drop some stuff off at the post office earlier so really i am TIRED!!)

you can find the "link to us" page with the site buttons here and the list of avatars here!
1757376025070.jpeg

it's not new by any means but maybe i should talk about the gallery on this site someday. i like how the gallery works
 
Last edited:
Congratulations on the 60minlkdt event! I checked out the website to see the buttons and they all look great, I'm a big fan of ezra's submission myself. Must've been a tough choice between all the awesome buttons people made!
Also I know you said its basic JS but anything related to JS is witchcraft to me. I cannot write JS to save my life.:bulbaFacepalm:
I'll sit down and learn eventually, but I'll continue avoiding it for as long as I can get away with (that is, until my own projects grow more ambitious).
The randomly selected avatar is such a simple but neat way to showcase everyone's fanwork.
Make sure to get some rest! :bulbaLove:
 
  • Thread starter
  • Staff
  • #9
Also I know you said its basic JS but anything related to JS is witchcraft to me. I cannot write JS to save my life.
it can be overwhelming at first but really javascript is just giving the computer commands to edit the HTML/CSS of the page on the fly based on user input or whatever else, which means you can start out really... really simple. like if you learn how to make a button change the color of some text when clicked, you're already halfway there
HTML:
<p id="text" style="color: black;">This is some text.</p>

<button onclick="changeTextColor('red')">Change color to red</button>
<button onclick="changeTextColor('blue')">Change color to blue</button>

<script>
    function changeTextColor(color) {
        document.getElementById('text').style.color = color;
    }
</script>
i think this block of code is pretty intuitive? the two buttons have an "onclick" assigned with changeTextColor() inside. within the parentheses are two different options for paragraph color, in this case red and blue. then in the javascript, you can see what changeTextColor() actually is - the function has one argument in the parentheses, "color", which ends up being red or blue depending on the button clicked. and then it locates the paragraph of text by looking it up by the unique id ("text" in this case) and sets its style to the color in question!

i know you did not ask for a tutorial but i was in an infodumping mood. hopefully this makes it less scary. or maybe it made it more scary, idk

The randomly selected avatar is such a simple but neat way to showcase everyone's fanwork.
also thank you!! i thought so too. i really wanted everyone to be included
 
i guess working on the 60minlkdt website the other day lit a fire in me because i spent most of my day today touching up the site... honestly mostly stuff other people will never see just for my own sense of satisfaction, i guess.

first up: i did what i said i was going to do the other day and removed the excess javascript from the avatar and button pages so now the only javascript that runs on those pages is finding the currently shown avatar and loading its information properly. the list of images and artist credits is just plain HTML now. i was also going to go back and fix up the CSS but i went back and looked at it and it actually isn't nearly as bad as i remembered so i'm probably just going to leave it and maybe touch it up one day when i sit down with the entire site's CSS at once...

second: i wanted to remove the single line of jQuery that the site's been using this entire time. i enjoy my websites having 0 reliance on external libraries where possible and it really bothered me that i had the entirety of jQuery loading on the site just to run that one line of code. the issue is that the line of code in question is jQuery's fn.load command, which handles loading all of the content on the site, which is... really important. 60minlkdt is actually a singular webpage that has its content dynamically loaded via scripts - if you turn off javascript in your browser you get a notice to turn it back on and none of the links work lol:
1757549690926.png


i thought this would be an easy fix. i thought to myself that i would just fetch my pages and insert the HTML into the main container via innerHTML but unfortunately things are never that simple huh. doing it that way worked for almost everything... but scripts. any scripts on the pages being dynamically loaded wouldn't evaluate/execute. to get the scripts to execute properly, i ended up having to manually recreate them with createElement('script') and append them to get them to execute. my final code ended up looking like this:

JavaScript:
async function insertContent(page) {
    const url = `pages/${page}.txt`;
    try {
        const main = document.getElementById('main');

        const response = await fetch(url);
        const text = await response.text();
        const parser = new DOMParser();
        const html = parser.parseFromString(text, 'text/html');

        function createScript(old) {
            const script = document.createElement('script');
            Array.from(old.attributes).forEach(a => script.setAttribute(a.name, a.value));
            script.innerHTML = old.innerHTML;
            return script;
        }

        const requireds = Array.from(html.scripts).filter(s => s.src);
        function loadRequired() {
            if (!requireds.length) {
                loadRest();
                return;
            }
            const script = createScript(requireds.shift());
            script.onload = () => loadRequired();
            main.appendChild(script);
        }
        loadRequired();

        function loadRest() {
            main.innerHTML = text;
            Array.from(main.getElementsByTagName('script')).filter(s => !s.src).forEach(s => {
                s.parentNode.replaceChild(createScript(s), s)
            });

            const params = new URLSearchParams(window.location.search);
            if (params.has('t') == false) {
                main.scrollTop = 0;
                document.body.scrollIntoView();
            }
        }
    } catch (error) {console.error(error.message)}
}

the whole block of code that deals with "requireds" inserts any script tags with src applied first because those are dependencies and i want those to be fully loaded before i deal with the rest of the document. then i actually set innerHTML like i was planning originally and loop through non-src script tags, evaluating them properly with createScript(old). this whole solution would fall apart on a website with a different framework but hey i'm not here to write a library for other people to use, i'm here to get 60minlkdt to work specifically LOL

you may be wondering what that if (params.has('t') == false) block is for. well, lastly, i worked on something people will actually see and that i've been wanting to do for a while: i implemented theme/submission specific URLs on the gallery page. now it is possible to link to a specific theme or submission, and the user can refresh or press the back button and the gallery stays on the same submission/theme or goes to the previous submission/theme accordingly. the parameters are 't' for theme and 's' for submission, hence the little block of code checking for the 't' parameter, because i don't want the webpage to default back to the top of the page while browsing the gallery.

JavaScript:
function getParam(type, data) {
    const url = new URL(window.location.href);
    let value = url.searchParams.get(type);
    if (!data.includes(value)) {
        value = data[0];
    }
    url.searchParams.set(type, value);
    window.history.replaceState({}, null, url);
    return value;
}

function setParam(type, value) {
    const url = new URL(window.location.href);
    url.searchParams.set(type, value);
    window.history.pushState({}, null, url);
}

i suffered so much for this little block of code. at some point i made a typo, but it was properly evaluating so it wasn't throwing an error and i don't know if it's the fatigue or what but it took me over an HOUR to figure out what was going wrong. and it was a SINGLE TYPO. i'm going to explode. but hey it works! these functions are both used a few times throughout the gallery's code.

now that i've done this i should really change up the gallery UI so that the submission is more upfront as one of the first things you see but i don't know what exactly i want that to look like yet and i am far too tired after banging my head against the wall of code all day so... some other time. i have got to lock in on my college coding homework tomorrow instead of getting preoccupied with code that no one else will ever even see :bulbaFacepalm:
 
hello! it's been a while since i've updated here. other than just being tired and having a lot going on, most of my coding time has been spent on javascript homework for the javascript class i don't like that is boring me to death. but i won't complain about that right now because i finally got the time and energy to work on Blue Moon Falls for a good while today!

i won't rehash the whole announcement i made off-site, but the TLDR; is that i need to essentially recreate BMF from the ground up. i'm not changing or removing any content that exists on the site, but the actual code behind said content... yeah, i'm refactoring all of it. mostly from scratch, with the only exception being some of my JavaScript tools that will just get touch-ups instead of a complete re-code. um. actually that's not quite accurate either because i will be re-coding the stat experience tracker and the GB/C trader from scratch (the latter is going to be a huge ordeal) but i digress.

the reason i'm doing this is because BMF was my first ever website and i made it before i knew anything about coding. i have learned everything i know about coding websites from working on BMF and bashing my head against a wall trying to make what i want to make. this definitely worked, and i am pretty knowledgeable now, but the issue is this site has been around since 2021 and some of my old code from four years ago is still sitting around in the files. a lot of it, actually. i've tried to refactor the site a couple times in BMF's lifespan whenever i felt like i hit a particular milestone in my personal skill level, but now that the stuff i want to do on the site is getting more complex and everything... yeah, it's time for another refactor. and hopefully the last one - i am trying to futureproof this site as much as possible. i don't want to have to go back and edit every single page again, i think i'd die. this time is already going to be enough work.

i am not going to go over everything i did today because i've been coding most of my waking hours but i'll do a brief overview of some things!

so for today i focused on remaking my template for new pages and getting the site's layout working. i am very dedicated to keeping BMF completely in vanilla HTML/CSS/JS without fancier technologies or static site generators, but i still want some of the benefits that come with static site generators. this means i needed to make my HTML template as modular as humanly possible so that if i ever want to change the site's basic structure/layout in the future, i don't have to go back and edit every single page. i just want to edit my one script that loads the sidebars, header, footer, etc, onto the page one time and be done with it. i had a setup like this with BMF already, but it was filling in empty elements on the HTML document... which was nice for not having to add new links to my sidebar navigation every single time i made something new, but i couldn't really... move the sidebars around if i wanted to, for instance. and because i didn't know better when i first made BMF's page template and layout, my second sidebar was under the main page content in the hierarchy, which is very bad for people with screen-readers, do not be like me!! use CSS grid to position content!!

this is the end result of the HTML template redo:
HTML:
<body>
    <div id="site-container">
        <main>
            <noscript>
                <p><em>JavaScript is required for this site to function.</em> Please enable scripts in your browser for this site.</p>
                <p>Scripts are solely used for various Pokémon-related tools and loading essential site content & themes, and nothing nosy, obnoxious, or flashy.</p>
            </noscript>

            <!-- Page starts here -->
            <h1>Page Title</h1>
            <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Qui tempore perspiciatis, quod sed labore debitis quas itaque soluta accusantium? Voluptates quas officia numquam delectus similique perspiciatis dolor a at saepe! Lorem ipsum dolor sit amet consectetur adipisicing elit. Tempora illum fuga dicta facere dignissimos facilis provident sed, suscipit repellat error cupiditate soluta laudantium, exercitationem nemo eum, expedita porro et voluptatibus! Lorem ipsum dolor sit amet consectetur adipisicing elit. Alias fugiat voluptate magnam commodi dignissimos aperiam praesentium officia corrupti, ex itaque iure, illum placeat cumque quas dolorem suscipit iste? Repellat, possimus?</p>

            <p id="last-updated">Last updated Month DD YYYY. Page created.</p>
            <!-- Page ends here -->

        </main>
    </div>
</body>
it really is that short! a generic container for the whole site and then a <main> to put my actual page contents in. i'm also going to be copying a <noscript> everywhere but that's not really a big deal. the rest is optional and just filler for myself lol. everything else is loaded through JavaScript! and the end result of that script is the document's hierarchy looking like uh
HTML:
<div id="site-container">
    <header>
        <!-- Pretend the header is here -->
    </header>
    <nav id="nav-primary">
        <!-- Pretend the navigation is here -->
    </nav>
    <aside id="nav-secondary">
        <nav>
            <!-- Pretend the second navigation is here -->
        </nav>
    </aside>
    <main></main>
    <footer>
        <!-- Pretend the footer is here -->
    </footer>
</div>
this! wow! screen-reader friendly! who would have thought it... :bulbaFacepalm:

after getting the javascript loader working and setting up my pre-existing sidebar/header/footer content, i then started working on remaking the site's default stylesheet from scratch. my intention is for the site to look pretty much identical, but i'm adding slightly more color variety because i had too few colors before (hard to get things to contrast sometimes) and like... just touching up things in a couple places to make them look slightly nicer. but it's still the oldschool BMF everyone knows and loves.

the one major change that probably very few people will end up noticing is that i added another breakpoint between the 1 column mobile view and the 3 column desktop view - if you scrunch your browser window to be smaller but not like phone size, BMF adjusts accordingly now and drops the outgoing/advertisement links to the bottom of the page but keeps the sidebar on the left in a two column layout. so that's nice
1760046906859.jpeg

also! did you know nested CSS is a native thing now supported by all major browsers as of 2023? SASS isn't necessary for nested CSS anymore. it's wonderful. please look at how beautiful nested CSS is. this is the bit that handles the styling of my navigation menus/sidebars:
CSS:
#nav-primary, #nav-secondary {
    padding: 10px;
    border-radius: 0;
    color: var(--white);
    background-color: var(--dark);

    h2 {
        font-size: 1.3em;
        margin-top: 20px;
    }
    div:last-child {margin: 20px 0px;}

    ul {
        list-style-type: none;
        list-style-image: none;
        overflow: auto;
        border-radius: 5px;
        text-align: center;
       
        &:not(:has(img)) {background-color: var(--darkest);}
        &:has(img) {background-color: transparent;}
    }
    li {
        margin: 0;
        overflow: auto;
        &:has(img) {display: inline-block;}
        &:not(:has(img)) {border-radius: 5px;}

        a {
            display: inline-block;
            width: 100%;
            height: 100%;
            text-decoration: none;
            &:not(:has(img)) {padding: 5px;}

            &:link, &:visited {color: var(--accentlight);}
            &:hover, &:active {
                color: var(--accentlighter);
                background-color: var(--dark);
            }
        }

        &:has([type="application/rss+xml"]) { /* RSS Link */
            display: block;
            margin: 5px 0px;
        }
    }
    img {
        vertical-align: middle;
        image-rendering: pixelated;
        border: 2px dotted transparent;
        &:hover {border-color: var(--accentlighter);}
    }
}

i still have quite a bit to do on the CSS end, namely um:
  • figures/figcaptions
  • links
  • buttons
  • forms
  • columns
  • image sets
  • my pokemon summary cards... i want to make a secondary more detailed version of them too
  • might make a custom lightbox script for enlargeable images, not sure yet (this is partially JS but whatever)
but it still feels surreal to get the bulk of a layout that took me multiple days to even begin creating when i started done in... less than a full day haha

i'm excited because with the way i have things set up now, i should be able to recreate the theme switcher to actually fully change out stylesheets instead of just swapping header image and theme colors. so, i should be able to give BMF a total redesign sometime if i want, and have it as a toggle-able option. you know, similar to how the forum themes work here on bulbagarden! it's crazy to think about because BMF essentially looks the same as it did back in 2021...

i hope my aimless rambling was interesting to someone!
 
Whoaaahh!!! Even as a girlie who knows next to nothing about code I can sense the satisfying simplicity and directness in these screenshots!! They look streamlined, elegant, and uncluttered!! Congrats on taking on a big project and getting so much in a single day of work!! All the time and energy is really giving back to you! ^^
 
Whoaaahh!!! Even as a girlie who knows next to nothing about code I can sense the satisfying simplicity and directness in these screenshots!! They look streamlined, elegant, and uncluttered!! Congrats on taking on a big project and getting so much in a single day of work!! All the time and energy is really giving back to you! ^^
thank you so much, that means a lot! i hope i feel the same way years from now haha. futureproofing to the best of my ability
 
thank you so much, that means a lot! i hope i feel the same way years from now haha. futureproofing to the best of my ability
Even if you inadvertently make more work for yourself than would be ideal I’m sure your intentionality now coupled with the years of additional experience you’ll have whenever you might consider overhauling things again will make it feel like another significant experiential milestone and less daunting than it could have been were you not so committed to this craft!
 
i ended up being tired and unable to do much today, though i did do the CSS for links and buttons. their colors change dynamically depending on where they are placed on the page, and whether or not they're disabled. (in the screencap below, the white button is disabled)
1760150028053.png

CSS:
/*
    Buttons
*/
aside button, aside input[type="submit"] {
    color: var(--white);
    background-color: var(--darkest);
    &:hover:not(:disabled) {background-color: var(--black);}
    &:active:not(:disabled) {background-color: var(--darkest);}
}
section.separated button, .default-tool button, section.separated input[type="submit"], .default-tool input[type="submit"] {
    color: var(--black);
    background-color: var(--white);
    &:hover:not(:disabled) {background-color: var(--lightest);}
    &:active:not(:disabled) {background-color: var(--white);}
}
button, input[type="submit"] {
    cursor: pointer;
    padding: 10px;
    text-align: center;
    font-family: gsc, Verdana, sans-serif;
    font-size: 1em;
    color: var(--white);
    background-color: var(--accentdark);
    border: none;
    border-radius: 5px;
    &:hover:not(:disabled) {background-color: var(--accentdarker);}
    &:active:not(:disabled) {background-color: var(--accentdark);}
    &.mini {
        padding: 5px;
        font-size: 0.9em;
    }
    &:disabled {
        cursor: default;
        opacity: 70%;
    }
}

also, i never showed off this change i made to sidebar links yesterday. i am 99% certain this is how i wanted these links to look years ago but i just wasn't sure how to achieve the effect at the time.
warning, these could be considered slightly flashy?
links1.gif.gif
links2.gif.gif
it's such a small change but it really helps in my opinion! i hope i can get more done tomorrow... i could probably finish the main stylesheet tomorrow if i tried
 
this is about javascript homework and not any of my sites but i'm putting it here anyway because it's on theme and i need to scream somewhere

i am trying to get a lot of homework done in advance and get ahead before Z-A comes out on thursday so i can focus on playing the game without worries so i am crunching a bunch of javascript projects together in one go. today i sat down and intended to literally finish three different projects but that did not work out because for some reason my professor decided it would be a good idea to have project 3 be a simple "take text/number input from the user and display it back to them" project and then absolutely dwarf that with the level of effort and skill needed for project 4, which is to make an entire "turn based racing game" with some oddly specific requirements. mind you i am totally fine, this project was not hard for me (although much more time consuming than i had expected considering the caliber of our previous projects so i definitely didn't finish project 5 today) but like. my poor classmates. he even scheduled these in such a way that there is no class between projects 3 and 4 so if you wanted further instruction, explode i guess

complaining aside, i actually am not completely done with the project because i need to touch up the CSS a bit tomorrow, i just became too tired to continue. so like this table will look a little nicer and the inputs will be styled... tomorrow. this is the game i made (yes i'm on a mission to make every single homework assignment in this class pokemon related):
racer.gif.gif

i would never willingly use javascript alert() btw. i only used it because i was required to. also if you're wondering why pokemon are exploding randomly it's because he put in a requirement to have a random racer/team get taken out of the race every 3 turns lol

all things considered for something i made in one evening i think this came out pretty good and i just needed to post about it to document my efforts or something. and maybe complain a little bit about my professor cause wow this guy sucks at teaching but WHATEVER

JavaScript:
/****
// Name: CalculateTurn
// Param: N/A
// Returns: N/A (return is occasionally used to interrupt the function)
// Desc: Executes all of the random rolls and logic necessary for the race state to advance a turn
****/
function CalculateTurn()
{
    // Increment turn count
    race.turn = race.turn + 1;
    console.log(`Taking turn ${race.turn}...`);

    // Get array of racers for easy access and looping
    const racers = race.racing;
    console.log(`Current list of racers at start of turn: ${racers.join(', ')}`);

    // 50/50 chance for all racers to move forward in a turn, represented by array length
    console.log('Rolling a 50/50 for every racer to move forward in the race...');
    for (i = 0; i < racers.length; i++)
    {
        const isMoving = Math.floor(Math.random() * 2);
        if (isMoving)
        {
            // Increases array length to indicate position
            race.racers[racers[i]].push(true);
            console.log(`-- ${racers[i]} moved forward! (Position ${race.racers[racers[i]].length})`);
        }
        else
        {
            console.log(`-- ${racers[i]} did not move forward. (Position ${race.racers[racers[i]].length})`);
        }
    }

    // Check for winners
    console.log('Checking for winners this turn...');
    const winners = racers.filter(function(r)
    {
        return race.racers[r].length >= 5;
    });
    if (winners.length > 0)
    {
        // Find the winner. Randomized between winners array in case of a tie
        console.log(`${winners.length} potential winners found: ${winners.join(', ')}`);
        const winner = winners[Math.floor(Math.random() * winners.length)];
        console.log(`After rolling a random number, ${winner} won!`);
        
        // Get alert text for a tie if necessary
        let tieText = '';
        if (winners.length > 1)
        {
            tieText = 'It was so close that it was almost a tie, but ';
        }

        // Find out whether the user's chosen racer won and get the appropriate ending text for the alert
        let winnerText = '';
        if (winner == race.choice)
        {
            winnerText = 'Congratulations, your chosen racer won!';
        }
        else
        {
            winnerText = 'Your chosen racer didn\'t win, too bad...';
        }

        // Display the alert
        alert(`${tieText}${winner.charAt(0).toUpperCase() + winner.slice(1)} won the race!\n${winnerText}`);
        
        // End the race
        EndRace(winner);
        return;
    }
    console.log('No winners were found!');

    // If no winners, continue onto checking for racers to remove from the running if on a third turn
    if (race.turn % 3 == 0)
    {
        console.log('It\'s a third turn, so rolling a random racer to remove from the running...');
        const rand = Math.floor(Math.random() * racers.length);
        const toRemove = race.racing.splice(rand, 1)[0];
        console.log(`${toRemove} is being removed from the race!`);
        race.racing.push(toRemove);
        race.racing.pop();
        delete race.racers[toRemove];
        console.log(`The new racers list after ${toRemove} was removed is: ${race.racing.join(', ')}`);
    }
    // Handle if there's somehow no racers left (the odds of this happening are small)
    if (race.racing.length < 1)
    {
        console.error('No racers were left in the race! Attempting to end race without a winner...');
        alert(`Everyone fainted! No one won the race... Too bad.`);
        // Empty parameter = no winner
        EndRace();
        return;
    }

    console.log(`Turn ${race.turn} is over!`);
}
oh yeah and the amount of comments and console.log()s he requires and stuff give me a headache. he also told me he'd start taking points off if i didn't start putting both brackets on separate lines (i usually put the first bracket on the same line as the declaration) so i'm mad about that. i code so slow now... my poor muscle memory...
 
Sorry you didn’t quite make it through every project but you’re definitely killing it, this turned out awesome!! I hope project 5 isn’t another huge jump in complexity and you find yourself with plenty of time for Lumiose sightseeing!
 
Back
Top Bottom