Skip to main content

semantic_appearance/
semantic_appearance.rs

1use maruzzella::{
2    default_product_spec, run, text_tab, BottomPanelLayout, ButtonAppearance, ButtonStyle,
3    InputAppearance, MaruzzellaConfig, SurfaceAppearance, SurfaceLevel, TabGroupSpec,
4    TabStripAppearance, TabStripStyle, TextAppearance, TextRole, ThemeSpec, Tone,
5    WorkbenchNodeSpec,
6};
7
8fn main() {
9    let mut product = default_product_spec();
10    product.branding.title = "Semantic Appearance Demo".to_string();
11    product.branding.search_placeholder = "Search semantic appearance demo".to_string();
12    product.branding.status_text = "Panels, buttons, typography, and tabs styled by named roles"
13        .to_string();
14
15    product.layout.left_panel = TabGroupSpec::new(
16        "panel-left",
17        Some("overview"),
18        vec![
19            text_tab(
20                "overview",
21                "panel-left",
22                "Overview",
23                "This panel uses the `primary-panel` appearance id.",
24                false,
25            ),
26            text_tab(
27                "teams",
28                "panel-left",
29                "Teams",
30                "Downstream apps can override the meaning of `primary-panel` in ThemeSpec.",
31                false,
32            ),
33        ],
34    )
35    .with_panel_appearance("primary-panel")
36    .with_panel_header_appearance("panel-header")
37    .with_tab_strip_appearance("side-tabs");
38
39    product.layout.right_panel = TabGroupSpec::new(
40        "panel-right",
41        Some("inspector"),
42        vec![
43            text_tab(
44                "inspector",
45                "panel-right",
46                "Inspector",
47                "This panel uses `secondary-panel` without any raw CSS selectors.",
48                false,
49            ),
50            text_tab(
51                "history",
52                "panel-right",
53                "History",
54                "Buttons, labels, and tab strips resolve through the same semantic registry.",
55                false,
56            ),
57        ],
58    )
59    .with_panel_appearance("secondary-panel")
60    .with_panel_header_appearance("panel-header")
61    .with_tab_strip_appearance("side-tabs");
62
63    product.layout.bottom_panel = TabGroupSpec::new(
64        "panel-bottom",
65        Some("console"),
66        vec![
67            text_tab(
68                "console",
69                "panel-bottom",
70                "Console",
71                "The bottom panel uses a console-specific panel and tab-strip role.",
72                false,
73            )
74            .with_text_appearance("code"),
75        ],
76    )
77    .with_panel_appearance("console-panel")
78    .with_panel_header_appearance("panel-header")
79    .with_tab_strip_appearance("console-tabs")
80    .with_text_appearance("code");
81    product.layout.bottom_panel_layout = BottomPanelLayout::FullWidth;
82
83    product.layout.workbench = WorkbenchNodeSpec::Group(
84        TabGroupSpec::new(
85            "workbench-main",
86            Some("brief"),
87            vec![
88                text_tab(
89                    "brief",
90                    "workbench-main",
91                    "Brief",
92                    "The central workbench uses `canvas-panel` plus an `editor-tabs` strip.",
93                    false,
94                ),
95                text_tab(
96                    "copy",
97                    "workbench-main",
98                    "Typography",
99                    "This example also overrides named text roles such as `title`, `meta`, and `code`.",
100                    true,
101                )
102                .with_text_appearance("meta"),
103            ],
104        )
105        .with_panel_appearance("canvas-panel")
106        .with_panel_header_appearance("panel-header")
107        .with_tab_strip_appearance("editor-tabs"),
108    );
109
110    let config = MaruzzellaConfig::new("com.example.semantic-appearance")
111        .with_persistence_id("semantic-appearance-demo")
112        .with_theme(semantic_theme())
113        .with_product(product);
114
115    run(config);
116}
117
118fn semantic_theme() -> ThemeSpec {
119    let mut theme = ThemeSpec::default();
120    theme.typography.font_family = "\"Azeret Mono\", \"Noto Sans\", sans-serif".to_string();
121    theme.typography.mono_font_family = "\"IBM Plex Mono\", monospace".to_string();
122    theme.typography.font_size_base = 14;
123    theme.typography.font_size_ui = 13;
124    theme.typography.font_size_small = 12;
125    theme.typography.font_size_tiny = 11;
126    theme.typography.font_size_title = 28;
127    theme.palette.bg_0 = "#f1efe6".to_string();
128    theme.palette.bg_1 = "#e3dfd3".to_string();
129    theme.palette.workbench = "#f8f6ef".to_string();
130    theme.palette.panel_left = "#d7e1dd".to_string();
131    theme.palette.panel_right = "#e7ddd0".to_string();
132    theme.palette.panel_bottom = "#d9d5df".to_string();
133    theme.palette.border = "#b5b0a3".to_string();
134    theme.palette.border_strong = "#8f8777".to_string();
135    theme.palette.text_0 = "#1f1e1a".to_string();
136    theme.palette.text_1 = "#4b463d".to_string();
137    theme.palette.text_2 = "#6d665a".to_string();
138    theme.palette.accent = "#0f766e".to_string();
139    theme.palette.accent_strong = "#0b5f58".to_string();
140    theme.density.radius_medium = 12;
141    theme.density.radius_large = 16;
142    theme.density.toolbar_height = 46;
143    theme.density.tab_height = 32;
144    theme.density.panel_header_height = 30;
145
146    theme
147        .with_surface_appearance(
148            "primary-panel",
149            SurfaceAppearance::new(Tone::Primary, SurfaceLevel::Raised, TextRole::Body),
150        )
151        .with_surface_appearance(
152            "secondary-panel",
153            SurfaceAppearance::new(Tone::Secondary, SurfaceLevel::Raised, TextRole::Body),
154        )
155        .with_surface_appearance(
156            "console-panel",
157            SurfaceAppearance::new(Tone::Tertiary, SurfaceLevel::Sunken, TextRole::Code),
158        )
159        .with_surface_appearance(
160            "canvas-panel",
161            SurfaceAppearance::new(Tone::Neutral, SurfaceLevel::Sunken, TextRole::Body)
162                .borderless(),
163        )
164        .with_surface_appearance(
165            "panel-header",
166            SurfaceAppearance::new(Tone::Neutral, SurfaceLevel::Flat, TextRole::SectionLabel),
167        )
168        .with_button_appearance(
169            "primary",
170            ButtonAppearance::new(Tone::Accent, ButtonStyle::Solid, TextRole::BodyStrong),
171        )
172        .with_button_appearance(
173            "secondary",
174            ButtonAppearance::new(Tone::Primary, ButtonStyle::Soft, TextRole::Body),
175        )
176        .with_button_appearance(
177            "ghost",
178            ButtonAppearance::new(Tone::Neutral, ButtonStyle::Ghost, TextRole::Body),
179        )
180        .with_input_appearance(
181            "search",
182            InputAppearance::new(Tone::Secondary, SurfaceLevel::Sunken, TextRole::Body),
183        )
184        .with_text_appearance(
185            "title",
186            TextAppearance {
187                role: TextRole::Title,
188                tone: Tone::Accent,
189            },
190        )
191        .with_text_appearance(
192            "meta",
193            TextAppearance {
194                role: TextRole::Meta,
195                tone: Tone::Neutral,
196            },
197        )
198        .with_tab_strip_appearance(
199            "side-tabs",
200            TabStripAppearance::new(Tone::Primary, TabStripStyle::Utility, TextRole::TabLabel),
201        )
202        .with_tab_strip_appearance(
203            "editor-tabs",
204            TabStripAppearance::new(Tone::Neutral, TabStripStyle::Editor, TextRole::TabLabel),
205        )
206        .with_tab_strip_appearance(
207            "console-tabs",
208            TabStripAppearance::new(Tone::Tertiary, TabStripStyle::Console, TextRole::TabLabel),
209        )
210}