* fix: Firefox fallback breaks right-edge fade in related content (#133X)
Firefox does not support scroll-driven animations by default, causing
the animation to fall back to a 0s time-based animation that jumps to
the last keyframe — flipping the fade from right to left.
Wrap animation properties in @supports (animation-timeline: scroll())
so Firefox degrades gracefully to a static right-edge fade.
* chore: fix identation
* fix: add back wrongly deleted props
* language related deprecation fix
Reference: https://github.com/gohugoio/hugo/releases/tag/v0.158.0
.Site.LanguageCode -> .Site.Language.Locale
.Language.LanguageDirection -> .Language.Direction
.Language.LanguageCode -> .Language.Locale
Also, in the configuration hugo.toml, the user should change `languageCode` to `locale`.
* Update languageCode to locale in configuration
* fix(languages): update languageName to label in languages.toml
* .Language.LanguageName -> .Language.Label
* .Language.Lang -> .Language.Name
---------
Co-authored-by: Jimmy Cai <jimmy@cai.im>
* fix: comment scripts bypass cookie consent
When GDPR cookie consent is enabled, the comments container is simply
hidden with CSS. However, this still allows all third-party script
tags within the container to be evaluated and downloaded by the
browser.
This PR uses the <template> tag which stops inner scripts from evaluating.
Once consent is granted, the template content is cloned and inserted
into the container, and the scripts are manually loaded in order.
* refactor(comments): move consent-gated loader to TS asset
* fix(comments): add integrity attribute to comments consent script
* style: align comments consent docs and naming
* refactor(comments): simplify script handling
---------
Co-authored-by: Jimmy Cai <jimmy@cai.im>
* feat: support artalk comment system
* chore: change url from testingcf to cdn prefix
* refactor(comments): align artalk provider style and keep custom css overrides
* fix: update Artalk CDN links to use the correct URL
* refactor(comments): simplify Artalk implementation
* fix(artalk): update script tag to use ESM for Artalk
* fix(artalk): set locale to 'auto' for Artalk comments
* refactor: move artalk style and script link to external.toml
---------
Co-authored-by: Jimmy Cai <jimmy@cai.im>
* fix: do not show codeblock copy button if clipboard api is not available
related: https://github.com/CaiJimmy/hugo-theme-stack/issues/1309
* fix: remove duplicated codeblock script in main.ts
* feat: add i18n support to codeblock copy button
* feat: add translation strings for codeblock copy button
* fix: resolve related content card overflow and page horizontal scroll
* fix: prevent related content overflow on mobile while preserving shadow space
* fix: align related content cards and prevent shadow clipping
Expands the left margin of the related content carousel to match the main article, preserving the full card shadow. Adds a visual mask gradient to gracefully fade out the right edge.
* feat: add mask image also to left side of subsection list, which is activated when scrolling
* feat: fade out right mask image near the end of horizontal scroll
---------
Co-authored-by: Jimmy Cai <jimmy@cai.im>
* feat: add jump-to-page dialog on pagination ellipsis
- Replace pagination windowing algorithm with a tighter 5-item window
(curr-1 / curr / curr+1, clamped at edges) and hide-on-mobile for
pages more than 1 step away from the current page
- Ellipsis spans now carry the pagination-jump-trigger class and open
a native <dialog> on click instead of being static decorations
- Add <dialog id="pagination-jump-dialog"> with number input, Go button,
and Enter-key hint; data-total / data-first-url / data-format-url
attributes on <nav> give the JS all it needs to build the target URL
- Add assets/ts/pagination.ts: setupPaginationJump() with Fisher-Yates
backdrop-click and ESC handling, animated open/close via .closing class
- Wire setupPaginationJump() into Stack.init() in main.ts
- Extend pagination.scss with .hide-on-mobile, .pagination-jump-trigger
hover style, and the full #pagination-jump-dialog styling + keyframe
scale-up/scale-down animations
- Add [pagination] i18n keys (jumpToPage / jump / pressEnter) to
en / zh / zh-hant-tw / zh-hant-hk / ja
* style: align pagination code with upstream conventions
- pagination.html: collapse multi-line span/class/aria attrs to single
lines; use HTML5 void element syntax; fix hint div indentation
- pagination.scss: 2-space indent → 4-space indent
- pagination.ts: tab indent → 4-space indent; export const arrow fn →
export function declaration
* chore: update layouts/_partials/pagination.html
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* chore: add aria-labelledby attrributes for better accesibility
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* fix: use { once: true } for the listener and guard against re-entry when .closing is already set.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* style: replace hardcoded values with design tokens and improve dialog UX
- Replace all hardcoded px spacing in .page-link with --spacing-* tokens
- Use @include card-style in #pagination-jump-dialog; override with --shadow-l2
- Replace padding: 24px with var(--card-padding) for responsive dialog padding
- Align header title/subtitle to space-between for cleaner layout
- Replace hardcoded gaps/padding in form with spacing tokens
- Fix input/button border-radius from 8px to 6px (better proportion for 44px height)
- Fix focus ring: use color-mix(accent-color 20%) so it's visible in dark mode
- Add :focus-visible + outline:none to fully suppress native browser outline
- Remove translateY from button hover for more restrained interaction
- Scope transition to specific properties (border-color, box-shadow, background-color)
* chore: add pagination test articles for easier debugging
* style: enhance backdrop animations for pagination jump dialog
* style: replace pagination ellipsis spans with buttons for improved accessibility
* fix: check if browser supports dialog API before continue with pagination jump setup
* fix: retain focus on last active element when closing pagination jump dialog
* fix: style for page-link button (pagination-jump)
* style: adjust width of pagination jump dialog, add container padding
* style: simplify
* refactor: simplify pagination link logic
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Jimmy Cai <jimmy@cai.im>
* skip inaccessible images to avoid build failure
* undo changes to $result and $local
* fix: initialize $resource as a dictionary instead of an empty string
* feat: add external image processing timeout configuration
---------
Co-authored-by: Jimmy Cai <jimmy@cai.im>
* feat: use images.AutoOrient in `helper/image`
closes https://github.com/CaiJimmy/hugo-theme-stack/issues/1292
* fix: check if IsImageResourceProcessable before using filter
* remove local field
* feat: add configurable image auto-orientation via a new `autoOrient` parameter in site configuration
* refactor: rename `article-footer` to `article-meta`, and remove unnecesary structures
* fix: Update default article license URL to point to Creative Commons.
* refactor: rename article-time class to article-footer for consistency
* feat: show the number of subpages in `article-compact` when the page is a term
- archives.html: Link the "Archives" title to the archives page,
consistent with how individual year links already work
- taxonomy.html: Change showLink default from false to true so
tag-cloud and categories widget titles link to their taxonomy
listing pages by default
Widget titles are the primary affordance users interact with, so
linking them to the full listing page improves navigation. The
showLink param can still be set to false to disable this.
Co-authored-by: delize <4028612+delize@users.noreply.github.com>
* refactor: add standardized spacing variables and more style cleanup
* refactor: rename `spacing-xxl` to `spacing-2xl` and introduce `spacing-3xl` for consistent spacing variables.
* refactor: Migrate related content styling to use the `subsection-list` component with updated layout adjustments.
* style: adjust padding values for article list tiles.
* refactor: Introduce shadow variables and dynamic spacing to prevent shadow cutoff in list layouts.
* refactor: relocate dark scheme Mermaid variables within the root scope.
* fix: keep external image url, and use reflect.IsImageResourceProcessable to check for svg
* build: update Hugo version to 0.157.0 in the build script.
* docs: remove `Local` return value and simplify `Resource` description in image helper
* Check for reflect.IsImageResourceProcessable before getting color from image
* chore: update minimum Hugo version to 0.157.0
Fix reading time not displaying due to .Site context in dict partial
The details.html partial receives a dict context via:
partial "article/components/details" (dict "Page" $Page "IsList" $IsList)
When the dot context is a dict, .Site resolves to nil, so
.Site.Params.article.readingTime always returns empty/false,
causing the reading time to never display.
Lines 40 and 67 of the same file already correctly use
$Page.Site.Params — this fixes line 31 to be consistent.
Co-authored-by: delize <4028612+delize@users.noreply.github.com>
* feat: add GDPR cookie consent banner
Add a GDPR-compliant cookie consent banner that gates analytics (Google
Analytics) and functional cookies (comments) until user consent is given.
Features:
- Theme-consistent styling using CSS variables (light/dark mode)
- Settings panel with category toggles (necessary, analytics, functional)
- Consent stored in cookie for 365 days
- Google Analytics only loads after analytics consent
- Comments only load after functional consent
- Translations for all 32 supported languages
- Accessible with proper ARIA attributes and focus management
Configuration in hugo.yaml:
params:
cookies:
enabled: true
categories:
analytics: true
functional: true
When cookies.enabled is false, scripts load normally without consent gating.
Closes#412
* refactor: move consent-gated GA to cookies/analytics.html
Move Google Analytics consent logic to avoid shadowing Hugo's internal
`_internal/google_analytics.html` template.
Changes:
- Rename google_analytics.html → cookies/analytics.html
- Update head.html to conditionally load:
- cookies.enabled=true: cookies/analytics.html (consent-gated)
- cookies.enabled=false: Hugo's _internal/google_analytics.html
- Align cookies/analytics.html with Hugo's internal template:
- Support Privacy.GoogleAnalytics.Disable setting
- Support Privacy.GoogleAnalytics.RespectDoNotTrack setting
- Add UA- prefix deprecation warning
Note: Custom JS is required for consent-gated GA because Hugo templates
render at build time, but consent happens at runtime. We cannot call
Hugo's internal template after the user grants consent - we must
dynamically inject the GA script via JavaScript.
* style: indent cookies/analytics.html
* refactor: don't use default, put default configurations in params.toml
* fix: don't use `description` as translation key because it's reserved and will throw error
* fix: remove footer/components/custom-font.html call
* fix: banner showSettings key
* style: indent comments/include
* Add missing article.alert translation back
* style: format i18n toml files
* style: indent head/head.html
* style adjustment
* style: add missing -
* style: format demo/config/params.toml
* style: remove redundant comments from cookies.scss
* style: update box-shadow for cookie banner content
---------
Co-authored-by: delize <4028612+delize@users.noreply.github.com>
Co-authored-by: Jimmy Cai <jimmy@cai.im>
* docs: add blockquote with custom alert title
* feat: add alert types to i18n and update blockquote rendering
* fix: wrong translation key
* feat: add alert title translation for all supported languages
Done by AI
* Markdown alerts
* refactor(alert): move alert color to variable.scss and simplify the style
* style: fix indent in render-blockquote
* feat(alert): add customizable alert icons for different alert types
* doc: add example usage of markdown alert
---------
Co-authored-by: Jimmy Cai <jimmy@cai.im>
* refactor: introduce a generic taxonomy widget and refactor existing category and tag cloud widgets to use it.
* feat: Implement optional title linking for taxonomy widgets and enable parameter customization for taxonomy partials.
* refactor: remove fallback to taxonomy page title for widget display
Because this field always exists. I think it's better to let user customize via widget params
* Update layouts/_partials/widget/taxonomy.html
* refactor: check for .Params.taxonomy existence before accessing
* add option to set a custom canonical url
* style: use `with` to load cannonicalurl
* style: change to canonicalUrl
Frontmatter is case-insensitive so it does not matter
---------
Co-authored-by: FarisZR <35614734+FarisZR@users.noreply.github.com>
* Add support for comentario.
* feat: add Comentario client configuration options in TOML
* fix: format theme attribute in comentario comments component
---------
Co-authored-by: Jimmy Cai <jimmy@cai.im>