create-cocoframe 0.0.1 → 0.0.2

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 (36) hide show
  1. package/README.md +20 -18
  2. package/package.json +2 -1
  3. package/src/index.d.ts +5 -0
  4. package/src/index.js +28 -4
  5. package/template/app/routes/_layout.tsx +2 -1
  6. package/template/app/routes/index.page.tsx +8 -5
  7. package/template/app/styles/app.css +9 -24
  8. package/template/package.json +1 -0
  9. package/templates/catalog.json +41 -0
  10. package/templates/dashboard/README.md +10 -0
  11. package/templates/dashboard/_gitignore +8 -0
  12. package/templates/dashboard/app/routes/_layout.tsx +23 -0
  13. package/templates/dashboard/app/routes/api/health.route.ts +12 -0
  14. package/templates/dashboard/app/routes/index.page.tsx +29 -0
  15. package/templates/dashboard/app/styles/app.css +33 -0
  16. package/templates/dashboard/cocoframe.config.ts +2 -0
  17. package/templates/dashboard/package.json +26 -0
  18. package/templates/dashboard/tsconfig.json +17 -0
  19. package/templates/documentation/README.md +10 -0
  20. package/templates/documentation/_gitignore +8 -0
  21. package/templates/documentation/app/routes/_layout.tsx +17 -0
  22. package/templates/documentation/app/routes/api/health.route.ts +12 -0
  23. package/templates/documentation/app/routes/index.page.tsx +25 -0
  24. package/templates/documentation/app/styles/app.css +31 -0
  25. package/templates/documentation/cocoframe.config.ts +2 -0
  26. package/templates/documentation/package.json +26 -0
  27. package/templates/documentation/tsconfig.json +17 -0
  28. package/templates/marketing/README.md +10 -0
  29. package/templates/marketing/_gitignore +8 -0
  30. package/templates/marketing/app/routes/_layout.tsx +13 -0
  31. package/templates/marketing/app/routes/api/health.route.ts +12 -0
  32. package/templates/marketing/app/routes/index.page.tsx +39 -0
  33. package/templates/marketing/app/styles/app.css +32 -0
  34. package/templates/marketing/cocoframe.config.ts +8 -0
  35. package/templates/marketing/package.json +26 -0
  36. package/templates/marketing/tsconfig.json +17 -0
package/README.md CHANGED
@@ -2,27 +2,29 @@
2
2
 
3
3
  Dependency-free project creator for [CocoFrame](https://github.com/wiryosaputraofficial/cocoframe).
4
4
 
5
- After the public CocoFrame runtime packages are released:
6
-
7
- ~~~bash
5
+ ```bash
8
6
  npm create cocoframe@latest my-app
9
- cd my-app
10
- npm run dev
11
- ~~~
12
-
13
- For development from the CocoFrame repository:
7
+ npm create cocoframe@latest my-dashboard -- --template dashboard
8
+ ```
14
9
 
15
- ~~~bash
16
- npm run create -- examples/my-app --skip-install
17
- npm install
18
- npm run dev --workspace=my-app
19
- ~~~
10
+ Available templates: `starter`, `marketing`, `dashboard`, and `documentation`.
11
+ Every template uses official `@cocoframe/ui` components and tree-shakeable
12
+ `@cocoframe/icons` imports.
20
13
 
21
14
  ## Options
22
15
 
23
- - --package-manager npm|pnpm|yarn|bun selects the installer.
24
- - --skip-install or --no-install only writes the starter files.
25
- - --help prints usage.
26
- - --version prints the creator version.
16
+ - `--template starter|marketing|dashboard|documentation` selects the starter.
17
+ - `--package-manager npm|pnpm|yarn|bun` selects the installer.
18
+ - `--skip-install` or `--no-install` only writes the starter files.
19
+ - `--help` prints usage.
20
+ - `--version` prints the creator version.
21
+
22
+ The creator refuses filesystem roots and non-empty target directories. Each
23
+ starter includes a server-rendered page, typed health API, responsive CSS,
24
+ TypeScript configuration, and production scripts.
25
+
26
+ For development from the CocoFrame repository:
27
27
 
28
- The creator refuses filesystem roots and non-empty target directories. The starter includes a server-rendered page, a typed API route, an opt-in counter island, responsive CSS, TypeScript configuration, and production scripts.
28
+ ```bash
29
+ npm run create -- examples/my-app --template marketing --skip-install
30
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-cocoframe",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Dependency-free project creator for CocoFrame applications.",
5
5
  "keywords": [
6
6
  "cocoframe",
@@ -37,6 +37,7 @@
37
37
  "files": [
38
38
  "src",
39
39
  "template",
40
+ "templates",
40
41
  "README.md",
41
42
  "LICENSE"
42
43
  ]
package/src/index.d.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  import type { spawn } from "node:child_process";
2
2
 
3
3
  export type PackageManager = "npm" | "pnpm" | "yarn" | "bun";
4
+ export type TemplateName = "starter" | "marketing" | "dashboard" | "documentation";
4
5
 
5
6
  export interface CreatorArguments {
6
7
  projectDirectory?: string;
7
8
  packageManager: PackageManager;
9
+ template: TemplateName;
8
10
  install: boolean;
9
11
  help: boolean;
10
12
  version: boolean;
@@ -13,6 +15,7 @@ export interface CreatorArguments {
13
15
  export interface ScaffoldOptions {
14
16
  projectDirectory: string;
15
17
  packageManager?: PackageManager;
18
+ template?: TemplateName;
16
19
  install?: boolean;
17
20
  cwd?: string;
18
21
  templateDirectory?: string;
@@ -23,6 +26,7 @@ export interface ScaffoldResult {
23
26
  targetDirectory: string;
24
27
  packageName: string;
25
28
  packageManager: PackageManager;
29
+ template: TemplateName;
26
30
  installed: boolean;
27
31
  }
28
32
 
@@ -32,6 +36,7 @@ export interface CreatorOptions extends Omit<ScaffoldOptions, "projectDirectory"
32
36
  }
33
37
 
34
38
  export const helpText: string;
39
+ export const availableTemplates: readonly TemplateName[];
35
40
  export function parseArguments(args: string[], userAgent?: string): CreatorArguments;
36
41
  export function scaffoldProject(options: ScaffoldOptions): Promise<ScaffoldResult>;
37
42
  export function runCreator(args: string[], options?: CreatorOptions): Promise<ScaffoldResult | undefined>;
package/src/index.js CHANGED
@@ -5,7 +5,9 @@ import { fileURLToPath } from "node:url";
5
5
 
6
6
  const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
7
7
  const defaultTemplate = path.join(packageRoot, "template");
8
+ const templatesRoot = path.join(packageRoot, "templates");
8
9
  const supportedPackageManagers = new Set(["npm", "pnpm", "yarn", "bun"]);
10
+ export const availableTemplates = Object.freeze(["starter", "marketing", "dashboard", "documentation"]);
9
11
 
10
12
  export const helpText = [
11
13
  "Create a CocoFrame application",
@@ -14,6 +16,8 @@ export const helpText = [
14
16
  " npm create cocoframe@latest <project-directory> [options]",
15
17
  "",
16
18
  "Options:",
19
+ " --template <starter|marketing|dashboard|documentation>",
20
+ " Select a project template",
17
21
  " --package-manager <npm|pnpm|yarn|bun> Select the package manager",
18
22
  " --skip-install, --no-install Generate files without installing",
19
23
  " --help, -h Show this help",
@@ -23,6 +27,7 @@ export const helpText = [
23
27
  export function parseArguments(args, userAgent = process.env.npm_config_user_agent) {
24
28
  let projectDirectory;
25
29
  let packageManager = packageManagerFromUserAgent(userAgent);
30
+ let template = "starter";
26
31
  let install = true;
27
32
  let help = false;
28
33
  let version = false;
@@ -32,7 +37,13 @@ export function parseArguments(args, userAgent = process.env.npm_config_user_age
32
37
  if (argument === "--help" || argument === "-h") help = true;
33
38
  else if (argument === "--version" || argument === "-v") version = true;
34
39
  else if (argument === "--skip-install" || argument === "--no-install") install = false;
35
- else if (argument === "--package-manager") {
40
+ else if (argument === "--template") {
41
+ const value = args[++index];
42
+ if (!value) throw new Error("--template requires a value.");
43
+ template = validateTemplate(value);
44
+ } else if (argument?.startsWith("--template=")) {
45
+ template = validateTemplate(argument.slice("--template=".length));
46
+ } else if (argument === "--package-manager") {
36
47
  const value = args[++index];
37
48
  if (!value) throw new Error("--package-manager requires a value.");
38
49
  packageManager = validatePackageManager(value);
@@ -44,7 +55,7 @@ export function parseArguments(args, userAgent = process.env.npm_config_user_age
44
55
  }
45
56
 
46
57
  if (!help && !version && !projectDirectory) throw new Error("A project directory is required.");
47
- return { projectDirectory, packageManager, install, help, version };
58
+ return { projectDirectory, packageManager, template, install, help, version };
48
59
  }
49
60
 
50
61
  export async function scaffoldProject(options) {
@@ -52,7 +63,8 @@ export async function scaffoldProject(options) {
52
63
  const targetDirectory = resolveTargetDirectory(options.projectDirectory, cwd);
53
64
  const packageName = normalizePackageName(path.basename(targetDirectory));
54
65
  const packageManager = validatePackageManager(options.packageManager ?? "npm");
55
- const templateDirectory = path.resolve(options.templateDirectory ?? defaultTemplate);
66
+ const template = validateTemplate(options.template ?? "starter");
67
+ const templateDirectory = path.resolve(options.templateDirectory ?? templatePath(template));
56
68
  const entries = await directoryEntries(targetDirectory);
57
69
  if (entries.length > 0) throw new Error("Target directory is not empty: " + targetDirectory);
58
70
 
@@ -65,7 +77,7 @@ export async function scaffoldProject(options) {
65
77
  await installDependencies(targetDirectory, packageManager, options.spawnCommand);
66
78
  }
67
79
 
68
- return { targetDirectory, packageName, packageManager, installed: options.install !== false };
80
+ return { targetDirectory, packageName, packageManager, template, installed: options.install !== false };
69
81
  }
70
82
 
71
83
  export async function runCreator(args, options = {}) {
@@ -84,6 +96,7 @@ export async function runCreator(args, options = {}) {
84
96
  const result = await scaffoldProject({
85
97
  projectDirectory: parsed.projectDirectory,
86
98
  packageManager: parsed.packageManager,
99
+ template: parsed.template,
87
100
  install: parsed.install,
88
101
  cwd: options.cwd,
89
102
  templateDirectory: options.templateDirectory,
@@ -111,6 +124,17 @@ function validatePackageManager(value) {
111
124
  return value;
112
125
  }
113
126
 
127
+ function validateTemplate(value) {
128
+ if (!availableTemplates.includes(value)) {
129
+ throw new Error("Unknown template: " + value + ". Use " + availableTemplates.join(", ") + ".");
130
+ }
131
+ return value;
132
+ }
133
+
134
+ function templatePath(template) {
135
+ return template === "starter" ? defaultTemplate : path.join(templatesRoot, template);
136
+ }
137
+
114
138
  function resolveTargetDirectory(input, cwd) {
115
139
  if (!input?.trim()) throw new Error("A project directory is required.");
116
140
  const target = path.resolve(cwd, input);
@@ -1,9 +1,10 @@
1
1
  import { defineLayout } from "@cocoframe/core";
2
+ import BoxMinimalisticIcon from "@cocoframe/icons/linear/box-minimalistic";
2
3
 
3
4
  export default defineLayout(({ children }) => (
4
5
  <>
5
6
  <header class="site-header">
6
- <a href="/" class="brand">CocoFrame</a>
7
+ <a href="/" class="brand"><BoxMinimalisticIcon size={24} label="CocoFrame" /> <span>CocoFrame</span></a>
7
8
  <nav aria-label="Primary navigation">
8
9
  <a href="/">Home</a>
9
10
  <a href="/api/health">API</a>
@@ -1,4 +1,6 @@
1
1
  import { definePage } from "@cocoframe/core";
2
+ import CodeSquareIcon from "@cocoframe/icons/linear/code-square";
3
+ import { Badge, Card, Heading, Stack, Text } from "@cocoframe/ui";
2
4
  import Counter from "../islands/counter.island.tsx";
3
5
 
4
6
  export default definePage({
@@ -8,11 +10,12 @@ export default definePage({
8
10
  },
9
11
  view: () => (
10
12
  <main class="hero">
11
- <p class="eyebrow">SERVER-FIRST · AI-FRIENDLY</p>
12
- <h1>Your CocoFrame project is ready.</h1>
13
- <p>Useful HTML is rendered on the server. Browser JavaScript is isolated to the counter below.</p>
14
- <Counter initial={0} />
15
- <small>Edit <code>app/routes/index.page.tsx</code> to get started.</small>
13
+ <Stack gap="large">
14
+ <Badge variant="success"><CodeSquareIcon size={15} /> Server-first · AI-friendly</Badge>
15
+ <Heading level={1} size="xlarge">Your CocoFrame project is ready.</Heading>
16
+ <Text size="large" tone="muted">Useful HTML is rendered on the server. Browser JavaScript is isolated to the counter below.</Text>
17
+ <Card class="starter-card"><Counter initial={0} /><small>Edit <code>app/routes/index.page.tsx</code> to get started.</small></Card>
18
+ </Stack>
16
19
  </main>
17
20
  ),
18
21
  });
@@ -1,29 +1,14 @@
1
- :root {
2
- color: #15231c;
3
- background: #f6faf8;
4
- font-family: Inter, ui-sans-serif, system-ui, sans-serif;
5
- }
6
-
1
+ :root { color: #15231c; background: #f6faf8; font-family: Inter, ui-sans-serif, system-ui, sans-serif; }
7
2
  * { box-sizing: border-box; }
8
3
  body { margin: 0; }
9
4
  a { color: inherit; }
10
-
11
- .site-header {
12
- min-height: 68px;
13
- padding: 0 clamp(20px, 5vw, 72px);
14
- display: flex;
15
- align-items: center;
16
- justify-content: space-between;
17
- border-bottom: 1px solid #dce9e1;
18
- background: rgba(255, 255, 255, .9);
19
- }
20
-
21
- .brand { color: #187a55; font-weight: 800; text-decoration: none; }
5
+ .site-header { min-height: 68px; padding: 0 clamp(20px, 5vw, 72px); display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid #dce9e1; background: rgba(255, 255, 255, .9); }
6
+ .brand { display: inline-flex; align-items: center; gap: 9px; color: #187a55; font-weight: 800; text-decoration: none; }
22
7
  .site-header nav { display: flex; gap: 20px; font-size: 14px; }
23
8
  .hero { width: min(760px, calc(100% - 40px)); margin: 0 auto; padding: clamp(72px, 14vw, 150px) 0; }
24
- .eyebrow { color: #187a55; font-size: 12px; font-weight: 800; letter-spacing: .12em; }
25
- h1 { max-width: 680px; margin: 14px 0; font-size: clamp(40px, 8vw, 76px); line-height: 1; letter-spacing: -.055em; }
26
- .hero > p:not(.eyebrow) { max-width: 620px; color: #587064; font-size: 18px; line-height: 1.7; }
27
- .hero button { margin: 20px 0; }
28
- .hero small { display: block; color: #587064; }
29
- code { padding: 2px 6px; border-radius: 5px; background: #e8f3ed; }
9
+ .hero .coco-heading { max-width: 680px; margin: 0; letter-spacing: -.045em; }
10
+ .hero .coco-text { max-width: 620px; line-height: 1.7; }
11
+ .starter-card { display: flex; align-items: center; justify-content: space-between; gap: 24px; padding: 24px; }
12
+ .starter-card small { color: #587064; }
13
+ code { padding: 2px 6px; border-radius: 5px; background: #e8f3ed; }
14
+ @media (max-width: 540px) { .site-header nav { gap: 12px; } .starter-card { align-items: flex-start; flex-direction: column; } }
@@ -14,6 +14,7 @@
14
14
  "dependencies": {
15
15
  "@cocoframe/client": "0.0.1",
16
16
  "@cocoframe/core": "0.0.1",
17
+ "@cocoframe/icons": "0.0.1",
17
18
  "@cocoframe/jsx": "0.0.1",
18
19
  "@cocoframe/schema": "0.0.1",
19
20
  "@cocoframe/ui": "0.0.1"
@@ -0,0 +1,41 @@
1
+ {
2
+ "version": 1,
3
+ "templates": [
4
+ {
5
+ "id": "starter",
6
+ "name": "CocoFrame Starter",
7
+ "description": "Fondasi minimal server-first dengan typed API dan counter island.",
8
+ "category": "Starter Kits",
9
+ "technology": ["TypeScript", "CocoFrame"],
10
+ "components": ["Badge", "Button", "Card", "Heading", "Stack", "Text"],
11
+ "icons": ["box-minimalistic", "code-square"]
12
+ },
13
+ {
14
+ "id": "marketing",
15
+ "name": "Marketing Launch",
16
+ "description": "Landing page SEO-ready dengan hero, feature cards, metrics, dan CTA.",
17
+ "category": "Web Applications",
18
+ "technology": ["TypeScript", "CocoFrame", "CSS"],
19
+ "components": ["Badge", "Card", "Container", "Heading", "Inline", "Stack", "Stat", "Text"],
20
+ "icons": ["arrow-right", "bolt", "graph-up", "shield-check", "stars-minimalistic"]
21
+ },
22
+ {
23
+ "id": "dashboard",
24
+ "name": "Orbit Dashboard",
25
+ "description": "Dashboard admin responsif dengan sidebar, analytics, data table, dan status.",
26
+ "category": "Dashboards",
27
+ "technology": ["TypeScript", "CocoFrame", "Charts"],
28
+ "components": ["Badge", "Card", "Chart", "DataTable", "Heading", "Inline", "Sidebar", "Stat", "Text"],
29
+ "icons": ["bell", "calendar", "graph-up", "home", "magnifier", "settings", "users-group-rounded", "widget-4"]
30
+ },
31
+ {
32
+ "id": "documentation",
33
+ "name": "Atlas Documentation",
34
+ "description": "Dokumentasi server-first dengan sidebar, code samples, alert, dan route table.",
35
+ "category": "Documentation",
36
+ "technology": ["TypeScript", "CocoFrame"],
37
+ "components": ["Alert", "Badge", "Card", "Heading", "Sidebar", "SyntaxHighlighter", "Table", "Text"],
38
+ "icons": ["arrow-right", "book-2", "check-circle", "code-square", "document-text", "magnifier", "routing-2", "settings"]
39
+ }
40
+ ]
41
+ }
@@ -0,0 +1,10 @@
1
+ # {{PROJECT_NAME}}
2
+
3
+ Responsive admin dashboard built with CocoFrame `Sidebar`, `Stat`, `Chart`, `DataTable`, `Badge`, and Solar Linear icons.
4
+
5
+ ```bash
6
+ npm install
7
+ npm run dev
8
+ ```
9
+
10
+ Use `npm run inspect` to see the components and icons discovered by CocoFrame.
@@ -0,0 +1,8 @@
1
+ node_modules/
2
+ .cocoframe/
3
+ dist/
4
+ coverage/
5
+ *.log
6
+ .env
7
+ .env.*
8
+ !.env.example
@@ -0,0 +1,23 @@
1
+ import { defineLayout } from "@cocoframe/core";
2
+ import ChartIcon from "@cocoframe/icons/linear/graph-up";
3
+ import HomeIcon from "@cocoframe/icons/linear/home";
4
+ import SettingsIcon from "@cocoframe/icons/linear/settings";
5
+ import UsersIcon from "@cocoframe/icons/linear/users-group-rounded";
6
+ import WidgetIcon from "@cocoframe/icons/linear/widget-4";
7
+ import { Badge, Sidebar } from "@cocoframe/ui";
8
+
9
+ export default defineLayout(({ children }) => (
10
+ <div class="dashboard-shell">
11
+ <Sidebar
12
+ brand={<a class="dashboard-brand" href="/"><WidgetIcon size={24} /> <span>Orbit Admin</span></a>}
13
+ groups={[{ label: "Workspace", items: [
14
+ { label: "Overview", href: "/", current: true, icon: <HomeIcon size={18} /> },
15
+ { label: "Analytics", href: "#analytics", icon: <ChartIcon size={18} /> },
16
+ { label: "Customers", href: "#customers", icon: <UsersIcon size={18} />, badge: <Badge variant="success">24</Badge> },
17
+ { label: "Settings", href: "#settings", icon: <SettingsIcon size={18} /> },
18
+ ] }]}
19
+ footer={<small>Server-rendered with CocoFrame</small>}
20
+ />
21
+ <div class="dashboard-main">{children}</div>
22
+ </div>
23
+ ));
@@ -0,0 +1,12 @@
1
+ import { defineApi } from "@cocoframe/core";
2
+ import { schema } from "@cocoframe/schema";
3
+
4
+ export default defineApi({
5
+ id: "health",
6
+ method: "GET",
7
+ output: schema.object({
8
+ ok: schema.literal(true),
9
+ framework: schema.string(),
10
+ }),
11
+ handle: () => ({ ok: true, framework: "cocoframe" }),
12
+ });
@@ -0,0 +1,29 @@
1
+ import { definePage } from "@cocoframe/core";
2
+ import BellIcon from "@cocoframe/icons/linear/bell";
3
+ import CalendarIcon from "@cocoframe/icons/linear/calendar";
4
+ import GraphUpIcon from "@cocoframe/icons/linear/graph-up";
5
+ import MagnifierIcon from "@cocoframe/icons/linear/magnifier";
6
+ import { Badge, Card, Chart, DataTable, Heading, Inline, Stat, Text } from "@cocoframe/ui";
7
+
8
+ const rows = [
9
+ { customer: "Acme Studio", plan: "Growth", status: <Badge variant="success">Active</Badge>, revenue: "$4,800" },
10
+ { customer: "Northwind", plan: "Starter", status: <Badge variant="warning">Trial</Badge>, revenue: "$1,260" },
11
+ { customer: "Vertex Labs", plan: "Scale", status: <Badge variant="success">Active</Badge>, revenue: "$8,940" },
12
+ { customer: "Lumen Works", plan: "Growth", status: <Badge variant="neutral">Paused</Badge>, revenue: "$3,120" },
13
+ ];
14
+
15
+ export default definePage({
16
+ meta: { title: "{{PROJECT_NAME}} — Dashboard", description: "Accessible CocoFrame dashboard template with charts, stats, table, and official icons." },
17
+ view: () => <main class="dashboard-page">
18
+ <header class="dashboard-topbar"><div><Text tone="muted" size="small">Tuesday, 22 August</Text><Heading level={1}>Good morning, Alex</Heading></div><Inline gap="small"><button class="icon-action" type="button" aria-label="Search"><MagnifierIcon size={19} /></button><button class="icon-action" type="button" aria-label="Notifications"><BellIcon size={19} /></button><span class="avatar">AX</span></Inline></header>
19
+
20
+ <section class="stats-grid" aria-label="Key metrics"><Stat label="Monthly revenue" value="$42,860" trend="+12.4% this month" tone="positive" /><Stat label="Active customers" value="1,284" trend="+86 this week" tone="positive" /><Stat label="Conversion" value="8.42%" trend="+1.2 percentage points" tone="positive" /><Stat label="Open tickets" value="18" trend="4 need attention" tone="negative" /></section>
21
+
22
+ <section class="dashboard-grid" id="analytics">
23
+ <Card class="chart-card"><div class="card-heading"><div><Badge variant="primary"><GraphUpIcon size={14} /> Analytics</Badge><Heading level={2} size="medium">Revenue overview</Heading></div><button class="period-button" type="button"><CalendarIcon size={16} /> Last 6 months</button></div><Chart type="area" label="Monthly revenue" description="Revenue increased from 24 to 43 thousand dollars." labels={["Mar","Apr","May","Jun","Jul","Aug"]} datasets={[{ label: "Revenue", data: [24,28,27,34,38,43], tone: "primary", fill: true }]} formatValue={(value) => `$${value}k`} /></Card>
24
+ <Card class="goal-card"><Badge variant="success">On track</Badge><Heading level={2} size="medium">Quarter goal</Heading><strong>78%</strong><div class="goal-ring" aria-label="78 percent complete"><span>78%</span></div><Text tone="muted">$31,200 remaining to reach the quarterly target.</Text></Card>
25
+ </section>
26
+
27
+ <section id="customers"><Card class="customers-card"><div class="card-heading"><div><Heading level={2} size="medium">Recent customers</Heading><Text tone="muted" size="small">Latest account activity across the workspace.</Text></div><a href="#customers">View all</a></div><DataTable caption="Recent customers" columns={[{ key: "customer", label: "Customer", sortable: true }, { key: "plan", label: "Plan", sortable: true }, { key: "status", label: "Status" }, { key: "revenue", label: "Revenue", sortable: true }]} rows={rows} sortKey="customer" sortPath="/" /></Card></section>
28
+ </main>,
29
+ });
@@ -0,0 +1,33 @@
1
+ :root { color: #14221b; background: #f4f7f5; font-family: Inter, ui-sans-serif, system-ui, sans-serif; }
2
+ * { box-sizing: border-box; }
3
+ body { margin: 0; }
4
+ a { color: inherit; }
5
+ .dashboard-shell { min-height: 100vh; display: grid; grid-template-columns: 250px minmax(0,1fr); }
6
+ .dashboard-shell > .coco-sidebar { min-height: 100vh; border-right: 1px solid #dce6e0; background: #fff; position: sticky; top: 0; }
7
+ .dashboard-brand { display: flex; align-items: center; gap: 10px; color: #187a55; font-weight: 850; text-decoration: none; }
8
+ .dashboard-main { min-width: 0; }
9
+ .dashboard-page { width: min(1440px,100%); margin: auto; padding: clamp(22px,4vw,54px); }
10
+ .dashboard-topbar, .card-heading { display: flex; align-items: center; justify-content: space-between; gap: 20px; }
11
+ .dashboard-topbar { margin-bottom: 32px; }
12
+ .dashboard-topbar .coco-heading { margin: 4px 0 0; }
13
+ .icon-action, .period-button { display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-height: 42px; border: 1px solid #d8e4dd; border-radius: 10px; color: #365548; background: white; }
14
+ .icon-action { width: 42px; padding: 0; }
15
+ .period-button { padding: 0 13px; }
16
+ .avatar { width: 42px; height: 42px; display: grid; place-items: center; border-radius: 50%; color: white; background: #187a55; font-size: 13px; font-weight: 800; }
17
+ .stats-grid { display: grid; grid-template-columns: repeat(4,minmax(0,1fr)); gap: 14px; margin-bottom: 18px; }
18
+ .stats-grid .coco-stat { padding: 22px; border: 1px solid #dce6e0; border-radius: 14px; background: white; }
19
+ .dashboard-grid { display: grid; grid-template-columns: minmax(0,2fr) minmax(260px,.75fr); gap: 18px; margin-bottom: 18px; }
20
+ .chart-card, .goal-card, .customers-card { padding: 24px; }
21
+ .card-heading { margin-bottom: 18px; }
22
+ .card-heading .coco-heading { margin: 8px 0 0; }
23
+ .chart-card .coco-chart { border: 0; padding: 0; box-shadow: none; }
24
+ .goal-card { display: flex; flex-direction: column; align-items: flex-start; }
25
+ .goal-card > strong { margin: 20px 0 6px; font-size: 52px; letter-spacing: -.05em; }
26
+ .goal-ring { width: 150px; height: 150px; display: grid; place-items: center; margin: 18px auto; border-radius: 50%; background: conic-gradient(#187a55 0 78%,#dcebe3 78%); position: relative; }
27
+ .goal-ring::after { content: ""; position: absolute; inset: 15px; border-radius: inherit; background: white; }
28
+ .goal-ring span { position: relative; z-index: 1; font-weight: 800; }
29
+ .customers-card .coco-data-table { border: 0; }
30
+ .customers-card a { color: #187a55; font-weight: 750; }
31
+ @media (max-width: 1080px) { .stats-grid { grid-template-columns: repeat(2,1fr); } .dashboard-grid { grid-template-columns: 1fr; } }
32
+ @media (max-width: 760px) { .dashboard-shell { grid-template-columns: 1fr; } .dashboard-shell > .coco-sidebar { min-height: auto; position: static; } .dashboard-page { padding: 20px 14px 40px; } .dashboard-topbar { align-items: flex-start; } }
33
+ @media (max-width: 520px) { .stats-grid { grid-template-columns: 1fr; } .dashboard-topbar > .coco-inline .icon-action { display: none; } .card-heading { align-items: flex-start; flex-direction: column; } }
@@ -0,0 +1,2 @@
1
+ import { defineConfig } from "@cocoframe/core";
2
+ export default defineConfig({ language: "en", siteName: "{{PROJECT_NAME}}", stylesheets: ["/styles/app.css"], openapi: { title: "{{PROJECT_NAME}} API", version: "1.0.0" } });
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "cocoframe dev .",
8
+ "build": "cocoframe build .",
9
+ "start": "cocoframe start .",
10
+ "check": "tsc --noEmit",
11
+ "inspect": "cocoframe inspect .",
12
+ "generate": "cocoframe generate ."
13
+ },
14
+ "dependencies": {
15
+ "@cocoframe/core": "0.0.1",
16
+ "@cocoframe/icons": "0.0.1",
17
+ "@cocoframe/jsx": "0.0.1",
18
+ "@cocoframe/schema": "0.0.1",
19
+ "@cocoframe/ui": "0.0.1"
20
+ },
21
+ "devDependencies": {
22
+ "@cocoframe/cli": "0.0.1",
23
+ "@types/node": "^24.0.0",
24
+ "typescript": "^5.9.0"
25
+ }
26
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2023",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "strict": true,
7
+ "noUncheckedIndexedAccess": true,
8
+ "exactOptionalPropertyTypes": true,
9
+ "verbatimModuleSyntax": true,
10
+ "allowImportingTsExtensions": true,
11
+ "jsx": "react-jsx",
12
+ "jsxImportSource": "@cocoframe/jsx",
13
+ "skipLibCheck": true,
14
+ "noEmit": true
15
+ },
16
+ "include": ["app/**/*.ts", "app/**/*.tsx", "cocoframe.config.ts"]
17
+ }
@@ -0,0 +1,10 @@
1
+ # {{PROJECT_NAME}}
2
+
3
+ Documentation template built with CocoFrame `Sidebar`, `SyntaxHighlighter`, `Alert`, `Table`, `Card`, and Solar Linear icons.
4
+
5
+ ```bash
6
+ npm install
7
+ npm run dev
8
+ ```
9
+
10
+ Add new guides as route sections or split them into dedicated `.page.tsx` files.
@@ -0,0 +1,8 @@
1
+ node_modules/
2
+ .cocoframe/
3
+ dist/
4
+ coverage/
5
+ *.log
6
+ .env
7
+ .env.*
8
+ !.env.example
@@ -0,0 +1,17 @@
1
+ import { defineLayout } from "@cocoframe/core";
2
+ import BookIcon from "@cocoframe/icons/linear/book-2";
3
+ import CodeIcon from "@cocoframe/icons/linear/code-square";
4
+ import DocumentIcon from "@cocoframe/icons/linear/document-text";
5
+ import RouteIcon from "@cocoframe/icons/linear/routing-2";
6
+ import SettingsIcon from "@cocoframe/icons/linear/settings";
7
+ import { Badge, Sidebar } from "@cocoframe/ui";
8
+
9
+ export default defineLayout(({ children }) => (
10
+ <div class="docs-shell">
11
+ <Sidebar brand={<a class="docs-brand" href="/"><BookIcon size={24} /> <span>Atlas Docs</span></a>} groups={[
12
+ { label: "Getting started", items: [{ label: "Introduction", href: "#introduction", current: true, icon: <DocumentIcon size={17} /> }, { label: "Installation", href: "#installation", icon: <CodeIcon size={17} /> }] },
13
+ { label: "Core concepts", items: [{ label: "Routing", href: "#routing", icon: <RouteIcon size={17} /> }, { label: "Configuration", href: "#configuration", icon: <SettingsIcon size={17} />, badge: <Badge variant="success">New</Badge> }] },
14
+ ]} footer={<a href="/api/health">API status</a>} />
15
+ <div class="docs-main">{children}</div>
16
+ </div>
17
+ ));
@@ -0,0 +1,12 @@
1
+ import { defineApi } from "@cocoframe/core";
2
+ import { schema } from "@cocoframe/schema";
3
+
4
+ export default defineApi({
5
+ id: "health",
6
+ method: "GET",
7
+ output: schema.object({
8
+ ok: schema.literal(true),
9
+ framework: schema.string(),
10
+ }),
11
+ handle: () => ({ ok: true, framework: "cocoframe" }),
12
+ });
@@ -0,0 +1,25 @@
1
+ import { definePage } from "@cocoframe/core";
2
+ import ArrowRightIcon from "@cocoframe/icons/linear/arrow-right";
3
+ import CheckIcon from "@cocoframe/icons/linear/check-circle";
4
+ import CodeIcon from "@cocoframe/icons/linear/code-square";
5
+ import MagnifierIcon from "@cocoframe/icons/linear/magnifier";
6
+ import { Alert, Badge, Card, Heading, SyntaxHighlighter, Table, Text } from "@cocoframe/ui";
7
+
8
+ const installCode = `npm create cocoframe@latest my-app\ncd my-app\nnpm run dev`;
9
+ const pageCode = `import { definePage } from "@cocoframe/core";\n\nexport default definePage({\n meta: { title: "Hello" },\n view: () => <h1>Hello from CocoFrame</h1>,\n});`;
10
+
11
+ export default definePage({
12
+ meta: { title: "{{PROJECT_NAME}} — Documentation", description: "A searchable, server-first documentation template using official CocoFrame components and icons." },
13
+ view: () => <main class="docs-page">
14
+ <header class="docs-topbar"><a class="mobile-brand" href="/"><CodeIcon size={21} /> Atlas Docs</a><label class="docs-search"><MagnifierIcon size={17} /><span class="coco-visually-hidden">Search documentation</span><input type="search" placeholder="Search documentation…" /></label><a href="https://github.com/wiryosaputraofficial/cocoframe">GitHub</a></header>
15
+ <article class="docs-article">
16
+ <section id="introduction"><Badge variant="success"><CheckIcon size={14} /> Server-first documentation</Badge><Heading level={1} size="xlarge">Build documentation people can actually navigate.</Heading><Text size="large" tone="muted">Atlas Docs combines semantic navigation, accessible code samples, and a content layout that stays useful without browser JavaScript.</Text><div class="docs-actions"><a class="primary-link" href="#installation">Get started <ArrowRightIcon size={16} /></a><a class="secondary-link" href="#routing">Explore routing</a></div></section>
17
+
18
+ <section id="installation"><Heading level={2}>Installation</Heading><Text tone="muted">Create a project from the public registry and start the local development server.</Text><Card class="code-card"><SyntaxHighlighter code={installCode} language="bash" label="Install CocoFrame" showLineNumbers /></Card><Alert variant="info"><strong>Tip:</strong> Run <code>npm run inspect</code> to give developers and AI assistants a compact project manifest.</Alert></section>
19
+
20
+ <section id="routing"><Heading level={2}>File-based routing</Heading><Text tone="muted">A page owns its loader, metadata, and view. File names remain predictable and easy to scan.</Text><Card class="code-card"><SyntaxHighlighter code={pageCode} language="tsx" label="CocoFrame page" showLineNumbers /></Card><Table caption="Route conventions" headers={["File", "URL", "Purpose"]} rows={[["index.page.tsx", "/", "Static page"],["blog/[slug].page.tsx", "/blog/:slug", "Dynamic page"],["api/health.route.ts", "/api/health", "Typed API route"]]} striped /></section>
21
+
22
+ <section id="configuration"><Heading level={2}>Configuration</Heading><Text tone="muted">Keep cross-cutting behavior explicit in <code>cocoframe.config.ts</code>.</Text><div class="concept-grid"><Card><CodeIcon size={22} /><Heading level={3} size="medium">Typed config</Heading><Text tone="muted">Editors validate middleware, metadata, health checks, and asset options.</Text></Card><Card><CheckIcon size={22} /><Heading level={3} size="medium">Secure defaults</Heading><Text tone="muted">Security behavior stays outside render code and remains easy to inspect.</Text></Card></div></section>
23
+ </article>
24
+ </main>,
25
+ });
@@ -0,0 +1,31 @@
1
+ :root { color: #15231c; background: #fff; font-family: Inter, ui-sans-serif, system-ui, sans-serif; }
2
+ * { box-sizing: border-box; }
3
+ html { scroll-behavior: smooth; }
4
+ body { margin: 0; }
5
+ a { color: inherit; }
6
+ .docs-shell { min-height: 100vh; display: grid; grid-template-columns: 270px minmax(0,1fr); }
7
+ .docs-shell > .coco-sidebar { height: 100vh; border-right: 1px solid #dde7e1; background: #fbfdfc; position: sticky; top: 0; }
8
+ .docs-brand, .mobile-brand { display: flex; align-items: center; gap: 9px; color: #187a55; font-weight: 850; text-decoration: none; }
9
+ .docs-main { min-width: 0; }
10
+ .docs-topbar { min-height: 68px; padding: 0 clamp(20px,4vw,52px); display: flex; align-items: center; justify-content: flex-end; gap: 22px; border-bottom: 1px solid #e2eae5; position: sticky; top: 0; z-index: 5; background: rgba(255,255,255,.94); backdrop-filter: blur(14px); }
11
+ .mobile-brand { display: none; margin-right: auto; }
12
+ .docs-search { width: min(380px,50vw); min-height: 42px; display: flex; align-items: center; gap: 9px; padding: 0 13px; border: 1px solid #d7e2dc; border-radius: 10px; color: #658074; background: #f8fbf9; }
13
+ .docs-search input { width: 100%; border: 0; outline: 0; background: transparent; font: inherit; }
14
+ .docs-topbar > a:last-child { color: #187a55; font-weight: 750; }
15
+ .docs-article { width: min(850px,calc(100% - 40px)); margin: 0 auto; padding: 70px 0 120px; }
16
+ .docs-article section { padding: 42px 0; scroll-margin-top: 80px; border-bottom: 1px solid #e4ece7; }
17
+ .docs-article section:first-child { padding-top: 20px; }
18
+ .docs-article .coco-heading { max-width: 780px; margin: 16px 0; letter-spacing: -.035em; }
19
+ .docs-article .coco-text { max-width: 720px; line-height: 1.75; }
20
+ .docs-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 26px; }
21
+ .primary-link, .secondary-link { min-height: 44px; padding: 0 17px; display: inline-flex; align-items: center; gap: 8px; border-radius: 9px; font-weight: 750; text-decoration: none; }
22
+ .primary-link { color: white; background: #187a55; }.secondary-link { border: 1px solid #cfded6; }
23
+ .code-card { padding: 0; margin: 22px 0; overflow: hidden; }
24
+ .code-card .coco-syntax { border: 0; }
25
+ .docs-article .coco-alert { margin-block: 18px; }
26
+ .docs-article .coco-table-wrap { margin-top: 20px; }
27
+ .concept-grid { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: 16px; margin-top: 24px; }
28
+ .concept-grid .coco-card { padding: 24px; }
29
+ .concept-grid svg { color: #187a55; }
30
+ @media (max-width: 820px) { .docs-shell { grid-template-columns: 1fr; } .docs-shell > .coco-sidebar { display: none; } .mobile-brand { display: flex; } .docs-topbar { padding-inline: 16px; } .docs-search { width: min(300px,45vw); } }
31
+ @media (max-width: 520px) { .docs-topbar > a:last-child { display: none; } .docs-search { width: 44px; } .docs-search input { display: none; } .docs-article { width: min(100% - 28px,850px); padding-top: 40px; } .concept-grid { grid-template-columns: 1fr; } }
@@ -0,0 +1,2 @@
1
+ import { defineConfig } from "@cocoframe/core";
2
+ export default defineConfig({ language: "en", siteName: "{{PROJECT_NAME}}", stylesheets: ["/styles/app.css"], openapi: { title: "{{PROJECT_NAME}} API", version: "1.0.0" } });
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "cocoframe dev .",
8
+ "build": "cocoframe build .",
9
+ "start": "cocoframe start .",
10
+ "check": "tsc --noEmit",
11
+ "inspect": "cocoframe inspect .",
12
+ "generate": "cocoframe generate ."
13
+ },
14
+ "dependencies": {
15
+ "@cocoframe/core": "0.0.1",
16
+ "@cocoframe/icons": "0.0.1",
17
+ "@cocoframe/jsx": "0.0.1",
18
+ "@cocoframe/schema": "0.0.1",
19
+ "@cocoframe/ui": "0.0.1"
20
+ },
21
+ "devDependencies": {
22
+ "@cocoframe/cli": "0.0.1",
23
+ "@types/node": "^24.0.0",
24
+ "typescript": "^5.9.0"
25
+ }
26
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2023",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "strict": true,
7
+ "noUncheckedIndexedAccess": true,
8
+ "exactOptionalPropertyTypes": true,
9
+ "verbatimModuleSyntax": true,
10
+ "allowImportingTsExtensions": true,
11
+ "jsx": "react-jsx",
12
+ "jsxImportSource": "@cocoframe/jsx",
13
+ "skipLibCheck": true,
14
+ "noEmit": true
15
+ },
16
+ "include": ["app/**/*.ts", "app/**/*.tsx", "cocoframe.config.ts"]
17
+ }
@@ -0,0 +1,10 @@
1
+ # {{PROJECT_NAME}}
2
+
3
+ SEO-ready marketing template created with CocoFrame components and Solar Linear icons.
4
+
5
+ ```bash
6
+ npm install
7
+ npm run dev
8
+ ```
9
+
10
+ Customize `app/routes/index.page.tsx`, then run `npm run check` and `npm run build`.
@@ -0,0 +1,8 @@
1
+ node_modules/
2
+ .cocoframe/
3
+ dist/
4
+ coverage/
5
+ *.log
6
+ .env
7
+ .env.*
8
+ !.env.example
@@ -0,0 +1,13 @@
1
+ import { defineLayout } from "@cocoframe/core";
2
+ import StarsMinimalisticIcon from "@cocoframe/icons/linear/stars-minimalistic";
3
+
4
+ export default defineLayout(({ children }) => (
5
+ <>
6
+ <header class="marketing-header">
7
+ <a class="marketing-brand" href="/"><StarsMinimalisticIcon size={25} label="Northstar" /> <span>Northstar</span></a>
8
+ <nav aria-label="Primary navigation"><a href="#features">Features</a><a href="#results">Results</a><a href="#contact">Contact</a></nav>
9
+ <a class="header-cta" href="#contact">Start free</a>
10
+ </header>
11
+ {children}
12
+ </>
13
+ ));
@@ -0,0 +1,12 @@
1
+ import { defineApi } from "@cocoframe/core";
2
+ import { schema } from "@cocoframe/schema";
3
+
4
+ export default defineApi({
5
+ id: "health",
6
+ method: "GET",
7
+ output: schema.object({
8
+ ok: schema.literal(true),
9
+ framework: schema.string(),
10
+ }),
11
+ handle: () => ({ ok: true, framework: "cocoframe" }),
12
+ });
@@ -0,0 +1,39 @@
1
+ import { definePage } from "@cocoframe/core";
2
+ import ArrowRightIcon from "@cocoframe/icons/linear/arrow-right";
3
+ import BoltIcon from "@cocoframe/icons/linear/bolt";
4
+ import GraphUpIcon from "@cocoframe/icons/linear/graph-up";
5
+ import ShieldCheckIcon from "@cocoframe/icons/linear/shield-check";
6
+ import { Badge, Card, Container, Heading, Inline, Stack, Stat, Text } from "@cocoframe/ui";
7
+
8
+ const features = [
9
+ { icon: <BoltIcon size={22} />, title: "Launch faster", copy: "Ship a polished product page with server-rendered HTML and a tiny browser footprint." },
10
+ { icon: <GraphUpIcon size={22} />, title: "Convert clearly", copy: "Guide visitors with focused sections, measurable proof, and accessible calls to action." },
11
+ { icon: <ShieldCheckIcon size={22} />, title: "Stay reliable", copy: "Typed routes, secure defaults, and predictable files keep the project easy to maintain." },
12
+ ];
13
+
14
+ export default definePage({
15
+ meta: {
16
+ title: "{{PROJECT_NAME}} — Marketing template",
17
+ description: "A responsive, SEO-ready marketing template built with CocoFrame components.",
18
+ },
19
+ view: () => <main>
20
+ <Container as="section" size="large" class="marketing-hero">
21
+ <Stack gap="large" class="hero-copy">
22
+ <Badge variant="success"><BoltIcon size={14} /> Built with CocoFrame</Badge>
23
+ <Heading level={1} size="xlarge">Turn your next idea into a clear, fast product story.</Heading>
24
+ <Text size="large" tone="muted">Northstar gives modern teams an SEO-ready foundation made from official CocoFrame components and Solar Linear icons.</Text>
25
+ <Inline gap="medium"><a class="primary-link" href="#contact">Start building <ArrowRightIcon size={17} /></a><a class="secondary-link" href="#features">Explore features</a></Inline>
26
+ </Stack>
27
+ <Card class="hero-panel"><span class="panel-label">Weekly momentum</span><strong>+38%</strong><div class="spark-bars" aria-label="Growth chart"><i></i><i></i><i></i><i></i><i></i><i></i></div><small>More qualified product conversations</small></Card>
28
+ </Container>
29
+
30
+ <section id="features"><Container as="div" size="large" class="feature-section">
31
+ <Stack gap="small" class="section-heading"><Badge variant="primary">Product foundation</Badge><Heading level={2}>Everything needed for a confident launch</Heading><Text tone="muted">Composable sections stay readable for developers, search engines, and AI coding assistants.</Text></Stack>
32
+ <div class="feature-grid">{features.map((feature) => <Card class="feature-card"><span class="feature-icon">{feature.icon}</span><Heading level={3} size="medium">{feature.title}</Heading><Text tone="muted">{feature.copy}</Text></Card>)}</div>
33
+ </Container></section>
34
+
35
+ <section id="results"><Container as="div" size="large" class="results"><Stat label="Lighthouse-ready" value="100" trend="Semantic server HTML" tone="positive" /><Stat label="Browser runtime" value="Opt-in" trend="Islands only when needed" tone="positive" /><Stat label="Shared UI" value="80+" trend="Official primitives" tone="positive" /></Container></section>
36
+
37
+ <section id="contact"><Container as="div" size="large" class="final-cta"><Stack gap="medium"><Badge variant="success">Ready when you are</Badge><Heading level={2}>Build the next version of your product today.</Heading><Text tone="muted">Replace this copy with your offer, connect the typed API, and deploy.</Text><a class="primary-link" href="mailto:hello@example.com">Talk to the team <ArrowRightIcon size={17} /></a></Stack></Container></section>
38
+ </main>,
39
+ });
@@ -0,0 +1,32 @@
1
+ :root { color: #10231b; background: #fbfdfc; font-family: Inter, ui-sans-serif, system-ui, sans-serif; }
2
+ * { box-sizing: border-box; }
3
+ html { scroll-behavior: smooth; }
4
+ body { margin: 0; }
5
+ a { color: inherit; }
6
+ .marketing-header { min-height: 72px; padding: 0 clamp(20px, 5vw, 72px); display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 28px; border-bottom: 1px solid #dce9e1; background: rgba(255,255,255,.92); position: sticky; top: 0; z-index: 10; backdrop-filter: blur(16px); }
7
+ .marketing-brand { display: inline-flex; align-items: center; gap: 10px; color: #187a55; font-weight: 850; text-decoration: none; }
8
+ .marketing-header nav { display: flex; gap: 28px; font-size: 14px; }
9
+ .marketing-header nav a { text-decoration: none; }
10
+ .header-cta, .primary-link, .secondary-link { display: inline-flex; align-items: center; justify-content: center; gap: 8px; width: max-content; min-height: 44px; padding: 0 18px; border-radius: 10px; font-weight: 750; text-decoration: none; }
11
+ .header-cta { justify-self: end; color: white; background: #187a55; }
12
+ .primary-link { color: white; background: #187a55; box-shadow: 0 10px 24px rgba(24,122,85,.18); }
13
+ .secondary-link { border: 1px solid #cddfd5; background: white; }
14
+ .marketing-hero { min-height: 690px; display: grid; grid-template-columns: minmax(0,1.15fr) minmax(320px,.85fr); align-items: center; gap: clamp(40px,7vw,100px); padding-block: clamp(72px,10vw,130px); }
15
+ .hero-copy .coco-heading { max-width: 760px; margin: 0; letter-spacing: -.045em; }
16
+ .hero-copy .coco-text { max-width: 650px; line-height: 1.75; }
17
+ .hero-panel { min-height: 410px; padding: 34px; display: flex; flex-direction: column; justify-content: flex-end; overflow: hidden; color: white; background: linear-gradient(145deg,#0d2d22,#187a55); box-shadow: 0 28px 70px rgba(16,52,40,.22); }
18
+ .hero-panel strong { margin: 12px 0 24px; font-size: clamp(58px,8vw,92px); letter-spacing: -.06em; }
19
+ .panel-label, .hero-panel small { color: #c6ead9; }
20
+ .spark-bars { height: 120px; display: flex; align-items: end; gap: 10px; margin-bottom: 20px; }
21
+ .spark-bars i { flex: 1; height: 38%; border-radius: 7px 7px 2px 2px; background: rgba(255,255,255,.76); }
22
+ .spark-bars i:nth-child(2) { height: 48%; }.spark-bars i:nth-child(3) { height: 43%; }.spark-bars i:nth-child(4) { height: 66%; }.spark-bars i:nth-child(5) { height: 76%; }.spark-bars i:nth-child(6) { height: 92%; }
23
+ .feature-section { padding-block: 90px; }
24
+ .section-heading { max-width: 690px; margin-bottom: 36px; }
25
+ .feature-grid { display: grid; grid-template-columns: repeat(3,minmax(0,1fr)); gap: 18px; }
26
+ .feature-card { padding: 28px; }
27
+ .feature-icon { width: 46px; height: 46px; display: grid; place-items: center; margin-bottom: 28px; border-radius: 12px; color: #187a55; background: #e8f5ee; }
28
+ .results { display: grid; grid-template-columns: repeat(3,minmax(0,1fr)); gap: 16px; padding-block: 40px 90px; }
29
+ .final-cta { margin-bottom: 80px; padding-block: 70px; text-align: center; border: 1px solid #d7e8df; border-radius: 24px; background: linear-gradient(135deg,#f2faf6,#e4f4eb); }
30
+ .final-cta .coco-stack { align-items: center; max-width: 720px; margin: auto; }
31
+ @media (max-width: 820px) { .marketing-header { grid-template-columns: 1fr auto; } .marketing-header nav { display: none; } .marketing-hero { min-height: auto; grid-template-columns: 1fr; } .feature-grid, .results { grid-template-columns: 1fr; } .hero-panel { min-height: 340px; } }
32
+ @media (max-width: 420px) { .marketing-header { padding-inline: 16px; } .header-cta { padding-inline: 12px; } .marketing-hero { padding-block: 54px; } }
@@ -0,0 +1,8 @@
1
+ import { defineConfig } from "@cocoframe/core";
2
+
3
+ export default defineConfig({
4
+ language: "en",
5
+ siteName: "{{PROJECT_NAME}}",
6
+ stylesheets: ["/styles/app.css"],
7
+ openapi: { title: "{{PROJECT_NAME}} API", version: "1.0.0" },
8
+ });
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "cocoframe dev .",
8
+ "build": "cocoframe build .",
9
+ "start": "cocoframe start .",
10
+ "check": "tsc --noEmit",
11
+ "inspect": "cocoframe inspect .",
12
+ "generate": "cocoframe generate ."
13
+ },
14
+ "dependencies": {
15
+ "@cocoframe/core": "0.0.1",
16
+ "@cocoframe/icons": "0.0.1",
17
+ "@cocoframe/jsx": "0.0.1",
18
+ "@cocoframe/schema": "0.0.1",
19
+ "@cocoframe/ui": "0.0.1"
20
+ },
21
+ "devDependencies": {
22
+ "@cocoframe/cli": "0.0.1",
23
+ "@types/node": "^24.0.0",
24
+ "typescript": "^5.9.0"
25
+ }
26
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2023",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "strict": true,
7
+ "noUncheckedIndexedAccess": true,
8
+ "exactOptionalPropertyTypes": true,
9
+ "verbatimModuleSyntax": true,
10
+ "allowImportingTsExtensions": true,
11
+ "jsx": "react-jsx",
12
+ "jsxImportSource": "@cocoframe/jsx",
13
+ "skipLibCheck": true,
14
+ "noEmit": true
15
+ },
16
+ "include": ["app/**/*.ts", "app/**/*.tsx", "cocoframe.config.ts"]
17
+ }