The Layout Pattern
Define your application's shell once. Use
layout.html for
shared UI and
layout.py to
control the context dynamically.
Anatomy of a View
Core Principles
-
1
Strict Precedence
If
layout.pyexists, it is the sole authority. It must return the HTML content explicitly. -
2
Props Bubbling
Data flows upwards. Properties defined in a child layout (e.g.,
/docs/layout.py) override properties in the root layout. -
3
Nested Wrapping
Layouts render from the inside out. The page renders inside the leaf layout, which renders inside the parent layout, up to the root.
The Controller
from casp.layout import load_layout # 1. Define the logic def layout(context: dict): props = { "title": "My App", "body_class": "antialiased bg-background", "user": context.get("user") } # 2. Return Tuple: (HTML Content, Props) # Strict Mode: You MUST load the file explicitly return load_layout(__file__), props
Return Types
The View
<!-- Access props via layout.* --> <html> <head> <title>{{ layout.title }}</title> </head> <body class="{{ layout.body_class }}"> <nav>...</nav> <!-- The content injection point --> {{ children | safe }} </body> </html>
Route Groups & Multiple Root Layouts
Sometimes you need completely different layouts for different parts of
your app (e.g., a Marketing landing page vs. an App Dashboard). You can
use Route Groups by wrapping folder names in
parenthesis
(name).
How it works
Folders in parenthesis are omitted from the URL.
-
app/(marketing)/index.html→/ -
app/(dashboard)/index.html→/dashboard*
* Assuming dashboard is defined inside the group or handled via index precedence.
Benefit
This allows you to define a Marketing Layout (Navbar, Footer, Hero) that is completely different from your Dashboard Layout (Sidebar, Header, Graphs) without conditional logic spaghetti.