Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URLs added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using its URL and the proper URL extension.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Auto Save

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <content>

# ö**🍳**uery,
##### or: you can't make an omelette without breaking a few eggs.

---

**ö🍳uery** is a tiny DOM and events lib, with chainable async, some basic animation, and a few useful utilities. Its main usage is for hacking, playing and prototyping, when you have an idea and want to sketch it out fast. If you're courageous, it's probably stable enough for smaller projects, where you don't need a massive framework.
	
ö🍳uery is partially a subset of jQuery or Zepto, and draws some humble inspiration from lodash, but it's simpler, smaller, faster, and doesn't care about IE.

It is also excellent with a swedish keyboard (If you happen to own a non-Swedish keyboard, simply reassign `ö` to for example `è`, `ü`, `Ω` or `ß`). It relies heavily on ES2015-20 features, and aims to be compatible with as few browsers as possible 🤪. 

**Run code examples by clicking them.**

## Usage
For the npm package: 
`npm install oumlquery `
then:
`import ö from 'oumlquery';`
	
For codepen projects, you can `import 'https://codepen.io/smlsvnssn/full/BrQjRm.js';`. You can also use <a href=https://codepen.io/smlsvnssn/pen/MWpLOOz target=_top>this template</a>.

There is a <a href=https://github.com/smlsvnssn/oumlquery target=_blank>github repo for öQuery</a> as well, if you want fo fork it.
	
Use: 
<code class="runnable">ö('code').html('🤘');</code> 
instead of: 
<code class="runnable">document.querySelectorAll('code').forEach(e => e.innerHTML = '👎');</code>

Call `ö(selector)` or `` ö`selector` `` in order to create `Ö` collections. `Ö` extends `Array`, so all `Array` methods can be used to manipulate `Ö` collections. 

<!--`Ö` is instantiated mainly by calls to `ö(selector)` factory. `new Ö(...iterable)` is slightly faster than `ö(selector)`though, so if you happen to have an iterable full of `Element`s lying around, feel free to call `new Ö(...iterable)` directly.-->

`window` dispatches the event `'öQuery'` when `ö` is initialised, listen for it like so:
`window.addEventListener('öQuery', e => { yourStuffHere }, { once: true });`

#### ö( selector ) → Ö collection of Elements
`ö` eats `Element`, `Window`, `Document`,  `HTMLCollection`, `Nodelist`, `Ö`, `Array`, `Function` and `String`, and outputs an `Ö` collection of `Element`s. `Element`, `HTMLCollection`, `Nodelist` and `Ö` get converted directly, `Array`s are filtered for `Element`s only.

`String`s behave pretty much like jQuery, meaning you can create elements by passing `ö('<tag>')` or `` ö`<tag>` ``, and select elements by passing `ö('#cssSelector')` or `` ö`#cssSelector` ``.

`Window` and `Document` are mostly there for event handling and custom data, most other functions assume `Element`s.
	
`Function` gets executed after `DOMContentLoaded`.

Unlike jQuery, you can create `SVGElement`s by prefixing a tag with svg, like so: `'svg<circle>'`.
	
## Extensions
öQuery can be extended with **<a href=https://codepen.io/smlsvnssn/full/PomzZem target=_top>övents</a>**, a collection of custom events, and **<a href=https://codepen.io/smlsvnssn/full/jOmWwMB target=_top>öbservable</a>**, a minimal reactivity lib. If you import these extensions, you don't need to import öQuery explicitly, since the extensions also import öQuery.
	
### övents
<a href=https://codepen.io/smlsvnssn/full/PomzZem target=_top>övents</a> adds custom events `enterview` and `exitview`, emitted when an `element`'s bounding box enters or exits the viewport, `sticktotop` and `sticktobottom`, emitted when an `element`'s bounding box touches the top/bottom of the viewport, `swipeleft`, `swiperight`, `swipeup` and `swipedown`, emitted when user swipes on a touch device, and `clickoutside`, emitted on click or tap outside `element`. 
	
For codepen projects, `import 'https://codepen.io/smlsvnssn/pen/PomzZem.js';` Some time in the future there will be minified versions on npm and whatnot as well... 🙈
	
### öbservable
<a href=https://codepen.io/smlsvnssn/full/jOmWwMB target=_top>öbservable</a> is a (very) small plugin adding reactive values to öQuery. öbservable uses `Proxy` objects to intercept changes to observable values, and in doing so detects for exemple direct array manipulation. Adds methods `ö.observable()`, `ö.observe()` and `ö.isObservable()`.
	
For codepen projects, `import 'https://codepen.io/smlsvnssn/pen/jOmWwMB.js';`

## Inputs
#### selector
Anything that can convert to an `Element` or a list of `Element`s via `ö(selector)`.

#### f
Any sync or async function as callback. Event callbacks get the event object as argument.

#### event
An event name or space-separated string, i.e. 
<code class="runnable">ö('code').on( 'click pointermove', e => ö(e.target).html('👻') )</code> , 
or object with `{ eventtype: f }` (except for `waitFor()`, which takes a single event only).

You can register your own custom eventtypes with `ö.registerCustomEvent()`.
	
#### t
Time in milliseconds, or optionally for sync functions, a function with arguments `index, element` that returns `Number`.

Example: <code class="runnable">ö('code').rotate(180, (i) => i**2 )</code>

#### key
Property, attribute, style or custom data key to be retrieved or set. To get values, `key` can be either a single property key or a space-separated string, i.e. `'color border-color'`, specifying several keys. If only one key, the key's value is returned. If more than one, an object with `{ keys: values }` is returned. The value is retrieved from the first `Element` in the `Ö` collection.

Example: <code class="runnable">ö('code').each( e => e.html(e.prop('offsetTop')) )</code>

To set values, you can pass an object with `{ keys: newValues }`.

#### value
A value or a function returning a value. Functions are called for every `Element`, with `index, prevValue, element` as arguments.

Example: <code class="runnable">ö('code').prop( 'innerText', (i, v) => v.split('').reverse().join('') )</code>


## Outputs
Almost all methods return `this`, and are chainable. Some methods, such as `prop()` act as getters if `value` is not provided. The util methods do not output `this`, they return new `Ö` collections or other values.

`waitForQueue()` returns a `Promise` resolved when the running queue is finished.
	
## Queue 
All methods that return `this` are queueable. The queue is local to an `Ö` instance, use `await ö.wait, await ö.waitFor` etc for global async.

Sync methods get called immediately, and then removed from the queue, but queueing can be forced by first calling `startQueue()`, delaying execution to next tick. This can be useful if you want to loop the queue.
	
#### queue( f ) → this 
Arbitrary functions can be queued by passing them to `queue()`. Functions are called with `this` as argument. If you need to pass other arguments, use a closure. Functions passed to queue are not awaited, and any return value is abandoned, so use this primarily for functions with side effects. If you need to await your function, use `delay()` instead. 
	
#### startQueue( t ) → this
Forces subsequently queued methods into the queue, with optional delay. 

Example: <code class="runnable">ö('code').html('🕺').startQueue().rotate(25, 500).wait(500).rotate(-25, 500).wait(500).loop()</code>
	
#### stopQueue() → this
Stops queue, and rejects `waitForQueue()`.

#### pause() → this
Pauses queue and `waitFor()` listener.

#### unpause() → this
Unpauses queue and `waitFor()` listener.
	
#### loop( n = 0, reverse = false ) → this
Loops all functions in active queue, also subsequent calls. Loops n times, or infinitely if n is zero. Loops back and forth if `reverse` is `true`. If looping back and forth, first and last functions in the queue execute only once per lap.

Example: 
<code class="runnable">ö('code').loop(4, true).html('🥇').wait(300).html('🥈').wait(300).html('🥉')</code>



## Async
Async methods delay queue execution in various ways. Most are chainable, executed and awaited internally when queue runs. Await entire queue with `waitForQueue()`.


### Chainable
Cannot be awaited. Thenable/awaitable versions are found in `ö`. 
`delay()` is a sync method, but pauses/unpauses queue after delay.

#### wait( t = 1 ) → this
Delays queue by `t` milliseconds.
	
#### waitFor( selector, event ) → this
Delays queue until event occurs. Takes only one element (First in list if `selector` matches more than one element), and one event type.
		
#### waitFrames( n ) → this
Delays queue by `n` frames.
	
#### load( url, f, isJSON = false ) → this
Loads html from `url` and inserts it into elements in current collection, unless callback is provided. Optional callback with arguments `result, index, element`, called for every element in collection.
	
Optionally load `JSON` instead. Use `JSON` with a callback, or strange things will happen 😊.

Example: 
<code class="runnable">ö('code').html('Waiting...').load('https://randomuser.me/api/?results='+ö('code').length, (r, i, e) => ö(e).html(r.results[i].name.first + ' ' + r.results[i].name.last), true )</code>

#### delay( f, t = 1, removePrev = false ) → this
Delayed async callback, pauses running queue after `t` milliseconds and awaits callback. If `removePrev` is true, previous pending delay gets cleared. Callback gets `this` as argument.

	
### Thenable/awaitable
	
#### waitForQueue() → Promise
Resolved when queue finishes, rejected by `stopQueue()`. Use as last call for awaiting or thening a queue, like so: <code class="runnable">ö('code').scale(1.2, 500).wait(500).scale(1, 500).wait(500).waitForQueue().then( () => ö('p').rotate(180, 1000) ) </code>

	

## Sync
Sync methods modify an `Ö` collection's `Element`s in various ways. All sync methods are chainable, but some methods, such as `prop()`, act as getters if `value` is not provided. 
	
### Events
Event methods take an event name or a space-separated string, i.e. `'click pointerover'`, or an object with `{ eventtype: f }`, and a callback function. The callback receives the `Event` object as argument. If multiple event names, the same callback is added for all of them.

All listeners added by `Ö` methods are cached internally, and can be removed with `off()` without reference to the original callback, much like jQuery.

#### Custom events
öQuery can be extended with <a href=https://codepen.io/smlsvnssn/full/PomzZem target=_top>övents</a>, a small collection of useful custom events.

#### on( event, f ) → this
Adds event listeners to elements in collection. Yeah, that's it, really.
	
#### off( event, f ) → this
Removes event listeners added by `Ö` methods from elements in collection. `off()` removes all events, `off('eventtype')` removes all events with specified type/s, and `off('eventtype', f)` removes a specific listener. 

#### throttle( event, f , t = 50 ) → this
Adds event listeners, and throttles event handling to one call per `t` milliseconds. If called multiple times per period, the last call gets executed.

`throttle()`, `debounce()` and `onAnimationFrame()` are wrappers for corresponding methods in `ö`, returning new functions. If you need to keep a reference to the event handler passed to these three methods, call the `ö` methods directly instead, like so: 
`const handler = ö.throttle( e => yourFunctionHere );
ö('code').on('pointermove', handler);`
	
#### debounce( event, f, t = 50, immediately = false ) → this
Adds event listeners, and debounces event handling until no calls are made within `t` milliseconds. If called multiple times per period, the last call gets executed. If `immediately` is set to `true`, the first call gets executed as well.
	
#### onAnimationFrame( event, f ) → this
Defers event handling to next animation frame. If called multiple times per frame, the last call gets executed. Useful for for example `scroll` and `pointermove` event listeners.
	
#### trigger( event ) → this
Dispatches event/s from elements in collection.
	
#### once( event, f, oncePerElement = false) → this
Adds event listeners that triggers once to elements in collection. If `oncePerElement = true`, event/s are triggered once per element and event type, otherwise once per collection.
	
#### hover( over, out ) → this
Convenience method for `mouseenter` and `mouseleave`. Takes one or two functions. If only one, function is called for both events. Listens to mouse events instead of pointer events, in order to produce a toggleable state on (most) touchscreens.


Example: <code class="runnable">ö('code').hover( e => ö(e.target).scale(2, 1000), e => ö(e.target).scale(1, 1000) )</code>


	
### Iteration
Since `Ö` extends `Array`, it's easy to `filter`, `map` etc. `Array` methods return `Ö` objects, for example: 
<code class="runnable">ö('code').filter( e => e.innerHTML.match(/Iteration/) ).scale(2, 3000)</code> 

If you want to iterate over a collection with `Element`s as `Ö` objects, use `each()`.

	
#### each( f ) → this
Wraps each element in collection with `ö()`. Receives `ö(element), index, this` as arguments. Use `forEach()` or `for of` if you want to iterate over pure elements.

	
### DOM
DOM methods add or remove `Element`s from the DOM tree. If there are more than one `Element` in the collection, inserted `Element`s are cloned, otherwise moved.

Methods `append` through `insertBefore` can take a function returning an element, called with arguments `index, element.innerHTML, element` for each `element` in collection. 

#### append( selector ) → this
Appends `selector` to each `Element` in collection.

Example: <code class="runnable">ö('code').append('\<b\> 👈 \</b\>')</code>
	
#### appendTo( selector ) → this
Appends current collection to each `Element` in `selector`.

Example: <code class="runnable">ö('\<b\> 👈 \</b\>').appendTo(ö('code'))</code>
	
#### prepend( selector ) → this
Prepends `selector` to each `Element` in collection.
	
Example: <code class="runnable">ö('code').prepend('\<b\> 👉 \</b\>')</code>
	
#### prependTo( selector ) → this
Prepends current collection to each `Element` in `selector`.
	
#### after( selector ) → this
Inserts `selector` after each `Element` in collection.
	
Example: <code class="runnable">ö('code').after('\<b\> 👈 \</b\>')</code>

#### insertAfter( selector ) → this
Inserts current collection after each `Element` in `selector`.
	
#### before( selector ) → this
Inserts `selector` before each `Element` in collection.
	
Example: <code class="runnable">ö('code').before('\<b\> 👉 \</b\>')</code>
	
#### insertBefore( selector ) → this
Inserts current collection before each `Element` in `selector`.
	
#### wrap( selector ) → this
Wraps each `Element` in current collection with `selector`.

Example: <code class="runnable">ö('code').wrap('\<e\> 👉 \<e> 🌔 \<e>\</e\> 🌖 \</e\> 👈 \</e\>')</code>
	
#### wrapAll( selector ) → this
Wraps current collection with `selector`.

#### remove / detatch() → this
Removes all `Element`s in collection from the DOM.

Example: <code class="runnable">ö('code').remove()</code>
	
#### empty() → this
Removes all children of `Element`s in collection.
	

### Properties 
These methods handle getting and setting of values of `Element` properties. If `value` is not set, these methods act as getters, otherwise they return `this`.

To get values, `key` can be either a single property key or a space-separated string, i.e. `'background-color display'`, specifying several keys. If only one key, the key's value is returned. If more than one, an object with `{ keys: values }` is returned. The value is retrieved from the first `Element` in the `Ö` collection.

To set values, you can pass an object with `{ keys: newValues }`, specify a value or a function returning a value. Functions are called for every `Element`, with `index, prevValue, element` as arguments.

	
#### prop( key, value ) → this | value | object with values
Get/set properties of `Element`s. Properties are faster than attributes, use properties whenever possible.
	
#### attr( key, value ) → this | value | object with values
Get/set attributes of `Element`s.

#### data( key, value ) → this | value
Get/set custom data on `Element`s. If no key is provided, the entire `data` object is returned. `data()` cannot take space-separated keys as input, use it without `key` instead. `data` is populated with data from `Element.dataset`, i.e `data-`attributes.

`data` is associated with `Element`s via a `WeakMap` internally. `Ö` keeps a cache in the `data` object, prefixed with `ö_`. Please do not mess with it.

Functions as `value` are called with `index, prevValue, element` as arguments. If you want to store a function with `data()`, wrap it in a function, since functions get executed for return values, like so:
<code class="runnable">ö(document).data('clickHandler', () => e => ö(e.target).html('🧶'));
ö('code').on('click', ö(document).data('clickHandler'));</code>

You can also use `ö.data(element, key, value)`, which doesn't run functions on input.
	
#### css / style( key, value, t ) → this | value | object with values
Get/set style properties of `Element`s. Takes an optional `t` value, affecting styles that can be transitioned. All animations are handled with css transitions. Takes both `camelCased` and `kebab-cased` property names, as well as `--customVariables`.

Example: <code class="runnable">ö('code').style('text-shadow', i => \`0 ${i}px ${i/5}px #0000\`, i => i**1.5)</code>

	
#### html( value ) → this | value
Shortcut for `prop('innerHTML', value)`.

#### text( value ) → this | value
Shortcut for `prop('innerText', value)`.
	
#### val / value( value ) → this | value
Shortcut for `prop('value', value)`, for forms and the like.
	
#### removeAttr( name ) → this
Removes attribute.
	
### Easing
Handles easing for transitions applied by `Ö`. Supports shorthands for some nice `cubic-bezier` functions, `ease-in-back`, `ease-out-back` (bouncy), `ease-in-expo` and `ease-out-expo` (fast).
	
The same easing function get applied for all transitions handled by `Ö` methods on an `Element` at any one time. If you need different easing functions for different properties, use css classes instead. Transitions defined in css are not affected by `ease()`.
	
#### ease( easing ) → this
Gets/sets easing (`ease`, `ease-in-out` and the like) for all transitions applied by `Ö` methods on `Element`s in collection. 
	
Example: <code class="runnable">ö('code').ease('ease-in-expo').move(() => ö.random(1000, 5000)+'px', 0, 1000).wait(1000).ease('ease-out-back').move(0, 0, 1500)</code>

	
### Style 
Convenience methods for setting css styles. All arguments for these methods (except for booleans) can take functions with arguments `index, element`.

If numbers are given as arguments, `px` is assumed. Functions must specify units, i.e `value+'px'`.
	
#### position / pos( x, y, t = 0, forceFixed = false ) → this | positions object
`x` and `y` simply sets `left` and `top` style properties, optionally with `t`. `position: fixed` can be forced by setting `forceFixed = true`.
	
With no arguments, gets `positions` object, with lots of useful properties, retreieved from first `Element` in collection: `{ top, bottom, left, right, width, height, x, y, documentX, documentY, offsetParent, offsetX, offsetY, scrollX, scrollY }`
	
`pos()` animates slowly, use `move()` for fast animation.

Example: <code class="runnable">ö('code').pos('50%', '50%', 1000, true).wait(1000).move('-50%', '-50%', 1000)</code>
	
#### hide( t = 0, visibility = false ) → this
Hides `Element`s in collection. Takes an optional `t` value, which animates to `opacity: 0`, then sets `display: none`, optionally sets `visibility: hidden` instead.

Example: <code class="runnable">ö('code').hide(2500, true)</code>
	
#### show( t = 0 ) → this
Shows `Element`s in collection. Sets `display` to cached value, or to `block` if `none`. Takes an optional `t` value, which animates to `opacity: cachedValue`.

Example: <code class="runnable">ö('code').hide(0, true).wait().show(2500)</code> 
( `wait()` is needed to delay one tick, since `hide()` waits one tick to set `display: none` )

#### hideShow( f, t = 300 ) → this
Convenience method for fading out, changing, and fading in `Element`s in collection. The callback receives `this` as argument.
	
Example: <code class="runnable">ö('code').hideShow(o => o.rotate(180))</code>
	
#### transform( type, args = [value, ...], t ) → this
Applies transformations to `Element`s in collection, optionally with `transition` set by `t`. `type` takes the name of a <a href=https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function target=_blank>transform function</a>, `args` must have the correct number of arguments for that function in an array. No errors are thrown if inputs are badly formed, it fails silently. Arguments must specify css unit, i.e. `px` or the like. Values in `args` can be functions, called with `index, element` as arguments.

Transformations already applied via css are cached, read and reapplied before adding new transformations.

Example: <code class="runnable">ö('code').transform( 'rotateY', [ i => i**2+'deg' ], 2500 )</code>
	
### Move, rotate, scale
Shortcuts for useful transformations. If `Number`s are given as arguments, the appropriate unit is assumed.

Uses `translate3d()`, `rotate3d()` and `scale3d()` internally.

#### move( x, y, t ) → this
Functions must specify unit, i.e `value+'px'`, `value+'rem'` etc.

Example: <code class="runnable">ö('code').move(2000, 0, () => ö.random(10000))</code>
	
#### rotate( deg, t ) → this
Functions must specify unit, i.e `value+'deg'`.

Example: <code class="runnable">ö('code').rotate(() => ö.random(360)+'deg', 2500)</code>
	
#### scale( amount, t ) → this
Example: <code class="runnable">ö('code').scale(0, 2500)</code>
	

### Style shortcuts
Shortcuts for common css properties.
	
#### bg( value, t ) → this
Gets/sets `background-color`, optionally with `t`.

Example: <code class="runnable">ö('code').bg('#ff0', 2500)</code>

#### clr( value, t ) → this
Gets/sets `color`, optionally with `t`.

Example: <code class="runnable">ö('code').clr(i => ö.hsla(i*15), 2500)</code>
	
#### b( value = true ) → this
Sets `font-weight` to either `bold` or `normal`.

#### i( value = true ) → this
Sets `font-style` to either `italic` or `normal`.
	
#### u( value = true ) → this
Sets `text-decoration` to either `underline` or `none`.

Example: <code class="runnable">ö('code').b().wait(200).i().wait(200).u().wait(200).u(false).wait(200).i(false).wait(200).b(false)</code>
	
### Class 
Manipulates classes on `Element`s in collection.
	
#### addClass( list ) → this
Adds class/es to `Element`s in collection. Takes `'class'` or `'class1 class2'` as input.

#### removeClass( list ) → this
Removes class/es from `Element`s in collection. Takes `'class'` or `'class1 class2'` as input.
	
#### removeAllClasses() → this
Removes all classes from `Element`s in collection.
	
#### toggleClass( str, condition ) → this
Toggles a class on `Element`s in collection. Takes an optional `Boolean`, or a function with `index, element` as arguments returning a boolean, that determines if the class should be set or unset.
	
#### replaceClass( str, replace ) → this
Replaces a class on `Element`s in collection.

	
## Util
These methods return new `Ö` collections, `Element`s, `Boolean`s or index values, and are not queueable. The ones returning new `Ö` collections are chainable, though.
	
#### hasClass( list, all = false ) → Boolean
Determines if `Element`s in collection have specified class/es. If `all == false`, returns `true` if any `Element` has any class. If `all == true`, returns `true` if all `Element`s have all classes.
	
#### isInView( completely = false, all = true ) → Boolean 
Determines if `Element`s in collection are in the viewport. If `completely == true`, returns `true` if `Element`s are completely within viewport. If `completely == false`, returns `true` if any part of `Element`s bounding box are in viewport. If `all == true`, returns `true` if all `Element`s are in viewport. If `all == false`, returns `true` if any `Element` is in viewport.
	
#### equals( selector, strict = false ) → Boolean
Compares every `Element` in collection with `Element`s defined by `selector`. If `strict == false`, compares by `Element.isEqualNode()`. If `strict == true`, comparison is done with strict `===` equality.
	
#### index / getIndex( element ) → index | -1
Finds the index value for an `Element`, or the first `Element` in an `Ö` collection.

If input is an `Element` or an `Ö` collection, the search is performed within the current `Ö` collection.

If input is a `selector` string, the search is performed within the matching `Ö` collection, for the first `Element` in the current `Ö` collection.

If input is `undefined`, the search is performed within the parent of the first `Element` in the current `Ö` collection.
	
#### get / e( index = 0 ) → Element
Returns the `Element` at `index`.
	
#### eq / atIndex( index ) → Ö
Returns the `Element` at `index` wrapped in `Ö`.
	
#### find( selector ) → Ö
Finds descendants within current `Ö` collection. This method extends `Array.find()` by accepting a wider range of inputs.
	
If input is a `function`, behaves just like `Array.find()`, i.e. returns the first element that satisfies the testing function, wrapped in `Ö`.
If input is an `Element` or an `Ö` collection, the search is performed with `Element.contains()`.
If input is a `selector` string, the search is performed with `Element.querySelectorAll()`.
	
#### clone() → Ö
Clones an `Ö` collection and its `Element`s. The queue is not cloned.
	
#### parent( selector ) → Ö
Returns the parent/s of `Element`s in the current `Ö` collection, optionally filtered by `selector`.
	
#### prev( selector ) → Ö
Returns the previous sibling/s of `Element`s in the current `Ö` collection, optionally filtered by `selector`.

#### next( selector ) → Ö
Returns the next sibling/s of `Element`s in the current `Ö` collection, optionally filtered by `selector`.


## ö util methods
These methods aim to be useful in various ways. Some of them are used internally by `Ö`. Call them directly on the global `ö` object, like so: <code class="runnable">ö('code').html(ö.message(ö.toString()));</code>

### Generators / Iterators
Helper methods for iterations, less verbose than regular loops.
		
#### ö.range( start, end, step = 1 ) yields Number 
Yields `Number`s within specified range. Parameters `end` and `step` are optional. If `end` is not provided, range starts with `0`, and ends with `start`. Handles negative values. Useful in `for of` loops, for example `for (let i of ö.range(100)) doStuff(i);`.
	
#### ö.grid( width, height ) yields { x, y }
Yields `Object` with `x, y` coordinates. If `height` is omitted, `width` is assumed. Use like so: `for (let i of ö.grid(8)) drawChessboard(i.x, i.y);`.
	
#### ö.times( times, f = i => i, ...rest ) → Array
Calls a function `times` times, with `index` as argument. Additional arguments are passed on to `f` like so: `ö.times(100, (i, a, b) => i+a+b, 'a', 'b');`. 

Returns an array containing the return values of `f`, or an array containing index values if `f` is `undefined`.


### Array / Iterable
Methods for manipulating arrays or array-like objects. Inputs are coerced to `Array`, so `String`, `Set`, `Ö` and the like works as input as well. All methods are non-mutating.
	
#### ö.rangeArray( start, end, step = 1 ) → Array
Returns an `Array` populated with given range.

Example: <code class="runnable">ö('code').html((_, v) => ö.rangeArray(v.length/2).map(() => '🐥').join('🥚'));</code>

#### ö.unique( arr ) → Array
Returns an `Array` with unique entries.
	
#### ö.shuffle( arr ) → Array
Returns a new shuffled `Array`.
	
Example: <code class="runnable">ö('code').html((_, v) => ö.shuffle(Array.from(v)).join(''));</code>

#### ö.sample( arr, samples = 1 ) → Array item | Array
Returns random sample from `arr`, or an array of samples if `samples` is larger than one. 
	
#### ö.sum( arr ) → Number
Sums `arr`, with `Number` coercion. 
		
#### ö.mean( arr ) → Number
Calculates mean value of `arr`, with `Number` coercion.
	
#### ö.median( arr ) → Number
Calculates median value of `arr`, with `Number` coercion.

#### ö.max( arr ) → Number
Returns largest value in `arr`.
	
#### ö.min( arr ) → Number
Returns smallest value in `arr`.
	
#### ö.groupBy( arr, prop ) → Map
Takes an `Array` of `Objects` with a common property. Returns a `Map` with keys corresponding to `prop` values, holding grouped values.
	
### Set operations
Methods for comparing arrays or array-like objects. Inputs are coerced to `Array`. All methods return a new `Array`, or `Boolean`. 
	
If all inputs to these methods are `Set`s, the outputs adhere to strict set logic. If the inputs are `Array`s, duplicate items are allowed (except in `union()`).
	
#### ö.intersect( a, b ) → Array
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="0.5" y="0.5" width="9" height="9" stroke="white"/><rect x="5.5" y="5.5" width="9" height="9" stroke="white"/><path fill-rule="evenodd" clip-rule="evenodd" d="M10 5V10H5V5H10Z" fill="white"/></svg> Intersection, returns elements that are members of both `a` and `b`. 
	
Example: `ö.intersect([0, 1], [1, 2]) // returns [1]`
	
#### ö.subtract( a, b ) → Array
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="0.5" y="0.5" width="9" height="9" stroke="white"/><rect x="5.5" y="5.5" width="9" height="9" stroke="white"/><path fill-rule="evenodd" clip-rule="evenodd" d="M10 0H0V10H5V5H10V0Z" fill="white"/></svg> Difference, returns members of `a` but not members of `b`, i.e. subtracts `b` from `a`.
	
Example: `ö.subtract([0, 1], [1, 2]) // returns [0]`
	
#### ö.exclude( a, b ) → Array
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M10 0H0V10H5V15H15V5H10V0ZM10 5H5V10H10V5Z" fill="white"/></svg> Symmetric difference, returns elements that are members of `a` or `b`, but not both.
	
Example: `ö.exclude([0, 1], [1, 2]) // returns [0, 2]`
	
#### ö.union( a, b ) → Array
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="0.5" y="0.5" width="9" height="9" stroke="white"/><rect x="5.5" y="5.5" width="9" height="9" stroke="white"/><path fill-rule="evenodd" clip-rule="evenodd" d="M10 0H0V10H5V15H15V5H10V0Z" fill="white"/></svg> Returns (unique) members  of both `a` and `b`.
	
Example: `ö.union([0, 1], [1, 2]) // returns [0, 1, 2]`

#### ö.isSubset( a, b ) → Boolean
Returns `true` if `a` is a subset of `b`.
	
### Logical / generic
#### ö.isEqual( a, b, deep = true ) → Boolean
Checks equality by value rather than reference. Checks own enumerable properties only. Works for all basic types and most built in classes, but may produce unexpected results in edge cases. Equality is tricky, and depends on what you personally beleive to be equal 😇. Does deep comparison by default, and may be slow for large data structures. If `deep == false`, does flat comparison instead.
	
#### ö.clone( v, deep = true ) → new value
Performs cloning of the most common object types, including `Array` and typed arrays, `Map`, `Set`, `Date` and generic objects. Defaults to deep cloning, set `deep` to `false` to perform shallow cloning. Clones own enumerable properties only, and does not set `prototype`, so objects depending on inheritance or class instances are not cloned properly. Does not clone functions. Use with some caution 🤫.	
	
#### ö.pipe( v, ...funcs ) → value
Pipes function calls. For multiple arguments, use closures. Usage: `ö.pipe(1, x => x*6, x => x**2, x => x+6, ö.log) => logs 42`.
	
#### ö.memoise( f, keymaker ) → f
Creates and returns memoised functions. By default, the arguments to the memoised function are used as key for storing the result (If only one argument, the raw input is used as key, if more than one, the arguments are joined to a string). If the arguments are objects instead of primitive values, you should provide a `keymaker`. `keymaker` receives all inputs from the memoised function, and should return something unique to use as a `Map` key for a given set of inputs. Use for example `JSON.stringify` when you expect objects as input.
	
Example: `const memoised = ö.memoise(obj => doReallyComplexStuff(obj), JSON.stringify)`
	
#### ö.createEnum(arr) → Object;
Creates and returns an enumerable, i.e. an object where the keys and values are the same. Lets you create kinda sorta vanilla typechecking light. Takes an array of strings as input.
	
Example: `const sizes = ö.createEnum(['small', 'medium', 'large']); giveMeIcecream(sizes.large);`
		
### Mathy
#### ö.random( min, max, float = false ) → integer | Number
Shorthand for random integers between `min` and `max`-1. If `max` is omitted or `Boolean`, assumes a `min` value of 0. If `max` is `Boolean`, `float` is assumed. If `float` is true, returns float instead of integer.
	
#### ö.randomNormal( mean = 0, sigma = 1 ) → Number
Returns random number from reasonably approximated normal distribution, centered around `mean`, with <a href=https://en.wikipedia.org/wiki/68%E2%80%9395%E2%80%9399.7_rule target=_blank>more or less 68.2% of the sample set</a> within ± `sigma`. Values max out at a bit above ± 3 `sigma`, with extreme outliers up to about  ± 4 `sigma`. There are <a href=https://observablehq.com/@d3/d3-random#normal target=_blank>more mathematically accurate methods</a> to do this, but this method is fast, and good enough for most people. Use it for fun and visuals, not for statistical analysis 🤓.
	
Example: 
<pre class="runnable">
for (let i of ö.range(200)) 
	ö('&lt;star&gt;⭐️&lt;/star&gt;')
		.appendTo(ö('content'))
			.move(
				ö.randomNormal(0, window.innerWidth / 2), 
				ö.randomNormal(0, window.innerHeight / 2), 
				3000
			)
			.scale(ö.randomNormal(5, 3), 3000)
			.hide(3000);
</pre>
	
#### ö.round( n, precision = 0 ) → Number
Returns `n` rounded to `precision` decimals.

#### ö.clamp( n, min, max ) → Number
Clamps `n` between `min` and `max`.
	
#### ö.between( n, min, max ) → Boolean
Checks if `n` is between `min` and `max`.
	
#### ö.normalize( n, min, max, clamp = true ) → Number
Normalizes `n` to a value between 0 and 1, within range given by `min` and `max`. If `clamp == true` and value of `n` is out of range, the value is clamped. 
	
#### ö.lerp( a, b, t ) → Number

Interpolates linearly between `a` and `b`. `t` is a percentage value between 0 and 1.

#### ö.smoothstep( a, b, t ) → Number

Interpolates smoothly between `a` and `b`. `t` is a percentage value between 0 and 1.

#### ö.easeIn( a, b, t ) → Number

Eases in from `a` to `b`. `t` is a percentage value between 0 and 1.

#### ö.easeOut( a, b, t ) → Number

Eases out from `a` to `b`. `t` is a percentage value between 0 and 1.

#### ö.nthRoot( x, n ) → Number

Returns nth root of positive number, for example `ö.nthRoot( 256, 8 ) == 2`

#### ö.factorial( n ) → Number

Returns the factorial of `n`.

#### ö.nChooseK( n, k ) → Number

Returns the number of ways to choose `k` elements from a set of `n` elements, i.e. the binomial coefficient.

#### ö.toPolar(x, y) → { r, theta }

Converts cartesian coordinates to polar.

#### ö.toCartesian(r, theta) → { x, y }

Converts polar coordinates to cartesian.

	

### String
#### ö.prettyNumber( n, locale = 'sv-SE', precision = 2 )	→ String
Returns `n` rounded to `precision` decimals and formatted by `n.toLocaleString()`. Defaults to swedish formatting, because why not! `locale` is optional, if second argument is `Number`, `precision` is set instead. <code class="runnable">ö('code').html(() => ö.prettyNumber(ö.random(2**16, true)));</code>
	
#### ö.wrapFirstWords( s, numWords = 3, startWrap = '\<span\>', endWrap = '\</span\>', startAtChar = 0 ) → String
Returns `s` with first `numWords` words wrapped in `startWrap` and `endWrap`. Matches first words up to and including first punctuation. Optionally starts matching at index `startAtChar`. Matches special chars for nordic languages as well as \', ’ and -.
	
Example: <code class="runnable">ö('p').html((_, v) => ö.wrapFirstWords(v, 2, '\<b\>', '\</b\>'));</code>
	
#### ö.toCamelCase( str ) → String
Returns regular sentence, kebab-case or snake_case string converted to camelCase. Leaves `--custom-properties` alone.
		
#### ö.toKebabCase( str ) → String
Returns regular sentence or camelCase string converted to kebab-case. Leaves `--customProperties` alone.
	
### Colours
<a href=https://css-tricks.com/yay-for-hsla/ target=_blank>Hsla</a> lets you use colour in an understandable way. `hsla` is great! Use `hsla`!
	
#### ö.toHsla( colour, asString = false) → { h, s, l, a } | String
Returns `colour` converted to an object with `hsla` values. Optionally returns a colour string in `hsla` format. Takes hex values, as well as all valid forms of rgb/rgba strings.
	
Hsla is really easy to work with compared to rgb. For example, a `darken` method could look like this, given a `hsla` object as input: `const darken = (c, amount) => (c.l-=amount, c)`
	
Another example: 
<code class="runnable">const c = ö.toHsla(ö('body').bg()); 
ö('p, code').bg(() => ö.hsla(c.h + ö.randomNormal(0, 10), c.s, c.l-5));</code>
	
#### ö.hsla( h, s = 70, l = 50, a = 1 ) → String
Returns colour string in `hsla` format, for css input. Takes separate values, or a single object with properties `{ h, s, l, a }`.
	
Example: <code class="runnable">const h = ö.random(360); 
ö('body').css({'--bgcolor': ö.hsla(h, 70, 20), '--codecolor': ö.hsla(h+180, 70, 80)});</code>

		
### Async
Awaitable wrappers for `setTimeout`, `requestAnimationFrame` and events. Takes an optional awaited `f` with no arguments.

#### ö.wait( t = 0, f, resetPrevCall = false ) → Promise
Waits `t` milliseconds. If `resetPrevCall == true`, previous pending call is rejected.
		
#### ö.nextFrame( f ) → Promise
Waits one frame.
		
#### ö.waitFrames ( n = 1, f, everyFrame = false ) → Promise
Waits `n` frames. If `everyFrame == true`, callback is executed every frame.
		
#### ö.waitFor( selector, event, f ) → Promise
Waits for specified event. Takes only one element, and one event type.
	
#### ö.load( url, isJSON = true ) → Promise
Loads (and parses) JSON. Optionally loads HTML. Super simple fetch wrapper.
	

### Throttling
#### ö.throttle( f, t = 50 ) → Function
Throttles execution of `f` to one call per `t` milliseconds. If called multiple times per period, the last call gets executed.
	
#### ö.debounce( f, t = 50, immediately = false ) → Function
Debounces execution of `f` until no calls are made within `t` milliseconds. If called multiple times per period, the last call gets executed. If `immediately` is set to `true`, the first call gets executed as well.
	
#### ö.onAnimationFrame( f ) → Function
Defers execution of `f` to next animation frame. If called multiple times per frame, the last call gets executed.
	

### Event handling	
#### ö.addEvent( element, event, f, once = false )
Caches events using `ö.data()`, handles custom events, and adds event listeners to `element`.
	
#### ö.removeEvent( element, event, f )
Removes event listeners from cache and `element`, and unobserves custom events. If `event` is omitted, all events on `element` get removed. If `event` is provided, all events of that type get removed. If `f` is provided and matches an active listener, that specific listener gets removed. 	
	
#### ö.registerCustomEvent( eventtype, on, off )
Registers a custom `eventtype`, enabling listening for the eventtype through the `ö` event handling system. Requires an `eventtype` as `string`, an `on` handler with `element` as argument, responsible for dispatching an event from `element` as desired, and an `off` handler with `element` as argument, responsible for turning off event dispatching for `element`.
	
In its simplest form it would look like this, by simply hijacking the `click` event:

<pre>
const callback = e => {
	e.target.dispatchEvent(new Event('myCustomEvent'));
};
ö.registerCustomEvent(
	'myCustomEvent', 
	element => element.addEventListener('click', callback), 
	element => element.removeEventListener('click', callback)
);
ö('code').on('myCustomEvent', e => ö.log(e.type));
</pre>

	
### Error handling and logging	
#### ö.verbose( isVerbose, isThrowing = false ) → Boolean
Get/set `isVerbose`, turns off error/message logging when set to `false`. Defaults to `true`. Optionally set `isThrowing` to `true`, in order to throw errors instead.
	
#### ö.error( error, ...rest ) → console.error or thrown Error,  arguments
Logs errors to console, optionally throws instead. Can be silenced globally by calling `ö.verbose(false)`. Returns single argument, or multiple arguments as an array.
	
#### ö.warn( message, ...rest ) → console.warn, arguments
Outputs arguments to console. Can be silenced globally by calling `ö.verbose(false)`. Returns single argument, or multiple arguments as an array.
	
#### ö.log( ...messages ) → console.log, arguments
Outputs arguments to console. Can be silenced globally by calling `ö.verbose(false)`. Returns single argument, or multiple arguments as an array. Can be used like so: `const x = ö.log( y*z );` or to tap into a call chain.
		
#### ö.message( str ) → 'ö🍳uery says: ${str}'
Wrapper for internal messages.
	
### Basic type checking
Less verbose than `typeof`/`Array.isArray`/`instanceof`:
	
#### ö.isBool( v ) → Boolean
	
#### ö.isNum( v ) → Boolean
	
#### ö.isInt( v ) → Boolean
	
#### ö.isBigInt( v ) → Boolean
	
#### ö.isStr( v ) → Boolean
	
#### ö.isSym( v ) → Boolean
	
#### ö.isFunc( v ) → Boolean
	
#### ö.isArr( v ) → Boolean
	
#### ö.isNull( v ) → Boolean
	
#### ö.isDate( v ) → Boolean
	
#### ö.isMap( v ) → Boolean
	
#### ö.isSet( v ) → Boolean
	
#### ö.isRegex( v ) → Boolean
	
#### ö.is( v ) / ö.isDefined( v ) → Boolean
	
#### ö.isnt( v ) / ö.isUndefined( v ) → Boolean
	
#### ö.isObj( v ) → Boolean
`ö.isObj` excludes `Array`, `Map`, `Set`, `Date` and `RegExp`. And `null`, of course.
	
#### ö.isIterable( v ) → Boolean
Checks for `[Symbol.iterator]` in `v`.

### Internal
#### ö.checkSelector( selector, ...rest ) → Array of Elements
Core method for `Ö` creation. Checks selector in various ways in order to create an `Array` of `Element`s. Can be used as a template tag.
	
#### ö.createElement( html, isSvg = false ) → Element
Creates an `Element` from an html string. Optionally creates an `SVGElement`.
		
#### ö.parseDOMStringMap( o ) → Object
Parses a `DOMStringMap` as `JSON`. Used internally when reading from `Element.dataset`.

#### ö.data( element, key, value ) → data | data.key
Get/sets `data` on an `Element`. If no `key`, returns `data` object. Associates `Element` with `data` via `WeakMap`.

#### ö.deepest( element, selector = '\*' ) → Element
Finds deepest `Element` in `element`, optionally matching `selector`.

	
### Random stuff
#### ö.toString() → 'Hello ö🍳uery!'
Politeness.

#### ö.rorövovarorsospoproråkoketot( str ) → String
Converts string to Rövarspråket, like so: <code class="runnable">ö('code').text((_, v) => ö.rorövovarorsospoproråkoketot(v) );</code> 
___
##### © 2018-2021 <a href=https://lhli.net target=_blank>lhli.net</a>
##### Licence: <a href=https://opensource.org/licenses/MIT target=_blank>MIT</a>

</content>
<toc></toc>
<script>
	window.addEventListener('öQuery', () => {
		const 
			t = 3000,
			content = ö('content').html(),
			toc = ö('toc'),
			body = ö('body'),
			renderToc = () => {
				headlines
					.clone()
					.on('click', e => {
								headlines
									.find( el => el.innerText === e.target.innerText )
									.e().scrollIntoView({behavior: 'smooth', block: 'start'});
							})
					.appendTo('toc');
				toc.hover(() => {
					toc
						.addClass('active')
						.wait(300)
						.addClass('open');
					body
						.wait(300)
						.addClass('noScroll');
				}, () => {
					toc
						.stopQueue()
						.removeClass('active open');
					body
						.removeClass('noScroll');
					})
			}, 
			run = e => {
				const target = ö(e.target);
				scroll = window.scrollY;
				heightBefore = document.documentElement.scrollHeight;
				target.off();
				Function(target.text())();
				target
					.wait(t)
					.off()
					.queue(reset)
				doScrollback = (Math.abs(document.documentElement.scrollHeight - heightBefore) > 100);
			},
			reset = () => {
				ö('content')
					.html(content)
					.wait()
					.find('.runnable')
					.on('click', run);
				body
					.css({'--bgcolor': 'inherit', '--codecolor': 'inherit'})
				headlines = ö('h2, h3, h4');
				if (doScrollback) window.scroll(0, scroll);
			}
		let headlines = ö('h2, h3, h4'),
		scroll, doScrollback, heightBefore;
		//
		renderToc();
		ö('.runnable').on('click', run);
		// Debug scrollback
		window.scroll(0, localStorage.getItem('scrollY'))
		ö(window).throttle('scroll', e => localStorage.setItem('scrollY', scrollY), 500)
		//
		//ö.log(ö.prettyNumber(Math.E*1000))
		//let test = ö.shuffle(ö.rangeArray(1000000));
		//console.time('test')
		//ö.log(ö.sum(test))
		//console.timeEnd('test')
		//
		//const a = [1,2,3,4,'5',6,7,8,9,10];
		//ö.log(ö.median(a))
		//ö('code').onAnimationFrame('pointermove', (e) => ö.log(e.x), 100, true);
		/*ö('code').clr('#fff')
			.on('enterview', e => ö(e.target).clr('#ff0',ö.random(3000)) )
			.on('exitview', e => ö(e.target).clr('#fff'));*/
		//ö('.runnable').on('enterview', e => ö.log('enter', ö(e.target).isInView(true)) );
		//ö('.runnable').on('exitview', e => ö.log('exit', ö(e.target).isInView()) );
	}, { once: true })
</script>
              
            
!

CSS

              
                @import url("https://fonts.googleapis.com/css?family=Libre+Franklin:400,700&subset=latin");
@import url('https://fonts.googleapis.com/css2?family=Source+Code+Pro&display=swap');

$font: "Libre Franklin";
$codefont: 'Source Code Pro';
$size: 5rem;
$textcolor: #fff;
$bgcolor: #F06292; //#ffea00; #123456;
$codecolor: #ff0;
$alpha: 0.2;
$small: "only screen and (max-width : 600px)";
$scrollbarWidth: 7px;

:root {
	--bgcolor: #{$bgcolor};
	--codecolor: #{$codecolor};
}

html {
	width: 100%;
	height: 100%;
	box-sizing: border-box;
		
	font: 10px/10px $font;
}

*,
*:before,
*:after {
	box-sizing: inherit;
}

body {
	height: 100%;
	padding: $size;
	@media #{$small} {
  	padding: $size/2;
	}
	margin-bottom: $size/2;
	color: $textcolor;
	background: var(--bgcolor);
	
	font: $size*.3#{"/"}$size*.5 $font;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	
	&.noScroll {
		overflow-y: hidden;
		margin-right: $scrollbarWidth;
	}
}

content {
	margin: auto;
	max-width: $size*15;
	padding-bottom: $size;
	display: block;
}

hr {
	border: $textcolor solid 1px;
	width: 10%;
	margin: $size/2 0
}

h1 {
	font: $size*2#{"/"}$size*2 $font;
	font-weight: 700;
	letter-spacing: -($size/15);
	margin-bottom: $size/5;
	@media #{$small} {
  	font: $size#{"/"}$size $font;
		font-weight: 700;
		letter-spacing: -($size/30);
	}
	
	strong {
		font-size: $size*3;
		vertical-align: -($size*.9);
		margin-left: $size*.12;
		margin-right: -($size*.45);
		@media #{$small} {
  	  font-size: $size*1.5;
			vertical-align: -($size*.45);
			margin-left: $size*.06;
			margin-right: -($size*.22);
		}
	}
}

h2 {
	padding-top: $size/2;
	font: $size#{"/"}$size*1.5 $font;
	letter-spacing: -($size/30);
	font-weight: 700;
}

h3 {
	padding-top: $size/2;
	font: $size/2#{"/"}$size $font;
	letter-spacing: -($size/60);
	font-weight: 700;
}

h4 {
	padding-top: $size/2;
	font-weight: 700;
}

h5 {
	font-weight: 700;
}

p {
	margin-bottom: $size/4;
}

a {
	color: $textcolor;
}

strong, b {
	font-weight: 700;
}

i {
	font-style: italic;
}

code, pre {
	display: inline-block;
	font: $size*.28#{"/"}$size*.5 $codefont;
	color: var(--codecolor);
	word-break: break-all; 
	tab-size: 2;
	
	&.runnable {
		cursor: pointer;
		text-shadow: 0 0 10px;
		transition: text-shadow .5s;
		&:hover {
			text-shadow: 0 0 2px;
		}
	}
}

pre {
	display: block;
	max-width: 100%;
	overflow: auto;
}

.sticky {
	position: sticky;
	top: 0;
	color: var(--bgcolor);
	background: var(--codecolor);
}

star {
	font: 10px/10px $font;
	position: fixed;
	top: 50%;
	left: 50%;
}

toc {
	position: fixed;
	top: 0;
	right: 0;
	height: 100%;
	max-width: 50rem;
	overscroll-behavior: contain;
	@media #{$small} {
  	max-width: 80%;
	}
	overflow: visible;
	background: var(--codecolor);
	color: var(--bgcolor);
	padding: $size/2;
	transition: transform .3s ease-in-out;
	transform: translate(calc(100%));
	a {
		color: var(--bgcolor);
	}
	&:before {
		content: 'TOC';
		font-weight: 700;
		position: fixed;
		//top: -($size*.5);
		//left: -($size*1.5);
		top: -($size*.5);
		left: -($size*1.5)+$size*.08;
		width: $size*2.5;
		height: $size*1.5;
		background: var(--codecolor);
		transition: transform .3s ease-in-out;
		transform: rotate(45deg);
		text-align: center;
		line-height: $size*2.5;
	}
	&.active {
		transition: transform .3s ease-out;
		transform: translate(0);
		&:before {
			transition: transform .3s ease-out;
			transform: rotate(0deg) translate(0, -100%);
		}
	}
	&.open {
		overflow-y: auto;
	}
	
	h2, h3, h4 {
		cursor: pointer;
		transition: all .3s;
		&:hover {
			text-decoration: underline;
			transform: translatex(2px);
		}
	}
	
	h2 {
		padding-top: $size/2;
		font: $size/2#{"/"}$size/2 $font;
		letter-spacing: -($size/60);
		font-weight: 700;
	}

	h3 {
		padding-top: $size/4;
		font: $size/2.5#{"/"}$size/2 $font;
		letter-spacing: 0;
		font-weight: 700;
	}

	h4 {
		padding-top: $size/4;
		font: $size*.3#{"/"}$size*.3 $font;
		font-weight: 400;
	}
}

::-webkit-scrollbar-track
{
}

::-webkit-scrollbar
{
    width: $scrollbarWidth;
		height: $scrollbarWidth;
    background-color: var(--codecolor);
}

::-webkit-scrollbar-thumb
{
    background-color: var(--bgcolor);
		 border: 2px solid var(--codecolor);
    border-radius: $scrollbarWidth;
}

@mixin cmykify($c: 20, $m: 40, $y: 100, $k: 10, $raster: 3, $saturation: 1) {
	$base: "https://cmykify.vercel.app";
	background-image: url(#{$base}/cmyk/c#{$c}.png), url(#{$base}/cmyk/m#{$m}.png), url(#{$base}/cmyk/y#{$y}.png), url(#{$base}/cmyk/k#{$k}.png),
		url(#{$base}/cmyk/grain.png);
	background-size: #{$raster * 45}px, #{$raster * 45}px, #{$raster * 45}px, #{$raster * 45}px, 512px;
	filter: saturate($saturation);
}
              
            
!

JS

              
                /*
Source moved to npm, find it there: 
https://www.npmjs.com/package/oumlquery
*/

import ö from "https://cdn.skypack.dev/oumlquery@latest";
window.ö = ö; // break out of module to access ö globally.
              
            
!
999px

Console