diff --git a/public/AGENTS.md b/public/AGENTS.md new file mode 100644 index 0000000..d2fdc11 --- /dev/null +++ b/public/AGENTS.md @@ -0,0 +1,116 @@ +# Build an Askr application + +Use this guide when creating or changing an application built with Askr. Keep +the application legible: routes, state, data, server boundaries, and cleanup +must have an owner a reader can identify from the code. + +## Start with the installed contract + +The installed `@askrjs/*` packages are authoritative for exports, props, +types, runtime behavior, and errors. Examples are teaching aids and may lag +behind a published package. When they disagree, follow the installed package +declarations and report the stale example. + +Before using a prop, export, or CLI flag, verify it in the installed package. +Existence is not endorsement: read its declaration comments and canonical +documentation too. Do not choose an API marked legacy, deprecated, limited, +or experimental unless the task specifically calls for that status and +accepts its constraints. Do not fill gaps with assumptions from React, +Next.js, Solid, or another framework. + +## Structure the application + +Keep the route tree visible under `src/pages`: + +- `src/pages/_routes.tsx` owns the root route registry and composes route + groups. +- `src/pages/_layout.tsx` owns root providers and the application-wide shell. +- A nested route group owns its adjacent `_routes.tsx` and `_layout.tsx`. +- Files do not become routes merely because they are under `pages`. + +Preserve this composition direction: + +```text +pages -> features -> components -> Askr primitives +``` + +Pages connect routes to features. Features own coherent application +capabilities and their visible states. Components are reusable application UI +with explicit props. Components do not import features, and features do not +import pages. + +When a feature uses external or mutable data, preserve this second direction: + +```text +features -> queries and mutations -> services -> adapters +``` + +Queries and mutations call application services; services map raw adapter +results into application models. Do not make reusable components fetch +application data or make pages call transport clients directly. + +## Use the building blocks + +Import themed application components from `@askrjs/themes/components`. +`Block` is the general-purpose layout primitive. Use `Container` for content +width and page gutters, `Text` for themed copy, and `Grid` for explicit rows +and columns. Compose semantic page structure from published components such as +`Header`, `Main`, `Section`, `PageHeader`, and `Footer`. + +Use the exact props and token values accepted by the installed declarations. +For example, use `paddingY`, not an invented `py` shorthand. Use themed +components when the shared theme owns appearance and headless `@askrjs/ui` +primitives when the application must own it. + +## Load the composable skill + +For an end-to-end application workflow, read +[`/skills/build-askr-app/SKILL.md`](https://askrjs.com/skills/build-askr-app/SKILL.md). +It composes these capability skills: + +- [`/skills/project-structures/SKILL.md`](https://askrjs.com/skills/project-structures/SKILL.md) + for pages, features, components, adapters, queries, mutations, and ownership; +- [`/skills/routes/SKILL.md`](https://askrjs.com/skills/routes/SKILL.md) for + registries, layouts, URL state, data loading, access, navigation, metadata, + and fallbacks; +- [`/skills/themes/SKILL.md`](https://askrjs.com/skills/themes/SKILL.md) for + building blocks, tokens, visual customization, and rendered styles. +- [`/skills/control-flows/SKILL.md`](https://askrjs.com/skills/control-flows/SKILL.md) + for conditional rendering, keyed collections, row reactivity, and retained + render order. +- [`/skills/icons/SKILL.md`](https://askrjs.com/skills/icons/SKILL.md) for icon + and logo selection, imports, accessibility, sizing, and themed composition. +- [`/skills/queries-and-mutations/SKILL.md`](https://askrjs.com/skills/queries-and-mutations/SKILL.md) + for cache identity, query consistency, writes, invalidation, prefetch, and + hydration. +- [`/skills/i18n/SKILL.md`](https://askrjs.com/skills/i18n/SKILL.md) for typed + catalogs, locale ownership, direction, formatting, and hydration. +- [`/skills/charts/SKILL.md`](https://askrjs.com/skills/charts/SKILL.md) for + typed plot composition, data rules, interaction, accessibility, and export. + +Load only the references needed by the task. A five-page marketing SSG, an +authenticated SPA, and a server-rendered application compose these capabilities +differently; they are not separate outcome-specific skills. + +## Verify the result + +Run the repository-owned format, lint, type, analysis, test, and production +build gates appropriate to the application. Inspect SSR or SSG output before +hydration and exercise the hydrated browser at narrow and desktop widths. +Verify loading, empty, failure, cancellation, retry, and unauthorized states +where they exist. A pushed commit is not proof that a deployment succeeded. + +## Canonical references + +- [Getting started](https://askrjs.com/docs/getting-started/index.md) +- [Project structure](https://askrjs.com/docs/getting-started/project-structure/index.md) +- [Routing and data](https://askrjs.com/docs/routing/index.md) +- [Server and APIs](https://askrjs.com/docs/server/index.md) +- [UI and components](https://askrjs.com/docs/components/index.md) +- [Layout primitives and exact props](https://askrjs.com/docs/components/application-layout/index.md) +- [Production readiness](https://askrjs.com/docs/guides/production-readiness/index.md) +- [Generated API reference](https://askrjs.com/docs/reference/api/index.md) + +Use [llms.txt](https://askrjs.com/llms.txt) for documentation discovery and +[llms-full.txt](https://askrjs.com/llms-full.txt) for the complete generated +documentation corpus. diff --git a/public/llms.txt b/public/llms.txt index 93d5fe5..c6c06eb 100644 --- a/public/llms.txt +++ b/public/llms.txt @@ -8,6 +8,16 @@ Use the documentation links below as the primary source for Askr APIs and usage. ## Start here +- [Agent building guide](https://askrjs.com/AGENTS.md): Concise architectural rules and the entry point for agents building with Askr. +- [Build an Askr app skill](https://askrjs.com/skills/build-askr-app/SKILL.md): Thin end-to-end workflow that composes the project structures, routes, themes, and static-delivery guidance. +- [Askr themes skill](https://askrjs.com/skills/themes/SKILL.md): Theme boundaries, styled building blocks, tokens, customization, and server-rendered style integration. +- [Askr routes skill](https://askrjs.com/skills/routes/SKILL.md): Route registries, layouts, URL state, navigation, loaders, access, metadata, and fallbacks. +- [Askr project structures skill](https://askrjs.com/skills/project-structures/SKILL.md): Ownership and dependency direction across pages, features, components, data boundaries, and server code. +- [Askr control flows skill](https://askrjs.com/skills/control-flows/SKILL.md): Conditional rendering, keyed collections, reactive rows, and render-scoped ordering invariants. +- [Askr icons skill](https://askrjs.com/skills/icons/SKILL.md): Lucide icons, brand logos, accessible composition, semantic sizing, and custom SVG integration. +- [Askr queries and mutations skill](https://askrjs.com/skills/queries-and-mutations/SKILL.md): Query identity, consistency, reconciliation, mutations, invalidation, server prefetch, dehydration, and hydration. +- [Askr i18n skill](https://askrjs.com/skills/i18n/SKILL.md): Typed locale catalogs, application-owned locale resolution, direction, formatting, and SSR hydration. +- [Askr charts skill](https://askrjs.com/skills/charts/SKILL.md): Typed plotting, marks and transforms, interaction, accessibility, live data, styling, and export. - [Documentation](https://askrjs.com/docs/index.md): Documentation overview and navigation for the Askr platform. - [Getting started](https://askrjs.com/docs/getting-started/index.md): Install Askr, choose a starter, and build a first route. - [Core concepts](https://askrjs.com/docs/core-concepts/index.md): Components, state, scopes, lifecycle work, and deterministic rendering. diff --git a/public/skills/build-askr-app/SKILL.md b/public/skills/build-askr-app/SKILL.md new file mode 100644 index 0000000..1583aa6 --- /dev/null +++ b/public/skills/build-askr-app/SKILL.md @@ -0,0 +1,84 @@ +--- +name: build-askr-app +description: Build, change, or review an Askr application using explicit pages, features, components, data boundaries, rendering modes, and deployment paths. Use for Askr SPAs, SSR applications, SSG sites, full-stack applications, and API-backed features. +--- + +# Build an Askr app + +Build the smallest application mode that owns the requested behavior while +keeping every route, state transition, external boundary, and cleanup owner +visible. + +## Establish the contract + +1. Inspect the existing application before scaffolding or restructuring it. +2. Read its `package.json`, route registry, build configuration, and installed + `@askrjs/*` declarations. +3. Treat installed package declarations as authoritative. Verify exports, + props, token values, CLI flags, and declaration status rather than inferring + them from examples or another framework. An exported compatibility alias is + not a recommendation for new code. Avoid legacy and deprecated APIs; use + limited or experimental APIs only when their documented constraints fit the + request. For example, + `grep -n "" node_modules/@askrjs//dist/*.d.ts` before relying + on a prop or export named in this skill's references. +4. Identify the requested routes, rendering mode, external systems, mutable + actions, deployment target, and completion evidence. +5. Do not invent production URLs, repository names, contact destinations, + analytics identifiers, pricing, customer claims, or credentials. + +## Select the needed references + +Compose only the skills and references that apply: + +- Read the sibling [project structures skill](../project-structures/SKILL.md) + when placing pages, features, components, adapters, queries, mutations, or + server code. +- Read the sibling [routes skill](../routes/SKILL.md) when declaring routes, + layouts, URL state, loaders, access, navigation, metadata, or fallbacks. +- Read the sibling [themes skill](../themes/SKILL.md) when selecting a theme, + composing themed UI, customizing tokens, or integrating theme styles with + SSR or SSG. +- Read the sibling [control flows skill](../control-flows/SKILL.md) when + rendering conditional branches, keyed collections, or reactive row state. +- Read the sibling [icons skill](../icons/SKILL.md) when selecting icons or + logos, composing icon-only controls, or aligning custom SVGs with the shared + icon contract. +- Read the sibling + [queries and mutations skill](../queries-and-mutations/SKILL.md) when a + feature owns cached async data, writes, invalidation, consistency, or server + query hydration. +- Read the sibling [i18n skill](../i18n/SKILL.md) when the application owns + multiple locales, translated catalogs, direction, or locale hydration. +- Read the sibling [charts skill](../charts/SKILL.md) when a feature needs + typed plots, interaction, live visualization, or plot export. +- Read [static-delivery.md](references/static-delivery.md) when generating an + SSG, configuring a base path, or deploying static output to GitHub Pages. + +These skills compose. A marketing SSG normally needs project structures, +routes, themes, and static delivery. A small static feature does not need data +boundaries merely because a larger application might. + +## Implement in ownership order + +1. Register the route tree and its layouts. +2. Make each page compose its route-facing features. +3. Make features compose application components and own their visible states. +4. Add services, adapters, queries, and mutations only for real external or + mutable data, preserving their documented dependency direction. +5. Compose published Askr primitives using their installed contracts. +6. Configure the selected rendering and delivery path without adding a second + route manifest or build pipeline. + +## Verify the application + +Run the repository-owned format, lint, type, analysis, test, and production +build gates appropriate to the change. Then inspect the actual output: + +- exercise valid, empty, loading, failure, cancellation, retry, and access + states that exist; +- verify keyboard navigation, focus, accessible names, landmarks, headings, + responsive layout, hydration, and the browser console; +- inspect SSR or SSG HTML before hydration; +- for a deployment, verify the live routes, assets, metadata, fallback, and + deployment status rather than treating a local build or push as proof. diff --git a/public/skills/build-askr-app/agents/openai.yaml b/public/skills/build-askr-app/agents/openai.yaml new file mode 100644 index 0000000..03e50b9 --- /dev/null +++ b/public/skills/build-askr-app/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: 'Build an Askr App' + short_description: 'Compose and ship legible Askr applications' + default_prompt: 'Use $build-askr-app to build an Askr application with explicit architecture and appropriate delivery guidance.' diff --git a/public/skills/build-askr-app/references/static-delivery.md b/public/skills/build-askr-app/references/static-delivery.md new file mode 100644 index 0000000..3e5528d --- /dev/null +++ b/public/skills/build-askr-app/references/static-delivery.md @@ -0,0 +1,167 @@ +# Static generation and GitHub Pages + +## Contents + +- Define the static contract +- Configure one SSG path +- Handle GitHub Pages base paths +- Deploy and verify +- Five-page marketing composition + +## Define the static contract + +Before implementation, identify every crawlable route, its purpose, primary +action, title, description, canonical URL, Open Graph data, and navigation +label. Do not guess the repository segment, production origin, custom domain, +contact destination, analytics identifier, or social image. + +Use the shared route registry as the only route manifest. An SSG configuration +must render into the built client template, include required browser and public +assets, use the real `siteUrl`, and generate only intended crawlable routes. +When using themed SSR output, wrap the document renderer with the published +theme style integration and treat missing style registration as an error. + +Build client assets first and static HTML second. Do not add a second static +generator, sitemap implementation, or deployment build. + +## Configure one SSG path + +Keep the repository's generated build path. A typical application has one +`ssg.config.ts` that imports `routeRegistry`, injects generated `appHtml` into +the built client template, copies both `public/` and built browser assets, and +excludes the explicit `/404` document from the sitemap: + +```ts +// ssg.config.ts +import { readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; +import type { DocumentRenderArgs } from '@askrjs/askr/ssg'; +import { withThemeStyles } from '@askrjs/themes/ssr'; +import { routeRegistry } from './src/pages/_routes'; + +let clientTemplate: string | undefined; + +function renderDocument({ appHtml }: DocumentRenderArgs): string { + clientTemplate ??= readFileSync( + resolve(process.cwd(), '.askr/client/index.html'), + 'utf8' + ); + const appRoot = /]*\bid=["']app["'][^>]*)>\s*<\/div>/i; + if (!appRoot.test(clientTemplate)) { + throw new Error('Built client template must contain an empty #app root.'); + } + return clientTemplate.replace(appRoot, `${appHtml}`); +} + +export const staticConfig = { + registry: routeRegistry, + outputDir: 'dist', + document: withThemeStyles(renderDocument), + styleRegistrationValidation: 'error' as const, + assets: [ + { from: resolve(process.cwd(), 'public'), to: '.' }, + { from: resolve(process.cwd(), '.askr/client/assets'), to: 'assets' }, + ], + siteUrl: 'https://example.com', + sitemap: { routes: { '/404': false } }, +}; +``` + +Give every crawlable route explicit metadata. Register `/404` as a concrete +route so SSG emits `dist/404/index.html`, and also use a fallback so unmatched +navigation renders the same UI: + +```tsx +// src/pages/_routes.tsx +import { createRouteRegistry, fallback, route } from '@askrjs/askr/router'; +import NotFoundPage from './not-found'; + +export const routeRegistry = createRouteRegistry(() => { + route('/404', NotFoundPage, { + meta: { + title: 'Page not found', + description: 'The requested page does not exist.', + robots: 'noindex', + }, + }); + fallback(NotFoundPage); +}); +``` + +Copy the generated file to `dist/404.html` after the build for static hosts +that expect a top-level fallback: `cp dist/404/index.html dist/404.html`. + +## Handle GitHub Pages base paths + +- A custom domain or `owner.github.io` site uses the origin root. +- An `owner.github.io/repository` project site uses `/repository` as the route + registry `basePath`, `/repository/` as Vite's `base`, and includes that + segment in `siteUrl`. + +Set both route and asset bases; setting only one breaks either navigation or +assets. Use router links for internal navigation. Import build-owned assets or +prefix public assets with the configured base instead of hard-coding paths +such as `/images/hero.webp`. Canonical and Open Graph URLs must be absolute and +include the project segment. + +```ts +// src/pages/_routes.tsx +export const routeRegistry = createRouteRegistry(registerRoutes, { + basePath: '/repository', +}); + +// vite.config.ts +export default defineConfig({ + base: '/repository/', + plugins: [askr()], +}); +``` + +Use the real repository segment in both places, and omit both entirely for an +origin-root deployment (custom domain or `owner.github.io`). + +## Deploy and verify + +Use GitHub Pages artifact deployment rather than committing `dist/` or adding +a publishing branch. The workflow should install from the lockfile, run the +repository's validation gates, build once, create `dist/404.html`, verify +expected output, upload `dist/`, and deploy it. Grant only `contents: read`, +`pages: write`, and `id-token: write`; use the `github-pages` environment and +serialize deployments. Pin third-party actions to reviewed commits. + +Before merge, run the production build, then run the repository's preview +script (typically `vp preview`, which serves `dist/` honoring `vite.config.ts`'s +`base`) and open the printed local URL — for a project site that URL already +includes `//`; do not strip it. Verify all routes, assets, +internal links, metadata, sitemap, robots directive, 404, keyboard behavior, +narrow layout, hydration, and browser console. After merge, wait for the Pages +deployment and repeat those checks against the live URL. + +Match a broken build to its likely cause before guessing at a fix: + +| Symptom | Likely cause | +| -------------------------------------------------------- | ------------------------------------------------------------------------------- | +| Blank page, console 404s on `/assets/*` | `basePath`/Vite `base` missing or mismatched for a project site | +| Every route 404s except `/` | Only one of route `basePath` or Vite `base` is set — both are required together | +| Broken link preview on social share | `openGraph`/`canonical` missing or relative instead of absolute | +| GitHub Pages serves an unstyled 404 | `dist/404.html` copy step didn't run before upload | +| Images or CSS 404 only on the deployed site, not locally | Hard-coded root-relative asset path instead of an import or base-prefixed path | + +## Five-page marketing composition + +Treat “five pages” as five crawlable marketing routes; `/404` is deployment +infrastructure and does not count. Unless the brief says otherwise, start with +home, features, pricing, about, and contact, but decide their distinct jobs +before implementation. + +Use a marketing route group with its own `_routes.tsx` and `_layout.tsx`. Each +page composes one or more marketing features; features compose reusable +marketing components and published primitives. The layout owns shared header, +navigation, main landmark, container, and footer. Do not create five variants +of one hero or leave starter copy, fake claims, invented prices, dead links, or +controls that only appear to work. + +GitHub Pages cannot process a contact form. Use published contact information +or a mail link unless a real endpoint is authorized. When an endpoint exists, +put it behind a validated adapter and mutation, then show pending, failure, +recovery, and success states. diff --git a/public/skills/charts/SKILL.md b/public/skills/charts/SKILL.md new file mode 100644 index 0000000..d24723d --- /dev/null +++ b/public/skills/charts/SKILL.md @@ -0,0 +1,42 @@ +--- +name: charts +description: Build, change, debug, or review typed data visualizations in an Askr application. Use for @askrjs/charts, createPlot, typed channels, marks, transforms, scales, axes, legends, tooltips, selection, zoom, brush, live rows, accessible summaries, Canvas rendering, SVG or PNG export, or chart styling. +--- + +# Build Askr charts + +Use `@askrjs/charts` as a typed plotting engine. It compiles row-typed JSX +descriptors into one immutable scene used by mounted Canvas rendering and PNG, +SVG, and data export. The application still owns loading, errors, filters, +cards, routes, and product actions around the plot. + +## Verify the installed surface + +Read the installed root declarations and packaged charting documentation before +using a primitive. JavaScript comes from `@askrjs/charts`; structural styles +come from `@askrjs/charts/styles`. Do not use removed chart-specific wrappers or +invent `/components`, `/core`, `/default`, or per-chart CSS entrypoints. + +## Select the needed references + +- Read [plots-and-data.md](references/plots-and-data.md) for the typed factory, + root, row keys, channels, marks, scales, transforms, and missing-value rules. +- Read [interaction-and-accessibility.md](references/interaction-and-accessibility.md) + for labels, summaries, legends, tooltips, selection, keyboard inspection, + zoom, brush, meter semantics, and non-graphical access. +- Read [live-data-and-export.md](references/live-data-and-export.md) for + immutable row updates, follow-latest behavior, controlled view/selection, + APIs, and PNG, SVG, CSV, or JSON export. +- Read [styling-and-rendering.md](references/styling-and-rendering.md) for CSS + imports, tokens, responsive sizing, SSR, hydration, Canvas limits, and motion. +- Compose [queries and mutations](../queries-and-mutations/SKILL.md) for data + acquisition and [themes](../themes/SKILL.md) for the surrounding interface. + +## Verify the visualization + +Verify source, transformed, omitted, visible, and selected rows against the +product question. Exercise empty, missing, negative, zero, large, dense, and +live datasets; keyboard and pointer interaction; reduced motion; resizing; +light and dark themes; server output; and every enabled export path. Never use +a tooltip, color, or canvas pixel as the only representation of essential +information. diff --git a/public/skills/charts/agents/openai.yaml b/public/skills/charts/agents/openai.yaml new file mode 100644 index 0000000..caaf1a7 --- /dev/null +++ b/public/skills/charts/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: 'Build Askr Charts' + short_description: 'Compose typed and accessible Askr plots' + default_prompt: 'Use $charts to build a typed, accessible Askr visualization that preserves data truth.' diff --git a/public/skills/charts/references/interaction-and-accessibility.md b/public/skills/charts/references/interaction-and-accessibility.md new file mode 100644 index 0000000..b121676 --- /dev/null +++ b/public/skills/charts/references/interaction-and-accessibility.md @@ -0,0 +1,54 @@ +# Interaction and accessibility + +## Contents + +- Explain the plot without pixels +- Add inspection and selection +- Add navigation carefully +- Represent bounded values +- Keep application states outside + +## Explain the plot without pixels + +Provide a useful root `label` and, where appropriate, `title`, +`headingLevel`, `description`, and `summary`. A summary callback can report +source, transformed, omitted, and visible row counts. Do not make a tooltip, +canvas mark, or color the only source of essential meaning. + +Use redundant series encoding. The defaults cycle dash patterns for lines and +shapes for points when color creates multiple series; preserve or deliberately +replace that behavior rather than relying on hue alone. + +## Add inspection and selection + +`Tooltip` supports automatic, nearest-mark, or shared nearest-x inspection. +Add it for detail, not as a substitute for semantic context or data access. +`Crosshair` adds an inspection guide. `Legend` explains a scale and may filter +an interactive color scale. + +`Select` provides single or toggle selection. Pointer and keyboard selection +updates before `onActivate(row, key, target)`, which the application may use +for drill-down. Keep the product action outside the chart engine. + +## Add navigation carefully + +Add `Zoom` or `Brush` only when users need to inspect a domain. Arrow keys +inspect marks; Enter or Space activates; plus and minus zoom; Home resets; and +Shift plus arrows pans. Prefer Shift-modified brushing so ordinary dragging +remains available to the page. Verify equivalent pointer and keyboard paths. + +Use controlled `view` or `selection` only when a route, URL, or shared owner +must persist it; otherwise prefer the corresponding default state. + +## Represent bounded values + +For progress or gauge compositions, provide root `meter` semantics with +minimum, maximum, current value, and useful value text. A bounded bar or arc is +not accessible as a meter merely because it looks like one. + +## Keep application states outside + +Use root `empty` only for a successfully loaded dataset with no renderable +rows. Route or feature code owns loading, failure, authorization, and retry. +Keep those states distinct and do not mount a misleading empty plot while data +is still unknown. diff --git a/public/skills/charts/references/live-data-and-export.md b/public/skills/charts/references/live-data-and-export.md new file mode 100644 index 0000000..6e6caf8 --- /dev/null +++ b/public/skills/charts/references/live-data-and-export.md @@ -0,0 +1,47 @@ +# Live data and export + +## Contents + +- Update rows immutably +- Follow recent data without stealing control +- Capture the mounted API +- Export the intended view + +## Update rows immutably + +Use `appendPlotRows`, `upsertPlotRows`, `removePlotRows`, and `trimPlotRows` to +produce readonly arrays for live data. `upsertPlotRows` rejects duplicate keys; +use the same stable row identity as the plot. Pass an Askr state getter directly +as root data when updates are reactive. + +Bound retained history by a row count or temporal window. Do not let an +unbounded polling or streaming series grow for the life of the page. + +## Follow recent data without stealing control + +Configure `followLatest` with a row count or `{ durationMs, field }` matching +the retained data window. User pan or zoom pauses following. Resume only after +explicit operator intent through `PlotApi.resumeLive()`; new data must not pull +someone away from a range they are investigating. + +## Capture the mounted API + +Use `onApiChange` to receive the mounted `PlotApi` and clear the reference +when it becomes `null` during cleanup. The API exposes resolved rows, +`resetView`, `resumeLive`, and export operations. Do not assume it exists during +SSR or before mounted dimensions resolve. + +## Export the intended view + +- PNG export chooses current or full view, pixel ratio, background, and whether + transient overlays are included. +- SVG export uses the same immutable scene but is an export format, not the + mounted renderer; referenced fonts are not embedded. +- Data export chooses current or full view, source or transformed rows, + all/visible/selected scope, and CSV or JSON. + +PNG and SVG require a mounted plot with resolved dimensions. Transient hover, +crosshair, and brush overlays are excluded unless requested. CSV export +neutralizes formula-like strings, while JSON serializes dates and non-finite +values according to the installed contract. Verify exported data and graphics +against the visible filters, selection, and product wording. diff --git a/public/skills/charts/references/plots-and-data.md b/public/skills/charts/references/plots-and-data.md new file mode 100644 index 0000000..b17f6b9 --- /dev/null +++ b/public/skills/charts/references/plots-and-data.md @@ -0,0 +1,81 @@ +# Plots and data + +## Contents + +- Create one typed factory +- Establish root semantics +- Select channels and marks +- Use scales and transforms deliberately +- Preserve data truth + +## Create one typed factory + +Create a factory at module scope for one row contract: + +```tsx +import { createPlot } from '@askrjs/charts'; + +type RevenueRow = { + id: string; + day: Date; + revenue: number; + target: number; +}; + +const RevenuePlot = createPlot(); +``` + +Do not create it during render or mix descriptors from another factory into +its root. The factory binds field names, accessors, marks, scales, and +interaction to the row type. + +## Establish root semantics + +Every `Root` requires readonly data or a data getter, a stable `rowKey`, and an +accessible `label`: + +```tsx + + + + +``` + +Use unique string or number row keys. They preserve identity for selection, +transitions, and live updates. The root also owns plot title, description, +summary, empty state, dimensions, view, selection, activation, and API access. + +## Select channels and marks + +Channels accept a typed field, accessor, or immutable expression. Bare strings +name fields. Wrap literal strings such as fixed colors in `constant(...)`. + +Compose chart families from marks rather than named chart wrappers: + +- `Bar`, `Line`, `Area`, and `Point` for Cartesian plots. +- `Arc` for pie, donut, and bounded gauge compositions. +- `Cell` for heatmaps. +- `Rect` with `partition(...)` for hierarchical rectangles. +- `Rule`, `Point`, and `Text` for timelines. + +## Use scales and transforms deliberately + +Let inferred scales and axes handle a simple composition. Add explicit named +`Scale`, `Axis`, `Grid`, `Legend`, or `Tooltip` descriptors when domains, units, +time zones, or interaction differ. Use `utc` when calendar boundaries must not +depend on viewer locale. Use `symlog` when signed values or zero matter; a log +scale accepts strictly positive data only. + +Use immutable expressions for binning, grouping, aggregation, stacking, +normalization, moving windows, and regression. Use mark-local `filterRows`, +`sortRows`, or `partition` without mutating source rows. + +## Preserve data truth + +Finite negative values remain negative. `null`, `undefined`, invalid dates, +and non-finite numbers are missing, not zero. Aggregates skip missing numeric +inputs, and log scales omit zero and negative values. Enable diagnostics and +write a summary that reports omitted rows when omission affects interpretation. + +Normalize transport date strings and models before passing them to the plot. +Do not repair malformed data inside a visual channel accessor. diff --git a/public/skills/charts/references/styling-and-rendering.md b/public/skills/charts/references/styling-and-rendering.md new file mode 100644 index 0000000..daf6c76 --- /dev/null +++ b/public/skills/charts/references/styling-and-rendering.md @@ -0,0 +1,55 @@ +# Styling and rendering + +## Contents + +- Load chart styles once +- Size responsively +- Customize public tokens +- Understand SSR and hydration +- Respect renderer limits + +## Load chart styles once + +Import chart styles once at the application stylesheet boundary: + +```css +@import '@askrjs/charts/styles'; +``` + +The chart stylesheet is self-contained and adopts compatible Askr theme values +when present. A JavaScript import does not replace the CSS side effect. + +## Size responsively + +Mounted plots observe their container. `width` is an SSR and initial-layout +fallback, not a fixed mounted width. Give the container a real size and use a +deterministic height where layout shift matters. Test narrow containers, high +zoom, long labels, and device-pixel-ratio changes. + +## Customize public tokens + +Prefer `--ak-chart-*` tokens for height, gap, padding, radius, typography, +series colors, surfaces, borders, focus, selection, crosshair, and motion. Use +stable `data-slot="plot-*"` hooks only when tokens are insufficient. Do not +style generated scene IDs or assume canvas geometry exists as DOM. + +Check series contrast in light and dark themes. Preserve non-color encodings +and focus visibility after customization. + +## Understand SSR and hydration + +SSR emits a reserved region and semantic title, description, legend, summary, +empty state, and keyboard/data instructions. It does not emit graphical marks +or an SVG fallback. After hydration the plot mounts Canvas rendering, and the +transformed DOM table is materialized only when the user opens “View data.” + +Without JavaScript, essential semantic context remains but graphics do not. +Design the surrounding feature accordingly and verify hydration does not shift +the region unexpectedly. + +## Respect renderer limits + +Canvas 2D is the mounted renderer. SVG is export-only. There is no public +WebGL, worker, OffscreenCanvas, or custom-renderer surface. Motion is progressive +enhancement, and reduced-motion preferences disable nonessential transitions +without hiding state changes. diff --git a/public/skills/control-flows/SKILL.md b/public/skills/control-flows/SKILL.md new file mode 100644 index 0000000..ea554bd --- /dev/null +++ b/public/skills/control-flows/SKILL.md @@ -0,0 +1,39 @@ +--- +name: control-flows +description: Build, change, debug, or review reactive control flow in Askr components. Use for Show, Case, Match, For, keyed collections, positional lists, fallbacks, conditional rendering, reactive row state, selector, thunk props, or render-scoped ordering errors. +--- + +# Build Askr control flow + +Use Askr's control primitives when a branch or collection needs explicit +mounting, cleanup, fallback, identity, or reconciliation behavior. Do not +import the control-flow model of another framework. + +## Verify the installed contract + +Read the installed `@askrjs/askr/control` declarations and the root declaration +for `selector` before using an API. The canonical control subpath exports +`Show`, `Case`, `Match`, and `For`. Treat their render-scoped ordering and row +identity rules as lifecycle contracts, not syntax preferences. + +## Select the needed references + +- Read [branches.md](references/branches.md) for `Show`, `Case`, `Match`, + truthy narrowing, fallbacks, and branch lifecycle. +- Read [collections.md](references/collections.md) for `For`, stable keys, + positional identity, indexes, row reconciliation, `selector`, and thunk + props. +- Read [render-order.md](references/render-order.md) when nesting controls, + conditionally calling primitives, diagnosing hook-order errors, or reasoning + about mounting and cleanup. + +These references compose. A conditional keyed list needs all three; a simple +single branch may need only branches and render order. + +## Verify behavior + +Exercise every reachable branch and fallback. For collections, prove insertion, +removal, reorder, replacement, empty fallback, duplicate-key diagnostics, and +row-local state preservation as applicable. Verify mounting and cleanup rather +than checking markup alone. When SSR or hydration is involved, confirm the +initial branch and item identities agree before and after hydration. diff --git a/public/skills/control-flows/agents/openai.yaml b/public/skills/control-flows/agents/openai.yaml new file mode 100644 index 0000000..3999bd6 --- /dev/null +++ b/public/skills/control-flows/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: 'Build Askr Control Flows' + short_description: 'Render branches and keyed collections safely' + default_prompt: 'Use $control-flows to implement this Askr component with explicit branches, stable collection identity, and valid render order.' diff --git a/public/skills/control-flows/references/branches.md b/public/skills/control-flows/references/branches.md new file mode 100644 index 0000000..6faef88 --- /dev/null +++ b/public/skills/control-flows/references/branches.md @@ -0,0 +1,69 @@ +# Conditional branches + +## Contents + +- Choose a branch primitive +- Use Show +- Use Case and Match +- Own fallback states + +## Choose a branch primitive + +Use ordinary JavaScript to compute values. Use an Askr control boundary when +rendered content must mount, unmount, clean up, or retain an explicit position +in the render-scoped sequence. + +- Use `Show` for one truthy branch with an optional fallback. +- Use `Case` with direct `Match` children for ordered, mutually exclusive + branches with an optional fallback. + +Import them from the canonical subpath: + +```tsx +import { Case, Match, Show } from '@askrjs/askr/control'; +``` + +## Use Show + +`Show` accepts a value or getter through `when`. Its function-child form +receives the truthy, narrowed value: + +```tsx + currentUser()} fallback={}> + {(user) => } + +``` + +Use the function form when the branch consumes the narrowed value or should +delay creating nested control primitives until active. Keep loading, empty, +failure, and unauthorized states distinct rather than treating all falsy data +as one generic fallback. + +## Use Case and Match + +`Case` renders the first truthy `Match` in declaration order: + +```tsx +}> + + + + + + + + + + +``` + +`Match` is valid only as a direct child of `Case`; the runtime rejects other +placement and rejects non-`Match` children in a `Case`. Order overlapping +conditions from most specific to least specific. + +## Own fallback states + +A fallback is part of the user-visible contract. Use it for the actual state +that remains when no branch matches, not as a silent substitute for missing +data or an unexpected status. When no rendered fallback is correct, make that +choice deliberate and still verify cleanup of the branch that disappeared. diff --git a/public/skills/control-flows/references/collections.md b/public/skills/control-flows/references/collections.md new file mode 100644 index 0000000..206e8d0 --- /dev/null +++ b/public/skills/control-flows/references/collections.md @@ -0,0 +1,71 @@ +# Collections + +## Contents + +- Use For +- Choose identity +- Understand row reactivity +- Render indexes and fallbacks + +## Use For + +Import `For` from `@askrjs/askr/control`. Pass an array or reactive source to +`each` and choose exactly one identity mode: + +```tsx + projects()} by={(project) => project.id}> + {(project) => } + +``` + +`For` reconciles rows by identity, moving, inserting, removing, or replacing +owned DOM without rebuilding every stable row. + +## Choose identity + +Prefer `by={(item) => item.id}` with a stable string or number key. Keys must +be unique and retain their type; numeric `1` and string `"1"` are distinct. +Changing a key means changing row identity and therefore remounting that row. + +Use `byIndex={true}` only when positional identity is intentional, such as a +fixed sequence whose items have no durable identity and are not meaningfully +reordered. `by` and `byIndex` are mutually exclusive, and one is required. + +## Understand row reactivity + +The row callback runs when a row is created or reconciled; it is not a general +reactive scope. A changing parent value captured as a plain closure can be +snapshotted for an existing row. + +Use `selector()` for keyed membership such as selection: + +```tsx +import { selector } from '@askrjs/askr'; +import { For } from '@askrjs/askr/control'; + +const isSelected = selector(() => selectedId()); + + projects()} by={(project) => project.id}> + {(project) => ( + + )} +; +``` + +Declare `selector()` during component render. When only one DOM property needs +the changing value, pass a function-valued prop so the renderer can reevaluate +that property: + +```tsx +
  • (selectedId() === project.id ? 'true' : 'false')} /> +``` + +Do not assume a plain captured parent `state()`, `derive()`, or getter will +rerun every stable row. + +## Render indexes and fallbacks + +The row callback's second argument is an index getter, not a fixed number. Read +it when rendering a position that must follow reordering. Supply `fallback` +when an empty collection needs visible content, and distinguish a truly empty +successful result from a collection that is still loading or failed. diff --git a/public/skills/control-flows/references/render-order.md b/public/skills/control-flows/references/render-order.md new file mode 100644 index 0000000..11c011a --- /dev/null +++ b/public/skills/control-flows/references/render-order.md @@ -0,0 +1,58 @@ +# Render order and lifecycle + +## Contents + +- Preserve render-scoped order +- Nest controls safely +- Understand component boundaries +- Diagnose failures + +## Preserve render-scoped order + +`state()`, `derive()`, lifecycle operations, `For`, `Show`, `Case`, and other +primitives retaining render-owned state must be evaluated in the same order on +every render. Do not place the primitive call itself behind a changing `if`, +ternary, `&&`, or loop. + +This is unsafe because the `For` boundary disappears from the render sequence: + +```tsx +return open() ? ( + projects()} by={(project) => project.id}> + {(project) => } + +) : null; +``` + +## Nest controls safely + +Keep the outer retained boundary unconditional and move the changing branch +inside it: + +```tsx + open()}> + {() => ( + projects()} by={(project) => project.id}> + {(project) => } + + )} + +``` + +Use `Case` and direct `Match` children when several branches share that +position. This preserves identity, mount/unmount order, and cleanup ownership. + +## Understand component boundaries + +The invariant applies to primitives evaluated in the current component's +render scope. An ordinary JSX child component owns its own internal sequence; +selecting that child with normal JavaScript does not conditionally call the +child's internal hooks in the parent. + +## Diagnose failures + +When the runtime reports a changed render-scoped sequence, compare consecutive +renders and find the first conditional hook or eager control boundary. Restore +an unconditional call order and express the changing UI through `Show` or +`Case`. Do not catch or suppress the invariant error; it identifies lost +render ownership that can otherwise corrupt lifecycle and reconciliation. diff --git a/public/skills/i18n/SKILL.md b/public/skills/i18n/SKILL.md new file mode 100644 index 0000000..f35bbe9 --- /dev/null +++ b/public/skills/i18n/SKILL.md @@ -0,0 +1,39 @@ +--- +name: i18n +description: Add, change, debug, or review internationalization in an Askr application. Use for @askrjs/i18n, createI18n, typed message catalogs, locale selection, text direction, Intl formatting, locale routes, SSR locale isolation, dehydration, or hydration. +--- + +# Internationalize an Askr application + +Use `@askrjs/i18n` as an application-owned, lexically scoped translation +service. Keep locale policy explicit: the package validates aligned catalogs +and installs locale state, but it does not choose locales, parse ICU messages, +or create process-global state. + +## Verify the installed contract + +Read the installed `@askrjs/i18n` declarations before designing catalogs or +hydration. The source locale defines the exact message keys and argument tuples +every other locale must implement. Missing, extra, or incompatible messages +are type and runtime contract failures, not fallbacks. + +## Select the needed references + +- Read [catalogs-and-formatting.md](references/catalogs-and-formatting.md) when + defining messages, parameters, numbers, dates, currencies, or plural logic. +- Read [locale-ownership.md](references/locale-ownership.md) when resolving a + locale from routes, hosts, cookies, profiles, or user choice and when + deciding fallback policy. +- Read [scope-and-hydration.md](references/scope-and-hydration.md) for lexical + scopes, direction, request isolation, SSR, SSG, dehydration, and hydration. +- Compose the sibling [routes skill](../routes/SKILL.md) when locale is URL + state, and the sibling [themes skill](../themes/SKILL.md) when direction or + locale changes layout and visual presentation. + +## Verify localized behavior + +Exercise every supported locale with representative short, long, missing, +zero, singular, plural, negative, and large values relevant to the product. +Check direction, document language, metadata, navigation, formatting, overflow, +focus order, and server/client parity. Verify concurrent server requests do not +share locale state and hydration does not flash or replace the server locale. diff --git a/public/skills/i18n/agents/openai.yaml b/public/skills/i18n/agents/openai.yaml new file mode 100644 index 0000000..3284848 --- /dev/null +++ b/public/skills/i18n/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: 'Internationalize an Askr App' + short_description: 'Own typed locales, direction, and hydration' + default_prompt: 'Use $i18n to add typed, application-owned localization to this Askr application.' diff --git a/public/skills/i18n/references/catalogs-and-formatting.md b/public/skills/i18n/references/catalogs-and-formatting.md new file mode 100644 index 0000000..cc15d1f --- /dev/null +++ b/public/skills/i18n/references/catalogs-and-formatting.md @@ -0,0 +1,66 @@ +# Catalogs and formatting + +## Contents + +- Define the source contract +- Write messages as functions +- Format locale-sensitive values +- Keep catalogs maintainable + +## Define the source contract + +Create one application-owned service, normally at module scope: + +```ts +import { createI18n } from '@askrjs/i18n'; + +export const i18n = createI18n('en', { + en: { + welcome: ({ name }: { name: string }) => `Welcome, ${name}`, + projectCount: (count: number) => `${count} projects`, + }, + fr: { + welcome: ({ name }: { name: string }) => `Bienvenue, ${name}`, + projectCount: (count: number) => `${count} projets`, + }, +}); +``` + +The source locale's exact keys and argument tuples are required from every +other catalog. Do not use casts or broad record annotations to hide a catalog +mismatch. + +## Write messages as functions + +Catalog values are ordinary typed functions returning strings. Pass named +objects for messages with several values so call sites remain legible. Keep +markup and components outside the catalog unless the installed return contract +expands beyond strings. + +Use `i18n.text(key, ...args)` inside an active scope. Use +`i18n.format(locale, key, ...args)` only when explicitly formatting for a +locale other than the active one, such as a locale preview or generated export. + +## Format locale-sensitive values + +The package does not parse ICU messages or choose number, date, relative-time, +list, or plural rules. Implement those semantics explicitly with `Intl` inside +catalog functions or application-owned formatting helpers: + +```ts +total: (value: number) => + new Intl.NumberFormat('fr-FR', { + style: 'currency', + currency: 'EUR', + }).format(value), +``` + +Do not translate by concatenating separately translated fragments around +values; grammar and word order belong to each locale's complete message. + +## Keep catalogs maintainable + +Organize catalogs by stable product concepts rather than page copy order. +Reuse typed argument shapes where they improve consistency, but do not create a +dynamic untyped message registry. Treat a renamed key or changed argument tuple +as a contract change across every locale. diff --git a/public/skills/i18n/references/locale-ownership.md b/public/skills/i18n/references/locale-ownership.md new file mode 100644 index 0000000..5e66383 --- /dev/null +++ b/public/skills/i18n/references/locale-ownership.md @@ -0,0 +1,47 @@ +# Locale ownership + +## Contents + +- Resolve locale explicitly +- Decide fallback policy +- Keep locale in navigable state +- Preserve user intent + +## Resolve locale explicitly + +The application chooses the locale. Resolve it from an explicit source such as +a URL prefix, host, persisted preference, authenticated profile, or accepted +request language. Establish precedence in one composition boundary rather than +letting components guess independently. + +Validate the candidate against the catalog locale keys before installing it. +Do not pass an unchecked cookie, header, parameter, or storage string into the +scope. + +## Decide fallback policy + +`createI18n` does not perform missing-key or locale fallback. Decide what +happens when no supported locale matches: redirect to a default locale, select +the source locale, or return an explicit not-found response according to the +product's URL and indexing policy. + +Do not silently mix catalogs. Every supported catalog must satisfy the source +contract. + +## Keep locale in navigable state + +When locale changes the canonical URL, model it through the route tree and use +typed destinations for locale switching. Preserve the equivalent destination, +parameters, and meaningful search state where possible. Ensure canonical and +alternate metadata reflect the actual localized URLs owned by the application. + +When locale is preference-only, keep URL, cookie, profile, and hydration policy +consistent so refresh does not choose a different locale. + +## Preserve user intent + +Do not repeatedly override an explicit user choice with browser detection. +Changing locale must update visible text, formatting, direction, document +language, and persisted preference as one narratable action. Avoid a selector +that appears to change locale while navigation or server refresh restores the +old value. diff --git a/public/skills/i18n/references/scope-and-hydration.md b/public/skills/i18n/references/scope-and-hydration.md new file mode 100644 index 0000000..4b87abd --- /dev/null +++ b/public/skills/i18n/references/scope-and-hydration.md @@ -0,0 +1,55 @@ +# Scope and hydration + +## Contents + +- Install lexical locale state +- Align language and direction +- Isolate server requests +- Dehydrate and hydrate + +## Install lexical locale state + +Wrap the owned subtree with the service's `Scope`: + +```tsx + + + +``` + +Inside that lexical scope, `i18n.text`, `i18n.locale`, `i18n.direction`, and +`i18n.catalog` read the active selection. Nested scopes may intentionally +select another locale, but avoid accidental mixed-language regions. + +## Align language and direction + +Supply `dir="ltr"` or `dir="rtl"` according to the locale policy. Keep the +document renderer's `html.lang` and `html.dir`, route metadata, theme direction, +and the i18n scope aligned. Do not infer direction from translated text or use +CSS mirroring as a substitute for correct document semantics. + +Audit logical spacing, icon direction, tables, charts, form order, and +navigation in RTL rather than assuming text alignment is the only change. + +## Isolate server requests + +The service has no process-global active locale; selection is installed through +the scope. Resolve and install locale within each SSR request or SSG entry. +Never store the current request's locale in a mutable module-level variable. + +## Dehydrate and hydrate + +After installing the server scope, `i18n.dehydrate()` returns an immutable +versioned snapshot containing locale, direction, and selected catalog identity. +Embed that snapshot safely in the rendered document. On the client, install it +with the mutually exclusive hydration form: + +```tsx + + + +``` + +Do not pass `locale` or `dir` alongside `hydration`. Verify the server and +client use the same catalog set and that the first hydrated render preserves +text, language, direction, and layout without a flash or mismatch. diff --git a/public/skills/icons/SKILL.md b/public/skills/icons/SKILL.md new file mode 100644 index 0000000..be627ca --- /dev/null +++ b/public/skills/icons/SKILL.md @@ -0,0 +1,55 @@ +--- +name: icons +description: Select, import, compose, style, or review icons and brand logos in an Askr application. Use for @askrjs/lucide, @askrjs/logos, icon-only controls, accessible SVG semantics, semantic icon sizes, theme icon tokens, custom icons, or tree-shakeable icon imports. +--- + +# Use icons in Askr + +Choose an icon for communication, not decoration by default, and preserve the +shared SVG contract so accessibility and themes behave consistently. + +## Establish ownership + +- `@askrjs/askr/foundations/icon` owns the shared SVG prop and markup contract. +- `@askrjs/lucide` supplies generated Askr components for general interface + icons. +- `@askrjs/logos` supplies the small published set of brand marks. +- `@askrjs/themes` styles the shared hooks and provides semantic size and + stroke tokens. +- The application owns icon choice, accessible naming, surrounding control + semantics, and any product-specific SVG. + +## Verify the installed surface + +Check the installed package exports and declarations before choosing a name or +subpath. Neither icon package provides a string-based `` +component or runtime registry. Import the exact named component you use. Do not +infer an export from the upstream Lucide site or from an example built against +a different package version. + +## Select the needed references + +- Read [lucide.md](references/lucide.md) when choosing and importing general + interface icons from `@askrjs/lucide`. +- Read [logos.md](references/logos.md) when choosing, importing, naming, or + styling brand marks from `@askrjs/logos`. +- Read [accessibility-and-controls.md](references/accessibility-and-controls.md) + when an icon conveys meaning, labels a control, appears beside text, or + participates in feedback and navigation. +- Read [sizing-and-custom-icons.md](references/sizing-and-custom-icons.md) when + applying semantic sizes, theme tokens, colors, stroke widths, or authoring a + custom icon or logo wrapper. +- Compose the sibling [themes skill](../themes/SKILL.md) for broader token and + component styling decisions. + +Lucide icons and logos share the foundation contract but serve different +purposes. Do not substitute a general icon for a trademark or treat a brand +logo as ordinary interface decoration. + +## Verify the result + +Inspect rendered SVG attributes, not only appearance. Verify decorative and +meaningful variants with a screen reader, icon-only controls by keyboard, and +all icons in light, dark, forced-color, narrow, and high-zoom contexts relevant +to the application. Confirm the production bundle does not pull in an icon +registry or unrelated icon set merely to render a few named icons. diff --git a/public/skills/icons/agents/openai.yaml b/public/skills/icons/agents/openai.yaml new file mode 100644 index 0000000..3faea17 --- /dev/null +++ b/public/skills/icons/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: 'Use Askr Icons' + short_description: 'Choose and compose accessible Askr icons' + default_prompt: 'Use $icons to select and compose accessible, theme-aligned icons for this Askr interface.' diff --git a/public/skills/icons/references/accessibility-and-controls.md b/public/skills/icons/references/accessibility-and-controls.md new file mode 100644 index 0000000..210ddb4 --- /dev/null +++ b/public/skills/icons/references/accessibility-and-controls.md @@ -0,0 +1,56 @@ +# Accessibility and controls + +## Contents + +- Decide whether the SVG is decorative +- Name icon-only controls +- Compose icons with text +- Preserve interaction semantics + +## Decide whether the SVG is decorative + +Generated icons and logos are decorative by default. With no `title`, they +render `aria-hidden="true"` and `data-decorative="true"`. Keep that default +when adjacent text or the owning control already communicates the meaning. + +Pass `title` when the standalone SVG itself conveys meaningful content: + +```tsx + +``` + +This removes `aria-hidden` and renders an SVG ``. Do not give a +decorative icon a redundant title that causes a screen reader to repeat the +visible label. + +## Name icon-only controls + +Name the interactive element, not only the SVG: + +```tsx +<Button aria-label="Search" size="icon"> + <SearchIcon /> +</Button> +``` + +Keep the nested icon decorative because the button supplies the accessible +name. Use a stable action name rather than a visual description such as +“magnifying glass.” A tooltip may help sighted users discover the action, but +it does not replace a reliable accessible name on the control. + +## Compose icons with text + +When a button, link, alert, or navigation item already contains visible text, +leave its supporting icon decorative. Preserve a sensible DOM reading order +and do not hide the text at narrow widths unless the remaining control still +has an accessible name and discoverable purpose. + +For status and error messages, icons reinforce but never solely encode the +state. Pair color and shape with text users can understand and act upon. + +## Preserve interaction semantics + +An SVG is not a button or link. Place it inside the published interactive +primitive that owns focus, keyboard behavior, disabled state, and activation. +Do not attach click behavior directly to a bare icon. Verify focus visibility, +target size, accessible name, disabled behavior, and high-contrast rendering. diff --git a/public/skills/icons/references/logos.md b/public/skills/icons/references/logos.md new file mode 100644 index 0000000..50f21ef --- /dev/null +++ b/public/skills/icons/references/logos.md @@ -0,0 +1,51 @@ +# Brand logos + +## Contents + +- Choose an included logo +- Import exact components +- Preserve brand treatment +- Name meaningful marks + +## Choose an included logo + +Use `@askrjs/logos` for an authorized brand mark, not for general interface +concepts. The installed package currently determines which marks are available. +At version 0.2.0 it exports Apple, Facebook, GitHub, Google, and Microsoft logo +components. Verify the installed declarations rather than assuming another +brand is present. + +Do not approximate a missing trademark with a Lucide interface icon. Use the +authorized asset supplied by the product owner when the package does not +contain the required brand. + +## Import exact components + +Import a named logo from the package root: + +```tsx +import { GitHubLogo, GoogleLogo } from '@askrjs/logos'; +``` + +Focused subpaths such as `@askrjs/logos/logos/github` are also published. +Verify the installed export before using one. The package has no runtime logo +registry or string-selected logo component. + +## Preserve brand treatment + +Logo geometry and color are brand assets, not ordinary theme decoration. +Apple and GitHub currently inherit `currentColor`; Facebook, Google, and +Microsoft retain fixed brand colors. Verify the installed implementation and +applicable brand rules before recoloring, reshaping, combining, or animating a +mark. + +Keep enough space and contrast around a logo, and do not use a logo as an +unexplained action symbol. + +## Name meaningful marks + +When a logo is the only content identifying a destination or organization, +give the owning link or control an accessible name and decide whether the SVG +should remain decorative. When the standalone logo is meaningful content, +pass its `title`. Avoid making assistive technology announce the brand twice +when adjacent text already names it. diff --git a/public/skills/icons/references/lucide.md b/public/skills/icons/references/lucide.md new file mode 100644 index 0000000..7866df6 --- /dev/null +++ b/public/skills/icons/references/lucide.md @@ -0,0 +1,52 @@ +# Lucide icons + +## Contents + +- Choose an interface icon +- Import exact components +- Keep meanings consistent +- Preserve bundle behavior + +## Choose an interface icon + +Use `@askrjs/lucide` for general interface concepts such as search, menu, +close, edit, status, or directional navigation. Prefer visible words when an +action is unfamiliar, consequential, or ambiguous. An icon should reinforce +understanding rather than make the user decode a novel symbol. + +Choose by meaning, not visual resemblance alone. Use one symbol for one meaning +throughout the application and do not mix unrelated icon families without a +product reason. + +## Import exact components + +Import the named component exposed by the installed package: + +```tsx +import { MenuIcon, SearchIcon } from '@askrjs/lucide'; +``` + +The package also publishes focused paths such as: + +```tsx +import { SearchIcon } from '@askrjs/lucide/icons/search'; +``` + +Verify the installed export before using it. Do not assume every name on the +upstream Lucide site exists in the installed Askr package version. + +## Keep meanings consistent + +Do not reuse the same icon for conflicting actions. Pair icons with visible +labels until the action is genuinely conventional in its context. For status +and feedback, pair shape and color with text; an icon alone must not carry the +only explanation of success, warning, or failure. + +## Preserve bundle behavior + +`@askrjs/lucide` is a generated static binding layer with named exports and +individual icon subpaths. It does not ship a runtime registry or a string-based +`<Icon name="..." />` API. Keep component selection explicit in source so +missing names fail during development and bundlers can eliminate unused icons. +Do not generate import paths from user input or import the whole catalog merely +to render a dynamic name. diff --git a/public/skills/icons/references/sizing-and-custom-icons.md b/public/skills/icons/references/sizing-and-custom-icons.md new file mode 100644 index 0000000..6cf2725 --- /dev/null +++ b/public/skills/icons/references/sizing-and-custom-icons.md @@ -0,0 +1,63 @@ +# Sizing and custom icons + +## Contents + +- Use the shared SVG contract +- Prefer semantic sizes +- Style through theme tokens +- Integrate custom SVGs +- Treat logos carefully + +## Use the shared SVG contract + +Official icons and logos render through the same foundation and expose: + +- `data-slot="icon"` for theme targeting; +- `data-icon` for the exact asset identity; +- `data-size` for named sizes; +- `data-decorative="true"` when no title is supplied; +- `data-color="current"` when color inherits `currentColor`. + +Their public props include `size`, `strokeWidth`, `color`, `title`, `class`, +and `style` along with compatible SVG props. Verify the installed declaration +before passing additional attributes. + +## Prefer semantic sizes + +Use `sm`, `md`, `lg`, or `xl` when an icon participates in the application +design system. Named sizes resolve through theme variables. Use a numeric or +CSS string size only when a real illustration or integration constraint falls +outside that semantic scale. + +Let the owning themed component size ordinary child icons when it documents +that behavior. Avoid specifying a competing literal size on every icon inside +buttons, badges, menu items, or sidebars. + +## Style through theme tokens + +Theme defaults use `--ak-icon-size-sm|md|lg|xl` and +`--ak-icon-stroke-width-sm|md|lg|xl`. Override those semantic tokens for a +coherent application-wide icon scale. Use the lower-level `--ak-icon-size` and +`--ak-icon-stroke-width` only for a deliberately scoped exception. + +Prefer `currentColor` for interface icons so state and contrast follow the +owning component. Recheck contrast and forced-color behavior after custom +color overrides. + +## Integrate custom SVGs + +Prefer `IconBase` from `@askrjs/askr/foundations/icon` when building an +application-owned icon component so sizing, title, decorative state, refs, and +public hooks remain aligned. Ensure the SVG uses a stable view box and does +not embed unsafe remote references or depend on internal theme DOM. + +If a third-party SVG cannot use `IconBase`, make it emit the same public hooks +and accessibility behavior before placing it beside official icons. A raw SVG +that lacks `data-slot="icon"` will not receive automatic theme sizing. + +## Treat logos carefully + +Logo components share icon sizing and accessibility props but may intentionally +retain fixed brand colors. Apple and GitHub currently inherit `currentColor`; +Facebook, Google, and Microsoft retain their published colors. Verify the +installed package and applicable brand rules before modifying a logo. diff --git a/public/skills/project-structures/SKILL.md b/public/skills/project-structures/SKILL.md new file mode 100644 index 0000000..d5dd636 --- /dev/null +++ b/public/skills/project-structures/SKILL.md @@ -0,0 +1,54 @@ +--- +name: project-structures +description: Structure or review an Askr project using explicit ownership and dependency direction. Use when placing pages, layouts, features, components, adapters, queries, mutations, browser entry points, server code, or deciding when a small application should introduce another layer. +--- + +# Structure an Askr project + +Make the codebase explain who owns each route, capability, external boundary, +state transition, and cleanup path without relying on inferred file magic. + +## Preserve dependency direction + +```text +pages -> features -> components -> Askr primitives +``` + +Pages connect routes to features. Features own coherent product capabilities +and visible states. Components are reusable application UI with explicit +props. Components do not import features, and features do not import pages. + +For external or mutable data: + +```text +features -> queries and mutations -> services -> adapters +``` + +Queries and mutations call application services. Services map transport data +into application models. Adapters remain raw boundaries to generated clients, +`fetch`, storage, SDKs, or other systems. Reusable components do not fetch +application data, and pages do not call transport clients directly. + +## Select the needed references + +- Read [application-structure.md](references/application-structure.md) when + placing pages, layouts, features, components, entries, or server code. +- Read [data-boundaries.md](references/data-boundaries.md) when a feature reads + external data or needs a service and adapter boundary. +- Compose the sibling + [queries and mutations skill](../queries-and-mutations/SKILL.md) for query + definitions, cache identity, consistency, invalidation, mutations, and + server hydration. +- Compose the sibling [routes skill](../routes/SKILL.md) for the contents of + `_routes.tsx`, `_layout.tsx`, route inputs, loaders, and access. +- Compose the sibling [themes skill](../themes/SKILL.md) for the primitive and + visual layers below application components. +- Compose the sibling [control flows skill](../control-flows/SKILL.md) for + conditional branches, keyed collections, and retained render boundaries. + +## Keep structure proportional + +The directory model is a responsibility map, not a folder quota. Do not add an +adapter, query, mutation, nested route group, or server layer until a real +boundary needs it. Once a boundary exists, name and locate its owner explicitly +rather than collapsing responsibilities into a page or generic utility file. diff --git a/public/skills/project-structures/agents/openai.yaml b/public/skills/project-structures/agents/openai.yaml new file mode 100644 index 0000000..1acb04a --- /dev/null +++ b/public/skills/project-structures/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: 'Structure an Askr Project' + short_description: 'Place Askr code behind explicit owners' + default_prompt: 'Use $project-structures to organize this Askr project with explicit ownership and dependency direction.' diff --git a/public/skills/project-structures/references/application-structure.md b/public/skills/project-structures/references/application-structure.md new file mode 100644 index 0000000..3c5fcd2 --- /dev/null +++ b/public/skills/project-structures/references/application-structure.md @@ -0,0 +1,87 @@ +# Application structure + +## Contents + +- Choose an application mode +- Use the responsibility map +- Keep pages thin +- Own state and cleanup + +## Choose an application mode + +- SPA for browser-owned routing and rendering. +- SSR when the server must render the initial route. +- SSG when routes can become static files at build time. +- API when the project owns HTTP contracts without browser UI. +- Full stack when browser routes and server APIs belong to one application. + +Choose the smallest mode that owns the requested behavior. + +## Use the responsibility map + +```text +src/ + pages/ + _routes.tsx + _layout.tsx + not-found.tsx + public/ + _routes.tsx + _layout.tsx + home.tsx + app/ + _routes.tsx + _layout.tsx + dashboard.tsx + features/ + projects/ + project-list.tsx + queries.ts + mutations.ts + services/ + projects-service.ts + adapters/ + projects-client.ts + components/ + project-card.tsx + main.tsx + server.ts +``` + +Keep a small application smaller. Retain `pages/_routes.tsx` and +`pages/_layout.tsx` as the readable root of a browser application's route tree. +Add a nested page group only when it needs its own route prefix, layout, access +boundary, or navigation context. + +The browser entry owns mounting or hydration. A server entry owns server +composition. Neither should become a dumping ground for feature behavior. + +## Keep pages thin + +A page owns route-facing inputs, metadata, access, and composition. It normally +delegates product behavior and visible states to a feature: + +```tsx +import { PricingFeature } from '../../features/pricing/pricing'; + +export default function PricingPage() { + return <PricingFeature />; +} +``` + +A feature may be a small static composition. A dynamic feature also owns its +loading, empty, failure, refresh, cancellation, and success states. An +application component accepts explicit props and remains reusable without +knowing which page or feature rendered it. + +## Own state and cleanup + +Create state in the component that owns the interaction. Derive display values +instead of copying them into another cell. Compose the sibling +[control flows skill](../../control-flows/SKILL.md) when rendering branches or +collections rather than importing another framework's lifecycle model. + +Start browser-only work in the documented lifecycle boundary, not during +render. Keep cancellation and cleanup beside the timer, subscription, request, +observer, or external resource that created it. Preserve actionable Askr error +categories instead of swallowing them into a generic failure. diff --git a/public/skills/project-structures/references/data-boundaries.md b/public/skills/project-structures/references/data-boundaries.md new file mode 100644 index 0000000..32a6aa9 --- /dev/null +++ b/public/skills/project-structures/references/data-boundaries.md @@ -0,0 +1,45 @@ +# Data boundaries + +## Contents + +- Decide whether layers are needed +- Adapters +- Services +- Server boundaries + +## Decide whether layers are needed + +Skip services and adapters for a static feature with no external data. Add +them when their distinct ownership is real: + +```text +features -> queries and mutations -> services -> adapters +``` + +## Adapters + +An adapter is the raw boundary to something the application does not own: a +generated client, `fetch`, browser storage, an SDK, or another service. It owns +transport calls, cancellation, authentication attachment, response decoding, +and structured transport errors. It does not render UI or own reactive state. + +Prefer generated clients and validators when available. A TypeScript cast is +not runtime validation. Pass `AbortSignal` through cancellable operations. + +## Services + +An application service calls adapters and translates decoded transport values +into application vocabulary. It maps DTO names and shapes, composes transport +operations when necessary, and returns models queries and mutations can use. +It does not own component state, cache identity, or rendering. + +Queries and mutations call services, not raw transport adapters. Compose the +[queries and mutations skill](../../queries-and-mutations/SKILL.md) for their +runtime contract. + +## Server boundaries + +Validate request input and enforce authorization on the server path. Register +typed routes once and generate or check OpenAPI from the same runtime registry. +Browser validation improves experience but never becomes the authority for +access or correctness. diff --git a/public/skills/queries-and-mutations/SKILL.md b/public/skills/queries-and-mutations/SKILL.md new file mode 100644 index 0000000..4645a2a --- /dev/null +++ b/public/skills/queries-and-mutations/SKILL.md @@ -0,0 +1,56 @@ +--- +name: queries-and-mutations +description: Build, change, debug, or review Askr query and mutation data flow. Use for @askrjs/askr/data, defineQuery, createQuery, createMutation, query keys and scopes, consistency and reconciliation, invalidation, pending writes, data runtimes, server query handlers, prefetch, dehydration, or hydration. +--- + +# Build Askr queries and mutations + +Use the data runtime for application data that needs stable cache identity, +reactive read states, invalidation after writes, consistency checks, or shared +identity across server rendering and browser hydration. + +## Preserve the application boundary + +```text +feature -> query or mutation -> service -> adapter +``` + +Queries and mutations own reactive data state and cache effects. Services map +application operations and models. Adapters remain raw transport boundaries. +Do not fetch directly from reusable components or place transport mapping in a +query's UI consumer. + +Skip this layer for static content or lifecycle-local async work that does not +need cache identity, invalidation, or server/client continuity. + +## Verify the installed data API + +Read the installed `@askrjs/askr/data` declarations before using an option or +state field. Query state is a discriminated contract, not a generic +`data/isLoading/error` tuple. Do not infer semantics from another query +library. + +## Select the needed references + +- Read [definitions-and-keys.md](references/definitions-and-keys.md) for + reusable definitions, cache identity, scopes, runtimes, and ownership. +- Read [states-and-consistency.md](references/states-and-consistency.md) for + loading, fresh, refreshing, pending-write, stale reasons, consistency checks, + reconciliation, retry, and visible UI states. +- Read [mutations-and-invalidation.md](references/mutations-and-invalidation.md) + for mutation lifecycle, cancellation, affected prefixes, invalidation, and + bounded invalidation graphs. +- Read [server-prefetch-and-hydration.md](references/server-prefetch-and-hydration.md) + for server handlers, isolated runtimes, route prefetch, dehydration, + hydration, SSR, or SSG. +- Compose the sibling + [project structures skill](../project-structures/SKILL.md) for feature, + service, and adapter placement. + +## Verify behavior + +Exercise first load, fresh data, empty success, refresh with previous data, +first-load failure, refresh failure with previous data, inconsistent data, +aborted refresh, pending write, mutation failure, retry, cancellation, and +invalidation where applicable. For server rendering, prove the first browser +read adopts the hydrated cache identity without a duplicate request. diff --git a/public/skills/queries-and-mutations/agents/openai.yaml b/public/skills/queries-and-mutations/agents/openai.yaml new file mode 100644 index 0000000..9484f78 --- /dev/null +++ b/public/skills/queries-and-mutations/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: 'Build Askr Queries and Mutations' + short_description: 'Own cached reads, writes, and consistency' + default_prompt: 'Use $queries-and-mutations to implement this Askr data flow with stable identity, explicit states, and correct invalidation.' diff --git a/public/skills/queries-and-mutations/references/definitions-and-keys.md b/public/skills/queries-and-mutations/references/definitions-and-keys.md new file mode 100644 index 0000000..afe2791 --- /dev/null +++ b/public/skills/queries-and-mutations/references/definitions-and-keys.md @@ -0,0 +1,67 @@ +# Query definitions and keys + +## Contents + +- Define reusable queries +- Treat keys as contracts +- Build scoped keys +- Isolate runtimes deliberately + +## Define reusable queries + +Use `defineQuery` for a reusable contract. Its input determines a stable key +and is passed with an `AbortSignal` to the fetch function: + +```ts +import { defineQuery } from '@askrjs/askr/data'; +import { usersService } from '../../services/users-service'; + +export const userById = defineQuery({ + key: ({ id }: { id: string }) => `users:${id}`, + fetch: ({ id, signal }) => usersService.getById(id, { signal }), +}); +``` + +Create the reactive reader in the owning feature with +`createQuery(userById, input)`. Do not copy query data into component state +unless the user is editing a separate draft. + +Use inline `createQuery({ key, fetch, ... })` only when the contract is truly +local and will not be shared with server prefetch or another reader. + +## Treat keys as contracts + +Readers with the same key share cache state. The key therefore identifies not +only data but the query contract. Keep `fetch`, `isConsistent`, and `reconcile` +aligned for every reader of that key; development builds diagnose conflicting +definitions. + +Keys and invalidation prefixes must be stable and collision-safe. Do not +include incidental render state, non-serializable values, or ordering that can +change for the same data identity. + +## Build scoped keys + +Raw `invalidate(prefix)` uses literal string-prefix matching, so +`invalidate('user:1')` also matches `user:10`. Use explicit delimiters in +hand-built schemes or prefer `queryScope(namespace)` for structured feature +keys and prefixes: + +```ts +import { queryScope } from '@askrjs/askr/data'; + +export const users = queryScope('users'); +const key = users.key('detail', userId); +users.invalidate(['detail', userId]); +``` + +The namespace must remain non-empty after trimming. + +## Isolate runtimes deliberately + +The default runtime is sufficient for one browser application instance. Use +`createDataRuntime()` for tests, embedded or multi-root applications, server +requests, and static build entries that require isolated caches. Pass the same +runtime explicitly to queries, invalidation, prefetch, dehydration, and +hydration that belong to that owner. Never share a request cache between +unrelated users. diff --git a/public/skills/queries-and-mutations/references/mutations-and-invalidation.md b/public/skills/queries-and-mutations/references/mutations-and-invalidation.md new file mode 100644 index 0000000..5e08d5f --- /dev/null +++ b/public/skills/queries-and-mutations/references/mutations-and-invalidation.md @@ -0,0 +1,60 @@ +# Mutations and invalidation + +## Contents + +- Create mutations in the owning feature +- Render mutation lifecycle +- Invalidate affected reads +- Keep invalidation bounded + +## Create mutations in the owning feature + +Create a mutation while rendering the feature that owns the action. Give it a +stable key when tests or runtime-scoped overrides need to identify it. Its +`action` calls an application service and forwards the cancellation signal: + +```ts +import { createMutation } from '@askrjs/askr/data'; + +export function createSaveUserMutation() { + return createMutation({ + key: 'users/save', + action: (input: SaveUserInput, { signal }) => + usersService.save(input, { signal }), + affects: (input) => [`users:${input.id}`], + afterSuccess: 'invalidate', + }); +} +``` + +## Render mutation lifecycle + +Call `execute(input)` from the user action. A new execution aborts the previous +one. Use `status` to narrow idle, pending, success, and error; `pending`, +`result`, and `error` follow that status. Prevent duplicate user submission +where appropriate and expose failure, recovery, and success honestly. + +`abort()` cancels in-flight execution. `reset()` clears settled mutation state +to idle. Do not hide failed writes behind success or modify unrelated caches. + +## Invalidate affected reads + +`affects(input, result)` returns the query prefixes made stale by a successful +write. With `afterSuccess: 'invalidate'`, the runtime invalidates them and can +surface `pending-write` before confirmation refresh begins. Return the narrowest +complete prefixes that represent the changed data. + +Use `invalidate(prefix, { markPendingWrite: true })` for an explicit write +boundary outside automatic mutation handling. Use the same runtime as the +queries being invalidated. + +## Keep invalidation bounded + +Invalidation listeners run synchronously and may trigger a short acyclic +cascade. Re-entering an active prefix throws a cyclic-cascade diagnostic, and +changing-prefix cascades have a runtime depth limit. Keep the invalidation graph +small, narratable, and acyclic rather than relying on broad refresh storms. + +Use `invalidateOnInterval` only when periodic freshness is a demonstrated +requirement. Scope it by active routes, visibility, or focus as appropriate and +let component cleanup own its lifetime. diff --git a/public/skills/queries-and-mutations/references/server-prefetch-and-hydration.md b/public/skills/queries-and-mutations/references/server-prefetch-and-hydration.md new file mode 100644 index 0000000..bb88a28 --- /dev/null +++ b/public/skills/queries-and-mutations/references/server-prefetch-and-hydration.md @@ -0,0 +1,50 @@ +# Server prefetch and hydration + +## Contents + +- Register server handlers +- Prefetch at the route boundary +- Dehydrate and hydrate one identity +- Isolate request and build-entry caches + +## Register server handlers + +Pair each reusable query definition with its server implementation through +`serveQuery`, then collect entries with `defineServerQueries`. The handler +receives validated application input, the request when available, and an +`AbortSignal`. Keep request authentication and service dependencies on this +server path. + +The definition is shared identity; the server handler is not browser code. Do +not put secrets or server-only transport into the query definition shipped to +the client. + +## Prefetch at the route boundary + +Create an isolated runtime and a `createQueryPrefetchContext` with the server +registry, request, signal, and installed mode. Prefetch the same definition and +input the feature will later pass to `createQuery`. Route `preload` may use its +provided query-prefetch context directly. + +If the input or key differs between prefetch and render, hydration cannot adopt +the cached value and the browser will fetch again. + +## Dehydrate and hydrate one identity + +After prefetch, serialize the runtime with `dehydrateDataRuntime(runtime)` and +embed that JSON-safe payload in the rendered document. Before the first client +`createQuery` read, create the browser runtime and call +`hydrateDataRuntime(runtime, payload)`. + +The definition, input, key construction, and runtime passed to the browser +reader must match the server path. Verify that initial rendering adopts the +payload without a duplicate request, then that explicit refresh and +invalidation still use the browser runtime. + +## Isolate request and build-entry caches + +Create one runtime per SSR request. Never share it across users. For SSG, +create an isolated runtime per generated entry when entry data differs, then +embed only that entry's snapshot. Dehydration drops non-serializable cache +values; treat a missing value as a contract problem rather than silently +depending on client refetch. diff --git a/public/skills/queries-and-mutations/references/states-and-consistency.md b/public/skills/queries-and-mutations/references/states-and-consistency.md new file mode 100644 index 0000000..df1f045 --- /dev/null +++ b/public/skills/queries-and-mutations/references/states-and-consistency.md @@ -0,0 +1,51 @@ +# Query states and consistency + +## Contents + +- Render the state contract +- Keep previous data visible +- Model empty data +- Apply consistency and reconciliation + +## Render the state contract + +Read the discriminated fields directly: + +- `loading` means the first request has not produced data; `data` is `null`. +- `fresh` means committed data is current. +- `refreshing` keeps previous data while a confirming fetch runs. +- `pending-write` keeps previous data while a successful write is being + confirmed. +- `stale` is settled but not current, with reason `inconsistent`, `aborted`, or + `error`. + +Use `refresh()` for explicit retry or user refresh. Concurrent manual refresh +calls coalesce while work is pending. + +## Keep previous data visible + +A refresh failure can retain the last successful data. Keep that data visible, +explain that it is stale, and offer retry. A first-load failure has +`data === null` and needs a full failure state. An aborted refresh may also +retain stale data and should not be reported as an unexplained server failure. + +Treat `pending-write` as saved locally but still syncing. Do not claim remote +confirmation before the subsequent query state supports it. + +## Model empty data + +Queries reserve `null` for “no successful value yet.” Return an empty array or +an explicit result object for a successful empty response. Render that as a +normal empty state distinct from loading and failure. Do not return `null` or +`undefined` as successful query data. + +## Apply consistency and reconciliation + +Use `isConsistent(data)` when a fetched value must satisfy an application +invariant such as a minimum version. Use `reconcile(data, { key })` only when +the application has a real, bounded way to restore consistency. It may be +async, and its result is awaited before retry scheduling. + +A thrown consistency or reconciliation callback becomes a terminal stale +error. Keep both callbacks deterministic, aligned across readers of the same +key, and free of hidden UI behavior. diff --git a/public/skills/routes/SKILL.md b/public/skills/routes/SKILL.md new file mode 100644 index 0000000..3f74eb3 --- /dev/null +++ b/public/skills/routes/SKILL.md @@ -0,0 +1,40 @@ +--- +name: routes +description: Build, change, debug, or review routing in an Askr application. Use for route registries, nested layouts, paths and parameters, search state, typed destinations, navigation, loaders, deferred data, access policies, route metadata, fallbacks, base paths, SPA, SSR, or SSG route reuse. +--- + +# Build Askr routes + +Declare one explicit route tree and pass the resulting registry to every +renderer that needs it. Do not infer routes from files or maintain separate +SPA, SSR, and SSG route manifests. + +## Verify the installed router + +Read the installed `@askrjs/askr/router` declarations before selecting helpers +or signatures. Use returned typed route references to construct destinations. +Do not borrow path syntax, file-routing conventions, loader semantics, or +navigation APIs from another framework. + +## Select the needed references + +- Read [registries-and-layouts.md](references/registries-and-layouts.md) when + declaring a registry, nested pages, layouts, groups, indexes, fallbacks, or a + deployment base path. +- Read [navigation-and-url-state.md](references/navigation-and-url-state.md) + for parameters, search schemas, typed destinations, links, navigation, and + URL-owned state. +- Read [loaders-access-and-metadata.md](references/loaders-access-and-metadata.md) + for route data, deferred work, cancellation, authorization policies, + metadata, SSR, and SSG entries. +- Compose the sibling + [project structures skill](../project-structures/SKILL.md) for where page, + feature, and route-group files live. + +## Verify route behavior + +Exercise direct entry, client navigation, back/forward history, parameters, +search updates, cancellation, loader failure, redirects, denials, nested and +root fallbacks, metadata updates, and the configured base path where those +behaviors exist. For SSR or SSG, prove the same registry produces the expected +server or static route output. diff --git a/public/skills/routes/agents/openai.yaml b/public/skills/routes/agents/openai.yaml new file mode 100644 index 0000000..835b5b7 --- /dev/null +++ b/public/skills/routes/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: 'Build Askr Routes' + short_description: 'Declare and verify explicit Askr route trees' + default_prompt: 'Use $routes to build an explicit Askr route registry with typed navigation and owned route behavior.' diff --git a/public/skills/routes/references/loaders-access-and-metadata.md b/public/skills/routes/references/loaders-access-and-metadata.md new file mode 100644 index 0000000..7556077 --- /dev/null +++ b/public/skills/routes/references/loaders-access-and-metadata.md @@ -0,0 +1,54 @@ +# Loaders, access, and metadata + +## Contents + +- Load route data +- Defer non-blocking work +- Enforce access +- Own metadata +- Reuse routes across renderers + +## Load route data + +A route `loader` receives route context and an optional request; its result is +available through the route-data contract during rendering and hydration. +Forward its `AbortSignal` to cancellable work so superseded navigation cannot +finish late and overwrite current state. Keep transport translation in an +adapter rather than embedding it in a page. + +Use route `preload` to warm query data through its query-prefetch context. A +loader owns route data; a query owns cached reactive data. Choose by ownership, +not by habit. + +## Defer non-blocking work + +Use `defer(promise)` when a loader value may resolve after the route begins +rendering. Render it with `Resolve`, including explicit pending and rejected +content. Use `routeData<T>()` to read the loader result. Do not collapse +loading, empty, and error into one branch. + +## Enforce access + +Configure auth resolution at the registry boundary. Apply route or group auth +requirements and policies before rendering. Policies return explicit allow, +redirect, or denial decisions such as unauthorized, forbidden, or not found. +Client presentation is not authorization; preserve the same decisions on the +server request path. + +## Own metadata + +Provide `meta` at route or group registration. A metadata source may be static +or computed from resolved route context. Outer-to-inner metadata composes into +the final document contract. Public pages should normally provide distinct +titles, descriptions, canonical URLs, and Open Graph values; canonical and +social URLs must be absolute production URLs. + +Do not maintain page metadata in a second route list. Verify initial server or +static head markup and client navigation reconciliation. + +## Reuse routes across renderers + +The same registry supports SPA, SSR, and SSG resolution. Route context exposes +the active mode when behavior legitimately differs. Dynamic SSG routes use the +installed `entries` contract to enumerate parameter combinations. Do not fork +the route tree merely because the renderer changes. diff --git a/public/skills/routes/references/navigation-and-url-state.md b/public/skills/routes/references/navigation-and-url-state.md new file mode 100644 index 0000000..4267117 --- /dev/null +++ b/public/skills/routes/references/navigation-and-url-state.md @@ -0,0 +1,45 @@ +# Navigation and URL state + +## Contents + +- Model paths and parameters +- Model search state +- Build destinations +- Navigate accessibly + +## Model paths and parameters + +Use the installed router's path syntax. Static segments, `{name}` parameters, +single-segment wildcards, and named splats have distinct matching behavior. +Let `route()` infer parameter props from the literal path rather than manually +redeclaring them. + +Put state in path parameters when it identifies the resource or hierarchy. +Put shareable view state such as filters, search terms, sorting, and pagination +in the query string. Keep transient interaction state in the owning component. + +## Model search state + +Attach the installed schema contract to a route's `search` option so parsing +and destination construction share one type. Use `updateRouteQuery` for +query-string changes. Its default replacement behavior is appropriate for +high-frequency controls; choose pushed history only when each change should be +a navigable history entry. + +## Build destinations + +Keep the typed route reference returned by `route()`. Use `to(routeRef, params, +search?)` to build a destination rather than interpolating application paths by +hand. This preserves parameter, search, and registry base-path handling. + +## Navigate accessibly + +Use `Link` from `@askrjs/askr/router` for ordinary internal navigation. It +retains native anchor behavior. Use the themed `NavLink` from +`@askrjs/themes/components` when a navigation item also needs automatic active +route styling. Use buttons for actions, not navigation. + +Use programmatic `navigate` only when navigation follows application behavior +rather than a link the user can activate directly. Preserve meaningful link +text, `aria-current` where appropriate, and safe `rel` values for new browsing +contexts. diff --git a/public/skills/routes/references/registries-and-layouts.md b/public/skills/routes/references/registries-and-layouts.md new file mode 100644 index 0000000..e879dd2 --- /dev/null +++ b/public/skills/routes/references/registries-and-layouts.md @@ -0,0 +1,59 @@ +# Registries and layouts + +## Contents + +- Create one registry +- Compose route scopes +- Connect layouts +- Handle fallbacks +- Configure a base path + +## Create one registry + +Create the root registry in `pages/_routes.tsx` with +`createRouteRegistry(definition, options?)`. Export the resulting ordinary +value and pass it to browser mounting, hydration, SSR, or SSG. Two registries +may coexist when an application intentionally owns two route trees, but a +rendering mode is not a reason to duplicate one tree. + +## Compose route scopes + +- `route(path, Component, options?)` declares one route and returns a typed + route reference. +- `group(options, definition)` shares layout, auth, policies, or metadata + without adding a path segment. +- `page(path, Component, options?, definition)` declares a route and opens a + nested scope. +- `index(Component, options?)` declares the index inside a `page()` scope. +- `fallback(Component)` declares the catch-all for its valid enclosing scope. + +Keep each route group's declarations in its `_routes.tsx`. File placement does +not register a route. + +## Connect layouts + +Keep the layout adjacent to the route group it wraps. A root `_layout.tsx` +owns root providers and the application-wide shell; a nested `_layout.tsx` +owns only its group's shell, navigation context, or access presentation. +Render nested route content with the installed router's `Outlet` contract. + +Do not hide registration inside a page component. Pages compose features and +receive typed route inputs; layouts compose the shared frame around them. + +## Handle fallbacks + +Use a root `fallback(NotFoundPage)` for unmatched application navigation. A +`page()` scope may own its own fallback. Do not treat `fallback()` as a generic +wildcard; use a documented wildcard or named splat path on a normal route when +that is the actual route shape. + +For SSG, also register an explicit `/404` route when the generator must emit a +concrete document. The explicit route and navigation fallback may render the +same page but serve different contracts. + +## Configure a base path + +Set `RouteRegistryOptions.basePath` when the application is mounted below the +origin root. Coordinate it with the bundler asset base and production URL; the +router base alone does not relocate built assets. Use the actual deployment +segment rather than guessing it. diff --git a/public/skills/themes/SKILL.md b/public/skills/themes/SKILL.md new file mode 100644 index 0000000..9637c34 --- /dev/null +++ b/public/skills/themes/SKILL.md @@ -0,0 +1,59 @@ +--- +name: themes +description: Select, apply, compose, customize, or review Askr themes and themed components. Use for @askrjs/themes imports, Block-first layout, component styling, design tokens, light and dark modes, custom themes, themed accessibility, and SSR or SSG style integration. +--- + +# Use Askr themes + +Use themes as the visual layer of an Askr application without moving +application behavior or product-specific composition into the theme. + +## Establish the package boundary + +- `@askrjs/askr` owns what exists and when. +- `@askrjs/ui` owns behavior, state, focus, and ARIA coordination. +- `@askrjs/themes` owns tokens, default styling, visual-only composition, and + Block-first structural components. +- The application owns product-specific page and feature composition. + +Do not repair a behavior problem with theme CSS or put a product-specific +dashboard, marketing section, or workflow into the shared theme. + +## Verify the installed contract + +Inspect the installed package exports, declarations, declaration comments, +and `THEMING.md` before selecting an API. Export presence alone is not an +endorsement. Avoid legacy and deprecated aliases in new work, and use limited +or experimental components only when their documented constraints fit the +request. + +Use `@askrjs/themes/components` for the aggregate styled catalog or a real +documented component subpath when a focused import is useful. Do not invent a +subpath from a component name. + +## Select the needed references + +- Read [components.md](references/components.md) when choosing building + blocks, composing application chrome, forms, feedback, or responsive UI. +- Read [tokens-and-customization.md](references/tokens-and-customization.md) + when selecting a shipped theme, changing visual language, overriding tokens, + or authoring component CSS. +- Read [runtime-and-rendering.md](references/runtime-and-rendering.md) when + using theme selection controls, custom theme names, SSR, SSG, hydration, or + CSP-aware style output. +- Compose the sibling [icons skill](../icons/SKILL.md) for icon selection, + accessibility, imports, and the SVG contract. This skill owns only the theme + tokens and selectors that style that contract. + +The references compose. A static marketing page may need components and token +customization; an SSG using responsive `Block` props also needs runtime and +rendering guidance. + +## Verify the themed result + +Check light and dark modes at 320, 390, 768, 1024, and desktop widths. Verify +contrast, focus visibility, forced colors, reduced motion, text wrapping, +overflow, control sizing, elevated surfaces, and realistic long content. +Inspect server-rendered HTML and styles before hydration when SSR or SSG is in +scope. Run the owning repository's formatting, type, analysis, build, and +browser gates appropriate to the change. diff --git a/public/skills/themes/agents/openai.yaml b/public/skills/themes/agents/openai.yaml new file mode 100644 index 0000000..cb784e3 --- /dev/null +++ b/public/skills/themes/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: 'Use Askr Themes' + short_description: 'Compose and customize Askr themed interfaces' + default_prompt: 'Use $themes to compose an Askr interface from canonical themed components and tokens.' diff --git a/public/skills/themes/references/components.md b/public/skills/themes/references/components.md new file mode 100644 index 0000000..485dba2 --- /dev/null +++ b/public/skills/themes/references/components.md @@ -0,0 +1,94 @@ +# Themed components + +## Contents + +- Choose the right layer +- Build with Block +- Compose page structure +- Compose behavior-backed controls +- Represent application states +- Preserve accessibility + +## Choose the right layer + +Use `@askrjs/themes` when the shipped visual language should own appearance. +Use `@askrjs/ui` directly when the application or another design system must +own appearance. When a themed component wraps a headless primitive, behavior, +state, focus, and ARIA remain owned by `@askrjs/ui`; the theme supplies its +visual treatment. + +Some theme-only components are visual composition wrappers and need no +headless counterpart. Verify ownership and stability in the installed +declarations rather than assuming every themed export has a UI subpath. + +## Build with Block + +`Block` is the canonical layout engine. It owns direction, alignment, +justification, spacing, sizing, responsive values, borders, backgrounds, +radius, shadows, and semantic element selection. + +```tsx +import { Block, Container, Text } from '@askrjs/themes/components'; + +<Container size="xl" paddingY="lg"> + <Block direction="column" gap="md"> + <Text as="strong" size="lg"> + Projects + </Text> + <Text tone="muted">Active work in this workspace.</Text> + </Block> +</Container>; +``` + +Use exact installed prop and token values. Use `paddingY`, not an invented +`py`. Use `Block direction="column"` or `Block direction="row"` rather than +the legacy `Stack` and `Inline` compatibility aliases. Use `Grid` when the +layout genuinely needs explicit rows or columns. Use `Container` for content +width and gutters; use its `size` contract instead of overriding `maxWidth`. + +## Compose page structure + +Use semantic components such as `Header`, `Main`, `Aside`, `Footer`, `Page`, +`PageHeader`, and `Section` for the regions they name. Compose navigation from +`Navbar` or `Sidebar`. Use `Link` from `@askrjs/askr/router` for ordinary +internal navigation and themed `NavLink` when navigation also needs +route-aware active styling. + +These components compose Block's layout contract; they are not alternate +layout engines. Avoid the legacy `Shell`, `ShellNav`, and `ShellMain` aliases +in new code. Product-specific shells remain application composition. + +## Compose behavior-backed controls + +Use the themed versions of behavior-backed controls such as `Button`, +`Checkbox`, `Dialog`, `Select`, and `Switch` when default styling is desired. +Keep their published compound structure intact. Use `Button` for actions and +links for navigation. + +Use `Field` with the installed label, control, hint, and error contracts for +forms. Associate help and validation feedback with the control. Do not infer +compound parts or props from similarly named components in another library. + +## Represent application states + +Use canonical feedback components according to the state being communicated: + +- `Alert` for contextual information or actionable problems. +- `EmptyState` for an all-in-one empty result; use the separate compound + `Empty` family only when its parts are required. +- `Progress`, `Skeleton`, or `Spinner` for real pending work. +- `Toast` for transient feedback that does not replace an inline recoverable + error. +- `Card` for a bounded content group, not as default spacing. + +Never leave a loading indicator indefinitely where a failure state should +appear. + +## Preserve accessibility + +The theme does not relieve the application of content semantics. Maintain one +meaningful `h1`, logical heading order, landmarks, labels, accessible names, +focus order, useful alternative text, and actionable error recovery. Choose +configurable heading elements such as `titleAs` from the surrounding document +hierarchy. Verify behavior-backed components with a keyboard and screen +reader, not appearance alone. diff --git a/public/skills/themes/references/runtime-and-rendering.md b/public/skills/themes/references/runtime-and-rendering.md new file mode 100644 index 0000000..d952df8 --- /dev/null +++ b/public/skills/themes/references/runtime-and-rendering.md @@ -0,0 +1,54 @@ +# Theme runtime and rendering + +## Contents + +- Select theme state +- Register custom names +- Integrate SSR and SSG +- Preserve deterministic output + +## Select theme state + +Use the installed `@askrjs/themes/theme` exports for runtime theme selection. +`ThemeScope` establishes theme context, while `ThemePicker` and `ThemeToggle` +provide selection UI. The lower-level `theme` helper reads or controls the +document theme according to its installed contract. Do not invent helpers from +stale capability metadata or examples; verify the current declarations. + +Theme selection must be explicit at an appropriate ancestor. The resulting +DOM uses `data-theme` values such as `light` or `dark` for token selection. +Provide application-owned content or icons to `ThemeToggle`; it intentionally +does not supply icons. + +## Register custom names + +Custom theme names are intentionally allowed. Register each name in the +scope's theme options and supply a matching `[data-theme="..."]` token block. +Because arbitrary names type-check, a misspelling can silently select a name +with no matching token block; keep the option registry and CSS selector in the +same change. + +## Integrate SSR and SSG + +Responsive theme components can register render-time CSS. Wrap the SSR or SSG +document renderer with `withThemeStyles` from `@askrjs/themes/ssr` so styles +used by the rendered application are present before hydration: + +```ts +import { withThemeStyles } from '@askrjs/themes/ssr'; + +export const renderDocument = withThemeStyles(baseDocumentRenderer); +``` + +Use the renderer's strict style-registration validation when available so a +missing registration fails the build instead of producing an unstyled or +hydration-divergent page. Preserve any CSP nonce carried by the rendering +context. + +## Preserve deterministic output + +Resolve the initial theme deterministically for server and client. The server +HTML, `data-theme`, registered styles, and initial client state must agree +before hydration. Inspect generated HTML and CSS directly, then hydrate it in +a browser and check for console diagnostics, flashes of the wrong theme, or +layout changes. diff --git a/public/skills/themes/references/tokens-and-customization.md b/public/skills/themes/references/tokens-and-customization.md new file mode 100644 index 0000000..41dc8a1 --- /dev/null +++ b/public/skills/themes/references/tokens-and-customization.md @@ -0,0 +1,70 @@ +# Tokens and customization + +## Contents + +- Load a shipped theme +- Understand token ownership +- Customize in contract order +- Author stable selectors +- Check visual quality + +## Load a shipped theme + +Import one selected theme once from the application stylesheet: + +```css +@import '@askrjs/themes/default'; +``` + +CSS imports are side effects. Do not scatter duplicate imports through +components. Confirm the installed package exposes the selected CSS entry. + +## Understand token ownership + +Official tokens use the global `--ak-*` prefix and express semantic color, +typography, spacing, radius, borders, shadows, focus, motion, icons, layout, +and z-index. Prefer semantic tokens over component-specific or raw visual +values. Layout, spacing, typography scale, icon scale, and breakpoints normally +belong at the global root; theme mode blocks override values that genuinely +change between themes. + +The default light and dark token sets are contrast-tested. Consumer overrides +are ordinary CSS and cannot be validated by the runtime. + +## Customize in contract order + +1. Select the closest shipped theme. +2. Override semantic tokens for application visual identity and density. +3. Use published component props and variants. +4. Add narrow component CSS only when tokens and props cannot express the + requirement. + +```css +:root { + --ak-color-primary: purple; + --ak-radius-md: 12px; +} +``` + +Recheck every affected foreground/background contrast pair after color +overrides. Tune shared density, focus, motion, and icon tokens rather than +patching each component independently. + +## Author stable selectors + +Style public hooks: `data-slot`, `data-state`, `data-disabled`, +`data-orientation`, `data-variant`, and `data-size`. Prefer low-specificity +`:where(...)` rules. Do not target undocumented internal DOM, use deep +selectors, depend on generated class names, or reach for `!important`. + +Class aliases are conveniences; public `data-*` hooks are the canonical +contract. Product-specific styles belong in the application, not the shared +theme package. + +## Check visual quality + +Review light and dark mode with realistic content at phone, tablet, and +desktop widths. Look for clipped text, accidental page overflow, broken icon +alignment, inconsistent control heights, weak focus treatment, unreadable line +length, and flat or inconsistent elevated surfaces. Honor reduced motion and +verify forced-color behavior where interactive controls are present. diff --git a/src/pages/docs/content-overrides.ts b/src/pages/docs/content-overrides.ts index e4d63ac..97557ce 100644 --- a/src/pages/docs/content-overrides.ts +++ b/src/pages/docs/content-overrides.ts @@ -1037,11 +1037,11 @@ export const headingOverrides: Readonly< 'edge-cases': 'Because Block is the only layout engine, resist the temptation to reach for raw flexbox or grid CSS around it for one-off adjustments — inconsistent layout approaches are exactly what the single-engine design is meant to prevent. Also remember that broad slots like `main`, `sidebar`, and `navbar` are semantic presets over Block, not independent components with their own prop vocabulary, so their responsive behavior follows the same breakpoint rules as everything else.', 'install-and-import': - "Import structural components from the themes catalog: `import { Block, Container, Stack, Section, Grid } from '@askrjs/themes/components'`. Broader layout slots — `Main`, `Sidebar`, `Header`, `Footer`, `Shell` — are exported the same way and are, per the package's own docs, \"semantic Block presets, not independent layout engines.\"", + "Import structural components from the themes catalog: `import { Block, Container, Section, Grid } from '@askrjs/themes/components'`. Broader layout components such as `Main`, `Sidebar`, `Header`, and `Footer` are exported the same way and compose the same Block layout contract. Use `Block` with an explicit `direction` for row or column composition; `Stack`, `Inline`, and `Shell` are legacy compatibility aliases, not recommendations for new code.", 'keyboard-and-accessibility': "Because Block and its presets render plain structural elements rather than interactive controls, there's no dedicated keyboard contract to document here — accessibility for structural components mostly comes down to choosing sensible native elements and letting content inside them (buttons, links, form controls) carry their own accessible behavior. `Main`, `Header`, and similar presets do map to meaningful landmark elements, which is worth preserving when you customize them.", purpose: - '`Block` is described in the theme\'s own docs as "the only layout engine" — the single primitive responsible for spacing, sizing, alignment, and responsive flex direction across the whole component catalog. Higher-level structural pieces like `Container`, `Section`, `Stack`, `Inline`, and `Grid` are built as presets on top of it rather than separate systems, which keeps layout behavior consistent everywhere it\'s used.', + '`Block` is described in the theme\'s own docs as "the only layout engine" — the single primitive responsible for spacing, sizing, alignment, and responsive flex direction across the whole component catalog. Canonical structural components such as `Container`, `Section`, and `Grid` compose that layout contract rather than introducing separate systems. Use `Block direction="column"` or `Block direction="row"` instead of the legacy `Stack` and `Inline` aliases in new code.', 'related-pages': "See Portals and Layers for how overlay content escapes normal document flow rather than being laid out by Block, and Customization for how Block's own CSS variables participate in the token override system.", 'state-model':