This project started as a learning exercise to improve and practice my HTML, CSS and JavaScript. It uses pure CSS and JavaScript, which may be foolish but here we are.
word-puzzle/
├── answers.js
├── dictionary.txt
├── index.html
├── script.js
├── styles.css
└── styles.css
Important files:
index.htmlscript.jsstyles.css
- Colour coded keyboard and keys based on location of the letters in the answer
- New word generated every refresh
- Simple interface with a top bar containing helpful buttons
- A button that adds a hint, giving you a letter in the word
- A button that shows you what the answer is, if you want to check early
- Native mouse and touch functionality, with a virtual keyboard layout that scales based on device
- Native keyboard functionality if using a physical keyboard
This list may contain a great many inaccuracies, as the fact that I learned it does not mean that it is true!
- In networking, a
hostis a server - Putting the server on your computer and allowing said computer to make requests is a
loopback - The IP address for
localhostis found at127.0.0.1 - Web hosting sites are
remote hosts - Normally a website URL in your browser
https://www.example.comhas an associated IP address104.26.2.33- The
DNS lookupforlocalhostleads to127.0.0.1
- The
- The number after the IP address is the
port number, for instance127.0.0.1:8000is port8000
- You can set up a local server in the current folder with the simple command
python -m http.server - This will usually open port 8000 on localhost
127.0.0.1:8000 - If you open this in your browser it should process and render the
index.htmlin that folder - If you open a folder without
index.htmlit will open as a directory browser and you can browse files and subfolders - You can access other files in the root of your server by filename
http://localhost:8000/script.js- You can also access subdirectories
http://localhost:8000/subdirectory
- You can also access subdirectories
The simple batch file included in this project, LocalServer.bat automates the process to speed things up a little
@ECHO off & @REM Disable posting of commands to terminal
CD /d "%~dp0" & @REM Set the current working directory to the directory of this file
start http://localhost:8000 & @REM Open the local server in the default web browser
CALL python -m http.server & @REM Activate local server on http://localhost:8000/An example log for a simple server is below
PS C:\...\word-puzzle> python -m http.server
Serving HTTP on :: port 8000 (http://[::]:8000/) ...
::1 - - [02/Jan/2025 06:55:52] "GET / HTTP/1.1" 200 -
::1 - - [02/Jan/2025 06:55:52] "GET /styles.css HTTP/1.1" 200 -
::1 - - [02/Jan/2025 06:55:52] "GET /script.js HTTP/1.1" 200 -
::1 - - [02/Jan/2025 06:55:52] "GET /dictionary.txt HTTP/1.1" 200 -
::1 - - [02/Jan/2025 06:55:53] code 404, message File not found
::1 - - [02/Jan/2025 06:55:53] "GET /favicon.ico HTTP/1.1" 404 -
::1 - - [02/Jan/2025 06:56:11] "GET / HTTP/1.1" 304 -
::1 - - [02/Jan/2025 06:59:45] "GET / HTTP/1.1" 304 -
::1 - - [02/Jan/2025 06:59:45] "GET /script.js HTTP/1.1" 200 -
::1 - - [02/Jan/2025 06:59:45] "GET /dictionary.txt HTTP/1.1" 304 -- Status code
200means OK: The request was successful, and the server responded with the requested resource - Status code
304means Not Modified: The resource hasn't been modified since last access, use cached version - Status code
404means Not Found: The resource could not be found on the server
- You can open an
index.htmlfile in your browser as is, but it will struggle to access other files on your system- It will often throw
CORSrelated errors
- It will often throw
- You can avoid these errors with a local server as you are now allowing
GETandPOSTevents to pass through the server- You can now access local files
- These events can be read in the terminal that ran
python -m http.server
- Sites without a local server can only access files directly referenced in
index.htmland may still have issues - Sites with a local or remote server can access any files below the root directory of the server, never above
- A file deeper in the structure can make relative imports from higher directories but still never above the root of the server
When developing a site using HTML / CSS / JavaScript you may make tweaks to files, and want to see the result. In some cases, your browser may not actually update to show the changes as it is still using its cached version of the resource. In cases such as these, browsers have a "hard reload" option that clears the cache and reloads the page. In Google Chrome you can right click the refresh button while in Developer tools (Hotkey F12)
Much like other languages, if CSS is indeed a language, you can define reusable variables in CSS, the easiest way to do this is in the root element :root, which points to the highest level element, usually the <html> element itself
:root {
--debug-outline: 0px;
--colourA: rgba(255, 255, 255, 1.0);
--colourB: rgb(18, 18, 18);
--root-fs: 16px;
--outer-width: 100%;
--top-height: 8%;
}Above is an example of some simple CSS variables that can be used later
#outer {
width: var(--outer-width);
}You can create custom macros in VSCode that chain together different commands by using the multi-command extension
Commands for each action in VSCode are usually in the format editor.action.doThing and an easy way to get them is just looking at current hotkeys for things then right-clicking and selecting Copy Command ID
{
"key": "ctrl+f1",
"command": "extension.multiCommand.execute",
"args": {
"sequence": [
"editor.action.insertCursorAtEndOfEachLineSelected",
"editor.action.commentLine"
]
}
}Above is my custom macro that is especially useful in CSS as it will allow for multiple single line comments instead of a single block comment across multiple lines
/* --keyboard-width: 100%; */
/* --keyboard-height: 200px; */
/* --keyboard-margin: 0px 0px 4px 0px; */
/* --keyboard-rowgap: 0px; */Instead of
/* --keyboard-width: 100%;
--keyboard-height: 200px;
--keyboard-margin: 0px 0px 4px 0px;
--keyboard-rowgap: 0px; */This removes the issue where you can't toggle a line within the block comment without syntax issues
It also allows toggling two lines at the same time
/* --keyboard-padding: 0px; */
--keyboard-padding: 4px;to
--keyboard-padding: 0px;
/* --keyboard-padding: 4px; */.grid.row.square {
margin: 0px;
}
.grid .row {
margin: 0px;
}
.grid .row .square {
margin: 0px;
}.grid.row.squareselects an element that has all three classesgrid,rowandsquare
<div class="grid row square"></div>
.grid .rowselects allrowelements that are children of agridelement
<div class="grid">
<div class="row"></div>
<div class="row"></div>
</div>
.grid .row .squareselects allsquareelements that are children of arowelement, if therowelement is itself a child of agridelement
<div class="grid">
<div class="row">
<div class="square"></div>
</div>
<div class="row">
<div class="square"></div>
</div>
</div>
#grid .rowwould select allrowelements that are children of the specific ID:grid
> is the child combinator in CSS. It selects only the direct children of the specified parent element.
.container > .box > .item {
color: red;
}.container > .boxselectsboxelements that are direct children of acontainerelement.box > .itemselectsitemelements that are direct children of aboxelement
<div class="container">
<div class="box">
<div class="item">This is a direct child of a 'box' element</div>
<div class="item">This is a direct child of a 'box' element</div>
<div>
<div class="item">This is not a direct child of a 'box' element</div>
</div>
</div>
</div>- You can revert the last commit made to
GitHubusing the terminal - You can close an issue directly in the commit message using something like
Fixes #1 - Message - You can stop tracking a file or directory that was previously tracked by adding it to
.gitignoreand runninggit rm --cached file.txt test.txtfor any number of files, usegit rm -r --cached dir1 dir2for any number of directories, where the-rmeans recursive and will affect all files and directories in the given directory
git rm -r --cached directory
git commit -m "Stopped tracking 'directory' after rule added to .gitignore"
git push- Often
flexcontainers and elements usingmargincan get in each others' way- You can be better off without a
flexparent and usingmargin: autoon child elements
- You can be better off without a
- Using
margin: autocan fix srolling issues that can occur when trying to scroll aflexcontainer than has overflowed, the fix was found here - Using
z-index: 999can be useful to make an element appear above all other elementsz-indexcan be any value, any elements below that value are rendered below and the reverse for elements with a higherz-index
- Do not use
borderon objects as it is placed outside the element and can affect layout- Instead opt for
box-shadow: inset 0 0 0 var(--debug-outline) cyan - This also gives the ability to toggle outlines on all elements using
--debug-outline
- Instead opt for
- Use
box-shadow: inset 0 -1px 0 0px grey;to add a1pxgrey line below an element box-sizing: border-boxis the fix for many layout issues and padding issues where items seem to take up space greater than their set widths and heightsflex-shrink: 0stops an element being shrunk by parentflexcontainerflex-grow: 1tells an element to expand into free space in its parent'sflexcontainer- For the
body/htmlelements,100vwis the full width including the scrollbar,100%is the full width excluding the scrollbar - Using the class below, you can apply a hidden scrollbar to an element if
overflow: autooroverflow: scrollare already enabled
.hidden-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
.hidden-scrollbar::-webkit-scrollbar {
display: none;
}- When actually testing things make sure scrollbars for that element are not disabled, and you have
overflow: visibleas otherwise it makes it hard to see what is overflowing its container row-gap: 0pxdefines the space between rows in a grid or flex container- Similar in effect to
margin: 0pxon elements, but this is space between and controlled by the parent, rather than space around and controlled by the child
- Similar in effect to
column-gap: 0pxdefines the space between columns in a grid or flex container- Similar in effect to
margin: 0pxon elements, but this is space between and controlled by the parent, rather than space around and controlled by the child
- Similar in effect to
justify-contentproperty of flex-box aligns the items inside a flex container along the main axis just likealign-itemsdoes along the cross axisalign-itemsproperty of flex-box aligns the items inside a flex container along the cross axis just likejustify-contentdoes along the main axisalign-contentis for multi-line boxes where content has wrapped over to a new line eg. usingflex-wrap: wrap- Instead of making a class selector that is defined on its own, you can instead define styles for just certain multi-class combinations, allowing you to control exactly what the class, say
adaptivedoes to each other class
.theme-a {
background: #dca;
}
.theme-a.adaptive {
background: #753;
}
.theme-b {
background: #447;
}
.theme-b.adaptive {
background: #bcd;
}instead of
.adaptive {
background: #bcd;
}
.theme-a {
background: #dca;
}- You can set styles that apply for
dark modeandlight modewithinstyles.css, below any squares that are multi-classed with the empty selectoradaptivewill adapt based on user preference
.square {
background: blue;
}
@media (prefers-color-scheme: dark) {
.square.adaptive {
background: black;
}
}
@media (prefers-color-scheme: light) {
.square.adaptive {
background: grey;
}
}- You can change the entire stylesheet based on colour scheme also, by splitting
dark modeandlight modeintodark.cssandlight.css- You can then add them to your
index.htmlwith
- You can then add them to your
<link rel="stylesheet" href="light.css" media="(prefers-color-scheme: light)">
<link rel="stylesheet" href="dark.css" media="(prefers-color-scheme: dark)">.square {
transition: all 0.3s ease;
}The above snippet means that any transitions that happen to square elements will happen over 0.3 seconds, and will follow a smooth curve defined by ease, a few examples of things that might be subject to a transition are in the class new below
.thing {
transform: scale(1.2);
opacity: 0.5;
height: 200px;
background-color: red;
}Applying the thing class to a square would mean each of the 4 styles would follow the transition style defined in .square, and similarly they would reverse if you remove the thing class
You can also define specific rules for each style change eg. transform 0.3s ease-in, opacity 0.5s ease-out, height 0.4s linear, background-color 0.6s ease-in-out
You can also set a delay which means the transition will not start until the end of the delay, for transform 0.3s ease-in 0.5s the transform transition will not start until the 0.5 seconds are up, and will then take 0.3 seconds, for a total of 0.8 seconds
- Using
.square:nth-child(2)will select the 2ndsquarein the parent container that containssquare - Using
.square:nth-child(even)will select every even numbered child in the parent container that containssquare
<div class="container">
<div class="square">First</div>
<div class="square">Second</div>
<div class="square">Third</div>
</div>An example usage would be for elements in a row where you wanted them to transition one at a time, or offset times, left to right, you might set different transition-delay for each nth child of the row they are in
#grid .row .square {
transition: transform 2s ease-in-out, background-color 0.2s ease-in-out 2.2s;
}
#grid .row .square:nth-child(1) {
transition-delay: 0s;
}
#grid .row .square:nth-child(2) {
transition-delay: 0.3s;
}
#grid .row .square:nth-child(3) {
transition-delay: 0.6s;
}When working with CSS you will find that sites look vastly different on different devices. This is usually due to the size of the device's screen, and means that layouts need to adjust depending on the device, or the width or height.
The space available for viewing the content on your site is called the viewport and is what viewport units vh and vw use when calculating.
It seems common to say that sites should be designed using a mobile-first CSS design pattern because it can be very hard to make a site that was designed for a wide desktop screen look even half decent on a mobile screen.
Whilst mobiles, and modern screens, may have much higher resolutions (such as 3840x2160) this doesn't mean that their viewport matches their resolution. Usually they will have some sort of dpi (dots per inch) scaling so that a CSS "pixel" might map to say 2 physical pixels on their screen for a device at 2.0dpi, this means that a device at 1920x1080 with 1.0dpi would see the website almost exactly the same as a 3840x2160 device at 2.0dpi. This does make it somewhat easier as designing a site for 1920x1080 should also work for larger resolution displays.
You can target specific devices using CSS Media Queries, which allow you to apply styling if certain resolutions, dpi scaling, or orientations are applied
Some examples of CSS Media Queries
@media only screen and (orientation: portrait) {}@media only screen and (orientation: landscape) {}@media only screen and (max-width: 1920px) {}@media only screen and (min-height: 1080px) {}@media only screen and (min-device-pixel-ratio: 2) {}@media only screen and (resolution: 150dpi) {}@media only screen and (min-resolution: 2x) {}@media only screen and (min-height: 720px) and (max-height: 1080px) {}
The use of screen stops the styling from applying to print versions of the site, and the use of only is just a hack to stop older devices from parsing the query at all.
Some of the most useful queries to the HTML document from within your JavaScript file are below
document.querySelector('.square')selects the first element with classsquaredocument.getElementsByClassName('square')selects all elements with classsquaredocument.getElementById('square')selects the element with IDsquaredocument.getElementsByTagName('div')selects elements based on tag typedocument.getElementsByName('username')selects elements wherename='username'document.querySelectorAll('#keyboard .row .key')selects allkeyelements insiderowelements in IDkeyboardelementdocument.querySelector(':hover')selects all elements based onhoverstatesquare.querySelector(':nth-child(2)')is the second child of thesquareelement, where thesquareelement is found usingdocument.getElementById('square')square.querySelector(':first-child')is the first child of thesquareelementsquare.querySelector(':last-child')is the last child of thesquareelement
An example usage might be
const keys = document.querySelectorAll('#keyboard .row .key');
for (const key of keys) {
key.onclick = pressed
}- You could find the element with the ID
square(<div id="square"></div>) and then query it to find child elements of classthing, convert that to an array, and then log to console as below
let squareElement = document.getElementById("square");
let thingElements = squareElement.querySelectorAll('.thing');
let thingArray = Array.from(thingElements);
console.log(thingArray);- You can make a simple hotkey section with something similar to the snippet below
document.addEventListener('keydown', function (event) {
var prevent = true;
if (event.key === 'F1') {
var message = getValues()
alert(message);
}
else if (event.key === 'F2'){
toggleDebugOutlines()
}
else if (event.key === 'F3'){
toggleScrollbar('inner')
}
else {
prevent = false
}
if (prevent === true) {
event.preventDefault()
}
});- You can toggle a class on and off for an element using
element.classList.toggle('classname')
NotoSans.ttfis an open-sourcemonospacedfont fromGoogle Fontsthat can be found herematerial-128x128.icois an open-source icon from theGoogle Material Iconssite which can be found hereGoogle Material Iconsoffer a selection of icons that you can access via their api, or copy theSVGcode directly
- The aim of this project was to learn, and as such, the result was not intended for any commercial purposes
- This project, is not affiliated with, nor endorsed in any way by, the puzzle Wordle on the
New York Timeswebsite