Skip to content
Brok UI

Loading…

No results

CI Badge

Open source

A continuous-integration status badge (passing, failing, pending, cancelled) as a compact pill or a detailed card.

Version
v1.0.5
Stability
stable
License
MIT
Related
Release Badge
Badge
Status

Preview

passing failing pending cancelled
CI passing main 2m 22s
CI passing
main 2m 22s 30m ago
previews.components.ci-badge.default.blade.php Blade
<div class="flex flex-col items-start gap-4">
    <div class="flex flex-wrap items-center gap-2">
        <x-ui.ci-badge status="passing" />
        <x-ui.ci-badge status="failing" />
        <x-ui.ci-badge status="pending" />
        <x-ui.ci-badge status="cancelled" />
    </div>

    <x-ui.ci-badge
        status="passing"
        workflow="CI"
        branch="main"
        duration="2m 22s"
        :show-workflow="true"
        :show-branch="true"
        :show-duration="true"
    />

    <x-ui.ci-badge
        status="passing"
        layout="card"
        workflow="CI"
        branch="main"
        duration="2m 22s"
        time="30m ago"
    />
</div>

Installation

terminal
php artisan ui:add ci-badge

Registry contract

php artisan ui:add ci-badge writes only the files below. The CLI validates each file hash before writing and asks before it replaces a local change, unless you pass --force.

  • blade resources/views/components/ui/ci-badge.blade.php
Registry dependencies
None — installs on its own.
Packages
composer: jml/brok:^0.2

Use with AI

A brief for your coding agent: install command, usage, props, guidance and the rules. Copy it, or open a prompt about this component in an assistant.

ci-badge.md
# Brok UI: CI Badge (`ci-badge`)

A continuous-integration status badge (passing, failing, pending, cancelled) as a compact pill or a detailed card.

Brok UI is a Laravel Blade component registry. Installed components are plain Blade files the app owns.

## Install

```bash
php artisan ui:add ci-badge
```

## Usage

```blade
<div class="flex flex-col items-start gap-4">
    <div class="flex flex-wrap items-center gap-2">
        <x-ui.ci-badge status="passing" />
        <x-ui.ci-badge status="failing" />
        <x-ui.ci-badge status="pending" />
        <x-ui.ci-badge status="cancelled" />
    </div>

    <x-ui.ci-badge
        status="passing"
        workflow="CI"
        branch="main"
        duration="2m 22s"
        :show-workflow="true"
        :show-branch="true"
        :show-duration="true"
    />

    <x-ui.ci-badge
        status="passing"
        layout="card"
        workflow="CI"
        branch="main"
        duration="2m 22s"
        time="30m ago"
    />
</div>
```

## Props

- `status` (string, default `passing`)
- `workflow` (mixed|null, default `null`)
- `branch` (mixed|null, default `null`)
- `duration` (mixed|null, default `null`)
- `time` (mixed|null, default `null`)
- `layout` (string, default `inline`)
- `showWorkflow` (bool, default `false`)
- `showBranch` (bool, default `false`)
- `showDuration` (bool, default `false`)

## Use when

- Use for exact technical detail, code readability, diagnostics, and copyable reference material.
- Showing the result of the latest CI run for a branch or workflow.
- Putting build state next to a repository, release or pull request in a dashboard.

## Avoid when

- Do not surface developer-specific patterns in end-user flows unless the task genuinely requires them.
- Reporting a release version; release-badge carries the tag, date and assets.
- Conveying a status that is not a run outcome; status and status-indicator are the general primitives.

## Anti-patterns

- Exposing developer tooling in end-user flows without a task need

## Rules

- Use the `<brok:ci-badge>` tag (or `<x-ui.ci-badge>`) in Blade; do not rewrite the component.
- Prefer the documented props and variants over utility-class overrides; when a utility must win, use the `!` important modifier.
- Use semantic design tokens (`bg-primary`, `text-muted-foreground`), never raw colour utilities.
- Keep the `data-slot` attributes; they are the styling and test hooks.

## Links

- Docs: https://brokui.dev/docs/components/ci-badge
- Registry JSON (files, props, contract): https://brokui.dev/r/open/ci-badge.json

Working in Claude Code, Cursor or Codex? Give the agent the whole registry through the MCP server or the Brok UI skill.

API

manifest knowledge + registry-derived coverage

Props

Props accepted by this component: name, type, default value and description.
Prop Type Default Description
status string passing
workflow mixed | null null
branch mixed | null null
duration mixed | null null
time mixed | null null
layout string inline
showWorkflow bool false
showBranch bool false
showDuration bool false

Slots

  • default —

Data slots

Stable hooks for CSS overrides and browser tests.

ci-badge

Behavior

  • status selects the colour, icon and wording; unknown values fall back to the pending look.
  • layout="inline" is a compact pill, layout="card" adds the workflow, branch and duration lines that the show flags enable.
  • Declares registry capability flags: a11y, responsive, rtl, darkMode, localized.

Guidance

Developer and documentation tool

Expose exact technical or diagnostic information.

Use when

  • Use for exact technical detail, code readability, diagnostics, and copyable reference material.
  • Showing the result of the latest CI run for a branch or workflow.
  • Putting build state next to a repository, release or pull request in a dashboard.

Avoid when

  • Do not surface developer-specific patterns in end-user flows unless the task genuinely requires them.
  • Reporting a release version; release-badge carries the tag, date and assets.
  • Conveying a status that is not a run outcome; status and status-indicator are the general primitives.

Use instead

  • Plain-language product UI for nontechnical users

Anti-patterns

  • Exposing developer tooling in end-user flows without a task need
Anatomy
root
Theming hooks
ci-badge

Accessibility

WCAG 2.2 AA Keyboard focus-visible RTL-ready Localized labels Dark mode
Keyboard
No component-owned keyboard interaction; native element behavior applies.
Focus
none
  • The badge is a role="status" region with a composed accessible name, so the run result is announced as text.
  • The state icon is aria-hidden: the wording, not the colour or glyph, carries the meaning.
  • Semantic HTML and a stable data-slot attribute for styling and scripting hooks.
  • Focus-visible rings use the ring token, so keyboard focus is always visible.
  • Disabled and invalid states are conveyed to assistive tech, not by color alone.
  • Targets WCAG 2.2 AA; verify contrast in light, dark, admin and customer surfaces in the preview.
  • Labels go through __() and layout uses logical properties (ms-*, text-start), so it mirrors under dir="rtl" — flip the preview to RTL to confirm.
  • Dark mode uses the same semantic tokens under the dark class; high contrast follows forced-color system tokens.

Livewire

Safe

Livewire can update this component through forwarded wire:* attributes.

Source

The exact, editable file ui:add writes into your app. Previews render this same code; there are no preview-only components.

resources/views/components/ui/ci-badge.blade.php Blade
@props([
    'status' => 'passing',
    'workflow' => null,
    'branch' => null,
    'duration' => null,
    'time' => null,
    'layout' => 'inline',
    'showWorkflow' => false,
    'showBranch' => false,
    'showDuration' => false,
])

@php
    $status = in_array($status, ['passing', 'failing', 'pending', 'cancelled', 'skipped'], true) ? $status : 'passing';
    $layout = in_array($layout, ['inline', 'card'], true) ? $layout : 'inline';

    // Status → semantic-token tone + label. No raw colours: every colour is a
    // design token (success/destructive/warning/muted).
    $map = [
        'passing' => ['tone' => 'text-success', 'bg' => 'bg-success/10', 'border' => 'border-success/25', 'label' => __('passing')],
        'failing' => ['tone' => 'text-destructive', 'bg' => 'bg-destructive/10', 'border' => 'border-destructive/25', 'label' => __('failing')],
        'pending' => ['tone' => 'text-warning', 'bg' => 'bg-warning/10', 'border' => 'border-warning/25', 'label' => __('pending')],
        'cancelled' => ['tone' => 'text-muted-foreground', 'bg' => 'bg-muted', 'border' => 'border-border', 'label' => __('cancelled')],
        'skipped' => ['tone' => 'text-muted-foreground', 'bg' => 'bg-muted', 'border' => 'border-border', 'label' => __('skipped')],
    ];
    $s = $map[$status];

    $showWorkflow = filter_var($showWorkflow, FILTER_VALIDATE_BOOLEAN);
    $showBranch = filter_var($showBranch, FILTER_VALIDATE_BOOLEAN);
    $showDuration = filter_var($showDuration, FILTER_VALIDATE_BOOLEAN);
    $a11y = trim(($workflow ? $workflow.' ' : '').$s['label'].($branch ? ' '.$branch : ''));
@endphp

@php
    $icon = function (string $status, string $extra = '') {
        return match ($status) {
            'failing' => '<svg class="size-4 '.$extra.'" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 6 6 18M6 6l12 12"/></svg>',
            'pending' => '<svg class="size-4 animate-spin motion-reduce:animate-none '.$extra.'" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></svg>',
            'cancelled', 'skipped' => '<svg class="size-4 '.$extra.'" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14"/></svg>',
            default => '<svg class="size-4 '.$extra.'" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg>',
        };
    };
@endphp

@if ($layout === 'card')
    <div
        data-slot="ci-badge"
        data-status="{{ $status }}"
        data-layout="card"
        role="status"
        aria-label="{{ $a11y }}"
        {{ $attributes->merge(['class' => 'inline-flex items-center gap-3 rounded-md border border-border bg-card p-3 text-foreground']) }}
    >
        <span class="flex size-9 shrink-0 items-center justify-center rounded-full {{ $s['bg'] }} {{ $s['tone'] }}">{!! $icon($status) !!}</span>
        <div class="min-w-0">
            <div class="flex items-center gap-2">
                <span class="font-medium text-foreground">{{ $workflow ?? __('CI') }}</span>
                <span class="text-xs font-medium {{ $s['tone'] }}">{{ $s['label'] }}</span>
            </div>
            <div class="mt-0.5 flex flex-wrap items-center gap-x-3 gap-y-0.5 text-xs text-muted-foreground">
                @if ($branch)<span class="font-mono">{{ $branch }}</span>@endif
                @if ($duration)<span>{{ $duration }}</span>@endif
                @if ($time)<span>{{ $time }}</span>@endif
            </div>
        </div>
    </div>
@else
    <span
        data-slot="ci-badge"
        data-status="{{ $status }}"
        data-layout="inline"
        role="status"
        aria-label="{{ $a11y }}"
        {{ $attributes->merge(['class' => 'inline-flex items-center gap-2 rounded-full border '.$s['border'].' '.$s['bg'].' px-2 py-1 text-xs font-medium '.$s['tone']]) }}
    >
        {!! $icon($status) !!}
        @if ($showWorkflow && $workflow)<span class="text-foreground">{{ $workflow }}</span>@endif
        <span>{{ $s['label'] }}</span>
        @if ($showBranch && $branch)<span class="font-mono text-muted-foreground">{{ $branch }}</span>@endif
        @if ($showDuration && $duration)<span class="text-muted-foreground">{{ $duration }}</span>@endif
    </span>
@endif

Ownership & lifecycle

Owner, release state, review evidence and adoption for this item.
Owner
Platform UI (@JoshJML)
Current version
1.0.5
Status
Stable
License
open
Accessibility reviewed
No review date recorded
Last breaking change
No date recorded
Deprecation
Not deprecated
Contract
v6
Foundation
≥ 1.0.0