@lullabot/eleventy-decision-records 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +65 -0
- package/package.json +94 -0
- package/src/YYYYMMDD-decision-template.md +41 -0
- package/src/about.md +36 -0
- package/src/assets/fonts/OFL.txt +93 -0
- package/src/assets/fonts/gabarito-v9-latin-500.woff2 +0 -0
- package/src/assets/fonts/gabarito-v9-latin-700.woff2 +0 -0
- package/src/assets/fonts/gabarito-v9-latin-regular.woff2 +0 -0
- package/src/assets/icons/facebook.svg +1 -0
- package/src/assets/icons/github.svg +1 -0
- package/src/assets/icons/linkedin.svg +1 -0
- package/src/assets/icons/mastodon.svg +1 -0
- package/src/assets/icons/x.svg +1 -0
- package/src/assets/icons/youtube.svg +1 -0
- package/src/assets/images/horizontal.jpg +0 -0
- package/src/assets/images/vertical.jpg +0 -0
- package/src/assets/js/scrollable.js +49 -0
- package/src/assets/js/search.js +189 -0
- package/src/assets/js/time-since.js +31 -0
- package/src/assets/styles/content.css +149 -0
- package/src/assets/styles/fonts.css +33 -0
- package/src/assets/styles/footer.css +65 -0
- package/src/assets/styles/layout.css +483 -0
- package/src/assets/styles/pages.css +348 -0
- package/src/assets/styles/recent-decisions.css +104 -0
- package/src/assets/styles/reset.css +107 -0
- package/src/assets/styles/search-dialog.css +196 -0
- package/src/assets/styles/tags.css +16 -0
- package/src/assets/styles/tokens.css +181 -0
- package/src/assets/styles/typography.css +182 -0
- package/src/assets/styles/view-transitions.css +67 -0
- package/src/config.js +38 -0
- package/src/defaultConfig.json +99 -0
- package/src/index.js +227 -0
- package/src/index.md +28 -0
- package/src/lib/collections/adrs.js +10 -0
- package/src/lib/collections/contributors.js +15 -0
- package/src/lib/collections/index.js +14 -0
- package/src/lib/collections/topics.js +18 -0
- package/src/lib/data/index.js +19 -0
- package/src/lib/filters/collections.js +63 -0
- package/src/lib/filters/dates.js +68 -0
- package/src/lib/filters/index.js +26 -0
- package/src/lib/filters/strings.js +23 -0
- package/src/lib/plugins/bundle.js +23 -0
- package/src/lib/plugins/index.js +16 -0
- package/src/lib/plugins/markdown.js +10 -0
- package/src/lib/plugins/rss.js +24 -0
- package/src/lib/plugins/syntax-highlight.js +8 -0
- package/src/lib/plugins/toc.js +11 -0
- package/src/lib/shortcodes/icons.js +91 -0
- package/src/lib/shortcodes/index.js +11 -0
- package/src/lib/shortcodes/search.js +32 -0
- package/src/templates/assets/favicon.njk +7 -0
- package/src/templates/assets/search_index.njk +13 -0
- package/src/templates/layouts/adr.njk +59 -0
- package/src/templates/layouts/page.njk +136 -0
- package/src/templates/pages/contributor.njk +51 -0
- package/src/templates/pages/contributors.njk +46 -0
- package/src/templates/pages/decisions.njk +41 -0
- package/src/templates/pages/practice-areas.njk +48 -0
- package/src/templates/pages/topics.njk +50 -0
- package/src/templates/partials/footer.njk +12 -0
- package/src/templates/partials/recent-decisions.njk +33 -0
- package/src/templates/partials/site-nav.njk +61 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { feedPlugin } from '@11ty/eleventy-plugin-rss';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Register the Atom feed plugin, served at /feed.xml.
|
|
5
|
+
*/
|
|
6
|
+
export function rss(eleventyConfig, site) {
|
|
7
|
+
eleventyConfig.addPlugin(feedPlugin, {
|
|
8
|
+
type: 'atom',
|
|
9
|
+
outputPath: '/feed.xml',
|
|
10
|
+
collection: {
|
|
11
|
+
name: 'adrs',
|
|
12
|
+
limit: 0,
|
|
13
|
+
},
|
|
14
|
+
metadata: {
|
|
15
|
+
language: 'en',
|
|
16
|
+
title: `${site.title} — ${site.organization}`,
|
|
17
|
+
subtitle: site.description,
|
|
18
|
+
base: site.url,
|
|
19
|
+
author: {
|
|
20
|
+
name: site.organization,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { parse } from 'node-html-parser';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Resolves an icon name to a file path by checking each directory in
|
|
7
|
+
* order. The plugin assembles the list as: project icon overrides,
|
|
8
|
+
* then the theme's custom icons, then Material Symbols.
|
|
9
|
+
*/
|
|
10
|
+
function resolveIcon(iconDirs, name) {
|
|
11
|
+
for (const dir of iconDirs) {
|
|
12
|
+
const path = join(dir, `${name}.svg`);
|
|
13
|
+
if (existsSync(path)) {
|
|
14
|
+
return path;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
throw new Error(`Icon "${name}" not found in: ${iconDirs.join(', ')}`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Returns a standalone SVG suitable for use as a favicon.
|
|
22
|
+
* Embeds a <style> block so the icon adapts to light/dark mode.
|
|
23
|
+
*
|
|
24
|
+
* Usage: {% favicon "brand_family-fill" %}
|
|
25
|
+
*/
|
|
26
|
+
export function makeFavicon(iconDirs) {
|
|
27
|
+
return function favicon(name) {
|
|
28
|
+
const file = resolveIcon(iconDirs, name);
|
|
29
|
+
const root = parse(readFileSync(file, 'utf8'));
|
|
30
|
+
const svg = root.querySelector('svg');
|
|
31
|
+
|
|
32
|
+
svg.removeAttribute('width');
|
|
33
|
+
svg.removeAttribute('height');
|
|
34
|
+
|
|
35
|
+
const style = parse(`
|
|
36
|
+
<style>
|
|
37
|
+
svg {
|
|
38
|
+
background-color: #fff;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
path {
|
|
42
|
+
fill: #01070b;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@media(prefers-color-scheme:dark) {
|
|
46
|
+
svg {
|
|
47
|
+
background-color: #01070b;
|
|
48
|
+
}
|
|
49
|
+
path {
|
|
50
|
+
fill: #fff;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
</style>
|
|
54
|
+
`);
|
|
55
|
+
svg.insertAdjacentHTML('afterbegin', style.toString());
|
|
56
|
+
|
|
57
|
+
return root.toString();
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Inlines an SVG icon. Checks the project's icon directory first, then
|
|
63
|
+
* the theme's custom SVGs, then falls back to Material Symbols.
|
|
64
|
+
*
|
|
65
|
+
* Usage: {% icon "home-fill" %}
|
|
66
|
+
* {% icon "github", "GitHub" %}
|
|
67
|
+
*/
|
|
68
|
+
export function makeIcon(iconDirs) {
|
|
69
|
+
return function icon(name, label) {
|
|
70
|
+
const file = resolveIcon(iconDirs, name);
|
|
71
|
+
const root = parse(readFileSync(file, 'utf8'));
|
|
72
|
+
const svg = root.querySelector('svg');
|
|
73
|
+
|
|
74
|
+
svg.removeAttribute('width');
|
|
75
|
+
svg.removeAttribute('height');
|
|
76
|
+
svg.setAttribute('fill', 'currentColor');
|
|
77
|
+
|
|
78
|
+
for (const el of svg.querySelectorAll('[fill]')) {
|
|
79
|
+
el.setAttribute('fill', 'currentColor');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (label) {
|
|
83
|
+
svg.setAttribute('role', 'img');
|
|
84
|
+
svg.setAttribute('aria-label', label);
|
|
85
|
+
} else {
|
|
86
|
+
svg.setAttribute('aria-hidden', 'true');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return root.toString();
|
|
90
|
+
};
|
|
91
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { makeFavicon, makeIcon } from './icons.js';
|
|
2
|
+
import { oramaIndex } from './search.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Register all custom shortcodes with Eleventy.
|
|
6
|
+
*/
|
|
7
|
+
export default function (eleventyConfig, opts) {
|
|
8
|
+
eleventyConfig.addShortcode('favicon', makeFavicon(opts.iconDirs));
|
|
9
|
+
eleventyConfig.addShortcode('icon', makeIcon(opts.iconDirs));
|
|
10
|
+
eleventyConfig.addShortcode('oramaIndex', oramaIndex);
|
|
11
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { create, insert, save } from '@orama/orama';
|
|
2
|
+
import striptags from 'striptags';
|
|
3
|
+
import he from 'he';
|
|
4
|
+
import { isoDate } from '../filters/dates.js';
|
|
5
|
+
|
|
6
|
+
const SCHEMA = {
|
|
7
|
+
url: 'string',
|
|
8
|
+
title: 'string',
|
|
9
|
+
topics: 'string[]',
|
|
10
|
+
context: 'string',
|
|
11
|
+
content: 'string',
|
|
12
|
+
status: 'string',
|
|
13
|
+
date: 'string',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export async function oramaIndex(adrs) {
|
|
17
|
+
const db = create({ schema: SCHEMA });
|
|
18
|
+
|
|
19
|
+
for (const adr of adrs) {
|
|
20
|
+
insert(db, {
|
|
21
|
+
url: adr.page.url,
|
|
22
|
+
title: adr.data.title,
|
|
23
|
+
topics: adr.data.topics || [],
|
|
24
|
+
context: adr.data.context || '',
|
|
25
|
+
content: he.decode(striptags(adr.content)),
|
|
26
|
+
status: adr.data.status,
|
|
27
|
+
date: isoDate(adr.data.date),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return JSON.stringify(save(db));
|
|
32
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: false
|
|
3
|
+
permalink: /searchindex.json
|
|
4
|
+
eleventyExcludeFromCollections: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
{% filter spaceless %}
|
|
8
|
+
{% set rendercache %}
|
|
9
|
+
{% for adr in collections.adrs %}
|
|
10
|
+
{{ adr.content }}
|
|
11
|
+
{% endfor %}
|
|
12
|
+
{% endset %}
|
|
13
|
+
{% endfilter %}{% oramaIndex collections.adrs %}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: page.njk
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
<h1>{{ title }}</h1>
|
|
6
|
+
|
|
7
|
+
<dl class="adr-detail-meta">
|
|
8
|
+
<dt>Date</dt>
|
|
9
|
+
<dd>
|
|
10
|
+
<time datetime="{{ date | isoDate }}">{{ date | datetimeFormat }}</time>
|
|
11
|
+
</dd>
|
|
12
|
+
|
|
13
|
+
{%- if practiceArea %}
|
|
14
|
+
<dt>Practice Area</dt>
|
|
15
|
+
<dd>
|
|
16
|
+
<a href="/practice-areas/{{ practiceArea | slugify }}/"
|
|
17
|
+
>{{ practiceArea }}</a
|
|
18
|
+
>
|
|
19
|
+
</dd>
|
|
20
|
+
{%- endif %}
|
|
21
|
+
|
|
22
|
+
{%- if contributors | length %}
|
|
23
|
+
<dt>Contributors</dt>
|
|
24
|
+
<dd>
|
|
25
|
+
{%- for contributor in contributors %}
|
|
26
|
+
<a href="/contributors/{{ contributor | slugify }}/"
|
|
27
|
+
>{{ contributor }}</a
|
|
28
|
+
>{% if not loop.last %},{% endif %}
|
|
29
|
+
{%- endfor %}
|
|
30
|
+
</dd>
|
|
31
|
+
{%- endif %}
|
|
32
|
+
|
|
33
|
+
{%- if topics | length %}
|
|
34
|
+
<dt>Topics</dt>
|
|
35
|
+
<dd class="adr-detail-topics">
|
|
36
|
+
{%- for topic in topics %}
|
|
37
|
+
<a class="tag" href="/topics/{{ topic | lower | slugify }}/"
|
|
38
|
+
>{{ topic }}</a
|
|
39
|
+
>
|
|
40
|
+
{%- endfor %}
|
|
41
|
+
</dd>
|
|
42
|
+
{%- endif %}
|
|
43
|
+
</dl>
|
|
44
|
+
|
|
45
|
+
{%- set tocList = content | toc %}
|
|
46
|
+
<div class="adr-body{{ ' has-toc' if tocList }}">
|
|
47
|
+
{%- if tocList %}
|
|
48
|
+
<div class="toc-wrapper">
|
|
49
|
+
<nav class="toc" aria-labelledby="toc-heading">
|
|
50
|
+
<h2 id="toc-heading">On this page:</h2>
|
|
51
|
+
{{ tocList | safe }}
|
|
52
|
+
</nav>
|
|
53
|
+
</div>
|
|
54
|
+
{%- endif %}
|
|
55
|
+
|
|
56
|
+
<div class="adr-content">{{ content | scrollable | safe }}</div>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<script type="module" src="/js/scrollable.js"></script>
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>{{ title or site.title }} — {{ site.organization }}</title>
|
|
7
|
+
<meta name="description" content="{{ description or site.description }}" />
|
|
8
|
+
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
|
9
|
+
<style>
|
|
10
|
+
@view-transition {
|
|
11
|
+
navigation: auto;
|
|
12
|
+
}
|
|
13
|
+
</style>
|
|
14
|
+
<link
|
|
15
|
+
rel="preload"
|
|
16
|
+
href="/fonts/gabarito-v9-latin-regular.woff2"
|
|
17
|
+
as="font"
|
|
18
|
+
type="font/woff2"
|
|
19
|
+
crossorigin
|
|
20
|
+
/>
|
|
21
|
+
<link
|
|
22
|
+
rel="preload"
|
|
23
|
+
href="/fonts/gabarito-v9-latin-500.woff2"
|
|
24
|
+
as="font"
|
|
25
|
+
type="font/woff2"
|
|
26
|
+
crossorigin
|
|
27
|
+
/>
|
|
28
|
+
<link
|
|
29
|
+
rel="preload"
|
|
30
|
+
href="/fonts/gabarito-v9-latin-700.woff2"
|
|
31
|
+
as="font"
|
|
32
|
+
type="font/woff2"
|
|
33
|
+
crossorigin
|
|
34
|
+
/>
|
|
35
|
+
{% css %}{% include "styles/reset.css" %}{% endcss %}
|
|
36
|
+
{% css %}{% include "styles/fonts.css" %}{% endcss %}
|
|
37
|
+
{% css %}{% include "styles/tokens.css" %}{% endcss %}
|
|
38
|
+
{% css %}{% include "styles/typography.css" %}{% endcss %}
|
|
39
|
+
{% css %}{% include "styles/layout.css" %}{% endcss %}
|
|
40
|
+
{% css %}{% include "styles/recent-decisions.css" %}{% endcss %}
|
|
41
|
+
{% css %}{% include "styles/search-dialog.css" %}{% endcss %}
|
|
42
|
+
{% css %}{% include "styles/footer.css" %}{% endcss %}
|
|
43
|
+
{% css %}{% include "styles/pages.css" %}{% endcss %}
|
|
44
|
+
{% css %}{% include "styles/content.css" %}{% endcss %}
|
|
45
|
+
{% css %}{% include "styles/tags.css" %}{% endcss %}
|
|
46
|
+
{% css %}{% include "styles/view-transitions.css" %}{% endcss %}
|
|
47
|
+
<link rel="stylesheet" href="{% getBundleFileUrl 'css' %}" />
|
|
48
|
+
</head>
|
|
49
|
+
<body>
|
|
50
|
+
<a href="#site-main" class="skip-link">Skip to main content</a>
|
|
51
|
+
|
|
52
|
+
{% include "site-nav.njk" %}
|
|
53
|
+
|
|
54
|
+
<main class="site-main" id="site-main">{{ content | safe }}</main>
|
|
55
|
+
|
|
56
|
+
{% include "recent-decisions.njk" %}
|
|
57
|
+
{% include "footer.njk" %}
|
|
58
|
+
|
|
59
|
+
<dialog id="search-dialog" aria-labelledby="search-dialog-title">
|
|
60
|
+
<header>
|
|
61
|
+
<h2 id="search-dialog-title">Search Decisions</h2>
|
|
62
|
+
<form method="dialog">
|
|
63
|
+
<button aria-label="Close search">{% icon "close" %}</button>
|
|
64
|
+
</form>
|
|
65
|
+
</header>
|
|
66
|
+
<input
|
|
67
|
+
type="search"
|
|
68
|
+
placeholder="Search decisions…"
|
|
69
|
+
aria-label="Search decisions"
|
|
70
|
+
autofocus
|
|
71
|
+
/>
|
|
72
|
+
<p class="results-summary" aria-hidden="true"></p>
|
|
73
|
+
<p class="visually-hidden" aria-live="polite" aria-atomic="true"></p>
|
|
74
|
+
<ol>
|
|
75
|
+
<template>
|
|
76
|
+
<li>
|
|
77
|
+
<article>
|
|
78
|
+
<h3><a></a></h3>
|
|
79
|
+
<p class="snippet"></p>
|
|
80
|
+
<footer>
|
|
81
|
+
<span class="status"></span>
|
|
82
|
+
<span class="tags"></span>
|
|
83
|
+
<span class="age"></span>
|
|
84
|
+
</footer>
|
|
85
|
+
</article>
|
|
86
|
+
</li>
|
|
87
|
+
</template>
|
|
88
|
+
</ol>
|
|
89
|
+
</dialog>
|
|
90
|
+
|
|
91
|
+
<script>
|
|
92
|
+
document
|
|
93
|
+
.querySelector('.site-nav footer button')
|
|
94
|
+
.addEventListener('click', () => {
|
|
95
|
+
const nav = document.querySelector('.site-nav');
|
|
96
|
+
const btn = document.querySelector('.site-nav footer button');
|
|
97
|
+
const isExpanded = nav.classList.contains('expanded');
|
|
98
|
+
const isMobile = window.matchMedia('(max-width: 48rem)').matches;
|
|
99
|
+
|
|
100
|
+
if (isExpanded && isMobile) {
|
|
101
|
+
nav.classList.add('closing');
|
|
102
|
+
nav.classList.remove('expanded');
|
|
103
|
+
btn.setAttribute('aria-expanded', 'false');
|
|
104
|
+
nav.querySelector('.menu').addEventListener(
|
|
105
|
+
'animationend',
|
|
106
|
+
() => {
|
|
107
|
+
nav.classList.remove('closing');
|
|
108
|
+
},
|
|
109
|
+
{ once: true },
|
|
110
|
+
);
|
|
111
|
+
} else {
|
|
112
|
+
const expanded = nav.classList.toggle('expanded');
|
|
113
|
+
btn.setAttribute('aria-expanded', String(expanded));
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const searchDialog = document.getElementById('search-dialog');
|
|
118
|
+
document
|
|
119
|
+
.querySelector('.site-nav button[aria-label="Search"]')
|
|
120
|
+
.addEventListener('click', () => {
|
|
121
|
+
searchDialog.showModal();
|
|
122
|
+
});
|
|
123
|
+
</script>
|
|
124
|
+
<script type="module" src="/js/search.js"></script>
|
|
125
|
+
<script type="speculationrules">
|
|
126
|
+
{
|
|
127
|
+
"prefetch": [
|
|
128
|
+
{
|
|
129
|
+
"where": { "href_matches": "/*" },
|
|
130
|
+
"eagerness": "moderate"
|
|
131
|
+
}
|
|
132
|
+
]
|
|
133
|
+
}
|
|
134
|
+
</script>
|
|
135
|
+
</body>
|
|
136
|
+
</html>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
pagination:
|
|
3
|
+
data: collections.contributors
|
|
4
|
+
size: 1
|
|
5
|
+
alias: contributor
|
|
6
|
+
permalink: 'contributors/{{ contributor | slugify }}/'
|
|
7
|
+
eleventyExcludeFromCollections: true
|
|
8
|
+
eleventyComputed:
|
|
9
|
+
title: 'Decisions by {{ contributor }}'
|
|
10
|
+
description: 'Architectural decision records contributed to by {{ contributor }}'
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
{%- set contributorAdrs = collections.adrs | byContributor(contributor) %}
|
|
14
|
+
|
|
15
|
+
<h1>Decisions by {{ contributor }}</h1>
|
|
16
|
+
<p class="page-description">{{ description }}</p>
|
|
17
|
+
|
|
18
|
+
<div class="adr-filters">
|
|
19
|
+
<span class="adr-count"
|
|
20
|
+
>{{ contributorAdrs | length }}
|
|
21
|
+
decision{{ "s" if contributorAdrs | length != 1 }}</span
|
|
22
|
+
>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<ol class="adr-list" reversed>
|
|
26
|
+
{%- for adr in contributorAdrs %}
|
|
27
|
+
<li class="adr-item">
|
|
28
|
+
<div class="adr-header">
|
|
29
|
+
<span class="adr-status adr-status--{{ adr.data.status }}">
|
|
30
|
+
{% if adr.data.status == "accepted" %}{% icon "verified" %}{% else %}{% icon "cancel" %}{% endif %}
|
|
31
|
+
{{ adr.data.status }}
|
|
32
|
+
</span>
|
|
33
|
+
<time datetime="{{ adr.date | isoDate }}"
|
|
34
|
+
>{{ adr.date | timeSince }}</time
|
|
35
|
+
>
|
|
36
|
+
</div>
|
|
37
|
+
<a class="adr-title" href="{{ adr.url }}">{{ adr.data.title }}</a>
|
|
38
|
+
{%- if adr.data.context %}
|
|
39
|
+
<p class="adr-context">{{ adr.data.context }}</p>
|
|
40
|
+
{%- endif %}
|
|
41
|
+
<div class="adr-meta">
|
|
42
|
+
{%- if adr.data.practiceArea %}
|
|
43
|
+
<span class="tag">{{ adr.data.practiceArea }}</span>
|
|
44
|
+
{%- endif %}
|
|
45
|
+
{%- for topic in adr.data.topics %}
|
|
46
|
+
<span class="tag">{{ topic }}</span>
|
|
47
|
+
{%- endfor %}
|
|
48
|
+
</div>
|
|
49
|
+
</li>
|
|
50
|
+
{%- endfor %}
|
|
51
|
+
</ol>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Contributors
|
|
3
|
+
description: Team members who have contributed to our decision records
|
|
4
|
+
permalink: /contributors/
|
|
5
|
+
eleventyExcludeFromCollections: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
<h1>{{ title }}</h1>
|
|
9
|
+
<p class="page-description">{{ description }}</p>
|
|
10
|
+
|
|
11
|
+
<table class="contributor-table">
|
|
12
|
+
<thead>
|
|
13
|
+
<tr>
|
|
14
|
+
<th>Name</th>
|
|
15
|
+
<th>Decisions</th>
|
|
16
|
+
<th>Top Topics</th>
|
|
17
|
+
</tr>
|
|
18
|
+
</thead>
|
|
19
|
+
<tbody>
|
|
20
|
+
{%- for contributor in collections.contributors %}
|
|
21
|
+
{%- set adrs = collections.adrs | byContributor(contributor) %}
|
|
22
|
+
{%- set topicCounts = {} %}
|
|
23
|
+
{%- for adr in adrs %}
|
|
24
|
+
{%- for topic in adr.data.topics %}
|
|
25
|
+
{%- set key = topic | lower %}
|
|
26
|
+
{%- set topicCounts = topicCounts | setKey(key, (topicCounts[key] or 0) + 1) %}
|
|
27
|
+
{%- endfor %}
|
|
28
|
+
{%- endfor %}
|
|
29
|
+
<tr>
|
|
30
|
+
<td>
|
|
31
|
+
<a href="/contributors/{{ contributor | slugify }}/"
|
|
32
|
+
>{{ contributor }}</a
|
|
33
|
+
>
|
|
34
|
+
</td>
|
|
35
|
+
<td>{{ adrs | length }}</td>
|
|
36
|
+
<td>
|
|
37
|
+
<div class="tags">
|
|
38
|
+
{%- for topic in topicCounts | topTopics(3) %}
|
|
39
|
+
<span class="tag">{{ topic }}</span>
|
|
40
|
+
{%- endfor %}
|
|
41
|
+
</div>
|
|
42
|
+
</td>
|
|
43
|
+
</tr>
|
|
44
|
+
{%- endfor %}
|
|
45
|
+
</tbody>
|
|
46
|
+
</table>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: All Decisions
|
|
3
|
+
description: A complete list of architectural decision records
|
|
4
|
+
permalink: /{{ dirs.decisions }}/
|
|
5
|
+
eleventyExcludeFromCollections: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
<h1>{{ title }}</h1>
|
|
9
|
+
<p class="page-description">{{ description }}</p>
|
|
10
|
+
|
|
11
|
+
<div class="adr-filters">
|
|
12
|
+
<span class="adr-count">{{ collections.adrs | length }} decisions</span>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<ol class="adr-list" reversed>
|
|
16
|
+
{%- for adr in collections.adrs %}
|
|
17
|
+
<li class="adr-item">
|
|
18
|
+
<div class="adr-header">
|
|
19
|
+
<span class="adr-status adr-status--{{ adr.data.status }}">
|
|
20
|
+
{% if adr.data.status == "accepted" %}{% icon "verified" %}{% else %}{% icon "cancel" %}{% endif %}
|
|
21
|
+
{{ adr.data.status }}
|
|
22
|
+
</span>
|
|
23
|
+
<time datetime="{{ adr.date | isoDate }}"
|
|
24
|
+
>{{ adr.date | timeSince }}</time
|
|
25
|
+
>
|
|
26
|
+
</div>
|
|
27
|
+
<a class="adr-title" href="{{ adr.url }}">{{ adr.data.title }}</a>
|
|
28
|
+
{%- if adr.data.context %}
|
|
29
|
+
<p class="adr-context">{{ adr.data.context }}</p>
|
|
30
|
+
{%- endif %}
|
|
31
|
+
<div class="adr-meta">
|
|
32
|
+
{%- if adr.data.practiceArea %}
|
|
33
|
+
<span class="tag">{{ adr.data.practiceArea }}</span>
|
|
34
|
+
{%- endif %}
|
|
35
|
+
{%- for topic in adr.data.topics %}
|
|
36
|
+
<span class="tag">{{ topic }}</span>
|
|
37
|
+
{%- endfor %}
|
|
38
|
+
</div>
|
|
39
|
+
</li>
|
|
40
|
+
{%- endfor %}
|
|
41
|
+
</ol>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
pagination:
|
|
3
|
+
data: practiceAreas
|
|
4
|
+
size: 1
|
|
5
|
+
alias: area
|
|
6
|
+
permalink: 'practice-areas/{{ area.name | slugify }}/'
|
|
7
|
+
eleventyExcludeFromCollections: true
|
|
8
|
+
eleventyComputed:
|
|
9
|
+
title: '{{ area.name }} Decisions'
|
|
10
|
+
description: 'Architectural decision records for {{ area.name }}'
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
{%- set areaAdrs = collections.adrs | withPracticeArea(area.name) %}
|
|
14
|
+
|
|
15
|
+
<h1>{{ area.name }} Decisions</h1>
|
|
16
|
+
|
|
17
|
+
<p class="page-description">{{ description }}</p>
|
|
18
|
+
|
|
19
|
+
<div class="adr-filters">
|
|
20
|
+
<span class="adr-count"
|
|
21
|
+
>{{ areaAdrs | length }} decision{{ "s" if areaAdrs | length != 1 }}</span
|
|
22
|
+
>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<ol class="adr-list" reversed>
|
|
26
|
+
{%- for adr in areaAdrs %}
|
|
27
|
+
<li class="adr-item">
|
|
28
|
+
<div class="adr-header">
|
|
29
|
+
<span class="adr-status adr-status--{{ adr.data.status }}">
|
|
30
|
+
{% if adr.data.status == "accepted" %}{% icon "verified" %}{% else %}{% icon "cancel" %}{% endif %}
|
|
31
|
+
{{ adr.data.status }}
|
|
32
|
+
</span>
|
|
33
|
+
<time datetime="{{ adr.date | isoDate }}"
|
|
34
|
+
>{{ adr.date | timeSince }}</time
|
|
35
|
+
>
|
|
36
|
+
</div>
|
|
37
|
+
<a class="adr-title" href="{{ adr.url }}">{{ adr.data.title }}</a>
|
|
38
|
+
{%- if adr.data.context %}
|
|
39
|
+
<p class="adr-context">{{ adr.data.context }}</p>
|
|
40
|
+
{%- endif %}
|
|
41
|
+
<div class="adr-meta">
|
|
42
|
+
{%- for topic in adr.data.topics %}
|
|
43
|
+
<span class="tag">{{ topic }}</span>
|
|
44
|
+
{%- endfor %}
|
|
45
|
+
</div>
|
|
46
|
+
</li>
|
|
47
|
+
{%- endfor %}
|
|
48
|
+
</ol>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
pagination:
|
|
3
|
+
data: collections.topics
|
|
4
|
+
size: 1
|
|
5
|
+
alias: topic
|
|
6
|
+
permalink: 'topics/{{ topic.label | slugify }}/'
|
|
7
|
+
eleventyExcludeFromCollections: true
|
|
8
|
+
eleventyComputed:
|
|
9
|
+
title: 'Decisions about {{ topic.label }}'
|
|
10
|
+
description: 'Architectural decision records tagged with {{ topic.label }}'
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
{%- set topicAdrs = collections.adrs | withTopic(topic.label) %}
|
|
14
|
+
|
|
15
|
+
<h1>Decisions about {{ topic.label }}</h1>
|
|
16
|
+
<p class="page-description">{{ description }}</p>
|
|
17
|
+
|
|
18
|
+
<div class="adr-filters">
|
|
19
|
+
<span class="adr-count"
|
|
20
|
+
>{{ topicAdrs | length }} decision{{ "s" if topicAdrs | length != 1 }}</span
|
|
21
|
+
>
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<ol class="adr-list" reversed>
|
|
25
|
+
{%- for adr in topicAdrs %}
|
|
26
|
+
<li class="adr-item">
|
|
27
|
+
<div class="adr-header">
|
|
28
|
+
<span class="adr-status adr-status--{{ adr.data.status }}">
|
|
29
|
+
{% if adr.data.status == "accepted" %}{% icon "verified" %}{% else %}{% icon "cancel" %}{% endif %}
|
|
30
|
+
{{ adr.data.status }}
|
|
31
|
+
</span>
|
|
32
|
+
<time datetime="{{ adr.date | isoDate }}"
|
|
33
|
+
>{{ adr.date | timeSince }}</time
|
|
34
|
+
>
|
|
35
|
+
</div>
|
|
36
|
+
<a class="adr-title" href="{{ adr.url }}">{{ adr.data.title }}</a>
|
|
37
|
+
{%- if adr.data.context %}
|
|
38
|
+
<p class="adr-context">{{ adr.data.context }}</p>
|
|
39
|
+
{%- endif %}
|
|
40
|
+
<div class="adr-meta">
|
|
41
|
+
{%- if adr.data.practiceArea %}
|
|
42
|
+
<span class="tag">{{ adr.data.practiceArea }}</span>
|
|
43
|
+
{%- endif %}
|
|
44
|
+
{%- for t in adr.data.topics %}
|
|
45
|
+
<span class="tag">{{ t }}</span>
|
|
46
|
+
{%- endfor %}
|
|
47
|
+
</div>
|
|
48
|
+
</li>
|
|
49
|
+
{%- endfor %}
|
|
50
|
+
</ol>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<footer class="site-footer">
|
|
2
|
+
<ul class="social-links">
|
|
3
|
+
{%- for item in site.social %}
|
|
4
|
+
<li>
|
|
5
|
+
<a href="{{ item.url }}" aria-label="{{ item.label }}">
|
|
6
|
+
{% icon item.icon %}
|
|
7
|
+
</a>
|
|
8
|
+
</li>
|
|
9
|
+
{%- endfor %}
|
|
10
|
+
</ul>
|
|
11
|
+
<p>© {{ null | year }} {{ site.legalName }} CC-BY 4.0.</p>
|
|
12
|
+
</footer>
|