# Bits UI

Flexible, unstyled, and accessible primitives that provide the foundation for building your own high-quality Svelte component library.

Bits UI is a collection of headless component primitives for Svelte that prioritizes developer experience, accessibility, and flexibility. The goal is to empower developers to build high-quality, accessible user interfaces without sacrificing creative control or performance.

<Image src={banner} alt="Bits UI Banner" class="rounded-container shadow-xl" />

<figure class="linker bg-noise bg-[#cae2f7]!">
  <a class="btn preset-filled" href="https://www.bits-ui.com/" target="_blank">
    View Bits UI Docs
  </a>
</figure>

At minimum, we recommend you read the following documentation before you start this integration guide.

* [Getting Started](https://www.bits-ui.com/docs/getting-started)
* [Styling](https://www.bits-ui.com/docs/styling)

## Requirements

\| Tooling                              | Minimum Supported |
\| ------------------------------------ | ----------------- |
\| [Svelte](https://svelte.dev/)        | 5                 |
\| [Skeleton](https://skeleton.dev)     | 3                 |
\| [Tailwind](https://tailwindcss.com/) | 4                 |
\| [Bits UI](https://www.bits-ui.com/)  | 1                 |

## Introduction

In this guide we'll implement the following Bits UI `<Calendar>` component. This will showcase the bare minimum requirements for integrating Skeleton with Bits UI.

<Image src={datePicker} alt="Bits UI Date Picker Component" class="rounded-container shadow-xl" />

<figure class="linker bg-noise bg-[#cae2f7]!">
  <a class="btn preset-filled" href="https://www.bits-ui.com/docs/components/calendar" target="_blank">
    Calendar Documentation
  </a>
</figure>

## Get Started

<Process>
  <ProcessStep step="1">
    ### Create a SvelteKit Project

    To begin, we'll setup a new SvelteKit project, including Skeleton v3 and Tailwind v4.

    <a class="btn preset-filled mt-4" href={resolvePath(props.Astro, '/docs/[framework]/get-started/installation/sveltekit')} target="_blank">Setup SvelteKit App</a>
  </ProcessStep>

  <ProcessStep step="2">
    ### Install Bits UI

    Install the Bits UI package via your package manager of choice.

    ```bash
    npm install bits-ui
    ```

    We'll also include the [Adobe International Date](https://react-spectrum.adobe.com/internationalized/date/index.html) package to assist with date/time formatting.

    ```bash
    npm i -D @internationalized/date
    ```
  </ProcessStep>

  <ProcessStep step="3">
    ### Component Boilerplate

    Create a new component in `/lib/components/Calendar/Calendar.svelte` and insert the following markup. This will generate an unstyled version of the component.

    ```svelte
    <script lang="ts">
    	import { Calendar } from 'bits-ui';
    	import { getLocalTimeZone, today } from '@internationalized/date';

    	const isDateUnavailable: Calendar.RootProps['isDateUnavailable'] = (date) => {
    		return date.day === 17 || date.day === 18;
    	};

    	let value = $state(today(getLocalTimeZone()));
    </script>

    <Calendar.Root {isDateUnavailable} weekdayFormat="short" fixedWeeks={true} type="single" bind:value>
    	{#snippet children({ months, weekdays })}
    		<Calendar.Header>
    			<Calendar.PrevButton>
    				<span>&larr;</span>
    			</Calendar.PrevButton>
    			<Calendar.Heading />
    			<Calendar.NextButton>
    				<span>&rarr;</span>
    			</Calendar.NextButton>
    		</Calendar.Header>

    		{#each months as month}
    			<Calendar.Grid>
    				<Calendar.GridHead>
    					<Calendar.GridRow>
    						{#each weekdays as day}
    							<Calendar.HeadCell>
    								{day}
    							</Calendar.HeadCell>
    						{/each}
    					</Calendar.GridRow>
    				</Calendar.GridHead>
    				<Calendar.GridBody>
    					{#each month.weeks as weekDates}
    						<Calendar.GridRow>
    							{#each weekDates as date}
    								<Calendar.Cell {date} month={month.value}>
    									<Calendar.Day />
    								</Calendar.Cell>
    							{/each}
    						</Calendar.GridRow>
    					{/each}
    				</Calendar.GridBody>
    			</Calendar.Grid>
    		{/each}
    	{/snippet}
    </Calendar.Root>
    ```
  </ProcessStep>

  <ProcessStep step="check">
    ### Add the Component

    Finally, let's add our new component to the root `+page.svelte` so that we may preview it.

    ```svelte
    <script lang="ts">
    	import Calendar from '$lib/components/Calendar/Calendar.svelte';
    </script>

    <main class="p-10">
    	<Calendar />
    </main>
    ```
  </ProcessStep>
</Process>

## Styling

Each Bits UI component accepts a `class` attribute. Use this to provide Tailwind and Skeleton utility classes.

### Basic Styles

Styling the `<Calendar.Root>` parent component.

```svelte
<Calendar.Root class="card inline-block border border-surface-200-800 bg-surface-50-950 shadow-xl mt-6 p-4">
	<!-- ... -->
</Calendar.Root>
```

Styling the `<Calendar.PrevButton>` sub-component. You can clone these to `<Calendar.NextButton>` too.

```svelte
<Calendar.PrevButton class="btn-icon preset-filled-primary-500" title="Previous month" aria-label="Previous month">
	<span>&larr;</span>
</Calendar.PrevButton>
```

Styling the `<Calendar.Day>` sub-component.

```svelte
<Calendar.Day
	class="rounded hover:border-surface-200-800 data-selected:bg-foreground data-disabled:text-surface-600-400 data-selected:preset-filled data-unavailable:text-surface-600-400 data-disabled:pointer-events-none data-outside-month:pointer-events-none data-selected:font-medium data-unavailable:line-through group relative inline-flex size-10 items-center justify-center whitespace-nowrap border border-transparent bg-transparent p-0 text-sm font-normal"
>
	<div class="bg-foreground group-data-selected:bg-background group-data-today:block absolute top-[5px] hidden size-1 rounded-full"></div>
	{date.day}
</Calendar.Day>
```

For the sake of time we won't cover every sub-component.

### Complete Example

Below is a complete example showing the entire component with all styles and basic configuration.

```svelte
<script lang="ts">
	import { getLocalTimeZone, today } from '@internationalized/date';
	import { Calendar } from 'bits-ui';

	const isDateUnavailable: Calendar.RootProps['isDateUnavailable'] = (date) => {
		return date.day === 17 || date.day === 18;
	};

	let value = $state(today(getLocalTimeZone()));
</script>

<Calendar.Root
	class="card inline-block border border-surface-200-800 bg-surface-50-950 shadow-xl mt-6 p-4"
	{isDateUnavailable}
	weekdayFormat="short"
	fixedWeeks={true}
	type="single"
	bind:value
>
	{#snippet children({ months, weekdays })}
		<Calendar.Header class="flex items-center justify-between">
			<Calendar.PrevButton class="btn-icon preset-filled-primary-500" title="Previous month" aria-label="Previous month">
				<span>&larr;</span>
			</Calendar.PrevButton>
			<Calendar.Heading class="text-lg font-bold" />
			<Calendar.NextButton class="btn-icon preset-filled-primary-500" title="Next month" aria-label="Next month">
				<span>&rarr;</span>
			</Calendar.NextButton>
		</Calendar.Header>
		<div class="flex flex-col space-y-4 pt-4 sm:flex-row sm:space-x-4 sm:space-y-0">
			{#each months as month, i (i)}
				<Calendar.Grid class="w-full border-collapse select-none space-y-1">
					<Calendar.GridHead>
						<Calendar.GridRow class="mb-1 flex w-full justify-between">
							{#each weekdays as day}
								<Calendar.HeadCell class="text-surface-600-400 font-normal! w-10 rounded-md text-xs">
									<div>{day.slice(0, 2)}</div>
								</Calendar.HeadCell>
							{/each}
						</Calendar.GridRow>
					</Calendar.GridHead>
					<Calendar.GridBody>
						{#each month.weeks as weekDates}
							<Calendar.GridRow class="flex w-full">
								{#each weekDates as date}
									<Calendar.Cell {date} month={month.value} class="p-0! relative size-10 text-center text-sm">
										<Calendar.Day
											class="rounded hover:border-surface-200-800 data-selected:bg-foreground data-disabled:text-surface-600-400 data-selected:preset-filled data-unavailable:text-surface-600-400 data-disabled:pointer-events-none data-outside-month:pointer-events-none data-selected:font-medium data-unavailable:line-through group relative inline-flex size-10 items-center justify-center whitespace-nowrap border border-transparent bg-transparent p-0 text-sm font-normal"
										>
											<div
												class="bg-foreground group-data-selected:bg-surface-50-950 group-data-today:block absolute top-[5px] hidden size-1 rounded-full"
											></div>
											{date.day}
										</Calendar.Day>
									</Calendar.Cell>
								{/each}
							</Calendar.GridRow>
						{/each}
					</Calendar.GridBody>
				</Calendar.Grid>
			{/each}
		</div>
	{/snippet}
</Calendar.Root>
```

## Going Further

If you wish to match Skeleton component conventions, view our [contributor component guidelines](/docs/\[framework]/resources/contribute/components).

## Attribution

Bits UI is created and maintained by [Huntabyte](https://github.com/huntabyte). Consider [sponsoring him](https://github.com/sponsors/huntabyte) to support this open source project.
