← Back to task

Commit ddfc4272

commit ddfc4272e7f7f61702f586a9cfff36bd26eb55d8
Author: Coder Agent <coder@agents.omni>
Date:   Wed Feb 18 13:48:18 2026

    Unify task web navbar to double-bar pattern matching news/files
    
    Replace the hamburger sidebar navbar with a clean double-bar layout:
    - Top bar: omni brand link + top-level nav (tasks, news, files, stats)
      using shared on-nav classes from Omni.Web.Style
    - Sub-nav bar: context-sensitive task section links (ready, blocked,
      needs help, all, epics, kb) using new subnav/subnav-link classes
    
    Remove hamburger toggle, dropdown menus, and associated CSS. Keep
    navbar-dropdown styles (still used by sort dropdown widget).
    
    Task-Id: t-636

diff --git a/Omni/Task/Web/Components.hs b/Omni/Task/Web/Components.hs
index 91a9d665..0cdb164f 100644
--- a/Omni/Task/Web/Components.hs
+++ b/Omni/Task/Web/Components.hs
@@ -555,39 +555,21 @@ taskBreadcrumbs allTasks task =
 -- * Navbar
 
 navbar :: (Monad m) => Lucid.HtmlT m ()
-navbar =
-  Lucid.nav_ [Lucid.class_ "navbar"] <| do
-    Lucid.a_ [Lucid.href_ "/tasks", Lucid.class_ "navbar-brand"] "omni"
-    Lucid.input_
-      [ Lucid.type_ "checkbox",
-        Lucid.id_ "navbar-toggle",
-        Lucid.class_ "navbar-toggle-checkbox"
-      ]
-    Lucid.label_
-      [ Lucid.for_ "navbar-toggle",
-        Lucid.class_ "navbar-hamburger"
-      ]
-      <| do
-        Lucid.span_ [Lucid.class_ "hamburger-line"] ""
-        Lucid.span_ [Lucid.class_ "hamburger-line"] ""
-        Lucid.span_ [Lucid.class_ "hamburger-line"] ""
-    Lucid.div_ [Lucid.class_ "navbar-links"] <| do
-      Lucid.a_ [Lucid.href_ "/tasks", Lucid.class_ "navbar-link"] "tasks"
-      Lucid.a_ [Lucid.href_ "/news/", Lucid.class_ "navbar-link"] "news"
-      Lucid.a_ [Lucid.href_ "/files/", Lucid.class_ "navbar-link"] "files"
-      Lucid.div_ [Lucid.class_ "navbar-dropdown"] <| do
-        Lucid.button_ [Lucid.class_ "navbar-dropdown-btn"] "tasks ▾"
-        Lucid.div_ [Lucid.class_ "navbar-dropdown-content"] <| do
-          Lucid.a_ [Lucid.href_ "/ready", Lucid.class_ "navbar-dropdown-item"] "ready"
-          Lucid.a_ [Lucid.href_ "/blocked", Lucid.class_ "navbar-dropdown-item"] "blocked"
-          Lucid.a_ [Lucid.href_ "/intervention", Lucid.class_ "navbar-dropdown-item"] "needs help"
-          Lucid.a_ [Lucid.href_ "/tasks", Lucid.class_ "navbar-dropdown-item"] "all"
-      Lucid.div_ [Lucid.class_ "navbar-dropdown"] <| do
-        Lucid.button_ [Lucid.class_ "navbar-dropdown-btn"] "plans ▾"
-        Lucid.div_ [Lucid.class_ "navbar-dropdown-content"] <| do
-          Lucid.a_ [Lucid.href_ "/epics", Lucid.class_ "navbar-dropdown-item"] "epics"
-          Lucid.a_ [Lucid.href_ "/kb", Lucid.class_ "navbar-dropdown-item"] "kb"
-      Lucid.a_ [Lucid.href_ "/stats", Lucid.class_ "navbar-link"] "stats"
+navbar = do
+  Lucid.nav_ [Lucid.class_ "on-nav"] <| do
+    Lucid.a_ [Lucid.href_ "/", Lucid.class_ "on-nav-brand"] "omni"
+    Lucid.div_ [Lucid.class_ "on-nav-links"] <| do
+      Lucid.a_ [Lucid.href_ "/tasks", Lucid.class_ "on-nav-link on-active"] "tasks"
+      Lucid.a_ [Lucid.href_ "/news/", Lucid.class_ "on-nav-link"] "news"
+      Lucid.a_ [Lucid.href_ "/files/", Lucid.class_ "on-nav-link"] "files"
+      Lucid.a_ [Lucid.href_ "/stats", Lucid.class_ "on-nav-link"] "stats"
+  Lucid.div_ [Lucid.class_ "subnav"] <| do
+    Lucid.a_ [Lucid.href_ "/ready", Lucid.class_ "subnav-link"] "ready"
+    Lucid.a_ [Lucid.href_ "/blocked", Lucid.class_ "subnav-link"] "blocked"
+    Lucid.a_ [Lucid.href_ "/intervention", Lucid.class_ "subnav-link"] "needs help"
+    Lucid.a_ [Lucid.href_ "/tasks", Lucid.class_ "subnav-link"] "all"
+    Lucid.a_ [Lucid.href_ "/epics", Lucid.class_ "subnav-link"] "epics"
+    Lucid.a_ [Lucid.href_ "/kb", Lucid.class_ "subnav-link"] "kb"
 
 -- * Badges
 
diff --git a/Omni/Task/Web/Style.hs b/Omni/Task/Web/Style.hs
index f500d5f9..7a8d0f2c 100644
--- a/Omni/Task/Web/Style.hs
+++ b/Omni/Task/Web/Style.hs
@@ -147,65 +147,26 @@ layoutStyles = do
 
 navigationStyles :: Css
 navigationStyles = do
-  ".navbar" ? do
-    fontFamily WebStyle.fontMono [monospace]
-    backgroundColor WebStyle.cBg
-    padding (px 8) (px 16) (px 8) (px 16)
-    borderBottom (px 1) solid WebStyle.cBorder
-    display flex
-    alignItems center
-    justifyContent spaceBetween
-    flexWrap Flexbox.wrap
-    Stylesheet.key "gap" ("8px" :: Text)
-    position sticky
-    top nil
-    zIndex 100
-    Stylesheet.key "backdrop-filter" ("blur(12px)" :: Text)
-    Stylesheet.key "-webkit-backdrop-filter" ("blur(12px)" :: Text)
-  ".navbar-brand" ? do
-    fontSize (px 14)
-    fontWeight bold
-    color WebStyle.cAccent
-    textDecoration none
-    Stylesheet.key "letter-spacing" ("0.05em" :: Text)
-  ".navbar-brand" # hover ? do
-    textDecoration none
-    opacity 0.8
-  ".navbar-toggle-checkbox" ? display none
-  ".navbar-hamburger" ? do
-    display none
-    flexDirection column
-    justifyContent center
-    alignItems center
-    width (px 32)
-    height (px 32)
-    cursor pointer
-    Stylesheet.key "gap" ("4px" :: Text)
-  ".hamburger-line" ? do
-    display block
-    width (px 20)
-    height (px 2)
-    backgroundColor WebStyle.cFgMuted
-    borderRadius (px 1) (px 1) (px 1) (px 1)
-    transition "all" (ms 200) ease (sec 0)
-  ".navbar-links" ? do
+  -- Sub-navigation bar (second bar, section-specific links)
+  ".subnav" ? do
     display flex
     Stylesheet.key "gap" ("2px" :: Text)
-    flexWrap Flexbox.wrap
-    alignItems center
-  ".navbar-link" ? do
-    display inlineBlock
+    padding (px 4) (px 16) (px 4) (px 16)
+    borderBottom (px 1) solid WebStyle.cBorderSubtle
+    fontFamily WebStyle.fontMono [monospace]
+    fontSize (px 12)
+  ".subnav-link" ? do
+    fontSize (px 12)
+    color WebStyle.cFgFaint
     padding (px 4) (px 10) (px 4) (px 10)
-    color WebStyle.cFgMuted
-    textDecoration none
     borderRadius WebStyle.radiusSm WebStyle.radiusSm WebStyle.radiusSm WebStyle.radiusSm
-    fontSize (px 12)
-    fontWeight (weight 500)
+    textDecoration none
     transition "all" (ms 150) ease (sec 0)
-  ".navbar-link" # hover ? do
-    backgroundColor WebStyle.cBgHover
+  ".subnav-link" # hover ? do
     color WebStyle.cFg
+    backgroundColor WebStyle.cBgHover
     textDecoration none
+  -- Dropdown styles (used by sort dropdown)
   ".navbar-dropdown" ? do
     position relative
     display inlineBlock
@@ -1901,40 +1862,11 @@ responsiveStyles = do
   query Media.screen [Media.maxWidth (px 600)] <| do
     body ? fontSize (px 15)
     ".container" ? padding (px 8) (px 10) (px 8) (px 10)
-    ".navbar" ? do
-      padding (px 8) (px 10) (px 8) (px 10)
+    ".subnav" ? do
+      padding (px 4) (px 8) (px 4) (px 8)
       flexWrap Flexbox.wrap
-    ".navbar-hamburger" ? do
-      display flex
-      Stylesheet.key "order" ("2" :: Text)
-    ".navbar-links" ? do
-      display none
-      width (pct 100)
-      Stylesheet.key "order" ("3" :: Text)
-      flexDirection column
-      alignItems flexStart
-      paddingTop (px 8)
-      Stylesheet.key "gap" ("0" :: Text)
-    ".navbar-toggle-checkbox" # checked |+ ".navbar-hamburger" |+ ".navbar-links" ? do
-      display flex
-    ".navbar-link" ? do
-      padding (px 10) (px 8) (px 10) (px 8)
-      fontSize (px 14)
-      width (pct 100)
-    ".navbar-dropdown" ? do
-      width (pct 100)
-    ".navbar-dropdown-btn" ? do
-      padding (px 10) (px 8) (px 10) (px 8)
-      fontSize (px 14)
-      width (pct 100)
-      textAlign (alignSide sideLeft)
-    ".navbar-dropdown-content" ? do
-      position static
-      Stylesheet.key "box-shadow" ("none" :: Text)
-      paddingLeft (px 12)
-      backgroundColor transparent
-    ".navbar-dropdown-item" ? do
-      padding (px 8) (px 10) (px 8) (px 10)
+    ".subnav-link" ? do
+      padding (px 6) (px 8) (px 6) (px 8)
       fontSize (px 13)
     ".nav-content" ? do
       flexDirection column