docs: Update theme documentation

See #4460
This commit is contained in:
Bjørn Erik Pedersen 2018-06-12 07:38:41 +02:00
parent 80949dc73f
commit c74b0f8f9b
No known key found for this signature in database
GPG key ID: 330E6E2BD4859D8F
7 changed files with 172 additions and 263 deletions

View file

@ -366,7 +366,7 @@ For merging of content from other languages (i.e. missing content translations),
To support Multilingual mode in your themes, some considerations must be taken for the URLs in the templates. If there is more than one language, URLs must meet the following criteria:
* Come from the built-in `.Permalink` or `.URL`
* Come from the built-in `.Permalink` or `.RelPermalink`
* Be constructed with
* The [`relLangURL` template function][rellangurl] or the [`absLangURL` template function][abslangurl] **OR**
* Prefixed with `{{ .LanguagePrefix }}`

View file

@ -58,14 +58,7 @@ Section
## Hugo Layouts Lookup Rules With Theme
In Hugo, layouts can live in either the project's or theme's layout folder, and the most specific layout will be chosen. Hugo will interleave the lookups:
1. layouts/page/index.html
2. demoTheme/layouts/page/index.html
3. layouts/...
This way it is possible to override specific templates from the theme.
In Hugo, layouts can live in either the project's or the themes' layout folders, and the most specific layout will be chosen. Hugo will interleave the lookups listed below, finding the most specific one either in the project or themes.
## Examples: Layout Lookup for Regular Pages

View file

@ -29,59 +29,44 @@ Hugo can initialize a new blank theme directory within your existing `themes` us
hugo new theme [name]
```
## Theme Components
## Theme Folders
A theme consists of templates and static assets such as javascript and css files. Themes can also provide [archetypes][], which are archetypal content types used by the `hugo new` command to scaffold new content files with preconfigured front matter.
A theme component can provide files in one or more of the following standard Hugo folders:
layouts
: Templates used to render content in Hugo. Also see [Templates Lookup Order](/templates/lookup-order/).
static
: Static files, such as logos, CSS and JavaScript.
i18n
: Language bundles.
data
: Data files.
archetypes
: Content templates used in `hugo new`.
## Theme Configuration File
A theme component can also provide its own [Configuration File](/getting-started/configuration/), e.g. `config.toml`. There are some restrictions to what can be configured in a theme component, and it is not possible to overwrite settings in the project.
The following settings can be set:
* `params` (global and per language)
* `menu` (global and per language)
* `outputformats` and `mediatypes`
## Theme Description File
In addition to the configuration file, a theme can also provide a `theme.toml` file that describes the theme, the author and origin etc. See [Add Your Hugo Theme to the Showcase](/contribute/themes/).
{{% note "Use the Hugo Generator Tag" %}}
The [`.Hugo.Generator`](/variables/hugo/) tag is included in all themes featured in the [Hugo Themes Showcase](http://themes.gohugo.io). We ask that you include the generator tag in all sites and themes you create with Hugo to help the core team track Hugo's usage and popularity.
{{% /note %}}
## Layouts
Hugo is built around the concept that things should be as simple as possible.
Fundamentally, website content is displayed in two different ways, a single
piece of content and a list of content items. With Hugo, a theme layout starts
with the defaults. As additional layouts are defined, they are used for the
content type or section they apply to. This keeps layouts simple, but permits
a large amount of flexibility.
## Single Content
The default single file layout is located at `layouts/_default/single.html`.
## List of Contents
The default list file layout is located at `layouts/_default/list.html`.
## Partial Templates
Theme creators should liberally use [partial templates](/templates/partials/)
throughout their theme files. Not only is a good DRY practice to include shared
code, but partials are a special template type that enables the themes end user
to be able to overwrite just a small piece of a file or inject code into the
theme from their local /layouts. These partial templates are perfect for easy
injection into the theme with minimal maintenance to ensure future
compatibility.
## Static
Everything in the static directory will be copied directly into the final site
when rendered. No structure is provided here to enable complete freedom. It is
common to organize the static content into:
```
/css
/js
/img
```
The actual structure is entirely up to you, the theme creator, on how you would like to organize your files.
## Archetypes
If your theme makes use of specific keys in the front matter, it is a good idea
to provide an archetype for each content type you have. [Read more about archetypes][archetypes].
[archetypes]: /content-management/archetypes/

View file

@ -1,80 +0,0 @@
---
title: Customize a Theme
linktitle: Customize a Theme
description: Customize a theme by overriding theme layouts and static assets in your top-level project directories.
date: 2017-02-01
publishdate: 2017-02-01
lastmod: 2017-02-01
categories: [themes]
keywords: [themes, source, organization, directories]
menu:
docs:
parent: "themes"
weight: 20
weight: 20
sections_weight: 20
draft: false
aliases: [/themes/customize/]
toc: true
wip: true
---
The following are key concepts for Hugo site customization with themes. Hugo permits you to supplement *or* override any theme template or static file with files in your working directory.
{{% note %}}
When you use a theme cloned from its git repository, do not edit the theme's files directly. Instead, theme customization in Hugo is a matter of *overriding* the templates made available to you in a theme. This provides the added flexibility of tweaking a theme to meet your needs while staying current with a theme's upstream.
{{% /note %}}
## Override Static Files
There are times where you want to include static assets that differ from versions of the same asset that ships with a theme.
For example, a theme may use jQuery 1.8 in the following location:
```
/themes/<THEME>/static/js/jquery.min.js
```
You want to replace the version of jQuery that ships with the theme with the newer `jquery-3.1.1.js`. The easiest way to do this is to replace the file *with a file of the same name* in the same relative path in your project's root. Therefore, change `jquery-3.1.1.js` to `jquery.min.js` so that it is *identical* to the theme's version and place the file here:
```
/static/js/jquery.min.js
```
## Override Template Files
Anytime Hugo looks for a matching template, it will first check the working directory before looking in the theme directory. If you would like to modify a template, simply create that template in your local `layouts` directory.
The [template lookup order][lookup] explains the rules Hugo uses to determine which template to use for a given piece of content. Read and understand these rules carefully.
This is especially helpful when the theme creator used [partial templates][partials]. These partial templates are perfect for easy injection into the theme with minimal maintenance to ensure future compatibility.
For example:
```
/themes/<THEME>/layouts/_default/single.html
```
Would be overwritten by
```
/layouts/_default/single.html
```
{{% warning %}}
This only works for templates that Hugo "knows about" (i.e., that follow its convention for folder structure and naming). If a theme imports template files in a creatively named directory, Hugo wont know to look for the local `/layouts` first.
{{% /warning %}}
## Override Archetypes
If the archetype that ships with the theme for a given content type (or all content types) doesnt fit with how you are using the theme, feel free to copy it to your `/archetypes` directory and make modifications as you see fit.
{{% warning "Beware of `layouts/_default`" %}}
The `_default` directory is a very powerful force in Hugo, especially as it pertains to overwriting theme files. If a default file is located in the local [archetypes](/content-management/archetypes/) or layout directory (i.e., `archetypes/default.md` or `/layouts/_default/*.html`, respectively), it will override the file of the same name in the corresponding theme directory (i.e., `themes/<THEME>/archetypes/default.md` or `themes/<THEME>/layout/_defaults/*.html`, respectively).
It is usually better to override specific files; i.e. rather than using `layouts/_default/*.html` in your working directory.
{{% /warning %}}
[archetypes]: /content-management/archetypes/
[lookup]: /templates/lookup-order/
[partials]: /templates/partials/

View file

@ -0,0 +1,51 @@
---
title: Theme Components
linktitle: Theme Components
description: Hugo provides advanced theming support with Theme Components.
date: 2017-02-01
categories: [themes]
keywords: [themes, theme, source, organization, directories]
menu:
docs:
parent: "themes"
weight: 20
weight: 20
sections_weight: 20
draft: false
aliases: [/themes/customize/,/themes/customizing/]
toc: true
---
Since Hugo `0.42` a project can configure a theme as a composite of as many theme components you need:
{{< code-toggle file="config">}}
theme = ["my-shortcodes", "base-theme", "hyde"]
{{< /code-toggle >}}
You can even nest this, and have the theme component itself include theme components in its own `config.toml` (theme inheritance).[^1]
The theme definition example above in `config.toml` creates a theme with 3 theme components with presedence from left to right.
So, Hugo will, for any given file, data entry etc., look first in the project, and then in `my-shortcode`, `base-theme` and lastly `hyde`.
Hugo uses two different algorithms to merge the filesystems, depending on the file type:
* For `i18n` and `data` files, Hugo merges deeply using the translation id and data key inside the files.
* For `static`, `layouts` (templates) and `archetypes` files, these are merged on file level. So the left-most file will be chosen.
The name used in the `theme` definition above must match a folder in `/your-site/themes`, e.g. `/your-site/themes/my-shortcodes`. There are plans to improve on this and get a URL scheme so this can be resolved automatically.
Also note that a component that is part of a theme can have its own configuration file, e.g. `config.toml`. There are currently some restrictions to what a theme component can configure:
* `params` (global and per language)
* `menu` (global and per language)
* `outputformats` and `mediatypes`
The same rules apply here: The left-most param/menu etc. with the same ID will win. There are some hidden and experimental namespace support in the above, which we will work to improve in the future, but theme authors are encouraged to create their own namespaces to avoid naming conflicts.
[^1]: Including theme components in the themes is currently not supported for themes hosted on [The Hugo Themes Site](https://themes.gohugo.io/), but can be really useful if you want to create your own theme based on a theme you find on that site.

View file

@ -1187,6 +1187,15 @@
"mkd"
]
},
{
"Name": "plaintext",
"Aliases": [
"no-highlight",
"plain",
"text",
"txt"
]
},
{
"Name": "reStructuredText",
"Aliases": [
@ -1507,22 +1516,6 @@
"layouts/_default/single.html"
]
},
{
"Example": "Single page in \"posts\" section with theme",
"Kind": "page",
"OutputFormat": "HTML",
"Suffix": "html",
"Template Lookup Order": [
"layouts/posts/single.html.html",
"demoTheme/layouts/posts/single.html.html",
"layouts/posts/single.html",
"demoTheme/layouts/posts/single.html",
"layouts/_default/single.html.html",
"demoTheme/layouts/_default/single.html.html",
"layouts/_default/single.html",
"demoTheme/layouts/_default/single.html"
]
},
{
"Example": "AMP single page",
"Kind": "page",
@ -1621,38 +1614,6 @@
"layouts/_default/list.html"
]
},
{
"Example": "Home page with theme",
"Kind": "home",
"OutputFormat": "HTML",
"Suffix": "html",
"Template Lookup Order": [
"layouts/index.html.html",
"demoTheme/layouts/index.html.html",
"layouts/home.html.html",
"demoTheme/layouts/home.html.html",
"layouts/list.html.html",
"demoTheme/layouts/list.html.html",
"layouts/index.html",
"demoTheme/layouts/index.html",
"layouts/home.html",
"demoTheme/layouts/home.html",
"layouts/list.html",
"demoTheme/layouts/list.html",
"layouts/_default/index.html.html",
"demoTheme/layouts/_default/index.html.html",
"layouts/_default/home.html.html",
"demoTheme/layouts/_default/home.html.html",
"layouts/_default/list.html.html",
"demoTheme/layouts/_default/list.html.html",
"layouts/_default/index.html",
"demoTheme/layouts/_default/index.html",
"layouts/_default/home.html",
"demoTheme/layouts/_default/home.html",
"layouts/_default/list.html",
"demoTheme/layouts/_default/list.html"
]
},
{
"Example": "AMP home, French language\"",
"Kind": "home",
@ -1706,39 +1667,25 @@
]
},
{
"Example": "RSS home with theme",
"Example": "RSS home",
"Kind": "home",
"OutputFormat": "RSS",
"Suffix": "xml",
"Template Lookup Order": [
"layouts/index.rss.xml",
"demoTheme/layouts/index.rss.xml",
"layouts/home.rss.xml",
"demoTheme/layouts/home.rss.xml",
"layouts/rss.xml",
"demoTheme/layouts/rss.xml",
"layouts/list.rss.xml",
"demoTheme/layouts/list.rss.xml",
"layouts/index.xml",
"demoTheme/layouts/index.xml",
"layouts/home.xml",
"demoTheme/layouts/home.xml",
"layouts/list.xml",
"demoTheme/layouts/list.xml",
"layouts/_default/index.rss.xml",
"demoTheme/layouts/_default/index.rss.xml",
"layouts/_default/home.rss.xml",
"demoTheme/layouts/_default/home.rss.xml",
"layouts/_default/rss.xml",
"demoTheme/layouts/_default/rss.xml",
"layouts/_default/list.rss.xml",
"demoTheme/layouts/_default/list.rss.xml",
"layouts/_default/index.xml",
"demoTheme/layouts/_default/index.xml",
"layouts/_default/home.xml",
"demoTheme/layouts/_default/home.xml",
"layouts/_default/list.xml",
"demoTheme/layouts/_default/list.xml",
"layouts/_internal/_default/rss.xml"
]
},
@ -2978,6 +2925,24 @@
}
},
"path": {
"Base": {
"Description": "",
"Args": null,
"Aliases": null,
"Examples": null
},
"Dir": {
"Description": "",
"Args": null,
"Aliases": null,
"Examples": null
},
"Ext": {
"Description": "",
"Args": null,
"Aliases": null,
"Examples": null
},
"Join": {
"Description": "Join joins any number of path elements into a single path, adding a\nseparating slash if necessary. All the input\npath elements are passed into filepath.ToSlash converting any Windows slashes\nto forward slashes.\nThe result is Cleaned; in particular,\nall empty strings are ignored.",
"Args": [
@ -2992,6 +2957,18 @@
[
"{{ path.Join \"my\" \"path\" \"filename.txt\" }}",
"my/path/filename.txt"
],
[
"{{ \"my/path/filename.txt\" | path.Ext }}",
".txt"
],
[
"{{ \"my/path/filename.txt\" | path.Base }}",
"filename.txt"
],
[
"{{ \"my/path/filename.txt\" | path.Dir }}",
"my/path"
]
]
},
@ -3146,25 +3123,7 @@
"Aliases": [
"countrunes"
],
"Examples": [
[
"{{ \"Hello, 世界\" | countrunes }}",
"8"
]
]
},
"RuneCount": {
"Description": "RuneCount returns the number of runes in s",
"Args": [
"s"
],
"Aliases": [],
"Examples": [
[
"{{ \"Hello, 世界\" | strings.RuneCount }}",
"9"
]
]
"Examples": []
},
"CountWords": {
"Description": "CountWords returns the approximate word count in s.",
@ -3219,6 +3178,20 @@
"Aliases": null,
"Examples": null
},
"Repeat": {
"Description": "Repeat returns a new string consisting of count copies of the string s.",
"Args": [
"n",
"s"
],
"Aliases": null,
"Examples": [
[
"{{ \"yo\" | strings.Repeat 4 }}",
"yoyoyoyo"
]
]
},
"Replace": {
"Description": "Replace returns a copy of the string s with all occurrences of old replaced\nwith new.",
"Args": [
@ -3248,6 +3221,14 @@
],
"Examples": []
},
"RuneCount": {
"Description": "RuneCount returns the number of runes in s.",
"Args": [
"s"
],
"Aliases": null,
"Examples": []
},
"SliceString": {
"Description": "SliceString slices a string by specifying a half-open range with\ntwo indices, start and end. 1 and 4 creates a slice including elements 1 through 3.\nThe end index can be omitted, it defaults to the string's length.",
"Args": [
@ -3428,20 +3409,6 @@
]
]
},
"Repeat": {
"Description": "Repeat returns a new string consisting of count copies of the string s.",
"Args": [
"s",
"n"
],
"Aliases": null,
"Examples": [
[
"{{ \"yo\" | strings.Repeat 4 }}",
"yoyoyoyo"
]
]
},
"Truncate": {
"Description": "Truncate truncates a given string to the specified length.",
"Args": [

View file

@ -38,34 +38,31 @@ func createLayoutExamples() interface{} {
)
for _, example := range []struct {
name string
d LayoutDescriptor
hasTheme bool
f Format
name string
d LayoutDescriptor
f Format
}{
// Taxonomy output.LayoutDescriptor={categories category taxonomy en false Type Section
{"Single page in \"posts\" section", LayoutDescriptor{Kind: "page", Type: "posts"}, false, HTMLFormat},
{"Single page in \"posts\" section with layout set", LayoutDescriptor{Kind: "page", Type: "posts", Layout: demoLayout}, false, HTMLFormat},
{"Single page in \"posts\" section with theme", LayoutDescriptor{Kind: "page", Type: "posts"}, true, HTMLFormat},
{"AMP single page", LayoutDescriptor{Kind: "page", Type: "posts"}, false, AMPFormat},
{"AMP single page, French language", LayoutDescriptor{Kind: "page", Type: "posts", Lang: "fr"}, false, AMPFormat},
{"Single page in \"posts\" section", LayoutDescriptor{Kind: "page", Type: "posts"}, HTMLFormat},
{"Single page in \"posts\" section with layout set", LayoutDescriptor{Kind: "page", Type: "posts", Layout: demoLayout}, HTMLFormat},
{"AMP single page", LayoutDescriptor{Kind: "page", Type: "posts"}, AMPFormat},
{"AMP single page, French language", LayoutDescriptor{Kind: "page", Type: "posts", Lang: "fr"}, AMPFormat},
// All section or typeless pages gets "page" as type
{"Home page", LayoutDescriptor{Kind: "home", Type: "page"}, false, HTMLFormat},
{"Home page with type set", LayoutDescriptor{Kind: "home", Type: demoType}, false, HTMLFormat},
{"Home page with layout set", LayoutDescriptor{Kind: "home", Type: "page", Layout: demoLayout}, false, HTMLFormat},
{`Home page with theme`, LayoutDescriptor{Kind: "home", Type: "page"}, true, HTMLFormat},
{`AMP home, French language"`, LayoutDescriptor{Kind: "home", Type: "page", Lang: "fr"}, false, AMPFormat},
{"JSON home", LayoutDescriptor{Kind: "home", Type: "page"}, false, JSONFormat},
{"RSS home with theme", LayoutDescriptor{Kind: "home", Type: "page"}, true, RSSFormat},
{"RSS section posts", LayoutDescriptor{Kind: "section", Type: "posts"}, false, RSSFormat},
{"Taxonomy list in categories", LayoutDescriptor{Kind: "taxonomy", Type: "categories", Section: "category"}, false, RSSFormat},
{"Taxonomy terms in categories", LayoutDescriptor{Kind: "taxonomyTerm", Type: "categories", Section: "category"}, false, RSSFormat},
{"Section list for \"posts\" section", LayoutDescriptor{Kind: "section", Type: "posts", Section: "posts"}, false, HTMLFormat},
{"Section list for \"posts\" section with type set to \"blog\"", LayoutDescriptor{Kind: "section", Type: "blog", Section: "posts"}, false, HTMLFormat},
{"Section list for \"posts\" section with layout set to \"demoLayout\"", LayoutDescriptor{Kind: "section", Layout: demoLayout, Section: "posts"}, false, HTMLFormat},
{"Home page", LayoutDescriptor{Kind: "home", Type: "page"}, HTMLFormat},
{"Home page with type set", LayoutDescriptor{Kind: "home", Type: demoType}, HTMLFormat},
{"Home page with layout set", LayoutDescriptor{Kind: "home", Type: "page", Layout: demoLayout}, HTMLFormat},
{`AMP home, French language"`, LayoutDescriptor{Kind: "home", Type: "page", Lang: "fr"}, AMPFormat},
{"JSON home", LayoutDescriptor{Kind: "home", Type: "page"}, JSONFormat},
{"RSS home", LayoutDescriptor{Kind: "home", Type: "page"}, RSSFormat},
{"RSS section posts", LayoutDescriptor{Kind: "section", Type: "posts"}, RSSFormat},
{"Taxonomy list in categories", LayoutDescriptor{Kind: "taxonomy", Type: "categories", Section: "category"}, RSSFormat},
{"Taxonomy terms in categories", LayoutDescriptor{Kind: "taxonomyTerm", Type: "categories", Section: "category"}, RSSFormat},
{"Section list for \"posts\" section", LayoutDescriptor{Kind: "section", Type: "posts", Section: "posts"}, HTMLFormat},
{"Section list for \"posts\" section with type set to \"blog\"", LayoutDescriptor{Kind: "section", Type: "blog", Section: "posts"}, HTMLFormat},
{"Section list for \"posts\" section with layout set to \"demoLayout\"", LayoutDescriptor{Kind: "section", Layout: demoLayout, Section: "posts"}, HTMLFormat},
{"Taxonomy list in categories", LayoutDescriptor{Kind: "taxonomy", Type: "categories", Section: "category"}, false, HTMLFormat},
{"Taxonomy term in categories", LayoutDescriptor{Kind: "taxonomyTerm", Type: "categories", Section: "category"}, false, HTMLFormat},
{"Taxonomy list in categories", LayoutDescriptor{Kind: "taxonomy", Type: "categories", Section: "category"}, HTMLFormat},
{"Taxonomy term in categories", LayoutDescriptor{Kind: "taxonomyTerm", Type: "categories", Section: "category"}, HTMLFormat},
} {
l := NewLayoutHandler()
@ -90,12 +87,8 @@ func makeLayoutsPresentable(l []string) []string {
// This is a valid lookup, but it's more confusing than useful.
continue
}
ll = strings.TrimPrefix(ll, "_text/")
if strings.Contains(ll, "theme/") {
ll = strings.Replace(ll, "theme/", "demoTheme/layouts/", -1)
} else {
ll = "layouts/" + ll
}
ll = "layouts/" + strings.TrimPrefix(ll, "_text/")
if !strings.Contains(ll, "indexes") {
filtered = append(filtered, ll)
}