neuralglitch/font-manager
Font Manager for Laravel simplifies self-hosting typography: browse and install Google Fonts, register local font families, generate CSS and @font-face rules, and serve optimized font assets without external requests.
Start by confirming what the package can actually emit before writing any config — the format list is the core of 0.2.0 and it's large enough that guessing wastes time.
composer require <vendor>/<package> # if not already installed
php artisan vendor:publish --tag=<package>-config
php artisan fonts:formats # canonical list of format identifiers
php artisan fonts:format:info scss_variables
fonts:formats gives you the exact string you must put in export.formats (css_variables, css_modules, css_layer, scss_variables, scss_bootstrap, scss_mixins, esm_javascript, tailwind_config, typescript_definitions, json, design_tokens, figma_tokens, style_dictionary). fonts:format:info prints the usage instructions for one format — including where the file is expected to live and how to consume it — which is faster than reading docs/exports.md for a single lookup.
The config now has three relevant sections:
'build' => ['tool' => 'auto'], // auto | assetmapper | webpack | vite
'export' => [
'auto_detect' => true, // infer formats from the project
'formats' => ['scss_variables', 'typescript_definitions'],
'output' => [
'scss_variables' => 'resources/scss/_fonts.scss',
// unspecified formats fall back to build-tool-adjusted defaults
],
],
First real use case: you're on Vite + Tailwind and want fonts as a Tailwind theme module plus typed font names.
php artisan fonts:export --dry-run # see what would be written, where
php artisan fonts:export
Then in tailwind.config.js:
import fontFamily from './resources/js/fonts/tailwind.config.js';
export default { theme: { extend: { fontFamily } } };
That's the whole loop: publish config → pick formats → dry-run → export → import the artifact. Everything else in this release is variations on it.
Lock-and-export as one deploy step. fonts:lock now exports all configured formats after locking, so a single php artisan fonts:lock in your deploy/release script produces both the lockfile and every artifact. Keep this as your CI path — artifacts are then always derived from the lockfile rather than from whatever a developer happened to export locally.
Escape hatch for build-only pipelines. If exports are generated at build time (Vite plugin, AssetMapper importmap step), disable the side effect explicitly:
php artisan fonts:lock --no-export
Use this whenever the artifact is not meant to be committed, otherwise fonts:lock writes files that your build then overwrites, producing noise in git status.
Format partitioning. Split export.formats by consumer, not by convenience:
| Consumer | Formats |
|---|---|
| App stylesheet (plain CSS) | css_variables, css_layer |
| App stylesheet (Sass) | scss_variables, scss_mixins |
| Bootstrap project | scss_bootstrap (emits $font-family-base) |
| Tailwind project | tailwind_config |
| TS frontend | typescript_definitions, esm_javascript |
| Design team handoff | design_tokens, figma_tokens, style_dictionary |
| Generic tooling / debugging | json |
Keep design-token formats out of the app's own build config unless a designer actually consumes the file — each format is one more artifact to keep in sync and review.
Overriding one path, not all. export.output is per-format; leave the rest to defaults. This is the right lever when a framework expects a fixed path (e.g. Bootstrap SCSS must be imported before bootstrap/scss/bootstrap, so you want to know exactly where scss_bootstrap lands).
Bootstrap ordering. Generate the Bootstrap variables file, then import it ahead of Bootstrap itself:
@import 'fonts/scss_bootstrap'; // sets $font-family-base
@import 'bootstrap/scss/bootstrap';
If fonts don't appear, the import order is the first thing to check — not the generated file.
TypeScript as a build gate. Commit the typescript_definitions output (or generate it in a pre-typecheck step) so tsc fails when a font family is renamed instead of failing at runtime. This is the only format in the release that gives you a compile-time error for a font change.
Auto-detect as a default, not a policy. export.auto_detect = true is fine for a single-tool repo, but in a repo with a frontend package plus a Laravel root, pin it: build.tool and an explicit export.formats list. Auto-detection is a convenience for day one, and configuration for day thirty.
fonts:lock writing files you didn't ask for. This is the most likely surprise upgrade break: existing scripts that locked fonts now mutate the working tree. Audit every fonts:lock invocation in CI, Dockerfiles, and composer scripts, and add --no-export where you want the old behaviour.
Auto-detection picks one winner. build.tool: auto resolves to a single tool. Repos with both a Vite app and a Webpack admin bundle, or an AssetMapper project alongside a Node build, will silently get one tool's output paths and, with auto_detect, one tool's format list. Dry-run after any dependency change that could tip the detection.
Output paths are tool-dependent. Because default paths adjust based on the detected build tool, adding a build tool or changing build.tool can relocate files. Pin export.output for anything checked into VCS or referenced by a hardcoded import.
Format strings are exact. css_variables and css_modules are different formats; scss_bootstrap is not scss_variables. Run fonts:formats rather than typing from memory — a typo in export.formats surfaces as a missing file, not as a validation error, so verify with --dry-run first.
Dry-run before every config edit. fonts:export --dry-run is the fastest way to answer "did my output override take effect?" It's also the cheapest way to check what auto_detect resolved to without reading generated files.
Generated artifacts vs. generated-at-build. Decide per format. CSS/SCSS/JSON are stable and reviewable — committing them makes font changes visible in diffs. tailwind_config and esm_javascript are build inputs; committing them invites merge conflicts with the config they're imported into. typescript_definitions is worth committing purely for the tsc gate.
JSON is the debugging format. When an export looks wrong, add json temporarily and inspect the resolved font data (families, weights, styles, file URLs). It's the raw model; the other formats are views of it.
Design-token formats are an external contract. design_tokens (W3C), figma_tokens, and style_dictionary are consumed by tools you don't control. Treat their output as a public API: pin the output path, and don't let an unrelated build.tool change move the file a designer's Tokens Studio sync is pointed at.
Extension points. When a format is close but not exact, don't patch generated files — they're regenerated on the next lock. Override export.output, wrap the generated file in a hand-written shim that does the final transform, and keep your customisation in a file the package never writes. For anything structural, a custom format or a post-export step is the durable answer; the generated artifacts are build output and should be treated as read-only by humans.
How can I help you explore Laravel packages today?