SIG—09 Signature system
QueryBuilder
Conditions that read as a sentence: typed operators per field, nested any/all groups, a plain-language reading and an evaluator for local filtering.
Live — this one is real
Illustrative accounts. The table below is filtered live with matchesQuery; the reading comes from describeQuery.
| Region | ||||
|---|---|---|---|---|
| Cedar Studio | pro | 17 | India | 2026-09-22 |
| Bright Labs | pro | 16 | Japan | 2026-02-11 |
| Harbor Health | pro | 10 | Japan | 2026-08-13 |
| Harbor Foods | pro | 14 | Canada | 2026-08-18 |
| Kite Studio | team | 12 | Canada | 2026-08-31 |
| Signal Studio | pro | 11 | Norway | 2026-06-20 |
Usage
"use client";
import { useState } from "react";
import { QueryBuilder, type QueryGroup } from "mizu-ui";
export function Example() {
const [query, setQuery] = useState<QueryGroup>({
id: "root",
combinator: "and",
rules: [{ id: "a", field: "plan", operator: "is", value: "pro" }],
});
return (
<QueryBuilder
label="Audience"
value={query}
onValueChange={setQuery}
fields={[
{
id: "plan",
label: "Plan",
type: "select",
options: [
{ value: "free", label: "Free" },
{ value: "pro", label: "Pro" },
],
},
{ id: "seats", label: "Seats", type: "number" },
{ id: "joined", label: "Joined", type: "date" },
]}
/>
);
}
It starts with "use client" because it keeps state or passes functions to components, so it pastes into a Next.js page as it is. Other React setups ignore the directive.
This code, running — no configuration beyond the data
Source & setup
Add this component to your project with the shadcn CLI. The command copies its source into your configured UI directory, with base.css, Mizu’s tokens, and the stylesheets that hold its rules. Each rule lives in one stylesheet, so components you add together never repeat CSS. In a project without a components.json, run npx shadcn@latest init 0xuser64bit/mizu/preset first: plain init installs shadcn’s theme into your global CSS. Then load the fonts.
npx shadcn@latest add 0xuser64bit/mizu/query-builderEvery component imports from its own path, its name in kebab case. To paste the example, replace its mizu-ui import with:
import { QueryBuilder, type QueryGroup } from "@/components/ui/mizu/query-builder";For npm imports, import mizu-ui/styles.css once. Fonts are optional via mizu-ui/fonts.css. Override --mizu-* tokens or use className for local styling. Requires React and Motion. A Client Component: its source starts with “use client”.
Inspect signature/QueryBuilder.tsx
Source is included in the npm package under src/signature/QueryBuilder.tsx.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| label / fields | string / QueryField[] | — | Region name; fields have id, label, type (text, number, date, select, boolean), options and unit. |
| value / defaultValue / onValueChange | QueryGroup | — | The query: a combinator and rules or nested groups. |
| maxDepth | number | 3 | Group nesting limit. |
| footer | ReactNode | — | Beside the reading, e.g. a live match count. |
| describeQuery(query, fields) | function | — | The plain-language reading, for saved segments. |
| matchesQuery(query, record, fields, now?) | function | — | Evaluate a record locally; incomplete rules are ignored. |
Accessibility
Each group is a fieldset; the all/any choice is a real radio group. Every condition is a named group of native selects and inputs with specific labels, and an incomplete condition is described in words. Additions and removals are announced, and a new condition receives focus.
Motion
New conditions unfold from the left, removed ones close, and the and/or joins roll over like a counter when a group's logic changes. Reduced motion shows each change immediately.