Tag
An interactive chip for filters and selections — a bordered button or plain text, with optional checkbox, dot, flag, avatar, count and remove button.
Examples
Styles
Prominent (bordered, the default) and Subtle (text only). Tags are buttons — a filter toggle exposes its state with aria-pressed, which also drives the selected look.
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:0.75rem">
<button type="button" class="kompan-tag">Label</button>
<button type="button" class="kompan-tag" aria-pressed="true">Label</button>
<button type="button" class="kompan-tag kompan-tag_subtle">Label</button>
</div>Sizes
sm (default, 24px), md (24px, 14px text) and lg (28px). Checkbox, close and count scale with the tag.
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:0.75rem">
<button type="button" class="kompan-tag kompan-tag_sm">Small</button>
<button type="button" class="kompan-tag kompan-tag_md">Medium</button>
<button type="button" class="kompan-tag kompan-tag_lg">Large</button>
<span class="kompan-tag kompan-tag_sm">Small<button type="button" class="kompan-tag__close" aria-label="Remove Small"><svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 3 3 9M3 3l6 6"/></svg></button></span>
<span class="kompan-tag kompan-tag_md">Medium<button type="button" class="kompan-tag__close" aria-label="Remove Medium"><svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 3 3 9M3 3l6 6"/></svg></button></span>
<span class="kompan-tag kompan-tag_lg">Large<button type="button" class="kompan-tag__close" aria-label="Remove Large"><svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 3 3 9M3 3l6 6"/></svg></button></span>
</div>With checkbox
For multi-select filters the tag is a label wrapping a real checkbox; checking it selects the whole tag.
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:0.75rem">
<label class="kompan-tag"><input type="checkbox" class="kompan-tag__checkbox">Label</label>
<label class="kompan-tag"><input type="checkbox" class="kompan-tag__checkbox" checked>Label</label>
<label class="kompan-tag"><input type="checkbox" class="kompan-tag__checkbox" disabled>Label</label>
<label class="kompan-tag"><input type="checkbox" class="kompan-tag__checkbox" checked disabled>Label</label>
<label class="kompan-tag kompan-tag_md"><input type="checkbox" class="kompan-tag__checkbox">Label</label>
<label class="kompan-tag kompan-tag_lg"><input type="checkbox" class="kompan-tag__checkbox" checked>Label</label>
</div>Leading content
A status dot, a flag (or any 16px icon) or a round avatar before the label. The dot colour is set with --kompan-tag-dot.
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:0.75rem">
<button type="button" class="kompan-tag"><span class="kompan-tag__dot"></span>Label</button>
<button type="button" class="kompan-tag"><svg class="kompan-tag__icon" viewBox="0 0 16 16" aria-hidden="true"><circle cx="8" cy="8" r="8" fill="#d80027"/><path d="M6.5 0.1v6.4H0.1a8 8 0 0 0 0 3h6.4v6.4a8 8 0 0 0 3 0V9.5h6.4a8 8 0 0 0 0-3H9.5V0.1a8 8 0 0 0-3 0Z" fill="#f0f0f0"/></svg>Label</button>
<button type="button" class="kompan-tag"><img class="kompan-tag__avatar" src="https://images.unsplash.com/photo-1600603406200-5b2a104684ac?w=64&h=64&fit=crop&crop=faces&q=80" alt="">Label</button>
<button type="button" class="kompan-tag kompan-tag_subtle"><span class="kompan-tag__dot" style="--kompan-tag-dot: var(--color-utility-orange-500)"></span>Label</button>
<button type="button" class="kompan-tag kompan-tag_subtle"><svg class="kompan-tag__icon" viewBox="0 0 16 16" aria-hidden="true"><circle cx="8" cy="8" r="8" fill="#d80027"/><path d="M6.5 0.1v6.4H0.1a8 8 0 0 0 0 3h6.4v6.4a8 8 0 0 0 3 0V9.5h6.4a8 8 0 0 0 0-3H9.5V0.1a8 8 0 0 0-3 0Z" fill="#f0f0f0"/></svg>Label</button>
<button type="button" class="kompan-tag kompan-tag_subtle"><img class="kompan-tag__avatar" src="https://images.unsplash.com/photo-1600603406200-5b2a104684ac?w=64&h=64&fit=crop&crop=faces&q=80" alt="">Label</button>
</div>Count and remove
A count pill after the label, or a remove button. A removable tag is a span holding the button — buttons cannot nest — so combine with a checkbox rather than a button tag.
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:0.75rem">
<button type="button" class="kompan-tag">Label <span class="kompan-tag__count">5</span></button>
<span class="kompan-tag">Label<button type="button" class="kompan-tag__close" aria-label="Remove Label"><svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 3 3 9M3 3l6 6"/></svg></button></span>
<span class="kompan-tag"><svg class="kompan-tag__icon" viewBox="0 0 16 16" aria-hidden="true"><circle cx="8" cy="8" r="8" fill="#d80027"/><path d="M6.5 0.1v6.4H0.1a8 8 0 0 0 0 3h6.4v6.4a8 8 0 0 0 3 0V9.5h6.4a8 8 0 0 0 0-3H9.5V0.1a8 8 0 0 0-3 0Z" fill="#f0f0f0"/></svg>Label<button type="button" class="kompan-tag__close" aria-label="Remove Label"><svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 3 3 9M3 3l6 6"/></svg></button></span>
<label class="kompan-tag"><input type="checkbox" class="kompan-tag__checkbox" checked>Label<button type="button" class="kompan-tag__close" aria-label="Remove Label"><svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 3 3 9M3 3l6 6"/></svg></button></label>
<button type="button" class="kompan-tag kompan-tag_lg"><img class="kompan-tag__avatar" src="https://images.unsplash.com/photo-1600603406200-5b2a104684ac?w=64&h=64&fit=crop&crop=faces&q=80" alt="">Label <span class="kompan-tag__count">12</span></button>
</div>States
Default, selected, disabled and selected + disabled. Use the disabled attribute on buttons and inputs; aria-disabled on span tags.
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:0.75rem">
<button type="button" class="kompan-tag">Default</button>
<button type="button" class="kompan-tag" aria-pressed="true">Selected</button>
<button type="button" class="kompan-tag" disabled>Disabled</button>
<button type="button" class="kompan-tag" aria-pressed="true" disabled>Selected</button>
<span class="kompan-tag" aria-disabled="true">Removable<button type="button" class="kompan-tag__close" aria-label="Remove Removable"><svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 3 3 9M3 3l6 6"/></svg></button></span>
</div>Colour themes
Dark and Brand tags for use on light surfaces. Figma has no selected or disabled variants for these themes.
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:0.75rem">
<button type="button" class="kompan-tag kompan-tag_dark">Label</button>
<span class="kompan-tag kompan-tag_dark">Label<button type="button" class="kompan-tag__close" aria-label="Remove Label"><svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 3 3 9M3 3l6 6"/></svg></button></span>
<button type="button" class="kompan-tag kompan-tag_brand">Label</button>
<span class="kompan-tag kompan-tag_brand">Label<button type="button" class="kompan-tag__close" aria-label="Remove Label"><svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 3 3 9M3 3l6 6"/></svg></button></span>
<button type="button" class="kompan-tag kompan-tag_brand kompan-tag_lg"><svg class="kompan-tag__icon" viewBox="0 0 16 16" aria-hidden="true"><circle cx="8" cy="8" r="8" fill="#d80027"/><path d="M6.5 0.1v6.4H0.1a8 8 0 0 0 0 3h6.4v6.4a8 8 0 0 0 3 0V9.5h6.4a8 8 0 0 0 0-3H9.5V0.1a8 8 0 0 0-3 0Z" fill="#f0f0f0"/></svg>Label</button>
</div>Accessibility
- Pick the element by behaviour
A tag that toggles a filter is a <button type="button"> with aria-pressed. A tag in a multi-select group is a <label> wrapping input.kompan-tag__checkbox. A tag that only displays a value (with or without a remove button) is a <span>. Never put a button.kompan-tag__close inside a button tag — nested buttons are invalid and unreachable.
- Expose the selected state
The selected look follows aria-pressed="true", aria-selected="true" or a checked checkbox — prefer those over the kompan-tag_selected class so assistive technology gets the state too. Use aria-selected only inside a listbox/tab pattern.
- Remove button needs a name
button.kompan-tag__close shows only an × icon. Set aria-label="Remove {label}" so the action is announced with its target, and move focus to a sensible neighbour after the tag is removed.
- Disabled
Use the disabled attribute on button tags and on the checkbox; use aria-disabled="true" on span tags (it also disables the inner remove button). Disabled tags keep their visible label but drop to 10% text contrast, so do not rely on them to convey information.
- Focus is visible
Tags, checkboxes and remove buttons show the 2px + 4px focus ring on :focus-visible (the ring wraps the whole tag when its checkbox is focused). Do not remove it; on non-white backgrounds override --color-bg-primary in the ring if the gap becomes invisible.
- Contrast
Prominent text is 60% black on white (about 5.7:1, AA). Subtle text is 40% black (about 2.8:1) and the remove icon is 20% black — both fail AA. Treat Subtle as secondary UI or darken --kompan-tag-fg / --kompan-tag-close-fg when the tag carries essential information. The dot conveys status by colour only; say the status in the label too.
Stylesheet
Shipped as @wah-kompan/kompan-ui/components/tag.css. Every value is a token reference, so it themes with the rest of the system rather than being restyled.
.kompan-tag {
/* Size ramp — sm is the Figma default (24px) */
--kompan-tag-py: 0.1875rem; /* 3px, unbound in Figma */
--kompan-tag-px: var(--spacing-md);
--kompan-tag-gap: var(--spacing-xs);
--kompan-tag-font-size: var(--type-font-size-xs);
--kompan-tag-line-height: var(--type-line-height-xs);
--kompan-tag-checkbox: 0.875rem; /* 14px */
--kompan-tag-close: 0.875rem; /* 14px box */
--kompan-tag-close-pad: var(--spacing-xxs); /* → 10px icon */
--kompan-tag-count-h: 1rem;
--kompan-tag-count-px: var(--spacing-xs);
--kompan-tag-count-font-size: var(--type-font-size-xs);
--kompan-tag-edge: min(0px, calc(var(--spacing-xs) - var(--kompan-tag-px)));
/* Colour roles — Prominent / Light */
--kompan-tag-bg: var(--color-input-bg-primary);
--kompan-tag-fg: var(--color-input-fg-secondary);
--kompan-tag-border: var(--color-input-border-primary);
--kompan-tag-bg-hover: var(--color-button-secondary-subtle-bg-hover);
--kompan-tag-close-fg: var(--color-button-secondary-subtle-fg-tertiary);
--kompan-tag-focus-ring: 0 0 0 2px var(--color-bg-primary), 0 0 0 4px var(--color-button-focus-ring);
display: inline-flex;
align-items: center;
gap: var(--kompan-tag-gap);
box-sizing: border-box;
margin: 0;
padding: var(--kompan-tag-py) var(--kompan-tag-px);
border: 0;
border-radius: var(--radius-sm);
background: var(--kompan-tag-bg);
box-shadow: inset 0 0 0 var(--stroke-sm) var(--kompan-tag-border);
color: var(--kompan-tag-fg);
font-family: var(--type-font-family-body);
font-size: var(--kompan-tag-font-size);
font-weight: var(--type-font-weight-medium);
line-height: var(--kompan-tag-line-height);
letter-spacing: 0;
white-space: nowrap;
text-decoration: none;
cursor: default;
user-select: none;
transition: background-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease;
}
button.kompan-tag,
label.kompan-tag,
a.kompan-tag {
appearance: none;
cursor: pointer;
}
/* Implied hover — Figma has no hover variant; overlay so it works on any theme */
button.kompan-tag:hover,
label.kompan-tag:hover,
a.kompan-tag:hover {
background-image: linear-gradient(var(--kompan-tag-bg-hover), var(--kompan-tag-bg-hover));
}
.kompan-tag:focus-visible,
.kompan-tag:has(.kompan-tag__checkbox:focus-visible) {
outline: none;
box-shadow: inset 0 0 0 var(--stroke-sm) var(--kompan-tag-border), var(--kompan-tag-focus-ring);
}
/* Sizes */
.kompan-tag_sm {
--kompan-tag-py: 0.1875rem;
--kompan-tag-px: var(--spacing-md);
}
.kompan-tag_md {
--kompan-tag-py: var(--spacing-xxs);
--kompan-tag-px: 0.5625rem; /* 9px, unbound in Figma */
--kompan-tag-gap: 0.3125rem; /* 5px, unbound in Figma */
--kompan-tag-font-size: var(--type-font-size-sm);
--kompan-tag-line-height: var(--type-line-height-sm);
--kompan-tag-checkbox: 1rem;
--kompan-tag-close: 1rem;
--kompan-tag-count-h: 1.125rem;
--kompan-tag-count-px: 0.3125rem;
}
.kompan-tag_lg {
--kompan-tag-py: var(--spacing-xs);
--kompan-tag-px: 0.625rem; /* 10px, unbound in Figma */
--kompan-tag-gap: var(--spacing-sm);
--kompan-tag-font-size: var(--type-font-size-sm);
--kompan-tag-line-height: var(--type-line-height-sm);
--kompan-tag-checkbox: 1.125rem;
--kompan-tag-close: 1.25rem;
--kompan-tag-close-pad: 0.1875rem; /* → 14px icon */
--kompan-tag-count-h: 1.25rem;
--kompan-tag-count-px: var(--spacing-sm);
--kompan-tag-count-font-size: var(--type-font-size-sm);
}
/* Style: Subtle (Text) — no chrome, 2px hit padding */
.kompan-tag_subtle {
--kompan-tag-py: var(--spacing-xxs);
--kompan-tag-px: var(--spacing-xxs);
--kompan-tag-bg: transparent;
--kompan-tag-border: transparent;
--kompan-tag-fg: var(--color-input-fg-tertiary);
}
/* Colour themes */
.kompan-tag_dark {
--kompan-tag-bg: var(--color-bg-solid);
--kompan-tag-fg: var(--color-input-fg-selected);
--kompan-tag-border: var(--color-branch-button-tertiary-bg-hover);
--kompan-tag-bg-hover: var(--color-branch-button-tertiary-bg-hover);
--kompan-tag-close-fg: var(--color-button-secondary-subtle-fg-active);
}
.kompan-tag_brand {
--kompan-tag-bg: var(--color-branch-bg-primary);
--kompan-tag-fg: var(--color-branch-text-primary);
--kompan-tag-bg-hover: var(--color-branch-button-tertiary-bg-hover);
--kompan-tag-close-fg: var(--color-button-secondary-subtle-fg-active);
}
/* Selected */
.kompan-tag_selected,
.kompan-tag[aria-pressed="true"],
.kompan-tag[aria-selected="true"],
.kompan-tag:has(.kompan-tag__checkbox:checked) {
--kompan-tag-bg: var(--color-input-bg-selected);
--kompan-tag-fg: var(--color-input-fg-selected);
--kompan-tag-bg-hover: var(--color-branch-button-tertiary-bg-hover);
--kompan-tag-close-fg: var(--color-button-secondary-subtle-fg-active);
}
/* Disabled */
.kompan-tag:disabled,
.kompan-tag[aria-disabled="true"],
.kompan-tag:has(.kompan-tag__checkbox:disabled) {
--kompan-tag-fg: var(--color-input-fg-secondary-disabled);
--kompan-tag-border: var(--color-input-border-primary-disabled);
--kompan-tag-bg-hover: transparent;
cursor: not-allowed;
}
.kompan-tag_selected:disabled,
.kompan-tag_selected[aria-disabled="true"],
.kompan-tag[aria-pressed="true"]:disabled,
.kompan-tag[aria-pressed="true"][aria-disabled="true"],
.kompan-tag:has(.kompan-tag__checkbox:checked:disabled) {
--kompan-tag-fg: var(--color-input-fg-selected);
}
.kompan-tag[aria-disabled="true"] .kompan-tag__close,
.kompan-tag:disabled .kompan-tag__close {
pointer-events: none;
}
/* Leading: checkbox */
.kompan-tag__checkbox {
appearance: none;
flex: none;
box-sizing: border-box;
width: var(--kompan-tag-checkbox);
height: var(--kompan-tag-checkbox);
margin: 0;
margin-left: var(--kompan-tag-edge);
border: 0;
border-radius: var(--radius-xs);
background: var(--color-input-bg-primary) center / 72% no-repeat;
box-shadow: inset 0 0 0 var(--stroke-sm) var(--color-input-border-primary);
color: inherit;
cursor: inherit;
transition: background-color 0.15s ease, box-shadow 0.15s ease;
}
.kompan-tag__checkbox:checked {
background-color: var(--color-input-bg-selected);
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%23fff' stroke-width='1.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2.5 6.5 5 9l4.5-6'/%3E%3C/svg%3E");
box-shadow: inset 0 0 0 var(--stroke-sm) var(--color-input-bg-selected);
}
.kompan-tag__checkbox:disabled {
background-color: var(--color-input-bg-disabled);
box-shadow: inset 0 0 0 var(--stroke-sm) var(--color-input-border-primary-disabled);
}
.kompan-tag__checkbox:checked:disabled {
background-color: var(--color-input-bg-selected);
box-shadow: inset 0 0 0 var(--stroke-sm) var(--color-input-bg-selected);
}
.kompan-tag__checkbox:focus-visible {
outline: none;
}
/* Leading: dot / icon / avatar */
.kompan-tag__dot {
--kompan-tag-dot: var(--color-fg-success-secondary);
flex: none;
display: inline-block;
width: 0.5rem;
height: 0.5rem;
margin-left: min(0px, calc(var(--spacing-sm) - var(--kompan-tag-px)));
}
.kompan-tag__dot::before {
content: "";
display: block;
width: 0.375rem;
height: 0.375rem;
margin: 1px;
border-radius: 50%;
background: var(--kompan-tag-dot);
}
.kompan-tag__icon,
.kompan-tag__avatar {
flex: none;
display: block;
width: var(--width-icon-sm);
height: var(--width-icon-sm);
margin-left: var(--kompan-tag-edge);
}
.kompan-tag__avatar {
border-radius: var(--radius-full);
object-fit: cover;
box-shadow: inset 0 0 0 var(--stroke-xs) var(--color-border-tertiary);
}
/* Trailing: count */
.kompan-tag__count {
flex: none;
display: inline-block;
box-sizing: border-box;
min-width: var(--kompan-tag-count-h);
padding: 0 var(--kompan-tag-count-px);
margin-right: var(--kompan-tag-edge);
border-radius: 0.1875rem; /* 3px, unbound in Figma */
background: var(--color-input-bg-secondary);
color: var(--color-input-fg-secondary);
font-size: var(--kompan-tag-count-font-size);
line-height: var(--kompan-tag-count-h);
text-align: center;
}
/* Trailing: remove button */
.kompan-tag__close {
appearance: none;
flex: none;
display: inline-flex;
box-sizing: border-box;
width: var(--kompan-tag-close);
height: var(--kompan-tag-close);
padding: var(--kompan-tag-close-pad);
margin: 0;
margin-right: var(--kompan-tag-edge);
border: 0;
border-radius: 0.1875rem; /* 3px, unbound in Figma */
background: transparent;
color: var(--kompan-tag-close-fg);
cursor: pointer;
transition: color 0.15s ease, background-color 0.15s ease;
}
.kompan-tag__close svg {
display: block;
width: 100%;
height: 100%;
}
.kompan-tag__close:hover {
color: var(--kompan-tag-fg);
background: var(--kompan-tag-bg-hover);
}
.kompan-tag__close:focus-visible {
outline: none;
box-shadow: var(--kompan-tag-focus-ring);
}