Skip to content

Commit b5c68ed

Browse files
authored
feat: redesign homepage with new hero, themes bento grid, and features section (#205)
2 parents 2e415ab + 73864ae commit b5c68ed

8 files changed

Lines changed: 1240 additions & 774 deletions

File tree

src/components/SEO.astro

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ const {
1313
type = 'website',
1414
} = Astro.props;
1515
16-
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
17-
const ogImage = new URL(image, Astro.site);
16+
const siteBase = Astro.site ?? Astro.url.origin;
17+
const canonicalURL = new URL(Astro.url.pathname, siteBase);
18+
const ogImage = new URL(image, siteBase);
1819
const fullTitle = title === 'Spicetify' ? title : `${title} | Spicetify`;
1920
---
2021

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
---
2+
import ArrowIcon from '../icons/ArrowIcon.astro';
3+
4+
interface Props {
5+
features: {
6+
icon: string;
7+
title: string;
8+
description: string;
9+
href: string;
10+
}[];
11+
}
12+
13+
const { features } = Astro.props;
14+
---
15+
16+
<section id="features" class="section features-section" aria-labelledby="features-heading">
17+
<div class="section-container">
18+
<div class="section-header">
19+
<span class="section-tag">Features</span>
20+
<h2 id="features-heading">Everything you need</h2>
21+
<p>A complete toolkit to customize every corner of Spotify.</p>
22+
</div>
23+
24+
<div class="features-grid">
25+
{features.map((feature) => (
26+
<a href={feature.href} class="feature-card">
27+
<div class="feature-icon" data-icon={feature.icon}>
28+
{feature.icon === 'palette' && (
29+
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
30+
<circle cx="13.5" cy="6.5" r="2.5" /><circle cx="17.5" cy="10.5" r="2.5" /><circle cx="8.5" cy="7.5" r="2.5" /><circle cx="6.5" cy="12.5" r="2.5" />
31+
<path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.926 0 1.648-.746 1.648-1.688 0-.437-.18-.835-.437-1.125-.29-.289-.438-.652-.438-1.125a1.64 1.64 0 0 1 1.668-1.668h1.996c3.051 0 5.555-2.503 5.555-5.554C21.965 6.012 17.461 2 12 2z" />
32+
</svg>
33+
)}
34+
{feature.icon === 'puzzle' && (
35+
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
36+
<path d="M12 2v4m0 12v4M2 12h4m12 0h4m-3.17-6.83-2.83 2.83m-4 4-2.83 2.83M6.83 5.17l2.83 2.83m4 4 2.83 2.83" />
37+
</svg>
38+
)}
39+
{feature.icon === 'grid' && (
40+
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
41+
<rect x="3" y="3" width="7" height="7" rx="1" /><rect x="14" y="3" width="7" height="7" rx="1" /><rect x="3" y="14" width="7" height="7" rx="1" /><rect x="14" y="14" width="7" height="7" rx="1" />
42+
</svg>
43+
)}
44+
{feature.icon === 'terminal' && (
45+
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
46+
<polyline points="4 17 10 11 4 5" /><line x1="12" y1="19" x2="20" y2="19" />
47+
</svg>
48+
)}
49+
</div>
50+
<h3>{feature.title}</h3>
51+
<p>{feature.description}</p>
52+
<span class="feature-arrow">
53+
<ArrowIcon size={14} />
54+
</span>
55+
</a>
56+
))}
57+
</div>
58+
</div>
59+
</section>
60+
61+
<style>
62+
.features-section {
63+
background: var(--color-bg-secondary);
64+
}
65+
66+
.features-grid {
67+
display: grid;
68+
grid-template-columns: repeat(2, 1fr);
69+
gap: 1rem;
70+
}
71+
72+
.feature-card {
73+
position: relative;
74+
display: flex;
75+
flex-direction: column;
76+
padding: 1.75rem;
77+
border-radius: 14px;
78+
border: 1px solid var(--color-border);
79+
background: var(--color-bg);
80+
text-decoration: none;
81+
color: var(--color-text);
82+
transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
83+
overflow: hidden;
84+
}
85+
86+
.feature-card::before {
87+
content: '';
88+
position: absolute;
89+
inset: 0;
90+
background: linear-gradient(135deg, var(--accent-subtle) 0%, transparent 50%);
91+
opacity: 0;
92+
transition: opacity 0.3s ease;
93+
}
94+
95+
.feature-card:hover {
96+
text-decoration: none;
97+
transform: translateY(-2px);
98+
border-color: var(--accent-border-hover);
99+
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.06);
100+
}
101+
102+
.feature-card:hover::before {
103+
opacity: 1;
104+
}
105+
106+
.feature-card:focus-visible {
107+
outline: 2px solid var(--accent);
108+
outline-offset: 2px;
109+
}
110+
111+
.feature-icon {
112+
position: relative;
113+
display: flex;
114+
align-items: center;
115+
justify-content: center;
116+
width: 42px;
117+
height: 42px;
118+
border-radius: 10px;
119+
background: var(--accent-soft);
120+
color: var(--accent);
121+
margin-bottom: 1rem;
122+
}
123+
124+
.feature-card h3 {
125+
position: relative;
126+
font-family: var(--font-display);
127+
font-size: 1.1rem;
128+
font-weight: 700;
129+
margin-bottom: 0.4rem;
130+
}
131+
132+
.feature-card p {
133+
position: relative;
134+
font-size: 0.88rem;
135+
color: var(--color-text-secondary);
136+
line-height: 1.55;
137+
margin: 0;
138+
}
139+
140+
.feature-arrow {
141+
position: absolute;
142+
top: 1.75rem;
143+
right: 1.75rem;
144+
display: flex;
145+
align-items: center;
146+
justify-content: center;
147+
width: 28px;
148+
height: 28px;
149+
border-radius: 50%;
150+
background: var(--accent-soft);
151+
color: var(--accent);
152+
opacity: 0;
153+
transform: translateX(-4px);
154+
transition: opacity 0.25s ease, transform 0.25s ease;
155+
}
156+
157+
.feature-card:hover .feature-arrow {
158+
opacity: 1;
159+
transform: translateX(0);
160+
}
161+
162+
@media (prefers-reduced-motion: reduce) {
163+
.feature-card,
164+
.feature-arrow {
165+
transition: none !important;
166+
}
167+
}
168+
169+
@media (max-width: 900px) {
170+
.features-grid {
171+
grid-template-columns: 1fr;
172+
}
173+
}
174+
</style>

0 commit comments

Comments
 (0)