Loading repository data…
Loading repository data…
cnuebred / repository
FrontForge is a minimalist TypeScript-based frontend framework that allows you to describe your entire HTML structure, styles, and logic in code - and compile it into a complete, standalone HTML file. Ideal for generating simple UI apps or static pages directly from TypeScript.

FrontForge is a minimalist TypeScript-based frontend framework that allows you to describe your entire HTML structure, styles, and logic in code - and compile it into a complete, standalone HTML file. Ideal for generating simple UI apps or static pages directly from TypeScript.
This project was created out of pure passion by a beginner developer who strongly believes in simplicity, transparency, and minimal dependencies. FrontForge is intentionally built to stay lean, with no heavy abstractions or runtime overhead — just TypeScript and the browser DOM.
Widget and ContainerWidgetPocket (based on Proxy)ForgeBundleFlex() and Grid().event()🚀 Quickstart: Express.js Proof of Concept (PoC)
Want to see it in action immediately? Here is a single-file, copy-paste Proof of Concept. This script automatically spins up an Express server, generates a client-side TypeScript file on the fly, bundles it using FrontForge, and serves the rendered HTML directly to your browser.
import express from 'express';
import { ForgeBundle } from '@cnuebred/frontforge';
import fs from 'fs';
const app = express();
const PORT = 3000;
app.get('/', async (req, res) => {
if (!fs.existsSync('client.ts')) {
fs.writeFileSync('client.ts', `
import { Widget, Flex, flex_direction_e } from '@cnuebred/frontforge';
const title = new Widget('h1', 'Witaj we FrontForge! 🚀');
const desc = new Widget('p', 'Strona wygenerowana i zaserwowana przez Express.js.');
Flex([title, desc], {
direction: flex_direction_e.column,
align_items: 'center'
}).hook('body');
`);
}
const bundle = new ForgeBundle();
bundle.head.title('FrontForge PoC');
await bundle.script('./client.ts');
const html = await bundle.build();
res.send(html);
});
app.listen(PORT, () => {
console.log(`✅ Server is running: http://localhost:${PORT}`);
});
(Just run npm i express @cnuebred/frontforge, save this as server.ts, and execute it with npx tsx server.ts!)
In an era of increasingly complex and oversized web frameworks, FrontForge takes a step back to re-evaluate how we build the web, focusing on the following core pillars:
To get started with FrontForge, you'll need a Node.js environment (v16+ LTS is highly recommended) and TypeScript installed in your project.
Initialize your project and install the library via npm:
mkdir my-frontforge-app
cd my-frontforge-app
npm init -y
npm install @cnuebred/frontforge
npm install -D typescript tsx
npx tsc --init
Pro Tip: We recommend using tsx or ts-node to execute your build scripts directly.
The Widget class is the atomic unit of FrontForge. It represents a single HTML element in the DOM. When instantiating a Widget, you can utilize an Emmet-style syntax to define the HTML tag and its initial classes simultaneously.
import { Widget } from "@cnuebred/frontforge";
// Define a <button> tag with "btn" and "btn-primary" classes in one string
const btn = new Widget("button.btn.btn-primary", "Click Me!");
// Safely modify classes on the fly using the built-in class operator
btn.class.add("active");
btn.class.toggle("highlight"); // Toggles the class on/off
btn.class.remove("btn-primary");
// Add arbitrary DOM attributes or strictly typed inline styles
btn.attribute = {
id: "submit-button",
disabled: false,
"data-tooltip": "Sends the form securely",
"aria-label": "Submit Form"
};
// Styles use standard JavaScript camelCase syntax which is automatically
// converted to kebab-case CSS by the engine.
btn.style.backgroundColor = "#ff5722";
btn.style.borderRadius = "8px";
// Attach standardized event listeners
btn.event("click", (e) => {
console.log("Button clicked!", e);
});
// Finally, render and attach the widget to the DOM
btn.hook("body"); // Appends to the end of the <body> tag
Dynamic Content Evaluation:
A Widget's content doesn't have to be static. You can pass a callback function that evaluates dynamically every time render() is called.
let userClicks = 0;
// The content function evaluates whenever .render() is triggered
const clickCounter = new Widget("span.badge", () => `Total Clicks: ${userClicks}`);
btn.event("click", () => {
userClicks++;
clickCounter.render(); // Instantly updates the DOM text node
});
ContainerWidget extends the base Widget but adds powerful logic for managing an array of child Widgets. It is essential for constructing complex component trees, managing lists, and handling bulk DOM insertions.
import { ContainerWidget, Widget } from "@cnuebred/frontforge";
const mainCard = new ContainerWidget("div.card.shadow-lg");
const title = new Widget("h2.card-title", "User Profile");
const description = new Widget("p.card-desc", "Manage your account settings below.");
// Chainable additions make tree construction highly readable
mainCard.add(title).add(description);
// List Management Features
// You can iterate over children, map them, or clear them dynamically
const actionRow = new ContainerWidget("div.actions");
actionRow.set([
new Widget("button", "Save"),
new Widget("button", "Cancel")
]);
// If state changes, you can clear the container and re-populate it safely
// without leaving orphaned event listeners in memory.
actionRow.clear();
mainCard.add(actionRow);
mainCard.hook("#app-root"); // Hook into a specific DOM ID
Forget complex useState hooks or verbose state management libraries. Pocket is a lightweight, ES6 Proxy-based state manager. It wraps your data objects, intercepts reads and writes, and allows you to trigger UI updates automatically whenever your state mutates.
import { Pocket, Widget } from "@cnuebred/frontforge";
// 1. Define your initial reactive state
const appState = new Pocket({
score: 0,
user: "Player 1",
theme: "dark"
});
// 2. Create UI widgets that depend on the state
const scoreDisplay = new Widget("h1", () => `${appState.target.user} Score: ${appState.target.score}`);
// 3. React to changes automatically via the setter callback
appState.set_setter_callback((target, property, value) => {
console.log(`State Property [${String(property)}] updated to:`, value);
// Whenever any property changes, we instruct the display to re-render
scoreDisplay.render();
});
// 4. Update the state naturally.
// You don't need special setter functions; just reassign the value!
const incrementBtn = new Widget("button", "Add +10 Points");
incrementBtn.event("click", () => {
appState.target.score += 10; // This seamlessly triggers the setter callback
});
scoreDisplay.hook("body");
incrementBtn.hook("body");
Writing CSS layouts by hand can be tedious. FrontForge provides highly declarative layout wrappers for standard CSS Flexbox and Grid, converting TypeScript configurations into perfectly formatted CSS on the fly.
The Flex wrapper quickly aligns items inside a newly generated ContainerWidget without requiring you to manually write CSS classes.
import { Flex, flex_direction_e, flex_justify_e, flex_align_items_e, Widget } from "@cnuebred/frontforge";
const avatar = new Widget("img").attribute = { src: "avatar.png" };
const username = new Widget("span", "JohnDoe99");
const logoutBtn = new Widget("button", "Logout");
// Creates a flex container holding the 3 widgets
const navbar = Flex([avatar, username, logoutBtn], {
direction: flex_direction_e.row,
justify_content: flex_justify_e.space_between,
align_items: flex_align_items_e.center,
column_gap: "20px",
width: "100%",
height: "64px"
});
navbar.style.borderBottom = "1px solid #ccc";
navbar.hook("body");
This is one of FrontForge's most powerful features. Create complex 2D layouts by literally drawing them in your code using a two-dimensional matrix (an array of arrays). It supports deep CSS Grid customizations including automatic col_span and row_span calculations.
import { Grid, Widget } from "@cnuebred/frontforge";
const header = new Widget("header.bg-blue", "Application Header");
const sidebar = new Widget("aside.bg-gray", "Navigation Menu");
const content = new Widget("main.bg-white", "Main Dashboard Content");
const footer = new Widget("footer.bg-dark", "System Footer");
// Visually define a 2D matrix representing rows and columns
const dashboardLayout = Grid([
// Row 1: Header spans across 2 columns
[ { widget: header, col_span: 2 } ],