@@ -37,7 +37,12 @@ const ApplicationCard: React.FC<{
37
37
deployments : Record < string , string > ;
38
38
} > = ( { app, services, platforms, deployments } ) => {
39
39
return (
40
- < div className = "app-card" >
40
+ < a
41
+ href = { app . url }
42
+ target = "_blank"
43
+ rel = "noopener noreferrer"
44
+ className = "app-card"
45
+ >
41
46
< div className = "card-image" >
42
47
< img src = { app . teaser } alt = { app . title } loading = "lazy" />
43
48
< div className = "card-badges" >
@@ -65,17 +70,12 @@ const ApplicationCard: React.FC<{
65
70
) }
66
71
</ div >
67
72
68
- < a
69
- href = { app . url }
70
- target = "_blank"
71
- rel = "noopener noreferrer"
72
- className = "card-link"
73
- >
73
+ < span className = "card-link" >
74
74
View Project →
75
- </ a >
75
+ </ span >
76
76
</ div >
77
77
</ div >
78
- </ div >
78
+ </ a >
79
79
) ;
80
80
} ;
81
81
@@ -197,15 +197,23 @@ export const ApplicationsShowcase: React.FC<ApplicationsShowcaseProps> = ({
197
197
198
198
/* Top Bar */
199
199
.top-bar {
200
- display: flex;
201
- gap: 1rem;
202
- align-items: center;
203
200
margin-bottom: 1.5rem;
204
201
padding: 1rem;
205
202
background: var(--sl-color-bg-sidebar);
206
203
border: 1px solid var(--sl-color-gray-6);
207
204
border-radius: 0.5rem;
205
+ }
206
+
207
+ .top-bar-row {
208
+ display: flex;
209
+ gap: 1rem;
210
+ align-items: flex-start;
208
211
flex-wrap: wrap;
212
+ margin-bottom: 1rem;
213
+ }
214
+
215
+ .top-bar-row:last-child {
216
+ margin-bottom: 0;
209
217
}
210
218
211
219
.search-container {
@@ -222,6 +230,7 @@ export const ApplicationsShowcase: React.FC<ApplicationsShowcaseProps> = ({
222
230
background: var(--sl-color-bg);
223
231
color: var(--sl-color-white);
224
232
font-size: 0.875rem;
233
+ margin-top: 0;
225
234
}
226
235
227
236
.search-input:focus {
@@ -258,6 +267,7 @@ export const ApplicationsShowcase: React.FC<ApplicationsShowcaseProps> = ({
258
267
color: var(--sl-color-white);
259
268
font-size: 0.875rem;
260
269
min-width: 140px;
270
+ margin-top: 0;
261
271
}
262
272
263
273
.filter-select:focus {
@@ -273,6 +283,7 @@ export const ApplicationsShowcase: React.FC<ApplicationsShowcaseProps> = ({
273
283
color: var(--sl-color-white);
274
284
cursor: pointer;
275
285
white-space: nowrap;
286
+ margin-top: 0.375rem;
276
287
}
277
288
278
289
.sort-select {
@@ -320,6 +331,13 @@ export const ApplicationsShowcase: React.FC<ApplicationsShowcaseProps> = ({
320
331
border-radius: 0.75rem;
321
332
overflow: hidden;
322
333
transition: transform 0.2s ease, box-shadow 0.2s ease;
334
+ margin-top: 0;
335
+ display: flex;
336
+ flex-direction: column;
337
+ height: 100%;
338
+ text-decoration: none;
339
+ color: inherit;
340
+ cursor: pointer;
323
341
}
324
342
325
343
.app-card:hover {
@@ -371,6 +389,9 @@ export const ApplicationsShowcase: React.FC<ApplicationsShowcaseProps> = ({
371
389
372
390
.card-content {
373
391
padding: 1.25rem;
392
+ display: flex;
393
+ flex-direction: column;
394
+ flex: 1;
374
395
}
375
396
376
397
.card-title {
@@ -393,6 +414,7 @@ export const ApplicationsShowcase: React.FC<ApplicationsShowcaseProps> = ({
393
414
justify-content: space-between;
394
415
align-items: center;
395
416
gap: 1rem;
417
+ margin-top: auto;
396
418
}
397
419
398
420
.service-icons {
@@ -411,6 +433,7 @@ export const ApplicationsShowcase: React.FC<ApplicationsShowcaseProps> = ({
411
433
align-items: center;
412
434
justify-content: center;
413
435
transition: all 0.2s ease;
436
+ margin-top: 0;
414
437
}
415
438
416
439
.service-icon:hover {
@@ -436,8 +459,7 @@ export const ApplicationsShowcase: React.FC<ApplicationsShowcaseProps> = ({
436
459
display: inline-flex;
437
460
align-items: center;
438
461
gap: 0.5rem;
439
- color: white;
440
- text-decoration: none;
462
+ color: var(--sl-color-white);
441
463
font-weight: 500;
442
464
font-size: 0.875rem;
443
465
padding: 0.5rem 0.75rem;
@@ -446,7 +468,7 @@ export const ApplicationsShowcase: React.FC<ApplicationsShowcaseProps> = ({
446
468
white-space: nowrap;
447
469
}
448
470
449
- .card-link :hover {
471
+ .app-card :hover .card-link {
450
472
color: var(--sl-color-accent);
451
473
}
452
474
@@ -486,7 +508,7 @@ export const ApplicationsShowcase: React.FC<ApplicationsShowcaseProps> = ({
486
508
padding: 0 0.75rem;
487
509
}
488
510
489
- .top-bar {
511
+ .top-bar-row {
490
512
flex-direction: column;
491
513
align-items: stretch;
492
514
}
@@ -519,96 +541,100 @@ export const ApplicationsShowcase: React.FC<ApplicationsShowcaseProps> = ({
519
541
520
542
< div className = "applications-showcase" >
521
543
< div className = "top-bar" >
522
- < div className = "search-container" >
523
- < input
524
- type = "text"
525
- placeholder = "Search applications..."
526
- value = { searchTerm }
527
- onChange = { ( e ) => setSearchTerm ( e . target . value ) }
528
- className = "search-input"
529
- />
530
- { searchTerm && (
531
- < button onClick = { ( ) => setSearchTerm ( '' ) } className = "search-clear" >
532
- ×
544
+ < div className = "top-bar-row" >
545
+ < div className = "search-container" >
546
+ < input
547
+ type = "text"
548
+ placeholder = "Search applications..."
549
+ value = { searchTerm }
550
+ onChange = { ( e ) => setSearchTerm ( e . target . value ) }
551
+ className = "search-input"
552
+ />
553
+ { searchTerm && (
554
+ < button onClick = { ( ) => setSearchTerm ( '' ) } className = "search-clear" >
555
+ ×
556
+ </ button >
557
+ ) }
558
+ </ div >
559
+
560
+ < select
561
+ value = { filters . services [ 0 ] || '' }
562
+ onChange = { ( e ) => e . target . value ? toggleFilter ( 'services' , e . target . value ) : null }
563
+ className = "filter-select"
564
+ >
565
+ < option value = "" > Services</ option >
566
+ { uniqueServices . map ( ( service ) => (
567
+ < option key = { service } value = { service } >
568
+ { services [ service ] || service }
569
+ </ option >
570
+ ) ) }
571
+ </ select >
572
+
573
+ < select
574
+ value = { filters . platforms [ 0 ] || '' }
575
+ onChange = { ( e ) => e . target . value ? toggleFilter ( 'platforms' , e . target . value ) : null }
576
+ className = "filter-select"
577
+ >
578
+ < option value = "" > Languages</ option >
579
+ { uniquePlatforms . map ( ( platform ) => (
580
+ < option key = { platform } value = { platform } >
581
+ { platforms [ platform ] || platform }
582
+ </ option >
583
+ ) ) }
584
+ </ select >
585
+
586
+ < select
587
+ value = { filters . deployments [ 0 ] || '' }
588
+ onChange = { ( e ) => e . target . value ? toggleFilter ( 'deployments' , e . target . value ) : null }
589
+ className = "filter-select"
590
+ >
591
+ < option value = "" > Deployment</ option >
592
+ { uniqueDeployments . map ( ( deployment ) => (
593
+ < option key = { deployment } value = { deployment } >
594
+ { deployments [ deployment ] || deployment }
595
+ </ option >
596
+ ) ) }
597
+ </ select >
598
+
599
+ < select
600
+ value = { filters . complexities [ 0 ] || '' }
601
+ onChange = { ( e ) => e . target . value ? toggleFilter ( 'complexities' , e . target . value ) : null }
602
+ className = "filter-select"
603
+ >
604
+ < option value = "" > Complexity</ option >
605
+ { uniqueComplexities . map ( ( complexity ) => (
606
+ < option key = { complexity } value = { complexity } >
607
+ { complexities . data [ complexity ] || complexity }
608
+ </ option >
609
+ ) ) }
610
+ </ select >
611
+
612
+ < label className = "pro-toggle" >
613
+ < input
614
+ type = "checkbox"
615
+ checked = { filters . showProOnly }
616
+ onChange = { ( e ) => setFilters ( prev => ( { ...prev , showProOnly : e . target . checked } ) ) }
617
+ />
618
+ Pro Only
619
+ </ label >
620
+
621
+ { hasActiveFilters && (
622
+ < button onClick = { clearAllFilters } className = "clear-filters" >
623
+ Clear
533
624
</ button >
534
625
) }
535
626
</ div >
536
-
537
- < select
538
- value = { filters . services [ 0 ] || '' }
539
- onChange = { ( e ) => e . target . value ? toggleFilter ( 'services' , e . target . value ) : null }
540
- className = "filter-select"
541
- >
542
- < option value = "" > Services</ option >
543
- { uniqueServices . map ( ( service ) => (
544
- < option key = { service } value = { service } >
545
- { services [ service ] || service }
546
- </ option >
547
- ) ) }
548
- </ select >
549
-
550
- < select
551
- value = { filters . platforms [ 0 ] || '' }
552
- onChange = { ( e ) => e . target . value ? toggleFilter ( 'platforms' , e . target . value ) : null }
553
- className = "filter-select"
554
- >
555
- < option value = "" > Languages</ option >
556
- { uniquePlatforms . map ( ( platform ) => (
557
- < option key = { platform } value = { platform } >
558
- { platforms [ platform ] || platform }
559
- </ option >
560
- ) ) }
561
- </ select >
562
-
563
- < select
564
- value = { filters . deployments [ 0 ] || '' }
565
- onChange = { ( e ) => e . target . value ? toggleFilter ( 'deployments' , e . target . value ) : null }
566
- className = "filter-select"
567
- >
568
- < option value = "" > Deployment</ option >
569
- { uniqueDeployments . map ( ( deployment ) => (
570
- < option key = { deployment } value = { deployment } >
571
- { deployments [ deployment ] || deployment }
572
- </ option >
573
- ) ) }
574
- </ select >
575
-
576
- < select
577
- value = { filters . complexities [ 0 ] || '' }
578
- onChange = { ( e ) => e . target . value ? toggleFilter ( 'complexities' , e . target . value ) : null }
579
- className = "filter-select"
580
- >
581
- < option value = "" > Complexity</ option >
582
- { uniqueComplexities . map ( ( complexity ) => (
583
- < option key = { complexity } value = { complexity } >
584
- { complexities . data [ complexity ] || complexity }
585
- </ option >
586
- ) ) }
587
- </ select >
588
627
589
- < label className = "pro-toggle" >
590
- < input
591
- type = "checkbox"
592
- checked = { filters . showProOnly }
593
- onChange = { ( e ) => setFilters ( prev => ( { ...prev , showProOnly : e . target . checked } ) ) }
594
- />
595
- Pro Only
596
- </ label >
597
-
598
- < select
599
- value = { sortBy }
600
- onChange = { ( e ) => setSortBy ( e . target . value as 'title' | 'complexity' ) }
601
- className = "sort-select"
602
- >
603
- < option value = "title" > A-Z</ option >
604
- < option value = "complexity" > By Complexity</ option >
605
- </ select >
606
-
607
- { hasActiveFilters && (
608
- < button onClick = { clearAllFilters } className = "clear-filters" >
609
- Clear
610
- </ button >
611
- ) }
628
+ < div className = "top-bar-row" >
629
+ < select
630
+ value = { sortBy }
631
+ onChange = { ( e ) => setSortBy ( e . target . value as 'title' | 'complexity' ) }
632
+ className = "sort-select"
633
+ >
634
+ < option value = "title" > A-Z</ option >
635
+ < option value = "complexity" > By Complexity</ option >
636
+ </ select >
637
+ </ div >
612
638
</ div >
613
639
614
640
< div className = "results-info" >
0 commit comments