Looking to hire Laravel developers? Try LaraJobs

open-a11y-laravel maintained by aotr

Description
The open-a11y accessibility widget for Laravel Blade and Livewire.
Last update
2026/10/27 05:36 (dev-main)
License
Downloads
1

Comments
comments powered by Disqus

open-a11y Laravel

Latest Version on Packagist Tests License: MIT PHP 8.2+

An accessibility widget with a real engine behind it — made native to Laravel.

open-a11y ships profiles, individual tools, keyboard shortcuts, persisted preferences, and a shadow-DOM interface. This package gives that runtime a Blade component, a Livewire component, a publishable config file, and a standalone browser bundle. No Node, Vite, or front-end build is required.

<x-open-a11y::widget :config="['position' => 'bottom-right', 'brand' => ['primary' => '#0f6b4f']]" />

Requirements

  • PHP 8.2+
  • Laravel 12
  • Livewire 3 (for the Livewire component)
  • A browser with Web Components and Shadow DOM: Chrome 90+, Firefox 90+, Safari 15+, Edge 90+

The Blade component is independent of Livewire. The package requires Livewire because it ships both integrations.

Installation

composer require aotr/open-a11y-laravel

Laravel package discovery registers Aotr\OpenA11y\OpenA11yServiceProvider automatically.

Publish the browser bundle:

php artisan vendor:publish --tag=open-a11y-assets

This copies the runtime to public/vendor/open-a11y/. The Blade and Livewire components inject the <script> tags once per page, so you do not need to add them to your layout.

Optionally publish the config file:

php artisan vendor:publish --tag=open-a11y-config

Set OPEN_A11Y_ASSET_URL (or open-a11y.assets.url) when serving the bundle from a CDN. The published public/vendor/open-a11y directory must be web accessible when using the default configuration.

About the JavaScript core

The framework-agnostic runtime is available on npm as @aotr/open-a11y:

npm install @aotr/open-a11y

This package ships the standalone IIFE browser bundle in resources/dist/open-a11y.js, so Laravel applications can use the widget without a JavaScript toolchain. If your app already bundles the npm core, point open-a11y.assets.url at your built asset (or publish and edit the package views).

Blade

<x-open-a11y::widget
    :config="[
        'position' => 'bottom-right',
        'side' => 'right',
        'offset' => [24, 24],
        'blur' => true,
        'buttonSize' => 56,
        'showButton' => true,
        'lang' => 'en',
        'brand' => [
            'name' => 'Acme',
            'url' => 'https://acme.example',
            'footerText' => 'Accessibility by Acme',
            'primary' => '#0f6b4f',
            'primaryDark' => '#0a4a37',
            'accent' => '#d4a13a',
        ],
        'labels' => [
            'widget.menuTitle' => 'Accessibility options',
        ],
    ]"
/>

The component merges your array over the package defaults in config/open-a11y.php, renders a hidden mount element with the JSON config, and includes the two browser scripts once.

Pass a stable widget-key when you want to target the instance from your own JavaScript:

<x-open-a11y::widget :config="$config" widget-key="site-widget" />

Livewire

<livewire:open-a11y::widget
    position="bottom-right"
    side="right"
    :offset-x="24"
    :offset-y="24"
    :blur="true"
    :button-size="56"
    :show-button="true"
    primary="#0f6b4f"
    primary-dark="#0a4a37"
    accent="#d4a13a"
    footer-name="Acme"
    footer-url="https://acme.example"
    footer-text="Accessibility by Acme"
    menu-title="Accessibility options"
    language="en"
/>

Every property is reactive. When a property changes, the component dispatches a browser event with the new config:

window.addEventListener('open-a11y:update', (event) => {
    // event.detail = { key: 'open-a11y-…', config: { … } }
});

The package's auto-init script listens for that event and remounts the widget. The underlying engine is shared across mounts, so the visitor's selected tools, contrast modes, and language stay in localStorage — a color change does not reset their preferences.

Bind properties from a parent Livewire component or update them on the component itself; both paths flow through the same event.

Customization

Blade passes the runtime config keys directly. Livewire exposes the most useful knobs as typed public properties.

Livewire property Blade config key Type Default
position position bottom-right, bottom-left, top-right, top-left bottom-right
side side right, left right
offsetX, offsetY offset [int, int] [20, 20]
blur blur bool false
buttonSize buttonSize int (px) 56
showButton showButton bool true
primary brand.primary CSS color #0f6b4f
primaryDark brand.primaryDark CSS color #0a4a37
accent brand.accent CSS color #d4a13a
footerName brand.name string ''
footerUrl brand.url string ''
footerText brand.footerText string ''
menuTitle labels.widget.menuTitle string Accessibility options
language lang en, hi en
widgetKey widget-key attribute string generated

An empty footerText hides the footer. When it is omitted and brand.name is set, the runtime falls back to Accessibility by {name}.

Package-level defaults live in config/open-a11y.php:

return [
    'position' => 'bottom-right',
    'side' => 'right',
    'offset' => [20, 20],
    'blur' => false,
    'button_size' => 56,
    'show_button' => true,
    'language' => 'en',
    'menu_title' => 'Accessibility options',
    'brand' => [
        'name' => null,
        'url' => null,
        'footer_text' => null,
        'primary' => '#0f6b4f',
        'primary_dark' => '#0a4a37',
        'accent' => '#d4a13a',
    ],
    'assets' => [
        'path' => 'vendor/open-a11y',
        'url' => null, // e.g. https://cdn.example.com/a11y
    ],
];

Need a custom UI string that is not exposed as a Livewire property? Use the Blade component and pass it through labels:

<x-open-a11y::widget :config="[
    'labels' => [
        'widget.menuTitle' => 'Reading options',
        'widget.reset' => 'Clear all settings',
    ],
]" />

Demo

The repository includes a Testbench workbench with two polished demos:

  • /blade — the Blade component with a plain-JavaScript customizer
  • /livewire — the Livewire component with server-reactive controls
composer install
composer demo

Then open http://127.0.0.1:4179/blade or http://127.0.0.1:4179/livewire.

The demo assets are served from the package's resources/dist directory so a fresh clone works without a publish step.

Testing

composer test

The suite uses Orchestra Testbench and PHPUnit. It covers:

  • Blade component rendering, JSON config serialization, and attribute merging
  • Livewire initial props, browser event dispatch, and language updates
  • Service provider view registration and publish tags
  • Both demo routes and the asset routes
  • Config defaults and override precedence

Publishing

Tag Copies
open-a11y-config config/open-a11y.php → config/open-a11y.php
open-a11y-views resources/views → resources/views/vendor/open-a11y
open-a11y-assets resources/dist → public/vendor/open-a11y
open-a11y all of the above
php artisan vendor:publish --tag=open-a11y

Use --force to overwrite previously published files.

How it fits together

<x-open-a11y::widget>            <livewire:open-a11y::widget>
        │                                  │
        └──────────────┬───────────────────┘
                       ▼
          hidden [data-open-a11y] element
          with JSON config attribute
                       │
                       ▼
          resources/dist/open-a11y-laravel.js
          reads config · owns shared engine
          persists to localStorage · remounts
                       │
                       ▼
          resources/dist/open-a11y.js (IIFE)
          web components · shadow DOM · tools

The widget's shadow DOM is intentionally left alone by the package and the demo. Brand colors and copy are passed through the runtime config; the host page keeps control of its own typography, spacing, and layout.

The upstream runtime maintains one active floating widget per page. Render one open-a11y component per page and use widget-key to target it from custom JavaScript.

Contributing

Bug reports and pull requests are welcome. See CONTRIBUTING.md for the local setup, the test command, and the review checklist.

License

open-a11y Laravel is open-source software licensed under the MIT license.