FuguWeb

App::FuguWeb::Page(3p) Perl Library Manual App::FuguWeb::Page(3p)

App::FuguWeb::Page - the shared chrome around one body fragment

    use App::FuguWeb::Page;
    my $page = App::FuguWeb::Page->new(config => $config, lang => 'en');
    $page->write("$out/en/install.html", 'Install', $fragment,
        name => 'install.html');

Every source format is reduced to one HTML body fragment, and this class wraps it: the head, the header, the navigation, the fragment, and the footer. The layout is fixed. What a project decides is the site name, the navigation, and the footer prose.

The class builds the document with string operations, not with a substitution over a template. A title may therefore hold any character, including the slash and the ampersand that a "sed" template could not take.

Two separators are not ASCII: an em dash between the page title and the site name, and a middle dot between navigation entries. Both are byte constants. No file in the namespace carries "use utf8", and Fugu::File reads and writes bytes, so those bytes reach the output unchanged.

The title and every navigation label go through "escape_html" in App::FuguWeb.

    App::FuguWeb::Page->new(config => $config, lang => 'en')
    App::FuguWeb::Page->new(config => $config, kind => 'keys')
    App::FuguWeb::Page->new(config => $config, kind => 'top')

"config" is an App::FuguWeb::Config and is required.

"lang" is the code of the language of the page, and it defaults to "en". The "html" element carries it as "lang". The navigation takes the labels of that language.

"kind" is the kind of the page, and it decides the form of each relative link of the chrome. An absolute URL and a fragment stay as they stand.

    kind    the page                        a link      the sheet
    tree    <code>/<page>, the default      <page>      ../style.css
    keys    <dir>/index.html, the key page  ../en/<page> ../style.css
    top     404.html at the top level       en/<page>   style.css

A tree page names each page of its own tree. The key page and the top-level copy of the 404 page are pages of the default language, and they link its tree. The "top" page also carries "<base href="/">". A host serves it at each path that no file answers, and the base makes each relative link resolve from the root of the origin.

The constructor dies for an unknown kind.

    $page->write($path, $title, $fragment, %page)

Write the whole page with Fugu::File: the chrome around the fragment, as bytes. The fragment goes in unchanged: it is already HTML, from a renderer or from the project's own source directory. The method returns true on success, and "undef" with a message in the log otherwise. The page arguments are those of "document".

    $page->document($title, $fragment,
        name      => 'install.html',
        body_lang => 'en')

The whole page as bytes. A caller that writes the file itself, or that holds the page beside other generated bytes, reads the document here and never repeats the chrome.

"name" is the name of the page in its tree. A tree page of a site of more than one language links the same page in each other language, so it needs the name, and the method dies without it.

"body_lang" is the language of the fragment, and it defaults to the language of the page. A fragment of another language carries its code as the "lang" of the "<main>" element. A page of another tree whose body comes from the default source therefore carries "<main lang="en">". The parts of an index page come from more than one language, so App::FuguWeb::Site passes the language of the tree for it. "body" in App::FuguWeb::Index marks each part of another language.

A tree page links the same page in each other language of the site. The links follow the navigation entries, in the order of "languages" in App::FuguWeb::Config:

    <a href="../de/install.html" hreflang="de">de</a>

The code is the text and the "hreflang" of each link. A site of one language carries no such link, and neither does a page of another kind.

A footer.body.html in the configured source directory becomes the footer prose. It is project text, not tool text, so it is content and not a setting. A project without it gets no "<footer>" element and no rule before one: an empty footer is a gap, not chrome.

A language takes its own fragment from <code>/footer.body.html below the source directory. A language without one takes the default fragment.

A tree page carries the footer, and a page of another kind carries none. The prose is the project's own, and this class copies it in unchanged. A relative link inside it resolves against the directory of the page. Inside a tree it names a page of that tree. On the key page it would name a file of the key directory, and on the top-level copy of the 404 page a file of the root. The writer of the fragment named neither.

App::FuguWeb, App::FuguWeb::Config, App::FuguWeb::Site, Fugu::File

Dick Olsson <hi@senzilla.io>

2026-10-21 OpenBSD