jj
    Preparing search index...

    jj

    Just JavaScript!

    Faster than VDOM. No Build Step. Use the Platform!

    JJ is a lightweight, no-transpilation library for modern web development. What You Write Is What Is Run (WYWIWIR).

    JJ complements browser-native capabilities instead of replacing them with a thick abstraction layer. It is designed for moderate to advanced web developers and AI agents that want fluent DOM helpers while staying close to standards.

    CDN:

    Import map: Add this to your HTML:

    <script type="importmap">
    {
    "jj": "https://cdn.jsdelivr.net/npm/jj/lib/bundle.min.js"
    // Or a specific version
    "jj": "https://cdn.jsdelivr.net/npm/jj@2/lib/bundle.min.js
    }
    </script>

    And then:

    import { JJHE } from 'jj'
    

    CDN:

    import { JJHE } from 'https://cdn.jsdelivr.net/npm/jj/lib/bundle.min.js'
    

    NPM:

    npm i jj
    
    import { JJHE } from 'jj'

    JJHE.create('div')
    .addClass('card')
    .setText('Hello World!')
    .on('click', () => console.log('Hi'))

    Use namespace-aware wrappers for non-HTML elements:

    import { JJSE, JJME } from 'jj'

    const icon = JJSE.create('svg').setAttr('viewBox', '0 0 24 24')
    const formula = JJME.create('math').addChild(JJME.create('mi').setText('x'))
    • Zero Build – Runs directly in modern browsers
    • Native Speed – Direct DOM manipulation, no VDOM overhead
    • Web Standards – First-class Web Components support
    • Fluent API – Chainable methods for cleaner code

    JJ is not a reactive framework, template compiler, or state-management system.

    The guides intentionally include verified MDN links so you can move between JJ helpers and underlying browser APIs.

    Batch setter helpers use plural methods:

    • setAttrs({...})
    • setAriaAttrs({...})
    • setDataAttrs({...})
    • setClasses({...})

    ARIA helpers use explicit singular naming:

    • getAriaAttr(name)
    • hasAriaAttr(name)
    • setAriaAttr(name, value)
    • setAriaAttrs({...})
    • rmAriaAttr(...names)
    • rmAriaAttrs(names)

    Dataset helpers use explicit singular/plural naming:

    • getDataAttr(name)
    • hasDataAttr(name)
    • setDataAttr(name, value)
    • setDataAttrs({...})
    • rmDataAttr(...names)
    • rmDataAttrs(names)

    Use defineComponent(name, constructor, options?) and expose a static defined promise on each custom element class:

    import { defineComponent } from 'jj'

    export class MyWidget extends HTMLElement {
    static defined = defineComponent('my-widget', MyWidget)
    }

    await MyWidget.defined

    defineComponent() resolves a Promise<boolean>:

    • false: this call registered the component.
    • true: it was already registered with the same constructor.

    This explicit definition step is important for reliability. If markup is parsed before the element is defined, upgrades can race with rendering and produce flaky behavior.

    πŸ‘‰ Visit the full site with tutorials, examples, and API docs

    JJ is designed for AI-assisted development. Install the skill for intelligent code suggestions:

    npx skills add alexewerlof/jj
    

    Once installed, AI agents (GitHub Copilot, Cursor, Claude Code, Windsurf, etc.) will:

    • Follow JJ's patterns and conventions automatically
    • Know when to use .ref for native DOM access
    • Suggest correct framework translations (React/Vue/jQuery/Svelte β†’ JJ)
    • Generate idiomatic, type-safe code

    The skill definition (SKILL.md) is also included in the npm package at node_modules/jj/SKILL.md.

    The entire public API is tested thoroughly. Tests live in the test/ folder and mirror the source filenames (e.g., test/JJE.test.ts for src/JJE.ts, test/JJME.test.ts for src/wrappers/JJME.ts) while importing the target from ./src/index.js.

    Run tests with:

    npm test
    

    MIT

    Made in Sweden πŸ‡ΈπŸ‡ͺ by Alex EwerlΓΆf