To ensure rapid delivery, maintainable code, and consistent styling across Cascade CMS implementations, adhere to the guidelines below. Following these practices yields cleaner code, simplifies User Acceptance Testing (UAT), and makes future customizations to the implementation more straightforward. When providing source files (e.g., via Git or local directories), ensure that fully compiled, static HTML versions are included. While access to source code is helpful, we require complete, production-ready HTML outputs to begin implementation.
Adhering to these guidelines will yield a maintainable, consistent codebase and streamline both initial implementation and future updates within Cascade CMS. Failure to follow them can result in delays while clarifications are sought, less-than-optimal implementations, and ultimately an unhappy client.
Base your layout on a stable, widely supported framework - e.g., the latest Bootstrap, Foundation, or Tailwind CSS.
Benefits:
Use classes for all visual styling. Don’t attach CSS rules to IDs - classes are reusable and don’t risk conflicts when a component appears multiple times.
Reserve IDs solely for elements that must be uniquely targeted (e.g., a specific anchor link, form field, or JavaScript hook).
<!-- Styling via class -->
<section class="promo-banner">
<h2 class="promo-banner__title">Spring Sale</h2>
<p class="promo-banner__text">Up to 50% off select items!</p>
</section>
<!-- When to use an ID -->
<header id="site-header">…</header>
<footer id="site-footer">…</footer>
<body> Classes - Use Semantic Containers InsteadAvoid adding classes on <body> unless absolutely required (e.g., a global mode switch).
Prefer applying page-template or context classes to a semantic wrapper - such as <main>, <article>, or a top-level <div>.
<!-- Discouraged -->
<body class="template-contact-page">
<!-- Preferred -->
<body>
<main class="template-contact-page">…</main>
</body>
Choose either <section> or <div> for each block; do not mix both for the same purpose.
Don’t use a component’s position.
Don’t use utility or BEM spacing classes.
Ensure a uniform vertical rhythm so that any section or row can be reordered without breaking spacing.
Because spacing can appear different on components with versus without backgrounds, establish rules that maintain equal visual gaps when components move.
Style all component headings by class - for example, .section__heading - rather than targeting <h1> - <h6> directly.
Separating visual style from semantic level lets you swap an <h2> for an <h3> (or vice versa) for accessibility, SEO, or logical hierarchy without touching CSS or breaking layouts.
Consistent naming: Reuse the same inner-class names across sections for maximum code reuse.
.feature .section__heading {
font-size: 2rem;
}
<section class="feature">
<h2 class="section__heading">Features Overview</h2>
<p>…</p>
</section>
.testimonial .section__heading {
font-size: 2.5rem;
}
<section class="testimonial">
<h3 class="section__heading">What Our Students Say</h3>
<blockquote>…</blockquote>
</section>
.cards .section__heading {
font-size: 2.5rem;
}
.cards .card .card__title {
font-size: 1.5rem;
}
<div class="cards">
<h4 class="section__heading">Card Title</h4>
<div class="card">
<h4 class="card__title">Card Title</h4>
<p>…</p>
</div>
<div class="card">
<h4 class="card__title">Card Title</h4>
<p>…</p>
</div>
</div>
Background images: Apply only the url() inline on the element. Never rely on position or order.<section class="hero" style="background-image:
url('/images/hero.jpg');"> … </section>
Alternatively, you can use CSS custom properties (also known as CSS variables) to make individual style values configurable per instance - especially for images, colors, spacing, or animation timing.
This approach allows developers to override values directly in the HTML (or Velocity/XML) without modifying or duplicating CSS rules. It keeps components flexible, reduces stylesheet bloat, and avoids the need for additional modifier classes or inline styles on individual sub-elements.
.hero {
background-size: cover;
background-position: center;
background-image: var(--bg-image);
}
<div class="hero" style="--bg-image: url('/images/jane.jpg');"></div>
This makes it easy to inject asset URLs or color values from Cascade using Velocity or structured data without changing the component itself.
Color variations via modifier classes: Use a clear modifier on the wrapper rather than positional selectors..card-light { background: #f9f9f9; color: #333; }
.card-dark { background: #333; color: #fff; }
<div class="card card-light">
<h3 class="card-title">Light Card</h3>
</div>
<div class="card card-dark">
<h3 class="card-title">Dark Card</h3>
</div>
Per-page scripts: Read all URLs, toggles, or options from that component’s own data- attributes - never hard-code in the full JS or rely on DOM order.
<section class="gallery" data-api-url="/api/gallery"
data-autoplay="true" data-slide-delay="5000”> … </section>
<script>(function() { const galleryEls = document.querySelectorAll('.gallery'); galleryEls.forEach(function(galleryEl) { const apiUrl = galleryEl.dataset.apiUrl; const autoplay = galleryEl.dataset.autoplay === 'true'; const delay = parseInt(galleryEl.dataset.slideDelay, 10); initializeGallery({ apiUrl, autoplay, delay }); });})();</script>
Most components can be used multiple times on a page so you’ll need to account for that by looping over every instance of the component.
“What You See Is What You Get” allows editors to format content visually. Always scope styling within a WYSIWYG container class: (e.g. <div class="wysiwyg">…</div>).
Target all elements via that parent container (e.g. .wysiwyg p, .wysiwyg h2, .wysiwyg h3, .wysiwyg h4) instead of styling tag selectors globally.
Important elements to style:
Paragraphs (p)
Headings (h2, h3, h4)
Bold (strong) & italics (em)
Links (a)
Lists (ul, ol)
Tables (table, th, td)
Blockquotes (blockquote)
Images & captions: Any image needing a caption must be wrapped in <figure> and use <figcaption>. Figure classes should work on both figure elements and image elements. Images that do not need a caption do not need to be wrapped in a figure element.
.wysiwyg p { margin-bottom: 1em; }
.wysiwyg h2 { font-size:1.75rem; margin-top:1.5em; }
.wysiwyg strong { font-weight:bold; }
.wysiwyg em { font-style:italic; }
.wysiwyg a { color:#0066cc; text-decoration:underline; }
.wysiwyg ul, .wysiwyg ol { margin:1em 0; padding-left:1.5em; }
.wysiwyg table { width:100%; border-collapse:collapse; }
.wysiwyg th, .wysiwyg td { padding:0.5em; border:1px solid #ddd; }
.wysiwyg blockquote {
margin:1.5em 0; padding:0.5em 1em;
border-left:4px solid #ccc; background:#f9f9f9;
}
/* Figure/image alignment */
.wysiwyg .figure, .wysiwyg img { width: 50%; }
.wysiwyg .figure img { width: 100%; height: auto; }
.wysiwyg .figure--align-left { width: 50%; float:left; margin:0 1em 1em 0; }
.wysiwyg .figure--align-right { width: 50%; float:right; margin:0 0 1em 1em; }
.wysiwyg .figure--full { display:block; width:100%; margin:1em 0; }
<div class="wysiwyg">
<p>This paragraph has <strong>bold</strong> and <em>italic</em> text plus <a href="#">a link</a>.</p>
<ul><li>Item one</li><li>Item two</li></ul>
<figure class="figure figure--align-left">
<img src="left.jpg" alt="">
<figcaption>Aligned left</figcaption>
</figure>
<figure class="figure figure--full">
<img src="full.jpg" alt="">
<figcaption>Full width</figcaption>
</figure>
</div>
Avoid overly specific or nested selectors like .wysiwyg blockquote.custom p.highlight.
Only tag-specific classes (e.g. .wysiwyg p.intro or .wysiwyg a.btn.cta-primary) if absolutely needed.
Follow W3C’s XHTML rules (https://www.w3schools.com/html/html_xhtml.asp):
All tags closed (even self-closing elements!)
Attribute values in quotes
Lowercase element & attribute names
Define everything.
Min/Max instances allowed
Contextual HTML differences (when the same component needs different code on different pages)
Conditional changes (e.g. no-image state, alternate data sets)
All variations (themes, sizes, states, layouts) with exact sample code must be provided.
If no example code is provided, it cannot be implemented.
Take a card component for example:
<div class="cards">
<div class="card">
<img class="card__image"… />
<h2 class="card__title">Card Title</h2>
<p class="card__content">
Curabitur tincidunt, felis a elementum tincidunt, ex felis fermentum dui, eget pulvinar arcu eros eu eros.
</p>
<a class="card__link" href="#">Read More</a>
</div>
</div>
As is, the card content text would use a plain text multiline field (text area) that outputs within the single <p>. But if its designed to be WYSIWYG content, then it should look like this: <div class="cards">
<div class="card">
<img class="card__image"… />
<h2 class="card__title">Card Title</h2>
<div class="card__content">
<p>Curabitur tincidunt, felis a <a href=”#”>elementum</a> tincidunt, ex felis fermentum dui, eget pulvinar arcu eros eu eros.</p>
<div>
<a class="card__link" href="#">
Read More
</a>
</div>
</div>
WYSIWYG fields can output a variety of elements <p>, <ul>, <a>, <strong>, etc., all of which must be styled. To keep your CSS DRY and consistent, always wrap WYSIWYG output in a single, shared class (for example, .wysiwyg). That way you can apply a common base style everywhere, while still letting each parent component layer on its own overrides when needed.
Matching your structured data to the expected HTML reduces template complexity and ensures editors have exactly the controls they need (and no more).
Load critical CSS inline or defer non-critical stylesheets. For example, the header and banner would be critical since most components will be rearrangeable.
Use loading="lazy" for images and iframes that appear below the fold.
For big images, serve the right image size for the user’s device using the <picture> element. This avoids oversized assets on mobile and improves perceived speed.
<picture>
<source media="(min-width:1024px)" srcset="../../assets/img/hero.webp" />
<source media="(min-width:700px)" srcset="../../assets/img/hero.webp" />
<img fetchpriority="high" src="../../assets/img/hero.webp" alt="hero pattern lorem" />
</picture>
Use modern formats like .webp and tailor breakpoints to your layout needs. Set fetchpriority="high" only for the most important above-the-fold image per page (like the banner).
Combine smaller assets when possible and remove unused CSS or JS.
Place JavaScript at the end of <body> or use the defer attribute.
Scripts that power interactive components (e.g., sliders or tabs) should initialize based on data attributes and be loaded conditionally or after user interaction.
Ensure all components are designed and implemented to meet WCAG 2.1 AA accessibility standards. Accessible websites benefit all users, improve SEO, and are often legally required in higher education.
Best Practices:
Use semantic HTML: Leverage proper elements (<header>, <nav>, <main>, <section>, <footer>) to convey meaning.
Headings must follow a logical order: Skip levels only with intent. Use heading tags to define structure, not style.
All images must include alt attributes: Describe purpose, not appearance. Decorative images should use alt="".
Keyboard navigability: All interactive elements (e.g. menus, tabs, modals) must be operable via keyboard alone.
Color contrast: Ensure sufficient contrast between text and backgrounds (minimum 4.5:1 for body text).
ARIA only when necessary: Use ARIA roles and properties to enhance - not replace - semantic HTML.
Focus styles must be visible: Avoid removing outlines without clear, visible alternatives.
Form inputs require labels: Every input must be associated with a <label> or aria-label.
Accessibility should be considered at the design, development, and content-entry levels. Components should never assume mouse input or visual cues alone.
When replacing the header and footer, remove the existing hh-global.css and hh-menu.js references and files.
Do not modify any existing hh-*.css or hh-*.js assets.
Any additional CSS or JavaScript must be placed in a new file and loaded after all current CSS/JS references.
Avoid altering any HTML beyond what is strictly necessary for the new header, footer, or their supporting markup.