Display

Energy Class Badge

EU energy-efficiency class chip (A–G) on the official green→red label scale, with a neutral quebi fallback for unknown or legacy values.

badgeenergylabelindicatoreu

Scale

Classes A–G on the official EU energy-label colour scale (dark-green → red).

ABCDEFG

Sizes

Three sizes — sm, md (default), lg.

AAA

Case & whitespace tolerant

Input is trimmed and upper-cased before matching, so messy data still renders right.

BCD

Unknown fallback

Legacy or unrecognised values (e.g. A+, ?) fall back to a neutral quebi chip showing the raw text — nothing is silently dropped.

A+A+++?

Source

Copy this into your project. Resolve its dependencies from the registryDependencies in the component's API entry.

import { tv, type VariantProps } from "tailwind-variants"
import { cn } from "@/lib/utils"

/**
 * EnergyClassBadge — EU energy-efficiency class chip (A–G).
 *
 * Renders the class letter on the official EU energy-label colour for that band
 * (A dark-green → G red). These per-band colours are *domain-semantic* (they ARE
 * the EU label scale, not quebi brand tokens) so they're kept as explicit values.
 * The chip itself is styled with quebi conventions (rounded-quebi-sm radius,
 * font-sans, neutral fallback on quebi tokens).
 *
 * The letter is always rendered as text, so colour is never the sole signal
 * (WCAG 1.4.1); pass a localised `aria-label` for a fuller screen-reader
 * description ("Energy efficiency class A"). Per-band text colour is fixed
 * (`text-white` / `text-black`) so each chip keeps ≥ 3:1 contrast against its
 * fill in any theme.
 *
 * Unknown / legacy values (e.g. "A+", "A+++") fall back to a neutral quebi chip
 * showing the raw text, so nothing is ever silently dropped.
 */
const KNOWN_CLASSES = ["A", "B", "C", "D", "E", "F", "G"] as const
type EnergyClassLetter = (typeof KNOWN_CLASSES)[number]

export const energyClassBadgeStyles = tv({
  base: "inline-flex items-center justify-center rounded-quebi-sm font-sans font-bold leading-none tracking-tight",
  variants: {
    // Official EU energy-label scale (dark-green A → red G). White text on the
    // dark ends (A, G), black on the bright middle bands keeps every chip ≥ 3:1
    // against its fill (large/graphical-object budget).
    band: {
      A: "bg-[#00843d] text-white",
      B: "bg-[#4caf30] text-black",
      C: "bg-[#bccf00] text-black",
      D: "bg-[#fff100] text-black",
      E: "bg-[#fabe00] text-black",
      F: "bg-[#ee7d00] text-black",
      G: "bg-[#e30613] text-white",
      unknown: "bg-white/[0.06] text-quebi-fg-muted border border-cyan-500/10",
    },
    size: {
      sm: "min-w-[20px] px-1.5 py-0.5 text-[12px]",
      md: "min-w-[28px] px-2 py-1 text-[15px]",
      lg: "min-w-[40px] px-2.5 py-1.5 text-[20px]",
    },
  },
  defaultVariants: { band: "unknown", size: "md" },
})

export interface EnergyClassBadgeProps
  extends Omit<React.ComponentProps<"span">, "children">,
    Pick<VariantProps<typeof energyClassBadgeStyles>, "size"> {
  /** Raw energy class value, e.g. "A", "b", "C". Whitespace/case tolerant. */
  energyClass: string
}

export function EnergyClassBadge({
  energyClass,
  size,
  className,
  ...props
}: EnergyClassBadgeProps) {
  const normalized = energyClass.trim().toUpperCase()
  const band: EnergyClassLetter | "unknown" = (KNOWN_CLASSES as readonly string[]).includes(
    normalized,
  )
    ? (normalized as EnergyClassLetter)
    : "unknown"
  // Recognised bands show the canonical single uppercase letter; unknown values
  // render verbatim (trimmed) so legacy "A+"-style data stays visible.
  const display = band === "unknown" ? energyClass.trim() : band

  return (
    <span {...props} className={cn(energyClassBadgeStyles({ band, size }), className)}>
      {display}
    </span>
  )
}