├── 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 |
115 | {% block preContent %} 116 |

Site header

117 | {% endblock preContent %} 118 | {% block content %} 119 |

Content title

120 |

Some content

121 | {% endblock content %} 122 | {% block postContent %} 123 |

Site footer

124 | {% endblock postContent %} 125 |
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 | --------------------------------------------------------------------------------