Changelog
All notable changes to GridSelect. The format follows Keep a Changelog, versions follow Semantic Versioning. Dates are ISO 8601 (YYYY-MM-DD), each version has a matching git tag vX.Y.Z.
Since 1.0.0 the JavaScript API, the options and attributes, the data-gridselect-* hooks and the --gs-* variables are stable: new features raise the minor version, fixes the patch version, and breaking changes need a new major version.
Unreleased
Changed
- Docs: the theme gallery in the styling guide shows the Tippkraft theme in light and dark mode, and the README's features table lists it.
1.2.0 - 2026-11-08
Added
- Theme
themes/tippkraft.css(Tippkraft): small items, plain digits, warm greys with orange for the cursor and matches, light and dark. - Touch screens: on phones (touch, at most 640 px wide) the panel opens as a full-screen sheet with the group's name and a close button, and ends above the on-screen keyboard. Tablets keep the normal panel with the close button. The keyboard only comes up when the search input is tapped, and digit badges are hidden on touch-only devices.
sheet: false(data-sheet="off",sheet="off", React and Vue prop) keeps the normal panel;closeText(close-text) names the button.
Fixed
- Phones: iOS no longer zooms into the page when the panel opens. On touch screens the search input is at least 16 px, the size below which iOS zooms into a focused input; the zoomed page made the panel wider than the screen.
- Phones: the panel measures the visible area instead of the whole window, so it ends above the on-screen keyboard and follows it when the keyboard opens or closes.
- Escape that closes the panel stops there and no longer closes a surrounding
<dialog>as well; with the panel closed it bubbles as usual (#3). - Inside a container that scrolls or clips (such as a modal
<dialog>withoverflow: auto) the panel measures its room against that container, not only the window, so it is no longer cut off. Moving the cursor scrolls only the list, never the page or a dialog around it (#2).
Changed
- Playground: a section with a GridSelect inside a scrolling modal dialog.
- Demo: the width section also shows a panel kept within a container (
data-panel-container), and its code comments sit on their own line.
1.1.0 - 2026-11-02
Added
- Panel within a container:
panelContainer: "<name>"(data-panel-container,panel-container, React and Vue prop) keeps the full-width panel inside the nearest ancestor markeddata-gridselect-container="<name>", between its inner edges and still 10 px inside the window.
Changed
- Documentation rewritten in full, explanatory prose for people new to GridSelect, with a new page "How GridSelect works" that explains the threshold, items, the panel, the cursor and digit tiers.
- Documentation: the whole site takes the look of the demo and the showcase theme (slate, sky blue, near-black dark mode, dark code blocks), and the live examples use the showcase theme; checked with axe in light and dark mode.
- Documentation: comments in code examples sit directly after the code or on their own line above it, so code lines no longer wrap.
Fixed
- README and documentation: the CDN links pointed to version 0 (
@0) instead of the current major version@1; a test now keeps them in line.
1.0.0 - 2026-11-02
The first stable release. From here on the JavaScript API, the options and attributes, the data-gridselect-* hooks and the --gs-* variables follow Semantic Versioning.
Added
- Documentation site at https://gridselect.im/docs/ (VuePress): a guide with a live example on every page, a reference of options, attributes, hooks, events and CSS variables, the changelog and contributing notes. The demo links to it.
- Demo: a section comparing the full-width panel with the narrow one (
data-panel-width="fixed").
Changed
- The README is a short overview with links into the documentation.
0.22.0 - 2026-11-02
Added
- Entries from a server:
load(query, signal)is called on open and 250 ms after typing; the answer replaces the list while the panel, search text and focus stay. Loading and error states are shown and announced (loadingText,errorText), stale requests are aborted, chosen entries stay chosen,minQueryLengthandminQueryTexthold back short queries.el.loadon<grid-select>(withmin-query-length,loading-text,error-text),loadprop in React and Vue.
Changed
- Size budget: core 9.5 kB, core plus element 11.5 kB (min+gzip). The demo now says about 9 kB.
0.21.0 - 2026-11-02
Added
<grid-select>is a form control of its own (ElementInternals): it is inform.elements, submits its value undername(each value withmultiple), and hasform,validity,validationMessage,willValidate,checkValidity(),reportValidity()andsetCustomValidity().requireduses the browser's message and:invalid; reset, a disabled fieldset and back/forward restore work like on native fields. Browsers without ElementInternals keep the inner inputs.
Fixed
<grid-select>that is already defined while the page is parsed (script in<head>) now takesselectedoptions and thevalueattribute that arrive after it; before, the first, empty build won. Once someone picks, their choice wins over later markup changes.
Changed
- Size budget: core plus element 10.5 kB (min+gzip).
0.20.0 - 2026-11-02
Added
- Panel width: by default (
panelWidth: "full") the panel spans the window, 10 px from each edge, and is only as wide as needed when all entries fit in one row;--gs-panel-max-widthcaps it. It follows resizing.panelWidth: "fixed"(data-panel-width,panel-width, React and Vue prop) keeps--gs-panel-widthfrom the field's edge.
Changed
- The panel's default width is now the window width; use
panelWidth: "fixed"for the previous width.
Fixed
- Demo: the size note says about 8 kB instead of the outdated 5 kB; a test now keeps it in line with the build. The package description speaks of items instead of chips.
0.19.0 - 2026-11-02
Added
- Panel placement: by default (
panelHeight: "auto") the panel opens below when everything fits, otherwise towards the side with more room (data-placement="top"), and its list grows up to 8 px before the window edge, never beyond the content, at least 10rem. It follows scrolling and resizing while open.panelHeight: "fixed"(data-panel-height,panel-height, React and Vue prop) keeps the previous behaviour: always below, list height from--gs-list-max-height.
Changed
- Size budget: core 8.5 kB, core plus element 10 kB (min+gzip).
- The panel's default height is now "as much as fits" instead of
--gs-list-max-height; usepanelHeight: "fixed"for the old behaviour.
0.18.1 - 2026-11-02
Changed
- Pinned and recent entries no longer show a second time in the list below, and the digits run on from the top into the list without a gap (no reserved places for recent picks). While searching, the top groups are hidden and the list holds every entry again.
0.18.0 - 2026-11-02
Added
- Descriptions: a second line per entry from
descriptioninitems,data-descriptionon an<option>or an element withdata-gridselect-descriptionin your own label. Shown small and muted under the label, read by screen readers as the description (aria-describedby), never in the closed field.searchDescription(data-search-description,search-description, React and Vue prop) makes the search find them, label matches first.
0.17.0 - 2026-11-02
Added
- Upper limit for multiple choice:
max(data-max,maxon the element, React and Vue prop). At the limit the other entries are locked (aria-disabled,data-limit, muted but not struck through), cannot be ticked by click, Space or digit, and a status region announces "Maximum of n reached" (maxText).
Changed
- Size budget: core 8 kB, core plus element 9.5 kB (min+gzip).
- Demo: the watchlist stops at three entries.
0.16.0 - 2026-11-02
Added
- Pinned and recent entries:
pinned(data-pinned,pinned) shows chosen values on top of the panel,recent(data-recent,recent) remembers the last picks inlocalStorageand shows them below. Both are groups with their own digits; the list keeps its entries and starts after the reserved places. OptionsrecentMax,pinnedText,recentText; React and Vue props of the same names.
Changed
- Size budget: core 7.5 kB, core plus element 9 kB (min+gzip).
- The demo moved to its own domain: https://gridselect.im (the old GitHub Pages address redirects there).
0.15.0 - 2026-11-02
Added
- React and Vue wrappers:
@emmgeede/gridselect/react(<GridSelect>withvalue/onChange,defaultValue,items, props in camelCase,ref; React 18 and 19) and@emmgeede/gridselect/vue(v-model, the same props,changeandinputpassed on; Vue 3.3+). Both wrap<grid-select>; React and Vue are optional peer dependencies.
0.14.0 - 2026-11-02
Added
- Themes
high-contrast(black and white, 2 px borders, 7:1 text, WCAG AAA) andcompact(dense spacing, colours of the default stylesheet), next toshowcase. - Any file in
themes/can be imported:"./themes/*"export, e.g.@emmgeede/gridselect/themes/compact.css. CONTRIBUTING.mdwith the rules for new themes. The browser tests check every theme automatically (axe, text contrast, chevron, focus and cursor, header comment, own layer, light and dark).- Demo: a Theme switch (built with GridSelect) offers Default and every theme, remembers the choice, and shows the theme's description.
Changed
- Demo: the light and dark switch is labelled "Mode".
0.13.0 - 2026-11-02
Added
- Group headings in the panel: options inside
<optgroup>(in a<select>or a<grid-select>) and group objects initems({ label, items: [...] }, also insetItems()) get a heading above their entries. Each group starts a new row, the heading stays at the top while its group scrolls by, and each group is arole="group"named by its heading. Arrow keys cross groups, digits run on, the filter keeps the group order.
Changed
- Size budget: core 7 kB, core plus element 8.5 kB (min+gzip).
- Errors for invalid
itemsname the path, e.g.items[1].items[0], instead ofposition 1.
0.12.2 - 2026-11-02
Added
- README images for the grid and flow layouts, and animations for search, multiple choice, keyboard navigation and digit shortcuts. With reduced motion turned on, a still image replaces each animation.
0.12.1 - 2026-11-01
Fixed
- A click next to the field (inside its wrapper, e.g. on empty space or the legend) now closes the open panel, like any other click outside.
Changed
- The demo shows both panel layouts, the auto-width grid and the flow layout.
- Releases are published from GitHub Actions with npm Trusted Publishing and provenance when a version tag is pushed.
- README logo and screenshots link to the demo, which also keeps GitHub from drawing a background behind them.
0.12.0 - 2026-11-01
Added
- Grid columns as wide as the widest item: the panel measures its items on open and sets
--gs-auto-width; the stylesheets use it (at least--gs-item-min-width, at most half the list), so short labels get as many columns as fit. - Flow layout,
layout: "flow"(data-layout, element attributelayout): items run one after another like chips; arrow up and down jump to the nearest item in the row above or below. - Logo in the README.
Changed
- The showcase theme, the demo and the README screenshots use sky blue as accent; the default stylesheet stays teal.
- Size budget: core 6.5 kB, core plus element 8 kB (min+gzip).
0.11.0 - 2026-11-01
Added
- Fuzzy matching, option
match: "fuzzy"(data-match-mode, element attributematch): letters of each word in order with gaps, words in any order, better matches first in filter mode. Works with both search modes.
Fixed
- The published demo missed
src/element.jsand showed nothing interactive. The site is now assembled bynpm run site, and a browser test checks the assembled site for script errors and missing files before every deploy.
0.10.0 - 2026-11-01
Added
- Custom element
<grid-select>(@emmgeede/gridselect/element, CDN filedist/gridselect-element.min.js): builds a named fieldset with native inputs from its<option>children or theitemsproperty, light DOM, attributes for all options,valueanditemsproperties, rebuilds on attribute and option changes while keeping choice and focus. - A test guards the size budget: core 6 kB, core plus element 7.5 kB gzip.
0.9.0 - 2026-11-01
Added
- Entries from a JS array: options
items,name,valueandmultiplebuild native radios or checkboxes in an empty wrapper.instance.setItems(items)swaps them later, keeps chosen values that still exist and switches between visible items and the panel as needed.
Changed
- README: installation right after the introduction, screenshots at full width with three columns in the panel and a transparent backdrop around the card, so they sit on GitHub's own light or dark background. Each screenshot has a caption explaining what it shows.
0.8.0 - 2026-11-01
Added
Optional showcase theme
@emmgeede/gridselect/themes/showcase.css: compact, contemporary look (14 px text, flat panel rows, soft fills, check mark on selected entries, keycap badges), light and dark, WCAG AA text contrast. Used by the demo and the README screenshots.Multiple choice in the panel: checkbox groups above the threshold get the field and panel, items toggle and the panel stays open, the field shows up to two labels or
selectedText(n)(option, defaultn + " selected").<select multiple>is supported: generated checkboxes, the select stays the source of truth and firesinputandchange.
Changed
<select multiple>is no longer left native with a warning.- README screenshots show the showcase theme, framed with padding, rounded corners and a light neutral backdrop.
- The demo page has a new split layout with a style switch and a note on what the showcase theme does and does not keep from WCAG.
- Grid rows in the panel share the height of the tallest row, and digit badges have a fixed width so labels line up. The listbox role moved to a new scroll container (
[data-gridselect-scroller]) around the item list.
Fixed
- Typing in the panel's search input no longer sends
inputandchangeevents to the wrapper, sochangelisteners only see real selections.
0.7.1 - 2026-11-01
Changed
- Published on npm as
@emmgeede/gridselect: npm rejected the plain namegridselectas too similar to an existing package. Imports are now@emmgeede/gridselectand@emmgeede/gridselect/gridselect.css. 0.7.0 was tagged but never published.
0.7.0 - 2026-11-01
Added
- Minified build
dist/gridselect.min.jsfor use without a bundler (window.gridselect), CDN fieldsunpkgandjsdelivr, tests and build run before every publish. - Demo page, published on GitHub Pages: https://emmgeede.github.io/gridselect/
- README screenshots (light and dark), badges, installation section.
- Automated accessibility checks with axe-core (WCAG 2.0/2.1/2.2 A and AA) in every state, light and dark, on every push.
- CI on GitHub Actions for unit and browser tests.
Fixed
- Filtering no longer does quadratic work per keystroke.
0.6.0 - 2026-11-01
Added
- Optional stylesheet
gridselect/gridselect.cssin@layer gridselect: items, field, panel, states and digit badges, light and dark mode (prefers-color-schemeordata-theme), all colours and sizes as--gs-*variables, WCAG 2.2 AA contrast.
0.5.0 - 2026-11-01
Added
<select>as source: GridSelect hides the select and builds radios for its options (not submitted), so selects get the same items, panel, search and digits. Picks setselect.valueand fireinputandchangeon the select. An option with an empty value is the placeholder.required,disabled, reset and the select's<label>are respected.<select multiple>stays native with a console warning,valuealready reads and writes it as an array.
Fixed
- The panel picks up select changes made by code without an event when it opens.
0.4.0 - 2026-11-01
Added
- Digit shortcuts, option
shortcuts/data-shortcuts="off": 1 to 9 and 0 pick ten entries per tier, tiers default to["", "Shift", "Alt"]and take any combination ofShift,Alt,Control,Meta. A digit badge ([data-gridselect-key]) is added to each label and panel item, or an existing one in the label is used. Inputs and items getaria-keyshortcuts. - Badges of modifier tiers carry readable letters in
data-prefix(S1,A3,CS2) instead of symbols.
Changed
- With digit shortcuts on (the default) GridSelect adds the digit badges to your labels.
destroy()removes them again.
Fixed
- Hidden native labels, the closed panel and filtered-out items stay hidden even when the page CSS sets
displayon them. - Number pad keys with NumLock off navigate instead of picking an item.
- Spaces around
+in tier names are accepted.
0.3.0 - 2026-11-01
Added
- Search in the panel, option
search/data-search:"filter"(default) hides non-matches and lists label-start matches first,"highlight"keeps items in place and marks them withdata-match/data-dimmed. Matching ignores case, surrounding spaces and accents. - Screen reader announcement of the match count, options
resultsTextandnoResultsText.
Fixed
- A throwing
resultsTextno longer stops the filtering.
0.2.0 - 2026-11-01
Added
- Grid panel for radio groups above a threshold (option
threshold/data-threshold, default 10): a closed field, a panel with a search input and one item per radio, following the WAI-ARIA combobox and listbox pattern. Arrow keys move through the grid using the column count from the CSS. Enter or click picks, Escape, Tab, outside clicks and a second click on the field close. Keys during IME composition are not intercepted. - Option
layout/data-layout:"list"for one column with long labels. - Options
placeholderandsearchPlaceholder. - Picking in the panel checks the native radio and fires
inputandchange. Form submit, reset,requiredanddisabledkeep working.
Changed
- Entries are called "items" (were "chips" during development).
0.1.0 - 2026-11-01
Added
gridselect(wrapper)turns a native radio or checkbox group inside a wrapper into an instance. Same wrapper, same instance.instance.valuereads and sets the selection: a string ornullfor radios, an array for checkboxes. Setting fires no event. Numbers match their string form, other types match nothing.instance.destroy().- Clear
TypeErrors for a missing element, mixed radios and checkboxes, and radios with different names. Warning for duplicate values. - TypeScript declarations generated from JSDoc.