Skip to content

Commit f416c6b

Browse files
guitaripodclaude
andauthored
Redesign API documentation landing page with modern grid layout (#37)
* Redesign API documentation landing page with modern grid layout - Replace dropdown selector with responsive grid layout showing all packages as cards - Add Vapor brand colors with purple/blue gradient theme - Include package descriptions for better discoverability - Implement responsive design (4 columns desktop, 2 tablet, 1 mobile) - Add hover effects and smooth animations for better UX - Ensure accessibility with proper focus styles and keyboard navigation - Maintain existing URL routing structure for all packages 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * Improve package descriptions - Clarify XCTVapor is for XCTest and VaporTesting is for Swift Testing - Update Fluent, Redis, and Leaf descriptions to reflect they are Vapor integration packages - Make descriptions more accurate about the relationship between core packages and their Vapor wrappers Co-Authored-By: @0xTim --------- Co-authored-by: Claude <[email protected]>
1 parent 735b41f commit f416c6b

File tree

2 files changed

+234
-31
lines changed

2 files changed

+234
-31
lines changed

generate-api-docs.swift

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,65 @@ let packages: [String: [String]] = [
3939
"apns": ["VaporAPNS"],
4040
]
4141

42-
let htmlMenu = packages.values.flatMap { $0 }
43-
.sorted()
44-
.map { "<option value=\"\($0.lowercased())/documentation/\($0.lowercased())\">\($0)</option>" }
45-
.joined(separator: "\n")
42+
// Package descriptions
43+
let packageDescriptions: [String: String] = [
44+
"Vapor": "Core web framework for building server-side Swift applications",
45+
"XCTVapor": "Testing utilities for Vapor applications when using XCTest",
46+
"VaporTesting": "Modern testing framework for Vapor apps when using Swift Testing",
47+
"AsyncKit": "Async/await utilities and helpers for concurrent programming",
48+
"RoutingKit": "High-performance routing engine for HTTP requests",
49+
"ConsoleKit": "Terminal UI and command-line tools framework",
50+
"ConsoleKitCommands": "Command parsing and execution for CLI apps",
51+
"ConsoleKitTerminal": "Terminal formatting and interaction utilities",
52+
"WebSocketKit": "WebSocket client and server implementation",
53+
"MultipartKit": "Multipart form data parsing and encoding",
54+
"PostgresNIO": "Non-blocking PostgreSQL client built on SwiftNIO",
55+
"MySQLNIO": "Non-blocking MySQL client built on SwiftNIO",
56+
"SQLiteNIO": "Non-blocking SQLite client built on SwiftNIO",
57+
"SQLKit": "SQL query building and execution framework",
58+
"PostgresKit": "PostgreSQL integration for SQLKit",
59+
"MySQLKit": "MySQL integration for SQLKit",
60+
"SQLiteKit": "SQLite integration for SQLKit",
61+
"FluentKit": "Core ORM framework for database operations",
62+
"FluentSQL": "SQL dialect support for Fluent ORM",
63+
"XCTFluent": "Testing utilities for Fluent ORM",
64+
"Fluent": "Vapor integration package for FluentKit",
65+
"FluentPostgresDriver": "PostgreSQL driver for Fluent ORM",
66+
"FluentMongoDriver": "MongoDB driver for Fluent ORM",
67+
"FluentMySQLDriver": "MySQL driver for Fluent ORM",
68+
"FluentSQLiteDriver": "SQLite driver for Fluent ORM",
69+
"Redis": "Vapor wrapper for using Redis",
70+
"QueuesRedisDriver": "Redis driver for job queue system",
71+
"Queues": "Job queue system for background processing",
72+
"XCTQueues": "Testing utilities for queue system",
73+
"LeafKit": "Core templating engine framework",
74+
"Leaf": "Vapor integration for LeafKit",
75+
"JWTKit": "JSON Web Token signing and verification framework",
76+
"JWT": "JWT integration for Vapor authentication",
77+
"VaporAPNS": "Apple Push Notification Service integration"
78+
]
79+
80+
// Generate package cards HTML
81+
let allModules = packages.values.flatMap { $0 }.sorted()
82+
let packageCards = allModules.map { module in
83+
let description = packageDescriptions[module] ?? "API documentation for \(module)"
84+
let href = "\(module.lowercased())/documentation/\(module.lowercased())"
85+
86+
return """
87+
<a href="\(href)" class="package-card" tabindex="0">
88+
<h2 class="package-name">\(module)</h2>
89+
<p class="package-description">\(description)</p>
90+
</a>
91+
"""
92+
}.joined(separator: "\n")
4693

4794
do {
4895
let publicDirUrl = URL(fileURLWithPath: "./public", isDirectory: true)
4996
try FileManager.default.removeItemIfExists(at: publicDirUrl)
5097
try FileManager.default.createDirectory(at: publicDirUrl, withIntermediateDirectories: true)
5198

5299
var htmlIndex = try String(contentsOf: URL(fileURLWithPath: "./index.html", isDirectory: false), encoding: .utf8)
53-
htmlIndex.replace("{{Options}}", with: "\(htmlMenu)\n", maxReplacements: 1)
100+
htmlIndex.replace("{{PackageCards}}", with: packageCards, maxReplacements: 1)
54101

55102
try htmlIndex.write(to: publicDirUrl.appendingPathComponent("index.html", isDirectory: false), atomically: true, encoding: .utf8)
56103
try FileManager.default.copyItem(at: URL(fileURLWithPath: "./api-docs.png", isDirectory: false), into: publicDirUrl)
@@ -81,4 +128,4 @@ extension FileManager {
81128
let dstItem = dst.appendingPathComponent(src.lastPathComponent, isDirectory: dst.hasDirectoryPath)
82129
try self.copyItem(at: src, to: dstItem)
83130
}
84-
}
131+
}

index.html

Lines changed: 181 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,46 +4,202 @@
44
<title>Vapor API Docs</title>
55
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
66
<style>
7+
* {
8+
box-sizing: border-box;
9+
margin: 0;
10+
padding: 0;
11+
}
12+
713
html, body {
8-
height: 100%;
9-
background: #0d0d0d;
14+
min-height: 100%;
15+
background: #0a0a0a;
1016
color: #ffffff;
11-
padding: 0;
12-
margin: 0;
17+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
18+
line-height: 1.6;
1319
}
1420

15-
.wrapper {
16-
height: 100%;
17-
display: flex;
18-
justify-content: center;
19-
align-items: center;
20-
text-align: center;
21+
body {
22+
background: linear-gradient(135deg, #0a0a0a 0%, #1a0a2e 100%);
23+
background-attachment: fixed;
2124
}
2225

23-
.main {
24-
margin-top: -50px;
26+
.container {
27+
max-width: 1200px;
28+
margin: 0 auto;
29+
padding: 2rem;
2530
}
2631

27-
.title {
32+
.header {
33+
text-align: center;
34+
margin-bottom: 3rem;
35+
}
36+
37+
.logo {
2838
background-image: url(api-docs.png);
2939
width: 300px;
3040
height: 78px;
31-
background-size: 300px auto;
41+
background-size: contain;
42+
background-repeat: no-repeat;
43+
background-position: center;
3244
text-indent: -9999px;
33-
margin-bottom: 0;
45+
margin: 0 auto 1rem;
46+
}
47+
48+
.subtitle {
49+
color: #a0a0a0;
50+
font-size: 1.1rem;
51+
margin-bottom: 2rem;
52+
}
53+
54+
.package-grid {
55+
display: grid;
56+
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
57+
gap: 1.5rem;
58+
margin-bottom: 3rem;
59+
}
60+
61+
.package-card {
62+
background: rgba(255, 255, 255, 0.05);
63+
border: 1px solid rgba(255, 255, 255, 0.1);
64+
border-radius: 12px;
65+
padding: 1.5rem;
66+
text-decoration: none;
67+
color: inherit;
68+
transition: all 0.3s ease;
69+
cursor: pointer;
70+
position: relative;
71+
overflow: hidden;
72+
}
73+
74+
.package-card::before {
75+
content: '';
76+
position: absolute;
77+
top: 0;
78+
left: 0;
79+
right: 0;
80+
height: 3px;
81+
background: linear-gradient(90deg, #7c3aed 0%, #3b82f6 100%);
82+
transform: scaleX(0);
83+
transition: transform 0.3s ease;
84+
}
85+
86+
.package-card:hover {
87+
background: rgba(255, 255, 255, 0.08);
88+
border-color: rgba(255, 255, 255, 0.2);
89+
transform: translateY(-2px);
90+
box-shadow: 0 8px 24px rgba(124, 58, 237, 0.2);
91+
}
92+
93+
.package-card:hover::before {
94+
transform: scaleX(1);
95+
}
96+
97+
.package-name {
98+
font-size: 1.25rem;
99+
font-weight: 600;
100+
margin-bottom: 0.5rem;
101+
color: #ffffff;
34102
}
103+
104+
.package-description {
105+
font-size: 0.9rem;
106+
color: #a0a0a0;
107+
line-height: 1.5;
108+
}
109+
110+
.footer {
111+
text-align: center;
112+
margin-top: 4rem;
113+
padding-top: 2rem;
114+
border-top: 1px solid rgba(255, 255, 255, 0.1);
115+
color: #666;
116+
font-size: 0.9rem;
117+
}
118+
119+
.footer a {
120+
color: #7c3aed;
121+
text-decoration: none;
122+
}
123+
124+
.footer a:hover {
125+
text-decoration: underline;
126+
}
127+
128+
/* Responsive design */
129+
@media (max-width: 768px) {
130+
.container {
131+
padding: 1rem;
132+
}
133+
134+
.package-grid {
135+
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
136+
gap: 1rem;
137+
}
138+
139+
.logo {
140+
width: 250px;
141+
height: 65px;
142+
}
143+
144+
.subtitle {
145+
font-size: 1rem;
146+
}
147+
}
148+
149+
@media (max-width: 480px) {
150+
.package-grid {
151+
grid-template-columns: 1fr;
152+
}
153+
}
154+
155+
/* Focus styles for accessibility */
156+
.package-card:focus {
157+
outline: 2px solid #7c3aed;
158+
outline-offset: 2px;
159+
}
160+
161+
/* Loading animation */
162+
@keyframes fadeIn {
163+
from {
164+
opacity: 0;
165+
transform: translateY(20px);
166+
}
167+
to {
168+
opacity: 1;
169+
transform: translateY(0);
170+
}
171+
}
172+
173+
.package-card {
174+
animation: fadeIn 0.5s ease forwards;
175+
opacity: 0;
176+
}
177+
178+
.package-card:nth-child(1) { animation-delay: 0.05s; }
179+
.package-card:nth-child(2) { animation-delay: 0.1s; }
180+
.package-card:nth-child(3) { animation-delay: 0.15s; }
181+
.package-card:nth-child(4) { animation-delay: 0.2s; }
182+
.package-card:nth-child(5) { animation-delay: 0.25s; }
183+
.package-card:nth-child(6) { animation-delay: 0.3s; }
184+
.package-card:nth-child(7) { animation-delay: 0.35s; }
185+
.package-card:nth-child(8) { animation-delay: 0.4s; }
186+
.package-card:nth-child(n+9) { animation-delay: 0.45s; }
35187
</style>
36188
</head>
37189
<body>
38-
<div class="wrapper">
39-
<div class="main">
40-
<h1 class="title">Vapor API Docs</h1>
41-
<br>
42-
<select onchange="this.options[this.selectedIndex].value && (window.location = this.options[this.selectedIndex].value); this.selectedIndex = 0;">
43-
<option value="">Choose package...</option>
44-
{{Options}}
45-
</select>
46-
</div>
190+
<div class="container">
191+
<header class="header">
192+
<h1 class="logo">Vapor API Docs</h1>
193+
<p class="subtitle">Explore the complete API documentation for Vapor and its ecosystem</p>
194+
</header>
195+
196+
<main class="package-grid">
197+
{{PackageCards}}
198+
</main>
199+
200+
<footer class="footer">
201+
<p>Built with <a href="https://vapor.codes" target="_blank" rel="noopener">Vapor</a><a href="https://github.com/vapor/api-docs" target="_blank" rel="noopener">View on GitHub</a></p>
202+
</footer>
47203
</div>
48204
</body>
49-
</html>
205+
</html>

0 commit comments

Comments
 (0)