Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Blade Laravel Package

lyra-ds/blade

Lyra DS Blade offers reusable Laravel Blade components for building consistent, accessible interfaces with the Lyra design system. Compose layouts, forms, and UI elements faster without repeating markup.

View on GitHub
Deep Wiki
Context7

Getting Started

lyra-ds/blade is a Blade component library (design system) published as <x-lyra::*> components. It is a front-end package: the PHP side only renders accessible markup, while behaviour lives in Alpine and presentation in CSS. That split drives everything about day-to-day use.

  1. Install the PHP package and the two JS/CSS peers it pins against:

    composer require lyra-ds/blade
    npm install @lyra-ds/alpine@^1.1 @lyra-ds/styles@^1.1
    
  2. Load the peer assets once in your layout (through your Vite entrypoint), before Alpine starts and before any Livewire/Alpine-bound component is hydrated. A component rendered without Alpine 1.1 will fall back to static markup — and for some components (tabs, toast-stack, workspace-switcher, file-upload) that means reduced or missing behaviour.

  3. Where to look first: the publishable views under resources/views/components (that is the actual component API — props, slots, @props defaults), then whatever the service provider registers (config file, Blade directives, asset publishing). Read CHANGELOG v1.0.0 alongside the source, because v1.0.0 changed markup contracts that tests and CSS depend on.

  4. First use case: a settings page. Drop <x-lyra::toast-stack /> once in the layout, build the page with <x-lyra::tabs> + panels, and use <x-lyra::otp-input> on a verification screen. That single page exercises the three areas that changed most in 1.0.0 (toast regions, tabs progressive enhancement, owned-x-data policy), so it doubles as an upgrade smoke test.

Implementation Patterns

Layout-level singletons. toast-stack is designed to exist once per layout. It now renders two persistent live regions ([data-lyra-toast-region="polite"] and [data-lyra-toast-region="assertive"]) rather than a single live root, so mount it in the app shell, not inside page views — re-rendering it per page breaks the "persistent region" assumption and screen-reader announcement continuity. Route danger/error toasts to the assertive region by dispatching the corresponding event/getter (assertiveToasts), informational ones to polite.

Owned-root x-data. v1.0.0 formalises this as policy: Alpine-bound components own their root x-data and do not forward a consumer one. The pattern is wrapping, not parameterising:

<div x-data="{ open: false }">
    <x-lyra::toast-stack />
</div>

Keep the wrapper outside the component. If your CSS relies on the component being a direct child of a container, add a wrapper class rather than nesting deeper. The same wrapping rule applies to tabs, workspace-switcher, dialog/drawer, file-upload and anything else with an Alpine root.

Navigation = real links. Passing href to sidebar-group / app-sidebar / bottom-nav / breadcrumb items now yields <a> instead of <button>. This is the biggest ergonomic win in the release: use route() directly and add wire:navigate for SPA-style transitions.

<x-lyra::app-sidebar :items="[
    ['label' => 'Dashboard', 'href' => route('dashboard'), 'icon' => 'home'],
    ['label' => 'Settings',  'href' => route('settings'),  'icon' => 'cog'],
]" />

Items without href still render buttons — so a single nav array can mix links and action buttons, and that is the intended way to keep "open command palette" style entries in the sidebar.

Controlled components over imperative ones. file-upload now uses the controlled upload lifecycle from Alpine 1.x, and overlays (dialog, drawer, bottom-sheet, command-palette, date-picker) restore focus on close. Treat these as state-driven: hold the source of truth in Livewire (wire:model on the upload, a $showDialog boolean), and let the component drive focus and lifecycle. Focus restoration means you no longer hand-roll "return focus to the trigger" in x-init/$watch — but it also means the trigger element must still exist in the DOM when the overlay closes; do not @if the trigger away.

Events as the integration seam. file-manager dispatches open and navigate events; workspace-switcher button items and the create action dispatch lyra:change with { value: dataId }; tag / toast / action-bar now have interactive remove/close/clear actions that surface as events. Bridge them into Livewire with x-on:lyra:change="$wire.set('workspace', $event.detail.value)" or #[On(...)] listeners, and keep the component's own state (open folder, active workspace, cleared filter set) in your server component so a re-render does not desync.

Dropdown and tooltip plumbing. Dropdown items now emit stable ids, the trigger can be rendered button-styled, and tooltip descriptions are focusable. In practice: you can deep-link or test dropdown items by id instead of by index, you get design-system typography on triggers for free by passing the button styling prop rather than hand-rolling classes, and keyboard users now reach tooltip content — so keep tooltip copy short and avoid putting interactive controls inside a tooltip description.

New components worth adopting directly. otp-input (2FA / email verification flows), calendar-view (scheduling, availability displays), create-workspace-dialog (multi-tenant onboarding paired with workspace-switcher), data-table + date-range-picker (both received accessibility work — safest baseline for admin screens).

Testing pattern. Assert on data attributes and classes, never on roles that Alpine sets at runtime:

$response->assertSee('data-lyra-toast-region="assertive"', false);
$response->assertSee('data-lyra-tabs-enhanced', false);
// not: assertSee('role="status"'), assertSee('aria-selected="true"')

For post-hydration state (aria-selected, role=tab, tooltip visibility), use a browser/Dusk test, not a PHPUnit HTTP assertion.

Gotchas and Tips

This is a breaking release — budget a migration pass, not a version bump. Concretely:

  • toast-stack: [role=status] is gone from both the stack root and toast rows. Migrate selectors to [data-lyra-toast-region] .lyra-toast (or [data-lyra-toast-region="assertive"] .lyra-toast for danger). Anything that asserted or styled [role=status] will silently match nothing — a green test suite can hide this if the assertion was assertDontSee.
  • tabs: the public id now lands on the root div[data-lyra-tabs], not the tablist. Tab/panel ids derive from it (<id>-tab-N, <id>-panel-N). Targeted #your-id as the tablist? Use #your-id [data-lyra-tabs-enhanced] or .lyra-tabs. Panels are now section[data-value] with an h2 — if your styles targeted div[role=tabpanel] or assumed a heading-free panel, re-check typography and spacing.
  • navigation: CSS/JS targeting button.lyra-sbgroup__item or button.lyra-bottomnav__item breaks for link items. Select on the class alone, or :is(a, button). Breadcrumb non-final items without href now render <span>, not <a href="#"> — delete any a[href="#"] "current page" hacks and review global a resets (colour, underline) leaking onto nav items.
  • workspace-switcher: listbox → disclosure. [role="listbox"], [role="option"], [aria-selected] and #*-listbox are gone; query .lyra-wssw__pop, .lyra-wssw__item[data-id], and [aria-current="true"] for the active workspace. Anchor items now navigate natively and no longer emit lyra:change — if you relied on that event to persist the workspace switch server-side, move the persistence into the route/controller (or a wire:navigate visit), and keep the event path only for button items and the create action.
  • shell: with sidebar-as="div", aria-label is no longer emitted on the sidebar container — don't assert on it; if you need a landmark, use a semantic sidebar element instead of div. skipLink now makes <main> focusable (tabindex="-1") with a generated/provided id and injects the skip link as the shell's first child — check that your layout's first-child assumptions (sticky headers, flex order) still hold, and pass an explicit id if you anchor-link to #main elsewhere.

Version pinning is not optional. Tabs, toast-stack, workspace-switcher, file-upload and the overlay focus work all require @lyra-ds/alpine ^1.1 and @lyra-ds/styles ^1.1. A stale Alpine gives you the old behaviour against new markup — usually a component that renders but never enhances. Check the lockfile first when "the component just doesn't do anything".

Progressive enhancement is a feature, not a bug. The tablist starts hidden with no static role, aria-selected, tabindex or lyra-tab--active. That is deliberate: a nav[data-lyra-tabs-fallback] of plain anchors covers the no-JS case. Do not "fix" the hidden tablist by removing hidden — you will double render.

x-data passthrough will fail loudly or quietly. <x-lyra::toast-stack x-data="..."> is unsupported (the stack owns its x-data). Extra attributes may be silently dropped rather than erroring, so the symptom is "my Alpine state isn't initialising" rather than a thrown exception. Wrap instead.

Container max keywords now map to pixel widths and validation parity changed — visual regression snapshots and any test asserting on inline style strings will need updating. Related: brand mark is now optional and falls back to aria-label for the initial; if you were passing a placeholder logo, you can drop it.

Tooltips and dropdowns changed shape. Because the tooltip description is focusable, keyboard traversal now reveals content that used to be hover-only — if you have browser tests asserting "tooltip not visible after tab", they will fail, and if you have long/rich tooltip content it now needs to be keyboard-tolerable. Dropdown items gained ids, so index-based selectors (li:nth-child(2)) are now a smell; prefer the emitted id or [data-value].

Debugging checklist for "component renders but doesn't work": (1) Alpine 1.1 loaded and started before the markup, (2) @lyra-ds/styles 1.1 present, (3) no consumer x-data on a component root, (4) selector in your test matches the new data attributes, (5) php artisan view:clear after publishing overrides — published views from 0.10.x will happily keep rendering the old markup.

Extension points. Publish and override the component views for structural changes (heading levels in tab panels, extra data-* hooks), prefer slots over new props, and use the dispatched events as the seam for app logic rather than forking markup. If you must change behaviour, wrap the component in your own Blade component that composes <x-lyra::*> — that survives minor upgrades far better than an overridden view, and keeps the owned-x-data contract intact.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect