Avenx-JS is a lightweight, experimental frontend framework designed for simplicity and performance. It features a custom compiler-driven component system, Proxy-based reactivity, scoped CSS, and powerful CLI toolingβall with zero runtime dependencies.
Modern frontend development often requires complex build chains and heavy runtime libraries. Avenx explores a different path by providing:
- β‘ Zero Boilerplate: Logic, state, and template in a single unified component file.
- π Transparent Reactivity: Automatic UI updates via JavaScript Proxies without manual
setStateorrefcalls. - π¨ Scoped Styling: CSS is automatically scoped to your component using hashed class generation.
- π οΈ Integrated Tooling: A built-in CLI handles project scaffolding, component generation, and development servers.
- π¦ Lightweight Core: Minimal runtime footprint for fast loading and execution.
State management is built directly into the core. Changing a property on the state object automatically triggers a re-render of only the affected parts of the DOM.
Define your UI using standard HTML with added superpowers. Components support state, computed properties, and actions (methods) defined directly in the .component.js file.
Styles defined in .component.css are automatically scoped to that specific component. Use the <@global> tag for global variables and the <@css> tag for component-specific styles.
Shared state across multiple components is handled via Bridges. These are global reactive objects that any component can subscribe to and update.
Generate components, pages, and bridges with a single command. The built-in dev server provides hot-reloading for a seamless development experience.
npm install avenx-core# Initialize project structure
npx avenx init
# Create a new component
npx avenx g counter
# Start development server
npx avenx serveYour app will be running at http://localhost:3000.
An Avenx component consists of two files: <name>.component.js and <name>.component.css.
<state count="0" title="Counter" />
<computed name="doubleCount" value="count * 2" />
<action name="increment"> state.count++; </action>
<div @css card>
<h1>{{ title }}</h1>
<p>Count: {{ count }} (Double: {{ doubleCount }})</p>
<button @css button @click="increment()">Increment</button>
</div><@global>
@def primary-color #646cff;
@def bg-color #242424;
</@global>
<@css>
card {
padding: 2rem;
border-radius: 8px;
background: @bg-color;
}
button {
background-color: @primary-color;
color: white;
border: none;
padding: 0.6em 1.2em;
cursor: pointer;
}
</@css>Bridges allow you to share reactive state between components without complex prop drilling. They are defined in the src/global/ directory.
npx avenx g bridge authimport { AvenxBridge } from 'avenx-core/runtime';
export default class AuthBridge extends AvenxBridge {
constructor() {
super();
this.isLoggedIn = false;
this.user = {
name: 'Guest',
role: 'visitor',
};
}
}Bridges are automatically available in your component templates and actions.
<p>Welcome, {{ AuthBridge.user.name }}</p>
<action name="login"> AuthBridge.isLoggedIn = true; AuthBridge.user.name = 'John Doe'; </action>Pages are special components designed for top-level routing. They reside in src/pages/.
npx avenx g page profilePages use the same syntax as components (<state>, <computed>, <action>).
<state userId="123" />
<div class="profile-page">
<h1>User Profile</h1>
<p>Viewing ID: {{ userId }}</p>
</div>Avenx-JS projects built with the CLI automatically scan, compile, and register page components. In your main application entry point, you initialize the built-in router with the route mappings:
import { AvenxApp } from 'avenx-core/runtime';
const app = new AvenxApp({ target: '#app' });
// Initialize the router mapping paths to page component names.
// Note: Pages inside src/pages/ are automatically registered by the compiler.
app.initRouter({
'': 'Home',
'#/': 'Home',
'#/profile/:userId': 'Profile',
});Components can be nested by using their name in PascalCase. Use <slot /> tags to define where transcluded child content should render:
<Navbar />
<main>
<Sidebar />
<slot />
</main>Use the @ prefix to bind event listeners:
<button @click="count++">Inline Action</button> <input @input="state.text = event.target.value" />Avenx-JS supports Sass/SCSS, PostCSS, and Less preprocessors inside .component.css or .page.css files.
To enable a preprocessor, add the style settings to your avenx.config.json file:
{
"style": {
"preprocessor": "scss"
}
}Available preprocessor options are "sass", "scss", "postcss", and "less".
When a preprocessor is enabled:
- You can write nested SCSS/Sass styles, variables, functions, and mixins directly inside your stylesheet.
- The compiler will automatically run your styles through the preprocessor module before applying Avenx-JS scoping logic.
- If the configured preprocessor package (e.g.
sass) is not installed, the compiler gracefully falls back to raw CSS processing and logs a warning.
A typical Avenx project looks like this:
my-avenx-app/
βββ src/
β βββ components/ # UI Components
β β βββ counter/
β β βββ counter.component.js
β β βββ counter.component.css
β βββ pages/ # Application Pages (Routed)
β βββ global/ # Shared Bridges & Styles
β βββ main.app.js # App entry point & registration
βββ dist/ # Compiled bundle (generated)
βββ index.html # Main entry HTML
βββ package.json
| Command | Description |
|---|---|
avenx init |
Scaffolds a new project structure. |
avenx g <name> |
Generates a new component (alias: generate). |
avenx g p <name> |
Generates a new page for routing (alias: g page). |
avenx g bridge <name> |
Generates a new shared reactive bridge. |
avenx g guard <name> |
Generates a new route guard. |
avenx d <name> |
Deletes a component (alias: destroy). |
avenx d p <name> |
Deletes a page (alias: d page). |
avenx d bridge <name> |
Deletes a shared reactive bridge. |
avenx d guard <name> |
Deletes a route guard. |
avenx build (or b) |
Compiles the project into dist/. |
avenx clean |
Clears build output directory. |
avenx check (or lint) |
Validates component templates without building. |
avenx serve [port] |
Starts the dev server with hot-reload (default: 3000). |
avenx watch (or w) |
Watch for file changes and rebuild automatically. |
| Option | Description |
|---|---|
--dry-run, -d |
Preview actions for generators and destructors without writing/deleting files. |
--port, -p <port> |
Configure the port for the development server. |
--host, -h <host> |
Configure the host for the development server (default: localhost). |
Avenx-JS provides comprehensive testing support, from fast unit tests to full browser E2E test suites:
- Unit Tests:
npm run test:unit - Integration Tests:
npm run test:integration - System Benchmarks & CLI Tests:
npm run test:system - Playwright End-to-End (E2E) Browser Tests:
npm run test:e2e - Full Test Suite:
npm test
Playwright E2E tests run against Chromium, Firefox, and WebKit in headless mode. See test/e2e/README.md for detailed configuration and usage guides.
This project is currently a proof-of-concept framework and actively evolving.
We are actively looking for contributors! Avenx-JS is PR and first-time open-source friendly. Whether you are fixing a typo, updating documentation, or adding features, we welcome your help.
Check out our CONTRIBUTING.md to get started!
Distributed under the MIT License. See LICENSE for more information.
If you like what we're building, please give us a β on GitHub!
Built with β€οΈ by the Avenx Team.
