Skip to content

Layouts

Tools for building layouts and several simple layouts for quick prototyping.

filter_body_tags

filter_body_tags(tags)

Given a list of tags, only list the ones that belong in body of an HTML document.

Source code in src/air/layouts.py
 9
10
11
def filter_body_tags(tags) -> list:
    """Given a list of tags, only list the ones that belong in body of an HTML document."""
    return [t for t in tags if not isinstance(t, HEAD_TAG_TYPES)]

filter_head_tags

filter_head_tags(tags)

Given a list of tags, only list the ones that belong in head of an HTML document.

Source code in src/air/layouts.py
14
15
16
def filter_head_tags(tags) -> list:
    """Given a list of tags, only list the ones that belong in head of an HTML document."""
    return [t for t in tags if isinstance(t, HEAD_TAG_TYPES)]

mvpcss

mvpcss(*children, is_htmx=False, **kwargs)

Renders the basic layout with MVP.css and HTMX for quick prototyping

  1. At the top level HTML head tags are put in the <head> tag
  2. Otherwise everything is put in the <body>
  3. Header and Nav tags are placed in the top of the body above the Main tag
  4. If is_htmx is True, then the layout isn't included. This is to support the hx_boost feature of HTMX

The mvpcss function is a quick prototyping tool. It isn't designed to be extensible. Rather the mvpcss layout function makes it easy to roll out quick demonstrations and proofs-of-concept. For more advanced layouts like Eidos or a full-fledged MVP.css implementation, you'll have to create your own layouts.

Parameters:

Name Type Description Default
children Any

These typically inherit from air.Tag but can be anything

()
is_htmx bool

Whether or not HTMX sent the request from the page

False

Example:

import air

app = air.Air()


@app.page
async def index(request: air.Request):
    return air.layouts.mvpcss(
        air.Title("Home"),
        air.Article(
            air.H1("Welcome to Air"),
            air.P(air.A("Click to go to Dashboard", href="/dashboard")),
            hx_boost="true",
        ),
        is_htmx=request.htmx.is_hx_request,
    )


@app.page
async def dashboard(request: air.Request):
    return air.layouts.mvpcss(
        air.Title("Dashboard"),
        air.Article(
            air.H1("Dashboard"),
            air.P(air.A("Go home", href="/")),
            hx_boost="true",
        ),
        is_htmx=request.htmx.is_hx_request,
    )


if __name__ == "__main__":
    import uvicorn

    uvicorn.run(app, host="127.0.0.1", port=8000)
Source code in src/air/layouts.py
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
def mvpcss(*children: Any, is_htmx: bool = False, **kwargs: AttributeType) -> Html | Children:
    """Renders the basic layout with MVP.css and HTMX for quick prototyping

    1. At the top level HTML head tags are put in the `<head>` tag
    2. Otherwise everything is put in the `<body>`
    3. `Header` and `Nav` tags are placed in the top of the body above the `Main` tag
    4. If `is_htmx` is True, then the layout isn't included. This is to support the `hx_boost`
        feature of HTMX

    The `mvpcss` function is a quick prototyping tool. It isn't designed to be extensible.
        Rather the `mvpcss` layout function makes it easy to roll out quick demonstrations and proofs-of-concept.
        For more advanced layouts like Eidos or a full-fledged MVP.css implementation,
        you'll have to create your own layouts.

    Args:
        children: These typically inherit from air.Tag but can be anything
        is_htmx: Whether or not HTMX sent the request from the page

    Example:

        import air

        app = air.Air()


        @app.page
        async def index(request: air.Request):
            return air.layouts.mvpcss(
                air.Title("Home"),
                air.Article(
                    air.H1("Welcome to Air"),
                    air.P(air.A("Click to go to Dashboard", href="/dashboard")),
                    hx_boost="true",
                ),
                is_htmx=request.htmx.is_hx_request,
            )


        @app.page
        async def dashboard(request: air.Request):
            return air.layouts.mvpcss(
                air.Title("Dashboard"),
                air.Article(
                    air.H1("Dashboard"),
                    air.P(air.A("Go home", href="/")),
                    hx_boost="true",
                ),
                is_htmx=request.htmx.is_hx_request,
            )


        if __name__ == "__main__":
            import uvicorn

            uvicorn.run(app, host="127.0.0.1", port=8000)
    """
    body_tags = filter_body_tags(children)
    head_tags = filter_head_tags(children)

    if is_htmx:
        return Children(Main(*body_tags), *head_tags)

    return Html(
        Head(
            Link(rel="stylesheet", href="https://unpkg.com/mvp.css"),
            Style("footer, header, main { padding: 1rem; } nav {margin-bottom: 1rem;}"),
            Script(
                src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.6/dist/htmx.min.js",
                integrity="sha384-Akqfrbj/HpNVo8k11SXBb6TlBWmXXlYQrCSqEWmyKJe+hDm3Z/B2WVG4smwBkRVm",
                crossorigin="anonymous",
            ),
            *head_tags,
        ),
        Body(
            _header(body_tags),
            Main(*[x for x in body_tags if not isinstance(x, Header)]),
        ),
    )

picocss

picocss(*children, is_htmx=False, **kwargs)

Renders the basic layout with PicoCSS and HTMX for quick prototyping

  1. At the top level HTML head tags are put in the <head> tag
  2. Otherwise everything is put in the <body>
  3. If is_htmx is True, then the layout isn't included. This is to support the hx_boost feature of HTMX
PicoCSS is a quick prototyping tool. It isn't designed to be extensible.

Rather the picocss layout function makes it easy to roll out quick demonstrations and proofs-of-concept. For more advanced layouts like Eidos or a full-fledged PicoCSS implementation, you'll have to create your own layouts.

Parameters:

Name Type Description Default
children Any

These typically inherit from air.Tag but can be anything

()
is_htmx bool

Whether or not HTMX sent the request from the page

False

Example:

import air

app = air.Air()


@app.page
async def index(request: air.Request):
    return air.layouts.picocss(
        air.Title("Home"),
        air.Article(
            air.H1("Welcome to Air"),
            air.P(air.A("Click to go to Dashboard", href="/dashboard")),
            hx_boost="true",
        ),
        is_htmx=request.htmx.is_hx_request,
    )


@app.page
async def dashboard(request: air.Request):
    return air.layouts.picocss(
        air.Title("Dashboard"),
        air.Article(
            air.H1("Dashboard"),
            air.P(air.A("Go home", href="/")),
            hx_boost="true",
        ),
        is_htmx=request.htmx.is_hx_request,
    )


if __name__ == "__main__":
    import uvicorn

    uvicorn.run(app, host="127.0.0.1", port=8000)
Source code in src/air/layouts.py
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
def picocss(*children: Any, is_htmx: bool = False, **kwargs: AttributeType) -> Html | Children:
    """Renders the basic layout with PicoCSS and HTMX for quick prototyping

    1. At the top level HTML head tags are put in the `<head>` tag
    2. Otherwise everything is put in the `<body>`
    3. If `is_htmx` is True, then the layout isn't included. This is to support the `hx_boost`
        feature of HTMX

    Note: `PicoCSS` is a quick prototyping tool. It isn't designed to be extensible.
        Rather the `picocss` layout function makes it easy to roll out quick demonstrations and proofs-of-concept.
        For more advanced layouts like Eidos or a full-fledged PicoCSS implementation,
        you'll have to create your own layouts.

    Args:
        children: These typically inherit from air.Tag but can be anything
        is_htmx: Whether or not HTMX sent the request from the page

    Example:

        import air

        app = air.Air()


        @app.page
        async def index(request: air.Request):
            return air.layouts.picocss(
                air.Title("Home"),
                air.Article(
                    air.H1("Welcome to Air"),
                    air.P(air.A("Click to go to Dashboard", href="/dashboard")),
                    hx_boost="true",
                ),
                is_htmx=request.htmx.is_hx_request,
            )


        @app.page
        async def dashboard(request: air.Request):
            return air.layouts.picocss(
                air.Title("Dashboard"),
                air.Article(
                    air.H1("Dashboard"),
                    air.P(air.A("Go home", href="/")),
                    hx_boost="true",
                ),
                is_htmx=request.htmx.is_hx_request,
            )


        if __name__ == "__main__":
            import uvicorn

            uvicorn.run(app, host="127.0.0.1", port=8000)
    """
    body_tags = filter_body_tags(children)
    head_tags = filter_head_tags(children)

    if is_htmx:
        return Children(Main(*body_tags, class_="container"), *head_tags)

    return Html(
        Head(
            Link(
                rel="stylesheet",
                href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css",
            ),
            Script(
                src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.6/dist/htmx.min.js",
                integrity="sha384-Akqfrbj/HpNVo8k11SXBb6TlBWmXXlYQrCSqEWmyKJe+hDm3Z/B2WVG4smwBkRVm",
                crossorigin="anonymous",
            ),
            *head_tags,
        ),
        Body(Main(*body_tags, class_="container")),
    )