# Sveltery Base This is the documentation for the `@sveltery/base` package, a Svelte 5 port of Base UI. It contains a collection of components and utilities for building user interfaces in Svelte. The library is designed to be composable and styling agnostic. The Tailwind CSS examples are written for Tailwind CSS v4. If `package.json` uses Tailwind CSS v3, automatically convert unsupported styles to v3-compatible equivalents. ## Overview ### Quick start A quick guide to getting started with Sveltery Base. #### Install the library Install Sveltery Base using a package manager. ##### pnpm ```bash pnpm add @sveltery/base ``` ##### npm ```bash npm i @sveltery/base ``` ##### yarn ```bash yarn add @sveltery/base ``` ##### bun ```bash bun add @sveltery/base ``` All components are included in a single package. Each component has its own entry point, such as `@sveltery/base/popover`, so your app bundle will contain only the components that you actually use. #### Set up ##### Portals Sveltery Base uses portals for components that render popups, such as Dialog and Popover. To make portaled components always appear on top of the entire page, add the following style to your application layout root. In a SvelteKit app, that is the element that wraps `%sveltekit.body%` in `src/app.html`: ```html title="app.html"
%sveltekit.body%
``` ```css title="styles.css" .root { isolation: isolate; } ``` The SvelteKit template gives that element `style="display: contents"`. Remove it, because an element with `display: contents` cannot create a stacking context. This style creates a separate stacking context for your application's `.root` element. This way, popups always appear above the page contents, and any `z-index` property in your styles won't interfere with them. ##### iOS 26+ Safari Starting with iOS 26, Safari allows content beneath the UI chrome to be visible. Backdrops such as those used by dialogs must use `position: absolute` instead of `position: fixed` to cover the entire visual viewport. For this to work after the page is scrolled, the following style must be added to your global styles: ```css title="styles.css" body { position: relative; } ``` #### Assemble a component This demo shows you how to import a [Popover](#popover) component, assemble its parts, and apply styles. There are examples for both Tailwind CSS and plain CSS below, but since Sveltery Base is unstyled, you can use CSS-in-JS, CSS Modules, or any other styling solution you prefer. #### Demo ##### Tailwind This example shows how to implement the component using Tailwind CSS. ```svelte Notifications Notifications You are all caught up. Good job! ``` ##### CSS Modules This example shows how to implement the component using CSS Modules. ```css /* index.module.css */ .Positioner { width: var(--positioner-width); height: var(--positioner-height); max-width: var(--available-width); } .Popup { box-sizing: border-box; position: relative; display: flex; flex-direction: column; gap: 0.25rem; padding: 0.75rem; outline: none; border: 1px solid oklch(14.5% 0 0deg); background-color: white; color: oklch(14.5% 0 0deg); box-shadow: 0.25rem 0.25rem 0 rgb(0 0 0 / 12%); transform-origin: var(--transform-origin); transition: transform 100ms ease-out, opacity 100ms ease-out; width: var(--popup-width, auto); height: var(--popup-height, auto); max-width: 500px; @media (prefers-color-scheme: dark) { border: 1px solid white; background-color: oklch(14.5% 0 0deg); color: white; box-shadow: none; } &[data-starting-style], &[data-ending-style] { opacity: 0; transform: scale(0.98); } } .Arrow { display: block; position: relative; width: 12px; height: 6px; overflow: clip; &[data-side='top'] { bottom: -6px; rotate: 180deg; } &[data-side='bottom'] { top: -6px; rotate: 0deg; } &[data-side='left'] { right: -9px; rotate: 90deg; } &[data-side='right'] { left: -9px; rotate: -90deg; } &::before { content: ''; display: block; position: absolute; bottom: 0; left: 50%; box-sizing: border-box; width: calc(6px * sqrt(2)); height: calc(6px * sqrt(2)); background-color: white; border: 1px solid oklch(14.5% 0 0deg); transform: translate(-50%, 50%) rotate(45deg); @media (prefers-color-scheme: dark) { background-color: oklch(14.5% 0 0deg); border: 1px solid white; } } } .Title { margin: 0; font-size: 0.875rem; line-height: 1.25rem; font-weight: 700; } .Description { margin: 0; font-size: 0.875rem; line-height: 1.25rem; color: oklch(43.9% 0 0deg); @media (prefers-color-scheme: dark) { color: oklch(70.8% 0 0deg); } } .Container { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; } .Button { box-sizing: border-box; display: flex; align-items: center; justify-content: center; gap: 0.5rem; height: 2rem; padding: 0 0.75rem; margin: 0; border: 1px solid oklch(14.5% 0 0deg); background-color: white; font-family: inherit; font-size: 0.875rem; font-weight: 400; line-height: 1; white-space: nowrap; color: oklch(14.5% 0 0deg); -webkit-user-select: none; user-select: none; @media (prefers-color-scheme: dark) { border: 1px solid white; background-color: oklch(14.5% 0 0deg); color: white; } @media (hover: hover) { &:hover:not([data-disabled], :disabled) { background-color: oklch(97% 0 0deg); @media (prefers-color-scheme: dark) { background-color: oklch(26.9% 0 0deg); } } } &:active:not([data-disabled], :disabled) { background-color: oklch(92.2% 0 0deg); @media (prefers-color-scheme: dark) { background-color: oklch(37.1% 0 0deg); } } &[data-pressed]:not([data-disabled], :disabled) { background-color: oklch(97% 0 0deg); @media (prefers-color-scheme: dark) { background-color: oklch(26.9% 0 0deg); } } &[data-disabled], &:disabled { color: oklch(55.6% 0 0deg); border-color: oklch(55.6% 0 0deg); @media (prefers-color-scheme: dark) { color: oklch(70.8% 0 0deg); border-color: oklch(70.8% 0 0deg); } } &:focus-visible { outline: 2px solid oklch(14.5% 0 0deg); outline-offset: -1px; @media (prefers-color-scheme: dark) { outline-color: white; } } } ``` ```svelte Notifications Notifications You are all caught up. Good job! ``` The same popover with plain CSS. The popup is portaled to ``, outside your component's markup, so the rules in a ` ``` #### Pre-styled components [Sveltery UI](https://ui.sveltery.dev) is a great place to start if you need pre-styled components with higher-level abstractions. It uses Sveltery Base as its unstyled foundation. Take a look at the [Community](#community) page to see more of the Sveltery Base ecosystem. #### Working with LLMs For those of you working with LLMs, each docs page has a "View as Markdown" link at the top, which can be shared with AI chat assistants to help them understand Sveltery Base concepts and component APIs. Additionally, there is an ["llms.txt"](https://base.sveltery.dev/llms.txt) link in the "Handbook" section of the navigation sidebar, which you can feed to AI chat assistants to help them navigate the docs. #### Next steps This walkthrough outlines the basics of putting together a Sveltery Base component. Continue to the [Handbook](#styling) section for broader guidance on topics like styling, animation, and composition, or explore the [components](#accordion). ### Accessibility Learn how to make the most of Sveltery Base's accessibility features and guidelines. Accessibility is a top priority for Sveltery Base. Sveltery Base components handle many complex accessibility details including ARIA attributes, role attributes, pointer interactions, keyboard navigation, and focus management. The goal is to provide an accessible user experience out of the box, with intuitive APIs for configuration. This page highlights some of the key accessibility features of Sveltery Base, as well as some ways you need to augment the library, to ensure that your application is accessible to everyone. #### Keyboard navigation Sveltery Base components adhere to the [WAI-ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/) to provide basic keyboard accessibility out of the box. This is critical for users who have difficulty using a pointer device, but it's also important for users who prefer navigating with a keyboard or other input mode. Many components provide support for arrow keys, alphanumeric keys, Home, End, Enter, and Esc. #### Focus management Sveltery Base components manage focus automatically following a user interaction. Additionally, some components provide props like `initialFocus` and `finalFocus`, to configure focus management. Where Base UI takes a ref for these props, Sveltery Base takes the element itself or a function that returns it, so read the element with `bind:this`. While Sveltery Base components manage focus, it's the developer's responsibility to visually indicate focus. This is typically handled by styling the `:focus` or `:focus-visible` CSS pseudo-classes. WCAG provides [guidelines on focus appearance](https://www.w3.org/WAI/WCAG22/Understanding/focus-appearance). #### Color contrast When styling elements, it's important to meet the minimum requirements for color contrast between each foreground element and its corresponding background element. Unless your application has strict requirements around compliance with current standards, consider adhering to [APCA](https://apcacontrast.com/). #### Accessible labels Sveltery Base provides components like [Form](#form), [Input](#input), [Field](#field), and [Fieldset](#fieldset) to automatically associate form controls. Additionally, you can use the native HTML `