Skip to main content

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>;