Type to search pages and rules.

Browse pages

The Expenses Screen

The expenses screen is one screen of the Expenses product, the example product these pages follow: it shows each employee their expenses at one address. This page follows that screen from the folder serving its address, through its data read with the API client generated from the published contract, the shared UI package it composes, and the drawn list option it starts from, naming the frontend rule each step applies. A developer or agent who builds a screen in the Expenses product reads it before adding the next route, and sees how the same rules shape every list the product adds.

Markdown

The expenses route#

The screen lives at apps/web/src/routes/expenses/+page.svelte in the Expenses example. A Svelte frontend holds one folder per address under src/routes, with the frame and the use-case code in src/lib/app and src/lib/modules, as Web Structure states. The route mirrors its use case path in kebab case, per litespec/frontend/structure#mirror-the-backend-use-case-path-in-kebab-case on Frontend Structure: the folder expenses answers the List expenses use case that Modules and Records describes.

A route file holds its input, its load, and its component, with no reusable logic and no import of another route file, per litespec/frontend/web/structure#keep-a-route-file-thin. The expenses route therefore validates its search, prefetches its read, and composes its view, while shared code lives in the module folder or the frame. Imports point from the route to a module to the frame to a shared package, per litespec/frontend/web/structure#keep-imports-directional, so the route reads the client from the frame and the generated operations from the shared API package.

Reading through the generated client#

The screen never duplicates the API contract. The workspace generates its client, types, schemas, and query options from the published OpenAPI document, per litespec/frontend/web/data#generate-the-client-from-the-published-contract on Web Data and State, which follows litespec/frontend/data#generate-the-client-from-the-published-contract on Frontend Data and State. The generated client lives in packages/api, and the list reads through its listExpenses operation in packages/api/src/generated/sdk.gen.ts:

TypeScript
export const listExpenses = (options) => (options.client ?? client).get({
    url: '/api/spending/list-expenses',
});

The route's load prefetches the read through the query cache before the component runs, per litespec/frontend/web/rendering#load-route-data-in-the-loader on Web Rendering and Routes, and the component reads the same cache key with a suspense query, per litespec/frontend/web/rendering#read-route-data-with-a-suspense-query. Written the way the stack pages require, the load looks like this:

TypeScript
export async function load({ parent, url }) {
  const { queryClient } = await parent();
  const search = expensesSearch.parse(Object.fromEntries(url.searchParams));
  await queryClient.ensureQueryData(listExpensesQuery(search.page));
  return { search };
}

The search schema keeps the filters and the page in the address, per litespec/frontend/web/rendering#keep-search-parameters-validated-by-a-schema, so a filtered list stays a link the team can share. The example keeps only the file layout the model builder reads: its expenses page imports submitExpense from @expenses/api as a placeholder for the client call. The code above shows what the stack pages require a real page to do.

The shared UI package#

The screen composes the shared UI package @expenses/ui at packages/ui, which the project declares in paths.uiPackage of litespec.project.json. The package pins its baseline in the source lock at packages/ui/ui-source-lock.json: generator shadcn-svelte, CLI 1.7.0, with the button and skeleton components at baseline status. litespec ui checks the package, its source lock and digests, its design contract, its stylesheets, and each controlled frontend boundary, per Validate Controlled UI. Against the Expenses example it reports this, and exits zero:

Text
Consumer: D:\Personal-Projects\LiteSpec\examples\expenses
Shared UI package: @expenses/ui at packages/ui
Controlled frontends: 1

PASS: the shared package, its source lock, its design contract, its stylesheets, and each frontend boundary are valid.

The run names the one controlled frontend: the web frontend at apps/web, which declares platform: svelte.

The list pattern behind the screen#

The screen starts from the litespec/frontend/patterns/lists-and-tables#choosing-the-columns pattern on List and Table Patterns. That pattern asks which columns the table shows, and its default option five-columns-nothing-removable shows five columns with nothing removable: which item, who, when, what state, and what value. The expenses screen adapts that set to the facts the Expense record holds: the expense, the employee, the expense state, and the amount, which Modules and Records describes.

A consumer keeps one pattern catalog holding its interface decisions, per litespec/frontend/patterns#keep-one-pattern-catalog on UI Patterns. Each entry records at least 2 options with rationale and cost and names the chosen option, and each decision records its source as owner or recommended, per litespec/frontend/patterns#record-who-chose. The example keeps its catalog at docs/ui/pattern-catalog.json, and its entries record recommended sources, standing until an owner chooses.

The frontend rules the screen follows#

Each step of the screen answers to one frontend rule. Frontend Structure and Web Structure place the route folder, keep the route file thin, and point its imports toward the shared packages. Frontend Data and State and Web Data and State generate the client from the contract and route the read through it. Web Rendering and Routes prefetches the list in the load, reads it from the cache in the component, and keeps the view in the address. Validate Controlled UI checks the shared package, its source lock, and each frontend boundary. List and Table Patterns gives the columns decision its options, and UI Patterns records the choice. The Svelte Stack selects the rendering mode for the Expenses app by its audience. A screen that keeps each of these rules reads the same way as every other screen, so the next route is a small variation on this one. The Model and Its Checks shows how the model proves the screen, its use case, and its handler agree.