# CodeAtlas UI: full documentation > Open-source Svelte 5 component library (`@codeatlasdev/ui`). Index: https://ui.codeatlas.com.br/llms.txt --- # Introduction Group: Getting started. URL: https://ui.codeatlas.com.br/docs/ CodeAtlas UI is an open-source Svelte 5 component library with a sober solid default, opt-in glass materials, an OKLCH theme engine and native-feeling mobile behavior. CodeAtlas UI gives Svelte 5 apps full screens, not just bricks: page templates, an app shell, data tables, charts, forms and overlays, all built on a headless layer of our own and themed through CSS custom properties. ```svelte {#snippet children(list)} {#each list.rows as u (u.id)} {/each} {/snippet} ``` That is a whole users page: header, search, pagination and empty state, with no CSS written. ## What you get - Page templates instead of primitives: `AppList`, `AppForm`, `AppSettings`, `PageShell` and `AppShellLayout` bring layout, spacing and empty states. - Sober by default, glass on request: surfaces are solid and opaque out of the box. The `liquid` recipe (or `defaultStyle: "glass"`) brings back Liquid Glass, with five material tiers over a real `backdrop-filter` and opaque fallbacks for reduced transparency. - A theme engine with eight OKLCH presets, brand presets from one hex color, nine surface styles, fifteen design-style recipes and no flash on load. - Native feel on mobile: safe areas, keyboard handling, 44px touch targets, bottom sheets with swipe to dismiss and a browser chrome color that follows the page. - Accessibility: WAI-ARIA keyboard and focus behavior, axe-tested components, reduced motion and reduced transparency support. - Small bundles: Charts, DataTable and CodeSnippet are separate entry points with optional peers, so they stay out of your bundle unless imported. - Agent-ready: closed prop vocabularies, typed props, an MCP server, `llms.txt` and installable skills. ## Install ## Start here ## Explore --- # Installation Group: Getting started. URL: https://ui.codeatlas.com.br/docs/getting-started/installation/ Install @codeatlasdev/ui and wire the theme into SvelteKit, Astro or plain Vite without a flash of the wrong theme. CodeAtlas UI needs Svelte 5 (runes mode). In a SvelteKit or Astro app, `bunx @codeatlasdev/ui init` does the whole setup below for you (see [The `init` command](#the-init-command)). Setup is three things: import the stylesheet once, inject the theme script before first paint, and start the `colorScheme` store in your root layout. The core bundle stays small. Heavy features are separate entry points with optional peer dependencies, so install only what you use: | Feature | Import from | Also install | |---|---|---| | Charts | `@codeatlasdev/ui/charts` | `echarts` | | DataTable | `@codeatlasdev/ui/table` | `@tanstack/svelte-table` | | CodeSnippet | `@codeatlasdev/ui/code` | `shiki` | | Masked and money fields | `@codeatlasdev/ui/forms` | nothing | | Accessibility test helpers | `@codeatlasdev/ui/testing` | `axe-core` | | Icons in your own code (`KpiCard`, `Chip`, `Button` and nav items take icon components) | `@lucide/svelte` | `@lucide/svelte` | | Brand icons (`caBrandPlugin`) | `@codeatlasdev/ui/vite` | `@resvg/resvg-js` and `png-to-ico` | | `migrate` command | `bunx @codeatlasdev/ui migrate` | `typescript` (resolved from the project; without it the command prints the install hint and exits 1) | `@sveltejs/kit` and `astro` are optional peers too: install the one your app uses. Icons are `@lucide/svelte` components. The package depends on it for its own icons, but Bun's isolated linker (and pnpm) do not expose a dependency's dependency to your app, so `import { ArrowRight } from "@lucide/svelte"` fails until you run `bun add @lucide/svelte` yourself. ### Release channels Releases are published by version tag (`@codeatlasdev/ui@X.Y.Z`), not from branches. The prerelease id in the version is the npm dist-tag. | Version | Install | When to use it | |---|---|---| | `X.Y.Z` | `bun add @codeatlasdev/ui` (dist-tag `latest`) | Production. Semver: breaking changes only in a major. | | `X.Y.Z-beta.N` | `bun add @codeatlasdev/ui@beta` | A prerelease of the next version. It never moves `latest`. Try it before it ships. | A prerelease with another id (`X.Y.Z-alpha.N`, for example) is published under a dist-tag with that id. Pin an exact version (`bun add @codeatlasdev/ui@3.1.0-beta.2`) to keep a prerelease stable. Every version is published from CI with an npm provenance statement, so you can check which commit and workflow built it (`npm audit signatures`). ## The `init` command ```sh bunx @codeatlasdev/ui init [--write] [--dir ] ``` `init` detects the framework from `package.json` (`@sveltejs/kit` means SvelteKit, otherwise `astro` means Astro) and prints `Detected SvelteKit.` or `Detected Astro.`. With neither it prints an error and exits with code 1. Then it goes through the files of that framework, one line each: | Line | Meaning | |---|---| | `create ` | The file was missing and is now written (`--write`). | | `would create ` | The file is missing. Nothing is written until you re-run with `--write`. | | `ok (already set up)` | Nothing to change. Running `init` again on a finished setup prints only `ok` lines. | | `diff (exists, not modified)` | The file exists and needs a change. A unified diff follows; nothing is written until you re-run with `--write`. | | `patch ` | The change was applied (`--write`). | | `manual : could not patch safely.` | `init` could not edit the file safely (no `plugins: [...]` array, an existing `handle` of your own, no ``). It says what to add by hand and exits with code 1. | | `skip (not found)` | The file does not exist and `init` does not create it (`vite.config.ts`, `src/app.html` and the Astro config). | | `skip : the app already imports its theme from ""` | `vite.config.ts`, `hooks.server.ts` (or the Astro config) already import `theme` from somewhere else, for example a shared workspace package. `init` does not create `src/lib/theme.ts` (`src/theme.ts` on Astro) and wires the files it adds to that import. | | Flag | Effect | |---|---| | `--write` | Create the missing files and apply the proposed changes. Without it `init` is a dry run: nothing touches the disk. | | `--dir ` | Project directory. Defaults to the current directory. | When anything is pending it ends with `N change(s) not applied. Re-run with --write to create the files and apply the diffs above.` A clean run ends with `Done. Run your dev server: the theme paints on the first frame.` ## SvelteKit `@codeatlasdev/ui` supports SvelteKit 2 (`^2.12`) and SvelteKit 3. The steps below are the same on both; the differences are in [SvelteKit 3](#sveltekit-3). `init` reads the installed Kit major (`node_modules/@sveltejs/kit`, or the range in `package.json` before `install` ran; anything it cannot parse counts as 2) and writes the right theme import. ### What `init` writes For each file below, `init` creates it when it is missing, except `vite.config.ts` and `src/app.html`, which it only patches (it prints `skip` when they are not there). Without `--write` nothing is written: `init` prints `would create` for a missing file and a diff for an existing one. A file that already exists is never overwritten: its diff is applied only with `--write`. Re-running it changes nothing once the setup is complete. | File | What `init` puts there | If the file exists | |---|---|---| | `src/lib/theme.ts` | the shared `theme` object (`defaultPreset: "amber"`, `defaultScheme: "system"`) | left alone | | `vite.config.ts` | `caThemePlugin(theme)` and `caErrorPlugin({ scaffold: true, theme })`, appended to `plugins`, in one `@codeatlasdev/ui/vite` import (plus `import { theme } from "./src/lib/theme.ts"`) | adds only the plugin that is missing | | `src/hooks.server.ts` | `handle = sequence(caThemeHandle(theme))` and `handleError = createErrorHandler()` | adds only what is missing; reports `manual` if you already define a `handle` of your own; never touches your `handleError` | | `src/routes/+layout.svelte` | the stylesheet import, `colorScheme.init()` in `onMount` and a default `` (title, canonical from `page.url`, `manifest="/manifest.webmanifest"`) | adds only what is missing; an existing `` is kept | | `src/app.html` | the viewport meta with `viewport-fit=cover, interactive-widget=resizes-content` | rewrites only the viewport meta; reports `manual` when there is none | | `src/routes/manifest.webmanifest/+server.ts` | a prerendered manifest built with `manifest({ name, theme, icons })` | left alone | | `src/routes/robots.txt/+server.ts`, `src/routes/sitemap.xml/+server.ts` | prerendered `robotsTxt()` (pointing at the sitemap) and `sitemapXml()` listing the static pages (no `[param]` routes, route groups dropped, no `/offline`) | left alone | | `src/service-worker.ts` (Kit 2) or `src/service-worker/index.ts` (Kit 3), `src/routes/offline/` | the worker from [PWA](/docs/getting-started/pwa/#service-worker) and the prerendered offline page it falls back to | left alone | After creating these, `init` prints what the app still has to provide: its public origin (`paths.origin`, otherwise sitemap, `robots.txt` and canonical URLs carry Kit's prerender placeholder), the manifest icons, and on Kit 3 the `src/service-worker` entry in the `exclude` of `tsconfig.json`. See [SEO](/docs/getting-started/seo/) and [PWA](/docs/getting-started/pwa/). `caErrorPlugin({ scaffold: true, theme })` then creates `src/routes/+error.svelte` and `src/error.html` the first time Vite runs (dev or build), and never overwrites them. `src/error.html` takes its colors, default scheme and preset from the `theme` you pass (the same object as `caThemePlugin`), carries the theme init script in ``, and has no inline handler or `javascript:` URL, so it works under a strict CSP. Its copy and `lang` follow `locale` (`caErrorPlugin({ scaffold: true, theme, locale: "pt-BR" })`; default `en`, with a build warning until you set it). The result is the setup below, which you can also write by hand. ### Full setup 1. **Register both Vite plugins.** `caThemePlugin` bundles the ESM-only dependencies for SSR (`@tanstack/svelte-table`, `shiki`) and warns at build time if the theme script is not injected. `caErrorPlugin` creates the error pages when they are missing (`scaffold: true`), or only warns without it. Import the theme **with its `.ts` extension**: Vite loads the config with Node's native loader, which does not resolve extensionless imports (Vite 8 warns about it). That needs `"allowImportingTsExtensions": true` in `tsconfig.json`; SvelteKit's generated base config already sets it. ```ts // vite.config.ts import { sveltekit } from "@sveltejs/kit/vite"; import { caErrorPlugin, caThemePlugin } from "@codeatlasdev/ui/vite"; import { defineConfig } from "vite"; import { theme } from "./src/lib/theme.ts"; export default defineConfig({ plugins: [sveltekit(), caThemePlugin(theme), caErrorPlugin({ scaffold: true, theme })], }); ``` 2. **Inject the theme script and sanitize server errors in the server hook.** `caThemeHandle` puts the brand presets and the init script right after `` of every SSR and prerendered page, so the first paint already has the right scheme and preset. `createErrorHandler` turns every unexpected server error into `{ message, code, id }` without leaking the message of a 5xx; [pass a `locale` or a `report` callback](/docs/getting-started/i18n/#server-errors) when you need them. ```ts // src/hooks.server.ts import { sequence } from "@sveltejs/kit/hooks"; import { caThemeHandle, createErrorHandler } from "@codeatlasdev/ui/kit"; import { theme } from "$lib/theme"; export const handle = sequence(caThemeHandle(theme)); export const handleError = createErrorHandler(); ``` 3. **Define the theme once.** Both the hook and the plugin take the same object. An empty object is valid and gives you the built-in amber preset in dark mode. ```ts // src/lib/theme.ts import type { ThemeOptions } from "@codeatlasdev/ui/vite"; export const theme: ThemeOptions = { defaultScheme: "system", }; ``` 4. **Import the stylesheet and start the store in the root layout.** ```svelte {@render children()} ``` 5. **Set the viewport meta in `src/app.html`.** See [Mobile](/docs/getting-started/mobile/) for what each part does. ```html ``` Neither plugin depends on where it sits in `plugins`. `caThemePlugin` is `enforce: "pre"`, so Vite runs it first wherever you put it, and it looks for SvelteKit in the resolved plugin list, not in the array you wrote. `caErrorPlugin` only reacts to `configResolved`, `buildStart` and the dev server start. Checked with SvelteKit 3.0.1 and Vite 8.3: building with `caThemePlugin` and `caErrorPlugin` before, after and around `sveltekit()` gives the same result (theme script injected, error pages scaffolded, `ssr.noExternal` set), and in plain Vite `caThemePlugin()` before or after `svelte()` injects the script once, in build and in dev. The examples differ (after `sveltekit()`, before `svelte()`) only by convention; `init` follows the SvelteKit one. If you cannot use a hook, paste the script into `src/app.html` instead. `CA_THEME_SCRIPT_TAG` from `@codeatlasdev/ui/vite` is a ready-to-paste tag for the default theme; for a custom theme, use the hook. ### SvelteKit 3 Kit 3 changed three things this setup touches. `init` handles the third (the theme import in the hook) and, for the other two, prints a `note` line instead of rewriting `svelte.config.js` or `tsconfig.json`: it reports `svelte.config.js`/`.ts`/`.mjs` when one exists, and a `tsconfig.json` that does not mention `$app/tsconfig`. On a clean Kit 3 run you get no `Done.` line while a note is pending. 1. **Configuration moves into the Vite plugin.** `svelte.config.js` is ignored (`config_file_unsupported`) and the `kit` namespace is gone (`config_kit_namespace`). Pass the options directly to `sveltekit()`, next to the two plugins: ```ts // vite.config.ts import adapter from "@sveltejs/adapter-auto"; import { sveltekit } from "@sveltejs/kit/vite"; import { caErrorPlugin, caThemePlugin } from "@codeatlasdev/ui/vite"; import { defineConfig } from "vite"; import { theme } from "./src/lib/theme.ts"; export default defineConfig({ plugins: [sveltekit({ adapter: adapter() }), caThemePlugin(theme), caErrorPlugin({ scaffold: true, theme })], }); ``` 2. **`tsconfig.json` extends `$app/tsconfig`.** `.svelte-kit/tsconfig.json` is no longer generated (`tsconfig_extends_missing`): ```json { "extends": "$app/tsconfig" } ``` 3. **`$lib` is removed** (`module_removed_lib`). Use the `#lib/*` import that `sv create` adds to `package.json` (`"imports": { "#lib/*": "./src/lib/*" }`), written with the extension. In the hook, import the theme as `#lib/theme.ts`: ```ts // src/hooks.server.ts import { theme } from "#lib/theme.ts"; ``` Without a `#lib` map, use a relative path (`./lib/theme.ts` from `src/hooks.server.ts`). Everything else (the layout, the stylesheet import, `colorScheme.init()` and the viewport meta) is identical to Kit 2. ## Astro Use the integration together with `@astrojs/svelte`. `caUI` injects the theme into the `` of every page and handles the SSR bundling. `bunx @codeatlasdev/ui init` sets this up for an Astro project: | File | What `init` puts there | If the file exists | |---|---|---| | `src/theme.ts` | the shared `theme` object (`defaultPreset: "amber"`, `defaultScheme: "system"`) | left alone | | `astro.config.mjs` (or `.ts`, `.js`, `.mts`) | `caUI(theme)` and `svelte()` first in `integrations`, with their imports | adds each one unless `caUI(` / `svelte(` is already there; reports `manual` when there is no `integrations: [` array. `init` cannot install `@astrojs/svelte`: when it is not in `package.json` it prints a `note` to run `npx astro add svelte` | | the first `.astro` file in `src/layouts` that renders `` | the stylesheet import, the viewport meta and a client ` ``` Components are Svelte islands. Add a client directive to anything interactive (`client:visible` or `client:load`); purely visual components render on the server with no JavaScript. Call `colorScheme.init()` once per page, from that layout script or from one island that is always present. With `` (View Transitions) the theme survives navigation: `caUI` copies the injected ` ``` Use it on links you render yourself (a list of records, a card, a custom tab). It matches on pathname, so a link to `/billing?tab=plans` is also marked while `/billing` loads. Like the progress bar it only changes after SvelteKit reports a pending navigation, and a preloaded navigation is usually over before it shows. ## View transitions For a cross-fade between routes, SvelteKit's `onNavigate` hook starts a browser view transition and waits for the new page. Add it once in the root layout. Skip it when the user prefers reduced motion. ```svelte ``` The browser cross-fades screenshots, so there is no per-element code. It does not replace the progress bar: for a navigation that takes time, `NavigationProgress` is what tells the user something is happening, and the transition runs once the page is ready. ## Putting it together | Layer | Handles | Tool | |---|---|---| | Preload | Most navigations finish before the click completes | `data-sveltekit-preload-data="hover"` and `data-sveltekit-preload-code="viewport"` on `` | | Whole navigation | A route that still takes more than 120 ms | `NavigationProgress` in the root layout | | Tapped link | Feedback where the finger is | `pendingLink()` on the link | | Data inside a page | A region that awaits | `LoadingBoundary` around it | | Screen reader | The page changed | `RouteAnnouncer` in the root layout | | Between routes | A smooth change | `onNavigate` with `document.startViewTransition` | --- # StickyActions Group: Layout. URL: https://ui.codeatlas.com.br/docs/layout/sticky-actions/ A form action bar pinned to the bottom of a scrolling container, with safe-area and virtual keyboard support. ```svelte ``` Place it as the last child of the element that scrolls. On screens narrower than 900px it sticks to the bottom edge with the `--ca-glass-bg` fill (blurred only under the `glass` and `frosted` styles), respects the home-indicator inset (`--ca-safe-bottom`) and rises with the virtual keyboard. From 900px up it becomes a plain inline row without chrome. ### End aligned Buttons sit on the right. The bar only renders while the form is dirty (show). ### Stretch On touch screens the primary button fills the width and the ghost button stays compact. ### Props | Prop | Type | Default | Description | |---|---|---|---| | `children` | `Snippet` | | The buttons of the bar. | | `align` | `'end' \| 'stretch'` | 'end' | 'end' packs the buttons to the right. 'stretch' makes the buttons fill the width on touch screens. | | `show` | `boolean` | true | Renders the bar only while there is something to confirm. | | `class` | `string` | '' | Extra class names. | --- # SkipLink Group: Layout. URL: https://ui.codeatlas.com.br/docs/layout/skip-link/ A "Skip to content" link that appears on keyboard focus and moves focus to the main landmark. Built into the page shells. ```svelte
…
…
``` `AppShellLayout`, `DocsShell` and `AuthLayout` already render it, so you only need `SkipLink` in shells you build yourself. Press Tab on any docs page to see it: it is the first focusable element and stays off-screen until focused. The link scrolls to the target and moves focus to it, so the next Tab continues inside the content. The target needs `tabindex="-1"`; the package hides the focus ring on `main[tabindex="-1"]` because that focus is only programmatic. Keep exactly one `id="main"` and one `
` per page. See [Skip link and landmarks](/docs/getting-started/accessibility/#skip-link-and-landmarks) for who owns `#main` in each shell. ### Props | Prop | Type | Default | Description | |---|---|---|---| | `href` | `string` | '#main' | Fragment of the target landmark. It must match the `id` of the element. | | `children` | `Snippet` | | Replaces the localized label (`skipLink.label` in the i18n catalog). | | `class` | `string` | '' | Extra class names. | --- # Layout primitives Group: Layout. URL: https://ui.codeatlas.com.br/docs/layout/layout-primitives/ Box, Flex, Stack, Grid, Container and Section: spacing and alignment as token props, rendered as data attributes with no inline style. ```svelte
… …
``` Every primitive renders any element (`as`) and forwards `class` and other attributes. `Box`, `Flex`, `Stack` and `Grid` are typed on the tag: `as="form"` takes the form's own attributes and handlers (`novalidate`, `action`, `onsubmit` with `currentTarget: HTMLFormElement`), `as="a"` takes `href`, `as="label"` takes `for`, and an attribute of another tag is a type error. A token prop never becomes inline CSS: it is a `data-*` attribute (`
`) styled by one stylesheet in `@layer ca.utilities`, so the markup is cache-friendly, passes a strict CSP, and an app can override any step with a plain class. | Primitive | Renders | Props | |---|---|---| | `Box` | `div`, padding | `p`, `px`, `py` | | `Flex` | `display: flex` | `gap`, `p`, `px`, `py`, `align`, `justify`, `direction`, `wrap` | | `Stack` | a Flex column | `gap`, `p`, `px`, `py`, `align`, `justify` | | `Grid` | `display: grid`, equal tracks | `columns`, `gap`, `p`, `px`, `py`, `align` | | `Container` | centered, width-capped, fluid gutter | `size` (`sm` `md` `lg` `xl`), `px`, `py` | | `Section` | a `
` with the page rhythm | `size` (`1` `2` `3`), `px`, `py` | Spacing values are steps of the spacing scale as strings (`"4"` is `--ca-space-4`, 16px at the default density; `"0.5"` is 2px). The scale is `0 0.5 1 1.5 2 2.5 3 3.5 4 4.5 5 6 7 8 10 11 12 16 20 24`. A value off the scale logs a `[ca-ui]` warning in development and renders unstyled: for a one-off value add a class. `align` is `start | center | end | stretch | baseline`, `justify` is `start | center | end | between | around | evenly`, `direction` is `row | column | row-reverse | column-reverse`, and `columns` is 1 to 12 equal tracks. For a template like `2fr 1fr` or `repeat(auto-fill, minmax(200px, 1fr))` write `grid-template-columns` in a class. ## Responsive values Pass an object instead of a value. `base` applies everywhere, and each breakpoint applies from its width up, on the shared scale: `sm` 600, `md` 900, `lg` 1200, `xl` 1536 px. The object becomes `data-gap="2" data-md-gap="4"`. ### Flex, Stack and Box ### Responsive Grid ### Container and Section ### Props | Prop | Type | Default | Description | |---|---|---|---| | `as` | `keyof SvelteHTMLElements` | per component | Element to render (`div`, `section`, `ul`, `form`...). Box, Flex, Stack and Grid also accept that element's own attributes and event types. | | `gap` | `Space \| responsive` | undefined | Gap between children. | | `p, px, py` | `Space \| responsive` | undefined | Padding on every side, horizontal, vertical. | | `align` | `'start' \| 'center' \| 'end' \| 'stretch' \| 'baseline' \| responsive` | undefined | `align-items`. | | `justify` | `'start' \| 'center' \| 'end' \| 'between' \| 'around' \| 'evenly' \| responsive` | undefined | `justify-content`. | | `direction` | `'row' \| 'column' \| 'row-reverse' \| 'column-reverse' \| responsive` | 'row' | Flex only. | | `wrap` | `boolean \| responsive` | false | Flex only: `flex-wrap: wrap`. | | `columns` | `1–12 \| responsive` | undefined | Grid only: equal tracks. | ## Migrating from Stack, Grid and Row `Stack`'s `gap` used to be pixels (`gap={16}`) and `Grid` took `cols`. They now take spacing steps (`gap="4"`) and `columns`, and `Row` is deprecated in favor of `Flex`. Run the codemod, which converts every static usage and leaves the rest for you: ```sh bunx @codeatlasdev/ui migrate --write src ``` | Before | After | |---|---| | `` | `` | | `` | `` | | `` | `` | | `` | `` | | `` | `` and a class with `grid-template-columns: 2fr 1fr` (`cols` is reported, the gap is still converted) | What the codemod cannot map is left as it was and listed at the end with file, line, reason and recipe, for example `src/T.svelte:5 Grid: cols is a template string (write grid-template-columns in a class)` or `src/T.svelte:6 Stack: dynamic gap (use a step string: px / 4, e.g. gap={16} is gap="4")`. A `gap={gap}` left untouched is not harmless: it is now a step, so `16` means 64px. Add `--strict` to exit with code 1 when anything was left (CI, scripts). --- # MobilePane Group: Layout. URL: https://ui.codeatlas.com.br/docs/layout/mobile-pane/ A full-screen pane above the shell for mobile drill-down such as chat threads, history and detail views. ```svelte (chatId = null)}> ``` The pane is `position: fixed` and covers the viewport, with a header that has a back button. Escape and the back button close it, focus moves into the pane on open and returns to the previous element on close, and the body respects safe-area insets and the virtual keyboard. It replaces hand-rolled fixed panels. ### Chat Open the pane. On a phone the text field rises with the virtual keyboard. ### Props | Prop | Type | Default | Description | |---|---|---|---| | `open` | `boolean` | false | Visibility (bindable). The back button and Escape set it to false. | | `title` | `string` | | Header title and accessible name of the pane. | | `onback` | `() => void` | | Called after the back button or Escape closes the pane. | | `children` | `Snippet` | | Scrollable body. | | `actions` | `Snippet` | | Right side of the header. | | `headerTitle` | `Snippet` | | Replaces the title text in the header (for example avatar plus name). title stays as the accessible name. | | `scroll` | `boolean` | true | false renders the body as a flex column without its own scroll, for layouts with a scrollable area and a fixed composer. | | `class` | `string` | '' | Extra class names on the pane. | --- # AppShell Group: Layout. URL: https://ui.codeatlas.com.br/docs/layout/app-shell/ Application layout with a module rail, a navigation panel, a mobile header and a bottom navigation, independent of any router. ```svelte goto(path)}> {#snippet header()}{/snippet} {@render children()} ``` The shell never imports a router. You pass the current `pathname` and handle `onNavigate`, so it works with SvelteKit, Astro, or plain state. `onNavigate` fires when the user switches module from the rail or the bottom navigation. Panel items are plain `` links, so a client-side router takes them over like any other link. Put it only on application routes, never on marketing pages. The panel is skipped for a module with fewer than two destinations, since it would only repeat the rail icon and the page title. Below 900px the rail and panel disappear and the mobile header plus bottom navigation take over. See the [templates](/templates/) for complete applications built on it. The rail magnifies the module icon under the pointer, like a dock. Only the glyph grows, never the button, so the hover background cannot cover the active neighbour. The effect follows the motion scale: it is off with `motion: { scale: 0 }` in the theme and under `prefers-reduced-motion`. ### Anatomy Rail (52px), panel (200px by default) and the main content area. ### Live shell The real component inside a fixed-height frame. The pathname lives in local state; click the rail and panel to navigate. ### Panel width panelWidth={280} with resizablePanel: drag the edge or focus it and use the arrow keys. ### Props | Prop | Type | Default | Description | |---|---|---|---| | `modules` | `NavModule[]` | | Navigation modules. Each has id, label, icon, basePath, optional pathPrefixes and items (NavItem or NavSection). Prefixes match whole path segments and the longest one wins, so a module at `basePath: "/"` only gets the pages no other module claims. | | `pathname` | `string` | | Current location. Decides the active module and item. An item at `path: "/"` is active on `/` only. | | `onNavigate` | `(path: string) => void` | | Called with the first path of the module when the rail or the bottom navigation switches module. Panel items are links and do not call it. | | `defaultModule` | `number` | 0 | Module index used when the URL matches no module and none was stored. | | `persistPanel` | `boolean` | true | Keeps the shell across page loads: panel open or closed, selected module and expanded sections (localStorage), panel scroll (sessionStorage). Restored before the first paint. Turn it off for demos and embeds that must always start from the defaults. | | `panelWidth` | `number` | 200 | Width of the navigation panel in px, for lists with long labels. Held between 160 and 480. The content offset and the open and close animation follow it, so nothing misaligns. Prefer it to overriding `--ca-panel-width`, which the layout does not read. | | `resizablePanel` | `boolean` | false | Adds a drag handle on the panel's trailing edge. It is a keyboard-operable separator: Arrow keys move it by 8px, Shift by 32px, Home and End jump to the bounds. With `persistPanel` the chosen width is kept like the open state and restored before the first paint; `panelWidth` is the width used until the user picks one. | | `reorder` | `Component` | | Pass the exported `NavReorder` to enable `onReorder` on sections and modules. Opt-in so the drag engine is only bundled by apps that reorder. | | `itemMenu` | `Component` | | Pass the exported `NavItemMenu` to render `NavItem.actions` as a "…" menu. Opt-in so the dropdown menu is only bundled by apps that use it. | | `header` | `Snippet` | | Desktop header rendered above the page content. Hidden below 900px. | | `brand` | `Snippet` | | Logo shown at the top of the rail and in the mobile header. | | `railBottom` | `Snippet` | | Items pinned to the bottom of the rail, such as an avatar or notifications. | | `mobileHeaderActions` | `Snippet` | | Controls at the trailing edge of the mobile header. Use it for anything that must stay reachable below 900px. | | `mobileTitle` | `Snippet` | | Replaces the plain page title in the mobile header, for example a tappable title that opens a sheet. | | `mainId` | `string \| null` | 'main' | `id` of the content region, which is the page `
` landmark and the skip link target. Pass `null` when your page renders its own `
`: the region becomes a plain `div`. | | `skipLink` | `boolean` | true | Renders the SkipLink. Turn it off when the shell is embedded in a page that already has one. | | `children` | `Snippet` | | Page content. | ## Panel width The panel is 200px by default. For lists with long names pass `panelWidth` (px, held between 160 and 480): the content offset and the open and close animation follow it, so the page never misaligns. Add `resizablePanel` to let people choose: a handle sits on the panel's trailing edge and is a keyboard-operable separator (`role="separator"` with `aria-valuenow`, `aria-valuemin` and `aria-valuemax`). Arrow keys move it by 8px, Shift by 32px, Home and End jump to the bounds. While the pointer holds the edge the panel follows it with no spring in between. With `persistPanel` the width is stored with the open state and restored before the first paint; `panelWidth` is the width used until the user picks one. A stored width is ignored when `resizablePanel` is off, so the prop stays the contract. ## Rich panel items A `NavItem` takes more than an icon and a label: | Field | What it does | |---|---| | `color` | A decorative dot before the label, any CSS color. The icon stays. | | `leading` | A snippet that replaces the icon (a color swatch, an avatar, an emoji). Decorative: the label is the name. | | `count` | A neutral total after the label (`1.2k` above 999). The link's name carries the exact number, never "unread". `0` renders. | | `badge` | Unread-style count: announced "N unread", capped at 99+. Use `count` for a plain total. | | `actions` | A snippet of `DropdownMenuItem`s. The row gets a "…" button named `Actions for ` inside it (`aria-current="page"` when active); the actions button and the grip are siblings of the link, because a link cannot contain controls. `SectionGroup` opens itself when it holds the current page, and inside an `AppShellLayout` with `persistPanel` it stores its open state under the section `label`; a bare `SectionGroup` keeps nothing. ### Props | Prop | Type | Default | Description | |---|---|---|---| | `item` | `NavItem` | | The destination: `id`, `label`, `icon`, `path`, and optional `badge`, `count`, `color`, `leading`, `actions`, `isNew`, `shortcut`, `description`. Required. | | `isActive` | `boolean` | | Marks the row as the current page (`aria-current="page"`, active pip). Required. | | `href` | `string` | | Link target. Required. | | `grip` | `boolean` | false | Adds the drag grip. Set it when the list the row sits in is reorderable (`NavSection.onReorder`). | | `describedby` | `string` | | `id` of the node that describes the keyboard drag (`dnd.instructionsId`), set on the grip. | | `class` | `string` | '' | Extra class names. | ### Props | Prop | Type | Default | Description | |---|---|---|---| | `section` | `NavSection` | | `label`, `items`, and optional `collapsible` (default `true`), `defaultOpen` (default `true`) and `onReorder` (makes the items reorderable). Required. | | `pathname` | `string` | | Current location, used to mark the active item and to keep the group holding it open. Required. | | `class` | `string` | '' | Extra class names. | --- # PageShell Group: Layout. URL: https://ui.codeatlas.com.br/docs/layout/page-shell/ The standard page container with a title, a description and an actions slot. ```svelte {#snippet actions()}{/snippet} ``` Below 900px the header stacks: the description leads and `actions` wrap onto their own row. Labeled buttons share that row full-width (large tap targets); icon-only controls (`IconButton`, `ThemeToggle`) keep their natural size and stay at the end of the row. The shell renders `title` as the page `

`; below 900px inside an `AppShellLayout` it is hidden visually because the mobile header already shows it. Inside a `PageShell`, `AppSettings` or `AppForm`, `EmptyState` and `FormSection` titles default to `

` (under the page `

`). Pass `headingLevel` to `EmptyState`, `FormSection` or `Card` to pick the level explicitly. ### Dashboard page KPI cards and a content card inside a PageShell. ### Props | Prop | Type | Default | Description | |---|---|---|---| | `title` | `string` | | Page title. | | `description` | `string` | | Supporting text under the title. | | `gap` | `number` | undefined | Vertical gap between the header and the content, in px. Omit for `--ca-space-7` (28px at the default density). | | `maxWidth` | `string` | undefined | Maximum width of the content column, any CSS length. Omit for 80rem (1280px at the default 4px spacing unit). | | `actions` | `Snippet` | | Right side of the page header. | | `children` | `Snippet` | | Page content. | | `class` | `string` | '' | Extra class names. | --- # Page layouts Group: Layout. URL: https://ui.codeatlas.com.br/docs/layout/list-page/ Ready-made page layouts for lists, forms, settings and dashboards, built from AppList, AppForm, AppSettings and PageShell. ```svelte ``` These layouts fill the content area of the [app shell](/docs/layout/app-shell/) and manage their own scrolling, so give them a parent with a fixed or flexible height. AppForm has its own page, see [AppForm](/docs/layout/form-page/). ### AppList Pass data and the layout handles search, filters and pagination. ### AppSettings Side navigation plus content. The active tab is managed internally. ### PageShell A scrollable container for dashboards and non-CRUD pages. ### AppList props | Prop | Type | Default | Description | |---|---|---|---| | `title` | `string` | | Page title. | | `description` | `string` | | Subtitle. Derived from the list count when omitted in data mode. | | `data` | `T[]` | | Full dataset. When set, AppList manages search, filters and pagination. | | `searchKeys` | `(keyof T)[]` | | Fields included in text search. Defaults to all string fields. | | `perPage` | `number` | 20 | Rows per page. | | `idKey` | `keyof T` | 'id' | Row identifier field. | | `searchPlaceholder` | `string` | | Placeholder of the search input. | | `ofLabel` | `string` | 'of' | Word between the numbers in the count and page info. | | `filters` | `Snippet<[ListCtx]>` | | Extra filter controls after the search input. | | `children` | `Snippet<[ListCtx]> \| Snippet` | | Table or grid content. Receives the list context in data mode. | | `actions` | `Snippet` | | Top-right slot for the primary action. | | `toolbar` | `Snippet` | | Manual toolbar, used only without data. | | `empty` | `Snippet<[ListCtx]> \| Snippet` | | Shown when filtered rows are zero. | | `footer` | `Snippet` | | Manual footer, used only without data. | | `maxWidth` | `string` | | Maximum width of the content. | | `class` | `string` | '' | Extra class names. | ### AppSettings props | Prop | Type | Default | Description | |---|---|---|---| | `title` | `string` | | Page title. | | `description` | `string` | | Subtitle. | | `tabs` | `{ value, label, icon?, badge?, danger? }[]` | | Side navigation entries. | | `defaultTab` | `string` | | Initial tab. Defaults to the first. | | `activeTab` | `string` | | Bindable active tab for external control. | | `onTabChange` | `(value: string) => void` | | Called when the tab changes. | | `actions` | `Snippet` | | Top-right slot for save and cancel buttons. | | `children` | `Snippet<[string]>` | | Content. Receives the active tab value. | | `class` | `string` | '' | Extra class names. | --- # KpiCard Group: Layout. URL: https://ui.codeatlas.com.br/docs/layout/kpi-card/ A metric card with a title, a value, an optional unit, a trend and an icon. ```svelte ``` Put several cards in a [KpiGrid](/docs/layout/kpi-grid/) so a row never ends on an empty cell. ### Basic metrics ### Without trend Omit trend and the indicator disappears. ### Negative trend ### Props | Prop | Type | Default | Description | |---|---|---|---| | `title` | `string` | | Metric name. | | `value` | `string \| number` | | Main value. | | `unit` | `string` | | Unit shown after the value (%, s, users). | | `trend` | `number` | | Percentage change. Positive is green, negative is red, zero is neutral. | | `icon` | `IconComponent` | | Lucide icon. Size and color are managed by the card. | | `skeleton` | `boolean` | false | Loading placeholder with the exact size of the loaded card. See Skeleton. | | `class` | `string` | '' | Extra class names. | --- # KpiGrid Group: Layout. URL: https://ui.codeatlas.com.br/docs/layout/kpi-grid/ A responsive row of KpiCards that never leaves an empty cell, whatever the count. ```svelte ``` `Grid cols="repeat(auto-fill, minmax(160px, 1fr))"` does not know how many cards it holds, so three cards on a 390px phone become two plus one with an empty cell. `KpiGrid` counts its children in CSS and only picks a column count that divides that number: three cards are three across when they fit and a single column when they do not, four cards are four across, then 2 x 2, then one column. It reacts to its own width, not the viewport, so it behaves the same in a sidebar layout or a narrow card, and it needs no JavaScript, so server-rendered pages are correct from the first paint. | Cards | 344px | 528px | 712px | 896px | 1080px | |---|---|---|---|---|---| | 1 | 1 | 1 | 1 | 1 | 1 | | 2 | 2 | 2 | 2 | 2 | 2 | | 3 | 1 + 1 + 1 | 3 | 3 | 3 | 3 | | 4 | 2 + 2 | 2 + 2 | 4 | 4 | 4 | | 5 | 1 per row | 1 per row | 1 per row | 5 | 5 | | 6 | 2 + 2 + 2 | 3 + 3 | 3 + 3 | 3 + 3 | 6 | Widths are the grid's own width (it uses a container query), which is smaller than the viewport inside a shell with a sidebar. A 390px phone with the usual page padding is the 344px column. Tiles are at least 10rem wide. Above 12 children there is no useful even split, so the grid falls back to `auto-fill` and may end on a partial row: use several `KpiGrid`s in that case. Odd prime counts (5, 7, 11) stack to one column below the width that fits them all, because no smaller column count divides them evenly. ### Three cards One row when it fits, one column on a phone, never 2 + 1. ### Four cards Four across, then 2 x 2, then one column. ### Props | Prop | Type | Default | Description | |---|---|---|---| | `gap` | `number \| string` | var(--ca-space-3) | Gap between cards. A number is pixels. | | `class` | `string` | '' | Extra class names on the wrapper. | | `children` | `Snippet` | | The KpiCards (or any equal-weight tiles). | --- # RecordList Group: Layout. URL: https://ui.codeatlas.com.br/docs/layout/record-list/ Records as dense rows (identity, a few columns, a row menu) that re-flow to a compact two-line row on a phone. ```svelte {#each apps as app (app.id)} {#snippet identity()}{app.name}{/snippet} {app.scopes} {app.installs} {#snippet actions()}…{/snippet} {/each} ``` The usual dashboard list is neither a table nor a card per record. `Table` with `responsive="cards"` makes each row three or four times taller on a phone, and a `Card` per record repeats the chrome. `RecordList` owns what every app hand-rolls around that shape: the flush container, the separators, one column template shared by the header and every row, the re-flow below a width, and a 44px actions cell. `columns` is one `grid-template-columns` value: the identity first, one track per `RecordCell`, the actions track last. `labels` are the column headers of the identity and the cells. A row without `actions` simply leaves the last track out of `columns`. ## Wide and stacked Wide, the header is an `aria-hidden` row and every `RecordCell` keeps its `label` as a visually hidden `dt`, so assistive technology still gets "Scopes: read:invoices" per cell. Below 36rem of the **list's own width** (a container query, not the viewport) the header disappears and the row becomes two lines: identity and actions on the first, the cells below as small label/value pairs that wrap. The same list behaves the same in a narrow card or beside a sidebar, and it needs no JavaScript, so server-rendered pages are correct from the first paint. With no rows, render your own [empty state](/docs/layout/empty-state/) instead of the list. ### Apps Identity with avatar, status chip and a row menu. Narrow the window or the container to see it stack. ### Phone width A 344px slot: the same list stacked, because the list reads its own width. ### Without a menu No actions slot and no actions track: three columns that stack the same way. ## Accessibility The list is a real `
    ` with one `
  • ` per record: give it an `aria-label`. Per-cell labels are `dt`/`dd` pairs inside a `dl`, so they are announced with their value in both layouts. Put an `aria-label` that names the record on every action button ("Actions for Billing"), because the visible column header is hidden from assistive technology. ### RecordList props | Prop | Type | Default | Description | |---|---|---|---| | `columns` | `string` | | `grid-template-columns` shared by the header and every row: identity, one track per cell, then actions. | | `labels` | `string[]` | | Column headers for the identity and each cell, in order. Omit for no header row. | | `class` | `string` | '' | Extra class names on the wrapper. | | `children` | `Snippet` | | The RecordRows. | ### RecordRow props | Prop | Type | Default | Description | |---|---|---|---| | `identity` | `Snippet` | | First column: what the record is. Heads the stacked row. | | `actions` | `Snippet` | | Last column, usually a DropdownMenu. At least 44px square. | | `class` | `string` | '' | Extra class names on the row. | | `children` | `Snippet` | | The RecordCells, in the order of the `columns` tracks. | ### RecordCell props | Prop | Type | Default | Description | |---|---|---|---| | `label` | `string` | | The column name: kept for assistive technology on wide rows, painted above the value when stacked. | | `class` | `string` | '' | Extra class names on the cell. | | `children` | `Snippet` | | The value. | --- # AppForm Group: Layout. URL: https://ui.codeatlas.com.br/docs/layout/form-page/ A page-level form layout with a fixed header, a scrollable body and a sticky footer. ```svelte {#snippet footer()} {/snippet} ``` `FormSection` titles inside an AppForm are `

    ` under the page `

    ` (pass `headingLevel` on the section to override). AppForm fills its parent, so give the parent a height. It replaces the removed FormPage. For a floating action bar inside your own scroll container, use [StickyActions](/docs/layout/sticky-actions/). ### Basic form Header with a back button, a grid of fields, a list of switches and a sticky footer. ### Without header Omit title, description, leading, trailing and backHref to drop the header. ### Props | Prop | Type | Default | Description | |---|---|---|---| | `title` | `string` | | Page title in the header. | | `description` | `string` | | Subtitle under the title. | | `backHref` | `string` | | Renders a back-arrow button linking here. Ignored when leading is provided. | | `backLabel` | `string` | 'Back' | Accessible label of the automatic back button. | | `leading` | `Snippet` | | Left side of the header, such as a back button or breadcrumb. Overrides backHref. | | `trailing` | `Snippet` | | Right side of the header, such as a status chip or secondary actions. | | `footer` | `Snippet` | | Sticky bottom bar for cancel, save and delete buttons. | | `children` | `Snippet` | | Form body. Usually FormSection components. | | `class` | `string` | '' | Extra class names. | --- # EmptyState Group: Layout. URL: https://ui.codeatlas.com.br/docs/layout/empty-state/ A placeholder for empty views with an icon, a title, a description and actions. ```svelte {#snippet actions()}{/snippet} ``` ### Basic Icon, title, description and a primary action. ### No results For searches and filters that match nothing. ### Error or offline Failed network requests and loading errors. ### Hero A larger, brand-tinted tile with a glow. On mobile the actions stack full width, so put the primary one first. ### No icon A minimal version without an icon. ### Props | Prop | Type | Default | Description | |---|---|---|---| | `icon` | `IconComponent` | | Lucide icon. Size and color are managed by the component. | | `title` | `string` | | Main title. | | `headingLevel` | `2 \| 3 \| 4 \| 5 \| 6` | 2 inside PageShell/AppSettings/AppForm, 5 elsewhere | Heading level of the title. Only the tag changes; the look stays on `.ca-empty__title`. Keeps the outline valid under the page's `

    `. | | `variant` | `'default' \| 'hero'` | 'default' | 'default' is a compact inline state. 'hero' adds a larger brand-tinted icon tile with a glow and bigger title. | | `description` | `string` | | Supporting text. | | `actions` | `Snippet` | | Call-to-action buttons. | | `children` | `Snippet` | | Extra content below the description. | | `class` | `string` | '' | Extra class names. | --- # ErrorPage Group: Layout. URL: https://ui.codeatlas.com.br/docs/layout/error-page/ A full-page error state for 404, 403, 500 and 503 with localized copy and home and back actions. ```svelte ``` Known statuses (404, 403, 500, 503) get an icon, a title and a hint in the active locale. Any other status falls back to a generic title. An explicit `message` replaces the hint and `title` replaces the title. The actions go back in history and to `/`. `caErrorPlugin({ scaffold: true })` writes a `+error.svelte` like this one for you (see [Installation](/docs/getting-started/installation/)). ### 404 without actions hideActions removes the back and home buttons. ### Props | Prop | Type | Default | Description | |---|---|---|---| | `status` | `number` | 500 | HTTP status shown as the large number. Picks the icon, title and hint for 404, 403, 500 and 503. | | `message` | `string` | | Replaces the preset hint under the title. | | `title` | `string` | | Replaces the preset title. | | `hideActions` | `boolean` | false | Hides the back and home buttons. | | `class` | `string` | '' | Extra classes on the root. | --- # AuthLayout Group: Layout. URL: https://ui.codeatlas.com.br/docs/layout/auth-layout/ A two-column page for sign in, sign up and password reset, with AuthCard for the form. ```svelte {#snippet panel()}
    Ship the boring parts in a day.
    {/snippet}
    {#snippet footer()}
    Create an account {/snippet} ``` `AuthLayout` renders the page `
    `, so a route that uses it must not add another one. The `panel` snippet fills the side column; below 768px it is hidden and `mobileBrand` shows a compact brand strip above the form. The full pages live in the [sign in, sign up and forgot password templates](/templates/). ### Props | Prop | Type | Default | Description | |---|---|---|---| | `logoMark` | `string` | 'CA' | Text inside the logo square. | | `logoName` | `string` | 'CodeAtlas UI' | Product name next to the mark. | | `logoHref` | `string` | '/' | Where the logo links to. | | `mobileBrand` | `boolean` | true | Shows the compact brand strip below 768px, where the side panel is hidden. | | `brand` | `Snippet` | | Replaces the default logo. | | `panel` | `Snippet` | | Content of the side column. | | `children` | `Snippet` | | The form column, usually an AuthCard. | ### Props | Prop | Type | Default | Description | |---|---|---|---| | `title` | `string` | | Heading of the card. | | `subtitle` | `string` | | Text under the heading. | | `footer` | `Snippet` | | Links under the form, such as sign up. | | `children` | `Snippet` | | The form. | --- # Motion Group: Motion. URL: https://ui.codeatlas.com.br/docs/motion/ A vocabulary of named motion: live samples, exact timing, and a way to ask an AI for a motion by name or number. Motion is hard to describe and easy to get wrong. "Make it fade in nicely" gives a different animation every time. This section gives every effect a **name**, a **number** and **exact values**, so a person can point at it and an AI can reproduce it without guessing. ## The vocabulary idea Each motion demo is a real, running element with four things attached: - **A name**: `Split text reveal`, `Fade rise`. The first name is canonical; the sidecar lists aliases, including other languages (`Subir com fade`). - **A number**: the same `#NNN` as every other demo in the [gallery](/gallery/). - **A spec**: the property, duration, easing, delay, stagger or spring. Each value appears verbatim in the demo source, and the build fails if one does not. - **A way to ask**: one line for your AI. ## Ask an AI by name or number Name it, or give the number. Both resolve to the same reference. ```text Use @codeatlasdev/ui motion "Fade rise" on the invoice cards. Use @codeatlasdev/ui demo #256 on the invoice cards. ``` With the MCP server, `search_demos({ query: "fade rise" })` matches names and aliases first, and `get_demo({ ids: ["#256"] })` returns the source, the `Motion:` lines and the instruction to reproduce the values exactly. Without MCP, fetch `/api/v1/en/p/256.txt`: it is the prompt, ready to paste. ## The contract every motion demo follows It is implemented once, by [`MotionStage`](/docs/motion/motion-stage/), exported from `@codeatlasdev/ui`. - **Plays in view.** A demo starts when at least half of it is visible and stops when it leaves the viewport or the tab is hidden. A gallery full of motion never animates everything at once. - **You can take over.** Touching, clicking or focusing the demo stops the autoplay and makes it interactive. A **Replay** button appears. - **You can pause.** A visible **Pause** button (`aria-pressed` when stopped) is always there while motion can run, so no motion lasts more than five seconds without a control (WCAG 2.2.2). - **Reduced motion renders the final state.** Under `prefers-reduced-motion: reduce` nothing animates, the Pause and Replay buttons are not shown (there is nothing to stop), and the demo shows where the motion ends. Your code must do the same. ## Pages --- # Basics Group: Motion. URL: https://ui.codeatlas.com.br/docs/motion/basics/ The smallest motion demos: one effect, one name, exact timing, wrapped in MotionStage. Every motion demo is wrapped in [`MotionStage`](/docs/motion/motion-stage/): it plays only while it is in view, stops when you touch it, and shows its final state under `prefers-reduced-motion`. Read [Motion](/docs/motion/) for the contract. ### Fade rise --- # MotionStage Group: Motion. URL: https://ui.codeatlas.com.br/docs/motion/motion-stage/ The frame of a motion demo: plays only in view, hands control to the user on touch, always pauses, and renders the final state under reduced motion. ```svelte {#snippet children(m)} {#key m.cycle}
    Invoice paid
    {/key} {/snippet}
    ``` `MotionStage` owns the timing contract so a demo only has to draw. It starts when at least half of it is in view and stops when it leaves the viewport or the tab is hidden, so a page full of motion never animates everything at once. Pointer, key or focus inside the demo takes over: autoplay stops, the demo stays interactive and a **Replay** button appears. A **Pause** button (`aria-pressed` while stopped) is always there while motion can run (WCAG 2.2.2). Under `prefers-reduced-motion: reduce`, or in a theme with `motion: { enter: "none" }` (`--ca-enter-scale: 0`), nothing autoplays, the buttons are not rendered and `m.reduced` is `true`: render the final state. The snippet receives `m` with `playing`, `reduced`, `cycle`, `takeover()`, `play()` and `pause()`. `cycle` increments on every loop and on Replay: key the animated element on it (`{#key m.cycle}`) so the animation runs again. Keep the animation in CSS and never start your own timers. ### Fade rise ### Props | Prop | Type | Default | Description | |---|---|---|---| | `label` | `string` | | Accessible name of the stage (the group label). Say what the demo shows. | | `loop` | `number` | 3200 | Autoplay loop length in milliseconds. `cycle` increments once per loop. | | `pauseLabel` | `string` | | Label of the pause button. Defaults to the i18n catalog (`motionStage.pause`). | | `replayLabel` | `string` | | Label of the replay button. Defaults to the i18n catalog (`motionStage.replay`). | | `children` | `Snippet<[MotionCtx]>` | | The demo. Receives `{ playing, reduced, cycle, takeover, play, pause }`. | --- # Hero and ambient Group: Motion. URL: https://ui.codeatlas.com.br/docs/motion/hero-ambient/ Cursor spotlight, grid ripple, gradient field, compare slider, cursor trail, hero intro and card tilt: named, numbered, with exact values. The effects people ask for on a first screen: a light that follows the cursor, a surface that reacts to a click, a background that never sits still. Every one runs inside [`MotionStage`](/docs/motion/motion-stage/), so it plays only in view, stops when you touch it and shows its final state under `prefers-reduced-motion`. Ambient effects are decoration: keep them behind the content and never carry meaning in them. ### Cursor spotlight ### Grid ripple ### Animated gradient field ### Before and after slider ### Cursor trail ### Hero intro sequence ### Card tilt with glare --- # Micro-interactions Group: Motion. URL: https://ui.codeatlas.com.br/docs/motion/micro-interactions/ Named micro-interactions with exact values: magnetic button, press spring, morphing toggle, like burst, hold to confirm, copy to check, add to cart fly. A micro-interaction is the small reply an element gives to a touch: it leans, presses, flips, confirms. Each demo below plays itself while in view with a faint fake pointer (hidden from assistive tech), and the moment you touch it, it is yours: the real pointer or keyboard drives the same effect. Under reduced motion every demo shows its final state. Read [Motion](/docs/motion/) for the contract. ## Hover and pointer ### Magnetic button ### Tilt card ## Press and toggle ### Press spring ### Morphing toggle ### Spring switch ## Feedback ### Like burst ### Hold to confirm ### Copy to check ### Add to cart fly --- # Text motion Group: Motion. URL: https://ui.codeatlas.com.br/docs/motion/text/ Named text effects with exact timing: split reveal, scramble, odometer, count up, marquee, typewriter, shine sweep and blur in. Text is the cheapest place to add motion and the easiest to break: split words that a screen reader spells out, counters that announce every frame, tickers nobody can stop. Every demo here keeps the real sentence or number as the accessible name, hides the animated copy with `aria-hidden`, shows the final text under `prefers-reduced-motion`, and can be taken over by touching it. Ask an AI for one by name or by number: `Use #300` or `Use a split text reveal on the hero headline`. ## Reveal ### Split text reveal ### Blur in ## Transform and decode ### Text scramble ### Typewriter ## Numbers ### Odometer roll ### Count up ## Loops and highlights ### Infinite marquee ### Shine sweep --- # Transitions Group: Motion. URL: https://ui.codeatlas.com.br/docs/motion/transitions/ Shared element, FLIP reorder, spring height, sliding tab indicator, stacked toasts, drag-to-dismiss sheet and filter exits: named, numbered, with exact values. A transition is the motion between two states of the same screen. Each demo below wraps real package components (`Card`, `Tabs`, `Chip`, `Button`) and animates around them; none of them is forked. Every demo is wrapped in [`MotionStage`](/docs/motion/motion-stage/): it plays only in view, stops when you touch it, and shows the final state under `prefers-reduced-motion`. ## How to ask for one Say the name, or the number. `Use #360 for the plan cards` and `Use a shared element transition from the plan list to its detail` resolve to the same reference, with the exact values. ### Shared element ### FLIP reorder ### Height spring ### Tab indicator ### Stacked toasts ### Sheet spring ### Filter list --- # Scroll Group: Motion. URL: https://ui.codeatlas.com.br/docs/motion/scroll/ Scroll-linked motion with names and exact values: staggered reveal, view-timeline reveal, parallax, reading progress, horizontal scroll, pinned steps and scroll-driven counters. Scroll motion ties an effect to where the reader is on the page. Every demo below scrolls **inside its own container**, so it works in a card, a gallery tile or a modal, and the stage scrolls it for you while it is in view. Scroll it yourself (wheel, trackpad or touch) to take over; the Pause button stops the autoplay. Where the browser supports **CSS scroll-driven animations** (`animation-timeline: scroll()` and `view()`), the effect runs on the compositor with no JavaScript. Where it does not, a passive `scroll` listener writes the same progress to a custom property and the same transforms read it. Under `prefers-reduced-motion` the container shows its final state and does not scroll by itself. ### Staggered reveal on scroll ### View timeline reveal ### Parallax ### Reading progress bar ### Horizontal scroll section ### Pinned step story ### Scroll-linked counter and chart draw --- # Charts overview Group: Charts. URL: https://ui.codeatlas.com.br/docs/charts/ Apache ECharts wrapped by the design system. Pass data only; themes, animation and glass tooltips are automatic. ```svelte ``` Charts live in the `@codeatlasdev/ui/charts` subpath and need the optional peer dependency `echarts` (`bun add echarts`, version 6). Every chart takes data and a few semantic props. There is no style or font prop on purpose: the palette follows the active preset, axes, legend and tooltips read the `--ca-*` tokens, and charts re-theme when the color scheme, preset or surface style changes. Series can take an explicit `color`; use hex or rgb, because ECharts paints on a canvas and cannot parse `oklch()`. Charts render on the client, so hydrate them with `client:visible` in Astro. |Component|Use for| |---|---| |[LineChart](/docs/charts/line/)|Trends over time, areas, live streams| |[BarChart](/docs/charts/bar/)|Vertical, horizontal, stacked and grouped bars| |[PieChart](/docs/charts/pie/)|Pie, donut and rose proportions| |[ScatterChart](/docs/charts/scatter/)|Correlations and bubble sizing| |[RadarChart](/docs/charts/radar/)|Multi-dimension profiles| |[GaugeChart](/docs/charts/gauge/)|KPI gauges and progress rings| |[HeatmapChart](/docs/charts/heatmap/)|Calendar and matrix density| |[Sparkline](/docs/charts/sparklines/)|Inline micro charts| All chart components also accept `height` (CSS length, each chart has its own default) and `class`. ### Dashboard Every chart type together inside glass cards. ### Props | Prop | Type | Default | Description | |---|---|---|---| | `height` | `string` | | CSS height of the chart. Defaults differ per chart (320px for line, bar and scatter; 300px for pie; 340px for radar; 280px for gauge; 240px for heatmap; 48px for sparkline). | | `class` | `string` | '' | Extra class names on the chart container. | --- # LineChart Group: Charts. URL: https://ui.codeatlas.com.br/docs/charts/line/ Temporal trends, multi-series comparisons, gradient area fills and live data. ```svelte `$${v / 1000}k`} /> ``` ### Simple line ### Multi-line comparison Each series gets its own palette color. ### Area with gradient fill area fills under each line with a gradient that fades to transparent. ### Real-time stream Replace the data array and the chart updates in place. ### Props | Prop | Type | Default | Description | |---|---|---|---| | `series` | `ChartSeries[]` | | Named series sharing one category axis. Each is { name, data: number[], color? }. | | `categories` | `string[]` | | X-axis labels, one per data point. | | `valueFormatter` | `(value: number) => string` | | Formats y-axis ticks and tooltip values. | | `area` | `boolean` | false | Fills the area under each line with a gradient. | | `height` | `string` | '320px' | CSS height of the chart. | | `class` | `string` | '' | Extra class names. | --- # BarChart Group: Charts. URL: https://ui.codeatlas.com.br/docs/charts/bar/ Vertical, horizontal, stacked and grouped bars. ```svelte ``` Multiple series render grouped by default. Set `stacked` to stack them into one column per category. ### Vertical bars ### Horizontal bars Good for long category names and rankings. ### Stacked bars ### Grouped and stacked Toggling stacked animates between both layouts. ### Props | Prop | Type | Default | Description | |---|---|---|---| | `series` | `ChartSeries[]` | | Named series sharing one category axis. Each is { name, data: number[], color? }. | | `categories` | `string[]` | | Category labels, one per bar group. | | `valueFormatter` | `(value: number) => string` | | Formats value-axis ticks and tooltip values. | | `horizontal` | `boolean` | false | Renders bars horizontally. | | `stacked` | `boolean` | false | Stacks all series into one column per category. | | `height` | `string` | '320px' | CSS height of the chart. | | `class` | `string` | '' | Extra class names. | --- # PieChart Group: Charts. URL: https://ui.codeatlas.com.br/docs/charts/pie/ Proportional data as pie, donut or Nightingale rose, with an optional center label. ```svelte ``` ### Basic donut ### Donut with center KPI centerLabel overlays a value and caption in the hole of a donut. ### Rose chart Nightingale rose: the radius of each slice scales with its value. ### Animated proportions Swap the data and the slices animate to the new proportions. ### Props | Prop | Type | Default | Description | |---|---|---|---| | `data` | `ChartSlice[]` | | Slices as { name, value }. | | `donut` | `boolean` | false | Renders a ring instead of a solid pie. | | `rose` | `boolean` | false | Nightingale rose, where the slice radius scales with the value. | | `centerLabel` | `{ value: string; label: string }` | | Text centered in a donut, such as a KPI total. Use with donut. | | `height` | `string` | '300px' | CSS height of the chart. | | `class` | `string` | '' | Extra class names. | --- # ScatterChart Group: Charts. URL: https://ui.codeatlas.com.br/docs/charts/scatter/ Correlation plots, bubble charts and clustered scatter by group. ```svelte ``` Points are `[x, y]` or `[x, y, size]`. When any point has a third value the chart switches to bubble mode automatically and scales the symbols by size. ### Basic scatter Visits against conversion rate. ### Bubble chart The third value of each point sets the bubble size. ### Clustered by tier One series per group, each with its own palette color. ### Props | Prop | Type | Default | Description | |---|---|---|---| | `series` | `ScatterSeries[]` | | Named series as { name, data: [x, y] \| [x, y, size][], color? }. | | `xName` | `string` | | Label of the x axis. | | `yName` | `string` | | Label of the y axis. | | `xFormatter` | `(v: number) => string` | | Formats x ticks and tooltip. Overrides valueFormatter for x. | | `yFormatter` | `(v: number) => string` | | Formats y ticks and tooltip. Overrides valueFormatter for y. | | `valueFormatter` | `(v: number) => string` | | Fallback formatter used for both axes. | | `height` | `string` | '320px' | CSS height of the chart. | | `class` | `string` | '' | Extra class names. | --- # RadarChart Group: Charts. URL: https://ui.codeatlas.com.br/docs/charts/radar/ Multi-dimensional profiles from single skill sets to interactive comparisons. ```svelte ``` `values` must follow the order of `indicators`. A legend appears automatically when there is more than one series. ### Simple radar ### Comparison Two series with a legend and focus on hover. ### Interactive team builder Drag the sliders and the radar updates live. ### Props | Prop | Type | Default | Description | |---|---|---|---| | `indicators` | `RadarIndicator[]` | | Axes as { name, max }. | | `series` | `RadarSeries[]` | | Named series as { name, values: number[], color? }, aligned to indicators. | | `height` | `string` | '340px' | CSS height of the chart. | | `class` | `string` | '' | Extra class names. | --- # GaugeChart Group: Charts. URL: https://ui.codeatlas.com.br/docs/charts/gauge/ KPI gauges, progress rings and animated counters. ```svelte ``` The default gauge colors its arc in three zones (error, warning, success). Override them with `zones`, where `until` is a fraction of `min` to `max`. ### Simple gauge ### Progress ring variant ring draws a single brand-colored arc with the value in the center. ### Custom zones Reverse the default zones when a high value is bad, such as CPU load. ### Animated counter Updating value animates the ring and counts the number. ### Props | Prop | Type | Default | Description | |---|---|---|---| | `value` | `number` | | Current value. | | `min` | `number` | 0 | Lower bound of the scale. | | `max` | `number` | 100 | Upper bound of the scale. | | `variant` | `'default' \| 'ring'` | 'default' | 'default' is a speedometer arc with a pointer. 'ring' is a full progress ring. | | `zones` | `GaugeZone[]` | error to 0.3, warning to 0.7, success to 1 | Colored arc zones as { until, color }. until is a fraction of min to max; color is success, warning, error, info, brand or any CSS color. | | `label` | `string` | | Caption shown above the value in the default variant. | | `formatter` | `(value: number) => string` | v => `${v}%` | Formats the displayed value. | | `animated` | `boolean` | true | Counts the displayed number up or down when value changes. The arc always animates. | | `height` | `string` | '280px' | CSS height of the chart. | | `class` | `string` | '' | Extra class names. | --- # HeatmapChart Group: Charts. URL: https://ui.codeatlas.com.br/docs/charts/heatmap/ Density and pattern visualization as calendar grids and hour-by-day matrices. ```svelte ``` `variant` is required. The calendar variant reads `data` and `range`; the matrix variant reads `matrixData`, `xCategories` and `yCategories`. ### Calendar heatmap Daily commit activity from January to September. ### Hour by day matrix Points are [xIndex, yIndex, value] into xCategories and yCategories. ### Year toggle Swap data and range to move between years. ### Props | Prop | Type | Default | Description | |---|---|---|---| | `variant` | `'calendar' \| 'matrix'` | | Which layout to render. Required. | | `data` | `CalendarHeatmapPoint[]` | [] | Calendar variant: [date as YYYY-MM-DD, value] per day. | | `range` | `[string, string]` | | Calendar variant: first and last date shown. | | `matrixData` | `MatrixHeatmapPoint[]` | [] | Matrix variant: [xIndex, yIndex, value]. | | `xCategories` | `string[]` | [] | Matrix variant: x-axis labels. | | `yCategories` | `string[]` | [] | Matrix variant: y-axis labels. | | `color` | `string` | | Hex or rgb color of the high end of the ramp. Defaults to the first palette color. Use hex or rgb, not oklch. | | `max` | `number` | | Value that maps to the full color. Defaults to the data maximum. | | `tooltipFormatter` | `(value: number) => string` | | Formats the value in the tooltip. | | `height` | `string` | '240px' | CSS height of the chart. | | `class` | `string` | '' | Extra class names. | --- # Sparkline Group: Charts. URL: https://ui.codeatlas.com.br/docs/charts/sparklines/ Inline micro charts for KPI cards, table rows and live streams. ```svelte ``` A sparkline has no axes, legend or tooltip. `trend` picks the color: success for `up`, error for `down` and a neutral tone otherwise. ### KPI cards with trend lines ### Table row sparklines ### Bar sparklines variant bar draws narrow columns instead of a line. ### Live streaming micro charts ### Props | Prop | Type | Default | Description | |---|---|---|---| | `data` | `number[]` | | Values to plot. | | `variant` | `'line' \| 'bar'` | 'line' | Line with a soft area, or narrow bars. | | `trend` | `'up' \| 'down' \| 'neutral'` | | Colors the series: success, error or neutral. | | `color` | `string` | | Explicit hex or rgb color. Overrides trend. Use hex or rgb, not oklch. | | `height` | `string` | '48px' | CSS height of the chart. | | `class` | `string` | '' | Extra class names. | --- # Docs primitives Group: Docs kit. URL: https://ui.codeatlas.com.br/docs/docs-kit/docs/ Components for developer documentation sites, from the page shell and navigation to code tabs, callouts and prose typography. This site is built with these primitives. The kit covers `DocsShell` (header, sidebar, page index and a mobile Sheet menu), `DocsNav`, `TableOfContents`, `DocsSearch`, `CodeTabs`, `Callout`, `Steps`, `LinkCard`, `CardGrid`, `MethodBadge` and the `ca-prose` typography. ```svelte fetch("/search.json").then((r) => r.json())} onnavigate={(href) => (location.href = href)} > {#snippet brand()} Acme Docs{/snippet}
    ...
    ``` `DocsShell` is a full-page frame, so it is not embedded here. The page you are reading is the reference integration: see `SiteDocsShell.svelte` in the `site/` workspace. On desktop (1024px and up) it is a sticky glass header over a three-column grid: sidebar, content and a "On this page" rail from 1280px. Below 1024px it becomes one compact header, the menu opens a bottom Sheet and the page index turns into an inline disclosure. `Cmd+K`, `Ctrl+K` and `/` open the search. The sidebar keeps its place between pages, also on a static site where every link loads a new document. Its scroll position is saved per section in `sessionStorage` and restored by a small inline script right after the sidebar, before the first paint. The current page is scrolled into the sidebar's view when it is outside it; the window scroll and focus are never touched. Under a Content Security Policy that blocks inline scripts, the position is restored on hydration instead. ### ca-prose, Callout and Steps Wrap rendered markdown in .ca-prose. Callout is an Alert vocabulary (note, tip, caution, danger); keep two per page. Steps turns an ordered list into a numbered rail. ### CodeTabs Language tabs with a copy button. Groups that share a sync key follow each other and persist the choice in localStorage. Highlighting stays with you: wrap each block in .ca-code. ### LinkCard, CardGrid and MethodBadge ### DocsNav and TableOfContents The sidebar tree and the page index. The inline variant is shown; DocsShell uses the rail variant on desktop. ### DocsSearch Full-text search. A dialog on desktop and a full-height sheet with a 16px input on phones. Pass entries or an async loader. ### Props | Prop | Type | Default | Description | |---|---|---|---| | `sections` | `DocsSection[]` | | { id, label, href } entries shown as header tabs and in the mobile Sheet. | | `active` | `string` | | id of the current section. | | `groups` | `DocsNavGroup[]` | [] | Sidebar tree of the current section. Empty hides the sidebar and the page index. | | `pathname` | `string` | | Current path, used to mark aria-current in the tree. | | `headings` | `DocsHeading[]` | [] | Page index: a rail from 1280px and an inline disclosure below. | | `searchIndex` | `DocsSearchEntry[] \| () => Promise` | | Required. Entries, or a lazy loader called on first open. | | `onnavigate` | `(href: string) => void` | | Required. Called when a search result is picked (pass goto). | | `brandHref` | `string` | '/' | Link target of the brand. | | `brandLabel` | `string` | | Accessible label of the brand link. | | `brand` | `Snippet` | | Required. Brand mark and name inside the header link. | | `actions` | `Snippet` | | Extra header actions on the right. Keep to one or two icon buttons. | | `menuFooter` | `Snippet` | | Content below the sidebar tree in the mobile Sheet, such as external links. | | `pager` | `{ prev?: DocsPagerLink; next?: DocsPagerLink }` | | Previous and next pages, as { href, title }. Cards at the end of the article on wide screens, a fixed bottom action bar on narrow ones. | | `children` | `Snippet` | | Required. Page content. | ### Props | Prop | Type | Default | Description | |---|---|---|---| | `groups` | `DocsNavGroup[]` | | Groups of { label?, items } where an item is { href, title, badge? }. A badge is an HTTP method shown as a MethodBadge. | | `pathname` | `string` | | Current path, marks the current page with aria-current. | | `label` | `string` | | Accessible label of the nav landmark. | | `onNavigate` | `() => void` | | Called after a link is followed, to close the mobile Sheet. | ### Props | Prop | Type | Default | Description | |---|---|---|---| | `headings` | `DocsHeading[]` | | { id, text, depth: 2 \| 3 } entries. Renders nothing for fewer than 2 headings. | | `variant` | `'rail' \| 'inline'` | 'rail' | A sticky list, or a collapsible disclosure that shows the current section. | | `label` | `string` | | Accessible label of the nav landmark. | | `class` | `string` | '' | Extra CSS classes. | ### Props | Prop | Type | Default | Description | |---|---|---|---| | `open` | `boolean` | false | Bindable open state. | | `index` | `DocsSearchEntry[] \| () => Promise` | | Required. Entries, or a loader called once on first open. | | `onselect` | `(href: string) => void` | | Required. Called with the picked result's href. | | `placeholder` | `string` | | Input placeholder. Defaults to the localized string. | | `hint` | `string` | | Footer hint text. Defaults to the localized string. | ### Props | Prop | Type | Default | Description | |---|---|---|---| | `CodeTabs.sync` | `string` | 'lang' | Groups with the same key follow the chosen label. An empty string disables sync. | | `CodeTabs.tabs` | `{ label: string; code?: string; html?: string }[]` | | Tabs as data instead of CodeTab children, for Astro and MDX pages where each island is its own Svelte tree. `html` is pre-highlighted markup. | | `CodeTab.label` | `string` | | Tab text and sync key, for example 'cURL' or 'Node.js'. | | `Callout.type` | `'note' \| 'tip' \| 'caution' \| 'danger'` | 'note' | Maps to Alert severity (info, success, warning, error). | | `Callout.title` | `string` | | Overrides the default localized title. | | `Steps` | `Snippet` | | Wraps an ordered list; each top-level item becomes a numbered step. | | `LinkCard.title / description / href` | `string` | | Whole-card link. title and href are required; the minimum height is 44px. | | `CardGrid` | `Snippet` | | Responsive auto-fill grid for LinkCard: 16rem minimum, one column on phones. | | `MethodBadge.method` | `string` | | HTTP method label: GET, POST, PUT, PATCH or DELETE. | | `MethodBadge.variant` | `'pill' \| 'plain'` | 'pill' | plain drops the tinted pill, for tight lists like a sidebar. | --- # Release lists Group: Docs kit. URL: https://ui.codeatlas.com.br/docs/docs-kit/release-lists/ The markup contract for changelog and release-notes pages inside .ca-prose, and how the heading anchors and the version index are linked. `.ca-prose` styles a changelog page with no custom CSS, as long as the markup follows the contract below. The package ships only the CSS: it never generates the ids, the anchors or the version index. Your app (its markdown pipeline or its template) renders them. The [changelog page](/changelog/) of this site is the reference: `site/functions/_lib/page.ts` builds exactly this markup. ## Markup ```html

    3.0.0 latest

    npm GitHub release

    Minor Changes

    • The rendered notes.

    Special thanks

    To our supporters.

    ``` | Class | Element | Role | |---|---|---| | `ca-release` | `
    ` | One per version, newest first. Draws the rule between versions and sets `scroll-margin-top` so a jump lands clear of the sticky header (`--ca-docs-header-h`). | | `ca-release-jump` | `