Overview
The Sunflower Capital website implements a sophisticated responsive design system that adapts to device type, screen size, and orientation. The architecture uses a combination of Tailwind utilities, custom CSS media queries, and runtime device detection.
Device Detection Strategy
Runtime Detection
The application uses react-device-detect for reliable device type detection:
import { isMobile } from 'react-device-detect' ;
const App : React . FC = () => {
const [ mobile , setMobile ] = useState ( true );
const [ lg , setLg ] = useState ( false );
useEffect (() => {
const handleResize = () => {
setLg ( window . innerWidth >= 1024 );
};
setMobile ( isMobile );
setLg ( window . innerWidth >= 1024 );
if ( isMobile ) {
setLoaded ( true );
} else {
setTimeout (() => {
setLoaded ( true );
}, 500 );
}
window . addEventListener ( "resize" , handleResize );
return () => {
window . removeEventListener ( "resize" , handleResize );
};
}, []);
};
The isMobile check detects actual mobile devices (phones/tablets) via user agent parsing, while lg tracks viewport breakpoint for responsive layouts.
State-Driven Responsive Logic
Mobile State
Breakpoint State
const [ mobile , setMobile ] = useState ( true );
Determined once on mount using react-device-detect. Used for:
Component variant selection
Touch vs mouse event handling
Navigation style (dots vs swipe)
const [ lg , setLg ] = useState ( false );
Tracks window.innerWidth >= 1024. Updates on resize. Used for:
Desktop vs mobile component rendering
Layout switching (Ethos component)
Breakpoint System
Custom Tailwind breakpoints align with common device sizes:
screens : {
'xs' : '425px' , // Large phones
'sm' : '640px' , // Small tablets
'md' : '768px' , // Tablets
'lg' : '1024px' , // Small laptops
'xl' : '1440px' , // Desktop
'2xl' : '1920px' , // Large desktop
}
xs (425px) Large phones (iPhone 12 Pro Max, Pixel 6)
md (768px) Tablets (iPad, Galaxy Tab)
lg (1024px) Laptops and small desktops
xl (1440px) Standard desktop monitors
2xl (1920px) Large displays and 1080p screens
Orientation-Based Layouts
The design heavily utilizes portrait/landscape media queries:
Portrait Mode
< div className = "flex portrait:flex-col justify-center portrait:items-center portrait:gap-20" >
< h1 className = "portrait:text-center" > SUNFLOWER < br /> CAPITAL </ h1 >
< div className = "flowerbox" >
{ /* Only center flower visible in portrait */ }
</ div >
</ div >
@media ( orientation : portrait ) {
.title {
font-size : 18 vw ;
line-height : 16 vw ;
}
.flower {
display : none ; /* Hide all decorative flowers */
}
.flower-4 {
width : 55 vw ;
height : 55 vw ;
display : block ;
position : relative ;
}
}
In portrait mode, only the center flower (flower-4) is displayed to conserve vertical space and maintain visual focus.
Landscape Mode with Aspect Ratio Ranges
Landscape layouts adapt based on aspect ratio:
Standard (< 2.05)
Wide (2.05 - 2.5)
Ultra-Wide (2.5 - 3)
Extreme (> 3)
@media ( orientation : landscape ) {
@media ( max-aspect-ratio : 2.05 ) {
.title {
font-size : 18.4 vw ;
line-height : 13 vw ;
padding-top : 1.5 rem ;
}
.flower-2 { width : 7 vw ; height : 7 vw ; top : 57 vh ; left : 5 vw ; }
.flower-3 { width : 8 vw ; height : 8 vw ; top : 56 vh ; left : 30 vw ; }
.flower-4 { width : 20 vw ; height : 20 vw ; top : 55 vh ; left : 40 vw ; }
.flower-5 { width : 15 vw ; height : 15 vw ; top : 36 vh ; left : 66.5 vw ; }
/* ... more flowers */
}
}
Typical laptops and desktop monitors @media ( min-aspect-ratio : 2.05 ) {
@media ( max-aspect-ratio : 2.5 ) {
.title {
font-size : 18.4 vw ;
line-height : 13 vw ;
padding-top : 0 ;
}
.flower-2 { width : 7 vw ; height : 7 vw ; top : 80 vh ; left : 5 vw ; }
/* Adjusted positions for ultra-wide */
}
}
Ultra-wide monitors (21:9) @media ( min-aspect-ratio : 2.5 ) {
@media ( max-aspect-ratio : 3 ) {
.title {
font-size : 12 vw ;
line-height : 9 vw ;
}
.logoflower {
display : none ;
}
/* Further position adjustments */
}
}
Super ultra-wide displays (32:9) @media ( min-aspect-ratio : 3 ) {
.title {
font-size : 12 vw ;
line-height : 9 vw ;
width : 66 % ;
height : 100 % ;
display : flex ;
justify-content : center ;
align-items : center ;
}
.flower-4 {
width : 66 vh ;
height : 66 vh ;
}
}
Extremely wide setups (3+ aspect ratio)
Aspect ratio-based layouts ensure the sunflower garden remains visually balanced across all monitor configurations, from standard 16:9 to ultra-wide 32:9 displays.
Conditional Component Rendering
Ethos Component Variants
< div id = "statement2" className = "h-[calc(100dvh)]" >
{ lg && < Ethos /> }
{ ! lg &&
< div className = "w-[85%] flex flex-col gap-6" >
< h1 className = "font-arya text-dark-green text-tmd" > Ethos </ h1 >
< div className = "flex flex-col gap-6" >
< h3 >
< span className = "font-semibold" > We often write the first check... </ span >
< br />
We back founders with original insights...
</ h3 >
{ /* More content */ }
</ div >
</ div >
}
</ div >
Desktop (lg >= 1024px)
Mobile (lg < 1024px)
< Ethos />
// Interactive dropdown component
Navigation Component Conditional
{ ! mobile &&
< DotNavigator
currentScreen = { currentPage }
onDotClick = { beforePageChange }
isMobile = { isMobile }
/>
}
Desktop users get dot navigation, while mobile users rely on swipe gestures (handled natively by ReactPageScroller).
Typography Scaling
Custom font sizes scale across breakpoints:
fontSize : {
// Titles
txl : [ '64px' , '64px' ],
tlg : [ '56px' , '56px' ],
tmd : [ '48px' , '48px' ],
tsm : [ '36px' , '36px' ],
// Body
b2xl : [ '84px' , '126px' ],
bxl : [ '64px' , '96px' ],
bxlg : [ '46px' , '69px' ],
blg : [ '36px' , '54px' ],
bmd : [ '32px' , '48px' ],
bsm : [ '28px' , '42px' ],
bxsm : [ '26px' , '39px' ],
bxs : [ '24px' , '36px' ],
b2xs : [ '20px' , '30px' ],
b3xs : [ '18px' , '27px' ],
b34xs : [ '16px' , '24px' ],
b4xs : [ '14px' , '21px' ],
b5xs : [ '12px' , '18px' ],
}
Responsive Typography Pattern
< h1 className = "font-arya text-dark-green
text-tmd xl:text-tlg 2xl:text-txl
" >
Portfolio
</ h1 >
Base (< xl)
text-tmd → 48px/48px
Desktop (xl)
xl:text-tlg → 56px/56px
Large (2xl)
2xl:text-txl → 64px/64px
Viewport Height Units
Dynamic Viewport Height (dvh)
All sections use calc(100dvh) for accurate mobile height:
className = "h-[calc(100dvh)]"
vh: Static viewport height, doesn’t account for browser chrome
dvh: Dynamic viewport height, adjusts for mobile browser UI (address bar, bottom nav)
On mobile Safari, the address bar can hide/show, changing available height. dvh ensures sections always fill the visible area.
Landscape vs Portrait Height
< div className = "landscape:h-screen portrait:h-[calc(100dvh)]" >
landscape: h-screen /* 100vh on desktop */
Mobile-Specific Optimizations
Touch Event Handling
src/components/PortfolioTable.tsx
< div
onTouchStart = { () => setScrollEnabled ( false ) }
onTouchEnd = { () => setScrollEnabled ( true ) }
>
.mobile::-webkit-scrollbar {
display : none ; /* Hide scrollbars on mobile */
}
.custom-scrollbar::-webkit-scrollbar {
display : block ;
width : 12 px ;
height : 12 px ;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background-color : rgba ( 0 , 0 , 0 , 0.2 );
border-radius : 10 px ;
}
className = { `overflow-y-auto ${ isMobile ? 'mobile' : 'custom-scrollbar' } ` }
No scrollbar chrome for cleaner mobile UI className = "overflow-y-auto custom-scrollbar"
Styled scrollbar with visual feedback
< meta
name = "viewport"
content = "initial-scale=0.9, width=device-width, height=device-height, viewport-fit=cover, user-scalable=no"
/>
< meta name = "mobile-web-app-capable" content = "yes" />
< meta name = "apple-mobile-web-app-status-bar-style" content = "black-translucent" />
Slightly zoomed out on mobile for better content fit
Extends into safe areas on notched devices (iPhone X+)
Prevents pinch-zoom for app-like experience
Portfolio Table Responsive Layout
Filter UI Adaptation
src/components/PortfolioTable.tsx
< div className = { ` ${ isMobile ? 'block' : 'hidden' } ` } >
< select
id = "industrySelect"
className = "font-bitter text-sm w-20 p-1 border rounded"
>
< option value = "All" > All </ option >
{ industries . map (( industry ) => (
< option key = { industry } value = { industry } >
{ industry }
</ option >
)) }
</ select >
</ div >
Compact dropdown selector src/components/PortfolioTable.tsx
< div className = { ` ${ isMobile ? 'hidden' : 'flex' } flex-wrap gap-2` } >
< button onClick = { () => changeTable ( "All" ) } >
< div className = { filter === "All" ? "bg-offblack" : "bg-[#6D8A54]" } />
All
</ button >
{ industries . map (( industry ) => (
< button key = { industry } onClick = { () => changeTable ( industry ) } >
{ industry }
</ button >
)) }
</ div >
Full button group with color indicators
Table Cell Widths
src/components/PortfolioTable.tsx
< td className = "
w-[117px] xs:w-[122px] md:w-[162px]
lg:w-[234px] xl:w-[244px] 2xl:w-[390px]
" >
Mobile (< xs)
117px - Minimal width for company names
xs (425px)
122px - Slightly wider on large phones
md (768px)
162px - Tablet optimization
lg (1024px)
234px - Laptop/desktop
xl (1440px)
244px - Standard desktop
2xl (1920px)
390px - Large displays
Transitions adapt based on device capabilities:
if ( isMobile ) {
setLoaded ( true ); // Immediate on mobile
} else {
setTimeout (() => {
setLoaded ( true ); // Delayed on desktop for animation
}, 500 );
}
Mobile devices skip the 500ms animation delay to reduce perceived load time and conserve battery.
Flower Animation System
Grow Effect (Desktop Only)
.grow:hover {
transform : scale ( 1.2 );
}
< div className = { `flower flower-2 ${ loaded ? '' : 'opacity-0' } grow` } >
Breathe Animation (Center Flower)
@keyframes breathe {
0% , 100% {
transform : scale ( 1 );
}
50% {
transform : scale ( 1.1 );
}
}
.breathe {
animation : breathe 3 s ease-in-out infinite ;
}
Hover effects (.grow, .hover:animate-spin) only activate on desktop. Mobile users don’t have hover capability, so these effects are design enhancements for desktop only.
Best Practices
Use Runtime Device Detection Sparingly
Rely on CSS media queries when possible. Use isMobile only for behavioral differences (events, component variants), not styling.
Combine Orientation and Breakpoints
The portrait: and landscape: utilities pair well with breakpoints for precise responsive control.
Test Across Aspect Ratios
Don’t just test at common resolutions. Verify layouts at 16:9, 21:9, and ultra-wide ratios.
Always use dvh for mobile to account for browser chrome. Falls back to vh on unsupported browsers.
Optimize Animations for Mobile
Reduce or eliminate animations on mobile to improve performance and battery life.
Architecture Overview High-level application structure
Page Scrolling ReactPageScroller implementation