Writing pages

The content of pages are written into file in YAML format.

Technically speaking Python translate YAML files in dictionaries and lists, so within Almoststatic, pages are dictionaries of data.

To facilitate the writing of pages, the HTML contents can be written in Markdown format which is easy to write and read and is less error prone then standard HTML. However, you can mix HTML and Markdown if you need more control.

Pages are stored by default in the ./content/pages folder, and we can arrange pages into subfolders.

Minimal page

Let see a minimal page:

title: My page
author: Me

  - type: text_html
    id: row1
    text: |
      ## My text

      This is a **Markdown** text

- type: text_html
   id: row2
   text: |
      <h2>My second text</h2>

      And this is a plain <b>html</b> text

Every page can have some metadata values, but are not required. If omitted, default values are kept. Metadata recognized are:





full pathname of file

Automatically calculated


name of page (same of url)

Automatically calculated


title of page

Should be present


description of page

Configurable in config.yaml


author of page

Configurable in config.yaml


template name used to render the page

Configurable in config.yaml


base page to extend

Configurable in config.yaml


if page is cacheable it is rendered only once

Set to False it page must be recalculated at every call.


list of two strings with code surrounding widgets



date of page

If not declared the last date of modification


special section with all data for blog pages

Empty for not blog pages

If needed, you can also add other fields available to all widgets, i.e.: if you add menu: EnglishMenu you can read it in pages as page.EnglishMenu

Every page must have a content key which contain a list of widgets which are rendered by templates. In the previous sample we have 2 widgets of type text_html, the first render some Markdown text and the second some HTML.

Each page can have a widgets_envelope which is the default envelope for each widget (see documentation for widgets below).

With this structure, the page content is send to the templ_name that extends the extends base page (see details in Jinja documentation). In this way the resulting page is an HTML page which contain some fixed contents like headers, navbars and footers whit a list of widgets in the middle.

With templ_name and extends metadata filed is possible to change the base look of classes of pages, i.e. you can have a look for standard pages and one for blog pages.

Some extra contents

Often you need more sophisticated pages; to do that, you can add content that is not automatically recognized but can be rendered into templates:

title: My page
author: Me

  $home: "[Home]({{get_url(^^index^^)}})"

  id: hero
  image: hero/hero1.jpg
  text: My content

  - type: text_html
    id: intro
    class: info-box
    text: |
      ## Aside
      This content is inserted out of main content flow.

  - type: text_html
    id: row1
    text: |
      ## My text

      This is a **Markdown** text

In this case, content is automatically recognized, but hero and aside can be intercepted by templates into the page and can also be used the macro $home which contains the link to home page.


So content list is composed by widgets and the same can be for extra contents, but what is a widget?

A widget is a portion of HTML. His data is declared in the page and the look is given by a template, so the widget:

- type: text_html
  disabled: false
  text: |
    ## My text
    This is a **Markdown** text

is rendered by text_html.html template which look like:


This is the simplest widget; to see more sophisticated widgets, take a look at the samples. With the power of Jinja2 template system, you can use conditions and loops with a full level of complexity.


in {{widget.text}}, "widget.text" is the field to write on page


Markdown text is rendered by Python-Markdown which can be configured a little with extensions (see documentation). By default enabled extensions are: ['tables', 'fenced_code', 'attr_list','admonition'] but you can add more in your source code with:


See the source of flaskapp.py sample.

You can add official extension or third part extension or even write your extensions, see documentation.

Widgets fields

Almoststatic has few rules. To add a widget within a page you have to declare its type so Almoststatic can find the right template. Within a widget, the only field recognized is text, which is rendered as html.

You can optionally surround the widget into an envelope which is a list of 2 snippets of HTML code the first added before the widget and the second added after the widget. If not present a widgets_envelope at page level is searched.

Then you can add as many fields as your template requires. The following is a widget a little more complex:

- type: carousel
  id: carousel1
  class: carousel-fade
  envelope: ['<span>','</span>']
  btn_class: "btn btn-primary btn-lg"
    - image: [carousel1/slide1.jpg, "image_class", "image_alt"]
      align: text-start
      text: |
        # My first image
        some text
      buttons: [["Learn more", "#"]]

    - image: [carousel1/slide2.jpg, "image_class1", "image_alt1"]
      text: |
        # My second image
        Another wonderful text, inline.
          ["Gallery", "#", "btn btn-info"],
          ["Once more", "#", "btn btn-primary"],

    - image: [carousel1/slide3.jpg, "image_class2", "image_alt2"]
      align: text-end
      text: |
        # lorem text
      buttons: [["Learn more", "#", "btn btn-info"]]

In this case we have a carousel with several slides with images, text and buttons

If you like, you can disable a widget, for example, to choose between different solutions. Each widget can have the disabled field which excludes it from rendering.

- type: text_html
  disabled: True
  text: |
    ## This will never be rendered

Widgets best practices

With widgets you have a full freedom in fields. But we advise to use a consistent style of naming fields and use at least some of this




Every widget should have a unique ID into the page, this helps to apply custom style


This can apply a CSS class to the widget


If id is set it’s easy to add some css style to the widget


is the main content field. It is also used to embed and include widgets

In the same way, use a clean style in your templates.

{# text_html

The simplest widget, display the text entered as html or markdown

id: give a unique ID to every widget in the page
style: additional css style rules for this ID
class: set additional classes for the widget
text: text or content

{%include "include/widget_style.html" -%}
<div id="{{widget.id}}" class="{{widget.class}}">

Blog pages

Almoststatic has some blog capabilities.

Blog pages are ordinary pages rendered in the same way used for other pages.

The only difference is that them have the blog section which contain blog metadata used to group and search blog pages.

title: My page
author: Me

  image: blog/blog1.jpg
  show_image: true
  disabled: false
  intro: |
    some intro info
  tags: [blog, article]
  categories: [python, test]
  tag_order: { blog: 98 }
  categ_order: { python: 99, flask: 3 }

  - type: text_html
    id: row1
    text: |
      ## My blog page

      This is a **Markdown** text

Blog section can contain:




image used as intro or to list blog pages


flag used to switch off image display


the page is excluded from blog metadata until is not ready


a brief intro text describing the content of the article


list of tags for the page


list of categories of the page


tags can be listed by priority and date


categories can be listed by priority and date

Blog info are stored in metadata information which can be queried with the query_blog function to discover all pages with same categories or tags of the current page or to get arbitrary blog pages.


Blog functions are easy and useful, but not that powerful. If you plan to write a blog with hundreds of pages, maybe Almoststatic is not the right solution, better chose not static system which can query databases.