├── templates
└── _layouts
│ ├── _base
│ ├── ajax.twig
│ └── web.twig
│ └── html-page.twig
├── composer.json
├── LICENSE.md
└── README.md
/templates/_layouts/_base/ajax.twig:
--------------------------------------------------------------------------------
1 | {# -- Base layout template that all AJAX requests inherit from -- #}
2 | {#
3 | ┌─────────────────────────────────────────────────────────────────────────────┐
4 | │ ┌─────────────────────────────────────────────────────────────────────────┐ │
5 | │ │ content │ │
6 | │ └─────────────────────────────────────────────────────────────────────────┘ │
7 | │ htmlPage │
8 | └─────────────────────────────────────────────────────────────────────────────┘
9 | #}
10 | {% block htmlPage %}
11 | {# -- Primary content block -- #}
12 | {% block content %}
13 | No content block defined.
14 | {% endblock content %}
15 | {% endblock htmlPage %}
16 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nystudio107/craft-twig-base-templates",
3 | "description": "Craft CMS Twig base templates",
4 | "type": "twig-bundle",
5 | "version": "1.0.1",
6 | "keywords": [
7 | "twig",
8 | "twig-bundle",
9 | "composer",
10 | "installer",
11 | "bundle"
12 | ],
13 | "homepage": "https://nystudio107.com",
14 | "require": {
15 | "craftcms/cms": "*",
16 | "nystudio107/twig-bundle-installer": "^1.0.0"
17 | },
18 | "support": {
19 | "email": "info@nystudio107.com",
20 | "issues": "https://github.com/nystudio107/craft-twig-base-templates/issues",
21 | "source": "https://github.com/nystudio107/craft-twig-base-templates",
22 | "docs": "https://github.com/nystudio107/craft-twig-base-templates"
23 | },
24 | "license": "MIT",
25 | "minimum-stability": "stable"
26 | }
27 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) nystudio107.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/templates/_layouts/_base/web.twig:
--------------------------------------------------------------------------------
1 | {# -- Base web layout template that all web requests inherit from -- #}
2 | {#
3 | ┌─────────────────────────────────────────────────────────────────────────────┐
4 | │ ┌─────────────────────────────────────────────────────────────────────────┐ │
5 | │ │ htmlTag │ │
6 | │ └─────────────────────────────────────────────────────────────────────────┘ │
7 | │ ┌─────────────────────────────────────────────────────────────────────────┐ │
8 | │ │ headTag │ │
9 | │ └─────────────────────────────────────────────────────────────────────────┘ │
10 | │ ┌─────────────────────────────────────────────────────────────────────────┐ │
11 | │ │ headContent │ │
12 | │ └─────────────────────────────────────────────────────────────────────────┘ │
13 | │ ┌─────────────────────────────────────────────────────────────────────────┐ │
14 | │ │ bodyTag │ │
15 | │ └─────────────────────────────────────────────────────────────────────────┘ │
16 | │ ┌─────────────────────────────────────────────────────────────────────────┐ │
17 | │ │ bodyContent │ │
18 | │ └─────────────────────────────────────────────────────────────────────────┘ │
19 | │ htmlPage │
20 | └─────────────────────────────────────────────────────────────────────────────┘
21 | #}
22 | {% set docPage %}
23 | {% block htmlPage %}
24 |
25 | {% block htmlTag %}
26 |
27 | {% endblock htmlTag %}
28 | {% block headTag %}
29 |
30 | {% endblock headTag %}
31 | {# -- Page content that should be included in the -- #}
32 | {% block headContent %}
33 | {% endblock headContent %}
34 |
35 |
36 | {% block bodyTag %}
37 |
38 | {% endblock bodyTag %}
39 | {# -- Page content that should be included in the -- #}
40 | {% block bodyContent %}
41 | {% endblock bodyContent %}
42 |
43 |
44 | {% endblock htmlPage %}
45 | {% endset %} {{ craft.app.config.general.devMode ? docPage : docPage | spaceless }}
46 |
--------------------------------------------------------------------------------
/templates/_layouts/html-page.twig:
--------------------------------------------------------------------------------
1 | {# -- Base HTML layout template that all HTML requests inherit from -- #}
2 | {#
3 | ┌─────────────────────────────────────────────────────────────────────────────┐
4 | │ ┌─────────────────────────────────────────────────────────────────────────┐ │
5 | │ │ headMeta │ │
6 | │ └─────────────────────────────────────────────────────────────────────────┘ │
7 | │ ┌─────────────────────────────────────────────────────────────────────────┐ │
8 | │ │ headLinks │ │
9 | │ └─────────────────────────────────────────────────────────────────────────┘ │
10 | │ ┌─────────────────────────────────────────────────────────────────────────┐ │
11 | │ │ headScripts │ │
12 | │ └─────────────────────────────────────────────────────────────────────────┘ │
13 | │ ┌─────────────────────────────────────────────────────────────────────────┐ │
14 | │ │ headStyles │ │
15 | │ └─────────────────────────────────────────────────────────────────────────┘ │
16 | │ headContent │
17 | └─────────────────────────────────────────────────────────────────────────────┘
18 | ┌─────────────────────────────────────────────────────────────────────────────┐
19 | │ ┌─────────────────────────────────────────────────────────────────────────┐ │
20 | │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │
21 | │ │ │ preContent │ │ │
22 | │ │ └─────────────────────────────────────────────────────────────────────┘ │ │
23 | │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │
24 | │ │ │ content │ │ │
25 | │ │ └─────────────────────────────────────────────────────────────────────┘ │ │
26 | │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │
27 | │ │ │ postContent │ │ │
28 | │ │ └─────────────────────────────────────────────────────────────────────┘ │ │
29 | │ │ bodyHtml │ │
30 | │ └─────────────────────────────────────────────────────────────────────────┘ │
31 | │ ┌─────────────────────────────────────────────────────────────────────────┐ │
32 | │ │ bodyScripts │ │
33 | │ └─────────────────────────────────────────────────────────────────────────┘ │
34 | │ bodyContent │
35 | └─────────────────────────────────────────────────────────────────────────────┘
36 | #}
37 | {% extends craft.app.request.isAjax() and not craft.app.request.getIsPreview()
38 | ? "vendor/nystudio107/craft-twig-base-templates/templates/_layouts/_base/ajax.twig"
39 | : "vendor/nystudio107/craft-twig-base-templates/templates/_layouts/_base/web.twig" %}
40 |
41 | {% block htmlTag %}
42 |
43 | {% endblock htmlTag %}
44 |
45 | {# -- Page content that should be included in the -- #}
46 | {% block headContent %}
47 | {# -- Any tags that should be included in the -- #}
48 | {% block headMeta %}
49 |
50 |
51 | {% endblock headMeta %}
52 |
53 | {# -- Any tags that should be included in the -- #}
54 | {% block headLinks %}
55 | {% endblock headLinks %}
56 |
57 | {# -- Any tags that should be included in the -- #}
58 | {% block headScripts %}
59 | {% endblock headScripts %}
60 |
61 | {# -- Any tags that should be included in the -- #}
62 | {% block headStyles %}
63 | {% endblock headStyles %}
64 |
65 | {% endblock headContent %}
66 |
67 | {# -- Page content that should be included in the -- #}
68 | {% block bodyContent %}
69 | {# -- Content that appears before the primary content block -- #}
70 | {% block preContent %}
71 | {% endblock preContent %}
72 |
73 | {# -- Primary content block -- #}
74 | {% block content %}
75 | {# -- Only content here will be rendered for AJAX requests -- #}
76 | {% endblock content %}
77 |
78 | {# -- Content that appears after the primary content block -- #}
79 | {% block postContent %}
80 | {% endblock postContent %}
81 |
82 | {# -- Any tags that should be included before -- #}
83 | {% block bodyScripts %}
84 | {% endblock bodyScripts %}
85 |
86 | {% endblock bodyContent %}
87 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Craft Twig Base Templates
2 |
3 | Craft CMS Twig base templates that provide flexible agnostic layout templates for any web-based Craft CMS project.
4 |
5 | They provide a base layout with baked-in blocks to structure the various sections of the `` and `` HTML tags.
6 |
7 | They also automatically handle both regular web requests and AJAX requests for the same page, returning only the content without the UX chrome for AJAX requests.
8 |
9 | These templates are installed automatically via the [Twig Bundle Installer](https://github.com/nystudio107/twig-bundle-installer) so they can be easily updated like any Composer dependency.
10 |
11 | ## Requirements
12 |
13 | These templates Craft CMS 3 or later (fully compatible with Craft CMS 4 & 5).
14 |
15 | ## Installation
16 |
17 | To install the Craft Twig Base Templates, follow these steps:
18 |
19 | 1. Follow the instructions for installing the [Twig Bundle Installer](https://github.com/nystudio107/twig-bundle-installer?tab=readme-ov-file#adding-twig-bundles-to-your-project) Composer plugin into your project
20 |
21 | 2. Open your terminal and go to your Craft project:
22 |
23 | cd /path/to/project
24 |
25 | 3. Then tell Composer to require the Craft Twig Base Templates package:
26 |
27 | composer require nystudio107/craft-twig-base-templates
28 |
29 | The templates will then be installed into the git-ignored `vendor/` directory inside of your Twig `templates/` directory.
30 |
31 | ## Usage
32 |
33 | The layouts are intentionally bare-bones, providing a sane structure on which any Craft CMS site can be built. The value provided is largely structural and organizational, at the expense of out of the box functionality.
34 |
35 | You can use the `html-page.twig` directly, or you can `extends` it with your own layout template that adds functionality you want available to all of your pages.
36 |
37 | ### Extending the `html-page.twig` base layout
38 |
39 | In your layout or page templates, extend the `html-page.twig` as follows:
40 |
41 | ```twig
42 | {% extends "vendor/nystudio107/craft-twig-base-templates/templates/_layouts/html-page.twig" %}
43 | ```
44 |
45 | ### The base layout blocks
46 |
47 | The layout has the following blocks defined that you can override as you see fit:
48 |
49 | ```
50 | ┌─────────────────────────────────────────────────────────────────────────────┐
51 | │ ┌─────────────────────────────────────────────────────────────────────────┐ │
52 | │ │ headMeta │ │
53 | │ └─────────────────────────────────────────────────────────────────────────┘ │
54 | │ ┌─────────────────────────────────────────────────────────────────────────┐ │
55 | │ │ headLinks │ │
56 | │ └─────────────────────────────────────────────────────────────────────────┘ │
57 | │ ┌─────────────────────────────────────────────────────────────────────────┐ │
58 | │ │ headScripts │ │
59 | │ └─────────────────────────────────────────────────────────────────────────┘ │
60 | │ ┌─────────────────────────────────────────────────────────────────────────┐ │
61 | │ │ headStyles │ │
62 | │ └─────────────────────────────────────────────────────────────────────────┘ │
63 | │ headContent │
64 | └─────────────────────────────────────────────────────────────────────────────┘
65 | ┌─────────────────────────────────────────────────────────────────────────────┐
66 | │ ┌─────────────────────────────────────────────────────────────────────────┐ │
67 | │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │
68 | │ │ │ preContent │ │ │
69 | │ │ └─────────────────────────────────────────────────────────────────────┘ │ │
70 | │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │
71 | │ │ │ content │ │ │
72 | │ │ └─────────────────────────────────────────────────────────────────────┘ │ │
73 | │ │ ┌─────────────────────────────────────────────────────────────────────┐ │ │
74 | │ │ │ postContent │ │ │
75 | │ │ └─────────────────────────────────────────────────────────────────────┘ │ │
76 | │ │ bodyHtml │ │
77 | │ └─────────────────────────────────────────────────────────────────────────┘ │
78 | │ ┌─────────────────────────────────────────────────────────────────────────┐ │
79 | │ │ bodyScripts │ │
80 | │ └─────────────────────────────────────────────────────────────────────────┘ │
81 | │ bodyContent │
82 | └─────────────────────────────────────────────────────────────────────────────┘
83 | ```
84 |
85 | Here's a breakdown of the blocks, and intended uses for each:
86 |
87 | * **`headContent`** - Wrapper block for everything that appears inside of the `` tag
88 | * **`headMeta`** - For `` tags such as ``
89 | * **`headLinks`** - For `` tags such as ``
90 | * **`headScripts`** - For `` that should appear inside the `` tag
91 | * **`headStyles`** - For any inline (critical) CSS `` tags
92 | * **`bodyContent`** - Wrapper block for everything that appears inside of the `` tag
93 | * **`bodyHtml`** - Wrapper block for HTML content that appears inside of the `` tag
94 | * **`preContent`** - HTML content that appears before the primary `content` block (such as a navbar or a site header)
95 | * **`content`** - The primary HTML content for the page. This is the only block rendered for AJAX requests
96 | * **`postContent`** - HTML content that appears after the primary `content` block (such as links or a site footer)
97 | * **`bodyScripts`** - For `` that should appear before the `` tag
98 |
99 | As a rule of thumb, override only the most specific block you need to. For instance, to add content to your page in a template that extends the `html-page` layout, just override the `content` block:
100 |
101 | ```twig
102 | {% block content %}
103 |
Some title
104 |
Some content
105 | {% endblock content %}
106 | ```
107 |
108 | ...rather than overriding the `bodyHtml` block.
109 |
110 | However, if you need to provide HTML that wraps your `content` block, you're free to do so as well:
111 |
112 | ```twig
113 | {% block bodyHtml %}
114 |
126 | {% endblock bodyHtml %}
127 | ```
128 |
129 | ### Rendering Parent Block Content
130 |
131 | You'll notice that even in these very basic base layout templates, some of the blocks have content in them, for example the `headMeta` block:
132 |
133 | ```twig
134 | {# -- Any tags that should be included in the -- #}
135 | {% block headMeta %}
136 |
137 |
138 | {% endblock headMeta %}
139 | ```
140 |
141 | This is provided as a convenience for you, because almost every website will want to have these tags on them.
142 |
143 | If you override a block in your own layout or page templates, your content will be displayed instead of the parent block's content.
144 |
145 | However, you can use `{{ parent() }}` to render the parent block's content, while also adding your own content to it:
146 |
147 | ```twig
148 | {% block headMeta %}
149 | {{ parent() }}
150 |
151 | {% endblock headMeta %}
152 | ```
153 |
154 | The above will render the content from the `html-page.twig`'s `headMeta` block, and then also output your content as well.
155 |
156 | ### The special `content` block for AJAX
157 |
158 | The `content` block is handled specially, in that when the incoming request is a web request, it will render the page normally with all of the UX chrome from the various blocks specified above.
159 |
160 | When the incoming request is an AJAX request, instead **only** the `content` block is rendered and returned.
161 |
162 | This allows you to easily create full web pages for your content (great for SEO and indexing) while also providing that same content in a modal or other presentation via JavaScript and AJAX requests.
163 |
164 | ### Advanced customization
165 |
166 | In addition to the blocks provided by the `html-page.twig` layout, further customization of the rendered page is available to you by overriding the blocks in the `web.twig` layout template (which the `html-page.twig` layout extends from):
167 |
168 | ```
169 | ┌─────────────────────────────────────────────────────────────────────────────┐
170 | │ ┌─────────────────────────────────────────────────────────────────────────┐ │
171 | │ │ htmlTag │ │
172 | │ └─────────────────────────────────────────────────────────────────────────┘ │
173 | │ ┌─────────────────────────────────────────────────────────────────────────┐ │
174 | │ │ headTag │ │
175 | │ └─────────────────────────────────────────────────────────────────────────┘ │
176 | │ ┌─────────────────────────────────────────────────────────────────────────┐ │
177 | │ │ headContent │ │
178 | │ └─────────────────────────────────────────────────────────────────────────┘ │
179 | │ ┌─────────────────────────────────────────────────────────────────────────┐ │
180 | │ │ bodyTag │ │
181 | │ └─────────────────────────────────────────────────────────────────────────┘ │
182 | │ ┌─────────────────────────────────────────────────────────────────────────┐ │
183 | │ │ bodyContent │ │
184 | │ └─────────────────────────────────────────────────────────────────────────┘ │
185 | │ htmlPage │
186 | └─────────────────────────────────────────────────────────────────────────────┘
187 | ```
188 |
189 | So if you need a ``, `` or `` tag with specific attributes on it, you can do that easily:
190 |
191 | ```twig
192 | {% block htmlTag %}
193 |
194 | {% endblock htmlTag %}
195 | ```
196 |
197 | ```twig
198 | {% block headTag %}
199 |
200 | {% endblock headTag %}
201 | ```
202 |
203 | ```twig
204 | {% block bodyTag %}
205 |
206 | {% endblock bodyTag %}
207 | ```
208 |
209 | You can also entirely replace the content wrapped in the `` or `` tags with:
210 |
211 | ```twig
212 | {% block headContent %}
213 | {# -- anything you like -- #}
214 | {% endblock headContent %}
215 | ```
216 |
217 | ```twig
218 | {% block bodyContent %}
219 | {# -- anything you like -- #}
220 | {% endblock bodyContent %}
221 | ```
222 |
223 | You can even entirely replace everything rendered on the page by overriding `htmlPage` block that encompasses everything the page renders:
224 |
225 | ```twig
226 | {% block htmlPage %}
227 | {# -- anything you like -- #}
228 | {% endblock htmlPage %}
229 | ```
230 |
231 | ## Roadmap
232 |
233 | Some things to do, and ideas for potential features:
234 |
235 | * Add more layouts that extend off of the `html-page.twig` to provide additional opinionated functionality
236 |
237 | Brought to you by [nystudio107](http://nystudio107.com)
238 |
--------------------------------------------------------------------------------