DEV Community

Cover image for A simple responsive navbar component with Juris
ArtyProg
ArtyProg

Posted on • Edited on

A simple responsive navbar component with Juris

A very simple responsivr Navbar with Juris.
Pure Javascript, no JSX, hooks, memo, no blunder.
Notice, that a great part of the script deals with styling

Navbar

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <title>Juris Responsive Header Component - Fixed</title>
  <script src="https://unpkg.com/juris@0.87.1/juris.mini.js"></script>
  <style>
    body {
      margin: 0;
      padding: 0;
      font-family: sans-serif;
      background: linear-gradient(135deg, #f0f0f0 0%, #e0e0e0 100%);
    }

    li:hover {
      color: red !important;
      text-decoration: underline;
    }

    /* Additional smooth transitions */
    nav {
      transition: all 0.3s ease;
    }

    .hamb {
      transition: transform 0.2s ease;
    }

    .hamb:hover {
      transform: scale(1.7) !important;
    }
  </style>
</head>

<body>

  <div id="app"></div>

  <script>
    const ResponsiveHeader = (props, context) => {
      // Correct: Using getState and setState from context
      const { getState, setState } = context;

      // Improved: Better media query handling
      const setupMediaQuery = () => {
        const mq = window.matchMedia("(max-width: 640px)");
        const updateMobile = (e) => {
          setState("is_mobile", e.matches);
          if (!e.matches) setState("menu_is_open", false);
        };

        // Initial check
        updateMobile(mq);

        // Add listener
        mq.addEventListener("change", updateMobile);
      };

      // Setup media query on component creation
      setupMediaQuery();

      const li_Style = () => {
        if (getState("is_mobile", false)) {
          return {
            display: "block",
            margin: "0 0.5rem",
            cursor: "pointer",
            padding: "0.5rem",
            borderRadius: "4px",
            transition: "background-color 0.2s ease"
          };
        } else {
          return {
            display: "inline-block",
            margin: "0 0.5rem",
            cursor: "pointer",
            padding: "0.25rem 0.5rem",
            borderRadius: "4px",
            transition: "background-color 0.2s ease"
          };
        }
      };

      return {
        header: {
          style: {
            display: "flex",
            justifyContent: "space-between",
            alignItems: "center",
            flexWrap: "wrap",
            background: "#2F2F2F",
            color: "white",
            padding: "1rem",
            position: "fixed",
            inset: "0 0 auto",
            fontFamily: "sans-serif",
            boxShadow: "0 2px 4px rgba(0,0,0,0.1)",
            zIndex: "1000"
          },
          children: [
            {
              section: {
                className: "left",
                style: {
                  display: "flex",
                  alignItems: "center",
                  flex: 1,
                  fontSize: "1.2rem",
                  fontWeight: "bold"
                },
                text: "BRAND",
              },
            },
            {
              section: {
                className: "center",
                style: {
                  display: "flex",
                  flexDirection: "column",
                  alignItems: "center",
                },
                children: [
                  {
                    div: {
                      className: "hamb",
                      text: "",
                      role: "button",
                      tabIndex: "0",
                      "aria-label": "Toggle menu",
                      "aria-expanded": () => getState("menu_is_open", false),
                      onClick: () => setState("menu_is_open", !getState("menu_is_open", false)),
                      onKeyDown: (e) => {
                        if (e.key === 'Enter' || e.key === ' ') {
                          e.preventDefault();
                          setState("menu_is_open", !getState("menu_is_open", false));
                        }
                      },
                      style: {
                        fontWeight: "bold",
                        transform: "scale(1.5)",
                        cursor: "pointer",
                        display: () => (getState("is_mobile", false) ? "block" : "none"),
                        padding: "0.5rem",
                        borderRadius: "4px",
                      },
                    },
                  },
                  {
                    nav: {
                      role: "navigation",
                      "aria-label": "Main navigation",
                      style: {
                        width: "320px",
                        textAlign: "center",
                        display: () => {
                          const isMobile = getState("is_mobile", false);
                          const isOpen = getState("menu_is_open", false);
                          return isMobile ? (isOpen ? "block" : "none") : "block";
                        },
                        position: () => (getState("is_mobile", false) ? "absolute" : "static"),
                        top: () => (getState("is_mobile", false) ? "55px" : "auto"),
                        background: "#2F2F2F",
                        borderRadius: () => (getState("is_mobile", false) ? "0 0 8px 8px" : "0"),
                        boxShadow: () => (getState("is_mobile", false) ? "0 4px 6px rgba(0,0,0,0.1)" : "none"),
                      },
                      children: {
                        ul: {
                          onClick: (e) => {
                            setState("menu_is_open", false);
                            setState("menu-item", e.target.textContent);
                          },
                          style: {
                            listStyle: "none",
                            padding: 0,
                            margin: 0,
                          },
                          children: [
                            { li: { text: "HOME", style: li_Style } },
                            { li: { text: "BLOG", style: li_Style } },
                            { li: { text: "ABOUT", style: li_Style } },
                          ],
                        },
                      },
                    },
                  },
                ],
              },
            },
            {
              section: {
                className: "right",
                style: {
                  display: "flex",
                  alignItems: "center",
                  flex: 1,
                  justifyContent: "flex-end",
                  fontSize: "0.9rem"
                },
                text: "HELP",
              },
            },
          ],
        },
      };
    };

    const PageComponent = (props, context) => {
      const { getState } = context;

      return {
        div: {
          style: {
            color: "#333",
            fontFamily: "sans-serif",
            paddingTop: "60px",
            textAlign: "center",
            minHeight: "calc(100vh - 60px)",
            background: "linear-gradient(135deg, #ff9a56 0%, #ffad56 100%)",
          },
          children: [
            {
              h1: {
                text: () => {
                  const menuItem = getState("menu-item", "HOME");
                  return `Current Page: ${menuItem}`;
                },
                style: {
                  fontSize: "1.5rem",
                  color: "#2F2F2F",
                  margin: "1rem 0"
                }
              }
            },
            {
              p: {
                text: "This is a responsive header demo. Try resizing your browser window to see the mobile menu in action. This is a responsive header demo. Try resizing your browser window to see the mobile menu in action. This is a responsive header demo. Try resizing your browser window to see the mobile menu in action. This is a responsive header demo. Try resizing your browser window to see the mobile menu in action. This is a responsive header demo. Try resizing your browser window to see the mobile menu in action. This is a responsive header demo. Try resizing your browser window to see the mobile menu in action. This is a responsive header demo. Try resizing your browser window to see the mobile menu in action. This is a responsive header demo. Try resizing your browser window to see the mobile menu in action. This is a responsive header demo. Try resizing your browser window to see the mobile menu in action. This is a responsive header demo. Try resizing your browser window to see the mobile menu in action. This is a responsive header demo. Try resizing your browser window to see the mobile menu in action. This is a responsive header demo. Try resizing your browser window to see the mobile menu in action. This is a responsive header demo. Try resizing your browser window to see the mobile menu in action. This is a responsive header demo. Try resizing your browser window to see the mobile menu in action. This is a responsive header demo. Try resizing your browser window to see the mobile menu in action. This is a responsive header demo. Try resizing your browser window to see the mobile menu in action. This is a responsive header demo. Try resizing your browser window to see the mobile menu in action. This is a responsive header demo. Try resizing your browser window to see the mobile menu in action. This is a responsive header demo. Try resizing your browser window to see the mobile menu in action. This is a responsive header demo. Try resizing your browser window to see the mobile menu in action!",
                style: {
                  maxWidth: "600px",
                  margin: "2rem auto",
                  lineHeight: "1.6",
                  fontSize: "1.1rem"
                }
              }
            }
          ]
        }
      }
    }

    const FooterComponent = () => ({
      div: {
        style: {
          color: "white",
          background: "linear-gradient(135deg, #2F2F2F 0%, #1a1a1a 100%)",
          fontFamily: "sans-serif",
          position: "fixed",
          inset: "auto 0 0",
          height: "4rem",
          display: "flex",
          alignItems: "center",
          justifyContent: "center",
          fontSize: "0.9rem",
          boxShadow: "0 -2px 4px rgba(0,0,0,0.1)"
        },
        text: "© 2024 BRAND - All Rights Reserved"
      }
    });

    const juris = new Juris({
      states: {
        "menu-item": "HOME",
        "is_mobile": false,
        "menu_is_open": false
      },
      components: {
        ResponsiveHeader,
        PageComponent,
        FooterComponent
      },
      layout: [
        { ResponsiveHeader: {} },
        { PageComponent: {} },
        { FooterComponent: {} }
      ],
    });

    juris.render();
  </script>

</body>

</html>

Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
jurisauthor profile image
jurisauthor

impressive and clever style-based theming here
{style: li_Style}

and it just worked!