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
2 changes: 2 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@
- [ ] Screenshots don't show personal info (notifications, accounts, contacts, work data)
- [ ] New themes in `themes/` pass `CommunityThemesTest` and have `"iconPack": null`
- [ ] No GPL code and nothing that needs root
- [ ] Follows the [Folio Standards](../docs/standards/README.md); rule IDs it touches: <!-- e.g. ADP-1, DYN-11 -->
- [ ] An AI agent helped: <!-- leave unticked if not. If ticked: which tool, and what it did. Bug tests and fixes only, see docs/standards/ai-contributions.md -->
12 changes: 10 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,14 @@ get in the way of helping:

Keep changes focused. In the PR, describe what's different for someone using Folio, and how you checked it.

Read the [code map](docs/architecture.md) for ownership, persistence and gesture constraints. The [user guide](docs/user-guide.md)
Read the [code map](docs/architecture.md) for ownership, persistence and gesture constraints, and the
[Folio Standards](docs/standards/README.md) for the rules a change follows (cite rule IDs like `ADP-1` in the PR). The [user guide](docs/user-guide.md)
and [troubleshooting guide](docs/troubleshooting.md) describe the behavior changes should preserve.

Folio builds with Java 17:

```bash
./gradlew :app:testDebugUnitTest :app:lintDebug :app:assembleDebug
./gradlew :app:testDebugUnitTest :market:testDebugUnitTest :app:lintDebug :market:lintDebug :app:assembleDebug
```

CI runs the same command on every pull request. Use disposable emulators for instrumentation tests. Fixtures that change
Expand All @@ -49,6 +50,13 @@ Preserve one-page-per-swipe behavior, native widget scrolling and long-press pic
Home-page retention. Keep access optional and explain it where it's used. Layouts follow screen size, not device
checks. Tests should reproduce failures or protect meaningful behavior.

## Using AI tools

If an AI agent helps with your contribution, it may only **test for and fix bugs**: reproduce a problem, write a test
that shows it, and make the smallest fix. New features, UI changes, refactors and translations need a person. Say in the
pull request which tool you used and what it did. Themes, tweaks and Market packages made with AI are welcome,
labelled "AI-assisted". The full rules are in [AI contributions](docs/standards/ai-contributions.md).

## Translating Folio

Folio's text lives in `app/src/main/res/values/strings.xml`. Everything in that file can be translated; text still
Expand Down
30 changes: 30 additions & 0 deletions docs/adr/0006-standards.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# 0006: Folio Standards are the build contract

- **Status:** proposed, 2026-09-22

## Context

Folio's design and engineering rules were spread across the README, CONTRIBUTING.md, architecture.md, review notes
and code comments. Each new feature risked inventing its own spacing, springs, breakpoints or state handling: there
are about 1,630 inline `.dp` values, around 40 hand-picked springs beside `FolioMotion`, and breakpoints of 560, 600,
650, 700 and 920 written as bare numbers.

AI coding agents now write code for contributors too. Features and design need the product's direction, and a
launcher runs under every app on the phone, so code nobody fully understands is a bigger risk than usual.

## Decision

- **`docs/standards/` is the contract** for design and code: eleven standards with numbered MUST / SHOULD / MAY rules,
each with the current state and the gaps.
- **Rules marked "(new code)" apply as code is written or rewritten.** Nobody has to fix the whole tree first.
- **Breaking a MUST needs a recorded exception:** a comment at the site, and an ADR if it lasts.
- **Outside AI agents only test for and fix bugs.** Anything else an AI helps make that ships to users (themes, tweaks,
packages) is labelled AI-assisted.

## Consequences

- **Good:** reviews can cite a rule instead of a preference. New surfaces share one visual and motion vocabulary. The
Gaps tables give a ranked list of clean-up work.
- **Bad:** the standards have to be kept current as code lands, or they turn into fiction. Some MUSTs (tokens, haptics,
keyboard) describe code that doesn't exist yet, so early changes carry the cost of adding it.
- **Bad:** restricting AI agents turns away some well-meant feature work, which now has to start as an issue.
1 change: 1 addition & 0 deletions docs/adr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ Short records of decisions that shape Folio, why they were made, and what they c
| 0003 | Script engine (QuickJS or LuaJ) | Decided in Phase 9 |
| [0004](0004-declarative-first.md) | Packages are declarative first; no downloaded executable code | Accepted |
| [0005](0005-org-json.md) | Keep org.json for Market parsing | Accepted |
| [0006](0006-standards.md) | Folio Standards are the build contract; outside AI agents only test and fix bugs | Proposed |
2 changes: 2 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Contributor code map

This page says how Folio works. The [Folio Standards](standards/README.md) say how changes to it should be built.

Folio is a Kotlin/Jetpack Compose Android Home application with one normal app module. It owns its Home content, dock, editing UI and widget hosts. Android owns the secure lock screen, recents, notification panels and system app transitions. Google owns the content and input inside its Discover feed.

## Where to start
Expand Down
5 changes: 5 additions & 0 deletions docs/sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ Make themes, tweaks and layouts for the Folio Market.
- **Data only:** JSON and images, plus an optional sandboxed script. No DEX, JAR or native code.
- **Declare everything:** list every permission your package uses. The privacy label is built from that list.
- **Credit and licensing:** credit anything that inspired you, and don't include GPL code.
- **Made with AI:** start `description` with "AI-assisted (tool name)." and keep that label on updates and forks
([AI-6](../standards/ai-contributions.md)).
- **Look, read and fit like Folio:** contrast, screens, real screenshots and testing are in the template's
[STANDARDS.md](https://github.com/McCal-Codes/folio-source-template/blob/main/STANDARDS.md), drawn from Folio's
[standards](../standards/README.md).

## Working on the parsers

Expand Down
113 changes: 113 additions & 0 deletions docs/standards/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Folio Standards

[architecture.md](../architecture.md) says how Folio works. These standards say how Folio should be built. When you
add or change anything, the change follows these rules, or it records why it doesn't.

They are written from the code as it is on 22 Sep 2026 (0.6.6), checked against current Android, Compose, Apple HIG
and WCAG guidance. Each standard has three parts:

1. **Rules**, numbered so a PR or review can cite them (`ADP-3`, `DYN-7`).
2. **Where Folio is today**: measured facts with file references, good and bad.
3. **Gaps**: what it takes to meet the rules, ranked, with a size (S / M / L).

## The governing principle

> **Folio adapts its behaviour without fragmenting its identity.**
>
> Folio is the same launcher on a flip cover, a phone, a foldable, a tablet and a resizable window. Layout may change
> a lot as available space, posture, input or system capability changes. The interaction model, the state, the words
> and the visual language stay the same.
>
> Behaviour may depend on a device only when it reflects a real capability or a verified hardware fact. Device names,
> manufacturers and model checks never stand in for measuring the window or detecting a capability.

Three smaller principles follow from it and show up throughout:

- **State decides, motion explains.** Meaningful state drives dynamic UI. Animation shows the change and never owns it.
- **Adaptive state decides the destination, motion is the journey.** Every layout is correct with animations off.
- **Everything works without root, without optional permissions and without the network.** Those only add.

## Normative words

As in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119):

| Word | Meaning |
|---|---|
| **MUST** / **MUST NOT** | Required. Breaking it needs a recorded exception (below). |
| **SHOULD** / **SHOULD NOT** | Expected. You may deviate for a reason you can state in the PR. |
| **MAY** | Allowed, your call. |

A rule marked **(new code)** applies to new and rewritten code now, and to existing code as its Gap item is done.
Nobody has to fix the whole tree before shipping a feature.

## The standards

| Standard | Prefix | Owns |
|---|---|---|
| [Design](design.md) | `DES` | Visual language, tokens, components, where iOS inspiration ends |
| [Dynamic UI](dynamic-ui.md) | `DYN` | Live, animated and context-driven elements, motion tokens, the D1 to D5 classes |
| [Interaction](interaction.md) | `INT` | Gestures, priority, cancellation, haptics, keyboard and pointer |
| [Adaptive layout](adaptive-layout.md) | `ADP` | Window size, folds and hinges, insets, multi-window |
| [Compose](compose.md) | `CMP` | How Folio's Kotlin and Compose code is written |
| [State and data](state-data.md) | `STA` | Who owns each piece of state, persistence, migrations |
| [Performance](performance.md) | `PRF` | Critical journeys, budgets, measuring |
| [Accessibility](accessibility.md) | `A11Y` | TalkBack, targets, contrast, text size, Reduce Motion, RTL |
| [Privacy and permissions](privacy-permissions.md) | `PRV` | Access, network, data, the System Bridge tier |
| [Testing](testing.md) | `TST` | What gets tested where, and what "done" means |
| [AI contributions](ai-contributions.md) | `AI` | What outside AI agents may do (bug testing and fixing only) and how AI-made add-ons are labelled |

Where standards overlap, the more specific one wins: Dynamic UI over Design for motion, Accessibility over Design for
contrast and size, Privacy over everything for what leaves the phone.

## What "Folio quality" means

A change is Folio quality when:

1. It works on every window in the support envelope ([ADP](adaptive-layout.md)), folded and unfolded, both
orientations, with a hinge half open.
2. It uses Folio's tokens and components, so it looks like it was always there ([DES](design.md)).
3. Its state has one owner, and it survives rotation, folding and process death where that matters ([STA](state-data.md)).
4. Its motion can be interrupted and it is correct with animations off ([DYN](dynamic-ui.md)).
5. TalkBack can reach and operate it, and it holds up at 200% text ([A11Y](accessibility.md)).
6. It works when optional access is refused and explains access where it asks ([PRV](privacy-permissions.md)).
7. It has a test that fails without it, where a test is possible ([TST](testing.md)).
8. UI changes were shown as a Mockup Lab scene or screenshots before they were committed.

## Exceptions

Breaking a MUST is sometimes right: a vendor bug, a platform gap, an audited workaround. When you do:

- Put a comment at the site starting `Standards exception (ADP-2):` with the reason and the condition for removing it.
- If it shapes more than one file or is meant to last, write an ADR in [docs/adr](../adr/README.md).
- List it in the standard's **Recorded exceptions** section.

`CameraArea.kt`'s SM-F971 camera rectangle and `DiscoverBounds.kt`'s Window Extensions 8 to 10 guard are the model:
narrow, versioned, and they fall back to normal behaviour.

## Changing a standard

Standards change by PR like code. A change that loosens a MUST, or adds a new one, also gets an ADR. Keep "Where
Folio is today" current when a Gap item lands, the same way the update map is kept current.

## For AI coding agents

If you're working for a contributor outside the maintainer, read [AI contributions](ai-contributions.md) first: you
may only test for and fix bugs, and the pull request must say an agent was used.

Read this file, then only the standards your change touches. Cite rule IDs in commit messages and PR descriptions when
a rule shaped the change ("keeps ADP-1: branches on window width, not model"). Don't mark work done until the
standard's checklist is met, and report any rule you couldn't meet instead of skipping it quietly.

## Sources

- Android: [adaptive apps](https://developer.android.com/develop/ui/compose/layouts/adaptive),
[window size classes](https://developer.android.com/develop/ui/compose/layouts/adaptive/use-window-size-classes),
[foldables](https://developer.android.com/develop/ui/compose/layouts/adaptive/foldables/make-your-app-fold-aware),
[app architecture](https://developer.android.com/topic/architecture),
[Compose performance](https://developer.android.com/develop/ui/compose/performance),
[core app quality](https://developer.android.com/docs/quality-guidelines/core-app-quality),
[large-screen quality](https://developer.android.com/docs/quality-guidelines/adaptive-app-quality).
- Apple: [Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines) (motion,
accessibility, layout, materials).
- W3C: [WCAG 2.2](https://www.w3.org/TR/WCAG22/) for contrast and target size.
- [Laws of UX](https://lawsofux.com), as applied in the 17 to 19 Sep 2026 review.
99 changes: 99 additions & 0 deletions docs/standards/accessibility.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Accessibility standard (`A11Y`)

Accessibility shapes the components, not a clean-up pass at the end. These rules follow Android's core app quality
guidelines, WCAG 2.2 AA and Apple's HIG.

## Rules

### Targets

- **A11Y-1 MUST** give every interactive element a hit area of at least 48 × 48dp. The glyph can be smaller; grow the
hit area with `minimumInteractiveComponentSize()` or padding.
- **A11Y-2 MUST** keep hit areas from overlapping, and stable while their element moves ([DYN-19](dynamic-ui.md)).

### Screen readers

- **A11Y-3 MUST** give icon-only controls a content description from `strings.xml`, saying what it does ("Close
folder"), not what it looks like.
- **A11Y-4 MUST** hide decorative images from TalkBack (`contentDescription = null`) and merge a row into one node
where it reads as one thing.
- **A11Y-5 MUST** expose state with semantics, not colour or position alone: `Role.Switch` with `toggleableState`,
`selected`, `stateDescription` for things like "Page 2 of 4", `heading()` for section titles.
- **A11Y-6 MUST** give every drag or gesture an accessible alternative: custom actions (like `moveActions`), a menu
item, or a button ([INT-2](interaction.md)).
- **A11Y-7 MUST** announce important changes with a polite live region: island notices, page changes, a finished
install, an undo offer.
- **A11Y-8 SHOULD** keep TalkBack order the same as visual order, including across the two panes when unfolded.

### Seeing

- **A11Y-9 MUST** meet contrast of 4.5:1 for normal text and 3:1 for large text, icons and control boundaries,
including over a bright wallpaper. `FolioColors.SecondaryLabel` (white at .6) and white at .55 need a scrim or a
stronger value on light backgrounds.
- **A11Y-10 MUST NOT** use colour as the only signal (a red dot also has a shape or label; an active Focus also has
text).
- **A11Y-11 MUST** support Reduce Transparency and high contrast through `LocalSolidGlass`.

### Text size

- **A11Y-12 MUST** use `sp` for text and keep layouts working at 200% font scale: text wraps, rows grow, nothing is
cut off. Prefer wrapping to `maxLines = 1` with an ellipsis where there's room.
- **A11Y-13 MAY** cap scaling only for glanceable chrome where the system does too (status icons, the Side Bar clock),
and must say so in a comment.

### Motion

- **A11Y-14 MUST** honour Reduce Motion through `LocalReduceMotion` ([DYN-11](dynamic-ui.md)).
- **A11Y-15 MUST NOT** flash more than three times a second.

### Language and direction

- **A11Y-16 MUST** keep every user-visible string translatable, with plurals as plurals ([DES-21](design.md)).
- **A11Y-17 SHOULD** work right to left: use `start` / `end`, AutoMirrored icons, and never hard-code
`LayoutDirection.Ltr` except for drawing that really is direction-free. Folio's left-handed mode is a separate choice
and must not replace RTL.

### Input

- **A11Y-18 MUST** be operable by keyboard and Switch Access where it's operable by touch
([INT-16](interaction.md)).
- **A11Y-19 SHOULD** use `getRecommendedTimeoutMillis` for anything that disappears on its own.

## Checklist

- [ ] TalkBack: every control reachable, named, and its state read
- [ ] 200% font scale in the cover and inner windows
- [ ] Contrast checked over a white and a black wallpaper
- [ ] Animations off
- [ ] Reduce Transparency / high contrast
- [ ] Keyboard only

## Where Folio is today

Good:

- 48dp rows and dock minimums (`LayoutModel.kt`), 44dp jiggle remove, `minimumInteractiveComponentSize` in 7 files.
- 77 content descriptions, 62 semantics blocks, custom move actions mirrored on the keyboard.
- `LocalReduceMotion` and `LocalSolidGlass` exist and are used.
- `getRecommendedTimeoutMillis` for Market notices.

Not yet:

- Targets under 44dp without a larger hit area: page dots 28dp (`LauncherScreen.kt:882`), folder controls 30dp
(`FolderPanel.kt:96`), AppPanel transport 30dp (`AppPanel.kt:142`), widget options close 32dp (`HomeWidgets.kt:362`),
picker close 36dp (`WidgetPicker.kt:237`), search clear 36dp (`IosControls.kt:106`).
- Widget picker secondary text scores 2.59:1 (`WidgetPicker.kt:228`).
- 51 ellipses and 68 `maxLines = 1`; no 200% pass yet.
- 3 live regions; no focus rings; no RTL check; `Role.Switch` used once.

## Gaps

| # | Work | Size |
|---|---|---|
| 1 | Grow the six small hit areas above | S |
| 2 | A `secondaryLabel` token that meets 4.5:1 over bright wallpapers | S |
| 3 | 200% font scale pass (cover and inner) | M |
| 4 | Live regions for island notices and page changes | S |
| 5 | TalkBack labels for the editor and page dots | S |
| 6 | `enableAccessibilityChecks()` in Compose UI tests | S |
| 7 | An RTL pseudo-locale pass | M |
Loading
Loading