This document is an onboarding note for the next maintainer.
The focus of this pass is to move the announcement system’s real source of truth out of config params and into theme data, so rendering, styling, and multilingual content all point to one consistent path.
Key changes in this pass
1) The real source of truth moved to themes/(0000-0000-0000-0001)/data/40-communication/announcement/_index.toml
Files:
themes/(0000-0000-0000-0001)/data/40-communication/announcement/_index.toml
Changes:
- Centralized operational values for announcements in theme data.
enabled,strategy,defaultMode,defaultVariant,placement, andmaxItemsare now managed in one place.- Each announcement item now owns its own
contentblock, while language-specific overrides live indata/40-communication/announcement/<lang>.toml. - Image, alt text, links, and CTA values are managed per announcement item through the same key structure.
- Multiple samples remain in the file, but visibility is controlled by
enabledandscope.
Why this matters:
- The renderer now reads a layout-adjacent source, instead of depending on a config-only mental model.
- Commit-level changes to copy, media, CTA, and visibility rules become much easier to review.
- Maintainers can open the actual source immediately instead of searching through config fragments.
2) Settings calculation moved into theme/announcement-config.html
Files:
themes/(0000-0000-0000-0001)/layouts/partials/theme/announcement-config.htmlthemes/(0000-0000-0000-0001)/layouts/partials/theme/settings.html
Changes:
- Theme data is read first; legacy params remain as a fallback layer.
- Announcement normalization moved into its own partial so
settings.htmlstays smaller and easier to maintain. - The rendering partial now receives normalized state instead of re-implementing the same logic.
Why this matters:
- Rendering and configuration should stay separate to avoid spaghetti code.
- New announcement rules can now be added in one dedicated place.
- The same pattern can be reused for future centralized modules.
3) Renderer now supports images and linkable media
Files:
themes/(0000-0000-0000-0001)/layouts/partials/theme/announcement.htmlthemes/(0000-0000-0000-0001)/assets/css/common/announcement.css
Changes:
- The renderer now supports image-based cards, linked media, and captions.
- Media can render in either
toporasideplacement. - The close button remains intact while the card layout stays stable.
Why this matters:
- Announcements often need more than plain copy.
- Release notes, operational notices, and guidance cards are easier to understand when image and CTA live together.
- Keeping everything in one renderer reduces maintenance cost.
4) Nested section and path matching was strengthened
Files:
themes/(0000-0000-0000-0001)/layouts/partials/theme/announcement.htmlthemes/(0000-0000-0000-0001)/data/40-communication/announcement/_index.toml
Changes:
- Nested paths like
sections = ["blog/theme-upgrade-lab"]are now matched using path-aware checks. pathPrefixesandpathsare compared with language prefixes in mind.- Matching is derived from page path information instead of hardcoding a specific locale or page.
Why this matters:
.Sectionalone is not enough for nested content trees.- In many cases, the section path is more important than the top-level content section.
- Maintainers can read the scope rule and understand the intent immediately.
5) config/_default/params/_index.toml was reduced to a path guide
Files:
config/_default/params/_index.toml
Changes:
- The real copy was removed from this config file.
- The file now points to the actual source, renderer, and style paths.
- It acts as a navigation note for maintainers instead of a second source of truth.
Why this matters:
- Config and theme data should not fight over the same responsibility.
- Root config is better kept for site-wide policy and compatibility hints.
- Design and announcements now each have their own centralized control path.
Updated tree
config/
└── _default/
├── hugo.toml
├── params.toml
├── params/
│ ├── _index.toml # path guide / compatibility file
│ └── cta.toml
└── languages/
├── languages.ko.toml
├── languages.en.toml
├── languages.jp.toml
└── languages.cn.toml
themes/
└── (0000-0000-0000-0001)/
├── assets/
│ └── css/
│ ├── common/
│ │ └── announcement.css
│ └── core/
│ └── theme-vars/
│ └── 80-announcement.css
├── data/
│ └── _index.toml # actual source of truth
└── layouts/
└── partials/
└── theme/
├── announcement-config.html
├── announcement.html
└── settings.html
Notes
- Do not move language files back to the root.
- Keep
hugo.tomlandparams.tomlunderconfig/_default/. - The announcement source of truth now lives in theme data.
- Keep design values inside the theme-vars layer.
- Keep UI strings inside theme i18n.
- Re-check nested section matching before shipping new samples.
Announcement structure addendum
This pass no longer treats announcements as a single items.<id>.content / data/40-communication/announcement/<lang>.toml blob.
The source is now split between one base file and language override files so structure and copy can evolve independently.
Current tree
themes/
└── (0000-0000-0000-0001)/
└── data/
└── _index.toml
└── announcement/
├── ko.toml
├── en.toml
├── jp.toml
└── cn.toml
Dismiss rules
session: hide for the current sessionpersistent: store in localStorage and hide until clearedhours: hide for a fixed number of hours
Pages to inspect
The showcase page uses
announcementProfile = "showcase"in front matter. That keeps the modal and inline examples reusable without hardcoding a single path.
Additional pass: global activation and scope normalization
This pass makes the announcement system easier to inspect by turning all banners on globally and standardizing the structure for every banner item.
What changed
- Added
scopeDefaultsinthemes/(0000-0000-0000-0001)/data/40-communication/announcement/_index.tomlso every item follows the same shape. languages / kinds / sections / pathPrefixes / taxonomies / termsnow treat["*"]as a wildcard.storageKeyis optional; leaving it blank now falls back to an auto-generated key.priorityis now treated as an ordering hint where larger numbers appear first.dismissModecontrols the close button persistence policy, withsessionas the default.snoozeHoursis reserved for the time-based hide action only.
Close button review
- Click handling now listens in the capture phase, so the close action is less likely to be blocked by ancestor listeners.
- Close behavior is split across session / persistent / hours policies.
- Storage failures do not block the visual hide action.
Layout review
- CTA and dismiss buttons stay content-sized on desktop and expand to full width on mobile only.
- Footer and dismiss alignment are controlled through theme-vars tokens rather than page-level hardcoding.
File tree to check
config/_default/params/_index.toml
themes/(0000-0000-0000-0001)/
├─ data/
│ ├─ _index.toml
│ └─ announcement/
│ ├─ ko.toml
│ ├─ en.toml
│ ├─ jp.toml
│ └─ cn.toml
├─ layouts/partials/theme/
│ ├─ announcement-config.html
│ └─ announcement.html
├─ assets/css/common/
│ └─ announcement.css
├─ assets/css/core/theme-vars/
│ └─ 80-announcement.css
└─ assets/js/04-composition-layer/cross-cutting-composition/
└─ announcement.js
Operational note
- To show every banner globally, keep
enabled = trueand the item scopes wide open. - Narrow the scope only when a banner truly belongs to a specific section or page class.
storageKeyshould be set only when a banner needs a long-lived dismissal identity.