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.
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.
--onav-* custom properties on :rootGetting installed
From WordPress admin
onav.zip and click Install Now, then Activate Plugin.Manual upload
onav/ folder to /wp-content/plugins/onav/.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.Plugin architecture
Constants
| Constant | Value | Description |
|---|---|---|
ONAV_VERSION | 1.0.0 | Plugin version string |
ONAV_PATH | plugin_dir_path(__FILE__) | Absolute filesystem path |
ONAV_URL | plugin_dir_url(__FILE__) | Absolute URL path |
ONAV_OPTION | onav_config | wp_options key for all nav config |
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.
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.
| Panel | What 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 strip | Live iframe preview. Toggle Desktop / Mobile view and Dark / Light theme. Click Refresh to reload after saving. |
Topbar tab
Configures the thin utility bar that sits above the primary nav.
| Field | Description |
|---|---|
| Enable topbar | Show/hide the topbar globally |
| Descriptor text | Short announcement or tagline shown on the left |
| Height (px) | Topbar height — default 36px |
| Background | Any CSS color value |
| Text / Link color | Colors for descriptor text and topbar links |
| Hide on mobile | Hides the topbar below the mobile breakpoint |
| Topbar Links | Links with a side setting (left / right) controlling which side of the topbar they appear on |
Mobile drawer tab
| Field | Description |
|---|---|
| Mirror primary nav | When on (default), mobile uses the same items as the primary nav |
| Menu style | accordion (bottom sheet), slide-left, slide-right, fullscreen |
| Breakpoint (px) | Window width below which mobile menu activates — default 768 |
| Background | Drawer background color |
| CTA Label / URL | CTA 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.
Scroll behavior
| Setting | Description |
|---|---|
| Sticky on scroll | Nav fixes to the top of the viewport after scrolling past it |
| Transparent start | Nav starts with transparent background (over a hero), fills in on scroll |
| Smart hide | Hides nav when scrolling down, reveals it when scrolling up |
| Shadow on scroll | Adds a drop shadow when the nav is in sticky/scrolled state |
| Background style | solid, blur (glass/frosted), or gradient |
| Blur amount | Backdrop blur in pixels — only applies when bg style = blur |
IntersectionObserver on a 1px sentinel element for zero scroll-event cost. Smart hide uses requestAnimationFrame-debounced scroll listener only when that setting is active.Appearance customizer
Color and dimension customizer. No CSS editing required — all values become CSS custom properties on :root.
| Token | Affects |
|---|---|
| Nav background | The nav bar background color |
| Nav background (on scroll) | Background once scrolled — for transparent-start nav |
| Link color | Default link text color |
| Link hover | Link color on hover |
| Active link | Color of the currently active nav item |
| CTA button background / text / hover | Colors for button-type nav items |
| Mega panel background / border | Mega panel popout colors |
| Dropdown background | Simple dropdown background |
| Nav height | Height of the primary nav bar — default 64px |
| Link font size | Font size of all nav link labels — default 14px |
All values accept #rrggbb, rgba(r,g,b,a), or CSS var() references.
Analytics tab
Lightweight, opt-in click tracking. Off by default. No personal data stored — aggregate counts only.
| Element | Description |
|---|---|
| Stat cards | Total clicks tracked · Number of distinct items tracked · Last updated timestamp |
| Opt-in toggle | Enable click tracking. When enabled, each nav item click increments its count in wp_options. |
| Click Counts table | Each tracked item with total click count and share percentage, sorted by count descending |
| Clear data | Permanently deletes all click data from wp_options |
Settings tab
| Feature | Description |
|---|---|
| Export JSON | Downloads a full ONav config .json — backup or migrate to another site |
| Import JSON | Upload a previously exported file. Overwrites current config with a confirmation prompt. |
| WP Menus Sync | Pulls the WP menu at the primary theme location and converts it to ONav items. One-time import — not a live sync. |
| Reset | Resets all config to factory defaults. Export first — this is permanent. |
| Developer API reference | In-panel code reference for all filters, actions, and helper functions |
Nav item types
Button styles
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
| Setting | Options | Description |
|---|---|---|
| Width | container, full | container aligns with the content grid; full spans viewport width |
| Columns | 1–4 | Number of columns in the grid |
| Animation | fade, slide, none | Opening animation |
| Trigger | hover, click | Whether the panel opens on mouse hover or requires a click |
Block types
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
}
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();
functions.php: add_action('get_header', function() { do_action('onav_primary'); }, 1);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.
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;
}
}
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);
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
});
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/
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 () {});
Accessibility (WCAG 2.1 AA)
| Feature | Implementation |
|---|---|
| Keyboard navigation | Arrow keys traverse the top-level nav; ArrowDown enters a dropdown; Escape closes any open panel |
| ARIA roles | role="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 dialog | role="dialog", aria-modal="true", aria-label on the mobile drawer |
| Overlay focus trap | Mobile drawer traps focus while open — focus returns to hamburger on close |
| Screen reader text | Icon-only items have aria-label attributes |
Click analytics
Opt-in and off by default. Aggregate counts only — no IP addresses, user IDs, or session tokens.
| Detail | Value |
|---|---|
| Storage | wp_options under key onav_analytics |
| Data format | {"clicks": {"home": 42, "pricing": 17, …}, "last_updated": "…"} |
| AJAX endpoint | wp_ajax_nopriv_onav_track — accessible to logged-out users so all visitors are counted equally |
| Personal data | None stored. No IPs, no user IDs, no sessions. |
| Clear data | Permanently deletes all analytics from wp_options |
Import & export
Export
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
.json file and confirm. The current config is fully replaced.Multisite support
ONav stores its configuration per-site in wp_options. No network-wide shared config by default.
| Scenario | Approach |
|---|---|
| Per-subsite config | Each subsite has its own independent ONav config — no extra setup needed |
| Network deployment | Export from a "master" site and import on each subsite |
| Programmatic cross-site read | switch_to_blog() + ONAV_Config::get() to read another site's config |
| Future Pro feature | Network admin panel to define a shared base config with per-site overrides |
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": {}
}
}
What's shipped
- 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_optionsas JSON
Frequently asked questions
<?php do_action('onav_primary'); ?> to your header.php and <?php do_action('onav_footer'); ?> to your footer.php. That's it.wp_options. Reactivate and everything comes back exactly as you left it.onav_config and onav_analytics from wp_options. All data is permanently deleted.:root you can override them with your own media query: @media (prefers-color-scheme: light) { :root { --onav-nav-bg: #ffffff; } }functions.php: add_action('get_header', function() { do_action('onav_primary'); }, 1); — or use OCodeInsert to add the hook without touching any theme files.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.Got a question about ONav?
Reach out directly — Kenneth replies within 24 hours.
