Developer documentation

ONav v1.0.0

A standalone WordPress navigation builder — completely independent of Appearance → Menus. Primary nav, topbar, footer, and mobile drawer, all managed from one place, with mega panels, click analytics, and a clean developer API.

WordPress plugin No database tables GPL-2.0+ v1.0.0 · Initial release
01 · Overview

What ONav does

ONav operates completely independently of WordPress's built-in menu system. Every nav item, mega panel, CTA button, and mobile menu is defined inside ONav — no WP menu assignment, no theme dependency, no way for a theme update to break your navigation.

📐
4 nav zones
Primary, Topbar, Footer, and Mobile — each configured independently
Mega panel builder
1–4 columns, 6 block types: link-list, featured, recent-posts, image, HTML, icon-grid
📱
Dedicated mobile editor
Separate items or mirror from primary. 4 drawer styles.
🎨
CSS token system
All colours and dimensions as --onav-* custom properties on :root
📊
Click analytics
Opt-in aggregate click counting — no IPs, no personal data
⌨️
Keyboard + ARIA
WCAG 2.1 AA — arrow keys, Escape, role=navigation, aria-expanded
📤
Import / export
Full JSON config backup and agency deployment workflow
🔄
WP menus sync
One-click import from an existing Appearance → Menus menu
02 · Installation

Getting installed

From WordPress admin

1
Go to Plugins → Add New → Upload Plugin.
2
Select onav.zip and click Install Now, then Activate Plugin.
3
Navigate to ONav in the admin sidebar.

Manual upload

1
Upload the onav/ folder to /wp-content/plugins/onav/.
2
Go to Plugins → Installed Plugins and activate ONav — Navigation Builder.
💡ONav creates no database tables. All configuration is stored in a single wp_options entry (onav_config) as JSON. On first activation the plugin uses built-in generic defaults — replace these with your site's actual content.
03 · Architecture

Plugin architecture

onav/ ├── onav.php Plugin bootstrap, constants, lifecycle hooks ├── includes/ │ ├── class-onav-config.php Options storage, defaults, sanitisation │ ├── class-onav-renderer.php Frontend HTML output │ └── class-onav-tracker.php Click analytics ├── admin/ │ └── class-onav-admin.php Admin UI (all tabs), AJAX handlers └── assets/ ├── css/ │ ├── onav-admin.css Admin UI styles (Orravo design system) │ └── onav-front.css Frontend nav styles └── js/ ├── onav-admin.js Admin interactions (jQuery) └── onav-front.js Frontend behavior (vanilla JS, ~8KB)

Constants

ConstantValueDescription
ONAV_VERSION1.0.0Plugin version string
ONAV_PATHplugin_dir_path(__FILE__)Absolute filesystem path
ONAV_URLplugin_dir_url(__FILE__)Absolute URL path
ONAV_OPTIONonav_configwp_options key for all nav config
04 · Admin Interface

Admin interface

Two-row sticky header (brand bar + horizontal tab nav), full-width card-based content — the Orravo Plugin Design System. No WP admin sidebar. Light/dark toggle persisted to localStorage under key onav_theme.

4.1 · Primary Nav Tab

Primary nav editor

The main editor for your site's header navigation. Split into a sortable item list on the left, an item editor panel on the right, and a live preview iframe below.

PanelWhat it does
Item List (left)Sortable rows showing type badge, label, URL, and actions. Drag by the ⠿ handle to reorder. Mega items show a Panel button to open the Mega Panel Builder.
Item Editor (right)Appears on Add Item or pencil icon. Fields adapt to the selected type. Dropdown items show a child link manager. Save Item stages changes — click the top-right Save button to persist.
Preview stripLive iframe preview. Toggle Desktop / Mobile view and Dark / Light theme. Click Refresh to reload after saving.
4.2 · Topbar Tab

Topbar tab

Configures the thin utility bar that sits above the primary nav.

FieldDescription
Enable topbarShow/hide the topbar globally
Descriptor textShort announcement or tagline shown on the left
Height (px)Topbar height — default 36px
BackgroundAny CSS color value
Text / Link colorColors for descriptor text and topbar links
Hide on mobileHides the topbar below the mobile breakpoint
Topbar LinksLinks with a side setting (left / right) controlling which side of the topbar they appear on
4.4 · Mobile Tab

Mobile drawer tab

FieldDescription
Mirror primary navWhen on (default), mobile uses the same items as the primary nav
Menu styleaccordion (bottom sheet), slide-left, slide-right, fullscreen
Breakpoint (px)Window width below which mobile menu activates — default 768
BackgroundDrawer background color
CTA Label / URLCTA button shown at the bottom of the drawer

When Mirror primary nav is off, define a completely separate set of items for mobile only using the Custom Mobile Items card.

4.5 · Behavior Tab

Scroll behavior

SettingDescription
Sticky on scrollNav fixes to the top of the viewport after scrolling past it
Transparent startNav starts with transparent background (over a hero), fills in on scroll
Smart hideHides nav when scrolling down, reveals it when scrolling up
Shadow on scrollAdds a drop shadow when the nav is in sticky/scrolled state
Background stylesolid, blur (glass/frosted), or gradient
Blur amountBackdrop blur in pixels — only applies when bg style = blur
ℹ️Sticky/transparent detection uses IntersectionObserver on a 1px sentinel element for zero scroll-event cost. Smart hide uses requestAnimationFrame-debounced scroll listener only when that setting is active.
4.6 · Appearance Tab

Appearance customizer

Color and dimension customizer. No CSS editing required — all values become CSS custom properties on :root.

TokenAffects
Nav backgroundThe nav bar background color
Nav background (on scroll)Background once scrolled — for transparent-start nav
Link colorDefault link text color
Link hoverLink color on hover
Active linkColor of the currently active nav item
CTA button background / text / hoverColors for button-type nav items
Mega panel background / borderMega panel popout colors
Dropdown backgroundSimple dropdown background
Nav heightHeight of the primary nav bar — default 64px
Link font sizeFont size of all nav link labels — default 14px

All values accept #rrggbb, rgba(r,g,b,a), or CSS var() references.

4.7 · Analytics Tab

Analytics tab

Lightweight, opt-in click tracking. Off by default. No personal data stored — aggregate counts only.

ElementDescription
Stat cardsTotal clicks tracked · Number of distinct items tracked · Last updated timestamp
Opt-in toggleEnable click tracking. When enabled, each nav item click increments its count in wp_options.
Click Counts tableEach tracked item with total click count and share percentage, sorted by count descending
Clear dataPermanently deletes all click data from wp_options
4.8 · Settings Tab

Settings tab

FeatureDescription
Export JSONDownloads a full ONav config .json — backup or migrate to another site
Import JSONUpload a previously exported file. Overwrites current config with a confirmation prompt.
WP Menus SyncPulls the WP menu at the primary theme location and converts it to ONav items. One-time import — not a live sync.
ResetResets all config to factory defaults. Export first — this is permanent.
Developer API referenceIn-panel code reference for all filters, actions, and helper functions
05 · Nav Item Types

Nav item types

link
Simple Link
label, url, new_tab
dropdown
Dropdown
label, url, new_tab + children[]
mega
Mega Panel
label, url + mega_panel{}
button
CTA Button
label, url, button_style, new_tab
search
Search
Icon that expands a search bar on click — no label/url needed
cart
Cart (Pro)
WooCommerce mini-cart icon
user
User
Avatar if logged in, login link if not
separator
Separator
Visual divider line — no fields
html
HTML
Raw HTML injected into the nav via html_content field

Button styles

primary
Get Started
secondary
Learn more
outline
Sign in
ghost
Docs
06 · Mega Panel Builder

Mega panel builder

Accessible via the Panel button on any mega type item. Build multi-column panels with up to 6 different block types per column.

Panel settings

SettingOptionsDescription
Widthcontainer, fullcontainer aligns with the content grid; full spans viewport width
Columns1–4Number of columns in the grid
Animationfade, slide, noneOpening animation
Triggerhover, clickWhether the panel opens on mouse hover or requires a click

Block types

link-list
Link List
Labeled section with links — label + URL + optional description
featured
Featured
Highlight block: image, title, body text, CTA link
recent-posts
Recent Posts
Auto-fetches latest N posts from an optional category
image
Image
An image with an optional link
html
HTML
Raw HTML content — supports any markup
icon-grid
Icon Grid
Like link-list but styled for icon + label + description cards

Block data structures

JSON · link-list / icon-grid{
  "column": 0,
  "type": "link-list",
  "heading": "Section Title",
  "links": [
    { "label": "Item One", "url": "/path/", "desc": "Short description", "icon": "" }
  ]
}
JSON · featured{
  "column": 1,
  "type": "featured",
  "image": "https://example.com/image.jpg",
  "title": "Featured Title",
  "body": "Description text.",
  "cta": "Learn more",
  "cta_url": "/learn-more/"
}
JSON · recent-posts{
  "column": 2,
  "type": "recent-posts",
  "heading": "Latest",
  "count": 3,
  "category": 0
}
08 · Frontend Usage

Frontend usage

ONav does not auto-inject into any theme location. Call it from your theme templates.

PHP · header.php<?php do_action('onav_primary'); ?>
PHP · footer.php<?php do_action('onav_footer'); ?>
PHP · direct rendererONAV_Renderer::render_primary();
ONAV_Renderer::render_footer();
ℹ️For child themes without editing parent files, add the hook in functions.php: add_action('get_header', function() { do_action('onav_primary'); }, 1);
8.1 · CSS Custom Properties

CSS custom properties

ONav injects a <style id="onav-vars"> block into wp_head with all color and dimension values as CSS custom properties on :root. Use them freely in your own theme CSS.

--onav-nav-bg: #0b0b0c; --onav-nav-bg-scroll: #0b0b0c; --onav-link: rgba(255,255,255,0.65); --onav-link-hover: #ffffff; --onav-link-active: #ff6a2b; --onav-cta-bg: #ff6a2b; --onav-cta-text: #ffffff; --onav-cta-hover: #ff7a42; --onav-mega-bg: #121214; --onav-mega-border: #1f1f23; --onav-dd-bg: #121214; --onav-fs: 14px; --onav-h: 64px; --onav-topbar-h: 36px; --onav-topbar-bg: #0b0b0c; --onav-topbar-text: rgba(255,255,255,.6); --onav-topbar-link: rgba(255,255,255,.85);
CSS · example usage.my-hero {
  /* Push content below both topbar + nav */
  padding-top: calc(var(--onav-h) + var(--onav-topbar-h));
}

/* Dark mode override */
@media (prefers-color-scheme: light) {
  :root {
    --onav-nav-bg: #ffffff;
    --onav-link:   #1a1814;
  }
}
09 · Developer API — Filters

Filters

onav_primary_items

Modify the array of primary nav items before they are rendered.

PHPadd_filter('onav_primary_items', function (array $items): array {
    // Add a conditional item for logged-in users
    if (is_user_logged_in()) {
        $items[] = [
            'id'    => 'my-account',
            'type'  => 'link',
            'label' => 'My Account',
            'url'   => '/my-account/',
        ];
    }
    return $items;
});

onav_render_item

Modify the rendered HTML of a single nav item.

PHPadd_filter('onav_render_item', function (string $html, array $item): string {
    if ($item['id'] === 'blog') {
        $html = str_replace('class="onav-label"', 'class="onav-label onav-label--featured"', $html);
    }
    return $html;
}, 10, 2);

onav_mega_panel

Modify the rendered mega panel HTML for a specific item.

PHPadd_filter('onav_mega_panel', function (string $html, array $item): string {
    return $html;
}, 10, 2);
9.2 · Actions

Action hooks

onav_before_render & onav_after_render

PHP// Fires before the primary nav HTML is output
add_action('onav_before_render', function (): void {
    // e.g. set up a nonce or output custom data
});

// Fires after the primary nav HTML is output
add_action('onav_after_render', function (): void {
    // e.g. output additional markup
});
9.3 · Functions

Helper functions

PHP// Get nav items for any slot
$primary_items = onav_get_items('primary');    // onav_primary_items filter applied
$footer_cols   = onav_get_items('footer');     // returns columns array
$topbar_items  = onav_get_items('topbar');

// Get the full merged config
$cfg      = ONAV_Config::get();
$behavior = $cfg['primary']['behavior'];

// Get a single slot's config
$primary = ONAV_Config::get_slot('primary');
$mobile  = ONAV_Config::get_slot('mobile');

// Resolve a relative URL
$url = ONAV_Config::resolve_url('/about/');
// → https://example.com/about/
9.4 · JavaScript Events

JavaScript events

ONav dispatches custom DOM events on document for all major interaction states.

JS// Dropdown or mega panel opened
document.addEventListener('onav:open', function (e) {
    console.log('Opened item:', e.detail.item);
});

// Dropdown or mega panel closed
document.addEventListener('onav:close', function (e) {
    console.log('Closed item:', e.detail.item);
});

// Mobile drawer opened
document.addEventListener('onav:mobile:open', function () {});

// Mobile drawer closed
document.addEventListener('onav:mobile:close', function () {});
10 · Accessibility

Accessibility (WCAG 2.1 AA)

FeatureImplementation
Keyboard navigationArrow keys traverse the top-level nav; ArrowDown enters a dropdown; Escape closes any open panel
ARIA rolesrole="navigation" on the bar; aria-haspopup and aria-expanded on dropdown triggers; role="menu" / role="menuitem" on dropdowns
Focus visible:focus-visible outlines on all interactive elements using --onav-link-active color
Mobile dialogrole="dialog", aria-modal="true", aria-label on the mobile drawer
Overlay focus trapMobile drawer traps focus while open — focus returns to hamburger on close
Screen reader textIcon-only items have aria-label attributes
11 · Click Analytics

Click analytics

Opt-in and off by default. Aggregate counts only — no IP addresses, user IDs, or session tokens.

DetailValue
Storagewp_options under key onav_analytics
Data format{"clicks": {"home": 42, "pricing": 17, …}, "last_updated": "…"}
AJAX endpointwp_ajax_nopriv_onav_track — accessible to logged-out users so all visitors are counted equally
Personal dataNone stored. No IPs, no user IDs, no sessions.
Clear dataPermanently deletes all analytics from wp_options
12 · Import / Export

Import & export

Export

1
Go to ONav → Settings.
2
Click Export JSON. A file named onav-config-YYYY-MM-DD.json is downloaded.

The export contains the full config for all slots — items, mega panels, behavior, appearance, and analytics settings (but not analytics data).

Import

1
Go to ONav → Settings → Import JSON.
2
Select your .json file and confirm. The current config is fully replaced.
3
The page reloads automatically after import.
💡Agency workflow: Export a base config from one site, then import it on every new client site to start from a consistent navigation structure.
14 · Multisite

Multisite support

ONav stores its configuration per-site in wp_options. No network-wide shared config by default.

ScenarioApproach
Per-subsite configEach subsite has its own independent ONav config — no extra setup needed
Network deploymentExport from a "master" site and import on each subsite
Programmatic cross-site readswitch_to_blog() + ONAV_Config::get() to read another site's config
Future Pro featureNetwork admin panel to define a shared base config with per-site overrides
15 · Config Schema

Config schema reference

The full onav_config option stores a JSON object with this structure.

JSON{
  "primary": {
    "items": [
      {
        "id": "string",
        "type": "link|dropdown|mega|button|search|separator|cart|user|html",
        "label": "string",
        "url": "string",
        "new_tab": false,
        "children": [],
        "mega_panel": {},
        "button_style": "primary|secondary|outline|ghost",
        "html_content": "string"
      }
    ],
    "behavior": {
      "sticky": true,
      "transparent": false,
      "smart_hide": false,
      "shadow_scroll": true,
      "bg_type": "solid|blur|gradient",
      "blur_amount": 12
    },
    "appearance": {
      "nav_bg": "#0b0b0c",
      "nav_bg_scroll": "#0b0b0c",
      "link_color": "rgba(255,255,255,0.65)",
      "link_hover": "#ffffff",
      "link_active": "#ff6a2b",
      "cta_bg": "#ff6a2b",
      "cta_text": "#ffffff",
      "cta_hover_bg": "#ff7a42",
      "mega_bg": "#121214",
      "mega_border": "#1f1f23",
      "dropdown_bg": "#121214",
      "font_size": 14,
      "height": 64
    }
  },
  "topbar": {
    "enabled": true,
    "items": [],
    "height": 36,
    "bg": "#0b0b0c",
    "text_color": "rgba(255,255,255,0.6)",
    "link_color": "rgba(255,255,255,0.85)",
    "descriptor": "",
    "hide_mobile": true
  },
  "footer": {
    "enabled": true,
    "columns": [
      {
        "heading": "string",
        "links": [ { "label": "string", "url": "string" } ]
      }
    ],
    "copyright": "© 2025 Your Brand",
    "copyright_url": ""
  },
  "mobile": {
    "items": [],
    "use_primary": true,
    "style": "accordion|slide-left|slide-right|fullscreen",
    "breakpoint": 768,
    "bg": "#0b0b0c",
    "cta_label": "Get Started",
    "cta_url": "/get-started/"
  },
  "analytics": {
    "enabled": false,
    "clicks": {}
  }
}
16 · Changelog

What's shipped

v1.0.0 Initial release
  • Full plugin from scratch (rebrand from fideograph-nav)
  • Multiple menu slots: primary, topbar, footer, mobile
  • 9 nav item types: link, dropdown, mega, button, search, cart, user, separator, html
  • Mega panel builder: 1–4 columns, 6 block types
  • Mobile drawer: accordion, slide-left, slide-right, fullscreen
  • Nav behavior: sticky, transparent, smart-hide, shadow-scroll
  • Appearance customizer: full CSS token system, height, font-size
  • Live preview iframe in admin
  • Import / export JSON config
  • WP Menus one-click import
  • Click analytics (opt-in, aggregate only)
  • Keyboard navigation (arrow keys, Escape)
  • ARIA roles throughout — WCAG 2.1 AA
  • Developer filters: onav_primary_items, onav_render_item, onav_mega_panel
  • Developer actions: onav_before_render, onav_after_render
  • Helper functions: onav_get_items(), ONAV_Config::get()
  • Clean install / uninstall hooks
  • Single consolidated JS file (~8KB, vanilla JS)
  • Orravo admin UI design system (exact Omailer match)
  • No database tables — stored in wp_options as JSON
17 · FAQ

Frequently asked questions

Does ONav replace WordPress menus?
Yes. ONav is entirely independent of WP Appearance → Menus. You do not need to create or assign a WP menu — ONav stores its own nav data and renders it directly. You can still sync from a WP menu if you have an existing one.
Will a theme update break my nav?
No. ONav renders its own HTML via PHP, outputs its own CSS, and drives its own JS. It does not depend on any theme file or WP menu registration.
How do I add the nav to my theme?
Add <?php do_action('onav_primary'); ?> to your header.php and <?php do_action('onav_footer'); ?> to your footer.php. That's it.
Can I use ONav with Elementor or Divi?
Yes. ONav outputs standard HTML/CSS and works alongside any page builder. You may need to set the builder's header to "none" or disable its built-in nav widget to avoid conflicts.
Does ONav track user data?
No. The optional click analytics only store aggregate counts — how many times each nav item was clicked. No IPs, user IDs, or sessions are stored.
What happens if I deactivate ONav?
The nav stops rendering. Your config is preserved in wp_options. Reactivate and everything comes back exactly as you left it.
What happens if I uninstall ONav?
The uninstall hook removes both onav_config and onav_analytics from wp_options. All data is permanently deleted.
Can I set different nav colors for dark mode?
ONav doesn't have a frontend dark/light toggle, but because all colors are CSS custom properties on :root you can override them with your own media query: @media (prefers-color-scheme: light) { :root { --onav-nav-bg: #ffffff; } }
How do I add ONav to a child theme without editing parent theme files?
Use a template hook from your child theme's functions.php: add_action('get_header', function() { do_action('onav_primary'); }, 1); — or use OCodeInsert to add the hook without touching any theme files.
Can I have more than one primary nav on a page?
Calling do_action('onav_primary') twice renders it twice. For two different nav configurations on one page, use onav_get_items() with the onav_primary_items filter to return different items for each instance.
✦ Need help?

Got a question about ONav?

Reach out directly — Kenneth replies within 24 hours.