Recipe sharing platform built on AT Protocol
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Updates styling

+308 -196
+194 -94
public/css/style.css
··· 1 1 :root { 2 - --bg: #ffffff; 3 - --text: #1a1a1a; 4 - --text-muted: #c9c8c8; 2 + --bg: #fafafa; 3 + --bg-card: #ffffff; 4 + --text: #0a0a0a; 5 + --text-muted: #737373; 5 6 --border: #e5e5e5; 6 - --link: #0066cc; 7 + --accent: #2563eb; 8 + --accent-hover: #1d4ed8; 9 + --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1); 10 + --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1); 7 11 } 8 12 9 13 @media (prefers-color-scheme: dark) { 10 14 :root { 11 15 --bg: #0a0a0a; 12 - --text: #ffffff; 13 - --text-muted: #c9c8c8; 14 - --border: #2a2a2a; 15 - --link: #4d9fff; 16 + --bg-card: #171717; 17 + --text: #fafafa; 18 + --text-muted: #a3a3a3; 19 + --border: #262626; 20 + --accent: #3b82f6; 21 + --accent-hover: #60a5fa; 22 + --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.3); 23 + --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.3); 16 24 } 17 25 } 18 26 ··· 23 31 } 24 32 25 33 body { 26 - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; 34 + font-family: 35 + -apple-system, BlinkMacSystemFont, "Segoe UI", "Inter", sans-serif; 27 36 background: var(--bg); 28 37 color: var(--text); 29 38 font-size: 16px; 30 39 line-height: 1.6; 40 + -webkit-font-smoothing: antialiased; 31 41 } 32 42 33 43 a { 34 44 color: var(--text); 35 45 text-decoration: none; 46 + transition: color 0.2s ease; 36 47 } 37 48 38 49 a:hover { 39 - color: var(--link); 50 + color: var(--accent); 40 51 } 41 52 42 53 /* Layout */ 43 54 .container { 44 55 display: grid; 45 - grid-template-columns: 280px 1fr; 46 - max-width: 920px; 56 + grid-template-columns: 260px 1fr; 57 + max-width: 1280px; 47 58 margin: 0 auto; 48 59 min-height: 100vh; 60 + gap: 60px; 49 61 } 50 62 51 63 /* Sidebar */ 52 64 .sidebar { 53 - padding: 80px 40px 40px 80px; 65 + padding: 60px 40px 40px 60px; 54 66 display: flex; 55 67 flex-direction: column; 56 68 justify-content: space-between; 57 69 position: sticky; 58 70 top: 0; 59 71 height: 100vh; 60 - overflow-y: auto; 72 + overflow-y: hidden; /* Changed from auto to hidden */ 61 73 } 62 74 63 75 .sidebar .logo { 64 - margin-bottom: 16px; 76 + margin-bottom: 40px; 65 77 } 78 + 66 79 .sidebar .logo img { 67 - height: 35px; 68 - width: 35px; 80 + height: 40px; 81 + width: 40px; 82 + border-radius: 8px; 69 83 } 70 84 71 85 .sidebar nav ul { 72 86 list-style: none; 73 - margin-bottom: 40px; 87 + margin-bottom: 32px; 74 88 } 75 89 76 90 .sidebar nav li { 77 - margin-bottom: 16px; 91 + margin-bottom: 8px; 78 92 } 79 93 80 94 .sidebar nav a { 81 95 color: var(--text-muted); 82 - font-size: 16px; 96 + font-size: 15px; 97 + display: block; 98 + padding: 8px 12px; 99 + border-radius: 6px; 100 + transition: all 0.2s ease; 83 101 } 84 102 85 103 .sidebar nav a:hover { 86 104 color: var(--text); 105 + background: var(--bg-card); 87 106 } 88 107 89 - .sidebar .active { 90 - font-weight: 500; 108 + .sidebar nav a.active { 109 + font-weight: 600; 91 110 color: var(--text); 111 + background: var(--bg-card); 92 112 } 93 113 94 114 .sidebar hr { 95 115 border: none; 96 116 border-top: 1px solid var(--border); 97 - margin-bottom: 40px; 117 + margin-bottom: 32px; 98 118 } 99 119 100 120 .sidebar-bottom { 101 121 color: var(--text-muted); 102 - font-size: 14px; 122 + font-size: 13px; 123 + padding-top: 24px; 124 + border-top: 1px solid var(--border); 103 125 } 104 126 105 127 /* Content */ 106 128 .content { 107 - padding: 80px 80px 80px 40px; 108 - max-width: 900px; 129 + padding: 60px 60px 60px 0; 130 + max-width: 800px; 131 + } 132 + 133 + .content-header { 134 + margin-bottom: 48px; 109 135 } 110 136 111 137 .content h1 { 112 - font-size: 32px; 113 - font-weight: 600; 114 - margin-bottom: 24px; 115 - line-height: 1.1; 138 + font-size: 36px; 139 + font-weight: 700; 140 + margin-bottom: 12px; 141 + line-height: 1.2; 142 + letter-spacing: -0.02em; 116 143 } 117 144 118 145 .content .subtitle { 119 146 color: var(--text-muted); 120 - font-size: 16px; 121 - margin-bottom: 80px; 147 + font-size: 17px; 148 + line-height: 1.6; 122 149 } 123 150 151 + /* Recipe Cards */ 124 152 .recipe-list { 125 153 list-style: none; 154 + display: grid; 155 + gap: 24px; 126 156 } 127 157 128 158 .recipe-card { 129 - margin-bottom: 48px; 130 - padding-bottom: 48px; 131 - border-bottom: 1px solid var(--border); 159 + background: var(--bg-card); 160 + border: 1px solid var(--border); 161 + border-radius: 12px; 162 + padding: 24px; 163 + transition: all 0.2s ease; 164 + cursor: pointer; 132 165 } 133 166 134 - .recipe-card:last-child { 135 - border-bottom: none; 167 + .recipe-card:hover { 168 + border-color: var(--accent); 169 + box-shadow: var(--shadow-lg); 170 + transform: translateY(-2px); 136 171 } 137 172 138 173 .recipe-card h2 { 139 - font-size: 28px; 174 + font-size: 22px; 140 175 font-weight: 600; 141 176 margin-bottom: 12px; 177 + letter-spacing: -0.01em; 178 + } 179 + 180 + .recipe-card h2 a { 181 + color: var(--text); 182 + } 183 + 184 + .recipe-card h2 a:hover { 185 + color: var(--accent); 142 186 } 143 187 144 188 .recipe-meta { 189 + display: flex; 190 + gap: 16px; 145 191 color: var(--text-muted); 146 - font-size: 15px; 147 - margin-bottom: 16px; 192 + font-size: 14px; 193 + margin-bottom: 12px; 194 + flex-wrap: wrap; 148 195 } 149 196 150 197 .recipe-meta span { 151 - margin-right: 20px; 198 + display: flex; 199 + align-items: center; 200 + gap: 6px; 201 + } 202 + 203 + .recipe-meta span::before { 204 + content: ""; 205 + display: inline-block; 206 + width: 4px; 207 + height: 4px; 208 + background: var(--text-muted); 209 + border-radius: 50%; 210 + } 211 + 212 + .recipe-meta span:first-child::before { 213 + display: none; 152 214 } 153 215 154 216 .recipe-card p { 155 217 color: var(--text-muted); 156 - font-size: 16px; 157 - line-height: 1.7; 218 + font-size: 15px; 219 + line-height: 1.6; 220 + } 221 + 222 + .recipe-type-badge { 223 + display: inline-block; 224 + padding: 4px 12px; 225 + background: var(--bg); 226 + border: 1px solid var(--border); 227 + border-radius: 20px; 228 + font-size: 13px; 229 + font-weight: 500; 230 + color: var(--text-muted); 231 + text-transform: capitalize; 158 232 } 159 233 160 234 .empty-state { 235 + text-align: center; 236 + padding: 80px 20px; 161 237 color: var(--text-muted); 162 - font-size: 18px; 238 + } 239 + 240 + .empty-state p { 241 + font-size: 17px; 242 + margin-bottom: 24px; 243 + } 244 + 245 + .empty-state a { 246 + color: var(--accent); 247 + font-weight: 500; 163 248 } 164 249 165 250 /* Forms */ ··· 172 257 } 173 258 174 259 .form-box { 175 - max-width: 440px; 260 + max-width: 480px; 176 261 width: 100%; 262 + background: var(--bg-card); 263 + border: 1px solid var(--border); 264 + border-radius: 12px; 265 + padding: 40px; 266 + box-shadow: var(--shadow-lg); 177 267 } 178 268 179 269 .form-box h1 { 180 - font-size: 40px; 181 - font-weight: 600; 182 - margin-bottom: 40px; 270 + font-size: 32px; 271 + font-weight: 700; 272 + margin-bottom: 32px; 273 + letter-spacing: -0.02em; 183 274 } 184 275 185 276 .form-group { 186 - margin-bottom: 28px; 277 + margin-bottom: 24px; 187 278 } 188 279 189 280 .form-group label { 190 281 display: block; 191 - margin-bottom: 10px; 192 - font-weight: 500; 193 - font-size: 15px; 194 - } 195 - 196 - .form-group input { 197 - width: 100%; 198 - padding: 14px 16px; 199 - border: 1px solid var(--border); 200 - border-radius: 6px; 201 - background: var(--bg); 202 - color: var(--text); 203 - font-size: 16px; 204 - } 205 - 206 - .form-group input:focus { 207 - outline: none; 208 - border-color: var(--link); 209 - } 210 - 211 - .form-group small { 212 - display: block; 213 - margin-top: 8px; 214 - color: var(--text-muted); 282 + margin-bottom: 8px; 283 + font-weight: 600; 215 284 font-size: 14px; 216 285 } 217 286 287 + .form-group input, 218 288 .form-group select, 219 289 .form-group textarea { 220 290 width: 100%; 221 - padding: 14px 16px; 291 + padding: 12px 16px; 222 292 border: 1px solid var(--border); 223 - border-radius: 6px; 293 + border-radius: 8px; 224 294 background: var(--bg); 225 295 color: var(--text); 226 - font-size: 16px; 296 + font-size: 15px; 227 297 font-family: inherit; 298 + transition: all 0.2s ease; 228 299 } 229 300 301 + .form-group input:focus, 230 302 .form-group select:focus, 231 303 .form-group textarea:focus { 232 304 outline: none; 233 - border-color: var(--link); 305 + border-color: var(--accent); 306 + box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1); 234 307 } 235 308 236 309 .form-group textarea { 237 310 resize: vertical; 238 - min-height: 100px; 311 + min-height: 120px; 312 + line-height: 1.6; 313 + } 314 + 315 + .form-group small { 316 + display: block; 317 + margin-top: 6px; 318 + color: var(--text-muted); 319 + font-size: 13px; 320 + } 321 + 322 + .form-grid { 323 + display: grid; 324 + grid-template-columns: repeat(3, 1fr); 325 + gap: 16px; 326 + margin-bottom: 24px; 239 327 } 240 328 241 329 button { 242 - padding: 14px 28px; 243 - background: var(--text); 244 - color: var(--bg); 330 + width: 100%; 331 + padding: 14px 24px; 332 + background: var(--accent); 333 + color: white; 245 334 border: none; 246 - border-radius: 6px; 247 - font-size: 16px; 335 + border-radius: 8px; 336 + font-size: 15px; 248 337 font-weight: 600; 249 338 cursor: pointer; 339 + transition: all 0.2s ease; 250 340 } 251 341 252 342 button:hover { 253 - opacity: 0.9; 343 + background: var(--accent-hover); 344 + transform: translateY(-1px); 345 + box-shadow: var(--shadow-lg); 346 + } 347 + 348 + button:active { 349 + transform: translateY(0); 254 350 } 255 351 256 352 .error { 257 - background: rgba(255, 0, 0, 0.1); 258 - border: 1px solid rgba(255, 0, 0, 0.3); 259 - color: #dd0000; 260 - padding: 16px 20px; 261 - border-radius: 6px; 262 - margin-bottom: 28px; 263 - font-size: 15px; 353 + background: rgba(239, 68, 68, 0.1); 354 + border: 1px solid rgba(239, 68, 68, 0.2); 355 + color: #dc2626; 356 + padding: 16px; 357 + border-radius: 8px; 358 + margin-bottom: 24px; 359 + font-size: 14px; 264 360 } 265 361 362 + /* Responsive */ 266 363 @media (max-width: 900px) { 267 364 .container { 268 365 grid-template-columns: 1fr; 366 + gap: 0; 269 367 } 270 368 271 369 .sidebar { 272 370 padding: 40px; 371 + position: relative; 372 + height: auto; 273 373 } 274 374 275 375 .content { 276 - padding: 40px; 376 + padding: 0 40px 40px; 277 377 } 278 378 279 - .content h1 { 280 - font-size: 48px; 379 + .form-grid { 380 + grid-template-columns: 1fr; 281 381 } 282 382 }
public/images/bluerecipes.svg public/images/favicon.svg
+9
public/images/logo.svg
··· 1 + <svg width="96" height="96" viewBox="0 0 96 96" fill="none" xmlns="http://www.w3.org/2000/svg"> 2 + <rect width="96" height="96" rx="10" fill="#3B82F6"/> 3 + <path d="M25.6204 37.4013C25.6204 36.7805 26.1236 36.2772 26.7445 36.2772H69.46C70.0808 36.2772 70.5841 36.7805 70.5841 37.4013V64.3795C70.5841 67.4836 68.0678 70 64.9637 70H31.2408C28.1367 70 25.6204 67.4836 25.6204 64.3795V37.4013Z" stroke="white" stroke-width="5.3895"/> 4 + <circle cx="22.8102" cy="43.5838" r="2.81024" fill="white"/> 5 + <circle cx="73.3945" cy="43.5838" r="2.81024" fill="white"/> 6 + <rect x="35.7373" y="20.5399" width="3.37228" height="7.86866" rx="1.68614" fill="white"/> 7 + <rect x="45.8542" y="19" width="3.37228" height="7.86866" rx="1.68614" fill="white"/> 8 + <rect x="55.9709" y="20.5399" width="3.37228" height="7.86866" rx="1.68614" fill="white"/> 9 + </svg>
+3
routes/auth.js
··· 7 7 router.get("/login", (req, res) => { 8 8 res.render("auth/login", { 9 9 title: "Sign in - BlueRecipes", 10 + description: "Sign in to BlueRecipes with your Bluesky account.", 10 11 error: null, 11 12 }); 12 13 }); ··· 18 19 if (!handle || !appPassword) { 19 20 return res.render("auth/login", { 20 21 title: "Sign in - BlueRecipes", 22 + description: "Sign in to BlueRecipes with your Bluesky account.", 21 23 error: "Please provide both handle and app password", 22 24 }); 23 25 } ··· 27 29 if (!result.success) { 28 30 return res.render("auth/login", { 29 31 title: "Sign in - BlueRecipes", 32 + description: "Sign in to BlueRecipes with your Bluesky account.", 30 33 error: "Login failed. Check your handle and app password.", 31 34 }); 32 35 }
+4
routes/index.js
··· 15 15 16 16 res.render("index", { 17 17 title: "BlueRecipes - Discover Recipes", 18 + description: 19 + "Discover and share recipes on the open web. Powered by AT Protocol.", 18 20 recipes: recipes, 19 21 user: req.session.user || null, 20 22 }); ··· 22 24 console.error("Error loading recipes:", error); 23 25 res.render("index", { 24 26 title: "BlueRecipes - Discover Recipes", 27 + description: 28 + "Discover and share recipes on the open web. Powered by AT Protocol.", 25 29 recipes: [], 26 30 user: req.session.user || null, 27 31 });
+4
routes/recipe.js
··· 15 15 router.get("/new", requireAuth, (req, res) => { 16 16 res.render("recipe/new", { 17 17 title: "New Recipe - BlueRecipes", 18 + description: "Share your culinary creations on BlueRecipes.", 18 19 user: req.session.user, 19 20 error: null, 20 21 }); ··· 45 46 ) { 46 47 return res.render("recipe/new", { 47 48 title: "New Recipe - BlueRecipes", 49 + description: "Share your culinary creations on BlueRecipes.", 48 50 user: req.session.user, 49 51 error: "All fields except description are required", 50 52 }); ··· 66 68 if (!result.success) { 67 69 return res.render("recipe/new", { 68 70 title: "New Recipe - BlueRecipes", 71 + description: "Share your culinary creations on BlueRecipes.", 69 72 user: req.session.user, 70 73 error: "Failed to create recipe: " + result.error, 71 74 }); ··· 97 100 console.error("Error creating recipe:", error); 98 101 res.render("recipe/new", { 99 102 title: "New Recipe - BlueRecipes", 103 + description: "Share your culinary creations on BlueRecipes.", 100 104 user: req.session.user, 101 105 error: "Something went wrong. Please try again.", 102 106 });
+2 -5
views/auth/login.ejs
··· 1 1 <!DOCTYPE html> 2 2 <html lang="en"> 3 3 <head> 4 - <meta charset="UTF-8"> 5 - <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 - <title><%= title %></title> 7 - <link rel="stylesheet" href="/css/style.css"> 4 + <%- include('../partials/head') %> 8 5 </head> 9 6 <body> 10 7 <div class="form-center"> ··· 30 27 <button type="submit">Sign in</button> 31 28 </form> 32 29 33 - <p style="margin-top: 24px;"><a href="/">← Back</a></p> 30 + <p style="margin-top: 24px; text-align: center;"><a href="/">← Back to home</a></p> 34 31 </div> 35 32 </div> 36 33 </body>
+30 -57
views/index.ejs
··· 1 1 <!DOCTYPE html> 2 2 <html lang="en"> 3 3 <head> 4 - <meta charset="UTF-8" /> 5 - <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 - <title><%= title %></title> 7 - <link rel="stylesheet" href="/css/style.css" /> 4 + <%- include('partials/head') %> 8 5 </head> 9 6 <body> 10 7 <div class="container"> 11 - <aside class="sidebar"> 12 - <div class="sidebar-top"> 13 - <div class="logo"> 14 - <img 15 - src="/images/bluerecipes.svg" 16 - alt="Blue Recipes logo" 17 - /> 18 - </div> 19 - 20 - <nav> 21 - <ul> 22 - <li><a href="/" class="active">Discover</a></li> 23 - <% if (user) { %> 24 - <li><a href="/recipe/new">New Recipe</a></li> 25 - <li> 26 - <a href="/user/<%= user.handle %>" 27 - >My Recipes</a 28 - > 29 - </li> 30 - <li><a href="/auth/logout">Sign out</a></li> 31 - <% } else { %> 32 - <li><a href="/auth/login">Sign in</a></li> 33 - <% } %> 34 - </ul> 35 - </nav> 36 - 37 - <hr /> 8 + <%- include('partials/sidebar', { currentPage: 'discover' }) %> 38 9 39 - <nav> 40 - <ul> 41 - <li><a href="/?type=breakfast">Breakfast</a></li> 42 - <li><a href="/?type=lunch">Lunch</a></li> 43 - <li><a href="/?type=dinner">Dinner</a></li> 44 - <li><a href="/?type=snacks">Snacks</a></li> 45 - </ul> 46 - </nav> 10 + <main class="content"> 11 + <div class="content-header"> 12 + <h1>Your recipes, your way.</h1> 13 + <p class="subtitle"> 14 + Share culinary creations on the open web. Powered by AT 15 + Protocol. 16 + </p> 47 17 </div> 48 18 49 - <div class="sidebar-bottom"> 50 - <p>© 2026</p> 19 + <% if (recipes.length === 0) { %> 20 + <div class="empty-state"> 21 + <p>No recipes yet.</p> 22 + <% if (user) { %> 23 + <a href="/recipe/new">Share the first recipe →</a> 24 + <% } else { %> 25 + <a href="/auth/login">Sign in to share a recipe →</a> 26 + <% } %> 51 27 </div> 52 - </aside> 53 - 54 - <main class="content"> 55 - <h1>Your recipes, your way.</h1> 56 - <p class="subtitle"> 57 - Share culinary creations on the open web. Powered by AT 58 - Protocol. 59 - </p> 60 - 61 - <% if (recipes.length === 0) { %> 62 - <div class="empty-state">No recipes yet.</div> 63 28 <% } else { %> 64 29 <ul class="recipe-list"> 65 30 <% recipes.forEach(recipe => { %> 66 31 <li class="recipe-card"> 67 - <h2><%= recipe.title %></h2> 32 + <h2> 33 + <a href="/recipe/<%= recipe.id %>" 34 + ><%= recipe.title %></a 35 + > 36 + </h2> 68 37 <div class="recipe-meta"> 69 38 <span 70 - ><%= recipe.prep_time + recipe.cook_time 71 - %>min</span 39 + ><%= recipe.prep_time + recipe.cook_time %> 40 + min</span 72 41 > 73 - <span><%= recipe.servings %></span> 74 - <span><%= recipe.type %></span> 42 + <span><%= recipe.servings %> servings</span> 43 + <span class="recipe-type-badge" 44 + ><%= recipe.type %></span 45 + > 75 46 </div> 47 + <% if (recipe.description) { %> 76 48 <p><%= recipe.description %></p> 49 + <% } %> 77 50 </li> 78 51 <% }) %> 79 52 </ul>
+16
views/partials/head.ejs
··· 1 + <meta charset="UTF-8"> 2 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 3 + <meta name="description" content="<%= description %>"> 4 + <meta name="keywords" content="recipes, cooking, AT Protocol, decentralized, open web, bluesky"> 5 + <meta name="author" content="BlueRecipes"> 6 + <meta property="og:type" content="website"> 7 + <meta property="og:title" content="<%= title %>"> 8 + <meta property="og:description" content="<%= description %>"> 9 + <meta property="og:site_name" content="BlueRecipes"> 10 + <meta name="twitter:card" content="summary"> 11 + <meta name="twitter:title" content="<%= title %>"> 12 + <meta name="twitter:description" content="<%= description %>"> 13 + <link rel="icon" type="image/svg+xml" href="/images/favicon.svg"> 14 + <link rel="shortcut icon" href="/images/favicon.svg"> 15 + <title><%= title %></title> 16 + <link rel="stylesheet" href="/css/style.css">
+37
views/partials/sidebar.ejs
··· 1 + <aside class="sidebar"> 2 + <div class="sidebar-top"> 3 + <div class="logo"> 4 + <a href="/"> 5 + <img src="/images/logo.svg" alt="BlueRecipes"> 6 + </a> 7 + </div> 8 + 9 + <nav> 10 + <ul> 11 + <li><a href="/" class="<%= currentPage === 'discover' ? 'active' : '' %>">Discover</a></li> 12 + <% if (user) { %> 13 + <li><a href="/recipe/new" class="<%= currentPage === 'new-recipe' ? 'active' : '' %>">New Recipe</a></li> 14 + <li><a href="/user/<%= user.handle %>" class="<%= currentPage === 'my-recipes' ? 'active' : '' %>">My Recipes</a></li> 15 + <li><a href="/auth/logout">Sign out</a></li> 16 + <% } else { %> 17 + <li><a href="/auth/login">Sign in</a></li> 18 + <% } %> 19 + </ul> 20 + </nav> 21 + 22 + <hr> 23 + 24 + <nav> 25 + <ul> 26 + <li><a href="/?type=breakfast">Breakfast</a></li> 27 + <li><a href="/?type=lunch">Lunch</a></li> 28 + <li><a href="/?type=dinner">Dinner</a></li> 29 + <li><a href="/?type=snacks">Snacks</a></li> 30 + </ul> 31 + </nav> 32 + </div> 33 + 34 + <div class="sidebar-bottom"> 35 + <p>© 2026 BlueRecipes</p> 36 + </div> 37 + </aside>
+9 -40
views/recipe/new.ejs
··· 1 1 <!DOCTYPE html> 2 2 <html lang="en"> 3 3 <head> 4 - <meta charset="UTF-8"> 5 - <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 - <title><%= title %></title> 7 - <link rel="stylesheet" href="/css/style.css"> 4 + <%- include('../partials/head') %> 8 5 </head> 9 6 <body> 10 7 <div class="container"> 11 - <aside class="sidebar"> 12 - <div class="sidebar-top"> 13 - <div class="logo"> 14 - <img src="/images/bluerecipes.svg" alt="BlueRecipes"> 15 - </div> 8 + <%- include('../partials/sidebar', { currentPage: 'new-recipe' }) %> 16 9 17 - <nav> 18 - <ul> 19 - <li><a href="/">Discover</a></li> 20 - <li><a href="/recipe/new" class="active">New Recipe</a></li> 21 - <li><a href="/user/<%= user.handle %>">My Recipes</a></li> 22 - <li><a href="/auth/logout">Sign out</a></li> 23 - </ul> 24 - </nav> 25 - 26 - <hr> 27 - 28 - <nav> 29 - <ul> 30 - <li><a href="/?type=breakfast">Breakfast</a></li> 31 - <li><a href="/?type=lunch">Lunch</a></li> 32 - <li><a href="/?type=dinner">Dinner</a></li> 33 - <li><a href="/?type=snacks">Snacks</a></li> 34 - </ul> 35 - </nav> 10 + <main class="content"> 11 + <div class="content-header"> 12 + <h1>Share a recipe</h1> 13 + <p class="subtitle">Add your culinary creation to the open web.</p> 36 14 </div> 37 - 38 - <div class="sidebar-bottom"> 39 - <p>© 2026</p> 40 - </div> 41 - </aside> 42 - 43 - <main class="content"> 44 - <h1>Share a recipe</h1> 45 - <p class="subtitle">Add your culinary creation to the open web.</p> 46 15 47 16 <% if (error) { %> 48 17 <div class="error"><%= error %></div> ··· 72 41 </select> 73 42 </div> 74 43 75 - <div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 16px;"> 44 + <div class="form-grid"> 76 45 <div class="form-group"> 77 - <label for="prepTime">Prep Time (mins)</label> 46 + <label for="prepTime">Prep (mins)</label> 78 47 <input type="number" id="prepTime" name="prepTime" placeholder="15" min="0" required> 79 48 </div> 80 49 81 50 <div class="form-group"> 82 - <label for="cookTime">Cook Time (mins)</label> 51 + <label for="cookTime">Cook (mins)</label> 83 52 <input type="number" id="cookTime" name="cookTime" placeholder="30" min="0" required> 84 53 </div> 85 54