dowel-ui 0.18.2 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,7 +11,7 @@ A dowel is the hidden peg that joins two boards so the seam does not show. That
11
11
  **[Documentation](https://lacodda.github.io/dowel/)** — what everything is and why it is that way.
12
12
  **[The stand](https://lacodda.github.io/dowel/stand/)** — every component, live, in either theme and in the accent of any product of the line.
13
13
 
14
- **Status:** v0.18.2 - the theme, the scales, an accent per product, forty components - overlays, menus, the command palette and the three ways of saying something happened - and the gates each one passes: axe, the keyboard, a dependency budget and a picture in both themes. Components install from a versioned registry, the docs are served in the form an agent reads, and `dowel check` tells a project what stands between it and the vocabulary. Two products of the line live on it. See the [roadmap](#roadmap).
14
+ **Status:** v0.19.0 - the theme, the scales, an accent per product, forty components - overlays, menus, the command palette and the three ways of saying something happened - and the gates each one passes: axe, the keyboard, a dependency budget and a picture in both themes. Every component has a page of its own on the stand, which remembers the theme and the accent you left it in. Components install from a versioned registry, the docs are served in the form an agent reads, and `dowel check` tells a project what stands between it and the vocabulary. Two products of the line live on it. See the [roadmap](#roadmap).
15
15
 
16
16
  ## The theme
17
17
 
@@ -222,7 +222,7 @@
222
222
  "description": "A message that stays on the screen, in the flow of the page, about the thing next to it: this field could not be saved, this profile has no axes yet, this export is out of date.",
223
223
  "dependencies": [
224
224
  "class-variance-authority",
225
- "dowel-ui@^0.18.2"
225
+ "dowel-ui@^0.19.0"
226
226
  ],
227
227
  "registryDependencies": [],
228
228
  "files": [
@@ -241,7 +241,7 @@
241
241
  "description": "A small piece of state attached to something else: a count, a status, a label. It is not a button and never was - if it can be clicked it is a Chip.",
242
242
  "dependencies": [
243
243
  "class-variance-authority",
244
- "dowel-ui@^0.18.2"
244
+ "dowel-ui@^0.19.0"
245
245
  ],
246
246
  "registryDependencies": [],
247
247
  "files": [
@@ -260,7 +260,7 @@
260
260
  "description": "A strip across the top of the application, about the application: you are offline, this build is a preview, your licence expires on Friday, a new version is ready to install.",
261
261
  "dependencies": [
262
262
  "class-variance-authority",
263
- "dowel-ui@^0.18.2"
263
+ "dowel-ui@^0.19.0"
264
264
  ],
265
265
  "registryDependencies": [],
266
266
  "files": [
@@ -280,7 +280,7 @@
280
280
  "dependencies": [
281
281
  "@base-ui/react",
282
282
  "class-variance-authority",
283
- "dowel-ui@^0.18.2"
283
+ "dowel-ui@^0.19.0"
284
284
  ],
285
285
  "registryDependencies": [],
286
286
  "files": [
@@ -314,7 +314,7 @@
314
314
  "title": "Calendar",
315
315
  "description": "The sums live next door in `calendar-math`, which has no React in it; this is the grid that draws them and the keyboard that moves around it.",
316
316
  "dependencies": [
317
- "dowel-ui@^0.18.2"
317
+ "dowel-ui@^0.19.0"
318
318
  ],
319
319
  "registryDependencies": [
320
320
  "https://lacodda.github.io/dowel/r/calendar-math.json"
@@ -335,7 +335,7 @@
335
335
  "description": "The interesting part is the words. A checkbox on its own is a nine-pixel target that says nothing; wired to a label it is the whole row, and the row is what a finger and a pointer both aim at. So the label is part of the component rather than something a caller remembers to add - the commonest bug in a hand-rolled checkbox is a `<label>` that is next to the input instead of tied to it, which looks identical and does nothing.",
336
336
  "dependencies": [
337
337
  "@base-ui/react",
338
- "dowel-ui@^0.18.2"
338
+ "dowel-ui@^0.19.0"
339
339
  ],
340
340
  "registryDependencies": [],
341
341
  "files": [
@@ -354,7 +354,7 @@
354
354
  "description": "A badge you can act on: a filter that can be removed, a tag with a count, a selected value in a field. The difference from a Badge is entirely about whether something happens when you click it - and if something does, that part is a real `<button>` with a real label, not a decorative cross.",
355
355
  "dependencies": [
356
356
  "class-variance-authority",
357
- "dowel-ui@^0.18.2"
357
+ "dowel-ui@^0.19.0"
358
358
  ],
359
359
  "registryDependencies": [],
360
360
  "files": [
@@ -374,7 +374,7 @@
374
374
  "dependencies": [
375
375
  "@base-ui/react",
376
376
  "class-variance-authority",
377
- "dowel-ui@^0.18.2"
377
+ "dowel-ui@^0.19.0"
378
378
  ],
379
379
  "registryDependencies": [
380
380
  "https://lacodda.github.io/dowel/r/input.json",
@@ -385,7 +385,7 @@
385
385
  "path": "ui/combobox.tsx",
386
386
  "target": "@ui/combobox.tsx",
387
387
  "type": "registry:ui",
388
- "content": "import type { Ref } from 'react'\nimport { Combobox as Base } from '@base-ui/react/combobox'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { cn } from 'dowel-ui'\nimport { fieldClasses } from './input'\nimport { selectItemVariants, selectPopupVariants } from './select'\n\n/*\n * Combobox.\n *\n * A Select you can type in. The list narrows as the query is typed, which is\n * the only difference that matters and the reason to reach for this one: a\n * Select stops being usable somewhere around thirty options, and a country\n * picker or a tag field is well past that.\n *\n * Everything the Select comment says about the native element applies here\n * too - there is no `<select>` under it, and the input is a real `<input\n * role=\"combobox\">` so autofill, spellcheck and the phone keyboard still\n * work.\n *\n * Filtering is Base UI's: give the root an `items` array and it matches the\n * query against them with `Intl.Collator`, so accents and case behave the way\n * a reader in that language expects rather than the way `toLowerCase` does.\n * `filter` replaces the comparison; `filter={null}` turns it off for a list\n * that is filtered on a server.\n *\n * The chips are Base UI's too - Chips, Chip, ChipRemove - and that is worth\n * saying because inventing them is the obvious move and it goes wrong in one\n * specific way: hand-made chips end up as `<div>`s with an X that only a\n * pointer can reach, and the multi-select becomes keyboard-inaccessible at\n * exactly the point where it holds the most state. Base UI's are focusable,\n * walk with the arrows, and delete with Backspace.\n *\n * `Empty` renders only when nothing matched, and announces itself politely.\n * Its element stays mounted for that announcement to work, so it must not be\n * hidden with `display: none` or removed conditionally - which is why it is a\n * component here rather than a `{items.length === 0 && …}` in the product.\n */\n\n/* Two bases, chosen by `bare`, rather than one base and an override.\n *\n * Inside `ComboboxChips` the container is the field, so the input has no\n * border, no background and no focus ring of its own - a bordered box inside a\n * bordered box reads as two controls, and two focus rings appear as one thick\n * one. The obvious way to write that is `fieldClasses` plus a few `-none`\n * classes, and it does not work: `tailwind-merge` does not treat\n * `focus-visible:outline-none` as conflicting with\n * `focus-visible:outline-2 … outline-accent`, so both survive and the later\n * one in the stylesheet wins. The same trap took `w-full` versus `w-auto`\n * earlier in this file.\n *\n * So the variant picks which set applies instead of trying to subtract from\n * one - nothing is left to a merge that has no opinion. */\nexport const comboboxInputVariants = cva('', {\n variants: {\n size: {\n sm: 'h-8 text-xs',\n md: 'h-9',\n lg: 'h-10 text-base',\n },\n bare: {\n true: 'h-7 w-auto min-w-24 flex-1 bg-transparent px-1 text-sm text-text placeholder:text-faint outline-none',\n false: fieldClasses,\n },\n },\n defaultVariants: { size: 'md', bare: false },\n})\n\n/** The list, and a row in it, are Select's - imported rather than copied.\n *\n * The two popups are the same object seen twice: a dropdown of options, one\n * of which can be chosen. A reader who uses both on one screen should not be\n * able to tell which is which until they type. Two `cva` calls that started\n * identical do not stay that way - one gets the padding fix - and then the\n * form has two dropdowns that are almost the same. */\nexport const comboboxPopupVariants = selectPopupVariants\nexport const comboboxItemVariants = selectItemVariants\n\n/** The root. `items` is what gets filtered; `multiple` turns the value into an\n * array and makes the chips meaningful. */\nexport const Combobox = Base.Root\n\n/** The wrapper for an input with something beside it - a clear button, an\n * icon, the chips. */\nexport const ComboboxInputGroup = Base.InputGroup\n\n/** The button that opens the list without typing, for a reader who wants to\n * see everything there is. */\nexport const ComboboxTrigger = Base.Trigger\n\n/** The chevron. Decorative. */\nexport const ComboboxIcon = Base.Icon\n\n/** A labelled group of rows. */\nexport const ComboboxGroup = Base.Group\n\n/** The rows of one group, as a render function over that group's items.\n *\n * A `List` is the listbox and there is one per combobox, so a grouped list is\n * a `List` over the groups with a `Collection` inside each - not a `List`\n * inside a `List`. Mapping by hand instead works, but the component then has\n * to be told how to match an item to a value, which is a second place for that\n * knowledge to live. */\nexport const ComboboxCollection = Base.Collection\n\n/** The tick, drawn only on a chosen row. */\nexport const ComboboxItemIndicator = Base.ItemIndicator\n\n/** A polite live region for the state of an asynchronous list. Stays mounted,\n * like `Empty`, so the announcement actually fires. */\nexport const ComboboxStatus = Base.Status\n\n/** The container the chips sit in. Its children are plain nodes, not a render\n * function - the chosen values are mapped by `ComboboxValue` inside it.\n *\n * It wears the field's clothes and lays the chips out in a row that wraps,\n * which is the whole difference between a control and a list: unstyled, the\n * chips stack one per line and the box grows into a column of pills with the\n * input stranded underneath. The input sits on the same line as the last\n * chip and takes the rest of the width, so a half-filled field still looks\n * like a field. */\nexport function ComboboxChips({\n ref,\n className,\n ...props\n}: Base.Chips.Props & { ref?: Ref<HTMLDivElement> }) {\n return (\n <Base.Chips\n // Taken out of `...props` and passed on deliberately: a product needs a\n // handle on this box to anchor the list to it, because the input inside\n // is only as wide as what has been typed.\n ref={ref}\n className={cn(\n fieldClasses,\n 'flex min-h-9 flex-wrap items-center gap-1 py-1',\n 'focus-within:outline-2 focus-within:outline-offset-0 focus-within:outline-accent',\n className,\n )}\n {...props}\n />\n )\n}\n\n/** The current value, as a render function of it. This is what turns a\n * `multiple` value into one chip per entry. */\nexport const ComboboxValue = Base.Value\n\n/** `size` is taken from the native `<input size>` - a width in characters,\n * which nothing here wants - and given to the variant instead. */\nexport interface ComboboxInputProps\n extends Omit<Base.Input.Props, 'size'>,\n VariantProps<typeof comboboxInputVariants> {}\n\n/** Where the query is typed. A real `<input role=\"combobox\">`.\n *\n * Inside `ComboboxChips` it drops its own border and background: the\n * container is the field there, and a bordered input inside a bordered box\n * reads as two controls. */\nexport function ComboboxInput({ size, bare, className, ...props }: ComboboxInputProps) {\n // `bare` is pulled out and handed to `cva`. Left in `...props` it would be\n // spread onto the `<input>` as an unknown attribute and change nothing -\n // which is exactly what it did: the variant existed, the prop was passed,\n // and the class list came out without a trace of either.\n return <Base.Input className={cn(comboboxInputVariants({ size, bare }), className)} {...props} />\n}\n\nconst iconButtonClasses = cn(\n 'rounded-sm p-1 text-faint transition-colors hover:text-text',\n 'focus-visible:outline-2 focus-visible:outline-accent',\n 'data-[disabled]:pointer-events-none data-[disabled]:opacity-50',\n)\n\n/** Empties the value. Base UI hides it while there is nothing to clear. */\nexport function ComboboxClear({ className, ...props }: Base.Clear.Props) {\n return <Base.Clear className={cn(iconButtonClasses, className)} {...props} />\n}\n\nexport interface ComboboxPopupProps\n extends Base.Popup.Props,\n VariantProps<typeof comboboxPopupVariants> {\n /** Preferred side of the input. Base UI flips it when it does not fit. */\n side?: Base.Positioner.Props['side']\n /** Alignment along that side. */\n align?: Base.Positioner.Props['align']\n /** Distance from the input, in pixels. */\n sideOffset?: Base.Positioner.Props['sideOffset']\n /** Where to portal to. Defaults to the document body. */\n container?: Base.Portal.Props['container']\n}\n\n/** The list. Portalled and positioned against the input. */\nexport function ComboboxPopup({\n size,\n side,\n align,\n sideOffset = 4,\n container,\n className,\n children,\n ...props\n}: ComboboxPopupProps) {\n return (\n <Base.Portal container={container}>\n <Base.Positioner\n side={side}\n align={align}\n sideOffset={sideOffset}\n className=\"[z-index:var(--z-menu)]\"\n >\n <Base.Popup className={cn(comboboxPopupVariants({ size }), className)} {...props}>\n {children}\n </Base.Popup>\n </Base.Positioner>\n </Base.Portal>\n )\n}\n\n/** The rows, as a list. Undressed: it is a wrapper, and the popup around it\n * already carries the border and the padding. */\nexport const ComboboxList = Base.List\n\n/** A row. */\nexport function ComboboxItem({ className, ...props }: Base.Item.Props) {\n return <Base.Item className={cn(comboboxItemVariants(), className)} {...props} />\n}\n\n/** What is shown when nothing matched. The words are the product's. */\nexport function ComboboxEmpty({ className, ...props }: Base.Empty.Props) {\n return <Base.Empty className={cn('px-2 py-3 text-center text-sm text-faint', className)} {...props} />\n}\n\n/** One chosen value, in a multiple combobox. Focusable, so it can be reached\n * and removed without a pointer. */\nexport function ComboboxChip({ className, ...props }: Base.Chip.Props) {\n return (\n <Base.Chip\n className={cn(\n 'flex items-center gap-1 rounded-sm bg-soft px-1.5 py-0.5 text-xs text-text',\n 'outline-none data-[highlighted]:bg-accent-soft data-[highlighted]:text-accent',\n className,\n )}\n {...props}\n />\n )\n}\n\n/** The X on a chip. A real button, which is what makes Backspace and Enter\n * both work on it. */\nexport function ComboboxChipRemove({ className, ...props }: Base.ChipRemove.Props) {\n return <Base.ChipRemove className={cn(iconButtonClasses, 'p-0', className)} {...props} />\n}\n\n/** The caption above a group. */\nexport function ComboboxGroupLabel({ className, ...props }: Base.GroupLabel.Props) {\n return (\n <Base.GroupLabel\n className={cn('px-2 py-1.5 text-2xs uppercase tracking-caption text-faint', className)}\n {...props}\n />\n )\n}\n"
388
+ "content": "import type { Ref } from 'react'\nimport { Combobox as Base } from '@base-ui/react/combobox'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { cn } from 'dowel-ui'\nimport { fieldClasses } from './input'\nimport { selectItemVariants, selectPopupVariants } from './select'\n\n/*\n * Combobox.\n *\n * A Select you can type in. The list narrows as the query is typed, which is\n * the only difference that matters and the reason to reach for this one: a\n * Select stops being usable somewhere around thirty options, and a country\n * picker or a tag field is well past that.\n *\n * Everything the Select comment says about the native element applies here\n * too - there is no `<select>` under it, and the input is a real `<input\n * role=\"combobox\">` so autofill, spellcheck and the phone keyboard still\n * work.\n *\n * Filtering is Base UI's: give the root an `items` array and it matches the\n * query against them with `Intl.Collator`, so accents and case behave the way\n * a reader in that language expects rather than the way `toLowerCase` does.\n * `filter` replaces the comparison; `filter={null}` turns it off for a list\n * that is filtered on a server.\n *\n * The chips are Base UI's too - Chips, Chip, ChipRemove - and that is worth\n * saying because inventing them is the obvious move and it goes wrong in one\n * specific way: hand-made chips end up as `<div>`s with an X that only a\n * pointer can reach, and the multi-select becomes keyboard-inaccessible at\n * exactly the point where it holds the most state. Base UI's are focusable,\n * walk with the arrows, and delete with Backspace.\n *\n * `Empty` renders only when nothing matched, and announces itself politely.\n * Its element stays mounted for that announcement to work, so it must not be\n * hidden with `display: none` or removed conditionally - which is why it is a\n * component here rather than a `{items.length === 0 && …}` in the product.\n */\n\n/* Two bases, chosen by `bare`, rather than one base and an override.\n *\n * Inside `ComboboxChips` the container is the field, so the input has no\n * border, no background and no focus ring of its own - a bordered box inside a\n * bordered box reads as two controls, and two focus rings appear as one thick\n * one. The obvious way to write that is `fieldClasses` plus a few `-none`\n * classes, and it does not work: `tailwind-merge` does not treat\n * `focus-visible:outline-none` as conflicting with\n * `focus-visible:outline-2 … outline-accent`, so both survive and the later\n * one in the stylesheet wins. The same trap took `w-full` versus `w-auto`\n * earlier in this file.\n *\n * So the variant picks which set applies instead of trying to subtract from\n * one - nothing is left to a merge that has no opinion. */\nexport const comboboxInputVariants = cva('', {\n variants: {\n size: {\n sm: 'h-8 text-xs',\n md: 'h-9',\n lg: 'h-10 text-base',\n },\n bare: {\n true: 'h-7 w-auto min-w-24 flex-1 bg-transparent px-1 text-sm text-text placeholder:text-faint outline-none',\n false: fieldClasses,\n },\n },\n defaultVariants: { size: 'md', bare: false },\n})\n\n/** The list, and a row in it, are Select's - imported rather than copied.\n *\n * The two popups are the same object seen twice: a dropdown of options, one\n * of which can be chosen. A reader who uses both on one screen should not be\n * able to tell which is which until they type. Two `cva` calls that started\n * identical do not stay that way - one gets the padding fix - and then the\n * form has two dropdowns that are almost the same. */\nexport const comboboxPopupVariants = selectPopupVariants\nexport const comboboxItemVariants = selectItemVariants\n\n/** The root. `items` is what gets filtered; `multiple` turns the value into an\n * array and makes the chips meaningful. */\nexport const Combobox = Base.Root\n\n/** The wrapper for an input with something beside it - a clear button, an\n * icon, the chips. */\nexport const ComboboxInputGroup = Base.InputGroup\n\n/** The button that opens the list without typing, for a reader who wants to\n * see everything there is. */\nexport const ComboboxTrigger = Base.Trigger\n\n/** The chevron. Decorative. */\nexport const ComboboxIcon = Base.Icon\n\n/** A labelled group of rows. */\nexport const ComboboxGroup = Base.Group\n\n/** The rows of one group, as a render function over that group's items.\n *\n * A `List` is the listbox and there is one per combobox, so a grouped list is\n * a `List` over the groups with a `Collection` inside each - not a `List`\n * inside a `List`. Mapping by hand instead works, but the component then has\n * to be told how to match an item to a value, which is a second place for that\n * knowledge to live. */\nexport const ComboboxCollection = Base.Collection\n\n/** The tick, drawn only on a chosen row. */\nexport const ComboboxItemIndicator = Base.ItemIndicator\n\n/** A polite live region for the state of an asynchronous list. Stays mounted,\n * like `Empty`, so the announcement actually fires. */\nexport const ComboboxStatus = Base.Status\n\n/** The container the chips sit in. Its children are plain nodes, not a render\n * function - the chosen values are mapped by `ComboboxValue` inside it.\n *\n * It wears the field's clothes and lays the chips out in a row that wraps,\n * which is the whole difference between a control and a list: unstyled, the\n * chips stack one per line and the box grows into a column of pills with the\n * input stranded underneath. The input sits on the same line as the last\n * chip and takes the rest of the width, so a half-filled field still looks\n * like a field. */\nexport function ComboboxChips({\n ref,\n className,\n ...props\n}: Base.Chips.Props & { ref?: Ref<HTMLDivElement> }) {\n return (\n <Base.Chips\n // Taken out of `...props` and passed on deliberately: a product needs a\n // handle on this box to anchor the list to it, because the input inside\n // is only as wide as what has been typed.\n ref={ref}\n className={cn(\n fieldClasses,\n 'flex min-h-9 flex-wrap items-center gap-1 py-1',\n 'focus-within:outline-2 focus-within:outline-offset-0 focus-within:outline-accent',\n className,\n )}\n {...props}\n />\n )\n}\n\n/** The current value, as a render function of it. This is what turns a\n * `multiple` value into one chip per entry. */\nexport const ComboboxValue = Base.Value\n\n/** `size` is taken from the native `<input size>` - a width in characters,\n * which nothing here wants - and given to the variant instead. */\nexport interface ComboboxInputProps\n extends Omit<Base.Input.Props, 'size'>,\n VariantProps<typeof comboboxInputVariants> {}\n\n/** Where the query is typed. A real `<input role=\"combobox\">`.\n *\n * Inside `ComboboxChips` it drops its own border and background: the\n * container is the field there, and a bordered input inside a bordered box\n * reads as two controls. */\nexport function ComboboxInput({ size, bare, className, ...props }: ComboboxInputProps) {\n // `bare` is pulled out and handed to `cva`. Left in `...props` it would be\n // spread onto the `<input>` as an unknown attribute and change nothing -\n // which is exactly what it did: the variant existed, the prop was passed,\n // and the class list came out without a trace of either.\n return <Base.Input className={cn(comboboxInputVariants({ size, bare }), className)} {...props} />\n}\n\nconst iconButtonClasses = cn(\n 'rounded-sm p-1 text-faint transition-colors hover:text-text',\n 'focus-visible:outline-2 focus-visible:outline-accent',\n 'data-[disabled]:pointer-events-none data-[disabled]:opacity-50',\n)\n\n/** Empties the value. Base UI hides it while there is nothing to clear. */\nexport function ComboboxClear({ className, ...props }: Base.Clear.Props) {\n return <Base.Clear className={cn(iconButtonClasses, className)} {...props} />\n}\n\nexport interface ComboboxPopupProps\n extends Base.Popup.Props,\n VariantProps<typeof comboboxPopupVariants> {\n /** Preferred side of the input. Base UI flips it when it does not fit. */\n side?: Base.Positioner.Props['side']\n /** Alignment along that side. */\n align?: Base.Positioner.Props['align']\n /** Distance from the input, in pixels. */\n sideOffset?: Base.Positioner.Props['sideOffset']\n /**\n * What to line the list up with. Defaults to the input that owns it.\n *\n * Pass the `ComboboxChips` box when there is one: with chips, the input is\n * only as wide as what has been typed - an empty one measured 214px inside\n * a 288px field - so a list anchored to it hangs short of the box a reader\n * sees. Base UI publishes the anchor's width as `--anchor-width`, which is\n * how the mismatch is visible from outside.\n */\n anchor?: Base.Positioner.Props['anchor']\n /** Where to portal to. Defaults to the document body. */\n container?: Base.Portal.Props['container']\n}\n\n/** The list. Portalled and positioned against the input, or the given anchor. */\nexport function ComboboxPopup({\n size,\n side,\n align,\n sideOffset = 4,\n anchor,\n container,\n className,\n children,\n ...props\n}: ComboboxPopupProps) {\n return (\n <Base.Portal container={container}>\n <Base.Positioner\n side={side}\n align={align}\n sideOffset={sideOffset}\n anchor={anchor}\n className=\"[z-index:var(--z-menu)]\"\n >\n <Base.Popup className={cn(comboboxPopupVariants({ size }), className)} {...props}>\n {children}\n </Base.Popup>\n </Base.Positioner>\n </Base.Portal>\n )\n}\n\n/** The rows, as a list. Undressed: it is a wrapper, and the popup around it\n * already carries the border and the padding. */\nexport const ComboboxList = Base.List\n\n/** A row. */\nexport function ComboboxItem({ className, ...props }: Base.Item.Props) {\n return <Base.Item className={cn(comboboxItemVariants(), className)} {...props} />\n}\n\n/** What is shown when nothing matched. The words are the product's. */\nexport function ComboboxEmpty({ className, ...props }: Base.Empty.Props) {\n return <Base.Empty className={cn('px-2 py-3 text-center text-sm text-faint', className)} {...props} />\n}\n\n/** One chosen value, in a multiple combobox. Focusable, so it can be reached\n * and removed without a pointer. */\nexport function ComboboxChip({ className, ...props }: Base.Chip.Props) {\n return (\n <Base.Chip\n className={cn(\n 'flex items-center gap-1 rounded-sm bg-soft px-1.5 py-0.5 text-xs text-text',\n 'outline-none data-[highlighted]:bg-accent-soft data-[highlighted]:text-accent',\n className,\n )}\n {...props}\n />\n )\n}\n\n/** The X on a chip. A real button, which is what makes Backspace and Enter\n * both work on it. */\nexport function ComboboxChipRemove({ className, ...props }: Base.ChipRemove.Props) {\n return <Base.ChipRemove className={cn(iconButtonClasses, 'p-0', className)} {...props} />\n}\n\n/** The caption above a group. */\nexport function ComboboxGroupLabel({ className, ...props }: Base.GroupLabel.Props) {\n return (\n <Base.GroupLabel\n className={cn('px-2 py-1.5 text-2xs uppercase tracking-caption text-faint', className)}\n {...props}\n />\n )\n}\n"
389
389
  }
390
390
  ]
391
391
  },
@@ -397,7 +397,7 @@
397
397
  "dependencies": [
398
398
  "@base-ui/react",
399
399
  "class-variance-authority",
400
- "dowel-ui@^0.18.2"
400
+ "dowel-ui@^0.19.0"
401
401
  ],
402
402
  "registryDependencies": [
403
403
  "https://lacodda.github.io/dowel/r/combobox.json",
@@ -420,7 +420,7 @@
420
420
  "dependencies": [
421
421
  "@base-ui/react",
422
422
  "class-variance-authority",
423
- "dowel-ui@^0.18.2"
423
+ "dowel-ui@^0.19.0"
424
424
  ],
425
425
  "registryDependencies": [],
426
426
  "files": [
@@ -439,7 +439,7 @@
439
439
  "description": "The same list of actions as Menu, opened the other way round: by right click, or by a long press on a touch screen, over an *area* rather than from a button. So the trigger is not a control - it is the region the menu belongs to, a row, a canvas, a file tile - and it renders a `<div>`.",
440
440
  "dependencies": [
441
441
  "@base-ui/react",
442
- "dowel-ui@^0.18.2"
442
+ "dowel-ui@^0.19.0"
443
443
  ],
444
444
  "registryDependencies": [
445
445
  "https://lacodda.github.io/dowel/r/menu.json"
@@ -459,7 +459,7 @@
459
459
  "title": "Copyable",
460
460
  "description": "Any text that someone will eventually want to copy - an id, a path, a hash, a token - copied with one click. The rule comes from nitid: if a value is worth showing, it is worth being able to take away, and selecting a monospaced id by hand is a small daily tax.",
461
461
  "dependencies": [
462
- "dowel-ui@^0.18.2"
462
+ "dowel-ui@^0.19.0"
463
463
  ],
464
464
  "registryDependencies": [],
465
465
  "files": [
@@ -477,7 +477,7 @@
477
477
  "title": "Date-picker",
478
478
  "description": "The trigger is a button rather than a text input, and that is the decision worth stating. A typable date field has to answer \"what does `03/04/26` mean\" in a locale it cannot be sure of, and it answers wrong for half the world; a button showing the date spelled out has no such question. Where typing genuinely matters - a birth date, forty years back - the calendar is the wrong control anyway and a product should reach for a plain field.",
479
479
  "dependencies": [
480
- "dowel-ui@^0.18.2"
480
+ "dowel-ui@^0.19.0"
481
481
  ],
482
482
  "registryDependencies": [
483
483
  "https://lacodda.github.io/dowel/r/calendar.json",
@@ -500,7 +500,7 @@
500
500
  "title": "Date-range-picker",
501
501
  "description": "The interesting part is the state between them. After the first click there is a start and no end, and that is not an incomplete range to be hidden or a range of one day - it is the normal middle of the interaction, and the calendar has to show it: the first day marked, the days under the pointer shading as the reader moves, the popup staying open. Products that skip it end up with a picker that seems to do nothing until the second click.",
502
502
  "dependencies": [
503
- "dowel-ui@^0.18.2"
503
+ "dowel-ui@^0.19.0"
504
504
  ],
505
505
  "registryDependencies": [
506
506
  "https://lacodda.github.io/dowel/r/calendar.json",
@@ -525,7 +525,7 @@
525
525
  "dependencies": [
526
526
  "@base-ui/react",
527
527
  "class-variance-authority",
528
- "dowel-ui@^0.18.2"
528
+ "dowel-ui@^0.19.0"
529
529
  ],
530
530
  "registryDependencies": [],
531
531
  "files": [
@@ -545,7 +545,7 @@
545
545
  "dependencies": [
546
546
  "@base-ui/react",
547
547
  "class-variance-authority",
548
- "dowel-ui@^0.18.2"
548
+ "dowel-ui@^0.19.0"
549
549
  ],
550
550
  "registryDependencies": [],
551
551
  "files": [
@@ -563,7 +563,7 @@
563
563
  "title": "Duration-field",
564
564
  "description": "The alternative is what products keep building: two number boxes labelled \"hours\" and \"minutes\", which means two tab stops, two validations, and a reader who has to divide 90 minutes in their head before typing. Here they write `1h 30m`, or `90m`, or `1.5h`, and it means the same thing.",
565
565
  "dependencies": [
566
- "dowel-ui@^0.18.2"
566
+ "dowel-ui@^0.19.0"
567
567
  ],
568
568
  "registryDependencies": [
569
569
  "https://lacodda.github.io/dowel/r/input.json"
@@ -584,7 +584,7 @@
584
584
  "description": "Every form is the same four parts repeated: a name for the control, the control, sometimes a hint, and sometimes an error. Written by hand each time, they drift - the label loses its `htmlFor`, the hint is a `<div>` no screen reader mentions, the error appears in red and is announced by nothing at all. This is that arrangement, once.",
585
585
  "dependencies": [
586
586
  "@base-ui/react",
587
- "dowel-ui@^0.18.2"
587
+ "dowel-ui@^0.19.0"
588
588
  ],
589
589
  "registryDependencies": [],
590
590
  "files": [
@@ -602,7 +602,7 @@
602
602
  "title": "Input",
603
603
  "description": "A single-line field. It is a plain `<input>` with the line's clothes on, so everything a browser gives an input for free - autofill, spellcheck, the right keyboard on a phone, `type=\"email\"` validation - still works.",
604
604
  "dependencies": [
605
- "dowel-ui@^0.18.2"
605
+ "dowel-ui@^0.19.0"
606
606
  ],
607
607
  "registryDependencies": [],
608
608
  "files": [
@@ -620,7 +620,7 @@
620
620
  "title": "Kbd",
621
621
  "description": "A key, as printed in a menu or a hint: `Ctrl` `K`. It is a `<kbd>` element because that is what the element is for - a screen reader announces it as keyboard input rather than reading a stray capital letter.",
622
622
  "dependencies": [
623
- "dowel-ui@^0.18.2"
623
+ "dowel-ui@^0.19.0"
624
624
  ],
625
625
  "registryDependencies": [],
626
626
  "files": [
@@ -640,7 +640,7 @@
640
640
  "dependencies": [
641
641
  "@base-ui/react",
642
642
  "class-variance-authority",
643
- "dowel-ui@^0.18.2"
643
+ "dowel-ui@^0.19.0"
644
644
  ],
645
645
  "registryDependencies": [],
646
646
  "files": [
@@ -659,7 +659,7 @@
659
659
  "description": "A number typed into a text input is a string that happens to look like a number, and every product then writes the same four fixes: strip the letters, clamp to a range, round to a step, and decide what an empty box means. This is those four, once, plus the stepper - because a value with a small range is faster nudged than typed.",
660
660
  "dependencies": [
661
661
  "@base-ui/react",
662
- "dowel-ui@^0.18.2"
662
+ "dowel-ui@^0.19.0"
663
663
  ],
664
664
  "registryDependencies": [
665
665
  "https://lacodda.github.io/dowel/r/input.json"
@@ -680,7 +680,7 @@
680
680
  "description": "The raised surface everything else sits on. It is the one place a screen gets its structure from, so it stays deliberately plain: a ground, a hairline, a corner.",
681
681
  "dependencies": [
682
682
  "class-variance-authority",
683
- "dowel-ui@^0.18.2"
683
+ "dowel-ui@^0.19.0"
684
684
  ],
685
685
  "registryDependencies": [],
686
686
  "files": [
@@ -698,7 +698,7 @@
698
698
  "title": "Password-field",
699
699
  "description": "The reveal is the whole component, and it is not a convenience. A masked field is the only one in a form where a typo cannot be seen, so people either paste (fine) or type slowly and get it wrong anyway; the toggle is what turns an unverifiable field into a checkable one, and it is why long passphrases became usable at all.",
700
700
  "dependencies": [
701
- "dowel-ui@^0.18.2"
701
+ "dowel-ui@^0.19.0"
702
702
  ],
703
703
  "registryDependencies": [
704
704
  "https://lacodda.github.io/dowel/r/input.json"
@@ -720,7 +720,7 @@
720
720
  "dependencies": [
721
721
  "@base-ui/react",
722
722
  "class-variance-authority",
723
- "dowel-ui@^0.18.2"
723
+ "dowel-ui@^0.19.0"
724
724
  ],
725
725
  "registryDependencies": [],
726
726
  "files": [
@@ -740,7 +740,7 @@
740
740
  "dependencies": [
741
741
  "@base-ui/react",
742
742
  "class-variance-authority",
743
- "dowel-ui@^0.18.2"
743
+ "dowel-ui@^0.19.0"
744
744
  ],
745
745
  "registryDependencies": [],
746
746
  "files": [
@@ -760,7 +760,7 @@
760
760
  "dependencies": [
761
761
  "@base-ui/react",
762
762
  "class-variance-authority",
763
- "dowel-ui@^0.18.2"
763
+ "dowel-ui@^0.19.0"
764
764
  ],
765
765
  "registryDependencies": [],
766
766
  "files": [
@@ -778,7 +778,7 @@
778
778
  "title": "Rating-scale",
779
779
  "description": "Generalised from kilna, where it is how a work is scored on each of its axes. The shape is a row of marks rather than stars: stars carry a meaning of their own - a review, a public verdict - and this is as often \"how hard was this\" or \"how finished is it\" as it is \"how good\".",
780
780
  "dependencies": [
781
- "dowel-ui@^0.18.2"
781
+ "dowel-ui@^0.19.0"
782
782
  ],
783
783
  "registryDependencies": [],
784
784
  "files": [
@@ -796,7 +796,7 @@
796
796
  "title": "Search-field",
797
797
  "description": "An Input that knows it is a search box, which is three small things the products kept not doing:\n * - a magnifier, so the field is recognisable before it is read; - a way to clear it that is not \"select all and delete\" - and one that a keyboard can reach, which a decorative `<span>` cannot; - the shortcut that focuses it, shown in the field rather than learned.",
798
798
  "dependencies": [
799
- "dowel-ui@^0.18.2"
799
+ "dowel-ui@^0.19.0"
800
800
  ],
801
801
  "registryDependencies": [
802
802
  "https://lacodda.github.io/dowel/r/input.json",
@@ -820,7 +820,7 @@
820
820
  "dependencies": [
821
821
  "@base-ui/react",
822
822
  "class-variance-authority",
823
- "dowel-ui@^0.18.2"
823
+ "dowel-ui@^0.19.0"
824
824
  ],
825
825
  "registryDependencies": [
826
826
  "https://lacodda.github.io/dowel/r/input.json"
@@ -857,7 +857,7 @@
857
857
  "description": "The case for it over a NumberField is that the number does not matter much: a volume, an opacity, a weight in a search filter. Where the exact figure does matter, a slider is a worse field with more pixels - it cannot be typed into, it cannot be pasted into, and it has no state for \"empty\".",
858
858
  "dependencies": [
859
859
  "@base-ui/react",
860
- "dowel-ui@^0.18.2"
860
+ "dowel-ui@^0.19.0"
861
861
  ],
862
862
  "registryDependencies": [],
863
863
  "files": [
@@ -876,7 +876,7 @@
876
876
  "description": "Something is happening and the answer has not arrived. It carries no text of its own - what is loading is the product's word, not the system's - but it does have to say *something* to a screen reader, or a page that is busy is silently identical to a page that is empty.",
877
877
  "dependencies": [
878
878
  "class-variance-authority",
879
- "dowel-ui@^0.18.2"
879
+ "dowel-ui@^0.19.0"
880
880
  ],
881
881
  "registryDependencies": [],
882
882
  "files": [
@@ -895,7 +895,7 @@
895
895
  "description": "The difference from Checkbox is not how it looks, and getting it wrong is the commonest mistake in the pair. A checkbox is an answer collected now and submitted later, with the rest of the form; a switch is a setting that applies the moment it moves. Put a switch in a form with a Save button and the reader cannot tell whether anything happened - they flipped it, and nothing said so.",
896
896
  "dependencies": [
897
897
  "@base-ui/react",
898
- "dowel-ui@^0.18.2"
898
+ "dowel-ui@^0.19.0"
899
899
  ],
900
900
  "registryDependencies": [],
901
901
  "files": [
@@ -913,7 +913,7 @@
913
913
  "title": "Textarea",
914
914
  "description": "A multi-line field that can grow with what is typed into it, which is the only interesting part: a fixed box makes someone scroll inside a scroll, and a box that grows without limit pushes the button they are trying to reach off the screen. `autoResize` grows it; `maxRows` says when to stop and let it scroll after all.",
915
915
  "dependencies": [
916
- "dowel-ui@^0.18.2"
916
+ "dowel-ui@^0.19.0"
917
917
  ],
918
918
  "registryDependencies": [
919
919
  "https://lacodda.github.io/dowel/r/input.json"
@@ -933,7 +933,7 @@
933
933
  "title": "Time-field",
934
934
  "description": "No donor for this one: neither product of the line had a time field, so this is written from the same shape as DurationField, and for the same reason. Anything a person plausibly types is accepted - `9`, `9:30`, `930`, `9.30`, `9pm`, `21:30` - and what comes back is always `HH:MM`.",
935
935
  "dependencies": [
936
- "dowel-ui@^0.18.2"
936
+ "dowel-ui@^0.19.0"
937
937
  ],
938
938
  "registryDependencies": [
939
939
  "https://lacodda.github.io/dowel/r/input.json"
@@ -955,7 +955,7 @@
955
955
  "dependencies": [
956
956
  "@base-ui/react",
957
957
  "class-variance-authority",
958
- "dowel-ui@^0.18.2"
958
+ "dowel-ui@^0.19.0"
959
959
  ],
960
960
  "registryDependencies": [],
961
961
  "files": [
@@ -975,7 +975,7 @@
975
975
  "dependencies": [
976
976
  "@base-ui/react",
977
977
  "class-variance-authority",
978
- "dowel-ui@^0.18.2"
978
+ "dowel-ui@^0.19.0"
979
979
  ],
980
980
  "registryDependencies": [],
981
981
  "files": [
@@ -993,7 +993,7 @@
993
993
  "title": "Truncate",
994
994
  "description": "Text that does not fit, cut with an ellipsis - and, importantly, still readable in full: the element carries its own text as a `title`, so hovering shows what was cut. Every product wrote the one-line version of this and none of them remembered the title.",
995
995
  "dependencies": [
996
- "dowel-ui@^0.18.2"
996
+ "dowel-ui@^0.19.0"
997
997
  ],
998
998
  "registryDependencies": [],
999
999
  "files": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dowel-ui",
3
- "version": "0.18.2",
3
+ "version": "0.19.0",
4
4
  "description": "The lacodda line design system: theme tokens and React primitives, distributed as a shadcn-compatible registry.",
5
5
  "license": "MIT",
6
6
  "type": "module",