JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 2
  • Score
    100M100P100Q78289F
  • License MIT

Zero-dependency component library and SSR/SSG framework

Package Exports

  • b0nes
  • b0nes/atoms
  • b0nes/compose
  • b0nes/framework
  • b0nes/molecules
  • b0nes/organisms
  • b0nes/router
  • b0nes/utils

Readme

b0nes Framework

npm version License: MIT Node.js Version

b0nes is a lightweight, composition-first component library and SSR/SSG framework built with pure JavaScript and no dependencies.

Features

  • 🧩 Atomic Design System - Well-organized component hierarchy (atoms β†’ molecules β†’ organisms)
  • 🎯 Pure JavaScript - No TypeScript, no frameworks. Just functions and JSDoc
  • πŸ“¦ Zero Dependencies - No npm packages required
  • πŸš€ Server-Side Rendering - Built-in SSR support with Node.js
  • πŸ”„ Static Generation - Generate static HTML files
  • πŸ§ͺ Auto-Testing - Built-in test discovery and runner
  • 🎨 Composition - Nested components work seamlessly

Project Structure

b0nes/
β”œβ”€β”€ src/          
β”‚   β”œβ”€β”€ components/           # Component Library
β”‚   β”‚   β”‚   β”œβ”€β”€ atoms/        # Basic elements (button, link, text, etc)
β”‚   β”‚   β”‚   β”œβ”€β”€ molecules/    # Combinations (card, form-group, etc)
β”‚   β”‚   β”‚   └── organisms/    # Sections (header, footer, hero, etc)
β”‚   β”‚   └── utils/
β”‚   └── framework/            # Framework
β”‚       β”œβ”€β”€ compose.js        # Component composition engine
β”‚       β”œβ”€β”€ renderPage.js     # HTML template used to render
β”‚       β”œβ”€β”€ routes.js         # Route definitions
β”‚       β”œβ”€β”€ routes.js         # Route definitions
β”‚       β”œβ”€β”€ pages/            # Page templates
β”‚       └──utils/build/
β”‚                 └── ssg.js  # Static site generation

Installation

git clone https://github.com/iggydotdev/b0nes.git
cd b0nes

No npm install needed! Everything runs with Node.js (v24).

Get Started in 5 Minutes ⚑

Prerequisites

  • Node.js v24 installed
  • A terminal
  • That's it!

Step 1: Clone & Explore (30 seconds)

git clone https://github.com/iggydotdev/b0nes.git
cd b0nes
ls src/components/  # Check out the components type 
ls src/components/atoms #Check out the atom components
...

Step 2: Start Dev Server (30 seconds)

node src/framework/index.js

Dev mode

npm run dev

Dev + hot reload

npm run dev:watch

Open http://localhost:3000 - You'll see a working site! πŸŽ‰

Step 3: Create Your First Component (2 minutes)

# Generate a new atom component
node src/components/utils/generator/index.js atom badge
# Generate a new atom component
npm run generate atom badge

This creates:

src/components/atoms/badge/
β”œβ”€β”€ index.js
β”œβ”€β”€ badge.js
└── badge.test.js

You can now start tweaking/edit src/components/atoms/badge/badge.js:

Step 4: Use Your Component (1 minute)

Edit src/framework/pages/home.js:

export const components = [
    {
        type: 'organism',
        name: 'hero',
        props: {
            slot: [
                {
                    type: 'atom',
                    name: 'text',
                    props: {
                        is: 'h1',
                        slot: 'Welcome to b0nes'
                    }
                },
                {
                    type: 'atom',
                    name: 'text',
                    props: {
                        is: 'p',
                        slot: 'Build modern web applications with ease'
                    }
                }
            ]
        }
    }
];

Refresh http://localhost:3000 - See your changes live! ✨

Step 5: Build for Production (30 seconds)

node src/framework/utils/build/index.js public
npm run build

Your static site is ready in public/:

public/
β”œβ”€β”€ index.html           # Your homepage
└── blog/
    └── [postid]/
        └── index.html   # Dynamic routes

Step 6: Deploy Anywhere (30 seconds)

# Serve locally
npx serve public

# Or deploy to:
# - Netlify
# - Vercel
# - GitHub Pages
# - Any static host!

🎯 What You Just Did

βœ… Built a component-based site
βœ… Zero npm dependencies
βœ… No build tools (except for production)
βœ… Pure JavaScript + HTML
βœ… Ready to deploy


Real-World Example: Complete Landing Page

Here's a complete landing page in one file:

// src/framework/pages/landing.js
export const components = [
    {
        type: 'organism',
        name: 'header',
        props: {
            slot: [
                { type: 'atom', name: 'link', props: { url: '/', slot: 'Home' }},
                { type: 'atom', name: 'link', props: { url: '/pricing', slot: 'Pricing' }},
                { type: 'atom', name: 'link', props: { url: '/docs', slot: 'Docs' }}
            ]
        }
    },
    {
        type: 'organism',
        name: 'hero',
        props: {
            slot: [
                { type: 'atom', name: 'text', props: { is: 'h1', slot: 'Ship Faster' }},
                { type: 'atom', name: 'text', props: { is: 'p', slot: 'Zero deps. Pure JS. Simple.' }},
                { type: 'atom', name: 'button', props: { slot: 'Get Started', className: 'cta' }}
            ]
        }
    },
    {
        type: 'organism',
        name: 'cta',
        props: {
            slot: [
                { type: 'atom', name: 'text', props: { is: 'h2', slot: 'Ready to build?' }},
                { type: 'atom', name: 'button', props: { type: 'submit', slot: 'Start Now' }}
            ]
        }
    },
    {
        type: 'organism',
        name: 'footer',
        props: {
            slot: { type: 'atom', name: 'text', props: { is: 'p', slot: 'Β© 2025 Your Company' }}
        }
    }
];

Add the route in src/framework/routes.js:

import { components as landingComponents } from './pages/landing.js';

export const routes = [
    {
        name: 'Landing',
        pattern: new URLPattern({ pathname: '/landing' }),
        meta: { title: 'Landing Page' },
        components: landingComponents
    }
];

Build it:

node src/framework/utils/build/index.js public

Done! πŸš€


Why b0nes?

Traditional React Setup:

npx create-react-app my-app    # 2 minutes, 250MB node_modules
cd my-app
npm start                        # 30 seconds to start
npm run build                    # 1 minute to build
# Result: 500KB+ JavaScript bundle

b0nes Setup:

git clone b0nes.git          # 10 seconds
cd b0nes
node src/framework/index.js     # Instant
node src/framework/utils/build/index.js public  # 1 second
# Result: Pure HTML, 0KB JavaScript

How It Works

Your Code (home.js)
        ↓
   Components Array
        ↓
   compose() function
        ↓
   HTML Strings
        ↓
   renderPage()
        ↓
   Static HTML File

That's it. No JSX compilation. No virtual DOM. No hydration. Just functions returning strings.


Perfect For:

  • πŸ“ Blogs
  • 🎨 Marketing sites
  • πŸ“š Documentation
  • 🎯 Landing pages
  • πŸ’Ό Portfolios

Next Steps

  • Add more components: node src/components/utils/generator/index.js molecule card
  • Run tests: node src/components/utils/tester.js
  • Customize styles: Add your own CSS (no opinions here!)
  • Create pages: Add routes in src/framework/routes.js
  • Read the docs: Check out the Component API documentation below

Creating Components

Atoms (Basic Elements)

// src/components/atoms/button/button.js
import { processSlot } from '../../../utils/processSlot.js';

export const button = ({ type = 'button', attrs, className, slot }) => {
    attrs = attrs? ` ${attrs}` : '';
    className = className? ` ${className}` : '';
    slot = processSlot(slot) ?? '';
    return `<button type="${type}" class="btn${className}"${attrs}>${slot}</button>`;
}

Using Components

// Standalone
button({ type: 'submit', slot: 'Click Me', className: 'primary' });

// In composition
{
    type: 'atom',
    name: 'button',
    props: {
        type: 'submit',
        slot: 'Submit',
        className: 'primary'
    }
}

Component Generator

Generate new components quickly using the built-in generator:

# Create an atom
node src/components/utils/generator/index.js atom my-button

# Create a molecule
node src/components/utils/generator/index.js molecule my-card

# Create an organism
node src/components/utils/generator/index.js organism my-header

The generator creates:

  • index.js - Component exports
  • componentName.js - Component implementation
  • componentName.test.js - Unit tests

Example output structure:

components/
└── atoms/
    └── my-button/
        β”œβ”€β”€ index.js
        β”œβ”€β”€ my-button.js
        └── my-button.test.js

Generator Options

  • atom - Create basic element (button, text, input...)
  • molecule - Create compound component (card, form-group...)
  • organism - Create page section (header, footer...)

Component Props API

All components support:

  • attrs - Raw HTML attributes (string)
  • className - CSS classes (string)
  • slot - Content (string or array of components)

Some components have additional props:

  • text: is (HTML tag)
  • button: type (submit, reset, button)
  • link: url (href)
  • image: src (image source)

Routing & Pages

Define Routes

// src/framework/routes.js
export const routes = [{
    name: 'Home',
    pattern: new URLPattern({ pathname: '/' }),
    meta: { title: 'Home' },
    components: homeComponents
}];

Create Page Components

// src/framework/pages/home.js
export const components = [
    {
        type: 'organism',
        name: 'header',
        props: { slot: [...] }
    },
    {
        type: 'organism',
        name: 'hero',
        props: { slot: [...] }
    }
];

Composition System

Components are composed recursively:

const components = [
    {
        type: 'organism',
        name: 'card',
        props: {
            slot: [
                {
                    type: 'atom',
                    name: 'text',
                    props: {
                        is: 'h2',
                        slot: 'Title'
                    }
                },
                {
                    type: 'atom',
                    name: 'text',
                    props: {
                        is: 'p',
                        slot: 'Description'
                    }
                }
            ]
        }
    }
];

const html = compose(components);

Component Testing

Tests are auto-discovered and run:

// src//components/atoms/button/button.test.js
export const test = () => {
    const actual = button({ type: 'submit', slot: 'Click' });
    const expected = '<button type="submit" class="btn">Click</button>';
    return actual === expected? true : console.error({actual, expected}) || false;
};

Run all tests:

node src/components/utils/tester.js

API Reference

Core Components

Atoms

  • button - { type, slot, className, attrs }
  • link - { url, slot, className, attrs }
  • text - { is, slot, className, attrs }
  • image - { src, className, attrs }
  • input - { type, className, attrs }
  • textarea - { className, attrs }
  • box - { is, slot, className, attrs }
  • accordion - { titleSlot, detailsSlot, className, attrs }
  • divider - { className, attrs }
  • video - { src, slot, className, attrs }
  • picture - { slot, className, attrs }

Molecules

  • card - { slot, headerSlot, mediaSlot, linkSlot, contentSlot, className, attrs }

Organisms

  • header - { slot, className, attrs }
  • footer - { slot, className, attrs }
  • hero - { slot, className, attrs }
  • cta - { slot, className, attrs }

Functions

compose(components)

Recursively composes component tree into HTML.

const html = compose([
    { type: 'atom', name: 'text', props: { is: 'p', slot: 'Hello' } }
]);
// Returns: '<p class="text">Hello</p>'

renderPage(content, meta)

Wraps composed HTML in full page template.

const html = renderPage(content, { title: 'My Page' });

router(url, routes)

Matches URL to route and returns route info.

const route = router(new URL('http://localhost/'), routes);
// Returns: { params, query, meta, components, ... }

Advanced Usage

Custom Styling

Since there's no CSS, add styles via attrs or className class names ( you will need the global stylesheet. See next):

button({
    slot: 'Styled Button',
    attrs: 'style="background: blue; color: white; padding: 10px;"',
    className: 'btn-blue'
})

Or include a global stylesheet:

// In renderPage, add to <head>
<link rel="stylesheet" href="/styles.css">

Conditional Rendering

Use JavaScript to conditionally build components:

const components = [
    showHeader && {
        type: 'organism',
        name: 'header',
        props: { slot: [...] }
    },
    {
        type: 'atom',
        name: 'text',
        props: { is: 'p', slot: content }
    }
].filter(Boolean); // Remove falsy values

const html = compose(components);

Roadmap

  • Develop better routing and dynamic routes
  • Client-side interactivity (tabs, modals, etc)
  • ISR (Incremental Static Regeneration)
  • Plugin system
  • Middleware support
  • CLI tool with scaffolding
  • Develop a component viewer (storybook-like) integration

License

MIT - See LICENSE file

Contributing

Contributions welcome! Please ensure:

  • All tests pass: node src/components/utils/tester.js
  • New components follow the pattern
  • JSDoc comments included

Known Issues (v1)

  • Some tests are not great. We will leverage node:test in upcoming versions.
  • Some components have many individual props (refactoring to attrs object)
  • Component generator has an issue where componentName and componentType are being replace several times. It will be addresses.
  • Dynamic generated routes are still cooked.

We're aware of these and they'll be addressed in the next release.

Before we wrap up.

This is just an attempt to do something different. I am not replacing anything I am just proposing another way. I think we have for quite some time overengineered solutions that give us more headaches than anything else. We need to question our choices and ask ourselves: "Is this really worth it"?

Obviously contributions are welcome. If you think that you have a better approach propose it but remember this has 0 dependencies, and its core feature is that is easy to understand.

If you are reading at this point I really appreciate it. If you think this is useful let me know. If you learn a thing or two with this, let me also know.

Let's make this something useful for everyone.

Many Thanks! Iggy