Raqam
Recipes

Tailwind CSS

Styling raqam with Tailwind CSS — data attributes, focus rings, and dark mode.

raqam ships unstyled. Tailwind classes work naturally on all components. Use data-* state attributes from the root element for conditional styling.

Basic field

import { NumberField } from "raqam";

export function PriceInput() {
  return (
    <NumberField.Root locale="en-US" defaultValue={0} className="flex flex-col gap-1.5">
      <NumberField.Label className="text-sm font-medium text-gray-700">
        Price
      </NumberField.Label>

      <NumberField.Group className="flex rounded-md border border-gray-300 overflow-hidden focus-within:ring-2 focus-within:ring-blue-500 focus-within:border-blue-500">
        <NumberField.Decrement className="px-3 py-2 bg-gray-50 border-r border-gray-300 hover:bg-gray-100 text-gray-500 select-none">

        </NumberField.Decrement>

        <NumberField.Input className="flex-1 px-3 py-2 text-sm outline-none bg-white min-w-0" />

        <NumberField.Increment className="px-3 py-2 bg-gray-50 border-l border-gray-300 hover:bg-gray-100 text-gray-500 select-none">
          +
        </NumberField.Increment>
      </NumberField.Group>
    </NumberField.Root>
  );
}

data-* attribute styling

raqam sets data attributes on the root element. Use Tailwind's data-* variant for state-based styling:

<NumberField.Root
  locale="en-US"
  className="flex flex-col gap-1.5 group"
>
  <NumberField.Label className="text-sm font-medium text-gray-700 group-data-[disabled]:opacity-50">
    Amount
  </NumberField.Label>

  <NumberField.Group
    className={[
      "flex rounded-md border overflow-hidden transition-shadow",
      "border-gray-300",
      "group-data-[focused]:ring-2 group-data-[focused]:ring-blue-500 group-data-[focused]:border-blue-500",
      "group-data-[invalid]:border-red-400 group-data-[invalid]:ring-red-300",
      "group-data-[disabled]:opacity-50 group-data-[disabled]:cursor-not-allowed",
    ].join(" ")}
  >
    <NumberField.Decrement className="px-3 py-2 bg-gray-50 border-r border-gray-200 hover:bg-gray-100 disabled:opacity-40">

    </NumberField.Decrement>
    <NumberField.Input className="flex-1 px-3 py-2 outline-none bg-transparent" />
    <NumberField.Increment className="px-3 py-2 bg-gray-50 border-l border-gray-200 hover:bg-gray-100 disabled:opacity-40">
      +
    </NumberField.Increment>
  </NumberField.Group>

  <NumberField.ErrorMessage className="text-xs text-red-600" />
</NumberField.Root>

Available data attributes on the root:

AttributeWhen set
data-focusedInput is focused
data-invalidValue is invalid
data-disableddisabled={true}
data-readonlyreadOnly={true}
data-requiredrequired={true}
data-scrubbingA scrub drag is in progress

The input element also carries data-invalid, data-disabled, data-readonly, data-required, and data-rtl, so you can style it directly (e.g. data-[invalid]:border-red-500 on <NumberField.Input>).

Dark mode

<NumberField.Group
  className={[
    "flex rounded-md border overflow-hidden",
    "border-gray-300 dark:border-gray-600",
    "bg-white dark:bg-gray-800",
    "focus-within:ring-2 focus-within:ring-blue-500",
  ].join(" ")}
>
  <NumberField.Decrement className="px-3 py-2 bg-gray-50 dark:bg-gray-700 border-r border-gray-200 dark:border-gray-600 text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-600">

  </NumberField.Decrement>
  <NumberField.Input className="flex-1 px-3 py-2 text-gray-900 dark:text-gray-100 bg-transparent outline-none" />
  <NumberField.Increment className="px-3 py-2 bg-gray-50 dark:bg-gray-700 border-l border-gray-200 dark:border-gray-600 text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-600">
    +
  </NumberField.Increment>
</NumberField.Group>

Compact input (no steppers)

<NumberField.Root locale="en-US" className="flex flex-col gap-1">
  <NumberField.Label className="text-xs font-medium text-gray-500 uppercase tracking-wide">
    Quantity
  </NumberField.Label>
  <NumberField.Input className="w-24 px-2 py-1 text-sm border border-gray-300 rounded focus:outline-none focus:ring-1 focus:ring-blue-500" />
</NumberField.Root>

With validation styles

<NumberField.Root
  locale="en-US"
  validate={(v) => v !== null && v > 0 ? true : "Must be positive"}
  className="flex flex-col gap-1.5"
>
  <NumberField.Label className="text-sm font-medium">Price</NumberField.Label>
  <NumberField.Input
    className={[
      "px-3 py-2 border rounded-md text-sm outline-none",
      "border-gray-300 focus:ring-2 focus:ring-blue-400",
      // The input carries data-invalid, so you can style it directly:
      "data-[invalid]:border-red-500 data-[invalid]:ring-red-400",
    ].join(" ")}
  />
  <NumberField.ErrorMessage className="text-xs text-red-600" />
</NumberField.Root>

On this page