diff --git a/README.md b/README.md index 0e0901c..c1c225e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,32 @@ # FWDekker Template -The template used on many pages on fwdekker.com. +The base template for pages on fwdekker.com. + +This module contains templating functions (e.g. `header`, `footer`), CSS libraries, and some common utility methods that +are used on nearly all pages anyway. + + +## Development +### Requirements +* [npm](https://www.npmjs.com/) + +### Setting up +```shell script +# Install dependencies (only needed once) +$> npm ci +``` + +### Building +```shell script +# Build the application in `build/` for development +$> npm run dev +# Build the application in `build/` for deployment +$> npm run deploy +``` + +### Publishing +```shell script +# Log in to npm +$> npm login +# Push to npm +$> npm publish --access public +``` diff --git a/package-lock.json b/package-lock.json index 8d9f95a..0cf07af 100644 Binary files a/package-lock.json and b/package-lock.json differ diff --git a/package.json b/package.json index 779a6e4..5b619c4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@fwdekker/template", - "version": "0.0.5", - "description": "The template used on many pages on fwdekker.com.", + "version": "0.0.6", + "description": "The base template for pages on fwdekker.com.", "author": "Felix W. Dekker (https://fwdekker.com)", "license": "MIT", "homepage": "https://git.fwdekker.com/FWDekker/fwdekker-template", @@ -12,8 +12,20 @@ "bugs": { "url": "https://git.fwdekker.com/FWDekker/fwdekker-template/issues" }, - "main": "src/main/js/Template.js", + "module": "dist/index.js", + "scripts": { + "dev": "webpack --mode=development", + "deploy": "webpack --mode=production" + }, "dependencies": { - "hyperscript": "^2.0.2" + "hyperscript": "^2.0.2", + "milligram": "^1.3.0", + "normalize.css": "^8.0.1" + }, + "devDependencies": { + "css-loader": "^3.5.3", + "style-loader": "^1.2.1", + "webpack": "^4.42.1", + "webpack-cli": "^3.3.11" } } diff --git a/src/main/css/common.css b/src/main/css/common.css new file mode 100644 index 0000000..35e10e7 --- /dev/null +++ b/src/main/css/common.css @@ -0,0 +1,124 @@ +:root { + --fwdekker-theme-color: #0033cc; +} + +html { + height: 100%; +} + +body { + position: relative; + + padding-top: 5rem; + padding-bottom: 9rem; /* Vertical footer paddings + 1 */ + min-height: 100%; +} + +.footer { + position: absolute; + bottom: 0; + + padding-top: 5rem; + padding-bottom: 3rem; + width: 100%; +} + + +/* Make arrow next to dropdown visible */ +select { + -webkit-appearance: menulist; + -moz-appearance: menulist; + appearance: auto; +} + + +/* Override color scheme */ +/* Replaces #9b4dca with #0033cc */ +.button, +button, +input[type='button'], +input[type='reset'], +input[type='submit'] { + background-color: var(--fwdekker-theme-color); + border: 0.1rem solid var(--fwdekker-theme-color); +} + +.button[disabled]:focus, .button[disabled]:hover, +button[disabled]:focus, +button[disabled]:hover, +input[type='button'][disabled]:focus, +input[type='button'][disabled]:hover, +input[type='reset'][disabled]:focus, +input[type='reset'][disabled]:hover, +input[type='submit'][disabled]:focus, +input[type='submit'][disabled]:hover { + background-color: var(--fwdekker-theme-color); + border-color: var(--fwdekker-theme-color); +} + +.button.button-outline, +button.button-outline, +input[type='button'].button-outline, +input[type='reset'].button-outline, +input[type='submit'].button-outline { + color: var(--fwdekker-theme-color); +} + +.button.button-outline[disabled]:focus, .button.button-outline[disabled]:hover, +button.button-outline[disabled]:focus, +button.button-outline[disabled]:hover, +input[type='button'].button-outline[disabled]:focus, +input[type='button'].button-outline[disabled]:hover, +input[type='reset'].button-outline[disabled]:focus, +input[type='reset'].button-outline[disabled]:hover, +input[type='submit'].button-outline[disabled]:focus, +input[type='submit'].button-outline[disabled]:hover { + color: var(--fwdekker-theme-color); +} + +.button.button-clear, +button.button-clear, +input[type='button'].button-clear, +input[type='reset'].button-clear, +input[type='submit'].button-clear { + color: var(--fwdekker-theme-color); +} + +.button.button-clear[disabled]:focus, .button.button-clear[disabled]:hover, +button.button-clear[disabled]:focus, +button.button-clear[disabled]:hover, +input[type='button'].button-clear[disabled]:focus, +input[type='button'].button-clear[disabled]:hover, +input[type='reset'].button-clear[disabled]:focus, +input[type='reset'].button-clear[disabled]:hover, +input[type='submit'].button-clear[disabled]:focus, +input[type='submit'].button-clear[disabled]:hover { + color: var(--fwdekker-theme-color); +} + +pre { + border-left: 0.3rem solid var(--fwdekker-theme-color); +} + +input[type='email']:focus, +input[type='number']:focus, +input[type='password']:focus, +input[type='search']:focus, +input[type='tel']:focus, +input[type='text']:focus, +input[type='url']:focus, +input[type='color']:focus, +input[type='date']:focus, +input[type='month']:focus, +input[type='week']:focus, +input[type='datetime']:focus, +input[type='datetime-local']:focus, +input:not([type]):focus, +textarea:focus, +select:focus { + border-color: var(--fwdekker-theme-color); +} + +a { + color: var(--fwdekker-theme-color); +} diff --git a/src/main/js/Template.js b/src/main/js/Template.js index df96d75..2148da2 100644 --- a/src/main/js/Template.js +++ b/src/main/js/Template.js @@ -1,4 +1,33 @@ -const h = require("hyperscript"); +import h from "hyperscript"; +import "milligram/dist/milligram.css"; +import "normalize.css/normalize.css"; +import "../css/common.css"; + + +/** + * Alias for `document.querySelector`. + * + * @param q {string} the query string + * @returns {HTMLElement} the element identified by the query string + */ +export const $ = q => document.querySelector(q); + +/** + * Runs the given function once the page is loaded. + * + * This function can be used multiple times. It does not overwrite existing callbacks for the page load event. + * + * @param fun {function(...*): *} the function to run + */ +export const doAfterLoad = function (fun) { + const oldOnLoad = window.onload || (() => { + }); + + window.onload = (() => { + oldOnLoad(); + fun(); + }); +}; /** @@ -8,14 +37,14 @@ const h = require("hyperscript"); * @param description {string} the description to display * @returns {HTMLElement} a header element */ -exports.header = function ({title, description}) { +export const header = function ({title, description}) { return h("header.header", h("section.container", h("h1", title), h("p", h("em", description)) ) ); -} +}; /** * Creates a footer element with the given data. @@ -29,7 +58,7 @@ exports.header = function ({title, description}) { * @param [version] {string} the page version * @returns {HTMLElement} a footer element */ -exports.footer = function ({author, authorURL, license, licenseURL, vcs, vcsURL, version}) { +export const footer = function ({author, authorURL, license, licenseURL, vcs, vcsURL, version}) { return h("footer.footer", h("section.container", footerLink("Made by ", author, authorURL, ". "), @@ -38,7 +67,7 @@ exports.footer = function ({author, authorURL, license, licenseURL, vcs, vcsURL, h("div", version || "", {style: {"float": "right"}}) ) ); -} +}; /** @@ -60,4 +89,4 @@ const footerLink = function (prefix, text, url, suffix) { : h("span", text), h("span", suffix) ); -} +}; diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..ade1ebe --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,26 @@ +const path = require("path"); + +module.exports = { + entry: "./src/main/js/Template.js", + module: { + rules: [ + { + test: /\.js$/i, + exclude: /node_modules/, + }, + { + test: /\.css$/i, + use: ["style-loader", "css-loader"], + }, + ], + }, + resolve: { + extensions: [".js", ".css"], + }, + output: { + library: "fwdekker-template", + libraryTarget: "umd", + filename: "index.js", + path: path.resolve(__dirname, "dist"), + } +};