+
+
diff --git a/static/img/social-linkedin.svg b/static/img/social-linkedin.svg
new file mode 100644
index 0000000..7d0f952
--- /dev/null
+++ b/static/img/social-linkedin.svg
@@ -0,0 +1,3 @@
+
diff --git a/static/img/social-youtube.svg b/static/img/social-youtube.svg
new file mode 100644
index 0000000..f683be7
--- /dev/null
+++ b/static/img/social-youtube.svg
@@ -0,0 +1,3 @@
+
diff --git a/static/js/modules/RecentPostsCarousel.js b/static/js/modules/RecentPostsCarousel.js
new file mode 100644
index 0000000..3570ad2
--- /dev/null
+++ b/static/js/modules/RecentPostsCarousel.js
@@ -0,0 +1,48 @@
+const mobileViewport = window.matchMedia("(max-width: 640px)");
+
+const initializeCarousel = (carousel) => {
+ const cards = Array.from(carousel.querySelectorAll(".recent-posts__card"));
+ const dots = Array.from(carousel.querySelectorAll(".recent-posts__dot"));
+ const previous = carousel.querySelector(".recent-posts__control--previous");
+ const next = carousel.querySelector(".recent-posts__control--next");
+
+ if (cards.length < 2 || !previous || !next) {
+ return;
+ }
+
+ let activeIndex = 0;
+
+ const render = () => {
+ cards.forEach((card, index) => {
+ const isActive = index === activeIndex;
+ card.classList.toggle("is-active", isActive);
+ card.hidden = mobileViewport.matches && !isActive;
+ });
+
+ dots.forEach((dot, index) => {
+ const isActive = index === activeIndex;
+ dot.classList.toggle("is-active", isActive);
+ dot.toggleAttribute("aria-current", isActive);
+ });
+ };
+
+ const showArticle = (index) => {
+ activeIndex = (index + cards.length) % cards.length;
+ render();
+ };
+
+ previous.addEventListener("click", () => showArticle(activeIndex - 1));
+ next.addEventListener("click", () => showArticle(activeIndex + 1));
+ dots.forEach((dot, index) =>
+ dot.addEventListener("click", () => showArticle(index)),
+ );
+ mobileViewport.addEventListener("change", render);
+
+ render();
+};
+
+const initRecentPostsCarousels = () => {
+ document.querySelectorAll(".recent-posts").forEach(initializeCarousel);
+};
+
+export default initRecentPostsCarousels;
diff --git a/static/js/theme.js b/static/js/theme.js
index c1e01ed..cdd6b8b 100644
--- a/static/js/theme.js
+++ b/static/js/theme.js
@@ -7,6 +7,7 @@ import { registerBackToTopButton } from './components/backToTop.js';
import GetHeaderHeight from './modules/GetHeaderHeight.js';
import tagExternalLinks from './modules/TagExternalLinks.js';
import Navigation from './modules/Navigation.js';
+import initRecentPostsCarousels from './modules/RecentPostsCarousel.js';
// Add passive event listeners
! function (e) {
@@ -68,6 +69,9 @@ document.addEventListener('DOMContentLoaded', () => {
// Initialize Header Height
GetHeaderHeight();
+ // Initialize mobile Recent Insights carousels.
+ initRecentPostsCarousels();
+
// Add Back to Top button to body
// const backToTop = document.createElement('x-back-to-top');
// document.body.appendChild(backToTop);
diff --git a/styles/base/colors.css b/styles/base/colors.css
index bd18612..09c8bf8 100644
--- a/styles/base/colors.css
+++ b/styles/base/colors.css
@@ -24,6 +24,7 @@
--color-primary-900: color-mix(in oklch, var(--color-primary) 90%, white);
--color-secondary: var(--color-cwc-orange-01);
+ --color-secondary-accessible: #c43c2b;
--color-secondary-100: color-mix(in oklch, var(--color-secondary) 10%, white);
--color-secondary-200: color-mix(in oklch, var(--color-secondary) 20%, white);
--color-secondary-300: color-mix(in oklch, var(--color-secondary) 30%, white);
diff --git a/styles/blocks/buttons.css b/styles/blocks/buttons.css
index 8da169e..96df3e6 100644
--- a/styles/blocks/buttons.css
+++ b/styles/blocks/buttons.css
@@ -143,10 +143,10 @@ x-button:has(.button[data-button-width="full"]) { @apply w-full; }
* like the ones below and changing the color values.
*/
.btn[data-button-color="secondary"], .button[data-button-color="secondary"] {
- --button-bg: var(--color-secondary);
+ --button-bg: var(--color-secondary-accessible);
--button-color: var(--color-white);
- --button-border-color: var(--color-secondary);
- --button-outline-color: var(--color-secondary);
+ --button-border-color: var(--color-secondary-accessible);
+ --button-outline-color: var(--color-secondary-accessible);
--button-hover-bg: color-mix(in oklch, var(--color-secondary) 74%);
--button-hover-border-color: color-mix(in oklch, var(--color-secondary) 74%);
--button-hover-color: var(--color-white);
diff --git a/tests/site-a11y.spec.js b/tests/site-a11y.spec.js
index 0e9593c..a6e19db 100644
--- a/tests/site-a11y.spec.js
+++ b/tests/site-a11y.spec.js
@@ -1,60 +1,88 @@
-const { test, expect } = require('@playwright/test');
-const AxeBuilder = require('@axe-core/playwright').default;
+const { test, expect } = require("@playwright/test");
+const AxeBuilder = require("@axe-core/playwright").default;
-const domain = 'http://domain.local/';
+const domain =
+ process.env.TEST_BASE_URL || "http://community-works-collaborative.test/";
test.use({
- viewport: { width: 1920, height: 1080 },
+ viewport: { width: 1920, height: 1080 },
});
-test.describe('site-test', () => {
- test('Homepage Test', async ({ page }, testInfo) => {
- await page.goto(domain);
+test.describe("site-test", () => {
+ test("Homepage Test", async ({ page }, testInfo) => {
+ await page.goto(domain);
- await page.screenshot({ path: 'test-results/homepage.png', fullPage: true });
+ await page.screenshot({
+ path: "test-results/homepage.png",
+ fullPage: true,
+ });
- const accessibilityScanResults = await new AxeBuilder({ page })
- .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22a', 'wcag22aa'])
- .analyze();
+ const accessibilityScanResults = await new AxeBuilder({ page })
+ .withTags([
+ "wcag2a",
+ "wcag2aa",
+ "wcag21a",
+ "wcag21aa",
+ "wcag22a",
+ "wcag22aa",
+ ])
+ .analyze();
- await testInfo.attach('accessibility-scan-results', {
- body: JSON.stringify(accessibilityScanResults, null, 2),
- contentType: 'application/json'
+ await testInfo.attach("accessibility-scan-results", {
+ body: JSON.stringify(accessibilityScanResults, null, 2),
+ contentType: "application/json",
+ });
+
+ expect(accessibilityScanResults.violations).toEqual([]);
});
- expect(accessibilityScanResults.violations).toEqual([]);
- });
+ test("Blog Index Page Test", async ({ page }, testInfo) => {
+ await page.goto(`${domain}news/`);
- test('Blog Index Page Test', async ({ page }, testInfo) => {
- await page.goto(`${domain}news/`);
+ await page.screenshot({
+ path: "test-results/blog-index.png",
+ fullPage: true,
+ });
- await page.screenshot({ path: 'test-results/blog-index.png', fullPage: true });
+ const accessibilityScanResults = await new AxeBuilder({ page })
+ .withTags([
+ "wcag2a",
+ "wcag2aa",
+ "wcag21a",
+ "wcag21aa",
+ "wcag22a",
+ "wcag22aa",
+ ])
+ .analyze();
- const accessibilityScanResults = await new AxeBuilder({ page })
- .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22a', 'wcag22aa'])
- .analyze();
+ await testInfo.attach("accessibility-scan-results", {
+ body: JSON.stringify(accessibilityScanResults, null, 2),
+ contentType: "application/json",
+ });
- await testInfo.attach('accessibility-scan-results', {
- body: JSON.stringify(accessibilityScanResults, null, 2),
- contentType: 'application/json'
+ expect(accessibilityScanResults.violations).toEqual([]);
});
- expect(accessibilityScanResults.violations).toEqual([]);
- });
+ test("404 Page Test", async ({ page }, testInfo) => {
+ await page.goto(`${domain}yaya/`);
- test('404 Page Test', async ({ page }, testInfo) => {
- await page.goto(`${domain}yaya/`);
+ await page.screenshot({ path: "test-results/404.png", fullPage: true });
- await page.screenshot({ path: 'test-results/404.png', fullPage: true });
+ const accessibilityScanResults = await new AxeBuilder({ page })
+ .withTags([
+ "wcag2a",
+ "wcag2aa",
+ "wcag21a",
+ "wcag21aa",
+ "wcag22a",
+ "wcag22aa",
+ ])
+ .analyze();
- const accessibilityScanResults = await new AxeBuilder({ page })
- .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22a', 'wcag22aa'])
- .analyze();
+ await testInfo.attach("accessibility-scan-results", {
+ body: JSON.stringify(accessibilityScanResults, null, 2),
+ contentType: "application/json",
+ });
- await testInfo.attach('accessibility-scan-results', {
- body: JSON.stringify(accessibilityScanResults, null, 2),
- contentType: 'application/json'
+ expect(accessibilityScanResults.violations).toEqual([]);
});
-
- expect(accessibilityScanResults.violations).toEqual([]);
- });
});
diff --git a/views/blocks/recent-posts/recent-posts.css b/views/blocks/recent-posts/recent-posts.css
index 2b9c825..24c4612 100644
--- a/views/blocks/recent-posts/recent-posts.css
+++ b/views/blocks/recent-posts/recent-posts.css
@@ -75,6 +75,11 @@
position: relative;
}
+.recent-posts__controls,
+.recent-posts__pagination {
+ display: none;
+}
+
.recent-posts__grid::before {
background: var(--color-cwc-blue-03);
content: "";
@@ -273,10 +278,57 @@
top: min(184px, calc(100cqw * 250 / 397));
}
- .recent-posts__card:nth-child(n + 2) {
+ .recent-posts__card {
display: none;
}
+ .recent-posts__card.is-active {
+ display: flex;
+ }
+
+ .recent-posts__controls {
+ display: block;
+ }
+
+ .recent-posts__control {
+ align-items: center;
+ background: var(--color-secondary);
+ border: 0;
+ border-radius: 50%;
+ color: var(--color-white);
+ display: flex;
+ height: 3rem;
+ justify-content: center;
+ padding: 0;
+ position: absolute;
+ top: 10rem;
+ width: 3rem;
+ z-index: 3;
+ }
+
+ .recent-posts__control:hover,
+ .recent-posts__control:focus-visible {
+ background: var(--color-secondary-800);
+ }
+
+ .recent-posts__control svg {
+ fill: none;
+ height: 1.75rem;
+ stroke: currentColor;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-width: 2.5;
+ width: 1.75rem;
+ }
+
+ .recent-posts__control--previous {
+ left: -1.5rem;
+ }
+
+ .recent-posts__control--next {
+ right: -1.5rem;
+ }
+
.recent-posts__image-link {
border-radius: 0.5rem 0.5rem 0 0;
}
@@ -303,17 +355,38 @@
.recent-posts__footer {
align-items: center;
flex-direction: column;
- gap: 1.25rem;
- margin-top: 1.4rem;
+ margin-top: 1.25rem;
}
- .recent-posts__footer::before {
- background:
- radial-gradient(circle, var(--color-cwc-blue-01) 0 0.2rem, transparent 0.22rem) left center / 0.75rem 0.5rem repeat-x;
+ .recent-posts__pagination {
+ display: flex;
+ gap: 0.75rem;
+ justify-content: center;
+ margin-top: 1.5rem;
+ }
+
+ .recent-posts__dot {
+ align-items: center;
+ background: transparent;
+ border: 0;
+ display: flex;
+ height: 1.5rem;
+ justify-content: center;
+ padding: 0;
+ width: 1.5rem;
+ }
+
+ .recent-posts__dot::before {
+ background: var(--color-cwc-blue-01);
+ border-radius: 50%;
content: "";
- display: block;
height: 0.5rem;
- width: 2.25rem;
+ opacity: 0.45;
+ width: 0.5rem;
+ }
+
+ .recent-posts__dot.is-active::before {
+ opacity: 1;
}
.recent-posts__blog-link.button {
diff --git a/views/blocks/recent-posts/recent-posts.php b/views/blocks/recent-posts/recent-posts.php
index b94be6f..aef2a38 100644
--- a/views/blocks/recent-posts/recent-posts.php
+++ b/views/blocks/recent-posts/recent-posts.php
@@ -56,13 +56,15 @@ $wrapper = blockWrapperAttributes( $classes, $is_preview );
have_posts() ) : ?>
have_posts() ) :
$recent_posts->the_post();
$post_title = get_the_title();
$post_permalink = get_permalink();
+ $active_class = 0 === $post_index ? ' is-active' : '';
?>
-
+
@@ -96,7 +98,34 @@ $wrapper = blockWrapperAttributes( $classes, $is_preview );
-
+
+
+
+
+
+
diff --git a/views/icons/facebook.php b/views/icons/facebook.php
index 82dbfe3..7cbd0a9 100644
--- a/views/icons/facebook.php
+++ b/views/icons/facebook.php
@@ -1 +1 @@
-
diff --git a/views/icons/linkedin.php b/views/icons/linkedin.php
index b6a19b1..3c16583 100644
--- a/views/icons/linkedin.php
+++ b/views/icons/linkedin.php
@@ -1 +1 @@
-
diff --git a/views/icons/youtube.php b/views/icons/youtube.php
index 34003d0..3c5e5e6 100644
--- a/views/icons/youtube.php
+++ b/views/icons/youtube.php
@@ -1 +1 @@
-