ezsystems/hybrid-platform-ui-core-components
Core custom elements for eZ Hybrid Platform UI, including ez-platform-ui-app, ez-navigation-hub, and ez-toolbar. Also provides an eZ.TabsMixin class expression mixin and base CSS for tab markup, enabling components to recognize and switch tabs automatically.
This is not a Composer/Laravel package — it is a front-end (Web Components / Polymer-era) asset bundle that ships three custom elements and one mixin used by the eZ Platform / Ibexa "Hybrid Platform UI" admin shell:
| Export | Purpose |
|---|---|
<ez-platform-ui-app> |
Top-level app shell element (routing/layout host) |
<ez-navigation-hub> |
Left-hand navigation / hub menu |
<ez-toolbar> |
Contextual toolbar (action buttons) |
eZ.TabsMixin(...) |
Class expression mixin that turns a specific <div class="ez-tabs"> markup block into working tabs |
First steps, in order:
ezplatform-admin-ui-assets / ibexa/...-admin-ui). Installing it standalone into a Laravel app is a deliberate, unusual choice. Since the repo is archived with 0 dependents and 0 stars, treat it as vendored legacy code, not a maintained dependency.package.json (or vendor the dist//source tree under resources/vendor/) — the registry entry may vanish or be a stale mirror.package.json (main/module), the element definitions in the repo root (e.g. ez-platform-ui-app.html, ez-navigation-hub.html, ez-toolbar.html) and mixins/ez-tabs.html. The README is the only doc — there is no API reference, so the element source is the documentation.<link rel="import" href="...">, you are on Polymer 2 / HTML Imports; if they use import { PolymerElement } from '@polymer/polymer/polymer-element.js', you're on ESM. The README snippet (static get is(), customElements.define) is the Polymer 2 syntax.First real use case: drop the tabs mixin into an existing Blade-rendered admin screen. It is the lowest-risk, highest-value piece — it needs no app-shell wiring:
<link rel="import" href="/vendor/hybrid-platform-ui-core-components/mixins/ez-tabs.html">
<script>
class ProductTabs extends eZ.TabsMixin(Polymer.Element) {
static get is() { return 'product-tabs'; }
}
window.customElements.define(ProductTabs.is, ProductTabs);
</script>
with exactly this markup (class names are contractual):
<product-tabs>
<div class="ez-tabs">
<ul class="ez-tabs-list">
<li class="ez-tabs-label is-tab-selected"><a href="#details">Details</a></li>
<li class="ez-tabs-label"><a href="#history">History</a></li>
</ul>
<div class="ez-tabs-panels">
<div class="ez-tabs-panel is-tab-selected" id="details">…</div>
<div class="ez-tabs-panel" id="history">…</div>
</div>
</div>
</product-tabs>
1. Asset delivery from a Laravel app. Serve the component tree statically rather than trying to bundle it with Vite. The HTML Imports-style dependencies resolve via <link rel="import"> and break under Vite's ESM rewriting:
// routes/web.php — or just copy into public/vendor/
Route::get('/vendor/hpui/{path}', fn ($path) =>
response()->file(base_path("vendor/hybrid-platform-ui-core-components/{$path}"))
)->where('path', '.*');
Then reference /vendor/hpui/... from a Blade partial that you @include once per layout.
2. Shell composition. Build the admin chrome as a small set of nested elements, letting <ez-platform-ui-app> own the layout and the other two fill its slots:
<ez-platform-ui-app>
<ez-navigation-hub slot="navigation" items="[[navigationItems]]"></ez-navigation-hub>
<ez-toolbar slot="toolbar" actions="[[toolbarActions]]"></ez-toolbar>
<main>…routed content…</main>
</ez-platform-ui-app>
Pass data down as properties (arrays of {id, label, href}), not as DOM children — the elements expect data, and hand-built children fight the internal templating.
3. Tabs as a progressive-enhancement layer. Because ez-tabs.html also injects base CSS for the tab visuals, you can render the markup server-side from Blade and let the mixin upgrade it. Keep the is-tab-selected class on both the label <li> and its panel <div> — the mixin toggles both in lockstep and the CSS keys off each independently.
4. State + routing integration. Tab anchors (href="#tab1") are plain in-page hashes. If your app uses hash routing (or a front-end router), scope tabs to elements that are not under the router root, or intercept the hash and call the element's selection API instead of relying on the anchor. Similarly, let the Laravel side own the top-level URL and use these elements purely for in-page UI state.
5. Theming. The core components expose their look through CSS custom properties / ::shadow-era styling hooks. In practice: set custom properties on a wrapping element (--ez-...) rather than trying to reach into shadow DOM from a stylesheet. Check each element's <style> block for the property names — they are undocumented outside the source.
6. Testing. These elements need a real DOM. Use @webcomponents/webcomponentsjs loaders plus a headless browser (Karma/wtr or Playwright) — jsdom will not run HTML Imports or Polymer's template machinery. Keep any new component logic in a plain JS module so the mixin usage itself stays a two-line shim.
<link rel="import">, it only runs with the webcomponentsjs HTML Imports polyfill and a legacy-era browser target — a hard blocker for anything modern. Verify the module format before planning the integration.webcomponents-loader.js (or the specific custom-elements-es5-adapter.js) must load before any component script, and the adapter must not be loaded on native-ESM browsers. Loading the ES5 adapter unconditionally is the classic "custom element already defined" crash.customElements.define runs once per page. Double-including a Blade partial that defines the same element throws NotSupportedError: ... already been used. Guard the include, or wrap registration in if (!customElements.get('my-component')).eZ is a global. The mixin hangs off window.eZ. In a module-scoped build you must ensure ez-tabs.html executes before your class expression, or you get eZ is not defined at class-definition time (not at call time — so it fails on page load, not on interaction).ez-tabs, ez-tabs-list, ez-tabs-label, is-tab-selected, ez-tabs-panels, ez-tabs-panel are all hard-coded in the mixin's selectors. Renaming, adding a wrapper <div>, or letting a Blade component reorder nodes silently breaks selection. Add a smoke test that asserts the first panel is visible after upgrade.id="details" collide; the second set will not switch. Namespace your IDs (product-details, order-details).slot-based content (light DOM), which is stylable normally. If the element looks unstyled, you probably imported the element but not its base CSS module.<ez-navigation-hub> or patching its template couples you to an unmaintained tree with no changelog — prefer wrapping.How can I help you explore Laravel packages today?