Naming Convention¶
Every view is a *_View.yaml + *_ViewModel.py pair. The naming is
enforced at runtime by both App.load and instantiate:
from tesserae import App
from Counter_ViewModel import CounterViewModel
app = App(width=240, height=120, title="My App")
view, viewmodel = app.load("Counter_View.yaml", CounterViewModel)
Raises ValueError immediately if:
- the view file doesn't end in
_View.yaml, - the
ViewModel's own defining file doesn't end in_ViewModel.py(checked viainspect.getfileagainst the class itself, not a string you pass separately), or - the two prefixes don't match (
Counter_View.yamlpaired withSettings_ViewModel.py, say).
Style, stylesheet and theme files¶
Three more names say what a YAML file is:
| Suffix | What it holds | Used by |
|---|---|---|
*_Style.yaml |
one node's style: style: over a mapping of style fields, and an optional id: |
a node's style:, a stylesheet or theme rule's style: |
*_Stylesheet.yaml |
a stylesheet: a styles: list of rules |
App(stylesheet=), app.load(..., stylesheet=), load_stylesheet |
*_Theme.yaml |
a theme: seed:, colors:, styles:, ... |
App(default_theme=, custom_theme=), load_theme |
A node's style: given as a file must be a *_Style.yaml one. A file
named for one kind and loaded as another is refused, naming what its
name says it is -- load_theme("card_Style.yaml"), say. A theme or
stylesheet file with none of these suffixes (themes/Brand.yaml) still
loads; the suffixes are recommended, not required.
A component's stylesheet¶
A file named <Name>_Stylesheet.yaml next to a view, where <Name> is a
component, is that component's stylesheet. The component is a built-in one
(ButtonFilled_Stylesheet.yaml) or your own (<Name>_Component.yaml).
# ButtonFilled_Stylesheet.yaml
styles:
- id: root
style: {background: tertiary, corner_radius: "{{ corner_radius }}"}
It is a styles: list of {id: <part>, style: {...}} rules, and each
id must be a part of the component. Its values may use the component's
{{ parameters }}. It goes over the built-in stylesheet field by field,
so you restyle only the fields you name. If you write your own
<Name>_Component.yaml for a built-in name, your fragment replaces the
built-in one whole, and only the sheets from your own folders apply. See
Stylesheets.
Why enforce this at all?¶
A ViewModel is scoped one-per-view-file by convention, so a
self-contained, independently-loadable pair is what lets App
register several and switch which one is shown without re-bootstrapping
each one from scratch. Catching a mismatched pair immediately, at load
time, is worth more than a cryptic failure much later when a handler
name doesn't resolve against the wrong ViewModel.
Where it's checked¶
Both call sites use the same shared check
(tesserae.naming.check_naming_convention):
App.load-- for top-level screens.instantiate-- for embedded components (see Components & Embedding), so a mismatched component pair fails immediately at instantiation time too.
App.register and manually constructed Components bypass this: they
take an already-constructed View and ViewModel. The convention is
enforced at the two construction entry points, not on whatever you hand
register.