> ## Documentation Index
> Fetch the complete documentation index at: https://anvil.servicetitan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Date Field Single – Design

> A form field for selecting a single date with calendar picker support.

export const DoDont = ({text, type, children}) => {
  return <>
      {type === "do" && <div className="do-dont do">
          {children && <div className="do-dont-content">{children}</div>}
          <Check>
            <p>
              <strong>Do</strong>
              {text && <span className="m-inline-start-1">{text}</span>}
            </p>
          </Check>
        </div>}
      {type === "dont" && <div className="do-dont dont">
          {children && <div className="do-dont-content">{children}</div>}
          <Danger>
            <p>
              <strong>Don't</strong>
              {text && <span className="m-inline-start-1">{text}</span>}
            </p>
          </Danger>
        </div>}
      {type === "caution" && <div className="do-dont caution">
          {children && <div className="do-dont-content">{children}</div>}
          <Warning>
            <p>
              <strong>Caution</strong>
              {text && <span className="m-inline-start-1">{text}</span>}
            </p>
          </Warning>
        </div>}
    </>;
};

export const LiveCode = ({children, customHeight, clickToLoad, example, fullWidth, fullHeight, hideCodeInLiveCode, screenshot, screenshotOnly, showCode: showCodeProp}) => {
  const SCREENSHOTS_BASE = "https://servicetitan.github.io/anvil2-docs-live-code/screenshots";
  const STACKBLITZ_BASE = "https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples";
  const [showCodeBlock, setShowCodeBlock] = useState(showCodeProp ?? false);
  const [isLocalOverride, setIsLocalOverride] = useState(false);
  useEffect(() => {
    const examplePath = `/images/live-code-screenshots-tmp/${example}.png`;
    fetch(examplePath, {
      method: "HEAD"
    }).then(r => {
      if (r.ok) setIsLocalOverride(true);
    }).catch(() => {});
  }, [example]);
  const screenshotBase = isLocalOverride ? "/images/live-code-screenshots-tmp" : SCREENSHOTS_BASE;
  if (screenshotOnly) {
    return <Frame className="flex flex-col">
        <div className="flex dark:hidden" style={{
      justifyContent: "center",
      alignItems: "center",
      width: fullWidth ? "100%" : "50%",
      minHeight: fullHeight ? "284px" : undefined,
      background: "#FFFFFF"
    }}>
          <img srcset={`${screenshotBase}/${example}.png, ${screenshotBase}/${example}-2x.png 2x`} src={`${screenshotBase}/${example}.png`} alt={example} noZoom />
        </div>
        <div className="hidden dark:flex" style={{
      justifyContent: "center",
      alignItems: "center",
      width: fullWidth ? "100%" : "50%",
      minHeight: fullHeight ? "284px" : undefined,
      background: "#141414"
    }}>
          <img srcset={`${screenshotBase}/${example}-dark.png, ${screenshotBase}/${example}-dark-2x.png 2x`} src={`${screenshotBase}/${example}-dark.png`} alt={example} noZoom />
        </div>
      </Frame>;
  }
  if (screenshot) {
    return <Frame className="flex flex-col -mb-2">
        <div className="flex dark:hidden bg-white dark:bg-codeblock border border-gray-950/10 dark:border-white/10 dark:twoslash-dark rounded-2xl overflow-hidden" style={{
      justifyContent: "center",
      alignItems: "center",
      width: fullWidth ? "100%" : "50%",
      minHeight: fullHeight ? "284px" : undefined
    }}>
          <img srcset={`${screenshotBase}/${example}.png, ${screenshotBase}/${example}-2x.png 2x`} src={`${screenshotBase}/${example}.png`} alt={example} noZoom />
        </div>

        <div className="hidden dark:flex bg-white dark:bg-codeblock border border-gray-950/10 dark:border-white/10 dark:twoslash-dark rounded-2xl overflow-hidden" style={{
      background: "#141414",
      justifyContent: "center",
      alignItems: "center",
      width: fullWidth ? "100%" : "50%",
      minHeight: fullHeight ? "284px" : undefined
    }}>
          <img srcset={`${screenshotBase}/${example}-dark.png, ${screenshotBase}/${example}-dark-2x.png 2x`} src={`${screenshotBase}/${example}-dark.png`} alt={example} noZoom />
        </div>

        <div className="flex justify-end items-center text-xs py-2 px-1 gap-4">
          {!showCodeProp ? <button className="inline-flex justify-end items-center text-gray-700 dark:text-gray-50 hover:text-blue-500 dark:hover:text-blue-300 transition-colors group self-end gap-1 cursor-pointer" onClick={() => setShowCodeBlock(!showCodeBlock)} style={{
      appearance: "none"
    }}>
              <Icon icon="code" size="12px" className="group-hover:bg-blue-500 dark:group-hover:bg-blue-300" />
              <span>{showCodeBlock ? "Hide code" : "Show code"}</span>
            </button> : null}

          <a className="inline-flex justify-end items-center hover:text-blue-500 dark:hover:text-blue-300 transition-colors group self-end gap-1" href={`${STACKBLITZ_BASE}/${example}?file=src/App.tsx`} target="_blank" rel="noreferrer">
            <Icon icon="bolt" size="12px" className="group-hover:bg-blue-500 dark:group-hover:bg-blue-300" />
            <span>StackBlitz demo</span>
          </a>
        </div>

        <div className="grid transition-[grid-template-rows] duration-300 ease-in-out overflow-auto overflow-y-hidden overflow-x-auto" style={showCodeBlock ? {
      gridTemplateRows: "1fr"
    } : {
      gridTemplateRows: "0fr"
    }}>
          <div style={{
      minHeight: 0,
      overflowX: "auto",
      overflowY: "hidden",
      marginBlockStart: "-1.25rem",
      marginBlockEnd: "-1.5rem"
    }}>
            {children}
          </div>
        </div>
      </Frame>;
  } else {
    return <div style={{
      display: "flex",
      width: fullWidth ? "100%" : "50%",
      minHeight: customHeight ? customHeight : "316px",
      resize: "vertical",
      overflow: "auto"
    }}>
        <iframe title={example} style={{
      flex: 1,
      width: fullWidth ? "100%" : "50%",
      minHeight: customHeight ? customHeight : "316px"
    }} src={`${STACKBLITZ_BASE}/${example}?embed=1&hideNavigation=1&hideExplorer=1&terminalHeight=0&file=src/App.tsx${clickToLoad ? "&ctl=1" : ""}${hideCodeInLiveCode ? "&view=preview" : ""}`} allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts" />
      </div>;
  }
};

<Frame>
  <div className="w-full h-full bg-[#FFFFFF] p-2 rounded flex items-center justify-center">
    <img noZoom src="https://mintcdn.com/servicetitan/66tZkkkyD48ugyZL/images/docs/web/components/date-field-single/overview-image.png?fit=max&auto=format&n=66tZkkkyD48ugyZL&q=85&s=5f8f2c2b9297e5c0e9db8390bbb3ec6f" width="640" height="360" data-path="images/docs/web/components/date-field-single/overview-image.png" />
  </div>
</Frame>

## Anatomy

Date Field Single consists of five primary elements that enable users to select a single date through text input or a calendar picker.

<Frame>
  <div className="w-full h-full bg-[#FFFFFF] p-2 rounded flex items-center justify-center">
    <img src="https://mintcdn.com/servicetitan/66tZkkkyD48ugyZL/images/docs/web/components/date-field-single/design/date-field-single-anatomy.png?fit=max&auto=format&n=66tZkkkyD48ugyZL&q=85&s=088296ad3ffb538b1e0a4c94ccdf3139" alt="Anatomy of the Date Field Single component" width="896" height="326" data-path="images/docs/web/components/date-field-single/design/date-field-single-anatomy.png" />
  </div>
</Frame>

1. **Label** - Identifies the field and its purpose
2. **Input field** - Text input for manual date entry with format mask
3. **Helper text** (Optional) - Description, format hint or error message

## Options

Date Field Single supports the following configurations to accommodate different use cases and regional preferences.

### Date Format Modes

<LiveCode example="datefieldsingle-mode" screenshot fullWidth>
  ```tsx lines theme={null}
  import { DateFieldSingle, Flex } from "@servicetitan/anvil2";

  function App() {
    return (
      <Flex direction="column" gap="4" style={{ minWidth: "384px" }}>
        {/** US format (default) */}
        <DateFieldSingle label="US Format" mode="mm/dd/yyyy" />

        {/** International format */}
        <DateFieldSingle label="International Format" mode="dd/mm/yyyy" />
      </Flex>
    );
  }

  export default App;
  ```
</LiveCode>

Date Field Single supports two date format modes to match regional conventions. The default format is `mm/dd/yyyy` for US audiences, while `dd/mm/yyyy` is available for international users.

| Mode       | Format   | Use Case                  |
| ---------- | -------- | ------------------------- |
| mm/dd/yyyy | Default  | US date format            |
| dd/mm/yyyy | Optional | International date format |

### Sizes

<LiveCode example="datefieldsingle-size" screenshot fullWidth>
  ```tsx lines theme={null}
  import { DateFieldSingle, Flex } from "@servicetitan/anvil2";

  function App() {
    return (
      <Flex direction="column" gap="4" style={{ minWidth: "384px" }}>
        <DateFieldSingle label="Small" size="small" />
        <DateFieldSingle label="Medium" size="medium" />
        <DateFieldSingle label="Large" size="large" />
      </Flex>
    );
  }

  export default App;
  ```
</LiveCode>

Three size options accommodate different layout densities and visual hierarchies.

| Size   | Height |
| ------ | ------ |
| Small  | 32px   |
| Medium | 40px   |
| Large  | 48px   |

### Calendar Picker

<LiveCode example="datefieldsingle-disablecalendar" screenshot fullWidth>
  ```tsx lines theme={null}
  import { DateFieldSingle } from "@servicetitan/anvil2";

  function App() {
    return <DateFieldSingle disableCalendar />;
  }

  export default App;
  ```
</LiveCode>

The calendar picker can be disabled when manual text entry is preferred or when the calendar interface is not suitable for the use case.

## Behavior

Date Field Single responds to user interaction with distinct visual states and validation feedback.

### Visual States

<LiveCode example="datefieldsingle-visual-states" screenshot fullWidth>
  ```tsx lines theme={null}
  import { DateFieldSingle, Grid } from "@servicetitan/anvil2";

  function App() {
    return (
      <Grid gap="6">
        <DateFieldSingle
          label="Appointment Date"
          defaultValue="2025-07-15"
          required
        />
        <DateFieldSingle
          label="Appointment Date"
          defaultValue="2025-07-15"
          required
          disabled
        />
        <DateFieldSingle
          label="Appointment Date"
          defaultValue="2025-07-15"
          required
          error="This date is unavailable"
        />
      </Grid>
    );
  }

  export default App;
  ```
</LiveCode>

The component displays different visual states to communicate field status and interactivity. Default state shows the field ready for input. Focus state highlights the active field. Error state displays validation messages when dates are invalid or required fields are empty. Disabled state prevents interaction while maintaining visual context.

### Calendar Interaction

Clicking the input field opens a popover calendar. Users can navigate months and select dates directly from the calendar. The calendar closes automatically after selection, and the selected date populates the input field.

### Validation

<LiveCode example="datefieldsingle-isdatevalid" screenshot fullWidth>
  ```tsx lines theme={null}
  import {
    DateFieldSingle,
    Flex,
    type DateFieldSingleChange,
  } from "@servicetitan/anvil2";
  import { useState } from "react";

  function App() {
    const [value, setValue] = useState<string | null>();
    const [latestEvent, setLatestEvent] = useState<DateFieldSingleChange>();
    const [errorMessage, setErrorMessage] = useState("");

    const handleChange = (change: DateFieldSingleChange) => {
      setValue(change.date);
      setLatestEvent(change);
      if (change.isDateValid) setErrorMessage("");
    };

    const handleBlur = () => {
      if (!latestEvent) return;
      if (!latestEvent.isDateValid) return setErrorMessage("Invalid date");
      if (!latestEvent.isInputValid && !latestEvent.isInputEmpty)
        return setErrorMessage("Valid date, but input isn't fully filled");
    };
    return (
      <Flex direction="column">
        <DateFieldSingle
          label="Weekdays Only"
          description="Type a date falling on a weekend to see an error"
          value={value}
          onChange={handleChange}
          onBlur={handleBlur}
          error={errorMessage}
          unavailable={{ daysOfWeek: [6, 7] }}
        />
        <pre>{JSON.stringify(latestEvent, null, 2)}</pre>
      </Flex>
    );
  }

  export default App;
  ```
</LiveCode>

Date Field Single validates input in real time. The component checks for parseable dates, required field completion, date constraints (min/max dates), and unavailable dates or days of the week. Error messages appear below the field when validation fails.

## Usage Guidelines

### When to Use

Use Date Field Single when you need to:

* Capture a single, specific date
* Provide calendar visual context for date selection
* Support both manual entry and visual selection
* Validate dates against constraints like minimum or maximum dates

### When not to use

Avoid using Date Field Single for:

* **Date ranges** - Use [Date Field Range](/docs/web/components/date-field-range/design) instead
* **Recurring dates without year** - Use [Date Field Yearless](/docs/web/components/date-field-yearless/design) for birthdays, anniversaries, or seasonal dates
* **Time selection** - Use [Time Field](/docs/web/components/time-field/design) for time-only inputs

### Alternatives

#### Date Field Single vs Date Field Yearless

Use Date Field Single when the year is essential to the date selection, such as appointment scheduling or deadline setting. Use Date Field Yearless when the year is irrelevant, such as recurring annual events or template dates.

### How to Use

#### Labeling and Context

Provide clear, descriptive labels that communicate the purpose of the date selection. Labels should indicate what the date represents and why it's needed. Use helper text or descriptions when additional context helps users understand constraints or requirements.

<LiveCode example="datefieldsingle-appointment-scheduling" screenshot fullWidth>
  ```tsx lines theme={null}
  import { DateFieldSingle } from "@servicetitan/anvil2";

  function App() {
    return (
      <DateFieldSingle
        label="Event Date"
        description="Select the date when this event occurs"
        minDate={new Date().toISOString()}
        required
      />
    );
  }

  export default App;
  ```
</LiveCode>

<DoDont type="do" />

* Write labels that clearly communicate the purpose of the date selection
* Include format hints to guide users on expected input format
* Set appropriate date constraints (min/max dates) to prevent invalid selections
* Display validation errors immediately when they occur
* Use the calendar picker to provide visual date selection when helpful

#### Anti-pattern: Ambiguous Context and Format

Avoid creating confusion by omitting format guidance or using Date Field Single for dates where year information is irrelevant.

<LiveCode example="datefieldsingle-recurring-events-dont" screenshot fullWidth>
  ```tsx lines theme={null}
  import { DateFieldSingle } from "@servicetitan/anvil2";

  function App() {
    return <DateFieldSingle label="Date" required />;
  }

  export default App;
  ```
</LiveCode>

<DoDont type="dont" />

* Use Date Field Single for recurring annual events where year doesn't matter
* Omit format hints when the date format might be ambiguous to users
* Hide validation errors until form submission
* Use placeholder text to communicate date format instead of format hints
* Create labels that don't clearly indicate what date is being selected

## Content

Content within Date Field Single should be clear, concise, and actionable.

* Labels should clearly indicate what date is being selected
* Error messages should explain why a date is invalid and how to fix it
* Format hints should match the selected mode (mm/dd/yyyy or dd/mm/yyyy)
* Descriptions should provide context without overwhelming the user

## Keyboard Interaction

Users can navigate Date Field Single using standard keyboard controls.

### Input Field

| Key         | Description                            |
| ----------- | -------------------------------------- |
| Tab         | Moves focus to the next element        |
| Shift + Tab | Moves focus to the previous element    |
| Arrow Down  | Opens the calendar picker when focused |
| Esc         | Closes the calendar picker             |

### Calendar Picker

| Key               | Description                                       |
| ----------------- | ------------------------------------------------- |
| Arrow Left        | Moves focus to the previous day                   |
| Arrow Right       | Moves focus to the next day                       |
| Arrow Up          | Moves focus to the same day in the previous week  |
| Arrow Down        | Moves focus to the same day in the next week      |
| Page Up           | Moves focus to the same day in the previous month |
| Page Down         | Moves focus to the same day in the next month     |
| Shift + Page Up   | Moves focus to the same day in the previous year  |
| Shift + Page Down | Moves focus to the same day in the next year      |
| Enter             | Selects the focused date                          |
| Space             | Selects the focused date                          |
| Esc               | Closes the calendar picker                        |

### Accessibility

Date Field Single includes built-in accessibility features. The component supports full keyboard navigation and screen reader compatibility. Ensure labels are descriptive and error messages are clear for assistive technology users.

For more guidance on form field labels and context, see [input field context association best practices](/docs/accessibility/labels-and-ctas#input-field-context-association).
