Skip to content

LayrzAlert

Kenny Mochizuki Escalona edited this page Aug 17, 2026 · 7 revisions

LayrzAlert

A Material-free inline status callout that communicates information, success, warnings, or errors with semantic color, icons, and flexible visual styles.

Metadata
Mirrors: ThemedAlert (layrz_theme)
Phase: M2 (Core primitives)
Domain: Feedback
Primitive: Hand-rolled (Container + BoxDecoration + Row + Column + Icon)
Status: Confirmed scope.


Overview

LayrzAlert is an inline status callout that displays a semantic message via color, icon, title text, and body text. It supports multiple visual styles and semantic types to fit different contexts — from informational hints to critical error callouts. The alert can be either non-interactive (default, read-only) or interactive (with optional tap handling and hover/press feedback).

Design Principles

  • Semantic type mapping — the type parameter maps to a semantic color (info, success, warning, danger, context, or custom), automatically selecting both the icon and color without explicit parameters.
  • Fixed content structuretitle and description are both required; there are no optional variants. This enforces a consistent information hierarchy and prevents incomplete callouts.
  • Five visual styleslayrz (default), filledTonal, filled, outlined, and filledIcon, each with a distinct visual treatment suited to different container contexts.
  • Responsive layout — no fixed heights on text-bearing elements, supporting WCAG 1.4.4 (Text Scaling, AAA) — text scales with the system font size without causing layout issues.
  • Optional interactivity — alerts are read-only by default (onTap: null). When onTap is provided, the alert becomes interactive with hover, press, and focus feedback. A containing view manages the alert's lifecycle.
  • Geometry consistency — per decision D15, only color and shadow change; size, padding, and radius remain constant across all interaction states. The lift during hover/focus is paint-only (via transform), never affecting layout or hit-testing.

API Structure

Core Constructor

class LayrzAlert extends StatelessWidget {
  /// The semantic type of the alert.
  ///
  /// Determines the default icon and colour. Defaults to [LayrzAlertType.info].
  /// If [type] is [LayrzAlertType.custom], the [color] and [icon] parameters
  /// control appearance; otherwise they are ignored.
  final LayrzAlertType type;

  /// The title text of the alert.
  ///
  /// Required. Displays as bold title text. Scales with system text scale factor.
  final String title;

  /// The description text of the alert.
  ///
  /// Required. Displays as body text. Limited to [maxLines] lines before ellipsis.
  /// Scales with system text scale factor.
  final String description;

  /// The maximum number of lines for [description] text.
  ///
  /// If the description exceeds this many lines, it is truncated with an ellipsis.
  /// Defaults to 3.
  final int maxLines;

  /// The visual style of the alert.
  ///
  /// Determines background, border, icon chip, and text colours.
  /// Defaults to [LayrzAlertStyle.layrz].
  final LayrzAlertStyle style;

  /// The custom colour of the alert, used only when [type] is [LayrzAlertType.custom].
  ///
  /// If [type] is not custom, this parameter is ignored.
  /// If null and [type] is custom, defaults to [LayrzTokens.colors.primary].
  final Color? color;

  /// The custom icon glyph, used only when [type] is [LayrzAlertType.custom].
  ///
  /// If [type] is not custom, this parameter is ignored.
  /// If null and [type] is custom, defaults to [LayrzIcons.solarOutlineInfoSquare].
  final IconData? icon;

  /// The size of the icon glyph.
  ///
  /// If null, defaults to [kLayrzAlertFilledIconSize] for [LayrzAlertStyle.filledIcon],
  /// or [kLayrzAlertIconSize] for all other styles.
  final double? iconSize;

  /// Called when the user taps the alert.
  ///
  /// When null (the default), the alert is not interactive: no cursor change,
  /// no hover/press feedback, not focusable, and not announced as a button to
  /// assistive technology. The alert renders exactly as it did before, with no
  /// state changes during interaction.
  ///
  /// When non-null, the alert becomes interactive: on hover or focus, the surface
  /// lifts by [kLayrzAlertHoverLift] and the shadow steps up one level; on press,
  /// it settles back down. The lift is paint-only (via transform), so geometry
  /// remains constant. The alert is focusable by Tab and activatable by Enter or
  /// Space, and announces as a button to assistive technology.
  final VoidCallback? onTap;

  /// Creates a [LayrzAlert].
  const LayrzAlert({
    super.key,
    this.type = LayrzAlertType.info,
    required this.title,
    required this.description,
    this.maxLines = 3,
    this.style = LayrzAlertStyle.layrz,
    this.color,
    this.icon,
    this.iconSize,
    this.onTap,
  });
}

LayrzAlertType

Semantic type classification that determines the alert's accent color and icon:

enum LayrzAlertType {
  /// Informational semantic type — use `tokens.colors.info` for neutral messages.
  ///
  /// Associated icon: [LayrzIcons.solarOutlineInfoSquare].
  info,

  /// Success semantic type — use `tokens.colors.success` for positive/affirmative messages.
  ///
  /// Associated icon: [LayrzIcons.solarOutlineCheckSquare].
  success,

  /// Warning semantic type — use `tokens.colors.warning` for cautionary messages.
  ///
  /// Associated icon: [LayrzIcons.solarOutlineDangerSquare].
  warning,

  /// Danger semantic type — use `tokens.colors.danger` for destructive/critical messages.
  ///
  /// Associated icon: [LayrzIcons.solarOutlineCloseSquare].
  danger,

  /// Contextual semantic type — use `tokens.colors.contextual` for context-dependent messages.
  ///
  /// Associated icon: [LayrzIcons.solarOutlineMenuDotsSquare].
  context,

  /// Custom type — use explicit `icon` and `color` values from the [LayrzAlert] constructor.
  ///
  /// The `color` and `icon` parameters are only honoured when `type == custom`.
  /// Defaults to `tokens.colors.primary` and [LayrzIcons.solarOutlineInfoSquare] if
  /// `color` and `icon` are null, respectively.
  custom,
}
Type Token Color Icon Use Case
info tokens.colors.info (blue) solarOutlineInfoSquare Neutral, informational messages
success tokens.colors.success (green) solarOutlineCheckSquare Confirmation, successful operations
warning tokens.colors.warning (orange) solarOutlineDangerSquare Cautionary, attention-needed messages
danger tokens.colors.danger (red) solarOutlineCloseSquare Critical, destructive, error messages
context tokens.colors.contextual solarOutlineMenuDotsSquare Context-dependent, custom application logic
custom Provided via color parameter Provided via icon parameter Any other semantic meaning

LayrzAlertStyle

Visual style classification that determines appearance:

enum LayrzAlertStyle {
  /// Default Layrz style with neutral surface, severity-tinted border, and tinted icon chip.
  layrz,

  /// Tonal fill style with muted semantic color and matching text.
  filledTonal,

  /// Solid fill style with semantic color and contrasting text.
  filled,

  /// Outlined style with semantic border and matching text.
  outlined,

  /// Split-panel style with semantic color on left, neutral surface on right.
  filledIcon,
}

Style Rendering Guide

Each style resolves background, border, icon chip, title, and body colors independently. The table below shows how each style handles color rendering:

Style Background Border Icon Chip Background Title Color Body Color Icon Color
layrz surface Tonal accent Tonal accent fg1 fg2 Accent
filledTonal Tonal accent None None Accent Accent Accent
filled Solid accent Solid accent None Contrast Contrast Contrast
outlined Transparent Accent None Accent Accent Accent
filledIcon surface (right) None Solid accent (left) fg1 fg2 Contrast

Tonal opacity: The tonal accent is computed as accent.withOpacityValue(tokens.colors.tonalOpacity).
Contrast color: For solid-colored backgrounds, the text and icon use contrastColor to ensure readability (white or black depending on accent brightness).

.layrz — The Flagship Style

The .layrz style is the default and most versatile. It was newly specified for layrz_ui and represents the design system's preferred alert appearance:

  • Background: neutral surface (white in light mode), matching the container it sits within
  • Border: tonal accent (semi-transparent severity color), providing semantic emphasis without overwhelming
  • Icon chip: tonal accent with circular background, positioning the semantic icon as a visual anchor
  • Text: neutral foreground colors (fg1 for title, fg2 for body), ensuring strong readability

This style works universally on neutral backgrounds and is the safe choice for inline alerts in forms, cards, and dialogs.


Layout Variants

Standard Layout (all styles except filledIcon)

A single-row container with:

  • Left: circular icon chip (fixed size kLayrzAlertIconBoxSize = 34.0 logical pixels)
  • Center: 12 logical pixels gap
  • Right: text column with bold title and body description
┌────────────────────────────────────┐
│ ⊕ │  Title Bold              │
│   │  Description text...      │
└────────────────────────────────────┘

Split-Panel Layout (filledIcon)

A row split into two panels:

  • Left panel: solid accent background with a larger centered icon (kLayrzAlertFilledIconSize = 25.0 logical pixels)
  • Right panel: neutral surface background with title and description
┌────────────┬────────────────────────┐
│  ⊕ accent  │ Title Bold             │
│  color     │ Description text...    │
└────────────┴────────────────────────┘

Content Requirements

Both title and description are required. Providing neither or only one triggers a constructor assertion error.

  • title (String, required): Bold, single-line header text. Displays in tokens.typography.titleMedium with fontWeight.bold.
  • description (String, required): Body text. Displays in tokens.typography.bodyMedium. Limited to maxLines (default 3) before ellipsis truncation.

This strict 1:1 structure is a deliberate port of ThemedAlert — every alert has a title and body, preventing incomplete or ambiguous callouts.


Interactive Alerts

LayrzAlert supports optional interactivity via the onTap callback. Non-interactive alerts (the default, with onTap: null) remain read-only with no state changes. Interactive alerts (with onTap: non-null) respond to hover, press, and focus with visual feedback.

Interaction Model

Non-interactive (onTap: null):

  • No cursor change; uses the default pointer cursor.
  • No hover or press visual feedback.
  • Not focusable via keyboard Tab navigation.
  • Announces as a container to assistive technology, not a button.
  • Rendering is identical to the pre-interactive alert design.

Interactive (onTap: non-null):

  • Cursor: Changes to SystemMouseCursors.click on hover.
  • Hover: Surface lifts by kLayrzAlertHoverLift (4.0 logical pixels) and shadow steps up one level (via AnimatedContainer). Animation uses tokens.motion.dHover and tokens.motion.easingEnter.
  • Focus: Renders identically to hover — surface lifts and shadow steps up. Reachable via Tab navigation.
  • Press: Surface settles back down to its resting position; shadow steps down one level.
  • Keyboard Activation: Enter or Space key activates the alert (calls onTap). Activation is automatic inside LayrzApp via WidgetStateIntent / ActivateIntent binding; no explicit shortcut configuration is needed.
  • Accessibility: Announces as an interactive button with enabled state.

Lift Mechanism: Paint-Only Transform

The hover/focus lift is implemented as a paint-only transform (Matrix4.translationValues(0, -_currentLift, 0)), not a layout change. This means:

  • The alert's bounding box and hit-test region remain constant across all interaction states.
  • No reflow or repositioning of surrounding widgets.
  • The pointer is not carried away by a moving surface, preventing hover oscillation loops (the same failure pattern LayrzTooltip encountered and resolved via LayrzTooltip's anchor-always-visible guarantee).

This design satisfies the spirit of decision D15 (geometry consistency during interaction) while adding lift feedback. See Decisions (D15) for the broader context, and the amendment recorded on 2026-08-16 permitting paint-only transforms under controlled conditions.

Realistic Example: Dismissible Alert

class DismissibleAlertExample extends StatefulWidget {
  @override
  State<DismissibleAlertExample> createState() => _DismissibleAlertExampleState();
}

class _DismissibleAlertExampleState extends State<DismissibleAlertExample> {
  bool showAlert = true;

  @override
  Widget build(BuildContext context) {
    if (!showAlert) {
      return SizedBox.shrink();
    }

    return LayrzAlert(
      type: LayrzAlertType.warning,
      title: 'Important Notice',
      description: 'Tap this alert to dismiss it.',
      onTap: () {
        setState(() => showAlert = false);
      },
    );
  }
}

In this example, the alert becomes interactive and provides visual feedback (lift and shadow) on hover/focus. When tapped or activated via keyboard, it calls the onTap callback, which dismisses the alert. Non-interactive alerts are the default and remain the common case for informational callouts that do not require user action.


Sizing and Spacing

Property Value
Container padding tokens.spacing.sp16 (16 logical pixels all sides)
Border radius tokens.radius.r12 (12 logical pixels)
Icon box size (standard styles) kLayrzAlertIconBoxSize = 34.0 logical pixels
Icon glyph size (standard styles) kLayrzAlertIconSize = 22.0 logical pixels
Icon glyph size (filledIcon style) kLayrzAlertFilledIconSize = 25.0 logical pixels
Gap between icon and text tokens.spacing.sp12 (12 logical pixels)
Gap between title and description tokens.spacing.sp4 (4 logical pixels)
Max description lines maxLines parameter (default 3)

No fixed heights on text: Title and description elements have no explicit height constraints, allowing them to scale with the system font size (WCAG 1.4.4, AAA).


LayrzAlertIcon

A standalone circular icon chip widget that renders a semantic alert icon. It is NOT directly used by LayrzAlert — instead, LayrzAlert builds its own icon chips internally. This widget is provided as a reusable building block for other contexts.

API

class LayrzAlertIcon extends StatelessWidget {
  /// The semantic type of the alert icon.
  ///
  /// Determines the default icon and colour if [icon] or [color] are not provided.
  /// Defaults to [LayrzAlertType.info].
  final LayrzAlertType type;

  /// The outer size of the icon chip container (width and height in logical pixels).
  ///
  /// Defaults to [kLayrzAlertIconWidgetSize].
  final double size;

  /// The size of the icon glyph inside the chip.
  ///
  /// Defaults to [kLayrzAlertIconWidgetIconSize].
  final double iconSize;

  /// Padding between the chip edge and the icon glyph.
  final EdgeInsetsGeometry? padding;

  /// The colour of the icon chip, used only when [type] is [LayrzAlertType.custom].
  ///
  /// If null and [type] is [LayrzAlertType.custom], defaults to
  /// [LayrzTokens.colors.primary]. Ignored for non-custom types.
  final Color? color;

  /// The icon glyph, used only when [type] is [LayrzAlertType.custom].
  ///
  /// If null and [type] is [LayrzAlertType.custom], defaults to
  /// [LayrzIcons.solarOutlineInfoSquare]. Ignored for non-custom types.
  final IconData? icon;

  /// Creates a [LayrzAlertIcon].
  const LayrzAlertIcon({
    super.key,
    this.type = LayrzAlertType.info,
    this.size = kLayrzAlertIconWidgetSize,
    this.iconSize = kLayrzAlertIconWidgetIconSize,
    this.padding,
    this.color,
    this.icon,
  });
}

Standalone usage:

// Info icon chip
LayrzAlertIcon(
  type: LayrzAlertType.info,
  size: 40,
  iconSize: 24,
)

// Custom icon chip
LayrzAlertIcon(
  type: LayrzAlertType.custom,
  color: Colors.purple,
  icon: LayrzIcons.solarOutlineHeartLinear,
)

Material-Free Construction

LayrzAlert is built entirely without Material or Cupertino imports, using only package:flutter/widgets.dart primitives:

  1. Container — background and border rendering
  2. Row — horizontal layout for icon and text
  3. Column — vertical layout for title and description
  4. Icon — semantic icon rendering
  5. Text — text rendering with styling
  6. ClipRRect — corner radius clipping for filledIcon style
  7. IntrinsicHeight — height matching between panels in filledIcon style

Usage Examples

Basic Info Alert

LayrzAlert(
  type: LayrzAlertType.info,
  title: 'Information',
  description: 'This is an informational message for the user.',
)

Success Alert

LayrzAlert(
  type: LayrzAlertType.success,
  title: 'Operation Successful',
  description: 'Your changes have been saved.',
  style: LayrzAlertStyle.filledTonal,
)

Warning Alert with Custom Max Lines

LayrzAlert(
  type: LayrzAlertType.warning,
  title: 'Warning',
  description: 'This action requires your confirmation. Please review carefully before proceeding.',
  maxLines: 4,
  style: LayrzAlertStyle.outlined,
)

Danger (Error) Alert

LayrzAlert(
  type: LayrzAlertType.danger,
  title: 'Error',
  description: 'An unexpected error occurred. Please try again later.',
  style: LayrzAlertStyle.filled,
)

Context (Custom Semantic) Alert

LayrzAlert(
  type: LayrzAlertType.context,
  title: 'Context-Dependent',
  description: 'This alert\'s meaning depends on the surrounding application state.',
  style: LayrzAlertStyle.filledIcon,
)

Custom Type with Explicit Color and Icon

LayrzAlert(
  type: LayrzAlertType.custom,
  title: 'Custom Alert',
  description: 'Using a custom color and icon.',
  color: Colors.purple,
  icon: LayrzIcons.solarOutlineHeartLinear,
)

Inside a Form or Container

Container(
  padding: EdgeInsets.all(16),
  child: Column(
    children: [
      LayrzAlert(
        type: LayrzAlertType.info,
        title: 'Form Instructions',
        description: 'Please fill in all required fields marked with an asterisk (*).',
      ),
      SizedBox(height: 24),
      // Form fields follow...
    ],
  ),
)

Interactive Alert with Action Handler

LayrzAlert(
  type: LayrzAlertType.success,
  title: 'Update Available',
  description: 'A new version is ready. Tap to install.',
  style: LayrzAlertStyle.filledTonal,
  onTap: () {
    // Handle the tap — e.g., initiate download/installation
    handleUpdateInstall();
  },
)

When onTap is provided, the alert lifts on hover and announces as a button. Non-interactive alerts (onTap: null) remain the default and recommended pattern for informational callouts that require no user action.


Styling by Context

Choose the alert style based on the context where it appears:

Context Recommended Style Rationale
Inside a form on neutral background .layrz (default) Tonal border and icon chip provide semantic emphasis without overwhelming
Inside a card or elevated container .filledTonal or .filledIcon Solid fill ensures the alert stands out against the container background
Dismissible alert in a banner or overlay .filled Solid color commands attention for critical messages
Inline hint or secondary message .outlined Minimal visual weight, suitable for supplementary information
Split-screen or two-panel layout .filledIcon Left panel with icon, right panel with text; strong visual separation

Dependencies

  • M1 Theme System (LayrzTheme, LayrzThemeData, LayrzTokens) — colors, typography, spacing, radius, and border tokens
  • Flutter primitives (Container, Row, Column, Icon, Text, ClipRRect) — from package:flutter/widgets.dart
  • layrz_icons (^2.0.0) — icon constants (bare IconData, not wrapped)

Accessibility

  • Semantics:
    • Non-interactive alerts (onTap: null) are wrapped in Semantics(container: true, label: '<title>. <description>'), announcing as a container.
    • Interactive alerts (onTap: non-null) are wrapped in Semantics(button: true, enabled: true, label: '<title>. <description>'), announcing as an enabled button.
  • Keyboard activation: Interactive alerts are reachable via Tab and activatable by Enter or Space. No explicit shortcut binding is needed; LayrzApp (via WidgetStateIntent routes) supplies the binding automatically.
  • Color not the sole cue: the icon is a non-colour severity cue, satisfying WCAG 1.4.1 (Use of Color, A).
  • Text scaling: no fixed heights on text elements support WCAG 1.4.4 (Text Scaling, AAA) — alerts remain usable at 2x system font size.
  • Contrast: title and body text colors are resolved with sufficient contrast against their backgrounds per WCAG 1.4.3 (Contrast, AA).
  • Focus visible: Interactive alerts show focus via shadow elevation (lifted state), satisfying WCAG 2.4.7 (Focus Visible, AA).

Last updated: 2026-08-16
Related documents: Milestone 2, Design Tokens, Architecture, Decisions (D15), Roadmap

Clone this wiki locally