From f92e7af18d1a3d95e1ce2ca3b4a64abb011c1266 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Fri, 26 Dec 2025 19:11:01 -0800 Subject: [PATCH] FORGE --- .gitignore | 34 ++++++ README.md | 66 +++++++++++ bun.lock | 31 +++++ examples/button.tsx | 150 +++++++++++++++++++++++ examples/helpers.tsx | 78 ++++++++++++ examples/index.tsx | 77 ++++++++++++ examples/profile.tsx | 277 +++++++++++++++++++++++++++++++++++++++++++ package.json | 17 +++ server.tsx | 23 ++++ src/index.tsx | 126 ++++++++++++++++++++ src/types.ts | 121 +++++++++++++++++++ tsconfig.json | 30 +++++ 12 files changed, 1030 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 bun.lock create mode 100644 examples/button.tsx create mode 100644 examples/helpers.tsx create mode 100644 examples/index.tsx create mode 100644 examples/profile.tsx create mode 100644 package.json create mode 100644 server.tsx create mode 100644 src/index.tsx create mode 100644 src/types.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a14702c --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +# dependencies (bun install) +node_modules + +# output +out +dist +*.tgz + +# code coverage +coverage +*.lcov + +# logs +logs +_.log +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# caches +.eslintcache +.cache +*.tsbuildinfo + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/README.md b/README.md new file mode 100644 index 0000000..171e0a5 --- /dev/null +++ b/README.md @@ -0,0 +1,66 @@ +# forge + +Example: + +```tsx +import { define } from "forge" + +export const Button = define("button", { + layout: { + padding: 20, + }, + look: { + background: "blue", + }, + variants: { + kind: { + danger: { look: { background: "red" } }, + warning: { look: { background: "yellow" } }, + } + }, +}) + +// Usage + + + + +export const Profile = define("div", { + layout: { + padding: 50, + }, + look: { + background: "red", + }, + + parts: { + header: { layout: { display: "flex" } }, + avatar: { layout: { width: 50 } }, + bio: { look: { color: "gray" } }, + }, + + variants: { + size: { + small: { + parts: { avatar: { layout: { width: 20 }}} + } + } + }, + + render({ props, parts: { root, header, avatar, bio } }) { + return ( + +
+ + {props.bio} +
+
+ ) + }, +}) + +// Usage: +import { Profile } from './whatever' + + +``` diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..b9ae47c --- /dev/null +++ b/bun.lock @@ -0,0 +1,31 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "forge", + "dependencies": { + "hono": "^4.11.3", + }, + "devDependencies": { + "@types/bun": "latest", + }, + "peerDependencies": { + "typescript": "^5", + }, + }, + }, + "packages": { + "@types/bun": ["@types/bun@1.3.5", "", { "dependencies": { "bun-types": "1.3.5" } }, "sha512-RnygCqNrd3srIPEWBd5LFeUYG7plCoH2Yw9WaZGyNmdTEei+gWaHqydbaIRkIkcbXwhBT94q78QljxN0Sk838w=="], + + "@types/node": ["@types/node@25.0.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA=="], + + "bun-types": ["bun-types@1.3.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw=="], + + "hono": ["hono@4.11.3", "", {}, "sha512-PmQi306+M/ct/m5s66Hrg+adPnkD5jiO6IjA7WhWw0gSBSo1EcRegwuI1deZ+wd5pzCGynCcn2DprnE4/yEV4w=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + } +} diff --git a/examples/button.tsx b/examples/button.tsx new file mode 100644 index 0000000..12e2b3b --- /dev/null +++ b/examples/button.tsx @@ -0,0 +1,150 @@ +import { define } from '../src' +import { Layout, ExampleSection } from './helpers' + +const Button = define('Button', { + base: 'button', + + layout: { + padding: "12px 24px", + display: "inline-flex", + alignItems: "center", + justifyContent: "center", + gap: 8, + }, + + look: { + background: "#3b82f6", + color: "white", + border: "none", + borderRadius: 8, + fontSize: 16, + fontWeight: 600, + cursor: "pointer", + transition: "all 0.2s ease", + userSelect: "none", + boxShadow: "0 4px 6px rgba(59, 130, 246, 0.4), 0 2px 4px rgba(0, 0, 0, 0.1)", + transform: "translateY(0)", + }, + + states: { + ":not(:disabled):hover": { + look: { + transform: 'translateY(-2px) !important', + filter: 'brightness(1.05)' + } + }, + ":not(:disabled):active": { + look: { + transform: 'translateY(1px) !important', + boxShadow: '0 2px 3px rgba(0, 0, 0, 0.2) !important' + } + }, + }, + + variants: { + intent: { + primary: { + look: { + background: "#3b82f6", + color: "white", + boxShadow: "0 4px 6px rgba(59, 130, 246, 0.4), 0 2px 4px rgba(0, 0, 0, 0.1)", + }, + }, + secondary: { + look: { + background: "#f3f4f6", + color: "#374151", + boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.06)", + }, + }, + danger: { + look: { + background: "#ef4444", + color: "white", + boxShadow: "0 4px 6px rgba(239, 68, 68, 0.4), 0 2px 4px rgba(0, 0, 0, 0.1)", + }, + }, + ghost: { + look: { + background: "transparent", + color: "#aaa", + boxShadow: "0 4px 6px rgba(0, 0, 0, 0.2), 0 2px 4px rgba(0, 0, 0, 0.1)", + border: "1px solid #eee", + }, + }, + }, + size: { + small: { + layout: { + padding: "8px 16px", + }, + look: { + fontSize: 14, + }, + }, + large: { + layout: { + padding: "16px 32px", + }, + look: { + fontSize: 18, + }, + }, + }, + disabled: { + look: { + opacity: 0.5, + cursor: "not-allowed", + }, + }, + }, +}) + +const ButtonRow = define('ButtonRow', { + layout: { + display: 'flex', + gap: 16, + flexWrap: 'wrap', + alignItems: 'center', + } +}) + +export const ButtonExamplesPage = () => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +) diff --git a/examples/helpers.tsx b/examples/helpers.tsx new file mode 100644 index 0000000..ea46141 --- /dev/null +++ b/examples/helpers.tsx @@ -0,0 +1,78 @@ +import { define, Styles } from '../src' + +export const Body = define('Body', { + base: 'body', + layout: { + margin: 0, + padding: '40px 20px', + + }, + look: { + fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif", + background: '#f3f4f6', + } +}) + +const Container = define('Container', { + layout: { + maxWidth: 1200, + margin: '0 auto' + } +}) + +export const Header = define('Header', { + base: 'h1', + layout: { + marginBottom: 40, + }, + look: { + color: '#111827' + } +}) + +export const ExampleSection = define('ExampleSection', { + layout: { + marginBottom: 40 + }, + parts: { + Header: { + base: 'h2', + layout: { + marginBottom: 16 + }, + look: { + color: '#374151', + fontSize: 18 + } + } + }, + render({ props, parts: { Root, Header } }) { + return ( + +
{props.title}
+ {props.children} +
+ ) + } +}) + +export const Layout = define({ + render({ props }) { + return ( + + + + + {props.title} + + + + +
{props.title}
+ {props.children} +
+ + + ) + } +}) diff --git a/examples/index.tsx b/examples/index.tsx new file mode 100644 index 0000000..01f8874 --- /dev/null +++ b/examples/index.tsx @@ -0,0 +1,77 @@ +export const IndexPage = () => ( + + + + + Forge Examples +