# Perron

This app uses Perron, Mylder's design system, from the npm package `@mylder/perron`. Read this before you style a screen or write a word a rider sees.

## Install

```sh
npm install @mylder/perron
```

## Rules

- Install `@mylder/perron` and load `@mylder/perron/perron.css` once at the app root. It brings the fonts, the custom properties for both themes and for reduced motion, the typography classes and a few page defaults.
- Pages name tokens the way the sources do, `color.text.primary`. In CSS that token is `var(--perron-color-text-primary)`: `--perron-`, then the name with dots as hyphens. Never copy a value.
- In script, `@mylder/perron/tokens.ts` has every value per theme, typed by name, and the reduced motion values under `modifiers`.
- Light is the default theme. Set `data-theme="dark"` on any element for dark; without it, the system's dark mode applies. Reduced motion follows the system setting on its own.
- Icons and logos are SVG files in `@mylder/perron/icons/` and `@mylder/perron/logos/`, listed with their use in `icons.json` and `logos.json`.
- A token changes only in Perron's own repository, in `packages/perron/tokens/*.tokens.json`, followed by `npm run tokens`. Never edit a generated file, or a table on these pages, by hand.

Every word on screen follows [Content design](https://marcin-sieradzki.github.io/perron/docs/content-design.md).

## Components

Ask the Perron MCP server about a component before you use it. Add it to the app's `.mcp.json`, as the [Agents page](https://marcin-sieradzki.github.io/perron/agents/) shows:

```json
{ "mcpServers": { "perron": { "command": "npx", "args": ["-y", "@mylder/perron-mcp"] } } }
```

`list_components` finds the component or pattern a screen needs, by words, layer and category. `get_component` gives one whole: its props, when to use another entry instead, and the rules of its Do and don't. Keep every `must` rule, and break a `should` rule only for a reason you can give. Check the rules marked `review` yourself, since nothing else catches them.

## Where to look

Read the page for each choice before you make it: a colour, a type role, a space, an icon. Each link is Markdown; the same pages, drawn live, are on the [docs site](https://marcin-sieradzki.github.io/perron/) and in the [Storybook](https://marcin-sieradzki.github.io/perron/storybook/).

### Foundations

- [Contents](https://marcin-sieradzki.github.io/perron/docs/foundations/README.md): Perron is the design system of Mylder, a live map of Copenhagen's public transport: every bus, S-tog, metro and regional train, where it is now, and how sure we are about that.
- [Accessibility](https://marcin-sieradzki.github.io/perron/docs/foundations/accessibility.md): Perron targets WCAG 2.2 level AA.
- [Elevation](https://marcin-sieradzki.github.io/perron/docs/foundations/elevation.md): The surface and shadow tokens.
- [Icons](https://marcin-sieradzki.github.io/perron/docs/foundations/icons.md): One small set, drawn for Mylder.
- [Layout](https://marcin-sieradzki.github.io/perron/docs/foundations/layout.md): Where the layout switches, and what the map screen is built from.
- [Logos](https://marcin-sieradzki.github.io/perron/docs/foundations/logos.md): The Mylder mark, and how it is used.
- [Motion](https://marcin-sieradzki.github.io/perron/docs/foundations/motion.md): Motion explains a change of place.
- [Primitive colours](https://marcin-sieradzki.github.io/perron/docs/foundations/primitive-colours.md): The palette holds Perron's raw colour values.
- [Semantic colours](https://marcin-sieradzki.github.io/perron/docs/foundations/semantic-colours.md): Every colour token in both themes.
- [Semantic spacing](https://marcin-sieradzki.github.io/perron/docs/foundations/semantic-spacing.md): The spacing, radius and border tokens.
- [Typography](https://marcin-sieradzki.github.io/perron/docs/foundations/typography.md): Three families, self-hosted.
- [Data states](https://marcin-sieradzki.github.io/perron/docs/foundations/data-states.md): Every vehicle is a vehicle dot, drawn in its line colour in one of five states.
- [Map](https://marcin-sieradzki.github.io/perron/docs/foundations/map.md): The map is the ground every panel floats over, drawn quiet so the vehicles carry the colour.

### Guidelines

- [Content design](https://marcin-sieradzki.github.io/perron/docs/content-design.md): How every word in Mylder reads: plain sentences, exact about certainty, in English, Danish, Swedish and Norwegian Bokmål.
- [Glossary](https://marcin-sieradzki.github.io/perron/docs/glossary.md): The words used to talk about Perron, Mylder's design system.

### Components

- [Contract](https://marcin-sieradzki.github.io/perron/docs/components/README.md): The rules every Perron component follows: its files, props, events, styling, words, stories and checks.
- [Spec template](https://marcin-sieradzki.github.io/perron/docs/components/TEMPLATE.md): The page every component's spec starts from.

### Specs

- [Button](https://marcin-sieradzki.github.io/perron/docs/components/button.md): A button that runs one action on the screen it sits on, such as "Show departures", in ink (`neutral`) or, for the one main action of a screen, in the brand yellow (`brand`).
- [IconButton](https://marcin-sieradzki.github.io/perron/docs/components/icon-button.md): A button that runs one action and shows it as an icon alone, such as zoom in or show help, named for screen readers by its required `label`.
- [LinkButton](https://marcin-sieradzki.github.io/perron/docs/components/link-button.md): A link that looks like a Button: it takes the reader to another page or place, such as "Back to the map" or "Open in Rejseplanen", where a Button runs an action on the screen it sits on.
- [Toolbar](https://marcin-sieradzki.github.io/perron/docs/components/toolbar.md): A bar of actions that is one tab stop, moved through with the arrow keys, such as the map's zoom and locate controls or a glass group in the top bar.
- [Brand](https://marcin-sieradzki.github.io/perron/docs/components/brand.md): The Mylder mark beside the wordmark "mylder", drawn in both themes from the tokens, for the brand slot of a top bar or a kiosk bar.
- [Count](https://marcin-sieradzki.github.io/perron/docs/components/count.md): A small pill holding how many there are, such as the "3" after "Delays" in a filter chip or the "12" after "Lines" on a tab, set in tabular figures on an inset or tinted surface.
- [FactList](https://marcin-sieradzki.github.io/perron/docs/components/fact-list.md): A short list of facts about one thing, each a name and what it is, such as "Operator: Movia" and "Last report: 08:14" about a vehicle, set as a description list in a detail panel or a report.
- [Kbd](https://marcin-sieradzki.github.io/perron/docs/components/kbd.md): Kbd shows one key of the keyboard, such as "Esc", "+" or "↑", inside help text, so a sentence like "Keys: + and − zoom, arrows pan, Esc closes panels." shows each key as a key.
- [Meter](https://marcin-sieradzki.github.io/perron/docs/components/meter.md): A bar that shows where one measured value sits within a known range, with the words that name it and the figure it reaches, such as "Reporting live" at "82%", for the share of a network's vehicles that report their position.
- [Stat](https://marcin-sieradzki.github.io/perron/docs/components/stat.md): A key figure with the words that say what it counts, such as "412" under "Reporting live", set large so it reads at a glance in a report, a panel or on the kiosk.
- [Tag](https://marcin-sieradzki.github.io/perron/docs/components/tag.md): A short word or two on a pill that marks a property of what it sits beside, such as "Night bus", "Replacement bus" or "Cancelled" on a departure or a vehicle, set in capitals in one of the intents' colours.
- [Text](https://marcin-sieradzki.github.io/perron/docs/components/text.md): Text sets a piece of text in one of the typography roles and one of the text colours, on the element the caller's document needs.
- [Alert](https://marcin-sieradzki.github.io/perron/docs/components/alert.md): A message set into the page, such as a panel or a sheet, that says in plain sentences what is happening and how much it matters, in one of the intents' tinted colours, with an optional title, an action and a close button.
- [EmptyState](https://marcin-sieradzki.github.io/perron/docs/components/empty-state.md): Stands in for a list or panel that has nothing to show, and says in plain words what is empty, why, and what to try next.
- [Progress](https://marcin-sieradzki.github.io/perron/docs/components/progress.md): A bar that shows how far a task has got, such as an export, with the words that name the task and the share done, such as "Exporting the report" at "45%", or, while how far is not known, that the task is under way.
- [Toast](https://marcin-sieradzki.github.io/perron/docs/components/toast.md): A short message that floats in at the bottom of the screen to confirm what just happened, such as "Link copied", in Alert's look and intents, and leaves again on its own after a few seconds, with an optional title, an action and a close button.
- [Checkbox](https://marcin-sieradzki.github.io/perron/docs/components/checkbox.md): A box with its words beside it that turns one setting or one answer on and off, such as "Show planned works on the map", ticked when it is on and dashed when it stands for a set that is partly on, and submitted with a form.
- [CheckboxGroup](https://marcin-sieradzki.github.io/perron/docs/components/checkbox-group.md): A named set of checkboxes that answer one question together, such as which modes the map shows, holding the ticked values as one list, with a line of help and an error message tied to the set, submitted with a form under one name, and, where the set needs one, a parent checkbox that ticks or unticks them all.
- [Field](https://marcin-sieradzki.github.io/perron/docs/components/field.md): The frame round one form control: its label above it, a line of help, and the error message under it when the value is wrong, all tied to the control for the ear by Base UI.
- [Fieldset](https://marcin-sieradzki.github.io/perron/docs/components/fieldset.md): A named group of related controls, such as the modes shown on the map or the fields of a report form: the group's name above, the caller's controls below it, and one switch that disables them all.
- [FilterChip](https://marcin-sieradzki.github.io/perron/docs/components/filter-chip.md): A pill that turns one filter on and off, such as "Delays" in the top bar's filters, with an optional icon before its words and a count after them, drawn in ink when it is on.
- [HighlightChip](https://marcin-sieradzki.github.io/perron/docs/components/highlight-chip.md): A pill that floats over the map on glass and turns one highlight of the map on and off, such as line 5C in focus with everything outside it dimmed; while it is selected, the map shows the highlight.
- [NumberField](https://marcin-sieradzki.github.io/perron/docs/components/number-field.md): The box a person types a number into, or steps up and down with its minus and plus buttons and the arrow keys, such as how many departures a board shows, framed by the Field that gives it its label, its line of help and its error message.
- [RadioGroup](https://marcin-sieradzki.github.io/perron/docs/components/radio-group.md): A named set of two to six choices of which exactly one holds at a time, such as the theme the map is drawn in, each a round mark beside its words.
- [SearchField](https://marcin-sieradzki.github.io/perron/docs/components/search-field.md): The field that finds a line or a stop: a labelled text input with the search icon, a clear button while it holds text, and a list of suggestions under it that narrows as the person types.
- [SegmentedControl](https://marcin-sieradzki.github.io/perron/docs/components/segmented-control.md): A row of two to five segments on an inset track, one of them selected, that picks how one view shows its content, such as a stop's departures by mode.
- [Select](https://marcin-sieradzki.github.io/perron/docs/components/select.md): The field that picks one value from a short, fixed list: a labelled box showing the chosen option, or a placeholder, that opens a list of options under it, with the field's help and error round it.
- [Slider](https://marcin-sieradzki.github.io/perron/docs/components/slider.md): A control for choosing a number, or a range between two numbers, along a track by dragging a thumb or pressing the arrow keys, such as how many minutes of movement the map draws behind each vehicle, with its label, its line of help and its error from Field.
- [Switch](https://marcin-sieradzki.github.io/perron/docs/components/switch.md): A two-state control that turns one setting on or off at once, such as showing vehicle trails on the map.
- [TextArea](https://marcin-sieradzki.github.io/perron/docs/components/text-area.md): The box a person types several lines of words into, such as a note about what they saw on a trip, framed by the Field that gives it its label, its line of help and its error message.
- [TextField](https://marcin-sieradzki.github.io/perron/docs/components/text-field.md): The box a person types one line of words into, such as a stop's name or an email address, framed by the Field that gives it its label, its line of help and its error message.
- [Accordion](https://marcin-sieradzki.github.io/perron/docs/components/accordion.md): A stack of headed sections, each opening and closing its own content under its heading, such as the questions on a help panel or the details of each planned work on a line.
- [Section](https://marcin-sieradzki.github.io/perron/docs/components/section.md): A titled group of content inside a panel or a page, such as a stop's departures or the lines that call at it: a small heading in capitals, an optional action beside it, and the caller's content below.
- [Separator](https://marcin-sieradzki.github.io/perron/docs/components/separator.md): A hairline that divides two groups of content, across a column (horizontal) or down a row (vertical), and tells a screen reader there is a break there.
- [Surface](https://marcin-sieradzki.github.io/perron/docs/components/surface.md): A box that sets content on one step of the surface ladder: glass or an opaque panel floating over the map, a raised card inside a panel, or an inset box, each with its fill, edge, radius, shadow and padding from the tokens.
- [Attribution](https://marcin-sieradzki.github.io/perron/docs/components/attribution.md): Attribution is the map's credits: one small strip of caption text, laid on glass in a corner of the map, that names where the map and its data come from, such as "Map data © OpenStreetMap contributors", with the caller's links to each source.
- [MapControls](https://marcin-sieradzki.github.io/perron/docs/components/map-controls.md): The map's zoom in, zoom out and centre buttons, and, when the screen handles them, a 3D toggle, a compass that turns the map back to north and a pause button for the map's motion, in one glass bar that is one tab stop, named and labelled in the reader's language.
- [Link](https://marcin-sieradzki.github.io/perron/docs/components/link.md): A link in text: words, in a sentence or standing alone, that take the reader to another page or place, such as "Departures from Rejseplanen" or "Show all departures", underlined in the colour of the text around them.
- [Tabs](https://marcin-sieradzki.github.io/perron/docs/components/tabs.md): A row of tabs that switches between views of one thing, one panel showing at a time, such as a stop's departures, its lines and its disruptions in the detail panel.
- [AlertDialog](https://marcin-sieradzki.github.io/perron/docs/components/alert-dialog.md): A modal question over the map and a `color.scrim` backdrop, such as "Clear all filters?", that one of its actions or Esc answers: it moves focus to the first of its actions, Cancel, wraps Tab inside the popup while focus is in it, and on close gives focus back to the button that opened it.
- [Dialog](https://marcin-sieradzki.github.io/perron/docs/components/dialog.md): A modal panel over the map and a `color.scrim` backdrop, such as the help with the keys or a question before clearing every filter, which holds focus until it closes and then gives it back to the button that opened it.
- [MapTooltip](https://marcin-sieradzki.github.io/perron/docs/components/map-tooltip.md): The map's hover card: a small inverse card of the caller's words, placed against a point on the map or an element, shown while the pointer rests on a vehicle or a stop.
- [Menu](https://marcin-sieradzki.github.io/perron/docs/components/menu.md): A list of actions and settings that opens from a button, such as the top bar's menu of kiosk mode, help and the map's display settings, moved through with the arrow keys and closed with Esc, a choice or a press outside it.
- [Popover](https://marcin-sieradzki.github.io/perron/docs/components/popover.md): A small glass panel anchored to the button that opens it, such as a short explanation of the data or a few settings, which leaves the map and the rest of the screen usable while it is open and gives focus back to its button when it closes.
- [Sheet](https://marcin-sieradzki.github.io/perron/docs/components/sheet.md): A panel that slides in from the edge of the screen and floats over the map: the overview sheet and the detail panel on a wide screen, and the bottom sheet they become on a phone (Layout).
- [Tooltip](https://marcin-sieradzki.github.io/perron/docs/components/tooltip.md): A short visual label beside a control, such as "Zoom in" over the map's zoom button, shown while a mouse pointer rests on the control or the keyboard focuses it, for sighted mouse and keyboard users.
- [Delay](https://marcin-sieradzki.github.io/perron/docs/components/delay.md): How late or early a vehicle or departure runs, as a short figure in the delay's colour, such as "+3 min", with the same thing said in words to a screen reader.
- [LineBadge](https://marcin-sieradzki.github.io/perron/docs/components/line-badge.md): A small filled label that names one Copenhagen line, a metro, S-tog, bus or regional train, in the operator's own line colour.
- [NormalService](https://marcin-sieradzki.github.io/perron/docs/components/normal-service.md): The good news about a line: a mark and the words "Running normally" in the success colour, set where a line's disruptions would be listed, such as a line's detail panel or a row in a list of lines, when the line has none.
- [SeverityLabel](https://marcin-sieradzki.github.io/perron/docs/components/severity-label.md): The kind of a disruption on a line, as its icon and its name in its colour, such as a warning triangle and "Delays", heading a disruption card or a row in a list of disruptions.
- [StationIcon](https://marcin-sieradzki.github.io/perron/docs/components/station-icon.md): A small round marker that stands for a stop or station beside its name, in the colour of the line that serves it, as in a detail panel's header and on each stop of a stop timeline.
- [VehicleDot](https://marcin-sieradzki.github.io/perron/docs/components/vehicle-dot.md): One vehicle drawn as a dot in its line's colour, its shape saying how fresh the vehicle's data is, one of the five states on Data states, for rows, lists and legends beside the map.
- [VehicleDotLabel](https://marcin-sieradzki.github.io/perron/docs/components/vehicle-dot-label.md): A vehicle's data state as its dot and its word side by side, such as a solid dot and "Live", for rows, tables and strips that say how fresh a vehicle's data is.
- [VehicleDotLegend](https://marcin-sieradzki.github.io/perron/docs/components/vehicle-dot-legend.md): The key to the vehicle dots: the five data states, each as its dot's shape beside its word, in the order Data states lists them, for the bottom of the map and for any panel that explains the dots.
- [DataStateKey](https://marcin-sieradzki.github.io/perron/docs/patterns/data-state-key.md): The key to how sure the map is: the five data states, each as its dot and word, how many vehicles are in it, and one sentence saying what it means in the app's own thresholds, such as "Live 412, Reported by the vehicle in the last 2.5 min", for the panel that explains how much of what the map shows is live.
- [DataStatePanel](https://marcin-sieradzki.github.io/perron/docs/patterns/data-state-panel.md): Holds a data view, such as a vehicle table or a day's chart, and shows the caller's content once its data is in, or in its place a calm message while the data loads, when there is none, or when it could not be loaded.
- [DayProfileChart](https://marcin-sieradzki.github.io/perron/docs/patterns/day-profile-chart.md): How one count moves across a day, such as the vehicles in service each hour from midnight to midnight: one line in the chart colour, the area under it, a 24-hour time axis, and a value tip that gives the time and the count under the pointer or the keys.
- [ReportStrip](https://marcin-sieradzki.github.io/perron/docs/patterns/report-strip.md): How a set of vehicles is reporting right now: how many are in each of the five data states, each count beside its state's dot and word, in one row, for the data views and panels that say how much of what the map shows is live.
- [SourceLine](https://marcin-sieradzki.github.io/perron/docs/patterns/source-line.md): One caption line that names where a panel's data comes from and the time of that data, "Rejseplanen · updated 08:14:05", with an optional second source and its own time, each time worded, when the app gives its own words, by what it is of, "Rejseplanen · position 08:14:05 · times 08:14:20", and two states: paused, and may be out of date.
- [VehicleTable](https://marcin-sieradzki.github.io/perron/docs/patterns/vehicle-table.md): The vehicles in service as a table, one row each: the vehicle's line, where it is going, how fresh its position is, optionally when it last reported, and how late it runs, the map's text alternative, in the order the caller sorts it by.
- [DepartureRow](https://marcin-sieradzki.github.io/perron/docs/patterns/departure-row.md): One departure from a stop, as one row of the stop's list of departures: the line's badge and where it goes, how fresh the vehicle's data is, and how soon it leaves, with its timetabled time, or the expected time with the timetabled one struck through beside it, and how late it runs, or, for a line that runs to an interval, how often it runs.
- [DetailHeader](https://marcin-sieradzki.github.io/perron/docs/patterns/detail-header.md): The top of the detail panel: it names what the panel shows, a line's vehicle by its line badge and destination or a stop by its marker and name, with a line under it and room for the panel's own controls.
- [DetailPanel](https://marcin-sieradzki.github.io/perron/docs/patterns/detail-panel.md): The panel that opens on the right of the map when a rider picks a vehicle or a stop: a Sheet headed by a DetailHeader, which names the line's vehicle or the stop, with the close button at the end of its header row and the caller's details below, and the bottom sheet it becomes on a phone.
- [StopTimeline](https://marcin-sieradzki.github.io/perron/docs/patterns/stop-timeline.md): The stops of one vehicle's trip, in order, on a rail in its line's colour: each stop's name, its time, the time real-time data expects there and how late the vehicle runs there, with the stops already passed set back and the next stop marked, as in a vehicle's detail panel.
- [DisruptionCard](https://marcin-sieradzki.github.io/perron/docs/patterns/disruption-card.md): A card for one disruption on Copenhagen's network: its kind, the lines it touches, a short title and a plain sentence on what is happening, then the stops it affects, when it runs and one action, set as a raised card in a list of disruptions or in a line's detail panel.
- [ScaleBar](https://marcin-sieradzki.github.io/perron/docs/patterns/scale-bar.md): The map's scale: a short bar on glass at the bottom right of the map, with the ground distance its length spans written above it, such as "300 m" or "2 km", which shortens and lengthens as the map zooms.
- [KioskBar](https://marcin-sieradzki.github.io/perron/docs/patterns/kiosk-bar.md): The bar a Mylder screen in kiosk mode shows over the map, for a display people read from across a room and never touch: the Mylder brand, a few key figures about the network, and the clock, large.
- [TopBar](https://marcin-sieradzki.github.io/perron/docs/patterns/top-bar.md): The bar along the top of Mylder's moving map, on every screen size: the Mylder brand, the status line that says when the map's data is from, the screen's filters and its tools, on one glass panel over the map.
