@everystate/pattern-catalogue
v1.1.0
Published
EveryState Pattern Catalogue - 13 UI patterns demonstrating the No Ceiling architecture: declarative specs, imperative components, and the { component } escape hatch
Maintainers
Readme
@everystate/pattern-catalogue
EveryState Pattern Catalogue - A comprehensive demonstration of 13 UI patterns using the "No Ceiling" architecture.
This application showcases the hybrid approach where declarative view specs and imperative components work together seamlessly through the { component } escape hatch. The store's dot-paths are the universal interface.
Architecture: No Ceiling
Declarative Specs Imperative Components
(from @everystate/view-ui) (from @everystate/ui)
\ /
------> composed via <-----
{ component: "name" }No floor: Beginners write declarative specs
No ceiling: Experts drop to imperative when needed
Universal interface: The store's dot-paths connect everything
What's Included
13 UI Patterns
| Pattern | Type | Package | Demonstrates | |---------|------|---------|--------------| | Counters | Declarative | @everystate/view-ui | forEach, bind, drag-and-drop reordering | | Table | Declarative | @everystate/view-ui | Nested forEach, fragment forEach, bind interpolation | | Undo/Redo | Imperative | @everystate/ui | History tracking, time-travel debugging | | Accordion | Imperative | @everystate/ui | Exclusive visibility, toggle state | | Toasts | Imperative | @everystate/ui | Auto-dismiss timers, stacking | | FSM | Imperative | @everystate/ui | Finite state machines, parallel HSM | | Grid | Imperative | @everystate/ui | Derived state, computed subtotals | | Form | Imperative | @everystate/ui | Validation, error display, derived valid state | | Todos | Imperative | @everystate/ui | Hierarchical TOC, nested lists | | SWAPI | Imperative | @everystate/ui | Async data, loading states, pagination | | Tabs | Imperative | @everystate/ui | Exclusive panels, content preservation | | Modals | Imperative | @everystate/ui | Stacking, focus management | | Theme | Imperative | @everystate/ui | CSS custom properties, data-theme attribute |
5 Pages
- Home (
/) - Counters, Undo/Redo, Accordion, Toasts, FSM - Data (
/data) - Table, Grid - Forms (
/forms) - Form validation, Todos - Async (
/async) - SWAPI async data fetching - UI (
/ui) - Tabs, Modals
Installation
npm install @everystate/pattern-catalogue
cd node_modules/@everystate/pattern-catalogue
npm install
npm run devOpen http://localhost:8080
Quick Start (Clone & Run)
git clone https://github.com/ImsirovicAjdin/everystate-pattern-catalogue
cd everystate-pattern-catalogue
npm install
npm run devHow It Works
1. Store as Universal Interface
All components (declarative and imperative) read and write to the same store:
const store = createEveryState({
counterIds: ['a', 'b', 'c'],
counters: { a: { count: 0 }, b: { count: 0 }, c: { count: 0 } },
// ... all other state
});2. One Factory, One Line Per Page
createSpecPage auto-merges handler factories and auto-names component mount functions:
import { createSpecPage } from '@everystate/view/app';
import { tableHandlers, countersHandlers } from '...';
import { mountTabs, mountGrid, mountFSM } from '...';
const page = createSpecPage({
store,
handlers: [tableHandlers, countersHandlers],
components: [mountTabs, mountGrid, mountFSM],
});3. Pages Are Pure Data
Specs are JSON-serializable objects. No boot functions, no manual wiring:
const homeSpec = {
tag: "div", children: [
countersSpec, // declarative: forEach + bind
{ component: "undoRedo" }, // imperative escape hatch
{ component: "fsm" }, // imperative escape hatch
]
};4. Routes Map Specs to Pages
const router = createRouter({
store,
routes: [
{ path: '/', view: 'home', component: page(homeSpec) },
{ path: '/data', view: 'data', component: page(dataSpec) },
{ path: '/forms', view: 'forms', component: page(formsSpec) },
],
});
router.start();The entire app wiring is 3 statements: create the store, create the page factory, start the router.
Key Concepts
The { component } Escape Hatch
When a declarative spec contains { component: "name" }, the view interpreter:
- Creates a container element
- Looks up the mount function from the
componentsregistry - Calls
mountFn(store, container) - Stores the returned teardown function
- Treats it as a leaf node (no child recursion)
The imperative component has full DOM control inside its container. The store is the only shared interface.
Dot-Path Subscriptions
Both declarative and imperative components use the same subscription model:
// Declarative: bind attribute
{ tag: "h2", bind: "counters.{counterId}.count" }
// Imperative: explicit subscription
store.subscribe('counters.a.count', (v) => el.textContent = v);Wildcards work everywhere:
store.subscribe('counters.*', () => recalculateTotal());TOC Pattern
Table of Contents (TOC) arrays drive list rendering:
{
counterIds: ['a', 'b', 'c'], // <- TOC
counters: { // <- data indexed by TOC
a: { count: 0 },
b: { count: 0 },
c: { count: 0 },
}
}Declarative: forEach: "counterIds"
Imperative: syncTOC(store, 'counterIds', container, mountFn)
Architecture Notes
See option6-no-ceiling-notes.md for a deep dive into:
- Why this architecture exists
- Comparison to other frameworks
- Design principles and tradeoffs
- The journey from options 1-6
Dependencies
All EveryState packages are peer dependencies or regular dependencies:
@everystate/core- Store with dot-path subscriptions@everystate/view- Declarative spec interpreter@everystate/view-ui- Declarative component specs@everystate/ui- Imperative components@everystate/router- SPA routing as state@everystate/aliases- DOM shorthand (mk, data, cls, on, append)
Scripts
npm run dev # Start Vite dev server on port 8080
npm run build # Build for production
npm run preview # Preview production buildPhilosophy
- Transparent: Every spec is a readable plain object, every component is vanilla JS
- No ceiling: Escape to imperative when declarative becomes awkward
- No floor: Declarative specs are beginner-friendly
- Zero magic: No virtual DOM, no reconciliation, no hidden reactivity
- Longevity: Plain JS objects and functions never break
License
MIT © Ajdin Imsirovic
