create-berna-stencil 2.6.0 → 2.6.2
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/README.md +1 -1
- package/_tools/assistant.js +92 -88
- package/_tools/modules/constants.js +23 -0
- package/_tools/modules/pageComponents.js +77 -0
- package/_tools/modules/updateData.js +25 -44
- package/_tools/modules/updateOutputPath.js +59 -63
- package/_tools/modules/updatePage.js +131 -104
- package/_tools/modules/utils.js +16 -19
- package/_tools/modules/validation.js +20 -0
- package/bin/create.js +74 -12
- package/out/.htaccess +16 -0
- package/out/404.html +163 -0
- package/out/assets/brand/favicon.svg +37 -0
- package/out/assets/brand/logo.svg +37 -0
- package/out/backend/.htaccess +7 -0
- package/out/backend/_core/composer.json +5 -0
- package/out/backend/_core/composer.lock +492 -0
- package/out/backend/_core/index.php +148 -0
- package/out/backend/_core/init.php +34 -0
- package/out/backend/_core/modules/RateLimiter.php +31 -0
- package/out/backend/_core/modules/Response.php +49 -0
- package/{src → out}/backend/_core/vendor/composer/autoload_static.php +10 -10
- package/{src → out}/backend/_core/vendor/composer/installed.json +6 -6
- package/{src → out}/backend/_core/vendor/composer/installed.php +2 -2
- package/out/backend/_core/vendor/graham-campbell/result-type/CHANGELOG.md +53 -0
- package/{src → out}/backend/_core/vendor/graham-campbell/result-type/LICENSE +21 -21
- package/out/backend/_core/vendor/graham-campbell/result-type/README.md +42 -0
- package/{src → out}/backend/_core/vendor/graham-campbell/result-type/composer.json +33 -33
- package/out/backend/_core/vendor/graham-campbell/result-type/phpunit.xml.dist +13 -0
- package/{src → out}/backend/_core/vendor/graham-campbell/result-type/src/Error.php +121 -121
- package/{src → out}/backend/_core/vendor/graham-campbell/result-type/src/Result.php +69 -69
- package/{src → out}/backend/_core/vendor/graham-campbell/result-type/src/Success.php +120 -120
- package/out/backend/_core/vendor/graham-campbell/result-type/tests/ResultTest.php +95 -0
- package/{src → out}/backend/_core/vendor/phpoption/phpoption/LICENSE +200 -200
- package/out/backend/_core/vendor/phpoption/phpoption/Makefile +17 -0
- package/out/backend/_core/vendor/phpoption/phpoption/README.md +201 -0
- package/{src → out}/backend/_core/vendor/phpoption/phpoption/composer.json +50 -50
- package/out/backend/_core/vendor/phpoption/phpoption/phpstan-baseline.neon +44 -0
- package/out/backend/_core/vendor/phpoption/phpoption/phpstan.neon.dist +7 -0
- package/out/backend/_core/vendor/phpoption/phpoption/phpunit.xml.dist +13 -0
- package/{src → out}/backend/_core/vendor/phpoption/phpoption/src/PhpOption/LazyOption.php +175 -175
- package/{src → out}/backend/_core/vendor/phpoption/phpoption/src/PhpOption/None.php +136 -136
- package/{src → out}/backend/_core/vendor/phpoption/phpoption/src/PhpOption/Option.php +434 -434
- package/{src → out}/backend/_core/vendor/phpoption/phpoption/src/PhpOption/Some.php +169 -169
- package/out/backend/_core/vendor/phpoption/phpoption/tests/PhpOption/Tests/EnsureTest.php +72 -0
- package/out/backend/_core/vendor/phpoption/phpoption/tests/PhpOption/Tests/LazyOptionTest.php +357 -0
- package/out/backend/_core/vendor/phpoption/phpoption/tests/PhpOption/Tests/NoneTest.php +153 -0
- package/out/backend/_core/vendor/phpoption/phpoption/tests/PhpOption/Tests/OptionTest.php +166 -0
- package/out/backend/_core/vendor/phpoption/phpoption/tests/PhpOption/Tests/SomeTest.php +194 -0
- package/out/backend/_core/vendor/phpoption/phpoption/tests/bootstrap.php +8 -0
- package/out/backend/_core/vendor/phpoption/phpoption/vendor-bin/phpstan/composer.json +8 -0
- package/{src → out}/backend/_core/vendor/symfony/polyfill-ctype/Ctype.php +232 -232
- package/{src → out}/backend/_core/vendor/symfony/polyfill-ctype/LICENSE +19 -19
- package/{src → out}/backend/_core/vendor/symfony/polyfill-ctype/README.md +12 -12
- package/{src → out}/backend/_core/vendor/symfony/polyfill-ctype/bootstrap.php +50 -50
- package/{src → out}/backend/_core/vendor/symfony/polyfill-ctype/bootstrap80.php +46 -46
- package/{src → out}/backend/_core/vendor/symfony/polyfill-ctype/composer.json +38 -38
- package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/LICENSE +19 -19
- package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/Mbstring.php +1077 -1077
- package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/README.md +13 -13
- package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/Resources/unidata/caseFolding.php +119 -119
- package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php +1397 -1397
- package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/Resources/unidata/titleCaseRegexp.php +5 -5
- package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php +1489 -1489
- package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/bootstrap.php +171 -171
- package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/bootstrap80.php +167 -167
- package/{src → out}/backend/_core/vendor/symfony/polyfill-mbstring/composer.json +39 -39
- package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/LICENSE +19 -19
- package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/Php80.php +115 -115
- package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/PhpToken.php +106 -106
- package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/README.md +25 -25
- package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/Resources/stubs/Attribute.php +31 -31
- package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/Resources/stubs/PhpToken.php +16 -16
- package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/Resources/stubs/Stringable.php +20 -20
- package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php +16 -16
- package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/Resources/stubs/ValueError.php +16 -16
- package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/bootstrap.php +42 -42
- package/{src → out}/backend/_core/vendor/symfony/polyfill-php80/composer.json +37 -37
- package/out/backend/_core/vendor/vlucas/phpdotenv/.editorconfig +15 -0
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/LICENSE +30 -30
- package/out/backend/_core/vendor/vlucas/phpdotenv/Makefile +17 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/README.md +370 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/UPGRADING.md +196 -0
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/composer.json +60 -60
- package/out/backend/_core/vendor/vlucas/phpdotenv/phpstan-baseline.neon +157 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/phpstan.neon.dist +7 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/phpunit.xml.dist +13 -0
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Dotenv.php +267 -267
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Exception/ExceptionInterface.php +12 -12
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Exception/InvalidEncodingException.php +12 -12
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Exception/InvalidFileException.php +12 -12
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Exception/InvalidPathException.php +12 -12
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Exception/ValidationException.php +12 -12
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Loader/Loader.php +48 -48
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Loader/LoaderInterface.php +20 -20
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Loader/Resolver.php +65 -65
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Parser/Entry.php +59 -59
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Parser/EntryParser.php +299 -299
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Parser/Lexer.php +58 -58
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Parser/Lines.php +127 -127
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Parser/Parser.php +53 -53
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Parser/ParserInterface.php +19 -19
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Parser/Value.php +88 -88
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/AdapterInterface.php +15 -15
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ApacheAdapter.php +89 -89
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ArrayAdapter.php +80 -80
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/EnvConstAdapter.php +88 -88
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/GuardedWriter.php +85 -85
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ImmutableWriter.php +110 -110
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/MultiReader.php +48 -48
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/MultiWriter.php +64 -64
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/PutenvAdapter.php +91 -91
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ReaderInterface.php +17 -17
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ReplacingWriter.php +104 -104
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/ServerConstAdapter.php +88 -88
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/Adapter/WriterInterface.php +27 -27
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/AdapterRepository.php +107 -107
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/RepositoryBuilder.php +272 -272
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Repository/RepositoryInterface.php +51 -51
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Store/File/Paths.php +44 -44
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Store/File/Reader.php +81 -81
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Store/FileStore.php +72 -72
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Store/StoreBuilder.php +141 -141
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Store/StoreInterface.php +17 -17
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Store/StringStore.php +37 -37
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Util/Regex.php +112 -112
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Util/Str.php +108 -108
- package/{src → out}/backend/_core/vendor/vlucas/phpdotenv/src/Validator.php +207 -207
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/DotenvTest.php +387 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Loader/LoaderTest.php +86 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Parser/EntryParserTest.php +234 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Parser/LexerTest.php +40 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Parser/LinesTest.php +53 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Parser/ParserTest.php +98 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Repository/Adapter/ArrayAdapterTest.php +57 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Repository/Adapter/EnvConstAdapterTest.php +75 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Repository/Adapter/PutenvAdapterTest.php +52 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Repository/Adapter/ServerConstAdapterTest.php +75 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Repository/RepositoryTest.php +305 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/Store/StoreTest.php +141 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/Dotenv/ValidatorTest.php +479 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/.env +5 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/assertions.env +18 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/booleans.env +33 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/commented.env +15 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/empty.env +1 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/example.env +1 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/exported.env +7 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/immutable.env +1 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/integers.env +17 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/large.env +2 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/multibyte.env +3 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/multiline.env +14 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/multiple.env +4 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/mutable.env +1 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/nested.env +15 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/quoted.env +11 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/specialchars.env +8 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/unicodevarnames.env +2 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/utf8-with-bom-encoding.env +3 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/tests/fixtures/env/windows.env +1 -0
- package/out/backend/_core/vendor/vlucas/phpdotenv/vendor-bin/phpstan/composer.json +15 -0
- package/out/backend/api/protected/example-protected.php +17 -0
- package/out/backend/api/public/example-public.php +17 -0
- package/out/backend/database/Database.php +24 -0
- package/out/backend/database/migrations/create_example_db.sql +1 -0
- package/out/backend/web.config +17 -0
- package/out/css/fonts/bootstrap-icons.woff +0 -0
- package/out/css/fonts/bootstrap-icons.woff2 +0 -0
- package/out/css/github-markdown-dark.css +1124 -0
- package/out/css/github-markdown-light.css +1124 -0
- package/out/css/pages/404.css +21517 -0
- package/out/css/pages/homepage.css +21508 -0
- package/out/data/site.json +44 -0
- package/out/index.html +1147 -0
- package/out/js/bootstrap.bundle.min.js +7 -0
- package/out/js/pages/js/pages/404.js +6 -0
- package/out/js/pages/js/pages/homepage.js +6 -0
- package/out/js/pages/ts/pages/404.js +6 -0
- package/out/js/pages/ts/pages/homepage.js +6 -0
- package/out/llms.txt +14 -0
- package/out/robots.txt +4 -0
- package/out/sitemap.xml +15 -0
- package/out/web.config +27 -0
- package/package.json +14 -47
- package/src/frontend/data/site.json +1 -1
- package/.gitignore +0 -4
- package/_tools/modules/updateIncludes.js +0 -81
- package/src/backend/config.example.php +0 -28
- /package/{src → out}/backend/_core/vendor/autoload.php +0 -0
- /package/{src → out}/backend/_core/vendor/composer/ClassLoader.php +0 -0
- /package/{src → out}/backend/_core/vendor/composer/InstalledVersions.php +0 -0
- /package/{src → out}/backend/_core/vendor/composer/LICENSE +0 -0
- /package/{src → out}/backend/_core/vendor/composer/autoload_classmap.php +0 -0
- /package/{src → out}/backend/_core/vendor/composer/autoload_files.php +0 -0
- /package/{src → out}/backend/_core/vendor/composer/autoload_namespaces.php +0 -0
- /package/{src → out}/backend/_core/vendor/composer/autoload_psr4.php +0 -0
- /package/{src → out}/backend/_core/vendor/composer/autoload_real.php +0 -0
- /package/{src → out}/backend/_core/vendor/composer/platform_check.php +0 -0
package/out/index.html
ADDED
|
@@ -0,0 +1,1147 @@
|
|
|
1
|
+
<!-- This is the base of every page that you will create -->
|
|
2
|
+
|
|
3
|
+
<!DOCTYPE html>
|
|
4
|
+
<html lang="en" data-bs-theme="dark">
|
|
5
|
+
|
|
6
|
+
<head>
|
|
7
|
+
<meta charset="UTF-8">
|
|
8
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
<link rel="canonical" href="https://yoursite.com/"/>
|
|
21
|
+
<title>Homepage</title>
|
|
22
|
+
<meta name="description" content="Description">
|
|
23
|
+
<meta name="keywords" content="keyword1, keyword2, keyword3">
|
|
24
|
+
<meta name="author" content="Name and surname">
|
|
25
|
+
<meta name="theme-color" content="">
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
<meta property="og:title" content="Homepage">
|
|
29
|
+
<meta property="og:description" content="Description">
|
|
30
|
+
<meta property="og:type" content="website">
|
|
31
|
+
<meta property="og:url" content="https://yoursite.com/">
|
|
32
|
+
<meta property="og:image" content="https://yoursite.com/assets/brand/logo.svg">
|
|
33
|
+
<meta property="og:site-name" content="NaN">
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
<meta name="twitter:card" content="summary_large_image">
|
|
37
|
+
<meta name="twitter:title" content="Homepage">
|
|
38
|
+
<meta name="twitter:description" content="Description">
|
|
39
|
+
<meta name="twitter:image" content="https://yoursite.com/assets/brand/logo.svg">
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
<script type="application/ld+json">
|
|
44
|
+
{
|
|
45
|
+
"@context": "https://schema.org",
|
|
46
|
+
"@type": "WebPage",
|
|
47
|
+
"name": "Homepage",
|
|
48
|
+
"description": "Description",
|
|
49
|
+
"url": "https://yoursite.com/",
|
|
50
|
+
"author": {
|
|
51
|
+
"@type": "Person",
|
|
52
|
+
"name": "Name and surname"
|
|
53
|
+
},
|
|
54
|
+
"publisher": {
|
|
55
|
+
"@type": "Organization",
|
|
56
|
+
"name": "Site name",
|
|
57
|
+
"logo": {
|
|
58
|
+
"@type": "ImageObject",
|
|
59
|
+
"url": "https://yoursite.com/assets/brand/logo.svg"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
</script>
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
<link rel="icon" type="image/svg+xml" href="/assets/brand/favicon.svg">
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
<link rel="stylesheet" href="/css/github-markdown-dark.css">
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
<link rel="stylesheet" href="/css/pages/homepage.css"/>
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
</head>
|
|
86
|
+
|
|
87
|
+
<body>
|
|
88
|
+
|
|
89
|
+
<header>
|
|
90
|
+
<nav>
|
|
91
|
+
<a class="logo" href="/">
|
|
92
|
+
<img src="/assets/brand/logo.svg" alt="Site title" height="40">
|
|
93
|
+
</a>
|
|
94
|
+
<div class="nav-links">
|
|
95
|
+
<a class="btn" href="/">Homepage</a>
|
|
96
|
+
<a class="btn" href="/example-page">Example page</a>
|
|
97
|
+
</div>
|
|
98
|
+
</nav>
|
|
99
|
+
</header>
|
|
100
|
+
|
|
101
|
+
<main>
|
|
102
|
+
|
|
103
|
+
<!-- NJK (HTML) components per page -->
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
<div class="layout-container layout-my-3">
|
|
107
|
+
<h1>Welcome to <span class="berna-stencil">Berna-Stencil</span></h1>
|
|
108
|
+
<div class="slogan">The boilerplate you need, simplified</div>
|
|
109
|
+
|
|
110
|
+
<div class="guide-filter">
|
|
111
|
+
<label>
|
|
112
|
+
<input type="radio" name="guide-filter" value="welcome" checked />
|
|
113
|
+
<span class="filter-pill">Welcome</span>
|
|
114
|
+
</label>
|
|
115
|
+
<label>
|
|
116
|
+
<input type="radio" name="guide-filter" value="assistant" />
|
|
117
|
+
<span class="filter-pill">Assistant</span>
|
|
118
|
+
</label>
|
|
119
|
+
<label>
|
|
120
|
+
<input type="radio" name="guide-filter" value="styling" />
|
|
121
|
+
<span class="filter-pill">Styling</span>
|
|
122
|
+
</label>
|
|
123
|
+
<label>
|
|
124
|
+
<input type="radio" name="guide-filter" value="javascript" />
|
|
125
|
+
<span class="filter-pill">Javascript</span>
|
|
126
|
+
</label>
|
|
127
|
+
<label>
|
|
128
|
+
<input type="radio" name="guide-filter" value="creating-pages" />
|
|
129
|
+
<span class="filter-pill">Creating Pages</span>
|
|
130
|
+
</label>
|
|
131
|
+
<label>
|
|
132
|
+
<input type="radio" name="guide-filter" value="components" />
|
|
133
|
+
<span class="filter-pill">Components</span>
|
|
134
|
+
</label>
|
|
135
|
+
<label>
|
|
136
|
+
<input type="radio" name="guide-filter" value="head-seo" />
|
|
137
|
+
<span class="filter-pill">Head & SEO</span>
|
|
138
|
+
</label>
|
|
139
|
+
<label>
|
|
140
|
+
<input type="radio" name="guide-filter" value="backend" />
|
|
141
|
+
<span class="filter-pill">Backend</span>
|
|
142
|
+
</label>
|
|
143
|
+
<label>
|
|
144
|
+
<input type="radio" name="guide-filter" value="deploy" />
|
|
145
|
+
<span class="filter-pill">Deploy</span>
|
|
146
|
+
</label>
|
|
147
|
+
</div>
|
|
148
|
+
|
|
149
|
+
<div class="tabs-container">
|
|
150
|
+
|
|
151
|
+
<div id="content-welcome" class="tab-content active">
|
|
152
|
+
<div class="grid" style="display: flex; gap: 1rem; flex-wrap: wrap;">
|
|
153
|
+
<a href="https://github.com/rhaastrake/berna-stencil" class="card" target="_blank" rel="noopener noreferrer" style="flex: 1; min-width: 250px;">
|
|
154
|
+
<svg class="card-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
155
|
+
<path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path>
|
|
156
|
+
</svg>
|
|
157
|
+
<h3>GitHub repository</h3>
|
|
158
|
+
<p>Community-driven. Contributions, issues and PRs are welcome.</p>
|
|
159
|
+
<span class="card-link">Open the repository
|
|
160
|
+
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" style="margin-left:4px;">
|
|
161
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M14 5l7 7m0 0l-7 7m7-7H3"></path>
|
|
162
|
+
</svg>
|
|
163
|
+
</span>
|
|
164
|
+
</a>
|
|
165
|
+
<a href="https://github.com/Rhaastrake/Berna-Stencil/tree/main/docs" class="card" target="_blank" rel="noopener noreferrer" style="flex: 1; min-width: 250px;">
|
|
166
|
+
<svg class="card-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
167
|
+
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"></path>
|
|
168
|
+
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"></path>
|
|
169
|
+
</svg>
|
|
170
|
+
<h3>Documentation</h3>
|
|
171
|
+
<p>Everything you need to get started, from setup to advanced topics and customizations</p>
|
|
172
|
+
<span class="card-link">Go to documentation
|
|
173
|
+
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" style="margin-left:4px;">
|
|
174
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M14 5l7 7m0 0l-7 7m7-7H3"></path>
|
|
175
|
+
</svg>
|
|
176
|
+
</span>
|
|
177
|
+
</a>
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
|
|
181
|
+
<div id="content-assistant" class="tab-content markdown-body">
|
|
182
|
+
<h1>Assistant CLI</h1>
|
|
183
|
+
<blockquote>
|
|
184
|
+
<p>Examples use JavaScript, but everything applies equally to TypeScript. The only difference is the file extension (<code>.ts</code> instead of <code>.js</code>), that imports do <strong>not</strong> include the extension, and that paths use <code>src/frontend/ts/</code> instead of <code>src/frontend/js/</code>.</p>
|
|
185
|
+
</blockquote>
|
|
186
|
+
<p>An interactive CLI to manage pages without touching files manually.</p>
|
|
187
|
+
<pre><code>npm run assistant
|
|
188
|
+
</code></pre>
|
|
189
|
+
<h2>Menu</h2>
|
|
190
|
+
<pre><code>1. Create page
|
|
191
|
+
2. Remove page
|
|
192
|
+
3. Rename page
|
|
193
|
+
4. Configure output path
|
|
194
|
+
</code></pre>
|
|
195
|
+
<p>Use <code>CTRL/CMD + C</code> to exit.</p>
|
|
196
|
+
<h2>Create page</h2>
|
|
197
|
+
<p>Enter a page name in any format — the CLI converts it to kebab-case automatically.</p>
|
|
198
|
+
<p>For a page named <code>my-page</code>, the following files are created:</p>
|
|
199
|
+
<table>
|
|
200
|
+
<thead>
|
|
201
|
+
<tr>
|
|
202
|
+
<th>File</th>
|
|
203
|
+
<th>Purpose</th>
|
|
204
|
+
</tr>
|
|
205
|
+
</thead>
|
|
206
|
+
<tbody>
|
|
207
|
+
<tr>
|
|
208
|
+
<td><code>src/frontend/scss/pages/myPage.scss</code></td>
|
|
209
|
+
<td>SCSS entry point</td>
|
|
210
|
+
</tr>
|
|
211
|
+
<tr>
|
|
212
|
+
<td><code>src/frontend/js/pages/myPage.js</code></td>
|
|
213
|
+
<td>JS entry point</td>
|
|
214
|
+
</tr>
|
|
215
|
+
<tr>
|
|
216
|
+
<td><code>src/frontend/_routes/my-page.njk</code></td>
|
|
217
|
+
<td>Nunjucks template</td>
|
|
218
|
+
</tr>
|
|
219
|
+
</tbody>
|
|
220
|
+
</table>
|
|
221
|
+
<p>It also adds an <code>elif</code> block in <code>pageComponents.njk</code> and a stub entry in <code>site.json</code>:</p>
|
|
222
|
+
<pre><code class="language-json">"myPage": {
|
|
223
|
+
"seo": {
|
|
224
|
+
"title": "My Page",
|
|
225
|
+
"description": "description"
|
|
226
|
+
},
|
|
227
|
+
"cdn": {
|
|
228
|
+
"css": [],
|
|
229
|
+
"js": []
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
</code></pre>
|
|
233
|
+
<h2>Remove page</h2>
|
|
234
|
+
<p>Deletes all source files for the page and cleans up the output directory, <code>pageComponents.njk</code>, and <code>site.json</code>.</p>
|
|
235
|
+
<h2>Rename page</h2>
|
|
236
|
+
<p>Renames all three source files, updates the <code>elif</code> block in <code>pageComponents.njk</code>, and renames the record in <code>site.json</code> while preserving all existing fields.</p>
|
|
237
|
+
<h2>Configure output path</h2>
|
|
238
|
+
<p>Updates the output directory across <code>.eleventy.js</code> and all relevant <code>package.json</code> scripts in one shot. The old output folder is deleted automatically.</p>
|
|
239
|
+
<blockquote>
|
|
240
|
+
<p>⚠️ <code>homepage</code> and <code>404</code> are protected — they cannot be created, removed, or renamed via the CLI.</p>
|
|
241
|
+
</blockquote>
|
|
242
|
+
|
|
243
|
+
</div>
|
|
244
|
+
<div id="content-styling" class="tab-content markdown-body">
|
|
245
|
+
<h1>Styling with SCSS</h1>
|
|
246
|
+
<h2>Page CSS</h2>
|
|
247
|
+
<p>Each page has its own SCSS entry point in <code>src/frontend/scss/pages/</code></p>
|
|
248
|
+
<p>It must contain <code>_root.scss</code> + other modules like <code>_global.scss</code> or any other one that you need and its own specific css rules</p>
|
|
249
|
+
<p><code>_root.scss</code> uses <code>@use</code> to enable namespaced access (<code>root.$var</code>); other modules use <code>@import</code> as they don't expose variables.</p>
|
|
250
|
+
<h3>examplePage.scss <small>(<code>src/frontend/scss/pages/</code>)</small></h3>
|
|
251
|
+
<pre><code class="language-scss">//==========================
|
|
252
|
+
// CSS MODULES IMPORTS
|
|
253
|
+
//==========================
|
|
254
|
+
|
|
255
|
+
@use "../modules/root" as root;
|
|
256
|
+
|
|
257
|
+
@import "../modules/global";
|
|
258
|
+
|
|
259
|
+
@import "../modules/notification";
|
|
260
|
+
|
|
261
|
+
//==========================
|
|
262
|
+
// PAGE CUSTOM CSS RULES
|
|
263
|
+
//==========================
|
|
264
|
+
|
|
265
|
+
body {
|
|
266
|
+
background-color: root.$primary;
|
|
267
|
+
}
|
|
268
|
+
</code></pre>
|
|
269
|
+
<h2>Global Variables</h2>
|
|
270
|
+
<p>Instead of using <code>:root</code> in your custom modules or pages, the best thing to do is to centralize all your variables in a single file (that will be tree-shaken automatically by Sass)</p>
|
|
271
|
+
<h3>_root.scss <small>(<code>src/frontend/scss/modules/</code>)</small></h3>
|
|
272
|
+
<pre><code class="language-scss">$header-height: 10vh;
|
|
273
|
+
|
|
274
|
+
// Usage example (in any other file):
|
|
275
|
+
header {
|
|
276
|
+
height: root.$header-height;
|
|
277
|
+
}
|
|
278
|
+
</code></pre>
|
|
279
|
+
<h2>Scss modules</h2>
|
|
280
|
+
<p>You can create your custom css modules by creating a new <code>.scss</code> file in <code>src/frontend/scss/modules/</code> (the name of the file must start with <code>_</code>)</p>
|
|
281
|
+
<p>You can create subfolders if you want to refactor the structure, but be sure to update the relative paths in the pages that import them</p>
|
|
282
|
+
<h3>_yourModule.scss <small>(<code>src/frontend/scss/modules/subfolder/</code>)</small></h3>
|
|
283
|
+
<pre><code class="language-scss">@use '../root' as root;
|
|
284
|
+
|
|
285
|
+
body {
|
|
286
|
+
background-color: root.$primary;
|
|
287
|
+
}
|
|
288
|
+
</code></pre>
|
|
289
|
+
<h3>examplePage.scss</h3>
|
|
290
|
+
<pre><code class="language-scss">@import "../modules/subfolder/yourModule";
|
|
291
|
+
|
|
292
|
+
// This page will now inherit the body tag rules
|
|
293
|
+
// If the same property is declared in both, the last imported one wins
|
|
294
|
+
body {
|
|
295
|
+
color: root.$dark;
|
|
296
|
+
}
|
|
297
|
+
</code></pre>
|
|
298
|
+
<h3>Pre-existing modules</h3>
|
|
299
|
+
<table>
|
|
300
|
+
<thead>
|
|
301
|
+
<tr>
|
|
302
|
+
<th>File</th>
|
|
303
|
+
<th>Purpose</th>
|
|
304
|
+
</tr>
|
|
305
|
+
</thead>
|
|
306
|
+
<tbody>
|
|
307
|
+
<tr>
|
|
308
|
+
<td><code>_root.scss</code></td>
|
|
309
|
+
<td>Global variables (colors, spacing)</td>
|
|
310
|
+
</tr>
|
|
311
|
+
<tr>
|
|
312
|
+
<td><code>_global.scss</code></td>
|
|
313
|
+
<td>Site-wide base rules and frameworks</td>
|
|
314
|
+
</tr>
|
|
315
|
+
<tr>
|
|
316
|
+
<td><code>_typography.scss</code></td>
|
|
317
|
+
<td>Font rules</td>
|
|
318
|
+
</tr>
|
|
319
|
+
<tr>
|
|
320
|
+
<td><code>_header.scss</code></td>
|
|
321
|
+
<td>Header styles</td>
|
|
322
|
+
</tr>
|
|
323
|
+
<tr>
|
|
324
|
+
<td><code>_footer.scss</code></td>
|
|
325
|
+
<td>Footer styles</td>
|
|
326
|
+
</tr>
|
|
327
|
+
<tr>
|
|
328
|
+
<td><code>_mobile.scss</code></td>
|
|
329
|
+
<td>Media query rules</td>
|
|
330
|
+
</tr>
|
|
331
|
+
<tr>
|
|
332
|
+
<td><code>_buttons.scss</code></td>
|
|
333
|
+
<td>Style and hovers for buttons</td>
|
|
334
|
+
</tr>
|
|
335
|
+
<tr>
|
|
336
|
+
<td><code>_animations.scss</code></td>
|
|
337
|
+
<td>Keyframe animations (<code>fade-in</code>, <code>spin</code>)</td>
|
|
338
|
+
</tr>
|
|
339
|
+
<tr>
|
|
340
|
+
<td><code>_notification.scss</code></td>
|
|
341
|
+
<td>Notification component style</td>
|
|
342
|
+
</tr>
|
|
343
|
+
</tbody>
|
|
344
|
+
</table>
|
|
345
|
+
<h2>CSS Framework</h2>
|
|
346
|
+
<p>Some of the most popular css frameworks that supports scss with modules are already installed in <code>node_modules</code></p>
|
|
347
|
+
<p>You can choose one or none of them (more than 1 works, but you may get in various conflicts)</p>
|
|
348
|
+
<p>To enable/disable them you have to modify 3 files around the project by just commenting them</p>
|
|
349
|
+
<h3>1. _global.scss <small>(<code>src/frontend/scss/modules/</code>)</small></h3>
|
|
350
|
+
<pre><code class="language-scss">@import "../modules/frameworks/bootstrap";
|
|
351
|
+
// @import "../modules/frameworks/bulma";
|
|
352
|
+
// @import "../modules/frameworks/foundation";
|
|
353
|
+
// @import "../modules/frameworks/uikit";
|
|
354
|
+
</code></pre>
|
|
355
|
+
<h3>2. base.njk <small>(<code>src/frontend/layouts/</code>)</small></h3>
|
|
356
|
+
<pre><code class="language-html">{# Bootstrap JS #}
|
|
357
|
+
<script src="/js/bootstrap.bundle.min.js" defer></script>
|
|
358
|
+
|
|
359
|
+
{# Foundation JS #}
|
|
360
|
+
{# <script src="/js/foundation.min.js" defer></script> #}
|
|
361
|
+
|
|
362
|
+
{# UIkit JS #}
|
|
363
|
+
{# <script src="/js/uikit.min.js" defer></script> #}
|
|
364
|
+
{# <script src="/js/uikit-icons.min.js" defer></script> #}
|
|
365
|
+
|
|
366
|
+
{# Bulma — no JS needed #}
|
|
367
|
+
</code></pre>
|
|
368
|
+
<h3>3. .eleventy.js</h3>
|
|
369
|
+
<pre><code class="language-javascript">eleventyConfig.addPassthroughCopy({
|
|
370
|
+
// Bootstrap
|
|
371
|
+
"node_modules/bootstrap/dist/js/bootstrap.bundle.min.js": "js/bootstrap.bundle.min.js",
|
|
372
|
+
"node_modules/bootstrap-icons/font/fonts": "css/fonts",
|
|
373
|
+
|
|
374
|
+
// Foundation
|
|
375
|
+
// "node_modules/foundation-sites/dist/js/foundation.min.js": "js/foundation.min.js",
|
|
376
|
+
|
|
377
|
+
// UIkit
|
|
378
|
+
// "node_modules/uikit/dist/js/uikit.min.js": "js/uikit.min.js",
|
|
379
|
+
// "node_modules/uikit/dist/js/uikit-icons.min.js": "js/uikit-icons.min.js",
|
|
380
|
+
|
|
381
|
+
// Bulma — CSS only, no JS passthrough needed
|
|
382
|
+
});
|
|
383
|
+
</code></pre>
|
|
384
|
+
<h3>Reducing bundle size</h3>
|
|
385
|
+
<p>To reduce the bundle size, open the corresponding framework file (<code>src/frontend/scss/modules/frameworks/</code>) and comment out any modules you don't need</p>
|
|
386
|
+
<pre><code class="language-scss">@import "bootstrap/scss/card"; // Cards
|
|
387
|
+
@import "bootstrap/scss/carousel"; // Carousel
|
|
388
|
+
</code></pre>
|
|
389
|
+
|
|
390
|
+
</div>
|
|
391
|
+
<div id="content-javascript" class="tab-content markdown-body">
|
|
392
|
+
<h1>JavaScript</h1>
|
|
393
|
+
<blockquote>
|
|
394
|
+
<p>Examples use JavaScript, but everything applies equally to TypeScript. The only difference is the file extension (<code>.ts</code> instead of <code>.js</code>), that imports do <strong>not</strong> include the extension, and that paths use <code>src/frontend/ts/</code> instead of <code>src/frontend/js/</code>.</p>
|
|
395
|
+
</blockquote>
|
|
396
|
+
<h2>Page JS</h2>
|
|
397
|
+
<p>Each page has its own JS entry point in <code>src/frontend/js/pages/</code>, bundled and minified by esbuild and loaded automatically by <code>base.njk</code>.</p>
|
|
398
|
+
<p>Import only what the page needs.</p>
|
|
399
|
+
<h3>examplePage.js <small>(<code>src/frontend/js/pages/</code>)</small></h3>
|
|
400
|
+
<pre><code class="language-js">// import { initExampleModule } from '../modules/exampleModule.js';
|
|
401
|
+
|
|
402
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
403
|
+
// initExampleModule();
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
// Page logic here
|
|
407
|
+
</code></pre>
|
|
408
|
+
<h2>Modules</h2>
|
|
409
|
+
<p>Modules live in <code>src/frontend/js/modules/</code>. Modules that interact with the DOM must be called inside <code>DOMContentLoaded</code>; others can be called anywhere.</p>
|
|
410
|
+
<h2>Adding a module</h2>
|
|
411
|
+
<p>Create a new <code>.js</code> file in <code>src/frontend/js/modules/</code>. Subfolders are allowed.</p>
|
|
412
|
+
<p>Use ESM syntax — esbuild handles the bundling:</p>
|
|
413
|
+
<h3>exampleModule.js <small>(<code>src/frontend/js/modules/</code>)</small></h3>
|
|
414
|
+
<pre><code class="language-js">export function exampleModule() {
|
|
415
|
+
// Module logic here
|
|
416
|
+
}
|
|
417
|
+
</code></pre>
|
|
418
|
+
<p>Then import it in the pages that need it:</p>
|
|
419
|
+
<pre><code class="language-js">import { exampleModule } from '../modules/exampleModule.js';
|
|
420
|
+
</code></pre>
|
|
421
|
+
<p>In TypeScript, omit the extension:</p>
|
|
422
|
+
<pre><code class="language-ts">import { exampleModule } from '../modules/exampleModule';
|
|
423
|
+
</code></pre>
|
|
424
|
+
<blockquote>
|
|
425
|
+
<p>⚠️ Files inside <code>_tools/</code> run directly in Node.js without a bundler — use CommonJS (<code>require</code> / <code>module.exports</code>) there, not ESM.</p>
|
|
426
|
+
</blockquote>
|
|
427
|
+
|
|
428
|
+
</div>
|
|
429
|
+
<div id="content-creating-pages" class="tab-content markdown-body">
|
|
430
|
+
<h1>Creating Pages</h1>
|
|
431
|
+
<blockquote>
|
|
432
|
+
<p>Examples use JavaScript, but everything applies equally to TypeScript. The only difference is the file extension (<code>.ts</code> instead of <code>.js</code>), that imports do <strong>not</strong> include the extension, and that paths use <code>src/frontend/ts/</code> instead of <code>src/frontend/js/</code>.</p>
|
|
433
|
+
</blockquote>
|
|
434
|
+
<p>The recommended way is via the <strong>Assistant CLI</strong></p>
|
|
435
|
+
<h2>What gets created</h2>
|
|
436
|
+
<p>For a page named <code>my-page</code>:</p>
|
|
437
|
+
<table>
|
|
438
|
+
<thead>
|
|
439
|
+
<tr>
|
|
440
|
+
<th>File</th>
|
|
441
|
+
<th>Purpose</th>
|
|
442
|
+
</tr>
|
|
443
|
+
</thead>
|
|
444
|
+
<tbody>
|
|
445
|
+
<tr>
|
|
446
|
+
<td><code>src/frontend/_routes_/my-page.njk</code></td>
|
|
447
|
+
<td>Template with front matter</td>
|
|
448
|
+
</tr>
|
|
449
|
+
<tr>
|
|
450
|
+
<td><code>src/frontend/scss/pages/myPage.scss</code></td>
|
|
451
|
+
<td>Imports framework + modules</td>
|
|
452
|
+
</tr>
|
|
453
|
+
<tr>
|
|
454
|
+
<td><code>src/frontend/js/pages/myPage.js</code></td>
|
|
455
|
+
<td>Imports JS modules</td>
|
|
456
|
+
</tr>
|
|
457
|
+
</tbody>
|
|
458
|
+
</table>
|
|
459
|
+
<h2>Adding content</h2>
|
|
460
|
+
<ol>
|
|
461
|
+
<li>Create a component in <code>src/frontend/components/</code> (e.g. <code>_myPage.njk</code>)</li>
|
|
462
|
+
<li>Include it in <code>src/frontend/layouts/pageComponents.njk</code> inside the generated <code>elif</code> block:</li>
|
|
463
|
+
</ol>
|
|
464
|
+
<pre><code class="language-njk">{% elif title == "myPage" %}
|
|
465
|
+
{% include "_myPage.njk" %}
|
|
466
|
+
</code></pre>
|
|
467
|
+
<p>See <strong>Components</strong> DOC file for more info</p>
|
|
468
|
+
<h2>URL and title</h2>
|
|
469
|
+
<p>The URL is the kebab-case name (<code>/my-page/</code>). The <code>title</code> in the front matter is camelCase (<code>myPage</code>) and is used internally to load the correct CSS and JS files — do not change it.</p>
|
|
470
|
+
<h2>Subpages (nested URLs)</h2>
|
|
471
|
+
<p>To create a URL like <code>domain.it/about/team</code>, edit the <code>permalink</code> in <code>src/frontend/_routes/team.njk</code> and add the parent segment before the final slash:</p>
|
|
472
|
+
<pre><code class="language-njk">---
|
|
473
|
+
title: "team"
|
|
474
|
+
permalink: "about/team/"
|
|
475
|
+
layout: pageComponents.njk
|
|
476
|
+
---
|
|
477
|
+
</code></pre>
|
|
478
|
+
<p>The parent path (<code>about</code>) does <strong>not</strong> need to exist as a real page — it's just a URL prefix. Only the last segment (<code>team</code>) must match the filename and the <code>title</code> in the front matter.</p>
|
|
479
|
+
<table>
|
|
480
|
+
<thead>
|
|
481
|
+
<tr>
|
|
482
|
+
<th>Goal URL</th>
|
|
483
|
+
<th>permalink value</th>
|
|
484
|
+
<th>File</th>
|
|
485
|
+
</tr>
|
|
486
|
+
</thead>
|
|
487
|
+
<tbody>
|
|
488
|
+
<tr>
|
|
489
|
+
<td><code>/team/</code></td>
|
|
490
|
+
<td><code>/team/</code></td>
|
|
491
|
+
<td><code>_routes/team.njk</code></td>
|
|
492
|
+
</tr>
|
|
493
|
+
<tr>
|
|
494
|
+
<td><code>/about/team/</code></td>
|
|
495
|
+
<td><code>/about/team/</code></td>
|
|
496
|
+
<td><code>_routes/team.njk</code></td>
|
|
497
|
+
</tr>
|
|
498
|
+
<tr>
|
|
499
|
+
<td><code>/company/about/team/</code></td>
|
|
500
|
+
<td><code>/company/about/team/</code></td>
|
|
501
|
+
<td><code>_routes/team.njk</code></td>
|
|
502
|
+
</tr>
|
|
503
|
+
</tbody>
|
|
504
|
+
</table>
|
|
505
|
+
<h2>SEO</h2>
|
|
506
|
+
<p>The CLI creates a stub entry in <code>src/frontend/data/site.json</code>. Fill it in:</p>
|
|
507
|
+
<pre><code class="language-json">"myPage": {
|
|
508
|
+
"seo": {
|
|
509
|
+
"title": "My Page | Site Name",
|
|
510
|
+
"description": "Page description"
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
</code></pre>
|
|
514
|
+
<p>See <strong>Head & SEO</strong> for all available options.</p>
|
|
515
|
+
|
|
516
|
+
</div>
|
|
517
|
+
<div id="content-components" class="tab-content markdown-body">
|
|
518
|
+
<h1>Nunjucks (HTML) Components</h1>
|
|
519
|
+
<h2>What is Nunjucks</h2>
|
|
520
|
+
<p>Nunjucks (<code>.njk</code>) is an HTML file that supports logic like variables, <code>if</code> statements, and <code>for</code> loops. It can extend a base layout and include other <code>.njk</code> components</p>
|
|
521
|
+
<h2>Create a component</h2>
|
|
522
|
+
<p>Create a new <code>.njk</code> file anywhere inside <code>src/frontend/components/</code>. You can organize them into subfolders freely</p>
|
|
523
|
+
<pre><code>src/frontend/components/
|
|
524
|
+
├── global/
|
|
525
|
+
├── layouts/
|
|
526
|
+
├── modals/
|
|
527
|
+
│ └── privacyModal.njk # You can move it to a modals/subfolder
|
|
528
|
+
├── welcome.njk
|
|
529
|
+
</code></pre>
|
|
530
|
+
<h2>Include a component</h2>
|
|
531
|
+
<p>To render a component inside a page, navigate to <code>src/frontend/layouts/</code> and edit <code>pageComponents.njk</code></p>
|
|
532
|
+
<h3>pageComponents.njk <small>(<code>src/frontend/layouts/</code>)</small></h3>
|
|
533
|
+
<pre><code class="language-js">{% if title == "homepage" %}
|
|
534
|
+
{% include "welcome.njk" %}
|
|
535
|
+
|
|
536
|
+
{% elif title == "examplePage" %}
|
|
537
|
+
{% include "exampleComponent1.njk" %}
|
|
538
|
+
{% include "subfolder/exampleComponent2.njk" %}
|
|
539
|
+
|
|
540
|
+
{% else %}
|
|
541
|
+
{% include "404/_404.njk" %}
|
|
542
|
+
{{ content | safe }}
|
|
543
|
+
{% endif %}
|
|
544
|
+
</code></pre>
|
|
545
|
+
<p>Add a new <code>{% elif %}</code> block for each page, listing its components in order. If a component lives in a subfolder, specify the relative path accordingly</p>
|
|
546
|
+
<blockquote>
|
|
547
|
+
<p>⚠️ A new <code>elif</code> block is automatically added when you create a page via the Assistant CLI</p>
|
|
548
|
+
</blockquote>
|
|
549
|
+
<blockquote>
|
|
550
|
+
<p>⚠️ If you move or delete a component, always update <code>pageComponents.njk</code> or the site will break</p>
|
|
551
|
+
</blockquote>
|
|
552
|
+
<h3>Using Markdown files in components</h3>
|
|
553
|
+
<p>You can also render a <code>.md</code> file directly inside any <code>.njk</code> component using the <code>mdFile</code> shortcode:</p>
|
|
554
|
+
<pre><code class="language-njk">{% mdFile "docs/your-file.md" %}
|
|
555
|
+
</code></pre>
|
|
556
|
+
<p>The path is relative to the project root (where <code>.eleventy.js</code> lives).</p>
|
|
557
|
+
<blockquote>
|
|
558
|
+
<p>⚠️ The file is read at build time — changes to the <code>.md</code> file trigger a rebuild in watch mode.</p>
|
|
559
|
+
</blockquote>
|
|
560
|
+
<h2>Nest components</h2>
|
|
561
|
+
<p>A component can include other components. This is useful for breaking complex sections into smaller, reusable pieces.</p>
|
|
562
|
+
<h3>exampleComponent.njk</h3>
|
|
563
|
+
<pre><code class="language-js"><section class="hero">
|
|
564
|
+
{% include "ui/heroTitle.njk" %}
|
|
565
|
+
{% include "ui/heroButton.njk" %}
|
|
566
|
+
</section>
|
|
567
|
+
</code></pre>
|
|
568
|
+
<blockquote>
|
|
569
|
+
<p>The same path rules apply: if the included component is in a subfolder, specify the full relative path.</p>
|
|
570
|
+
</blockquote>
|
|
571
|
+
<h2>Global components</h2>
|
|
572
|
+
<p>Header and footer live in <code>src/frontend/components/global/</code> and are automatically included in every page via <code>base.njk</code>. Edit them to change the site-wide layout</p>
|
|
573
|
+
<h2>Site data in components</h2>
|
|
574
|
+
<p>All values defined in <code>src/frontend/data/site.json</code> are globally available in every component via <code>{{ site.* }}</code></p>
|
|
575
|
+
<h3>site.json <small>(<code>src/data/</code>)</small></h3>
|
|
576
|
+
<pre><code class="language-json">{
|
|
577
|
+
"title": "My Site",
|
|
578
|
+
"logo": "/img/logo.png",
|
|
579
|
+
"legal": {
|
|
580
|
+
"privacy": "/privacy"
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
</code></pre>
|
|
584
|
+
<h3>Usage in any <code>.njk</code> file</h3>
|
|
585
|
+
<pre><code class="language-js"><p>{{ site.title }}</p>
|
|
586
|
+
<a href="{{ site.legal.privacy }}">Privacy Policy</a>
|
|
587
|
+
<img src="{{ site.logo }}" alt="{{ site.title }}">
|
|
588
|
+
</code></pre>
|
|
589
|
+
|
|
590
|
+
</div>
|
|
591
|
+
<div id="content-head-seo" class="tab-content markdown-body">
|
|
592
|
+
<h1>Head & SEO</h1>
|
|
593
|
+
<p>This json holds global settings used across all pages in <code>base.njk</code> and other components:</p>
|
|
594
|
+
<h3>site.json <small><code>(src/frontend/data/)</code></small></h3>
|
|
595
|
+
<pre><code class="language-json">"site_name": "Site name",
|
|
596
|
+
"title": "Site title",
|
|
597
|
+
"description": "Site description",
|
|
598
|
+
"keywords": "keyword1, keyword2, keyword3",
|
|
599
|
+
"domain": "yoursite.com",
|
|
600
|
+
"url": "https://yoursite.com",
|
|
601
|
+
"lang": "en",
|
|
602
|
+
"author": "Name and surname",
|
|
603
|
+
"data_bs_theme": "dark",
|
|
604
|
+
"favicon": "/assets/brand/favicon.svg",
|
|
605
|
+
"logo": "/assets/brand/logo.svg",
|
|
606
|
+
...
|
|
607
|
+
</code></pre>
|
|
608
|
+
<h2>Per-page SEO and CDN</h2>
|
|
609
|
+
<p>Each page entry is keyed by its camelCase <code>title</code> from the front matter:</p>
|
|
610
|
+
<p>If you don't want to use a particular cdn inserting it in <code>base.njk</code> for all pages, you can add extra specific cdn (css, js) by inserting the link in each page of site.json separating them with a <code>,</code> and setting them in ""</p>
|
|
611
|
+
<h3>site.json <small><code>(src/frontend/data/)</code></small></h3>
|
|
612
|
+
<pre><code class="language-json">"pages": {
|
|
613
|
+
...
|
|
614
|
+
"examplePage": {
|
|
615
|
+
"seo": {
|
|
616
|
+
"title": "Example Page",
|
|
617
|
+
"description": "description"
|
|
618
|
+
},
|
|
619
|
+
"cdn": {
|
|
620
|
+
// You can leave the [] empty
|
|
621
|
+
"css": ["https://example1.com/lib.min.css", "https://example2.com/lib.min.css"],
|
|
622
|
+
"js": ["https://example1.com/lib.min.js", "https://example2.com/lib.min.js"]
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
...
|
|
626
|
+
}
|
|
627
|
+
</code></pre>
|
|
628
|
+
<h2>AI & SEO bots</h2>
|
|
629
|
+
<p><code>llms.txt</code> and <code>robots.txt</code> are generated automatically from <code>site.json</code> via their respective <code>.njk</code> files — no manual editing needed.</p>
|
|
630
|
+
<table>
|
|
631
|
+
<thead>
|
|
632
|
+
<tr>
|
|
633
|
+
<th>File</th>
|
|
634
|
+
<th>Purpose</th>
|
|
635
|
+
<th>Reachable at</th>
|
|
636
|
+
</tr>
|
|
637
|
+
</thead>
|
|
638
|
+
<tbody>
|
|
639
|
+
<tr>
|
|
640
|
+
<td><code>llms.njk</code></td>
|
|
641
|
+
<td>Tells AI models what your site is about</td>
|
|
642
|
+
<td><code>yoursite.com/llms.txt</code></td>
|
|
643
|
+
</tr>
|
|
644
|
+
<tr>
|
|
645
|
+
<td><code>robots.njk</code></td>
|
|
646
|
+
<td>Controls search engine crawling</td>
|
|
647
|
+
<td><code>yoursite.com/robots.txt</code></td>
|
|
648
|
+
</tr>
|
|
649
|
+
</tbody>
|
|
650
|
+
</table>
|
|
651
|
+
<p>To customize them, edit <code>src/llms.njk</code> or <code>src/robots.njk</code> directly.</p>
|
|
652
|
+
<h3>Customizing llms.txt</h3>
|
|
653
|
+
<p><code>src/llms.njk</code> ships with a base template — <strong>replace the placeholders with your own content</strong>:</p>
|
|
654
|
+
<pre><code># {{ site.site_name }}
|
|
655
|
+
|
|
656
|
+
> {{ site.description }}
|
|
657
|
+
|
|
658
|
+
Built by {{ site.author }} — {{ site.url }}
|
|
659
|
+
|
|
660
|
+
## Pages
|
|
661
|
+
|
|
662
|
+
- {{ site.url }}: Homepage
|
|
663
|
+
|
|
664
|
+
## Notes
|
|
665
|
+
|
|
666
|
+
- Language: {{ site.lang }}
|
|
667
|
+
- All content may be used for AI inde xing unless otherwise stated
|
|
668
|
+
</code></pre>
|
|
669
|
+
<blockquote>
|
|
670
|
+
<p>The more accurate and detailed your <code>llms.txt</code>, the better AI models will understand and reference your site.</p>
|
|
671
|
+
</blockquote>
|
|
672
|
+
<h2>Configuration field description</h2>
|
|
673
|
+
<table>
|
|
674
|
+
<thead>
|
|
675
|
+
<tr>
|
|
676
|
+
<th>Field</th>
|
|
677
|
+
<th>Purpose</th>
|
|
678
|
+
</tr>
|
|
679
|
+
</thead>
|
|
680
|
+
<tbody>
|
|
681
|
+
<tr>
|
|
682
|
+
<td><code>site_name</code></td>
|
|
683
|
+
<td>Brand name (used in meta tags)</td>
|
|
684
|
+
</tr>
|
|
685
|
+
<tr>
|
|
686
|
+
<td><code>title</code></td>
|
|
687
|
+
<td>Default page title</td>
|
|
688
|
+
</tr>
|
|
689
|
+
<tr>
|
|
690
|
+
<td><code>description</code></td>
|
|
691
|
+
<td>Default meta description</td>
|
|
692
|
+
</tr>
|
|
693
|
+
<tr>
|
|
694
|
+
<td><code>keywords</code></td>
|
|
695
|
+
<td>Default meta keywords</td>
|
|
696
|
+
</tr>
|
|
697
|
+
<tr>
|
|
698
|
+
<td><code>domain</code> / <code>url</code></td>
|
|
699
|
+
<td>Used for canonical URLs and og:url</td>
|
|
700
|
+
</tr>
|
|
701
|
+
<tr>
|
|
702
|
+
<td><code>lang</code></td>
|
|
703
|
+
<td>HTML <code>lang</code> attribute</td>
|
|
704
|
+
</tr>
|
|
705
|
+
<tr>
|
|
706
|
+
<td><code>author</code></td>
|
|
707
|
+
<td>Meta author tag</td>
|
|
708
|
+
</tr>
|
|
709
|
+
<tr>
|
|
710
|
+
<td><code>data_bs_theme</code></td>
|
|
711
|
+
<td>Bootstrap color scheme (<code>light</code> / <code>dark</code>)</td>
|
|
712
|
+
</tr>
|
|
713
|
+
<tr>
|
|
714
|
+
<td><code>favicon</code></td>
|
|
715
|
+
<td>Path to the favicon</td>
|
|
716
|
+
</tr>
|
|
717
|
+
<tr>
|
|
718
|
+
<td><code>logo</code></td>
|
|
719
|
+
<td>Path to the logo (available as <code>{{ site.logo }}</code>)</td>
|
|
720
|
+
</tr>
|
|
721
|
+
</tbody>
|
|
722
|
+
</table>
|
|
723
|
+
|
|
724
|
+
</div>
|
|
725
|
+
<div id="content-backend" class="tab-content markdown-body">
|
|
726
|
+
<h1>Backend</h1>
|
|
727
|
+
<blockquote>
|
|
728
|
+
<p><strong>PHP requirement</strong> — the backend runs only on servers with PHP support:</p>
|
|
729
|
+
<ul>
|
|
730
|
+
<li><strong>Apache</strong> (shared hosting, VPS) ✅</li>
|
|
731
|
+
<li><strong>Nginx</strong> + PHP-FPM (VPS) ✅</li>
|
|
732
|
+
<li><strong>IIS</strong> + FastCGI (Windows Server) ✅</li>
|
|
733
|
+
<li><strong>Static hosting</strong> (Netlify, Vercel, GitHub Pages, Cloudflare Pages) ❌</li>
|
|
734
|
+
</ul>
|
|
735
|
+
</blockquote>
|
|
736
|
+
<p>The backend is a PHP REST API located in <code>src/backend/</code>, copied to the output directory automatically at build time.</p>
|
|
737
|
+
<h2>Structure</h2>
|
|
738
|
+
<pre><code>src/backend/
|
|
739
|
+
├── api/
|
|
740
|
+
│ ├── public/ # Endpoints accessible without an API key
|
|
741
|
+
│ └── protected/ # Endpoints requiring X-Api-Key header
|
|
742
|
+
├── db/
|
|
743
|
+
│ ├── Database.php
|
|
744
|
+
│ ├── models/
|
|
745
|
+
│ └── migrations/
|
|
746
|
+
├── config.php # Your local config — never commit this
|
|
747
|
+
└── config.example.php
|
|
748
|
+
</code></pre>
|
|
749
|
+
<h2>Configuration</h2>
|
|
750
|
+
<p><code>config.php</code> works like a <code>.env</code> file — it holds secrets and environment settings that stay local and out of version control.</p>
|
|
751
|
+
<p>Copy <code>config.example.php</code> to <code>config.php</code> and fill in your values:</p>
|
|
752
|
+
<h3>config.php <small>(<code>src/backend/</code>)</small></h3>
|
|
753
|
+
<pre><code class="language-php">return [
|
|
754
|
+
// Default key for protected endpoints that don't have a specific key in CUSTOM_ENDPOINT_KEYS
|
|
755
|
+
'GENERAL_API_KEY' => 'default-key',
|
|
756
|
+
|
|
757
|
+
// Per-endpoint keys. For subfolder endpoints, use the relative path ('subfolder/endpoint')
|
|
758
|
+
'CUSTOM_ENDPOINT_KEYS' => [
|
|
759
|
+
// 'subfolder/endpoint' => 'example-key',
|
|
760
|
+
],
|
|
761
|
+
|
|
762
|
+
// Default CORS origins applied to every endpoint
|
|
763
|
+
'GENERAL_ALLOWED_ORIGINS' => [
|
|
764
|
+
'https://example.com',
|
|
765
|
+
],
|
|
766
|
+
|
|
767
|
+
// Per-endpoint CORS origins. Same path format as CUSTOM_ENDPOINT_KEYS.
|
|
768
|
+
// When an endpoint is listed here, these origins replace GENERAL_ALLOWED_ORIGINS for it.
|
|
769
|
+
'CUSTOM_ENDPOINT_ORIGINS' => [
|
|
770
|
+
// 'subfolder/endpoint' => ['https://app.example.com'],
|
|
771
|
+
],
|
|
772
|
+
|
|
773
|
+
'DB_HOST' => '127.0.0.1',
|
|
774
|
+
'DB_NAME' => 'example_db',
|
|
775
|
+
'DB_USER' => 'root',
|
|
776
|
+
'DB_PASS' => '',
|
|
777
|
+
];
|
|
778
|
+
</code></pre>
|
|
779
|
+
<h3>API keys</h3>
|
|
780
|
+
<p><code>GENERAL_API_KEY</code> is the fallback key for all protected endpoints. Use <code>CUSTOM_ENDPOINT_KEYS</code> to assign a different key to a specific endpoint — for subfolder endpoints, use the relative path as the key.</p>
|
|
781
|
+
<blockquote>
|
|
782
|
+
<p>⚠️ The API key travels in the <code>X-Api-Key</code> header on every request. Use it only for server-to-server calls over HTTPS. Never embed it in frontend code, where it would be publicly visible.</p>
|
|
783
|
+
</blockquote>
|
|
784
|
+
<h3>CORS (allowed origins)</h3>
|
|
785
|
+
<p>Cross-origin access mirrors the API-key model:</p>
|
|
786
|
+
<ul>
|
|
787
|
+
<li><code>GENERAL_ALLOWED_ORIGINS</code> — the default list of origins allowed to call any endpoint from a browser.</li>
|
|
788
|
+
<li><code>CUSTOM_ENDPOINT_ORIGINS</code> — overrides the default for a specific endpoint, keyed by the same relative path used in <code>CUSTOM_ENDPOINT_KEYS</code>.</li>
|
|
789
|
+
</ul>
|
|
790
|
+
<p>Origins must be exact (scheme + host, no trailing slash), e.g. <code>https://example.com</code>. When a request's <code>Origin</code> is in the resolved list, it is reflected back in <code>Access-Control-Allow-Origin</code> (with <code>Vary: Origin</code>). An empty list sends no CORS header — the most restrictive setting; same-origin requests still work. Use <code>'*'</code> as the only element to allow any origin (not recommended for protected endpoints).</p>
|
|
791
|
+
<p>Resolution order for a given endpoint: <code>CUSTOM_ENDPOINT_ORIGINS[path]</code> if present, otherwise <code>GENERAL_ALLOWED_ORIGINS</code>.</p>
|
|
792
|
+
<h2>How routing works</h2>
|
|
793
|
+
<p>The file path inside <code>api/</code> maps directly to the URL. Extra URL segments become route parameters available as <code>$requestParams[]</code>.</p>
|
|
794
|
+
<p>Every endpoint file has access to:</p>
|
|
795
|
+
<table>
|
|
796
|
+
<thead>
|
|
797
|
+
<tr>
|
|
798
|
+
<th>Variable</th>
|
|
799
|
+
<th>Description</th>
|
|
800
|
+
</tr>
|
|
801
|
+
</thead>
|
|
802
|
+
<tbody>
|
|
803
|
+
<tr>
|
|
804
|
+
<td><code>$method</code></td>
|
|
805
|
+
<td>HTTP method (<code>GET</code>, <code>POST</code>, <code>PUT</code>, <code>PATCH</code>, <code>DELETE</code>)</td>
|
|
806
|
+
</tr>
|
|
807
|
+
<tr>
|
|
808
|
+
<td><code>$requestParams</code></td>
|
|
809
|
+
<td>Extra URL segments (e.g. <code>/api/posts/42</code> → <code>['42']</code>)</td>
|
|
810
|
+
</tr>
|
|
811
|
+
</tbody>
|
|
812
|
+
</table>
|
|
813
|
+
<h2>Creating a public endpoint</h2>
|
|
814
|
+
<p>Create a <code>.php</code> file anywhere inside <code>api/public/</code></p>
|
|
815
|
+
<h3>api/public/example.php</h3>
|
|
816
|
+
<pre><code class="language-php"><?php
|
|
817
|
+
declare(strict_types=1);
|
|
818
|
+
|
|
819
|
+
require_once CORE_PATH . '/modules/Response.php';
|
|
820
|
+
|
|
821
|
+
if ($method !== 'GET') {
|
|
822
|
+
Response::error('Method not allowed', 405);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
$id = isset($requestParams[0]) ? (int)$requestParams[0] : null;
|
|
826
|
+
|
|
827
|
+
Response::success(['id' => $id]);
|
|
828
|
+
</code></pre>
|
|
829
|
+
<h2>Creating a protected endpoint</h2>
|
|
830
|
+
<p>Create a <code>.php</code> file inside <code>api/protected/</code>. The API key check happens automatically before your file runs.</p>
|
|
831
|
+
<h3>api/protected/example.php</h3>
|
|
832
|
+
<pre><code class="language-php"><?php
|
|
833
|
+
declare(strict_types=1);
|
|
834
|
+
|
|
835
|
+
require_once CORE_PATH . '/modules/Response.php';
|
|
836
|
+
|
|
837
|
+
if ($method !== 'GET') {
|
|
838
|
+
Response::error('Method not allowed', 405);
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
Response::success(['visits' => 1024]);
|
|
842
|
+
</code></pre>
|
|
843
|
+
<p>To assign a dedicated key, add it to <code>config.php</code>:</p>
|
|
844
|
+
<pre><code class="language-php">'CUSTOM_ENDPOINT_KEYS' => [
|
|
845
|
+
'endpoint' => 'custom-key',
|
|
846
|
+
],
|
|
847
|
+
</code></pre>
|
|
848
|
+
<h2>The Response helper</h2>
|
|
849
|
+
<pre><code class="language-php">Response::success($data, $code); // default 200
|
|
850
|
+
Response::error($message, $code, $details); // default 400
|
|
851
|
+
Response::noContent(); // 204
|
|
852
|
+
</code></pre>
|
|
853
|
+
<h2>Pre-built endpoints</h2>
|
|
854
|
+
<table>
|
|
855
|
+
<thead>
|
|
856
|
+
<tr>
|
|
857
|
+
<th>Route</th>
|
|
858
|
+
<th>Method</th>
|
|
859
|
+
<th>Description</th>
|
|
860
|
+
</tr>
|
|
861
|
+
</thead>
|
|
862
|
+
<tbody>
|
|
863
|
+
<tr>
|
|
864
|
+
<td><code>/api/example-public</code></td>
|
|
865
|
+
<td><code>GET</code></td>
|
|
866
|
+
<td>Example endpoint that doesn't require any key</td>
|
|
867
|
+
</tr>
|
|
868
|
+
<tr>
|
|
869
|
+
<td><code>/api/example-protected</code></td>
|
|
870
|
+
<td><code>GET</code></td>
|
|
871
|
+
<td>Example endpoint that requires X-API-KEY</td>
|
|
872
|
+
</tr>
|
|
873
|
+
</tbody>
|
|
874
|
+
</table>
|
|
875
|
+
|
|
876
|
+
</div>
|
|
877
|
+
<div id="content-deploy" class="tab-content markdown-body">
|
|
878
|
+
<h1>Server Configuration</h1>
|
|
879
|
+
<p>Berna-Stencil includes ready-made server configuration for Apache and IIS. For Nginx, a reference config is provided in the project root.</p>
|
|
880
|
+
<h2>Apache</h2>
|
|
881
|
+
<p><code>.htaccess</code> files at <code>src/frontend/</code> and <code>src/backend/</code> are automatically copied to the build output by Eleventy. No additional setup required.</p>
|
|
882
|
+
<p>Covers:</p>
|
|
883
|
+
<ul>
|
|
884
|
+
<li>Directory listing disabled</li>
|
|
885
|
+
<li>403 / 404 → <code>/404.html</code></li>
|
|
886
|
+
<li>Sensitive files blocked (<code>web.config</code>, dotfiles, etc.)</li>
|
|
887
|
+
<li><code>/api/*</code> → <code>backend/_core/index.php</code></li>
|
|
888
|
+
<li>HTTPS redirect</li>
|
|
889
|
+
</ul>
|
|
890
|
+
<h2>IIS</h2>
|
|
891
|
+
<p><code>web.config</code> files at <code>src/frontend/</code> and <code>src/backend/</code> are automatically copied to the build output by Eleventy. No additional setup required.</p>
|
|
892
|
+
<p>Covers the same rules as the Apache configuration above.</p>
|
|
893
|
+
<h2>Nginx</h2>
|
|
894
|
+
<p>Nginx does not support per-directory configuration files. The <code>nginx.conf</code> in the project root is a reference config that must be manually placed on the server.</p>
|
|
895
|
+
<h3>Setup</h3>
|
|
896
|
+
<ol>
|
|
897
|
+
<li>Copy <code>nginx.conf</code> to the server:</li>
|
|
898
|
+
</ol>
|
|
899
|
+
<pre><code class="language-bash">scp nginx.conf user@server:/etc/nginx/sites-available/your-site
|
|
900
|
+
</code></pre>
|
|
901
|
+
<ol start="2">
|
|
902
|
+
<li>Edit <code>server_name</code> and <code>root</code> to match your environment:</li>
|
|
903
|
+
</ol>
|
|
904
|
+
<pre><code class="language-nginx">server_name example.com;
|
|
905
|
+
root /var/www/html/out;
|
|
906
|
+
</code></pre>
|
|
907
|
+
<ol start="3">
|
|
908
|
+
<li>
|
|
909
|
+
<p>Add your SSL certificate paths.</p>
|
|
910
|
+
</li>
|
|
911
|
+
<li>
|
|
912
|
+
<p>Enable and reload:</p>
|
|
913
|
+
</li>
|
|
914
|
+
</ol>
|
|
915
|
+
<pre><code class="language-bash">ln -s /etc/nginx/sites-available/your-site /etc/nginx/sites-enabled/
|
|
916
|
+
nginx -t && systemctl reload nginx
|
|
917
|
+
</code></pre>
|
|
918
|
+
<h3>PHP-FPM socket</h3>
|
|
919
|
+
<p>The default socket path in <code>nginx.conf</code> targets RHEL / Fedora systems. Adjust for your distro:</p>
|
|
920
|
+
<table>
|
|
921
|
+
<thead>
|
|
922
|
+
<tr>
|
|
923
|
+
<th>Distro</th>
|
|
924
|
+
<th>Path</th>
|
|
925
|
+
</tr>
|
|
926
|
+
</thead>
|
|
927
|
+
<tbody>
|
|
928
|
+
<tr>
|
|
929
|
+
<td>RHEL / Fedora</td>
|
|
930
|
+
<td><code>unix:/run/php-fpm/php-fpm.sock</code></td>
|
|
931
|
+
</tr>
|
|
932
|
+
<tr>
|
|
933
|
+
<td>Debian / Ubuntu</td>
|
|
934
|
+
<td><code>unix:/run/php/php-fpm.sock</code></td>
|
|
935
|
+
</tr>
|
|
936
|
+
<tr>
|
|
937
|
+
<td>TCP fallback</td>
|
|
938
|
+
<td><code>127.0.0.1:9000</code></td>
|
|
939
|
+
</tr>
|
|
940
|
+
</tbody>
|
|
941
|
+
</table>
|
|
942
|
+
|
|
943
|
+
</div>
|
|
944
|
+
|
|
945
|
+
</div>
|
|
946
|
+
</div>
|
|
947
|
+
|
|
948
|
+
<style>
|
|
949
|
+
.layout-container {
|
|
950
|
+
width: 100%;
|
|
951
|
+
max-width: 1140px;
|
|
952
|
+
margin-right: auto;
|
|
953
|
+
margin-left: auto;
|
|
954
|
+
padding-right: 15px;
|
|
955
|
+
padding-left: 15px;
|
|
956
|
+
}
|
|
957
|
+
.layout-my-3 {
|
|
958
|
+
margin-top: 1rem;
|
|
959
|
+
margin-bottom: 1rem;
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
h1 {
|
|
963
|
+
text-align: center;
|
|
964
|
+
font-weight: 400;
|
|
965
|
+
}
|
|
966
|
+
h1 .berna-stencil {
|
|
967
|
+
color: #42b883;
|
|
968
|
+
font-weight: 600;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
.markdown-body {
|
|
972
|
+
background-color: transparent !important;
|
|
973
|
+
|
|
974
|
+
h1 {
|
|
975
|
+
text-align: unset;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
.slogan {
|
|
980
|
+
text-align: center;
|
|
981
|
+
color: #4b71a0;
|
|
982
|
+
font-size: 1.5rem;
|
|
983
|
+
margin-bottom: 1rem;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
.tab-content {
|
|
987
|
+
display: none;
|
|
988
|
+
animation: fadeIn 0.3s ease-in-out;
|
|
989
|
+
}
|
|
990
|
+
.tab-content.active {
|
|
991
|
+
display: block;
|
|
992
|
+
}
|
|
993
|
+
@keyframes fadeIn {
|
|
994
|
+
from { opacity: 0; transform: translateY(5px); }
|
|
995
|
+
to { opacity: 1; transform: translateY(0); }
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
.card {
|
|
999
|
+
background: #0d1526;
|
|
1000
|
+
border: 0.8px solid #1e2d4a;
|
|
1001
|
+
border-radius: 10px;
|
|
1002
|
+
padding: 18px;
|
|
1003
|
+
display: flex;
|
|
1004
|
+
flex-direction: column;
|
|
1005
|
+
gap: 0.75rem;
|
|
1006
|
+
margin-top: 1rem;
|
|
1007
|
+
margin-bottom: 1rem;
|
|
1008
|
+
transition: border-color 0.2s, transform 0.15s;
|
|
1009
|
+
text-decoration: none;
|
|
1010
|
+
}
|
|
1011
|
+
.card:hover {
|
|
1012
|
+
border-color: #42b883;
|
|
1013
|
+
transform: translateY(-2px);
|
|
1014
|
+
}
|
|
1015
|
+
.card-icon {
|
|
1016
|
+
width: 2rem;
|
|
1017
|
+
height: 2rem;
|
|
1018
|
+
color: #42b883;
|
|
1019
|
+
}
|
|
1020
|
+
.card h3 {
|
|
1021
|
+
font-size: 1.5rem;
|
|
1022
|
+
font-weight: 600;
|
|
1023
|
+
color: #cbd5e1;
|
|
1024
|
+
letter-spacing: 0.01em;
|
|
1025
|
+
margin: 0;
|
|
1026
|
+
}
|
|
1027
|
+
.card p {
|
|
1028
|
+
font-size: 1rem;
|
|
1029
|
+
color: #38649b;
|
|
1030
|
+
line-height: 1.6;
|
|
1031
|
+
margin: 0;
|
|
1032
|
+
}
|
|
1033
|
+
.card-link {
|
|
1034
|
+
display: flex;
|
|
1035
|
+
align-items: center;
|
|
1036
|
+
gap: 4px;
|
|
1037
|
+
font-size: 1rem;
|
|
1038
|
+
color: #42b883;
|
|
1039
|
+
opacity: 0.8;
|
|
1040
|
+
transition: opacity 0.15s;
|
|
1041
|
+
margin-top: auto;
|
|
1042
|
+
}
|
|
1043
|
+
.card:hover .card-link {
|
|
1044
|
+
opacity: 1;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
.guide-filter {
|
|
1048
|
+
display: flex;
|
|
1049
|
+
gap: 0.5rem;
|
|
1050
|
+
flex-wrap: wrap;
|
|
1051
|
+
justify-content: center;
|
|
1052
|
+
margin-bottom: 2rem;
|
|
1053
|
+
}
|
|
1054
|
+
.guide-filter label {
|
|
1055
|
+
cursor: pointer;
|
|
1056
|
+
}
|
|
1057
|
+
.guide-filter label input[type="radio"] {
|
|
1058
|
+
display: none;
|
|
1059
|
+
}
|
|
1060
|
+
.filter-pill {
|
|
1061
|
+
display: inline-block;
|
|
1062
|
+
padding: 5px 16px;
|
|
1063
|
+
border-radius: 20px;
|
|
1064
|
+
border: 0.8px solid #1e2d4a;
|
|
1065
|
+
background: #0d1526;
|
|
1066
|
+
color: #38649b;
|
|
1067
|
+
font-size: 0.9rem;
|
|
1068
|
+
transition: border-color 0.2s, color 0.2s, background 0.2s;
|
|
1069
|
+
user-select: none;
|
|
1070
|
+
}
|
|
1071
|
+
.guide-filter input[type="radio"]:checked + .filter-pill {
|
|
1072
|
+
border-color: #42b883;
|
|
1073
|
+
color: #42b883;
|
|
1074
|
+
background: rgba(66, 184, 131, 0.08);
|
|
1075
|
+
}
|
|
1076
|
+
.guide-filter label:hover .filter-pill {
|
|
1077
|
+
border-color: #42b883;
|
|
1078
|
+
color: #42b883;
|
|
1079
|
+
}
|
|
1080
|
+
</style>
|
|
1081
|
+
|
|
1082
|
+
<script>
|
|
1083
|
+
document.querySelectorAll('input[name="guide-filter"]').forEach((radio) => {
|
|
1084
|
+
radio.addEventListener("change", (e) => {
|
|
1085
|
+
document.querySelectorAll(".tab-content").forEach((content) => {
|
|
1086
|
+
content.classList.remove("active");
|
|
1087
|
+
});
|
|
1088
|
+
const targetId = `content-${e.target.value}`;
|
|
1089
|
+
const targetContent = document.getElementById(targetId);
|
|
1090
|
+
if (targetContent) {
|
|
1091
|
+
targetContent.classList.add("active");
|
|
1092
|
+
}
|
|
1093
|
+
});
|
|
1094
|
+
});
|
|
1095
|
+
</script>
|
|
1096
|
+
|
|
1097
|
+
|
|
1098
|
+
</main>
|
|
1099
|
+
|
|
1100
|
+
<footer>
|
|
1101
|
+
<div>
|
|
1102
|
+
<h6>Site title</h6>
|
|
1103
|
+
<p>Site description</p>
|
|
1104
|
+
</div>
|
|
1105
|
+
|
|
1106
|
+
<div>
|
|
1107
|
+
<h6>Sitemap</h6>
|
|
1108
|
+
<ul>
|
|
1109
|
+
<li><a href="/">Homepage</a></li>
|
|
1110
|
+
</ul>
|
|
1111
|
+
</div>
|
|
1112
|
+
<div>
|
|
1113
|
+
© 2026 Copyright text
|
|
1114
|
+
</div>
|
|
1115
|
+
<div>
|
|
1116
|
+
<h6>Legal</h6>
|
|
1117
|
+
<small>
|
|
1118
|
+
<ul>
|
|
1119
|
+
<li><a href="">Privacy Policy</a></li>
|
|
1120
|
+
<li><a href="">Cookie Policy</a></li>
|
|
1121
|
+
<li><a href="">Terms and conditions</a></li>
|
|
1122
|
+
</ul>
|
|
1123
|
+
</small>
|
|
1124
|
+
</div>
|
|
1125
|
+
</div>
|
|
1126
|
+
</footer>
|
|
1127
|
+
|
|
1128
|
+
|
|
1129
|
+
|
|
1130
|
+
|
|
1131
|
+
<script src="/js/bootstrap.bundle.min.js" defer></script>
|
|
1132
|
+
|
|
1133
|
+
|
|
1134
|
+
|
|
1135
|
+
|
|
1136
|
+
|
|
1137
|
+
|
|
1138
|
+
|
|
1139
|
+
|
|
1140
|
+
|
|
1141
|
+
|
|
1142
|
+
|
|
1143
|
+
<script src="/js/pages/homepage.js"></script>
|
|
1144
|
+
|
|
1145
|
+
</body>
|
|
1146
|
+
|
|
1147
|
+
</html>
|