Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow theme = ["base", "my-theme" ] (aka theme composition and inheritance) #4460

Closed
bep opened this issue Feb 28, 2018 · 65 comments · Fixed by #4467
Closed

Allow theme = ["base", "my-theme" ] (aka theme composition and inheritance) #4460

bep opened this issue Feb 28, 2018 · 65 comments · Fixed by #4467

Comments

@bep
Copy link
Member

bep commented Feb 28, 2018

I know this has been requested before, in the form of some extends = some-other-theme.

This isn't "theme inheritance" in its purest form (which would be "theme1 extends theme2 extends theme3"), more like theme composition. Which is much simpler to understand/implement, but with most of the added benefits.

This relates to my work on https://github.com/bep/html5up-to-hugo -- which, with the current Hugo, becomes less elegant than it could be.

The simple rule is that the themes' files will form a big overlay/union file system from left to right.

So:

theme = ["base", "my-theme" ]
  • All files in base will be used unless there is a file with the same name in my-theme.
  • This will work with layouts, data, i18n, static (and in the future: content etc.)

What do you say?


@bep bep added the Proposal label Feb 28, 2018
@bep bep added this to the v0.38 milestone Feb 28, 2018
@bep
Copy link
Member Author

bep commented Feb 28, 2018

Note that this is a "proposal", I'm not done with my part of the thinking here -- maybe this belongs in the theme itself etc...

@bep bep added the Keep label Feb 28, 2018
@RickCogley
Copy link
Contributor

One thing I see on the forum a lot is, "how can I make page X look different from the rest of the site". Could this facilitate styling a page like that, kind of overriding the theme applied to most of the site?

@brunoamaral
Copy link
Contributor

From @bep's description, yes it does. You could for example have your own version of single.html or of a shortcode.

I can see how this is useful. It's the same mechanic that Wordpress uses in creating 'child-themes', but I am not a fan. I remember losing track of whether I was editing the right files.

It's a personal perspective. Others will make it useful and may help produce more themes for Hugo. I don't have the knowledge to comment on the implementation. So I will abstain from voting and may even end up using it anyway.

@kaushalmodi
Copy link
Contributor

+1 if I can have my personal "theme" of partial and shortcode collection, and can keep that theme as the secondary theme.

Thinking of something like:

theme = ["actual-theme", "partial-shortcode-collection-theme" ]

I've wanted this feature for a while. I had tried symlinking my commonly used partials/shortcodes from a common git repo, but symlinks don't work.

@RickCogley
Copy link
Contributor

@kaushalmodi +1 for that. That's a cool idea.

@Jos512
Copy link

Jos512 commented Feb 28, 2018

Child themes are a great idea. In theory a feature like this should make it easier to get started with Hugo for beginners. And they probably also make it a lot easier for people to update the original, parent theme.

@bep
Copy link
Member Author

bep commented Feb 28, 2018

@RickCogley not sure about your example, but @kaushalmodi 's example is a primary use case, and I think this becomes super-powerful when we get proper dependency management, i.e:

theme = ["https://github.com/spf13/hyde/v2", "https://github.com/kaushalmodi/shortcode-pack/v1" ]

When Russ Cox is done with his brilliant thinking, I'm going to steal his thoughts: https://research.swtch.com/vgo

To me, this is mostly about DRY. On the project side, we can already today extend the theme by adding a single.html or overriding a partial etc. But that is not an option in a theme. So we get themes like hydex and hydey -- only slightly different ports of the same theme. If you want to create a new hyde theme with some blue colour, this should be possible without too much duplication.

Thinking about it, I think I will try to get this to work both on theme and in project level (themable themes).

So in theme.toml for my new Hyde theme hyde32:

# This theme is based on SPF13's port of Hyde, but with prettier colours.
theme = ["hyde"]

And then in my config.toml:

theme = ["hyde32", "partial-shortcode-collection-theme"]

The ordered set of file collections will then be:

hyde, hyde32, partial-shortcode-collection-theme

This may be too complex if the graph gets too big. Will think.

In its first iteration, the end user is responsible for pulling the above 3 themes into /themes -- but the ultimate goal here is to do:

# Shallow clone, no themes (only referenced in config.toml)
git clone https://github.com/bep/my-site
cd my-site
# hugo will download dependencies if not cached.
hugo 

@regisphilibert
Copy link
Member

regisphilibert commented Feb 28, 2018

I think it will be very useful. One use case coming to mind is with output formats. If you want to add an "api" (json output) layer to your content, all you'd have to do is add a special "api" theme and reference it. Rather than singlehandedly adding the json templates in your layouts/_default or other.

@regisphilibert
Copy link
Member

regisphilibert commented Feb 28, 2018

Also it will allow theme developer to release several "extension" for their theme which not everyone will need. Like a "photo gallery" extension or a "Dentist" extension for a theme whose base is made for broader "health" structure.

@budparr
Copy link

budparr commented Feb 28, 2018

This would be very useful. My use case is similar to @kaushalmodi in that I keep a set of really basic files (_default layouts, etc.) to reuse on all projects and then layouts/themes for specific types of projects on top of that.

@bep
Copy link
Member Author

bep commented Feb 28, 2018

A development tip: Features like this are hard to get going unless you have a solid and failing test up and running:

const (
		themeStandalone = `
			name = "Theme Standalone"
`
		themeCyclic = `
			name = "Theme Cyclic"
			theme = "theme3"
`
		theme1 = `
			name = "Theme #1"
`

		theme2 = `
			name = "Theme #2"
			theme = "theme1"
`

		theme3 = `
			name = "Theme #3"
			theme = ["theme2", "themeStandalone", "themeCyclic"]
`

		theme4 = `
			name = "Theme #4"
			theme = "theme3"
`

		site1 = `
			theme = "theme4"
`
		site2 = `
			theme = ["theme2", "themeStandalone"]
`
	)

With a test that fails to build site1 and site2 -- then the fun starts! It is a threshold of boring work to get there.

@bep bep added Enhancement and removed Proposal labels Mar 1, 2018
bep added a commit to bep/hugo that referenced this issue Mar 1, 2018
bep added a commit to bep/hugo that referenced this issue Mar 1, 2018
bep added a commit to bep/hugo that referenced this issue Mar 2, 2018
bep added a commit to bep/hugo that referenced this issue Mar 2, 2018
bep added a commit to bep/hugo that referenced this issue Mar 2, 2018
@bep bep modified the milestones: v0.38, v0.39 Mar 11, 2018
@regisphilibert
Copy link
Member

regisphilibert commented Mar 16, 2018

On more use case is "shortcode" bundles. Your theme layout files would only contains shortcode files and people could use them by just adding your theme to their "theme set" and easily upgrade with future updates.

I am not very intelligible when excited.

Just saw @kaushalmodi's comment above :/

@bep
Copy link
Member Author

bep commented Mar 16, 2018

I just figured out that this would also be really cool for bundling of shortcodes!

:-)

@regisphilibert
Copy link
Member

Or bundling of output formats templates ! 🎉
You could even design an "Algolia" search theme "addon" along with script, css, template, output format template etc...

So much possibilities!

@mwyvr
Copy link

mwyvr commented Mar 16, 2018

Yep, all those things that we copy from theme to theme, often without changing a single line. Shortcodes, output formats. Heck even "base" for many.

Note if it makes dealing with static asset customization (like a site font file or landing image

@bep
Copy link
Member Author

bep commented Apr 7, 2018

Just a quick note that this is still pretty much priority.

In Hugo 0.38 themes got config.toml with namespaced .Params etc. and this will, of course, extend to these composites. Which will make something really powerful.

bep added a commit to bep/hugo that referenced this issue Apr 8, 2018
@maiki
Copy link

maiki commented May 28, 2018

I've been thinking of it as everything is a set of one or more templates that operate on your content following a "theme". It could be visual layout, or shortcodes, or an output format, or all of those. So if we explain how we use "theme", it should be fairly straightforward.

I've read this conversation in a lot of projects over the years (naming and "theme"), and it is recurring issue that new users will likely know about themes solely via a PHP CMS. So they have to explain it a bit more. And we kinda have to explain everything a bit more with Hugo, it seems; a lot of folks don't quite grok the simplicity of a "template + content" workflow. ^_^

bep added a commit to bep/hugo that referenced this issue May 29, 2018
kaushalmodi added a commit to kaushalmodi/hugo that referenced this issue May 30, 2018
bep added a commit to bep/hugo that referenced this issue Jun 3, 2018
bep added a commit to bep/hugo that referenced this issue Jun 4, 2018
@bep bep modified the milestones: v0.42, v0.43 Jun 5, 2018
bep added a commit to bep/hugo that referenced this issue Jun 5, 2018
@bep
Copy link
Member Author

bep commented Jun 6, 2018

Just a little heads up to people waiting for this: I'm very close to finishing implementing this now. To get this done, I had to refactor and get real control of all the theme related filesystem handling. With this issue closed, we will have a fully virtualized filesystem interaction without all the path logic sprinkled around.

And it seems to make Hugo faster, at least for sites with themes. The hugo docs builds about 15% faster on my MacBook.

bep added a commit to bep/hugo that referenced this issue Jun 10, 2018
bep added a commit to bep/hugo that referenced this issue Jun 10, 2018
bep added a commit to bep/hugo that referenced this issue Jun 10, 2018
bep added a commit to bep/hugo that referenced this issue Jun 10, 2018
bep added a commit to bep/hugo that referenced this issue Jun 10, 2018
bep added a commit to bep/hugo that referenced this issue Jun 10, 2018
bep added a commit to bep/hugo that referenced this issue Jun 10, 2018
bep added a commit to bep/hugo that referenced this issue Jun 10, 2018
bep added a commit to bep/hugo that referenced this issue Jun 10, 2018
This commit adds support for theme composition and inheritance in Hugo.

With this, it helps thinking about a theme as a set of ordered components:

```toml
theme = ["my-shortcodes", "base-theme", "hyde"]
```

The theme definition example above in `config.toml` creates a theme with the 3 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.

A final note: Themes/components can also have a `theme` definition in their `config.toml` and similar, which is the "inheritance" part of this commit's title. This is currently not supported by the Hugo theme site. We will have to wait for some "auto dependency" feature to be implemented for that to happen, but this can be a powerful feature if you want to create your own theme-variant based on others.

Fixes gohugoio#4460
Fixes gohugoio#4450
@bep bep closed this as completed in #4467 Jun 10, 2018
bep added a commit that referenced this issue Jun 12, 2018
@ghost
Copy link

ghost commented Jun 17, 2018

#2639 cross-linking for reference

@zxdawn2
Copy link

zxdawn2 commented May 24, 2019

Is there any tutorial on how to use this function? I'm trying to use specific theme (hugo-resume) for 'about' page and common theme (AllinOne) for others. Will this function works for this situation?

I've tried this method to use two themes. But the structure is different and results in the wrong 'about' page.

@regisphilibert
Copy link
Member

theme:
  - hugo-resume
  - allinone

The first homonymous template/data/i18n match will overwrite the later.

@github-actions
Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 17, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.