create-bascik 1.0.0-rc.1

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.
package/dist/cli.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ export interface CliOptions {
2
+ yesFlag: boolean;
3
+ noDevFlag: boolean;
4
+ }
5
+ export declare function parseCliOptions(args: string[]): CliOptions;
package/dist/cli.js ADDED
@@ -0,0 +1,7 @@
1
+ export function parseCliOptions(args) {
2
+ return {
3
+ yesFlag: args.includes("-y") || args.includes("--yes"),
4
+ noDevFlag: args.includes("--no-dev"),
5
+ };
6
+ }
7
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,eAAe,CAAC,IAAc;IAC5C,OAAO;QACL,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;QACtD,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC;KACrC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+ import { createInterface } from "node:readline/promises";
3
+ import { spawnSync } from "node:child_process";
4
+ import { join } from "node:path";
5
+ import { parseCliOptions } from "./cli.js";
6
+ import { scaffold, validateProjectName } from "./scaffold.js";
7
+ const args = process.argv.slice(2);
8
+ const { yesFlag, noDevFlag } = parseCliOptions(args);
9
+ const nameArg = args.find((a) => !a.startsWith("-"));
10
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
11
+ let projectName;
12
+ if (nameArg) {
13
+ projectName = nameArg;
14
+ }
15
+ else {
16
+ const answer = await rl.question("Project name (bascik-app): ");
17
+ projectName = answer.trim() || "bascik-app";
18
+ }
19
+ const error = validateProjectName(projectName);
20
+ if (error) {
21
+ rl.close();
22
+ console.error(`\nError: ${error}\n`);
23
+ process.exit(1);
24
+ }
25
+ console.log(`\nCreating Bascik project "${projectName}"…\n`);
26
+ await scaffold(projectName);
27
+ console.log(`✓ Scaffolded ${projectName}/\n`);
28
+ let shouldInstall;
29
+ let shouldDev;
30
+ if (yesFlag) {
31
+ shouldInstall = true;
32
+ shouldDev = !noDevFlag;
33
+ }
34
+ else {
35
+ const installAnswer = await rl.question("Install dependencies now? (Y/n) ");
36
+ shouldInstall = installAnswer.trim().toLowerCase() !== "n";
37
+ const devAnswer = await rl.question("Start the dev server after install? (Y/n) ");
38
+ shouldDev = devAnswer.trim().toLowerCase() !== "n";
39
+ }
40
+ rl.close();
41
+ const projectDir = join(process.cwd(), projectName);
42
+ const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
43
+ if (shouldInstall) {
44
+ console.log("\nInstalling dependencies…\n");
45
+ spawnSync(npmCmd, ["install"], { cwd: projectDir, stdio: "inherit" });
46
+ }
47
+ if (shouldDev) {
48
+ console.log("\nStarting dev server…\n");
49
+ spawnSync(npmCmd, ["run", "dev"], { cwd: projectDir, stdio: "inherit" });
50
+ console.log(`\nTo start again: cd ${projectName} && npm run dev\n`);
51
+ }
52
+ else {
53
+ console.log("\nNext steps:\n");
54
+ if (!shouldInstall)
55
+ console.log(` cd ${projectName}`);
56
+ if (!shouldInstall)
57
+ console.log(" npm install");
58
+ console.log(" npm run dev\n");
59
+ }
60
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * @module scaffold
3
+ *
4
+ * Core file-creation logic for create-bascik.
5
+ * Separated from the CLI entry so it can be unit-tested without I/O.
6
+ */
7
+ export declare const PACKAGE_JSON: (name: string) => string;
8
+ export declare const VSCODE_EXTENSIONS_JSON: string;
9
+ export declare const VSCODE_LAUNCH_JSON: string;
10
+ export declare const VITE_CONFIG = "import { defineConfig } from 'vite';\n\nexport default defineConfig({\n test: {\n include: ['src/**/*.test.ts'],\n coverage: {\n provider: 'v8',\n reporter: ['text', 'json', 'html'],\n },\n },\n});\n";
11
+ export declare const PLAYWRIGHT_CONFIG = "import { defineConfig } from '@playwright/test';\n\nexport default defineConfig({\n testDir: './',\n use: {\n baseURL: 'http://localhost:8080',\n headless: true,\n },\n webServer: {\n command: 'npx bascik --build && npx bascik --server --port 8080',\n url: 'http://localhost:8080',\n reuseExistingServer: !process.env.CI,\n },\n});\n";
12
+ export declare const E2E_APP_SPEC = "import { test, expect } from '@playwright/test';\n\ntest.describe('Starter App E2E Tests', () => {\n test('navigates pages and checks titles', async ({ page }) => {\n await page.goto('/');\n await expect(page).toHaveTitle(/Home/);\n\n await page.getByRole('link', { name: 'About' }).first().click();\n await expect(page).toHaveURL('/about');\n await expect(page).toHaveTitle(/About/);\n });\n\n test('counter component increments and decrements count', async ({ page }) => {\n await page.goto('/');\n\n const val = page.getByTestId('counter-val').first();\n const decBtn = page.getByTestId('counter-dec').first();\n const incBtn = page.getByTestId('counter-inc').first();\n\n await expect(val).toHaveText('0');\n\n await incBtn.click();\n await expect(val).toHaveText('1');\n\n await decBtn.click();\n await expect(val).toHaveText('0');\n });\n\n test('mobile navigation toggle expands and collapses menu', async ({ page }) => {\n await page.setViewportSize({ width: 375, height: 667 });\n await page.goto('/');\n\n const toggle = page.getByTestId('nav-toggle').first();\n const menu = page.getByTestId('nav-menu').first();\n\n await expect(toggle).toHaveAttribute('aria-expanded', 'false');\n\n await toggle.click();\n await expect(toggle).toHaveAttribute('aria-expanded', 'true');\n await expect(menu).toHaveClass(/is-open/);\n\n await toggle.click();\n await expect(toggle).toHaveAttribute('aria-expanded', 'false');\n });\n});\n";
13
+ export declare const GITIGNORE = "node_modules/\ndist/\ncoverage/\n*.pem\n";
14
+ export declare const STYLES_CSS = "/* \u2500\u2500 Design system \u2500\u2500 */\n*, *::before, *::after { box-sizing: border-box; }\n\n:root {\n --bg: #18191b;\n --surface: #1e2022;\n --elevated: #242628;\n --border: rgba(255,255,255,0.07);\n --border-hover: rgba(211,255,141,0.35);\n --accent: #d3ff8d;\n --accent-dim: rgba(211,255,141,0.12);\n --text: #f0f1f2;\n --text-muted: #8d929e;\n --mono: 'SF Mono','Fira Code',Menlo,monospace;\n --r: 10px;\n --r-sm: 6px;\n}\n\nhtml { scroll-behavior: smooth; }\n\nbody {\n background: var(--bg);\n color: var(--text);\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;\n font-size: 16px;\n line-height: 1.7;\n margin: 0;\n min-height: 100vh;\n display: flex;\n flex-direction: column;\n}\n\nmain { flex: 1; }\n\n/* \u2500\u2500 Layout \u2500\u2500 */\n.container { max-width: 1140px; margin: 0 auto; padding: 0 28px; }\n.section { padding: 72px 0; }\n.section + .section { border-top: 1px solid var(--border); }\n\n/* \u2500\u2500 Typography \u2500\u2500 */\nh1, h2, h3, h4 { margin-top: 0; line-height: 1.25; font-weight: 700; }\nh1 { font-size: clamp(2rem, 5vw, 3.25rem); margin-bottom: 16px; }\nh2 { font-size: clamp(1.4rem, 3vw, 2rem); margin-bottom: 12px; }\nh3 { font-size: 1.05rem; margin-bottom: 6px; }\np { margin-top: 0; color: var(--text-muted); }\n\na { color: var(--accent); text-decoration: none; transition: opacity .15s; }\na:hover { opacity: 0.8; }\np a, li a { text-decoration: underline; text-underline-offset: 3px; }\n\ncode {\n font-family: var(--mono);\n font-size: 0.84em;\n background: var(--elevated);\n border: 1px solid var(--border);\n border-radius: var(--r-sm);\n padding: 2px 7px;\n color: var(--accent);\n}\n\n/* \u2500\u2500 Buttons \u2500\u2500 */\n.btn {\n display: inline-flex; align-items: center; gap: 8px;\n padding: 10px 22px; border: none; border-radius: var(--r-sm);\n font-size: 0.9rem; font-weight: 600; cursor: pointer;\n text-decoration: none; transition: filter .15s, opacity .15s; line-height: 1;\n}\n.btn-primary { background: var(--accent); color: #18191b; }\n.btn-ghost { background: transparent; color: var(--text); border: 1px solid var(--border); }\n@media (hover: hover) {\n .btn-primary:hover { filter: brightness(0.88); opacity: 1; }\n .btn-ghost:hover { border-color: var(--accent); color: var(--accent); opacity: 1; }\n}\n\n/* \u2500\u2500 Cards \u2500\u2500 */\n.card {\n background: var(--elevated); border: 1px solid var(--border);\n border-radius: var(--r); padding: 24px;\n transition: border-color .2s, box-shadow .2s;\n}\n.card:hover { border-color: var(--border-hover); box-shadow: 0 0 0 1px var(--accent-dim); }\n.card h3 { margin-top: 0; }\n.card p { margin-bottom: 0; }\n\n/* \u2500\u2500 Grid \u2500\u2500 */\n.grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; }\n.grid-3 { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 20px; }\n\n/* \u2500\u2500 Callout \u2500\u2500 */\n.callout {\n background: var(--accent-dim); border: 1px solid rgba(211,255,141,0.22);\n border-radius: var(--r); padding: 16px 20px; margin: 20px 0;\n}\n.callout p { color: var(--text); margin: 0; }\n\n/* \u2500\u2500 Form \u2500\u2500 */\n.form-group { margin-bottom: 20px; }\n.form-group label {\n display: block; font-size: 0.88rem; font-weight: 600;\n color: var(--text); margin-bottom: 6px;\n}\n.form-group input,\n.form-group textarea {\n width: 100%; background: var(--elevated); border: 1px solid var(--border);\n border-radius: var(--r-sm); color: var(--text); font-family: inherit;\n font-size: 0.9rem; padding: 10px 14px; transition: border-color .15s; outline: none;\n}\n.form-group input:focus,\n.form-group textarea:focus { border-color: var(--accent); }\n.form-group textarea { min-height: 120px; resize: vertical; }\n\n/* \u2500\u2500 Hero \u2500\u2500 */\n.hero { padding: 96px 0 72px; text-align: center; }\n.hero h1 span { color: var(--accent); }\n.hero > .container > p { font-size: 1.1rem; max-width: 520px; margin: 0 auto 36px; }\n.hero-actions { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }\n\n/* \u2500\u2500 Utilities \u2500\u2500 */\n.text-accent { color: var(--accent); }\n.text-muted { color: var(--text-muted); }\n.section-label {\n font-size: 0.72rem; font-weight: 700; text-transform: uppercase;\n letter-spacing: .1em; color: var(--accent); margin-bottom: 8px;\n}\n.page-intro { font-size: 1.05rem; color: var(--text-muted); max-width: 600px; margin-bottom: 40px; }\n\n/* \u2500\u2500 Responsive \u2500\u2500 */\n@media (max-width: 640px) {\n .section { padding: 48px 0; }\n .hero { padding: 64px 0 48px; }\n}\n";
15
+ export declare const FAVICON_SVG = "<svg viewBox=\"0 0 32 32\" width=\"32\" height=\"32\" xmlns=\"http://www.w3.org/2000/svg\">\n <polygon points=\"9,2 30,2 23,30 2,30\" fill=\"#d3ff8d\" />\n <rect x=\"15\" y=\"9\" width=\"2\" height=\"14\" rx=\"1\" fill=\"#0e0f10\">\n <animate attributeName=\"opacity\" values=\"0.9;0.9;0;0\" keyTimes=\"0;0.49;0.5;1\" dur=\"1.1s\" repeatCount=\"indefinite\" />\n </rect>\n</svg>\n";
16
+ export declare const FAVICON_ICO_B64 = "AAABAAMAEBAAAAEAIADIAAAANgAAACAgAAABACAACAEAAP4AAAAwMAAAAQAgAGcBAAAGAgAiVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAeklEQVQ4jWNgoDa4/L93/uX/vf8J4577OAzouU+cAb3zMTSf/9+vQKTm/xf/9yRgGHDxf08CsqINV8vBGJsBIMsI+r+g3Q+MyfZ/AU4DiPR/AQ4DiPL/ZTwGEOX/yzgNICH+C7C7gPj434AlGon2/2UcmGj/0y/9UwoAAEX+8tkvEHwAAAAASUVORK5CYIKJUE5HDQoaCgAAAA1JSERSAAAAIAAAACAIBgAAAHN6evQAAAAJcEhZcwAACxMAAAsTAQCanBgAAAC6SURBVFiF7ZbRDYMwDEQzwcEIDFC6QJXf0jEYKBEjMBJCGYBRXNEFepZshaqx5O+8+HQ+h9DqS22yDEWy2HY6Alu7pNkeIK80QJG8WgOcn1IApMMa4JSVenxr+svF9X88bxKnsZ7+6PpPV/M/1ADG/ocSwNz/UAKY+x8qAIf9D90E7Pc/FAAu+x8KAJf9DxrAKf/BT8An/0ECuOU/SAC3/I/TKPF1r6N/4bvdf4OL/oXqpn/6kfv/r+sNLlrC+A4T1ksAAAAASUVORK5CYIKJUE5HDQoaCgAAAA1JSERSAAAAMAAAADAIBgAAAFcC+YcAAAAJcEhZcwAACxMAAAsTAQCanBgAAAEZSURBVGiB7ZjBDcJADARTAVBCKKKGSwfpgo+PlHDfQDOWSQGpBIwuD/7EVpYIr2Qpr+hGXln2Nk0otL0mzWXSrJii2QGAZhxALqbHsw4t8PEqSskEUH+ABGAdWhNA+F/D/+sV/teY/9vNf3mS0piWerxof/OfxqSH42mpfOv2N//7y/kDUL935//eFSBvv/94Aghi//EEYMT+4wdAmP3fsQMFsv97AQhq//cCYNT+7wNAuPvXqQMFdv96AAjy/vUAYOT9awcgAANi40iAAAn3/00EAB2U7f56m/2eQf30E14yR/0AAAABJRU5ErkJggg==";
17
+ export declare const FAVICON_32_PNG_B64 = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAuklEQVRYhe2W0Q2DMAxEM8HBCAxQukCV39IxGCgRIzASQhmAUVzRBXqWbIWqseTvvPh0PofQ6kttsgxFsth2OgJbu6TZHiCvNECRvFoDnJ9SAKTDGuCUlXp8a/rLxfV/PG8Sp7Ge/uj6T1fzP9QAxv6HEsDc/1ACmPsfKgCH/Q/dBOz3PxQALvsfCgCX/Q8awCn/wU/AJ/9BArjlP0gAt/yP0yjxda+jf+G73X+Di/6F6qZ/+pH7/6/rDS5awvgOE9ZLAAAAAElFTkSuQmCC";
18
+ export declare const APPLE_TOUCH_ICON_PNG_B64 = "iVBORw0KGgoAAAANSUhEUgAAALQAAAC0CAYAAAA9zQYyAAAACXBIWXMAAAsTAAALEwEAmpwYAAAGKUlEQVR4nO2bzWqdVRSGN72AtqO0WicWJ1KtoTi0dGjTK8gFGGtnH1ghJpXOw1l0kHoTUvlIU6rQa9Dg2R9W8OcK2guwky0nkIGU0kTfsvZZ63nhnWWwz7ufnTwcSCmEEEIIIYQQQgghhBBCCCGEEEII6SG12Xptsyd0Fm2DrZIxtdlYmzVq0TYYSra0dvfUvM2edTA+bdoNpnZvtWTL4kMDU7zHNG+z54tfViVbFn+WvMen9iY2GEvG4M9hH9RQsgV/jtsJf/a/BGqSDfBnYGrBNsCfO7gE2mQb4M8AZWE2wJ87uARqkg3wZ2CK9pjGkjF8/xy2Q8kWvn+O24nvn/0vgZpkA/wZmKI9prFkDP4ctkPJFvw5bif82f8SqEk2wJ+BKdpjGkvG4M9hO5RswZ/jdsKf/S+BmmQD/BmYoj2msWQM/hy2Q8kW/DluJ/zZ/xKoSTbAn4Ep2mMaS8bgz2E7lGzBn+N2wp/9L4GaZAP8GZiiPaaxZAz+HLZDyRb8OW4n/Nn/EqhJNsCfgSnaYxpLxuDPYTuUbMGf43bCn5evj3/farsPNw77+I9t9/PUToo/L1n3ft1sn3z6YTt95uy/evX65bb3dNP9fNW/+POy9Lufvmzn3l55Ceajnr+w0h4c3HY/Z/Ut/rwM/fnvnfb+6ruvhPmol65cbAcvdtzPW52KPy9Jd/c+ey3MR72/v+F+3upQ/HmJenN77dhAf3Fnzf281af487J0/da1YwO9+Fnv81af4s8dXAJAN81G+HMHoAK0SfbBnzuAFKBNuRH+7A0pQJtyI/zZG1KANtk++HMHkAK0SbbBnzsAFKBNuQ/+7A0oQJtyH/zZG1CANtk2+HMHgAK0SXbBnzuAE6BNuQ3+7A0nQJtyG/zZG04ANtku+HMHcAK0STbBnzsAE6BNuQv+7A0mQJtyF/zZG0yANtkm+HMHYAK0SfbAnzuAEqBNuQn+7A0lQJtyE/zZG0qANtke+HMHUAK0SbbAnzsAEgBNuQf+7A0kQJtyD/zZG0iANtkW+HMHQAK0SXbAnzuAEaBNuQX+7A0jQJtyC/zZG0aANtkO+HMHMAK0STbAnzsAEQBNuQP+7A0iQJtyB/zZG0SANtkG+HMHIAK0ZgP8uQMIAdqUG+DP3hACtCk3wJ+9IQRok22AP3cAIUCbZAP8uQMAAdqUG+DP3gACtCk3wJ+9AQRok22AP3cAIECbZAP8uQP4ANqUG+DP3vABtCk3wJ+94QNok22AP3cAH0CbZAP8uQPwANqUG+DP3uABtCk3wJ+9wQNok22AP3cAHkCbZAP8uQPoANqUG+DP3tABtCk3wJ+9oQNok22AP3cAHUCbZAP8uQPgANqUG+DP3sABtCk3wJ+9gQNok22AP3cAHECbZAP8uQPYANqUG+DP3rABtCk3wJ+9YQNok22AP3cAG0CbZAP8uQPQANqUG+DP3qABtCk3wJ+9QQNok22AP3cAGkCbZAP8uQPIANqUG+DPmXpze62dPnP2WL31zQ3389aTF3/O1N2HG8cG+ttHn7uft56w+HOyHrzYaZeuXHwtzB98/N7hz3qft56g+HPSPji43c5fWHklzG+9c659/8tX7uesJy/+nLV7Tzfb1euXX4L52o2P2v5vX7ufr/634s/Z+8Of2+3+/sZhf/zrjvt56v8o/tzBJVCTbIA/A1O0xzSWjFl88A7Gp02+Af4MWHEe1tTurZZsWXxo7+GpyTfAnwEr2sMaS8bgz2E7lGxp7e6peZs962B82rQb4M9AFeZRzdvs+eKXVcmWxZ8l7/GpvYnW/d2iwC0CBAgAIAAAAAAAAgAAAAAAQIECBAgAIAAAAAAAAgAAAAAAQIECBAgAIAAAAAAAAgAAAAAAQIECBAgAIAAAAAAAAgAAAAAAQIEC";
19
+ /** Head component — charset, viewport, favicon, CSS link. */
20
+ export declare const SITE_META_HTML = "<meta charset=\"UTF-8\" />\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n<meta name=\"description\" content=\"A fast, modern website built with Bascik.\" />\n<link rel=\"icon\" href=\"/favicon.ico\" sizes=\"any\" />\n<link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/assets/favicon-32x32.png\" />\n<link rel=\"icon\" type=\"image/svg+xml\" href=\"/assets/favicon.svg\" />\n<link rel=\"apple-touch-icon\" href=\"/assets/apple-touch-icon.png\" />\n<link rel=\"stylesheet\" href=\"/css/styles.css\" />\n";
21
+ export declare const SITE_HEADER_CSS = ".header {\n position: sticky;\n top: 0;\n z-index: 100;\n background: rgba(24, 25, 27, 0.9);\n backdrop-filter: blur(12px);\n -webkit-backdrop-filter: blur(12px);\n border-bottom: 1px solid var(--border);\n}\n\n.container { max-width: 1140px; margin: 0 auto; padding: 0 28px; }\n\n.nav {\n display: flex;\n align-items: center;\n height: 60px;\n gap: 24px;\n}\n\n.logo {\n font-weight: 700;\n font-size: 1rem;\n color: var(--text);\n letter-spacing: -0.01em;\n flex-shrink: 0;\n}\n.logo:hover { opacity: 0.8; }\n\n.nav-menu {\n list-style: none;\n margin: 0 0 0 auto;\n padding: 0;\n display: flex;\n gap: 4px;\n}\n.nav-menu a {\n display: block;\n padding: 6px 12px;\n font-size: 0.9rem;\n color: var(--text-muted);\n border-radius: var(--r-sm);\n transition: color .15s, background .15s;\n}\n.nav-menu a:hover { color: var(--text); background: var(--elevated); opacity: 1; }\n\n.nav-toggle {\n display: none;\n flex-direction: column;\n justify-content: center;\n gap: 5px;\n width: 36px;\n height: 36px;\n background: none;\n border: none;\n cursor: pointer;\n padding: 6px;\n margin-left: auto;\n border-radius: var(--r-sm);\n}\n.nav-toggle span {\n display: block;\n height: 2px;\n background: var(--text);\n border-radius: 2px;\n}\n\n@media (max-width: 640px) {\n .nav-toggle { display: flex; }\n\n .nav-menu {\n display: none;\n position: absolute;\n top: 60px;\n left: 0;\n right: 0;\n flex-direction: column;\n gap: 0;\n background: var(--surface);\n border-bottom: 1px solid var(--border);\n padding: 8px 16px;\n }\n .nav-menu.is-open { display: flex; }\n .nav-menu li { width: 100%; }\n .nav-menu a { padding: 10px 4px; }\n}\n";
22
+ /** Sticky top nav with logo (brand prop) and mobile hamburger. */
23
+ export declare const SITE_HEADER_HTML = "<style>\n.header {\n position: sticky;\n top: 0;\n z-index: 100;\n background: rgba(24, 25, 27, 0.9);\n backdrop-filter: blur(12px);\n -webkit-backdrop-filter: blur(12px);\n border-bottom: 1px solid var(--border);\n}\n\n.container { max-width: 1140px; margin: 0 auto; padding: 0 28px; }\n\n.nav {\n display: flex;\n align-items: center;\n height: 60px;\n gap: 24px;\n}\n\n.logo {\n font-weight: 700;\n font-size: 1rem;\n color: var(--text);\n letter-spacing: -0.01em;\n flex-shrink: 0;\n}\n.logo:hover { opacity: 0.8; }\n\n.nav-menu {\n list-style: none;\n margin: 0 0 0 auto;\n padding: 0;\n display: flex;\n gap: 4px;\n}\n.nav-menu a {\n display: block;\n padding: 6px 12px;\n font-size: 0.9rem;\n color: var(--text-muted);\n border-radius: var(--r-sm);\n transition: color .15s, background .15s;\n}\n.nav-menu a:hover { color: var(--text); background: var(--elevated); opacity: 1; }\n\n.nav-toggle {\n display: none;\n flex-direction: column;\n justify-content: center;\n gap: 5px;\n width: 36px;\n height: 36px;\n background: none;\n border: none;\n cursor: pointer;\n padding: 6px;\n margin-left: auto;\n border-radius: var(--r-sm);\n}\n.nav-toggle span {\n display: block;\n height: 2px;\n background: var(--text);\n border-radius: 2px;\n}\n\n@media (max-width: 640px) {\n .nav-toggle { display: flex; }\n\n .nav-menu {\n display: none;\n position: absolute;\n top: 60px;\n left: 0;\n right: 0;\n flex-direction: column;\n gap: 0;\n background: var(--surface);\n border-bottom: 1px solid var(--border);\n padding: 8px 16px;\n }\n .nav-menu.is-open { display: flex; }\n .nav-menu li { width: 100%; }\n .nav-menu a { padding: 10px 4px; }\n}\n</style>\n<header class=\"header\">\n <div class=\"container\">\n <nav class=\"nav\">\n <a href=\"/\" class=\"logo\" data-bascik-prop-brand>My Site</a>\n <button id=\"nav-toggle\" class=\"nav-toggle\" aria-label=\"Toggle menu\" aria-expanded=\"false\" data-testid=\"nav-toggle\">\n <span></span><span></span><span></span>\n </button>\n <ul id=\"nav-menu\" class=\"nav-menu\" data-testid=\"nav-menu\">\n <li><a href=\"/\">Home</a></li>\n <li><a href=\"/about\">About</a></li>\n <li><a href=\"/contact\">Contact</a></li>\n </ul>\n </nav>\n </div>\n</header>\n<script>\n var toggle = document.getElementById('nav-toggle');\n var menu = document.getElementById('nav-menu');\n toggle.addEventListener('click', function () {\n var open = toggle.getAttribute('aria-expanded') === 'true';\n toggle.setAttribute('aria-expanded', String(!open));\n menu.classList.toggle('is-open');\n });\n</script>\n";
24
+ export declare const SITE_FOOTER_CSS = ".footer {\n border-top: 1px solid var(--border);\n padding: 28px 0;\n}\n\n.container { max-width: 1140px; margin: 0 auto; padding: 0 28px; }\n\n.footer-inner {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n flex-wrap: wrap;\n}\n\n.footer-copy { font-size: 0.85rem; color: var(--text-muted); }\n\n.footer-nav {\n display: flex;\n gap: 20px;\n}\n.footer-nav a {\n font-size: 0.85rem;\n color: var(--text-muted);\n}\n.footer-nav a:hover { color: var(--text); opacity: 1; }\n\n@media (max-width: 480px) {\n .footer-inner { flex-direction: column; align-items: flex-start; }\n}\n";
25
+ /** Footer with brand prop and build-time year. */
26
+ export declare const SITE_FOOTER_HTML = "<style>\n.footer {\n border-top: 1px solid var(--border);\n padding: 28px 0;\n}\n\n.container { max-width: 1140px; margin: 0 auto; padding: 0 28px; }\n\n.footer-inner {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n flex-wrap: wrap;\n}\n\n.footer-copy { font-size: 0.85rem; color: var(--text-muted); }\n\n.footer-nav {\n display: flex;\n gap: 20px;\n}\n.footer-nav a {\n font-size: 0.85rem;\n color: var(--text-muted);\n}\n.footer-nav a:hover { color: var(--text); opacity: 1; }\n\n@media (max-width: 480px) {\n .footer-inner { flex-direction: column; align-items: flex-start; }\n}\n</style>\n<footer class=\"footer\">\n <div class=\"container\">\n <div class=\"footer-inner\">\n <span class=\"footer-copy\">\n &copy; <script data-bascik-build>console.log(new Date().getFullYear())</script>\n <span data-bascik-prop-brand>My Site</span>\n </span>\n <nav class=\"footer-nav\" aria-label=\"Footer navigation\">\n <a href=\"/\">Home</a>\n <a href=\"/about\">About</a>\n <a href=\"/contact\">Contact</a>\n </nav>\n </div>\n </div>\n</footer>\n";
27
+ export declare const FEAT_CARD_CSS = ".fcard {\n background: var(--elevated);\n border: 1px solid var(--border);\n border-radius: var(--r);\n padding: 24px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n transition: border-color .2s, box-shadow .2s, transform .2s;\n}\n.fcard:hover {\n border-color: var(--border-hover);\n box-shadow: 0 0 0 1px var(--accent-dim), 0 8px 32px rgba(0,0,0,0.3);\n transform: translateY(-2px);\n}\n\n.fcard-header {\n font-size: 0.7rem;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: .1em;\n color: var(--accent);\n}\n.fcard-header:empty { display: none; }\n\n.fcard-body { flex: 1; }\n.fcard-body h3 { margin: 0 0 6px; font-size: 1rem; color: var(--text); }\n.fcard-body p { margin: 0; font-size: 0.88rem; color: var(--text-muted); }\n\n.fcard-footer {\n padding-top: 12px;\n border-top: 1px solid var(--border);\n margin-top: auto;\n}\n.fcard-footer:empty { display: none; }\n";
28
+ /**
29
+ * Card component demonstrating named slots.
30
+ * Slots: header (label/icon area), default (title + body), footer (actions).
31
+ */
32
+ export declare const FEAT_CARD_HTML = "<style>\n.fcard {\n background: var(--elevated);\n border: 1px solid var(--border);\n border-radius: var(--r);\n padding: 24px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n transition: border-color .2s, box-shadow .2s, transform .2s;\n}\n.fcard:hover {\n border-color: var(--border-hover);\n box-shadow: 0 0 0 1px var(--accent-dim), 0 8px 32px rgba(0,0,0,0.3);\n transform: translateY(-2px);\n}\n\n.fcard-header {\n font-size: 0.7rem;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: .1em;\n color: var(--accent);\n}\n.fcard-header:empty { display: none; }\n\n.fcard-body { flex: 1; }\n.fcard-body h3 { margin: 0 0 6px; font-size: 1rem; color: var(--text); }\n.fcard-body p { margin: 0; font-size: 0.88rem; color: var(--text-muted); }\n\n.fcard-footer {\n padding-top: 12px;\n border-top: 1px solid var(--border);\n margin-top: auto;\n}\n.fcard-footer:empty { display: none; }\n</style>\n<article class=\"fcard\">\n <div class=\"fcard-header\" data-bascik-slot=\"header\"></div>\n <div class=\"fcard-body\" data-bascik-slot></div>\n <div class=\"fcard-footer\" data-bascik-slot=\"footer\"></div>\n</article>\n";
33
+ export declare const MY_COUNTER_CSS = ".counter {\n display: inline-flex;\n align-items: center;\n gap: 16px;\n font-size: 1.25rem;\n}\n\n.count-val {\n font-family: var(--mono);\n font-size: 1.5rem;\n min-width: 2ch;\n text-align: center;\n color: var(--accent);\n}\n\n.btn {\n display: inline-flex; align-items: center; gap: 8px;\n padding: 10px 22px; border: none; border-radius: var(--r-sm);\n font-size: 0.9rem; font-weight: 600; cursor: pointer;\n text-decoration: none; transition: filter .15s, opacity .15s; line-height: 1;\n}\n.btn-primary { background: var(--accent); color: #18191b; }\n.btn-ghost { background: transparent; color: var(--text); border: 1px solid var(--border); }\n@media (hover: hover) {\n .btn-primary:hover { filter: brightness(0.88); opacity: 1; }\n .btn-ghost:hover { border-color: var(--accent); color: var(--accent); opacity: 1; }\n}\n";
34
+ /** Interactive counter — two instances on a page stay fully independent. */
35
+ export declare const MY_COUNTER_HTML = "<style>\n.counter {\n display: inline-flex;\n align-items: center;\n gap: 16px;\n font-size: 1.25rem;\n}\n\n.count-val {\n font-family: var(--mono);\n font-size: 1.5rem;\n min-width: 2ch;\n text-align: center;\n color: var(--accent);\n}\n\n.btn {\n display: inline-flex; align-items: center; gap: 8px;\n padding: 10px 22px; border: none; border-radius: var(--r-sm);\n font-size: 0.9rem; font-weight: 600; cursor: pointer;\n text-decoration: none; transition: filter .15s, opacity .15s; line-height: 1;\n}\n.btn-primary { background: var(--accent); color: #18191b; }\n.btn-ghost { background: transparent; color: var(--text); border: 1px solid var(--border); }\n@media (hover: hover) {\n .btn-primary:hover { filter: brightness(0.88); opacity: 1; }\n .btn-ghost:hover { border-color: var(--accent); color: var(--accent); opacity: 1; }\n}\n</style>\n<div class=\"counter\">\n <button id=\"btn-dec\" class=\"btn btn-ghost\" data-testid=\"counter-dec\">\u2212</button>\n <span id=\"count-val\" class=\"count-val\" data-testid=\"counter-val\">0</span>\n <button id=\"btn-inc\" class=\"btn btn-primary\" data-testid=\"counter-inc\">+</button>\n</div>\n<script>\n (function () {\n var count = 0;\n var val = document.getElementById('count-val');\n document.getElementById('btn-dec').addEventListener('click', function () {\n val.textContent = --count;\n });\n document.getElementById('btn-inc').addEventListener('click', function () {\n val.textContent = ++count;\n });\n }());\n</script>\n";
36
+ export declare const SITE_META_TEST = "import { describe, it, expect } from 'vitest';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\ndescribe('site-meta component', () => {\n const path = join(process.cwd(), 'src/components/site-meta/site-meta.html');\n\n it('contains charset, viewport, favicon, and stylesheet link tags', async () => {\n const html = await readFile(path, 'utf8');\n expect(html).toContain('charset=\"UTF-8\"');\n expect(html).toContain('name=\"viewport\"');\n expect(html).toContain('name=\"description\"');\n expect(html).toContain('favicon.ico');\n expect(html).toContain('favicon-32x32.png');\n expect(html).toContain('favicon.svg');\n expect(html).toContain('apple-touch-icon.png');\n expect(html).toContain('styles.css');\n });\n});\n";
37
+ export declare const SITE_HEADER_TEST = "import { describe, it, expect } from 'vitest';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\ndescribe('site-header component', () => {\n const path = join(process.cwd(), 'src/components/site-header/site-header.html');\n\n it('renders brand prop placeholder and navigation links', async () => {\n const html = await readFile(path, 'utf8');\n expect(html).toContain('data-bascik-prop-brand');\n expect(html).toContain('href=\"/\"');\n expect(html).toContain('href=\"/about\"');\n expect(html).toContain('href=\"/contact\"');\n expect(html).toContain('id=\"nav-toggle\"');\n });\n});\n";
38
+ export declare const SITE_FOOTER_TEST = "import { describe, it, expect } from 'vitest';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\ndescribe('site-footer component', () => {\n const path = join(process.cwd(), 'src/components/site-footer/site-footer.html');\n\n it('renders brand prop placeholder and build-time year script', async () => {\n const html = await readFile(path, 'utf8');\n expect(html).toContain('data-bascik-prop-brand');\n expect(html).toContain('data-bascik-build');\n expect(html).toContain('getFullYear');\n });\n});\n";
39
+ export declare const FEAT_CARD_TEST = "import { describe, it, expect } from 'vitest';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\ndescribe('feat-card component', () => {\n const path = join(process.cwd(), 'src/components/feat-card/feat-card.html');\n\n it('defines header, default, and footer slots', async () => {\n const html = await readFile(path, 'utf8');\n expect(html).toContain('data-bascik-slot=\"header\"');\n expect(html).toContain('data-bascik-slot');\n expect(html).toContain('data-bascik-slot=\"footer\"');\n });\n});\n";
40
+ export declare const MY_COUNTER_TEST = "import { describe, it, expect } from 'vitest';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\ndescribe('my-counter component', () => {\n const path = join(process.cwd(), 'src/components/my-counter/my-counter.html');\n\n it('renders increment and decrement controls with scoped script', async () => {\n const html = await readFile(path, 'utf8');\n expect(html).toContain('id=\"btn-dec\"');\n expect(html).toContain('id=\"btn-inc\"');\n expect(html).toContain('id=\"count-val\"');\n expect(html).toContain('addEventListener');\n });\n});\n";
41
+ export declare const indexPage: (brand: string) => string;
42
+ export declare const aboutPage: (brand: string) => string;
43
+ export declare const contactPage: (brand: string) => string;
44
+ export declare const notFoundPage: (brand: string) => string;
45
+ /** Returns an error message if the name is invalid, or null if it is fine. */
46
+ export declare function validateProjectName(name: string): string | null;
47
+ /**
48
+ * Scaffold a new Bascik project into `targetDir/<projectName>`.
49
+ *
50
+ * @param projectName The name used for the directory and package.json name.
51
+ * @param targetDir Parent directory in which to create the project (defaults to cwd).
52
+ */
53
+ export declare function scaffold(projectName: string, targetDir?: string): Promise<void>;