Skip to content

Filter Chips

Active-filter bar: “field: value” chips with a remove (×) button each, a result-count region and “Clear all”; announces removals.

neo-corporate/ui/filter-chips
Open ↗

Source

"use client";

import { useState } from "react";
import { X } from "lucide-react";
import { cn } from "@/lib/utils";

export interface FilterChip {
  id: string;
  field: string;
  value: string;
}

export interface FilterChipsProps {
  filters?: FilterChip[];
  defaultFilters?: FilterChip[];
  onFiltersChange?: (filters: FilterChip[]) => void;
  className?: string;
}

const DEFAULTS: FilterChip[] = [
  { id: "status", field: "Status", value: "Overdue" },
  { id: "entity", field: "Entity", value: "Ledgerly GmbH" },
  { id: "amount", field: "Amount", value: "> $5,000" },
  { id: "due", field: "Due", value: "Last 30 days" },
];

/** Active-filter bar: “field: value” chips with a remove (×) button each, a result-count region and “Clear all”; announces removals. */
export function FilterChips({ filters, defaultFilters = DEFAULTS, onFiltersChange, className }: FilterChipsProps) {
  const [inner, setInner] = useState(defaultFilters);
  const list = filters ?? inner;
  const [msg, setMsg] = useState("");
  const update = (next: FilterChip[], m: string) => {
    setInner(next);
    onFiltersChange?.(next);
    setMsg(m);
  };
  return (
    <div className={cn("flex flex-wrap items-center gap-2", className)}>
      <span className="text-xs font-semibold tracking-da-label text-da-muted-fg uppercase">Filters</span>
      <ul className="contents">
        {list.map((f) => (
          <li
            key={f.id}
            className="da-stroke inline-flex items-center rounded-da-md bg-da-surface text-sm text-da-surface-fg transition-opacity duration-(--da-duration) starting:opacity-0"
          >
            <span className="py-1 pl-2.5">
              <span className="text-da-muted-fg">{f.field}:</span> <span className="font-semibold">{f.value}</span>
            </span>
            <button
              type="button"
              aria-label={`Remove filter ${f.field}: ${f.value}`}
              onClick={() =>
                update(
                  list.filter((x) => x.id !== f.id),
                  `Removed ${f.field} filter`,
                )
              }
              className="da-focus ml-1 grid size-7 place-items-center rounded-r-da-md text-da-muted-fg hover:bg-da-surface-2 hover:text-da-fg"
            >
              <X aria-hidden className="size-3.5" />
            </button>
          </li>
        ))}
      </ul>
      {list.length > 0 ? (
        <button
          type="button"
          onClick={() => update([], "All filters cleared")}
          className="da-focus rounded-da-sm px-1 text-sm font-semibold text-da-primary hover:underline"
        >
          Clear all
        </button>
      ) : (
        <span className="text-sm text-da-muted-fg">No filters applied</span>
      )}
      <span role="status" className="sr-only">
        {msg}
      </span>
    </div>
  );
}

export default FilterChips;

modules/neo-corporate/ui/filter-chips/index.tsx

Props

PropTypeDefaultDescription
filtersFilterChip[]—Filters.
defaultFiltersFilterChip[]—Default Filters.
onFiltersChange(filters: FilterChip[]) => void—Callback.
classNamestring—Extra classes on the root.

Other badge variants in Neo Corporate

Badge in other art directions

Badge

Mono uppercase label with ink stroke in 8 solid variants (status + brand colors), two sizes, optional square status dot and a tilted sticker mode.

BrutalistBadge

Counter Badge

Notification counter that wraps any element (icon button, avatar) and pins a ruled square count to its corner; caps at max+, dot mode, three tones, hidden at zero.

BrutalistBadge

Sticker Badge

Round SVG sticker badge: dashed scalloped edge, text running around a circular path, big center label, hard offset drop shadow and a slight tilt. Four solid tones, any size.

BrutalistBadge

Tag List

Editable tag field: color-cycling ruled chips with remove buttons inside a boxed input, Enter/comma to add, Backspace to remove last, quick-add suggestion chips and a max limit.

BrutalistBadge

Avatar Stack

Overlapping gradient initials avatars inside a glass pill with a dark “+N” overflow bubble and a count label; avatars lift on hover.

GlassBadge

Badge

Pill badge in frosted glass, gradient or tinted status variants (primary, success, warning, danger) with optional dot, glowing live dot and leading icon. Two sizes.

GlassBadge

Presence Avatar

Gradient initials avatar with a presence dot (online, busy, away, offline); the “speaking” state adds a spinning conic gradient ring. Three sizes.

GlassBadge

Reaction Chip

Slack-style emoji reaction chips in glass: click to add/remove your reaction, the count pops, and reacted chips get a primary ring and tint.

GlassBadge

Badge

Small tinted label (neutral, accent, success, warning, danger, outline) with optional status dot, live pulse, pill shape and mono mode for IDs and service names.

MinimalBadge

Kbd Hint

Keyboard shortcut hint made of raised keycaps: combos (⌘ K), sequences (G then I) and an optional action label. Symbols get spoken names for screen readers.

MinimalBadge

Severity Badge

Incident severity badge: four signal bars filled according to level (SEV1–SEV4) with a mono code and optional label, so color is never the only cue.

MinimalBadge

Status Badge

Service status pill (operational, degraded, major outage, maintenance, no data) with a colored dot that pulses during incidents; soft, outline and dot-only variants.

MinimalBadge

Badge

Square mono tag in four variants: hairline outline, ink solid, subtle gray and signal orange (for “New” only).

Mono CleanBadge

Index Counter

Editorial index counter “03 / 12” in mono with zero padding and an optional hairline progress bar — for galleries, slides and case-study chapters.

Mono CleanBadge

Kbd

Keyboard shortcut hint: square hairline key caps in mono, joined, with an optional muted action label — e.g. “⌘ K Search”.

Mono CleanBadge

Status Dot

Minimal status indicator: a 6px dot (filled, hollow or pulsing signal for “building”) followed by a mono uppercase label.

Mono CleanBadge

Badge

Soft rounded pill label in natural tints (sage, clay, sand, forest, sun, berry), two sizes, optional leading icon.

Organic SoftBadge

Impact Badge

Impact/effort rating shown as three leaves (filled up to the level) with a text label — used on reduction actions.

Organic SoftBadge

Scope Badge

GHG scope indicator: a leaf-shaped chip with the scope number (forest / sage / clay) and an optional label; title explains the scope.

Organic SoftBadge

Status Pill

Target/action progress pill with a soft pulsing dot for active states (on track, at risk, off track) and static ones (done, not started).

Organic SoftBadge

Badge

Rounded pastel pill badge in seven tones (peach, mint, lavender, butter, neutral, primary, danger) and two sizes, with optional emoji/icon or dot.

Soft FlatBadge

Label Tags

Editable label set: pastel tags (rotating colors) with round × buttons and a dashed “+ Add label” pill that becomes an inline input (Enter adds, Escape cancels).

Soft FlatBadge

Status Dot

Task status indicator: colored dot in a soft halo plus label (To do, In progress (pulsing), In review, Done, Blocked); compact dot-only mode keeps a hidden label.

Soft FlatBadge

Streak Badge

Gamified streak badge: flame + “12-day streak” on a butter pill and seven round weekday dots, peach with a check on active days.

Soft FlatBadge