Files
sure/mobile/lib/widgets/sure_card.dart
ghost 3a7dc3c346 design-system(mobile): SureSpacing + SureTypography scale tokens (#2438)
* feat(mobile): add SureSpacing + SureTypography scale tokens

Introduce hand-authored spacing and type-scale constants mirroring the
Tailwind defaults the web design system relies on, so widgets reference a
named step instead of a raw numeric EdgeInsets/SizedBox/fontSize.

- SureSpacing: xs..huge mapping to Tailwind space-1..space-8 (4..32px).
- SureTypography: xs..xxl mapping to Tailwind text-xs..text-2xl font sizes.

Both are hand-written rather than generated from sure.tokens.json because
spacing and the type ramp come from Tailwind's built-in scale, not the
canonical token file (consistent with the tracker's guidance).

Adopt them in the existing primitives (card padding, button metrics +
gap, chip/segmented/list-group gaps and padding, text-field padding +
label gap). All migrations are value-preserving — each token equals the
literal it replaces — so there is no layout change; off-scale one-offs
(control heights, hairlines, deliberate 14px field padding) stay literal.

flutter analyze: no new issues; full suite (166) green.

* docs(mobile): note off-scale SureChip inset; expose SureTypography line heights

Address review feedback (jjmata):

- SureChip: add an inline comment explaining the horizontal:14 content inset is
  deliberately off the SureSpacing scale (between lg=12 and xl=16) — a tuned
  FilterChip-parity dimension, not a spacing step — so it isn't "fixed" to a
  token later.
- SureTypography: expose the paired line heights (xsLineHeight … xxlLineHeight,
  logical px matching the Tailwind text-* defaults) that were previously only in
  doc comments, with a note on deriving Flutter's TextStyle.height multiplier
  (lineHeight / fontSize). No behavior change.
2026-06-30 07:23:24 +02:00

68 lines
2.2 KiB
Dart

import 'package:flutter/material.dart';
import '../theme/sure_colors.dart';
import '../theme/sure_spacing.dart';
import '../theme/sure_tokens.dart';
/// Sure design-system card — a tokenized content surface mirroring the web card
/// chrome (`bg-container` + a hairline border + rounded corners + the subtle DS
/// shadow). Use it instead of a Material [Card] so the chrome stays in lockstep
/// with `sure.tokens.json` and reads correctly in light and dark.
///
/// Colors resolve from the active [SureColors] palette (brightness-aware). When
/// [onTap] is provided the whole card is tappable, with a flat ink response
/// clipped to the card's radius.
class SureCard extends StatelessWidget {
const SureCard({
super.key,
required this.child,
this.padding = const EdgeInsets.all(SureSpacing.xl),
this.margin,
this.onTap,
this.elevated = true,
});
final Widget child;
/// Inner padding around [child].
final EdgeInsetsGeometry padding;
/// Outer spacing around the card (e.g. separation in a list).
final EdgeInsetsGeometry? margin;
/// When non-null, the card is tappable.
final VoidCallback? onTap;
/// Apply the subtle DS card shadow. Set false for cards sitting on an inset
/// surface, where the border alone provides enough separation.
final bool elevated;
@override
Widget build(BuildContext context) {
final palette = SureColors.of(context).palette;
final radius = BorderRadius.circular(SureTokens.radiusLg);
Widget content = Padding(padding: padding, child: child);
if (onTap != null) {
// Material(transparency) gives InkWell a surface to paint on without
// covering the card's tokenized background or border.
content = Material(
type: MaterialType.transparency,
child: InkWell(onTap: onTap, borderRadius: radius, child: content),
);
}
return Container(
margin: margin,
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
color: palette.container,
borderRadius: radius,
border: Border.all(color: palette.borderSecondary),
boxShadow: elevated ? palette.shadowXs : null,
),
child: content,
);
}
}