yukey

hello, yukey. a new ui component library, for you who loves the keyboard.

commit 5377260618dc000d25c81703f506766f52926aec · browse this tree
warpfork, 2026-10-10 02:12:06 UTC

This will be a bunch of pretty sizable code dumps
because it's already been implemented in another repo
(it's was incubated in a game prototype, but seems more useful than the game!);
I'm starting with the docs, because A) docs are good
and B) those needed the largest manual detangling, too.
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..4e1d18d
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,6 @@
+[*]
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+indent_style = tab
+indent_size = 4
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..0b44d77
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,14 @@
+This library is dual-licensed under either of Apache 2.0 or MIT terms:
+
+- Apache-2.0 Software License: https://www.apache.org/licenses/LICENSE-2.0
+
+- MIT Software License: https://opensource.org/licenses/MIT
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the terms listed in this notice is distributed on
+an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+either express or implied. See each License for the specific language
+governing permissions and limitations under that License.
+
+<!--- SPDX-License-Identifier: Apache-2.0 OR MIT -->
+`SPDX-License-Identifier: Apache-2.0 OR MIT`
diff --git a/LICENSE-APACHE b/LICENSE-APACHE
new file mode 100644
index 0000000..270573e
--- /dev/null
+++ b/LICENSE-APACHE
@@ -0,0 +1,7 @@
+Copyright (c) 2026 Eric Myhre and various authors and contributors
+
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
diff --git a/LICENSE-MIT b/LICENSE-MIT
new file mode 100644
index 0000000..35276e4
--- /dev/null
+++ b/LICENSE-MIT
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2026 Eric Myhre and various authors and contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3e3d5af
--- /dev/null
+++ b/README.md
@@ -0,0 +1,76 @@
+Yukey
+=====
+
+*a ui framework for the keyboard lover*
+
+
+What is it?
+-----------
+
+Yukey is a set of web components for making extremely keyboard-driven application UIs.
+Yes, you can still use the mouse or a touchscreen on them -- but
+the intent is to make use with the keyboard on par *or better* than mouse or touchscreen use.
+
+
+### Show me!
+
+Sure!  This is a screenshot of some UI elements composed with Yukey:
+
+![screenshot of a yukey megacolumn](docs/demo-screenshot.png)
+
+What you're looking at is called a "megacolumn" element,
+and this one contains a series of buttons and other interactables
+(sliders, text entry, even some lists with activatable elements).
+
+Some details to notice here:
+
+- The "request docking" button is what's currently focused.
+  The focus is visually *loud and clear*.
+- If you used the up and down arrow keys... guess where the focus goes :)
+- If you hovered the "reactor" element and used left and right... you can guess what that does too.
+- (You're catching the theme here?  Yukey is all about making it obvious what the keyboard can do for you, at all times!)
+- If you press keys matching the yellow letters, focus jumps to that element.
+- Activation is distinct from focus.  You can always shift the focus; whether to activate an element after that is a separate choice.
+
+Other features are harder to show in a single screenshot.
+For example, pressing "?" at any point will show more info about
+any focused element.
+(It's like tooltips, but without needing a mouse!)
+
+
+### Tell me more!
+
+Check out the [docs](docs/) dir.
+
+All the "`precepts-*.md`" files talk about the considerations used when making Yukey.
+(The "we-love-the-keyboard" file is particularly rich, and contains a lot of the uniquest parts of Yukey.)
+
+More user guide?  Yeah, sorry, coming soon.
+You can probably figure it out from the component list
+and the docs in the code, though :)
+
+
+### Does it play well with others?
+
+Sure!
+
+Yukey is just web components.
+It should, at a technical level, freely compose
+with anything else you want to mix and match it with.
+
+That said, Yukey does have a fairly distinctive visual language of its own,
+and part of how it empowers the keyboard user also means it has opinions
+about what the keyboard should do at any time that a Yukey element has focus.
+And we did generally design Yukey to make the most sense when an application uses it consistently.
+
+Still: Yukey element groups can be moved between with regular tabindex
+(even though Yukey takes greater control over a concept of "focus" whenever a Yukey element has it),
+generally participate in the same focus systems as the DOM already has,
+and also does everything it can to meet ARIA standards and norms.
+It should play nice with whatever you want.
+
+
+License
+-------
+
+SPDX-License-Identifier: Apache-2.0 OR MIT
diff --git a/docs/demo-screenshot.png b/docs/demo-screenshot.png
new file mode 100644
index 0000000..602e073
Binary files /dev/null and b/docs/demo-screenshot.png differ
diff --git a/docs/precepts-aesthetics.md b/docs/precepts-aesthetics.md
new file mode 100644
index 0000000..f801e7b
--- /dev/null
+++ b/docs/precepts-aesthetics.md
@@ -0,0 +1,9 @@
+yukey's (default) aesthetic
+===========================
+
+Aesthetically, yukey should feel:
+
+- Very cyber
+- Very SCADA
+- Very functional/brutalist
+- ... but it's always ready to explain itself.
diff --git a/docs/precepts-make-it-usable.md b/docs/precepts-make-it-usable.md
new file mode 100644
index 0000000..1fa49a2
--- /dev/null
+++ b/docs/precepts-make-it-usable.md
@@ -0,0 +1,70 @@
+Make It Usable
+==============
+
+Usability is important.
+
+Here are some rules of thumb we've been mindful of
+to make Yukey interfaces usable.
+
+
+### columns are good
+
+We take note that many UX studies over the decades find
+that forms which are completely single-dimensional scrolling
+are easier to interact with than those that vary the x coordinate of blanks,
+or that attempt to fit multiple input areas on one row.
+
+So Yukey really favors helping you build that.
+(The fact this is easy to navigate rapidly with the keyboard is a happy coincidence.)
+
+We don't rule out letting you compose things in other ways, obviously.
+But all the best affordances we offer steer towards simple columns
+rather than jagged multi-dimensional stuff.
+
+(This is where "the megacolumn" UI element comes from,
+and why we feature it so heavily!)
+
+
+### we are not ants
+
+All elements have reasonable sizes.
+
+Nothing is ever 8px by 8px: that is too small.
+Things need to be easily seen,
+and to be easily engaged with on a touchscreen.
+
+
+### hit zones must be visually clear
+
+If a button is a button, it has a strong border appearance,
+and the border marks where the hitzone is.
+
+We never do iconographic buttons that lack clear edges.
+
+
+### let users see what they're doing before committing it
+
+(This is probably good guiding advice for you who is composing your application's logical flow!
+But it guides some design of Yukey's component internals, too.)
+
+Flows should almost always be:
+User moves focus ->
+User sees and mentally confirms intended focus ->
+{User activates | User realizes desync of their mental model from UI state, pauses, and reorients}.
+
+Time alone isn't enough for this.  Separating two parts of action is.
+
+As an example: in Yukey, hotkeys generally cause focus jumps,
+but if they land focus on an activatable like the button,
+*the actual activation is a second action*.
+This means two keystrokes are needed to get the activation to occur,
+but it gives users a chance to confirm if their hotkey jumped them to where they expected
+before they make the second keystroke.
+This lets users *move more confidently* through the UI.
+
+(Consider that is another situation where mouses have different affordances
+that are frankly better developed than for keyboards.
+Mouse hover is already part of "see what you're doing before commit";
+even mouse-down on a button without the matched mouse-up is a furtherance of this.
+Keyboards shortcuts don't always have natural equivalents;
+this is something we're trying to be mindful of and improve on with Yukey!)
diff --git a/docs/precepts-we-love-the-keyboard.md b/docs/precepts-we-love-the-keyboard.md
new file mode 100644
index 0000000..cdc65cf
--- /dev/null
+++ b/docs/precepts-we-love-the-keyboard.md
@@ -0,0 +1,275 @@
+We love the keyboard
+====================
+
+A lot of modern UI is focused on the mouse and touchscreen.
+
+Yukey isn't.
+
+Yukey is for you who wants the keyboard to be extremely powerful.
+
+Not just an afterthought.  Not just tabindex navigation.
+And not just hotkeys for the poweruser who's memorized them.
+Yukey is for a keyboard that's deeply empowered, and always with affordances
+so that it's immediately usable even in low-familiarity situations.
+
+
+keyboard capabilities must be obvious
+-------------------------------------
+
+The number one step to making the keyboard empowered is deceptively simple:
+Exactly what the keyboard can do should always be obvious.
+
+The mouse gets some natural affordances about advertising its capabilities.
+People mouseover on things and expect popups and tooltips,
+and they click anything clickable and see what happens.
+That's great; and _none of it transfers trivially to the keyboard_.
+
+But nonetheless, if there's one thing any kind of usability demands:
+it's that interactable elements must look interactable;
+the ways to get to them must be obvious;
+and there should be as many affordances as possible that make it clear what interacting with an element will do.
+That whole list applies to doorknobs in the physical world and buttons in a UI, both the same.
+
+So, for any aggregated piece of UI that can be interactable,
+Yukey's design has to run through these questions and make sure they have reasonable answers:
+
+- Where's the focus?  Needs to be visible.
+- Is the focused element interactable?  Needs to be obvious.
+- Interactable _in which ways_?
+	- Toggle?
+	- Activate and causes action?
+	- Activate and makes a whole new piece of UI?
+	- Can it also be interacted with for just more info, like a tooltip?
+- Interactable _with what triggers_?
+	- Is the enter key gonna do stuff?
+	- What's the norm for the spacebar, does it usually do the same stuff?
+	- Is this text entry or nah?
+	- Are there other hotkey norms that we'll train users to expect?
+- What's going to end up focused next if the arrow keys are used?  Needs to be obvious.
+	- Is the thing to my {direction} focusable?  Needs to be obvious.
+	- Is the thing to my {direction} interactable?  Needs to be obvious.
+	- If I move my focus to the {direction}, can I move it back to the {inverse_direction} afterwards,
+	  and if I do so, _do I end up in the same place?_  This one is a little flexible, but weigh it.
+- Are there *other* focus jump affordances?  Make them visible.
+
+
+### buttons always show their hotkey
+
+Hotkeys should always be immediately discoverable.
+
+If the hotkey matches a letter in the button's label, that letter is a different color.
+
+Otherwise, there's a visual position about 70% of the way over to the right
+where the hotkey letter is shown in the same significant color.
+
+If a button is a complex button with more than one concept of activation,
+then it shows an icon with this meaning in the same 70% offset position.
+
+
+keyboard actions must be high power and low finger-stretch
+----------------------------------------------------------
+
+Yukey's quest to make the keyboard ultra-powerful means that hotkeys are often *single keystrokes*.
+E.g., it's not "==alt-K==", it's just "==K==" when in a context where "k" is a valid hotkey.
+
+Yukey is effectively what you might call "modal"
+(in the same sense that "vim is a modal editor").
+
+(That being said, Yukey's modes should be a lot more obvious than e.g. Vim's,
+because Yukey does a lot more visual emphasis about where your focus is,
+and the "mode" isn't a mode so much as just a property of where your focus is.)
+
+
+
+more norms
+----------
+
+Yukey introduces several norms that, once you've seen one Yukey interface,
+means you can navigate all Yukey interfaces more rapidly.
+
+
+### activation
+
+Many elements are activatable.  Nuff said.
+
+#### the enter key does activation
+
+Also nuff said.  The Enter key doing activation is common.
+
+Yukey is not pointlessly weird, so the Enter key also does activation in Yukey.
+
+Except in multi-line text entry.  There, the Enter key adds a linebreak.
+
+#### so does the spacebar
+
+The spacebar usually triggers activation, just like the enter key
+(at least, as long as whatever's focused isn't a text input area).
+
+Whacking the biggest button on the keyboard should usually do the most highly-afforded action in context.
+That's usually whatever the Enter key is already doing.
+But text entry dominates: if we're doing text entry, then that's what we're doing,
+and the space key is for inserting spaces.
+
+
+### info cards
+
+One norm that Yukey standardizes,
+specifically to counter the "mouse has tooltips, keyboard has whoops" problem:
+
+On almost any element that's activatable,
+and also many that are purely informative:
+the hotkey `?` means "info, please",
+and does something similar to raising up a tooltip.
+
+#### what goes in info cards?
+
+User's choice.
+
+It's a whole element.
+It can contain a short sentence or phrase,
+or a whole... anything.
+
+But broadly, it should probably be on the short end of the range.
+
+#### what *doesn't* go in info cards?
+
+An info card should used for novel details about the element in question,
+not reiterations of general interaction guides.
+
+For example, an info card should not:
+
+- **Name hotkeys.**  Yukey has a whole visual language for that already.
+- **Describe global norms.**  E.g. it makes no sense to describe what Escape does, nor what arrows do.
+  Norms are learned once; narrating them per-element is noise everywhere.
+- **Describe what Enter/activation does in generic terms.**  Enter is activate.
+  This falls under the same admonition against describing global norms, but further:
+  As a UX story in your application, if what activation of an element will do isn't clear,
+  then explaining it in the info card isn't the right place: a clearer label should be used,
+  or the button is simply too dangerous -- fix that; don't try to hide warnings in the lower-visibility margin notes!
+- **Narrate what focus or hover does.**  By the time the tooltip is open,
+  focus or hover already happened.  Describing what already happened would make no sense.
+
+
+major element types
+-------------------
+
+Yukey introduces some significant element types that may be unusual,
+but provide an easy and powerful way to compose interfaces
+while offering massive clarity for understanding how the keyboard can interact with them
+with nothing more than a first glance.
+
+
+### the megacolumn
+
+Yukey's main UI practice:
+
+Make a whole aggregate UI section act like a single megacolumn.
+
+What's that mean?
+
+- The granularity of the focus is the *row*.
+- There is logically one column.  (Visually, there can still be two: label and input widget.)
+- There is **exactly one interactable element per row**.
+
+Limiting?  Perhaps.  But consider what it gives us:
+
+- Everything is functionally selectable **by using just the up and down arrow keys**.
+	- That immediately solves most of the capability discovery problems; bam, done.
+- And that means _everything else on the keyboard_ can be seen as usable for the row's interaction.
+  (Left, right, enter: great, sliders, toggles, activation: all clear.)
+	- And that means we can route those inputs directly to the inner element of the row,
+	  without faffing about with any more selection questions.  Isn't that nice?
+- If the whole primary unit of UI on the screen is just one megacolumn... it's just so simple.
+	- Seriously, users will love this.  They won't even realize how relaxed it makes them, but it does.
+
+And make no mistake: the megacolumn concept can also still be quite flexible.
+For example: a tree can be still be navigated and treated (mostly, if not entirely) like a megacolumn!
+The fact that there are *visually* varying left indents is fine;
+in terms of the keyboard interactions, there's still a clear ordering of elements,
+and the up and down arrows can navigate on that basis,
+and that means a whole lot of good UX that's usable without surprises.
+
+Now.  If there's one piece of the UI that's clearly the main gig, at any given time,
+and that thing is a megacolumn: then, that pretty much guarantees that the user
+will be able to see and understand where the focus is, and how to move and use it;
+and also, when we write the code, we've got very little work to do,
+because everything going on is basically just norms.
+Isn't that sweet?
+
+#### what if there's REALLY two controls in this row??
+
+First of all, usually, no, there's not.
+
+But let's say there is.
+Suppose we've got a piece of UI that contains a list of things.
+Each item in the list is activatable.
+But also, each item is *removable*.
+
+Alright.  We can still do most of a megacolumn.
+
+- Up/down arrows move the focus.
+- In each row, there's the main interactable element, and also a small red X element on the right.
+- Those elements should have the exact same height, so it's obvious they're in the same family.
+- Those elements should not have large gaps between them, so it's obvious they're in the same family.
+  (If there's dynamic content, then that whole area had better have a border, a background, an ellipsis: something.)
+- When switching between rows, the main element should always get the focus.
+  (We don't want it to be too easy to trigger the deletion action.)
+- The right arrow key should move the focus to the deletion button.
+  (This is what the user does if they don't know there's any other hotkeys in play.)
+- If the user correctly guesses that the 'x' key or 'delete' key will trigger delete of a row,
+  then hooray, they're happy.
+- If the user doesn't guess this, but moves the focus to the delete button and presses `i`,
+  then we make a popup and tell them.
+
+The core concepts of the megacolumn still hold.
+
+#### a column of megacolumns?
+
+If there's one section of UI that is all about one object,
+but it has lots of properties, some of which are tightly related to each other,
+and it's a whole... situation...
+
+Like let's say that list, described in the last section,
+but this object has two of those lists, and some other properties, too.
+What do we do?
+
+Still just use a single megacolumn.  Just do it.
+
+Use visual gaps, vertically, between sections.
+But let focus jump across the gap to the next selectable element, exactly as per usual of a megacolumn.
+It'll be fine.
+There's a 93% chance that letting the focus jump the gaps will be unsurprising to a user
+versus whatever other more nested/complicated/stateful thing that's probably your next alternative idea.
+
+
+### tables
+
+Sometimes one legitimately has a 2D table of data.
+
+For a 2D table, megacolumns aren't it anymore.
+Yukey does indeed have a table component.
+
+It's made with all the usual precepts still in force:
+
+- The focus?  Needs to be visible.
+- What's going to end up focused next if the arrow keys are used?  Needs to be obvious.
+- Does a cell have additional interaction capabilities?  It needs to look like it.
+- Would it have a tooltip if it was mouseover'd?  Then there'd better be a hotkey that does the same.
+
+What changes vs UI parts that get to use the megacolumn approach is that in a table,
+the left and right arrow keys... you guessed it!  They move the focus across cells.
+
+And that means?  Yep, sliders don't get to auto-activate with the right and left arrow keys.
+If there's a slider in a cell in a 2D table, then
+the user would have to activate the cell before interacting with the slider,
+and deactivate it explicitly when done and wanting to go left or right in the table, too;
+or, we'd have to remap other keys to do the slider thing
+(and then have to figure out how to make that obvious, which... well, so,
+that's quite nontrivial, so usually this resolves to a "no, then").
+
+But one step further, what do we do with that?  Ding ding!
+Don't put sliders in 2D tables!  Because why would we do that, really?
+It would probably look terrible!  And all those decades of UX researchers
+about "no more than one input region per row" would also be rolling over in their graves!
+The thing that's hard to do is a thing we shouldn't do anyway;
+copacetic.