knuckleswtf/pastel
Pastel is a lightweight PHP library for adding color and styling to console output. It offers a clean, chainable API for colors, backgrounds, and text formatting, helping you build readable, attractive CLI messages and Laravel commands.
composer require knuckleswtf/pastel. There is no service provider, no config file, no assets to publish. It runs in plain PHP scripts and in Artisan commands alike, which is the main reason to reach for it over Laravel's own console helpers.Knuckles\Pastel\Pastel (a thin static entry point exposing Pastel::console()), then Knuckles\Pastel\ConsoleOutput, which extends Symfony's ConsoleOutput and is where every helper actually lives. If a method isn't in ConsoleOutput, it doesn't exist — don't go looking for a facade or a config.title(), section(), text(), listing(), unorderedList(), twoColumnDetail(), success() / warning() / error(), ask() / secret() / confirm() / choice(), progressBar(), table(), newLine().$this->line() calls and give it structure. A typical rewrite:use Knuckles\Pastel\Pastel;
$console = Pastel::console();
$console->title('Importing users');
$console->section('Step 1: Reading files');
$console->listing(['users.csv', 'roles.csv']);
$console->twoColumnDetail('Rows found', '1,203');
$console->success('Import finished in 4.2s');
^1.3 snippet you find online still works verbatim. The package is archived, so treat it as a stable dependency, not something that will gain features.Pastel::console() returns a singleton ConsoleOutput. Calling it from twenty places makes the code hard to test and impossible to swap. Bind ConsoleOutput in a service provider (or add a tiny helper function) and inject it, so you can later substitute a buffered output in tests.title() → section() per phase → twoColumnDetail() for a stats/config summary → progressBar() around the main loop → success() or error() at the end. Long-running commands become scannable in CI logs instead of an undifferentiated stream.twoColumnDetail($label, $value) for aligned summaries — config dumps, per-step timings, record counts. Do not hand-pad with str_pad(); the helper handles alignment and styling together.listing([...]) for filenames and instructions, unorderedList([...]) for prose-style bullets. Both beat concatenating " - $item" strings, and they stay consistent if the styling changes later.$bar = $console->progressBar(count($rows));
$bar->start();
foreach ($rows as $row) {
// work
$bar->advance();
}
$bar->finish();
$console->newLine(); // Pastel does not add a trailing newline for you
The returned object is a plain Symfony\Component\Console\Helper\ProgressBar, so setFormat(), setMessage(), setRedrawFrequency(), and iterate() all work as documented by Symfony.
ask(), secret(), confirm(), and choice() are most valuable in standalone scripts — deploy helpers, scaffolding generators, one-off setup binaries — where Laravel's Prompts isn't loaded. Inside an Artisan command, prefer $this->components / $this->confirm() unless you specifically want Pastel's presentation.text() accepts Symfony tags: $console->text('<fg=green;options=bold>OK</>'). Don't scatter tag strings through command classes — put recurring ones in class constants or a domain subclass (see below).Artisan::call('foo') hands you a plain string with tags already rendered. If you're building a wrapper CLI, print a status line through Pastel and let the inner command write directly to the terminal, rather than trying to re-style a string that's already been formatted.ConsoleOutput and add domain helpers — summary(array $stats), step(string $label, callable $fn), rule() — then bind that class. Command classes stay free of presentation markup, and styling stays consistent as the command set grows. This is the cleanest long-term pattern with Pastel.$this->components. Both produce their own spacing and separators. Pick one per command; mixing them yields output that looks broken even when both halves are individually correct.Pastel::console() ignores the running command's flags. The singleton is bound to real stdout with normal verbosity and decoration, so -q, -v, and --no-ansi passed to the Artisan command are not respected — you'll still print colour into a pipe. Inside commands, construct a ConsoleOutput from $this->output->getVerbosity() and $this->output->isDecorated(), or route everything through $this->output and use Pastel only for formatting strings.Artisan::output()-style assertions won't capture it. Two workable approaches: (a) extract formatting into methods that return strings and test those, or (b) inject a BufferedOutput/custom stream and assert on the rendered text after stripping tags with preg_replace('/<[^>]+>/', '', $text).<fg=...> bleeds styling through every subsequent line; a mismatched close throws from Symfony's formatter. When output goes weird halfway down the screen, look for a missing </> first.fg=, bg=, options=, plus the shorthands info, comment, question, error. Undefined tags either render literally or throw depending on the Symfony version. Register custom styles once with setStyle() if you genuinely need them.twoColumnDetail(), table(), and listing() assume roughly 80–120 columns. Long labels or long values wrap and destroy the layout in CI logs and split panes. Keep left-hand labels short; truncate values yourself before passing them in.title() / success() misalign in some terminal fonts and CI viewers, and can render as garbage in log-aggregation tooling. Gate them behind an env check if the output is consumed by machines as well as humans.ask() (ask(question: ..., default: ...)) require PHP 8. On PHP 7.x, pass them positionally or you'll get a fatal error at call time.^1.3. Since the project is archived, the constraint is effectively frozen — that's fine, but skim CHANGELOG.md for the other 1.3.x patches. 1.3.7 itself is a drop-in with nothing to migrate.ConsoleOutput subclass or interface so that swapping to Laravel's $this->components (or a maintained fork) later is a one-class change rather than a sweep through every command.How can I help you explore Laravel packages today?