{"id":31768963,"url":"https://github.com/tamasmajer/magic-box-ui","last_synced_at":"2026-05-16T11:31:13.937Z","repository":{"id":314873572,"uuid":"1056032478","full_name":"tamasmajer/magic-box-ui","owner":"tamasmajer","description":"Think in boxes. A tiny (\u003c2.5KB) library for building reactive UIs by composing simple HTML containers. Just declare your boxes, populate them with data, and assemble your interface piece by piece. No build tools required.","archived":false,"fork":false,"pushed_at":"2025-10-05T16:18:08.000Z","size":183,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-10T02:25:15.012Z","etag":null,"topics":["client","html","js","library","reactive","tag","tag-ui","tagui","tiny","ui","vanjs"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tamasmajer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-13T08:51:25.000Z","updated_at":"2025-10-05T16:20:47.000Z","dependencies_parsed_at":"2025-09-30T13:25:00.708Z","dependency_job_id":"0fa239f1-b93c-4778-8611-694b85778298","html_url":"https://github.com/tamasmajer/magic-box-ui","commit_stats":null,"previous_names":["tamasmajer/refjs","tamasmajer/tag-ui","tamasmajer/magic-box-ui"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/tamasmajer/magic-box-ui","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tamasmajer%2Fmagic-box-ui","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tamasmajer%2Fmagic-box-ui/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tamasmajer%2Fmagic-box-ui/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tamasmajer%2Fmagic-box-ui/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tamasmajer","download_url":"https://codeload.github.com/tamasmajer/magic-box-ui/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tamasmajer%2Fmagic-box-ui/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33100795,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-16T04:41:52.686Z","status":"ssl_error","status_checked_at":"2026-05-16T04:41:52.009Z","response_time":115,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["client","html","js","library","reactive","tag","tag-ui","tagui","tiny","ui","vanjs"],"created_at":"2025-10-10T02:25:00.635Z","updated_at":"2026-05-16T11:31:13.932Z","avatar_url":"https://github.com/tamasmajer.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MagicBox\n\nThink in boxes. A tiny (\u003c2.5KB) library for building reactive UIs by composing simple HTML containers. Just declare your boxes, populate them with data, and assemble your interface piece by piece. No build tools required.\n\n\n## Overview\n\n### Core Concept\n\nPut your HTML elements and your data into boxes, then compose them together.\n\n**Name your HTML elements:**\n```html\n\u003cdiv box=\"App\"\u003e\u003c/div\u003e\n\u003ctemplate box=\"Counter\"\u003e\n  \u003cbutton box=\"Increment\"\u003e+\u003c/button\u003e\n  \u003cspan box=\"Display\"\u003e0\u003c/span\u003e\n\u003c/template\u003e\n```\n\n**Box your data:**\n```javascript\nconst count = box(0)\nconst doubled = box(() =\u003e count.box * 2)\n```\n\n**Compose and react:**\n```javascript\nbox.App(box.Counter({\n  Display: () =\u003e `${count.box} × 2 = ${doubled.box}`,\n  Increment: { onclick: () =\u003e count.box++ }\n}))\n```\n\nWhen you use a box inside another box, MagicBox tracks the dependency. Update the inner box → outer box updates automatically.\n\n\n### Working Example\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003cbody\u003e\n  \u003cdiv box=\"App\"\u003e\u003c/div\u003e\n\n  \u003ctemplate box=\"Counter\"\u003e\n    \u003cspan box=\"Display\"\u003e0\u003c/span\u003e\n    \u003cbutton box=\"Increase\"\u003e+\u003c/button\u003e\n  \u003c/template\u003e\n  \n  \u003cscript type=\"module\"\u003e\n    import box from 'magic-box.js'\n    \n    const counter = box(0)\n    \n    const { App, Counter } = box\n    App( \n      Counter({\n        Display: () =\u003e counter.box,\n        Increase: { onclick: () =\u003e counter.box += 1 },\n      })\n    )\n  \u003c/script\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n### Disclaimer\n\nThis is a proof of concept - a personal experiment shared to gauge interest in simpler alternatives to today's frameworks.\n\n### Getting Started\n\n1. **Requirements**: Modern browser with ES6 module support, no build tools required\n2. **Import**: `import box from 'https://cdn.jsdelivr.net/gh/tamasmajer/magic-box/magic-box.min.js'`\n3. **Name your elements**: `\u003cdiv box=\"App\"\u003e\u003c/div\u003e`\n4. **Box your data**: `const count = box(0)`\n5. **Compose them**: `box.App(box.Counter({ Display: count }))`\n\n\n### Creating a Box\n\nThe type of the box depends on the type of the first argument.\n\n```javascript\nbox(firstArgument, ...options)\n```\n\n| First Argument | Behavior | Example |\n|----------------|----------|---------|\n| **Data** | Box data we can update later | `box(0)`, `box({ name: 'John' })` |\n| **Function** | Box a derived value | `box(() =\u003e count.box * 2)` |\n| **Box + Function** | Update only when this box changes | `box(count, () =\u003e expensive())` |\n| **Boxes + Function** | Update only when these boxes change | `box([count, user], () =\u003e expensive())` |\n| **Storage Object** | Box localStorage namespace | `box(localStorage, 'app/')` |\n| **`fetch`** | Create a remote box | `const remote = box(fetch)` |\n| **DOM Node** | Box an existing element | `box(window, { onresize: handler })` |\n| **Property Access** | Find boxes by name | `box.Button`, `box.div` |\n\n### Examples\n\n**Box your data:**\n```javascript\nconst counter = box(0)\nconst user = box({ name: 'John' })\nconst doubled = box(() =\u003e counter.box * 2)      // Computed (auto-deps)\nconst expensive = box(counter, () =\u003e heavyCalc()) // Updates only if 'counter' changes\nconst optimized = box([counter, user], () =\u003e compute()) // Updates only if 'counter' or 'user' changes\n```\n\n**Combine the contents of boxes:**\n```javascript\nconst firstName = box('John')\nconst lastName = box('Doe')\nconst fullName = box(() =\u003e `${firstName.box} ${lastName.box}`)\n\nfirstName.box = 'Jane'  // fullName automatically updates to \"Jane Doe\"\n```\n\n**Create boxes from localStorage:**\n```javascript\nconst { settings } = box(localStorage)           // Auto-sync to localStorage\nconst { prefs } = box(localStorage, 'myapp/')    // With namespace prefix\n```\n\n**Create a remote box we can talk to:**\n```javascript\nconst remote = box(fetch)                        // Box the ability to ask a server\nconst api = box(fetch, { url: '/api' })          // With default config\n```\n\n**Create boxes from DOM nodes:**\n```javascript\nbox(window, { onresize: () =\u003e updateLayout() })     // Bind to window\nbox(document.body, { class: 'dark-theme' })         // Bind to body\nbox(myElement, { onclick: handler }, 'New content') // Bind + add children\nbox(myElement, { Title: 'New Title', Button: { onclick: newHandler } }) // Update descendants by box names\n```\n\n**Find boxes by name:**\n```javascript\nbox.Counter({ count: () =\u003e counter.box })       // Use template\nbox.MyButton.box.focus()                        // Direct DOM access\nconst { div, span } = box                       // Create elements\n```\n\n### Return Values\n\n**Data box** → Access with `.box`\n```javascript\nconst count = box(0)\ncount.box = 5              // Update\nconsole.log(count.box)     // Read\n```\n\n**localStorage box** → Destructure boxes\n```javascript\nconst { settings } = box(localStorage, 'app/')\nsettings.box = { darkMode: true }  // Auto-saves\n```\n\n**Remote box** → Call to make requests\n```javascript\nconst api = box(fetch, { url: '/api' })\napi({ path: '/users' })                 // Makes request\n```\n\n**DOM node box** → Returns the same node\n```javascript\nconst elem = box(myDiv, { class: 'active' }).focus()\n```\n\n**Template box** → Returns a clone of the template node, a new DOM element\n```javascript\nconst counter = box.Counter({ Display: () =\u003e count.box })\ndocument.body.append(counter)           // Add to page\n```\n\n**Element boxes** → Return new DOM elements\n```javascript\nconst { div, span } = box\nconst widget = div({ class: 'widget' }, span('Hello'))\n```\n\n## Features\n\n- [Data Boxes](#data-boxes)\n- [Elements with Box Attributes](#elements-with-box-attributes)\n- [Elements without Box Attributes](#elements-without-box-attributes)\n- [Creating Elements with Tags](#creating-elements-with-tags)\n- [List Handling](#list-handling)\n- [Remote Boxes](#remote-boxes)\n- [Local Box from/to Remote Box](#local-box-fromto-remote-box)\n- [Best Practices](#best-practices)\n- [VanJS Enhancements](#vanjs-enhancements)\n\n\n\n### Data Boxes\n\nBox your data to make it reactive.\n\n**Basic usage:**\n```javascript\nconst counter = box(0)\nconst user = box({ name: 'John', age: 25 })\n\n// Access/modify with .box\ncounter.box = 10\nuser.box = { ...user.box, age: 26 }  // Always replace, never mutate\n```\n\n**Computed values:**\n```javascript\nconst price = box(100)\nconst quantity = box(2)\nconst total = box(() =\u003e price.box * quantity.box)\n\nprice.box = 150  // total automatically becomes 300\n```\n\n**Performance optimization:**\n```javascript\nconst result = box([dep1, dep2], () =\u003e compute())  // Only updates when deps change\n```\n\n**Persistent boxes:**\n```javascript\nconst { settings } = box(localStorage)\nconst { userPrefs } = box(localStorage, 'myapp/')\n\nsettings.box = { darkMode: true }  // Auto-saves\n```\n\n### Elements with Box Attributes\n\n**Template behavior:**\n- `\u003ctemplate box=\"Name\"\u003e` → `box.Name()` clones the template\n- `\u003cdiv box=\"Name\"\u003e` → `box.Name()` updates element in-place\n\n**Naming convention:**\nUse uppercase names: `box=\"Counter\"` not `box=\"counter\"`\n\n**Element access:**\n```javascript\nconst button = box.MyButton.box  // Get the DOM element\nbutton.focus()\n```\n\n**Template binding patterns:**\n```javascript\nbox.Counter({\n  Display: 'text only',                           // Content only\n  Button: { onclick: handler },                   // Properties only\n  Link: [{ href: '/page', class: 'active' }, 'Visit']  // Properties + content\n})\n\n// Mixed format: combine element properties with descendant updates\nbox.UserCard({\n  class: 'active',              // lowercase = element property  \n  UserName: user.box.name,      // Uppercase = descendant content\n  EditBtn: { onclick: edit }    // Uppercase = descendant properties\n})\n```\n\n**Element operations:**\n```javascript\n// Properties only (keeps existing children)\nbox.Element({ onclick: handler, class: 'active' })\n\n// Properties + replace all children\nbox.Element([{ onclick: handler }, 'new content'])\n\n// Update descendants by box names\nbox.Element({ \n  Child1: 'new text', \n  Child2: { class: 'highlight' } \n})\n\n// Mixed format: element properties + descendant updates (case sensitive)\nbox.Element({ \n  class: 'container',           // lowercase = element property\n  Title: 'New title',           // Uppercase = descendant update\n  Button: { onclick: handler }  // Uppercase = descendant update\n})\n```\n\n### Elements without Box Attributes\n\nBind properties and events to existing DOM nodes.\n\n**Direct node binding:**\n```javascript\n// Bind to global objects\nbox(window, { \n  onresize: () =\u003e updateLayout(),\n  onbeforeunload: (e) =\u003e e.preventDefault()\n})\n\nbox(document, { onclick: handleGlobalClick })\n\nbox(document.body, { onkeydown: (e) =\u003e {\n  if (e.key === 'Escape') closeModal()\n}})\n\n// Bind to any DOM element\nconst myDiv = document.getElementById('myDiv')\nbox(myDiv, {\n  onclick: handleClick,\n  class: () =\u003e isActive.box ? 'active' : ''\n})\n```\n\n### Creating Elements with Tags\n\nBuild UI elements programmatically.\n\n**Element destructuring:**\n```javascript\nconst { div, span, button, h1 } = box\n\nconst widget = div({ class: 'widget' },\n  h1('Title'),\n  span(() =\u003e counter.box),\n  button({ onclick: () =\u003e counter.box++ }, 'Increment')\n)\n```\n\n**Content format:**\nAll content compiles to `[{ props }, ...children]`.\n- `h1('text')` → `[{}, 'text']`\n- `h1({ class: 'title' })` → `[{ class: 'title' }]`\n- `h1({ class: 'title' }, 'text')` → `[{ class: 'title' }, 'text']`\n\n**Reactive content:**\nElements update automatically when reactive dependencies change.\n```javascript\nspan(() =\u003e counter.box)  // Updates automatically\ndiv({ \n  class: () =\u003e counter.box % 2 ? 'odd' : 'even' \n}, () =\u003e `Count: ${counter.box}`)\n```\n\n### List Handling\n\n**Always replace, never mutate:**\n```javascript\n// ✅ Correct\nitems.box = [...items.box, newItem]\nitems.box = items.box.filter(item =\u003e item.id !== targetId)\n\n// ❌ Wrong\nitems.box.push(newItem)\nitems.box[0] = newValue\n```\n\n**Rendering lists:**\nMap arrays to DOM elements with empty state handling.\n```javascript\nbox.TodoList(() =\u003e \n  todos.box.length === 0 \n    ? div({ class: 'empty' }, 'No todos yet!')\n    : todos.box.map(todo =\u003e \n        box.TodoItem({ \n          Text: todo.text,\n          Delete: { onclick: () =\u003e removeTodo(todo.id) }\n        })\n      )\n)\n```\n\n### Remote Boxes\n\nHandle API calls with reactive loading states and error handling.\n\n**Basic usage:**\n```javascript\nconst remote = box(fetch)\nconst users = await remote({ url: '/api/users' })\n```\n\n**Default configuration:**\nCreate remote boxes with default settings.\n```javascript\nconst api = box(fetch, { \n  headers: { Authorization: `Bearer ${token}` },\n  url: 'https://api.example.com'\n})\n\n// Use with specific overrides\nconst users = api({ path: '/users' })                    // GET https://api.example.com/users\nconst createUser = api({ path: '/users', body: userData }) // POST (automatic when body present)\n```\n\n**Reactive callbacks:**\nUse reactive callbacks for loading states.\n```javascript\nconst load = (id, filter) =\u003e remote({\n  url: 'https://api.example.com',\n  path: '/append/' + id,                    // optional path append\n  query: { filter },                        // optional query parameters\n  body: input.box,                          // POST body (auto-JSON if object, method: 'POST' automatic)\n  loading: url =\u003e loading.box = url ? 'loading' : '',  // Called before/after\n  failed: ({ response, error }) =\u003e {        // Error handling with more details\n    if (response) console.log('failed', response.status) \n    else console.log('error', error)\n  },\n  result: data =\u003e output.box = data         // Success callback\n})\n```\n\n**Method auto-detection:**\nHTTP method is determined by request configuration.\n```javascript\n// GET request (no body)\nremote({ url: '/api/users' })\n\n// POST request (body present, method auto-detected)\nremote({ url: '/api/users', body: { name: 'John' } })\n\n// Explicit method override\nremote({ url: '/api/users/123', method: 'PATCH', body: { name: 'Jane' } })\n```\n\n**Composable configuration:**\nBuild reusable request configurations.\n```javascript\nconst remote = box(fetch)\nconst serverConfig = { url: 'https://api.example.com' }\nconst session = { ...serverConfig, headers: { Authorization: token } }\nconst endpoint = { ...session, path: '/notes' }\n\nconst saveNote = (note) =\u003e remote({ \n  ...endpoint, \n  body: note,                               // method: 'POST' automatic when body present\n  result: (data) =\u003e notes.box = [...notes.box, data]\n})\n```\n\n### Local Box from/to Remote Box\n\n**Remote to Local**\n```javascript\n// Automatically refetch when dependencies change\nconst remote = box(fetch)\nconst docId = box(1)\nconst doc = box(() =\u003e remote({ \n  url: `https://api.example.com/posts/${docId.box}` \n}))\n\n// Usage in templates\nbox.PostView({\n  Title: () =\u003e doc.box?.title,\n  Content: () =\u003e doc.box?.content\n})\n```\n\n**Editable Remote Doc**\n```javascript\n// Load remote doc when docId changes\nconst remote = box(fetch)\nconst docId = box(456)\nconst serverDoc = box(() =\u003e remote({ \n  url: `/api/documents/${docId.box}`,\n  loading: url =\u003e isLoading.box = !!url\n}))\n\n// Create local\nconst localDoc = box({})\n\n// Initialize local from remote\nbox(() =\u003e {\n  if (serverDoc.box \u0026\u0026 !localDoc.box.id) {\n    localDoc.box = { ...serverDoc.box }\n  }\n})\n\n// Save local to remote\nconst save = () =\u003e remote({\n  url: `/api/documents/${docId.box}`,\n  method: 'PATCH',                         // Explicit method for PATCH (not auto-detected)\n  body: localDoc.box,\n  result: () =\u003e showSaved.box = true\n})\n```\n\n\n### Customizing the Library\n\nCustomize attribute and property names used throughout the framework.\n\n**Create custom instances:**\n\nVue-like box attribute, box function, and .value:\n```javascript\nimport Box from 'magic-box.js'\nconst box = new Box('box', 'value'), $boxes = box\n// \u003cdiv box=\"App\"\u003e\u003c/div\u003e\n$boxes.App($boxes.Counter(...))\nconst counter = box(1)\ncounter.value = 2\n```\n\nBox-def variant:\n```javascript\nimport Box from 'magic-box.js'\nconst box = new Box('box', 'def'), def = box\n// \u003cdiv box=\"App\"\u003e\u003c/div\u003e\nbox.App(box.Counter(...))\nconst counter = def(1)\ncounter.def = 2\n```\n\nUI variant:\n```javascript\nimport Box from 'magic-box.js'\nconst ui = new Box('box', 'SIGNAL'), SIGNAL = ui\n// \u003cdiv box=\"App\"\u003e\u003c/div\u003e\nui.App(ui.Counter(...))\nconst counter = SIGNAL(1)\ncounter.SIGNAL = 2\n```\n\n### Best Practices\n\n1. **Template-first**: Write HTML templates with CSS, bind with minimal JavaScript\n2. **Prefer templates**: HTML templates are more maintainable than DOM creation\n3. **Always replace**: Use spread/filter/map - never mutate with push/splice\n4. **Uppercase names**: `box=\"UserCard\"` required to distinguish from attributes\n5. **Explicit dependencies**: Use `box([deps], fn)` for expensive computations\n6. **Direct DOM access**: `box.Element.box` gets the DOM element\n7. **localStorage prefixes**: Use `box(localStorage, 'app/')` to avoid conflicts\n\n### VanJS Enhancements\n\nMagicBox is built on VanJS 1.5.3 and includes several enhancements that make reactive development more powerful:\n\n**Fragment Support**\nReactive functions can return arrays of elements, enabling dynamic component composition:\n\n```javascript\nconst renderItems = () =\u003e items.box.map(item =\u003e \n  box.div({ class: 'item' }, item.name)\n)\n\nbox.Container(renderItems)  // Automatically handles array of elements\n```\n\nFragment support also works with conditional rendering:\n```javascript\nconst conditionalContent = () =\u003e [\n  isLoading.box \u0026\u0026 box.div('Loading...'),\n  hasError.box \u0026\u0026 box.div({ class: 'error' }, error.box),\n  data.box \u0026\u0026 box.div('Content loaded')\n].filter(Boolean)\n\nbox.App(conditionalContent)\n```\n\nTemplates with multiple root elements are automatically wrapped in document fragments. When a parent container only contains fragment children, the fragments unfold directly into the parent, preserving flexbox and grid layouts that require direct parent-child relationships.\n\n**Explicit Updates**\nControl when expensive computations run by explicitly declaring dependencies, perfect for tab interfaces and performance optimization:\n\n```javascript\n// Tab switching: only update when activeTab changes, not when content changes\nconst tabContent = box([activeTab], () =\u003e \n  activeTab.box === 'users' ? \n    box.UserList({ users: allUsers.box }) :  // Won't re-render when allUsers changes\n  activeTab.box === 'settings' ?\n    box.SettingsPanel({ config: appConfig.box }) :  // Won't re-render when appConfig changes\n    box.div('Select a tab')\n)\n\nbox.App(tabContent)\n```\n\nWithout explicit dependencies, this would re-render whenever `allUsers` or `appConfig` changes, even when those tabs aren't visible. With explicit updates, it only re-renders when `activeTab` changes.\n\nYou can also force updates for stateless calls:\n```javascript\n// Force update on user action, regardless of other dependencies\nconst refreshData = box([forceUpdate], () =\u003e {\n  // This runs when forceUpdate changes, ignoring other state changes\n  return fetchAndRenderExpensiveData()\n})\n\n// Trigger refresh manually\nconst handleRefresh = () =\u003e forceUpdate.box = Date.now()\n```\n\nThis prevents unnecessary re-renders and ensures proper cleanup of event listeners and DOM references in complex component hierarchies.\n\n**Shorter Conditional Syntax**\nMagicBox enables shorter conditional rendering by supporting the `\u0026\u0026` operator. It automatically filters out `false`, `null`, or `undefined` values but preserves the number zero. To handle zero values, use explicit comparisons like `value !== 0`:\n\n```javascript\n// Concise conditional syntax - no ternary needed\nconst message = () =\u003e user.box \u0026\u0026 `Welcome, ${user.box.name}!`\nconst errorDisplay = () =\u003e hasError.box \u0026\u0026 box.div({ class: 'error' }, 'Something went wrong')\nconst count = () =\u003e items.box.length \u003e 0 \u0026\u0026 box.span(`${items.box.length} items`)\n\n// Instead of verbose ternaries\nconst message = () =\u003e user.box ? `Welcome, ${user.box.name}!` : null\nconst errorDisplay = () =\u003e hasError.box ? box.div({ class: 'error' }, 'Something went wrong') : null\nconst count = () =\u003e items.box.length \u003e 0 ? box.span(`${items.box.length} items`) : null\n```\n\nSmart value handling preserves meaningful content while filtering out display issues:\n\n```javascript\n// These become empty strings\nbox.div(null)           // Empty div\nbox.span(undefined)     // Empty span  \nbox.p(false \u0026\u0026 'text')  // Empty paragraph\n\n// These preserve the actual value (numbers and strings are kept)\nbox.h1(0)              // Shows \"0\"\nbox.span('')           // Shows empty string\nbox.div(-1)            // Shows \"-1\"\n```\n\nThis makes conditional rendering more concise while preventing `null`, `undefined`, or `false` from appearing as unwanted text in your UI.\n\n**HTML-First Development**\nWrite component structure in HTML templates, then bind behavior with minimal JavaScript:\n\n```html\n\u003c!-- Define structure in HTML --\u003e\n\u003ctemplate box=\"TodoApp\"\u003e\n  \u003cdiv class=\"todo-container\"\u003e\n    \u003cinput box=\"NewTodo\" placeholder=\"Add todo...\" /\u003e\n    \u003cbutton box=\"AddBtn\" class=\"btn-primary\"\u003eAdd\u003c/button\u003e\n    \u003cdiv box=\"TodoList\" class=\"todo-list\"\u003e\u003c/div\u003e\n    \u003cdiv box=\"Summary\" class=\"summary\"\u003e\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/template\u003e\n\n\u003cscript type=\"module\"\u003e\n  import box from 'magic-box.js'\n  \n  const todos = box([])\n  const newTodo = box('')\n  \n  // Bind behavior to HTML structure\n  box.App(\n    box.TodoApp({\n      NewTodo: { \n        oninput: e =\u003e newTodo.box = e.target.value,\n        value: () =\u003e newTodo.box \n      },\n      AddBtn: { \n        onclick: () =\u003e {\n          if (newTodo.box.trim()) {\n            todos.box = [...todos.box, { id: Date.now(), text: newTodo.box }]\n            newTodo.box = ''\n          }\n        }\n      },\n      TodoList: () =\u003e todos.box.map(todo =\u003e \n        box.div({ class: 'todo-item' }, todo.text)\n      ),\n      Summary: () =\u003e `${todos.box.length} todos`\n    })\n  )\n\u003c/script\u003e\n```\n\nThis approach separates concerns cleanly: HTML handles structure and styling, JavaScript handles behavior and state management.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftamasmajer%2Fmagic-box-ui","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftamasmajer%2Fmagic-box-ui","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftamasmajer%2Fmagic-box-ui/lists"}