create-lacspace-app 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +10 -4
  2. package/dist/index.js +139 -6
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -36,11 +36,17 @@ It asks for a project name and a template, scaffolds the project, installs depen
36
36
 
37
37
  Every template is a real Next.js 15 App Router + Tailwind v4 project — dark, modern, responsive, with a gradient accent you can theme.
38
38
 
39
- ## Batteries already wired
39
+ ## A complete, polished project — not a blank page
40
40
 
41
- - **`lib/site.ts`** one [`@lacspace/seo`](https://www.npmjs.com/package/@lacspace/seo) `defineSite()` config drives your `<title>`, canonical, Open Graph, Twitter card and JSON-LD
42
- - **`next.config.mjs`** — hardened security headers via [`@lacspace/headers`](https://www.npmjs.com/package/@lacspace/headers)
43
- - **`app/robots.txt` + `app/sitemap.xml`**generated from your site config with [`@lacspace/robots`](https://www.npmjs.com/package/@lacspace/robots) & [`@lacspace/sitemap`](https://www.npmjs.com/package/@lacspace/sitemap)
41
+ Every generated app arrives finished, with the boring-but-essential things done:
42
+
43
+ - 🎨 **Beautiful home page** Tailwind v4, the Inter font, dark theme + a gradient accent
44
+ - 🔎 **SEO** — one [`@lacspace/seo`](https://www.npmjs.com/package/@lacspace/seo) `defineSite()` config drives your `<title>`, canonical, Open Graph, Twitter card and JSON-LD
45
+ - ✨ **Dynamic OG images** — every page auto-generates a social-share card at `/og` (no design tool)
46
+ - 🛡️ **Security headers** — HSTS, CSP, X-Frame-Options and more via [`@lacspace/headers`](https://www.npmjs.com/package/@lacspace/headers)
47
+ - 🗺️ **robots.txt + sitemap.xml** — generated from your config with [`@lacspace/robots`](https://www.npmjs.com/package/@lacspace/robots) & [`@lacspace/sitemap`](https://www.npmjs.com/package/@lacspace/sitemap)
48
+ - 🧩 **A styled 404, a PWA manifest, and `.env.example`** — the finishing touches most starters skip
49
+ - 🎁 **A `WELCOME.md`** that walks you through everything and shows the bonus packages you can drop in
44
50
 
45
51
  Edit `lib/site.ts` and `app/page.tsx` and you're off.
46
52
 
package/dist/index.js CHANGED
@@ -100,38 +100,59 @@ var globalsCss = (ctx) => `@import "tailwindcss";
100
100
  :root {
101
101
  --accent-from: ${ctx.template.accent[0]};
102
102
  --accent-to: ${ctx.template.accent[1]};
103
+ --bg: #0a0a0f;
104
+ --fg: #e5e7eb;
103
105
  }
104
106
 
107
+ * { border-color: rgb(255 255 255 / 0.08); }
105
108
  html { scroll-behavior: smooth; }
106
- body { background: #0a0a0f; color: #e5e7eb; -webkit-font-smoothing: antialiased; }
109
+ body {
110
+ background: var(--bg);
111
+ color: var(--fg);
112
+ -webkit-font-smoothing: antialiased;
113
+ text-rendering: optimizeLegibility;
114
+ }
115
+
116
+ ::selection { background: var(--accent-to); color: #0a0a0f; }
117
+ ::-webkit-scrollbar { width: 10px; height: 10px; }
118
+ ::-webkit-scrollbar-thumb { background: rgb(255 255 255 / 0.12); border-radius: 8px; }
119
+ :focus-visible { outline: 2px solid var(--accent-to); outline-offset: 2px; border-radius: 4px; }
107
120
 
108
121
  .gradient-text {
109
122
  background: linear-gradient(120deg, var(--accent-from), var(--accent-to));
110
123
  -webkit-background-clip: text; background-clip: text; color: transparent;
111
124
  }
112
125
  .gradient-bg { background: linear-gradient(120deg, var(--accent-from), var(--accent-to)); }
126
+
127
+ /* soft entrance for content */
128
+ @keyframes rise { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
129
+ main > section, main > * { animation: rise 0.6s cubic-bezier(0.22, 1, 0.36, 1) both; }
130
+ @media (prefers-reduced-motion: reduce) { *, ::before, ::after { animation: none !important; scroll-behavior: auto; } }
113
131
  `;
114
132
  var siteTs = (ctx) => `import { defineSite } from "@lacspace/seo";
115
133
 
116
134
  /** Your site's SEO configuration \u2014 set once, used everywhere. */
117
135
  export const site = defineSite({
118
136
  name: ${JSON.stringify(ctx.template.siteName)},
119
- url: "https://example.com",
137
+ url: process.env.NEXT_PUBLIC_SITE_URL ?? "https://example.com",
120
138
  description: ${JSON.stringify(ctx.template.siteDescription)},
121
139
  // twitter: "yourhandle",
122
- // ogImage: "/og",
140
+ ogImage: "/og", // \u2728 auto social-share images \u2014 see app/og/route.tsx
123
141
  });
124
142
  `;
125
143
  var layout = (ctx) => `import type { Metadata } from "next";
144
+ import { Inter } from "next/font/google";
126
145
  import { site } from "@/lib/site";
127
146
  import "./globals.css";
128
147
 
148
+ const inter = Inter({ subsets: ["latin"], display: "swap" });
149
+
129
150
  export const metadata: Metadata = site.meta({ title: ${JSON.stringify(ctx.template.siteName)} });
130
151
 
131
152
  export default function RootLayout({ children }: { children: React.ReactNode }) {
132
153
  return (
133
- <html lang="en">
134
- <body>
154
+ <html lang="en" className={inter.className}>
155
+ <body className="antialiased">
135
156
  {children}
136
157
  <script
137
158
  type="application/ld+json"
@@ -142,6 +163,113 @@ export default function RootLayout({ children }: { children: React.ReactNode })
142
163
  );
143
164
  }
144
165
  `;
166
+ var ogRoute = (ctx) => `import { ImageResponse } from "next/og";
167
+ import { site } from "@/lib/site";
168
+
169
+ // \u2728 Dynamic Open Graph images \u2014 every page gets a gorgeous social card,
170
+ // generated on the fly at /og?title=Your+Page+Title. No design tool needed.
171
+ export const runtime = "edge";
172
+
173
+ export function GET(req: Request) {
174
+ const title = new URL(req.url).searchParams.get("title") ?? site.config.name;
175
+ return new ImageResponse(
176
+ (
177
+ <div
178
+ style={{
179
+ width: "100%", height: "100%", display: "flex", flexDirection: "column",
180
+ justifyContent: "center", padding: "90px",
181
+ background: "linear-gradient(135deg, ${ctx.template.accent[0]}, ${ctx.template.accent[1]})",
182
+ color: "white", fontFamily: "sans-serif",
183
+ }}
184
+ >
185
+ <div style={{ fontSize: 72, fontWeight: 800, lineHeight: 1.05, letterSpacing: "-0.02em" }}>{title}</div>
186
+ <div style={{ marginTop: 28, fontSize: 34, opacity: 0.85 }}>{site.config.name}</div>
187
+ </div>
188
+ ),
189
+ { width: 1200, height: 630 },
190
+ );
191
+ }
192
+ `;
193
+ var notFound = () => `import Link from "next/link";
194
+
195
+ export default function NotFound() {
196
+ return (
197
+ <main className="flex min-h-screen flex-col items-center justify-center gap-6 px-6 text-center">
198
+ <div className="text-8xl font-black gradient-text">404</div>
199
+ <p className="text-lg text-white/60">This page wandered off.</p>
200
+ <Link href="/" className="gradient-bg rounded-full px-6 py-3 font-semibold text-black">Back home</Link>
201
+ </main>
202
+ );
203
+ }
204
+ `;
205
+ var manifestTs = (ctx) => `import type { MetadataRoute } from "next";
206
+ import { site } from "@/lib/site";
207
+
208
+ export default function manifest(): MetadataRoute.Manifest {
209
+ return {
210
+ name: site.config.name,
211
+ short_name: site.config.name,
212
+ description: site.config.description,
213
+ start_url: "/",
214
+ display: "standalone",
215
+ background_color: "#0a0a0f",
216
+ theme_color: "${ctx.template.accent[0]}",
217
+ };
218
+ }
219
+ `;
220
+ var envExample = () => `# Your production URL \u2014 powers canonical URLs, sitemap, robots and OG images.
221
+ NEXT_PUBLIC_SITE_URL=https://example.com
222
+ `;
223
+ var welcomeMd = (ctx) => `# \u{1F381} Welcome to ${ctx.name}
224
+
225
+ You didn't get a blank page \u2014 you got a running, good-looking **${ctx.template.label}** with the
226
+ boring-but-essential stuff already done. Here's what's in the box.
227
+
228
+ ## \u2705 Already set up for you
229
+
230
+ - **Beautiful home page** \u2014 styled with Tailwind v4, Inter font, dark theme + gradient accent.
231
+ - **SEO** \u2014 metadata, Open Graph, Twitter cards & JSON-LD, all from one file (\`lib/site.ts\`).
232
+ - **\u2728 Dynamic OG images** \u2014 every page auto-generates a social-share card at \`/og\`. Share a link and see.
233
+ - **Security headers** \u2014 HSTS, CSP, X-Frame-Options and more, via \`next.config.mjs\`.
234
+ - **robots.txt + sitemap.xml** \u2014 generated from your site config. No hand-editing.
235
+ - **A styled 404** and a **PWA manifest** \u2014 the finishing touches most starters skip.
236
+
237
+ ## \u{1F680} Run it
238
+
239
+ \`\`\`bash
240
+ npm run dev # http://localhost:3000
241
+ \`\`\`
242
+
243
+ ## \u{1F3A8} Make it yours (start here)
244
+
245
+ 1. Edit **\`lib/site.ts\`** \u2014 your name, URL and description flow into SEO, sitemap, robots and OG.
246
+ 2. Edit **\`app/page.tsx\`** \u2014 your home page.
247
+ 3. Set **\`NEXT_PUBLIC_SITE_URL\`** in \`.env\` before deploying (copy \`.env.example\`).
248
+
249
+ ## \u{1F389} Surprise: 49 more Lacspace packages, one install away
250
+
251
+ Your app is wired for the whole ecosystem. Drop any of these in \u2014 all zero-dependency:
252
+
253
+ \`\`\`bash
254
+ npm i @lacspace/id # uuidv7, nanoid, short ids
255
+ npm i @lacspace/pdf # invoices & receipts, no headless browser
256
+ npm i @lacspace/signed-url # magic-login & expiring download links
257
+ npm i @lacspace/webhooks # sign / verify / deliver webhooks
258
+ npm i @lacspace/flags # feature flags & A/B, no SaaS
259
+ npm i @lacspace/humanize # "1.5 KB", "3 hours ago", "1.2M"
260
+ \`\`\`
261
+
262
+ Browse them all \u2192 **https://lacspace.com/packages**
263
+
264
+ ## \u2601\uFE0F Deploy
265
+
266
+ Push to GitHub and import on **[Vercel](https://vercel.com/new)** \u2014 it just works. Remember to set
267
+ \`NEXT_PUBLIC_SITE_URL\` to your real domain.
268
+
269
+ ---
270
+
271
+ Built with \u2764\uFE0F using [Lacspace](https://lacspace.com/packages). This app is yours under the Lacspace Free Licence.
272
+ `;
145
273
  var robotsTs = () => `import { robotsForSite } from "@lacspace/robots";
146
274
  import { site } from "@/lib/site";
147
275
 
@@ -450,8 +578,13 @@ function buildFiles(ctx) {
450
578
  "app/layout.tsx": layout(ctx),
451
579
  "app/globals.css": globalsCss(ctx),
452
580
  "app/page.tsx": homePage(ctx),
581
+ "app/not-found.tsx": notFound(),
582
+ "app/og/route.tsx": ogRoute(ctx),
583
+ "app/manifest.ts": manifestTs(ctx),
453
584
  "app/robots.txt/route.ts": robotsTs(),
454
- "app/sitemap.xml/route.ts": sitemapTs()
585
+ "app/sitemap.xml/route.ts": sitemapTs(),
586
+ ".env.example": envExample(),
587
+ "WELCOME.md": welcomeMd(ctx)
455
588
  };
456
589
  }
457
590
  function parseArgs(list) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-lacspace-app",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Scaffold a beautiful, production-ready Next.js app from a Lacspace template — portfolio, business, e-commerce, SaaS, blog, docs, dashboard or restaurant — pre-wired with SEO, security headers, sitemap and robots. Like create-next-app, but you start gorgeous.",
5
5
  "type": "module",
6
6
  "bin": {