Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .cursor/rules/common
Submodule common updated 1 files
+0 −9 README.md
69 changes: 35 additions & 34 deletions website/modules/asset/ui/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,19 @@ import { setupTagSearchForInput } from './searchInputHandler';
import { FilterModal } from './filterModal';
import { initClientSideFiltering } from './clientSideFiltering';

/* eslint-enable sort-imports */
function revealLoaded() {
document
.querySelectorAll(
'.breadcrumb.loading, .sf-container.loading, .page-main_content.loading',
)
.forEach((el) => {
el.classList.remove('loading');
el.classList.add('loaded');
if (el.hasAttribute('aria-busy')) {
el.setAttribute('aria-busy', 'false');
}
});
}

function initConfiguration() {
window.DEFAULT_VISIBLE_TAGS_COUNT = 5;
Expand Down Expand Up @@ -57,13 +69,11 @@ function initFontChanger() {

setInterval(() => {
currentFontIndex = (currentFontIndex + 1) % fonts.length;
// Use Array.prototype.at() for safer array access
const currentFont = fonts.at(currentFontIndex);
heroContent.style.fontFamily = currentFont;
}, 500);
}

// Tag search filter for case studies page
function initCaseStudiesTagFilter({
inputSelector = '.tag-search',
containerSelector = '.filter-section',
Expand Down Expand Up @@ -92,30 +102,24 @@ function initializeAllComponents() {
initClientSideFiltering();
}

// Barba pages
function initBarbaPageTransitions() {
if (!document.querySelector('[data-barba="container"]')) return;

apos.util.onReady(() => {
// Initialize case studies filter handler
initCaseStudiesFilterHandler();

// Original Barba enter callback
const originalEnterCallback = function (data, hasFilterAnchor) {
// Scroll to the top after page transition (unless we have a filter anchor)
if (!hasFilterAnchor) {
window.scrollTo(0, 0);
}

// Close menu if it's open
const menuButton = document.getElementById('nav-icon');
const menu = document.querySelector('[data-menu]');
if (menuButton && menu) {
menu.classList.remove('open');
menuButton.classList.remove('open');
}

// Trigger video play after transition
const video = data.next.container.querySelector('video');
if (video) {
video.play();
Expand Down Expand Up @@ -160,7 +164,6 @@ function initBarbaPageTransitions() {
cacheIgnore: false,
preventRunning: true,
timeout: 10000,

transitions: [
{
sync: false,
Expand All @@ -175,28 +178,27 @@ function initBarbaPageTransitions() {
],
});

// Add after hook for updating menu state
barba.hooks.after(() => {
// Update menu active state
const currentPath = window.location.pathname;
const menuLinks = document.querySelectorAll('.sf-nav__list a');

// First, remove active class from all menu items
menuLinks.forEach((link) => link.classList.remove('active'));

// Then, add active class to the current menu item
menuLinks.forEach((link) => {
const href = link.getAttribute('href');
const hrefPath = new URL(href, window.location.origin).pathname;
if (hrefPath === currentPath) {
link.classList.add('active');
}
});

revealLoaded();
if (!window.caseStudiesFilterModal) {
initFilterModal();
}
});
});
}

// Anchor Navigation
function initAnchorNavigation() {
const anchors = document.querySelectorAll('a[href^="#"]');
if (!anchors.length) return;
Expand All @@ -216,7 +218,6 @@ function initAnchorNavigation() {

function initMenuToggle() {
apos.util.onReady(() => {
// Menu Open
const menuButton = document.getElementById('nav-icon');
const menu = document.querySelector('[data-menu]');

Expand All @@ -227,7 +228,6 @@ function initMenuToggle() {
menuButton.classList.toggle('open');
});

// Close menu when clicking on menu items for non-logged users
const menuLinks = menu.querySelectorAll('a');
menuLinks.forEach((link) => {
link.addEventListener('click', () => {
Expand All @@ -240,7 +240,6 @@ function initMenuToggle() {
});
}

// Filter Case Studies modal for Case Studies mobile page
function initFilterModal() {
if (!document.querySelector('.cs_list')) {
return;
Expand All @@ -257,19 +256,23 @@ function initFilterModal() {
});
}

document.addEventListener('DOMContentLoaded', initFilterModal);

if (typeof barba !== 'undefined') {
barba.hooks.after(() => {
initFilterModal();
});
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initFilterModal);
} else {
initFilterModal();
}

export default () => {
initConfiguration();

initializeAllComponents();

if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', revealLoaded);
} else {
revealLoaded();
}

apos.util.onReady(() => {
initCaseStudiesFilterHandler();
});
Expand All @@ -278,17 +281,15 @@ export default () => {
initAnchorNavigation();
initMenuToggle();

// Case studies anchor fix
setTimeout(() => {
const { pathname, search, hash } = window.location;
const isCasesPage = pathname.includes('/cases');
const hasFilterParams =
search.includes('industry') ||
search.includes('stack') ||
search.includes('caseStudyType') ||
hash.includes('filter');

if (isCasesPage && hasFilterParams) {
if (
pathname.includes('/cases') &&
(search.includes('industry') ||
search.includes('stack') ||
search.includes('caseStudyType') ||
hash.includes('filter'))
) {
const filterAnchor = document.getElementById('filter');
if (filterAnchor) filterAnchor.scrollIntoView({ behavior: 'smooth' });
}
Expand Down
37 changes: 37 additions & 0 deletions website/modules/asset/ui/src/scss/_animation.scss
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,40 @@
filter: blur(0);
}
}

// Loading/Loaded states for reveal animations
.loading {
opacity: 0;
overflow: hidden;
visibility: hidden;
transform: translateY(20px);
transition:
opacity 0.6s ease-out,
transform 0.6s ease-out;
}

.loaded {
opacity: 1;
overflow: visible;
visibility: visible;
transform: translateY(0);
}

// Specific loading states for breadcrumbs and containers
.breadcrumb.loading,
.sf-container.loading {
opacity: 0 !important;
visibility: hidden !important;
transform: translateY(15px);
transition:
opacity 0.5s ease-out,
visibility 0.5s ease-out,
transform 0.5s ease-out;
}

.breadcrumb.loaded,
.sf-container.loaded {
opacity: 1 !important;
visibility: visible !important;
transform: translateY(0);
}
1 change: 0 additions & 1 deletion website/modules/asset/ui/src/scss/_cases.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
&_content {
display: flex;
flex-direction: column;
gap: 2rem;
margin-bottom: 3rem;
}

Expand Down
18 changes: 17 additions & 1 deletion website/modules/case-studies-page/views/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@
data-default-visible-tags="{{ data.defaultVisibleTagsCount }}"
>
<div class="cs_content">
<div class="page-main_content">{% area data.page, 'main' %}</div>
<div
class="page-main_content loading"
id="page-main_content"
aria-busy="true"
>
{% area data.page, 'main' %}
</div>

<div id="filter" class="filter-anchor"></div>

Expand Down Expand Up @@ -295,6 +301,16 @@ <h4 class="portfolio-title">{{ article.portfolioTitle }}</h4>
<script>
window.totalPages = {{ data.totalPages | json }};
</script>

<noscript>
<style>
#page-main_content {
opacity: 1 !important;
overflow: visible !important;
visibility: visible !important;
}
</style>
</noscript>
<script src="/js/modules/case-studies-page/infinite-scroll.js"></script>

{% endblock main %}
39 changes: 27 additions & 12 deletions website/views/fragments/fragments.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,37 +31,52 @@
<footer class="sf-footer sf-container">
<div class="sf-footer__content">
<div class="sf-footer__top">
<p class="sf-footer__tagline">A Deliberately Developmental Organization.</p>
<p class="sf-footer__tagline">
A Deliberately Developmental Organization.
</p>
<p class="sf-footer__tagline">Built by real people, for real impact.</p>
<p class="sf-footer__tagline">Let's grow something meaningful together.</p>
<p class="sf-footer__tagline">
Let's grow something meaningful together.
</p>
</div>

<div class="sf-footer__links">
<div class="sf-footer__links-group">
<a href="https://www.linkedin.com/company/speed-and-function/"
class="sf-footer__links-item"
target="_blank"
rel="noopener noreferrer">
<a
href="https://www.linkedin.com/company/speed-and-function/"
class="sf-footer__links-item"
target="_blank"
rel="noopener noreferrer"
>
linkedin
</a>
<a href="mailto:connect@speedandfunction.com"
class="sf-footer__links-item">
<a
href="mailto:connect@speedandfunction.com"
class="sf-footer__links-item"
>
connect@speedandfunction.com
</a>
</div>
<a href="/privacy-policy" class="sf-footer__links-item sf-footer__links-item--policy">
<a
href="/privacy-policy"
class="sf-footer__links-item sf-footer__links-item--policy"
>
privacy policy
</a>
</div>

<div class="sf-footer__bottom">
<p class="sf-copy">&#169; {{ data.currentYear or '2025' }} S&amp;F</p>
</div>
</div>
</footer>
{% endfragment %} {% fragment breadcrumbs() %} {% if data.page._url !=
data.home._url %}
<nav class="breadcrumb" aria-label="Breadcrumb navigation">
<nav
class="breadcrumb loading"
aria-label="Breadcrumb navigation"
aria-busy="true"
>
<ol class="breadcrumb-list">
{% for page in data.page._ancestors %}
<li class="breadcrumb-item">
Expand Down
19 changes: 14 additions & 5 deletions website/views/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,19 @@
<div
class="bp-wrapper relative"
data-barba="wrapper"
data-barba-namespace="home"
>
{% render fragments.header() %}
<main
class="bp-main"
role="main"
id="main"
{% if not data.user %}
data-barba="container"
data-barba="container"
data-barba-namespace="{{ (data.page and data.page.type) or 'default' }}"
{% endif %}
>
{% render fragments.breadcrumbs() %}
<div class="sf-container">
<div class="sf-container loading" aria-busy="true" data-reveal="dom">
{% endblock %}

{% block main %}
Expand All @@ -46,7 +46,16 @@
</div>
</main>
{% render fragments.footer() %}
<script type="text/javascript"></script>
<noscript>
<style>
.breadcrumb.loading,
.sf-container.loading {
opacity: 1 !important;
overflow: visible !important;
visibility: visible !important;
}
</style>
</noscript>
</div>
{# Close .bp-wrapper #}
{# Close .bp-wrapper #}
{% endblock %}
Loading