This file is the operating guide for AI coding agents working in this repository. It applies to the whole repository unless a deeper AGENTS.md overrides it.
@typlog/ui is a Vue 3 component library built on Reka UI primitives and Radix color tokens. The library adds Typlog's theme, sizing, variants, and higher-level compositions while preserving Reka UI's accessibility and interaction behavior.
- Package manager: pnpm (the repository commits
pnpm-lock.yaml). - CI runtime: Node.js 24.
- Source language: Vue SFCs and strict TypeScript.
- Documentation: VitePress, with live examples under
docs/examples. - Styling: plain CSS, semantic custom properties, Radix colors, and stable class/data-attribute contracts.
- Tests: Vitest component regression tests under
packages/ui/test/, plus manual interactive checks in the docs site.
Read CONTRIBUTING.md before changing public APIs.
| Path | Purpose |
|---|---|
packages/ui/src/components/ |
Core components published from @typlog/ui. |
packages/ui/src/components/index.ts |
Main public component/type export surface. |
packages/ui/src/components/types.ts |
Shared public types such as ColorType and RadiusType. |
packages/ui/src/components/util.ts |
Prop forwarding and variant-class helpers. |
packages/ui/src/addons/ |
Optional or integration-specific components published from @typlog/ui/addons. |
packages/ui/src/styles/ |
Base tokens, theme variables, color mappings, and shared component CSS. |
packages/ui/src/styles/tailwind/ |
Tailwind-compatible token definitions included by the main stylesheet. |
packages/ui/test/ |
Vitest component regression tests, organized to mirror the source families they cover. |
docs/examples/ |
Runnable examples used by component documentation. |
docs/content/ |
VitePress Markdown content. |
.vitepress/config.ts |
Documentation navigation and Vite config. |
.vitepress/meta/ |
Generated component API metadata; do not edit it manually. |
scripts/generate-component-meta.ts |
Generates API metadata from exported component types and JSDoc. |
packages/ui/vite.config.ts |
Builds the public entries and emits the publishable dist/package.json. |
packages/ui/package.json |
Manifest template for the published package and its runtime dependencies. |
package.json |
Workspace, development dependencies, scripts, and release version. |
Run commands from the repository root.
pnpm install --frozen-lockfile # reproducible install
pnpm start # VitePress development server
pnpm lint # ESLint for source, docs, and VitePress code
pnpm typecheck # vue-tsc without emitting files
pnpm typecheck:ui # vue-tsc for package source and package tests
pnpm test # run Vitest once
pnpm check # lint, both typechecks, and tests
pnpm check:release # full metadata, package-consumer, and docs validation
pnpm build:meta # regenerate .vitepress/meta from public exports
pnpm build # build the publishable library into packages/ui/dist/
pnpm build:docs # build the documentation into site/public/Use pnpm add rather than another package manager. Add package runtime dependencies to the packages/ui workspace (for example, pnpm add <package> --filter @typlog/ui) and tooling dependencies to the root as dev dependencies. Do not rely on a transitive dependency.
When creating or changing a component, preserve these repository conventions:
- Put core UI in
packages/ui/src/components/<family>/and optional/vendor integrations inpackages/ui/src/addons/<family>/. - Use PascalCase Vue filenames and public component names. Use kebab-case directory and documentation names.
- Declare exported prop interfaces/types in a normal
<script lang="ts">block.vue-component-metareads these declarations and their JSDoc. - Prefer a Reka UI primitive for semantics, keyboard interaction, focus management, and ARIA. Extend its prop type and forward its emits instead of recreating behavior.
- Strip Typlog-only visual props before forwarding to Reka. Reuse
useForwardPropsWithout,useForwardPropsEmitsWithout,useForwardProps, anduseForwardExposeas appropriate. - Prefix component classes with
ui-. Visual prop classes user-<prop>-<value>(for exampler-size-2) and booleans user-<prop>(for exampler-high-contrast).buildPropsClasscreates these classes. - Use
data-accent-coloranddata-radiusfor local theme overrides. Consume existing CSS variables instead of introducing literal theme colors, spacing, radii, or shadows. - Style Reka state through its data attributes, such as
data-state,data-disabled,data-highlighted, anddata-orientation. Keep focus-visible and disabled behavior explicit. - Content rendered through a Portal leaves the provider DOM tree. Wrap portal content in
ThemeWrapperso.ui-themeand theme data attributes are restored. - Export the component and its public types from the family
index.ts, then export that family frompackages/ui/src/components/index.tsorpackages/ui/src/addons/index.ts. - Add a live example, a component page, generated API metadata, and a sidebar entry for a new public component.
Do not casually rename or remove exported symbols, props, emitted events, CSS classes, or documented behavior. They are part of the consumer-facing contract even when TypeScript cannot enforce them.
Choose a component shape based on behavior and composition:
- Styled primitive: For one semantic element, render a Reka
Primitiveor a single Reka component and keep the Typlog wrapper thin. Seebutton,badge, andswitch. - Compound component: Export separate parts when consumers need layout control. Put shared configuration on the root and provide typed refs to children with Reka
createContext; use a repository-scoped context name such asui:SelectRoot. Seeselect,combobox,accordion, andcollapsible. - Portalled overlay: Keep the Reka Portal and accessible composition visible. Place
ThemeWrapperinside the Portal around popup content so theme variables and.ui-themeare restored. Seedialog,popover, andselect. - Addon: Put vendor integrations and opinionated features outside the core design-system contract in
packages/ui/src/addons; publish them from@typlog/ui/addons.
Do not hide essential Reka parts when consumers need them for accessible composition. Re-export an unchanged primitive from the family barrel when wrapping it adds no value.
- Put exportable prop and emit types in a normal
<script lang="ts">block and runtime implementation in<script setup>. - Forward supported slots rather than converting them to string props. Preserve the underlying
v-modelandupdate:*event names. - Use
useForwardPropsWithoutfor props,useForwardPropsEmitsWithoutfor props plus emits, Reka'suseForwardPropswhen every prop belongs to the primitive, anduseForwardExposewhen consumers need the underlying element or API. - Set
inheritAttrs: falseand bind$attrsdeliberately when attributes belong on an inner element. - Keep compound-component context values reactive so runtime prop updates reach child parts.
- Prefer existing color (
--gray-*,--accent-*,--focus-*,--color-*), spacing (--space-*,--scaling), radius, typography, shadow, and animation tokens. Add a token only at the narrowest shared theme layer and verify light, dark, and high-contrast behavior. - Use
:where()for modifier and state selectors when low specificity helps consumer overrides. - Inside core components, prefer relative imports for nearby files and shared utilities. Inside addons, consume core APIs through
@typlog/ui. - Documentation examples must use consumer-facing imports:
@typlog/ui,@typlog/ui/addons, or@typlog/ui/charts. - Add runtime dependencies with
pnpm add <package> --filter @typlog/ui; root-only build and documentation tools belong in rootdevDependencies.
- Find the closest existing component by behavior and composition, not just appearance.
- Check the current Reka UI primitive and its prop/emit types in the installed dependency before designing the wrapper API.
- Define the public API and defaults. Reuse
ColorType,RadiusType, existing size scales, and established variant names where they fit. - Implement semantic behavior first, then styling and theme overrides.
- Add barrel exports and confirm the component is reachable from the intended package entry.
- Add
docs/examples/<component>/Overview.vueand focused examples for meaningful variants or states. - Add
docs/content/components/<component>.md, use<PropsTable name="ComponentName" />, and register the page in.vitepress/config.ts. - Run
pnpm build:metaand commit the generated metadata for the public component. - Run the validation matrix below and inspect the live docs in light and dark themes, with keyboard navigation.
| Change | Minimum validation |
|---|---|
| Documentation only | pnpm lint, pnpm build:docs |
| Component implementation or types | pnpm lint, pnpm typecheck, pnpm typecheck:ui, pnpm test, pnpm build |
| Public props/exports or new component | Above, plus pnpm build:meta and pnpm build:docs |
| Theme/token/CSS change | Above, plus visual checks in light/dark themes and at affected sizes/variants |
| Overlay or interactive behavior | Above, plus keyboard, focus, disabled, controlled/uncontrolled, and portal checks |
Generated outputs packages/ui/dist/ and site/public/ are ignored and should not be committed. Generated .vitepress/meta/*.json files are tracked and should be committed when their source API changes.
- Inspect
git statusbefore editing and preserve unrelated user changes. - Keep changes scoped. Do not reformat or refactor unrelated components while adding one.
- Treat source exports, runtime dependencies, docs, and generated metadata as one atomic public change.
- Do not hand-edit lockfiles, generated metadata,
packages/ui/dist/, orsite/public/. - Put component regression tests under
packages/ui/test/, use public package imports when testing exported APIs, and report only commands actually run. - If implementation and current documentation disagree, verify behavior in source and call out the discrepancy rather than silently codifying it.
- Prefer established local patterns over introducing a new abstraction for a single component.