@@ -24,6 +24,64 @@ interface WebsiteCardProps {
2424 isLoadingChart ?: boolean ;
2525}
2626
27+ function TrendStat ( {
28+ trend,
29+ className = 'flex items-center gap-1 font-medium text-xs sm:text-sm' ,
30+ } : {
31+ trend : ProcessedMiniChartData [ 'trend' ] | undefined ;
32+ className ?: string ;
33+ } ) {
34+ if ( ! trend ) {
35+ return null ;
36+ }
37+ if ( trend . type === 'up' ) {
38+ return (
39+ < div className = { className } >
40+ < TrendUpIcon
41+ aria-hidden = "true"
42+ className = "!text-success h-4 w-4"
43+ style = { { color : 'var(--tw-success, #22c55e)' } }
44+ weight = "duotone"
45+ />
46+ < span
47+ className = "!text-success"
48+ style = { { color : 'var(--tw-success, #22c55e)' } }
49+ >
50+ +{ trend . value . toFixed ( 0 ) } %
51+ </ span >
52+ </ div >
53+ ) ;
54+ }
55+ if ( trend . type === 'down' ) {
56+ return (
57+ < div className = { className } >
58+ < TrendDownIcon
59+ aria-hidden
60+ className = "!text-destructive h-4 w-4"
61+ style = { { color : 'var(--tw-destructive, #ef4444)' } }
62+ weight = "duotone"
63+ />
64+ < span
65+ className = "!text-destructive"
66+ style = { { color : 'var(--tw-destructive, #ef4444)' } }
67+ >
68+ -{ trend . value . toFixed ( 0 ) } %
69+ </ span >
70+ </ div >
71+ ) ;
72+ }
73+ return (
74+ < div className = { className } >
75+ < MinusIcon
76+ aria-hidden
77+ className = "h-4 w-4 text-muted-foreground"
78+ weight = "fill"
79+ />
80+ < span className = "text-muted-foreground" > 0%</ span >
81+ </ div >
82+ ) ;
83+ }
84+
2785const formatNumber = ( num : number ) => {
2886 if ( num >= 1_000_000 ) {
2987 return `${ ( num / 1_000_000 ) . toFixed ( 1 ) } M` ;
@@ -47,18 +105,19 @@ export const WebsiteCard = memo(
47105 ( { website, chartData, isLoadingChart } : WebsiteCardProps ) => {
48106 return (
49107 < Link
50- className = "group block"
108+ aria-label = { `Open ${ website . name } analytics` }
109+ className = "group block rounded focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background"
51110 data-section = "website-grid"
52111 data-track = "website-card-click"
53112 data-website-id = { website . id }
54113 data-website-name = { website . name }
55114 href = { `/websites/${ website . id } ` }
56115 >
57- < Card className = "flex h-full select-none flex-col bg-background transition-all duration-300 ease-in-out group-hover:border-primary/60 group-hover:shadow-primary/5 group-hover:shadow-xl" >
116+ < Card className = "flex h-full select-none flex-col overflow-hidden bg-background transition-all duration-300 ease-in-out group-hover:border-primary/60 group-hover:shadow-primary/5 group-hover:shadow-xl motion-reduce:transform-none motion-reduce:transition-none " >
58117 < CardHeader className = "pb-2" >
59- < div className = "flex items-center justify-between" >
118+ < div className = "flex items-center justify-between gap-2 " >
60119 < div className = "min-w-0 flex-1" >
61- < CardTitle className = "truncate font-bold text-base transition-colors group-hover:text-primary" >
120+ < CardTitle className = "truncate font-bold text-base leading-tight transition-colors group-hover:text-primary sm:text-lg " >
62121 { website . name }
63122 </ CardTitle >
64123 < CardDescription className = "flex items-center gap-1 pt-0.5" >
@@ -68,7 +127,9 @@ export const WebsiteCard = memo(
68127 domain = { website . domain }
69128 size = { 24 }
70129 />
71- < span className = "truncate text-xs" > { website . domain } </ span >
130+ < span className = "truncate text-xs sm:text-sm" >
131+ { website . domain }
132+ </ span >
72133 </ CardDescription >
73134 </ div >
74135 < ArrowRightIcon
@@ -86,69 +147,28 @@ export const WebsiteCard = memo(
86147 < Skeleton className = "h-3 w-12 rounded" />
87148 < Skeleton className = "h-3 w-8 rounded" />
88149 </ div >
89- < Skeleton className = "h-12 w-full rounded" />
150+ < Skeleton className = "h-12 w-full rounded sm:h-16 " />
90151 </ div >
91152 ) : chartData ? (
92153 chartData . data . length > 0 ? (
93154 < div className = "space-y-2" >
94155 < div className = "flex items-center justify-between" >
95- < span className = "font-medium text-muted-foreground text-xs" >
156+ < span className = "font-medium text-muted-foreground text-xs sm:text-sm " >
96157 { formatNumber ( chartData . totalViews ) } views
97158 </ span >
98- { chartData . trend && (
99- < div className = "flex items-center gap-1 font-medium text-xs" >
100- { chartData . trend . type === 'up' ? (
101- < >
102- < TrendUpIcon
103- aria-hidden = "true"
104- className = "!text-success h-4 w-4"
105- style = { { color : 'var(--tw-success, #22c55e)' } }
106- weight = "fill"
107- />
108- < span
109- className = "!text-success"
110- style = { { color : 'var(--tw-success, #22c55e)' } }
111- >
112- +{ chartData . trend . value . toFixed ( 0 ) } %
113- </ span >
114- </ >
115- ) : chartData . trend . type === 'down' ? (
116- < >
117- < TrendDownIcon
118- aria-hidden = "true"
119- className = "!text-destructive h-4 w-4"
120- style = { {
121- color : 'var(--tw-destructive, #ef4444)' ,
122- } }
123- weight = "fill"
124- />
125- < span
126- className = "!text-destructive"
127- style = { {
128- color : 'var(--tw-destructive, #ef4444)' ,
129- } }
130- >
131- -{ chartData . trend . value . toFixed ( 0 ) } %
132- </ span >
133- </ >
134- ) : (
135- < >
136- < MinusIcon
137- aria-hidden = "true"
138- className = "h-4 w-4 text-muted-foreground"
139- weight = "fill"
140- />
141- < span className = "text-muted-foreground" > 0%</ span >
142- </ >
143- ) }
144- </ div >
145- ) }
159+ < TrendStat trend = { chartData . trend } />
146160 </ div >
147- < div className = "transition-colors duration-300 [--chart-color:theme(colors.primary.DEFAULT)] group-hover:[--chart-color:theme(colors.primary.600)]" >
161+ < div className = "transition-colors duration-300 [--chart-color:theme(colors.primary.DEFAULT)] motion-reduce:transition-none group-hover:[--chart-color:theme(colors.primary.600)]" >
148162 < Suspense
149- fallback = { < Skeleton className = "h-12 w-full rounded" /> }
163+ fallback = {
164+ < Skeleton className = "h-12 w-full rounded sm:h-16" />
165+ }
150166 >
151- < MiniChart data = { chartData . data } id = { website . id } />
167+ < MiniChart
168+ data = { chartData . data }
169+ days = { chartData . data . length }
170+ id = { website . id }
171+ />
152172 </ Suspense >
153173 </ div >
154174 </ div >
@@ -175,11 +195,11 @@ export function WebsiteCardSkeleton() {
175195 return (
176196 < Card className = "h-full" >
177197 < CardHeader >
178- < Skeleton className = "h-6 w-3/4 rounded-md " />
179- < Skeleton className = "mt-1 h-4 w-1/2 rounded-md " />
198+ < Skeleton className = "h-6 w-3/4 rounded" />
199+ < Skeleton className = "mt-1 h-4 w-1/2 rounded" />
180200 </ CardHeader >
181201 < CardContent >
182- < Skeleton className = "h-20 w-full rounded-md " />
202+ < Skeleton className = "h-20 w-full rounded sm:h-24 " />
183203 </ CardContent >
184204 </ Card >
185205 ) ;
0 commit comments