# Sometic full documentation export > Complete consumer documentation for Sometic (`@sometic`), concatenated for LLM / agent ingestion. Companion index: https://sometic.aitistack.com/llms.txt Site: https://sometic.aitistack.com npm scope: @sometic License: MIT Maturity: public beta Important product facts for agents: - Packages are unstyled by default; consumers own CSS/fonts. - Prefer subpath imports for tree-shaking. - Custom elements use the `sometic-*` prefix. - Auth helpers are client UX; authorize on the server. - Server cache lives in @sometic/query; client UI state in @sometic/store. - Use Copy Prompt on surface docs; see https://sometic.aitistack.com/guide/agents. - Documentation search is local (client-side). ## Table of contents - [index.md](https://sometic.aitistack.com/) - [guide/accessibility.md](https://sometic.aitistack.com/guide/accessibility) - [guide/agents.md](https://sometic.aitistack.com/guide/agents) - [guide/app-shell.md](https://sometic.aitistack.com/guide/app-shell) - [guide/browser-support.md](https://sometic.aitistack.com/guide/browser-support) - [guide/cli.md](https://sometic.aitistack.com/guide/cli) - [guide/comparison.md](https://sometic.aitistack.com/guide/comparison) - [guide/contributing.md](https://sometic.aitistack.com/guide/contributing) - [guide/installation.md](https://sometic.aitistack.com/guide/installation) - [guide/introduction.md](https://sometic.aitistack.com/guide/introduction) - [guide/javascript.md](https://sometic.aitistack.com/guide/javascript) - [guide/package-managers.md](https://sometic.aitistack.com/guide/package-managers) - [guide/quick-start.md](https://sometic.aitistack.com/guide/quick-start) - [guide/ssr.md](https://sometic.aitistack.com/guide/ssr) - [guide/styling.md](https://sometic.aitistack.com/guide/styling) - [guide/troubleshooting.md](https://sometic.aitistack.com/guide/troubleshooting) - [guide/typescript.md](https://sometic.aitistack.com/guide/typescript) - [guide/whats-included.md](https://sometic.aitistack.com/guide/whats-included) - [guide/why-sometic.md](https://sometic.aitistack.com/guide/why-sometic) - [components/accordion.md](https://sometic.aitistack.com/components/accordion) - [components/alert.md](https://sometic.aitistack.com/components/alert) - [components/async-button.md](https://sometic.aitistack.com/components/async-button) - [components/badge.md](https://sometic.aitistack.com/components/badge) - [components/breadcrumb.md](https://sometic.aitistack.com/components/breadcrumb) - [components/button-group.md](https://sometic.aitistack.com/components/button-group) - [components/button.md](https://sometic.aitistack.com/components/button) - [components/checkbox.md](https://sometic.aitistack.com/components/checkbox) - [components/combobox.md](https://sometic.aitistack.com/components/combobox) - [components/context-menu.md](https://sometic.aitistack.com/components/context-menu) - [components/currency-input.md](https://sometic.aitistack.com/components/currency-input) - [components/date-input.md](https://sometic.aitistack.com/components/date-input) - [components/dialog.md](https://sometic.aitistack.com/components/dialog) - [components/drawer.md](https://sometic.aitistack.com/components/drawer) - [components/field.md](https://sometic.aitistack.com/components/field) - [components/file-input.md](https://sometic.aitistack.com/components/file-input) - [components/form.md](https://sometic.aitistack.com/components/form) - [components/icon-button.md](https://sometic.aitistack.com/components/icon-button) - [components/index.md](https://sometic.aitistack.com/components/) - [components/input.md](https://sometic.aitistack.com/components/input) - [components/masked-input.md](https://sometic.aitistack.com/components/masked-input) - [components/menu.md](https://sometic.aitistack.com/components/menu) - [components/number-input.md](https://sometic.aitistack.com/components/number-input) - [components/otp-input.md](https://sometic.aitistack.com/components/otp-input) - [components/password-input.md](https://sometic.aitistack.com/components/password-input) - [components/popover.md](https://sometic.aitistack.com/components/popover) - [components/progress.md](https://sometic.aitistack.com/components/progress) - [components/radio.md](https://sometic.aitistack.com/components/radio) - [components/select.md](https://sometic.aitistack.com/components/select) - [components/skeleton.md](https://sometic.aitistack.com/components/skeleton) - [components/spinner.md](https://sometic.aitistack.com/components/spinner) - [components/switch.md](https://sometic.aitistack.com/components/switch) - [components/tabs.md](https://sometic.aitistack.com/components/tabs) - [components/toast.md](https://sometic.aitistack.com/components/toast) - [components/toggle-button.md](https://sometic.aitistack.com/components/toggle-button) - [components/tooltip.md](https://sometic.aitistack.com/components/tooltip) - [frameworks/alpine.md](https://sometic.aitistack.com/frameworks/alpine) - [frameworks/angular.md](https://sometic.aitistack.com/frameworks/angular) - [frameworks/compatibility.md](https://sometic.aitistack.com/frameworks/compatibility) - [frameworks/htmx.md](https://sometic.aitistack.com/frameworks/htmx) - [frameworks/index.md](https://sometic.aitistack.com/frameworks/) - [frameworks/jquery.md](https://sometic.aitistack.com/frameworks/jquery) - [frameworks/preact.md](https://sometic.aitistack.com/frameworks/preact) - [frameworks/react.md](https://sometic.aitistack.com/frameworks/react) - [frameworks/solid.md](https://sometic.aitistack.com/frameworks/solid) - [frameworks/svelte.md](https://sometic.aitistack.com/frameworks/svelte) - [frameworks/vanilla.md](https://sometic.aitistack.com/frameworks/vanilla) - [frameworks/vue.md](https://sometic.aitistack.com/frameworks/vue) - [concepts/architecture.md](https://sometic.aitistack.com/concepts/architecture) - [concepts/bundlers.md](https://sometic.aitistack.com/concepts/bundlers) - [concepts/controlled-state.md](https://sometic.aitistack.com/concepts/controlled-state) - [concepts/design-tokens.md](https://sometic.aitistack.com/concepts/design-tokens) - [concepts/framework-adapters.md](https://sometic.aitistack.com/concepts/framework-adapters) - [concepts/state-attributes.md](https://sometic.aitistack.com/concepts/state-attributes) - [concepts/styling-slots.md](https://sometic.aitistack.com/concepts/styling-slots) - [concepts/tree-shaking.md](https://sometic.aitistack.com/concepts/tree-shaking) - [concepts/uncontrolled-state.md](https://sometic.aitistack.com/concepts/uncontrolled-state) - [primitives/accessibility.md](https://sometic.aitistack.com/primitives/accessibility) - [primitives/core.md](https://sometic.aitistack.com/primitives/core) - [primitives/date.md](https://sometic.aitistack.com/primitives/date) - [primitives/dom.md](https://sometic.aitistack.com/primitives/dom) - [primitives/events.md](https://sometic.aitistack.com/primitives/events) - [primitives/index.md](https://sometic.aitistack.com/primitives/) - [primitives/positioning.md](https://sometic.aitistack.com/primitives/positioning) - [primitives/styling.md](https://sometic.aitistack.com/primitives/styling) - [primitives/validation.md](https://sometic.aitistack.com/primitives/validation) - [theming/bootstrap.md](https://sometic.aitistack.com/theming/bootstrap) - [theming/css-variables.md](https://sometic.aitistack.com/theming/css-variables) - [theming/index.md](https://sometic.aitistack.com/theming/) - [theming/installation.md](https://sometic.aitistack.com/theming/installation) - [theming/plain-css.md](https://sometic.aitistack.com/theming/plain-css) - [theming/runtime-switching.md](https://sometic.aitistack.com/theming/runtime-switching) - [theming/tailwind.md](https://sometic.aitistack.com/theming/tailwind) - [theming/themes.md](https://sometic.aitistack.com/theming/themes) - [theming/tokens.md](https://sometic.aitistack.com/theming/tokens) - [authentication/authorization.md](https://sometic.aitistack.com/authentication/authorization) - [authentication/configuration.md](https://sometic.aitistack.com/authentication/configuration) - [authentication/firebase.md](https://sometic.aitistack.com/authentication/firebase) - [authentication/index.md](https://sometic.aitistack.com/authentication/) - [authentication/installation.md](https://sometic.aitistack.com/authentication/installation) - [authentication/interceptors.md](https://sometic.aitistack.com/authentication/interceptors) - [authentication/local-provider.md](https://sometic.aitistack.com/authentication/local-provider) - [authentication/oidc.md](https://sometic.aitistack.com/authentication/oidc) - [authentication/session-management.md](https://sometic.aitistack.com/authentication/session-management) - [authentication/supabase.md](https://sometic.aitistack.com/authentication/supabase) - [authentication/token-refresh.md](https://sometic.aitistack.com/authentication/token-refresh) - [authentication/troubleshooting.md](https://sometic.aitistack.com/authentication/troubleshooting) - [forms/async-validation.md](https://sometic.aitistack.com/forms/async-validation) - [forms/field-arrays.md](https://sometic.aitistack.com/forms/field-arrays) - [forms/fields.md](https://sometic.aitistack.com/forms/fields) - [forms/index.md](https://sometic.aitistack.com/forms/) - [forms/persistence.md](https://sometic.aitistack.com/forms/persistence) - [forms/server-errors.md](https://sometic.aitistack.com/forms/server-errors) - [forms/validation.md](https://sometic.aitistack.com/forms/validation) - [stores/index.md](https://sometic.aitistack.com/stores/) - [stores/store-immer.md](https://sometic.aitistack.com/stores/store-immer) - [stores/store.md](https://sometic.aitistack.com/stores/store) - [stores/theme.md](https://sometic.aitistack.com/stores/theme) - [utilities/head.md](https://sometic.aitistack.com/utilities/head) - [utilities/http.md](https://sometic.aitistack.com/utilities/http) - [utilities/index.md](https://sometic.aitistack.com/utilities/) - [utilities/query.md](https://sometic.aitistack.com/utilities/query) - [api/index.md](https://sometic.aitistack.com/api/) - [api/packages.md](https://sometic.aitistack.com/api/packages) - [releases/beta.md](https://sometic.aitistack.com/releases/beta) - [releases/changelog.md](https://sometic.aitistack.com/releases/changelog) - [releases/index.md](https://sometic.aitistack.com/releases/) - [legal/accessibility.md](https://sometic.aitistack.com/legal/accessibility) - [legal/index.md](https://sometic.aitistack.com/legal/) - [legal/license.md](https://sometic.aitistack.com/legal/license) - [legal/privacy.md](https://sometic.aitistack.com/legal/privacy) - [legal/security.md](https://sometic.aitistack.com/legal/security) - [legal/terms.md](https://sometic.aitistack.com/legal/terms) - [services/auth.md](https://sometic.aitistack.com/services/auth) - [services/http.md](https://sometic.aitistack.com/services/http) - [services/index.md](https://sometic.aitistack.com/services/) # index.md Source: https://sometic.aitistack.com/

What makes Sometic different

Not another component kit. A portable application behavior system. Prove it in three moves.

One engine → three surfaces

Write behavior once in a framework-agnostic controller. Bind it through React, Vue, or sometic-* elements without forking state, focus, or keyboard rules.

Auth refresh + HTTP queue

When a request gets 401, Sometic coordinates refresh and retries through a shared queue. Provider SDKs stay optional peers. The orchestration stays in core.

Same engine, your skin

No default look. Style with slots and state attributes. Keep brand CSS intact while the behavior layer stays patchable via npm.

Built for real production constraints

Most UI kits assume your design system or force heavy third-party SDKs into your runtime. Sometic stays out of your way.

Framework-agnostic controllers

State, focus, and interaction logic are completely decoupled from framework lifecycles. Swap your view layer tomorrow without rewriting a single line of core behavior.

Zero CSS contamination

No stylesheets, no default themes, no runtime CSS injection. You own design tokens and DOM structure completely.

Zero-dependency HTTP core

Built on native fetch with an interceptor pipeline and token refresh queue. No mandatory Axios or networking SDK in core.

SSR-safe from day one

Core controllers guard environment globals at import time, so Next.js, Nuxt, and Remix avoid hydration mismatches and window errors.

Tree-shakable subpath exports

Import only what you use. Feature-isolated packages keep production JavaScript lean and fast.

Declarative slot architecture

Layouts rely on native DOM slots and state attributes for precise control over rendered nodes and the accessibility tree.

Where the adapters live

The engine is shared. Adapters are how you mount it. Wave A is production-ready; B and C are experimental binds.

Production adapters

Full wrappers over shared engines for React, Vue, and Vanilla Web Components.

Store bindings Experimental

State-slice integrations for Angular, Svelte, Solid, and Preact.

HTML-first enhancements Experimental

Progressive hooks for Alpine.js, jQuery, and HTMX.

Start where the system is

Enter through architecture and app services first. UI engines are included. They are not the product story.

Architecture Controllers, layers, and adapters Read the model Auth + HTTP Sessions, refresh, fetch orchestration Open services Comparison Why not Radix, shadcn, or a visual kit See the difference
# guide/accessibility.md Source: https://sometic.aitistack.com/guide/accessibility # Accessibility Sometic prefers **native HTML semantics** first: real `; } ``` The React package is a **thin shell**. State, focus, and interaction live in shared engines. The same ones Vue and custom elements use. ### Vue ```bash pnpm add @sometic/vue @sometic/dom ``` ### Vanilla / Web Components ```bash pnpm add @sometic/elements @sometic/dom ``` ```ts import { defineButtonElements } from "@sometic/elements/button"; defineButtonElements(); ``` ```html Save ``` ## 2. Add app services (the differentiator) ### HTTP with auth refresh awareness ```bash pnpm add @sometic/http @sometic/auth ``` ```ts import { createHttp } from "@sometic/http"; const http = createHttp({ baseUrl: "https://api.example.com", }); ``` See [HTTP](/utilities/http) and [Authentication](/authentication/). ### Document head / SEO ```bash pnpm add @sometic/head ``` ```ts import { createHeadController, serializeHead } from "@sometic/head"; const head = createHeadController({ initial: { titleTemplate: "%s | My App" }, }); head.set("home", { title: "Home" }); ``` React: `@sometic/react/head` (`HeadProvider`, `useHead`). Vue: `@sometic/vue/head`. See [Head / SEO](/utilities/head). ### Theme (optional tokens, still your CSS) ```ts import { createThemeController, applyThemeToElement } from "@sometic/theme"; const theme = createThemeController({ defaultMode: "system" }); applyThemeToElement(document.documentElement, theme.get()); ``` ## 3. Read the model Before browsing every component: 1. [Architecture](/concepts/architecture) 2. [Why Sometic](/guide/why-sometic) 3. [Comparison](/guide/comparison) 4. [Bundlers](/concepts/bundlers) ## Related - [Installation](/guide/installation) - [What’s included](/guide/whats-included) - [Styling](/guide/styling) # guide/ssr.md Source: https://sometic.aitistack.com/guide/ssr # SSR Sometic packages are designed so **importing a module does not touch browser globals**. Controllers, stores, and adapters stay SSR-safe until you opt into DOM APIs. ## Rules 1. Never read `window`, `document`, `navigator`, `localStorage`, `sessionStorage`, `matchMedia`, `customElements`, or `HTMLElement` at **module evaluation** time. 2. Create DOM-bound controllers inside `useEffect` / `onMounted` / after `DOMContentLoaded`, or behind an injected environment. 3. Register custom elements only when `customElements` exists. 4. Prefer framework components (`@sometic/react`, `@sometic/vue`) for SSR markup. Upgrade `sometic-*` tags on the client. ## Framework notes ### React - Import `@sometic/react/*` in Server Components files only if those modules stay free of client hooks. Interactive controls belong in Client Components. - Pass `auth` / `http` instances created with storage that works on the server, or construct them in the browser. - Open overlays after mount so focus trap and scroll lock do not run during SSR render. ### Vue / Nuxt - Universal imports are fine for adapters. - Use client-only plugins for Element registration and browser storage. - Prefer `ClientOnly` (or equivalent) around overlays that require layout measurement. ### Vanilla / Elements ```ts import { registerButtonElements } from "@sometic/elements"; if (typeof customElements !== "undefined") { registerButtonElements(); } ``` SSR HTML can include `` tags as unknown elements; they upgrade when registration runs in the browser. ## Storage and theme Persistent stores and theme preference APIs accept injectable storage. During SSR, pass a memory storage or skip persistence until hydrate. Defaults that assume `localStorage` must not run at import time. ## Auth and HTTP - `createAuth` / `createHttp` should not access cookies or `window` unless you inject adapters. - Refresh queues and OAuth redirects are browser flows. Gate them behind client entry points. - Server authorization remains mandatory. Client session state is UX, not a security boundary. ## Common failures | Symptom | Likely cause | | ------------------------------------- | ------------------------------------------------------------------------------------------- | | `window is not defined` during import | Browser global at module top level (forbidden; file a bug if a published package does this) | | Custom element not upgrading | Registration never ran on the client | | Hydration mismatch | Server rendered different attributes than the client first paint | | Overlay breaks SSR HTML | Focus/scroll side effects during render | ## Document head Use `@sometic/head` to collect title/meta on the server (`serializeHead(controller.get())`) and inject into HTML. Call `applyHead` only in the browser after hydration, or rely on React/Vue adapters inside `HeadProvider` / `provideHead`. See [Head / SEO](/utilities/head). ## Related - [React](/frameworks/react) - [Vue](/frameworks/vue) - [Vanilla](/frameworks/vanilla) - [Stores](/stores/) - [Head / SEO](/utilities/head) - [Beta maturity](/releases/beta) - [Troubleshooting](/guide/troubleshooting) # guide/styling.md Source: https://sometic.aitistack.com/guide/styling # Styling Sometic cores and adapters are **unstyled by default**. You bring Tailwind, Bootstrap, plain CSS, or design tokens. The behavior engines stay free of a mandatory CSS framework runtime. ## Brand typography (docs and demo surfaces) Sometic docs and demo surfaces use a locked surface triad: **Chakra Petch** (display), **Urbanist** (UI/body), and **JetBrains Mono** (code). Those fonts are self-hosted for marketing and demo chrome only. Publishable `@sometic/*` packages do **not** ship fonts and do not set a mandatory `font-family`. Components inherit the consumer application's type system. ## Hooks you can rely on | Hook | Where | | ------------------------------------ | ------------------------------------------------------------ | | `class` / `className` | Host props on framework components | | `classes` / `styles` | Slot-oriented class and style maps where supported | | `cssVariables` / theme CSS variables | [`@sometic/theme`](/theming/) | | `data-slot`, `data-*` state attrs | Styling helpers and DOM engines | | `unstyled` | Skip default structural classes when a component offers them | See [Styling slots](/concepts/styling-slots) and [State attributes](/concepts/state-attributes) for the shared contract. ## Theme engine Use `@sometic/theme` for tokens, mode switching, and CSS variable emission. Defaults use the `sometic` prefix for variables and storage keys. Guide: [Theming](/theming/). ## Light DOM vs Shadow DOM Custom elements default to **Light DOM** so page CSS can target documented parts. Opt into `shadow` when you need embed isolation. Theme variables inherit into open shadow roots; element selectors in the document do not pierce shadow trees. See [Vanilla](/frameworks/vanilla). ## Framework tips - React: prefer `className` and `classes` maps; avoid styling through fragile child index selectors. - Vue: same idea with `class` bindings. - Do not hardcode one utility framework inside shared packages. Keep utility classes in app or CLI-generated wrappers. ## What not to do - Do not expect a visual theme to ship inside `@sometic/react` / `@sometic/vue` / `@sometic/elements` by default. - Do not style away focus rings without a visible replacement ([Accessibility](/guide/accessibility)). - Do not depend on undocumented internal DOM depth between Light and Shadow mounts. ## Related - [Theming](/theming/) - [Components](/components/) - [Stores](/stores/) (theme preference store) - [Beta maturity](/releases/beta) - [CLI](/guide/cli) # guide/troubleshooting.md Source: https://sometic.aitistack.com/guide/troubleshooting # Troubleshooting Quick fixes for common consumer issues. For maturity and known beta limits, see [Beta maturity](/releases/beta). ## Install and resolve ### Package not found / wrong scope Install from the `@sometic` scope only (for example `@sometic/react`). Older or alternate scopes are not published. ### Peer dependency warnings Wave A adapters expect: - `@sometic/react` → `react` `^18 || ^19` - `@sometic/vue` → `vue` `^3.5` Install the peer in the application. Adapters do not bundle React or Vue. ### Subpath export errors Import published subpaths only (`@sometic/react/button`, not deep `dist` paths). Check [Compatibility](/frameworks/compatibility) for the map. ## Components and elements ### Custom element not upgrading 1. Import the elements subpath or call `register*Elements()` in the browser. 2. Confirm the tag uses the `sometic-*` prefix. 3. Ensure a single version of `@sometic/elements` is on the page. ### Button looks unstyled Expected. Cores are unstyled. Add classes, theme CSS variables, or CLI-generated wrappers. See [Styling](/guide/styling). ### Dialog focus / outside click Modal dialog traps focus and locks scroll; Escape dismisses. Outside press does not dismiss in the current beta. Pass `titleId` / `descriptionId` (or an accessible name). See [Beta maturity](/releases/beta). ### Controlled input does not move If `value` is set, you must update it from `onValueChange` (or the framework equivalent). Passing `value` without a change handler freezes the control by design. ## Forms, auth, HTTP ### Form submit does nothing useful `Form` expects `onValid` (and optional `onInvalid`), not a generic `onSubmit` prop. See [Form](/components/form) and framework guides. ### Auth cannot secure APIs Client auth is UX orchestration. Enforce authorization on the server. Policies like `requirePermission` only reflect client session claims. ### HTTP 401 loops Configure the auth refresh queue and interceptors intentionally. Dispose clients when remounting apps so queues do not stack. ## Store and adapters ### `useStore` re-renders too often Pass a selector (and equality function on React) so you subscribe to a slice, not the whole state. ### Wave B / C “missing components” Angular, Svelte, Solid, Preact, Alpine, jQuery, and HTMX packages are Experimental and limited to `storeBind` (plus `button` on Wave C). They are not incomplete React ports. See [Frameworks](/frameworks/). ## SSR ### `window is not defined` A module touched browser globals at import time, or application code did. Sometic packages must not; create DOM work after mount. See [SSR](/guide/ssr). ### Hydration mismatch Server HTML must match the client’s first paint. Avoid rendering overlay open state or CE-only attributes differently on the server. ## CLI ### `sometic.config.json` already exists Pass `--force` to recreate (backs up under `.sometic/backup` when writing). Use `--dry-run` to preview. ### `diff` / `update` / `doctor` do nothing useful Those commands are **not implemented** yet. They print a deferred message. Use `init`, `add`, `list`, `info`, and `config`. Details: [CLI](/guide/cli). ## Still stuck 1. Confirm package versions and Wave label ([Beta maturity](/releases/beta)). 2. Reproduce with a minimal import of one subpath. 3. File a GitHub bug with framework, versions, and steps. ## Related - [CLI](/guide/cli) - [Components](/components/) - [Stores](/stores/) - [Frameworks](/frameworks/) - [Beta maturity](/releases/beta) # guide/typescript.md Source: https://sometic.aitistack.com/guide/typescript # TypeScript Every published `@sometic/*` package ships TypeScript declarations next to ESM builds. Prefer TypeScript for application code; JavaScript remains supported. ## Setup - Module resolution that understands `exports` (Node16 / Bundler). - `strict` recommended. Sometic itself uses strict flags including `noUncheckedIndexedAccess` and `exactOptionalPropertyTypes`. - With `exactOptionalPropertyTypes`, omit optional props instead of passing `undefined` unless the type allows `undefined`. ## Usage ```ts import { createStore } from "@sometic/store"; import { Button } from "@sometic/react/button"; type CounterState = { count: number }; const store = createStore({ count: 0 }); store.update((state) => ({ count: state.count + 1 })); ``` Import types from the same subpaths you use for values: ```ts import type { ButtonProps } from "@sometic/react/button"; import type { Store } from "@sometic/store"; ``` ## Adapters | Package | Typing notes | | ------------------ | ---------------------------------------------------------------------- | | `@sometic/react` | Props types exported beside components; hooks typed to store selectors | | `@sometic/vue` | Component props via shipped `.d.ts`; `useStore` returns `ComputedRef` | | `@sometic/elements` | Element instance types + event detail types on `/events` | | Wave B/C | Narrow bind types (`AngularStoreBind`, …) and capability constants | ## Errors Typed errors use stable codes (`SometicError`, `isSometicError`). Prefer narrowing on `code` over string matching messages. ## FAQ ### Do I need a triple-slash reference? No. Install the package and import normally. ### Can I weaken strictness to consume Sometic? You can, but optional prop and `unknown` catch edges are easier under strict settings that match the library. ### Where are deep API lists? Start from [API packages](/api/packages) and the live section docs ([Components](/components/), [Stores](/stores/)). ## Related - [JavaScript](/guide/javascript) - [Components](/components/) - [Stores](/stores/) - [Beta maturity](/releases/beta) # guide/whats-included.md Source: https://sometic.aitistack.com/guide/whats-included # What’s included (beta) Honest inventory of the public beta. If it is not listed under **Included**, do not assume it ships yet. ## Included (Wave A, production path) | Area | Packages / surfaces | | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Foundation | `core`, `events`, `store`, `store-immer`, `styling`, `theme`, `accessibility`, `positioning`, `dom`, `validation`, `validation-zod`, `validation-yup`, `date-*`, **`head`** | | Application | `forms`, `auth`, `auth-local`, `auth-firebase`, `auth-supabase`, `auth-oidc`, `http`, **`query`**, **`head`**, **`app-shell`** | | UI engines + adapters | Button family, Field/Input family, Selection (Checkbox, Switch, Radio, Select, **Combobox**), Overlay (Dialog, Popover, Tooltip, Toast, Alert, **Menu**, **Context menu**, **Drawer**), Structure (**Tabs**, **Accordion**, **Breadcrumb**), Feedback (**Progress**, **Spinner**, **Skeleton**, **Badge**), Form adapters | | Adapters | `@sometic/react` (full Wave A components), `@sometic/vue` (button/field/input/form/overlay Dialog family; structure/selection often resolve re-exports; see each component page), `@sometic/elements` (CEs below) | | Elements CEs (honest) | **Shipped:** button family, field/input family, form, selection (checkbox/switch/radio/select, **not** combobox), overlay (dialog/popover/tooltip/toast/alert, **not** menu/context-menu/drawer), structure feedback (**`sometic-badge`**, **`sometic-progress`**, **`sometic-spinner`**, **`sometic-skeleton`**). **Not shipped as CEs:** Menu, Context menu, Drawer, Tabs, Accordion, Breadcrumb, Combobox: use React or `@sometic/dom` controllers/resolve in Vanilla. | | Tooling | `@sometic/cli`, `@sometic/registry` (`init` / `add` / `list`, hybrid mode) | ## Experimental | Surface | Reality today | | ------------------------------ | ---------------------------------------------- | | Angular, Svelte, Solid, Preact | Store-bind foundation, not full component kits | | Alpine, jQuery, HTMX | Store + button bind depth | Prefer React, Vue, or Elements for production apps in this beta. ## Deferred (later phases) | Area | Examples | | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Rich selection | Autocomplete polish, Multi-select, Tags, Sliders, Color picker, Date/Time **picker UI** | | Navigation | Command palette, Tree, Pagination, Stepper | | Data & app primitives | Data **table** engines, query **builders** / catalogs, feature flags, offline queue, undo/redo. **`@sometic/query` (server-state cache) and `@sometic/app-shell` (System composition) ship** in this beta; see [Query](/utilities/query) and [App Shell](/guide/app-shell) | See [Beta maturity](/releases/beta) for stability labels and known limitations. ## Related - [Why Sometic](/guide/why-sometic) - [Comparison](/guide/comparison) - [Components](/components/) - [Head / SEO](/utilities/head) - [Query](/utilities/query) - [App Shell](/guide/app-shell) # guide/why-sometic.md Source: https://sometic.aitistack.com/guide/why-sometic # Why Sometic Sometic is a **portable application behavior system**: shared controllers for UI, forms, auth, and HTTP — with thin framework adapters and **your** styling system. It is **not** another pre-styled React component kit. If the first thing you need is buttons that look like a brand out of the box, use a visual library. If you need **one behavior model that survives a framework change**, keep reading. ## The problem Teams rebuild the same application behavior for every stack (forms, auth refresh, theme switching, accessible overlays), while visual libraries lock you into one look, one framework, or both. Behavior that should be portable becomes duplicated and hard to keep consistent, accessible, and secure. ## What Sometic gives you out of the box 1. **One behavior model**: Controllers and resolve APIs in `@sometic/dom`, `@sometic/forms`, `@sometic/auth`, and related packages own state and interaction. React, Vue, and `sometic-*` custom elements stay thin. 2. **Your styling system**: Engines are unstyled by default. Use slots, `data-state` / `data-slot` attributes, CSS variables from `@sometic/theme`, Tailwind, Bootstrap, CSS Modules, Sass, or plain CSS. No mandatory CSS framework. 3. **Native HTML first**: Real ` ); } ``` ```tsx [TS] import { Button, ButtonGroup } from "@sometic/react/button"; export function Example(): JSX.Element { return ( ); } ``` ```html [Vanilla] CSV JSON ``` ::: ## Vue ```vue ``` ## How it works 1. **Engine (`resolveButtonGroup`)**: sets `role="group"`, `data-slot="root"`, `data-orientation`, optional `data-disabled`, plus styleable root maps. 2. **Adapters**: React/Vue render a host `div` with resolved class/style/attributes and your button children. 3. **Custom element**: `sometic-button-group` observes `orientation`, `disabled`. No press gating or focus management lives on the group; children keep Button / Toggle / Async behavior. ## Anatomy | Part | `data-slot` | Role | | ---- | ----------- | --------------------------- | | Root | `root` | Group host (`role="group"`) | Children are your buttons. The group does not invent button items or slots per child. ## Props / attributes ### React `ButtonGroupProps` From `ResolveButtonGroupOptions` plus `children`, `className`, `style`. | Prop | Type | Default | Description | | -------------------------------------------------------------------------------------- | ---------------------------- | -------------- | ----------------------------------------------------------------------- | | `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Reflected as `data-orientation` | | `disabled` | `boolean` | `false` | Group disabled attr (style/ARIA cue; still disable each child for real) | | `unstyled` / `classes` / `styles` / `cssVariables` / `defaults` / `variants` / `merge` | styling | | Styleable (`root`) | | `children` | `ReactNode` | | Buttons | | `className` / `style` | | | Host extras | | `aria-label` / `aria-labelledby` | string | | **Provide a group name** | ### Vue Props: `orientation`, `disabled`. Default slot for children. Pass `aria-label` via fallthrough attrs. ### Custom element (`sometic-button-group`) Observed: `orientation`, `disabled`. ## Events / callbacks None at the group level. Listen on child buttons (`click`, `onPressedChange`, async completion, …). ## Controlled vs uncontrolled N/A for the group. Child Toggle / Async buttons keep their own state contracts. ## Form participation The group is not a form control. Child `type="submit"` / `"reset"` buttons still participate like native buttons. ## Accessibility | Concern | Guidance | | ------------- | ---------------------------------------------------------------------- | | Name | Always name the group (`aria-label` or `aria-labelledby`) | | Role | `role="group"`, not radiogroup / toolbar unless you add those yourself | | Keyboard | Tab / Shift+Tab move between focusable children; Space/Enter activate | | Disabled | Disable each child when the group is logically disabled | | Toggle groups | Exclusivity is app-owned; this is not a radiogroup | ## Styling Target: - `[data-slot="root"]` - `[data-orientation="horizontal"|"vertical"]` - `[data-disabled]` ```tsx ``` ## Edge cases - **Empty group**: still expose a name if rendered for AT consistency. - **Nested groups**: avoid unless structure truly needs it; names can collide for users. - **`disabled` alone**: does not automatically disable descendant presses; set `disabled` on each child. - **SSR**: resolve is pure; register CE in the browser. - **Mixed children**: Button + ToggleButton is fine for layout; do not assume toolbar semantics. ## Performance notes Pure resolve wrapper; negligible cost. Prefer `@sometic/react/button` subpath imports so unused async/toggle code tree-shakes. ## When to use / When not **Use** to group related actions visually and for assistive technology. **Do not use** as: - A select / radio substitute - A [Menu](/components/menu) - A toolbar pattern unless you add the correct roles and keyboard model yourself ## FAQ **Does `disabled` disable children automatically?** Resolve sets group attrs. Disable each `Button` / child for real interaction locking. **Horizontal vs vertical?** `orientation` only affects `data-orientation` for CSS and AT context. **Is this a toggle group?** No. Use multiple [Toggle button](/components/toggle-button)s with your own exclusivity logic. **Why `role="group"`?** Native grouping for related controls without claiming radiogroup or toolbar semantics. **Can submit buttons live inside?** Yes. Form participation is per child button. **CE tag?** `sometic-button-group`. **Need arrow-key roving tabindex?** Not built in. Implement yourself or wait for deferred toolbar patterns. **Refs?** Host is a `div`; child buttons still forward refs on React Button adapters. **Bundle tip?** Import from `@sometic/react/button` (or Vue / elements matching subpaths). ## Related links - [Button](/components/button) - [Toggle button](/components/toggle-button) - [Icon button](/components/icon-button) - [Async button](/components/async-button) - [Styling slots](/concepts/styling-slots) - [State attributes](/concepts/state-attributes) # components/button.md Source: https://sometic.aitistack.com/components/button # Button Accessible native `; } ``` ```tsx [TS] import { Button } from "@sometic/react/button"; export function Example(): JSX.Element { return ; } ``` ```html [Vanilla] Save ``` ::: ## How it works 1. **Engine (`@sometic/dom`)**: `resolveButton(options)` builds a view model: native `type`, `nativeDisabled` (forced when `loading`), `shouldIgnorePress`, root/slot class and style maps, and state attributes (`data-disabled`, `data-loading`, optional `data-size` / `data-variant`). `handleButtonPress` / `bindButton` gate clicks while disabled or loading. 2. **Adapters**: React (`Button` from `@sometic/react/button`) and Vue (`@sometic/vue/button`) call `resolveButton` each render and map slots (`prefix` / `content` / `suffix` / `loader`) onto a real ` ``` ## Edge cases - **`disabled` + `loading`**, both force ignored presses; loading alone still disables. - **Re-entrancy**, rapid clicks while loading are ignored; AsyncButton also aborts prior work. - **SSR**: `resolveButton` is pure; register `sometic-button` only in the browser. - **Multi-instance**, no module singletons; each resolve/bind is independent. - **Empty children**, still a focusable button; ensure an accessible name (text or `aria-label`). ## Performance notes Resolve is a pure function (no observers). Adapters re-resolve on prop change only. Prefer `@sometic/react/button` (or Vue/elements subpaths) over barrel imports so unused icon/toggle/async code tree-shakes away. Binding one listener in `bindButton` avoids duplicating press-gate logic in app code. ## When to use / When not **Use** for primary actions, form submit/reset, and any control that must behave like a native button across frameworks. **Do not use** for: - Navigation that should change the URL, use a link. - Icon-only chrome, [Icon button](/components/icon-button). - Long-running abortable work, [Async button](/components/async-button). - Pressed on/off chrome, [Toggle button](/components/toggle-button). ## FAQ **Does `loading` disable the button?** Yes. Loading implies disabled press handling and sets `nativeDisabled`. **Can I use `type="submit"` inside a form?** Yes. Native `type`, `name`, `value`, and `form` pass through. **Why slots instead of wrapping children myself?** Shared engines and custom elements need stable `data-slot` parts so CSS and loaders stay consistent across adapters. **Is there a visual theme baked in?** No. Pair with `@sometic/theme` or your CSS via `classes`, `styles`, and `cssVariables`. **Light DOM or shadow?** Light DOM is the default so page CSS reaches the control. Add `shadow` on the CE for isolation. **Does React forward refs?** Yes, to the underlying ` Confirm? ); } ``` ```tsx [TS] import { useState } from "react"; import { Dialog } from "@sometic/react/overlay"; export function Example(): JSX.Element { const [open, setOpen] = useState(false); return ( <> Confirm? ); } ``` ```html [Vanilla] Confirm? ``` ::: ## How it works 1. **Resolve (`resolveDialog`)**, pure view model: `role="dialog"`, `data-slot="root"`, `data-state="open"|"closed"`, `aria-modal` when open, optional `aria-labelledby` / `aria-describedby` from `titleId` / `descriptionId`. 2. **Controller (`createDialogController`)**, wraps `createOverlayController({ modal: true })`. On open: portal ensure, body scroll lock, focus trap (`returnFocus`, `initialFocus: "first"`), Escape dismiss. Outside press does **not** dismiss (modal). Optional `getTrigger` restores focus on close. 3. **Adapters**: React and Vue create a dialog controller bound to a panel ref, sync `open` / `defaultOpen` through `setOpen`, call `resolveDialog` for attributes, and dispose the controller on unmount. When closed, React/Vue return `null` (unmount panel). 4. **Custom element**: `sometic-dialog` moves children into `data-slot="panel"`, owns a controller, reflects `open`, emits `open-change`. ## Anatomy | Part | `data-slot` | Role | | ------------ | ------------------------------- | ------------------------------------------------------------------- | | Root / panel | `root` (resolve) / `panel` (CE) | Dialog surface | | Trigger | , | App-owned; pass `getTrigger` on the DOM controller for return-focus | Resolve attributes when open: `role="dialog"`, `aria-modal="true"`, `data-state="open"|"closed"`, optional labelledby/describedby. ## Props / attributes ### React `DialogProps` `HTMLAttributes` plus: | Prop | Type | Default | Description | | ---------------- | ------------------------- | ------- | ------------------------------------------------- | | `open` | `boolean` | , | Controlled open | | `defaultOpen` | `boolean` | `false` | Uncontrolled initial | | `onOpenChange` | `(open: boolean) => void` | , | Fired on Escape dismiss and when you call setters | | `titleId` | `string` | , | → `aria-labelledby` | | `descriptionId` | `string` | , | → `aria-describedby` | | `children` | `ReactNode` | , | Dialog content | | Native div attrs | , | , | Merged onto the panel when open | Engine resolve also supports `disabled`, styling (`unstyled`, `classes`, `styles`, `cssVariables`, …), and controller-only `portalId` / `getTrigger` / `getContent` when you call `createDialogController` directly. ### Vue Props: `open`, `defaultOpen`, `titleId`, `descriptionId`. Emits: `update:open`, `openChange`. ### Custom element (`sometic-dialog`) Observed: `open`, `shadow`. Event: `open-change` → `{ open: boolean }`. ## Events / callbacks | Surface | Event | Payload | | -------------- | --------------------------- | ------------------- | | React | `onOpenChange` | `boolean` | | Vue | `update:open`, `openChange` | `boolean` | | Custom element | `open-change` | `{ open: boolean }` | | DOM controller | `onOpenChange` | `boolean` | ## Controlled vs uncontrolled - **Controlled:** pass `open` and update it from `onOpenChange` (including Escape). - **Uncontrolled:** omit `open`, use `defaultOpen`; Escape updates internal state and still notifies `onOpenChange`. ## Form participation Dialog is not a form control. You may nest a `
` or [Form](/components/form) inside the panel; focus trap keeps tab order inside the dialog while open. ## Accessibility - Modal path: focus trap, scroll lock, Escape dismiss, `aria-modal="true"`. - Provide an accessible name (`titleId` → heading id, or `aria-label` on the panel). - Prefer a description id for destructive confirms. - Pass `getTrigger` (vanilla controller) when you need reliable return-focus to the opener; React/Vue adapters today sync content via panel ref and do not auto-wire a trigger element. - Outside click does not close (by design for modal). - Avoid casually nesting dialogs. ## Styling Target `[data-state="open"]`, `[role="dialog"]`, `[data-slot="panel"]` (CE) / `[data-slot="root"]` (resolve). Unstyled by default, you own backdrop and panel CSS. Portal root uses Sometic portal attributes from the overlay helpers. ## Edge cases - **Closed React/Vue**, component returns `null`; chrome deactivates via controller dispose/`setOpen(false)`. - **Escape while controlled**, always listen to `onOpenChange` or open will snap back. - **Missing content element**, overlay activate no-ops until `getContent()` returns a node (layout effect after mount covers the open path). - **SSR**, create controllers and register CE only in the browser. - **Popover/Tooltip React/Vue**, still resolve-only shells; full positioning/dismiss lives on DOM controllers / CEs. Dialog is the overlay adapter that wires the controller. ## Performance notes One overlay controller per Dialog instance (trap + dismiss + scroll lock only while open). Resolve stays pure for attributes. Prefer disposing on unmount (adapters do this) so traps and locks never leak across routes. ## When to use / When not **Use** for blocking confirms, modal forms, and any flow that needs trap + scroll lock + Escape. **Do not use** for non-modal anchored content ([Popover](/components/popover)), hover hints ([Tooltip](/components/tooltip)), or transient status ([Toast](/components/toast) / [Alert](/components/alert)). ## FAQ **Do React/Vue Dialogs trap focus?** Yes. Both call `createDialogController`, which uses modal `createOverlayController` (focus trap, scroll lock, Escape). **Outside click to close?** No on dialog (modal). Popovers dismiss on outside press. **How do I label the dialog?** Pass `titleId` / `descriptionId` matching heading/description element ids, or set `aria-label`. **Portal?** Overlay ensures a portal root (`portalId` optional on the controller). **Why return `null` when closed?** Avoids leaving an inert dialog in the tree; open remounts the panel and reactivates chrome. **Menu / Drawer?** See [Menu](/components/menu) and [Drawer](/components/drawer). **SSR?** Controllers and CE registration must run in the browser. **Return focus?** Provide `getTrigger` when using the DOM controller; compose the same if you need custom React trigger wiring. ## Related links - [Popover](/components/popover) - [Tooltip](/components/tooltip) - [Alert](/components/alert) - [Toast](/components/toast) - [Accessibility](/guide/accessibility) - [Styling slots](/concepts/styling-slots) - [Beta maturity](/releases/beta) # components/drawer.md Source: https://sometic.aitistack.com/components/drawer # Drawer Modal side panel with open state, `role="dialog"`, side placement (`data-side`), and the same modal overlay chrome as Dialog: portal mounting, body scroll lock, focus trap, and Escape dismiss. ## Usage ::: code-group ```tsx [JS] import { useState } from "react"; import { Drawer } from "@sometic/react/overlay"; export function Example() { const [open, setOpen] = useState(false); return ( <> Account settings ); } ``` ```tsx [TS] import { useState } from "react"; import { Drawer } from "@sometic/react/overlay"; export function Example(): JSX.Element { const [open, setOpen] = useState(false); return ( <> Account settings ); } ``` ```js [Vanilla] import { createDrawerController, resolveDrawer } from "@sometic/dom/drawer"; const panel = document.querySelector("#drawer"); const controller = createDrawerController({ defaultOpen: false, side: "right", getContent: () => panel, onOpenChange: (open) => { const view = resolveDrawer({ open, side: "right" }); for (const [key, value] of Object.entries(view.attributes)) { panel.setAttribute(key, value); } panel.hidden = !open; }, }); document.querySelector("#open-drawer").addEventListener("click", () => { controller.setOpen(true); }); ``` ::: > Custom element not shipped in this beta; use the DOM controller. Custom element **not shipped** for Drawer. Vanilla uses `@sometic/dom/drawer`. React + DOM are primary; no Vue Drawer component. ## How it works 1. **Resolve (`resolveDrawer`)**: pure view model with `role="dialog"`, `data-side`, `data-state`, and `aria-modal` when open. 2. **Controller (`createDrawerController`)**: wraps `createOverlayController({ modal: true })` like Dialog. 3. **React adapter**: syncs `open` / `defaultOpen` / `side`, disposes on unmount, returns `null` when closed. ## Anatomy | Part | `data-slot` / attrs | Role | | ------- | ------------------- | --------------------------------- | | Panel | `root`, `data-side` | Dialog surface anchored to a side | | Trigger | — | App-owned opener | ## Props / attributes ### React `DrawerProps` Extends `HTMLAttributes`. Remaining native div attrs are forwarded to the panel when open. | Prop | Type | Default | Description | | -------------- | ---------------------------------------- | --------- | ----------------------------- | | `open` | `boolean` | — | Controlled open | | `defaultOpen` | `boolean` | `false` | Uncontrolled initial | | `onOpenChange` | `(open: boolean) => void` | — | Open changes including Escape | | `side` | `"left" \| "right" \| "top" \| "bottom"` | `"right"` | Placement side | | `children` | `ReactNode` | — | Panel content | | Native attrs | remaining div HTML attrs | — | Forwarded to the panel | Engine resolve also supports `titleId`, `descriptionId`, `disabled`, and styling hooks (`unstyled`, `classes`, `styles`, `cssVariables`, …) when calling `resolveDrawer` / `createDrawerController` directly. ### Vue No Vue `Drawer` component. Use React or `@sometic/dom/drawer`. ### Custom element **CE not shipped.** Use Vanilla DOM controller or React. ## Events / callbacks | Surface | Event | Payload | | -------------- | -------------- | --------- | | React | `onOpenChange` | `boolean` | | Vue | — | — | | Custom element | — | — | | DOM controller | `onOpenChange` | `boolean` | ## Controlled vs uncontrolled - **Controlled:** pass `open` and update from `onOpenChange` (including Escape). - **Uncontrolled:** omit `open`, use `defaultOpen`. ## Accessibility - Modal path: focus trap, scroll lock, Escape dismiss, `aria-modal="true"`. - Name the panel with `titleId` / `aria-label` (engine options or native attrs). - Prefer one open drawer at a time. ## Styling Target `[data-side]`, `[data-state="open"]`, `[role="dialog"]`. Unstyled by default. ## When to use / When not **Use** for side settings panels and secondary flows that still need modal chrome. **Do not use** for centered confirms ([Dialog](/components/dialog)) or non-modal menus ([Menu](/components/menu)). ## FAQ **Same chrome as Dialog?** Yes. Modal overlay controller. **Outside click?** Does not dismiss (modal). **Is there an `sometic-drawer`?** No. CE not shipped. **Vue adapter?** Not shipped. React + DOM primary. **Does React forward native attrs?** Yes, onto the panel when open. **SSR?** Create controllers only in the browser. ## Related links - [Dialog](/components/dialog) - [Menu](/components/menu) - [Accessibility](/guide/accessibility) - [Styling slots](/concepts/styling-slots) # components/field.md Source: https://sometic.aitistack.com/components/field # Field Accessible field shell that wires label, description, control, and error with generated IDs and shared invalid / disabled / readonly / required state. Presentation and a11y chrome only; value ownership stays on the control or [Form](/components/form). ## Usage ::: code-group ```tsx [JS] import { Field } from "@sometic/react/field"; import { Input } from "@sometic/react/input"; export function Example() { return ( ); } ``` ```tsx [TS] import { Field } from "@sometic/react/field"; import { Input } from "@sometic/react/input"; export function Example(): JSX.Element { return ( ); } ``` ```html [Vanilla] ``` ::: ## Vue ```vue ``` ## How it works 1. **Engine (`@sometic/dom/field`)**: `createFieldIds` / `resolveField` produce stable ids and slot attributes for label, description, control, error, and extra. 2. **Adapters**: React/Vue render the shell and pass ids into child controls via cloning or `fieldIds` / control attributes. 3. **Custom element**: `sometic-field` observes `disabled`, `invalid`, `readonly`, `required`, `shadow`. Field does not validate or own values; pair with Input/Checkbox/Select and the forms engine when you need submit/validation. ## Anatomy | Part | `data-slot` | Role | | ----------- | ------------- | -------------------------------- | | Root | `root` | Field host | | Label | `label` | Associated `