Repository navigation
GridKit has moved a long way since the last release was published here in March.
The tags were pushed, the release notes were not — this one closes that gap.
The complete history of all 36 versions is in CHANGELOG.md.
Install
composer require mmollay/gridkitWhat GridKit is: PHP components for admin dashboards — table, form, modal,
sidebar, header and eleven more. No build step, no dependencies, no JavaScript
framework. One CSS file and one JS file. Live demo.
New in this release: an installable agent skill. cp -r skill ~/.claude/skills/gridkit,
or point any assistant at https://gridkit.ssi.at/skill/.
Everything since 1.50.0, in one release. The full history is in
CHANGELOG.md.
[1.54.0] - 2026-08-27
Three ways to hand the API to an assistant, where there was one.
Added — the installable skill
skill/ is GRIDKIT_SKILL.md split so an assistant reads the rules first —
which calls print and which return a string, how Layout::asset() resolves,
which components drop your filters unless told to keep them — and fetches one
reference when it needs one. Sixty-one kilobytes is a lot of context to spend on
a question about one method.
skill/SKILL.md 203 lines the rules, with frontmatter
skill/reference/components.md 1044 lines every component, every option
skill/reference/javascript.md 121 lines GK.*, live tables, global search
skill/reference/css.md 30 lines class names, utility classes
Generated by ci/build-skill.sh, never edited by hand: one source, two shapes,
and a test that fails if they drift. The split is lossless — all 39 headings,
all 46 runnable examples, all 128 public methods.
Install: cp -r skill ~/.claude/skills/gridkit.
Added — a stable address, and llms.txt
https://gridkit.ssi.at/skill serves the whole document as text/markdown,
with CORS open so an assistant can fetch it. llms.txt at the site root points
there, which is the convention a site uses to tell a model what it needs to
know. Both through .htaccess, which also stops serving composer.json,
tests/ and ci/ — none of which are part of the site.
Fixed — a commit that would have blocked every push
The 1.53.0 commit carried .github/workflows/ci.yml. The token this repository
is pushed with has no workflow scope, so that commit could never leave the
machine and everything after it was stuck behind it — the local branch sat one
ahead of the remote with no error to explain why. Recommitted without it; the
workflow still ships as ci/github-actions.yml.
2,199 assertions.
[1.53.0] - 2026-08-27
Two small things in the table, both of which had been quietly costing
readability. Neither changes any markup — every existing page gets them.
Changed — digits line up
font-variant-numeric: tabular-nums on table cells. Proportional digits
are narrower for a 1 than for a 0, so a column of amounts or dates wanders a
little in every row. In prose that is right; in a column of figures it is
noise. Only digits are affected — text in the same cell keeps its normal
spacing.
Visible immediately in any table with a date, a count or an amount.
Changed — the header reads as the header
The header rule is one step darker than the row rules
(--gk-surface-container-highest instead of --gk-outline-variant).
1.44.0 removed the grey bar behind thead for good reasons: caps, size and
weight already announce a header, and a filled bar says the same thing a second
time. But the line that was left to do the separating alone was the same
hairline that separates the rows — so it separated nothing in particular, and
in a long table the head floated among the rows.
One step darker is enough. The token carries its own dark-mode value, so this
holds in both modes without an exception rule.
Unchanged on purpose
The pill radius, the row padding and the zebra decision stay as they are. They
are taste, not craft, and taste belongs in a theme — not in a patch release
that every consuming system picks up without asking.
[1.52.0] - 2026-08-27
Packagist was updated, so composer require mmollay/gridkit finally installs
the current version rather than v1.4.0 from March. That made a test possible
that had never been run: download the archive Packagist really serves, unpack
it, write a page against it the way the README says, and use it.
It found a bug in about two minutes.
Fixed — a delete button that stopped asking
data-gk-confirm was not emitted by the client re-render. The server puts
it on the button; the browser rebuilds that button on every sort, search and
page change of a setData() table, and rebuilt it without the attribute. So a
delete button asked for confirmation once, on first load, and then never again
— the click went straight through to gk:rowaction.
Measured in a browser against the real package:
data-gk-confirm |
click asks? | |
|---|---|---|
| on load | "Delete this entry?" | yes |
| after a sort | missing | no |
1.32.0 fixed exactly this on the server side, where the delete button in the
README's own example was deleting without asking. The client path kept it.
The re-render now emits it, using js.confirm_delete_row — the single-row
wording that matches table.confirm_delete, not the bulk "Really delete
entries?" that js.confirm_delete carries.
Verified — the whole thing, from Packagist
Not a simulation this time. The zipball Packagist points at, unpacked into
vendor/mmollay/gridkit/, with a page written from the README: sidebar, fixed
header, stat cards with trends, a searchable sortable table with row actions and
client-side paging, in English and German.
All three assets 200. No PHP notice, no console error. Page two slices in the
browser with no network request. Search filters. The status labels colour
correctly. The delete button announces itself as "Delete" / "Löschen".
2,184 assertions.
[1.51.0] - 2026-08-27
Round twenty-three asked whether twenty-three rounds of edits still hold
together, and pointed five auditors at the demo — the shop window, audited for
its claims in 1.44.0 but never for its markup.
Fixed — nine of eleven demo sections were under the sidebar
One extra </div> closed .gk-with-sidebar right after the Form section.
Everything after it — Cards, Layout, Navigation, Search, Feedback, UI,
Examples, Tooltip, Changelog — was a child of <body>, so the left 260px of
each sat beneath the fixed sidebar. Not merely clipped: elementFromPoint(150, 300) returned a sidebar link, so that strip swallowed clicks meant for the
demo's own controls.
CHANGELOG 1.36.0 describes this exact failure as the thing the wrapper
prevents. The demo has had it ever since.
It survived because counting tags cannot see it: the stray close consumes the
wrapper, which demotes the intended closer at the end of the file to a trailing
stray, and browsers discard that silently. The div count nets to zero. Only an
ancestry assertion catches it, and there is one now.
Fixed — the demo at 390px
-
Three fixed-column grids gave each child 101px on a phone — the "Sizes"
sample rendered three tables at 101px each, labels printing over values. They
wrap now. -
An invisible tooltip widened the page by 58px.
[data-gk-tooltip]::afterisposition: absolutewithwhite-space: nowrap
andopacity: 0— laid out while invisible, so a long one pushes the
document wider with nothing visible anywhere near the edge. That is why
several mobile passes went straight past it. The left and right variants
reach out withleft/right: 100%and added their own. On a narrow screen
the text now wraps under a cap and the side variants move above their
element; the desktop look is untouched.The first attempt at that fix did nothing, because the override sat before
the rules it meant to beat and lost on source order. So did the first version
of the test for it, which compared the block against itself — the block
contains the very selector it was searching for.
Added
- A test that every
[data-section]in the rendered demo has.gk-with-sidebar
as an ancestor. Checked against the stray tag put back: nine failures. - A test that the narrow-screen tooltip cap exists and comes after the rules
it overrides. - A Composer install, end to end. The README has offered
composer require mmollay/gridkitfrom the start and nobody had ever run one.
The test builds the archive Packagist would ship, lays it out as Composer
would, generates the autoloader Composer would generate, and checks that the
documented asset path resolves to the file that is really there — the one
thing a plain clone never exercises. It passes.
2,174 assertions, on three interpreters.