Accessible CSS-only tab view - css

I'm working on a site that needs to (a) work without JavaScript and (b) be keyboard-accessible.
I have used the label target trick to build a tab view (https://css-tricks.com/functional-css-tabs-revisited/), but I've noticed that it relies on the label being clicked. I can't figure out how to make it work with the keyboard. Is this possible?
.tabs {
background-color: #eee;
min-height: 400px;
}
.tabs__list {
border-bottom: 1px solid black;
display: flex;
flex-direction: row;
list-style: none;
margin: 0;
padding: 0;
position: relative;
}
.tabs__tab {
padding: 0.5rem;
}
.tabs__content {
display: none;
left: 0;
padding: 0.5rem;
position: absolute;
top: 100%;
}
.tabs__input {
display: none;
}
.tabs__input+label {
cursor: pointer;
}
.tabs__input:focus,
.tabs__input:hover {
color: red;
}
.tabs__input:checked+label {
color: red;
}
.tabs__input:checked~.tabs__content {
display: block;
}
<div class="tabs">
<ul class="tabs__list">
<li class="tabs__tab">
<input class="tabs__input" type="radio" id="tab-0" name="tab-group" checked>
<label for="tab-0" class="tabs__label" tabindex="0" role="button">Tab 0</label>
<div class="tabs__content">
Tab 0 content
</div>
</li>
<li class="tabs__tab">
<input class="tabs__input" type="radio" id="tab-1" name="tab-group">
<label for="tab-1" class="tabs__label" tabindex="0" role="button">Tab 1</label>
<div class="tabs__content">
Tab 1 content
</div>
</li>
</ul>
</div>

Accepted answer is not an accessible solution.
I have made some corrections and some observations here. Do not use the accepted answer in production if you stumble across this question in the future. It is an awful experience with a keyboard.
The answer below fixes some of the CSS issues to make it more accessible.
However I would recommend you reconsider the no JavaScript requirement.
I can understand having a good fall-back (which the example I give below with the fixes is) but there is no way you can make a fully accessible set of CSS only tabs.
Firstly you should use WAI-ARIA to complement your HTML to make things even more clear for screen readers. See the tabs examples on W3C to see what WAI-ARIA roles you should be using. This is NOT possible without JavaScript as states need to change (aria-hidden for example should change).
Secondly, you should be able to use certain shortcut keys. Press the home key for example in order to return to the first tab, something you can only do with a little JS help.
With that being said here are a few things I fixed with the accepted answer to at least give you a good starting point as your 'no JavaScript fallback'.
Problem 1 - tabindex on the label.
By adding this you are creating a focusable element that cannot be activated via keyboard (you cannot press space or Enter on the label to change selection, unless you use JavaScript).
In order to fix this I simply removed the tabindex from the labels.
Problem 2 - no focus indicators when navigating via keyboard.
In the example the tabs only work when you are focused on the radio buttons (which are hidden). However at this point there is no focus indicator as the styling is applying styling to the checkbox when it is focused and not to its label.
In order to fix this I adjusted the CSS with the following
/*make it so when the checkbox is focused we add a focus indicator to the label.*/
.tabs__input:focus + label {
outline: 2px solid #333;
}
Problem 3 - using the same state for :hover and :focus states.
This is another bad practice that needs to go away, always have a different way of showing hover and focus states. Some screen reader and screen magnifier users will use their mouse to check they have the correct item focused and orientate themselves on a page. Without a separate hover state it is difficult to check you are hovered over a focused item.
/*use a different colour background on hover, you should not use the same styling for hover and focus states*/
.tabs__label:hover{
background-color: #ccc;
}
Example
In the example I have added a hyperlink at the top so you can see where your focus indicator is when using a keyboard.
When your focus indicator is on one of the two tabs you can press the arrow keys to change tab (which is expected behaviour) and the focus indicator will adjust accordingly to make it clear which tab was selected.
.tabs {
background-color: #eee;
min-height: 400px;
}
.tabs__list {
border-bottom: 1px solid black;
display: flex;
flex-direction: row;
list-style: none;
margin: 0;
padding: 0;
position: relative;
}
.tabs__tab {
padding: 0.5rem;
}
.tabs__content {
display: none;
left: 0;
padding: 0.5rem;
position: absolute;
top: 100%;
}
.tabs__input {
position: fixed;
top:-100px;
}
.tabs__input+label {
cursor: pointer;
}
.tabs__label:hover{
background-color: #ccc;
}
.tabs__input:focus + label {
outline: 2px solid #333;
}
.tabs__input:checked+label {
color: red;
}
.tabs__input:checked~.tabs__content {
display: block;
}
A link so you can see where your focus indicator is
<div class="tabs">
<ul class="tabs__list">
<li class="tabs__tab">
<input class="tabs__input" type="radio" id="tab-0" name="tab-group" checked>
<label for="tab-0" class="tabs__label" role="button">Tab 0</label>
<div class="tabs__content">
Tab 0 content
</div>
</li>
<li class="tabs__tab">
<input class="tabs__input" type="radio" id="tab-1" name="tab-group">
<label for="tab-1" class="tabs__label" role="button">Tab 1</label>
<div class="tabs__content">
Tab 1 content
</div>
</li>
</ul>
</div>

It is just radio buttons... Keyboard can be used to navigate through them using tab and space bar to check them.
I'd use :focus to highlight the chosen tab and the tabindex property to make it work as I wanted.
Please provide more dept if you have problem with a SPECIFIC problem related to it, and provide a basic code example here, no linking.
Since hidden inputs cannot be selected through keyboard, make them visible...
.tabs {
background-color: #eee;
min-height: 400px;
}
.tabs__list {
border-bottom: 1px solid black;
display: flex;
flex-direction: row;
list-style: none;
margin: 0;
padding: 0;
position: relative;
}
.tabs__tab {
padding: 0.5rem;
}
.tabs__content {
display: none;
left: 0;
padding: 0.5rem;
position: absolute;
top: 100%;
}
.tabs__input {
position: fixed;
top:-100px;
}
.tabs__input+label {
cursor: pointer;
}
.tabs__input:focus
.tabs__input:hover {
color: red;
}
.tabs__input:checked+label {
color: red;
}
.tabs__input:checked~.tabs__content {
display: block;
}
<div class="tabs">
<ul class="tabs__list">
<li class="tabs__tab">
<input class="tabs__input" type="radio" id="tab-0" name="tab-group" checked>
<label for="tab-0" class="tabs__label" tabindex="0" role="button">Tab 0</label>
<div class="tabs__content">
Tab 0 content
</div>
</li>
<li class="tabs__tab">
<input class="tabs__input" type="radio" id="tab-1" name="tab-group">
<label for="tab-1" class="tabs__label" tabindex="0" role="button">Tab 1</label>
<div class="tabs__content">
Tab 1 content
</div>
</li>
</ul>
</div>

Related

Hide a dropdown/popup menu when a menu item is clicked - without javascript (CSS only)

As posted in this question: Hide dropdown menu on click in CSS, I'm looking for a CSS-only way to hide a popup/dropdown menu when one of the links is clicked. An answer was given by Abhijeet Vadera that is almost a great answer - except links in the dropdown menu don't actually do anything/go anywhere. I copied and pasted the code into a test page I've been working on and modified the targets in the links. The dropdown does pop up when hovering over the button, but clicking any of the links does absolutely nothing other than hiding the dropdown.
Does anyone know why this is and (especially) how to make it work? So close....
P.S. Stackoverflow text below my answer on that question tells me that I should ask my own question rather than commenting on another answer or seeking clarification, so that's what I'm doing.
If you don't want to write JavaScript, the easiest way is to use hastags.
Let me explain.
If you press a button, it is an "a" tag whose "href" tag says, for example, #hide
And then the CSS part should look something like this:
I hope I could help.
.dropbtn {
background-color: #04AA6D;
color: white;
padding: 16px;
font-size: 16px;
border: none;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f1f1f1;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1;
}
.dropdown-content a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
}
.dropdown-content a:hover {background-color: #ddd;}
.dropdown:hover .dropdown-content {display: block;}
.dropdown:hover .dropbtn {background-color: #3e8e41;}
.dropbtn a {
text-decoration: none;
}
.dropdown-content {
visibility: visible;
}
#hide:target{
visibility: hidden;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h2>Hoverable Dropdown</h2>
<p>Move the mouse over the button to open the dropdown menu.</p>
<div class="dropdown">
<a class="dropbtn" href="#">Dropdown</a>
<div class="dropdown-content hide" id="hide">
Link 1
Link 2
Link 3
</div>
</div>
</body>
</html>
The best way to have js-like behaviour is to use checkboxes.
You hide an input checkbox in the same container (this is important) than the hidden menu. If the checkbox is checked, you display the menu, if the checkbox is not checked you hide it.
To trigger the checkbox you can put labels wherever you want on your code as long as it has the "for" attribute that matches with the id of your checkbox.
The trick here is to use the "~" selector which allows you to select a "brother" element. In my code you can see "#showMyMenu:checked ~ .popup" which means "select the popup class which is brother with a checked #showMyMenu".
On my example I putted two labels: one inside and one outside the menu, but you can of course use the same label to trigger the hide/display of your menu !
#showMyMenu {
display: none;
}
label {
display: inline-block;
}
.popup {
display: none;
position: absolute;
box-shadow: 0 0 5px rgba(0,0,0);
padding: 20px;
border-radius: 3px;
}
#showMyMenu:checked ~ .popup {
display: block;
}
<input type="checkbox" id="showMyMenu">
<label for="showMyMenu">Show menu</label>
<div class="popup">
<label for="showMyMenu">Hide menu</label>
<ul>
<li>
An item
</li>
<li>
An item
</li>
<li>
An item
</li>
</ul>
</div>

Defaulting Show/Hide to Hide with pure CSS using checkbox

I am looking to have the 'item value appears here' to be hidden by default on landing on the page or refreshing. I am using a checkbox with a label to be used as the show/hide click. This appears in 3 columns and currently clicking 'show' on the left column makes all 3 item values appear (this is not a problem and as I do not wish to use JavaScript, happy with this, although if clicking on one only unhides one value with css then I'm all ears!).
I am not able to use JavaScript and feel there must be a way to do this with CSS using checkboxes - any help will be greatly appreciated!
<div class="key-items-wrapper">
<div class="left-column">
<img src="/images/item.png">
<div class="center-column-text">
<p>item name</p>
<div class="content">
<p>item value appears here</p>
</div>
<input id="checkbox-privacymode" type="checkbox">
<label for="checkbox-privacymode"></label>
</div>
</div>
This is the code for the checkbox
#checkbox-privacymode {
display: none;
visibility:hidden;
}
#checkbox-privacymode + label {
display: block;
padding-right: 52px;
height: 35px;
background: transparent url(/images/eye-hidden.png) no-repeat scroll right center;
float: right;
font-size: 0.9em;
color: #777;
cursor: pointer;
}
#checkbox-privacymode + label::before {
content: 'hide';
}
#checkbox-privacymode:checked + label {
background-image: url(/images/eye.png);
display:block;
}
#checkbox-privacymode:checked + label::before {
content: 'show';
}
Many Thanks
Change <input id="checkbox-privacymode" type="checkbox"> to <input id="checkbox-privacymode" type="checkbox">
You will need to reposition your HTML elements so that the message is immediately after the checkbox.
Once the message is correctly positioned in the HTML code, in CSS you can select the message like this:
#checkbox-privacymode:checked + p {
display: none;
}

is it possible to activate a div when clicked without javascript?

Just wondering if a div can be called without using javascript.
such as
my_div:hover{ add new layout}
is there a version for click eg
my_div:click{add new layout}
Thanks
Yes, if you add tabindex="0" to your div, you make it clickable and can then use the :focus pseudo-class to apply styles.
<div class="clickable" tabindex="0"></div>
.clickable {
height: 100px;
background: blue;
}
.clickable:focus {
background: red;
}
Codepen example. Clicking the div should give it focus and apply the :focus CSS to it. Clicking away from it will unfocus (blur) it and reset the default styles.
Not directly, but you can fake it using checkboxes:
input[type=checkbox] {
display: none;
}
.content {
display: none;
padding: 20px;
background-color: #dadada;
}
input[type=checkbox]:checked+label+.content {
display: block;
}
<input type="checkbox" id="check">
<label for="check">Click me</label>
<div class="content">
<h3>Content</h3>
<p>lorem20</p>
</div>

css outer div for product box [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
Could anyone help me by describing how etc. I do to makes for a function as illustrated.
What I want is that when I mouse over a product box (have not fixed height),
I want to get a box with the buy button, etc. that looks like the picture.
Know that I do not put up the code or, but I do not know where to begin.
So if anyone has any tips or so, I'd be grateful!
Try
button {
display: none;
}
li:hover > button {
display: block;
}
<ul>
<li>Description 1<button>Buy</button></li>
<li>Description 2<button>Buy</button></li>
</ul>
The idea here is to use the > operator to tell CSS to change something in our target. The target being the Buy button inside the li tag.
http://jsfiddle.net/beautifulcoder/kj2XA/
1) First of all: make your items fixed size. This prevents later issues (in layout) and allows you to create effect you described. Like:
HTML (not complete):
<div class="item-wrapper">
<div class="item-content">
<!-- item images etc here -->
</div>
<div class="item-actions">
<button class="buy">Buy</button>
</div>
</div>
CSS:
.item-wrapper {
width: 200px;
overflow: visible;
float: left;
background: #999999;
margin: 5px;
position: relative;
border: 2px solid #fff; /* without this you have unwanted size effects on hover*/
}
.item-content {
width: 200px;
height: 300px;
}
.item-actions {
display: none;
position: absolute;
background: #888;
top:300px;
z-index: 10;
left: 0px;
width: 200px;
height: 100px;
}
2) create javascript with jquery for your items like:
$('.item-wrapper').hover(function () {
// Change css on hover .. this could be done also by changing class
$(this).css({'border':'2px solid #880088'});
$(this).find(".item-actions").slideDown("fast");
}, function(){
$(this).css({'border':'2px solid #fff'});
$(this).find(".item-actions").slideUp("fast");
});
Here is fiddle: http://jsfiddle.net/h23mY/
This is also nice effect: http://jsfiddle.net/ww53e/
lets say the item is enclosed by div tag, now use css hover on
<script>
//on document load item1-buy.hide(); dont forget to use jquery
</script>
<div id="item1">
//item goes here.
<input type="submit" id="item1-buy" value="Buy">
</div>
css:
#item1:hover {
//here you can style how ever you want. Add orange border and so on...
}
now on hover unhide the buy button using jquery #item1-buy.show();
Check the DEMO
I've made a simple markup to show you the idea:
<div class="item">
<img src="http://lorempixel.com/200/200/" alt="" />
<span>15$</span>
<div class="buy">BUY</div>
</div>
And the CSS:
.item {
float: left;
padding: 10px;
border: 1px solid #ccc;
margin: 10px 10px 0 0;
}
span{
display: block;
}
.buy {
padding: 5px;
background: green;
display: none;
}
.item:hover {
border: 1px solid yellow;
}
.item:hover .buy {
display: inline-block;
}
Update: still an issue with the last image in a row, but hope it helps: DEMO 2
<div class="item">
<img src="http://lorempixel.com/200/200/" alt="" />
<span>15$</span>
<div class="buy">
<span class="button">BUY</span>
</div>
</div>

Strange scrolling behavior in IE with checkboxes in a scrollable div

I have a "multiselect" control that looks like this (sorry for the long id names, they are kinda autogenerated because this whole thing is being generated by a custom tag):
<div class="default-skin-outer" id="myMapSelect_multiSelectOuterDiv">
<div class="default-control" id="myMapSelect_multiSelectControlDiv">
<span class="default-icon-check-text" id="myMapSelect_multiSelectControlCheckWrapperSpan">
<span class="default-icon default-icon-check" id="myMapSelect_multiSelectControlCheckIconSpan"></span><span class="default-icon default-icon-text" id="myMapSelect_multiSelectControlCheckTextSpan">Check All</span>
</span>
<span class="default-icon-uncheck-text" id="myMapSelect_multiSelectControlUncheckWrapperSpan">
<span class="default-icon default-icon-uncheck" id="myMapSelect_multiSelectControlUncheckIconSpan"></span><span class="default-icon default-icon-text" id="myMapSelect_multiSelectControlUncheckTextSpan">Uncheck All</span>
</span>
</div>
<div class="default-skin-inner" id="myMapSelect_multiSelectInnerDiv">
<ul class="default-multiselect">
<li class="default-multiselect">
<label class="default-label">
<input type="checkbox" value="0" class="default-checkbox" id="myMapSelect0" name="myMapSelect"> Zero
</label>
</li>
<li class="default-multiselect">
<label class="default-label">
<input type="checkbox" value="1" class="default-checkbox" id="myMapSelect1" name="myMapSelect"> One
</label>
</li>
<li class="default-multiselect">
<label class="default-label">
<input type="checkbox" value="2" class="default-checkbox" id="myMapSelect2" name="myMapSelect"> Two
</label>
</li>
<li class="default-multiselect">
<label class="default-label">
<input type="checkbox" value="3" class="default-checkbox" id="myMapSelect3" name="myMapSelect"> Three
</label>
</li>
<li class="default-multiselect">
<label class="default-label">
<input type="checkbox" value="4" class="default-checkbox" id="myMapSelect4" name="myMapSelect"> Four
</label>
</li>
<li class="default-multiselect">
<label class="default-label">
<input type="checkbox" value="5" class="default-checkbox" id="myMapSelect5" name="myMapSelect"> Five
</label>
</li>
<li class="default-multiselect">
<label class="default-label">
<input type="checkbox" value="6" class="default-checkbox" id="myMapSelect6" name="myMapSelect"> Six
</label>
</li>
<li class="default-multiselect">
<label class="default-label">
<input type="checkbox" value="7" class="default-checkbox" id="myMapSelect7" name="myMapSelect"> Seven
</label>
</li>
</ul>
</div>
</div>
The CSS for this whole thing is:
div.default-skin-outer {
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
width: 300px;
height: auto;
padding: 2px;
background-color: #f0f0f0;
border: 1px solid #999999;
}
div.default-skin-inner {
overflow: auto;
width: 300px;
height: 100px;
}
div.default-control {
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
width: auto;
border: 1px solid #555555;
background-color: #999999;
color: #f0f0f0;
vertical-align: middle;
padding: 2px;
margin-bottom: 2px;
font-weight: bold;
overflow: hidden;
}
ul.default-multiselect {
padding: 0px;
margin: 0px;
white-space: nowrap;
}
ul.default-with-padding {
padding: 0px;
padding-left: 20px;
margin: 0px;
white-space: nowrap;
}
li.default-multiselect {
list-style-type: none;
}
label.default-label {
display: block;
padding: 2px;
}
input.default-checkbox {
width: 13px;
height: 13px;
padding: 0;
margin: 0;
vertical-align: bottom;
position: relative;
top: -1px;
*overflow: hidden;
}
span.default-icon {
background-image: url("/resources/authoring/jqueryui/custom-theme/images/ui-icons_ffffff_256x240.png");
display: inline-block;
height: 16px;
width: 16px;
overflow: hidden;
}
span.default-icon-text {
width: auto;
background: none;
}
span.default-icon-text:hover {
text-decoration: underline;
cursor: pointer;
}
span.default-icon-check-text {
float: left;
}
span.default-icon-uncheck-text {
float: right;
}
span.default-icon-check {
background-position: -64px -144px;
}
span.default-icon-uncheck {
background-position: -96px -128px;
}
This works beautifully in Firefox. The checkboxes scroll without any problem in the scrollable div. But when I looked at this in IE8, it looks terrible.
Firstly, the extra checkboxes bleed outside the main div. Secondly (and this is the really strange thing) when I use the scroll bar, the text scrolls, but the checkboxes do not. They simply stay in place while the text scrolls. I tried googling for a solution but was unable to come up with anything.
Thanks!
UPDATE
So I found out that if I remove the funky part in the checkbox styling:
vertical-align:bottom;
position:relative;
top: -1px;
*overflow: hidden;
It works fine. But I put that in to make sure my labels and checkboxes are lined up properly.
Oh yes as far as the compatibility view is concerned, this is IE8 running under compatibility mode.
In response to the comments about inherited styles, here are styles that the checkbox inherits:
input {
border:1px solid #CFCFCF;
color:#000000;
font-family:Arial,Verdana,Sans-Serif;
font-size:12px;
padding-left:4px;
}
li.default-multiselect {
list-style-type:none;
}
ul.default-with-padding {
white-space:nowrap;
}
table {
empty-cells:show;
}
html, body {
line-height:16px;
}
I don't see anything that could potentially interfere...
There seems to be some strange interaction between inherited styles and the styles I have defined. That much is clear from Jacob's and Ray's comments since they were able to slap this code onto a page and have it render fine in IE without any issues.
I was able to make it behave properly by removing position:relative from the input.default-checkbox style.
I'm assuming that some sort of bizarre interaction is making the checkboxes think they are positioned statically or absolutely (or something) due to which they don't scroll. At least I think that's the reason; someone may be able to provide a better reason and shed light on this. At any rate, by removing the position:relative, I was able to make the strange scrolling-behavior stop. Thanks for helping me figure this out!
As #rossisdead suggested, adding position: relative to the scrolling element solves the problem. I also had to add position: relative to the parent of the scrolling element.
Make sure you don't have position: fixed set on your checkboxes in any other stylesheets.

Resources