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 · 62 comments

Comments

Projects
None yet
@bep
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

This comment has been minimized.

Member

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

This comment has been minimized.

Contributor

RickCogley commented Feb 28, 2018

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

This comment has been minimized.

Contributor

brunoamaral commented Feb 28, 2018

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

This comment has been minimized.

Member

kaushalmodi commented Feb 28, 2018

+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

This comment has been minimized.

Contributor

RickCogley commented Feb 28, 2018

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

@Jos512

This comment has been minimized.

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

This comment has been minimized.

Member

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

Member

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

This comment has been minimized.

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

This comment has been minimized.

Member

bep commented Mar 16, 2018

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

:-)

@regisphilibert

This comment has been minimized.

regisphilibert commented Mar 16, 2018

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!

@solutionroute

This comment has been minimized.

solutionroute 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

This comment has been minimized.

Member

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

@kaushalmodi

This comment has been minimized.

Member

kaushalmodi commented May 28, 2018

@regisphilibert It would just feel so odd to write..

theme = ["debugprint", "my-shortcodes", "my-theme-overrides", "base-theme"]

But yeah, as long as this is well documented and understood, should be good.

@bep

This comment has been minimized.

Member

bep commented May 28, 2018

I did not expect that outcome :) Oh well.

My head was the other way around when I first thought about this, but revisiting it some months later, it just looked backwards... Reading from left makes project extends theme1 extends theme2 extends theme3 work as a sentence.

@regisphilibert

This comment has been minimized.

regisphilibert commented May 28, 2018

Yes with the "extends" notion, it makes a lot of sense.

Here is a thought, I don't think it derails the issue, but stop me if it does.

Can we question the usage of the word "theme" for this config parameter? A theme is really something people take as a skin for their website. But now, it appears a lot of our themes will be something different. Like our already popular exemple of shortcode bundle, or output formats bundle.

I feel the word theme fails to encompass what this layout inheritance will really bring along. I think we need a word used as an umbrella for themes/plugins/addons/extension as it would make more sense in the long term.

@bep

This comment has been minimized.

Member

bep commented May 28, 2018

Can we question the usage of the word "theme" for this config parameter?

You may question it ...

I think it works if you think of a theme as a composite of components and other themes:

theme = ["component", "theme2", "another-component"]

Put all of those together and you got your theme.

Also, I don't want 2 terms for this, and changing it would be a fair amount of work, and you have to be really convincing to get me down that road.

@regisphilibert

This comment has been minimized.

regisphilibert commented May 28, 2018

I think it works if you think a theme as a composite of components and other themes

Again put this way it makes more sense. This should be the way to put it in the doc when the time comes.

Thanks.

@maiki

This comment has been minimized.

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

This comment has been minimized.

Member

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

Add support for theme composition and inheritance
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 in #4467 Jun 10, 2018

bep added a commit that referenced this issue Jun 12, 2018

@jhabdas

This comment has been minimized.

Contributor

jhabdas commented Jun 17, 2018

#2639 cross-linking for reference

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment