framework
framework copied to clipboard
head, header and footer can be specified as a function
head, header and footer can be specified as a function that receives the page’s meta data (path, title and unrestricted front matter), and returns an HTML fragment (string).
~~I removed the strict normalization of the front matter; now only the "known" properties are normalized, and custom properties are left as given in the page. [EDIT: removed this part]~~
closes #56
Partially addresses #1036, but I'm not sure how we can (or if we should) expose the meta data more generally:
- to the page renderer, so that we could pass the values to be rendered SSR (#931); probably in post-processing?
- to the page runtime (via a
define("meta", …))?
Partially addresses #1161.
Why is it necessary to remove the strict normalization of the front matter? I don’t think we want to encourage users to put anything they want in the front matter; that will make it harder for us to introduce new options in the future without breaking projects. What is the use case for adding arbitrary things into the front matter?
The idea is coming from metadata that I would like to both display in the page, and consume outside.
For example, author (to create a byline), source (which I use in pangea to refer to the original notebook, when applicable), tags, date_published, og_image, thumbnail, etc.
Some of these might go in the head (to inform opengraph tags), some in the indexation / listings (typically, the tags), yet others in the footer (e.g. tags with links to the tags pages.
Maybe it's the wrong approach and we should reserve front matter for “official business”, but then we'd need another mechanism that would be more extensible in user land. Maybe it's just a "meta" key in front matter that is not normalized, and made available both for SSR and for client-side JS?
Anyway I can dumb down this PR so that it only does the "head as a function" part and finally closes #56, and we continue the discussion on the extensibility separately.
Yes, let’s separate these things. I’d definitely want the “user space” for the front matter to be isolated from Framework’s options, say by using a meta or data option.
This also overlaps with the page loader concept #1216 which allows the entire page (not just the head, header, footer) to be dynamically generated. But I think it’s still likely reasonable to allow these options to be specified as functions in the config.
rebased
Looks like you’ll need to handle the case where the function returns null. (I think it should do that for consistency with the statically-supplied value.)
showing "undefined" or "null" was not very pretty (but … informative!) :)
I've tried defining a dynamic head function by following the footer example, to no avail. When testing it for header and footer, rather than evaluating the function it returns the function as string in the rendered HTML (when in dev preview, at least). Maybe I am doing something wrong?
@dleeftink What version of framework are you using?
@Fil an older one it seems (1.5.1), updated now tot 1.9.0 and no issues with the main observablehq.config.js. Just wondering how to override on a per file basis? Adding an arrow function to the YAML frontmatter of a page like so doesn't seem right:
footer: ()=>`test`
Also, how about making the per-page overrides configurable to replace (append=false) or append (append=true) to the globally defined functions in config.js? This way pages don't interfere with the global settings if only minor additions are required. Something like:
header: ({append = true}) => `<i>some subtitle</i>`
Maybe overrides can even be configured to replace/append to specific metadata fields, for instance to append a date to a globally defined title or a metadata page description. Not sure what that interface would like tho as it involves creating per-field flags for potentially nested frontmatter items.
EDIT:
After some thinking, maybe just pass the return string/DocFragment defined by the global config.js to the per-page override?
The current logic works the other way around: the function you define in observablehq.config.ts is downstream from the page, not upstream — it receives as input the page's meta data (slug, title and front-matter). One aspect we could make better is to allow "non normalized" front matter fields, so you could e.g. define categories: x, y, z in the front matter and use those in the footer. If you want to open a new issue, please try to provide details of the use case you have in mind.