project first commit
90
.eleventy.js
Normal file
@ -0,0 +1,90 @@
|
||||
const rssPlugin = require('@11ty/eleventy-plugin-rss');
|
||||
const syntaxHighlight = require('@11ty/eleventy-plugin-syntaxhighlight');
|
||||
const fs = require('fs');
|
||||
|
||||
// Import filters
|
||||
const dateFilter = require('./src/filters/date-filter.js');
|
||||
const markdownFilter = require('./src/filters/markdown-filter.js');
|
||||
const w3DateFilter = require('./src/filters/w3-date-filter.js');
|
||||
|
||||
// Import transforms
|
||||
const htmlMinTransform = require('./src/transforms/html-min-transform.js');
|
||||
const parseTransform = require('./src/transforms/parse-transform.js');
|
||||
|
||||
// Import data files
|
||||
const site = require('./src/_data/site.json');
|
||||
|
||||
module.exports = function(config) {
|
||||
// Filters
|
||||
config.addFilter('dateFilter', dateFilter);
|
||||
config.addFilter('markdownFilter', markdownFilter);
|
||||
config.addFilter('w3DateFilter', w3DateFilter);
|
||||
|
||||
// Layout aliases
|
||||
config.addLayoutAlias('home', 'layouts/home.njk');
|
||||
|
||||
// Transforms
|
||||
config.addTransform('htmlmin', htmlMinTransform);
|
||||
config.addTransform('parse', parseTransform);
|
||||
|
||||
// Passthrough copy
|
||||
config.addPassthroughCopy('src/fonts');
|
||||
config.addPassthroughCopy('src/images');
|
||||
config.addPassthroughCopy('src/js');
|
||||
config.addPassthroughCopy('src/admin/config.yml');
|
||||
config.addPassthroughCopy('src/admin/previews.js');
|
||||
config.addPassthroughCopy('node_modules/nunjucks/browser/nunjucks-slim.js');
|
||||
config.addPassthroughCopy('src/robots.txt');
|
||||
|
||||
const now = new Date();
|
||||
|
||||
// Custom collections
|
||||
const livePosts = post => post.date <= now && !post.data.draft;
|
||||
config.addCollection('posts', collection => {
|
||||
return [
|
||||
...collection.getFilteredByGlob('./src/posts/*.md').filter(livePosts)
|
||||
].reverse();
|
||||
});
|
||||
|
||||
config.addCollection('postFeed', collection => {
|
||||
return [...collection.getFilteredByGlob('./src/posts/*.md').filter(livePosts)]
|
||||
.reverse()
|
||||
.slice(0, site.maxPostsPerPage);
|
||||
});
|
||||
config.addCollection('newsFeed', collection => {
|
||||
return [...collection.getFilteredByGlob('./src/posts/*.md').filter(livePosts)]
|
||||
.reverse()
|
||||
.slice(0, site.maxNewsPerPage);
|
||||
});
|
||||
|
||||
// Plugins
|
||||
config.addPlugin(rssPlugin);
|
||||
config.addPlugin(syntaxHighlight);
|
||||
|
||||
// 404
|
||||
config.setBrowserSyncConfig({
|
||||
callbacks: {
|
||||
ready: function(err, browserSync) {
|
||||
const content_404 = fs.readFileSync('dist/404.html');
|
||||
|
||||
browserSync.addMiddleware('*', (req, res) => {
|
||||
// Provides the 404 content without redirect.
|
||||
res.write(content_404);
|
||||
res.end();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Watch sass
|
||||
// config.addWatchTarget("./src/scss/");
|
||||
|
||||
return {
|
||||
dir: {
|
||||
input: 'src',
|
||||
output: 'dist'
|
||||
},
|
||||
passthroughFileCopy: true,
|
||||
pathPrefix: '/preprod' //TODO remove when prod
|
||||
};
|
||||
};
|
19
.gitignore
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
*.log
|
||||
npm-debug.*
|
||||
*.scssc
|
||||
*.log
|
||||
*.swp
|
||||
.DS_Store
|
||||
.sass-cache
|
||||
node_modules
|
||||
dist/*
|
||||
deploy-prod.js
|
||||
deploy-preprod.js
|
||||
|
||||
# Specifics
|
||||
|
||||
# Hide design tokens
|
||||
src/scss/_tokens.scss
|
||||
|
||||
# Hide compiled CSS
|
||||
src/_includes/assets/*
|
6
.prettierrc
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"printWidth": 90,
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"bracketSpacing": false
|
||||
}
|
18
.stylelintrc.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": "stylelint-config-sass-guidelines",
|
||||
"rules": {
|
||||
"order/properties-alphabetical-order": null,
|
||||
"property-no-vendor-prefix": null,
|
||||
"selector-class-pattern": [
|
||||
"^[a-z0-9\\-_]+$",
|
||||
{
|
||||
"message":
|
||||
"Selector should be written in lowercase (selector-class-pattern)"
|
||||
}
|
||||
],
|
||||
"max-nesting-depth": 4,
|
||||
"number-leading-zero": "never",
|
||||
"selector-no-qualifying-type": null,
|
||||
"selector-max-compound-selectors": 4
|
||||
}
|
||||
}
|
21
LICENSE.txt
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 andy-bell.design and other contributors
|
||||
|
||||
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.
|
30
deploy.js
Normal file
@ -0,0 +1,30 @@
|
||||
var FtpDeploy = require("ftp-deploy");
|
||||
var ftpDeploy = new FtpDeploy();
|
||||
|
||||
var config = {
|
||||
user: "user",
|
||||
// Password optional, prompted if none given
|
||||
password: "password",
|
||||
host: "ftp.someserver.com",
|
||||
port: 21,
|
||||
localRoot: __dirname + "/local-folder",
|
||||
remoteRoot: "/public_html/remote-folder/",
|
||||
// include: ["*", "**/*"], // this would upload everything except dot files
|
||||
include: ["*.php", "dist/*", ".*"],
|
||||
// e.g. exclude sourcemaps, and ALL files in node_modules (including dot files)
|
||||
exclude: ["dist/**/*.map", "node_modules/**", "node_modules/**/.*", ".git/**"],
|
||||
// delete ALL existing files at destination before uploading, if true
|
||||
deleteRemote: false,
|
||||
// Passive mode is forced (EPSV command is not sent)
|
||||
forcePasv: true
|
||||
};
|
||||
|
||||
// use with promises
|
||||
ftpDeploy
|
||||
.deploy(config)
|
||||
.then(res => console.log("finished:", res))
|
||||
.catch(err => console.log(err));
|
||||
|
||||
ftpDeploy.on("uploading", function(data) {
|
||||
console.log(data.filename); // partial path with filename being uploaded
|
||||
});
|
9665
package-lock.json
generated
Normal file
56
package.json
Executable file
@ -0,0 +1,56 @@
|
||||
{
|
||||
"name": "astrolabe-website",
|
||||
"version": "0.1.0",
|
||||
"description": "Site web de la coopérative Astrolabe CAE",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@11ty/eleventy": "^0.10.0",
|
||||
"@11ty/eleventy-plugin-rss": "^1.0.7",
|
||||
"@11ty/eleventy-plugin-syntaxhighlight": "^2.0.3",
|
||||
"@tbranyen/jsdom": "^13.0.0",
|
||||
"concurrently": "^4.1.2",
|
||||
"html-minifier": "^4.0.0",
|
||||
"image-size": "^0.8.3",
|
||||
"json-to-scss": "^1.5.0",
|
||||
"sass": "^1.26.3",
|
||||
"semver": "^6.3.0",
|
||||
"slugify": "^1.4.0",
|
||||
"stalfos": "github:hankchizljaw/stalfos#c8971d22726326cfc04089b2da4d51eeb1ebb0eb"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@erquhart/rollup-plugin-node-builtins": "^2.1.5",
|
||||
"bl": "^3.0.0",
|
||||
"chokidar-cli": "^2.1.0",
|
||||
"cross-env": "^5.2.1",
|
||||
"ftp-deploy": "^2.3.7",
|
||||
"make-dir-cli": "^2.0.0",
|
||||
"prettier": "^1.19.1",
|
||||
"rollup": "^1.32.1",
|
||||
"rollup-plugin-commonjs": "^10.1.0",
|
||||
"rollup-plugin-json": "^4.0.0",
|
||||
"rollup-plugin-node-resolve": "^5.2.0",
|
||||
"stylelint": "^13.6.1",
|
||||
"stylelint-config-sass-guidelines": "^7.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"sass:tokens": "npx json-to-scss src/_data/tokens.json src/scss/_tokens.scss",
|
||||
"sass:lint": "npx stylelint src/scss/**/*.scss",
|
||||
"sass:process": "npm run sass:tokens && npm run sass:lint && sass src/scss/global.scss src/_includes/assets/css/global.css --style=compressed",
|
||||
"start": "concurrently \"npm run sass:process -- --watch\" \"npm run serve\"",
|
||||
"serve": "cross-env ELEVENTY_ENV=development npx eleventy --serve",
|
||||
"preprod": "cross-env ELEVENTY_ENV=preprod npm run sass:process && npx eleventy",
|
||||
"deploy-preprod": "npm run preprod && node deploy-preprod",
|
||||
"prod": "cross-env ELEVENTY_ENV=prod npm run sass:process && npx eleventy"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://git.ouvaton.coop/astrolabe/SiteWebAstrolabe.git"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://git.ouvaton.coop/astrolabe/SiteWebAstrolabe/issues"
|
||||
},
|
||||
"homepage": "https://git.ouvaton.coop/astrolabe/SiteWebAstrolabe"
|
||||
}
|
17
src/404.md
Normal file
@ -0,0 +1,17 @@
|
||||
---
|
||||
title: '404 - not found'
|
||||
layout: layouts/page.njk
|
||||
permalink: 404.html
|
||||
eleventyExcludeFromCollections: true
|
||||
---
|
||||
|
||||
We’re sorry, but that content can’t be found. Please go [back to home](/).
|
||||
|
||||
{% comment %}
|
||||
Read more: https://www.11ty.io/docs/quicktips/not-found/
|
||||
|
||||
This will work for both GitHub pages and Netlify:
|
||||
|
||||
- https://help.github.com/articles/creating-a-custom-404-page-for-your-github-pages-site/
|
||||
- https://www.netlify.com/docs/redirects/#custom-404
|
||||
{% endcomment %}
|
9
src/_data/global.js
Normal file
@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
random() {
|
||||
const segment = () => {
|
||||
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
|
||||
};
|
||||
return `${segment()}-${segment()}-${segment()}`;
|
||||
},
|
||||
now: Date.now()
|
||||
};
|
10
src/_data/helpers.js
Normal file
@ -0,0 +1,10 @@
|
||||
module.exports = {
|
||||
getNextHeadingLevel(currentLevel) {
|
||||
return parseInt(currentLevel, 10) + 1;
|
||||
},
|
||||
getReadingTime(text) {
|
||||
const wordsPerMinute = 200;
|
||||
const numberOfWords = text.split(/\s/g).length;
|
||||
return Math.ceil(numberOfWords / wordsPerMinute);
|
||||
}
|
||||
};
|
29
src/_data/navigation.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"text": "Comprendre la CAE",
|
||||
"url": "/comprendre",
|
||||
"external": false
|
||||
},
|
||||
{
|
||||
"text": "Nous rejoindre",
|
||||
"url": "/rejoindre",
|
||||
"external": false
|
||||
},
|
||||
{
|
||||
"text": "L'équipe",
|
||||
"url": "/equipe",
|
||||
"external": false
|
||||
},
|
||||
{
|
||||
"text": "Actualité",
|
||||
"url": "/actualite",
|
||||
"external": false
|
||||
},
|
||||
{
|
||||
"text": "Nous contacter",
|
||||
"url": "/contact",
|
||||
"external": false
|
||||
}
|
||||
]
|
||||
}
|
23
src/_data/site.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"showThemeCredit": true,
|
||||
"name": "Astrolabe CAE",
|
||||
"shortDesc": "Site web de la coopérative Astrolabe CAE",
|
||||
"url": "https://astrolabe.coop/preprod",
|
||||
"authorEmail": "contacte@astrolabe.coop",
|
||||
"authorHandle": "@AstrolabeCae",
|
||||
"authorName": "Astrolabe CAE",
|
||||
"authorAddress": "34 La Ville Allée",
|
||||
"authorCity": "35630 HEDE BAZOUGES",
|
||||
"authorSocial": {
|
||||
"mastodon": "https://framapiaf.org/@AstrolabeCAE",
|
||||
"twitter": "https://twitter.com/AstrolabeCae",
|
||||
"linkedin": "https://www.linkedin.com/company/astrolabe-cae/"
|
||||
},
|
||||
"designerName": "Yves Gatesoupe",
|
||||
"designerHandle": "https://twitter.com/YGdsgn",
|
||||
"illustrators": "Igé Maulana, Leopold Merleau",
|
||||
"enableThirdPartyComments": false,
|
||||
"maxPostsPerPage": 5,
|
||||
"maxNewsPerPage": 4,
|
||||
"faviconPath": "/images/favicon.png"
|
||||
}
|
28
src/_data/styleguide.js
Normal file
@ -0,0 +1,28 @@
|
||||
const tokens = require('./tokens.json');
|
||||
|
||||
module.exports = {
|
||||
colors() {
|
||||
let response = [];
|
||||
|
||||
Object.keys(tokens.colors).forEach(key => {
|
||||
response.push({
|
||||
value: tokens.colors[key],
|
||||
key
|
||||
});
|
||||
});
|
||||
|
||||
return response;
|
||||
},
|
||||
sizes() {
|
||||
let response = [];
|
||||
|
||||
Object.keys(tokens['size-scale']).forEach(key => {
|
||||
response.push({
|
||||
value: tokens['size-scale'][key],
|
||||
key
|
||||
});
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
};
|
28
src/_data/tokens.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"size-scale": {
|
||||
"base": "1rem",
|
||||
"300": "0.8rem",
|
||||
"500": "1.25rem",
|
||||
"600": "1.56rem",
|
||||
"700": "1.95rem",
|
||||
"800": "2.44rem",
|
||||
"900": "3.05rem",
|
||||
"max": "4rem"
|
||||
},
|
||||
"colors": {
|
||||
"primary": "#d6f253",
|
||||
"primary-shade": "#102538",
|
||||
"primary-glare": "#22547c",
|
||||
"secondary": "#282156",
|
||||
"highlight": "#d6f253",
|
||||
"light-gray": "#f1f0f7",
|
||||
"light": "#fff",
|
||||
"mid": "#ccc",
|
||||
"dark": "#111",
|
||||
"slate": "#404040"
|
||||
},
|
||||
"fonts": {
|
||||
"base": "\"'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'\"",
|
||||
"brand": "\"'Varela', sans-serif\""
|
||||
}
|
||||
}
|
13
src/_includes/icons/arrow.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
focusable="false"
|
||||
aria-hidden="true"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
d="M9.707 18.707l6-6a.999.999 0 0 0 0-1.414l-6-6a.999.999 0 1 0-1.414 1.414L13.586 12l-5.293 5.293a.999.999 0 1 0 1.414 1.414z"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 310 B |
3
src/_includes/icons/astrolabe_logo.svg
Normal file
After Width: | Height: | Size: 7.4 KiB |
16
src/_includes/layouts/archive.njk
Normal file
@ -0,0 +1,16 @@
|
||||
{% extends 'layouts/base.njk' %}
|
||||
|
||||
{# Intro content #}
|
||||
{% set introHeading = title %}
|
||||
{% set introSummary %}{{ content | safe }}{% endset %}
|
||||
|
||||
{# Post list content #}
|
||||
{% set postListHeading = 'All posts' %}
|
||||
{% set postListItems = collections.posts %}
|
||||
|
||||
{% block content %}
|
||||
<main id="main-content" tabindex="-1">
|
||||
{% include "partials/components/intro.njk" %}
|
||||
{% include "partials/components/post-list.njk" %}
|
||||
</main>
|
||||
{% endblock %}
|
32
src/_includes/layouts/base.njk
Normal file
@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="no-js">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<link rel="alternate" type="application/rss+xml" title="{{ site.title }}" href="{{ site.url }}/feed.xml" />
|
||||
<link rel="icon" href="{{ site.faviconPath }}" type="image/png" />
|
||||
{% include "partials/global/meta-info.njk" %}
|
||||
<script>document.documentElement.classList.remove('no-js');</script>
|
||||
<style>{% include "assets/css/global.css" %}</style>
|
||||
{% block head %}
|
||||
{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
{% include "partials/global/site-head.njk" %}
|
||||
{% block content %}
|
||||
{% endblock content %}
|
||||
{% include "partials/global/site-foot.njk" %}
|
||||
{% block foot %}
|
||||
{% endblock %}
|
||||
{# <script type="module" src="/js/components/theme-toggle.js" async defer></script> #}
|
||||
<script>
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', () => {
|
||||
navigator.serviceWorker.register('/service-worker.js');
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<script src="https://identity.netlify.com/v1/netlify-identity-widget.js" defer></script>
|
||||
</body>
|
||||
</html>
|
18
src/_includes/layouts/contact.njk
Normal file
@ -0,0 +1,18 @@
|
||||
{% extends 'layouts/base.njk' %}
|
||||
|
||||
{# Intro content #}
|
||||
{% set introHeading = title %}
|
||||
|
||||
{% block content %}
|
||||
<main id="main-content" tabindex="-1">
|
||||
<article class="[ post ] [ h-entry ]">
|
||||
{% include "partials/components/intro.njk" %}
|
||||
<div class="[ post__body ] [ inner-wrapper ] [ leading-loose pad-top-900 pad-bottom-900 text-500 ] [ sf-flow ] [ e-content ]">
|
||||
{{ content | safe }}
|
||||
{% include "partials/components/contact-form.njk" %}
|
||||
</div>
|
||||
</article>
|
||||
</main>
|
||||
{% endblock %}
|
||||
|
||||
{{ content | safe }}
|
19
src/_includes/layouts/home.njk
Normal file
@ -0,0 +1,19 @@
|
||||
{% extends 'layouts/base.njk' %}
|
||||
|
||||
{# Post list content #}
|
||||
{% set newsListHeading = newsHeading %}
|
||||
{% set newsListItems = collections.newsFeed %}
|
||||
|
||||
{# Presentation content #}
|
||||
{% set presentation %}
|
||||
{{ content | safe }}
|
||||
{% endset %}
|
||||
|
||||
{% block content %}
|
||||
<main id="main-content" tabindex="-1">
|
||||
{% include "partials/components/intro.njk" %}
|
||||
{% include "partials/components/news-list.njk" %}
|
||||
{% include "partials/components/post-list.njk" %}
|
||||
{% include "partials/components/presentation.njk" %}
|
||||
</main>
|
||||
{% endblock %}
|
17
src/_includes/layouts/page.njk
Normal file
@ -0,0 +1,17 @@
|
||||
{% extends 'layouts/base.njk' %}
|
||||
|
||||
{# Intro content #}
|
||||
{% set introHeading = title %}
|
||||
|
||||
{% block content %}
|
||||
<main id="main-content" tabindex="-1">
|
||||
<article class="[ post ] [ h-entry ]">
|
||||
{% include "partials/components/intro.njk" %}
|
||||
<div class="[ post__body ] [ inner-wrapper ] [ leading-loose pad-top-900 pad-bottom-900 text-500 ] [ sf-flow ] [ e-content ]">
|
||||
{{ content | safe }}
|
||||
</div>
|
||||
</article>
|
||||
</main>
|
||||
{% endblock %}
|
||||
|
||||
{{ content | safe }}
|
47
src/_includes/layouts/post.njk
Normal file
@ -0,0 +1,47 @@
|
||||
{% extends 'layouts/base.njk' %}
|
||||
|
||||
{# Intro content #}
|
||||
{% set introHeading = title %}
|
||||
{% set introSummary %}
|
||||
<p class="[ intro__meta ] [ text-500 leading-tight ]">
|
||||
{% if date %}
|
||||
<time datetime="{{ date | w3DateFilter }}" class="dt-published">{{ date | dateFilter }}</time>
|
||||
{% endif %}
|
||||
<span>— {{ helpers.getReadingTime(content) }} minute read</span>
|
||||
</p>
|
||||
{% endset %}
|
||||
|
||||
{% block content %}
|
||||
<main id="main-content" tabindex="-1">
|
||||
<article class="[ post ] [ h-entry ]">
|
||||
{% include "partials/components/intro.njk" %}
|
||||
<div class="[ post__body ] [ inner-wrapper ] [ leading-loose pad-top-900 {{ 'pad-bottom-900' if not site.enableThirdPartyComments }} text-500 ] [ sf-flow ] [ e-content ]">
|
||||
{{ content | safe }}
|
||||
</div>
|
||||
{% if site.enableThirdPartyComments %}
|
||||
<hr />
|
||||
<aside class="[ post__body ] [ inner-wrapper ] [ pad-bottom-900 text-500 ]">
|
||||
{% include "partials/global/third-party-comments.njk" %}
|
||||
</aside>
|
||||
{% endif %}
|
||||
{% if tags %}
|
||||
<footer class="[ post__footer ] [ pad-top-500 pad-bottom-500 ]">
|
||||
<div class="inner-wrapper">
|
||||
<div class="[ nav ] [ box-flex align-center ]">
|
||||
<h2 class="font-base text-600 weight-mid">Filed under</h2>
|
||||
<ul class="[ nav__list ] [ box-flex align-center pad-left-400 ] [ p-category ]">
|
||||
{% for item in tags %}
|
||||
<li class="nav__item">
|
||||
<a href="/tags/{{ item }}">{{ item }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
{% endif %}
|
||||
</article>
|
||||
</main>
|
||||
{% endblock %}
|
||||
|
||||
{{ content | safe }}
|
181
src/_includes/macros/form.njk
Normal file
@ -0,0 +1,181 @@
|
||||
{# ===================
|
||||
Forms
|
||||
=================== #}
|
||||
|
||||
{% macro label( text, name ) %}
|
||||
<label class="question__label" for="field-{{ name }}">{{ text }}</label>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro field( type, name, data ) %}
|
||||
<br>
|
||||
<input class="question__field"
|
||||
type="{{ type }}"
|
||||
name="{{ name }}"
|
||||
id="field-{{ name }}"
|
||||
{% if data.required %}required aria-required="true"{% endif %}
|
||||
{% if data.placeholder %}placeholder="{{ data.placeholder }}"{% endif %}
|
||||
{% if data.pattern %}pattern="{{ data.pattern }}"{% endif %}
|
||||
{% if data.description %}aria-describedby="description-{{ name }}"{% endif %}
|
||||
{% if data.autocomplete %}autocomplete="{{ data.autocomplete }}"{% endif %}
|
||||
{% if data.autocorrect %}autocorrect="{{ data.autocorrect }}"{% endif %}
|
||||
{% if data.spellcheck %}spellcheck="{{ data.spellcheck }}"{% endif %}
|
||||
{% if data.autocapitalize %}autocapitalize="{{ data.autocapitalize }}"{% endif %}
|
||||
>
|
||||
{% if data.description %}
|
||||
<br>
|
||||
{{ description( name, data.description ) }}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro confirm( text, name, data ) %}
|
||||
<label for="field-{{ name }}" class="question--confirm">
|
||||
<input class="question__field question__field--confirm"
|
||||
type="checkbox"
|
||||
name="{{ name }}"
|
||||
id="field-{{ name }}"
|
||||
value="1"
|
||||
{% if data.required %}required aria-required="true"{% endif %}
|
||||
{% if data.description %}aria-describedby="description-{{ name }}"{% endif %}
|
||||
>
|
||||
{{ text }}
|
||||
</label>
|
||||
{% if data.description %}
|
||||
<br>
|
||||
{{ description( name, data.description ) }}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro select( name, options, data ) %}
|
||||
<br>
|
||||
<select id="field-{{ name }}"
|
||||
name="{{ name }}"
|
||||
{% if data.required %}required aria-required="true"{% endif %}
|
||||
{% if data.multiple %}multiple{% endif %}
|
||||
{% if data.description %}aria-describedby="description-{{ name }}"{% endif %}
|
||||
>
|
||||
{% for opt in data.options_before %}
|
||||
{{ option( opt ) }}
|
||||
{% endfor %}
|
||||
{% for opt in options %}
|
||||
{{ option( opt ) }}
|
||||
{% endfor %}
|
||||
{% for opt in data.options_after %}
|
||||
{{ option( opt ) }}
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% if data.description %}
|
||||
<br>
|
||||
{{ description( name, data.description ) }}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro option( data ) %}
|
||||
{% if data.value %}
|
||||
<option value="{{ data.value }}">{{ data.label }}</option>
|
||||
{% else %}
|
||||
<option>{{ data }}</option>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro textarea( name, data ) %}
|
||||
<br>
|
||||
<textarea id="field-{{ name }}"
|
||||
name="{{ name }}"
|
||||
{% if data.rows %}rows="{{ data.rows }}"{% else %}rows="5"{% endif %}
|
||||
cols="100"
|
||||
{% if data.required %}required aria-required="true"{% endif %}
|
||||
{% if data.autocorrect %}autocorrect="{{ data.autocorrect }}"{% endif %}
|
||||
{% if data.spellcheck %}spellcheck="{{ data.spellcheck }}"{% endif %}
|
||||
{% if data.autocapitalize %}autocapitalize="{{ data.autocapitalize }}"{% endif %}
|
||||
{% if data.description %}aria-describedby="description-{{ name }}"{% endif %}
|
||||
></textarea>
|
||||
{% if data.description %}
|
||||
{{ description( name, data.description ) }}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro radios( label, name, options, data ) %}
|
||||
<fieldset>
|
||||
<legend
|
||||
{% if data.description %}aria-describedby="description-{{ name }}"{% endif %}
|
||||
>{{ label }}</legend>
|
||||
<ul class="field-list__field-group__list">
|
||||
{% for option in options %}
|
||||
<li>
|
||||
{% if option.value %}
|
||||
<label for="field-{{ name }}-{{ option.value }}">
|
||||
<input type="radio"
|
||||
name="{{ name }}"
|
||||
id="field-{{ name }}-{{ option.value }}"
|
||||
value="{{ option.value }}"
|
||||
{% if option.note %}aria-describedby="description-{{ name }}-{{ option.value }}"{% endif %}
|
||||
>{{ option.label }}</label>
|
||||
{% else %}
|
||||
<label for="field-{{ name }}-{{ option }}">
|
||||
<input type="radio"
|
||||
name="{{ name }}"
|
||||
id="field-{{ name }}-{{ option }}"
|
||||
value="{{ option }}"
|
||||
>{{ option }}</label>
|
||||
{% endif %}
|
||||
{% if option.note %}
|
||||
<br>
|
||||
{{ description( ( name + '-' + option.value ), option.note ) }}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% if data.description %}
|
||||
{{ description( name, data.description ) }}
|
||||
{% endif %}
|
||||
</fieldset>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro checkboxes( label, name, options, data ) %}
|
||||
<fieldset>
|
||||
<legend
|
||||
{% if data.description %}aria-describedby="description-{{ name }}"{% endif %}
|
||||
>{{ label }}</legend>
|
||||
<ul class="field-list__field-group__list">
|
||||
{% for option in options %}
|
||||
<li>
|
||||
{% if option.value %}
|
||||
<label for="field-{{ name }}-{{ option.value }}">
|
||||
<input type="checkbox"
|
||||
name="{{ name }}[]"
|
||||
id="field-{{ name }}-{{ option.value }}"
|
||||
value="{{ option.value }}"
|
||||
{% if option.note %}aria-describedby="description-{{ name }}-{{ option.value }}"{% endif %}
|
||||
>{{ option.label }}</label>
|
||||
{% else %}
|
||||
<label for="field-{{ name }}-{{ option }}">
|
||||
<input type="checkbox"
|
||||
name="{{ name }}[]"
|
||||
id="field-{{ name }}-{{ option }}"
|
||||
value="{{ option }}"
|
||||
>{{ option }}</label>
|
||||
{% endif %}
|
||||
{% if option.note %}
|
||||
<br>
|
||||
{{ description( ( name + '-' + option.value ), option.note ) }}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% if data.description %}
|
||||
{{ description( name, data.description ) }}
|
||||
{% endif %}
|
||||
</fieldset>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro description( id, html ) %}
|
||||
<em class="[ field-list__field-group__description ]" id="description-{{ id }}">{{ html | safe }}</em>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro hidden_field( name, value ) %}
|
||||
<input type="hidden" name="{{ name }}" id="field-{{ name }}" value="{{ value }}">
|
||||
{% endmacro %}
|
||||
|
||||
{% macro button( text ) %}
|
||||
<button type="submit" class="[ button ] [ font-base text-base weight-bold ]">{{ text }}</button>
|
||||
{% endmacro %}
|
4
src/_includes/macros/site.njk
Normal file
@ -0,0 +1,4 @@
|
||||
{# ========================
|
||||
Site-specific Macros
|
||||
======================== #}
|
||||
|
22
src/_includes/partials/components/contact-form.njk
Normal file
@ -0,0 +1,22 @@
|
||||
{% from "macros/form.njk" import label, field, textarea, button %}
|
||||
|
||||
<form name="contact" method="POST" data-netlify="true" action="/thank-you" netlify-honeypot="bot-field">
|
||||
<ol class="[ field-list ]">
|
||||
<li class="[ field-list__field-group ]">
|
||||
{{ label("What’s your name?", "name") }}
|
||||
{{ field( "text", "name", { required: true, placeholder: "Katherine Johnson", autocomplete: "name", autocorrect: "off", autocapitalize: "off" } ) }}
|
||||
</li>
|
||||
<li class="[ field-list__field-group ]">
|
||||
{{ label("What’s your email address?", "email") }}
|
||||
{{ field( "email", "email", { required: true, placeholder: "katherine@johnson.tld", autocomplete: "email" } ) }}
|
||||
</li>
|
||||
<li class="[ field-list__field-group ]">
|
||||
{{ label("What’s on your mind?", "message") }}
|
||||
{{ textarea( "message", { required: true, autocapitalize: "sentences", spellcheck: "true" } ) }}
|
||||
</li>
|
||||
<li hidden>
|
||||
<label>Don’t fill this out if you're human: <input name="bot-field" /></label>
|
||||
</li>
|
||||
</ol>
|
||||
{{ button("Send message") }}
|
||||
</form>
|
10
src/_includes/partials/components/intro.njk
Normal file
@ -0,0 +1,10 @@
|
||||
<header class="[ intro ]">
|
||||
<div class="[ inner-wrapper ]">
|
||||
<h1 class="[ intro__heading ]">{{ brandHeading }}</h1>
|
||||
{% if introSummary %}
|
||||
<div class="[ intro__summary ] [ sf-flow ] [ leading-mid measure-short ]">{{ introSummary | safe }}</div>
|
||||
{% endif %}
|
||||
<a role="button" href="/README" class="btn btn-secondary">Une CAE c'est quoi ?</button>
|
||||
<a role="button" href="/README" class="btn btn-primary">Nous rejoindre</button>
|
||||
</div>
|
||||
</header>
|
22
src/_includes/partials/components/nav.njk
Normal file
@ -0,0 +1,22 @@
|
||||
{% if navigation.items %}
|
||||
<nav class="nav" aria-label="{{ariaLabel}}">
|
||||
<ul class="[ nav__list ] [ box-flex align-center md:space-before ]">
|
||||
{% for item in navigation.items %}
|
||||
{% set relAttribute = '' %}
|
||||
{% set currentAttribute = '' %}
|
||||
|
||||
{% if item.rel %}
|
||||
{% set relAttribute = ' rel="' + item.rel + '"' %}
|
||||
{% endif %}
|
||||
|
||||
{% if page.url == item.url %}
|
||||
{% set currentAttribute = ' aria-current="page"' %}
|
||||
{% endif %}
|
||||
|
||||
<li class="nav__item">
|
||||
<a href="{{ item.url | url }}"{{ relAttribute | safe }}{{ currentAttribute | safe }}>{{ item.text }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</nav>
|
||||
{% endif %}
|
25
src/_includes/partials/components/news-list.njk
Normal file
@ -0,0 +1,25 @@
|
||||
{% if newsListItems.length %}
|
||||
<section class="[ news-list ]">
|
||||
<svg aria-hidden="true" viewBox="0 0 1440 349" width="auto" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill="#282156" d="M1440 154H0v195h1440z"/><path d="M1440 54.078l-48 12.41c-48 12.41-144 37.23-240 45.53-96 7.989-192 .232-288-29.01C768 54.079 672 4.438 576 .327c-96-4.421-192 37.463-288 45.452-96 8.3-192-16.52-240-28.931L0 4.437V203h1440V54.078z" fill="#282156"/></svg>
|
||||
<div class="[ wrapper ]">
|
||||
<div class="news-list__inner">
|
||||
<h2 class="[ news-list__heading ]">{{ newsListHeading }}</h2>
|
||||
<ol class="[ news-list__items ]" reversed>
|
||||
{% for item in newsListItems %}
|
||||
{% if item.date.getTime() <= global.now %}
|
||||
<li class="news-list__item">
|
||||
<h3 class="news-list__item-heading">
|
||||
<a href="{{ item.url | url }}" class="news-list__link" rel="bookmark">{{ item.data.title }}</a>
|
||||
</h3>
|
||||
<p class="news-list__item-date">
|
||||
<time datetime="{{ item.date | w3DateFilter }}">{{ item.date | dateFilter }}</time>
|
||||
</p>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ol>
|
||||
<a href="/" class="news-list__see-all">Voir tout</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
23
src/_includes/partials/components/pagination.njk
Normal file
@ -0,0 +1,23 @@
|
||||
{% set paginationLinkTokens = 'leading-tight text-500 weight-mid box-inline-flex align-center pad-bottom-300' %}
|
||||
|
||||
{% if paginationNextUrl or paginationPrevUrl %}
|
||||
<hr />
|
||||
<div class="inner-wrapper">
|
||||
<footer class="[ pagination ] [ pad-bottom-900 ]">
|
||||
<nav class="[ pagination__nav ] [ box-flex space-between align-center ]">
|
||||
{% if paginationPrevUrl %}
|
||||
<a href="{{ paginationPrevUrl }}" class="{{ paginationLinkTokens }}" data-direction="backwards">
|
||||
<span>{{ paginationPrevText if paginationPrevText else 'Previous' }}</span>
|
||||
{% include "icons/arrow.svg" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if paginationNextUrl %}
|
||||
<a href="{{ paginationNextUrl }}" class="{{ paginationLinkTokens }}" data-direction="forwards">
|
||||
<span>{{ paginationNextText if paginationNextText else 'Next' }}</span>
|
||||
{% include "icons/arrow.svg" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</nav>
|
||||
</footer>
|
||||
</div>
|
||||
{% endif %}
|
21
src/_includes/partials/components/post-list.njk
Normal file
@ -0,0 +1,21 @@
|
||||
{% if postListItems.length %}
|
||||
<section class="[ post-list ] [ pad-top-700 gap-bottom-900 ]">
|
||||
<div class="[ inner-wrapper ] [ sf-flow ]">
|
||||
<h2 class="[ post-list__heading ]">{{ postListHeading }}</h2>
|
||||
<ol class="[ post-list__items ] [ sf-flow ] [ pad-top-300 ]" reversed>
|
||||
{% for item in postListItems %}
|
||||
{% if item.date.getTime() <= global.now %}
|
||||
<li class="post-list__item">
|
||||
<h3 class="font-base leading-tight text-600 weight-mid">
|
||||
<a href="{{ item.url }}" class="post-list__link" rel="bookmark">{{ item.data.title }}</a>
|
||||
</h3>
|
||||
<p class="text-500 gap-top-300 weight-mid">
|
||||
<time datetime="{{ item.date | w3DateFilter }}">{{ item.date | dateFilter }}</time>
|
||||
</p>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ol>
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
5
src/_includes/partials/components/presentation.njk
Normal file
@ -0,0 +1,5 @@
|
||||
<section class="[ presentation ]">
|
||||
<div class="[ wrapper ]">
|
||||
{{ presentation | safe }}
|
||||
</div>
|
||||
</section>
|
41
src/_includes/partials/global/meta-info.njk
Normal file
@ -0,0 +1,41 @@
|
||||
{% set pageTitle = site.name + ' - ' + title %}
|
||||
{% set pageDesc = '' %}
|
||||
{% set siteTitle = site.name %}
|
||||
{% set currentUrl = site.url + page.url %}
|
||||
|
||||
{% if metaTitle %}
|
||||
{% set pageTitle = metaTitle %}
|
||||
{% endif %}
|
||||
|
||||
{% if metaDesc %}
|
||||
{% set pageDesc = metaDesc %}
|
||||
{% endif %}
|
||||
|
||||
<title>{{ pageTitle }}</title>
|
||||
<link rel="canonical" href="{{ currentUrl }}" />
|
||||
|
||||
<meta property="og:site_name" content="{{ siteTitle }}" />
|
||||
<meta property="og:title" content="{{ pageTitle }}" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="{{ currentUrl }}" />
|
||||
|
||||
{% if site.authorHandle %}
|
||||
<meta name="twitter:creator" content="@{{ site.authorHandle | replace('@', '') }}" />
|
||||
{% endif %}
|
||||
|
||||
{% if metaDesc %}
|
||||
<meta name="description" content="{{ metaDesc }}" />
|
||||
<meta name="twitter:description" content="{{ metaDesc }}" />
|
||||
<meta property="og:description" content="{{ metaDesc }}" />
|
||||
{% endif %}
|
||||
|
||||
{% if socialImage %}
|
||||
<meta property="og:image" content="{{ socialImage }}" />
|
||||
<meta name="twitter:image" content="{{ socialImage }}" />
|
||||
<meta property="og:image:alt" content="Page image for {{ site.name }}" />
|
||||
<meta name="twitter:image:alt" content="Page image for {{ site.name }}" />
|
||||
{% endif %}
|
||||
|
||||
{% if site.paymentPointer %}
|
||||
<meta name="monetization" content="{{ site.paymentPointer }}" />
|
||||
{% endif %}
|
92
src/_includes/partials/global/service-worker.js
Normal file
@ -0,0 +1,92 @@
|
||||
const CACHE_KEYS = {
|
||||
PRE_CACHE: `precache-${VERSION}`,
|
||||
RUNTIME: `runtime-${VERSION}`
|
||||
};
|
||||
|
||||
// URLS that we don’t want to end up in the cache
|
||||
const EXCLUDED_URLS = [
|
||||
'admin',
|
||||
'.netlify',
|
||||
'https://identity.netlify.com/v1/netlify-identity-widget.js',
|
||||
'https://unpkg.com/netlify-cms@^2.9.3/dist/netlify-cms.js',
|
||||
'/contact',
|
||||
'/thank-you'
|
||||
];
|
||||
|
||||
// URLS that we want to be cached when the worker is installed
|
||||
const PRE_CACHE_URLS = ['/', '/fonts/VarelaRound-Regular.ttf', '/fonts/OpenSans-Bold.ttf', '/fonts/OpenSans-SemiBold.ttf', '/fonts/OpenSans-Regular.ttf'];
|
||||
|
||||
// You might want to bypass a certain host
|
||||
const IGNORED_HOSTS = ['localhost', 'unpkg.com', ];
|
||||
|
||||
/**
|
||||
* Takes an array of strings and puts them in a named cache store
|
||||
*
|
||||
* @param {String} cacheName
|
||||
* @param {Array} items=[]
|
||||
*/
|
||||
const addItemsToCache = function(cacheName, items = []) {
|
||||
caches.open(cacheName).then(cache => cache.addAll(items));
|
||||
};
|
||||
|
||||
self.addEventListener('install', evt => {
|
||||
self.skipWaiting();
|
||||
|
||||
addItemsToCache(CACHE_KEYS.PRE_CACHE, PRE_CACHE_URLS);
|
||||
});
|
||||
|
||||
self.addEventListener('activate', evt => {
|
||||
// Look for any old caches that don't match our set and clear them out
|
||||
evt.waitUntil(
|
||||
caches
|
||||
.keys()
|
||||
.then(cacheNames => {
|
||||
return cacheNames.filter(item => !Object.values(CACHE_KEYS).includes(item));
|
||||
})
|
||||
.then(itemsToDelete => {
|
||||
return Promise.all(
|
||||
itemsToDelete.map(item => {
|
||||
return caches.delete(item);
|
||||
})
|
||||
);
|
||||
})
|
||||
.then(() => self.clients.claim())
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener('fetch', evt => {
|
||||
const {hostname} = new URL(evt.request.url);
|
||||
|
||||
// Check we don't want to ignore this host
|
||||
if (IGNORED_HOSTS.indexOf(hostname) >= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check we don't want to ignore this URL
|
||||
if (EXCLUDED_URLS.some(page => evt.request.url.indexOf(page) > -1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
evt.respondWith(
|
||||
caches.match(evt.request).then(cachedResponse => {
|
||||
// Item found in cache so return
|
||||
if (cachedResponse) {
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
// Nothing found so load up the request from the network
|
||||
return caches.open(CACHE_KEYS.RUNTIME).then(cache => {
|
||||
return fetch(evt.request)
|
||||
.then(response => {
|
||||
// Put the new response in cache and return it
|
||||
return cache.put(evt.request, response.clone()).then(() => {
|
||||
return response;
|
||||
});
|
||||
})
|
||||
.catch(ex => {
|
||||
return;
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
24
src/_includes/partials/global/site-foot.njk
Normal file
@ -0,0 +1,24 @@
|
||||
<footer role="contentinfo" class="[ site-foot ]">
|
||||
<div class="wrapper">
|
||||
<div class="[ site-foot__inner ]">
|
||||
<div class="">
|
||||
<h3>Mentions légales</h3>
|
||||
<p>Protection des données</p>
|
||||
</div>
|
||||
<div class="">
|
||||
<h3>Crédits</h3>
|
||||
<p>Design et intégration : <a href="{{site.designerHandle}}" target="_blank">{{site.designerName}}</a></p>
|
||||
<p>Illustrations : {{site.illustrators}}
|
||||
</div>
|
||||
<div class="">
|
||||
<h3>Contact</h3>
|
||||
<p>{{site.authorName}}</p>
|
||||
<p>{{site.authorAddress}}<br>{{site.authorCity}}</p>
|
||||
<ul>
|
||||
<a href="{{site.authorSocial.mastodon}}" class="social">Mastodon</a>
|
||||
<a href="{{site.authorSocial.twitter}}" class="social">Twitter</a>
|
||||
<a href="{{site.authorSocial.linkedin}}" class="social">Linkedin</a>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
13
src/_includes/partials/global/site-head.njk
Normal file
@ -0,0 +1,13 @@
|
||||
<a class="skip-link" href="#main-content">Skip to content</a>
|
||||
<header role="banner" class="[ site-head ]">
|
||||
<div class="wrapper">
|
||||
<div class="[ site-head__inner ] [ md:box-flex space-between align-center ]">
|
||||
<a href="{{ url }}" class="[ site-head__site-name ] [ leading-tight ]">
|
||||
<span class="visually-hidden">{{ site.name }} - Home</span>
|
||||
{% include "icons/astrolabe_logo.svg" %}
|
||||
</a>
|
||||
{% set ariaLabel = 'navigation' %}
|
||||
{% include "partials/components/nav.njk" %}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
7
src/_includes/partials/global/third-party-comments.njk
Normal file
@ -0,0 +1,7 @@
|
||||
<!-- ADD YOUR THIRD PARTY COMMENTS CODE HERE -->
|
||||
<!-- COMMENTO EXAMPLE
|
||||
<div id="commento"></div>
|
||||
<script defer
|
||||
src="https://cdn.commento.io/js/commento.js">
|
||||
</script>
|
||||
-->
|
4
src/archive.md
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
title: 'Posts Archive'
|
||||
layout: 'layouts/archive.njk'
|
||||
---
|
30
src/feed.njk
Normal file
@ -0,0 +1,30 @@
|
||||
---
|
||||
permalink: '/feed.xml'
|
||||
---
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
<title>{{ site.name }}</title>
|
||||
<subtitle></subtitle>
|
||||
<link href="{{ site.url }}{{ permalink }}" rel="self"/>
|
||||
<link href="{{ site.url }}/"/>
|
||||
{% if collections.posts|length %}
|
||||
<updated>{{ collections.posts | rssLastUpdatedDate }}</updated>
|
||||
{% endif %}
|
||||
<id>{{ site.url }}</id>
|
||||
<author>
|
||||
<name>{{ site.authorName }}</name>
|
||||
<email>{{ site.authorEmail }}</email>
|
||||
</author>
|
||||
{% for post in collections.posts %}
|
||||
{% set absolutePostUrl %}{{ site.url }}{{ post.url | url }}{% endset %}
|
||||
<entry>
|
||||
<title>{{ post.data.title }}</title>
|
||||
<link href="{{ absolutePostUrl }}"/>
|
||||
<updated>{{ post.date | rssDate }}</updated>
|
||||
<id>{{ absolutePostUrl }}</id>
|
||||
<content type="html"><![CDATA[
|
||||
{{ post.templateContent | safe }}
|
||||
]]></content>
|
||||
</entry>
|
||||
{% endfor %}
|
||||
</feed>
|
17
src/filters/date-filter.js
Normal file
@ -0,0 +1,17 @@
|
||||
// Stolen from https://stackoverflow.com/a/31615643
|
||||
const appendSuffix = n => {
|
||||
var s = ['th', 'st', 'nd', 'rd'],
|
||||
v = n % 100;
|
||||
return n + (s[(v - 20) % 10] || s[v] || s[0]);
|
||||
};
|
||||
|
||||
module.exports = function dateFilter(value) {
|
||||
const dateObject = new Date(value);
|
||||
|
||||
// const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
|
||||
const months = ['janv.', 'févr.', 'mars', 'avr.', 'mai', 'juin', 'juill.', 'août', 'sept.', 'oct.', 'nov.', 'déc.'];
|
||||
const dayWithSuffix = appendSuffix(dateObject.getDate());
|
||||
|
||||
// return `${dayWithSuffix} ${months[dateObject.getMonth()]} ${dateObject.getFullYear()}`;
|
||||
return `${dateObject.getDate()} ${months[dateObject.getMonth()]} ${dateObject.getFullYear()}`;
|
||||
};
|
9
src/filters/markdown-filter.js
Normal file
@ -0,0 +1,9 @@
|
||||
const markdownIt = require('markdown-it')({
|
||||
html: true,
|
||||
breaks: true,
|
||||
linkify: true
|
||||
});
|
||||
|
||||
module.exports = function markdown(value) {
|
||||
return markdownIt.render(value);
|
||||
};
|
5
src/filters/w3-date-filter.js
Normal file
@ -0,0 +1,5 @@
|
||||
module.exports = function w3cDate(value) {
|
||||
const dateObject = new Date(value);
|
||||
|
||||
return dateObject.toISOString();
|
||||
};
|
BIN
src/fonts/open-sans-v17-latin-300.woff
Normal file
BIN
src/fonts/open-sans-v17-latin-600.woff
Normal file
BIN
src/fonts/open-sans-v17-latin-700.woff
Normal file
BIN
src/fonts/open-sans-v17-latin-regular.woff
Normal file
BIN
src/fonts/varela-round-v12-latin-regular.woff
Normal file
BIN
src/fonts/varela-v10-latin-regular.woff
Normal file
BIN
src/images/demo-image-1.jpg
Normal file
After Width: | Height: | Size: 100 KiB |
BIN
src/images/demo-image-2.jpg
Normal file
After Width: | Height: | Size: 147 KiB |
BIN
src/images/favicon.png
Normal file
After Width: | Height: | Size: 7.8 KiB |
1
src/images/gitlab.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="110" height="101" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M109.052 56.536l-5.704-17.493a2.415 2.415 0 00-.138-.47L91.766 3.412a4.54 4.54 0 00-1.669-2.251 4.555 4.555 0 00-2.674-.846 4.478 4.478 0 00-2.661.856 4.464 4.464 0 00-1.632 2.266L72.222 36.942H37.795L26.871 3.438a4.465 4.465 0 00-1.624-2.26 4.478 4.478 0 00-2.65-.862h-.025a4.555 4.555 0 00-4.331 3.125L6.788 38.643c0 .032-.026.057-.035.09L.946 56.538a6.485 6.485 0 002.364 7.272l50.17 36.386a2.57 2.57 0 003.032-.016l50.179-36.37a6.493 6.493 0 002.361-7.275zM34.061 42.085l13.984 42.96-33.57-42.96H34.06zm27.911 42.97l13.41-41.187.578-1.783h19.602L65.19 80.92l-3.218 4.133zM87.467 6.735l9.827 30.206h-19.67l9.844-30.206zm-16.91 35.33l-9.743 29.927L55 89.816l-15.534-47.75h31.09zM22.55 6.736l9.846 30.206H12.739l9.81-30.206zM6.33 59.668a1.38 1.38 0 01-.501-1.547l4.311-13.225 31.624 40.466L6.329 59.668zm97.345 0L68.238 85.352l.118-.154 31.505-40.302 4.312 13.219a1.383 1.383 0 01-.498 1.55" fill="#111"/></svg>
|
After Width: | Height: | Size: 998 B |
BIN
src/images/social-share.jpg
Normal file
After Width: | Height: | Size: 23 KiB |
40
src/index.md
Normal file
@ -0,0 +1,40 @@
|
||||
---
|
||||
layout: home
|
||||
title: Accueil
|
||||
brandHeading: La Coopérative d'Activité et d'Emploi spécialisée en informatique !
|
||||
newsHeading: Actualité & Évenements
|
||||
metaDesc: 'Hylia is a lightweight Eleventy starter kit to help you to create your own blog or personal website.'
|
||||
---
|
||||
|
||||
<article>
|
||||
<div class="content">
|
||||
|
||||
## Qui sommes-nous ?
|
||||
Astrolabe CAE est une scop spécialisée dans la prestation de **services** autour des métiers du **numérique**.
|
||||
Notre objectif est de favoriser l’**autonomie** et l’**émancipation** de nos membres sur un modèle d’économie sociale et **solidaire**.
|
||||
</div>
|
||||
|
||||
![logo Gitlab](/images/gitlab.svg)
|
||||
</article>
|
||||
|
||||
<article>
|
||||
<div class="content">
|
||||
|
||||
## Communs numérique
|
||||
Chez Astrolabe nous aimons et faisons la promotion du **logiciel libre**. Nos sommes membres d’[Alliance Libre](http://www.alliance-libre.org/) et nous mettons nos documents et projets internes à disposition sur notre [gitlab]().
|
||||
</div>
|
||||
|
||||
![logo Gitlab](/images/gitlab.svg "test")
|
||||
</article>
|
||||
|
||||
<article>
|
||||
<div class="content">
|
||||
|
||||
## Des profils variés
|
||||
Nos coopérateurs possèdent des compétences propres allant de développement linux embarqué au web design et créent ainsi la **pluralité** de nos prestations.
|
||||
<br><br>
|
||||
Nous sommes également **distributeurs** de la solution logicielle [Naega](https://www.crealead.com/naega#bootstrap-fieldgroup-nav-item--prsentation).
|
||||
</div>
|
||||
|
||||
![logo Gitlab](/images/gitlab.svg)
|
||||
</article>
|
98
src/js/components/theme-toggle.js
Normal file
@ -0,0 +1,98 @@
|
||||
// For syntax highlighting only
|
||||
const html = String.raw;
|
||||
|
||||
class ThemeToggle extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.STORAGE_KEY = 'user-color-scheme';
|
||||
this.COLOR_MODE_KEY = '--color-mode';
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.render();
|
||||
}
|
||||
|
||||
getCSSCustomProp(propKey) {
|
||||
let response = getComputedStyle(document.documentElement).getPropertyValue(propKey);
|
||||
|
||||
// Tidy up the string if there’s something to work with
|
||||
if (response.length) {
|
||||
response = response.replace(/\'|"/g, '').trim();
|
||||
}
|
||||
|
||||
// Return the string response by default
|
||||
return response;
|
||||
}
|
||||
|
||||
applySetting(passedSetting) {
|
||||
let currentSetting = passedSetting || localStorage.getItem(this.STORAGE_KEY);
|
||||
|
||||
if (currentSetting) {
|
||||
document.documentElement.setAttribute('data-user-color-scheme', currentSetting);
|
||||
this.setButtonLabelAndStatus(currentSetting);
|
||||
} else {
|
||||
this.setButtonLabelAndStatus(this.getCSSCustomProp(this.COLOR_MODE_KEY));
|
||||
}
|
||||
}
|
||||
|
||||
toggleSetting() {
|
||||
let currentSetting = localStorage.getItem(this.STORAGE_KEY);
|
||||
|
||||
switch (currentSetting) {
|
||||
case null:
|
||||
currentSetting =
|
||||
this.getCSSCustomProp(this.COLOR_MODE_KEY) === 'dark' ? 'light' : 'dark';
|
||||
break;
|
||||
case 'light':
|
||||
currentSetting = 'dark';
|
||||
break;
|
||||
case 'dark':
|
||||
currentSetting = 'light';
|
||||
break;
|
||||
}
|
||||
|
||||
localStorage.setItem(this.STORAGE_KEY, currentSetting);
|
||||
|
||||
return currentSetting;
|
||||
}
|
||||
|
||||
setButtonLabelAndStatus(currentSetting) {
|
||||
this.modeToggleButton.innerText = `${
|
||||
currentSetting === 'dark' ? 'Light' : 'Dark'
|
||||
} theme`;
|
||||
this.modeStatusElement.innerText = `Color mode is now "${currentSetting}"`;
|
||||
}
|
||||
|
||||
render() {
|
||||
this.innerHTML = html`
|
||||
<div class="[ theme-toggle ] [ md:ta-right gap-top-500 ]">
|
||||
<div role="status" class="[ visually-hidden ][ js-mode-status ]"></div>
|
||||
<button class="[ button ] [ font-base text-base weight-bold ] [ js-mode-toggle ]">
|
||||
Dark theme
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
this.afterRender();
|
||||
}
|
||||
|
||||
afterRender() {
|
||||
this.modeToggleButton = document.querySelector('.js-mode-toggle');
|
||||
this.modeStatusElement = document.querySelector('.js-mode-status');
|
||||
|
||||
this.modeToggleButton.addEventListener('click', evt => {
|
||||
evt.preventDefault();
|
||||
|
||||
this.applySetting(this.toggleSetting());
|
||||
});
|
||||
|
||||
this.applySetting();
|
||||
}
|
||||
}
|
||||
|
||||
if ('customElements' in window) {
|
||||
customElements.define('theme-toggle', ThemeToggle);
|
||||
}
|
||||
|
||||
export default ThemeToggle;
|
9
src/pages/contact.md
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
title: 'Contact'
|
||||
permalink: '/contact/index.html'
|
||||
layout: 'layouts/contact.njk'
|
||||
---
|
||||
|
||||
You can have a contact page which uses a basic form. The [code with the form fields lives here](https://github.com/hankchizljaw/hylia/blob/master/src/_includes/layouts/contact.njk).
|
||||
|
||||
To delete the contact form for this site, delete this page in the CMS or at `src/pages/contact.md`. You probably will also want to delete `src/pages/thank-you.md`.
|
3
src/pages/pages.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"layout": "layouts/page.njk"
|
||||
}
|
6
src/pages/thank-you.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
title: 'Thank you'
|
||||
permalink: '/thank-you/index.html'
|
||||
---
|
||||
|
||||
This is your thank you page where if someone fills in your contact form, they will be directed to. Make sure you add a nice message 🙂
|
64
src/posts/a-post-with-code-samples.md
Normal file
@ -0,0 +1,64 @@
|
||||
---
|
||||
title: A post with code samples
|
||||
date: '2019-12-18'
|
||||
tags:
|
||||
- demo-content
|
||||
- code
|
||||
- blog
|
||||
---
|
||||
The best way to demo a code post is to display a real life post, so check out this one from [andy-bell.design](https://andy-bell.design/wrote/creating-a-full-bleed-css-utility/) about a full bleed CSS utility.
|
||||
|
||||
- - -
|
||||
|
||||
Sometimes you want to break your components out of the constraints that they find themselves in. A common situation where this occurs is when you don’t have much control of the container that it exists in, such as a CMS main content area.
|
||||
|
||||
This is even more the case with editing tools such as the [WordPress Gutenberg editor](https://wordpress.org/gutenberg/), where in theory, you could pull in a component from a design system and utilise it in the main content of your web page. In these situations, it can be pretty darn handy to have a little utility that makes the element 100% of the viewport’s width _and_ still maintain its flow within its parent container.
|
||||
|
||||
This is when I normally pull the `.full-bleed` utility class out of my back pocket.
|
||||
|
||||
## The `.full-bleed` utility
|
||||
|
||||
It’s small, but hella mighty:
|
||||
|
||||
```css
|
||||
.full-bleed {
|
||||
width: 100vw;
|
||||
margin-left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
```
|
||||
|
||||
Here it is in a context where it makes a fancy `<aside>` and a `<figure>` element bleed out of their parent container.
|
||||
|
||||
<iframe height="300" style="width: 100%;" scrolling="no" title="Piccalilli CSS Utility — Issue #2 — Full bleed utility" src="//codepen.io/andybelldesign/embed/Nmxrwv/?height=300&theme-id=dark&default-tab=css,result" frameborder="no" allowtransparency="true" allowfullscreen="true">
|
||||
See the Pen <a href='https://codepen.io/andybelldesign/pen/Nmxrwv/'>Piccalilli CSS Utility — Issue #2 — Full bleed utility</a> by Andy Bell
|
||||
(<a href='https://codepen.io/andybelldesign'>@andybelldesign</a>) on <a href='https://codepen.io'>CodePen</a>.
|
||||
</iframe>
|
||||
|
||||
The `.full-bleed` utility gives those elements prominence and _importantly_ keeps their semantic place in the page. Just how I like it.
|
||||
|
||||
- - -
|
||||
|
||||
🔥 **Pro tip**: When working with a utility like `.full-bleed`, it’s a good idea to add an inner container that has a max-width and auto horizontal margin. For this, I normal create a shared `.wrapper` component like this:
|
||||
|
||||
```css
|
||||
.wrapper {
|
||||
max-width: 50rem;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
```
|
||||
|
||||
Having a container like `.wrapper` helps to create consistent, centred content.
|
||||
|
||||
- - -
|
||||
|
||||
### How the `.full-bleed` utility works
|
||||
|
||||
We set the container to be `width: 100vw`, which equates to the full viewport width. We couldn’t set it to `width: 100%` because it would only fill the space of its parent element. The parent element’s width _is_ useful though, because by setting `margin-left: 50%`, we are telling the component to align its **left edge** to the center of its parent element, because `50%` is half of the **parent element’s** width.
|
||||
|
||||
Finally, we use CSS transforms to `translateX(-50%)`. Because the transform works off the element’s dimensions and not the parent’s dimensions, it’ll pull the element back `50vw`, because it’s `100vw` wide, thus making it sit perfectly flush with the viewport’s edges.
|
||||
|
||||
## Wrapping up
|
||||
|
||||
Hopefully this short and sweet trick will help you out on your projects. If it does, [drop me a tweet](https://twitter.com/andybelldesign), because I’d love to see it!
|
25
src/posts/a-post-with-figures-and-video.md
Normal file
@ -0,0 +1,25 @@
|
||||
---
|
||||
title: A post with figures and video
|
||||
date: '2019-04-18'
|
||||
tags:
|
||||
- demo-content
|
||||
- blog
|
||||
- media
|
||||
---
|
||||
A post to demonstrate how a blog post looks on Hylia. Content is all set in the “Body” field as markdown and Eleventy transforms it into a proper HTML post. You can also edit the markdown file directly if you prefer not to use the CMS.
|
||||
|
||||
If you want to make an image bleed-out, add a title attribute to it and the front-end will automatically wrap it in a `<figure>` tag for you.
|
||||
|
||||
![The top of a grey concrete building with a blue sky in the background](/images/demo-image-1.jpg "Brutalism at its finest. Photo by Artificial Photography on Unsplash.")
|
||||
|
||||
You can also add videos to posts from YouTube or Vimeo (or wherever, really) and the front-end will also make those bleed-out for you too.
|
||||
|
||||
<iframe width="560" height="315" src="https://www.youtube.com/embed/_38JDGnr0vA" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||
|
||||
Finally, how about a `<blockquote>`?
|
||||
|
||||
> Quotes will take a slightly different style to normal body text and look fancy.
|
||||
|
||||
![Person holds up a photograph of a riverside and buildings with the same river as a backdrop](/images/demo-image-2.jpg "Remember, if you want a figure and caption, add a 'title' attribute to image in the body field — Photo by Kharytonova Antonina on Unsplash.")
|
||||
|
||||
Hopefully, this has demonstrated how simple it is to make a nice looking blog with Hylia.
|
33
src/posts/a-scheduled-post.md
Normal file
@ -0,0 +1,33 @@
|
||||
---
|
||||
title: A scheduled post
|
||||
date: '2022-06-18'
|
||||
tags:
|
||||
- demo-content
|
||||
- simple-post
|
||||
- blog
|
||||
---
|
||||
|
||||
This post is scheduled for the future, specifically mid-2022. Hopefully you're still blogging by then too. Once that date ticks by, this post will automatically become published and visible.
|
||||
|
||||
Otherwise, below is some styled content for you to play with.
|
||||
|
||||
A simple post to demonstrate how a normal blog post looks on Hylia. Content is all set in the “Body” field as markdown and Eleventy transforms it into a proper HTML post. You can also edit the markdown file directly if you prefer not to use the CMS.
|
||||
|
||||
How about a `<blockquote>`?
|
||||
|
||||
> Maecenas faucibus mollis interdum. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Aenean lacinia bibendum nulla sed consectetur. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla vitae elit libero, a pharetra augue.
|
||||
|
||||
A list of stuff:
|
||||
|
||||
- Sed posuere consectetur est at lobortis
|
||||
- Aenean lacinia bibendum nulla sed consectetur
|
||||
- Sed posuere consectetur est at lobortis
|
||||
|
||||
How about an ordered list of stuff:
|
||||
|
||||
1. Sed posuere consectetur est at lobortis
|
||||
2. Aenean lacinia bibendum nulla sed consectetur
|
||||
3. Sed posuere consectetur est at lobortis
|
||||
|
||||
|
||||
Hopefully, this has demonstrated how simple it is to make a nice looking blog with Hylia.
|
28
src/posts/a-simple-post.md
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
title: A simple post
|
||||
date: '2019-05-18'
|
||||
tags:
|
||||
- demo-content
|
||||
- simple-post
|
||||
- blog
|
||||
---
|
||||
A simple post to demonstrate how a normal blog post looks on Hylia. Content is all set in the “Body” field as markdown and Eleventy transforms it into a proper HTML post. You can also edit the markdown file directly if you prefer not to use the CMS.
|
||||
|
||||
How about a `<blockquote>`?
|
||||
|
||||
> Maecenas faucibus mollis interdum. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Aenean lacinia bibendum nulla sed consectetur. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla vitae elit libero, a pharetra augue.
|
||||
|
||||
A list of stuff:
|
||||
|
||||
- Sed posuere consectetur est at lobortis
|
||||
- Aenean lacinia bibendum nulla sed consectetur
|
||||
- Sed posuere consectetur est at lobortis
|
||||
|
||||
How about an ordered list of stuff:
|
||||
|
||||
1. Sed posuere consectetur est at lobortis
|
||||
2. Aenean lacinia bibendum nulla sed consectetur
|
||||
3. Sed posuere consectetur est at lobortis
|
||||
|
||||
|
||||
Hopefully, this has demonstrated how simple it is to make a nice looking blog with Hylia.
|
28
src/posts/evenement-12-06-20.md
Normal file
@ -0,0 +1,28 @@
|
||||
---
|
||||
title: Réunion d'information Astrolabe
|
||||
date: '2020-06-12'
|
||||
time: '19:00'
|
||||
tags:
|
||||
- évenement
|
||||
- réunion
|
||||
---
|
||||
A simple post to demonstrate how a normal blog post looks on Hylia. Content is all set in the “Body” field as markdown and Eleventy transforms it into a proper HTML post. You can also edit the markdown file directly if you prefer not to use the CMS.
|
||||
|
||||
How about a `<blockquote>`?
|
||||
|
||||
> Maecenas faucibus mollis interdum. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Aenean lacinia bibendum nulla sed consectetur. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla vitae elit libero, a pharetra augue.
|
||||
|
||||
A list of stuff:
|
||||
|
||||
- Sed posuere consectetur est at lobortis
|
||||
- Aenean lacinia bibendum nulla sed consectetur
|
||||
- Sed posuere consectetur est at lobortis
|
||||
|
||||
How about an ordered list of stuff:
|
||||
|
||||
1. Sed posuere consectetur est at lobortis
|
||||
2. Aenean lacinia bibendum nulla sed consectetur
|
||||
3. Sed posuere consectetur est at lobortis
|
||||
|
||||
|
||||
Hopefully, this has demonstrated how simple it is to make a nice looking blog with Hylia.
|
3
src/posts/posts.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"layout": "layouts/post.njk"
|
||||
}
|
3
src/robots.txt
Normal file
@ -0,0 +1,3 @@
|
||||
User-agent: *
|
||||
Disallow: /preprod/
|
||||
Noindex: /preprod/
|
182
src/scss/_config.scss
Normal file
@ -0,0 +1,182 @@
|
||||
@import 'tokens';
|
||||
|
||||
/**
|
||||
* SIZE SCALE
|
||||
* This is a Major Third scale that powers all the utilities that
|
||||
* it is relevant for (font-size, margin, padding). All items are
|
||||
* calcuated off these tokens.
|
||||
*/
|
||||
$stalfos-size-scale: map-get($tokens, 'size-scale');
|
||||
|
||||
/**
|
||||
* COLORS
|
||||
* Colors are shared between backgrounds and text by default.
|
||||
* You can also use them to power borders, fills or shadows, for example.
|
||||
*/
|
||||
$stalfos-colors: map-get($tokens, 'colors');
|
||||
|
||||
/**
|
||||
* UTIL PREFIX
|
||||
* All pre-built, framework utilities will have this prefix.
|
||||
* Example: the wrapper utility is '.sf-wrapper' because the default
|
||||
* prefix is 'sf-'.
|
||||
*/
|
||||
$stalfos-util-prefix: 'sf-';
|
||||
|
||||
/**
|
||||
* METRICS
|
||||
* Various misc metrics to use around the site
|
||||
*/
|
||||
$metrics: (
|
||||
'wrap-max-width': 71.25rem,
|
||||
'wrap-inner-max-width': 62.25rem,
|
||||
'md-breakpoint': 48rem
|
||||
);
|
||||
|
||||
/**
|
||||
* CORE CONFIG
|
||||
* This powers everything from utility class generation to breakpoints
|
||||
* to enabling/disabling pre-built components/utilities.
|
||||
*/
|
||||
$stalfos-config: (
|
||||
'align': (
|
||||
'items': (
|
||||
'start': 'flex-start',
|
||||
'center': 'center',
|
||||
'end': 'flex-end'
|
||||
),
|
||||
'output': 'responsive',
|
||||
'property': 'align-items'
|
||||
),
|
||||
'bg': (
|
||||
'items': $stalfos-colors,
|
||||
'output': 'standard',
|
||||
'property': 'background'
|
||||
),
|
||||
'color': (
|
||||
'items': $stalfos-colors,
|
||||
'output': 'standard',
|
||||
'property': 'color'
|
||||
),
|
||||
'box': (
|
||||
'items': (
|
||||
'block': 'block',
|
||||
'flex': 'flex',
|
||||
'inline-flex': 'inline-flex',
|
||||
'hide': 'none'
|
||||
),
|
||||
'output': 'responsive',
|
||||
'property': 'display'
|
||||
),
|
||||
'font': (
|
||||
'items': map-get($tokens, 'fonts'),
|
||||
'output': 'standard',
|
||||
'property': 'font-family'
|
||||
),
|
||||
'gap-top': (
|
||||
'items': $stalfos-size-scale,
|
||||
'output': 'standard',
|
||||
'property': 'margin-top'
|
||||
),
|
||||
'gap-bottom': (
|
||||
'items': $stalfos-size-scale,
|
||||
'output': 'standard',
|
||||
'property': 'margin-bottom'
|
||||
),
|
||||
'leading': (
|
||||
'items': (
|
||||
'tight': '1.2',
|
||||
'mid': '1.5',
|
||||
'loose': '1.7'
|
||||
),
|
||||
'output': 'standard',
|
||||
'property': 'line-height'
|
||||
),
|
||||
'measure': (
|
||||
'items': (
|
||||
'long': '75ch',
|
||||
'short': '60ch',
|
||||
'compact': '40ch'
|
||||
),
|
||||
'output': 'standard',
|
||||
'property': 'max-width'
|
||||
),
|
||||
'pad-top': (
|
||||
'items': $stalfos-size-scale,
|
||||
'output': 'standard',
|
||||
'property': 'padding-top'
|
||||
),
|
||||
'pad-bottom': (
|
||||
'items': $stalfos-size-scale,
|
||||
'output': 'standard',
|
||||
'property': 'padding-bottom'
|
||||
),
|
||||
'pad-left': (
|
||||
'items': $stalfos-size-scale,
|
||||
'output': 'standard',
|
||||
'property': 'padding-left'
|
||||
),
|
||||
'space': (
|
||||
'items': (
|
||||
'between': 'space-between',
|
||||
'around': 'space-around',
|
||||
'before': 'flex-end'
|
||||
),
|
||||
'output': 'responsive',
|
||||
'property': 'justify-content'
|
||||
),
|
||||
'stack': (
|
||||
'items': (
|
||||
'300': 0,
|
||||
'400': 10,
|
||||
'500': 20,
|
||||
'600': 30,
|
||||
'700': 40
|
||||
),
|
||||
'output': 'standard',
|
||||
'property': 'z-index'
|
||||
),
|
||||
'ta': (
|
||||
'items': (
|
||||
'right': 'right',
|
||||
'left': 'left',
|
||||
'center': 'center'
|
||||
),
|
||||
'output': 'responsive',
|
||||
'property': 'text-align'
|
||||
),
|
||||
'text': (
|
||||
'items': $stalfos-size-scale,
|
||||
'output': 'responsive',
|
||||
'property': 'font-size'
|
||||
),
|
||||
'weight': (
|
||||
'items': (
|
||||
'light': '300',
|
||||
'regular': '400',
|
||||
'mid': '600',
|
||||
'bold': '700'
|
||||
),
|
||||
'output': 'standard',
|
||||
'property': 'font-weight'
|
||||
),
|
||||
'width': (
|
||||
'items': (
|
||||
'full': '100%',
|
||||
'half': percentage(1/2),
|
||||
'quarter': percentage(1/4),
|
||||
'third': percentage(1/3)
|
||||
),
|
||||
'output': 'responsive',
|
||||
'property': 'width'
|
||||
),
|
||||
'breakpoints': (
|
||||
'md': #{'(min-width: ' + map-get($metrics, 'md-breakpoint') + ')'}
|
||||
),
|
||||
'utilities': (
|
||||
'reset': 'on',
|
||||
'icon': 'off',
|
||||
'flow': 'on',
|
||||
'wrapper': 'off'
|
||||
)
|
||||
);
|
55
src/scss/_theme.scss
Normal file
@ -0,0 +1,55 @@
|
||||
:root {
|
||||
// Pull the tokens and generate custom props
|
||||
@each $color in $stalfos-colors {
|
||||
#{'--color-' + nth($color, 1)}: #{nth($color, 2)};
|
||||
}
|
||||
|
||||
// Set theme aliases
|
||||
--color-mode: 'light';
|
||||
--color-bg: #{get-color('light')};
|
||||
--color-bg-glare: #{get-color('light')};
|
||||
--color-text: #{get-color('dark')};
|
||||
--color-text-glare: #{get-color('dark')};
|
||||
--color-selection-text: #{get-color('light')};
|
||||
--color-selection-bg: #{get-color('dark')};
|
||||
--color-stroke: #{get-color('mid')};
|
||||
--color-action-bg: #{get-color('primary')};
|
||||
--color-action-text: #{get-color('light')};
|
||||
--color-theme-primary: #{get-color('primary')};
|
||||
--color-theme-primary-glare: #{get-color('primary-glare')};
|
||||
--color-theme-highlight: #{get-color('highlight')};
|
||||
--color-theme-highlight-block: #{get-color('highlight')};
|
||||
--color-theme-secondary: #{get-color('secondary')};
|
||||
--color-light-gray: #{get-color('light-gray')};
|
||||
--color-white: #{get-color('light')};
|
||||
}
|
||||
|
||||
@include dark-mode() {
|
||||
--color-bg: #{get-color('dark')};
|
||||
--color-bg-glare: #{get-color('slate')};
|
||||
--color-text: #{get-color('light')};
|
||||
--color-selection-text: #{get-color('dark')};
|
||||
--color-selection-bg: #{get-color('light')};
|
||||
--color-stroke: #{get-color('slate')};
|
||||
--color-theme-primary: #{lighten(get-color('primary'), 50%)};
|
||||
--color-theme-primary-glare: #{lighten(get-color('primary-glare'), 50%)};
|
||||
--color-action-bg: var(--color-theme-primary-glare);
|
||||
--color-action-text: #{get-color('dark')};
|
||||
--color-theme-highlight: #{get-color('highlight')};
|
||||
--color-theme-highlight-block: #{get-color('slate')};
|
||||
--color-theme-feature-text: #{get-color('highlight')};
|
||||
}
|
||||
|
||||
body {
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
main {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
::selection {
|
||||
color: var(--color-selection-text);
|
||||
background-color: var(--color-selection-bg);
|
||||
}
|
94
src/scss/_typography.scss
Normal file
@ -0,0 +1,94 @@
|
||||
/* varela-round-regular - latin */
|
||||
@font-face {
|
||||
font-family: 'Varela Round';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: local('Varela Round Regular'), local('VarelaRound-Regular'),
|
||||
url('/preprod/fonts/varela-round-v12-latin-regular.woff') format('woff');
|
||||
}
|
||||
|
||||
/* varela-regular - latin */
|
||||
@font-face {
|
||||
font-family: 'Varela';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Varela'),
|
||||
url('/preprod/fonts/varela-v10-latin-regular.woff') format('woff'), /* Modern Browsers */
|
||||
}
|
||||
|
||||
/* open-sans-300 - latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Open Sans Light'), local('OpenSans-Light'),
|
||||
url('/preprod/fonts/open-sans-v17-latin-300.woff') format('woff'), /* Modern Browsers */
|
||||
}
|
||||
|
||||
/* open-sans-regular - latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Open Sans Regular'), local('OpenSans-Regular'),
|
||||
url('/preprod/fonts/open-sans-v17-latin-regular.woff') format('woff'), /* Modern Browsers */
|
||||
}
|
||||
/* open-sans-600 - latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Open Sans SemiBold'), local('OpenSans-SemiBold'),
|
||||
url('/preprod/fonts/open-sans-v17-latin-600.woff') format('woff'), /* Modern Browsers */
|
||||
}
|
||||
/* open-sans-700 - latin */
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Open Sans Bold'), local('OpenSans-Bold'),
|
||||
url('/preprod/fonts/open-sans-v17-latin-700.woff') format('woff'), /* Modern Browsers */
|
||||
}
|
||||
|
||||
body {
|
||||
$line-height: get-size(600);
|
||||
@include apply-utility('font', 'base');
|
||||
|
||||
// Strip the unit off the size ratio to generate a line height
|
||||
line-height: $line-height / ($line-height * 0 + 1);
|
||||
}
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
@include apply-utility('font', 'brand');
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.125rem;
|
||||
line-height: 1.4;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: get-size(500);
|
||||
}
|
||||
|
||||
@include media-query('md') {
|
||||
// h1 {
|
||||
// font-size: get-size(900);
|
||||
// }
|
||||
|
||||
// h2 {
|
||||
// font-size: get-size(800);
|
||||
// }
|
||||
|
||||
// h3 {
|
||||
// font-size: get-size(700);
|
||||
// }
|
||||
}
|
57
src/scss/components/_button.scss
Normal file
@ -0,0 +1,57 @@
|
||||
.button {
|
||||
display: inline-block;
|
||||
border: 0;
|
||||
background-color: var(--color-action-bg);
|
||||
color: var(--color-action-text);
|
||||
padding: get-size(300) get-size('base');
|
||||
line-height: 1;
|
||||
margin: 0;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
}
|
||||
|
||||
.button:hover,
|
||||
.button:focus {
|
||||
filter: brightness(1.2);
|
||||
}
|
||||
|
||||
.button:focus:hover {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.button:focus:not(:hover) {
|
||||
outline: 1px solid var(--color-action-text);
|
||||
outline-offset: -4px;
|
||||
}
|
||||
|
||||
.button:active {
|
||||
transform: scale(.99);
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: 1.25rem 2rem;
|
||||
border: 0;
|
||||
border-radius: 1.75rem;
|
||||
line-height: 1;
|
||||
text-decoration: none;
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
|
||||
+ .btn {
|
||||
margin-left: 2rem;
|
||||
}
|
||||
|
||||
&.btn-primary {
|
||||
color: var(--color-dark);
|
||||
background-color: var(--color-primary);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
&.btn-secondary {
|
||||
color: var(--color-white);
|
||||
background-color: var(--color-secondary);
|
||||
}
|
||||
}
|
87
src/scss/components/_form.scss
Normal file
@ -0,0 +1,87 @@
|
||||
/* Form */
|
||||
|
||||
form {
|
||||
max-width: 35rem;
|
||||
}
|
||||
|
||||
form br {
|
||||
display: none;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
input,
|
||||
select {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
input,
|
||||
textarea,
|
||||
select {
|
||||
@include apply-utility('font', 'base');
|
||||
background-color: #fff;
|
||||
font: inherit;
|
||||
border: 1px solid var(--color-text);
|
||||
margin-top: .15rem;
|
||||
padding: .5rem 1rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
label input {
|
||||
margin: -.25rem .5rem 0 0;
|
||||
width: auto;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.field-list {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.field-list__field-group {
|
||||
margin-bottom: 2rem;
|
||||
transition: transform 150ms;
|
||||
|
||||
&__description {
|
||||
display: block;
|
||||
margin-top: .3rem;
|
||||
font-size: .875rem;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
textarea + &__description {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
&--confirm {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
&__list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
|
||||
label {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.field-list__field-group__description {
|
||||
margin: 0 0 0 1.35rem;
|
||||
}
|
||||
}
|
||||
}
|
11
src/scss/components/_heading-permalink.scss
Normal file
@ -0,0 +1,11 @@
|
||||
.heading-permalink {
|
||||
color: var(--color-theme-primary-glare);
|
||||
font-size: .8em;
|
||||
margin-left: .3em;
|
||||
margin-top: .2em;
|
||||
|
||||
@include media-query('md') {
|
||||
font-size: .6em;
|
||||
margin-top: .4em;
|
||||
}
|
||||
}
|
29
src/scss/components/_intro.scss
Normal file
@ -0,0 +1,29 @@
|
||||
.intro {
|
||||
// background: var(--color-theme-highlight-block);
|
||||
// padding: 8rem 0;
|
||||
margin-top: 8rem;
|
||||
|
||||
&__summary {
|
||||
--flow-space: #{get-size(500)};
|
||||
font-size: get-size(500);
|
||||
|
||||
a {
|
||||
color: currentColor;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__heading {
|
||||
max-width: 44rem;
|
||||
color: var(--color-text);
|
||||
margin-bottom: 4rem;
|
||||
font-size: 2.5rem;
|
||||
|
||||
&--compact {
|
||||
max-width: 20ex;
|
||||
}
|
||||
}
|
||||
}
|
33
src/scss/components/_nav.scss
Normal file
@ -0,0 +1,33 @@
|
||||
.nav {
|
||||
&__list {
|
||||
overflow-x: auto;
|
||||
|
||||
// Add padding and neg margin to allow focus style visibility
|
||||
padding: .5rem;
|
||||
margin: -.5rem;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
-ms-overflow-style: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
> * + * {
|
||||
margin-left: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__item {
|
||||
padding: get-size(300) 0;
|
||||
flex-shrink: 0;
|
||||
|
||||
a {
|
||||
@include apply-utility('weight', 'mid');
|
||||
color: currentColor;
|
||||
|
||||
&:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
93
src/scss/components/_news-list.scss
Normal file
@ -0,0 +1,93 @@
|
||||
.news-list {
|
||||
margin-top: 8rem;
|
||||
margin-bottom: 24rem;
|
||||
position: relative;
|
||||
|
||||
// > svg {
|
||||
// position: absolute;
|
||||
// z-index: -1;
|
||||
// top: -18rem;
|
||||
// }
|
||||
|
||||
.wrapper {
|
||||
// display: flex;
|
||||
// flex-direction: column;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&__inner {
|
||||
position: absolute;
|
||||
top: -9rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&__heading {
|
||||
margin-bottom: 3rem;
|
||||
color: var(--color-white);
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
&__items {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
grid-template-rows: 1fr;
|
||||
grid-column-gap: 1.5rem;
|
||||
grid-row-gap: 0;
|
||||
}
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--color-primary);
|
||||
color: var(--color-dark);
|
||||
border-radius: 1rem;
|
||||
|
||||
&-heading {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
padding: 4.5rem 1.5rem 4rem;
|
||||
}
|
||||
|
||||
&-date {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
background-color: var(--color-light-gray);
|
||||
border-radius: 0 0 1rem 1rem;
|
||||
padding: 1rem 1.5rem;
|
||||
margin-top: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&__link {
|
||||
&,
|
||||
&:visited {
|
||||
color: var(--color-dark);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
&__see-all {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
align-self: flex-end;
|
||||
margin-top: 3rem;
|
||||
padding: .875rem 1.5rem;
|
||||
|
||||
&,
|
||||
&:visited {
|
||||
color: var(--color-dark);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-primary);
|
||||
border-radius: 1.5rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
29
src/scss/components/_pagination.scss
Normal file
@ -0,0 +1,29 @@
|
||||
.pagination {
|
||||
flex-wrap: wrap;
|
||||
|
||||
a {
|
||||
color: var(--color-theme-primary);
|
||||
|
||||
&:not(:hover) {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
// Flip content if direction is backwards
|
||||
&[data-direction='backwards'] {
|
||||
flex-direction: row-reverse;
|
||||
|
||||
svg {
|
||||
transform: rotate(-180deg);
|
||||
}
|
||||
}
|
||||
|
||||
// If only child and forwards, push out to the right
|
||||
&[data-direction='forwards']:only-child {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
17
src/scss/components/_post-list.scss
Normal file
@ -0,0 +1,17 @@
|
||||
.post-list {
|
||||
&__item {
|
||||
--flow-space: #{get-size(700)};
|
||||
}
|
||||
|
||||
&__link {
|
||||
&,
|
||||
&:visited {
|
||||
color: var(--color-theme-primary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
137
src/scss/components/_post.scss
Normal file
@ -0,0 +1,137 @@
|
||||
.post {
|
||||
&__body {
|
||||
--flow-space: #{get-size(800)};
|
||||
|
||||
/**
|
||||
* Generic HTML styles
|
||||
*/
|
||||
h2 + *,
|
||||
h3 + * {
|
||||
--flow-space: #{get-size(500)};
|
||||
}
|
||||
|
||||
h2,
|
||||
h3 {
|
||||
@include apply-utility('leading', 'tight');
|
||||
position: relative;
|
||||
/*display: flex;*/
|
||||
}
|
||||
|
||||
a:not([class]) {
|
||||
@include apply-utility('leading', 'tight');
|
||||
color: var(--color-dark);
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
background: var(--color-theme-highlight);
|
||||
padding: .2rem .4rem .3rem;
|
||||
text-decoration: none;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
a:not([class]):hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 1.2em;
|
||||
color: var(--color-theme-primary);
|
||||
font-weight: 600;
|
||||
margin-left: .01ch;
|
||||
margin-right: .01ch;
|
||||
}
|
||||
|
||||
pre > code {
|
||||
margin-right: 0;
|
||||
border: 1px solid rgba(255, 255, 255, .1);
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: .4rem solid var(--color-theme-primary-glare);
|
||||
margin-left: 0;
|
||||
padding-left: get-size(500);
|
||||
font-style: italic;
|
||||
font-size: get-size(600);
|
||||
|
||||
p {
|
||||
opacity: .85;
|
||||
padding: get-size(500) 0;
|
||||
}
|
||||
}
|
||||
|
||||
ol:not([class]),
|
||||
ul:not([class]) {
|
||||
margin-left: get-size(800);
|
||||
|
||||
li + li {
|
||||
margin-top: get-size(300);
|
||||
}
|
||||
}
|
||||
|
||||
figure,
|
||||
figure + *,
|
||||
pre > code,
|
||||
.video-player,
|
||||
.video-player + *,
|
||||
video {
|
||||
--flow-space: #{get-size('max')};
|
||||
}
|
||||
|
||||
figure,
|
||||
pre > code,
|
||||
.video-player,
|
||||
video {
|
||||
width: 100vw;
|
||||
max-width: map-get($metrics, 'wrap-max-width');
|
||||
margin-left: 50%; /*changing this value to 47% removes the horizontal scrollbar once the viewport is < 930px */
|
||||
transform: translateX (-50%); /* changing this value to 49% allows for the suggestion above to also eliminate the horizontal scroll. */
|
||||
position: relative;
|
||||
}
|
||||
|
||||
figure img,
|
||||
pre > code,
|
||||
.video-player {
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, .15);
|
||||
}
|
||||
|
||||
figure img {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
figcaption {
|
||||
font-size: .8em;
|
||||
font-style: italic;
|
||||
max-width: map-get($metrics, 'wrap-inner-max-width');
|
||||
margin: .5rem auto 0;
|
||||
padding: 0 get-size(500);
|
||||
}
|
||||
|
||||
pre > code {
|
||||
display: block;
|
||||
background: var(--color-dark);
|
||||
padding: get-size(700);
|
||||
font-size: get-size(500);
|
||||
}
|
||||
}
|
||||
|
||||
&__footer {
|
||||
background: var(--color-theme-highlight);
|
||||
|
||||
h2 {
|
||||
flex-shrink: 0;
|
||||
margin-right: get-size('base');
|
||||
color: var(--color-dark);
|
||||
}
|
||||
|
||||
h2 a {
|
||||
@extend %visually-hidden;
|
||||
}
|
||||
|
||||
a {
|
||||
background: var(--color-bg);
|
||||
padding: .4rem .6rem;
|
||||
}
|
||||
}
|
||||
}
|
36
src/scss/components/_site-foot.scss
Normal file
@ -0,0 +1,36 @@
|
||||
.site-foot {
|
||||
background: var(--color-secondary);
|
||||
color: var(--color-white);
|
||||
|
||||
&__inner {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-template-rows: 1fr;
|
||||
grid-column-gap: 1.5rem;
|
||||
grid-row-gap: 0;
|
||||
padding: 3.5rem 0 2.5rem;
|
||||
font-weight: 300;
|
||||
|
||||
h3 {
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: currentColor;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__credit {
|
||||
// text-align: center;
|
||||
}
|
||||
}
|
10
src/scss/components/_site-head.scss
Normal file
@ -0,0 +1,10 @@
|
||||
.site-head {
|
||||
padding-top: 3rem;
|
||||
padding-bottom: 1.5rem;
|
||||
|
||||
&__site-name {
|
||||
font-weight: 700;
|
||||
text-decoration: none;
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
17
src/scss/components/_skip-link.scss
Normal file
@ -0,0 +1,17 @@
|
||||
// Hide skip link visually by default
|
||||
.skip-link:not(:focus) {
|
||||
@extend %visually-hidden;
|
||||
}
|
||||
|
||||
.skip-link:focus {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
padding: get-size(300) get-size(500) get-size('base') get-size(500);
|
||||
background-color: var(--color-action-bg);
|
||||
color: var(--color-action-text);
|
||||
line-height: 1;
|
||||
text-decoration: none;
|
||||
font-weight: 700;
|
||||
}
|
146
src/scss/components/_syntax-highlighting.scss
Normal file
@ -0,0 +1,146 @@
|
||||
/**
|
||||
* a11y-dark theme for JavaScript, CSS, and HTML
|
||||
* Based on the okaidia theme: https://github.com/PrismJS/prism/blob/gh-pages/themes/prism-okaidia.css
|
||||
* @author ericwbailey
|
||||
*/
|
||||
|
||||
code[class*='language-'],
|
||||
pre[class*='language-'] {
|
||||
color: #f8f8f2;
|
||||
background: none;
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
text-align: left;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
|
||||
-moz-tab-size: 2;
|
||||
-o-tab-size: 2;
|
||||
tab-size: 2;
|
||||
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
:not(pre) > code[class*='language-'] {
|
||||
padding: .1em;
|
||||
border-radius: .3em;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: #d4d0ab;
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: #fefefe;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.tag,
|
||||
.token.constant,
|
||||
.token.symbol,
|
||||
.token.deleted {
|
||||
color: #ffa07a;
|
||||
}
|
||||
|
||||
.token.boolean,
|
||||
.token.number {
|
||||
color: #00e0e0;
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.attr-name,
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.builtin,
|
||||
.token.inserted {
|
||||
color: #abe338;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url,
|
||||
.language-css .token.string,
|
||||
.style .token.string,
|
||||
.token.variable {
|
||||
color: #00e0e0;
|
||||
}
|
||||
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.function {
|
||||
color: #ffd700;
|
||||
}
|
||||
|
||||
.token.keyword {
|
||||
color: #00e0e0;
|
||||
}
|
||||
|
||||
.token.regex,
|
||||
.token.important {
|
||||
color: #ffd700;
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
@media screen and (-ms-high-contrast: active) {
|
||||
code[class*='language-'],
|
||||
pre[class*='language-'] {
|
||||
color: windowText;
|
||||
background: window;
|
||||
}
|
||||
|
||||
:not(pre) > code[class*='language-'],
|
||||
pre[class*='language-'] {
|
||||
background: window;
|
||||
}
|
||||
|
||||
.token.important {
|
||||
background: highlight;
|
||||
color: window;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.function,
|
||||
.token.keyword,
|
||||
.token.operator,
|
||||
.token.selector {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.token.attr-value,
|
||||
.token.comment,
|
||||
.token.doctype,
|
||||
.token.function,
|
||||
.token.keyword,
|
||||
.token.operator,
|
||||
.token.property,
|
||||
.token.string {
|
||||
color: highlight;
|
||||
}
|
||||
|
||||
.token.attr-value,
|
||||
.token.url {
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
23
src/scss/components/_video-player.scss
Normal file
@ -0,0 +1,23 @@
|
||||
.video-player {
|
||||
position: relative;
|
||||
padding-top: 56.25%;
|
||||
|
||||
@include media-query('md') {
|
||||
.post & {
|
||||
padding-top: 66%;
|
||||
}
|
||||
}
|
||||
|
||||
> iframe {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
// If it’s bleeding out in a post, it needs more padding
|
||||
.post & {
|
||||
padding-top: 63%;
|
||||
}
|
||||
}
|
54
src/scss/components/presentation.scss
Normal file
@ -0,0 +1,54 @@
|
||||
.presentation {
|
||||
article {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
grid-template-rows: auto;
|
||||
grid-column-gap: 2.125rem;
|
||||
grid-row-gap: 2.125rem;
|
||||
|
||||
&:nth-child(2n + 1) {
|
||||
.content {
|
||||
grid-column: 1;
|
||||
}
|
||||
|
||||
figure {
|
||||
grid-column: 2;
|
||||
}
|
||||
}
|
||||
|
||||
&:nth-child(2n) {
|
||||
.content {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
figure {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
h2 {
|
||||
font-size: 2.125rem;
|
||||
margin-bottom: 2rem;
|
||||
|
||||
+ p {
|
||||
font-size: 1.25rem;
|
||||
margin-bottom: 8rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
figure {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
// justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
margin-top: 3.5rem;
|
||||
}
|
||||
}
|
||||
}
|
79
src/scss/global.scss
Normal file
@ -0,0 +1,79 @@
|
||||
@import 'config';
|
||||
|
||||
// Pull in the stalfos lib
|
||||
@import '../../node_modules/stalfos/stalfos';
|
||||
|
||||
// Local mixins
|
||||
@import 'mixins/dark-mode';
|
||||
|
||||
@import 'theme';
|
||||
|
||||
// Local dependencies
|
||||
@import 'typography';
|
||||
|
||||
/**
|
||||
* GLOBAL STYLES
|
||||
*/
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
scroll-behavior: smooth;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1 0 auto;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
display: block;
|
||||
height: 1px;
|
||||
max-width: 500px;
|
||||
background: var(--color-stroke);
|
||||
border: 0;
|
||||
margin: get-size(900) auto;
|
||||
}
|
||||
|
||||
// For when metric attributes are added to img elements
|
||||
img {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
:focus {
|
||||
outline: 1px solid var(--color-theme-primary-glare);
|
||||
outline-offset: 0.25rem;
|
||||
}
|
||||
|
||||
/**
|
||||
* PROJECT IMPORTS
|
||||
*/
|
||||
|
||||
// Utils
|
||||
@import 'utilities/inner-wrapper';
|
||||
@import 'utilities/visually-hidden';
|
||||
@import 'utilities/wrapper';
|
||||
|
||||
// Components
|
||||
@import 'components/button';
|
||||
@import 'components/form';
|
||||
@import 'components/heading-permalink';
|
||||
@import 'components/intro';
|
||||
@import 'components/nav';
|
||||
@import 'components/pagination';
|
||||
@import 'components/post';
|
||||
@import 'components/post-list';
|
||||
@import 'components/news-list';
|
||||
@import 'components/presentation';
|
||||
@import 'components/site-head';
|
||||
@import 'components/site-foot';
|
||||
@import 'components/skip-link';
|
||||
@import 'components/syntax-highlighting';
|
||||
@import 'components/video-player';
|
21
src/scss/mixins/_dark-mode.scss
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* DARK MODE MIXIN
|
||||
*
|
||||
* A little wrapper that lets you define your dark mode custom
|
||||
* properties in a way that supports the theme toggle web component
|
||||
*/
|
||||
@mixin dark-mode() {
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--color-mode: 'dark';
|
||||
}
|
||||
|
||||
:root:not([data-user-color-scheme]) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
[data-user-color-scheme='dark'] {
|
||||
@content;
|
||||
}
|
||||
}
|
7
src/scss/utilities/_inner-wrapper.scss
Normal file
@ -0,0 +1,7 @@
|
||||
.inner-wrapper {
|
||||
max-width: map-get($metrics, 'wrap-inner-max-width');
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding-left: get-size(500);
|
||||
padding-right: get-size(500);
|
||||
}
|
12
src/scss/utilities/_visually-hidden.scss
Normal file
@ -0,0 +1,12 @@
|
||||
%visually-hidden,
|
||||
.visually-hidden {
|
||||
border: 0;
|
||||
clip: rect(0 0 0 0);
|
||||
height: auto;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
white-space: nowrap;
|
||||
}
|
7
src/scss/utilities/_wrapper.scss
Normal file
@ -0,0 +1,7 @@
|
||||
.wrapper {
|
||||
max-width: map-get($metrics, 'wrap-max-width');
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding-left: get-size(500);
|
||||
padding-right: get-size(500);
|
||||
}
|
5
src/service-worker.njk
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
permalink: '/service-worker.js'
|
||||
---
|
||||
const VERSION = '{{ global.random() }}';
|
||||
{% include "partials/global/service-worker.js" %}
|
149
src/styleguide.njk
Normal file
@ -0,0 +1,149 @@
|
||||
---
|
||||
title: 'Styleguide'
|
||||
permalink: /styleguide/
|
||||
---
|
||||
|
||||
{% extends 'layouts/base.njk' %}
|
||||
|
||||
{# Intro content #}
|
||||
{% set introHeading = title %}
|
||||
|
||||
{% block head %}
|
||||
<style>
|
||||
.swatches {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(17.5rem, 1fr));
|
||||
grid-gap: 2.5rem 2rem;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.swatches > * {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
.props dt {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.props dd + dt {
|
||||
margin-top: 0.5rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.color {
|
||||
border: 1px solid var(--color-mid);
|
||||
}
|
||||
|
||||
.bg-dark code {
|
||||
color: var(--color-light);
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<main id="main-content" tabindex="-1">
|
||||
{% include "partials/components/intro.njk" %}
|
||||
<article class="[ inner-wrapper ] [ post ]">
|
||||
<section class="[ post__body ] [ sf-flow ] [ pad-top-900 pad-bottom-900 ]">
|
||||
<h2>Colours</h2>
|
||||
<p class="visually-hidden">Colour swatches with various values that you can copy.</p>
|
||||
<ul class="swatches">
|
||||
{% for color in styleguide.colors() %}
|
||||
<li class="sf-flow">
|
||||
<div class="[ color ] [ pad-top-900 pad-bottom-900 ]" style="background: {{ color.value }}"></div>
|
||||
<h3 class="font-base text-500">{{ color.key }}</h3>
|
||||
<dl class="props">
|
||||
<dt>Value</dt>
|
||||
<dd><code>{{ color.value }}</code></dd>
|
||||
<dt>Sass function</dt>
|
||||
<dd><code>get-color('{{ color.key }}')</code></dd>
|
||||
<dt>Custom Property</dt>
|
||||
<dd><code>var(--color-{{ color.key }})</code></dd>
|
||||
<dt>Text util class</dt>
|
||||
<dd><code>color-{{ color.key }}</code></dd>
|
||||
<dt>Background util class</dt>
|
||||
<dd><code>bg-{{ color.key }}</code></dd>
|
||||
</dl>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<h2>Fonts</h2>
|
||||
<h3>Base — System stack</h3>
|
||||
<p class="sf-flow">
|
||||
<span aria-hidden="true" role="presentation" class="font-base text-600 md:text-800 box-block leading-tight">The quick brown fox jumps over the lazy fox</span>
|
||||
<code>.font-base</code>
|
||||
</p>
|
||||
<h3>Serif — Lora</h3>
|
||||
<p class="sf-flow">
|
||||
<span aria-hidden="true" role="presentation" class="font-serif text-600 md:text-800 box-block leading-tight">The quick brown fox jumps over the lazy fox</span>
|
||||
<code>.font-serif</code>
|
||||
</p>
|
||||
<h2>Text sizes</h2>
|
||||
<p>Text sizes are available as standard classes or media query prefixed, such as <code>lg:text-500</code>.</p>
|
||||
{% for size in styleguide.sizes() %}
|
||||
<p class="text-{{ size.key }}" style="--flow-space: 1.5rem">{{ size.value }} - <code>text-{{ size.key }}</code></p>
|
||||
{% endfor %}
|
||||
<h2>Spacing</h2>
|
||||
<p>There’s size ratio utilities that give you margin (<code>gap-top, gap-bottom</code>) and padding (<code>pad-top, pad-left, pad-bottom</code>).
|
||||
<h3>Margin</h3>
|
||||
<p class="visually-hidden">Margin token classes that you can copy</p>
|
||||
<div>
|
||||
{% for size in styleguide.sizes() %}
|
||||
<div class="[ bg-dark color-light pad-top-base pad-bottom-base pad-left-base md:width-half ] [ gap-top-{{ size.key }} ]">
|
||||
<code>gap-top-{{ size.key }}</code>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<h3>Padding</h3>
|
||||
<p class="visually-hidden">Padding token classes that you can copy</p>
|
||||
{% for size in styleguide.sizes() %}
|
||||
<div class="[ bg-dark pad-left-base color-light md:width-half ] [ pad-top-{{ size.key }} ]">
|
||||
<code>pad-top-{{ size.key }}</code>
|
||||
</div>
|
||||
<div class="[ bg-dark pad-left-base color-light md:width-half ] [ pad-bottom-{{ size.key }} ]">
|
||||
<code>pad-bottom-{{ size.key }}</code>
|
||||
</div>
|
||||
<div class="[ bg-dark color-light md:width-half ] [ pad-left-{{ size.key }} ]">
|
||||
<code>pad-left-{{ size.key }}</code>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<h2>Forms</h2>
|
||||
{% from "macros/form.njk" import label, field, textarea, confirm, select, radios, checkboxes, hidden_field, button %}
|
||||
<form>
|
||||
<ol class="[ field-list ]">
|
||||
<li class="[ field-list__field-group ]">
|
||||
{{ label("Text Label", "field-text-name") }}
|
||||
{{ field( "text", "field-text-name", { required: true, placeholder: "Katherine Johnson", autocomplete: "name", autocorrect: "off", autocapitalize: "off", description: "Optional description. Note: This field type can take any valid input type." } ) }}
|
||||
</li>
|
||||
<li class="[ field-list__field-group ]">
|
||||
{{ label("Email Label", "field-email-name") }}
|
||||
{{ field( "email", "field-email-name", { required: true, placeholder: "katherine@johnson.tld", autocomplete: "email" } ) }}
|
||||
</li>
|
||||
<li class="[ field-list__field-group ]">
|
||||
{{ label("Textarea Label", "field-textarea-name") }}
|
||||
{{ textarea( "field-textarea-name", { required: true, autocapitalize: "sentences", spellcheck: "true" } ) }}
|
||||
</li>
|
||||
<li class="[ field-list__field-group ]">
|
||||
{{ label("Select Label", "field-select-name") }}
|
||||
{{ select( "select", [ "option 1", {label: "Option II", value: "option 2"} ], { required: true, options_before: ["prepended option"], options_after: ["appended option"], description: "Optional description." } ) }}
|
||||
</li>
|
||||
<li class="[ field-list__field-group field-list__field-group--confirm ]">
|
||||
{{ confirm("Confirm this statement", "confirm") }}
|
||||
</li>
|
||||
<li class="[ field-list__field-group ]">
|
||||
{{ radios("Radio Legend", "field-radio-name", [ "Yes", { label: "Not really", value: "No", note: "Note about choice." } ], { description: "Optional description." } ) }}
|
||||
</li>
|
||||
<li class="[ field-list__field-group ]">
|
||||
{{ checkboxes("Checkbox Legend", "field-checkbox-name", [ "Choice 1", { label: "Choice 2", value: "Choice 2", note: "Note about choice." }, "Choice 3" ], { description: "Optional description." } ) }}
|
||||
</li>
|
||||
<li class="[ field-list__field-group ]">
|
||||
There is a hidden field here…
|
||||
{{ hidden_field("hidden-name", "hidden-value") }}
|
||||
</li>
|
||||
</ol>
|
||||
{{ button("Button Text") }}
|
||||
</form>
|
||||
</section>
|
||||
</article>
|
||||
</main>
|
||||
{% endblock %}
|
35
src/tags.njk
Normal file
@ -0,0 +1,35 @@
|
||||
---
|
||||
title: Tag Archive
|
||||
pagination:
|
||||
data: collections
|
||||
size: 1
|
||||
alias: tag
|
||||
filter:
|
||||
- all
|
||||
- nav
|
||||
- post
|
||||
- posts
|
||||
- tagList
|
||||
- postFeed
|
||||
addAllPagesToCollections: true
|
||||
permalink: /tags/{{ tag }}/
|
||||
---
|
||||
|
||||
{% extends 'layouts/base.njk' %}
|
||||
|
||||
{# Intro content #}
|
||||
{% set introHeading %}Posts filed under “{{ tag }}”{% endset %}
|
||||
{% set introHeadingLevel = '2' %}
|
||||
|
||||
{# Post list content #}
|
||||
{% set postListHeadingLevel = '2' %}
|
||||
{% set postListHeading = 'Posts' %}
|
||||
{% set postListItems = collections[tag] %}
|
||||
|
||||
{% block content %}
|
||||
<main id="main-content" tabindex="-1">
|
||||
{% include "partials/components/intro.njk" %}
|
||||
{% include "partials/components/post-list.njk" %}
|
||||
{% include "partials/components/pagination.njk" %}
|
||||
</main>
|
||||
{% endblock %}
|
14
src/transforms/html-min-transform.js
Normal file
@ -0,0 +1,14 @@
|
||||
const htmlmin = require('html-minifier');
|
||||
|
||||
module.exports = function htmlMinTransform(value, outputPath) {
|
||||
if (outputPath.indexOf('.html') > -1) {
|
||||
let minified = htmlmin.minify(value, {
|
||||
useShortDoctype: true,
|
||||
removeComments: true,
|
||||
collapseWhitespace: true,
|
||||
minifyCSS: true
|
||||
});
|
||||
return minified;
|
||||
}
|
||||
return value;
|
||||
};
|
95
src/transforms/parse-transform.js
Normal file
@ -0,0 +1,95 @@
|
||||
const jsdom = require('@tbranyen/jsdom');
|
||||
const {JSDOM} = jsdom;
|
||||
const minify = require('../utils/minify.js');
|
||||
const slugify = require('slugify');
|
||||
const getSize = require('image-size');
|
||||
|
||||
module.exports = function(value, outputPath) {
|
||||
if (outputPath.endsWith('.html')) {
|
||||
const DOM = new JSDOM(value, {
|
||||
resources: 'usable'
|
||||
});
|
||||
|
||||
const document = DOM.window.document;
|
||||
const articleImages = [...document.querySelectorAll('main article img, .intro img')];
|
||||
const articleHeadings = [
|
||||
...document.querySelectorAll('main article h2, main article h3')
|
||||
];
|
||||
const articleEmbeds = [...document.querySelectorAll('main article iframe')];
|
||||
|
||||
if (articleImages.length) {
|
||||
articleImages.forEach(image => {
|
||||
image.setAttribute('loading', 'lazy');
|
||||
|
||||
const file = image.getAttribute('src');
|
||||
|
||||
// if preprod env TODO remove when prod
|
||||
if(this.config.pathPrefix.localeCompare('/')) { // pathPrefix is different from '/'
|
||||
// if(process.env.ELEVENTY_ENV == "preprod") { // not working
|
||||
image.setAttribute('src', this.config.pathPrefix + file);
|
||||
}
|
||||
|
||||
if (file.indexOf('http') < 0) {
|
||||
const dimensions = getSize('src' + file);
|
||||
|
||||
image.setAttribute('width', dimensions.width);
|
||||
image.setAttribute('height', dimensions.height);;
|
||||
}
|
||||
|
||||
// Replace p tags by figure tag for img
|
||||
const figure = document.createElement('figure');
|
||||
|
||||
image.removeAttribute('title');
|
||||
figure.appendChild(image.cloneNode(true));
|
||||
|
||||
// If an image has a title it means that the user added a caption
|
||||
// so replace the image with a figure containing that image and a caption
|
||||
if (image.hasAttribute('title')) {
|
||||
const figCaption = document.createElement('figcaption');
|
||||
|
||||
figCaption.innerHTML = image.getAttribute('title');
|
||||
figure.appendChild(figCaption);
|
||||
}
|
||||
|
||||
image.parentNode.replaceWith(figure);
|
||||
});
|
||||
}
|
||||
|
||||
if (articleHeadings.length) {
|
||||
// Loop each heading and add a little anchor and an ID to each one
|
||||
articleHeadings.forEach(heading => {
|
||||
const headingSlug = slugify(heading.textContent.toLowerCase());
|
||||
const anchor = document.createElement('a');
|
||||
|
||||
anchor.setAttribute('href', `#heading-${headingSlug}`);
|
||||
anchor.classList.add('heading-permalink');
|
||||
anchor.innerHTML = minify(`
|
||||
<span class="visually-hidden"> permalink</span>
|
||||
<svg fill="currentColor" aria-hidden="true" focusable="false" width="1em" height="1em" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M9.199 13.599a5.99 5.99 0 0 0 3.949 2.345 5.987 5.987 0 0 0 5.105-1.702l2.995-2.994a5.992 5.992 0 0 0 1.695-4.285 5.976 5.976 0 0 0-1.831-4.211 5.99 5.99 0 0 0-6.431-1.242 6.003 6.003 0 0 0-1.905 1.24l-1.731 1.721a.999.999 0 1 0 1.41 1.418l1.709-1.699a3.985 3.985 0 0 1 2.761-1.123 3.975 3.975 0 0 1 2.799 1.122 3.997 3.997 0 0 1 .111 5.644l-3.005 3.006a3.982 3.982 0 0 1-3.395 1.126 3.987 3.987 0 0 1-2.632-1.563A1 1 0 0 0 9.201 13.6zm5.602-3.198a5.99 5.99 0 0 0-3.949-2.345 5.987 5.987 0 0 0-5.105 1.702l-2.995 2.994a5.992 5.992 0 0 0-1.695 4.285 5.976 5.976 0 0 0 1.831 4.211 5.99 5.99 0 0 0 6.431 1.242 6.003 6.003 0 0 0 1.905-1.24l1.723-1.723a.999.999 0 1 0-1.414-1.414L9.836 19.81a3.985 3.985 0 0 1-2.761 1.123 3.975 3.975 0 0 1-2.799-1.122 3.997 3.997 0 0 1-.111-5.644l3.005-3.006a3.982 3.982 0 0 1 3.395-1.126 3.987 3.987 0 0 1 2.632 1.563 1 1 0 0 0 1.602-1.198z"/>
|
||||
</svg>`);
|
||||
|
||||
heading.setAttribute('id', `heading-${headingSlug}`);
|
||||
heading.appendChild(anchor);
|
||||
});
|
||||
}
|
||||
|
||||
// Look for videos are wrap them in a container element
|
||||
if (articleEmbeds.length) {
|
||||
articleEmbeds.forEach(embed => {
|
||||
if (embed.hasAttribute('allowfullscreen')) {
|
||||
const player = document.createElement('div');
|
||||
|
||||
player.classList.add('video-player');
|
||||
|
||||
player.appendChild(embed.cloneNode(true));
|
||||
|
||||
embed.replaceWith(player);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return '<!DOCTYPE html>\r\n' + document.documentElement.outerHTML;
|
||||
}
|
||||
return value;
|
||||
};
|
3
src/utils/minify.js
Normal file
@ -0,0 +1,3 @@
|
||||
module.exports = function minify(input) {
|
||||
return input.replace(/\s{2,}/g, '').replace(/\'/g, '"');
|
||||
};
|