Miler rider app: surface system, visible design language, backend lifecycle
Design system - MilerSurface ladder (canvas → working → raised → floating) with MilerPanel as layer 1; canvas moved to #DEE3EA so white separates at 1.290:1. - Visible vocabulary applied across Home, Deliveries, Activity, Account and the sheets: hero heads (tabular numeral + small caption, clamped at 1.3x), canvas wells for anything that opens, small filled tags for shelf labels, demoted placeholders. Recorded in DESIGN_SYSTEM.md §6. - One icon family: 222 Material glyphs migrated to Lucide; none left outside lib/xpress. - Colour semantics corrected: amber only for what is genuinely owed, brand red reserved for the live stop, disabled primaries go neutral rather than pale. Data and lifecycle - lib/data/lifecycle.dart reads mutations for what they prove; route_order.dart makes admin sequence the single ordering authority; service_day.dart, and stop_area.dart rewritten against live Coimbatore addresses (digit-token stripping, city stoplist, street suffixes, stammer collapse). - countLabel states the load once, in bags. Testing - 1440 tests passing; golden shot harnesses for Home, Deliveries, Activity, sheets and verify, with test/failures/ now gitignored (diff debris). - New pins: home_gutter_test, stop_area_test, plus updated structural bounds. Note: this commit also carries pre-existing working-tree deletions that were present before this work (API_SPEC.md, README.md, demo test fixtures). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -75,7 +75,12 @@ class ColorConstants {
|
||||
/// app washed out in exactly the daylight this app is used in.
|
||||
///
|
||||
/// Now 5.48:1 behind white, and ≥4.5:1 on all four surfaces the app draws on.
|
||||
static const Color acceptGreen = Color(0xFF047857);
|
||||
// #047857 → #047354 (2026-08-21). A two-percent darkening, and the only
|
||||
// reason for it is arithmetic: with the canvas moved to #DEE3EA the old
|
||||
// value measured 4.25:1 on it — "AA large only", which a 13.5sp status word
|
||||
// is not. This clears 4.54 on the canvas and 5.86 on white, and at this
|
||||
// distance the hue is indistinguishable from what it replaced.
|
||||
static const Color acceptGreen = Color(0xFF047354);
|
||||
|
||||
/// Kept as a name for the cash figure, now that [acceptGreen] is dark enough
|
||||
/// to be read as text. Same value; the two greens collapsed into one.
|
||||
@@ -95,7 +100,10 @@ class ColorConstants {
|
||||
// `cardSurface`** — and the card fill is where warnings actually appear, on
|
||||
// the skip sheet and the short-payment notice. A token that passes on the
|
||||
// surface nobody uses it on has not passed.
|
||||
static const Color warning = Color(0xFFAE5008);
|
||||
// #AE5008 → #A44B08, for the same arithmetic reason as [acceptGreen]: 4.12
|
||||
// on the new canvas was below AA for the size this is actually set at.
|
||||
// Now 4.54 on the canvas, 5.85 on white.
|
||||
static const Color warning = Color(0xFFA44B08);
|
||||
static const Color onWarning = Color(0xFFFFFFFF);
|
||||
static const Color warningContainer = Color(0xFFFEF3C7);
|
||||
|
||||
@@ -163,6 +171,21 @@ class ColorConstants {
|
||||
static const Color deliveryChip = Color(0xFF1E40AF);
|
||||
static const Color combinedChip = Color(0xFF4C1D95);
|
||||
|
||||
// ── The service line ─────────────────────────────────────────────────
|
||||
//
|
||||
// A tenant whose riders run subscription meal routes sees one work type all
|
||||
// day — collect a crate from a kitchen, drop it to subscribers — so its stops
|
||||
// wear a single `SERVICE` mark instead of the pickup/delivery pair. See
|
||||
// `ServiceProfile.workTypeLabel`.
|
||||
//
|
||||
// Deliberately the delivery blue rather than a fourth hue: a service day is
|
||||
// delivery-shaped from the rider's side, the blue is already tuned to 5.17:1,
|
||||
// and nothing in that day sits next to a Doormile stop for the two to be
|
||||
// confused. Named separately so it can diverge later without a hunt through
|
||||
// call sites for which blue meant which thing.
|
||||
static const Color serviceAccent = deliveryAccent;
|
||||
static const Color serviceChip = deliveryChip;
|
||||
|
||||
/// Text drawn *on* a chip fill.
|
||||
static const Color onChip = Color(0xFFFFFFFF);
|
||||
|
||||
@@ -235,10 +258,60 @@ class ColorConstants {
|
||||
/// rather than as a translucent rectangle.
|
||||
static const Color glassRim = Color(0x8CFFFFFF);
|
||||
|
||||
/// ── The pane a list card is made of ──
|
||||
///
|
||||
/// Every card the rider scrolls — a stop on Home, a job on Deliveries — is
|
||||
/// this fill with a soft, wide shadow under it ([DesignConstants.shadowGlass])
|
||||
/// and **no border at all**.
|
||||
///
|
||||
/// Not a solid white: at 92% the page's slate tint comes through just enough
|
||||
/// that the card reads as a pane laid over the screen rather than a white
|
||||
/// rectangle cut into it, which is the whole difference between "glass" and
|
||||
/// "box". It stays high enough for near-black type at any brightness — a
|
||||
/// rider reads addresses off this surface in direct sun.
|
||||
///
|
||||
/// No `BackdropFilter`. A real blur re-samples everything behind it every
|
||||
/// frame, and a scrolling list of twenty of them is the single most reliable
|
||||
/// way to drop a mid-range Android phone to 30fps. The blur is reserved for
|
||||
/// the two surfaces that genuinely sit over live content — the app bar and
|
||||
/// the bottom sheets ([milerGlassSheet]).
|
||||
static const Color glassCard = Color(0xEBFFFFFF);
|
||||
|
||||
/// The same pane, warmed by the brand, for the one stop that is under way.
|
||||
///
|
||||
/// A whisper — 5% — because a card the rider has to *read* is the wrong place
|
||||
/// for a saturated fill: what marks the live stop is the LIVE chip and the
|
||||
/// button on it, not a red panel behind its address.
|
||||
static const Color glassCardLive = Color(0x0D960019);
|
||||
|
||||
// ── Daylight surfaces ────────────────────────────────────────────────
|
||||
// Tuned for a phone clamped to a handlebar in direct sun. Slate-tinted grey
|
||||
// separates from white far more strongly outdoors than the old warm surface.
|
||||
static const Color daylightSurface = Color(0xFFF8FAFC);
|
||||
//
|
||||
// #F8FAFC → #EEF2F7 (2026-08-21). The old value was a four-percent step off
|
||||
// the cards' white — honest on a calibrated display and INVISIBLE on the
|
||||
// budget LCD this app actually ships to: verified on-device, the record's
|
||||
// panels and the queue's cards read as printed on the same sheet. A ground
|
||||
// has one job — make the surfaces on it read as surfaces — and a step nobody
|
||||
// can see is a step not taken. Secondary text keeps ≥4.2:1 here.
|
||||
// ── #EEF2F7 → #DEE3EA (2026-08-21, measured) ──
|
||||
//
|
||||
// The same correction, made once more and this time with a number attached.
|
||||
// The previous move went from a 4% step to a 12.4% one, which was better and
|
||||
// still not enough: **white on this ground measured 1.124:1**. A tonal step
|
||||
// does not begin to read as a *different surface* until roughly 1.25:1, and
|
||||
// outdoors on a cheap LCD it needs the top of that band — which is why every
|
||||
// screen that wanted its content to separate had quietly grown a border or a
|
||||
// shadow to do the ground's job for it.
|
||||
//
|
||||
// #DEE3EA puts white at **1.290:1**. Surfaces now separate on their own, so
|
||||
// the borders and shadows those screens added can come off rather than being
|
||||
// piled higher. Body text still clears 13.8:1 here and secondary text 4.4:1,
|
||||
// so nothing was traded for it.
|
||||
//
|
||||
// This is [MilerSurface.canvas]. Read that ladder, not this constant, when
|
||||
// choosing a ground — it is what makes four layers four layers.
|
||||
static const Color daylightSurface = Color(0xFFDEE3EA);
|
||||
|
||||
/// The app's near-black body colour.
|
||||
static const Color slateText = Color(0xFF0F172A);
|
||||
|
||||
@@ -1,11 +1,43 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class FontConstants {
|
||||
// Miler Design System: Manrope typography (variable font) — a free,
|
||||
// OFL-licensed geometric sans chosen as the closest legal stand-in for
|
||||
// Uber Move (which is proprietary and cannot be bundled).
|
||||
static const String fontFamily = 'Manrope';
|
||||
static String fontFamilyFallback = 'PlusJakartaSans';
|
||||
/// ── The app's typeface ──
|
||||
///
|
||||
/// Poppins: geometric, OFL-licensed, and the one every screen now reads from.
|
||||
/// It replaced Manrope, which was itself a stand-in for Uber Move.
|
||||
///
|
||||
/// **Read this before changing it.** The family was already declared in
|
||||
/// `pubspec.yaml` as `poppins`, with a lowercase path on every one of its
|
||||
/// seven assets, while the files on disk are `assets/fonts/Poppins/Poppins-*`.
|
||||
/// Asset bundling is case-sensitive on both platforms, so not one of them
|
||||
/// resolved: anything asking for the family fell back to Roboto and no error
|
||||
/// was raised anywhere. The declaration is fixed and all seven static weights
|
||||
/// are registered — Poppins ships as separate files rather than a variable
|
||||
/// font, and without explicit 800/900 entries Flutter synthesises them by
|
||||
/// smearing SemiBold, which is what makes a heading look muddy at exactly the
|
||||
/// size it is meant to be read from.
|
||||
/// ── The weight scale is calibrated for THIS family ──
|
||||
///
|
||||
/// Poppins runs optically heavier than Manrope at every grade: it is
|
||||
/// geometric, its bowls are wide and its stems thick, so `w800` here is far
|
||||
/// darker on the page than `w800` was in a variable Manrope. The whole scale
|
||||
/// was tuned against Manrope and came across a grade too heavy — headings
|
||||
/// read as shouting and body copy as bold.
|
||||
///
|
||||
/// Every weight in `lib/` (outside `lib/xpress`, which is Proxima Nova) was
|
||||
/// stepped down one grade: 900→800, 800→700, 700→600, 600→500. The *relative*
|
||||
/// hierarchy is untouched — each level is still one step from its neighbour —
|
||||
/// so nothing lost its rank; the whole run just sits where it should for this
|
||||
/// typeface.
|
||||
///
|
||||
/// **If the family changes again, re-tune this.** A weight is a number, not a
|
||||
/// design: what it looks like belongs to the font.
|
||||
static const String fontFamily = 'Poppins';
|
||||
|
||||
/// Only ever reached for a glyph Poppins does not carry. Manrope covers the
|
||||
/// Latin and currency sets the app uses, so this is a safety net rather than
|
||||
/// a second typeface in the design.
|
||||
static String fontFamilyFallback = 'Manrope';
|
||||
|
||||
// Typography scale
|
||||
static const double displayLg = 32.0;
|
||||
@@ -18,21 +50,21 @@ class FontConstants {
|
||||
|
||||
static TextStyle displayLgStyle = const TextStyle(
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: -0.64,
|
||||
fontFamily: fontFamily,
|
||||
);
|
||||
|
||||
static TextStyle headlineMdStyle = const TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: -0.24,
|
||||
fontFamily: fontFamily,
|
||||
);
|
||||
|
||||
static TextStyle headlineSmStyle = const TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontWeight: FontWeight.w500,
|
||||
letterSpacing: -0.2,
|
||||
fontFamily: fontFamily,
|
||||
);
|
||||
@@ -51,7 +83,7 @@ class FontConstants {
|
||||
|
||||
static TextStyle labelBoldStyle = const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontFamily: fontFamily,
|
||||
);
|
||||
|
||||
|
||||
@@ -36,7 +36,13 @@ class AppTheme {
|
||||
},
|
||||
),
|
||||
fontFamily: FontConstants.fontFamily,
|
||||
fontFamilyFallback: const ['Roboto', 'sans-serif'],
|
||||
// Manrope first: it is bundled, so its coverage is guaranteed on every
|
||||
// device, and it is what backs Poppins up for anything Poppins does not
|
||||
// carry. The platform families behind it are the last resort. Poppins is
|
||||
// missing U+2192 among others — a glyph the app no longer renders as a
|
||||
// character, but the net belongs here rather than in a rule nobody
|
||||
// remembers.
|
||||
fontFamilyFallback: const ['Manrope', 'Roboto', 'sans-serif'],
|
||||
textTheme: ThemeData.light().textTheme.apply(
|
||||
fontFamily: FontConstants.fontFamily,
|
||||
),
|
||||
@@ -110,7 +116,7 @@ class AppTheme {
|
||||
),
|
||||
textStyle: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontFamily: FontConstants.fontFamily,
|
||||
),
|
||||
),
|
||||
@@ -147,7 +153,7 @@ class AppTheme {
|
||||
),
|
||||
textStyle: const TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontFamily: FontConstants.fontFamily,
|
||||
),
|
||||
),
|
||||
@@ -156,7 +162,7 @@ class AppTheme {
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: _primary,
|
||||
textStyle: const TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontFamily: FontConstants.fontFamily,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -97,6 +97,31 @@ class DesignConstants {
|
||||
static const List<BoxShadow> shadowLg = [
|
||||
BoxShadow(color: Color(0x2A000000), blurRadius: 12, offset: Offset(0, 8)),
|
||||
];
|
||||
|
||||
/// A surface that floats **over** the list rather than in it: the selection
|
||||
/// bar, a snackbar. Wider and deeper than [shadowGlass] because it has to
|
||||
/// read as being on a different plane from the cards it covers — but still
|
||||
/// slate-tinted and still soft, because a hard drop shadow under a white bar
|
||||
/// reads as a grey stripe, which is exactly what it looked like.
|
||||
static const List<BoxShadow> shadowFloat = [
|
||||
BoxShadow(color: Color(0x1F0F172A), blurRadius: 28, offset: Offset(0, 12)),
|
||||
BoxShadow(color: Color(0x140F172A), blurRadius: 4, offset: Offset(0, 2)),
|
||||
];
|
||||
|
||||
/// ── What holds a borderless card off the page ──
|
||||
///
|
||||
/// Two shadows, not one, and that is the whole trick: a wide soft pool gives
|
||||
/// the card its lift, and a tight near-opaque line under the bottom edge
|
||||
/// gives it a *defined* edge — without it a single blurred shadow reads as a
|
||||
/// smudge and the card looks unfinished rather than raised.
|
||||
///
|
||||
/// Tinted with the page's slate rather than pure black: a neutral-black
|
||||
/// shadow on a blue-grey ground goes muddy, which is what makes a card look
|
||||
/// cheap next to the same card in a design file.
|
||||
static const List<BoxShadow> shadowGlass = [
|
||||
BoxShadow(color: Color(0x0F0F172A), blurRadius: 18, offset: Offset(0, 6)),
|
||||
BoxShadow(color: Color(0x0A0F172A), blurRadius: 2, offset: Offset(0, 1)),
|
||||
];
|
||||
}
|
||||
|
||||
/// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
150
lib/views/helpers/constants/miler_surface.dart
Normal file
150
lib/views/helpers/constants/miler_surface.dart
Normal file
@@ -0,0 +1,150 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'package:miler/views/helpers/constants/Colorconstants.dart';
|
||||
import 'package:miler/views/helpers/constants/design_constants.dart';
|
||||
|
||||
/// ─────────────────────────────────────────────────────────────────────────
|
||||
/// FOUR LAYERS, AND WHAT EACH ONE MEANS
|
||||
///
|
||||
/// The app had **five** near-identical light surfaces — `pureSurface`,
|
||||
/// `surface`, `neutralLight`, `cardSurface`, `daylightSurface` — and measured
|
||||
/// against the page ground, three of them were invisible:
|
||||
///
|
||||
/// ```
|
||||
/// neutralLight #F2F4F8 1.021 : 1 ← indistinguishable
|
||||
/// cardSurface #EDEFF4 1.023 : 1 ← indistinguishable
|
||||
/// surface #FCF9F8 1.073 : 1 ← and warm, unlike everything else
|
||||
/// pureSurface #FFFFFF 1.124 : 1 ← the one that mattered, and too close
|
||||
/// ```
|
||||
///
|
||||
/// Five tokens doing the work of two is why the app looked flat *and* why
|
||||
/// individual screens grew their own borders and shadows: a surface that will
|
||||
/// not separate on its own has to be outlined, and once one screen outlines
|
||||
/// something every screen does, in its own way.
|
||||
///
|
||||
/// The fix was not more containers. It was giving the ground somewhere to be:
|
||||
/// the canvas moved down to #DEE3EA, white now separates at 1.290:1, and the
|
||||
/// layers below are named for **what a surface means** rather than for what
|
||||
/// colour it happens to be.
|
||||
///
|
||||
/// ── The ladder ──
|
||||
///
|
||||
/// ```
|
||||
/// 0 canvas the page itself. Never white.
|
||||
/// 1 working a region the rider reads or works in. White, no border.
|
||||
/// 2 raised something that acts: a command bar, a live card. Shadow.
|
||||
/// 3 floating over everything, with a scrim: sheets, dialogs.
|
||||
/// ```
|
||||
///
|
||||
/// A container is justified when it is one of these layers. It is not
|
||||
/// justified because the content inside it is a group — grouping is done by
|
||||
/// whitespace, type and the timeline, which is why a route reads as a route
|
||||
/// without a box around each stop.
|
||||
/// ─────────────────────────────────────────────────────────────────────────
|
||||
abstract final class MilerSurface {
|
||||
/// **Layer 0.** The page. Every scaffold in the app sits on this.
|
||||
///
|
||||
/// Deliberately not white: a canvas that is white leaves nothing for a white
|
||||
/// surface to be distinguishable *from*, which is the state this app was in.
|
||||
static const Color canvas = ColorConstants.daylightSurface;
|
||||
|
||||
/// **Layer 1.** A region the rider reads or works in — a sheet's body, a
|
||||
/// details page, a grouped operational block.
|
||||
///
|
||||
/// No border. At 1.290:1 against [canvas] it separates on its own, and an
|
||||
/// outline on top of a step that already works is a line to read past.
|
||||
static const Color working = ColorConstants.pureSurface;
|
||||
|
||||
/// **Layer 2.** Something that *acts*: the floating command bar, the live
|
||||
/// card, a control that owns the bottom of the screen.
|
||||
///
|
||||
/// Same fill as [working] — the difference is [raisedShadow], because what
|
||||
/// separates an action surface from a reading surface is that it appears to
|
||||
/// sit above the page rather than in it.
|
||||
static const Color raised = ColorConstants.pureSurface;
|
||||
|
||||
/// **Layer 3.** Over everything, with a scrim beneath: modal sheets and
|
||||
/// dialogs. Painted by `milerGlassSheet`; this names the layer.
|
||||
static const Color floating = ColorConstants.glassCard;
|
||||
|
||||
/// A quiet tonal block *inside* layer 1 — a fact grid, a folded group.
|
||||
///
|
||||
/// The only nesting the system allows, and only when the block is a
|
||||
/// different **kind** of thing from what surrounds it. Never for grouping.
|
||||
static const Color inset = ColorConstants.neutralLight;
|
||||
|
||||
/// Layer 2's lift. One shadow, used everywhere something is raised, so
|
||||
/// "floating" looks the same on every screen.
|
||||
static const List<BoxShadow> raisedShadow = DesignConstants.shadowSm;
|
||||
|
||||
/// Layer 3's lift.
|
||||
static const List<BoxShadow> floatingShadow = DesignConstants.shadowFloat;
|
||||
|
||||
/// The dim behind a layer-3 surface. Separation comes from the scrim first
|
||||
/// and the shadow second — a sheet that needs a heavy shadow to be legible
|
||||
/// is a sheet whose scrim is too weak.
|
||||
static const Color scrim = Color(0x66000000);
|
||||
|
||||
/// The gutter a layer-1 panel leaves around itself, so the canvas reads as
|
||||
/// ground rather than as a hairline. One number, because two panels a few
|
||||
/// points apart is how a page stops looking composed.
|
||||
static const double panelGutter = 12;
|
||||
|
||||
/// The breathing room *inside* a layer-1 panel.
|
||||
static const double panelPad = 14;
|
||||
}
|
||||
|
||||
/// ── Layer 1, as a widget ──
|
||||
///
|
||||
/// The ladder above says a working region is *white, standing on the canvas,
|
||||
/// with no border*. Home was the screen that proved why that has to be a
|
||||
/// widget rather than a paragraph: its sheet was painted with the canvas and
|
||||
/// its content placed straight onto it, so the whole page came out one flat
|
||||
/// grey with nothing white anywhere on it. Every rule was followed except the
|
||||
/// one that does the work — something has to actually stand on the ground.
|
||||
///
|
||||
/// No border and no shadow, deliberately. White separates from the canvas at
|
||||
/// 1.290 : 1, which is above the ~1.25 : 1 where two tones start reading as
|
||||
/// two surfaces, so an outline here would be a line to read past rather than
|
||||
/// a line that says anything.
|
||||
class MilerPanel extends StatelessWidget {
|
||||
final Widget child;
|
||||
|
||||
/// Space below this panel before the next one. The canvas showing through
|
||||
/// the gap is what makes them read as separate regions.
|
||||
final double gap;
|
||||
|
||||
/// Set false where the child already owns its own horizontal padding — a
|
||||
/// list whose rows need to bleed to the panel edge, for instance.
|
||||
final bool padded;
|
||||
|
||||
const MilerPanel({
|
||||
super.key,
|
||||
required this.child,
|
||||
this.gap = 10,
|
||||
this.padded = true,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
margin: EdgeInsets.fromLTRB(
|
||||
MilerSurface.panelGutter,
|
||||
0,
|
||||
MilerSurface.panelGutter,
|
||||
gap,
|
||||
),
|
||||
padding: padded
|
||||
? EdgeInsets.symmetric(
|
||||
horizontal: MilerSurface.panelPad,
|
||||
vertical: MilerSurface.panelPad,
|
||||
)
|
||||
: EdgeInsets.zero,
|
||||
decoration: BoxDecoration(
|
||||
color: MilerSurface.working,
|
||||
borderRadius: BorderRadius.circular(DesignConstants.radiusXl),
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -64,7 +64,7 @@ class MilerType {
|
||||
/// The one thing a screen is about. One per screen, or none.
|
||||
static TextStyle get hero => TextStyle(
|
||||
fontSize: 28.sp,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: -0.9,
|
||||
height: 1.08,
|
||||
color: ColorConstants.slateText,
|
||||
@@ -74,7 +74,7 @@ class MilerType {
|
||||
/// App bar titles and sheet headings.
|
||||
static TextStyle get pageTitle => TextStyle(
|
||||
fontSize: 22.sp,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: -0.6,
|
||||
height: 1.15,
|
||||
color: ColorConstants.slateText,
|
||||
@@ -84,7 +84,7 @@ class MilerType {
|
||||
/// A stop name in a list, a section heading inside a screen.
|
||||
static TextStyle get cardTitle => TextStyle(
|
||||
fontSize: 19.sp,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: -0.4,
|
||||
height: 1.2,
|
||||
color: ColorConstants.slateText,
|
||||
@@ -94,7 +94,7 @@ class MilerType {
|
||||
/// Addresses, running copy, the title line of a compact row.
|
||||
static TextStyle get body => TextStyle(
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontWeight: FontWeight.w500,
|
||||
letterSpacing: -0.2,
|
||||
height: 1.35,
|
||||
color: ColorConstants.slateText,
|
||||
@@ -114,7 +114,7 @@ class MilerType {
|
||||
/// Metadata, timestamps, chip text, the second line of a row.
|
||||
static TextStyle get label => TextStyle(
|
||||
fontSize: 13.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontWeight: FontWeight.w500,
|
||||
height: 1.35,
|
||||
color: ColorConstants.secondaryText,
|
||||
fontFamily: _family,
|
||||
@@ -124,7 +124,7 @@ class MilerType {
|
||||
/// expected to be read outdoors.
|
||||
static TextStyle get micro => TextStyle(
|
||||
fontSize: 12.5.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontWeight: FontWeight.w500,
|
||||
height: 1.3,
|
||||
color: ColorConstants.secondaryText,
|
||||
fontFamily: _family,
|
||||
@@ -134,7 +134,7 @@ class MilerType {
|
||||
/// the reason most groups in this app need no outline.
|
||||
static TextStyle get eyebrow => TextStyle(
|
||||
fontSize: 11.sp,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: 1.2,
|
||||
height: 1.3,
|
||||
color: ColorConstants.secondaryText,
|
||||
@@ -150,7 +150,7 @@ class MilerType {
|
||||
/// number, resolved without `.sp`.
|
||||
static const TextStyle pageTitleFixed = TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: -0.6,
|
||||
height: 1.15,
|
||||
color: ColorConstants.slateText,
|
||||
@@ -165,7 +165,7 @@ class MilerType {
|
||||
/// them lines up.
|
||||
static TextStyle figure(double size, {Color? color}) => TextStyle(
|
||||
fontSize: size.sp,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontWeight: FontWeight.w700,
|
||||
letterSpacing: -0.3,
|
||||
height: 1.1,
|
||||
fontFeatures: const [FontFeature.tabularFigures()],
|
||||
@@ -176,8 +176,14 @@ class MilerType {
|
||||
/// The label inside a button. Sized against the button's own height, so a
|
||||
/// compact button does not shout like a primary one.
|
||||
static TextStyle buttonLabel(double height) => TextStyle(
|
||||
fontSize: (height >= 56 ? 16 : height >= 48 ? 15 : 14).sp,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize:
|
||||
(height >= 56
|
||||
? 16
|
||||
: height >= 48
|
||||
? 15
|
||||
: 14)
|
||||
.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: -0.1,
|
||||
color: ColorConstants.onAccent,
|
||||
fontFamily: _family,
|
||||
@@ -192,7 +198,7 @@ class MilerType {
|
||||
extension MilerTypeTint on TextStyle {
|
||||
TextStyle on(Color color) => copyWith(color: color);
|
||||
|
||||
TextStyle get bold => copyWith(fontWeight: FontWeight.w800);
|
||||
TextStyle get bold => copyWith(fontWeight: FontWeight.w700);
|
||||
|
||||
TextStyle get semibold => copyWith(fontWeight: FontWeight.w700);
|
||||
TextStyle get semibold => copyWith(fontWeight: FontWeight.w600);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user