
Security News
The Hidden Blast Radius of the Axios Compromise
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.
A lightweight, event-driven JavaScript UI framework for building complex, interactive web applications. Zooy provides a component-based architecture with lifecycle management, declarative event handling, and powerful layout primitives.
Originally developed for ThingZoo (now marketed as Connect), an IoT platform designed to manage a "menagerie" of connected devices from around the world.
The UI framework evolved from:
The name reflects the framework's core purpose: orchestrating diverse UI components (panels, views, splits) and managing different component libraries (MDC, Carbon, custom) — just as a zoo manages diverse creatures.
Zooy is a UI framework that emphasizes:
EVT (Event Target)
└─ Component
├─ Panel
│ └─ FormPanel
├─ Dragger
└─ Split
EVT
├─ View
└─ Conductor
ComponentLibraryRegistry (Static)
├─ MDC Library (Material Design Components)
└─ Carbon Library (IBM Carbon Design System)
Base class that extends EventTarget with enhanced listener management:
Base class for all visual components:
Specialized component for dynamic content:
Central registry system for component libraries:
Enhanced panel for form handling:
Manages multiple panels as a cohesive screen:
Top-level orchestrator for the entire application:
Sophisticated layout component for dividing space:
Component for creating draggable elements:
Components follow a predictable lifecycle:
const panel = new Panel('/api/content');
panel.target = document.getElementById('container');
panel.render(); // Creates DOM → Enters document → Fires READY event
// ... use component ...
panel.dispose(); // Exits document → Cleans up listeners → Removes DOM
Components communicate through standardized events:
// Panel dispatches event
panel.dispatchPanelEvent('custom_action', { data: 'value' });
// View listens and handles
view.mapPanEv('custom_action', (eventData, panel) => {
console.log('Panel action:', eventData.data);
});
Zooy enhances HTML with data attributes for interactions:
<!-- Button with custom event -->
<button class="zoo__button" data-zv="save" data-pk="123">Save</button>
<!-- Form with interception -->
<form class="intercept_submit" data-zv="search" data-href="/api/search">
<input name="q" />
</form>
<!-- Async content loading -->
<div class="zoo_async_html" data-href="/api/widget"></div>
<!-- Toggle class interaction -->
<button class="zoo__toggle_class_driver"
data-toggle_class_target_id="menu"
data-toggle_class="open">
Toggle Menu
</button>
Panels can load content from URIs with query parameter management:
const panel = new Panel('/api/data?page=1');
panel.addToQParams('filter', 'active');
panel.renderWithTemplate(); // Fetches: /api/data?page=1&filter=active
FormPanel provides validation and AJAX submission:
const form = new FormPanel('/api/form');
form.onSubmitSuccess((panel, response) => {
console.log('Form submitted successfully:', response);
});
form.render();
Split component creates complex, resizable layouts:
const split = new Split();
split.render(document.getElementById('app'));
// Create horizontal split: [A | B | C]
split.addSplit(undefined, 'EW', 200, 200);
// Nest A can now be split vertically
split.addSplit(split.getNest('A'), 'NS', 100, 100);
// Results in: [A1 | B | C]
// [A2 | | ]
// [A3 | | ]
// Programmatic control
split.open('A');
split.close('C');
split.toggle('B');
Conductor manages application navigation with history:
const conductor = new Conductor();
// Register view constructors
conductor.registerViewConstructor('dashboard', (pk, data) => {
return new DashboardView(pk, data);
});
// Switch views (records history)
conductor.switchView(new DashboardView());
// Browser back/forward automatically handled
Register UI libraries at application startup:
import zooy from 'zooy';
const entryFunc = async (user) => {
// Register component libraries (lazy-loaded)
await zooy.registerMdcLibrary(); // Material Design Components
await zooy.registerCarbonLibrary(); // IBM Carbon Design System
// Now render your application
const view = new MyView();
view.render();
};
MDC components automatically initialize when panels render:
<button class="mdc-button">
<span class="mdc-button__label">Click Me</span>
</button>
<ul class="mdc-list mdc-tree">
<li class="mdc-list-item">Tree Item 1</li>
<li class="mdc-list-item">Tree Item 2</li>
</ul>
<!-- Panel.parseContent() automatically initializes all MDC components -->
Carbon Web Components load dynamically as needed:
<!-- Use data-carbon-component to specify which component to load -->
<div data-carbon-component="button">
<cds-button>Click Me</cds-button>
</div>
<div data-carbon-component="accordion">
<cds-accordion>
<cds-accordion-item title="Section 1">
Content here
</cds-accordion-item>
</cds-accordion>
</div>
<!-- Components are imported dynamically and cached for performance -->
The ComponentLibraryRegistry architecture provides several key benefits:
Use MDC, Carbon, or build custom components — zooy doesn't care. Mix and match as needed.
Component libraries load on-demand via dynamic imports:
Migrate from one component library to another incrementally:
As component libraries evolve (MDC → Material Web Components), zooy's architecture remains stable. Just swap the registration module.
If your application imports @carbon/styles for theming (e.g., to customize Carbon component styles), you'll need to handle font path configuration:
The Issue: Carbon's default configuration uses Webpack-style paths (~@ibm/plex) that don't work with vanilla Sass compilation.
Solution: Override the font path when importing Carbon styles in your application's SCSS:
// In your application's theme file
@use '@carbon/styles' as * with (
$font-path: '../path/to/node_modules/@ibm/plex'
);
Required Dependencies: Your application's package.json should include:
{
"dependencies": {
"@carbon/styles": "^1.x.x",
"@ibm/plex": "^6.x.x"
}
}
Path Calculation: The $font-path should be relative from your CSS output location to the font files:
/static/css/theme.css/static/js/node_modules/@ibm/plex/...../js/node_modules/@ibm/plexNote: Zooy itself only imports Carbon Web Components (JavaScript), not the Carbon styles (CSS). CSS theming is the responsibility of the implementing application, allowing full control over design tokens, typography, and component styling.
⚠️ Deprecated: Client-side placeholder loading has been removed.
Zooy previously supported client-side icon placeholder loading ([data-carbon-icon]), but this has been removed in favor of server-side rendering for better performance and simplicity.
For Django applications: Use django-zooy which provides server-side icon rendering:
{% load carbon %}
{% carbon_icon "add" 16 slot="icon" %}
<!-- Renders: <svg xmlns="..."><path d="..."/></svg> -->
Benefits of server-side rendering:
For non-Django applications: The programmatic icon API (icons-api.js) is still available for runtime icon generation if needed:
import zooy from 'zooy';
// Create icon programmatically
const iconSvg = await zooy.icons.createIcon('add', 16, { slot: 'icon' });
document.body.appendChild(iconSvg);
However, server-side rendering (in templates/build step) is strongly recommended over runtime generation.
npm install
npm run lint # Check for issues
npm run lint:fix # Auto-fix issues
Husky is configured to run linting before each commit, ensuring code quality.
zooy/
├── src/
│ ├── ui/ # Core UI components
│ │ ├── evt.js # Event system base class
│ │ ├── component.js # Component base class
│ │ ├── panel.js # Content panel (library-agnostic)
│ │ ├── form.js # Form panel with validation
│ │ ├── view.js # Panel orchestrator
│ │ ├── conductor.js # Application controller
│ │ ├── split.js # Resizable layouts
│ │ ├── dragger.js # Drag functionality
│ │ ├── component-library-registry.js # Component library registry
│ │ ├── handlers/ # Event handler collections
│ │ ├── mdc/ # Material Design Components
│ │ │ ├── register.js # MDC registration (lazy-loaded)
│ │ │ └── tree-utils.js # MDC tree utilities
│ │ ├── carbon/ # IBM Carbon Design System
│ │ │ ├── register.js # Carbon registration (lazy-loaded)
│ │ │ ├── renderers.js # Dynamic component loading
│ │ │ └── icons-api.js # Programmatic icon API
│ │ └── zoo/ # Custom Zooy components
│ ├── dom/ # DOM utilities
│ ├── events/ # Event types and utilities
│ ├── uri/ # URI parsing and manipulation
│ └── user/ # User management
├── dist/ # Build output (not committed)
│ ├── zooy.es.js # ES module entry point
│ ├── zooy.cjs.js # CommonJS entry point
│ └── chunks/ # Code-split chunks
│ ├── main-[hash].js # Main framework bundle (~101KB, 27KB gzipped)
│ ├── register-[hash].js # MDC registration chunk (~463KB, 62KB gzipped)
│ └── register-[hash].js # Carbon registration chunk (~34KB, 7KB gzipped)
├── docs/ # Documentation
│ ├── architecture/ # Architecture docs
│ ├── migration/ # Migration guides
│ └── guides/ # Usage guides
├── vite.config.js # Build configuration
├── eslint.config.js # ESLint configuration
└── package.json # Project dependencies
npm run lint:fix to auto-fix issues[License information to be added]
FAQs
UI Building Blocks
We found that zooy demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.

Research
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.

Research
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.