bits-ui

repository·main·Indexed 25 days ago

https://github.com/huntabyte/bits-ui

A collection of headless, unstyled, and accessible component primitives for Svelte developers to build custom component libraries. It includes components such as Accordion and Alert Dialog, and provides a `child` snippet for customizing HTML elements while maintaining accessibility and internal logic.

Tokens
99.1K
Snippets
397
Records
555
Agent score
84%

What's inside bits-ui

  1. Overview of Bits UI

    main

    Bits UI is a headless component library for Svelte. It is designed to provide high-quality, accessible UI primitives while giving developers full creative control over styling and implementation.

    Key features include:

    • Headless Architecture: Components ship unstyled (except for core functional requirements), allowing you to use standard class props or data-* attributes for styling.
    • Accessibility: Built-in WAI-ARIA compliance, keyboard navigation, focus management, and screen reader support.
    • Developer Experience: Full TypeScript coverage, stable APIs, and a flexible event override system.
    • Composability: Uses primitives like Render Delegation to allow for flexible component composition.
  2. Understand DateValue types

    main

    Bits UI uses DateValue objects from @internationalized/date. These are immutable objects that represent different levels of date/time precision. Components will adapt their behavior based on the specific type you provide.

    TypeDescriptionExample
    CalendarDateDate without time2024-07-10
    CalendarDateTimeDate with time2024-07-10T12:30:00
    ZonedDateTimeDate, time, and timezone2024-07-10T21:00:00:00-04:00[America/New_York]
  3. Implement a Rating Group

    main

    Use the RatingGroup component to create a rating interface. The RatingGroup.Root component manages the state and provides a snippet of items to render individual RatingGroup.Item components. Each item provides its index and state (active, inactive, or partial).

    <script lang="ts">
      import { RatingGroup } from "bits-ui";
    </script>
    
    <RatingGroup.Root max={5}>
      {#snippet children({ items })}
        {#each items as item (item.index)}
          <RatingGroup.Item index={item.index}>
            {#if item.state === "active"}
              ⭐
            {:else}
              ☆
            {/if}
          </RatingGroup.Item>
        {/each}
      {/snippet}
    </RatingGroup.Root>
  4. Integrate PinInput with HTML forms

    main

    PinInput.Root integrates with standard HTML forms.

    1. Form Submission: Add the name prop to PinInput.Root so the value is included in the form data.
    2. Auto-submit: Use the onComplete prop to trigger a form submission automatically once the user has entered the required number of characters.
    <script lang="ts">
      import { PinInput } from "bits-ui";
      let form = $state<HTMLFormElement>(null!);
    </script>
    
    <form method="POST" bind:this={form}>
      <PinInput.Root name="mfaCode" onComplete={() => form.submit()}>
        <!-- ... -->
      </PinInput.Root}
    </form>
  5. Manage Menubar value state

    main

    You can control which menu is active using the value prop on Menubar.Root.

    Two-Way Binding

    Use bind:value for automatic synchronization with a Svelte state variable.

    Fully Controlled

    Use Svelte Function Binding by passing a getter and setter function to bind:value for complete control over reads and writes.

    <script lang="ts">
      import { Menubar } from "bits-ui";
      let activeValue = $state("");
    </script>
    
    <button onclick={() => (activeValue = "menu-1")}>Open Menubar Menu</button>
    <Menubar.Root bind:value={activeValue}>
      <Menubar.Menu value="menu-1">
        <!-- ... -->
      </Menubar.Menu>
      <Menubar.Menu value="menu-2">
        <!-- ... -->
      </Menubar.Menu>
    </Menubar.Root>
  6. Apply transitions to Floating Content Components

    main

    Components that rely on Floating UI (like Popover.Content) require an additional step in the child snippet. You must wrap your transition element in a container that receives the wrapperProps to ensure the floating positioning logic remains intact.

    <Popover.Content forceMount>
      {#snippet child({ wrapperProps, props, open })}
        {#if open}
          <div {...wrapperProps}>
            <div {...props} transition:fly>
              <!-- ... -->
            </div>
          </div>
        {/if}
      {/snippet}
    </Popover.Content>
  7. Set a default value for DateField

    main

    To initialize a DateField with a value from an external source (like an ISO 8601 string from an API), use the parsing functions from @internationalized/date:

    • parseDate(string) -> CalendarDate
    • parseDateTime(string) -> CalendarDateTime
    • parseZonedDateTime(string) -> ZonedDateTime
    <script lang="ts">
      import { DateField } from "bits-ui";
      import { parseDate } from "@internationalized/date";
    
      const date = "2024-08-03";
      let value = $state(parseDate(date));
    </script>
    
    <DateField.Root {value}>
      <!-- ... -->
    </DateField.Root>
  8. Migrate Checkbox to v1.x

    main

    When migrating Checkbox to v1:

    • Indicator: Checkbox.Indicator is removed. Use the children snippet to access the checked state and render a custom indicator.
    • Input: Checkbox.Input is removed. Providing a name prop to Checkbox.Root now automatically renders a hidden input.
    • State: The checked state is now a boolean. To handle indeterminate states, use the indeterminate prop.
    • Groups: Use the new Checkbox.Group component for checkbox groups.
  9. Use the WithElementRef type helper for custom components

    main

    The WithElementRef type helper allows you to implement the same ref prop pattern used by Bits UI components in your own custom components. It merges a base props type with an optional ref property that can be bound to an HTML element.

    WithElementRef<T, U extends HTMLElement = HTMLElement>

    • T: The base props type.
    • U: The specific HTML element type (defaults to HTMLElement).
    <script lang="ts">
      import type { WithElementRef } from "bits-ui";
    
      type Props = WithElementRef<
        {
          yourPropA: string;
          yourPropB: number;
        },
        HTMLButtonElement
      >;
    
      let { yourPropA, yourPropB, ref = $bindable(null) }: Props = $props();
    </script>
    
    <button bind:this={ref}>
      <!-- ... -->
    </button>
  10. Fully Control Accordion state

    main

    For complete control over state reads and writes, use Svelte Function Binding with bind:value. Pass an object containing a getter and a setter function to Accordion.Root.

    <script lang="ts">
      import { Accordion } from "bits-ui";
      let myValue = $state("");
    
      function getValue() {
        return myValue;
      }
    
      function setValue(newValue: string) {
        myValue = newValue;
      }
    </script>
    
    <Accordion.Root type="single" bind:value={getValue, setValue}>
      <!-- ... -->
    </Accordion.Root>