Catalog

main · a3f7c2d
NanoStores store
12
Active storage
+3 for sprint
NanoTags action
47
Tags in the system
+12 for sprint
Sync Rate event
99.2%
Synchronization
+0.8%
Bundle Size computed
1.8KB
Gzipped size
-0.3KB

Application Architecture

Visualization of the NanoStories architecture: the relationship between the store, NanoTags, and scroll synchronization between the sidebar and content viewport.

STATE
Nano Stores
activeSection
scrollOffset
ACTIONS
NanoTags
navigate
syncScroll
VIEW
Components
Sidebar
Viewport
Sidebar — Fixed Layout
Fixed navigation with chapter-links. Subscribed to activeSection to highlight the current section.
Content Viewport — Scrollable
Scrollable content area. Sends onScroll events via NanoTags for synchronization.

State — Nano Stores

Atomic state stores: each store is an independent atom, subscription via subscribe()

stores.js
import { atom, computed } from 'nanostores'

// Atomic store - active partition
export const activeSection = atom('hero')

// Atomic Store - Scroll Offset
export const scrollOffset = atom(0)

// Computed store - reading progress
export const readProgress = computed(
  [scrollOffset],
  (offset) => Math.min(100, (offset / 2000) * 100)
)

// Subscribe to changes
activeSection.subscribe(section => {
  updateSidebarHighlight(section)
})

Stores Registry

StoreTypeValueStatus
activeSectionatom"hero"active
scrollOffsetatom0active
readProgresscomputed0%derived
sidebarCollapsedatomfalseactive
themeModeatom"dark"active
visibleSectionscomputedSet{...}derived

NanoTags — Tagging system

Declarative markup system: annotating actions, events, computed values, and side effects.

@storeState Store Tags
nanotags/store.js
import { tag } from 'nanostories/nanotags'

/** @store — atomic storage */
export const ActiveSectionStore = tag('@store', {
  name: 'activeSection',
  initial: 'hero',
  persist: true,
  validate: (v) => SECTIONS.includes(v)
})
Опции
persist, validate
Методы
get, set, subscribe
Side effects
auto-reconcile
@actionAction and mutation tags
nanotags/actions.js
import { tag } from 'nanostories/nanotags'

/** @action — mutation of the store */
export const NavigateAction = tag('@action', {
  name: 'navigate',
  target: ActiveSectionStore,
  handler: (sectionId) => {
    if (SECTIONS.includes(sectionId)) return sectionId
    throw new Error(`Invalid section: ${sectionId}`)
  },
  debounce: 16,
  track: true
})
@eventEvent and subscription tags
nanotags/events.js
import { tag } from 'nanostories/nanotags'

/** @event — DOM event subscription */
export const ScrollEvent = tag('@event', {
  name: 'viewport:scroll',
  source: '#content-viewport',
  type: 'scroll',
  action: SyncScrollAction,
  options: { passive: true }
})
@computedComputed tags
nanotags/computed.js
import { tag } from 'nanostories/nanotags'

/** @computed — derivative state */
export const ReadProgressTag = tag('@computed', {
  name: 'readProgress',
  deps: [ScrollOffsetStore],
  compute: (offset) => {
    const max = document.querySelector('#content-viewport')
      .scrollHeight - window.innerHeight
    return Math.min(100, (offset / max) * 100)
  },
  memoize: true
})

Matrix NanoTags

6
@store tags
14
@action tags
19
@event tags
8
@computed tags

Data Flow

Unidirectional flow: DOM Event → NanoTag → Store → UI Update. No two-way binding.

DOM Event
NanoTag
Store
dataflow.js
// Scroll processing chain
ScrollEvent
  .pipe(SyncScrollAction)
  .pipe(ScrollOffsetStore)
  .pipe(ReadProgressTag)
  .subscribe((progress) => {
    updateProgressBar(progress)
    updateSidebarState(progress)
  })
dom-structure.html
<div id="app">
  <aside data-nanotag="sidebar">
    <nav data-nanotag="@store:nav">
      <!-- Chapter links -->
    </nav>
  </aside>
  <main data-nanotag="viewport">
    <section data-nanotag="@event:visible">
      <!-- Scrollable content -->
    </section>
  </main>
</div>

Scroll Sync — Synchronization

Synchronization of content scroll position with navigation highlighting via IntersectionObserver and NanoTags.

scroll-sync.js
import { IntersectionEvent, NavigateAction } from './nanotags'

/**
 * Scroll Sync Engine
 * Links IntersectionObserver to NanoTags
 */
export function initScrollSync(sections, navLinks) {
  const observer = new IntersectionObserver(
    (entries) => {
      for (const entry of entries) {
        if (entry.isIntersecting) {
          IntersectionEvent.fire({
            sectionId: entry.target.id,
            ratio: entry.intersectionRatio
          })
          NavigateAction.dispatch(entry.target.id)
        }
      }
    },
    { threshold: [0.2, 0.5, 0.8],
      rootMargin: '-10% 0px -60% 0px' }
  )

  sections.forEach(s => observer.observe(s))

  NavigateAction.onSuccess((id) => {
    navLinks.forEach(link => {
      link.classList.toggle('active',
        link.dataset.section === id)
    })
  })

  return observer
}

Sync Status

ObserverActive
Threshold0.2, 0.5, 0.8
Root Margin-10% 0 -60% 0
Latency~2ms

Read Progress

Progress 0%

DOM Structure

Document structure with NanoTags markup for declarative component binding.

index — structure
<body data-nanotag="app:root">
  <aside data-nanotag="sidebar:fixed" class="fixed-layout">
    <div data-nanotag="@store:logo-section">...</div>
    <nav data-nanotag="@store:chapter-nav">
      <a data-nanotag="@action:navigate" href="#hero">Dashboard</a>
      <a data-nanotag="@action:navigate" href="#architecture">Architecture</a>
      <!-- ... -->
    </nav>
    <div data-nanotag="@computed:sync-status">...</div>
  </aside>

  <main data-nanotag="viewport:scrollable" class="scrollable">
    <section id="hero" data-nanotag="@event:visible">...</section>
    <section id="architecture" data-nanotag="@event:visible">...</section>
    <!-- Each section — @event:visible tag -->
  </main>
</body>

Performance metrics

Monitoring NanoStories key metrics: response time, memory usage, re-render rate.

Store UpdatesLive
1,247
Updates per session
Render TimeAvg
0.8ms
Average Render Time
Memory UsageStable
2.1MB
Peak consumption

Details by NanoTag types

store action event computed
ComponentTypeCallsAverage timeP99Re-renders
activeSection@store3420.3ms1.2ms12
navigate@action2890.5ms2.1ms8
viewport:scroll@event4,8310.1ms0.8ms
readProgress@computed4,8310.2ms0.9ms24
syncScroll@action4,8310.1ms0.4ms
section:visible@event1780.4ms1.8ms6

Analytics and charts

Visualization of NanoTags performance and usage patterns.

Store Updates Over Time

@store @action @event

Tag Distribution

Response Time by Tag Type

Re-render Frequency

Key Principles

Fundamental principles of the NanoStories architecture and the advantages of the approach.

Atomic State
Each store is an independent atom. There is no global state tree. Subscriptions are limited to the atoms you need.
Declarative Tags
NanoTags annotate behavior: @store, @action, @event, @computed. Declarative description instead of imperative code.
Unidirectional Flow
Unidirectional flow: Event → Tag → Store → UI. No cyclic dependencies.
Scroll Sync Engine
IntersectionObserver + NanoTags = Automatic synchronization of sidebar and viewport.

Architecture Benefits

1.8KB
Minimum bundle
Nanostores + NanoTags together weigh less than 2KB gzipped.
0.8ms
Response time
Average time to process an action from the event to the DOM update.
99.2%
Sync Accuracy
Accuracy of scroll and sidebar synchronization.