In both #597 and #467 possible solutions include making changes to the existing themes so that users can override part of the template without having to re-implement the entire template. While those issues address specific requests, this is intended as an attempt to define a general model/policy for how this should work. Once the decisions are made, someone(s) can go through the templates and start to implement it.
I see three potential approaches:
- Includes:
Break out each overridable piece of the template into a separate file and use an include in the parent template to pull it in. The parent template would look like this:
<div>
<!-- some content -->
</div>
{% include 'somefeature.html' %}
<div>
<!-- some more content -->
</div>
Then the file somefeature.html would simply contain the template code to implement "somefeature".
If a user wants to override "somefeature," she can create a directory and assign it to the theme_dir config setting. Within that dir, she then creates a file somefeature.html which contains the replacement template for that feature. When the build process runs, Jinja should check the theme_dir first for each "included" template and fall back to the dir of the theme assigned to the theme config setting. That way, the user defined template gets precedence and a user can override a given feature without messing with the entire template.
For example, to disable analytics, a user would simply create a blank file with the appropriate name (analytics.html) and save it to the theme_dir. Or if a user wanted to use a service other than Google's, she could create the appropriate file (analytics.html) which contains the HTML required by her service of choice.
2. Blocks (template inheritance):
Wrap each overridable piece in a separate block, like this:
<div>
<!-- some content -->
</div>
{% block somefeature %}
<!-- implementation here -->
{% endblock %}
<div>
<!-- some more content -->
</div>
The default implementation would then be defined right there in the built-in template (as it is now).
To override "somefeature," the user would create a directory and assign it to the theme_dir config setting. Within that dir, she then creates a template file which "extends" the theme's base template:
{% extends "base.html" %}
{% block somefeature %}
<!-- custom implementation here -->
{% endblock %}
Jinja would need to be configured so that if a base template exists in the users theme_dir is loads that template as the base template, but it also will look in the theme's dir to load templates which the user's base would extend.
3. Blocks and Includes:
Implement both solutions together so that the include statement is wrapped in a block, like this:
<div>
<!-- some content -->
</div>
{% block somefeature %}
{% include 'somefeature.html' %}
{% endblock %}
<div>
<!-- some more content -->
</div>
Then the user can choose either method to override the default behavior.
Note that in all three scenarios the user does not need to change any settings to alter the behavior except for the theme_dir setting. If a user desires to add their own items to the context (to make them available to their custom template), they can use the extra setting.
A benefit of option 1 is that each feature is broken out into a separate file, which keeps everything modular. Of course, as more features are broken out, the number of files grows. And as a user overrides more features, that users number of files also grows. But users don't need to know anything about template inheritance.
A benefit of option 2 is that there is no additional files (everything can be contained in one file). And the user only needs to add one file. However, that one file could become unwieldy, expressly for beginners. Additionally, users need to understand how inheritance works to at least some extent, which could be overwhelming for beginners.
Option 3 provides the benefits of both and allows the users to eliminate the negatives according to their individual priorities.
Personally, I like option 3. My suggestion would be to use includes (wrapped in blocks) for a few of the more popular features and then wrap most (all) of the various parts of the template in a series of blocks. Fully document the includes (with example code, etc.) for beginners and simply mention that the blocks exist for advanced users.
This proposal was based in part on observing how various Sphinx themes have addressed this issue. A few have implemented option 3 and those themes now have a bunch of alternate themes available (some thirds party, some built-in) which inherit from them using blocks. At the same time, users can easily override a few basic features using includes. When the alternate theme also makes use of the includes (often including the parent's implementation), the user can even use the same override mechanisms when using the alternate theme. To be clear, I'm not suggesting MkDocs should strive to build up such a large collection of (only slightly different) built-in themes, or even support multiple levels of inheritance. I just mention it as an example of the flexibility option 3 could potentially provide users.
Any and all feedback is welcome.
In both #597 and #467 possible solutions include making changes to the existing themes so that users can override part of the template without having to re-implement the entire template. While those issues address specific requests, this is intended as an attempt to define a general model/policy for how this should work. Once the decisions are made, someone(s) can go through the templates and start to implement it.
I see three potential approaches:
Break out each overridable piece of the template into a separate file and use an include in the parent template to pull it in. The parent template would look like this:
Then the file
somefeature.htmlwould simply contain the template code to implement "somefeature".If a user wants to override "somefeature," she can create a directory and assign it to the
theme_dirconfig setting. Within that dir, she then creates a filesomefeature.htmlwhich contains the replacement template for that feature. When the build process runs, Jinja should check thetheme_dirfirst for each "included" template and fall back to the dir of the theme assigned to thethemeconfig setting. That way, the user defined template gets precedence and a user can override a given feature without messing with the entire template.For example, to disable analytics, a user would simply create a blank file with the appropriate name (
analytics.html) and save it to thetheme_dir. Or if a user wanted to use a service other than Google's, she could create the appropriate file (analytics.html) which contains the HTML required by her service of choice.2. Blocks (template inheritance):
Wrap each overridable piece in a separate block, like this:
The default implementation would then be defined right there in the built-in template (as it is now).
To override "somefeature," the user would create a directory and assign it to the
theme_dirconfig setting. Within that dir, she then creates a template file which "extends" the theme's base template:Jinja would need to be configured so that if a base template exists in the users
theme_diris loads that template as the base template, but it also will look in thetheme's dir to load templates which the user's base would extend.3. Blocks and Includes:
Implement both solutions together so that the
includestatement is wrapped in ablock, like this:Then the user can choose either method to override the default behavior.
Note that in all three scenarios the user does not need to change any settings to alter the behavior except for the
theme_dirsetting. If a user desires to add their own items to the context (to make them available to their custom template), they can use theextrasetting.A benefit of option 1 is that each feature is broken out into a separate file, which keeps everything modular. Of course, as more features are broken out, the number of files grows. And as a user overrides more features, that users number of files also grows. But users don't need to know anything about template inheritance.
A benefit of option 2 is that there is no additional files (everything can be contained in one file). And the user only needs to add one file. However, that one file could become unwieldy, expressly for beginners. Additionally, users need to understand how inheritance works to at least some extent, which could be overwhelming for beginners.
Option 3 provides the benefits of both and allows the users to eliminate the negatives according to their individual priorities.
Personally, I like option 3. My suggestion would be to use includes (wrapped in blocks) for a few of the more popular features and then wrap most (all) of the various parts of the template in a series of blocks. Fully document the includes (with example code, etc.) for beginners and simply mention that the blocks exist for advanced users.
This proposal was based in part on observing how various Sphinx themes have addressed this issue. A few have implemented option 3 and those themes now have a bunch of alternate themes available (some thirds party, some built-in) which inherit from them using blocks. At the same time, users can easily override a few basic features using includes. When the alternate theme also makes use of the includes (often including the parent's implementation), the user can even use the same override mechanisms when using the alternate theme. To be clear, I'm not suggesting MkDocs should strive to build up such a large collection of (only slightly different) built-in themes, or even support multiple levels of inheritance. I just mention it as an example of the flexibility option 3 could potentially provide users.
Any and all feedback is welcome.