Skip to content

QuantityButton

A quantity field with a decrease and increase button on either side.

Used to display any entity quantity and let the user change it, either with the buttons or by typing into the field. onValueChange reports the new quantity for both.

The quantity is held to min and max: the buttons disable at each end, a typed value is capped, and an emptied field settles on min when it loses focus. Arrow keys, PageUp/PageDown and Home/End work too, and holding a button repeats it.

The field needs an accessible name — pass aria-label, or id with a label of your own pointing at it.

import { QuantityButton } from "@caspeco/casper-ui-library";
import { useState } from "react";
export default function Demo() {
const [quantity, setQuantity] = useState(2);
return (
<QuantityButton value={quantity} onValueChange={setQuantity} min={1} max={20} aria-label="quantity" />
);
}
import { Grid, QuantityButton, Stack, Text, ThemeColor, ThemeFontSize } from "@caspeco/casper-ui-library";
import { useState } from "react";
const sizes = ["sm", "lg"] as const;
export default function Demo() {
const [quantities, setQuantities] = useState<Record<(typeof sizes)[number], number>>({
sm: 2,
lg: 2,
});
return (
<Stack gap="8px">
<Grid
templateColumns="repeat(2, auto)"
gap="16px"
alignItems="center"
justifyItems="center"
border="1px solid"
borderColor={ThemeColor.OnBackgroundSubdued}
padding="16px"
borderRadius="8px"
>
{sizes.map((size) => (
<Text
key={`${size}-header`}
fontSize={ThemeFontSize.Small}
textAlign="center"
color={ThemeColor.OnBackgroundHeader}
>
{size.toUpperCase()}
</Text>
))}
{sizes.map((size) => (
<QuantityButton
key={size}
size={size}
value={quantities[size]}
onValueChange={(value) => setQuantities((prev) => ({ ...prev, [size]: value }))}
min={1}
max={20}
aria-label={`quantity (${size})`}
/>
))}
</Grid>
</Stack>
);
}