> For the complete documentation index, see [llms.txt](https://playground.e107sk.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://playground.e107sk.com/e107-aragorn-theme/guides/fixing-badge-text-color.md).

# Fixing invisible badge text (bg-\* vs. text-bg-\*)

## Symptom

A Tabler `.badge` renders as a colored blob with **no visible text or number** — the markup and shortcode output are correct, the color is applied, but the label itself cannot be read. Three variations were found in Aragorn:

* `<span class="badge bg-secondary rounded-pill badge-secondary">2</span>` (a forum topic/reply count) — completely invisible.
* `<span class="badge bg-primary">0</span>` (a news comment count in a list-group) — visible but very low contrast, easy to mistake for an empty pill at small size.
* `<span class="badge bg-primary rounded-pill">141</span>` (download counts in a sidebar menu) — low contrast, same cause as above.

## Root cause

Tabler's `.badge` sets its **own** text color independently of whatever background utility class is added next to it:

```css
.badge {
    --tblr-badge-color: var(--tblr-secondary);
    color: var(--tblr-badge-color);
    ...
}
```

`bg-secondary` / `bg-primary` are plain **background-only** utilities — they never touch `--tblr-badge-color`. So:

* `badge bg-secondary` → background `var(--tblr-secondary)` (`#6b7280`), text *also* `var(--tblr-secondary)` (`#6b7280`) — identical color on color, text fully invisible.
* `badge bg-primary` → background `var(--tblr-primary)`, text stays the default secondary gray (`#6b7280`) — different colors, but a muted gray on a saturated background is low-contrast and hard to read at badge font sizes.

{% hint style="info" %}
This is unrelated to font-size or the badge's `em`-based sizing (`--tblr-badge-font-size: 0.85714285em`) — that only matters inside ancestors using legacy `style="font-size:0"` whitespace-collapse hacks (a separate, real issue in some old e107 list markup), which is worth ruling out first but was **not** the cause here.
{% endhint %}

## The fix

There are two places the fix can live, and Aragorn uses both:

1. **Theme CSS** — a safety net that repairs every `badge bg-*` combination, including markup the theme does not own (forum, download menus, core and third-party plugins).
2. **Markup** — `text-bg-*` in templates the theme already overrides, so those templates are correct on their own.

### 1. Theme CSS: pair `bg-*` with Tabler's `-fg` token

Tabler ships a contrasting foreground token for every color — `--tblr-primary-fg`, `--tblr-secondary-fg`, `--tblr-light-fg`, and so on. Dark colors resolve to `var(--tblr-light)`, while `--tblr-light-fg` resolves to `var(--tblr-dark)`. These are exactly the values the badge needs, so the theme only has to connect each background class to its token:

```css
.badge.bg-primary   { --tblr-badge-color: var(--tblr-primary-fg); }
.badge.bg-secondary { --tblr-badge-color: var(--tblr-secondary-fg); }
.badge.bg-success   { --tblr-badge-color: var(--tblr-success-fg); }
.badge.bg-info      { --tblr-badge-color: var(--tblr-info-fg); }
.badge.bg-warning   { --tblr-badge-color: var(--tblr-warning-fg); }
.badge.bg-danger    { --tblr-badge-color: var(--tblr-danger-fg); }
.badge.bg-dark      { --tblr-badge-color: var(--tblr-dark-fg); }
.badge.bg-light     { --tblr-badge-color: var(--tblr-light-fg); }
```

The same rules generated in SCSS:

```scss
// Tabler's .badge sets its own text color, so plain bg-* utilities leave
// the label unreadable. Pairing each bg-* with Tabler's matching -fg token
// repairs legacy plugin markup without touching templates.
$badge-fg-colors: primary, secondary, success, info, warning, danger, dark, light;

@each $name in $badge-fg-colors {
  .badge.bg-#{$name} {
    --tblr-badge-color: var(--tblr-#{$name}-fg);
  }
}
```

To cover Tabler's extended palette as well (`bg-blue`, `bg-azure`, `bg-purple`, …), add those names to `$badge-fg-colors` — every one of them has a matching `--tblr-<name>-fg` token.

**Why this works:**

* **Specificity.** `.badge.bg-primary` (0,2,0) beats Tabler's `.badge` (0,1,0), so no `!important` is needed.
* **The variable, not `color`.** Setting `--tblr-badge-color` keeps Tabler's own `color: var(--tblr-badge-color)` in charge. Other badge modifiers that read the same variable keep working.
* **Exact class match.** `.bg-primary` does not match `bg-primary-lt`, so the light badge variants are untouched — they already set their own text color.
* **Dark mode.** Tabler's dark theme does not redefine `--tblr-light`, so the `-fg` tokens stay correct in both modes.

### 2. Markup: `text-bg-*` in theme-owned templates

Where the theme overrides a template anyway, use Bootstrap/Tabler's paired utility class **`text-bg-*`** instead of `bg-*`. Unlike `bg-*`, `text-bg-*` sets background **and** a contrasting text color together:

```css
.text-bg-secondary {
    color: #f9fafb !important;
    background-color: rgba(var(--tblr-secondary-rgb), var(--tblr-bg-opacity, 1)) !important;
}
```

This rule already ships in `tabler.min.css` — there is no need to copy it into the theme stylesheet.

Change colored badges in theme templates from:

```html
<span class="badge bg-secondary rounded-pill badge-secondary">2</span>
```

to:

```html
<span class="badge text-bg-secondary rounded-pill">2</span>
```

(`badge-secondary` without `bg-` is a leftover Bootstrap 4 class name that does nothing in Bootstrap 5/Tabler — safe to drop while you're there.)

Applied in the news templates (`badge bg-primary` → `badge text-bg-primary` in `news_template.php`, `news_grid_template.php`, `news_menu_template.php` — see [News templates](/e107-aragorn-theme/plugins/news.md)).

## Why copying `.text-bg-*` into the theme did not help

An early attempt added the `.text-bg-secondary` rule above to the theme stylesheet. It changed nothing, because a class rule only applies to elements that carry the class — forum and menu markup still outputs `bg-secondary` / `bg-primary`. The rule was also a duplicate of what Tabler already provides.

## Why not a single generic override

Two shortcuts look tempting but break other badges:

* **`.badge { color: #fff; }`** — ignores which background the badge has. White text disappears on `bg-light`, and the light `bg-*-lt` variants lose their colored text.
* **`.badge[class*="bg-"] { color: #fff; }`** — the attribute selector also matches `bg-primary-lt`, `bg-transparent`, `bg-body` and similar classes, with the same result.

The compound selector `.badge.bg-<color>` avoids both problems because it knows exactly which background each badge is paired with.

## Checklist for future badges

* In theme-owned templates, use `badge text-bg-<color>` for solid colored badges — never `badge bg-<color>` alone.
* Keep the `.badge.bg-*` safety net in the theme stylesheet for plugin markup the theme does not override.
* When adding a new solid badge color, add its name to `$badge-fg-colors`.
* Drop legacy `badge-<color>` (no `bg-`/`text-bg-` prefix) classes on sight — Bootstrap 4 leftovers, inert in Bootstrap 5.
