Type to search pages and rules.

Browse pages

TypeScript Web Components

This page states how the TypeScript web stacks realize the frontend component principles (Frontend components).

Markdown7 provisions

Intent#

This page states how the TypeScript web stacks realize the frontend component principles (Frontend components). It states the registry source, the closed element list, and the visual file patterns. It also states the raw HTML sinks and the route suite checks the React and Svelte stacks share.

React#

The React stack uses these bindings:

  • A class or style binding spells className or style.
  • A visual file ends with *.visual.tsx.
  • The raw HTML sink is dangerouslySetInnerHTML.
  • The route error surface is the route error component.
  • Component authoring follows react/components.md.

Svelte#

The Svelte stack uses these bindings:

  • A class or style binding spells class, class:, style, or style:.
  • A visual file ends with *.visual.svelte.
  • The raw HTML sink is {@html}.
  • The route error surface is the route error page.
  • Component authoring follows svelte/components.md.

Standards#

Keep primitives and composites in one shared package#

ruleguidance
litespec/frontend/web/components#keep-primitives-and-composites-in-one-shared-package
Requirement

A workspace MUST hold its registry primitives and its composites in one shared UI package, with no copy in an application.

Why

A primitive copied into two applications diverges on the first upstream update. The divergence is invisible because both copies compile. This realizes litespec/frontend/components#keep-shared-ui-in-one-package.

Example

The primitives sit in one folder that the registry CLI writes. No one edits that folder by hand. The composites sit beside them and import them.

Checka reviewer answers

Does one shared package hold the registry source and the composites, with no copy in an application?

Marked guidance: a reviewer answers this question.

Use no raw element in page code#

ruleguidance
litespec/frontend/web/components#use-no-raw-element-in-page-code
Requirement

A component under a route or module folder MUST NOT declare a layout or interactive element, and each refusal names the composite to use.

Why

A raw element carries no focus management, no state, and no accessibility wiring. The reader of the lint message chooses between two answers. The message gives one. This realizes litespec/frontend/components#use-composites-for-layout-and-interaction-in-page-code.

Example

The refused elements and their replacements are one closed list.

RefusedUse instead
div, spanStack, Inline, Columns, Section, or a floorplan slot
buttonButton from the shared package
aTextLink or ButtonLink
input, select, textareaa field from the shared form library
formthe shared form component
tableDataTable
imgthe shared image component, which litespec/frontend/components#use-a-composite-for-a-content-image owns

Text elements stay available, because a sentence is not a component decision. p, h2, h3, em, strong, ul, ol, and li carry prose. The floorplan owns the h1, so it is not in the list. Any other element follows the same rule: page code declares no other element, and the author adds a composite or a visual file instead.

Checka reviewer answers

Does page code declare a refused element, and does each refusal name its composite?

Marked guidance: a reviewer answers this question.

Place a drawing no composite makes in a visual file#

ruleguidance
litespec/frontend/web/components#place-a-drawing-no-composite-makes-in-a-visual-file
Requirement

A visual file MAY write class names, and uses semantic theme tokens only.

Why

A stock-location map, a packing-slip preview, and a public frame have no composite. Refusing them pushes the drawing into an arbitrary value that no token governs. The file name declares this exception. The React and Svelte sections name the file pattern. This realizes litespec/frontend/components#place-a-drawing-no-composite-makes-in-an-exception-file.

Checka reviewer answers

Does every visual file use semantic tokens only, and does any other file write class names?

Marked guidance: a reviewer answers this question.

Meet accessibility requirements#

rulecheckable
litespec/frontend/web/components#meet-accessibility-requirements
Requirement

The route suite MUST check keyboard reach, visible focus, target size, and status announcements on every route.

Why

The principle's guarantees hold only when every route proves them. This realizes litespec/frontend/components#support-keyboard-focus-labels-and-announcements.

Checkchecked by a tool

tests/routes/every-route.spec.ts presses Tab and asserts every focused element shows an indicator.

Marked checkable: the tool named here checks it.

Protect rich content boundaries#

ruleguidance
litespec/frontend/web/components#protect-rich-content-boundaries
Requirement

A component MUST NOT pass untrusted content to a raw HTML sink.

Why

The React and Svelte sections name the sink for each stack. Stored rich content requires a decision record naming the sanitizer, the allowed elements and attributes, the link policy, and the test cases. This realizes litespec/frontend/components#sanitize-untrusted-rich-content.

Checka reviewer answers

Does any component pass untrusted content to a raw HTML sink without its recorded decision?

Marked guidance: a reviewer answers this question.

Conventions#

Give card and section titles heading semantics#

conventioncheckable
litespec/frontend/web/components#give-card-and-section-titles-heading-semantics
Default

Render a visible card, panel, or section title as a heading element at its correct level.

ReplacementA consumer replaces this default by recording an exception with its reason and review date.

Why

Some primitive sources default a title to a non-heading element, which removes it from the outline and the browser test that finds it. The route suite asserts each visible section title resolves as a heading. This realizes litespec/frontend/components#expose-card-and-section-titles-as-headings.

Checkchecked by a tool

tests/routes/every-route.spec.ts asserts each visible section title resolves as a heading.

Marked checkable: the tool named here checks it.

Compose class names in the shared package#

conventionguidance
litespec/frontend/web/components#compose-class-names-in-the-shared-package
Default

Compose class names through the one merge helper the shared package exports, and keep every merge or variant helper import inside it.

ReplacementA consumer replaces this default by recording an exception with its reason and review date.

Why

A second merging helper produces a different conflict resolution for the same class pair. Page code has no reason to hold one.

Checka reviewer answers

Does every class merge go through the shared package helper?

Marked guidance: a reviewer answers this question.

Verification#

IDMarkEvidence
litespec/frontend/web/components#keep-primitives-and-composites-in-one-shared-packageguidanceDoes one shared package hold the registry source and the composites, with no copy in an application?
litespec/frontend/web/components#use-no-raw-element-in-page-codeguidanceDoes page code declare a refused element, and does each refusal name its composite?
litespec/frontend/web/components#place-a-drawing-no-composite-makes-in-a-visual-fileguidanceDoes every visual file use semantic tokens only, and does any other file write class names?
litespec/frontend/web/components#meet-accessibility-requirementscheckabletests/routes/every-route.spec.ts presses Tab and asserts every focused element shows an indicator.
litespec/frontend/web/components#protect-rich-content-boundariesguidanceDoes any component pass untrusted content to a raw HTML sink without its recorded decision?
litespec/frontend/web/components#give-card-and-section-titles-heading-semanticscheckabletests/routes/every-route.spec.ts asserts each visible section title resolves as a heading.
litespec/frontend/web/components#compose-class-names-in-the-shared-packageguidanceDoes every class merge go through the shared package helper?