halkyon/packages/halkyon_ui/README.md
Tizian 「ティツィアーン」 3f441cbc98
feat: the rail opens into a sidebar with labels from expanded windows on
Decided by the developer on 9 and 10 Nov 2026: phones keep the bar,
medium windows (600 to 840 dp) the rail alone, and from 840 dp a toggle
at the rail's foot opens it into a sidebar 256 dp wide, as Proton
Drive's: the app named beside its icon, each tab its label beside its
icon, the icons where the rail had them, so nothing moves under the
pointer. Its labels come in one after another, top to bottom, all at once
where animations are off. Open or closed is kept per device, in
halkyon_app's Look (look.json, `sidebar`), which Learn keeps in its
DeviceSettings (`sidebarOpen`), as it does the appearance and language.

NavigationFrame takes sidebarOpen and onSidebarChanged (no toggle without
it), and sidebarSections, what the app lists below the tabs, which Todo's
favourites and projects will be. The closed rail keeps Flutter's widget,
its toggle pinned at the foot (trailingAtBottom); the open one is the
frame's own, as the staggered labels need. Learn, Todo, Train and
Meditate keep the choice.

Whats-New: In wide windows, the button at the foot of the side rail opens it into a sidebar with names, and it stays open.
Whats-New-De: In breiten Fenstern öffnet der Knopf unten an der Seitenleiste sie mit Namen, und sie bleibt offen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-11-10 18:04:09 +01:00

101 lines
4.7 KiB
Markdown

# halkyon_ui
Halkyon's look, shared by its apps (Learn, Todo, Train): one theme around the
kingfisher's cobalt and copper, the bundled Noto Sans, Material 3's window
sizes, and the widgets every app's screens are built of, with their texts in
British English, American English and German.
Written as the apps are: Dart 3.13's primary constructors, Material from
`package:material_ui` (never `package:flutter/material.dart`), Lucide icons,
LeanCode's lints.
## What it holds
- **Theme** (`lib/src/theme/`): `appTheme(brightness, extensions: …)`, Material
3 with hand-written light and dark schemes (primary cobalt, tertiary copper,
cobalt-tinted neutrals), Material 3 Expressive's 64 dp navigation bar, flat
app bars, floating snack bars; `Brand`; `ThemeExtras`, the status colours
Material has no role for (`Theme.of(context).extras`); `PaletteColor`, the
20 colours a person can give content, each with a text colour held to WCAG
AA by a test; `FittedSnackBars`.
- **Fonts**: Noto Sans for Latin, Greek and Cyrillic, every weight in one
variable file, as `appFontFamily` (`packages/halkyon_ui/Noto Sans`). Chinese,
Japanese and Korean fall back to `Noto Sans JP`, `SC`, `TC` and `KR`, which
an app bundles itself if it needs them (`appFontFallbackFor` puts a
language's own first).
- **Layout** (`lib/src/layout/`): `WindowSize` (`context.windowSize`) and
`readableContentMaxWidth`.
- **Widgets** (`lib/src/widget/`): `showAdaptiveSheet` with `SheetScaffold`;
`TopGradient`, `ScreenTint`, `ScreenColor`, `TintFrame`, `FramedScreen`
and `TabVisibility`; `FlushNavigationBar`, and `NavigationFrame`, every
app's frame around its tabs (the rail with the app's icon, opening from
expanded windows on into a sidebar with labels and what the app lists
there (`sidebarOpen`, `onSidebarChanged`, `sidebarSections`), the bar flush
with the foot with a fade above it, its rounded top corners and the
wave a tap sends over it in the colour of the screen in view, the tab
chosen in that colour made legible (`readableTone`), without Material's
ripple or the indicator growing, on the bar's neutral surface (decided
8 Nov 2026, ADR 0030's amendment of D60), a tick on every choice, a status such as a recording under way above
the bar), whichever router drives it;
`AppLoadingIndicator`, `ButtonLoadingIndicator`, `AppRefreshIndicator`;
`SectionCard`, `SliverCentered`, `EmptyListMessage`, `CountBadge`,
`TitleWithKicker`, `CollapsingRow`, `ScrollHidingFab`, `Shimmer` and `SkeletonBox`, `liftWhileDragged`,
`ReorderToggle`, `ReorderGrip`; `ChevronField`, `requiredDecoration`,
`FieldInfoButton`, `CountStepper`, `PaletteColorPicker`, `TextLimits` with
`nearLimitCounter`; `confirmDestructiveAction`, `showUndoSnackBar`,
`showMoreInfoSheet`; `FoundById`; `KeyboardBackOnResume`; `BrandWordmark`
and `LaunchScreen`, which take the app's word and icon.
What belongs to one app stays in it: its own colours (an extension passed to
`appTheme`), routes, repositories, editors and texts.
## Using it in an app
Depend on it by path, within the repository's workspace (an app's packages sit in
`apps/<app>/`), so a change to the package and to the apps using it is one commit:
```yaml
dependencies:
halkyon_ui:
path: ../../../packages/halkyon_ui
```
Then set the app up as `example/lib/main.dart` does:
```dart
MaterialApp(
theme: appTheme(Brightness.light, extensions: [MyColors.light]),
darkTheme: appTheme(Brightness.dark, extensions: [MyColors.dark]),
localizationsDelegates: const [
AppLocalizations.delegate,
HalkyonUiLocalizations.delegate,
// material_ui's: flutter_localizations' serve only the SDK's Material.
...GlobalMaterialLocalizations.delegates,
],
supportedLocales: AppLocalizations.supportedLocales,
builder: (context, child) => FittedSnackBars(child: child!),
…
);
```
and call `registerFontLicenses()` at start. An app bundling the CJK fonts
declares them in its own `pubspec.yaml` under exactly the names
`appFontFallback` lists (no `packages/` prefix) and registers their licence
itself.
## Texts
`lib/l10n/halkyon_ui_en.arb` is the template (British English, also the
fallback); `halkyon_ui_de.arb` German, informal "du"; `halkyon_ui_en_US.arb`
only what American English spells differently; `halkyon_ui_en_GB.arb` empty. A
new text goes into the template and German together (`test/l10n/` fails
otherwise). `flutter pub get` writes `lib/l10n/halkyon_ui_localizations*.dart`,
which are committed and never edited or
formatted by hand.
## Checks
From the workspace root: `dart analyze --fatal-infos`; then `flutter test` in
`packages/halkyon_ui` and in `packages/halkyon_ui/example`, whose tests check
what only an app sees (the font under the name an app finds it by, its
licence).