import Elementary import Foundation /// A page of a website: an HTML document with the shared scaffolding assembled around the page's content. /// /// A conforming page supplies its locale, its title, the stylesheets and scripts it needs, its head metadata, and its content; the protocol assembles the /// rest of the document around them: the viewport declaration and stylesheet links followed by the metadata in the head, and the content followed by /// the script tags in the body. public protocol Page: HTMLDocument, Sendable { // MARK: Associated types /// The type of the page's markup. associatedtype Content: HTML /// The type of the page's head metadata markup. associatedtype Metadata: HTML // MARK: Properties /// The version token appended to the page's asset URLs, or `nil` to leave them unversioned. var assetVersion: String? { get } /// The page's markup, rendered before the ``scripts``. @HTMLBuilder var content: Content { get } /// The locale the page content is localized to. var locale: Locale { get } /// The markup placed in the document head after the ``stylesheets``: icon and manifest /// links, extra meta tags, and the like. @HTMLBuilder var metadata: Metadata { get } /// The scripts loaded at the end of the document body, in order. var scripts: [any Asset] { get } /// The stylesheets linked in the document head, in order. var stylesheets: [any Asset] { get } } // MARK: - Implementations public extension Page { // MARK: Computed /// The page ``content`` followed by its ``scripts``. @HTMLBuilder var body: some HTML { content for file in scripts { script(.src(file.urlPath( for: .js, version: assetVersion ))) {} } } /// The viewport declaration and ``stylesheets`` links followed by the ``metadata``, placed in the document head. /// /// The charset declaration is omitted: Elementary's `HTMLDocument` scaffolding already emits `` before this markup, /// and HTML5 allows only one. @HTMLBuilder var head: some HTML { meta( .name(.viewport), .content("width=device-width, initial-scale=1") ) metadata for file in stylesheets { link( .rel(.stylesheet), .href(file.urlPath( for: .css, version: assetVersion )) ) } } }