# What you may choose

**Arena is built with bun, React 18, Angular 22 and TypeScript. None of that is a requirement on
you**, and this page exists because the two are easy to read as one thing. What a design system is
built with leaks into its documentation as a default nobody chose, and an agent that inherits it
picks a toolchain for the wrong reason: not because the product needs it, but because the sample
command happened to be written that way.

So this page is the repertoire. Every axis a project settles before its first screen, every answer
Arena supports on that axis, and **how much evidence each answer actually has**. Read this once per
project, beside [`cold-start.md`](./cold-start.md) and before the first screen, and choose each axis
on what the product needs rather than on what Arena happens to be made of.

## The evidence column, and why it is there

A yes with nothing behind it is worth less than an honest maybe, so no row here says yes. Each one
says how it is known.

| Evidence | What it means |
|---|---|
| held by a gate | proved on every run of Arena's own board. If it broke, a build here would fail before a release went out |
| exercised by Arena's own suites | the code path runs here, repeatedly, against this version |
| verified by hand once | somebody installed it that way and it worked. Nothing revalidates it, so it is a record rather than a guarantee |
| allowed by the manifest, not exercised | the declared range resolves it and nothing is known against it. Nobody has run it |
| does not work | with the reason, so you can plan around it instead of discovering it |

**A row is never promoted without being exercised.** "Allowed by the manifest, not exercised" is a
finished answer, and reading it as a soft yes is the mistake this column exists to prevent. If you
need one of those rows to be certain, run it early and small rather than late and whole.

## The repertoire

<!-- @support matrix GENERATED by bun run generate:support. Edit that script, not this copy. -->

**Which package manager installs this?**

| You may choose | Evidence | What is true |
|---|---|---|
| npm | verified by hand once | the packages declare their peers rather than assuming a flat tree, so no hoisting flag is needed |
| pnpm | verified by hand once | the strict layout is the one worth naming, because it is the one that would break a package assuming otherwise: installed under it, the command still resolves the icon font through the symlinked store |
| bun | verified by hand once | the same install. Bun is also what Arena is built with, which is a fact about Arena rather than a requirement on you |
| yarn | allowed by the manifest, not exercised | nothing here works against it: the peers are declared, there is no install script to trust, and no flat tree is assumed. Nobody has run it, which is why it says allowed rather than yes |

**What runs the command?**

| You may choose | Evidence | What is true |
|---|---|---|
| Node >=22 | held by a gate | the command is a Node program, and that floor is the oldest line Node still supports rather than a capability the command needs |
| any runner that reaches a Node program | allowed by the manifest, not exercised | npx and bunx both reach the same file. No runner is required: the command imports three node modules and no runtime API of its own |

**How is the package loaded?**

| You may choose | Evidence | What is true |
|---|---|---|
| an ES module import | held by a gate | the package is a module, and every target its exports name resolves to a file that is there |
| a CommonJS require | does not work | there is no require condition and no second build. A project that cannot load a module cannot load this one, and it fails at the first import rather than somewhere later |

**How does the stylesheet reach the page?**

| You may choose | Evidence | What is true |
|---|---|---|
| an import from a module of yours | verified by hand once | the idiom this page shows. It is an instruction to a bundler rather than to TypeScript, which is why a project may need a declaration before it typechecks |
| an @import from a stylesheet of yours | allowed by the manifest, not exercised | what the package hands you is a barrel of relative imports, so whatever resolves those resolves all of it |
| the global styles list a framework CLI takes | allowed by the manifest, not exercised | the same file named as a global style rather than imported from a module, which is how a CLI-driven workspace usually takes one |
| a link element | allowed by the manifest, not exercised | this one needs the package directory served as it stands, because the barrel reaches its parts by relative path |

**What assembles it?**

| You may choose | Evidence | What is true |
|---|---|---|
| any bundler that resolves exports and imports CSS | allowed by the manifest, not exercised | nothing in the package is written for one: no raw-text import, no build-time environment global, and nothing compiled at install time |
| a framework CLI | allowed by the manifest, not exercised | where a layer ships in the package format its framework defines, partially compiled, the consumer build finishes it the way it finishes any library it depends on |
| no bundler at all | allowed by the manifest, not exercised | the stylesheets are compiled already and the components are plain modules, so what a project needs is something that resolves a module specifier |

**Which layer, and which version of it?**

| You may choose | Evidence | What is true |
|---|---|---|
| React 18 | exercised by Arena's own suites | the version Arena builds and renders against on every run, server rendering included |
| React 19 | allowed by the manifest, not exercised | inside the range the package declares, and not exercised here. Nothing is known against it |
| Angular 22 | exercised by Arena's own suites | the version the layer is compiled and tested against, shipped partially compiled so your own build finishes it |
| Angular 20 or 21 | allowed by the manifest, not exercised | at or above the floor the package declares, and not exercised here |
| neither layer | allowed by the manifest, not exercised | the tokens, the stylesheets for markup of your own, the behaviour contracts and the command are framework-neutral. What you do not get is a component, so every element is yours to write and yours to hold to a pattern |

<!-- @support matrix end -->

## What each layer requires

Peer dependencies are declared rather than bundled, so your project installs them and your project
decides their versions inside the ranges below. **A list of peers with the Arena package missing
installs nothing that draws**, and an Arena package with a peer missing fails at the first import.

<!-- @support peers GENERATED by bun run generate:support. Edit that script, not this copy. -->

| Package | Peer | Range | Required |
|---|---|---|---|
| `@dravensoft/arena-react` | `react` | `^18 \|\| ^19` | always |
| `@dravensoft/arena-react` | `react-dom` | `^18 \|\| ^19` | always |
| `@dravensoft/arena-react` | `@phosphor-icons/web` | `^2.1.2` | always |
| `@dravensoft/arena-angular` | `@angular/core` | `>=20` | always |
| `@dravensoft/arena-angular` | `@angular/common` | `>=20` | always |
| `@dravensoft/arena-angular` | `@angular/platform-browser` | `>=20` | always |
| `@dravensoft/arena-angular` | `@angular/cdk` | `>=20` | always |
| `@dravensoft/arena-angular` | `@angular/router` | `>=20` | until you reach what needs it |
| `@dravensoft/arena-angular` | `@phosphor-icons/web` | `^2.1.2` | always |

<!-- @support peers end -->

Two of those rows carry more than a number.

**The icon font is a peer in both layers.** Icons are Phosphor class-name strings a component
renders, and no package bundles the font, so a project that skips this peer renders components with
empty squares where the icons go and nothing reports it.

**One peer is conditional.** The router is required only once a project reaches the entry point that
writes the document head, which is why it is declared optional: a project that never reaches for
metadata never installs a router it does not use. [`seo.md`](./seo.md) is where that decision is
made, and node 8 of [`cold-start.md`](./cold-start.md) is where it is reached.

## What this page does not decide

**Which layer you take, and how the application is assembled.** Those are questions with their own
nodes, and duplicating them here would give a project two places to answer one thing. Node 8 of
[`cold-start.md`](./cold-start.md) decides the layer, because how much of the product has to be
found from outside it is what settles both the layer and one peer. Node 9 of the same tree answers
the render architecture, a single-page application, server rendering with hydration, static
generation, server components or a microfrontend, with what is true of each and what you carry.

**How much of Arena you take.** [`surface.md`](./surface.md) is the whole offer in one table, with
three steps a project may stop at and the line where Arena stops on purpose.

**What your product looks like.** Nothing on this page touches appearance.
[`style-kernel.md`](./style-kernel.md) is that decision, and it is independent of every axis here:
the same style plugin travels whatever installs it and whatever assembles it.

## Where the repertoire stops

- **No matrix is installed on every run.** The package-manager and bundler rows are claims about
  somebody else's tool, and nothing here revalidates them per release. That is why they carry the
  evidence they carry rather than a stronger word.
- **No version is pinned upward for you.** The React range is closed at the majors that have been
  rendered, because an open range would promise a major nobody has seen. The Angular floor is open
  above it, so a version past the exercised one resolves and is untested: that is a permission, not
  a promise.
- **No project of yours is read.** No gate here opens your application, so every rule of the
  language holds because you hold it. The closest thing is the command's own audit, which reads your
  source text for the five rules source text can show and reports rather than fails unless you ask
  it to.
