Skip to content

Commit 21d8cd0

Browse files
emir-karabegclaude
andcommitted
improvement(landing): optimize core web vitals and accessibility
Code-split AuthModal and DemoRequestModal via next/dynamic across 7 landing components to move auth-client bundle (~150-250KB) out of the initial JS payload. Replace useSession import in navbar with direct SessionContext read to avoid pulling the entire better-auth client into the landing page bundle. Add immutable cache header for content-hashed _next/static assets. Defer PostHog session recording until user identification to avoid loading the recorder (~80KB) on anonymous visits. Fix accessibility issues flagged by Lighthouse: add missing aria-label on preview submit button, add inert to aria-hidden ReactFlow wrapper, set decorative alt on logos inside labeled links, disambiguate duplicate footer API links. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent cd8c5bd commit 21d8cd0

File tree

13 files changed

+69
-16
lines changed

13 files changed

+69
-16
lines changed

apps/sim/app/(landing)/components/collaboration/collaboration.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
'use client'
22

33
import { useCallback, useEffect, useRef, useState } from 'react'
4+
import dynamic from 'next/dynamic'
45
import Image from 'next/image'
56
import Link from 'next/link'
67
import { Badge } from '@/components/emcn'
7-
import { AuthModal } from '@/app/(landing)/components/auth-modal/auth-modal'
88
import { trackLandingCta } from '@/app/(landing)/landing-analytics'
99

10+
const AuthModal = dynamic(() =>
11+
import('@/app/(landing)/components/auth-modal/auth-modal').then((m) => m.AuthModal)
12+
)
13+
1014
interface DotGridProps {
1115
className?: string
1216
cols: number

apps/sim/app/(landing)/components/features/features.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@
22

33
import { useRef, useState } from 'react'
44
import { type MotionValue, motion, useScroll, useTransform } from 'framer-motion'
5+
import dynamic from 'next/dynamic'
56
import Image from 'next/image'
67
import { Badge } from '@/components/emcn'
7-
import { AuthModal } from '@/app/(landing)/components/auth-modal/auth-modal'
88
import { FeaturesPreview } from '@/app/(landing)/components/features/components/features-preview'
99
import { trackLandingCta } from '@/app/(landing)/landing-analytics'
1010

11+
const AuthModal = dynamic(() =>
12+
import('@/app/(landing)/components/auth-modal/auth-modal').then((m) => m.AuthModal)
13+
)
14+
1115
function hexToRgba(hex: string, alpha: number): string {
1216
const r = Number.parseInt(hex.slice(1, 3), 16)
1317
const g = Number.parseInt(hex.slice(3, 5), 16)

apps/sim/app/(landing)/components/footer/footer-cta.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@
22

33
import { useCallback, useRef, useState } from 'react'
44
import { ArrowUp } from 'lucide-react'
5+
import dynamic from 'next/dynamic'
56
import { cn } from '@/lib/core/utils/cn'
67
import { captureClientEvent } from '@/lib/posthog/client'
7-
import { AuthModal } from '@/app/(landing)/components/auth-modal/auth-modal'
88
import { useLandingSubmit } from '@/app/(landing)/components/landing-preview/components/landing-preview-panel/landing-preview-panel'
99
import { trackLandingCta } from '@/app/(landing)/landing-analytics'
1010
import { useAnimatedPlaceholder } from '@/hooks/use-animated-placeholder'
1111

12+
const AuthModal = dynamic(() =>
13+
import('@/app/(landing)/components/auth-modal/auth-modal').then((m) => m.AuthModal)
14+
)
15+
1216
const MAX_HEIGHT = 120
1317

1418
const CTA_BUTTON =

apps/sim/app/(landing)/components/footer/footer.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ const BLOCK_LINKS: FooterItem[] = [
3838
{ label: 'Router', href: 'https://docs.sim.ai/blocks/router', external: true },
3939
{ label: 'Function', href: 'https://docs.sim.ai/blocks/function', external: true },
4040
{ label: 'Condition', href: 'https://docs.sim.ai/blocks/condition', external: true },
41-
{ label: 'API', href: 'https://docs.sim.ai/blocks/api', external: true },
41+
{ label: 'API Block', href: 'https://docs.sim.ai/blocks/api', external: true },
4242
{ label: 'Workflow', href: 'https://docs.sim.ai/blocks/workflow', external: true },
4343
{ label: 'Parallel', href: 'https://docs.sim.ai/blocks/parallel', external: true },
4444
{ label: 'Guardrails', href: 'https://docs.sim.ai/blocks/guardrails', external: true },
@@ -194,7 +194,7 @@ export default function Footer({ hideCTA }: FooterProps) {
194194
<Link href='/' aria-label='Sim home'>
195195
<Image
196196
src='/logo/sim-landing.svg'
197-
alt='Sim'
197+
alt=''
198198
width={85}
199199
height={26}
200200
className='h-[26.4px] w-auto'

apps/sim/app/(landing)/components/hero/hero.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,18 @@
22

33
import dynamic from 'next/dynamic'
44
import { cn } from '@/lib/core/utils/cn'
5-
import { AuthModal } from '@/app/(landing)/components/auth-modal/auth-modal'
6-
import { DemoRequestModal } from '@/app/(landing)/components/demo-request/demo-request-modal'
75
import { trackLandingCta } from '@/app/(landing)/landing-analytics'
86

7+
const AuthModal = dynamic(() =>
8+
import('@/app/(landing)/components/auth-modal/auth-modal').then((m) => m.AuthModal)
9+
)
10+
11+
const DemoRequestModal = dynamic(() =>
12+
import('@/app/(landing)/components/demo-request/demo-request-modal').then(
13+
(m) => m.DemoRequestModal
14+
)
15+
)
16+
917
const LandingPreview = dynamic(
1018
() =>
1119
import('@/app/(landing)/components/landing-preview/landing-preview').then(

apps/sim/app/(landing)/components/landing-preview/components/landing-preview-home/landing-preview-home.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,7 @@ export const LandingPreviewHome = memo(function LandingPreviewHome({
334334
type='button'
335335
onClick={handleSubmit}
336336
disabled={isEmpty}
337+
aria-label='Submit message'
337338
className='flex h-[28px] w-[28px] items-center justify-center rounded-full border-0 p-0 transition-colors'
338339
style={{
339340
background: isEmpty ? '#808080' : '#e0e0e0',

apps/sim/app/(landing)/components/landing-preview/components/landing-preview-panel/landing-preview-panel.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
import { memo, useCallback, useEffect, useRef, useState } from 'react'
44
import { AnimatePresence, motion } from 'framer-motion'
55
import { ArrowUp } from 'lucide-react'
6+
import dynamic from 'next/dynamic'
67
import { useRouter } from 'next/navigation'
78
import { createPortal } from 'react-dom'
89
import { Blimp, BubbleChatPreview, ChevronDown, MoreHorizontal, Play } from '@/components/emcn'
910
import { AgentIcon, HubspotIcon, OpenAIIcon, SalesforceIcon } from '@/components/icons'
1011
import { LandingPromptStorage } from '@/lib/core/utils/browser-storage'
1112
import { captureClientEvent } from '@/lib/posthog/client'
12-
import { AuthModal } from '@/app/(landing)/components/auth-modal/auth-modal'
1313
import {
1414
EASE_OUT,
1515
type EditorPromptData,
@@ -21,6 +21,10 @@ import {
2121
} from '@/app/(landing)/components/landing-preview/components/landing-preview-workflow/workflow-data'
2222
import { trackLandingCta } from '@/app/(landing)/landing-analytics'
2323

24+
const AuthModal = dynamic(() =>
25+
import('@/app/(landing)/components/auth-modal/auth-modal').then((m) => m.AuthModal)
26+
)
27+
2428
type PanelTab = 'copilot' | 'editor'
2529

2630
const EDITOR_BLOCK_ICONS: Record<string, React.ComponentType<{ className?: string }>> = {

apps/sim/app/(landing)/components/navbar/navbar.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
'use client'
22

3-
import { useCallback, useEffect, useRef, useState, useSyncExternalStore } from 'react'
3+
import { useCallback, useContext, useEffect, useRef, useState, useSyncExternalStore } from 'react'
4+
import dynamic from 'next/dynamic'
45
import Image from 'next/image'
56
import Link from 'next/link'
67
import { useSearchParams } from 'next/navigation'
78
import { GithubOutlineIcon } from '@/components/icons'
8-
import { useSession } from '@/lib/auth/auth-client'
99
import { cn } from '@/lib/core/utils/cn'
10-
import { AuthModal } from '@/app/(landing)/components/auth-modal/auth-modal'
10+
import { SessionContext } from '@/app/_shell/providers/session-provider'
1111
import {
1212
BlogDropdown,
1313
type NavBlogPost,
@@ -17,6 +17,10 @@ import { GitHubStars } from '@/app/(landing)/components/navbar/components/github
1717
import { trackLandingCta } from '@/app/(landing)/landing-analytics'
1818
import { getBrandConfig } from '@/ee/whitelabeling'
1919

20+
const AuthModal = dynamic(() =>
21+
import('@/app/(landing)/components/auth-modal/auth-modal').then((m) => m.AuthModal)
22+
)
23+
2024
type DropdownId = 'docs' | 'blog' | null
2125

2226
interface NavLink {
@@ -48,7 +52,9 @@ interface NavbarProps {
4852
export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps) {
4953
const brand = getBrandConfig()
5054
const searchParams = useSearchParams()
51-
const { data: session, isPending: isSessionPending } = useSession()
55+
const sessionCtx = useContext(SessionContext)
56+
const session = sessionCtx?.data ?? null
57+
const isSessionPending = sessionCtx?.isPending ?? true
5258
const isAuthenticated = Boolean(session?.user?.id)
5359
const isBrowsingHome = searchParams.has('home')
5460
const useHomeLinks = isAuthenticated || isBrowsingHome
@@ -125,7 +131,7 @@ export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps
125131
) : (
126132
<Image
127133
src='/logo/sim-landing.svg'
128-
alt='Sim'
134+
alt=''
129135
width={71}
130136
height={22}
131137
className='h-[22px] w-auto'

apps/sim/app/(landing)/components/pricing/pricing.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
'use client'
22

3+
import dynamic from 'next/dynamic'
34
import { Badge } from '@/components/emcn'
4-
import { AuthModal } from '@/app/(landing)/components/auth-modal/auth-modal'
5-
import { DemoRequestModal } from '@/app/(landing)/components/demo-request/demo-request-modal'
65
import { trackLandingCta } from '@/app/(landing)/landing-analytics'
76

7+
const AuthModal = dynamic(() =>
8+
import('@/app/(landing)/components/auth-modal/auth-modal').then((m) => m.AuthModal)
9+
)
10+
11+
const DemoRequestModal = dynamic(() =>
12+
import('@/app/(landing)/components/demo-request/demo-request-modal').then(
13+
(m) => m.DemoRequestModal
14+
)
15+
)
16+
817
interface PricingTier {
918
id: string
1019
name: string

apps/sim/app/(landing)/components/templates/templates.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ export default function Templates() {
470470
aria-labelledby={`template-tab-${activeIndex}`}
471471
className='relative hidden flex-1 lg:block'
472472
>
473-
<div aria-hidden='true' className='h-full'>
473+
<div aria-hidden='true' inert className='h-full'>
474474
<LandingPreviewWorkflow
475475
key={activeIndex}
476476
workflow={activeWorkflow}

0 commit comments

Comments
 (0)