Skip to content

Commit

Permalink
Merge pull request #69 from bigskysoftware/toggle
Browse files Browse the repository at this point in the history
Toggle
  • Loading branch information
dz4k authored Dec 11, 2024
2 parents 0efeb80 + 82dee63 commit 1e2d5a4
Show file tree
Hide file tree
Showing 4 changed files with 222 additions and 28 deletions.
74 changes: 74 additions & 0 deletions src/aria.css
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,77 @@
width: fit-content;
text-align: center;
}


input[type=checkbox][role=switch] {
all: unset;
appearance: none;

display: inline-grid;
vertical-align: middle;
grid-template-columns: repeat(2, 1rem);
aspect-ratio: 2 / 1;
margin-block: auto;

&::before, &::after {
content: '';
display: inline-block;

transition: transform .2s ease-in-out, background-color .2s ease-in-out, background-color .2s ease-in-out;
}

&::before {
grid-column: 1 / span 2;
grid-row: 1;
border: 1px solid var(--graphical-fg);
background: var(--bg);
border-radius: 9999rem;
}

&::after {
--toggle-nub-margin: 2px;
grid-column: 1;
grid-row: 1;
margin: var(--toggle-nub-margin);
border-radius: 99999rem;
background: var(--graphical-fg);
}

&:checked {
&::before {
border-color: var(--accent);
background: var(--accent);
}
&::after {
transform: translateX(calc(100% + 2 * var(--toggle-nub-margin)));
background: var(--bg);
}
}

&:indeterminate {
&::after {
transform: translateX(calc(50% + var(--toggle-nub-margin)));
background: var(--bg);
border: 1px solid var(--graphical-fg);
}
}

&:is(label > *):not(#specificity-hack) {
margin-block-end: auto;
}
&:not(label > *) {
padding-block: calc(var(--gap) / 4 + (var(--rhythm) - 1em) / 2);
}

:is(label:has(> &)) {
/* Lightning CSS requires :is() when nested selector doesn't start with & */
display: flex;
gap: var(--gap);
flex-direction: row;
}
:is(label:has(+ &)) {
/* Lightning CSS requires :is() when nested selector doesn't start with & */
width: 100%;
}

}
2 changes: 1 addition & 1 deletion src/core/sanitize.css
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ button, input, select {
* Correct the inability to style buttons in iOS and Safari.
*/

button, [type="button"], [type="reset"], [type="submit"]) {
button, [type="button"], [type="reset"], [type="submit"] {
-webkit-appearance: button;
}

Expand Down
50 changes: 25 additions & 25 deletions src/js/19.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/**
/**
* a DOM helper library.
* "1 US$ = 18.5842 TR₺ · Oct 16, 2022, 20:52 UTC"
*/
Expand Down Expand Up @@ -42,13 +42,13 @@
/**
* Creates a logging function.
* The {@link scope} will be prepended to each message logged.
*
*
* We usually use `ilog` as a name for the resulting function.
* It returns its last argument,
* which makes it easier to log intermediate values in an expression:
*
*
* const x = a + ilog("b:", b); // x = a + b
*
*
* @param {string} scope The name of the component/module/etc. that will use this logger.
* @returns {Logger} The `ilog` function.
*/
Expand Down Expand Up @@ -103,26 +103,26 @@ export function traverse(
? $(root, selector)
: $$(root, selector).at(-1);
}

if (!current) return wrapIt();

// Traverse left to right, bottom to top.
//
// (begin ascii art diagram)
// (R)
// / \
// / \
// (r) (4) <- return value
// / | \ / \
// current -> (1) (2) (3) (*) (*)
// (end diagram)
//
// In the diagram above, 1, 2, 3 are tested by the selector (assuming we
// start at 1). Then, having run out of siblings, we move up (as many times
// as needed) before advancing, ending up at 4.
// as needed) before advancing, ending up at 4.
//
// To "test" an element, ee call Element#matches, then if that returns false,
// querySelector. The querySelector call is how the items marked with
// asterisks can be checked.
// asterisks can be checked.
let cursor = current;
while (true) {
while (cursor[advance] === null) { // 3
Expand Down Expand Up @@ -169,7 +169,7 @@ export function $$(scope, sel) {
* @property {EventTarget} target
* @property {string} type
* @property {EventListener} listener
* @property {object} options
* @property {object} options
*/

/**
Expand Down Expand Up @@ -211,7 +211,7 @@ export function off({ target, type, listener, options }) {
/**
* "Halt" an event -- convenient wrapper for `preventDefault`, `stopPropagation`, and `stopImmediatePropagation`.
* @param {string} o - How to halt. Space-separated list of "default", "bubbling" and "propagation".
* @param {Event} e - The event.
* @param {Event} e - The event.
*/
export function halt(o, e) {
for (const t of o.split(" ")) {
Expand All @@ -226,7 +226,7 @@ export function halt(o, e) {

/**
* Decorator for any event listener to call {@link halt}.
*
*
* on(el, "click", halts("default", e => ...))
*
* @template {Event} T
Expand All @@ -251,13 +251,13 @@ export function dispatch(el, type, detail, options) {

/**
* Get, remove or set an attribute.
*
*
* - attr(el, "name") Get the attribute "name"
* - attr(el, "name", "value") Set the attribute "name" to "value"
* - attr(el, "name", null) Remove the attribute "name"
* - attr(el, [ nameA: "valueA", nameB: "valueB" ]) Set the attributes name-a to "valueA", name-b to "valueB"
*
* @param {Element} el
*
* @param {Element} el
* @param {string | Record<string, unknown>} name - The attribute name **or** a map of names to values.
* If an object is passed, camelCase attribute names will be converted to kebab-case.
* @param {unknown} value - The value of the attribute, when setting. Pass `null` to remove an attribute.
Expand Down Expand Up @@ -333,9 +333,9 @@ export function htmlescape(s) {
* Template literal that escapes HTML in interpolated values and returns a DocumentFragment.
* Can also be called with a string to parse it as HTML.
* To let trusted HTML through escaping, parse it first:
*
*
* html`<p>My trusted markup: ${html(trustedMarkup)}</p>`
*
*
* @param {TemplateStringsArray | string} str
* @param {...unknown} values
* @returns {DocumentFragment}
Expand Down Expand Up @@ -421,10 +421,10 @@ export function prev(root, selector, current, options = {}) {

/**
* Create a handler for keyboard events using a keyboard shortcut DSL.
*
*
* - "ArrowLeft"
* - "Ctrl+Alt+3"
*
*
* @param {Record<string, KeyboardEventListener>} hotkeys
* @returns KeyboardEventListener
*/
Expand All @@ -440,7 +440,7 @@ export function hotkey(hotkeys) {
const
tokens = hotkeySpec.split("+"), key = /** @type {string} */ (tokens.pop());
let modifiers = 0 | 0;
for (const token in tokens)
for (const token of tokens)
switch (token.toLowerCase()) {
case "alt": modifiers |= alt; break;
case "ctrl": modifiers |= ctrl; break;
Expand All @@ -460,8 +460,8 @@ export function hotkey(hotkeys) {

/**
* Debounce a function.
*
* @template {unknown[]} TArgs
*
* @template {unknown[]} TArgs
* @param {number} t The debounce time.
* @param {(...args: TArgs) => void} f The function.
* @param {object} [options]
Expand Down Expand Up @@ -507,13 +507,13 @@ export function behavior(selector, init) {
/**
* @template TData
* @typedef {object} Repeater
*
*
* @property {(datum: TData) => string} idOf
* Returns the HTML id for a given data value.
*
*
* @property {(datum: TData, ctx: { id: string }) => ChildNode} create
* Creates a an element for a data value.
*
*
* @property {(el: Element, datum: TData) => Element | null} update
* Update an element for a new data value.
*/
Expand Down
124 changes: 122 additions & 2 deletions www/docs/40-aria.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ The fiex direction will be set based on `aria-orientation`.

## Feed

Use `feed` role with `<article/>` children — see [WAI: Feed][]. Nested feeds are supported.
Use `feed` role with `<article>` children — see [WAI: Feed][]. Nested feeds are supported.

To get the actual behavior of an accessible feed, you can use [Missing.js &sect; Feed](/docs/js#feed).

Expand Down Expand Up @@ -198,5 +198,125 @@ To get the actual behavior of an accessible feed, you can use [Missing.js &sect;
</div>

</figure>

[WAI: Feed]: https://www.w3.org/WAI/ARIA/apg/patterns/feed/


## Toggle Switch

Use `switch` role with `<input type="checkbox">`. The indeterminate state is supported, but it must be set with JavaScript.

<figure>
<figcaption>Code: Toggle Switches</figcaption>

~~~ html
<div class="f-switch">
<fieldset class="f-col">
<legend>Toggles inside labels</legend>
<label><input type="checkbox" role="switch">Toggle me</label>
<label><input type="checkbox" role="switch" checked>But not me</label>
<label><input type="checkbox" role="switch" class="indeterminate">I'm not sure</label>
</fieldset>
<fieldset class="f-col">
<legend>Toggles inside labels, flipped</legend>
<label class="justify-content:space-between">Toggle me<input type="checkbox" role="switch"></label>
<label class="justify-content:space-between">But not me <input type="checkbox" role="switch" checked></label>
<label class="justify-content:space-between">I'm not sure <input type="checkbox" role="switch" class="indeterminate"></label>
</fieldset>
<script>
document.querySelectorAll('.indeterminate').forEach(
el => {el.indeterminate = true;}
)
</script>
</div>
~~~

<div class="f-switch">
<fieldset class="f-col">
<legend>Toggles inside labels</legend>
<label><input type="checkbox" role="switch">Toggle me</label>
<label><input type="checkbox" role="switch" checked>But not me</label>
<label><input type="checkbox" role="switch" class="indeterminate">I'm not sure</label>
</fieldset>
<fieldset class="f-col">
<legend>Toggles inside labels, flipped</legend>
<label class="justify-content:space-between">Toggle me<input type="checkbox" role="switch"></label>
<label class="justify-content:space-between">But not me <input type="checkbox" role="switch" checked></label>
<label class="justify-content:space-between">I'm not sure <input type="checkbox" role="switch" class="indeterminate"></label>
</fieldset>
</div>

~~~ html
<div class="f-switch">
<fieldset class="table rows">
<legend>Toggles before labels</legend>
<div>
<input id="toggle-1" type="checkbox" role="switch">
<label for="toggle-1">Toggle me</label>
</div>
<div>
<input id="toggle-2"type="checkbox" role="switch" checked>
<label for="toggle-2">But not me</label>
</div>
<div>
<input id="toggle-3" type="checkbox" role="switch" class="indeterminate">
<label for="toggle-3">I'm not sure</label>
</div>
</fieldset>
<fieldset class="table rows">
<legend>Toggles after labels</legend>
<div>
<label for="toggle-4">Toggle me</label>
<input id="toggle-4" type="checkbox" role="switch">
</div>
<div>
<label for="toggle-5">But not me</label>
<input id="toggle-5" type="checkbox" role="switch" checked>
</div>
<div>
<label for="toggle-6">I'm not sure</label>
<input id="toggle-6" type="checkbox" role="switch" class="indeterminate">
</div>
</fieldset>
<script>
document.querySelectorAll('.indeterminate').forEach(
el => {el.indeterminate = true;}
)
</script>
</div>
~~~

<div class="f-switch">
<fieldset class="table rows">
<legend>Toggles before labels</legend>
<div>
<input id="toggle-1" type="checkbox" role="switch">
<label for="toggle-1">Toggle me</label>
</div>
<div>
<input id="toggle-2"type="checkbox" role="switch" checked>
<label for="toggle-2">But not me</label>
</div>
<div>
<input id="toggle-3" type="checkbox" role="switch" class="indeterminate">
<label for="toggle-3">I'm not sure</label>
</div>
</fieldset>
<fieldset class="table rows">
<legend>Toggles after labels</legend>
<div>
<label for="toggle-4">Toggle me</label>
<input id="toggle-4" type="checkbox" role="switch">
</div>
<div>
<label for="toggle-5">But not me</label>
<input id="toggle-5" type="checkbox" role="switch" checked>
</div>
<div>
<label for="toggle-6">I'm not sure</label>
<input id="toggle-6" type="checkbox" role="switch" class="indeterminate">
</div>
</fieldset>
</div>

<script>document.querySelectorAll('.indeterminate').forEach(el => {el.indeterminate = true;})</script>
</figure>

0 comments on commit 1e2d5a4

Please sign in to comment.