# 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` | | Brand icons (`caBrandPlugin`) | `@codeatlasdev/ui/vite` | `@resvg/resvg-js` and `png-to-ico` | | `migrate` command | `bunx @codeatlasdev/ui migrate` | `typescript` | `@sveltejs/kit` and `astro` are optional peers too: install the one your app uses. Icons come from `@lucide/svelte`, which the package already depends on. ### 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. | | `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). | | Flag | Effect | |---|---| | `--write` | Apply the proposed changes to existing files. Files that do not exist yet are always created. | | `--dir ` | Project directory. Defaults to the current directory. | When diffs are pending it ends with `N existing file(s) left untouched. Re-run with --write to 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). A file that already exists is never overwritten: `init` prints a diff and applies it 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 })`, 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 and `colorScheme.init()` in `onMount` | adds only the missing import or call | | `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 | `caErrorPlugin({ scaffold: true })` then creates `src/routes/+error.svelte` and `src/error.html` the first time Vite runs (dev or build), and never overwrites them. 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 })], }); ``` 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 })], }); ``` 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)` first in `integrations`, with its two imports | adds `caUI(theme)` unless `caUI(` is already there; reports `manual` when there is no `integrations: [` array | | 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. Astro needs `astro` 5, 6 or 7 as a peer. Components that read `colorScheme` render correctly on the server because the init script sets `class="dark"` and `data-preset` before the stylesheet applies. ## Plain Vite `caThemePlugin` also works without SvelteKit: it injects the theme head through `transformIndexHtml`. ```ts // vite.config.ts import { svelte } from "@sveltejs/vite-plugin-svelte"; import { caThemePlugin } from "@codeatlasdev/ui/vite"; import { defineConfig } from "vite"; export default defineConfig({ plugins: [caThemePlugin(), svelte()] }); ``` ```ts // src/main.ts import "@codeatlasdev/ui/styles/global.css"; import { colorScheme } from "@codeatlasdev/ui"; import { mount } from "svelte"; import App from "./App.svelte"; colorScheme.init(); mount(App, { target: document.getElementById("app")! }); ``` ## Verify ```svelte ``` Reload with the toggle on dark and check that there is no white flash. Continue with [Theming](/docs/getting-started/theming/). --- # Theming Group: Getting started. URL: https://ui.codeatlas.com.br/docs/getting-started/theming/ Define a theme with defineTheme, derive a brand preset from one hex color, switch schemes and surface styles, and override tokens through the cascade. A theme is a plain object. The library compiles it to CSS once at build time (the same object always gives the same CSS) and injects it before any stylesheet, so there is no per-render style work and no flash. ```ts // src/lib/theme.ts import type { ThemeOptions } from "@codeatlasdev/ui/vite"; export const theme: ThemeOptions = { presets: [{ id: "acme", color: "#2563eb" }], defaultPreset: "acme", defaultScheme: "system", defaultStyle: "solid", fonts: { body: "Geist, system-ui, sans-serif" }, radius: 0.8, density: "comfortable", overrides: { light: { "--ca-card-padding": "16px" } }, }; ``` Pass the same object to `caThemeHandle` and `caThemePlugin` (SvelteKit), `caUI` (Astro) or `themeHead` (custom setups), see [Installation](/docs/getting-started/installation/). `ThemeOptions` is `Partial`, so every field is optional. ## Options | Option | Default | Description | |---|---|---| | `presets` | `[]` | Brand presets generated from a hex color. Built-in presets are always available. | | `defaultPreset` | `"amber"` | Preset used until the user picks one. Must be a built-in or one of `presets`. | | `defaultScheme` | `"dark"` | `"light"`, `"dark"` or `"system"`. `bunx @codeatlasdev/ui init` scaffolds `"system"`. | | `defaultStyle` | `"solid"` | Surface material: `"solid"`, `"flat"`, `"frosted"`, `"glass"`, `"outlined"`, `"neumorphic"`, `"clay"`, `"tonal"` or `"chrome"`. Solid is the sober default; glass is the opt-in [`liquid`](#the-liquid-recipe) look. | | `fonts` | `{}` | `body`, `mono` and `display`. Each is a font stack string or a self-hosted font descriptor (`@font-face`, preload, metric-matched fallback), see [Performance](/docs/getting-started/performance/#fonts). They set `--ca-font-sans`, `--ca-font-mono` and `--ca-font-display`. | | `radius` | `0.8` | Multiplier applied to the `--ca-radius-xs` to `--ca-radius-xl` scale (`0.8` makes `--ca-radius-md` 8px). | | `density` | `"comfortable"` | `"compact"` (0.75), `"comfortable"` (1), `"airy"` (1.25) or a number. Scales the `--ca-space-*` tokens, so padding, gaps and page spacing follow. | | `paper` | `"plain"` | Page background: `"plain"`, `"mesh"` (brand glow, drawn by the `glass` and `frosted` styles), `"grid"`, `"dots"` or `"lines"`. Flat patterns are drawn in `--ca-outline-subtle`, so they follow the preset and scheme. | | `iconStroke` | `1.75` | Lucide stroke width. | | `controlRadius` | `"md"` | Corner role of buttons, fields and chips: `"xs"`, `"sm"`, `"md"`, `"lg"`, `"xl"` or `"pill"`. | | `border` | `{ width: 1, strong: 2 }` | Border weights in px (`--ca-border-width`, `--ca-border-width-strong`). | | `elevation` | `"soft"` | How shadows are drawn: `"soft"`, `"none"`, `"hard"` (offset, no blur) or `"glow"`. | | `typography` | `{}` | `scale`, `weights` (`heading` defaults to `600`), `headingTracking`, `headingCase`, `labelTracking`, `labelCase`, `lineHeight` and `measure`. | | `motion` | `{}` | `scale` (multiplier on every duration) and `easing`: `"standard"`, `"emphasized"`, `"spring"`, `"linear"` or `"steps"`. | | `noise` | `0` | Film-grain over the page background, from `0` to `0.12`. | | `overrides` | `{}` | Raw `--ca-*` token overrides, per scheme (`light`, `dark`). Prefer preset role colors (below) for palettes. | `defineTheme(options)` resolves the options (defaults, validation) and `themeCSS(resolved)` returns the CSS string. You rarely call them yourself; the integrations do. Invalid input fails the build: a preset `id` that clashes with a built-in, a color that is not `#rrggbb`, or a `defaultPreset` that does not exist. ## Presets Eight OKLCH presets ship with the package: `amber`, `ocean`, `crimson`, `cyan`, `violet`, `emerald`, `rose` and `slate`. One attribute switches the whole app: ```html ``` In code, use the store: ```ts import { colorScheme } from "@codeatlasdev/ui"; colorScheme.setPreset("violet"); colorScheme.presets; // every valid id: built-ins plus your brand presets ``` ### Brand preset from a hex color Give the library your logo color and it derives hue, chroma and lightness in OKLCH, then emits the complete light and dark token set (brand, surfaces, text, outlines, glass tints). Charts use the hex as their first series color. ```ts export const theme: ThemeOptions = { presets: [{ id: "acme", color: "#2563eb", label: "Acme" }], defaultPreset: "acme", }; ``` Values stored by the user that your theme does not know are discarded and removed from storage. If you rename or delete a brand preset, returning users fall back to `defaultPreset` instead of seeing a broken theme. ### Porting a palette by role To bring an existing palette (a terminal or editor theme) into a preset, set role colors per scheme. Unset roles keep the generated value; a per-scheme `brand` regenerates that scheme's brand ramp, and `background` also drives ``. Roles are scoped to the preset, so switching presets and theme islands keep working. ```ts presets: [{ id: "tokyo", color: "#7aa2f7", colors: { light: { brand: "#34548a" }, dark: { background: "#1a1b26", surface: "#232430", text: "#c0caf5", border: "#565f89", error: "#f7768e" }, }, }], ``` Roles: `brand`, `background`, `sunken`, `surface`, `overlay`, `text`, `textSecondary`, `textHint`, `textDisabled`, `borderSubtle`, `border`, `borderStrong`, `success`, `warning`, `info`, `error`. Check contrast after overriding text or surfaces: the generated palette guarantees AA, hand-picked colors are yours to verify. ### Sketchbook style and hand-drawn marks `designStyle("sketchbook", { ... })` is a journal look: grid paper, serif display, square outlined cards with hard shadows. It pairs with the opt-in marks stylesheet: ```ts import "@codeatlasdev/ui/styles/marks.css"; ``` ```html already knows.

A margin note with a sketched arrow.

``` Marks are SVG masks painted with `--ca-mark-color` (brand by default); swap the shapes with `--ca-mark-scribble` and `--ca-mark-arrow`. In forced-colors mode they paint in `CanvasText`. ## Schemes `defaultScheme: "system"` follows the operating system, including live changes while the page is open. The init script also keeps `` in sync with the preset's surface color. ```ts colorScheme.preference; // "light" | "dark" | "system" colorScheme.resolved; // "light" | "dark" (system already resolved) colorScheme.isDark; // boolean colorScheme.setPreference("system"); colorScheme.toggle(); // flips resolved light and dark ``` ## Surface styles `data-style` on `` swaps the material of every surface at once. | Style | Look | |---|---| | `solid` | Default. No blur, opaque surfaces, plain page, hairline edges, no glow. Keeps soft neutral shadows. | | `flat` | No blur, no mesh, no glow, no drop shadows: separation comes from borders. | | `frosted` | Less blur, calmer mesh background. | | `glass` | Liquid Glass: strong blur and saturation, translucent tints, brand mesh, glow and rim light. Opt-in. | | `outlined` | Border-defined opaque surfaces with no elevation. Used by the `swiss`, `brutalism`, `terminal` and `sketchbook` recipes. | | `neumorphic` | One base color extruded by paired light and dark shadows. Low contrast: use it for showcases, not dense data UIs. | | `clay` | Soft, inflated opaque surfaces. | | `tonal` | Surfaces tinted with the brand hue and soft elevation (Material 3 style). | | `chrome` | Glossy gradient with a metallic rim and brand glow. Blur stays on. | ```ts colorScheme.setStyle("glass"); ``` ### The liquid recipe Earlier versions looked like Liquid Glass: glass surfaces over a three-hue mesh, glow on selected states, round corners (10px controls), 2px icons, bold headings and hue-tinted shadows. That look is now the opt-in `liquid` recipe: ```ts import { designStyle } from "@codeatlasdev/ui/astro"; import type { ThemeOptions } from "@codeatlasdev/ui/vite"; export const theme: ThemeOptions = designStyle("liquid", { presets: [{ id: "acme", color: "#2563eb" }], defaultPreset: "acme", defaultScheme: "dark", }); ``` `designStyle("default")` is the sober default. `designStyle` is plain data, so `theme.ts` can import it in Node from `@codeatlasdev/ui/astro` (it is also exported from the root and from `@codeatlasdev/ui/kit`, which pull in Svelte files that `vite.config` cannot load). To migrate an app that relied on the old defaults, use `designStyle("liquid", { ... })`, or set `defaultStyle: "glass"` yourself. ### Other design styles `DESIGN_STYLES` maps each id to `{ id, label, description, taxonomy, theme }`. `designStyle(id, overrides)` returns that recipe's `theme` with your overrides merged on top, and `mergeTheme(a, b)` merges two partial themes (`b` wins). The ids are `default`, `liquid`, `swiss`, `brutalism`, `neumorphism`, `glassmorphism`, `material`, `fluent`, `apple`, `bento`, `claymorphism`, `y2k`, `editorial`, `terminal` and `sketchbook`. Each recipe only sets the design knobs from the options table (material, radius, border, elevation, typography, motion, icon stroke, paper). Users on `prefers-reduced-transparency: reduce` get opaque surfaces regardless of the style. ## The colorScheme store Call `colorScheme.init()` once in the root layout (inside `onMount`). It re-applies the state to ``, follows OS scheme changes and keeps the browser chrome color in sync. It is idempotent and returns a cleanup function. | Member | Description | |---|---| | `preference`, `resolved`, `preset`, `style`, `isDark`, `presets` | Reactive getters (Svelte 5 `$state`). | | `setPreference(pref)`, `toggle()` | Change the scheme. | | `setPreset(id)`, `setStyle(style)` | Change preset or surface style. | | `setTokens(vars)` | Runtime token overrides, see below. | | `init()` | Start listeners. Returns cleanup. | All choices persist in `localStorage` (`ca-scheme`, `ca-preset`, `ca-style`). Storage errors in private mode are ignored, and the values are validated against the theme before use. ### Runtime tokens `setTokens` writes your `--ca-*` variables to a single `