Starting from the start, as most things do, my idea is to have a portfolio style website.
How do we do this though, without really knowing what it takes to make a website? I've seen a lot of frameworks that all claim to make website building easy, and that's probably true if you put in a thousand hours to learn their ecosystem. But who has that sort of time?
Without even really scratching the surface, that sure is a lot of stuff to evaluate and memorize.
The obvious question then is, of course, which one to choose? They all have their benefits and problems, and trying each one individually would take to much time.
What if we just choose the most popular one? yeah that could work, but how do we know what the most popular one is? This needs more investigation.
I could use php, as I've already got an xampp stack installed, but the general security of php without a framework seems abysmal. I could use a framework, but learning that on top of all the other stuff would take too much time. Php does make it pretty easy to get something up and running though, I have used it a little bit for one off scripts in the past.
I'll probably use node.js not only because it's already installed, but because it seems to be the biggest and has been around the longest. It also has npm, which seems like a nice tool for getting and maintaining packages.
I want the website to be easy to add stuff to, and to have a simple design that's easy to navigate. The page also has to load fast and be responsive.
I'll organise the site into three main sections: a gallery of all the good looking things I make, a section detailing the nitty-gritty of how I made the things look good, and this blog. I'll also put in a contact page, because every site needs a contact page.
This leads directly to the first problem: how wide should a webpage be? Well, a resposive website doesn't have any fixed width, and it's possible that people will be looking at it on potentially any device with a screen, but there's a limit to how long a line of text can be before it becomes unreadable.
For images though, the bigger the better, so I'll let their maximum width be the whole screen, so I'll think 960px as the maximum width will be pretty nice.
I'll of course need a common header and footer that contain links to all the other pages, but they'll need to be collapsible into a easy menu for mobile screens.
This is my first concept for the site. Not my best work, but it gets the point across. Pretty sure this will do the trick, now to make the html and css look like that. Somehow.
start from the top down, the logo is the beginning i guess
want images to span the whole page width it's so obvious that margin: auto; makes the page content centered. so obvious. other css layout info seems okay, css reference is pretty good. w3 schools also has good info. positioning is tedious though
google webfonts seems okay? it might have load time implications though. not to worry, the whole font for the page that i like is 18.3kb and will be used for all the text, this seems okay. making the logo for a start, which font size to use? px? em? vw? guess i'll use pixels. this seems to scale with view width nicely too.
alright so after fudging with the font for a while,
after fucking about i saw that user agents stylesheet was being used for some reason, and after googling it seems that it does that for no particular reason? oh normalize.css seems to be a useful thing, found it after googling for the user agent thingy.
after spending too much time trying to do the logo with css, I think it's easier and faster to just make one in photoshop and scale it.
so that's what I did:
as you can see, it doesn't look good. -resons heree- the one i got up top left is bestteer.
ok so after a while trying to make a site with just html and css from scratch on my own, I have found that it will take an unreasonable amount of time to make what I wanna make. I need another solution.
so i found that w3 has some templates that use their own w3.css thingy, and in their plethora of templates I found one that looks like a good start for my desired layout.
okey doke so an immediate problem for trying to integrate the way I'm writing this blog is that w3.css uses multiple css classes to style it's elements, which is handy in one way because I don't have to worry about writing the css myself, but is difficult because I need multiple classes attached to one in order to style things how I want.
oh i probably need to mention that my initial idea for the blog portion of the site is to write it in markdown, because that's easiest and just transform it into html for upload. luckily i found a npm package Marked that doesn't have any dependencies and outputs a nice html file. it can conveniently also be used as an api, so i can probably automate all the content publishing in the future.
also for the record i am aware of the thousands of "make a blog in 5 minutes" tutorials, but eh, i wanna do it myself.
a solution to this need for inheritance has presented itself in the form of Less, a css preprocessor that has inheritance as a feature. The only problem is that this is another thing that I need to learn to some degree to integrate it into my pipeline. so much for simplicity I guess.
another thing i wanted entirely because i thought it looked cool, is the floating points behind the login screen on netlify. by using the handy-dandy chrome dev tools and clicking on the background, i found that they're using node garden, which is nicely under the MIT license. so I took a copy of the script file and customised it for a while to make it look like it does now.
I also removed the mouse interaction from it, which was harder to figure out than I thought it would be, although it was solved simply by adding style="pointer-events:none;" to the canvas element. and it works! but now it's in the wrong position when the browser window is wider than the page, another straightforward fix though, just change it to position:absolute; and done. now, it doesn't display when the screen is too small, it's supposed to be hidden until the menu button is clicked, but it doesn't display at all. hmm, what to do.
so a good while later, it seems that there's a problem with the hidden menu when the screen is not full width. I don't really know how to fix this, and all my attempts so far have failed. it looks fine without it, even though they still technically load, so i'm just going to leave it for now and come back when I'm more experienced/less frustrated with it.
the problem, specifically, is that it appears that my browser (chrome) is adding height:0 and width:0 attributes to the canvas and the div surrounding it, but only when the top nav element is hidden by default. my attempted fix for this was to set the height and width and also the style's height and width when the open menu button is pressed. this doens't work and i don't know why, it seems there's a race condition between when the height and width are set and when the function is called.
so back to the matter at hand, what do i want as the main content attractor of the page? well everyone loves a picture, they tell a thousand words so i've heard.
so if i have a 4x4 grid, it looks fine, but if the window shrinks then it collapses into 2 colums, and eventually into 1 column on a very thin screen. this is fine, but with 16 images it's too long to scroll through reasonably. the solution i think is to paginate them into 2x8 images then 4x4 images, just got to work out the css to do that. how hard could it possibly be.
I've become distracted by a bunch of stuff, and will return to pagination after some other stuff. perhaps worth noting, while i don't have a bunch of images to put up, i'm using place kittens for placeholders.
it occured to me that in regards to the nice floating node garden i have behind the nav menu, there's no reason for it not to just cover the whole page. so im going to move it up to the top of the body and let it be an overlay with a z-index below everything else, and stop it from being scrolled offscreen. this is so much simpler than trying to mess with the other css.
so here some time has passed and I've decided I don't like the look of the garden. it's just a bit too messy for my tastes, which is the point of the thing, a random garden of nodes that float around, meaning to imply a connected system or something like that. It's just not what I want to convey on the page. the page also feels a bit cold and lifeless because there's no color anywhere. also somewhere i made the images greyscale until hovered which doesn't help.
the solution I worked out? it's easy enough, given we're allowed to have some transparency in our linear gradient. we have the transparent gradient on top of an element with an animated background-color, it's so simple I could kiss myself. but aha, a problem that hit me out of left field is that css properties marked as !important cannot be animated. why? surely more good reasons. so I've just got to go through and remove the bits that mark the color as important, easy enough. also got to remember to make the overlay's height and width the same as the rotating colours' so it all fits right. the fun cannot be halted.
aha! i have solved the full size modal image problem! all i had to do was make the image part of the element max-height: 90vh! like all css it's just so intuitive! wow! it's not 100vh because it looks silly having the image touch the borders of the screen.
after a bit of searching i've found nodemailer, a npm package with no dependencies. so far I haven't used any node/npm stuff for this project, and this seems like a fine place to start. installing and using it seems easy enough, all I've gotta do is work out how to get the form data into the script.
so it took a fair while to make the email work properly, and i ran into a couple of issues along the way that weren't easy to debug, and were essentially educated guessing on my part.
5:05:48 PM: (node:1) UnhandledPromiseRejectionWarning: Error: 140264722483072:error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol:../deps/openssl/openssl/ssl/s23_clnt.c:827: 5:05:48 PM: (node:1) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1) 5:05:48 PM: (node:1) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
which to me doesn't make a lick of sense. my best guess is that I was returning the wrong thing from the function, but who knows. so it seems the problem was that netlify is expecting the callback function to be called to let it know that the function has been completed, so calling that as the nodemailer's SendMail function's promise callback thingy seems to have fixed it. it also complained about being an async/await function, so to google I headed once again to try and find a solution. I stumbled upon a nodemailer example which does exactly what I want, it must be my lucky day, so copying it almost word for word, it worked immediately.
I used netlify to hold the secret username and password for the email client to send the emails, but I wrote them apparently wrong which gave me this error:
5:43:46 PM: (node:1) UnhandledPromiseRejectionWarning: Error: Invalid login: 535 Authentication Failed
which was handily fixed by removing the quotes from the value part of the netlify environment variable. so now I've got it to the point where it successfully emails me and I get a notification on my phone, which I'm quite pleased with. there's just one problem, the email's body is unreadably by a human. the string appears to be base64 encoded, but I'm not sure whether it's the formdata or the fetch call encoding it, no problem, I can simply decode it before sending the email. a quick search tells me that atob() is a function for decoding base64 strings, but it gives me this error:
TypeError: t.body.atob is not a function
hmm, that's a strange one, guess I'll have to find another way to decode the string. further searching finds me the Buffer.from() method which takes a string and an encoding and returns a decoded buffer, and luckily the nodemailer can take a buffer as the email's text, so the problem is solved, the contact form is now fully functional.
setting up the template for the page was easy enough, all I had to do was strip all the content from the main page and make a little template for the blog post link, which I decided will be an image with some text on it so it's easy to click, and some tags so they can be found and sorted easily.
so while this looks good, I also want to have the time I posted the post to be under the image inline with the tags, my initial thought was to put the post date and the tags in their own spans inside the p element, and set their text alignments separately. this didn't work, but I learned through a quick search that I only need a span on the tags part, and can have the main p element text-align:left; and set the tag span to float:right; and it just works. sometimes css is ok.
another small problem I ran into while setting up the blog page, the go-to-top button that only appears when you scroll down wasn't appearing. the reason I discovered is that the script that sets the scrolling to a function wasn't getting called, because the line before it asks to get a button by it's id, but the button doesn't exist on the page, so the script errored and stopped execution. my solution was to add a button with the same id, but make it do nothing and hide it.
now to actually write the generator for the blogs, an easy enough proposition i suppose, all it's gotta do is call marked to transform the .md into some html and insert that into a template, then add an entry into the blog page on the site.
alrighty, so writing the code was quite straightforward, the challenge was finding a way to index all files in a particular subfolder and only grabbing th e.md and .html extensions, but I found a packaged called glob that does just this, but I couldn't figure out how to use multiple arguments with it, so I just manually separated them afterwards. the next trick was to only get the md files that don't have a corresponding html file, because that means they haven't been genrated yet, which i accomplished with a simple symmetric filter which looks like this:
let diff = htmlFiles .filter(x => !mdFiles.includes(x)) .concat(mdFiles.filter(x => !htmlFiles.includes(x)));
it's probably wrong in a bunch of ways, but it seems to work for my purposes. the next thing to do was also straightforward, read in the md file, extract a bit of metadata from the top of it to generate an entry on the main blog index, read in and replace the body of the template file, and finally write out the data to a new html file. easy as cake. a note about the metadata, i decided to use the format: Title || Post Date || Tags, because if I need to update a post in the future the order of the posts on the main blog page won't change because the date is entered manually.
incidentally, i believe that I've run into a small version of what is known as 'callback hell', because the current count of callbacks in the blog generator is: 8. which seems like a problem, and there's almost certainly a better way, but I've been following what was written in the official docs so it's probably fine? it does what I want it to do, but it feels just a bit wrong, you know?
it's probably not a problem, probably, because it is a series of callbacks from the multiple readfiles and writefiles and foreaches that need to be done to loop through all the things, theoretically this will handle it even if I make multiple blog posts before running the generator and uplaoding them.
fix this shit up
so while trying to add a bit of style to the blog posts, i've discovered that css has no previous sibling selector. The reason I discovered this is because I want to remove the margin between the image and it's caption, but the image is wrapped in it's own p tag as a result of the markdown generator. my solution to this was to translate the caption using css TransformY(-10px); to move the text upwards a bit, it seems to work fine but might break on mobile.
the next immediate problem is that it wasn't getting the text for the modal's caption from the image's caption text, so I had to search and find how to navigate through the html DOM to find the image's parent's next sibling, because that's where the caption text is stored because of how the md to html conversion works. so to finally get to the image's caption it's: element.parentNode.nextElementSibling.innerText. luckily iterating through the process was easier thanks to the modifications I made to the blog making process I made earlier.