Number Field
A numeric input with increment and decrement controls.
View source
import * as NumberField from "@danielfrg/solid-ui/number-field"
import styles from "./index.module.css"
export function DemoNumberFieldHero() {
return (
<NumberField.Root class={styles.root} defaultValue={50} minValue={0} maxValue={100}>
<NumberField.Label class={styles.label}>Quantity</NumberField.Label>
<div class={styles.group}>
<NumberField.DecrementTrigger class={styles.button} aria-label="Decrement">
<MinusIcon />
</NumberField.DecrementTrigger>
<NumberField.Input class={styles.input} />
<NumberField.IncrementTrigger class={styles.button} aria-label="Increment">
<PlusIcon />
</NumberField.IncrementTrigger>
</div>
<NumberField.HiddenInput />
</NumberField.Root>
)
}
function MinusIcon() {
return (
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
<path d="M3 7H11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
</svg>
)
}
function PlusIcon() {
return (
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
<path d="M7 3V11M3 7H11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
</svg>
)
}.root {
display: flex;
flex-direction: column;
gap: 0.375rem;
width: 10rem;
}
.label {
font-size: 0.875rem;
line-height: 1.25rem;
font-weight: 500;
color: var(--color-gray-900);
}
.group {
display: flex;
align-items: center;
border: 1px solid var(--color-gray-300);
border-radius: 6px;
overflow: hidden;
}
.input {
all: unset;
flex: 1;
text-align: center;
font-size: 0.875rem;
padding: 6px 0;
color: var(--color-gray-900);
min-width: 0;
}
.button {
all: unset;
display: inline-flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 2rem;
color: var(--color-gray-600);
cursor: pointer;
flex-shrink: 0;
}
.button:hover {
background-color: var(--color-gray-100);
color: var(--color-gray-900);
}
.button[data-disabled] {
opacity: 0.4;
cursor: default;
}
.description {
font-size: 0.75rem;
line-height: 1.25rem;
color: var(--color-gray-600);
}
.error {
font-size: 0.75rem;
line-height: 1.25rem;
color: #dc2626;
}
.status {
margin: 1rem 0 0;
font-size: 0.875rem;
line-height: 1.25rem;
color: var(--color-gray-600);
}
.form {
display: flex;
flex-direction: column;
gap: 1rem;
}
.actions {
display: flex;
gap: 0.5rem;
}:root {
--color-blue: oklch(45% 50% 264deg);
--color-red: oklch(50% 55% 31deg);
--color-gray-50: oklch(98% 0.25% 264deg);
--color-gray-100: oklch(12% 9.5% 264deg / 5%);
--color-gray-200: oklch(12% 9% 264deg / 7%);
--color-gray-300: oklch(12% 8.5% 264deg / 17%);
--color-gray-400: oklch(12% 8% 264deg / 38%);
--color-gray-500: oklch(12% 7.5% 264deg / 50%);
--color-gray-600: oklch(12% 7% 264deg / 67%);
--color-gray-700: oklch(12% 6% 264deg / 77%);
--color-gray-800: oklch(12% 5% 264deg / 85%);
--color-gray-900: oklch(12% 5% 264deg / 90%);
--color-gray-950: oklch(12% 5% 264deg / 95%);
}
@media (prefers-color-scheme: dark) {
:root {
--color-blue: oklch(69% 50% 264deg);
--color-red: oklch(80% 55% 31deg);
--color-gray-50: oklch(17% 0.25% 264deg);
--color-gray-100: oklch(28% 0.75% 264deg / 65%);
--color-gray-200: oklch(29% 0.75% 264deg / 80%);
--color-gray-300: oklch(35% 0.75% 264deg / 80%);
--color-gray-400: oklch(47% 0.875% 264deg / 80%);
--color-gray-500: oklch(64% 1% 264deg / 80%);
--color-gray-600: oklch(82% 1% 264deg / 80%);
--color-gray-700: oklch(92% 1.125% 264deg / 80%);
--color-gray-800: oklch(93% 0.875% 264deg / 85%);
--color-gray-900: oklch(95% 0.5% 264deg / 90%);
--color-gray-950: oklch(94% 0.375% 264deg / 95%);
}
}Examples
Default value
Use defaultValue to set the initial value without controlling the state.
View source
import * as NumberField from "@danielfrg/solid-ui/number-field"
import styles from "./index.module.css"
export function DemoNumberFieldDefaultValue() {
return (
<NumberField.Root class={styles.root} defaultValue={40}>
<NumberField.Label class={styles.label}>Quantity</NumberField.Label>
<div class={styles.group}>
<NumberField.DecrementTrigger class={styles.button} aria-label="Decrement">
<MinusIcon />
</NumberField.DecrementTrigger>
<NumberField.Input class={styles.input} />
<NumberField.IncrementTrigger class={styles.button} aria-label="Increment">
<PlusIcon />
</NumberField.IncrementTrigger>
</div>
<NumberField.HiddenInput />
</NumberField.Root>
)
}
function MinusIcon() {
return (
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
<path d="M3 7H11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
</svg>
)
}
function PlusIcon() {
return (
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
<path d="M7 3V11M3 7H11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
</svg>
)
}.root {
display: flex;
flex-direction: column;
gap: 0.375rem;
width: 10rem;
}
.label {
font-size: 0.875rem;
line-height: 1.25rem;
font-weight: 500;
color: var(--color-gray-900);
}
.group {
display: flex;
align-items: center;
border: 1px solid var(--color-gray-300);
border-radius: 6px;
overflow: hidden;
}
.input {
all: unset;
flex: 1;
text-align: center;
font-size: 0.875rem;
padding: 6px 0;
color: var(--color-gray-900);
min-width: 0;
}
.button {
all: unset;
display: inline-flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 2rem;
color: var(--color-gray-600);
cursor: pointer;
flex-shrink: 0;
}
.button:hover {
background-color: var(--color-gray-100);
color: var(--color-gray-900);
}
.button[data-disabled] {
opacity: 0.4;
cursor: default;
}
.description {
font-size: 0.75rem;
line-height: 1.25rem;
color: var(--color-gray-600);
}
.error {
font-size: 0.75rem;
line-height: 1.25rem;
color: #dc2626;
}
.status {
margin: 1rem 0 0;
font-size: 0.875rem;
line-height: 1.25rem;
color: var(--color-gray-600);
}
.form {
display: flex;
flex-direction: column;
gap: 1rem;
}
.actions {
display: flex;
gap: 0.5rem;
}:root {
--color-blue: oklch(45% 50% 264deg);
--color-red: oklch(50% 55% 31deg);
--color-gray-50: oklch(98% 0.25% 264deg);
--color-gray-100: oklch(12% 9.5% 264deg / 5%);
--color-gray-200: oklch(12% 9% 264deg / 7%);
--color-gray-300: oklch(12% 8.5% 264deg / 17%);
--color-gray-400: oklch(12% 8% 264deg / 38%);
--color-gray-500: oklch(12% 7.5% 264deg / 50%);
--color-gray-600: oklch(12% 7% 264deg / 67%);
--color-gray-700: oklch(12% 6% 264deg / 77%);
--color-gray-800: oklch(12% 5% 264deg / 85%);
--color-gray-900: oklch(12% 5% 264deg / 90%);
--color-gray-950: oklch(12% 5% 264deg / 95%);
}
@media (prefers-color-scheme: dark) {
:root {
--color-blue: oklch(69% 50% 264deg);
--color-red: oklch(80% 55% 31deg);
--color-gray-50: oklch(17% 0.25% 264deg);
--color-gray-100: oklch(28% 0.75% 264deg / 65%);
--color-gray-200: oklch(29% 0.75% 264deg / 80%);
--color-gray-300: oklch(35% 0.75% 264deg / 80%);
--color-gray-400: oklch(47% 0.875% 264deg / 80%);
--color-gray-500: oklch(64% 1% 264deg / 80%);
--color-gray-600: oklch(82% 1% 264deg / 80%);
--color-gray-700: oklch(92% 1.125% 264deg / 80%);
--color-gray-800: oklch(93% 0.875% 264deg / 85%);
--color-gray-900: oklch(95% 0.5% 264deg / 90%);
--color-gray-950: oklch(94% 0.375% 264deg / 95%);
}
}Controlled
Use rawValue and onRawValueChange to control the numeric value externally.
Value: 40
View source
import { createSignal } from "solid-js"
import * as NumberField from "@danielfrg/solid-ui/number-field"
import styles from "./index.module.css"
export function DemoNumberFieldControlled() {
const [rawValue, setRawValue] = createSignal(40)
return (
<div>
<NumberField.Root class={styles.root} rawValue={rawValue()} onRawValueChange={setRawValue}>
<NumberField.Label class={styles.label}>Quantity</NumberField.Label>
<div class={styles.group}>
<NumberField.DecrementTrigger class={styles.button} aria-label="Decrement">
<MinusIcon />
</NumberField.DecrementTrigger>
<NumberField.Input class={styles.input} />
<NumberField.IncrementTrigger class={styles.button} aria-label="Increment">
<PlusIcon />
</NumberField.IncrementTrigger>
</div>
<NumberField.HiddenInput />
</NumberField.Root>
<p class={styles.status}>Value: {rawValue()}</p>
</div>
)
}
function MinusIcon() {
return (
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
<path d="M3 7H11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
</svg>
)
}
function PlusIcon() {
return (
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
<path d="M7 3V11M3 7H11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
</svg>
)
}.root {
display: flex;
flex-direction: column;
gap: 0.375rem;
width: 10rem;
}
.label {
font-size: 0.875rem;
line-height: 1.25rem;
font-weight: 500;
color: var(--color-gray-900);
}
.group {
display: flex;
align-items: center;
border: 1px solid var(--color-gray-300);
border-radius: 6px;
overflow: hidden;
}
.input {
all: unset;
flex: 1;
text-align: center;
font-size: 0.875rem;
padding: 6px 0;
color: var(--color-gray-900);
min-width: 0;
}
.button {
all: unset;
display: inline-flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 2rem;
color: var(--color-gray-600);
cursor: pointer;
flex-shrink: 0;
}
.button:hover {
background-color: var(--color-gray-100);
color: var(--color-gray-900);
}
.button[data-disabled] {
opacity: 0.4;
cursor: default;
}
.description {
font-size: 0.75rem;
line-height: 1.25rem;
color: var(--color-gray-600);
}
.error {
font-size: 0.75rem;
line-height: 1.25rem;
color: #dc2626;
}
.status {
margin: 1rem 0 0;
font-size: 0.875rem;
line-height: 1.25rem;
color: var(--color-gray-600);
}
.form {
display: flex;
flex-direction: column;
gap: 1rem;
}
.actions {
display: flex;
gap: 0.5rem;
}:root {
--color-blue: oklch(45% 50% 264deg);
--color-red: oklch(50% 55% 31deg);
--color-gray-50: oklch(98% 0.25% 264deg);
--color-gray-100: oklch(12% 9.5% 264deg / 5%);
--color-gray-200: oklch(12% 9% 264deg / 7%);
--color-gray-300: oklch(12% 8.5% 264deg / 17%);
--color-gray-400: oklch(12% 8% 264deg / 38%);
--color-gray-500: oklch(12% 7.5% 264deg / 50%);
--color-gray-600: oklch(12% 7% 264deg / 67%);
--color-gray-700: oklch(12% 6% 264deg / 77%);
--color-gray-800: oklch(12% 5% 264deg / 85%);
--color-gray-900: oklch(12% 5% 264deg / 90%);
--color-gray-950: oklch(12% 5% 264deg / 95%);
}
@media (prefers-color-scheme: dark) {
:root {
--color-blue: oklch(69% 50% 264deg);
--color-red: oklch(80% 55% 31deg);
--color-gray-50: oklch(17% 0.25% 264deg);
--color-gray-100: oklch(28% 0.75% 264deg / 65%);
--color-gray-200: oklch(29% 0.75% 264deg / 80%);
--color-gray-300: oklch(35% 0.75% 264deg / 80%);
--color-gray-400: oklch(47% 0.875% 264deg / 80%);
--color-gray-500: oklch(64% 1% 264deg / 80%);
--color-gray-600: oklch(82% 1% 264deg / 80%);
--color-gray-700: oklch(92% 1.125% 264deg / 80%);
--color-gray-800: oklch(93% 0.875% 264deg / 85%);
--color-gray-900: oklch(95% 0.5% 264deg / 90%);
--color-gray-950: oklch(94% 0.375% 264deg / 95%);
}
}Description
Use NumberField.Description to add a hint below the input.
Enter a quantity between 1 and 99.
View source
import * as NumberField from "@danielfrg/solid-ui/number-field"
import styles from "./index.module.css"
export function DemoNumberFieldDescription() {
return (
<NumberField.Root class={styles.root} defaultValue={1} minValue={1} maxValue={99}>
<NumberField.Label class={styles.label}>Quantity</NumberField.Label>
<div class={styles.group}>
<NumberField.DecrementTrigger class={styles.button} aria-label="Decrement">
<MinusIcon />
</NumberField.DecrementTrigger>
<NumberField.Input class={styles.input} />
<NumberField.IncrementTrigger class={styles.button} aria-label="Increment">
<PlusIcon />
</NumberField.IncrementTrigger>
</div>
<NumberField.Description class={styles.description}>Enter a quantity between 1 and 99.</NumberField.Description>
<NumberField.HiddenInput />
</NumberField.Root>
)
}
function MinusIcon() {
return (
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
<path d="M3 7H11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
</svg>
)
}
function PlusIcon() {
return (
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
<path d="M7 3V11M3 7H11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
</svg>
)
}.root {
display: flex;
flex-direction: column;
gap: 0.375rem;
width: 10rem;
}
.label {
font-size: 0.875rem;
line-height: 1.25rem;
font-weight: 500;
color: var(--color-gray-900);
}
.group {
display: flex;
align-items: center;
border: 1px solid var(--color-gray-300);
border-radius: 6px;
overflow: hidden;
}
.input {
all: unset;
flex: 1;
text-align: center;
font-size: 0.875rem;
padding: 6px 0;
color: var(--color-gray-900);
min-width: 0;
}
.button {
all: unset;
display: inline-flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 2rem;
color: var(--color-gray-600);
cursor: pointer;
flex-shrink: 0;
}
.button:hover {
background-color: var(--color-gray-100);
color: var(--color-gray-900);
}
.button[data-disabled] {
opacity: 0.4;
cursor: default;
}
.description {
font-size: 0.75rem;
line-height: 1.25rem;
color: var(--color-gray-600);
}
.error {
font-size: 0.75rem;
line-height: 1.25rem;
color: #dc2626;
}
.status {
margin: 1rem 0 0;
font-size: 0.875rem;
line-height: 1.25rem;
color: var(--color-gray-600);
}
.form {
display: flex;
flex-direction: column;
gap: 1rem;
}
.actions {
display: flex;
gap: 0.5rem;
}:root {
--color-blue: oklch(45% 50% 264deg);
--color-red: oklch(50% 55% 31deg);
--color-gray-50: oklch(98% 0.25% 264deg);
--color-gray-100: oklch(12% 9.5% 264deg / 5%);
--color-gray-200: oklch(12% 9% 264deg / 7%);
--color-gray-300: oklch(12% 8.5% 264deg / 17%);
--color-gray-400: oklch(12% 8% 264deg / 38%);
--color-gray-500: oklch(12% 7.5% 264deg / 50%);
--color-gray-600: oklch(12% 7% 264deg / 67%);
--color-gray-700: oklch(12% 6% 264deg / 77%);
--color-gray-800: oklch(12% 5% 264deg / 85%);
--color-gray-900: oklch(12% 5% 264deg / 90%);
--color-gray-950: oklch(12% 5% 264deg / 95%);
}
@media (prefers-color-scheme: dark) {
:root {
--color-blue: oklch(69% 50% 264deg);
--color-red: oklch(80% 55% 31deg);
--color-gray-50: oklch(17% 0.25% 264deg);
--color-gray-100: oklch(28% 0.75% 264deg / 65%);
--color-gray-200: oklch(29% 0.75% 264deg / 80%);
--color-gray-300: oklch(35% 0.75% 264deg / 80%);
--color-gray-400: oklch(47% 0.875% 264deg / 80%);
--color-gray-500: oklch(64% 1% 264deg / 80%);
--color-gray-600: oklch(82% 1% 264deg / 80%);
--color-gray-700: oklch(92% 1.125% 264deg / 80%);
--color-gray-800: oklch(93% 0.875% 264deg / 85%);
--color-gray-900: oklch(95% 0.5% 264deg / 90%);
--color-gray-950: oklch(94% 0.375% 264deg / 95%);
}
}Error message
Use validationState and NumberField.ErrorMessage to show validation feedback.
Quantity must be exactly 40.
View source
import { createSignal } from "solid-js"
import * as NumberField from "@danielfrg/solid-ui/number-field"
import styles from "./index.module.css"
export function DemoNumberFieldErrorMessage() {
const [rawValue, setRawValue] = createSignal(0)
return (
<NumberField.Root
class={styles.root}
rawValue={rawValue()}
onRawValueChange={setRawValue}
validationState={rawValue() !== 40 ? "invalid" : "valid"}
>
<NumberField.Label class={styles.label}>Quantity</NumberField.Label>
<div class={styles.group}>
<NumberField.DecrementTrigger class={styles.button} aria-label="Decrement">
<MinusIcon />
</NumberField.DecrementTrigger>
<NumberField.Input class={styles.input} />
<NumberField.IncrementTrigger class={styles.button} aria-label="Increment">
<PlusIcon />
</NumberField.IncrementTrigger>
</div>
<NumberField.ErrorMessage class={styles.error}>Quantity must be exactly 40.</NumberField.ErrorMessage>
<NumberField.HiddenInput />
</NumberField.Root>
)
}
function MinusIcon() {
return (
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
<path d="M3 7H11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
</svg>
)
}
function PlusIcon() {
return (
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
<path d="M7 3V11M3 7H11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
</svg>
)
}.root {
display: flex;
flex-direction: column;
gap: 0.375rem;
width: 10rem;
}
.label {
font-size: 0.875rem;
line-height: 1.25rem;
font-weight: 500;
color: var(--color-gray-900);
}
.group {
display: flex;
align-items: center;
border: 1px solid var(--color-gray-300);
border-radius: 6px;
overflow: hidden;
}
.input {
all: unset;
flex: 1;
text-align: center;
font-size: 0.875rem;
padding: 6px 0;
color: var(--color-gray-900);
min-width: 0;
}
.button {
all: unset;
display: inline-flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 2rem;
color: var(--color-gray-600);
cursor: pointer;
flex-shrink: 0;
}
.button:hover {
background-color: var(--color-gray-100);
color: var(--color-gray-900);
}
.button[data-disabled] {
opacity: 0.4;
cursor: default;
}
.description {
font-size: 0.75rem;
line-height: 1.25rem;
color: var(--color-gray-600);
}
.error {
font-size: 0.75rem;
line-height: 1.25rem;
color: #dc2626;
}
.status {
margin: 1rem 0 0;
font-size: 0.875rem;
line-height: 1.25rem;
color: var(--color-gray-600);
}
.form {
display: flex;
flex-direction: column;
gap: 1rem;
}
.actions {
display: flex;
gap: 0.5rem;
}:root {
--color-blue: oklch(45% 50% 264deg);
--color-red: oklch(50% 55% 31deg);
--color-gray-50: oklch(98% 0.25% 264deg);
--color-gray-100: oklch(12% 9.5% 264deg / 5%);
--color-gray-200: oklch(12% 9% 264deg / 7%);
--color-gray-300: oklch(12% 8.5% 264deg / 17%);
--color-gray-400: oklch(12% 8% 264deg / 38%);
--color-gray-500: oklch(12% 7.5% 264deg / 50%);
--color-gray-600: oklch(12% 7% 264deg / 67%);
--color-gray-700: oklch(12% 6% 264deg / 77%);
--color-gray-800: oklch(12% 5% 264deg / 85%);
--color-gray-900: oklch(12% 5% 264deg / 90%);
--color-gray-950: oklch(12% 5% 264deg / 95%);
}
@media (prefers-color-scheme: dark) {
:root {
--color-blue: oklch(69% 50% 264deg);
--color-red: oklch(80% 55% 31deg);
--color-gray-50: oklch(17% 0.25% 264deg);
--color-gray-100: oklch(28% 0.75% 264deg / 65%);
--color-gray-200: oklch(29% 0.75% 264deg / 80%);
--color-gray-300: oklch(35% 0.75% 264deg / 80%);
--color-gray-400: oklch(47% 0.875% 264deg / 80%);
--color-gray-500: oklch(64% 1% 264deg / 80%);
--color-gray-600: oklch(82% 1% 264deg / 80%);
--color-gray-700: oklch(92% 1.125% 264deg / 80%);
--color-gray-800: oklch(93% 0.875% 264deg / 85%);
--color-gray-900: oklch(95% 0.5% 264deg / 90%);
--color-gray-950: oklch(94% 0.375% 264deg / 95%);
}
}HTML form
Use the name prop with NumberField.HiddenInput to include the value in a native form submission.
View source
import * as NumberField from "@danielfrg/solid-ui/number-field"
import styles from "./index.module.css"
export function DemoNumberFieldHtmlForm() {
return (
<form
onSubmit={(e) => {
e.preventDefault()
const data = new FormData(e.currentTarget)
window.alert(JSON.stringify(Object.fromEntries(data), null, 2))
}}
class={styles.form}
>
<NumberField.Root class={styles.root} name="quantity" defaultValue={1} minValue={1}>
<NumberField.Label class={styles.label}>Quantity</NumberField.Label>
<div class={styles.group}>
<NumberField.DecrementTrigger class={styles.button} aria-label="Decrement">
<MinusIcon />
</NumberField.DecrementTrigger>
<NumberField.Input class={styles.input} />
<NumberField.IncrementTrigger class={styles.button} aria-label="Increment">
<PlusIcon />
</NumberField.IncrementTrigger>
</div>
<NumberField.HiddenInput />
</NumberField.Root>
<div class={styles.actions}>
<button type="reset" class={styles.button}>
Reset
</button>
<button type="submit" class={styles.button}>
Submit
</button>
</div>
</form>
)
}
function MinusIcon() {
return (
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
<path d="M3 7H11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
</svg>
)
}
function PlusIcon() {
return (
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
<path d="M7 3V11M3 7H11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
</svg>
)
}.root {
display: flex;
flex-direction: column;
gap: 0.375rem;
width: 10rem;
}
.label {
font-size: 0.875rem;
line-height: 1.25rem;
font-weight: 500;
color: var(--color-gray-900);
}
.group {
display: flex;
align-items: center;
border: 1px solid var(--color-gray-300);
border-radius: 6px;
overflow: hidden;
}
.input {
all: unset;
flex: 1;
text-align: center;
font-size: 0.875rem;
padding: 6px 0;
color: var(--color-gray-900);
min-width: 0;
}
.button {
all: unset;
display: inline-flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 2rem;
color: var(--color-gray-600);
cursor: pointer;
flex-shrink: 0;
}
.button:hover {
background-color: var(--color-gray-100);
color: var(--color-gray-900);
}
.button[data-disabled] {
opacity: 0.4;
cursor: default;
}
.description {
font-size: 0.75rem;
line-height: 1.25rem;
color: var(--color-gray-600);
}
.error {
font-size: 0.75rem;
line-height: 1.25rem;
color: #dc2626;
}
.status {
margin: 1rem 0 0;
font-size: 0.875rem;
line-height: 1.25rem;
color: var(--color-gray-600);
}
.form {
display: flex;
flex-direction: column;
gap: 1rem;
}
.actions {
display: flex;
gap: 0.5rem;
}:root {
--color-blue: oklch(45% 50% 264deg);
--color-red: oklch(50% 55% 31deg);
--color-gray-50: oklch(98% 0.25% 264deg);
--color-gray-100: oklch(12% 9.5% 264deg / 5%);
--color-gray-200: oklch(12% 9% 264deg / 7%);
--color-gray-300: oklch(12% 8.5% 264deg / 17%);
--color-gray-400: oklch(12% 8% 264deg / 38%);
--color-gray-500: oklch(12% 7.5% 264deg / 50%);
--color-gray-600: oklch(12% 7% 264deg / 67%);
--color-gray-700: oklch(12% 6% 264deg / 77%);
--color-gray-800: oklch(12% 5% 264deg / 85%);
--color-gray-900: oklch(12% 5% 264deg / 90%);
--color-gray-950: oklch(12% 5% 264deg / 95%);
}
@media (prefers-color-scheme: dark) {
:root {
--color-blue: oklch(69% 50% 264deg);
--color-red: oklch(80% 55% 31deg);
--color-gray-50: oklch(17% 0.25% 264deg);
--color-gray-100: oklch(28% 0.75% 264deg / 65%);
--color-gray-200: oklch(29% 0.75% 264deg / 80%);
--color-gray-300: oklch(35% 0.75% 264deg / 80%);
--color-gray-400: oklch(47% 0.875% 264deg / 80%);
--color-gray-500: oklch(64% 1% 264deg / 80%);
--color-gray-600: oklch(82% 1% 264deg / 80%);
--color-gray-700: oklch(92% 1.125% 264deg / 80%);
--color-gray-800: oklch(93% 0.875% 264deg / 85%);
--color-gray-900: oklch(95% 0.5% 264deg / 90%);
--color-gray-950: oklch(94% 0.375% 264deg / 95%);
}
}Format options
Use formatOptions to format the displayed value, for example as currency.
View source
import * as NumberField from "@danielfrg/solid-ui/number-field"
import styles from "./index.module.css"
export function DemoNumberFieldFormat() {
return (
<NumberField.Root
class={styles.root}
defaultValue={4}
minValue={0}
formatOptions={{ style: "currency", currency: "USD" }}
>
<NumberField.Label class={styles.label}>Price</NumberField.Label>
<div class={styles.group}>
<NumberField.DecrementTrigger class={styles.button} aria-label="Decrement">
<MinusIcon />
</NumberField.DecrementTrigger>
<NumberField.Input class={styles.input} />
<NumberField.IncrementTrigger class={styles.button} aria-label="Increment">
<PlusIcon />
</NumberField.IncrementTrigger>
</div>
<NumberField.HiddenInput />
</NumberField.Root>
)
}
function MinusIcon() {
return (
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
<path d="M3 7H11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
</svg>
)
}
function PlusIcon() {
return (
<svg width="14" height="14" viewBox="0 0 14 14" fill="none">
<path d="M7 3V11M3 7H11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
</svg>
)
}.root {
display: flex;
flex-direction: column;
gap: 0.375rem;
width: 10rem;
}
.label {
font-size: 0.875rem;
line-height: 1.25rem;
font-weight: 500;
color: var(--color-gray-900);
}
.group {
display: flex;
align-items: center;
border: 1px solid var(--color-gray-300);
border-radius: 6px;
overflow: hidden;
}
.input {
all: unset;
flex: 1;
text-align: center;
font-size: 0.875rem;
padding: 6px 0;
color: var(--color-gray-900);
min-width: 0;
}
.button {
all: unset;
display: inline-flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 2rem;
color: var(--color-gray-600);
cursor: pointer;
flex-shrink: 0;
}
.button:hover {
background-color: var(--color-gray-100);
color: var(--color-gray-900);
}
.button[data-disabled] {
opacity: 0.4;
cursor: default;
}
.description {
font-size: 0.75rem;
line-height: 1.25rem;
color: var(--color-gray-600);
}
.error {
font-size: 0.75rem;
line-height: 1.25rem;
color: #dc2626;
}
.status {
margin: 1rem 0 0;
font-size: 0.875rem;
line-height: 1.25rem;
color: var(--color-gray-600);
}
.form {
display: flex;
flex-direction: column;
gap: 1rem;
}
.actions {
display: flex;
gap: 0.5rem;
}:root {
--color-blue: oklch(45% 50% 264deg);
--color-red: oklch(50% 55% 31deg);
--color-gray-50: oklch(98% 0.25% 264deg);
--color-gray-100: oklch(12% 9.5% 264deg / 5%);
--color-gray-200: oklch(12% 9% 264deg / 7%);
--color-gray-300: oklch(12% 8.5% 264deg / 17%);
--color-gray-400: oklch(12% 8% 264deg / 38%);
--color-gray-500: oklch(12% 7.5% 264deg / 50%);
--color-gray-600: oklch(12% 7% 264deg / 67%);
--color-gray-700: oklch(12% 6% 264deg / 77%);
--color-gray-800: oklch(12% 5% 264deg / 85%);
--color-gray-900: oklch(12% 5% 264deg / 90%);
--color-gray-950: oklch(12% 5% 264deg / 95%);
}
@media (prefers-color-scheme: dark) {
:root {
--color-blue: oklch(69% 50% 264deg);
--color-red: oklch(80% 55% 31deg);
--color-gray-50: oklch(17% 0.25% 264deg);
--color-gray-100: oklch(28% 0.75% 264deg / 65%);
--color-gray-200: oklch(29% 0.75% 264deg / 80%);
--color-gray-300: oklch(35% 0.75% 264deg / 80%);
--color-gray-400: oklch(47% 0.875% 264deg / 80%);
--color-gray-500: oklch(64% 1% 264deg / 80%);
--color-gray-600: oklch(82% 1% 264deg / 80%);
--color-gray-700: oklch(92% 1.125% 264deg / 80%);
--color-gray-800: oklch(93% 0.875% 264deg / 85%);
--color-gray-900: oklch(95% 0.5% 264deg / 90%);
--color-gray-950: oklch(94% 0.375% 264deg / 95%);
}
}