codeat3/blade-fontaudio
A Laravel Blade component package for Font Audio, giving you simple Blaze-style tags to drop crisp audio and synth iconography into your views. Install via Composer, then use the provided components anywhere in your Blade templates.
Install with Composer; there is no npm step, no publish step, and no migration.
composer require codeat3/blade-fontaudio
Laravel's package auto-discovery registers the service provider, so the <x-fontaudio-* /> namespace is available on the next request. If your app has composer.lock committed and CI runs with --no-dev, verify the provider shows up via:
php artisan about # look for the provider under "Drivers"/"Packages" in newer Laravel
php artisan view:clear # cheap insurance after any install
Where to look first — the SVG directory. The component name is the SVG filename. Before writing any markup, list what you actually have instead of guessing:
ls vendor/codeat3/blade-fontaudio/resources/svg | head -50
That directory listing is your API reference. Anything you find there becomes <x-fontaudio-{filename-without-.svg} />.
First use case — transport controls in a media row. The most common day-one need is play/pause/stop on an audio element:
<button type="button" class="inline-flex items-center gap-2 rounded-md px-3 py-2 bg-slate-900 text-white">
<x-fontaudio-play class="w-5 h-5" />
<span>Play</span>
</button>
Then wire behaviour with Alpine or Livewire and swap the icon by state. Two minutes from composer require to a working button — that is the whole point of the package.
The rendered output is a single inline <svg> using fill="currentColor", so it inherits text colour and you control size with utility classes. Both work:
<x-fontaudio-volume class="w-6 h-6 text-slate-500" />
<x-fontaudio-volume class="size-4 text-emerald-600 dark:text-emerald-400" />
Passing class merges with whatever default class is configured in config/blade-icons.php, so avoid duplicating a global default in every call site.
Do not scatter <button><x-fontaudio-… /></button> markup across views. Build one accessible button and use it everywhere:
{{-- resources/views/components/audio-icon-button.blade.php --}}
@props(['icon', 'label'])
<button
type="button"
{{ $attributes->merge(['class' => 'inline-flex items-center justify-center rounded-md p-2 hover:bg-slate-100']) }}
>
<x-dynamic-component :component="'fontaudio-' . $icon" class="w-5 h-5" aria-hidden="true" />
<span class="sr-only">{{ $label }}</span>
</button>
Usage stays readable and accessibility is solved once:
<x-audio-icon-button icon="play" label="Play preview" wire:click="play" />
<x-audio-icon-button icon="pause" label="Pause" />
Anywhere the icon name comes from data (a track's state, a config array, a DB column), use x-dynamic-component with the full component name including the fontaudio- prefix:
@php
$transport = match ($track->state) {
'playing' => 'pause',
'paused' => 'play',
'error' => 'stop',
default => 'play',
};
@endphp
<x-dynamic-component :component="'fontaudio-' . $transport" class="w-5 h-5" />
A match or a small enum mapping is worth writing down: it centralises the icon vocabulary so a future design change (or an exit from this package) is a one-file edit rather than a grep across templates.
For a player bar or a recorder control strip, wrap the whole cluster in an anonymous Blade component and take the state as a prop. That keeps the FontAudio tags in exactly one file and makes the rest of the app depend on your component, not the vendor package:
{{-- resources/views/components/transport-controls.blade.php --}}
@props(['state' => 'paused'])
<div {{ $attributes->merge(['class' => 'flex items-center gap-1']) }}>
<x-audio-icon-button icon="skip-back" label="Previous" />
<x-audio-icon-button :icon="$state === 'playing' ? 'pause' : 'play'" label="Play/Pause" />
<x-audio-icon-button icon="skip-forward" label="Next" />
<x-audio-icon-button icon="loop" label="Toggle loop" />
</div>
This wrapper is also your migration seam — swapping to inline SVGs or a webfont later touches one template.
Server-rendered icons re-render fine under Livewire as long as the icon name is derived from server state. Things to know:
:class), the SVG stays the same — you are only restyling.x-show). Rendering both is cheap for two-state controls and avoids a round trip:<div x-data="{ playing: false }">
<x-fontaudio-play class="w-5 h-5" x-show="!playing" />
<x-fontaudio-pause class="w-5 h-5" x-show="playing" x-cloak />
</div>
Because it is plain server-side markup, assertions are trivial and worth adding once as a smoke test for framework upgrades:
public function test_player_renders_transport_icons(): void
{
$this->get('/tracks/1')
->assertOk()
->assertSee('fontaudio', false)
->assertSee('aria-hidden="true"', false);
}
That single test is what makes a Laravel major bump fail loudly instead of silently dropping icons from production.
Component names are exact filenames. FontAudio uses kebab-case names, and there is no fuzzy matching — <x-fontaudio-skipforward /> will throw Unable to locate a class or view for component while <x-fontaudio-skip-forward /> works. Always confirm against the resources/svg listing rather than extrapolating from a docs page. A misspelled component is a hard Blade exception at render time, not a silent blank.
x-dynamic-component needs the full prefixed name. :component="$icon" alone resolves to a root-level component and fails. Use 'fontaudio-' . $icon. If you store icon names in a DB or config, store them bare (play) and add the prefix at render, so the vendor prefix appears in exactly one place.
Clear caches on deploy. Blade compiles templates and Blade Icons can be configured to cache its component manifest. A deploy that ships new templates without php artisan view:clear is the classic "icons render locally, 500 in production" bug. Put php artisan view:clear (and php artisan icons:clear / icons:cache if your blade-icons config enables caching) into the deploy script.
Accessibility is not solved at the icon layer. The SVG carries no meaning. Every icon-only control needs either aria-hidden="true" on the SVG plus sr-only text inside the button, or an aria-label on the interactive element — never both text and aria-label redundantly. Icons that are purely decorative next to a text label should be aria-hidden="true" so screen readers do not announce stray characters.
Attribute passthrough works, but verify before relying on it. Standard HTML attributes and wire:/x- directives pass through to the <svg>. Non-standard SVG attributes may be dropped depending on how the icon view merges attributes — if you need something unusual (a custom stroke-width or a viewBox override), test it in isolation first.
Missing-icon behaviour is configurable, not automatic. config/blade-icons.php supports a fallback component. In development you want a loud failure; in production a fallback that renders a placeholder beats a 500 on a listing page that renders hundreds of icons from data. Decide deliberately rather than accepting the default.
Watch the HTML weight on dense pages. The whole icon set lives in vendor/ (no tree-shaking at the package level), but only icons you actually render hit the response — each is an inlined <svg>. On a page rendering hundreds of icons (a long tracklist with per-row transport controls, a waveform editor), the markup adds up. Measure a real page before optimising; if it matters, hoist repeated icons into a cached partial or a sprite.
Prefix collision with sibling blade-* icon packages. Running this alongside another codeat3 icon package is fine only if the prefixes differ. Confirm fontaudio does not clash with anything already registered, and check config/blade-icons.php class defaults do not impose a size/colour you did not intend for the audio icons specifically.
Keep the scope audio-only. This is an audio-domain vocabulary, not a design system. It is tempting to start using it for generic UI, which produces an inconsistent icon set fast. Put a note in your PR template or a comment in the wrapper component reminding the team.
Pin the version and know the exit ramp. Zero stars, zero dependents, single maintainer. Use an explicit version constraint rather than a floating ^ range, commit the lockfile, and keep the wrapper component from the Implementation Patterns section so migrating to vendored SVGs or the upstream webfont is a day of work confined to one file.
Verify the bundled SVG licence before commercial shipping. The package is MIT, but the release notes do not state the licence of the underlying FontAudio icon set or any attribution obligation. Cheap to check now, expensive to discover at launch.
Upgrade smoke test. Release 1.8.0 is compatibility-only (Laravel 13 support, no API change). Treat the compatibility claim as a hypothesis: after any Laravel or PHP bump, run your one render test and spot-check a page with a dynamic icon before merging.
How can I help you explore Laravel packages today?