mirror of
https://github.com/tiennm99/HugoBlox-kit.git
synced 2026-10-13 22:15:11 +00:00
feat(slides): add persistent branding with per-slide visibility control
- Add branding overlays: logo, title, author, and footer text - Use Reveal.js theme CSS variables for automatic color adaptation - Inline SVGs for currentColor support on any theme - Per-slide control via HTML comments: - <!-- no-branding -->: hide all elements - <!-- no-header -->: hide logo + title - <!-- no-footer -->: hide author + footer - Fix body-level CSS selectors for proper visibility - Add comprehensive documentation in example slides
This commit is contained in:
1 parent
1352d89867
commit
aa9edea5df
16 files changed
+902
-17
No files matched your search
@@ -0,0 +1,157 @@
|
||||
{{/*
|
||||
Slide Branding Component
|
||||
|
||||
Renders persistent branding elements (logo, title overlay, etc.) that appear
|
||||
across all slides in a presentation.
|
||||
|
||||
Configuration (site.Params.slides.branding or .Params.slides.branding):
|
||||
logo:
|
||||
src: "media/logo.png" # Path to logo image
|
||||
alt: "Organization Logo" # Alt text
|
||||
position: "top-left" # top-left, top-right, bottom-left, bottom-right
|
||||
width: "100px" # Logo width
|
||||
margin: "20px" # Distance from edges
|
||||
title:
|
||||
show: true # Show presentation title
|
||||
position: "bottom-left" # Position of title text
|
||||
author:
|
||||
show: true # Show author/presenter name
|
||||
position: "bottom-right" # Position of author text
|
||||
*/}}
|
||||
|
||||
{{/* Merge page-level config with site-level defaults */}}
|
||||
{{ $site_branding := site.Params.slides.branding | default dict }}
|
||||
{{ $page_branding := .Params.slides.branding | default dict }}
|
||||
{{ $branding := merge $site_branding $page_branding }}
|
||||
|
||||
{{/* Logo rendering */}}
|
||||
{{ with $branding.logo }}
|
||||
{{ if .filename }}
|
||||
{{ $resource_path := printf "media/%s" .filename }}
|
||||
{{ $logo_resource := resources.Get $resource_path }}
|
||||
{{ with $logo_resource }}
|
||||
{{ $position := $branding.logo.position | default "top-left" }}
|
||||
{{ $width := $branding.logo.width | default "80px" }}
|
||||
{{ $margin := $branding.logo.margin | default "20px" }}
|
||||
{{ $alt := $branding.logo.alt | default "Logo" }}
|
||||
|
||||
{{/* Calculate CSS position based on position string */}}
|
||||
{{ $positionCSS := "" }}
|
||||
{{ if eq $position "top-left" }}
|
||||
{{ $positionCSS = printf "top: %s; left: %s;" $margin $margin }}
|
||||
{{ else if eq $position "top-right" }}
|
||||
{{ $positionCSS = printf "top: %s; right: %s;" $margin $margin }}
|
||||
{{ else if eq $position "bottom-left" }}
|
||||
{{ $positionCSS = printf "bottom: %s; left: %s;" $margin $margin }}
|
||||
{{ else if eq $position "bottom-right" }}
|
||||
{{ $positionCSS = printf "bottom: %s; right: %s;" $margin $margin }}
|
||||
{{ else }}
|
||||
{{/* Custom position - expect CSS string */}}
|
||||
{{ $positionCSS = $position }}
|
||||
{{ end }}
|
||||
|
||||
<div id="slide-logo" style="{{ $positionCSS | safeCSS }} width: {{ $width }};" aria-label="{{ $alt }}">
|
||||
{{/* Inline SVGs to enable currentColor, use img for other formats */}}
|
||||
{{ if strings.HasSuffix .Name ".svg" }}
|
||||
{{ .Content | safeHTML }}
|
||||
{{ else }}
|
||||
<img src="{{ .RelPermalink }}" alt="{{ $alt }}">
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Title overlay (optional) */}}
|
||||
{{ if $branding.title }}
|
||||
{{ with $branding.title }}
|
||||
{{ if .show }}
|
||||
{{ $position := .position | default "bottom-left" }}
|
||||
{{ $margin := .margin | default "20px" }}
|
||||
|
||||
{{ $positionCSS := "" }}
|
||||
{{ if eq $position "top-left" }}
|
||||
{{ $positionCSS = printf "top: %s; left: %s;" $margin $margin }}
|
||||
{{ else if eq $position "top-right" }}
|
||||
{{ $positionCSS = printf "top: %s; right: %s;" $margin $margin }}
|
||||
{{ else if eq $position "bottom-left" }}
|
||||
{{ $positionCSS = printf "bottom: %s; left: %s;" $margin $margin }}
|
||||
{{ else if eq $position "bottom-right" }}
|
||||
{{ $positionCSS = printf "bottom: %s; right: %s;" $margin $margin }}
|
||||
{{ end }}
|
||||
|
||||
<div id="slide-title-overlay" style="{{ $positionCSS | safeCSS }}">
|
||||
{{ .text | default $.Title }}
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Author overlay (optional) */}}
|
||||
{{ if $branding.author }}
|
||||
{{ with $branding.author }}
|
||||
{{ if .show }}
|
||||
{{ $position := .position | default "bottom-right" }}
|
||||
{{ $margin := .margin | default "20px" }}
|
||||
|
||||
{{ $positionCSS := "" }}
|
||||
{{ if eq $position "top-left" }}
|
||||
{{ $positionCSS = printf "top: %s; left: %s;" $margin $margin }}
|
||||
{{ else if eq $position "top-right" }}
|
||||
{{ $positionCSS = printf "top: %s; right: %s;" $margin $margin }}
|
||||
{{ else if eq $position "bottom-left" }}
|
||||
{{ $positionCSS = printf "bottom: %s; left: %s;" $margin $margin }}
|
||||
{{ else if eq $position "bottom-right" }}
|
||||
{{ $positionCSS = printf "bottom: %s; right: %s;" $margin $margin }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Get author from page params or first author */}}
|
||||
{{ $author := "" }}
|
||||
{{ with $.Params.authors }}
|
||||
{{ if reflect.IsSlice . }}
|
||||
{{ $author = index . 0 }}
|
||||
{{ else }}
|
||||
{{ $author = . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ with $.Params.author }}
|
||||
{{ $author = . }}
|
||||
{{ end }}
|
||||
|
||||
{{ if $author }}
|
||||
<div id="slide-author-overlay" style="{{ $positionCSS | safeCSS }}">
|
||||
{{ $author }}
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Footer text overlay (optional) - e.g. for copyright */}}
|
||||
{{ if $branding.footer }}
|
||||
{{ with $branding.footer }}
|
||||
{{ if .text }}
|
||||
{{ $position := .position | default "bottom-center" }}
|
||||
{{ $margin := .margin | default "20px" }}
|
||||
|
||||
{{ $positionCSS := "" }}
|
||||
{{ if eq $position "top-left" }}
|
||||
{{ $positionCSS = printf "top: %s; left: %s;" $margin $margin }}
|
||||
{{ else if eq $position "top-right" }}
|
||||
{{ $positionCSS = printf "top: %s; right: %s;" $margin $margin }}
|
||||
{{ else if eq $position "bottom-left" }}
|
||||
{{ $positionCSS = printf "bottom: %s; left: %s;" $margin $margin }}
|
||||
{{ else if eq $position "bottom-right" }}
|
||||
{{ $positionCSS = printf "bottom: %s; right: %s;" $margin $margin }}
|
||||
{{ else if eq $position "bottom-center" }}
|
||||
{{ $positionCSS = printf "bottom: %s; left: 50%%; transform: translateX(-50%%);" $margin }}
|
||||
{{ end }}
|
||||
|
||||
<div id="slide-footer-text-overlay" style="{{ $positionCSS | safeCSS }}">
|
||||
{{ .text | markdownify }}
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
{{/*
|
||||
Slides Hook Loader - Independent Implementation
|
||||
|
||||
This is a self-contained hook system for the slides module that doesn't
|
||||
depend on the blox module. Users can create partials in their own project's
|
||||
layouts/_partials/hooks/{hook-name}/ directory.
|
||||
|
||||
Usage: {{ partial "functions/slides_get_hook" (dict "hook" "slide-header" "context" .) }}
|
||||
|
||||
Available hooks for slides:
|
||||
- slide-header: Top of presentation (inside .reveal, above .slides)
|
||||
- slide-footer: Bottom of presentation (inside .reveal, below .slides)
|
||||
- slide-head-end: End of <head> tag (for custom CSS/JS)
|
||||
- slide-body-end: End of <body> tag (for custom JS)
|
||||
|
||||
Input: dict with "hook" (string) and "context" (page context)
|
||||
Output: Renders all partials found in the hook directory
|
||||
*/}}
|
||||
|
||||
{{ $loaded := false }}
|
||||
{{ $partial_dir := printf "hooks/%s/" .hook }}
|
||||
{{ $context := .context }}
|
||||
{{ $hook_dir_path := path.Join "layouts/_partials" $partial_dir }}
|
||||
|
||||
{{/* Use try to gracefully handle missing directories */}}
|
||||
{{ with try (os.ReadDir $hook_dir_path) }}
|
||||
{{ with .Value }}
|
||||
{{ range . }}
|
||||
{{ if not .IsDir }}
|
||||
{{ $partial_path := path.Join $partial_dir .Name }}
|
||||
{{ partial $partial_path $context }}
|
||||
{{ $loaded = true }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Debug: uncomment to see if hooks are loaded */}}
|
||||
{{/* return $loaded */}}
|
||||
@@ -0,0 +1,22 @@
|
||||
{{/*
|
||||
Example Slide Body-End Hook
|
||||
|
||||
This hook allows injecting custom JavaScript at the end of the <body>
|
||||
tag, after Reveal.js has been loaded. Useful for:
|
||||
- Custom Reveal.js plugins
|
||||
- Analytics tracking on slide changes
|
||||
- Custom keyboard shortcuts
|
||||
- Integration with other JS libraries
|
||||
|
||||
To use this hook, create a file at:
|
||||
layouts/_partials/hooks/slide-body-end/your-custom-script.html
|
||||
*/}}
|
||||
|
||||
{{/* Uncomment below to track slide changes with console logging */}}
|
||||
{{/*
|
||||
<script>
|
||||
Reveal.on('slidechanged', event => {
|
||||
console.log('Slide changed to:', event.indexh, event.indexv);
|
||||
});
|
||||
</script>
|
||||
*/}}
|
||||
@@ -0,0 +1,20 @@
|
||||
{{/*
|
||||
Example Slide Footer Hook
|
||||
|
||||
This is an example file showing how to create custom footer content
|
||||
that appears at the bottom of every slide in a presentation.
|
||||
|
||||
To use this hook, create a file at:
|
||||
layouts/_partials/hooks/slide-footer/your-custom-footer.html
|
||||
|
||||
The content will be rendered inside a <div class="slide-footer"> container.
|
||||
|
||||
Example usage: show social media handle or contact info
|
||||
*/}}
|
||||
|
||||
{{/* Uncomment below to show author's social handle in footer */}}
|
||||
{{/*
|
||||
<div style="font-size: 0.5em; color: rgba(255,255,255,0.5); padding: 10px; text-align: right; width: 100%;">
|
||||
@yourhandle
|
||||
</div>
|
||||
*/}}
|
||||
@@ -0,0 +1,25 @@
|
||||
{{/*
|
||||
Example Slide Head-End Hook
|
||||
|
||||
This hook allows injecting custom CSS or JavaScript into the <head>
|
||||
section of the presentation. Useful for:
|
||||
- Custom fonts
|
||||
- Additional CSS frameworks
|
||||
- Analytics scripts
|
||||
- Custom Reveal.js configurations
|
||||
|
||||
To use this hook, create a file at:
|
||||
layouts/_partials/hooks/slide-head-end/your-custom-head.html
|
||||
*/}}
|
||||
|
||||
{{/* Uncomment below to add custom font */}}
|
||||
{{/*
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Fira+Code&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
.reveal pre code {
|
||||
font-family: 'Fira Code', monospace;
|
||||
}
|
||||
</style>
|
||||
*/}}
|
||||
@@ -0,0 +1,30 @@
|
||||
{{/*
|
||||
Example Slide Header Hook
|
||||
|
||||
This is an example file showing how to create custom header content
|
||||
that appears at the top of every slide in a presentation.
|
||||
|
||||
To use this hook, create a file at:
|
||||
layouts/_partials/hooks/slide-header/your-custom-header.html
|
||||
|
||||
The content will be rendered inside a <div class="slide-header"> container.
|
||||
|
||||
Available context variables:
|
||||
.Title - Presentation title
|
||||
.Params - All front matter parameters
|
||||
.Params.authors - List of authors
|
||||
.Params.course - Course code/name
|
||||
.Params.venue - Event venue
|
||||
.Date - Presentation date
|
||||
|
||||
Example usage:
|
||||
*/}}
|
||||
|
||||
{{/* Uncomment below to show course code in header */}}
|
||||
{{/*
|
||||
{{ with .Params.course }}
|
||||
<div style="font-size: 0.6em; color: rgba(255,255,255,0.6); padding: 10px;">
|
||||
{{ . }}{{ with $.Params.lecture_number }} · Lecture {{ . }}{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
*/}}
|
||||
@@ -26,7 +26,20 @@
|
||||
{{/* Each `<section>` defines a new slide. */}}
|
||||
{{/* Only begin new slide `<section>` if not already added by custom `slide` shortcode. */}}
|
||||
{{ if not (in . "data-noprocess") }}
|
||||
<section{{ if $isHidden }} data-visibility="hidden"{{ end }}>
|
||||
|
||||
{{/* Parse visibility comments */}}
|
||||
{{ $state := slice }}
|
||||
{{ if in . "<!-- no-branding -->" }}
|
||||
{{ $state = $state | append "no-branding" }}
|
||||
{{ end }}
|
||||
{{ if in . "<!-- no-header -->" }}
|
||||
{{ $state = $state | append "no-header" }}
|
||||
{{ end }}
|
||||
{{ if in . "<!-- no-footer -->" }}
|
||||
{{ $state = $state | append "no-footer" }}
|
||||
{{ end }}
|
||||
|
||||
<section{{ if $isHidden }} data-visibility="hidden"{{ end }} {{ if gt (len $state) 0 }}data-state="{{ delimit $state " " }}"{{ end }}>
|
||||
{{ end }}
|
||||
{{ $slideContent | safeHTML }}
|
||||
</section>
|
||||
@@ -35,4 +48,21 @@
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
{{/* Branding elements - placed inside .reveal but outside .slides to be persistent and fixed */}}
|
||||
|
||||
{{/* Branding overlay (logo, title, author) */}}
|
||||
{{ $pageContext := index . 0 }}
|
||||
{{ partial "components/slide-branding" $pageContext }}
|
||||
|
||||
{{/* Hook: slide-header */}}
|
||||
<div class="slide-header">
|
||||
{{ partial "functions/slides_get_hook" (dict "hook" "slide-header" "context" $pageContext) }}
|
||||
</div>
|
||||
|
||||
{{/* Hook: slide-footer */}}
|
||||
<div class="slide-footer">
|
||||
{{ partial "functions/slides_get_hook" (dict "hook" "slide-footer" "context" $pageContext) }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -1,5 +1,23 @@
|
||||
{{/*
|
||||
Present Baseof - Presentation output only
|
||||
|
||||
Hook Points Available:
|
||||
- slide-head-end: End of <head> for custom CSS/JS
|
||||
- slide-header: Top of presentation (inside .reveal wrapper)
|
||||
- slide-footer: Bottom of presentation (inside .reveal wrapper)
|
||||
- slide-body-end: End of <body> for custom JS
|
||||
|
||||
Branding Configuration (site.Params.slides.branding or page front matter):
|
||||
logo:
|
||||
src: "media/logo.png"
|
||||
position: "top-left" # top-left, top-right, bottom-left, bottom-right
|
||||
width: "100px"
|
||||
title:
|
||||
show: true
|
||||
position: "bottom-left"
|
||||
author:
|
||||
show: true
|
||||
position: "bottom-right"
|
||||
*/}}
|
||||
|
||||
<!doctype html>
|
||||
@@ -38,6 +56,13 @@
|
||||
{{- $theme := $.Param "slides.theme" | default "black" -}}
|
||||
<link rel="stylesheet" href="{{ $cdn_url_reveal }}/dist/theme/{{ $theme }}.min.css">
|
||||
|
||||
{{/* Load branding CSS */}}
|
||||
{{ $branding_css := resources.Get "css/slides-branding.css" }}
|
||||
{{ with $branding_css }}
|
||||
{{ $branding_css = . | minify | fingerprint }}
|
||||
<link rel="stylesheet" href="{{ $branding_css.RelPermalink }}">
|
||||
{{ end }}
|
||||
|
||||
{{/* Hugo Chroma Syntax Highlighter */}}
|
||||
{{ $hl_theme := $.Param "slides.highlight_style" | default "dracula" }}
|
||||
{{ $hl_theme_path := printf "css/libs/chroma/%s.css" $hl_theme }}
|
||||
@@ -58,6 +83,10 @@
|
||||
document.head.appendChild(link);
|
||||
})();
|
||||
</script>
|
||||
|
||||
{{/* Hook: slide-head-end - Custom CSS/JS injection point */}}
|
||||
{{ partial "functions/slides_get_hook" (dict "hook" "slide-head-end" "context" .) }}
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -80,7 +109,8 @@
|
||||
{{ $slidejs := resources.Get "js/hugoblox-slides.js" | js.Build (dict "params" (dict "slides" $.Params.slides )) | fingerprint }}
|
||||
<script src="{{ $slidejs.RelPermalink }}"></script>
|
||||
|
||||
{{ partial "functions/get_hook" (dict "hook" "body-end" "context" .) }}
|
||||
{{/* Hook: slide-body-end - End of body for custom JS */}}
|
||||
{{ partial "functions/slides_get_hook" (dict "hook" "slide-body-end" "context" .) }}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user