I'm playing around with some Expand Boxes.
I'm trying to create an HTML + CSS Expand Box without using JavaScript.
For now my code looks something like this:
/* no js */
.expand_box{}
.expand_box > .expand_box_checkbox{
display: none;
}
.expand_box > .expand_box_headline{}
.expand_box > .expand_box_content{
display: none;
}
.expand_box > .expand_box_checkbox:checked + .expand_box_headline + .expand_box_content{
display: block;
}
<div class="expand_box">
<input id="eb1" class="expand_box_checkbox" type="checkbox" />
<label for="eb1" class="expand_box_headline">click me</label>
<div class="expand_box_content">
Content to see after clicking on "click me"
</div>
</div>
Now, this is actually a nice solution, as it doesn't depend on JS and runs on practically every device.
What I am wondering about:
I am using the checkbox as a storage for state-information.
But this piece of information technically doesn't belong into the HTML, as I only use it for styling.
Is there any way to keep track of the state "user activated the 'click me' button", using pure CSS?
I know of the existence of CSS-variables. Maybe I can use them in some way?
HTML5 has a native control for expand box. It's details/summary elements. In supporting browsers it doesn't need JS and needs CSS only for custom styling. It may need a bit of JS only to work in non-supporting browsers (as a polyfill).
Besides hidden checkbox hack, CSS itself has another possibility to "emulate" states with the very long transition delay (like described here, warning: the example uses unitless 0 value of transition-duration which seems not working anymore, but it works with 0s instead). But these solutions are also too hacky. CSS was never supposed to maintain states, but it's excellent in presenting them.
Related
I'm working on a dynamic form builder, where the user can add different blocks of content to a page (buttons, text inputs, paragraphs of text, images etc), which they can later publish and it will be a simple HTML form they can use.
For the 'preview' section of this application (where they are adding different blocks of content and modifying the content and look of it), it occurs to me that the simplest way to do this, is to just display the end product HTML itself and that way I'm more or less guaranteed that what is displayed in the preview matches what is displayed in the actual form.
Where this is important is with obscure styling quirks, such as the behaviour of buttons with display:block.
However, I don't want the the preview to be actually interactive like the real form would. That is, I don't want the following behaviours:
Inputs receiving focus and being able to insert content
Buttons having click animations
Clicking links navigating
Default hover styles occuring.
Also, in terms of accessibility, it might be very confusing if there are a bunch of input that don't do anything appearing on the screen.
Is there a browser native/standard way to do this?
My fallback will likely be to disable all of the elements. But this won't work for links for example.
An alternative might be to put an invisible div on top of the form, which captures all of the click events. But I don't like this from an accessibility POV.
Is using the role="presentation" intended for this?
Code Example
As an example here, see some sample code here: https://codepen.io/dwjohnston/pen/qBrGPmr?editors=1111
Now for this, I have the divs with role="button" - These buttons are intended to be clickable, - when you click them the side panel will show content configuration eg, the default value of the input, label, etc.
Already, there is a problem - interactive content is not permitted inside a button.
But also, I shouldn't be able to tab to those controls, (now I can manually add tabindex = "-1", but that's not the complete picture). I don't want to confuse screen readers etc.
INFO
There is an edit to this question that addresses the specific issues OP has with their current implementation. I have left this first part here as my original answer.
I don't want the the preview to be actually interactive like the real form would.
Why? Having the form be interactive would be great for all users (taking into account that some functionality needs to not work fully in a preview).
Let people see what default hover styles look like, what clicking links would do (i.e. click the link and alert says "would navigate to xxx page") etc.
I understand not wanting to be able to navigate to new pages and submit the form (any "save" or "update" actions plus any "navigation" actions) but everything else I believe should be able to work in a preview.
Also, in terms of accessibility, it might be very confusing if there are a bunch of input that don't do anything appearing on the screen.
It would be far more confusing if a preview appeared on screen but then I couldn't navigate to the buttons, links etc.
How would that behave for a screen reader user? i.e. would you hide all elements using aria-hidden="true" (in which case how would they test the page?).
My fallback will likely be to disable all of the elements. But this won't work for links for example.
This is really one of the worst ways you could handle it, disabled inputs generally can't receive focus so keyboard users would suffer when testing.
Plus if some elements in the final production form are disabled until a certain field is filled in (for example) then how could a user test that in your end form?
Also if you intercept the navigation for a link without explaining why the link doesn't work that could also cause confusion. (and yet again could that link open in a new window in "preview" mode if you explain that to end users? Obviously depends if it is a static link etc. but just a thought on a way users can check any custom links point to the right place).
Proposed Solution
I would say you need two versions of the finished form. The "real world" one and the "preview" one.
The HTML can be (read "should be") identical but you need to implement JavaScript handlers to disable the functionality you don't want to be active.
There is nothing wrong with having an editing tool only work with JavaScript if that is one of your concerns, as long as you provide a warning to end users that the tool requires JavaScript to work. (The difference between a publishing tool and an end user experience is you can be much more reliant on JavaScript.)
For example: Let's say you have a <button> that when clicked should add a new row to the form (in the production version of the form) and for whatever reason you do not want this functionality to be active in the preview version.
In that case in the preview / demo version of the form when you click this button an alert could be shown that says "will add a new row to the form".
That way screen reader users and keyboard only users can test the form without you needing to do too much extra work (and can also see if the form has usability issues for keyboard users while they are at it!)
I think this would be the most clear for everybody and a much better user experience.
You just need to think of navigating in and out of preview mode (Esc to close for example) and quick edits (when you preview the form give the option to focus the previously focused element if the forms are particularly long, keyboard only users will thank you for this! The same for when you go back to the edit, put focus back on the last edited item).
Further reading
From an accessibility perspective WCAG isn't what you are looking for in this scenario, Authoring Tool Accessibility Guidelines (ATAG) is.
This is a little known set of guidance on how to create editors, WYSIWYGS etc. etc.
As with all W3C guidance it is a heavy read but if you understand the core principles it will guide you to good decisions and you can always ask here if you need clarity on some of the individual points that aren't clear :-).
EDIT: How could you achieve an accessible interface with the current design
The issues that you have within the codepen you linked and their solutions are as follows:
Stop interactive items receiving focus
As you wrap each element you have correctly diagnosed your biggest issue which is nested interactive elements.
The solution to this is quite straight forward: Give every interactive element a tabindex="-1" as you said. This makes sure they cannot be focused incorrectly.
Make sure the actual wrapper item can be focused and activated
Then we just need to make sure that the wrapper .content-item has tabindex="0" so it is added to the focus order.
We also need to capture the Enter and the Space keys and ensure our panel is activated with them also.
Visual focus indicator
I also added an outline to the wrapper "button" so that visual focus indicators are available.
So that essentially fixes normal keyboard navigation issues, the next issue is how a screen reader will interpret nested buttons, inputs etc.
Screen reader corrections
Here I would also go simple, hide the button, input etc. entirely from a screen reader and instead explain what element is inside.
Effectively we make the button invisible and instead describe the contents of the .content-item.
To hide the element we use aria-hidden="true"
To announce something meaningful we can use some visually-hidden (screen reader only) text inside a <span>. This will be far more robust that using aria-label with such a complex application.
make the application behave well with a mouse
Final challenge - stop mouse events.
Luckily pointer-events: none has really good support and even on the few browsers that don't support it being able to click into an input isn't the end of the world (we have fixed it for keyboard users)
Example with all issues resolved:
This solves all of the issues with your selection method.
Please note it doesn't take into account accessibility errors of the components themselves (for example the <input> having no <label> etc.) but I assume that is just because it is an example.
The only thing you need to work out is how to generate the visually hidden text describing the item and the .content-info "button" action.
document.addEventListener("DOMContentLoaded", function () {
const items = document.getElementsByClassName("content-item");
const rightPanel = document.getElementById('right-panel');
for (let i = 0; i< items.length; i++) {
const item = items[i];
item.addEventListener("click", (e) => {
console.log(e.target);
const id = e.target.dataset.id;
rightPanel.innerHTML = id;
});
// ADDED this to capture the Enter and Space presses on our wrapper button
item.addEventListener("keydown", function(e){
if(e.keyCode == "32" || e.keyCode == "13"){
console.log(e.target);
const id = e.target.dataset.id;
rightPanel.innerHTML = id;
}
});
}
});
.main {
display: flex;
flex-flow: row nowrap;
}
.left {
flex: 1 1 auto;
}
.right {
flex: 1 1 auto;
}
.content-item {
margin-top: 20px;
width: 600;
border: solid 2px blue;
cursor: pointer;
}
/* ADDED a focus indicator so keyboard users know where they are */
.content-item:focus{
outline: 4px solid #333;
}
/* ADDED to stop pointer events reaching the input, button etc. */
input, button{
pointer-events: none;
}
/* ADDED the visually hidden class so we can provide meaningful text to screen reader users */
.visually-hidden {
border: 0;
padding: 0;
margin: 0;
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px 1px 1px 1px); /* IE6, IE7 - a 0 height clip, off to the bottom right of the visible 1px box */
clip: rect(1px, 1px, 1px, 1px); /*maybe deprecated but we need to support legacy browsers */
clip-path: inset(50%); /*modern browsers, clip-path works inwards from each corner*/
white-space: nowrap; /* added line to stop words getting smushed together (as they go onto seperate lines and some screen readers do not understand line feeds as a space */
}
<div class ="main">
<div class ="left">
<!-- Added `tabindex="0"` so the wrapper is focusable by keyboard -->
<div class="content-item" role="button" data-id="item-1" tabindex="0">
<!-- added `aria-hidden="true"` and `tabindex="-1"` to hide the input from screen readers and make sure it cannot be focused by keyboard, I also added `role="presentation"` just for good measure, although it shouldn't be needed. -->
<input type="text" aria-hidden="true" tabindex="-1" role="presentation">
<!-- Added this span that explains the action that the "content-item button" would perform if clicked. You need to come up with something meaningful for the contents of this -->
<span class="visually-hidden">Edit input [input identifier]</span>
</div>
<div class="content-item" role="button" data-id="item-2" tabindex="0">
<button aria-hidden="true" tabindex="-1" `role="presentation"`> I am a button</button>
<span class="visually-hidden">Edit button [button identifier]</span>
</div>
</div>
<div class ="right" >
<h2> Right panel</h2>
<div id ="right-panel">
</div>
</div>
</div>
You might want to look into the inert attribute. It is not supported by any browser at the moment, but there are polyfill libraries.
The idea is that marking this on any element will make it and all its children non-interactive and invisible for accessibility purposes.
You can read some background and download the library here:
https://github.com/WICG/inert
Try this
$(document).ready(function(){
var demoPage = $('div.demoPage');
demoPage.find('a').attr('href','#');
demoPage.find('button').attr("disabled", true)
})
div {
padding: 20px;
margin: 20px;
background: #eee;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<div class="no-copy">
<p>You can't click me.</p>
</div>
<div class="demoPage">
<a href="www.google.com" readonly="" class="no-copy">
It is a link
</a>
<button> It is a button</button>
</div>
<!--you can do this-->
<input type="text" id="input" disabled>
<script>
document.getElementById('input').disabled = true;
</script>
You can do that very simple by adding some css classes:
<div class="content-item disable-control" role="button" data-id="item-1">
<input type="text" />
</div>
<div class="content-item disable-control" role="button" data-id="item-2">
<button> I am a button</button>
</div>
^^ I added an additional "disable-control" class
.content-item.disable-control * {
pointer-events: none;
cursor: inherit;
user-select: none;
}
.content-item.disable-control *::selection {
background: transparent;
}
Make sure to add some browser-specifix css if needed, e.g.
*::-moz-selection
.. and of course, you can modify my code to apply the class only once (for the whole left pane) and not for each control seperately
Add the disabled attribute to all form elements that you want to disable.
For HTML tags such as <input type="hidden"> when I look at the browser's standard CSS, I see:
input[type="hidden" i] {
display: none;
}
So I added this to my CSS:
input[type="hidden" i] {
display: block !important;
}
And for HTML I have <input name=countrycode type=hidden value=US>
When I open up the developer tools I can see that the old display:none is ignored, however, the field never shows up in the page and is still hidden!
Why doesn't the browser follow my CSS rules, is there another way to force it to do so? If I add width: 200;height:200;background: black this should make it appear in the page, right?
The reason I want to do this is because I have a lot of hidden inputs in many pages that now I want them to be visible (to get input from user) so I decided to do it fast with CSS. I know this can be easily done with JavaScript, but just curious why CSS is not working or maybe it will work on other browsers but not Google Chrome?
The reason that your input[type=hidden] is hidden, is because it doesn't have a control type to show the user it's value, so your display: block; should work, but the browser'll have nothing to render. Your hidden input could be a checkbox, a text field or a number.
More info: https://www.w3.org/wiki/HTML/Elements/input
You should give that input an id="countrycode" and just use javascript to set the value of type attribute from "hiden" to "text" like this:
document.getElementById("countrycode").setAttribute("type", "text");
OR if you prefer to use the name attribute, then do this:
document.getElementByName("countrycode").setAttribute("type", "text");
Voila!
I've been searching for a solution to this problem for a few hours now without any luck. Seems like everyone uses a background image to solve their problem, and I only want to use a color so the solution scales better without extra CSS needed for mobile devices.
I almost have a working solution, but 1) the "checkbox" is not clickable like the rest of the label; 2) I just can't seem to find any way to add space between the label and the "checkbox" without altering the HTML (something that I prefer not to do, but will try if there's no workable CSS-only or JQuery solution).
Here's the HTML:
<div id="edit-field-school-education-und" class="form-checkboxes">
<div class="form-item form-type-checkbox form-item-field-school-education-und-28">
<input type="checkbox" id="edit-field-school-education-und-28" name="field_school_education[und][28]" value="28" checked="checked" class="form-checkbox">
<label class="option" for="edit-field-school-education-und-28">Evening Classes & Distance Learning </label>
</div>
...... Repeated for several checkboxes .......
</div>
Here's my CSS so far:
.form-checkbox {display:none}
.form-checkbox + label:before {content: "\00a0\00a0\00a0\00a0"; height: 19px; width: 19px; border: 1px solid black; cursor:pointer; }
input[type="checkbox"]:checked + label:before {background-color: #34c1ce;}
I prefer a CSS3 solution, but would be okay with a generic HTML or JS solution. I can't have a solution based on IDs etc. since the checkbox list may grow at any time. However, I could probably figure out a way to output a div with the same label text.
You cant set a background color to a checkbox it is rendered depending on the browser not the css, But you could set a background image here is a handy tutorial
http://www.whatstyle.net/articles/18/pretty_form_controls_with_css
Best option is to create your own checkboxes with buttons and JavaScript or use a JavaScript UI. This however means that it doesn't work if JavaScript isn't enabled so you may wish to create a fallback (have a checkbox shown by default and use JS to switch it out with the custom one).
css file:
.radio {
background: url(images/radio-off.png) no-repeat;
height: 21px;
width: 21px;
}
html file:
<input type="radio" name="sendmail" class="radio" value="send" />
It still doesn't show the background, it still shows the "classic" one. What am I doing wrong?
Thank you.
You didn't mention what browser you're using - this is important information.
The ability to style radio buttons (and form elements in general) varies a lot between different browsers.
See here: http://www.456bereastreet.com/lab/form_controls/radio_buttons/
The usual workaround is to use JavaScript to replace the radio buttons with custom elements, that you can style however you like.
For example: jqTransform.
You can't style radio buttons like that because they are native O/S controls.
The solution, for now anyway, is to overlay an element that mimics a radio button on top of the original radio.
Check out input enhancement libraries such as http://www.emblematiq.com/lab/niceforms/.
The best way to do this is with Javascript to ensure cross browser compatibility.
You can use the following script.
http://ryanfait.com/resources/custom-checkboxes-and-radio-buttons/
I have a form where depending on the website's brand one of two input fields should be visible at one given spot.
I figured I just put both input fields in the same container and then through my stylesheet set one of them to display:none;
This does hide the field, but it still makes it take up space.
I also tried setting the height and width to 0 or setting visibility to hidden or collapse but none of those worked.
Untill now all the branding things could be done with css style sheets so I would like to keep it that way.
The solution should at least be supported in IE6 & up, Firefox 2 & up and Chrome (latest).
why don't you use input type="hidden" ?
What about setting the invisible input field to position: absolute; which should take it out of the rendering flow.
However, setting it to display: none should in theory do the same...
<style>
.hideme
{
display:none;
visibility:hidden;
}
.showme
{
display:inline;
visibility:visible;
}
</style>
<input type="text" name="mytext" class="hideme">
You can either set class="hideme" to hide your control or class="showme" to show your control. You can set this toggeling using JavaScript or server side coding.
This does hide the field, but it still
makes it take up space.
This shouldn't happen; display: none should cause the element to not be included in the flow. Check the rest of your CSS (try using Firebug to figure out where the extra "space", which is probably just padding or margin of some surrounding element, is coming from).
Using the visibility property takes up rendering space even if the element is not visible. Instead of using visivility you have to use display property.
You can set the display to none if you want to hide the element and display to block or inline if you want to show them.
To have a look on display check this
If setting your display property doesn't solve your problem, then I think the textboxes might be absolutely positioned. It might be the reason for the layout not to be changed.
Can you please post the complete code?
You can do this if you want to isolate the css code from other input:
input[type="checkbox"] {
display: none;
}
You can also further isolate it from the same type by indicating another class.
I'm not too familiar with CSS, but you can try implementing JQuery which combines Javascript and CSS to let you do stuff like that with relative ease.