commit 3171afa636a03db2c434990186f684f3a059d31e Author: Chris Watson Date: Fri Dec 9 09:04:34 2022 -0700 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..47e9570 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +_site +_cache +deno.lock \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..27ec43b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "deno.enable": true, + "deno.lint": true, + "deno.unstable": true, + "deno.suggest.imports.hosts": { + "https://deno.land": true + } +} diff --git a/404.yml b/404.yml new file mode 100644 index 0000000..b46bce3 --- /dev/null +++ b/404.yml @@ -0,0 +1,10 @@ +layout: layouts/error.njk +title: "404: Page Not Found" +description: | + Sorry, the page you are looking for could not be found. + +content: + title: "Error 404:
Page Not Found" + description: Sorry, the page you are looking for could not be found. + button: + title: Return Home diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6301e6f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Óscar Otero + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/_components/fire.njk b/_components/fire.njk new file mode 100644 index 0000000..7c55668 --- /dev/null +++ b/_components/fire.njk @@ -0,0 +1,205 @@ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + diff --git a/_config.ts b/_config.ts new file mode 100644 index 0000000..9e18b74 --- /dev/null +++ b/_config.ts @@ -0,0 +1,35 @@ +import lume from "lume/mod.ts"; +import prism from "lume/plugins/prism.ts"; +import inline from "lume/plugins/inline.ts"; +import resolveUrls from "lume/plugins/resolve_urls.ts"; +import esbuild from "lume/plugins/esbuild.ts"; +import imagick from "lume/plugins/imagick.ts"; +import minifyHTML from "lume/plugins/minify_html.ts"; +import sitemap from "lume/plugins/sitemap.ts"; +import windi from "lume/plugins/windi_css.ts"; + +const site = lume( + { + location: new URL("https://watzon.tech"), + server: { + page404: "/404/", + }, + }, +); + +site + .ignore("README.md") + .ignore("scripts") + .copy("static", ".") + .use(prism()) + .use(windi()) + .use(inline()) + .use(esbuild({ + extensions: [".js"], + })) + .use(resolveUrls()) + .use(imagick()) + .use(sitemap()) + .use(minifyHTML()); + +export default site; diff --git a/_includes/layouts/base.njk b/_includes/layouts/base.njk new file mode 100644 index 0000000..4fed3ea --- /dev/null +++ b/_includes/layouts/base.njk @@ -0,0 +1,52 @@ + + + + + + + {{ title }} - Lume + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {% include "templates/navbar.njk" %} +
+ {{ content | safe }} +
+ {% include "templates/footer.njk" %} + + diff --git a/_includes/layouts/error.njk b/_includes/layouts/error.njk new file mode 100644 index 0000000..e2e6d0e --- /dev/null +++ b/_includes/layouts/error.njk @@ -0,0 +1,20 @@ +--- +layout: ./base.njk +page_css: error.css +--- + +
+

{{ content.title | safe }}

+ +
+ {{ comp.fire() | safe }} +
+ + {{ content.description | md | safe }} + + + + +
\ No newline at end of file diff --git a/_includes/layouts/landing.njk b/_includes/layouts/landing.njk new file mode 100644 index 0000000..d95560b --- /dev/null +++ b/_includes/layouts/landing.njk @@ -0,0 +1,130 @@ +--- +layout: ./base.njk +page_css: landing.css +--- +
+
+

{{ header.title | safe }}

+ +
+ {{ comp.fire() | safe }} +
+ +
+ {{ install.title | md | safe }} + +
+
{{ install.code }}
+ + + +
+ + deno.land/x/lume +
+
+ + +
+ {% for block in usage %} +
+

{{ block.title }}

+ {{ block.description | md | safe }} +
+
+ {{ block.code | md | safe }} +
+ {% endfor %} +
+ + + +
+
+

{{ examples.title }}

+ + + + + +
+ + + {% set showcase = search.page("url=/showcase/") %} + {% for site in showcase.data.sites | selectattr("img") | slice(8) %} + + {{ site.title }} + + {% endfor %} + {{ examples.more.text }} + +
+ +
+

{{ testimonials.title }}

+ + +
+ +
+

{{ support.title }}

+ +
    + {% for sponsor in support.sponsors %} +
  • + + {{ sponsor.name }} + +
  • + {% endfor %} +
+ +
+

{{ support.contribute.title }}

+ + {{ support.contribute.description | md | safe }} +
+
+
\ No newline at end of file diff --git a/_includes/templates/footer.njk b/_includes/templates/footer.njk new file mode 100644 index 0000000..6497fc2 --- /dev/null +++ b/_includes/templates/footer.njk @@ -0,0 +1,4 @@ + diff --git a/_includes/templates/navbar.njk b/_includes/templates/navbar.njk new file mode 100644 index 0000000..2f0a0eb --- /dev/null +++ b/_includes/templates/navbar.njk @@ -0,0 +1,26 @@ + \ No newline at end of file diff --git a/deno.json b/deno.json new file mode 100644 index 0000000..92a5749 --- /dev/null +++ b/deno.json @@ -0,0 +1,36 @@ +{ + "importMap": "import_map.json", + "compilerOptions": { + "lib": [ + "dom", + "dom.iterable", + "dom.asynciterable", + "deno.ns", + "deno.unstable" + ] + }, + "tasks": { + "build": "deno task lume", + "serve": "deno task lume -s", + "lume": "echo \"import 'lume/cli.ts'\" | deno run --unstable -A -" + }, + "lint": { + "files": { + "exclude": [ + "./_site" + ] + }, + "rules": { + "tags": [ + "recommended" + ] + } + }, + "fmt": { + "files": { + "exclude": [ + "./_site" + ] + } + } +} diff --git a/import_map.json b/import_map.json new file mode 100644 index 0000000..8ff37b9 --- /dev/null +++ b/import_map.json @@ -0,0 +1,5 @@ +{ + "imports": { + "lume/": "https://deno.land/x/lume@v1.13.0/" + } +} diff --git a/index.njk b/index.njk new file mode 100644 index 0000000..978be17 --- /dev/null +++ b/index.njk @@ -0,0 +1,5 @@ +--- +layout: layouts/base.njk +title: Chris Watson - Full Stack Engineer, 3D Printing Enthusiast, and Open Sourcerer +description: Personal website for Chris Watson, also known as @watzon most places. +--- \ No newline at end of file diff --git a/main.js b/main.js new file mode 100644 index 0000000..1f817e2 --- /dev/null +++ b/main.js @@ -0,0 +1,19 @@ +import CodeExample from "./scripts/components/code_example.js"; +import ThemeToggle from "./scripts/components/theme_toggle.js"; + +customElements.define("code-examples", CodeExample); +customElements.define("theme-toggle", ThemeToggle); + +// For testing purpose of CSP middleware +const userAgentString = navigator.userAgent; +const chromeAgent = userAgentString.indexOf("Chrome") > -1; + +if (chromeAgent) { + const observer = new ReportingObserver((reports) => { + for (const report of reports) { + console.log(report.type, report.url, report.body); + } + }, { buffered: true }); + + observer.observe(); +} diff --git a/netlify.toml b/netlify.toml new file mode 100644 index 0000000..9d29cb9 --- /dev/null +++ b/netlify.toml @@ -0,0 +1,6 @@ +[build] + publish = "_site" + command = """ + curl -fsSL https://deno.land/x/install/install.sh | sh && \ + /opt/buildhome/.deno/bin/deno task build \ + """ \ No newline at end of file diff --git a/scripts/components/code_example.js b/scripts/components/code_example.js new file mode 100644 index 0000000..098f375 --- /dev/null +++ b/scripts/components/code_example.js @@ -0,0 +1,70 @@ +// ARIA: tab role, best practices: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/tab_role +// customElements eventListener: https://www.jonasfaehrmann.com/writing/post/2021-02-01-custom-elements-event-listener-and-this/ + +export default class CodeExample extends HTMLElement { + constructor() { + super(); + this.tabFocus = 0; + this.tabs = this.querySelectorAll('[role="tab"]'); + this.tabList = this.querySelector('[role="tablist"]'); + this.buttonBoundListener = this.handleTabChange.bind(this); + this.keydownBoundListener = this.handleKeyPress.bind(this); + } + + connectedCallback() { + this.tabs.forEach((tab) => { + tab.addEventListener("click", this.buttonBoundListener); + }); + + this.tabList.addEventListener("keydown", this.keydownBoundListener); + } + + handleKeyPress(e) { + if (e.keyCode === 39 || e.keyCode === 37) { + this.tabs[this.tabFocus].setAttribute("tabindex", -1); + if (e.keyCode === 39) { + this.tabFocus++; + if (this.tabFocus >= this.tabs.length) { + this.tabFocus = 0; + } + } else if (e.keyCode === 37) { + this.tabFocus--; + if (this.tabFocus < 0) { + this.tabFocus = this.tabs.length - 1; + } + } + + this.tabs[this.tabFocus].setAttribute("tabindex", 0); + this.tabs[this.tabFocus].focus(); + } + } + + handleTabChange(e) { + const target = e.target; + const parent = target.parentNode; + const grandparent = parent.parentNode; + + const current = target.getAttribute("aria-controls"); + + grandparent.querySelectorAll('[aria-selected="true"]').forEach((t) => { + if (t === target) return; + t.setAttribute("aria-selected", false); + t.setAttribute("tabindex", -1); + t.classList.remove("is-active"); + }); + + target.setAttribute("aria-selected", true); + target.setAttribute("tabindex", 0); + target.classList.add("is-active"); + + grandparent.parentNode.querySelectorAll('[role="tabpanel"]').forEach( + (p) => { + if (p.id === current) { + p.removeAttribute("hidden"); + } else { + p.setAttribute("hidden", true); + } + }, + ); + } +} diff --git a/scripts/components/theme_toggle.js b/scripts/components/theme_toggle.js new file mode 100644 index 0000000..4e2e796 --- /dev/null +++ b/scripts/components/theme_toggle.js @@ -0,0 +1,52 @@ +export default class CodeExample extends HTMLElement { + constructor() { + super(); + this.themeToggleBtn = this.querySelector("#theme-toggle"); + this.themeToggleDarkIcon = this.themeToggleBtn.querySelector( + "#theme-toggle-dark-icon", + ); + this.themeToggleLightIcon = this.themeToggleBtn.querySelector( + "#theme-toggle-light-icon", + ); + + // Change the icons inside the button based on previous settings + if ( + localStorage.getItem("color-theme") === "dark" || + (!("color-theme" in localStorage) && + window.matchMedia("(prefers-color-scheme: dark)").matches) + ) { + this.themeToggleLightIcon.classList.remove("hidden"); + } else { + this.themeToggleDarkIcon.classList.remove("hidden"); + } + + this.onclick = this.toggleTheme.bind(this); + } + + toggleTheme() { + // toggle icons inside button + this.themeToggleDarkIcon.classList.toggle("hidden"); + this.themeToggleLightIcon.classList.toggle("hidden"); + + // if set via local storage previously + if (localStorage.getItem("color-theme")) { + if (localStorage.getItem("color-theme") === "light") { + document.documentElement.classList.add("dark"); + localStorage.setItem("color-theme", "dark"); + } else { + document.documentElement.classList.remove("dark"); + localStorage.setItem("color-theme", "light"); + } + + // if NOT set via local storage previously + } else { + if (document.documentElement.classList.contains("dark")) { + document.documentElement.classList.remove("dark"); + localStorage.setItem("color-theme", "light"); + } else { + document.documentElement.classList.add("dark"); + localStorage.setItem("color-theme", "dark"); + } + } + } +} diff --git a/static/android-chrome-192x192.png b/static/android-chrome-192x192.png new file mode 100644 index 0000000..d0f9c82 Binary files /dev/null and b/static/android-chrome-192x192.png differ diff --git a/static/android-chrome-512x512.png b/static/android-chrome-512x512.png new file mode 100644 index 0000000..b4cdf28 Binary files /dev/null and b/static/android-chrome-512x512.png differ diff --git a/static/apple-touch-icon.png b/static/apple-touch-icon.png new file mode 100644 index 0000000..5d2d4ff Binary files /dev/null and b/static/apple-touch-icon.png differ diff --git a/static/avatar.png b/static/avatar.png new file mode 100755 index 0000000..d6bc27e Binary files /dev/null and b/static/avatar.png differ diff --git a/static/favicon-16x16.png b/static/favicon-16x16.png new file mode 100644 index 0000000..a51cc24 Binary files /dev/null and b/static/favicon-16x16.png differ diff --git a/static/favicon-32x32.png b/static/favicon-32x32.png new file mode 100644 index 0000000..37152df Binary files /dev/null and b/static/favicon-32x32.png differ diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 0000000..65c8d98 Binary files /dev/null and b/static/favicon.ico differ diff --git a/static/fonts/epilogue.woff2 b/static/fonts/epilogue.woff2 new file mode 100644 index 0000000..319218a Binary files /dev/null and b/static/fonts/epilogue.woff2 differ diff --git a/static/fonts/inter.woff2 b/static/fonts/inter.woff2 new file mode 100644 index 0000000..fff3df9 Binary files /dev/null and b/static/fonts/inter.woff2 differ diff --git a/static/fonts/jetbrains-mono.woff2 b/static/fonts/jetbrains-mono.woff2 new file mode 100644 index 0000000..f412968 Binary files /dev/null and b/static/fonts/jetbrains-mono.woff2 differ diff --git a/static/icons/arrow-left.svg b/static/icons/arrow-left.svg new file mode 100644 index 0000000..97f4cd5 --- /dev/null +++ b/static/icons/arrow-left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/icons/arrow-right.svg b/static/icons/arrow-right.svg new file mode 100644 index 0000000..90ca098 --- /dev/null +++ b/static/icons/arrow-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/icons/deno.svg b/static/icons/deno.svg new file mode 100644 index 0000000..eb7e044 --- /dev/null +++ b/static/icons/deno.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/icons/deploy.svg b/static/icons/deploy.svg new file mode 100644 index 0000000..9e21928 --- /dev/null +++ b/static/icons/deploy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/icons/extend.svg b/static/icons/extend.svg new file mode 100644 index 0000000..e6b0220 --- /dev/null +++ b/static/icons/extend.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/static/icons/stack.svg b/static/icons/stack.svg new file mode 100644 index 0000000..a6b682a --- /dev/null +++ b/static/icons/stack.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/static/manifest.json b/static/manifest.json new file mode 100644 index 0000000..5b4f5e8 --- /dev/null +++ b/static/manifest.json @@ -0,0 +1,30 @@ +{ + "name": "lume.land", + "short_name": "lume", + "description": "The fast & flexible static site generator for Deno.", + "lang": "en", + "id": "/", + "start_url": "/", + "theme_color": "#fff", + "background_color": "#141b1f", + "orientation": "natural", + "display": "standalone", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/styles/main.windi.css b/styles/main.windi.css new file mode 100644 index 0000000..e69de29