Skip to content

feat (theme): Add selectable horizontal dropdown menu themes#9891

Open
gitzone83 wants to merge 2 commits intoopnsense:masterfrom
gitzone83:feature/horizontal-menu
Open

feat (theme): Add selectable horizontal dropdown menu themes#9891
gitzone83 wants to merge 2 commits intoopnsense:masterfrom
gitzone83:feature/horizontal-menu

Conversation

@gitzone83
Copy link
Copy Markdown

Adds two new selectable themes (opnsense-hmenu and opnsense-dark-hmenu) that render the navigation as a horizontal top menu bar instead of the default vertical sidebar.

Changes

  • New horizontal menu partial (base_menu_system_horizontal.volt)
  • Theme CSS/JS with hover-based dropdowns and flyout submenus
  • Legacy PHP page support (head.inc, fbegin.inc) for consistent horizontal menu across all pages
  • Fix Volt template syntax (ends with → explicit comparison)
  • Fix dropdown clipping (overflow-x: autooverflow: visible on .hmenu-nav)

Screenshots

image image image image

Test plan

  • Select opnsense-hmenu theme in System > Settings > General
  • Verify horizontal menu appears on MVC pages (dashboard)
  • Verify horizontal menu appears on legacy pages (e.g. Interfaces > Assignments)
  • Verify dropdown and flyout menus open on hover
  • Verify switching back to default opnsense theme restores sidebar
  • Verify opnsense-dark-hmenu variant works correctly

@Monviech
Copy link
Copy Markdown
Member

Monviech commented Mar 4, 2026

Custom themes are more in the plugin scope.

https://github.com/opnsense/plugins/tree/master/misc

gitzon83 added 2 commits March 5, 2026 09:27
Add a generic theme configuration mechanism that enables theme plugins
to declare alternative menu layouts without modifying core files.

Themes can include a theme.conf JSON file specifying:
- menu_system: which menu partial to render (default: sidebar)
- content_class: CSS classes for the main content wrapper

Core templates read this configuration to dynamically select the menu
partial (base_menu_system_{name}.volt / fbegin_menu_{name}.inc) and
content wrapper classes. The menu_system value is sanitized against
path traversal. Themes without theme.conf behave identically to today.

The sidebar menu code in fbegin.inc is extracted to a separate
fbegin_menu_sidebar.inc include for modularity. The sidebar toggle
in opnsense_theme.js is guarded with a navigation element existence
check so it only activates when the sidebar is present.
Add two horizontal menu themes (opnsense-hmenu and opnsense-dark-hmenu)
that replace the sidebar navigation with a horizontal dropdown menu bar.

Includes theme.conf declaring menu_system as "horizontal", Volt and PHP
menu partials (base_menu_system_horizontal.volt, fbegin_menu_horizontal.inc),
theme.js with dropdown behavior, touch support, and viewport edge detection,
and full CSS/font/image assets for both light and dark variants.

Depends on the theme.conf core mechanism introduced in the previous commit.
@gitzone83 gitzone83 force-pushed the feature/horizontal-menu branch from 3ae55e4 to b95856d Compare March 5, 2026 16:30
opnsenseuser pushed a commit to opnsenseuser/core that referenced this pull request Mar 14, 2026
Fix:

// Upon loading, ( w i n d o w ) . h e i g h t ( ) a n d (window).width() are immediately checked – the same condition as in the resize handler (navHeight / 760px).
// If the viewport is too small → the sidebar is hidden, mouse events are disabled, and the toggle button is hidden.
// If the viewport is large enough → normal behavior as usual (including the localStorage preset).
// Viewport on Safari/iPad (correctly considers tab and address bars)
// iOS window.innerHeight instead of $(window).height()
// Add selectable horizontal dropdown menu themes >> opnsense#9891

Readability & Structure

// var consistently replaced with const/let
// Helpful functions like isSidebarLeft() and isSidebarHidden() extracted to avoid duplicate hasClass calls transition_duration → setTransitionDuration, mouse_events_off → offMouseEvents (clearer names)

Performance

// jQuery selectors are cached only once (e.g., toggle_btn, allLayers, aLayers, divLayers)

// $.each(document.styleSheets, ...) replaced with modern Array.from(...).some(...) – shorter and terminates early toggle_sidebar_loaded check with early return instead of deeply nested if block

Redundancy Reduction

// Duplicate calls consolidated in event handlers Repeated offMouseEvents()/transition_duration() calls in Resize handler consolidates winWidth directly from $(window).width() instead of via win variable
@opnsenseuser opnsenseuser mentioned this pull request Mar 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants