feat(style): cascading theme system and demo#110
Merged
mathiasbourgoin merged 25 commits intomainfrom Feb 18, 2026
Merged
Conversation
0ecb573 to
487e6b3
Compare
Introduce a new miaou_style library providing: - Style.t: core style type with fg/bg colors, text attributes, adaptive colors - Border: border style variants (Single, Double, Rounded, Ascii, Heavy) - Selector: CSS-like selector parser with pseudo-classes (:focus, :selected, :nth-child(even/odd/n)) and combinators (> for child, space for descendant) - Theme.t: semantic tokens (primary, error, text, etc.) + CSS-like rules - Theme_loader: file discovery, JSON parsing, theme merging - Style_context: effect-based implicit theme access Theme file discovery order (later overrides earlier): 1. Built-in default theme 2. ~/.config/miaou/theme.json (user global) 3. .miaou/theme.json (project local) 4. $MIAOU_THEME env var Also includes example theme files (dark.json, light.json, high-contrast.json) and unit tests for all modules.
Update layout widgets to set up style context for child widgets when rendering: - Pass widget_name, index, and count to Style_context.with_child_context - This enables CSS-like selectors such as :nth-child(even) to work for alternating row styles in flex/grid layouts Child widgets can now access their position via Style_context.current_context() to apply position-based styling rules from the theme.
Add themed_* functions to Widgets module that use Style_context to access the current theme: - themed_primary, themed_secondary, themed_accent - themed_error, themed_warning, themed_success, themed_info - themed_text, themed_muted, themed_emphasis - themed_border, themed_selection - themed_background, themed_background_alt - themed_contextual (for CSS-like selector-based styling) - current_widget_style() for accessing full style context Update AGENTS.md with comprehensive styling guidelines: - Document two-layer approach (semantic + contextual) - List all available themed functions with use cases - Explain when raw fg/bg is acceptable (gradients, charts) - Show how to set up style context for child widgets Widget authors should use these functions instead of hardcoded fg/bg colors to ensure consistent, themeable rendering across all widgets.
Update widgets to use semantic themed functions instead of hardcoded colors: - button_widget: Use themed_selection for focus state, themed_muted for disabled - card_widget: Use themed_emphasis for title (when no accent override) - Mark accent field as deprecated, encourage semantic styling - line_chart_widget: Use themed_emphasis for chart titles - sparkline_widget: Use themed_emphasis for focus state - bar_chart_widget: Use themed_emphasis for chart titles Chart widgets continue to accept colors from their data model (series colors, threshold colors) as this is appropriate for data visualization where colors convey data meaning rather than UI semantics.
- select_widget: use themed_selection for highlighted items - textarea_widget: use themed_border for box lines and themed_muted for placeholder/indicator text - toast_widget: map severities to themed_info/success/warning/error - progress_widget: use themed_muted for percentage and themed_secondary for labels (retain gradient fill) Spinner and progress gradients remain raw-color by design for animated/visual feedback; these are accepted uses of raw colors per styling guidelines.
Update complex widgets and markdown rendering to use semantic themed styles: - pager_widget: replace hardcoded ANSI colors in help modal, status line, search prompt, and cursor highlight with themed_* functions - table_widget: use themed borders, emphasis, selection, accent, and background styles across both terminal and SDL renderers - file_browser_widget: apply themed selection, accent, muted, success, warning, and error styles for path bar, entries, and status messages - box_widget: default border styling now uses themed_border; title uses themed_emphasis while preserving legacy per-side color overrides - modal_utils: markdown_to_ansi now uses themed semantic styles for code, headings, links, quotes, and list markers - tui_page.mli: document styling requirements for PAGE_SIG view functions Raw color codes remain only where gradients or data-driven chart colors are intended, aligning with the new styling guidelines.
Add a new demo showing the cascading style system with runtime theme switching and contextual styling via flex-child selectors. The demo includes three embedded themes (dark, light, high-contrast) and a simple flex layout of tiles to highlight semantic tokens and contextual rules.
- Ensure embedded themes provide all required fields so JSON parsing succeeds - Preserve flex-child index/count when adding focus context so nth-child rules apply
Show whether embedded themes parsed successfully so it is obvious when the style context falls back to the default theme. This helps diagnose why visual changes might not appear when switching themes.
Make Style.t fields optional in JSON parsing so theme files can specify only the attributes they care about. This fixes demo theme parsing errors and aligns with the intended cascading behavior.
Allow color values in theme JSON to be provided as:
- {Fixed: 75}
- [Fixed, 75]
- 75 (defaults to Fixed)
- {Adaptive: {light: 15, dark: 231}}
- [Adaptive, {light: 15, dark: 231}]
This preserves backward compatibility and fixes theme parsing failures in the
demo and bundled theme files.
Replace derived Style.t JSON parsing with a custom decoder that tolerates missing fields and ignores nulls. This prevents theme parsing failures for partial style objects and supports the intended cascading behavior. Also expose to_yojson/of_yojson aliases for compatibility with existing Theme JSON serialization.
Provide a tolerant widget_style JSON parser so theme rules can omit optional fields like border_style/border_fg/border_bg without failing. This resolves Border.style parse errors when loading demo themes.
Allow Border.style to parse from simple string values like Rounded in JSON theme files, fixing Border.style parse errors in demo themes.
Apply contextual background to padded lines in Box_widget and render_frame, reapplying background across ANSI resets so inline styling doesn't punch holes. This makes container backgrounds fill the full content width.
Adjust demo theme selection colors so selection stands out from contextual background fills and focus styles.
Introduce Theme.validate to detect low-contrast fg/bg pairs in semantic styles and CSS-like rule styles. The style-system demo now surfaces the first warning (if any) to help authors avoid unreadable combinations.
Add a None_ border style to Box_widget so containers can render without borders. The style-system demo now uses borderless tiles with padded content, matching the OpenCode-style layout with contrasted backgrounds.
Ensure the style-system demo preserves the flex-child widget name when adding focus/selection context so nth-child rules apply and tiles alternate backgrounds.
Document the new cascading style system, theme JSON support, widget theming changes, and the Box_widget None_ border style breaking change.
Ship example/demos/style_system/theme.json as an OpenCode-like flex theme and load it as the dark theme when present, so users can iterate on CSS-like rules without recompiling.
When example/demos/style_system/theme.json fails to load, the demo now falls back to the embedded theme JSON and surfaces a warning so users know the file-based theme was ignored.
1b626cb to
e6c4927
Compare
Box_widget now wraps border chars in ANSI codes via themed_border, so first.[0] is no longer '+'. Check for '+' using Str.search_forward to match the pattern used by other tests in the file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Testing