feat: implement hero-style header for standard posts and add mobile-responsive sidebar hiding.

This commit is contained in:
2026-07-20 17:46:40 +07:00
parent d6d722386f
commit 7d937a71f8
4 changed files with 152 additions and 234 deletions
@@ -1,198 +1,41 @@
<div class="cc--component-container cc--news-events">
<div class="c--component c--news-events">
<!-- Phần 2 cột: Tin mới (Trái) và Sự kiện (Phải) -->
<div class="news-events-row">
<div class="news-column">
<h2 class="section-title--news" style="font-size: 2rem;">
TIN TỨC MỚI
</h2>
<div class="news-slider-wrapper">
<button class="news-slider-btn prev-btn" aria-label="Previous">&lsaquo;</button>
<div class="news-slider-container">
<div class="news-slider-track">
<!-- Shortcode Tin Tức hiển thị dạng list (limit tăng lên 6 để có thể trượt) -->
[component:news-grid data-source="posts:tag=news,limit=6,collection=news_posts"]
<div class="content-top">
<div class="lc--layout-container lc--full">
<div class="l--layout l--full">
<div class="lr--layout-region lr--main">
<div class="cc--component-container cc--homepage-hero ">
<div class="c--component c--homepage-hero">
<div class="slides-container">
<div class="image-video-container has-video">
<img src="https://www.umass.edu/sites/default/files/styles/1_1_1920x1920/public/2025-09/250616_UMASS_4515.jpg" alt="Students collaborate using a driving simulator in the UMass Center for Transportation." srcset="https://www.umass.edu/sites/default/files/styles/1_1_1920x1920/public/2025-09/250616_UMASS_4515.jpg?h=9855f42d&amp;itok=d27r8GaT 1920w">
<div class="f--ambient-video" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; overflow: hidden; pointer-events: none; z-index: 1;">
<iframe src="https://www.youtube.com/embed/2dC8bjTpjKk?autoplay=1&mute=1&controls=0&playsinline=1&rel=0&modestbranding=1&showinfo=0" style="position: absolute; top: 50%; left: 50%; width: 100vw; height: 56.25vw; min-height: 100vh; min-width: 177.77vh; transform: translate(-50%, -50%); border: none;" allow="autoplay; encrypted-media" allowfullscreen></iframe>
</div>
<!-- <div class="video-controls" data-once="ambientVideo">
<div class="video-controls-inner">
<button aria-labelledby="pauseBtn" class="video-button video-pause-button">
...
</button>
</div>
<button class="news-slider-btn next-btn" aria-label="Next">&rsaquo;</button>
</div> -->
</div>
</div>
<div class="events-column">
<h2 class="events-title" style="font-size: 2rem;">
SỰ KIỆN
</h2>
<div class="events-widget">
<!-- Shortcode Sự kiện -->
[component:events-list data-source="posts:tag=event,limit=4,collection=event_posts"]
</div>
</div>
</div>
<!-- Header chung (Mobile) -->
<div class="news-events-section__footer">
<a href="/news/news-events" class="news-events-section__cta news-events-section__cta--mobile">
Xem Thêm
<div class="text-container centered">
<div class="slide-text-container-inner">
<h3>
<a href="https://www.umass.edu/gateway/why-umass">Stroke International School 2026</a>
</h3>
<div class="f--field f--button">
<a href="https://www.umass.edu/gateway/why-umass" class="button-secondary button-context-dark "
aria-label="Learn More Learn more about UMass Amherst." data-component-id="umass_base:button">
<span class="button-text"> Learn More </span>
</a>
</div>
[menu:INFO:dropdown]
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<style>
.news-slider-wrapper {
position: relative;
padding: 0px;
}
.news-slider-container {
overflow: hidden;
width: 100%;
}
.news-slider-wrapper .news-grid,
.news-slider-wrapper .row,
.news-slider-track {
display: flex !important;
flex-wrap: nowrap !important;
gap: 20px;
margin: 0 !important;
transition: transform 0.4s ease;
}
/* Force children to be fixed width cards - 2 cards at a time because this column is narrow */
.news-slider-wrapper .news-grid > *,
.news-slider-wrapper .row > *,
.news-slider-track > * {
flex: 0 0 calc(50% - 10px) !important;
max-width: none !important;
}
@media (max-width: 992px) {
.news-slider-wrapper .news-grid > *,
.news-slider-wrapper .row > *,
.news-slider-track > * {
flex: 0 0 calc(50% - 10px) !important;
}
}
@media (max-width: 576px) {
.news-slider-wrapper .news-grid > *,
.news-slider-wrapper .row > *,
.news-slider-track > * {
flex: 0 0 100% !important;
}
}
.news-slider-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 40px;
height: 40px;
border-radius: 50%;
background-color: rgba(200, 200, 200, 0.5);
color: #333;
border: none;
font-size: 30px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 10;
transition: background-color 0.2s, color 0.2s;
line-height: 1;
}
.news-slider-btn:hover {
background-color: rgba(0, 0, 0, 0.7);
color: #fff;
}
.news-slider-btn.prev-btn {
left: -15px;
}
.news-slider-btn.next-btn {
right: -15px;
}
.news-slider-btn:disabled {
opacity: 0.2;
cursor: not-allowed;
}
</style>
<script>
document.addEventListener("DOMContentLoaded", function() {
const wrappers = document.querySelectorAll('.news-slider-wrapper');
wrappers.forEach(wrapper => {
const prevBtn = wrapper.querySelector('.prev-btn');
const nextBtn = wrapper.querySelector('.next-btn');
let track = wrapper.querySelector('.news-slider-track');
if (track && track.children.length === 1 && (track.children[0].classList.contains('news-grid') || track.children[0].classList.contains('row'))) {
track = track.children[0];
track.classList.add('news-slider-track');
}
if (!track) return;
let currentIndex = 0;
function updateSlider() {
const items = track.children;
if(items.length === 0) return;
const itemWidth = items[0].getBoundingClientRect().width;
const gap = parseFloat(window.getComputedStyle(track).gap) || 20;
track.style.transform = `translateX(-${currentIndex * (itemWidth + gap)}px)`;
const containerWidth = wrapper.querySelector('.news-slider-container').getBoundingClientRect().width;
const visibleCount = Math.max(1, Math.floor((containerWidth + gap) / (itemWidth + gap)));
prevBtn.disabled = currentIndex === 0;
nextBtn.disabled = currentIndex >= items.length - visibleCount;
}
prevBtn.addEventListener('click', () => {
if (currentIndex > 0) {
currentIndex--;
updateSlider();
}
});
nextBtn.addEventListener('click', () => {
const items = track.children;
const itemWidth = items[0].getBoundingClientRect().width;
const gap = parseFloat(window.getComputedStyle(track).gap) || 20;
const containerWidth = wrapper.querySelector('.news-slider-container').getBoundingClientRect().width;
const visibleCount = Math.max(1, Math.floor((containerWidth + gap) / (itemWidth + gap)));
if (currentIndex < items.length - visibleCount) {
currentIndex++;
updateSlider();
}
});
window.addEventListener('resize', () => {
const items = track.children;
if(items.length === 0) return;
const itemWidth = items[0].getBoundingClientRect().width;
const gap = parseFloat(window.getComputedStyle(track).gap) || 20;
const containerWidth = wrapper.querySelector('.news-slider-container').getBoundingClientRect().width;
const visibleCount = Math.max(1, Math.floor((containerWidth + gap) / (itemWidth + gap)));
if (currentIndex > items.length - visibleCount) {
currentIndex = Math.max(0, items.length - visibleCount);
}
updateSlider();
});
setTimeout(updateSlider, 200);
});
});
</script>
@@ -555,3 +555,11 @@ body, h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 {
.lc--layout-container ol li, .lc--layout-container ul li, .sg-pattern-example ol li, .sg-pattern-example ul li {
font-weight: var(--font-weight-extrabold);
}
/* Hide widget column on mobile */
@media (max-width: 768px) {
.events-column,
.widget-sidebar-area {
display: none !important;
}
}
@@ -1,6 +1,6 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{fragments/layout}">
layout:decorate="~{themes/umass/layout}">
<head>
<title th:text="${post.title} + ' | UMass Amherst'">Post Title | UMass Amherst</title>
@@ -8,35 +8,63 @@
</head>
<body>
<div layout:fragment="content">
<div id="block-umass-base-content" class="block block-system block-system-main-block tc--template-container tc--article">
<div class="t--template t--article">
<!-- Content Top (e.g. Hero Image & Title) -->
<div layout:fragment="content-top">
<!-- Content Top (Banner 3 lớp kết hợp Title bài Post) -->
<div class="content-top">
<div class="lc--layout-container lc--full">
<div class="l--layout l--full">
<div class="lr--layout-region lr--main">
<div class="cc--component-container cc--hero-article">
<div class="c--component c--hero-article">
<div class="f--field f--image" th:if="${post.featuredImage != null and !post.featuredImage.isEmpty()}">
<img th:src="${post.featuredImage}" th:alt="${post.title}">
</div>
<div class="text-container">
<div class="f--field f--eyebrow" th:if="${post.category != null}" th:text="${post.category.name}">
<div class="cc--component-container cc--homepage-hero">
<div class="c--component c--homepage-hero"
style="position: relative; overflow: hidden; height: 100vh; max-height: 38.125rem;">
<!-- ====== ẢNH NỀN (Dùng ảnh bài viết hoặc ảnh mặc định) ====== -->
<img class="static-fallback-image" th:src="${post.featuredImage != null and !post.featuredImage.isEmpty() ? post.featuredImage : 'https://res.cloudinary.com/ipufdhda/image/upload/v1784534008/BANNER-WEB-scaled_yeox4u.jpg'}" th:alt="${post.title}" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; z-index: 1;" />
<!-- ====== TEXT OVERLAY (Nội dung chữ nằm trên ảnh, Hiển thị Tiêu đề bài viết) ====== -->
<!-- Thêm một lớp nền mờ (rgba đen) để chữ luôn hiển thị rõ ràng trên mọi hình ảnh -->
<div class="text-container centered"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 4; pointer-events: none; display: flex; align-items: center; justify-content: center; background-color: rgba(0, 0, 0, 0.4);">
<div class="slide-text-container-inner" style="pointer-events: auto; text-align: center; padding: 20px;">
<div class="f--field f--eyebrow" th:if="${post.category != null}" th:text="${post.category.name}" style="color: white; text-shadow: 0 2px 5px rgba(0,0,0,0.7); margin-bottom: 10px; font-weight: bold; text-transform: uppercase;">
University News
</div>
<div class="f--field f--page-title">
<h1 th:text="${post.title}">Post Title</h1>
<h1 class="hero-post-title" th:text="${post.title}">Post Title</h1>
</div>
</div>
</div>
<style>
.hero-post-title {
color: white;
text-shadow: 0 2px 5px rgba(0,0,0,0.7);
line-height: 1.2;
margin: 0 auto;
max-width: 1000px;
font-size: 3.5rem;
}
@media (max-width: 1024px) {
.hero-post-title { font-size: 2.8rem; max-width: 800px; }
}
@media (max-width: 768px) {
.hero-post-title { font-size: 2.2rem; max-width: 600px; }
}
@media (max-width: 480px) {
.hero-post-title { font-size: 1.8rem; }
}
</style>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div layout:fragment="content">
<div id="block-umass-base-content" class="block block-system block-system-main-block tc--template-container tc--article">
<div class="t--template t--article">
<!-- Content Main (Two Column Layout with Sidebar) -->
<div class="content-main">
<div class="lc--layout-container lc--two-column">
@@ -57,23 +85,52 @@
</div>
<div class="cc--component-container cc--share-block ">
<div class="c--component c--share-block">
<div data-component-id="umass_base:section-title" class="f--section-title">
<h2>Share This</h2>
<div class="f--section-title">
<h2 style="color: #881c1c; font-size: 1rem; font-weight: 700; text-transform: uppercase; margin-bottom: 20px;">Share This</h2>
</div>
<ul class="social-media-links--platforms a2a_kit a2a_default_style" style="line-height: 16px;">
<style>
.custom-share-list {
list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 15px; align-items: flex-start;
}
.custom-share-list li a {
display: flex; align-items: center; justify-content: center; width: 45px; height: 45px; border-radius: 50%; border: 1px solid #111; text-decoration: none; transition: all 0.2s;
}
.custom-share-list li a:hover {
background-color: #f5f5f5;
}
.custom-share-list li a svg {
width: 18px; height: 18px;
}
</style>
<ul class="custom-share-list">
<li>
<a class="fa a2a_button_facebook facebook" aria-label="Share on Facebook" target="_blank" href="#" rel="nofollow noopener">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 19 19" fill="currentColor" aria-hidden="true"><path fill="#1877f2" d="M18,0H1.1C0.5,0,0,0.5,0,1.1V18c0,0.6,0.5,1,1.1,1h9.1v-7.3H7.7V8.8h2.5V6.7c0-2.5,1.5-3.8,3.7-3.8c0.7,0,1.5,0,2.2,0.1v2.6h-1.5c-1.2,0-1.4,0.6-1.4,1.4v1.8H16l-0.4,2.9h-2.5V19H18c0.6,0,1-0.5,1-1V1.1C19,0.5,18.5,0,18,0z"></path></svg>
<a href="#" aria-label="Website">
<svg viewBox="0 0 512 512" fill="#28a745"><path d="M352 256c0 22.2-1.2 43.6-3.3 64H163.3c-2.2-20.4-3.3-41.8-3.3-64s1.2-43.6 3.3-64h185.4c2.2 20.4 3.3 41.8 3.3 64zm28.8-64h123.1c5.3 20.5 8.1 41.9 8.1 64s-2.8 43.5-8.1 64H380.8c2.1-20.6 3.2-41.9 3.2-64s-1.1-43.4-3.2-64zm112.6-32H376.7c-10-63.9-29.8-117.4-55.3-151.6c78.3 20.7 142 77.5 171.9 151.6zm-149.1 0H167.7c16.1-70.4 46.2-128 88.3-128s72.2 57.6 88.3 128zM256 480c-42.1 0-72.2-57.6-88.3-128h176.6c-16.1 70.4-46.2 128-88.3 128zM144 141.6C118.5 175.8 98.7 229.3 88.7 293.2H18.6C48.5 219.1 112.2 162.3 190.5 141.6h-46.5zM18.6 325.2H88.7c10 63.9 29.8 117.4 55.3 151.6c-78.3-20.7-142-77.5-171.9-151.6zM131.2 256c0-22.1 1.1-43.4 3.2-64H11.3C6 212.5 3.2 233.9 3.2 256s2.8 43.5 8.1 64h123.1c-2.1-20.6-3.2-41.9-3.2-64z"/></svg>
</a>
</li>
<li>
<a class="fa a2a_button_x x" aria-label="Share on X" target="_blank" href="#" rel="nofollow noopener">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 -2 50 50" fill="currentColor" aria-hidden="true"><path fill="#111111" d="M5.9199219 6L20.582031 27.375L6.2304688 44h3.1796874L21.986328 29.421875L31.986328 44H44L28.681641 21.669922L42.199219 6h-3.169922L27.275391 19.617188L17.933594 6z"></path></svg>
<a aria-label="Share on Facebook" target="_blank" href="#">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19 19" fill="#1877f2"><path d="M18,0H1.1C0.5,0,0,0.5,0,1.1V18c0,0.6,0.5,1,1.1,1h9.1v-7.3H7.7V8.8h2.5V6.7c0-2.5,1.5-3.8,3.7-3.8c0.7,0,1.5,0,2.2,0.1v2.6h-1.5c-1.2,0-1.4,0.6-1.4,1.4v1.8H16l-0.4,2.9h-2.5V19H18c0.6,0,1-0.5,1-1V1.1C19,0.5,18.5,0,18,0z"></path></svg>
</a>
</li>
<li>
<a class="fa a2a_button_linkedin linkedin" aria-label="Share on LinkedIn" target="_blank" href="#" rel="nofollow noopener">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 448 512" fill="currentColor" aria-hidden="true" focusable="false"><path fill="#0a66c2" d="M100.28 448H7.4V148.9h92.88zM53.79 108.1C24.09 108.1 0 83.5 0 53.8a53.79 53.79 0 0 1 107.58 0c0 29.7-24.1 54.3-53.79 54.3zM447.9 448h-92.68V302.4c0-34.7-.7-79.2-48.29-79.2-48.29 0-55.69 37.7-55.69 76.7V448h-92.78V148.9h89.08v40.8h1.3c12.4-23.5 42.69-48.3 87.88-48.3 94 0 111.28 61.9 111.28 142.3V448z"></path></svg>
<a aria-label="Share on X" target="_blank" href="#">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -2 50 50" fill="#111111"><path d="M5.9199219 6L20.582031 27.375L6.2304688 44h3.1796874L21.986328 29.421875L31.986328 44H44L28.681641 21.669922L42.199219 6h-3.169922L27.275391 19.617188L17.933594 6z"></path></svg>
</a>
</li>
<li>
<a aria-label="Share on LinkedIn" target="_blank" href="#">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" fill="#0a66c2"><path d="M100.28 448H7.4V148.9h92.88zM53.79 108.1C24.09 108.1 0 83.5 0 53.8a53.79 53.79 0 0 1 107.58 0c0 29.7-24.1 54.3-53.79 54.3zM447.9 448h-92.68V302.4c0-34.7-.7-79.2-48.29-79.2-48.29 0-55.69 37.7-55.69 76.7V448h-92.78V148.9h89.08v40.8h1.3c12.4-23.5 42.69-48.3 87.88-48.3 94 0 111.28 61.9 111.28 142.3V448z"></path></svg>
</a>
</li>
<li>
<a aria-label="Email" href="mailto:">
<svg viewBox="0 0 512 512" fill="#881c1c"><path d="M48 64C21.5 64 0 85.5 0 112c0 15.1 7.1 29.3 19.2 38.4L236.8 313.6c11.4 8.5 27 8.5 38.4 0L492.8 150.4c12.1-9.1 19.2-23.3 19.2-38.4c0-26.5-21.5-48-48-48H48zM0 176V384c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V176L294.4 339.2c-22.8 17.1-54 17.1-76.8 0L0 176z"/></svg>
</a>
</li>
<li>
<a aria-label="Print" href="javascript:window.print()">
<svg viewBox="0 0 512 512" fill="#111"><path d="M128 0C92.7 0 64 28.7 64 64v96h64V64H384v96h64V64c0-35.3-28.7-64-64-64H128zM18.6 243.4c-11.7 5.1-18.6 16.6-18.6 29C0 293.7 17.3 311 38.6 311H473.4c21.3 0 38.6-17.3 38.6-38.6c0-12.4-6.9-23.9-18.6-29l-38.2-16.7C442.2 221.7 430.7 216 418.5 216H93.5c-12.2 0-23.7 5.7-29.2 15.2L18.6 243.4zM160 384h192c17.7 0 32-14.3 32-32V256c0-17.7-14.3-32-32-32H160c-17.7 0-32 14.3-32 32v96c0 17.7 14.3 32 32 32zM0 400c0 35.3 28.7 64 64 64h48v32c0 8.8 7.2 16 16 16h256c8.8 0 16-7.2 16-16v-32h48c35.3 0 64-28.7 64-64v-16H0v16z"/></svg>
</a>
</li>
</ul>
@@ -20,24 +20,34 @@
<div class="dialog-off-canvas-main-canvas" data-off-canvas-main-canvas="">
<!-- Include Header Fragment -->
<header th:replace="~{themes/__${activeTheme}__/header :: header}"></header>
<!-- Full-width top content area (e.g., Hero Banners) -->
<div layout:fragment="content-top"></div>
<main id="main-content">
<div class="l--content">
<div class="content">
<div class="region region-content r--region r--content">
<!-- Content Area -->
<div th:with="isFullWidth=${(page != null and page.layout != null and page.layout.name() == 'FULL_WIDTH') or (post != null and post.layout != null and post.layout.name() == 'FULL_WIDTH')}" th:style="${isFullWidth} ? 'min-height: 400px;' : 'display: flex; min-height: 400px; padding: 20px; max-width: 1400px; margin: 0 auto;'">
<div th:style="${isFullWidth} ? 'width: 100%;' : 'flex: 3; padding-right: 20px;'" layout:fragment="content"></div>
<aside th:unless="${isFullWidth}" style="flex: 1; padding: 15px; border-radius: 5px;">
<div class="responsive-flex-container" th:with="isFullWidth=${(page != null and page.layout != null and page.layout.name() == 'FULL_WIDTH') or (post != null and post.layout != null and post.layout.name() == 'FULL_WIDTH')}" th:style="${isFullWidth} ? 'min-height: 400px;' : 'display: flex; min-height: 400px; padding: 20px; max-width: 1400px; margin: 0 auto;'">
<div class="responsive-flex-content" th:style="${isFullWidth} ? 'width: 100%;' : 'flex: 3; padding-right: 20px;'" layout:fragment="content"></div>
<aside class="widget-sidebar-area" th:unless="${isFullWidth}" style="flex: 1; padding: 15px; border-radius: 5px;">
<th:block th:if="${sidebarWidgets != null and !sidebarWidgets.empty}">
<h4>Sidebar</h4>
<div th:each="widget : ${sidebarWidgets}" style="margin-bottom: 20px;">
<h5 th:text="${widget.title}" style="border-bottom: 1px solid #ddd; padding-bottom: 5px;">Widget Title</h5>
<div th:if="${widget.type == 'HTML'}" th:utext="${widget.content}"></div>
<div th:if="${widget.type == 'TEXT'}" th:text="${widget.content}"></div>
</div>
<div th:if="${sidebarWidgets == null || sidebarWidgets.empty}">
<p>No widgets added to sidebar.</p>
</div>
</th:block>
</aside>
<style>
@media (max-width: 768px) {
.widget-sidebar-area { display: none !important; }
.responsive-flex-container { flex-direction: column !important; padding: 10px !important; }
.responsive-flex-content { padding-right: 0 !important; width: 100% !important; }
}
</style>
</div>
</div>
</div>