Menu
Overview
General
ButtonBadgePillsAvatarBrand lockupLayout
ContainerGrid & RowSurface & text utilitiesApp shellPage headerData Display
CardTableChartsCalendarStatsBulletsLeadersLogo wallCollapse & DisclosureTimelineValue chainDescriptionsFeedback
AlertProgressModalSlide-overSkeletonEmpty stateForms
Field & InputDropdownLead form & CTA rowDate pickerFilter barNavigation
NavSide navigationTop navigationApp switcherUser menuStepperTabsBreadcrumbPaginationTemplates
Sign-in pageSigned-out pageSettings layoutTheming
Browser tabProduct themesForms
Filter bar
Search with a two-pane sub-filter sheet, removable active chips, and quick filter chips with live counts.
.filterbar · .quickchips · .filtermenu · .filteropt · .filterchip · .filterrow--nowrap
<div class="filterbar">
<div class="quickchips" role="radiogroup" aria-label="Status">
<label class="quickchip"><input type="radio" name="fb-status" checked><span>All</span><span class="quickchip__count">24</span></label>
<label class="quickchip"><input type="radio" name="fb-status"><span>Settled</span><span class="quickchip__count">18</span></label>
<label class="quickchip"><input type="radio" name="fb-status"><span>Pending</span><span class="quickchip__count">4</span></label>
<label class="quickchip"><input type="radio" name="fb-status"><span>Failed</span><span class="quickchip__count">2</span></label>
</div>
<div class="filterbar__top">
<label class="filterbar__search"><svg class="icon icon--sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="6.5"/><path d="m16 16 4.5 4.5"/></svg><input class="input" type="search" placeholder="Search id, terminal, or product" aria-label="Search transactions"></label>
<details class="filtermenu" data-dismiss>
<summary class="filtermenu__trigger filtermenu__trigger--on"><svg class="icon icon--sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 5h16l-6.2 7.4V18l-3.6 2v-7.6Z"/></svg><span>Filters</span><span class="filtermenu__count">1</span><svg class="icon icon--sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg></summary>
<div class="filtermenu__sheet" role="group" aria-label="Filters">
<div class="filtermenu__tabs" role="radiogroup" aria-label="Filter category">
<label class="filtermenu__tab"><input type="radio" name="fb-cat" checked><span class="truncate">Product</span><span class="filtermenu__tabcount">1</span><svg class="icon icon--sm filtermenu__chev" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m9 6 6 6-6 6"/></svg></label>
<label class="filtermenu__tab"><input type="radio" name="fb-cat"><span class="truncate">Terminal</span><span class="filtermenu__tabcount" hidden>0</span><svg class="icon icon--sm filtermenu__chev" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m9 6 6 6-6 6"/></svg></label>
<label class="filtermenu__tab"><input type="radio" name="fb-cat"><span class="truncate">Amount</span><span class="filtermenu__tabcount" hidden>0</span><svg class="icon icon--sm filtermenu__chev" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m9 6 6 6-6 6"/></svg></label>
</div>
<div class="filtermenu__panels">
<fieldset class="filtermenu__panel"><legend class="filtermenu__head">Product</legend>
<label class="filteropt"><input type="checkbox" name="fb-product"><span class="truncate">novabank</span></label>
<label class="filteropt"><input type="checkbox" name="fb-product" checked><span class="truncate">novapay</span></label>
<label class="filteropt"><input type="checkbox" name="fb-product"><span class="truncate">novastore</span></label>
</fieldset>
<fieldset class="filtermenu__panel"><legend class="filtermenu__head">Terminal</legend>
<label class="filteropt"><input type="checkbox" name="fb-terminal"><span class="truncate">TRM-0100</span></label>
<label class="filteropt"><input type="checkbox" name="fb-terminal"><span class="truncate">TRM-0107</span></label>
<label class="filteropt"><input type="checkbox" name="fb-terminal"><span class="truncate">TRM-0114</span></label>
<label class="filteropt"><input type="checkbox" name="fb-terminal"><span class="truncate">TRM-0121</span></label>
<label class="filteropt"><input type="checkbox" name="fb-terminal"><span class="truncate">TRM-0128</span></label>
<label class="filteropt"><input type="checkbox" name="fb-terminal"><span class="truncate">TRM-0135</span></label>
<label class="filteropt"><input type="checkbox" name="fb-terminal"><span class="truncate">TRM-0142</span></label>
<label class="filteropt"><input type="checkbox" name="fb-terminal"><span class="truncate">TRM-0149</span></label>
</fieldset>
<fieldset class="filtermenu__panel"><legend class="filtermenu__head">Amount</legend>
<label class="filteropt"><input type="checkbox" name="fb-amount"><span class="truncate">Under 200.00</span></label>
<label class="filteropt"><input type="checkbox" name="fb-amount"><span class="truncate">200.00 to 499.99</span></label>
<label class="filteropt"><input type="checkbox" name="fb-amount"><span class="truncate">500.00 and above</span></label>
</fieldset>
</div>
</div>
</details>
</div>
<div class="filterchips" aria-label="Active filters">
<span class="filterchip"><span class="filterchip__group">Product</span> <span class="filterchip__label">novapay</span><button type="button" class="filterchip__remove" aria-label="Remove Product novapay"><svg class="icon icon--sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 6 12 12M18 6 6 18"/></svg></button></span>
<button type="button" class="linkbtn">Clear all</button>
</div>
</div>
<div class="filtermenu__sheet" role="group" aria-label="Filters" style="position:static;width:auto;max-width:34rem">
<div class="filtermenu__tabs" role="radiogroup" aria-label="Filter category">
<label class="filtermenu__tab"><input type="radio" name="fs-cat" checked><span class="truncate">Product</span><span class="filtermenu__tabcount">1</span><svg class="icon icon--sm filtermenu__chev" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m9 6 6 6-6 6"/></svg></label>
<label class="filtermenu__tab"><input type="radio" name="fs-cat"><span class="truncate">Terminal</span><span class="filtermenu__tabcount" hidden>0</span><svg class="icon icon--sm filtermenu__chev" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m9 6 6 6-6 6"/></svg></label>
<label class="filtermenu__tab"><input type="radio" name="fs-cat"><span class="truncate">Amount</span><span class="filtermenu__tabcount" hidden>0</span><svg class="icon icon--sm filtermenu__chev" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m9 6 6 6-6 6"/></svg></label>
</div>
<div class="filtermenu__panels">
<fieldset class="filtermenu__panel"><legend class="filtermenu__head">Product</legend>
<label class="filteropt"><input type="checkbox" name="fs-product"><span class="truncate">novabank</span></label>
<label class="filteropt"><input type="checkbox" name="fs-product" checked><span class="truncate">novapay</span></label>
<label class="filteropt"><input type="checkbox" name="fs-product"><span class="truncate">novastore</span></label>
</fieldset>
<fieldset class="filtermenu__panel"><legend class="filtermenu__head">Terminal</legend>
<label class="filteropt"><input type="checkbox" name="fs-terminal"><span class="truncate">TRM-0100</span></label>
<label class="filteropt"><input type="checkbox" name="fs-terminal"><span class="truncate">TRM-0107</span></label>
<label class="filteropt"><input type="checkbox" name="fs-terminal"><span class="truncate">TRM-0114</span></label>
<label class="filteropt"><input type="checkbox" name="fs-terminal"><span class="truncate">TRM-0121</span></label>
<label class="filteropt"><input type="checkbox" name="fs-terminal"><span class="truncate">TRM-0128</span></label>
<label class="filteropt"><input type="checkbox" name="fs-terminal"><span class="truncate">TRM-0135</span></label>
<label class="filteropt"><input type="checkbox" name="fs-terminal"><span class="truncate">TRM-0142</span></label>
<label class="filteropt"><input type="checkbox" name="fs-terminal"><span class="truncate">TRM-0149</span></label>
</fieldset>
<fieldset class="filtermenu__panel"><legend class="filtermenu__head">Amount</legend>
<label class="filteropt"><input type="checkbox" name="fs-amount"><span class="truncate">Under 200.00</span></label>
<label class="filteropt"><input type="checkbox" name="fs-amount"><span class="truncate">200.00 to 499.99</span></label>
<label class="filteropt"><input type="checkbox" name="fs-amount"><span class="truncate">500.00 and above</span></label>
</fieldset>
</div>
</div>
<div class="filterrow filterrow--nowrap">
<span class="selectwrap"><select class="select" aria-label="Period"><option>Last 90 days</option><option>Last 30 days</option><option>Last 7 days</option><option>Custom range</option></select></span>
<input class="input" type="search" placeholder="Account" aria-label="Account">
<span class="selectwrap"><select class="select" aria-label="Type"><option>All types</option><option>Settlement</option><option>Refund</option></select></span>
<span class="selectwrap"><select class="select" aria-label="Channel"><option>All channels</option><option>Card present</option><option>QR</option></select></span>
<input class="input" type="date" aria-label="From date">
<input class="input" type="date" aria-label="To date">
</div>
Usage
- Search takes the remaining width; the Filters trigger sits at the end of the same row and shows how many filters are on.
- The sheet has two panes: categories on one side (native radio tabs), the chosen category's options on the other (checkboxes). Choosing a category never closes the sheet; long option lists scroll inside their pane; six categories at most. On phones the categories sit above the options.
- Every ticked option appears as a removable chip naming its category and value, followed by Clear all. Options within a category combine with OR; categories combine with AND.
- Quick chips with live counts sit above queue-style lists for the one dimension people switch most (status, bucket); exactly one is selected. Keep that dimension out of the sheet so the two controls never disagree.
- Only a few filters? Use the compact row: plain controls on one line that scrolls sideways instead of wrapping. A dropdown there is a trigger, not a text field — it keeps the Filters button's height, radius and inset padding, and the row's inline padding keeps the first and last control off the edge it scrolls against.
- Everything opens and closes with JavaScript off (native details, radios, checkboxes); applying the filters is the application's job, and an empty result offers to clear them.
CSS
These rules ship in the console pattern layer, admin-kits/shared/novus-admin.css, which this site and every Admin Kit flavor load after tokens.css. Copy the whole layer, or only the sections below; every value is a token.
.filterbar { display: flex; flex-direction: column; gap: var(--space-3); margin-bottom: var(--space-4); }
.quickchips { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.quickchip { position: relative; display: inline-flex; align-items: center; gap: var(--space-1); min-height: 2.25rem; padding: var(--space-1) var(--space-3); border: 1px solid var(--border-strong); border-radius: var(--radius-pill); background: var(--surface); color: var(--text-secondary); font-size: var(--text-sm); font-weight: var(--weight-medium); cursor: pointer; transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease; }
.quickchip input { position: absolute; opacity: 0; pointer-events: none; }
.quickchip:hover { color: var(--text); border-color: var(--accent); }
.quickchip:has(input:checked) { color: var(--text-on-accent); background: var(--accent); border-color: var(--accent); }
.quickchip:has(input:focus-visible) { box-shadow: var(--focus-ring); }
.quickchip__count { font-weight: var(--weight-regular); font-variant-numeric: tabular-nums; }
.quickchip__count::before { content: "("; }
.quickchip__count::after { content: ")"; }
.filterbar__top { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
.filterbar__search { position: relative; flex: 1 1 18rem; min-width: 0; display: flex; align-items: center; }
.filterbar__search > .icon { position: absolute; inset-inline-start: var(--space-3); color: var(--text-muted); pointer-events: none; }
.filterbar__search > .input { min-height: 2.75rem; padding-inline-start: calc(var(--space-3) + 1rem + var(--space-2)); }
.filtermenu { position: relative; flex: 0 0 auto; }
.filtermenu__trigger { list-style: none; display: inline-flex; align-items: center; gap: var(--space-2); min-height: 2.75rem; padding: var(--space-1) var(--space-3); border: 1px solid var(--border-strong); border-radius: var(--radius-md); background: var(--surface); color: var(--text-secondary); font-size: var(--text-sm); font-weight: var(--weight-medium); white-space: nowrap; cursor: pointer; transition: color 0.15s ease, background-color 0.15s ease, border-color 0.15s ease; }
.filtermenu__trigger::-webkit-details-marker { display: none; }
.filtermenu__trigger:hover { color: var(--text); background: var(--bg-subtle); }
.filtermenu__trigger:focus-visible { outline: none; border-color: var(--accent); box-shadow: var(--focus-ring); }
.filtermenu[open] > .filtermenu__trigger, .filtermenu__trigger--on { color: var(--accent-text); border-color: var(--accent); background: var(--accent-subtle); }
.filtermenu__count, .filtermenu__tabcount { min-width: 1.25rem; padding: 0 var(--space-1); border-radius: var(--radius-pill); background: var(--accent); color: var(--text-on-accent); font-size: var(--text-xs); line-height: var(--lh-xs); text-align: center; font-variant-numeric: tabular-nums; }
.filtermenu__count[hidden], .filtermenu__tabcount[hidden] { display: none; }
.filtermenu__sheet { position: absolute; inset-inline-end: 0; top: calc(100% + var(--space-1)); z-index: 40; display: grid; grid-template-columns: minmax(9rem, max-content) minmax(14rem, 1fr); width: min(34rem, calc(100vw - 2 * var(--gutter))); overflow: hidden; background: var(--surface-raised); border: 1px solid var(--border); border-radius: var(--radius-md); box-shadow: var(--shadow-2); font-size: var(--text-sm); line-height: var(--lh-sm); }
.filtermenu__tabs { display: flex; flex-direction: column; gap: var(--space-1); padding: var(--space-2); background: var(--bg-subtle); border-inline-end: 1px solid var(--border); }
.filtermenu__tab { position: relative; display: flex; align-items: center; gap: var(--space-2); min-height: 2.5rem; padding: var(--space-1) var(--space-2); border: 1px solid transparent; border-radius: var(--radius-sm); color: var(--text); white-space: nowrap; cursor: pointer; }
.filtermenu__tab input { position: absolute; opacity: 0; pointer-events: none; }
.filtermenu__tab > .truncate { flex: 1; }
.filtermenu__chev { color: var(--text-muted); }
.filtermenu__tab:hover { background: var(--surface); }
.filtermenu__tab:has(input:checked) { color: var(--accent-text); background: var(--surface); border-color: var(--accent); }
.filtermenu__tab:has(input:focus-visible) { box-shadow: var(--focus-ring); }
.filtermenu__panels { min-width: 0; }
.filtermenu__panel { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; max-height: min(20rem, 60vh); margin: 0; padding: var(--space-3); overflow-y: auto; border: 0; }
.filtermenu__head { padding: 0; margin-bottom: var(--space-1); font-size: var(--text-xs); font-weight: var(--weight-medium); color: var(--text-secondary); }
/* the N-th category tab shows the N-th panel; without :has every panel stays listed */
@supports selector(:has(*)) {
.filtermenu__sheet:has(.filtermenu__tab input:checked) .filtermenu__panel { display: none; }
.filtermenu__sheet:has(.filtermenu__tab:nth-child(1) input:checked) .filtermenu__panel:nth-child(1),
.filtermenu__sheet:has(.filtermenu__tab:nth-child(2) input:checked) .filtermenu__panel:nth-child(2),
.filtermenu__sheet:has(.filtermenu__tab:nth-child(3) input:checked) .filtermenu__panel:nth-child(3),
.filtermenu__sheet:has(.filtermenu__tab:nth-child(4) input:checked) .filtermenu__panel:nth-child(4),
.filtermenu__sheet:has(.filtermenu__tab:nth-child(5) input:checked) .filtermenu__panel:nth-child(5),
.filtermenu__sheet:has(.filtermenu__tab:nth-child(6) input:checked) .filtermenu__panel:nth-child(6) { display: flex; }
}
.filteropt { display: flex; align-items: center; gap: var(--space-2); min-height: 2.25rem; padding: 0 var(--space-2); border-radius: var(--radius-sm); color: var(--text); cursor: pointer; }
.filteropt:hover { background: var(--bg-subtle); }
.filteropt input { width: 1rem; height: 1rem; flex: 0 0 auto; margin: 0; accent-color: var(--accent); }
.filteropt > .truncate { flex: 1; }
.filterdate { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); min-height: 2.75rem; font-size: var(--text-sm); }
.filterdate .input { width: 10rem; min-height: 2.75rem; }
.filterchips { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.filterchips[hidden] { display: none; }
.filterchip { display: inline-flex; align-items: center; gap: var(--space-1); max-width: 100%; min-height: 2rem; padding-inline-start: var(--space-3); border: 1px solid var(--accent); border-radius: var(--radius-pill); background: var(--accent-subtle); color: var(--text); font-size: var(--text-sm); }
.filterchip__group { font-size: var(--text-xs); color: var(--text-secondary); }
.filterchip__label { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.filterchip__remove { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 2rem; height: 2rem; padding: 0; border: 0; border-radius: var(--radius-pill); background: transparent; color: var(--accent-text); cursor: pointer; }
.filterchip__remove::after { content: ""; position: absolute; inset: calc(var(--space-2) * -0.75); }
.filterchip__remove:hover { background: var(--surface); }
.filterchip__remove:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.linkbtn { min-height: 2.25rem; padding: var(--space-1) var(--space-2); border: 0; border-radius: var(--radius-sm); background: transparent; font: inherit; font-size: var(--text-sm); color: var(--accent-text); text-decoration: underline; white-space: nowrap; cursor: pointer; }
.linkbtn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
/* A dropdown in a toolbar is a trigger, not a text field: same height, radius and
inset padding as the Filters button above, so a row of them reads as one set of
controls. The row's inline padding keeps the first and last control off the edge
it scrolls against; a control flush to that edge reads as clipped, not as last. */
.filterrow { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; margin-bottom: var(--space-4); padding-inline: var(--space-1); }
.filterrow--nowrap { flex-wrap: nowrap; overflow-x: auto; padding-bottom: var(--space-1); }
.filterrow--nowrap > * { flex: 0 0 auto; }
.filterrow--nowrap > input[type="search"] { flex: 1 1 14rem; min-width: 11rem; }
.filterrow--nowrap > .input { width: auto; }
.filterrow--nowrap > input[type="date"] { width: 9.5rem; }
.filterrow > .btn { min-height: 2.75rem; }
@media (pointer: coarse) { .quickchip, .filteropt, .filtermenu__tab, .linkbtn { min-height: 2.75rem; } }
@media (pointer: coarse) { .userdd__item { min-height: 2.75rem; } }
@media (max-width: 40rem) {
.filterbar__top { position: relative; }
.filtermenu { position: static; }
.filtermenu__sheet { inset-inline: 0; width: auto; grid-template-columns: minmax(0, 1fr); }
.filtermenu__tabs { flex-direction: row; flex-wrap: wrap; border-inline-end: 0; border-bottom: 1px solid var(--border); }
.filtermenu__chev { display: none; }
}