diff --git a/packages/web/src/public-site/pages/landing-2026/components/CreateFutureCTA.module.css b/packages/web/src/public-site/pages/landing-2026/components/CreateFutureCTA.module.css
index 82a264a954e..7135bd6050d 100644
--- a/packages/web/src/public-site/pages/landing-2026/components/CreateFutureCTA.module.css
+++ b/packages/web/src/public-site/pages/landing-2026/components/CreateFutureCTA.module.css
@@ -80,7 +80,10 @@
background: transparent;
cursor: pointer;
text-decoration: none;
- transition: box-shadow 0.25s ease;
+ transition:
+ box-shadow 0.25s ease,
+ background 0.2s ease,
+ border-color 0.2s ease;
box-sizing: border-box;
}
@@ -92,6 +95,17 @@
inset -4px 0px 4px 0px rgba(175, 72, 255, 0.25);
}
+/* Figma 2738-4495: press state */
+.ctaButton:active {
+ background: rgba(53, 20, 75, 0.85);
+ border-color: rgba(175, 72, 255, 0.9);
+ box-shadow: none;
+}
+
+.ctaButton:active .ctaLabel {
+ color: #e7c7ff;
+}
+
.ctaLabel {
font-family: 'Urbanist', sans-serif;
font-weight: 700;
@@ -100,12 +114,12 @@
color: #fff;
text-transform: capitalize;
white-space: nowrap;
+ transition: color 0.2s ease;
}
@media (max-width: 800px) {
.section {
padding: 32px 16px;
- box-shadow: 0 10px 50px rgba(53, 5, 152, 0.25);
}
.content {
diff --git a/packages/web/src/public-site/pages/landing-2026/components/FAQ2026.module.css b/packages/web/src/public-site/pages/landing-2026/components/FAQ2026.module.css
index 6eed216def2..e7a635da17d 100644
--- a/packages/web/src/public-site/pages/landing-2026/components/FAQ2026.module.css
+++ b/packages/web/src/public-site/pages/landing-2026/components/FAQ2026.module.css
@@ -72,9 +72,16 @@
text-shadow 0.2s ease;
}
-.faqItem[aria-expanded='true'] .faqQuestion,
-.faqItem:hover .faqQuestion,
-.faqItem:active .faqQuestion,
+/* Purple hover when whole FAQ row is hovered; carets match text color */
+.faqItem:hover .faqQuestion {
+ color: #e7c7ff;
+ text-shadow:
+ -4px 0px 4px rgba(175, 72, 255, 0.25),
+ 4px 0px 4px rgba(175, 72, 255, 0.25),
+ 0px -4px 4px rgba(175, 72, 255, 0.25),
+ 0px 4px 4px rgba(175, 72, 255, 0.25);
+}
+
.faqItem:focus-visible .faqQuestion {
color: #e7c7ff;
text-shadow:
@@ -84,28 +91,39 @@
0px 4px 4px rgba(175, 72, 255, 0.25);
}
+/* Press state: same purple as hover (Figma 2777-2986) */
+.faqItem:active .faqQuestion {
+ color: #e7c7ff;
+ text-shadow:
+ -4px 0px 4px rgba(175, 72, 255, 0.25),
+ 4px 0px 4px rgba(175, 72, 255, 0.25),
+ 0px -4px 4px rgba(175, 72, 255, 0.25),
+ 0px 4px 4px rgba(175, 72, 255, 0.25);
+}
+
.chevron {
width: 16px;
height: 16px;
flex-shrink: 0;
- transition: transform 0.3s ease;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ color: #fff;
+ transition:
+ transform 0.3s ease,
+ color 0.2s ease;
}
-.chevron svg,
-.chevron svg path {
- fill: #fff !important;
- transition: fill 0.2s ease;
+.chevron svg {
+ width: 100%;
+ height: 100%;
}
-.faqItem[aria-expanded='true'] .chevron svg,
-.faqItem[aria-expanded='true'] .chevron svg path,
-.faqItem:hover .chevron svg,
-.faqItem:hover .chevron svg path,
-.faqItem:active .chevron svg,
-.faqItem:active .chevron svg path,
-.faqItem:focus-visible .chevron svg,
-.faqItem:focus-visible .chevron svg path {
- fill: #e7c7ff !important;
+/* Purple when whole row hover/focus/active, same as question */
+.faqItem:hover .chevron,
+.faqItem:focus-visible .chevron,
+.faqItem:active .chevron {
+ color: #e7c7ff;
}
.faqItem[aria-expanded='true'] .chevron {
diff --git a/packages/web/src/public-site/pages/landing-2026/components/FAQ2026.tsx b/packages/web/src/public-site/pages/landing-2026/components/FAQ2026.tsx
index 80f976bb866..07710f2f6f9 100644
--- a/packages/web/src/public-site/pages/landing-2026/components/FAQ2026.tsx
+++ b/packages/web/src/public-site/pages/landing-2026/components/FAQ2026.tsx
@@ -1,9 +1,30 @@
import { useState } from 'react'
-import { IconCaretDown } from '@audius/harmony'
-
import styles from './FAQ2026.module.css'
+/** Inline chevron so we control color (white default, purple on hover) without Harmony theme override */
+function ChevronDown({ className }: { className?: string }) {
+ return (
+
+
+
+ )
+}
+
const faqItems = [
{
question: 'Who is Audius made for?',
@@ -62,11 +83,7 @@ export const FAQ2026 = (_props: FAQ2026Props) => {
>
{isOpen ? (
diff --git a/packages/web/src/public-site/pages/landing-2026/components/FeaturedContests2026.module.css b/packages/web/src/public-site/pages/landing-2026/components/FeaturedContests2026.module.css
index 8d22f778abb..676eaadce4e 100644
--- a/packages/web/src/public-site/pages/landing-2026/components/FeaturedContests2026.module.css
+++ b/packages/web/src/public-site/pages/landing-2026/components/FeaturedContests2026.module.css
@@ -111,12 +111,30 @@
overflow: hidden;
}
+.artworkSkeletonInner {
+ position: absolute;
+ inset: 0;
+ background: rgba(255, 255, 255, 0.06);
+}
+
+.artworkWrapLoaded .artworkSkeletonInner {
+ opacity: 0;
+ pointer-events: none;
+ transition: opacity 0.25s ease;
+}
+
.artwork {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
+ opacity: 0;
+ transition: opacity 0.35s ease;
+}
+
+.artworkWrapLoaded .artwork {
+ opacity: 1;
}
.bwOverlay {
diff --git a/packages/web/src/public-site/pages/landing-2026/components/FeaturedContests2026.tsx b/packages/web/src/public-site/pages/landing-2026/components/FeaturedContests2026.tsx
index a4157f4aadd..c052d10fbb2 100644
--- a/packages/web/src/public-site/pages/landing-2026/components/FeaturedContests2026.tsx
+++ b/packages/web/src/public-site/pages/landing-2026/components/FeaturedContests2026.tsx
@@ -1,4 +1,4 @@
-import { MouseEvent } from 'react'
+import { MouseEvent, useState } from 'react'
import {
useExploreContent,
@@ -6,9 +6,13 @@ import {
useTrack,
useUser
} from '@audius/common/api'
-import { ID } from '@audius/common/models'
+import { imageBlank } from '@audius/common/assets'
+import { useImageSize } from '@audius/common/hooks'
+import { ID, SquareSizes } from '@audius/common/models'
import { useLinkClickHandler } from 'react-router'
+import { preload } from 'utils/image'
+
import featuredLines from '../assets/featured-lines.svg?url'
import styles from './FeaturedContests2026.module.css'
@@ -30,10 +34,19 @@ function ContestCard({
id: ID
setRenderPublicSite: (v: boolean) => void
}) {
+ const [imageLoaded, setImageLoaded] = useState(false)
const { data: contest, isPending: contestPending } = useRemixContest(id)
const { data: track, isPending: trackPending } = useTrack(contest?.entityId)
const { data: user, isPending: userPending } = useUser(track?.owner_id)
+ const artwork = track?.artwork
+ const { imageUrl, onError } = useImageSize({
+ artwork,
+ targetSize: SquareSizes.SIZE_480_BY_480,
+ defaultImage: imageBlank as string,
+ preloadImageFn: preload
+ })
+
const isPending = contestPending || trackPending || userPending || !track
const permalink = track?.permalink ?? ''
const handleNavigate = useLinkClickHandler(permalink)
@@ -43,6 +56,8 @@ function ContestCard({
handleNavigate(e as MouseEvent
)
}
+ const showImage = imageUrl != null && imageUrl !== (imageBlank as string)
+
if (isPending) {
return (
@@ -52,10 +67,6 @@ function ContestCard({
)
}
- const artworkUrl =
- (track?.artwork && (track.artwork as Record
)['480x480']) ??
- null
-
return (
@@ -297,11 +324,15 @@ function MobileNavOverlay({
function ResourcesDropdown({
setRenderPublicSite,
navigate,
- onClose
+ onClose,
+ onClosingComplete,
+ isClosing
}: {
setRenderPublicSite: (v: boolean) => void
navigate: ReturnType
onClose: () => void
+ onClosingComplete: () => void
+ isClosing: boolean
}) {
const handleItemClick = (href: string) => (e: MouseEvent) => {
onClose()
@@ -313,8 +344,18 @@ function ResourcesDropdown({
}
}
+ const handleAnimationEnd = (e: React.AnimationEvent) => {
+ if (e.animationName === 'dropdownFadeOut' && isClosing) {
+ onClosingComplete()
+ }
+ }
+
return (
-
+
{MENU_ITEMS.map((item) => (
0
- ? profilePicture.mirrors
- : DEFAULT_IMAGE_MIRRORS
- return { ...profilePicture, mirrors }
-}
-
const { profilePage } = route
+/** Same artists as OG landing page (WhoUsesAudius.tsx) – name and handle only; images from API. */
+const ARTISTS: { name: string; handle: string }[] = [
+ { name: 'deadmau5', handle: 'deadmau5' },
+ { name: 'Skrillex', handle: 'skrillex' },
+ { name: 'Zedd', handle: 'zedd' },
+ { name: 'Kenny Beats', handle: 'kennybeats' },
+ { name: 'Matt OX', handle: 'mattox' },
+ { name: 'Aluna', handle: 'alunaaa' },
+ { name: 'Diplo', handle: 'diplo' },
+ { name: 'Lolo Zouai', handle: 'lolozouai' },
+ { name: 'Rezz', handle: 'officialrezz' }
+]
+
const messages = {
headline: 'Who uses Audius?',
subline:
- 'Hundreds of thousands of artists, labels, collectives, and music lovers, here for the culture just like you.'
+ 'Thousands of artists, labels, collectives, and music lovers, here for the culture just like you.'
}
-const FEATURED_LIMIT = 10
-
type WhoUsesAudius2026Props = {
isMobile: boolean
setRenderPublicSite: (shouldRender: boolean) => void
}
function ArtistCard({
- userId,
name,
handle,
setRenderPublicSite,
navigate
}: {
- userId: number
name: string
handle: string
setRenderPublicSite: (v: boolean) => void
navigate: ReturnType
}) {
- const { data: partialUser } = useUser(userId, {
+ const [imageLoaded, setImageLoaded] = useState(false)
+ const { data: partialUser } = useUserByHandle(handle, {
select: (user) => pick(user, 'profile_picture', 'updatedProfilePicture')
})
const { profile_picture, updatedProfilePicture } = partialUser ?? {}
- const artwork = artworkWithDefaultMirrors(profile_picture)
-
const { imageUrl, onError } = useImageSize({
- artwork,
+ artwork: profile_picture,
targetSize: SquareSizes.SIZE_480_BY_480,
defaultImage: imageProfilePicEmpty as string,
preloadImageFn: preload
})
- const displayUrl =
- updatedProfilePicture?.url ?? imageUrl ?? (imageProfilePicEmpty as string)
+ const displayUrl = updatedProfilePicture?.url ?? imageUrl
+ const hasRealImage =
+ displayUrl != null && displayUrl !== (imageProfilePicEmpty as string)
const onClick = (e: MouseEvent) => {
handleClickRoute(profilePage(handle), setRenderPublicSite, navigate)(e)
@@ -93,6 +77,8 @@ function ArtistCard({
}
}
+ const handleImageLoad = () => setImageLoaded(true)
+
return (
-
-

+
+
+ {hasRealImage ? (
+

+ ) : null}
{name}
@@ -117,11 +109,6 @@ function ArtistCard({
export const WhoUsesAudius2026 = (props: WhoUsesAudius2026Props) => {
const navigate = useNavigate()
- const { data: users, isPending } = useFeaturedProfiles({
- limit: FEATURED_LIMIT
- })
-
- const allUsers = users?.slice(0, FEATURED_LIMIT) ?? []
return (
@@ -133,23 +120,15 @@ export const WhoUsesAudius2026 = (props: WhoUsesAudius2026Props) => {
- {isPending
- ? Array.from({ length: FEATURED_LIMIT }).map((_, i) => (
-
- ))
- : allUsers.map((user) => (
-
- ))}
+ {ARTISTS.map((artist) => (
+
+ ))}