Content Architecture Guide¶
Audience: Developers building on the Progress Credit Union platform Stack: Umbraco 17, .NET 10, Bootstrap 5.3, Razor Views
1. Document Type Hierarchy¶
The platform organizes content into three tiers of document types: the site root, configuration nodes, and content pages.
Site Root¶
The content tree has a single Home node (homeGrid) as the public site root. Two configuration nodes sit at root level outside the Home tree:
| Root Node | Document Type | Purpose |
|---|---|---|
| Home | homeGrid |
Public site root, parent of all content pages |
| Website Configuration | websiteConfiguration |
Global settings (logo, contact info, social URLs) |
| 2FA Settings | twoFASettings |
Two-factor authentication toggle |
Configuration Nodes¶
Configuration nodes live under Home but have no public URL. They store site-wide settings that views read through SiteSettingsService:
| Document Type | What It Controls |
|---|---|
menuConfiguration |
Navigation structure, mega-menu items |
footerConfiguration |
Footer layout and links |
siteStyleConfiguration |
Brand colors, fonts, CSS variables |
seoConfiguration |
Default meta tags, Open Graph image |
calculatorsConfiguration |
Loan/savings calculator rates and limits |
loanBoxesConfiguration |
Loan product cards, APR rates |
homepageSliderConfiguration |
Hero slider images and CTAs |
googleMapConfiguration |
Map markers, API settings |
googleTagManagerConfiguration |
GTM container ID |
cookieConfiguration |
Cookiebot ID, consent text |
privacyPolicyConfiguration |
DPO details, privacy statements |
Content Pages¶
Content pages are the public-facing document types. Key page types:
| Document Type | Template | Children Allowed |
|---|---|---|
homeGrid |
Homepage | Most page types |
standardPage |
General content | None |
standardPageNew |
Modern full-width content | None |
article |
News/blog post | None |
articleList |
News archive | newsCategoryItems |
faqs |
FAQ page | faqQuestionItem |
gallery |
Photo gallery | None |
galleryList |
Gallery index | gallery |
contactus |
Contact page with map | None |
testimonialCategories |
Testimonial container | testimonialCategoryItem |
careers |
Job listings | job |
2. BlockGrid 4-Level Structure¶
The page builder uses a strict 4-level nesting hierarchy. Every piece of content on a BlockGrid page follows this chain:
┌─────────────────────────────────────────────────────┐
│ Row Config │
│ Controls which layouts are available. │
│ Only block type allowed at root level. │
│ │
│ ┌───────────────────────────────────────────────┐ │
│ │ Layout │ │
│ │ Defines column structure (e.g., 6+6, 4+4+4) │ │
│ │ │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ │ │
│ │ │ Area Wrapper │ │ Area Wrapper │ │ │
│ │ │ Carries cell │ │ (one per column)│ │ │
│ │ │ styles: bg, │ │ │ │ │
│ │ │ padding, AOS │ │ │ │ │
│ │ │ │ │ │ │ │
│ │ │ ┌────────────┐ │ │ ┌────────────┐ │ │ │
│ │ │ │ Content │ │ │ │ Content │ │ │ │
│ │ │ │ (RTE, │ │ │ │ (Card, │ │ │ │
│ │ │ │ Hero, │ │ │ │ Form, │ │ │ │
│ │ │ │ Card...) │ │ │ │ Chart...)│ │ │ │
│ │ │ └────────────┘ │ │ └────────────┘ │ │ │
│ │ └─────────────────┘ └─────────────────┘ │ │
│ └───────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
Level Details¶
| Level | Element Type Pattern | Properties | allowAtRoot |
|---|---|---|---|
| Row Config | grid_{dtId}_rowconfig_{name} |
None (structural only) | true |
| Layout | grid_{dtId}_layout_{name} |
None (structural only) | false |
| Area Wrapper | grid_{dtId}_areawrapper_{name}_{idx} |
Cell background, padding, animation, visibility | false |
| Content | grid_builtin_{alias} or grid_element_{alias} |
Component-specific | false |
Settings at Each Level¶
| Level | Available Settings |
|---|---|
| Row Config | class, ID, data-aos-*, background-image, background-color, padding, margin, visible |
| Area Wrapper | background-color |
| Content | Varies per component (text, images, links, colors, etc.) |
How Editors Build a Page¶
- Add a Row -- select a row configuration (e.g., "1 column layout", "Left Sidebar")
- Choose a Layout -- pick column structure (e.g., Half = 6+6, Thirds = 4+4+4)
- Drop Components -- drag content blocks into each column area
- Configure -- set properties on each component and row/cell settings
Example: Two-Column Page Section¶
Row Config "1 column layout" ← root block
└── Layout "Half" ← 2 areas: col-6 + col-6
├── Area Wrapper (half_0) ← left column
│ ├── Heading Element ← content block
│ └── Rich Text Editor ← content block
└── Area Wrapper (half_1) ← right column
└── Card Grid Controls ← content block
Common Layout Patterns¶
| Layout | Columns | Bootstrap Classes | Typical Use |
|---|---|---|---|
| Full Width | 1 | col-12 |
Hero banners, full-width sections |
| Half | 2 | col-6 + col-6 |
Text + image, side-by-side |
| Thirds | 3 | col-4 + col-4 + col-4 |
Card grids, features |
| Fours | 4 | col-3 + col-3 + col-3 + col-3 |
Icon grids, stats |
| Left Nav | 2 | col-4 + col-8 |
Sidebar navigation + content |
| Right Sidebar | 2 | col-8 + col-4 |
Content + sidebar widgets |
| Five + Seven | 2 | col-5 + col-7 |
Asymmetric layout |
Rendering Views¶
The BlockGrid renders through a chain of partial views:
| View | Responsibility |
|---|---|
BootstrapGrid.cshtml |
Entry point, renders row configs with <div class="container"> wrapper |
_MultiSectionGrid.cshtml |
Handles multi-area layouts |
area.cshtml / areas.cshtml |
Renders individual grid areas |
items.cshtml |
Iterates content blocks within an area |
Components/*.cshtml |
Individual component rendering (120+ views) |
3. Page Templates vs BlockGrid Components¶
Page Templates¶
Templates define the overall page structure: header, footer, navigation, and where the BlockGrid sits. There are 46 templates organized by function:
| Category | Examples | Count |
|---|---|---|
| Main content | standardPage, homeGrid, article |
10 |
| Special purpose | contactus, cookies, Faq, search |
14 |
| Authentication | Login1, CustomerOnlyPage1 |
4 |
| Framework | master, masterNoHeaderNoFooter, SPALoader |
5 |
| Configuration | Non-public, backoffice-only | 13 |
Templates inherit from master.cshtml, which provides:
- Service injection (
SiteSettingsService,IDictionaryService) - Header and footer partials
- SEO meta tags from
seoConfiguration - Google Tag Manager script from
googleTagManagerConfiguration - CSS and JavaScript asset loading
BlockGrid Components¶
Components are the drag-and-drop building blocks editors place inside BlockGrid areas. There are 120+ components across 25 categories:
| Category | Count | Examples |
|---|---|---|
| Hero & Banner | 6 | Hero Element, Header, CTA Curves |
| Text & Content | 10 | Rich Text Editor, Heading, Quote |
| Call to Action | 6 | CTA Element, Button Widget, Loan Input CTA |
| Media & Video | 12 | Fancy Video, Video Full Width, Parallax Image |
| Cards | 8 | Card Grid, Flip Card, Spotlight |
| Lists | 10 | Numbered List, Image List, Link List |
| Accordion | 7 | Accordion Controls, FAQ Controls |
| Calculators | 5 | Main Calculator, Loan Input CTA |
| Charts | 6 | Bar Chart, Donut Chart, Comparison Table |
| Social | 7 | Facebook, Instagram, Twitter, Trustpilot |
| Maps | 4 | Google Map, Leaflet Map |
Each component is a Razor partial in Views/Partials/blockgrid/Components/. The view receives a BlockGridItem model:
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage<BlockGridItem>
@{
var content = Model.Content;
var title = content.Value<string>("title") ?? string.Empty;
}
4. Media Management¶
MediaWithCrops Pattern¶
All media properties use the MediaWithCrops type from Umbraco's MediaPicker3. The critical rule for single-pick media:
// CORRECT: Single-pick (multiple: false)
var image = content.Value<MediaWithCrops>("image");
var url = image?.MediaUrl() ?? string.Empty;
// WRONG: These return null silently
var image = content.Value<IPublishedContent>("image"); // null
var images = content.Value<IEnumerable<MediaWithCrops>>("image"); // null
For multi-pick media:
// Multi-pick (multiple: true)
var images = content.Value<IEnumerable<MediaWithCrops>>("images")
?? Enumerable.Empty<MediaWithCrops>();
foreach (var img in images)
{
var url = img.MediaUrl();
var alt = img.Value<string>("altText") ?? string.Empty;
}
Image URL Generation¶
// Basic URL
var url = image?.MediaUrl();
// With crop
var url = image?.GetCropUrl("thumbnail");
// With width/height
var url = image?.GetCropUrl(width: 800, height: 600);
5. Dictionary Items for Multi-Language Labels¶
The platform uses Umbraco Dictionary items for translatable UI labels. There are 169 dictionary keys covering calculators, widgets, forms, and navigation.
Accessing Dictionary Values in Views¶
// In Razor views (Umbraco helper available)
var label = Umbraco.GetDictionaryValue("widget.Address");
var faxLabel = Umbraco.GetDictionaryValue("widget.Fax");
var telLabel = Umbraco.GetDictionaryValue("widget.Tel");
Common Dictionary Key Patterns¶
| Prefix | Purpose | Example Keys |
|---|---|---|
widget.* |
Component labels | widget.Address, widget.Tel, widget.Fax, Widget.Email |
Calculator.* |
Calculator UI | Calculator.Amount, Calculator.Term, Calculator.Rate |
TCMobile.* |
Terms/mobile | TCMobile.CreditUnionWebsite |
Search.* |
Search UI | Search.NoResults, Search.Placeholder |
Case Sensitivity
Dictionary keys are case-sensitive. Some keys use widget. (lowercase) while others use Widget. (capitalized). Always match the exact key as stored in Umbraco.
6. Content Tree Organization¶
The content tree follows a logical hierarchy that maps to the site's URL structure:
Root
├── Home /
│ ├── Membership /membership/
│ │ ├── How to Join /membership/how-to-join/
│ │ └── Switch Account /membership/switch-account/
│ ├── Loans /loans/
│ │ ├── Motor Loan /loans/motor-loan/
│ │ ├── Holiday Loan /loans/holiday-loan/
│ │ ├── Home Improvement /loans/home-improvement/
│ │ └── Apply Now /loans/apply-now/
│ ├── Savings /savings/
│ ├── Services /services/
│ ├── About /about/
│ ├── Contact /contact/
│ ├── News & Events /news/
│ │ └── News Categories (category folders)
│ ├── FAQ /faq/
│ │ ├── Loans FAQ /faq/loans/
│ │ ├── Savings FAQ /faq/savings/
│ │ └── (more categories)
│ ├── Help /help/
│ ├── Search /search/
│ ├── Testimonials /testimonials/
│ │ └── Categories (Loans, General, etc.)
│ ├── Privacy Policy /privacy-policy/
│ ├── Terms and Conditions /terms-and-conditions/
│ ├── Cookie Notice /cookie-notice/
│ │
│ └── [Configuration Nodes] (no public URL)
│ ├── Calculators Config
│ ├── Loan Boxes Config
│ ├── Menu Config
│ ├── Footer Config
│ ├── Homepage Slider Config
│ ├── SEO Config
│ ├── Site Styles Config
│ ├── Google Maps Config
│ ├── GTM Config
│ ├── Cookie Config
│ └── Privacy Config
│
├── Website Configuration (root level, no URL)
└── 2FA Settings (root level, no URL)
Total content nodes: ~116
Access Control¶
- Credit Union Administrators -- see all content including Website Configuration
- Credit Union Editors -- see only content under Home (cannot access root-level config nodes)
7. Website Configuration Node¶
The websiteConfiguration document type is the central settings hub. It stores global values that every page needs. Views access it through SiteSettingsService, which caches all configuration nodes in a single snapshot.
How SiteSettingsService Works¶
The service scans the content tree at startup, finds each configuration node by document type alias, and wraps them in typed adapter interfaces:
// Injection in views
@inject ISiteSettingsService SiteSettings
// Access typed settings
var logo = SiteSettings.Website?.Logo;
var phone = SiteSettings.Website?.PhoneNumber;
var brandColor = SiteSettings.Styles?.PrimaryColor;
var gtmId = SiteSettings.GoogleTagManager?.ContainerId;
Available Settings Properties¶
| Property | Source | Type |
|---|---|---|
SiteSettings.Website |
websiteConfiguration |
IWebsiteSettings -- logo, contact info, social URLs |
SiteSettings.Styles |
siteStyleConfiguration |
ISiteStyleSettings -- brand colors, fonts |
SiteSettings.Menu |
menuConfiguration |
IMenuSettings -- navigation items |
SiteSettings.Footer |
footerConfiguration |
IFooterSettings -- footer content |
SiteSettings.Seo |
seoConfiguration |
ISeoSettings -- default meta tags, OG image |
SiteSettings.GoogleTagManager |
googleTagManagerConfiguration |
IGoogleTagManagerSettings -- GTM ID |
SiteSettings.HomepageSlider |
homepageSliderConfiguration |
IHomepageSliderSettings -- hero slides |
SiteSettings.Cookie |
cookieConfiguration |
ICookieSettings -- Cookiebot settings |
SiteSettings.LoanBoxesConfiguration |
loanBoxesConfiguration |
IPublishedContent -- raw content node |
SiteSettings.GoogleMapConfiguration |
googleMapConfiguration |
IPublishedContent -- raw content node |
SiteSettings.CalculatorsConfiguration |
calculatorsConfiguration |
IPublishedContent -- raw content node |
SiteSettings.SearchPage |
search |
IPublishedContent -- search page node |
Caching¶
The SiteSettingsService uses Umbraco's RuntimeCache with key Progress_SiteSettings_v3. The entire settings snapshot is built once and cached until the application pool recycles or cache is explicitly cleared. This means all 12 configuration node lookups happen in a single cache miss, not per-request.
Adding New Configuration¶
To add a new site-wide setting:
- Create a new document type (e.g.,
newFeatureConfiguration) in the backoffice - Add a content node of that type under Home
- Add a property to
SiteSettingsSnapshotand a lookup inSiteSettingsService.GetSettings() - Create an adapter implementing a new interface (e.g.,
INewFeatureSettings) - Expose it as a property on
ISiteSettingsService
Appendix: Grid Types Reference¶
The platform has 14 BlockGrid datatypes, each scoped to specific page types:
| Grid Type | Layouts | Components | Used By |
|---|---|---|---|
| Standard Page Grid | 20 | 108 | standardPage, gallery |
| Std Page Full Width | 11 | 41 | standardPageNew |
| Homepage Grid | 5 | 61 | homeGrid |
| Animated Grid | 6 | 50 | standardPageWow |
| Footer Grid | 7 | 31 | Footer partial |
| Article Grid | 4 | 35 | article, articleType2 |
| Contact Us Grid | 5 | 14 | contactus |
| Cookie Grid | 5 | 8 | cookies |
| Services Grid | 4 | 16 | Services pages |
| Nav Left Grid | 4 | 12 | Left-sidebar pages |
| Full Width Grid | 4 | 8 | Simple full-width pages |
| Full Width Bottom | 4 | 1 | Bottom-of-page sections |
Each grid type controls which layouts and components are available to editors, preventing inappropriate component usage (e.g., hero banners in the footer).