Resurrected from the basement…
This commit is contained in:
parent
9a724a2c43
commit
f8f9d3eb20
23 changed files with 762 additions and 68 deletions
|
|
@ -69,6 +69,8 @@ export default async function (eleventyConfig) {
|
|||
}
|
||||
});
|
||||
|
||||
eleventyConfig.addPlugin(plugins.EleventyNavigationPlugin);
|
||||
|
||||
// --------------------- bundle
|
||||
eleventyConfig.addBundle('css', {hoist: true});
|
||||
|
||||
|
|
@ -117,6 +119,11 @@ export default async function (eleventyConfig) {
|
|||
eleventyConfig.on('eleventy.after', events.svgToJpeg);
|
||||
}
|
||||
|
||||
// Pagefind search index
|
||||
if (process.env.ELEVENTY_RUN_MODE === 'build') {
|
||||
eleventyConfig.on('eleventy.after', events.buildPagefind);
|
||||
}
|
||||
|
||||
// --------------------- Passthrough File Copy
|
||||
|
||||
// -- same path
|
||||
|
|
|
|||
24
package-lock.json
generated
24
package-lock.json
generated
|
|
@ -1,17 +1,18 @@
|
|||
{
|
||||
"name": "eleventy-excellent",
|
||||
"version": "4.5.0",
|
||||
"name": "hypnagaga",
|
||||
"version": "0.0.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "eleventy-excellent",
|
||||
"version": "4.5.0",
|
||||
"name": "hypnagaga",
|
||||
"version": "0.0.1",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@11ty/eleventy": "^3.1.2",
|
||||
"@11ty/eleventy-fetch": "^5.1.0",
|
||||
"@11ty/eleventy-img": "^6.0.4",
|
||||
"@11ty/eleventy-navigation": "^1.0.4",
|
||||
"@11ty/eleventy-plugin-rss": "^2.0.4",
|
||||
"@11ty/eleventy-plugin-syntaxhighlight": "^5.0.2",
|
||||
"@11ty/eleventy-plugin-webc": "^0.11.2",
|
||||
|
|
@ -42,6 +43,7 @@
|
|||
"markdown-it-prism": "^3.0.1",
|
||||
"netlify-plugin-cache": "^1.0.3",
|
||||
"pa11y-ci": "^4.0.1",
|
||||
"pagefind": "^1.4.0",
|
||||
"postcss": "^8.5.6",
|
||||
"postcss-cli": "^11.0.1",
|
||||
"postcss-import": "^16.1.1",
|
||||
|
|
@ -604,6 +606,19 @@
|
|||
"@img/sharp-win32-x64": "0.33.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@11ty/eleventy-navigation": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@11ty/eleventy-navigation/-/eleventy-navigation-1.0.5.tgz",
|
||||
"integrity": "sha512-zb6xe29cM9viSdYtZywKIkJw2HIROyBINdBcFWC9uD0c/jYOTAex5nwy3HNEuh5t6/Ld/S9V4gEizfmeYuYpCQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dependency-graph": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/11ty"
|
||||
}
|
||||
},
|
||||
"node_modules/@11ty/eleventy-plugin-bundle": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@11ty/eleventy-plugin-bundle/-/eleventy-plugin-bundle-3.0.7.tgz",
|
||||
|
|
@ -2786,7 +2801,6 @@
|
|||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"anymatch": "~3.1.2",
|
||||
"braces": "~3.0.2",
|
||||
|
|
|
|||
15
package.json
15
package.json
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"name": "eleventy-excellent",
|
||||
"version": "4.5.0",
|
||||
"description": "Eleventy starter built around the workflow suggested by Andy Bell's buildexcellentwebsit.es.",
|
||||
"author": "Lene Saile",
|
||||
"name": "hypnagaga",
|
||||
"version": "0.0.1",
|
||||
"description": "A web site.",
|
||||
"author": "Ben Aultowski",
|
||||
"license": "ISC",
|
||||
"type": "module",
|
||||
"engines": {
|
||||
|
|
@ -16,8 +16,9 @@
|
|||
"screenshots": "node ./src/_config/setup/generate-screenshots.js",
|
||||
"dev:11ty": "cross-env ELEVENTY_ENV=development eleventy --serve",
|
||||
"build:11ty": "cross-env ELEVENTY_ENV=production eleventy",
|
||||
"build:search": "pagefind --site 'dist' --glob '**/*.html'",
|
||||
"start": "npm run dev:11ty",
|
||||
"build": "npm run clean && npm run build:11ty",
|
||||
"build": "npm run clean && npm run build:11ty && npm run build:search",
|
||||
"pa11y:build": "npm run clean && cross-env ELEVENTY_ENV=test eleventy",
|
||||
"pa11y:test": "sleep 3 && pa11y-ci --config ./dist/pa11y.json",
|
||||
"pa11y:serve": "ELEVENTY_ENV=test eleventy --serve --ignore-initial",
|
||||
|
|
@ -26,12 +27,13 @@
|
|||
"keywords": [],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/madrilene/eleventy-excellent.git"
|
||||
"url": "https://git.hypnagaga.com/wires/hypnagaga.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@11ty/eleventy": "^3.1.2",
|
||||
"@11ty/eleventy-fetch": "^5.1.0",
|
||||
"@11ty/eleventy-img": "^6.0.4",
|
||||
"@11ty/eleventy-navigation": "^1.0.4",
|
||||
"@11ty/eleventy-plugin-rss": "^2.0.4",
|
||||
"@11ty/eleventy-plugin-syntaxhighlight": "^5.0.2",
|
||||
"@11ty/eleventy-plugin-webc": "^0.11.2",
|
||||
|
|
@ -62,6 +64,7 @@
|
|||
"markdown-it-prism": "^3.0.1",
|
||||
"netlify-plugin-cache": "^1.0.3",
|
||||
"pa11y-ci": "^4.0.1",
|
||||
"pagefind": "^1.4.0",
|
||||
"postcss": "^8.5.6",
|
||||
"postcss-cli": "^11.0.1",
|
||||
"postcss-import": "^16.1.1",
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import {svgToJpeg} from './events/svg-to-jpeg.js';
|
||||
import {buildAllCss} from './events/build-css.js';
|
||||
import {buildAllJs} from './events/build-js.js';
|
||||
import {buildPagefind} from './events/build-pagefind-index.js';
|
||||
|
||||
export default {
|
||||
svgToJpeg,
|
||||
buildAllCss,
|
||||
buildAllJs
|
||||
buildAllJs,
|
||||
buildPagefind
|
||||
};
|
||||
|
|
|
|||
13
src/_config/events/build-pagefind-index.js
Normal file
13
src/_config/events/build-pagefind-index.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import {execSync} from 'node:child_process';
|
||||
|
||||
export const buildPagefind = () => {
|
||||
console.log('Building Pagefind index...');
|
||||
try {
|
||||
execSync(`npx pagefind --site dist --glob "**/*.html"`, {
|
||||
encoding: 'utf-8',
|
||||
stdio: 'inherit'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Pagefind build failed: ', error.message);
|
||||
}
|
||||
};
|
||||
|
|
@ -4,6 +4,7 @@ import rss from '@11ty/eleventy-plugin-rss';
|
|||
import syntaxHighlight from '@11ty/eleventy-plugin-syntaxhighlight';
|
||||
import webc from '@11ty/eleventy-plugin-webc';
|
||||
import {eleventyImageTransformPlugin} from '@11ty/eleventy-img';
|
||||
import EleventyNavigationPlugin from '@11ty/eleventy-navigation';
|
||||
|
||||
// custom
|
||||
import {markdownLib} from './plugins/markdown.js';
|
||||
|
|
@ -18,6 +19,7 @@ export default {
|
|||
syntaxHighlight,
|
||||
webc,
|
||||
eleventyImageTransformPlugin,
|
||||
EleventyNavigationPlugin,
|
||||
markdownLib,
|
||||
drafts,
|
||||
htmlConfig
|
||||
|
|
|
|||
|
|
@ -1,24 +1,24 @@
|
|||
export const url = process.env.URL || 'http://localhost:8080';
|
||||
// Extract domain from `url`
|
||||
export const domain = new URL(url).hostname;
|
||||
export const siteName = 'Eleventy Excellent';
|
||||
export const siteDescription = 'Eleventy starter for building modern, resilient websites';
|
||||
export const siteName = 'Hypnagaga';
|
||||
export const siteDescription = 'A vision quest.';
|
||||
export const siteType = 'Person'; // schema
|
||||
export const locale = 'en_EN';
|
||||
export const lang = 'en';
|
||||
export const skipContent = 'Skip to content';
|
||||
export const author = {
|
||||
name: 'Lene Saile', // i.e. Lene Saile - page / blog author's name. Must be set.
|
||||
name: 'Ben Aultowski',
|
||||
avatar: '/icon-512x512.png', // path to the author's avatar. In this case just using a favicon.
|
||||
email: 'hola@lenesaile.com', // i.e. hola@lenesaile.com - email of the author
|
||||
website: 'https://www.lenesaile.com', // i.e. https.://www.lenesaile.com - the personal site of the author
|
||||
fediverse: '@lene@front-end.social' // used for highlighting journalism on the fediverse. Can be Mastodon, Flipboard, Threads, WordPress (with the ActivityPub plugin installed), PeerTube, Pixelfed, etc. https://blog.joinmastodon.org/2024/07/highlighting-journalism-on-mastodon/
|
||||
email: 'benaultowski@proton.me',
|
||||
website: 'https://www.hypnagaga.com',
|
||||
fediverse: '' // used for highlighting journalism on the fediverse. Can be Mastodon, Flipboard, Threads, WordPress (with the ActivityPub plugin installed), PeerTube, Pixelfed, etc. https://blog.joinmastodon.org/2024/07/highlighting-journalism-on-mastodon/
|
||||
};
|
||||
export const creator = {
|
||||
name: 'Lene Saile', // i.e. Lene Saile - creator's (developer) name.
|
||||
email: 'hola@lenesaile.com',
|
||||
website: 'https://www.lenesaile.com',
|
||||
social: 'https://front-end.social/@lene'
|
||||
name: 'Ben Aultowski', // i.e. Lene Saile - creator's (developer) name.
|
||||
email: 'benaultowski@proton.me',
|
||||
website: 'https://www.hypnagaga.com',
|
||||
social: ''
|
||||
};
|
||||
export const pathToSvgLogo = 'src/assets/svg/misc/logo.svg'; // used for favicon generation
|
||||
export const themeColor = '#dd4462'; // used in manifest, for example primary color value
|
||||
|
|
|
|||
|
|
@ -1,38 +1,26 @@
|
|||
export default {
|
||||
top: [
|
||||
{
|
||||
text: 'About',
|
||||
url: '/about/'
|
||||
text: 'Why',
|
||||
url: '/why/'
|
||||
},
|
||||
{
|
||||
text: 'Docs',
|
||||
url: '/get-started/'
|
||||
text: 'Thoughts',
|
||||
url: '/thoughts/'
|
||||
},
|
||||
{
|
||||
text: 'Built with',
|
||||
url: '/built-with/'
|
||||
text: 'Stories',
|
||||
url: '/stories/'
|
||||
},
|
||||
{
|
||||
text: 'Blog',
|
||||
url: '/blog/'
|
||||
text: 'Search',
|
||||
url: '/search/'
|
||||
}
|
||||
],
|
||||
bottom: [
|
||||
{
|
||||
text: 'Style guide',
|
||||
url: '/styleguide/'
|
||||
},
|
||||
{
|
||||
text: 'Imprint',
|
||||
url: '/imprint/'
|
||||
},
|
||||
{
|
||||
text: 'Privacy',
|
||||
url: '/privacy/'
|
||||
},
|
||||
{
|
||||
text: 'Accessibility',
|
||||
url: '/accessibility/'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ schema: BlogPosting
|
|||
%}
|
||||
<!-- tags -->
|
||||
{% for tag in tags %}{% if tag != "posts" %}
|
||||
<a class="button" href="/tags/{{ tag | slugify }}/" data-small-button> {{ tag }} </a>
|
||||
<a class="button" href="/tags/{{ tag | slugify }}/" data-pagefind-filter="tag:{{ tag }}" data-pagefind-meta="tag:{{ tag }}" data-small-button> {{ tag }} </a>
|
||||
{% endif %}{% endfor %}
|
||||
{% endif %}
|
||||
</p>
|
||||
|
|
|
|||
255
src/assets/css/global/blocks/search.css
Normal file
255
src/assets/css/global/blocks/search.css
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
/*=============================================
|
||||
= Search Form =
|
||||
=============================================*/
|
||||
|
||||
.search-form {
|
||||
display: flex;
|
||||
gap: var(--space-xs);
|
||||
}
|
||||
|
||||
.search-form__input {
|
||||
flex-grow: 1;
|
||||
padding: var(--space-xs) var(--space-s);
|
||||
border: 1px solid var(--color-primary);
|
||||
border-radius: var(--rounded);
|
||||
font-size: var(--text-base);
|
||||
background-color: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.search-form__input:focus {
|
||||
outline: 2px solid var(--color-secondary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.search-form__button {
|
||||
padding: var(--space-xs) var(--space-s);
|
||||
border: 1px solid var(--color-primary);
|
||||
border-radius: var(--rounded);
|
||||
background-color: var(--color-primary);
|
||||
color: var(--color-bg);
|
||||
cursor: pointer;
|
||||
font-size: var(--text-base);
|
||||
}
|
||||
|
||||
.search-form__button:hover {
|
||||
background-color: var(--color-secondary);
|
||||
}
|
||||
|
||||
/*=============================================
|
||||
= Search Results =
|
||||
=============================================*/
|
||||
|
||||
.search-results-summary {
|
||||
margin-block-end: var(--space-l);
|
||||
font-size: var(--text-l);
|
||||
font-weight: var(--font-bold);
|
||||
}
|
||||
|
||||
.search-results-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: grid;
|
||||
gap: var(--space-l);
|
||||
}
|
||||
|
||||
.search-result {
|
||||
padding: var(--space-m);
|
||||
border: 1px solid var(--color-bg-accent);
|
||||
border-radius: var(--rounded-lg);
|
||||
background-color: var(--color-bg-accent);
|
||||
}
|
||||
|
||||
.search-result__title {
|
||||
margin-block-start: 0;
|
||||
margin-block-end: var(--space-xs);
|
||||
font-size: var(--text-xl);
|
||||
}
|
||||
|
||||
.search-result__title a {
|
||||
text-decoration: none;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.search-result__title a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.search-result__excerpt {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.search-results-list custom-card p mark {
|
||||
background-color: var(--color-highlight);
|
||||
color: var(--color-text);
|
||||
padding: 0.1em 0.2em;
|
||||
border-radius: var(--rounded-sm);
|
||||
}
|
||||
|
||||
/*=============================================
|
||||
= Search Filters =
|
||||
=============================================*/
|
||||
|
||||
#custom-search-filters {
|
||||
margin-block-start: var(--space-l);
|
||||
padding: var(--space-m);
|
||||
border: 1px solid var(--color-bg-accent);
|
||||
border-radius: var(--rounded-lg);
|
||||
background-color: var(--color-bg-accent);
|
||||
}
|
||||
|
||||
#custom-search-filters h3 {
|
||||
margin-block-start: 0;
|
||||
margin-block-end: var(--space-m);
|
||||
font-size: var(--text-l);
|
||||
font-weight: var(--font-bold);
|
||||
}
|
||||
|
||||
.filter-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-s);
|
||||
}
|
||||
|
||||
.filter-group label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-xs);
|
||||
padding: var(--space-xs) var(--space-s);
|
||||
border: 1px solid var(--color-bg-accent);
|
||||
border-radius: var(--rounded);
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.filter-group label:hover {
|
||||
background-color: var(--color-bg-accent);
|
||||
}
|
||||
|
||||
.filter-group input[type="checkbox"] {
|
||||
/* Hide the default checkbox */
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
border: 1px solid var(--color-primary);
|
||||
border-radius: var(--rounded-sm);
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.filter-group input[type="checkbox"]:checked {
|
||||
background-color: var(--color-primary);
|
||||
}
|
||||
|
||||
.filter-group input[type="checkbox"]:checked::after {
|
||||
content: '✔';
|
||||
color: var(--color-bg);
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 0.8em;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/*=============================================
|
||||
= Search Result Cards (custom-card styling) =
|
||||
=============================================*/
|
||||
|
||||
.search-results-list custom-card {
|
||||
--gutter: var(--space-xs-s);
|
||||
background-color: var(--card-bg, var(--color-bg-accent));
|
||||
border: 4px solid var(--color-bg-accent);
|
||||
color: var(--color-text);
|
||||
padding: var(--space-s-m);
|
||||
border-radius: var(--border-radius-medium);
|
||||
max-inline-size: unset;
|
||||
display: grid;
|
||||
grid-template-rows: [image] max-content [headline] max-content [meta] max-content [desc] auto [footer] max-content;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
transition: border-color 0.2s ease;
|
||||
}
|
||||
|
||||
/* avoid flow space being added to unused elements */
|
||||
.search-results-list custom-card > :empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.search-results-list custom-card ::selection {
|
||||
color: var(--color-dark);
|
||||
background-color: var(--color-secondary);
|
||||
}
|
||||
|
||||
.search-results-list custom-card :is(h2, h3) {
|
||||
--flow-space: var(--space-m);
|
||||
grid-row: headline;
|
||||
}
|
||||
|
||||
.search-results-list custom-card :is(h2, h3) a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.search-results-list custom-card > :is(picture, figure) {
|
||||
grid-row: image;
|
||||
--flow-space: 0;
|
||||
}
|
||||
|
||||
.search-results-list custom-card img {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
}
|
||||
|
||||
.search-results-list custom-card > .meta {
|
||||
grid-row: meta;
|
||||
font-size: var(--size-step-min-1);
|
||||
}
|
||||
|
||||
.search-results-list custom-card > p {
|
||||
grid-row: desc;
|
||||
}
|
||||
|
||||
.search-results-list custom-card > footer {
|
||||
grid-row: footer;
|
||||
font-size: var(--size-step-min-2);
|
||||
}
|
||||
|
||||
/* avoid overflow of long words */
|
||||
.search-results-list custom-card :is(a, p, h2, h3) {
|
||||
overflow-wrap: break-word;
|
||||
word-wrap: break-word;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
/* squared image variant */
|
||||
.search-results-list custom-card[img-square] img {
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
/* clickable variant */
|
||||
.search-results-list custom-card[clickable]:hover,
|
||||
.search-results-list custom-card[clickable]:focus-within {
|
||||
border: 4px solid var(--color-primary);
|
||||
}
|
||||
|
||||
.search-results-list custom-card[clickable]:focus-within a:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.search-results-list custom-card[clickable] {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.search-results-list custom-card[clickable] a:after {
|
||||
bottom: 0;
|
||||
content: '';
|
||||
left: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
33
src/assets/css/local/search.css
Normal file
33
src/assets/css/local/search.css
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
form#search {
|
||||
.filter-and-results {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
&.filter-and-results--hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
fieldset.types {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: fit-content;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
.results-area {
|
||||
padding-right: 0.7em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen(ltsm) {
|
||||
form#search {
|
||||
.filter-and-results {
|
||||
flex-direction: column;
|
||||
|
||||
.results-area {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
src/assets/og-images/gasheyes-creek-preview.jpeg
Normal file
BIN
src/assets/og-images/gasheyes-creek-preview.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
226
src/assets/scripts/bundle/search.js
Normal file
226
src/assets/scripts/bundle/search.js
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
import dayjs from 'dayjs';
|
||||
|
||||
const allTypes = ['Page', 'Post'];
|
||||
|
||||
async function ensurePagefind() {
|
||||
if (window.pagefind) return Promise.resolve(window.pagefind);
|
||||
return import('/pagefind/pagefind.js')
|
||||
.then(function (mod) {
|
||||
mod.options({
|
||||
highlightParam: 'highlight'
|
||||
});
|
||||
window.pagefind = mod;
|
||||
return window.pagefind || mod.pagefind || mod.default || mod;
|
||||
})
|
||||
.catch(function () {
|
||||
return new Promise(function (resolve) {
|
||||
const s = document.createElement('script');
|
||||
s.src = '/pagefind/pagefind.js';
|
||||
s.type = 'module';
|
||||
s.onload = function () {
|
||||
resolve(window.pagefind);
|
||||
};
|
||||
s.onerror = function () {
|
||||
resolve(undefined);
|
||||
};
|
||||
document.head.appendChild(s);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function renderItem(item) {
|
||||
let {
|
||||
url,
|
||||
excerpt,
|
||||
meta: {author, date, title, type, image, image_alt, tag}
|
||||
} = item;
|
||||
|
||||
//debugging
|
||||
console.log(item.meta);
|
||||
|
||||
// create date
|
||||
const dateHTML = date ? `<span slot="date">${dayjs(date).format('MMMM D, YYYY') }</span>` : '';
|
||||
|
||||
// create hero image preview
|
||||
let imageHTML = '';
|
||||
if (item.meta.image) {
|
||||
// Try to get alt text from Pagefind metadata, fallback to title, then generic text
|
||||
const altText = item.meta['image[alt]'] || item.meta.title || 'Search result image';
|
||||
imageHTML = `<picture><img src="${item.meta.image}" alt="${altText}"></picture>`;
|
||||
}
|
||||
|
||||
// create tags
|
||||
let tagsHTML = '';
|
||||
if (item.filters && item.filters.tag && item.filters.tag.length > 0) {
|
||||
tagsHTML = `
|
||||
|
||||
${item.filters.tag.map(tag => `<span class="button" data-small-button>${tag}</span>`).join('')}
|
||||
|
||||
`;
|
||||
}
|
||||
|
||||
let variant;
|
||||
switch (type) {
|
||||
case 'Page':
|
||||
variant = 'secondary';
|
||||
break;
|
||||
default:
|
||||
variant = 'primary';
|
||||
break;
|
||||
}
|
||||
|
||||
//output the search result
|
||||
return `
|
||||
<custom-card clickable class="mt-s-m">
|
||||
${imageHTML}
|
||||
<h2 slot="headline" class="text-step-2">
|
||||
<a href="${url}">${title}</a>
|
||||
</h2>
|
||||
${dateHTML}
|
||||
<div slot="type" class="meta | cluster gutter-xs-s" webc:nokeep>
|
||||
<span class="button" data-small-button data-button-variant=${variant}>${type}</span> ${tagsHTML}
|
||||
</div>
|
||||
<div slot="content" webc:nokeep>${excerpt}</div>
|
||||
</custom-card>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderItems(q, items) {
|
||||
var results = items.length == 1 ? 'result' : 'results';
|
||||
document.querySelector('#results-count').innerHTML = `${items.length} ${results} for ${q}`;
|
||||
|
||||
let content = items.map(renderItem).join('');
|
||||
document.querySelector('#results').innerHTML = content;
|
||||
}
|
||||
|
||||
function doSearch(isPopEvent = false) {
|
||||
let form = document.querySelector('form#search');
|
||||
|
||||
// Clear current content
|
||||
document.querySelector('#results').innerHTML = '';
|
||||
document.querySelector('#results-count').innerHTML = '';
|
||||
|
||||
// Get form data
|
||||
let formData = new FormData(form);
|
||||
let q = formData.get('q');
|
||||
|
||||
let types = [];
|
||||
allTypes.map(possibleFilter => {
|
||||
if (formData.get(possibleFilter)) {
|
||||
types.push(possibleFilter);
|
||||
}
|
||||
});
|
||||
|
||||
let tags = [];
|
||||
formData.getAll('tag').forEach(tag => {
|
||||
if (tag) tags.push(tag);
|
||||
});
|
||||
|
||||
// Only do a search if there's a query
|
||||
if (q) {
|
||||
// Update url unless it's a popstate event
|
||||
if (!isPopEvent) {
|
||||
setWindowLocation(q, types, tags, isPopEvent);
|
||||
}
|
||||
|
||||
// Show results area
|
||||
form.querySelector('#filter-and-results').classList.remove('filter-and-results--hidden');
|
||||
|
||||
// Find and display results
|
||||
window.pagefind
|
||||
.search(q, {
|
||||
filters: {
|
||||
type: { any: types },
|
||||
tag: { any: tags }
|
||||
}
|
||||
})
|
||||
|
||||
.then(search =>
|
||||
Promise.all(search.results.map(result => result.data()))
|
||||
.then(data => renderItems(q, data))
|
||||
.catch(console.error)
|
||||
)
|
||||
.catch(console.error);
|
||||
}
|
||||
}
|
||||
|
||||
function setWindowLocation(q, types, tags) {
|
||||
const url = new URL(window.location);
|
||||
if (q) {
|
||||
url.searchParams.set('q', q);
|
||||
}
|
||||
|
||||
url.searchParams.delete('types');
|
||||
for (const type of types) {
|
||||
url.searchParams.append('types', type);
|
||||
}
|
||||
|
||||
url.searchParams.delete('tags');
|
||||
for (const tag of tags) {
|
||||
url.searchParams.append('tags', tag);
|
||||
}
|
||||
|
||||
|
||||
window.history.pushState({search: url.searchParams.toString()}, '', url);
|
||||
}
|
||||
|
||||
function setFormFromLocation() {
|
||||
const url = new URL(window.location);
|
||||
let searchParams = url.searchParams;
|
||||
setFormFromSearchParams(searchParams);
|
||||
}
|
||||
|
||||
function setFormFromSearchParams(searchParams) {
|
||||
let q = searchParams.get('q');
|
||||
document.querySelector('form#search input#q').value = q;
|
||||
|
||||
let types = searchParams.getAll('types');
|
||||
|
||||
for (const type of allTypes) {
|
||||
document.querySelector(`form#search input[name="${type}"]`).checked = types.includes(type);
|
||||
}
|
||||
|
||||
let tags = searchParams.getAll('tags');
|
||||
document.querySelectorAll('form#search input[name="tag"]').forEach(input => {
|
||||
input.checked = tags.includes(input.value);
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener('DOMContentLoaded', _ => {
|
||||
ensurePagefind()
|
||||
.then(_ => {
|
||||
setFormFromLocation();
|
||||
doSearch();
|
||||
})
|
||||
.catch(e => console.error('page find error', e));
|
||||
|
||||
let form = document.querySelector('form#search');
|
||||
form.addEventListener('submit', e => {
|
||||
e.preventDefault();
|
||||
|
||||
doSearch();
|
||||
});
|
||||
|
||||
// Submit form on post type change
|
||||
let checkboxes = form.querySelectorAll('input[type="checkbox"]');
|
||||
checkboxes.forEach(checkbox => {
|
||||
checkbox.addEventListener('change', _ => {
|
||||
doSearch();
|
||||
});
|
||||
});
|
||||
|
||||
// Using browser back or forward button
|
||||
window.addEventListener('popstate', event => {
|
||||
// Only intercept if on search page
|
||||
if (!['/search/', '/search'].includes(window.location.pathname)) return;
|
||||
|
||||
ensurePagefind()
|
||||
.then(_ => {
|
||||
let searchParams = new URLSearchParams(event.state?.search ?? '');
|
||||
setFormFromSearchParams(searchParams);
|
||||
// Don't update history as we're navigating through history!
|
||||
doSearch(true);
|
||||
})
|
||||
.catch(e => console.error('page find error', e));
|
||||
});
|
||||
});
|
||||
9
src/assets/svg/divider/layeredwaves.svg
Normal file
9
src/assets/svg/divider/layeredwaves.svg
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<svg
|
||||
width="1em"
|
||||
height="1em"
|
||||
preserveAspectRatio="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 1000 100"
|
||||
>
|
||||
<g fill="#000"><path d="M1000 100C500 100 500 64 0 64V0h1000v100Z" opacity=".5"></path><path d="M1000 100C500 100 500 34 0 34V0h1000v100Z" opacity=".5"></path><path d="M1000 100C500 100 500 4 0 4V0h1000v100Z"></path></g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 356 B |
9
src/assets/svg/divider/spikes.svg
Normal file
9
src/assets/svg/divider/spikes.svg
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<svg
|
||||
width="1em"
|
||||
height="1em"
|
||||
preserveAspectRatio="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 1000 100"
|
||||
>
|
||||
<path d="M0 0h1000v4H0zM10 100 0 4h20l-10 96zM30 100 20 4h20l-10 96zM50 100 40 4h20l-10 96zM70 100 60 4h20l-10 96zM90 100 80 4h20l-10 96zM110 100 100 4h20l-10 96zM130 100 120 4h20l-10 96zM150 100 140 4h20l-10 96zM170 100 160 4h20l-10 96zM190 100 180 4h20l-10 96zM210 100 200 4h20l-10 96zM230 100 220 4h20l-10 96zM250 100 240 4h20l-10 96zM270 100 260 4h20l-10 96zM290 100 280 4h20l-10 96zM310 100 300 4h20l-10 96zM330 100 320 4h20l-10 96zM350 100 340 4h20l-10 96zM370 100 360 4h20l-10 96zM390 100 380 4h20l-10 96zM410 100 400 4h20l-10 96zM430 100 420 4h20l-10 96zM450 100 440 4h20l-10 96zM470 100 460 4h20l-10 96zM490 100 480 4h20l-10 96zM510 100 500 4h20l-10 96zM530 100 520 4h20l-10 96zM550 100 540 4h20l-10 96zM570 100 560 4h20l-10 96zM590 100 580 4h20l-10 96zM610 100 600 4h20l-10 96zM630 100 620 4h20l-10 96zM650 100 640 4h20l-10 96zM670 100 660 4h20l-10 96zM690 100 680 4h20l-10 96zM710 100 700 4h20l-10 96zM730 100 720 4h20l-10 96zM750 100 740 4h20l-10 96zM770 100 760 4h20l-10 96zM790 100 780 4h20l-10 96zM810 100 800 4h20l-10 96zM830 100 820 4h20l-10 96zM850 100 840 4h20l-10 96zM870 100 860 4h20l-10 96zM890 100 880 4h20l-10 96zM910 100 900 4h20l-10 96zM930 100 920 4h20l-10 96zM950 100 940 4h20l-10 96zM970 100 960 4h20l-10 96zM990 100 980 4h20l-10 96z" fill="#000"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
9
src/assets/svg/divider/triangle.svg
Normal file
9
src/assets/svg/divider/triangle.svg
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<svg
|
||||
width="1em"
|
||||
height="1em"
|
||||
preserveAspectRatio="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 1000 100"
|
||||
>
|
||||
<path d="M0 0v4l500 96 500-96V0H0z" fill="#000"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 191 B |
|
|
@ -1,7 +1,22 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="1em" height="1em" fill="#DD4462">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M10.788 3.21c.448-1.077 1.976-1.077 2.424 0l2.082 5.007 5.404.433c1.164.093 1.636 1.545.749 2.305l-4.117 3.527 1.257 5.273c.271 1.136-.964 2.033-1.96 1.425L12 18.354 7.373 21.18c-.996.608-2.231-.29-1.96-1.425l1.257-5.273-4.117-3.527c-.887-.76-.415-2.212.749-2.305l5.404-.433 2.082-5.006z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 1024 1024">
|
||||
<!-- Generator: Adobe Illustrator 29.6.0, SVG Export Plug-In . SVG Version: 2.1.1 Build 207) -->
|
||||
<defs>
|
||||
<style>
|
||||
.st0 {
|
||||
fill: #4800ff;
|
||||
}
|
||||
|
||||
.st1 {
|
||||
fill: #ff00b4;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<ellipse class="st0" cx="498.56" cy="513.53" rx="575.6" ry="570.45" transform="translate(-27.04 999.36) rotate(-88.6)"/>
|
||||
<circle class="st1" cx="504" cy="504" r="504"/>
|
||||
<circle class="st0" cx="504" cy="504" r="432"/>
|
||||
<ellipse class="st1" cx="504" cy="505" rx="360" ry="359"/>
|
||||
<circle class="st0" cx="504" cy="504" r="288"/>
|
||||
<circle class="st1" cx="512" cy="512" r="216"/>
|
||||
<circle class="st0" cx="512" cy="512" r="155.04"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 465 B After Width: | Height: | Size: 807 B |
|
|
@ -7,6 +7,9 @@ pagination:
|
|||
permalink: /tags/{{ tag | slugify }}/
|
||||
eleventyComputed:
|
||||
title: '{{ meta.blog.tagSingle }}: {{ tag }}'
|
||||
eleventyNavigation:
|
||||
key: '{{ tag }}'
|
||||
parent: Stories
|
||||
---
|
||||
|
||||
<custom-masonry layout="50-50">
|
||||
|
|
|
|||
|
|
@ -1,22 +1,8 @@
|
|||
---
|
||||
title: About
|
||||
permalink: /about/index.html
|
||||
description: 'Eleventy Excellent is inspired bythe companion website of Andy Bell’s talk "Be the browser’s mentor, not its micromanager".'
|
||||
title: Why Create?
|
||||
permalink: /why/index.html
|
||||
description: 'Why create?'
|
||||
layout: page
|
||||
---
|
||||
|
||||
This starter uses modern CSS, fluid type & space, flexible Layouts and Progressive Enhancement, wrapped in a basic template.
|
||||
|
||||
It is built around the CSS boilerplate by Andy Bell and inspired by Andy's talk **'Be the browser’s mentor, not its micromanager'**.
|
||||
|
||||
If you want to know exactly how it all works, [read this article on piccalil.li](https://piccalil.li/blog/a-css-project-boilerplate/).
|
||||
|
||||
The aim is to spread the idea and use of this _excellent_ workflow. To work with it efficiently you should be familiar with [cube.fyi](https://cube.fyi/), [utopia.fyi](https://utopia.fyi/) and [every-layout.dev](https://every-layout.dev/).
|
||||
|
||||
- buildexcellentwebsit.es
|
||||
- Study the [original CSS boilerplate](https://github.com/Set-Creative-Studio/cube-boilerplate/tree/main)
|
||||
|
||||
## Watch the talk
|
||||
|
||||
<div><custom-youtube @slug="JqnMI1AXl6w" @label="Andy Bell – Be the browser’s mentor, not its micromanager"> </custom-youtube></div>
|
||||
|
||||
Hypngaga is a vision quest by forty-something year old captive of a culture seemingly bent upon self-destruction.
|
||||
44
src/pages/search.njk
Normal file
44
src/pages/search.njk
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
title: Search
|
||||
permalink: /search/index.html
|
||||
description: 'Sift through this rubble. '
|
||||
layout: page
|
||||
---
|
||||
|
||||
<search>
|
||||
<form role="search" method="GET" id="search">
|
||||
<fieldset>
|
||||
<label for="q" class="hidden">Search</label>
|
||||
<input id="q" type="search" name="q" placeholder="Search" autofocus>
|
||||
<input type="hidden" name="sites" value="{{meta.domain}}">
|
||||
<input type="submit" value="Search" class="hidden">
|
||||
</fieldset>
|
||||
|
||||
<div class="filter-and-results filter-and-results--hidden" id="filter-and-results">
|
||||
<fieldset class="types">
|
||||
<label><input type="checkbox" name="Page">Page</label>
|
||||
<label><input type="checkbox" name="Post">Post</label>
|
||||
</fieldset>
|
||||
<fieldset class="tags">
|
||||
{% for tag in collections.tagList %}
|
||||
<label><input type="checkbox" name="tag" value="{{ tag }}">{{ tag }}</label>
|
||||
{% endfor %}
|
||||
</fieldset>
|
||||
<script id="all-tags" type="application/json">{{ collections.tagList | dump | safe }}</script>
|
||||
<div class="results-area">
|
||||
<header id="results-count"></header>
|
||||
<div class="results" id="results"></div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</search>
|
||||
|
||||
|
||||
{% css "local" %}
|
||||
{% include "css/forms.css" %}
|
||||
{% include "css/search.css" %}
|
||||
{% include "css/custom-card.css" %}
|
||||
{% endcss %}
|
||||
{% js "defer" %}
|
||||
{% include "scripts/search.js" %}
|
||||
{% endjs %}
|
||||
23
src/pages/stories.njk
Normal file
23
src/pages/stories.njk
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
layout: base
|
||||
permalink: /stories/
|
||||
title: 'Stories'
|
||||
description: 'asdf'
|
||||
eleventyNavigation:
|
||||
key: Stories
|
||||
---
|
||||
|
||||
<div class="wrapper">
|
||||
<header class="full | section" style="--spot-color: var(--color-secondary)">
|
||||
<div class="section__inner flow region" style="--region-space-top: var(--space-xl-2xl)">
|
||||
<h1 class="text-center" style="color: var(--color-light);">{{ title }}</h1>
|
||||
</div>
|
||||
|
||||
{% svg "divider/triangle", null, "divider" %}
|
||||
</header>
|
||||
|
||||
<div class="region flow prose" style="--region-space-top: var(--space-xl-2xl)">
|
||||
|
||||
{{ collections.all | eleventyNavigation('Stories') | eleventyNavigationToHtml | safe }}
|
||||
|
||||
</div>
|
||||
42
src/pages/thoughts.njk
Normal file
42
src/pages/thoughts.njk
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
---
|
||||
layout: base
|
||||
title: Thoughts
|
||||
description: 'Sift through the posts.'
|
||||
pagination:
|
||||
data: collections.allPosts
|
||||
size: 8
|
||||
permalink: 'thoughts/{% if pagination.pageNumber >=1 %}page-{{ pagination.pageNumber + 1 }}/{% endif %}index.html'
|
||||
---
|
||||
|
||||
<div class="wrapper">
|
||||
<header class="full | section" style="--spot-color: var(--color-secondary)">
|
||||
<div class="section__inner flow region" style="--region-space-top: var(--space-xl-2xl)">
|
||||
<h1 class="text-center" style="color: var(--color-light);">{{ title }}</h1>
|
||||
</div>
|
||||
|
||||
{% svg "divider/triangle", null, "divider" %}
|
||||
</header>
|
||||
|
||||
<div class="region flow prose" style="--region-space-top: var(--space-xl-2xl)">
|
||||
<p>This blog has a pagination of <strong>{{ pagination.size }}</strong> posts per page.<br>
|
||||
The pagination is only shown if there are more posts ({{ collections.posts.length }}) than items per
|
||||
page ({{ pagination.size }}).
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<custom-masonry layout="50-50">
|
||||
{% asyncEach item in pagination.items %}
|
||||
{% include "partials/card-blog.njk" %}
|
||||
{% endeach %}
|
||||
</custom-masonry>
|
||||
|
||||
<!-- set collection to paginate -->
|
||||
{% set collectionToPaginate = collections.posts %}
|
||||
<!-- set target pagination settings in meta.js -->
|
||||
{% set metaKey = "blog" %}
|
||||
<!-- if the number of items in the collection is greater than the number of items shown on one page -->
|
||||
{% if collectionToPaginate.length > pagination.size %}
|
||||
<!-- include pagination -->
|
||||
{% include 'partials/pagination.njk' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
11
src/posts/2025/2025-11-11-gasheyes-creek.md
Normal file
11
src/posts/2025/2025-11-11-gasheyes-creek.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
title: 'Gasheyes Creek'
|
||||
description: "A story about a creek."
|
||||
date: 2025-11-11
|
||||
tags: ['place']
|
||||
eleventyNavigation:
|
||||
key: Oakington Peninsula
|
||||
parent: Harford County
|
||||
---
|
||||
|
||||
Begin here.
|
||||
Loading…
Reference in a new issue