Skip to content

Commit c5550d5

Browse files
authored
Sample apps design fixes (#80)
* Fix top alignment of cards and badges * Fix search alignment * Fix icon alignment and link color * Update ApplicationsShowcase.tsx
1 parent 790c21e commit c5550d5

File tree

1 file changed

+128
-102
lines changed

1 file changed

+128
-102
lines changed

src/components/applications/ApplicationsShowcase.tsx

Lines changed: 128 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@ const ApplicationCard: React.FC<{
3737
deployments: Record<string, string>;
3838
}> = ({ app, services, platforms, deployments }) => {
3939
return (
40-
<div className="app-card">
40+
<a
41+
href={app.url}
42+
target="_blank"
43+
rel="noopener noreferrer"
44+
className="app-card"
45+
>
4146
<div className="card-image">
4247
<img src={app.teaser} alt={app.title} loading="lazy" />
4348
<div className="card-badges">
@@ -65,17 +70,12 @@ const ApplicationCard: React.FC<{
6570
)}
6671
</div>
6772

68-
<a
69-
href={app.url}
70-
target="_blank"
71-
rel="noopener noreferrer"
72-
className="card-link"
73-
>
73+
<span className="card-link">
7474
View Project →
75-
</a>
75+
</span>
7676
</div>
7777
</div>
78-
</div>
78+
</a>
7979
);
8080
};
8181

@@ -197,15 +197,23 @@ export const ApplicationsShowcase: React.FC<ApplicationsShowcaseProps> = ({
197197
198198
/* Top Bar */
199199
.top-bar {
200-
display: flex;
201-
gap: 1rem;
202-
align-items: center;
203200
margin-bottom: 1.5rem;
204201
padding: 1rem;
205202
background: var(--sl-color-bg-sidebar);
206203
border: 1px solid var(--sl-color-gray-6);
207204
border-radius: 0.5rem;
205+
}
206+
207+
.top-bar-row {
208+
display: flex;
209+
gap: 1rem;
210+
align-items: flex-start;
208211
flex-wrap: wrap;
212+
margin-bottom: 1rem;
213+
}
214+
215+
.top-bar-row:last-child {
216+
margin-bottom: 0;
209217
}
210218
211219
.search-container {
@@ -222,6 +230,7 @@ export const ApplicationsShowcase: React.FC<ApplicationsShowcaseProps> = ({
222230
background: var(--sl-color-bg);
223231
color: var(--sl-color-white);
224232
font-size: 0.875rem;
233+
margin-top: 0;
225234
}
226235
227236
.search-input:focus {
@@ -258,6 +267,7 @@ export const ApplicationsShowcase: React.FC<ApplicationsShowcaseProps> = ({
258267
color: var(--sl-color-white);
259268
font-size: 0.875rem;
260269
min-width: 140px;
270+
margin-top: 0;
261271
}
262272
263273
.filter-select:focus {
@@ -273,6 +283,7 @@ export const ApplicationsShowcase: React.FC<ApplicationsShowcaseProps> = ({
273283
color: var(--sl-color-white);
274284
cursor: pointer;
275285
white-space: nowrap;
286+
margin-top: 0.375rem;
276287
}
277288
278289
.sort-select {
@@ -320,6 +331,13 @@ export const ApplicationsShowcase: React.FC<ApplicationsShowcaseProps> = ({
320331
border-radius: 0.75rem;
321332
overflow: hidden;
322333
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;
323341
}
324342
325343
.app-card:hover {
@@ -371,6 +389,9 @@ export const ApplicationsShowcase: React.FC<ApplicationsShowcaseProps> = ({
371389
372390
.card-content {
373391
padding: 1.25rem;
392+
display: flex;
393+
flex-direction: column;
394+
flex: 1;
374395
}
375396
376397
.card-title {
@@ -393,6 +414,7 @@ export const ApplicationsShowcase: React.FC<ApplicationsShowcaseProps> = ({
393414
justify-content: space-between;
394415
align-items: center;
395416
gap: 1rem;
417+
margin-top: auto;
396418
}
397419
398420
.service-icons {
@@ -411,6 +433,7 @@ export const ApplicationsShowcase: React.FC<ApplicationsShowcaseProps> = ({
411433
align-items: center;
412434
justify-content: center;
413435
transition: all 0.2s ease;
436+
margin-top: 0;
414437
}
415438
416439
.service-icon:hover {
@@ -436,8 +459,7 @@ export const ApplicationsShowcase: React.FC<ApplicationsShowcaseProps> = ({
436459
display: inline-flex;
437460
align-items: center;
438461
gap: 0.5rem;
439-
color: white;
440-
text-decoration: none;
462+
color: var(--sl-color-white);
441463
font-weight: 500;
442464
font-size: 0.875rem;
443465
padding: 0.5rem 0.75rem;
@@ -446,7 +468,7 @@ export const ApplicationsShowcase: React.FC<ApplicationsShowcaseProps> = ({
446468
white-space: nowrap;
447469
}
448470
449-
.card-link:hover {
471+
.app-card:hover .card-link {
450472
color: var(--sl-color-accent);
451473
}
452474
@@ -486,7 +508,7 @@ export const ApplicationsShowcase: React.FC<ApplicationsShowcaseProps> = ({
486508
padding: 0 0.75rem;
487509
}
488510
489-
.top-bar {
511+
.top-bar-row {
490512
flex-direction: column;
491513
align-items: stretch;
492514
}
@@ -519,96 +541,100 @@ export const ApplicationsShowcase: React.FC<ApplicationsShowcaseProps> = ({
519541

520542
<div className="applications-showcase">
521543
<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
533624
</button>
534625
)}
535626
</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>
588627

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>
612638
</div>
613639

614640
<div className="results-info">

0 commit comments

Comments
 (0)