teksilo_core/styles/banner_style.rs
1// SPDX-License-Identifier: MPL-2.0
2// SPDX-FileCopyrightText: 2026 FernTech
3
4//! Tier-3 style protocol for `Banner`. See `docs/styling-system.md`.
5//!
6//! Themes the persistent inline status strip — the per-severity
7//! surface tint, corner radius, padding, and the arrangement of the
8//! leading severity glyph next to the message/action content. The
9//! `Banner` widget keeps its `Role::Status` / `Live::Polite`
10//! accessibility node and builds the functional `SeverityGlyph`
11//! painter itself (principle 6: a domain renderer is not chrome).
12
13use std::rc::Rc;
14
15use serde::{Deserialize, Serialize};
16use teksilo_tokens::SurfaceRole;
17
18use crate::build_context::BuildContext;
19use crate::widget_id::WidgetId;
20
21/// Banner severity level. Drives the surface tint, glyph color, and
22/// glyph shape. Apps with a "neutral" callout requirement should use
23/// a `Card` instead.
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
25pub enum BannerSeverity {
26 /// Informational notice — accent-tinted background, circle glyph.
27 Info,
28 /// Success / confirmation — green-tinted background, circle glyph.
29 Success,
30 /// Non-fatal warning — amber-tinted background, triangle glyph.
31 Warning,
32 /// Error / critical condition — red-tinted background, circle glyph.
33 Error,
34}
35
36impl BannerSeverity {
37 /// Surface-tint role for the banner strip background.
38 pub fn surface(self) -> SurfaceRole {
39 match self {
40 Self::Info => SurfaceRole::StatusInfo,
41 Self::Success => SurfaceRole::StatusSuccess,
42 Self::Warning => SurfaceRole::StatusWarning,
43 Self::Error => SurfaceRole::StatusError,
44 }
45 }
46
47 /// Foreground color for the leading severity glyph.
48 pub fn glyph_color(self, theme: &crate::styles::Theme) -> teksilo_tokens::Color {
49 match self {
50 Self::Info => theme.colors.status_info_fg,
51 Self::Success => theme.colors.status_success_fg,
52 Self::Warning => theme.colors.status_warning_fg,
53 Self::Error => theme.colors.status_error_fg,
54 }
55 }
56}
57
58#[derive(Clone, Debug)]
59pub struct BannerStyleConfig {
60 /// Severity hint — drives the recipe's surface tint.
61 pub severity: BannerSeverity,
62 /// Pre-built message + action content (everything but the leading
63 /// glyph) the strip arranges to the right of the glyph.
64 pub content: WidgetId,
65 /// Pre-built `SeverityGlyph` subtree — placed at the leading edge.
66 pub leading_glyph: WidgetId,
67}
68
69pub trait BannerStyle: 'static {
70 fn make_body(&self, cfg: &BannerStyleConfig, ctx: &mut BuildContext) -> WidgetId;
71}
72
73pub type SharedBannerStyle = Rc<dyn BannerStyle>;