Regarding my website design work, I worked primarily with the oceanwp theme, and the elementor and elementor pro plugins and theme builders within a multisite environment to build websites. Within those two systems, further customization with .css and javascript were regular and varied. I pride myself on an ability to make the impossible possible with uniquely coded solutions, and always enjoy a puzzle. Me and the rest of the web team worked within an AWS multisite environment, though some FSPs had unique websites that required special exceptions.
At Luson Media, we provide custom websites for Financial Service Professionals. These projects can range from clients needing a fast, templated build ready to go in a week, to more personalized projects with specific marketing intentions in mind. Because of the sheer volume of websites we manage (1000+), and to streamline the design process with clients that are often not design experts, we created a series of website templates that Financial Service Professionals could select before we began designing. For the example below, I created a logo and a basic enough brand that clients could hopefully project their own company onto the website template. Airborne, to help differentiate it from our other templates, was focused on movement. View the link below to see the full template design.
The two projects below demonstrate a more templated build (5280rg), and a more specialized approach (Educational Association). One demonstrates my ability to still inject individuality and branding within a pre-constructed structure, while the other displays my ability to expand beyond this framework to offer custom-tailored solutions to meet unique needs and requests.Â
For 5280rg, I prided myself on developing unique features that turned a website template into a branded and personalized experience. I developed a unique contact form system that would return resources to clients upon form submissions that integrated into their CRM system, which helped streamline their marketing flow and made it easier to gather relevant contact information for interested potential clients.Â
For the Educational Association, I had to create a completely custom website that highlighted the six-step plan he had developed for his clients’ retirement journey. It’s always fun to work with such a colorful brand, and I tried my best to use these colors in meaningful ways: corresponding to different steps in the retirement journey, and through eye-catching, interactive elements across the site.
After being provided a style guide, written material, and images for the new Meraki site I had to craft a new vision that would align with shifting values, while incorporating a more modern design. This was a fun, collaborative process that incorporated a custom-coded service wheel with description pop-ups, content-centered design with shifting, fluid elements to encapsulate the reactive nature of the company.Â
Like the Meraki project, I worked with a Graphic Designer within Luson to create a new branding guide for the Genesis company. Using this, I updated their outdated website into one that functioned better, while highlighting their full range of services and elevating the user experience with more comprehensive user flow. Per the client specifications, I was able to create a custom stock ticker that would scroll on the bottom of their site without the need to pay for any third-party extensions. See below for an example of the code used to create the custom “ticker” at the bottom of their website!
A custom “ticker” that shows stock information for three companies: S&P, NASDAQ, and DOW. This was a fun puzzle, as the ticker needed to be able to pull from publicly available sources as the client did not want to pay for the feature to exist on their website. After some research, alphavantage.co provided the necessary resources to make the magic happen!
<script>
window.addEventListener('load', function () {
const ticker = document.querySelector('.ticker');
if (!ticker) return;
const apiKey = 'S10HAO5L3JIBNTXX';
const symbols = [
{ name: 'S&P 500', symbol: 'SPY' },
{ name: 'NASDAQ', symbol: 'QQQ' },
{ name: 'DOW', symbol: 'DIA' }
];
ticker.innerHTML = '<div class="ticker-item">Loading market data…</div>';
const requests = symbols.map(function (item) {
const url =
'https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=' +
item.symbol +
'&apikey=' + apiKey;
return fetch(url)
.then(function (res) {
return res.json();
})
.then(function (data) {
console.log(item.symbol, data);
const quote = data['Global Quote'];
/* HANDLE FAILED API RESPONSES */
if (
!quote ||
!quote['05. price'] ||
!quote['10. change percent']
) {
console.warn('Missing quote data for:', item.symbol);
return {
name: item.name,
price: '--',
change: 'N/A',
dir: 'down'
};
}
const price = parseFloat(quote['05. price']);
const changeText = quote['10. change percent'];
const changeNumber = parseFloat(changeText);
return {
name: item.name,
price: price.toFixed(2),
change: changeText,
dir: changeNumber >= 0 ? 'up' : 'down'
};
})
.catch(function (err) {
console.error('Fetch failed:', item.symbol, err);
return {
name: item.name,
price: '--',
change: 'N/A',
dir: 'down'
};
});
});
Promise.all(requests).then(function (results) {
ticker.innerHTML = '';
results.forEach(function (item) {
const el = document.createElement('div');
el.className = 'ticker-item ' + item.dir;
el.innerHTML =
'<strong>' + item.name + '</strong>' +
'<span class="price">$' + item.price + '</span>' +
'<span class="change">' + item.change + '</span>';
ticker.appendChild(el);
});
});
});
</script>
/*ticker tape*/
/* Outer container */
.ticker-wrap {
width: 100%;
overflow: hidden;
background: #f7f9fc;
border-radius: 8px;
padding: 8px 0;
box-sizing: border-box;
}
/* 3-column layout */
.ticker {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
align-items: center;
width: 100%;
gap: 4px;
box-sizing: border-box;
}
/* EACH ITEM */
.ticker-item {
display: flex;
justify-content: center;
align-items: center;
gap: 6px;
padding: 10px 6px;
font-size: 13px;
font-weight: 500;
min-width: 0;
overflow: hidden;
text-align: center;
}
/* Prevent wrapping */
.ticker-item strong,
.ticker-item .price,
.ticker-item .change {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* LABEL */
.ticker-item strong {
color: #161819;
font-weight: 600;
}
/* PRICE */
.ticker-item .price {
color: #627793;
}
/* POSITIVE */
.ticker-item.up .change {
color: #17B022;
}
/* NEGATIVE */
.ticker-item.down .change {
color: #B01717;
}
/* MOBILE */
@media (max-width: 768px) {
/* KEEP 3 COLUMNS ON MOBILE */
.ticker {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.ticker-item {
font-size: 11px;
gap: 4px;
padding: 8px 4px;
}
}
<script>
document.addEventListener("DOMContentLoaded", function () {
const wrapper = document.querySelector(".orbit-wrapper");
const orbit = wrapper.querySelector(".e-con-inner");
const circles = document.querySelectorAll(".orbit-circle");
const total = circles.length;
const angleStep = 360 / total;
let rotation = 0;
let isDragging = false;
let startX = 0;
let autoplay;
function applyRotation() {
orbit.style.transform = `translate(-50%, -50%) rotate(${rotation}deg)`;
// keep content upright
circles.forEach(circle => {
const contents = circle.querySelectorAll(".orbit-content");
contents.forEach(c => {
c.style.transform = `rotate(${-rotation}deg)`;
});
});
}
function rotateNext() {
rotation -= angleStep;
applyRotation();
}
function startAutoplay() {
autoplay = setInterval(rotateNext, 3500);
}
function stopAutoplay() {
clearInterval(autoplay);
}
/* -----------------------
Drag Controls
----------------------- */
wrapper.addEventListener("mousedown", (e) => {
isDragging = true;
startX = e.clientX;
stopAutoplay();
});
window.addEventListener("mouseup", () => {
if (!isDragging) return;
isDragging = false;
// snap to nearest item
rotation = Math.round(rotation / angleStep) * angleStep;
applyRotation();
startAutoplay();
});
window.addEventListener("mousemove", (e) => {
if (!isDragging) return;
const delta = e.clientX - startX;
const sensitivity = 0.4;
rotation += delta * sensitivity;
startX = e.clientX;
applyRotation();
});
/* -----------------------
Touch support
----------------------- */
wrapper.addEventListener("touchstart", (e) => {
isDragging = true;
startX = e.touches[0].clientX;
stopAutoplay();
});
window.addEventListener("touchend", () => {
if (!isDragging) return;
isDragging = false;
rotation = Math.round(rotation / angleStep) * angleStep;
applyRotation();
startAutoplay();
});
window.addEventListener("touchmove", (e) => {
if (!isDragging) return;
const delta = e.touches[0].clientX - startX;
const sensitivity = 0.4;
rotation += delta * sensitivity;
startX = e.touches[0].clientX;
applyRotation();
});
/* -----------------------
Init
----------------------- */
applyRotation();
startAutoplay();
});
</script>
/*rotating service gallery*/
.orbit-wrapper {
position: relative;
width: 900px;
height: 900px; /* diameter of the large invisible orbit */
margin: 0 auto;
overflow: visible;
}
.orbit-circle {
position: absolute;
width: 350px;
height: 350px;
border-radius: 50%;
top: 50%;
left: 50%;
padding: 25px;
margin: -175px; /* center itself */
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
transition: transform 0.5s ease, z-index 0.5s ease;
z-index: 1;
}
/* Position each circle along the full 360° perimeter */
.orbit-circle:nth-child(1) { transform: rotate(0deg) translateY(-450px); }
.orbit-circle:nth-child(2) { transform: rotate(60deg) translateY(-450px); }
.orbit-circle:nth-child(3) { transform: rotate(120deg) translateY(-450px); }
.orbit-circle:nth-child(4) { transform: rotate(180deg) translateY(-450px); }
.orbit-circle:nth-child(5) { transform: rotate(240deg) translateY(-450px); }
.orbit-circle:nth-child(6) { transform: rotate(300deg) translateY(-450px); }
Stay tuned for more work examples!