Skip to content

Commit acf16d1

Browse files
committed
Carasouel based homepage layout
1 parent df993fc commit acf16d1

File tree

5 files changed

+157
-12
lines changed

5 files changed

+157
-12
lines changed

src/lib/components/global/ToolsCarousel.svelte

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
3737
const dupe = (arr: NavItem[]) => [...arr, ...arr]; // seamless loop
3838
39-
const durationFor = (count: number) => Math.max(12, Math.round(speedBase * (count / 8))); // scale with item count
39+
const durationFor = (count: number) => Math.max(12, Math.round(speedBase * (count / 4))); // scale with item count
4040
4141
const cardWidthCss = typeof cardWidth === 'number' ? `${cardWidth}px` : cardWidth;
4242
</script>
@@ -73,8 +73,13 @@
7373
// border: 1px solid var(--border-primary);
7474
border-radius: var(--radius-md);
7575
box-shadow: var(--shadow-md);
76-
:global(.tool-card):hover {
77-
transform: translateY(0);
76+
:global(.card-wrap) {
77+
--card-width: 16rem;
78+
}
79+
:global(.tool-card) {
80+
&:hover {
81+
transform: translateY(0);
82+
}
7883
}
7984
}
8085
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
<script lang="ts">
2+
import ToolsCarousel from '$lib/components/global/ToolsCarousel.svelte';
3+
import { SUB_NAV } from '$lib/constants/nav';
4+
import { site } from '$lib/constants/site';
5+
</script>
6+
7+
<div class="carousel-layout">
8+
<!-- Hero Title -->
9+
<section class="hero">
10+
<h1 class="title">{site.title}</h1>
11+
<p class="subtitle">{site.heroDescription}</p>
12+
<div class="search-cta">
13+
<kbd>⌘</kbd>
14+
<kbd>K</kbd>
15+
<span>to search</span>
16+
</div>
17+
</section>
18+
19+
<!-- Carousel -->
20+
<ToolsCarousel sections={SUB_NAV} speedBase={36} gap="var(--spacing-sm)" pauseOnHover reverseAlternate />
21+
</div>
22+
23+
<style lang="scss">
24+
.carousel-layout {
25+
display: flex;
26+
flex-direction: column;
27+
gap: var(--spacing-xl);
28+
justify-content: space-evenly;
29+
width: 100vw;
30+
min-height: calc(100vh - 5rem);
31+
position: absolute;
32+
left: 0;
33+
top: 5rem;
34+
background-color: var(--bg-primary);
35+
opacity: 1;
36+
background-image:
37+
linear-gradient(var(--bg-secondary) 1px, transparent 1px),
38+
linear-gradient(to right, var(--bg-secondary) 1px, var(--bg-primary) 1px);
39+
background-size: 20px 20px;
40+
}
41+
42+
.hero {
43+
text-align: center;
44+
padding: var(--spacing-xl) var(--spacing-lg) var(--spacing-md);
45+
margin: 0 auto;
46+
max-width: 50rem;
47+
}
48+
49+
.title {
50+
font-size: clamp(2.5rem, 6vw, 4.5rem);
51+
font-weight: 700;
52+
background: linear-gradient(
53+
135deg,
54+
var(--color-primary),
55+
color-mix(in srgb, var(--color-primary), var(--color-primary-hover) 40%)
56+
);
57+
background-clip: text;
58+
-webkit-background-clip: text;
59+
-webkit-text-fill-color: transparent;
60+
margin: 0 0 var(--spacing-md) 0;
61+
line-height: 1.1;
62+
letter-spacing: -0.02em;
63+
animation: titleEnter 0.8s cubic-bezier(0.16, 1, 0.3, 1) forwards;
64+
}
65+
66+
.subtitle {
67+
font-size: var(--font-size-lg);
68+
color: var(--text-secondary);
69+
margin: 0;
70+
line-height: 1.6;
71+
font-weight: 400;
72+
}
73+
74+
.search-cta {
75+
display: flex;
76+
align-items: center;
77+
justify-content: center;
78+
gap: var(--spacing-xs);
79+
margin-top: var(--spacing-lg);
80+
font-size: var(--font-size-md);
81+
color: var(--text-secondary);
82+
83+
kbd {
84+
display: inline-flex;
85+
align-items: center;
86+
justify-content: center;
87+
min-width: 2rem;
88+
height: 2rem;
89+
padding: 0 var(--spacing-sm);
90+
background: var(--bg-secondary);
91+
border: 1px solid var(--border-primary);
92+
border-radius: var(--radius-sm);
93+
font-family: inherit;
94+
font-size: var(--font-size-sm);
95+
font-weight: 600;
96+
color: var(--text-primary);
97+
box-shadow: 0 1px 0 0 var(--border-secondary);
98+
}
99+
100+
span {
101+
font-weight: 400;
102+
}
103+
}
104+
105+
:global(body:has(.carousel-layout) .footer) {
106+
position: absolute;
107+
bottom: 0;
108+
background: var(--bg-primary);
109+
}
110+
111+
@keyframes titleEnter {
112+
0% {
113+
transform: translateY(-50px);
114+
opacity: 0;
115+
clip-path: polygon(100% 0, 100% 100%, 0 100%, 0 80%);
116+
}
117+
100% {
118+
transform: translateY(0);
119+
opacity: 1;
120+
clip-path: polygon(100% 0, 100% 100%, 0 100%, 0 15%);
121+
}
122+
}
123+
124+
@media (max-width: 768px) {
125+
.hero {
126+
padding: var(--spacing-lg) var(--spacing-md) var(--spacing-sm);
127+
}
128+
129+
.subtitle {
130+
font-size: var(--font-size-md);
131+
}
132+
}
133+
</style>

src/lib/constants/site.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ export const site = {
44
description: 'A free set of online tools to help with IP addressing and subnetting.',
55
longDescription:
66
'Comprehensive IP address calculator with subnet calculations, CIDR conversion, IP format conversion, and network reference tools.',
7-
heroDescription:
8-
'Comprehensive suite of networking tools for subnet calculations, CIDR conversions, IP format transformations, and network reference information.',
7+
heroDescription: 'Your companion for all-things networking',
98
keywords: 'IP calculator, subnet calculator, CIDR converter, network tools, IP tools, networking',
109
url: 'https://networking-toolbox.as93.net',
1110
image: 'https://networking-toolbox.as93.net/og-image.png',

src/lib/stores/homepageLayout.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { writable } from 'svelte/store';
22
import { browser } from '$app/environment';
33

4-
export type HomepageLayoutMode = 'default' | 'minimal' | 'categories';
4+
export type HomepageLayoutMode = 'default' | 'minimal' | 'carousel' | 'categories';
55

66
export interface HomepageLayoutOption {
77
id: HomepageLayoutMode;
@@ -14,19 +14,24 @@ const STORAGE_KEY = 'homepage-layout';
1414
// Available homepage layout options
1515
export const homepageLayoutOptions: HomepageLayoutOption[] = [
1616
{
17-
id: 'default',
18-
name: 'Default',
19-
description: 'Full homepage with all sections and features',
17+
id: 'categories',
18+
name: 'Categories',
19+
description: 'Organized by tool categories with flexible grid layout',
2020
},
2121
{
2222
id: 'minimal',
2323
name: 'Minimal',
2424
description: 'Clean, simplified homepage layout',
2525
},
2626
{
27-
id: 'categories',
28-
name: 'Categories',
29-
description: 'Organized by tool categories with flexible grid layout',
27+
id: 'carousel',
28+
name: 'carousel',
29+
description: 'Full homepage with all sections and features',
30+
},
31+
{
32+
id: 'default',
33+
name: 'Legacy',
34+
description: 'Full homepage with all sections and features',
3035
},
3136
];
3237

src/routes/+page.svelte

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import HomepageDefault from '$lib/components/home/HomepageDefault.svelte';
77
import HomepageMinimal from '$lib/components/home/HomepageMinimal.svelte';
88
import HomepageCategories from '$lib/components/home/HomepageCategories.svelte';
9+
import HomepageCarousel from '$lib/components/home/HomepageCarousel.svelte';
910
1011
// Helper function to extract nav items from mixed structure
1112
function extractNavItems(items: (NavItem | NavGroup)[]): NavItem[] {
@@ -38,6 +39,8 @@
3839
<HomepageMinimal {toolPages} {referencePages} />
3940
{:else if $currentLayout === 'categories'}
4041
<HomepageCategories {toolPages} {referencePages} />
42+
{:else if $currentLayout === 'carousel'}
43+
<HomepageCarousel />
4144
{:else}
4245
<HomepageDefault {toolPages} {referencePages} />
4346
{/if}

0 commit comments

Comments
 (0)