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
<!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>
Top comments (1)
impressive and clever style-based theming here
{style: li_Style}
and it just worked!