I would like to design a navigation menu that responds to the width of the page by changing the horizontal menu into a single button that drops down a menu when clicked on.
All the examples I've seen seem to use Javascript or JQuery. Is there a CSS only way of doing this?
My thought was to create two different <ul></ul> elements. One for the horizontal and one for a vertical drop down menu. And use media query to hide one depending on screen size. Can this be done using a single <ul></ul> element instead?
Here's one way to achieve a Bootstrapish functionality using CSS. It is also possible to add some smooth animation using CSS and I'll leave it up to you to work that one out and to optimize the code wherever necessary. (Resize the preview window to see the effect).
Fiddle: http://jsfiddle.net/875v00ge/.
HTML:
<nav>
<span tabindex = "1"></span>
<span></span>
<div class = "menuWrapper">
<ul>
<li>About</li>
<li>Company</li>
<li>FAQ</li>
<li>Contact</li>
<li>Help</li>
</ul>
</div>
</nav>
CSS:
* {
margin: 0;
padding: 0;
}
ul {
list-style-type: none;
}
a {
color: rgb(50, 50, 50);
text-decoration: none;
}
nav {
background-color: rgb(240, 240, 240);
border: 1px solid rgb(200, 200, 200);
border-radius: 5px;
display: table;
height: calc(14px * 3);
width: 95%;
margin: 25px auto;
position: relative;
}
nav span {
width: calc(14px * 1.5);
height: 14px;
display: none;
position: absolute;
right: 14px;
top: 50%;
transform: translateY(-50%);
outline: 0;
cursor: pointer;
}
nav span:first-of-type {
border: solid rgb(100, 100, 100);
border-width: 2px 0;
padding: 4px 0;
height: 2px;
background-color: rgb(100, 100, 100);
background-clip: content-box;
z-index: 2;
}
nav span:nth-of-type(2) {
z-index: 1;
background-color: transparent;
}
nav li {
float: left;
font: normal 14px/3 Sans-Serif;
}
nav li:first-of-type {
margin-left: 10px;
}
nav li a {
display: inline-block;
padding: 0 10px;
}
nav li a:hover {
background-color: rgb(220, 220, 220);
}
#media screen and (max-width: 360px) {
nav .menuWrapper {
position: absolute;
top: 100%;
width: 100%;
left: -1px;
overflow: hidden;
border: 0px solid rgb(200, 200, 200);
}
nav ul {
transform: translateY(-100%);
background-color: rgb(235, 235, 235);
}
nav {
border-radius: 0;
}
nav li {
float: none;
}
nav li:first-of-type {
margin-left: 0;
}
nav li a {
display: block;
}
nav span {
display: block;
}
nav span:first-of-type:focus {
border-color: green;
background-color: green;
z-index: 0;
}
nav span:first-of-type:focus ~ .menuWrapper {
border-width: 1px;
}
nav span:first-of-type:focus ~ .menuWrapper > ul,
nav span:first-of-type:not(:focus) ~ .menuWrapper > ul:hover {
transform: translateY(0%);
}
}
Related
There is a question very similar to this one which already been solved, yet that solution is not helping me.
The issue I'm facing is the dropdown is not following its parent width ( position: absolute is in play here) and I think position: absolute is the only way to stop the whole nav bar to expand when the dropdown is accessed.
so is there any way I can make dropdown follow its parent width other than using media queries and defining a width for every device?
And forgive me if I'm not able to explain myself clearly. Beginner here.
Thanks
CSS
* {
box-sizing: border-box;
}
body {
margin: 0;
background: #333;
}
ul {
display: flex;
background: #9999ff;
list-style: none;
padding: 6px;
overflow: hidden;
justify-content: space-around;
line-height: 2;
}
li {
color: white;
text-decoration: none;
text-align: center !important;
flex-grow: 1;
}
.drop{
position: absolute;
visibility: hidden;
flex-direction: column;
color: white;
background-color: #9999ff;
line-height: 3;
/*width: 17em;*/
}
.drop li {
border: 1px solid transparent;
text-align: center;
}
li:hover {
background: black;
color: plum;
}
li:hover .drop {
visibility: visible;
}
.drop li:hover {
border: 1px solid rgba(255, 51, 153,1);
}
HTML
<ul>
<li>Home</li>
<li>JS
<ul class="drop">
<li>Node</li>
<li>React</li>
</ul>
</li>
<li>CSS</li>
<li>About</li>
</ul>
* {
box-sizing: border-box;
}
body {
margin: 0;
background: #333;
}
ul {
display: flex;
background: #9999ff;
list-style: none;
padding: 6px;
overflow: hidden;
justify-content: space-around;
line-height: 2;
}
li {
color: white;
text-decoration: none;
text-align: center !important;
flex-grow: 1;
}
.drop{
position: absolute;
visibility: hidden;
flex-direction: column;
color: white;
background-color: #9999ff;
line-height: 3;
/*width: 17em;*/
}
.drop li {
border: 1px solid transparent;
text-align: center;
}
li:hover {
background: black;
color: plum;
}
li:hover .drop {
visibility: visible;
}
.drop li:hover {
border: 1px solid rgba(255, 51, 153,1);
}
<ul>
<li>Home</li>
<li>JS
<ul class="drop">
<li>Node</li>
<li>React</li>
</ul>
</li>
<li>CSS</li>
<li>About</li>
</ul>
Solution for fixed number of items, all with the same width:
* {
box-sizing: border-box;
}
body {
margin: 0;
background: #333;
}
ul {
display: flex;
background: #9999ff;
list-style: none;
padding: 6px;
overflow: hidden;
justify-content: space-around;
line-height: 2;
}
li {
color: white;
text-decoration: none;
text-align: center !important;
flex-grow: 1;
width: calc(100vw / 4 - 15px);
}
.drop{
position: absolute;
visibility: hidden;
flex-direction: column;
color: white;
background-color: #9999ff;
line-height: 3;
/*width: 17em;*/
}
.drop li {
border: 1px solid transparent;
text-align: center;
}
li:hover {
background: black;
color: plum;
}
li:hover .drop {
visibility: visible;
}
.drop li:hover {
border: 1px solid rgba(255, 51, 153,1);
}
<ul>
<li>Home</li>
<li>JS
<ul class="drop">
<li>Node</li>
<li>React</li>
</ul>
</li>
<li>CSS</li>
<li>About</li>
</ul>
Relevant snippet:
li {
color: white;
text-decoration: none;
text-align: center !important;
flex-grow: 1;
width: calc(100vw / 4 - 15px); // full viewport width divided by number of elements, minus paddings
}
Explanation: Viewport width takes into account the entire viewport width. Divide this by the number of items in our header, and we get our item width.
Now this does not take into account paddings, margins and similar, so you have to supply that yourself - but calc() is pretty flexible for this, as long as you know about the actual value of these factors.
I came up with a workaround to fix your problem. It might not be the prettiest but it works!
* {
box-sizing: border-box;
}
body {
margin: 0;
background: #333;
}
ul {
display: flex;
background: linear-gradient(180deg, #9999ff 44px, transparent 0px);
list-style: none;
padding: 6px;
overflow: hidden;
justify-content: space-around;
line-height: 2;
}
li {
color: white;
text-decoration: none;
text-align: center !important;
flex-grow: 1;
}
.drop{
display: none;
flex-direction: column;
color: white;
background-color: #9999ff;
line-height: 3;
/*width: 17em;*/
}
.drop li {
border: 1px solid transparent;
text-align: center;
}
li:hover {
background: black;
color: plum;
}
li:hover .drop {
display: block;
background: #9999ff;
}
.drop li:hover {
border: 1px solid rgba(255, 51, 153,1);
}
<ul>
<li>Home</li>
<li>JS
<ul class="drop">
<li>Node</li>
<li>React</li>
</ul>
</li>
<li>CSS</li>
<li>About</li>
</ul>
Problem:
We need our .drop element to NOT have position:absolute (because we need to access its parent's width), and we also cannot specify a maximum height for the header itself, as the overflowing dropdown would be hidden from view.
Solution:
We set the background of the header to a linear-gradient which has a specified height of 44px for purple color, and 0px for transparent.
This means that even if the header changes height, the background gradient color will always have the same height.
As a result, the header itself WILL change size, but it is not visually detectable.
Is it a pretty solution? No.
Is it good practice? Probably not.
Does it work? Well, it does!
I suggest using javascript for similar problems in the future though :)
When you hover over, say, 'News', a blue underline will pop-in. From what I've seen, it is actually a background with a height transition from 0px -> 5px. However, my code is unable to replicate it.
* {
padding: 0;
margin: 0;
}
.navbar {
padding: 28px 36px;
background: black;
}
.navbar ul {
list-style: none;
}
.navbar li {
padding: 0 8px;
height: 100%;
position: relative;
transform: translateZ(0);
}
.navbar li::before {
display: block;
position: absolute;
bottom: 0;
left: 0;
z-index: -1;
background: blue;
transition: height .2s ease-in-out;
}
a {
display: block;
text-decoration: none;
color: white;
}
<div class="navbar">
<ul>
<li>Home
</ul>
</div>
I'm definitely missing something obvious here, but I don't get it. Help?
If I got you right, you did the most of the work, but you should always keep in mind when you using pseudo-elements like ::before you have to declare content property in it, otherwise it won't work at all. Then to make this work, you should just care about making the transition by putting initial with and height on ::before then make the height element to the actual one in hover.
So your final code should be something like this:
* {
padding: 0;
margin: 0;
}
.navbar {
padding: 28px 36px;
background: black;
}
.navbar ul {
list-style: none;
}
.navbar li {
padding: 0 8px;
height: 100%;
position: relative;
transform: translateZ(0);
}
.navbar li::before {
content: '';
display: block;
position: absolute;
bottom: 0;
left: 0;
z-index: -1;
width: 50px;
height: 0;
background: blue;
transition: height .2s ease-in-out;
}
.navbar li:hover::before {
height: 5px;
}
a {
display: block;
text-decoration: none;
color: white;
}
<div class="navbar">
<ul>
<li>Home
</ul>
</div>
now I am finally got my navbar centered I have gotten a submenu in my navbar
now and I am trying to get it to work but all my links are just ruined :-/
so hope someone could explain to me why it doesnt want to work on the way I am doing it?
I want to keep the style of my navbar, and I am currently playing around with the css to get
my submenu working but it keeps ruining it :-/.
also when adding display:inline; to my .topnav ul it is not centered anymore grr ,
I have watched a video but even that would not work with my navbar.
I know I am doing something wrong but what ??
(can be a litle messy just cause i seperated the codes for testing sorry for that!)
/*Topnav*/
.topnav {
width: 100%;
opacity: 1;
background-color: rgba(255,255,255,0.6);
margin-bottom: 10px;
padding: 5px 0px 5px 0;
border-bottom: dotted #66A761;
border-top: dotted #66A761;
position:relative;
}
.topnav ul {
text-align: center;
}
.topnav ul li {
display: inline;
margin-left: 15px;
}
.topnav a {
font-size: 20px;
font-weight: bold;
text-decoration: none;
}
.topnav a:visited {
color: #FFF;
}
.topnav a:link {
color :#9F257D;
}
.topnav a:hover {
color: #66A761;
}
.topnav input {
float: right;
padding: 3px;
margin: 0 5px;
position: absolute;
right: 20px;
}
/*submenu testing*/
.topnav ul li ul li {
display: none;
}
.topnav ul li:hover ul li {
display: block;
}
<!--Topnav-->
<div class="topnav">
<nav>
<ul>
<li>recepten
<ul>
<li>lactosevrij</li>
<li>suikervrij</li>
<li>glutenvrij</li>
</ul>
</li>
<li>varianten
<ul>
<li>basis</li>
<li>standaard</li>
<li>luxe</li>
</ul>
</li>
<li>contact</li>
<li>over ons</li>
<input type="text" style="right:0" placeholder="Search..">
</ul>
</nav>
</div>
.topnav li {
position:relative;
}
.topnav li ul {
position: absolute;
top:25px;
left:0px;
background-color: #fff;
}
If the intention is that these sub menus work as dropdown you might want to set their positioning to absolute, but note that you have to make the wrapper element relative or else it might take the viewport as anchor.
It's not entirely clear what you mean but it sounds like you want a horizontal nav with dropdown sub-menu items.
E.G:
/*Topnav*/
.topnav {
width: 100%;
opacity: 1;
background-color: rgba(255, 255, 255, 0.6);
margin-bottom: 10px;
padding: 5px 0px 5px 0;
border-bottom: dotted #66A761;
border-top: dotted #66A761;
position: relative;
}
.topnav ul {
text-align: center;
display: flex;
justify-content:space-between;
}
.topnav ul li {
display: inline-block;
margin: 0 7px;
padding: 0;
text-indent: 0;
position: relative;
}
.topnav ul li ul {
display: block;
position: absolute;
top: 100%;
left: -100px;
right: -100px;
margin: 0;
padding: 0;
}
.topnav ul li ul li {
display: block;
margin: 0;
padding: 0;
text-indent: 0;
}
.topnav a {
font-size: 20px;
font-weight: bold;
text-decoration: none;
}
.topnav a:visited {
color: #FFF;
}
.topnav a:link {
color: #9F257D;
}
.topnav a:hover {
color: #66A761;
}
.topnav input {
padding: 3px;
margin: 0 5px;
}
/*submenu testing*/
.topnav ul li ul li {
display: none;
}
.topnav ul li:hover ul li {
display: block;
}
<!--Topnav-->
<div class="topnav">
<nav>
<ul>
<li>recepten
<ul>
<li>lactosevrij</li>
<li>suikervrij</li>
<li>glutenvrij</li>
</ul>
</li>
<li>varianten
<ul>
<li>basis</li>
<li>standaard</li>
<li>luxe</li>
</ul>
</li>
<li>contact</li>
<li>over ons</li>
<input type="text" placeholder="Search..">
</ul>
</nav>
</div>
i changed the whole thing again and got it to react only got 2 problems with it
1 i can only get on the first link in the submenu, as soon as i slide down with my cursor it just closes before i hit my other link/links. (while in the snippet it works)
2 cant center my links in the middle under my logo on the page as it is , the spaces between it is perfect.
/*Topnav*/
.topnav {
width: 100%;
opacity: 1;
background-color: rgba(255, 255, 255, 0.6);
margin-bottom: 10px;
padding: 5px 0px 5px 0;
border-bottom: dotted #66A761;
border-top: dotted #66A761;
position: relative;
}
.topnav ul {
display: inline;
text-align: left;
}
.topnav ul li {
display: inline-block;
margin: 0 7px;
padding: 0;
text-indent: 0;
position: relative;
cursor: pointer;
-webkit-transition: all 0.2s;
-moz-transition: all 0.2s;
-ms-transition: all 0.2s;
-o-transition: all 0.2s;
transition: all 0.2s;
}
.topnav a {
font-size: 20px;
font-weight: bold;
text-decoration: none;
}
.topnav a:visited {
color: #FF00E0;
}
.topnav a:link {
color: #9F257D;
}
.topnav a:hover {
color: #66A761;
}
.topnav input {
float: right;
padding: 3px;
margin: 0 5px;
position: absolute;
right: 20px;
}
/*submenu testing*/
.topnav ul li ul {
display: block;
position: absolute;
background-color: rgba(255, 255, 255, 0.6);
padding: 5px 5px;
}
.topnav ul li:hover ul li {
display: block;
}
.topnav ul li ul li {
display: none;
}
<div class="topnav">
<nav>
<ul>
<li>recepten
<ul>
<li>lactosevrij</li>
<li>suikervrij</li>
<li>glutenvrij</li>
</ul>
</li>
<li>varianten
<ul>
<li>basis</li>
<li>standaard</li>
<li>luxe</li>
</ul>
</li>
<li>contact</li>
<li>over ons</li>
<input type="text" style="right:0" placeholder="Search..">
</ul>
</nav>
</div>
I am trying to add a border-top with 2px on my fixed navbar however on the a href hover it pushes all of the nav items down by 2px, I am trying to make it so it's static, any help is appreciated.
.fixed-nav-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 9999;
width: 100%;
height: 70px;
background-color: #3f3f3f;
}
.fixed-nav-bar li, .fixed-nav-bar a {
height: 0 auto;
line-height: 50px;
width: 100px;
overflow: hidden;
}
.menu {
width: 90%;
max-width: 960px;
margin: 0 auto;
text-align: center;
}
.menu a, .menu a:visited {
color: #ffffff;
overflow: hidden;
}
.menu a:hover, .menu a:target {
display: block;
border-top: 2px solid #72BCD4;
color: #72BCD4;
}
.menu-items { display: inline-block; }
.menu-items li {
display: inline-block;
margin-right: 10px;
margin-left: 10px;
}
.menu-items a {
text-decoration: none;
}
.show, .hide {
display: none;
padding-left: 15px;
background-color: transparent;
background-repeat: no-repeat;
background-position: center left;
color: #dde1e2;
}
.show {
background-image: url(../assets/down-arrow-icon.png);
}
.hide {
background-image: url(../assets/up-arrow-icon.png);
}
<nav class="fixed-nav-bar">
<div id="menu" class="menu">
<!-- Example responsive navigation menu -->
<a class="show" href="#menu">Menu</a><a class="hide" href="#hidemenu">Menu</a>
<ul class="menu-items">
<li>HOME</li>
<li>ABOUT</li>
<li>DESIGNS</li>
<li>CONTACT</li>
</ul>
</div>
</nav>
It's because of no border. Either give a transparent border with the same width or use the colour of the border. But in your case, it's screwed up. So I changed the overflow: hidden and used margin-top: -2px:
.fixed-nav-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 9999;
width: 100%;
height: 70px;
background-color: #3f3f3f;
}
.fixed-nav-bar li, .fixed-nav-bar a {
height: 0 auto;
line-height: 50px;
width: 100px;
}
.menu {
width: 90%;
max-width: 960px;
margin: 0 auto;
text-align: center;
}
.menu a, .menu a:visited {
color: #ffffff;
}
.menu a:hover, .menu a:target {
display: block;
border-top: 2px solid #72BCD4;
color: #72BCD4;
margin-top: -2px;
}
.menu-items {
display: inline-block;
}
.menu-items li {
display: inline-block;
margin-right: 10px;
margin-left: 10px;
}
.menu-items a {
text-decoration: none;
}
.show, .hide {
display: none;
padding-left: 15px;
background-color: transparent;
background-repeat: no-repeat;
background-position: center left;
color: #dde1e2;
}
.show {
background-image: url(../assets/down-arrow-icon.png);
}
.hide {
background-image: url(../assets/up-arrow-icon.png);
}
<nav class="fixed-nav-bar">
<div id="menu" class="menu">
<!-- Example responsive navigation menu -->
<a class="show" href="#menu">Menu</a><a class="hide" href="#hidemenu">Menu</a>
<ul class="menu-items">
<li>HOME</li>
<li>ABOUT</li>
<li>DESIGNS</li>
<li>CONTACT</li>
</ul>
</div>
</nav>
That's all because of box-sizing property. by default it adds value of paddings/margins/borders outside of content area, so element becomes bigger. Just define globally * {box-sizing: border-box;} it will make elements to fixed sizes. That means if element had no border, and then it adds, the element's area for content will be smaller, but the whole size will be the same. However, I strongly recommend you to use border at the beginning, otherwise, it will jump inside the element. You can set the color 'transparent' and it won't be visible. Then just by changing border-color it will be nicer.
I have created a little fiddler:
https://jsfiddle.net/marco_rensch/0hva2241/
this could be a possible solution
checkout CSS Code Changes between row 31 and 51
the key is to add a default border 2px transparent to the li's and change it by hover:
.menu-items li:hover{border-top: 2px solid #72BCD4;}
I'm trying to set up a website menu catalog for my restaurant and I am stuck on how to write text in the second column. See picture below. It is a drop down menu with two columns.
Also when I am using three columns it looks like this.
How do I fix it so the text are in the first column and they have the same formatting as the first picture? Here is my code,
body {
background: #db2811;
margin: 0 auto;
padding: 2em 2em 4em;
max-width: 65%;
box-shadow: 0 0 2px rgba(0, 0)
}
h1 {
text-align: center;
color: #fff200;
font-size: 50px;
}
.bold {
font-weight: 600;
}
.nav ul {
*zoom: 1;
list-style: none;
margin-top: 20%;
margin-left: -3%;
padding: 0;
}
.nav ul:before,
.nav ul:after {
content: "";
display: table;
}
.nav ul:after {
clear: both;
}
.nav ul > li {
float: left;
position: relative;
}
.nav a {
display: block;
padding: 10px 20px;
line-height: 1em;
color: #fff;
border-left: 1px solid #595959;
font-size: 16px
}
.nav a:hover {
text-decoration: none;
background: #595959;
}
.nav li ul {
background: #273754;
}
.nav li ul li {
-webkit-column-count: 3;
/* Chrome, Safari, Opera */
-moz-column-count: 3;
/* Firefox */
column-count: 3;
-webkit-column-rule: 1px solid lightblue;
/* Chrome, Safari, Opera */
-moz-column-rule: 1px solid lightblue;
/* Firefox */
column-rule: 1px solid lightblue;
width: 500px;
}
.nav li ul a {
border: none;
}
.nav li ul a:hover {
background: rgba(0, 0, 0, 0.2);
}
.nav li ul {
position: absolute;
left: 0;
top: 36px;
z-index: 1;
max-height: 0;
overflow: hidden;
-webkit-transform: perspective(400) rotate3d(1, 0, 0, -90deg);
-webkit-transform-origin: 50% 0;
-webkit-transition: 350ms;
-moz-transition: 350ms;
-o-transition: 350ms;
transition: 350ms;
}
.nav ul > li:hover ul {
max-height: 1000px;
-webkit-transform: perspective(400) rotate3d(0, 0, 0, 0);
}
<h1>Menu</h1>
<nav class="nav">
<ul>
<li>
Drinks
<ul>
<li><a>Pepsi</a></li>
<li><a>Diet Pepsi</a></li>
<li><a>Mountain Dew</a></li>
<li><a>Lemonade</a></li>
<li><a>Sierra Mist</a></li>
<li><a>Dr. Pepper</a></li>
<li>
<a></a>
</li>
</ul>
</li>
</ul>
</nav>
try to use inline-block display for you li elements and make their width to 50%. then the ul widthto 100%