hydrakit / admin
A composable admin backend for Hydra apps: declare a module, get routes and htmx screens.
Requires
- php: >=8.2
- hydrakit/auth: ^0.24
- hydrakit/authorization: ^0.24
- hydrakit/cache: ^0.24
- hydrakit/console: ^0.24
- hydrakit/core: ^0.24
- hydrakit/database: ^0.24
- hydrakit/filesystem: ^0.24
- hydrakit/http: ^0.24
- hydrakit/validation: ^0.24
- hydrakit/view: ^0.24
- psr/clock: ^1.0
- psr/event-dispatcher: ^1.0
- psr/http-message: ^2.0
- psr/log: ^3.0
Requires (Dev)
- hydrakit/broadcast: ^0.24
- hydrakit/event: ^0.24
- nyholm/psr7: ^1.8
- phpunit/phpunit: ^11.0
Suggests
- hydrakit/broadcast: Makes list screens and cards live: admin writes are published, and pages refetch what changed.
Provides
None
Conflicts
None
Replaces
None
- dev-main / 0.24.x-dev
- v0.24.0
- v0.23.0
- v0.22.1
- v0.22.0
- v0.21.0
- v0.20.0
- v0.19.0
- v0.18.0
- v0.17.0
- v0.16.0
- v0.15.0
- v0.14.0
- v0.13.0
- v0.12.0
- v0.11.0
- v0.10.2
- v0.10.1
- v0.10.0
- v0.9.17
- v0.9.16
- v0.9.15
- v0.9.14
- v0.9.13
- v0.9.12
- v0.9.11
- v0.9.10
- v0.9.9
- v0.9.8
- v0.9.7
- v0.9.6
- v0.9.5
- v0.9.4
- v0.9.3
- v0.9.2
- v0.9.1
- v0.9.0
- v0.8.1
- v0.8.0
- v0.7.2
- v0.7.1
- v0.7.0
- v0.6.3
- v0.6.2
- v0.6.1
- v0.6.0
- v0.5.8
- v0.5.7
- v0.5.6
- v0.5.5
- v0.5.4
- v0.5.3
- v0.5.2
- v0.5.1
- v0.5.0
- v0.4.1
- v0.4.0
- v0.3.4
- v0.3.3
- v0.3.2
- v0.3.1
- v0.3.0
- v0.2.1
This package is auto-updated.
Last update: 2026-10-02 15:43:41 UTC
README
Part of the Hydra PHP framework. Documentation: hydra.williamhleucka.com/docs.
Read-only mirror.
hydrakit/adminis developed in hydra-foundation/hydra underpackages/admin, and republished here on every push. A commit pushed to this repository is overwritten by the next one; issues are disabled for that reason, and a pull request opened here cannot be merged. Both belong upstream.
A composable admin backend for Hydra apps. Modules declare what they are (fields, a source, screens, etc) and the package compiles that into ordinary routes, a gate-filtered sidebar, and htmx-driven screens.
Views
The package ships the templates it renders, in views/. Hand that directory to
the view as a fallback and the admin renders without any templates of your own:
new PhpView( __DIR__ . '/views', $csrf, fallbacks: [AdminServiceProvider::views()], );
To change one of them, put a file of the same name in your own views
directory. views/admin/partials/table.php replaces the shipped table, and
everything else still comes from the package. Nothing is registered or published for that to
work; the file simply wins.
Two things the package deliberately does not ship, because they belong to the site rather than to the admin:
-
layouts/admin, the seam at which the admin attaches to your own page chrome. It rendersadmin/partials/shellinside whatever layout your site already has, and that is all it does:<?php $this->extends('layouts/base') ?> <?= $this->partial('admin/partials/shell', ['screen' => $screen, 'content' => $this->section('content')]) ?>
-
The templates your own
PageScreens name, such as a dashboard.
Swap depths
htmx swaps against two ids, both declared by shipped templates and both read
back by Renderer: admin-frame (the whole screen, what a sidebar link
replaces) and admin-body (just the table, what a filter or a page link
replaces). They are literal strings in the markup on purpose, because a
stylesheet and a template are what a designer edits, not a PHP constant, and
ShippedViewsTest fails if a target, a declaration, and Renderer ever stop
agreeing.
Tabs
A module declared ->tabOf('jobs') gives up its own sidebar entry and sits
behind the Jobs entry instead, with a strip of tabs under the heading of every
screen in the family. The frame draws the strip from admin/partials/tabs,
which takes a list of ['label' => …, 'url' => …, 'active' => …] and a
label for screen readers. A page screen whose categories are its own, the
way the skeleton's Settings is, renders the same partial with its own list,
so there is one look for tabs across the top of a module.
The stylesheet and the script
assets/admin.css and assets/admin.js are the package's, not yours. They are
served at {prefix}/assets/stylesheet and {prefix}/assets/script rather than
copied into your public/ directory, because a copy has to be re-made on every
upgrade and the one nobody re-made looks exactly like the one nobody needed:
the admin renders, and only the field type the new version added is unstyled.
Link them from your layouts/admin:
<link rel="stylesheet" href="/admin/assets/stylesheet" /> <script src="/admin/assets/script" defer></script>
The URLs carry no extension deliberately. A web server's static-file rules are
written against extensions, and a .css under a path with no file behind it is
a 404 from the server before PHP is reached — nginx's stock
location ~* \.(css|js)$ does precisely that. Extensionless, they fall through
to the front controller everywhere, which is the point: no per-application
server configuration.
Both are served with an ETag and must-revalidate, so a browser spends one
conditional request per page load and gets a 304. The route is registered ahead
of the module routes, and without the admin's middleware — a sign-in screen has
to be styled before anyone has signed in.
To override a rule, link a sheet of your own after this one. To replace the sheet entirely, do not link this one at all.
The theme contract
admin.css names no colour. Every one comes from a custom property your
application defines, which is what lets the sheet ship here without bringing a
palette with it, and what lets your themes reach into the admin. Define these
on :root, or the admin renders with whatever the browser makes of an empty
value:
--accent--accent-hover--accent-wash--danger--danger-line--danger-wash--ease--font-display--font-mono--font-text--hover-tint--ink--ink-strong--line--line-strong--muted--ok--on-accent--paper--r-1--r-2--r-3--s-1--s-2--s-3--s-4--s-5--s-6--s-7--scrim--sunk--surface--t-base--t-data--t-display--t-h1--t-h3--t-lead--t-micro--t-small--t-stat--warn
ShippedAssetsTest holds both halves: the sheet may name no colour, and it may
not ask for a token this list omits.