chore: initial setup

This commit is contained in:
Stefan Imhoff
2021-04-28 18:07:34 +02:00
commit f76fd39fee
35 changed files with 21422 additions and 0 deletions

11
.editorconfig Normal file
View File

@@ -0,0 +1,11 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
charset = utf-8
indent_size = 2
indent_style = space
end_of_line = lf
insert_final_newline = true

87
.eleventy.js Normal file
View File

@@ -0,0 +1,87 @@
const pluginLazyImages = require('eleventy-plugin-lazyimages');
const filterNbsp = require('eleventy-nbsp-filter');
const filters = require('./src/utils/filters.js');
const shortcodes = require('./src/utils/shortcodes.js');
const collections = require('./src/utils/collections.js');
module.exports = function (config) {
// Plugins
if (process.env.ELEVENTY_ENV == 'production') {
config.addPlugin(pluginLazyImages);
}
// Markdown It
let markdownIt = require('markdown-it');
let markdownItFootnotes = require('markdown-it-footnote');
let markdownItGitHubHeadings = require('markdown-it-github-headings');
let markdownItSub = require('markdown-it-sub');
let markdownItSup = require('markdown-it-sup');
let markdownItExternalAnchor = require('markdown-it-external-anchor');
let options = {
html: true,
};
let markdownLib = markdownIt(options)
.use(markdownItFootnotes)
.use(markdownItGitHubHeadings)
.use(markdownItExternalAnchor, {
domain: 'www.kogakure.de',
class: 'external',
})
.use(markdownItSub)
.use(markdownItSup);
config.setLibrary('md', markdownLib);
// Compress and combine JS files
config.addFilter('jsmin', require('./src/utils/minify-js.js'));
// Minify the HTML in production
if (process.env.ELEVENTY_ENV == 'production') {
config.addTransform('htmlmin', require('./src/utils/minify-html.js'));
}
// Shortcodes
// Filters
Object.keys(filters).forEach((filterName) => {
config.addFilter(filterName, filters[filterName]);
});
config.addFilter('nbsp', filterNbsp(2, 15));
// Watch for changes and reload
config.addWatchTarget('src/assets');
// Copy static files to dist
config.addPassthroughCopy({ 'src/static/**/*.{xml,html,ico}': '.' });
config.addPassthroughCopy({ 'src/static/.well-known/*': '.well-known' });
config.addPassthroughCopy({ 'src/downloads': 'downloads' });
config.addPassthroughCopy({ 'src/assets/fonts': 'assets/fonts' });
config.addPassthroughCopy({ 'src/assets/images': 'assets/images' });
// Deep-Merge
config.setDataDeepMerge(true);
// Custom Collections
Object.keys(collections).forEach((collectionName) => {
config.addCollection(collectionName, collections[collectionName]);
});
// Set input and output folders
return {
dir: {
input: 'src',
data: 'data',
includes: 'includes',
layouts: 'layouts',
output: 'dist',
},
templateFormats: ['njk', 'md', '11ty.js', 'jpg', 'png', 'svg'],
htmlTemplateEngine: 'njk',
dataTemplateEngine: 'njk',
markdownTemplateEngine: 'njk',
passthroughFileCopy: true,
};
};

15
.eslintrc.json Normal file
View File

@@ -0,0 +1,15 @@
{
"env": {
"es6": true,
"node": true,
"browser": true
},
"extends": ["eslint:recommended", "plugin:prettier/recommended"],
"plugins": ["prettier"],
"parserOptions": {
"sourceType": "module"
},
"rules": {
"prettier/prettier": "error"
}
}

10
.gitignore vendored Normal file
View File

@@ -0,0 +1,10 @@
# npm
node_modules
# 11ty
dist
src/includes/critical
.lazyimages.json
# Local Netlify folder
.netlify

1
.nvmrc Normal file
View File

@@ -0,0 +1 @@
12.16.3

15
.prettierrc.json Normal file
View File

@@ -0,0 +1,15 @@
{
"endOfLine": "lf",
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"overrides": [
{
"files": "*.md",
"options": {
"singleQuote": false
}
}
]
}

1
.stylelintignore Normal file
View File

@@ -0,0 +1 @@
src/assets/styles/base/_normalize.css

35
.stylelintrc.json Normal file
View File

@@ -0,0 +1,35 @@
{
"plugins": [
"stylelint-order",
"stylelint-config-rational-order/plugin",
"stylelint-a11y",
"stylelint-high-performance-animation",
"stylelint-prettier"
],
"extends": [
"stylelint-config-recommended",
"stylelint-a11y/recommended",
"stylelint-prettier/recommended"
],
"syntax": "scss",
"rules": {
"property-no-unknown": [
true,
{
"ignoreProperties": ["suffix", "symbols", "system"]
}
],
"no-descending-specificity": null,
"order/order": ["custom-properties", "declarations"],
"order/properties-alphabetical-order": true,
"plugin/no-low-performance-animation-properties": true,
"at-rule-no-unknown": [
true,
{
"ignoreAtRules": ["define-placeholder", "extend"]
}
],
"string-quotes": "single",
"prettier/prettier": true
}
}

33
README.md Normal file
View File

@@ -0,0 +1,33 @@
[![Netlify Status](https://api.netlify.com/api/v1/badges/ee50c303-884e-4f40-88a9-754fb6cabce4/deploy-status)](https://app.netlify.com/sites/kogakure-de/deploys)
[![Live](https://img.shields.io/badge/live-kogakure.de-green.svg)](https://www.kogakure.de/)
![Maintenance](https://img.shields.io/maintenance/yes/2021.svg)
# kogakure.de
This the source of my [martial arts website][kogakure] build with [Eleventy], [PostCSS], and [Gulp].
## Installation
```sh
npm install
```
## Running the Website
```sh
npm start
```
For more tasks have a look into the `package.json`.
## License
All content is copyrighted by [Stefan Imhoff][si] unless otherwise stated. Feel free to learn from the source code and reuse code for your projects. The only thing which is not allowed is the usage of my design (the unique combination of layout, fonts, images), private photos and logo.
In easier words: **This is not a free theme**. Learn from it. Remix. Reuse. Build your own stuff. 🤘
[eleventy]: https://www.11ty.dev/
[gulp]: https://gulpjs.com/
[kogakure]: https://www.kogakure.de
[postcss]: https://postcss.org/
[si]: https://www.stefanimhoff.de

3
babel.config.json Normal file
View File

@@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-env"]
}

125
gulpfile.js Normal file
View File

@@ -0,0 +1,125 @@
const { task, src, dest, watch, series, parallel } = require('gulp');
const babel = require('babelify');
const browserify = require('browserify');
const buffer = require('vinyl-buffer');
const source = require('vinyl-source-stream');
const imagemin = require('gulp-imagemin');
const plumber = require('gulp-plumber');
const postcss = require('gulp-postcss');
const size = require('gulp-size');
const sourcemaps = require('gulp-sourcemaps');
const svgSprite = require('gulp-svg-sprite');
/**
* Package JavaScript with Babel and Browserify
*/
task('js', () => {
return browserify(['./src/assets/scripts/main.js'], {
debug: true,
})
.transform(babel)
.bundle()
.on('error', function (err) {
console.error(err);
this.emit('end');
})
.pipe(source('main.js'))
.pipe(buffer())
.pipe(sourcemaps.init({ loadMaps: true }))
.pipe(sourcemaps.write('.'))
.pipe(dest('./dist/assets/scripts'));
});
/**
* Create CSS and Sourcemaps with PostCSS
*/
task('css', function () {
return src([
'./src/assets/styles/*.css',
'./src/assets/styles/critical/*.css',
])
.pipe(plumber())
.pipe(sourcemaps.init())
.pipe(postcss())
.pipe(sourcemaps.write('.'))
.pipe(dest('./dist/assets/styles'));
});
/**
* Create CSS with PostCSS for production
*/
task('css:production', function () {
return src(['./src/assets/styles/*.css'])
.pipe(plumber())
.pipe(postcss())
.pipe(dest('./dist/assets/styles'));
});
/**
* Copy critical CSS files to project folder
*/
task('css:critical', function () {
return src(['./src/assets/styles/critical/*.css'])
.pipe(plumber())
.pipe(postcss())
.pipe(dest('./src/includes/critical'));
});
/**
* Create SVG sprite map from SVG files
*/
task('svg', () => {
return src('./src/icons/*.svg')
.pipe(plumber())
.pipe(
svgSprite({
mode: {
symbol: {
dest: '.',
sprite: 'icons.svg',
},
svg: {
xmlDeclaration: false,
doctypeDeclaration: false,
},
},
})
)
.pipe(dest('src/includes'));
});
/**
* Optimize and minimize images
*/
task('optimize:images', () => {
return src('src/assets/images/**/*.{jpg,jpeg,png,gif,svg}')
.pipe(
imagemin({
optimizationLevel: 3,
progressive: true,
interlaced: true,
})
)
.pipe(dest('src/assets/images/'))
.pipe(size());
});
/**
* Run build for production
*/
task('build', parallel('js', 'css:production', 'css:critical'));
/**
* Watch for changes in files
*/
task('watch', () => {
watch('./src/assets/scripts/**/*.js', series('js'));
watch('./src/assets/styles/**/*.css', series('css'));
});
/**
* Compile assets and start watching for changes
*/
task('default', parallel('js', 'css', 'watch'));

21
netlify.toml Normal file
View File

@@ -0,0 +1,21 @@
[build]
command = "npm run build"
publish = "dist"
[build.environment]
NODE_VERSION = "12.16"
[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"
X-XSS-Protection = "1; mode=block"
X-Content-Type-Options = "nosniff"
Referrer-Policy= "no-referrer-when-downgrade"
Permissions-Policy = "interest-cohort=()"
[[headers]]
for = "/sw.js"
[headers.values]
cache-control = "max-age=0,no-cache,no-store,must-revalidate"

20726
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

84
package.json Normal file
View File

@@ -0,0 +1,84 @@
{
"name": "website-11ty-kogakure.de",
"version": "1.0.0",
"description": "Source code of my martial arts website",
"scripts": {
"build": "ELEVENTY_ENV=production npm-run-all build:assets build:html build:sw",
"build:assets": "gulp build",
"build:html": "eleventy",
"build:sw": "workbox generateSW workbox.config.js",
"clean": "npx del dist",
"debug": "DEBUG=* npx eleventy",
"plop": "plop",
"prebuild": "npm run clean",
"prestart": "npm run clean",
"start": "npm run watch",
"svg": "gulp svg",
"watch": "npm-run-all --parallel watch:*",
"watch:assets": "gulp",
"watch:html": "eleventy --serve"
},
"repository": {
"type": "git",
"url": "git+https://github.com/kogakure/website-11ty-kogakure.de.git"
},
"keywords": [
"11ty"
],
"author": "Stefan Imhoff",
"license": "ISC",
"homepage": "https://www.kogakure.de",
"devDependencies": {
"@11ty/eleventy": "^0.12.1",
"autoprefixer": "^10.2.5",
"babelify": "^10.0.0",
"browserify": "^17.0.0",
"cssnano": "^5.0.2",
"del-cli": "^3.0.1",
"eleventy-nbsp-filter": "^0.1.0",
"eleventy-plugin-lazyimages": "^2.1.0",
"eslint": "^7.25.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.0",
"gulp": "^4.0.2",
"gulp-imagemin": "^7.1.0",
"gulp-plumber": "^1.2.1",
"gulp-postcss": "^9.0.0",
"gulp-size": "^3.0.0",
"gulp-sourcemaps": "^3.0.0",
"gulp-svg-sprite": "^1.5.0",
"html-minifier": "^4.0.0",
"markdown-it-external-anchor": "^1.0.0",
"markdown-it-footnote": "^3.0.2",
"markdown-it-github-headings": "^2.0.0",
"markdown-it-sub": "^1.0.0",
"markdown-it-sup": "^1.0.0",
"moment": "^2.29.1",
"npm-run-all": "^4.1.5",
"outdent": "^0.8.0",
"postcss": "^8.2.13",
"postcss-100vh-fix": "^1.0.2",
"postcss-advanced-variables": "^3.0.1",
"postcss-cli": "^8.3.1",
"postcss-custom-media": "^8.0.0",
"postcss-custom-properties": "^11.0.0",
"postcss-extend": "^1.0.5",
"postcss-import": "^14.0.1",
"postcss-media-minmax": "^5.0.0",
"postcss-nested": "^5.0.5",
"postcss-sort-media-queries": "^3.8.9",
"prettier": "^2.2.1",
"stylelint": "^13.13.0",
"stylelint-a11y": "^1.2.3",
"stylelint-config-prettier": "^8.0.2",
"stylelint-config-rational-order": "^0.1.2",
"stylelint-config-recommended": "^5.0.0",
"stylelint-high-performance-animation": "^1.5.2",
"stylelint-order": "^4.1.0",
"stylelint-prettier": "^1.2.0",
"uglify-js": "^3.13.4",
"vinyl-buffer": "^1.0.1",
"vinyl-source-stream": "^2.0.0",
"workbox-cli": "^6.1.5"
}
}

15
postcss.config.js Normal file
View File

@@ -0,0 +1,15 @@
module.exports = {
plugins: [
require('postcss-advanced-variables'),
require('postcss-import'),
require('postcss-custom-properties'),
require('postcss-nested'),
require('postcss-extend'),
require('postcss-media-minmax'),
require('postcss-custom-media'),
require('postcss-100vh-fix'),
require('postcss-sort-media-queries')(),
require('autoprefixer'),
...(process.env.ELEVENTY_ENV === 'production' ? [require('cssnano')] : []),
],
};

View File

@@ -0,0 +1,5 @@
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js').catch((registrationError) => {
console.error('SW registration failed: ', registrationError);
});
});

View File

@@ -0,0 +1,42 @@
(function () {
const root = document.getElementsByTagName('html')[0];
function setTheme(newTheme) {
window.__theme = newTheme;
preferredTheme = newTheme;
const currentTheme = newTheme === 'light' ? 'dark' : 'light';
root.classList.add(newTheme);
root.classList.remove(currentTheme);
}
let preferredTheme;
try {
preferredTheme = localStorage.getItem('theme');
} catch (err) {
console.error(err);
}
window.__setPreferredTheme = function (newTheme) {
setTheme(newTheme);
try {
localStorage.setItem('theme', newTheme);
} catch (err) {
console.error(err);
}
};
window.__toggleTheme = function () {
const currentTheme = window.__theme;
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
window.__setPreferredTheme(newTheme);
};
const darkQuery = window.matchMedia('(prefers-color-scheme: dark)');
darkQuery.addEventListener('change', function (e) {
window.__setPreferredTheme(e.matches ? 'dark' : 'light');
});
setTheme(preferredTheme || (darkQuery.matches ? 'dark' : 'light'));
})();

View File

@@ -0,0 +1 @@
console.log('Hello, world');

View File

View File

9
src/data/site.js Normal file
View File

@@ -0,0 +1,9 @@
module.exports = {
isProduction: process.env.ELEVENTY_ENV === 'production',
buildTime: new Date(),
title: '',
description: '',
url: 'https://www.kogakure.de',
author: 'Stefan Imhoff',
twitter: '@kogakure',
};

View File

@@ -0,0 +1,4 @@
<link rel="icon" href="/favicon.ico" />
<link rel="icon" href="{{ site.faviconPath }}favicon.svg" type="image/svg+xml" />
<link rel="apple-touch-icon" href="{{ site.faviconPath }}apple-touch-icon.png" />
<link rel="manifest" href="/manifest.webmanifest" />

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="0" height="0">
{# {% include "icons.svg" %} #}
</svg>

After

Width:  |  Height:  |  Size: 123 B

View File

@@ -0,0 +1,45 @@
{%- set ogTitle -%}
{%- if page.url === "/" -%}
{{ site.author }} · {{ site.description }}
{%- else -%}
{{ title }} · {{ site.author }}
{%- endif -%}
{%- endset -%}
{%- set ogDescription -%}
{%- if page.url === "/" -%}
{{ site.tagline }}
{%- else -%}
{{ description or title }}
{%- endif -%}
{%- endset -%}
{%- set ogImage -%}
{%- if og -%}
{{ site.url }}/assets/images/branding/og/{{ og }}
{%- else -%}
{{ site.url }}/assets/images/branding/og/banner.png
{%- endif -%}
{%- endset -%}
<!-- Meta -->
<meta name="author" content="{{ site.author }}" />
<meta name="description" content="{{ ogDescription }}" />
<meta name="theme-color" content="#e7e6e4" />
<!-- Open graph -->
<meta property="og:title" content="{{ ogTitle }}" />
<meta property="og:url" content="{{ site.url }}{{ page.url }}" />
<meta property="og:description" content="{{ ogDescription }}" />
<meta property="og:type" content="article" />
<meta property="og:image" content="{{ ogImage }}"/>
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="675" />
<!-- Twitter -->
<meta name="twitter:title" content="{{ ogTitle }}" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="{{ site.twitter }}" />
<meta name="twitter:description" content="{{ ogDescription }}" />
<meta name="twitter:image" content="{{ ogImage }}" />
<meta name="twitter:creator" content="{{ site.twitter }}" />

2
src/includes/preload.njk Normal file
View File

@@ -0,0 +1,2 @@
<link rel="preload" href="{{ '/assets/fonts/xyz1.woff2' | url }}" as="font" type="font/woff2" crossorigin />
<link rel="preload" href="{{ '/assets/fonts/xyz2.woff2' | url }}" as="font" type="font/woff2" crossorigin />

5
src/includes/robots.njk Normal file
View File

@@ -0,0 +1,5 @@
{% if robots %}
<meta name="robots" content="{{ robots }}" />
{% else %}
<meta name="robots" content="all" />
{% endif %}

7
src/includes/scripts.njk Normal file
View File

@@ -0,0 +1,7 @@
{% set js %}
{% include "../assets/scripts/embedded/theme-switcher.js" %}
{% if site.isProduction %}
{% include "../assets/scripts/embedded/register-serviceworker.js" %}
{% endif %}
{% endset %}
<script>{{ js | jsmin | safe }}</script>

28
src/includes/styles.njk Normal file
View File

@@ -0,0 +1,28 @@
{% if site.isProduction %}
{% set criticalCSS %}
{% include "critical/base.css" %}
{% if css %}
{% include "critical/" + css %}
{% endif %}
{% endset %}
<style>
{{ criticalCSS | safe }}
</style>
{% else %}
<link rel="stylesheet" href="{{ '/assets/styles/base.css' | url }}" />
{% if css %}
<link rel="stylesheet" href="{{ '/assets/styles/' + css | url }}" />
{% endif %}
{% endif %}
<link rel="stylesheet" href="{{ '/assets/styles/main.css' | url }}" media="print" onload="this.media='all'" />
<link rel="stylesheet" href="{{ '/assets/styles/print.css' | url }}" media="print" />
<noscript>
<link rel="stylesheet" href="{{ '/assets/styles/main.css' | url }}" />
</noscript>

34
src/layouts/base.njk Normal file
View File

@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="de" class="no-js">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>
{%- if page.url === "/" -%}
{{ site.author }} · {{ site.description }}
{%- else -%}
{{ title }} · {{ site.author }}
{%- endif -%}
</title>
<link rel="canonical" href="{{ site.url }}{{ page.url }}" />
<script type="module">
document.documentElement.classList.remove('no-js');
document.documentElement.classList.add('js');
</script>
{% include "scripts.njk" %}
{% include "styles.njk" %}
{% include "favicons.njk" %}
{% include "meta-tags.njk" %}
{% include "preload.njk" %}
{% include "robots.njk" %}
</head>
<body {% if bodyClass %}class="{{ bodyClass }}"{% endif %}>
{% include "icon-sprites.njk" %}
{{ content | safe }}
<script src="/assets/scripts/main.js" async></script>
</body>
</html>

7
src/pages/homepage.njk Normal file
View File

@@ -0,0 +1,7 @@
---
layout: base
title: Home
permalink: /
---
Hello, world!

1
src/utils/collections.js Normal file
View File

@@ -0,0 +1 @@
module.exports = {};

10
src/utils/filters.js Normal file
View File

@@ -0,0 +1,10 @@
const moment = require('moment');
module.exports = {
dateToFormat: function (date, format = 'MMMM Do, YYYY') {
return moment(date).format(format);
},
dateToISO: function (date) {
return moment(date).format();
},
};

15
src/utils/minify-html.js Normal file
View File

@@ -0,0 +1,15 @@
const htmlmin = require('html-minifier');
module.exports = function (content, outputPath) {
if (outputPath.endsWith('.html')) {
let minified = htmlmin.minify(content, {
useShortDoctype: true,
removeComments: true,
collapseWhitespace: true,
});
return minified;
}
return content;
};

12
src/utils/minify-js.js Normal file
View File

@@ -0,0 +1,12 @@
const UglifyJS = require('uglify-js');
module.exports = function (code) {
let minified = UglifyJS.minify(code);
if (minified.error) {
console.log('UglifyJS error: ', minified.error);
return code;
}
return minified.code;
};

10
src/utils/shortcodes.js Normal file
View File

@@ -0,0 +1,10 @@
const outdent = require('outdent')({ newline: ' ' });
module.exports = {
test: function () {
const html = outdent`
<div>Hello, World</div>`;
return html;
},
};