Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions mypolitics/spec/quiz/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,36 @@
## Contents
| File | Description |
|---|---|
| [Archetype](./archetype.md) | The named archetype, its match, its description and the ranking |
| [Double axis chart](./double-axis-chart.md) | Two opposing orientations on one bar |
| [Header](./header.md) | The top of the result: one orientation, its confidence and the tabs |
| [Horizontal bar chart](./horizontal-bar-chart.md) | A ranked list of orientations, flat or grouped |
| [Module wrapper](./module-wrapper.md) | The frame every result module sits in |
| [Multi axis chart](./multi-axis-chart.md) | Groups of axes, collapsed to their winners |
| [Nolan chart](./nolan-chart.md) | Two axes crossed into a map |
| [Single axis chart](./single-axis-chart.md) | One orientation, one bar |
| [Traits](./traits.md) | The traits a taker earned, as a set of pills |
| [Universal axis](./universal-axis.md) | The bar every result module is built from |

## Build order
The result modules lean on each other, so they are built in steps. Everything inside one step can be built at the same time; a step needs the ones above it only where the last column says so.

| Step | Spec | Needs first |
|---|---|---|
| 0 | [Universal axis](./universal-axis.md) | Nothing |
| 0 | [Module wrapper](./module-wrapper.md) | Nothing |
| 1 | [Single axis chart](./single-axis-chart.md) | Universal axis, module wrapper |
| 1 | [Double axis chart](./double-axis-chart.md) | Universal axis, module wrapper |
| 1 | [Traits](./traits.md) | Module wrapper |
| 1 | [Header](./header.md) | Nothing |
| 1 | [Horizontal bar chart](./horizontal-bar-chart.md) | Universal axis, module wrapper |
| 2 | [Multi axis chart](./multi-axis-chart.md) | Double axis chart, for the lead rule |
| 2 | [Archetype](./archetype.md) | Header, for the match bands; horizontal bar chart, for the ranked row |
| 3 | [Nolan chart](./nolan-chart.md) | Multi axis chart, for the axis row |

Four rules are written once and used by more than one module. Each lives in the spec that is built first:

- **Lead rule** - which side of a pair wins, and when it is a tie. In the [double axis chart](./double-axis-chart.md).
- **Match bands** - no match, partial match, match. In the [header](./header.md).
- **Ranked row** - a name, a badge and a one-sided bar. In the [horizontal bar chart](./horizontal-bar-chart.md).
- **Axis row** - a heading naming an axis and its lead, over a double-sided bar. In the [multi axis chart](./multi-axis-chart.md).
148 changes: 148 additions & 0 deletions mypolitics/spec/quiz/archetype.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# Archetype

> Technical specification of the module that names the archetype a taker came out closest to.

Docs: [Archetype](../../docs/modules/quiz/results/modules/archetype.md). | Design: [Figma](https://www.figma.com/design/DIInW4qrIxsgXmKbSHukNm/mypolitics-app?node-id=5513-28187)

![Archetype states, from no match to the full ranking](../../assets/archetype.png)

## Kind
Front-end

## Scope
One result module: a [module wrapper](./module-wrapper.md) around the leading archetype - its name, its match on a one-sided [universal axis](./universal-axis.md), and a description - with two controls that open the full description and the ranking of every other archetype. The module picks the leader from what it is given and remembers what the taker opened; it computes no score.

Not covered here:

- **How a bar draws a value or a comparison** - the [universal axis](./universal-axis.md) spec.
- **The three match bands** - the [header](./header.md) spec.
- **The row a ranking is made of** - the ranked row in the [horizontal bar chart](./horizontal-bar-chart.md) spec.
- **The card frame and its two buttons** - the [module wrapper](./module-wrapper.md) spec.
- **Whether an author's description is acceptable** - moderation.

## Data
| Input | Data | Meaning | Rules |
|---|---|---|---|
| Title | Text | The card's name | Optional, as in the wrapper |
| Archetypes | A list of orientation, match, short description and full description | Every archetype the quiz scores | Descriptions are optional |
| Comparison | The other party, and their match per archetype | A friend's matches on the same bars | Optional |
| Statistics action, info action | Present or absent | The wrapper's two buttons | Optional, independent |

An archetype is an orientation: a name, an image and a colour. A **match** is a number from 0 to 100. Names and both descriptions are author-supplied and untrusted.

The **leader** is the archetype with the highest match. With equal matches, the first in the order given leads.

## Interface
| Direction | Name | Shape | Notes |
|---|---|---|---|
| In | Everything in Data | - | Passed in by the result screen |
| Out | Statistics requested | An event with no payload | Passed through from the wrapper |
| Out | Info requested | An event with no payload | Passed through from the wrapper |

Which view is open is the module's own state.

## Behaviour

### Leader
| Case | Behaviour |
|---|---|
| The leader's match is a match or a partial match | Its name, and a one-sided bar with its image on the cap |
| The leader's match is no match | The "no match" wording, quiet, in place of the name, and an empty track with no cap |
| The bar | No marker and no labels |
| The bar's colour | The colour of the match's band, never the archetype's own colour |

### Views
The body under the leader shows one of three views.

| View | Content |
|---|---|
| Summary | The leader's short description |
| Full description | The leader's full description |
| Ranking | Every other archetype as a ranked row, highest match first, each bar in the colour of its own band |

The ranking is never cut: it shows every archetype it has.

### Controls
Two controls sit at the foot of the card, description first, ranking second.

| Case | Behaviour |
|---|---|
| Description control pressed | Opens the full description. Pressed again, returns to the summary |
| Ranking control pressed | Opens the ranking. Pressed again, returns to the summary |
| One view open and the other control pressed | Switches straight to the other view |
| The open view's control | Marked as active |
| Ranking control, closed | Previews the first few images from the ranking |
| Leader has no full description | No description control |
| No other archetypes | No ranking control |
| Neither control applies | No foot at all |

### No match
| Case | Behaviour |
|---|---|
| Summary | No description. The module does not describe an archetype it declined to name |
| Description control | Not shown |
| Ranking | Shows every archetype, the leader included, since none of them is drawn above it |

### Comparison
| Case | Behaviour |
|---|---|
| Comparison value for an archetype | Passed to that archetype's bar, on the leader and in the ranking |
| No comparison value for an archetype | That bar is drawn without an overlay |
| Comparison present | The leader and the order of the ranking do not change. The card stays the taker's |

## States and lifecycle
| State | Condition | What is possible in it |
|---|---|---|
| Summary - "standard" | Nothing opened | Open the full description, open the ranking |
| Full description | Description control active | Return to the summary, switch to the ranking |
| Ranking | Ranking control active | Return to the summary, switch to the full description |
| No match | The leader is below 50 | Open the ranking |

The module starts in the summary. The state lasts as long as the card is on screen and is not saved anywhere.

## Rules and constraints
- **No match is a result.** The module never names the least bad option.
- **One leader.** Runners-up live in the ranking, however close they are.
- **Descriptions are plain text.** Paragraph breaks are kept; markup, links and anything else in the text are shown as written and never interpreted.
- **A band colours a bar, an orientation does not.** Every bar on this card takes its colour from its match's band, so the ranking reads as strong, loose and none at a glance.
- **The module never hides itself.** With no archetypes it is an empty card under its title.

## Invalid and edge input
| Input | Behaviour |
|---|---|
| No archetypes | An empty card under its title |
| Match absent or not a number | Treated as zero, and listed last |
| Match outside 0-100 | Clamped |
| Short description missing, full description present | The summary has no text; the description control still opens the full one |
| Full description identical to the short one | No description control |
| Description that is only whitespace | Treated as missing |
| Archetype without an image | Its cap is drawn with colour only, and it is left out of the ranking control's preview |
| Archetype name longer than the row | Truncated; complete for assistive technology |
| Comparison value for an archetype not in the list | Ignored |

Nothing here throws.

## Non-functional

### Rendering contexts
The result screen, comparison mode and the generated result image. In the image the caller passes no actions and the module is drawn in the summary, since nothing there can be opened.

### Accessibility
- The leader's name is a heading inside the card.
- Each bar announces its archetype, match and comparison value, as the universal axis specifies.
- A band is never carried by colour alone: every bar has its number, and no match changes the wording.
- The two controls are buttons with text names, since each is drawn as an icon. Each says whether its view is open, and works from the keyboard.
- The ranking is an ordered list.
- When a view opens, focus stays on the control that opened it.

## Dependencies
**Build order: step 2.** Needs the [universal axis](./universal-axis.md) and the [module wrapper](./module-wrapper.md), both built, and from step 1 the [header](./header.md) for the match bands and the [horizontal bar chart](./horizontal-bar-chart.md) for the ranked row.

Relies on:

- [Archetype doc](../../docs/modules/quiz/results/modules/archetype.md) - the idea this implements.
- [Universal axis](./universal-axis.md) and [module wrapper](./module-wrapper.md).
- [Header](./header.md) - the match bands.
- [Horizontal bar chart](./horizontal-bar-chart.md) - the ranked row.

Relied on by nothing in this folder. The [short results card](../../docs/modules/quiz/results/short-results-card.md) reads the same leader and match.
114 changes: 114 additions & 0 deletions mypolitics/spec/quiz/double-axis-chart.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# Double axis chart

> Technical specification of the module that shows two opposing orientations on one bar.

Docs: [Double axis chart](../../docs/modules/quiz/results/modules/double-axis-chart.md). | Design: [Figma](https://www.figma.com/design/DIInW4qrIxsgXmKbSHukNm/mypolitics-app?node-id=5500-2387)

![Double axis states and examples](../../assets/double-axis-chart.png)

## Kind
Front-end

## Scope
One result module: a [module wrapper](./module-wrapper.md) whose title names the leading side and whose body is a single double-sided [universal axis](./universal-axis.md) with both poles labelled. It holds no state and computes no score.

It also defines the **lead rule** - which side of a pair is the winner, and when there is none. The [multi axis chart](./multi-axis-chart.md) uses the same rule.

Not covered here:

- **How the bar draws two values, a marker or a comparison** - the [universal axis](./universal-axis.md) spec.
- **The card frame and its two buttons** - the [module wrapper](./module-wrapper.md) spec.
- **Which two orientations are paired** - quiz configuration. The pairing is the author's.

## Data
| Input | Data | Meaning | Rules |
|---|---|---|---|
| Start entry | Orientation and value | The pole on the left cap | Required |
| End entry | Orientation and value | The pole on the right cap | Required |
| Marker | Position 0-100, or off | The reference line on the bar | Defaults to 50 |
| Comparison | Orientation and value | The other party on the same bar | Optional |
| Statistics action, info action | Present or absent | The wrapper's two buttons | Optional, independent |

An orientation carries a name, an image and a colour, as in the universal axis.

The two values are drawn as given. The module does not make them add up to a hundred: a pair of 47 and 31 leaves a gap in the middle of the bar.

## Interface
| Direction | Name | Shape | Notes |
|---|---|---|---|
| In | Everything in Data | - | Passed in by the result screen |
| Out | Statistics requested | An event with no payload | Passed through from the wrapper |
| Out | Info requested | An event with no payload | Passed through from the wrapper |

## Behaviour

### Lead
The lead is decided on the values as the taker sees them - rounded to whole numbers - so the title can never contradict the bar.

| Case | Lead |
|---|---|
| One displayed value is higher | That side |
| Both displayed values are equal, including both zero | None. It is a tie |
| One value absent | The side that has a value, if it is above zero |
| Both values absent | None |

### Title
| Case | Behaviour |
|---|---|
| There is a lead | A chip with the leading orientation's icon and name, filled with its colour |
| Tie | A neutral chip naming both poles, start first, with no icon and no colour |
| Leading orientation without an image | The chip shows the name alone |

The title names the side the taker landed on, not the axis. The chip is never interactive.

### Bar
| Case | Behaviour |
|---|---|
| Both values present | A double-sided bar, each side filled from its own cap |
| Values that do not reach 100 together | The gap stays in the middle |
| One value absent | That side has no fill and no number; its cap and label still show |
| Both values absent | An empty double-sided track with both caps and labels |
| Comparison present | Passed to the bar unchanged |

Labels are always on: each pole is named under its own cap.

## Rules and constraints
- The module adds nothing to the bar's own rules. Value placement, hidden small values, the marker and the comparison band behave exactly as the universal axis specifies.
- A narrow lead is still a lead. 51 against 49 names a side; the marker is what shows how narrow it is.
- The module never hides itself.

## Invalid and edge input
| Input | Behaviour |
|---|---|
| Value outside 0-100 | Clamped by the bar. The lead uses the clamped values |
| Values that exceed 100 together | Scaled by the bar. The lead uses the values as given |
| Value not a number | Treated as absent |
| Orientation name missing | Its label row stays reserved and empty. If it leads, the chip shows the image alone |
| Orientation names longer than the room | Each label is truncated by the bar, the title by the wrapper; both stay complete for assistive technology |
| Orientation without a colour | The neutral fallback, on the chip and on the bar |
| Both poles are the same orientation | Drawn as given. The module does not check the pairing |

Nothing here throws.

## Non-functional

### Rendering contexts
The result screen, comparison mode and the generated result image. In the image the caller passes no actions.

### Accessibility
- The card is named after the leading orientation, or after both poles on a tie, through the accessible name the wrapper takes for a component title.
- The bar announces both orientations, both values and the comparison value, as the universal axis specifies.
- The lead is never carried by colour alone: the title says it in words.

## Dependencies
**Build order: step 1.** Needs the [universal axis](./universal-axis.md) and the [module wrapper](./module-wrapper.md), both built. It can be built alongside the [single axis chart](./single-axis-chart.md), [traits](./traits.md) and the [header](./header.md).

Relies on:

- [Double axis chart doc](../../docs/modules/quiz/results/modules/double-axis-chart.md) - the idea this implements.
- [Universal axis](./universal-axis.md) and [module wrapper](./module-wrapper.md).

Relied on by:

- [Multi axis chart](./multi-axis-chart.md) - uses the lead rule for every group, so this comes first.
- [Nolan chart](./nolan-chart.md) - opens into two double-sided bars.
Loading
Loading