npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@kaiserofthenight/human-js

v1.0.1

Published

A framework built for humans, not machines. Zero dependencies, zero build tools, 100% readable.

Readme

🦉 HumanJS Framework

A framework built for humans, not machines.

Simple. Readable. Easy to extend. Zero magic. No build tools required.


🌟 Philosophy

HumanJS prioritizes human understanding over machine optimization.

Core Principles

  1. Zero Magic - Every behavior is traceable in ~200 lines of code
  2. Human-First API - Read like English sentences
  3. Minimal Abstractions - Use native DOM APIs when possible
  4. Easy to Extend - Modify the source, don't fight it
  5. No Heavy Dependencies - Pure JavaScript (ES6+), browser APIs only

🚀 Quick Start

1. Clone or Download

git clone https://github.com/kaiserofthenight/humanjs.git
cd humanjs

2. Open in Browser

# No build step required!
# Just open index.html in your browser
open index.html

3. Your First App

import { app, html } from './src/index.js';

app.create({
  state: { count: 0 },
  
  render: (state) => {
    const element = html`
      <div style="padding: 40px; text-align: center;">
        <h1>Count: ${state.count}</h1>
        <button id="increment">+1</button>
      </div>
    `;
    
    const events = {
      '#increment': {
        click: () => state.count += 1
      }
    };
    
    return { element, events };
  }
});

That's it! No compilation, no bundling, no configuration.


📚 Core Concepts

1. Reactive State

import { createState } from './src/index.js';

const state = createState({ count: 0 }, (prop, value) => {
  console.log(`${prop} changed to ${value}`);
});

state.count = 5; // Triggers callback

Advanced state features:

// Watch specific properties
state.$watch('count', (newVal, oldVal) => {
  console.log(`Count: ${oldVal} → ${newVal}`);
});

// Computed properties
state.$computed('double', function() {
  return this.count * 2;
});

// Reset state
state.$reset({ count: 0 });

// Get raw state
const raw = state.$raw();

2. HTML Rendering

import { html } from './src/index.js';

// Simple
const greeting = html`<h1>Hello, World!</h1>`;

// With dynamic values
const count = 5;
const counter = html`<div>Count: ${count}</div>`;

// With arrays
const items = ['Apple', 'Banana', 'Orange'];
const list = html`
  <ul>
    ${items.map(item => html`<li>${item}</li>`)}
  </ul>
`;

Conditional rendering:

import { when } from './src/index.js';

when(
  user.isLoggedIn,
  () => html`<div>Welcome, ${user.name}!</div>`,
  () => html`<div>Please log in</div>`
);

List rendering:

import { each } from './src/index.js';

each(users, (user) => html`
  <div class="user-card">
    <h3>${user.name}</h3>
    <p>${user.email}</p>
  </div>
`);

3. Components

import { app, html } from './src/index.js';

app.create({
  // Where to mount
  root: document.getElementById('app'),
  
  // Initial state
  state: {
    count: 0,
    user: { name: 'John' }
  },
  
  // Render function
  render: (state) => {
    const element = html`<div>${state.count}</div>`;
    const events = {
      '#btn': { click: () => state.count++ }
    };
    return { element, events };
  },
  
  // Lifecycle hooks
  onMount: (state) => {
    console.log('Component mounted!');
  },
  
  onUpdate: (state) => {
    console.log('State updated!');
  },
  
  onDestroy: (state) => {
    console.log('Component destroyed!');
  }
});

4. Event Handling

// Basic events
const events = {
  '#button': {
    click: () => console.log('Clicked!'),
    mouseover: () => console.log('Hovered!')
  },
  '#input': {
    input: (e) => state.value = e.target.value,
    focus: () => console.log('Focused!')
  }
};

// Form submission
'#form': {
  submit: (e) => {
    e.preventDefault();
    const data = getFormData(e.target);
    console.log(data);
  }
}

Event helpers:

import { on, debounce, throttle } from './src/index.js';

// Debounce search input
const handleSearch = debounce((e) => {
  state.query = e.target.value;
}, 300);

// Throttle scroll events
const handleScroll = throttle(() => {
  console.log('Scrolling...');
}, 100);

5. Routing

import { createRouter, Link } from './src/index.js';

const router = createRouter({
  '/': () => html`<h1>Home Page</h1>`,
  '/about': () => html`<h1>About Page</h1>`,
  '/user/:id': (params) => html`<h1>User ${params.id}</h1>`,
  '*': () => html`<h1>404 - Not Found</h1>`
}, {
  beforeEach: (to, from, params) => {
    console.log(`Navigating from ${from} to ${to}`);
  },
  afterEach: (to, from, params) => {
    console.log(`Navigated to ${to}`);
  }
});

// Navigate programmatically
router.navigate('/about');

// Create links
const link = Link('/about', 'About Us', 'nav-link');

🔌 Plugins

Form Validation

import { createValidator, rules, displayErrors } from './src/plugins/validator.js';

const validator = createValidator({
  email: [rules.required, rules.email],
  password: [rules.required, rules.minLength(8)],
  age: [rules.required, rules.number, rules.min(18)],
  confirmPassword: [rules.required, rules.match('password', 'Password')]
});

const result = validator.validate(formData);

if (!result.isValid) {
  displayErrors(formElement, result.errors);
} else {
  // Submit form
}

Available rules:

  • required - Field must have a value
  • email - Valid email format
  • minLength(n) - Minimum length
  • maxLength(n) - Maximum length
  • min(n) - Minimum numeric value
  • max(n) - Maximum numeric value
  • pattern(regex, msg) - Custom regex
  • url - Valid URL
  • number - Numeric value
  • integer - Integer value
  • match(field, name) - Match another field
  • custom(fn, msg) - Custom validation

HTTP Client

import { createHttp, http } from './src/plugins/http.js';

// Use default instance
const { data } = await http.get('/api/users');
await http.post('/api/users', { name: 'John' });
await http.put('/api/users/1', { name: 'Jane' });
await http.delete('/api/users/1');

// Custom instance
const api = createHttp({
  baseURL: 'https://api.example.com',
  headers: { 'Authorization': 'Bearer token' },
  timeout: 10000,
  onRequest: (config) => {
    console.log('Request:', config);
  },
  onResponse: (response) => {
    console.log('Response:', response);
  },
  onError: (error) => {
    console.error('Error:', error);
  }
});

const users = await api.get('/users');

Storage

import { local, session, createNamespace } from './src/plugins/storage.js';

// LocalStorage (persists)
local.set('user', { name: 'John', age: 30 });
const user = local.get('user');
local.remove('user');
local.clear();

// SessionStorage (cleared on close)
session.set('token', 'abc123');
const token = session.get('token');

// Namespaced storage
const appStorage = createNamespace('myapp');
appStorage.local.set('settings', { theme: 'dark' });

🛠️ Utilities

import { helpers } from './src/index.js';

// Generate unique ID
const id = helpers.uid('todo'); // 'todo_1234567890_abc'

// Deep clone
const copy = helpers.clone(originalObject);

// Deep merge
const merged = helpers.merge(obj1, obj2, obj3);

// Format date
const formatted = helpers.formatDate(new Date(), 'YYYY-MM-DD HH:mm');

// Sleep/delay
await helpers.sleep(1000); // Wait 1 second

// String utilities
helpers.capitalize('hello'); // 'Hello'
helpers.truncate('Long text...', 10); // 'Long text...'

// Array utilities
helpers.shuffle([1, 2, 3, 4, 5]);
helpers.unique([1, 2, 2, 3, 3]); // [1, 2, 3]
helpers.groupBy(users, 'role');

// Object utilities
helpers.get(obj, 'user.address.city', 'Unknown');
helpers.set(obj, 'user.name', 'John');
helpers.pick(obj, ['name', 'email']);
helpers.omit(obj, ['password', 'token']);

// Async utilities
await helpers.waitFor(() => element.isReady, 5000);
await helpers.retry(() => fetchData(), 3, 1000);

📦 Project Structure

humanjs-framework/
├── index.html              # Demo page
├── README.md              # This file
│
├── src/                   # Framework source
│   ├── core/             # Core modules
│   │   ├── state.js      # Reactive state
│   │   ├── render.js     # HTML rendering
│   │   ├── component.js  # Components
│   │   ├── router.js     # SPA routing
│   │   └── events.js     # Event handling
│   │
│   ├── plugins/          # Optional plugins
│   │   ├── validator.js  # Form validation
│   │   ├── http.js       # HTTP client
│   │   └── storage.js    # Storage wrapper
│   │
│   ├── utils/            # Utilities
│   │   └── helpers.js    # Helper functions
│   │
│   └── index.js          # Main entry point
│
└── examples/             # Example apps
    ├── todo-app/
    │   └── app.js
    └── api-example/
        └── app.js

🎓 Examples

Todo App

A complete todo application with:

  • Add, edit, delete todos
  • Mark as complete
  • Filter (all, active, completed)
  • LocalStorage persistence
  • Form validation

View Source

API Integration

Demonstrates API usage with:

  • Fetch users from API
  • Loading states
  • Error handling
  • Search and filter
  • Debounced input

View Source


🆚 Comparison with Other Frameworks

vs React

| Feature | HumanJS | React | |---------|---------|-------| | Learning curve | 1 day | 1-2 weeks | | Setup | Open HTML file | Complex build setup | | Bundle size | ~5KB | ~40KB (min) | | Abstraction | Minimal | Heavy (JSX, virtual DOM) | | Debugging | Trace in source | Complex stack traces |

vs Vue

| Feature | HumanJS | Vue | |---------|---------|-----| | Template syntax | Native HTML | Custom directives | | Reactivity | Proxy (native) | Proxy + compiler | | Components | Functions | Classes/objects | | Build step | None | Required for SFC |

vs Svelte

| Feature | HumanJS | Svelte | |---------|---------|--------| | Compilation | None | Required | | Runtime size | ~5KB | ~2KB | | Learning curve | 1 day | 3-4 days | | Debugging | Browser native | Compiled output |

When to use HumanJS

Use when:

  • Building simple to medium apps
  • Learning frontend concepts
  • Prototyping quickly
  • Want full control
  • No build step desired

Don't use when:

  • Building complex enterprise apps
  • Need large ecosystem
  • Team requires specific framework experience
  • Need SSR or mobile apps

🔮 Future Improvements

Optional Enhancements

  1. Virtual DOM - For better performance
  2. TypeScript - Type definitions
  3. SSR Support - Server-side rendering
  4. Dev Tools - Browser extension
  5. Plugin System - Third-party plugins
  6. CLI Tool - Project scaffolding

How to Extend

The framework is designed to be modified:

  1. Fork the repository
  2. Modify src/ files directly
  3. Add new plugins in src/plugins/
  4. Share your improvements!

License

MIT License - do whatever you want with it!


Contributing

Contributions welcome! Please:

  1. Keep it simple
  2. Maintain readability
  3. Add tests for new features
  4. Update documentation

Support


Remember: Frameworks are tools, not religions. Use what makes you productive! 🚀