UNPKG

secst

Version:

SECST is a semantic, extensible, computational, styleable tagged markup language. You can use it to joyfully create compelling, interactive documents backed by HTML.

1,417 lines • 74.3 kB
:header[:p({align:"left"})[:a(.#the-joy-of-secst)[SECST] Markup Language] :p({align:"right"})[:a(https://github.com/anywhichway/secst)[GitHub]]]
:footer[:p({align:"left"})[Copyright 2022, AnyWhichWay LLC]]
:section[

:h[The Joy of SECST]
    :abbr[SECST] (:strong[S]emantic, :strong[E]xtensible, :strong[C]omputational, :strong[S]tyleable, :strong[T]agged) is a more expressive, but easy to learn and use, alternative to Markdown. It provides these features and more:

    :ul[
        - Support for almost all HTML tags :a(.#tags)[and then some...], with :a(.#theme)[themes and styles].
        - :a(.#tables-of-contents)[Table of contents], :a(.#footnotes)[footnote], :a(.#hashtags)[hashtag] and :a(.#mentions)[social media] support.
        - Reactive :a(.#values-and-formulas)[formulas and data import] that are as simple as Excel formulas, e.g. :code[:value[2 * 2]] = :value[2 * 2].
        - SEO optimization via server rendering with JSON-LD :a(.#structured-data)[structured data] for both content generation and layout.
    ]

    Use SECST to joyfully create compelling, interactive HTML documents.

    This is the source for the introduction above:

    :code({language:"secst"})[
        :abbr[SECST] (:strong[S]emantic, :strong[E]xtensible, :strong[C]omputational, :strong[S]tyleable, :strong[T]agged) is a more expressive, but easy to learn and use, alternative to Markdown. It provides these features and more:

        :ul[
            - Support for almost all HTML tags :a(.#tags)[and then some...], with :a(.#theme)[themes and styles].
            - :a(.#tables-of-contents)[Table of contents], :a(.#footnotes)[footnote], :a(.#hashtags)[hashtag] and :a(.#mentions)[social media] support.
            - Reactive :a(.#values-and-formulas)[formulas and data import] that are as simple as Excel formulas, e.g. :code[:value[2 * 2]] = :value[2 * 2].
            - SEO optimization via server rendering with JSON-LD :a(.#structured-data)[structured data] for both content generation and layout.
        ]

        Use SECST to joyfully create compelling, interactive HTML documents.
    ]
]
:section[
    :toc(toggle)[Table Of Contents]
]
:section[
    :h[About This Guide]

      The SECST language, reference HTML transpiler and runtime support implementations are currently in :strong[ALPHA]. Most, but not all, documented features are implemented, testing is far from complete and resource sizes have not been fully optimized.

      This site's navigation, footnotes, computations, special styles like :kbd[Key] formatting and imported data are built entirely using SECST markup.

      You can tap on the icon next to a heading to access a table of contents at any time. You can goto the top of the guide by tapping of SECST in the top left corner.

      This guide is comprehensive, which may make it a little overwhelming. However, SECST has been designed so that you can start using it just a few tags and capabilities at a time.

      The following order of learning is suggested:

      :ol[
        :li[Use tags that support markup similar to :a(.#basic-markdown)[Markdown].]
        :li[Start leveraging attributes like :code[align] on :a(.#images)[img].]
        :li[Try :a(.#theme)[themes or styles].]
        :li[If you are into music, math, or chemistry try :a(.#sheet-music)[:code[sheetMusic]] or :a(.#latex)[:code[Latex]].]
        :li[Add some simple :a(.#values-and-formulas)[values and formulas].]
        - Anything else that gives you joy!
      ]

      For gentle overviews see the Medium and Hackernoon articles:

      :ul[
          :li[:a(https://medium.com/@anywhichway/the-joy-of-secst-markup-333d16263225?sk=2f2c7ea14e3086e093b00ca7bec5252d)[Medium:The Joy of SECST Markup]]
          :li[:a(https://hackernoon.com/the-joy-of-using-secst-markup)[Hackernoon:The Joy of Using SECST Markup]]
      ]

]
:section[
    :h[Why Another Language]

   :dl[
        :dt[Semantic Tag]
        :dd[A tag that has meaning beyond that used to facilitate document layout and style.]
        :dd[:code[<b>] for bold is not semantic, but :code[<strong>] for indicating importance and rendering as bold is semantic.]
    ]

    :section[
        :h[HTML and Markdown Issues]

        :ul[
           :li[HTML is verbose as a result of the need for matching open and close named tags.]
           :li[Markdown's goal is simplicity, for which it sacrifices a lot of expressive power. This sacrifice is the result of a non-uniform, non-semantic syntax used to support its simplicity.]
           :li[Ensuring that HTML documents are meaningful for textual analysis or supportive of assistive software is hard.]
           :li[Markdown is not semantic, you must learn the different annotations to format your document and the resulting HTML is not necessarily meaningful to textual analysis or assistive software. When Markdown initially had a very limited number of ways to format documents, its syntax was a benefit, for some it is now becoming a burden.]
           :li[Some Markdown syntax is invisible, e.g. spaces for line breaks]
           :li[The built-in self creating document navigation capabilities of HTML and Markdown are limited.]
           :li[Using HTML's computational capabilities, i.e. JavaScript, requires programming skills.]
           :li[Markdown provides no intrinsic computational capability. You must use something like Jupyter Notebook to support computation.]
           :li[Markdown provides no way to bundle assets for delivery. HTML supports asset bundling through the use of data URLs, but it is rather obscure and not automatic.]
           :li[Both HTML and Markdown are require specialized programming skills to extend.]
           :li[HTML's legacy allows the creation of documents with security flaws.]
        ]
   ]

    :section[
        :h[SECST Benefits]

        :ul[
            :li[A consistent, uniform compact representation. Where SECST can become verbose, it has shorthand notation similar to Markdown.]
            :li[Semantic tags, which make it easier to learn and remember than Markdown and encourages more meaningful documents than straight HTML. Many tags are named after their semantic HTML counterparts to make SECST easier to learn, maintain, and extend.]
            :li[Enforcement of required attributes and valid values for proper behavior or accessibility reasons.]
            :li[Extensive, built-in :a(.#tables-of-contents)[document navigation] for tables of contents and footnotes (more than Markdown).]
            :li[Using SECST's built-in :a({href:"#values-and-formulas"})[computational capability] for math and text expressions is no harder than writing Excel formulas.]
            :li[A predominantly declarative extension technique make SECTS easier to extend than HTML. Expertise in programming custom elements is not required.]
            :li[Asset bundling via the use of the attribute :code[static] on :code[audio], :code[img], :code[video] and :code[value] tags.]
            :li[SECST is easier to extend than Markdown. Expertise in parsers or regular expressions is not required.]
            :li[SECST is translated to safe HTML constructs to limit the chance of XSS and other attacks.]
        ]
    ]
]
:section[
    :h[When To Use SECST]

    If you only need basic Markdown, then SECST is probably not for you.

    If you need extended Markdown, then SECST may be a good option.

    If you need more than Markdown, then SECST may be your only option short of HTML, CSS, and JavaScript so long as you are focused on interactive document authoring. If you are building an application use Svelte, VUE, React or some other framework.

    :section[
        :h[Basic Markdown]

        See :a[https://www.markdownguide.org/basic-syntax/].

        :table[
            :thead[
                Document Item | Markdown | SECST | Example
            ]
            :tr[
                headings | :td[:code[h# heading text]] :td[:code[h#[heading text]]] :td[]
            ]
            :tr[
                bold | :td[:code[**text**]] :td[:code[:strong[text]]] :td[:strong[text]]
            ]
            :tr[
                italics | :td[:code[*text*]] :td[:code[:em[text]]] :td[:em[text]]
            ]
            :tr[
                blockquote | :td[:code[> quote text]] :td[:code[:blockquote[quote text]]] :td[]
            ]
            :tr[
                nested blockquote | :td[:code[> quote text] :kbd[Enter] :code[>> nested quote text]] :td[:code[:blockquote[quote text :blockquote[nested quote text]]]] :td[]
            ]
            :tr[
                code | :td[:code['the code']]  :td[:code[:code[the code]]] :td[:code[the code]]
            ]
            :tr[
                horizontal rule | :td[:code[***]] :td[:code[:hr]] :td[]
            ]
            :tr[
                newline | a new line | :td[a new line or :code[:br]] :td[]
            ]
            :tr[
                paragraph | two new lines, i.e. an empty line | :td[two new lines or :code[:p[<content>...]]]  :td[]
            ]
        ]

       Where SECST can become un-necessarily verbose, e.g. lists items and table cells, it has shorthand notation similar to Markdown.

        Also see :a({href:"#lists"})[Lists], :a({href:"#code"})[Code], :a({href:"#links"})[Links], :a({href:"#images"})[Images].
    ]
    :section[
        :h[Extended Markdown]

        See :a[https://www.markdownguide.org/extended-syntax/].

        :table[
            :thead[
                Document Item | Markdown | SECST | Example
            ]
            :tr[
                :td[:a(.#footnotes)[footnotes]] :td[:code[[number]]] :td[:code[:footnote[number] or :footnote[] for automatic numbering]] :td[]
            ]
            :tr[
                heading ids | :td[:code[# My Heading {#my-id}]] :td[:code[:h1(#my-id)[My Heading]]] :td[]
            ]
             :tr[
                 definition lists | :td[see :a({href:"#definition-lists"})[definition lists]] :td[] :td[]
             ]
             :tr[
                 strikethrough | :td[:code[~~struck out~~]] :td[:code[:strike[struck out]]] :td[:strike[struck out]]
             ]
             :tr[
                 task lists | :td[see :a({href:"#task-lists"})[task lists]] :td[] :td[]
             ]
             :tr[
                 emojis | :td[:code[:]:code[emoji-name:]] :td[:code[:emoji[emoji-name]]] :td[:emoji[smile]]
             ]
             :tr[
                  highlight | :td[:code[==marked words==]] :td[:code[:mark[marked words]]] :td[:mark[marked words]]
              ]
              :tr[
                  subscript | :td[:code[H~2~O]] :td[:code[H:sub[2]0]] :td[H:sub[2]0]
              ]
              :tr[
                  superscript | :td[:code[X^2^]] :td[:code[X:sup[2]]] :td[X:sup[2]]
              ]
              :tr[
                    automatic URL linking | :td[:code[https://my.url.com]] :td[:code[:a[https://my.url.com]]]
                ]
        ]
    ]
    :section[
        :h[Markdown Gaps]
        :ul[
            :li[:a({href:"#hashtags"})[hashtags]]
            :li[:a({href:"#audio"})[audio]]
            :li[:a({href:"#video"})[video]]
            :li[:ins[inserted] and :del[deleted], see :a({href:"inserted-and-deleted"})[inserted and deleted]]
            :li[:a({href:"#tables-of-contents"})[tables of contents]]
            :li[:a({href:"#social-media-and-other-mentions"})[social media and other mentions]]
        ]
    ]
]
:section[
    :h[Authoring]

    Authors write documents using semantic tags with the format :code[<tag>[<content>]] or :code[<tag>(<parameters>)[<content>]].

    For example:
    :ul[
        :li[:code[:strong[bolded important content]] produces :strong[bolded important content]]
        :li[:code[:em[italicized emphasized content]] produces :em[italicized emphasized content]]
    ]


    :section[
        :h[Markup Tags With HTML Names]

        Almost every semantic tag supported by HTML5 is one of the :a({href:"#tags"})[tags supported by SECST]. Most non-semantic tags are excluded from SECST, e.g. :code[<div>] and :code[<span>]. Exceptions are :code[<hr>], :code[<table>], :code[<ol>], :code[<ul>] and :code[<style>]; however, since they are so commonly used :code[:b[]] for :b[bold] and :code[:i[]] for :i[italic] are included, as are aliases :code[:bold[]] and :code[:italic[]].

        A few tags of note are documented below to illustrate the use of SECST or highlight special features.

        :section[
            :h[Tags of Note]
            :section[
                :h[Paragraph]

                SECST will treat two newlines in sequence as a paragraph break. There is also a paragraph tag :code[:p[]]. Newlines inside a :code[p] tag will produce separate HTML :code[<br>] elements. A :code[:p[]] tag is not strictly necessary for really simple content. A SECST parser/transpiler will do a reasonable job of figuring out where paragraph breaks are required; however behavior may vary from implementation to implementation.

                If you get an unexpected document layout, try putting content inside a :code[:p[]] tag.
            ]
            :section[
                :h[Code]

                Contents wrapped in :code[:code[]] will be inline unless the contents contain a newline, in which case the code will be displayed in block format.

                If you have a single line you wish to show as a block, end it with a newline.
            ]
            :section[
                :h[Headings]

                Headings automatically get ids you can use for the destination of links. The id is the same as the text content in lower case with spaces and special characters replaced by dashes. To make it more convenient, you can also provide your own ids, e.g. :code[:h1(#myheading)[My Heading]] with link :code[:a({href:"#myheading"})[Go To My Heading]].

                :em[Note: All tags can optionally take an id starting with :code[#]. If an id is present, it must be the first parameter.]
            ]
            :section[
                :h[Hyperlinks]

                Sometimes, e.g. with hyperlinks, you need to specify more than content. This is provided using an attribute block in parentheses, e.g.

                :code[:a({href:"./index.txt",target:"_tab"})[SECST]] yields :a({href:"./index.txt",target:"_tab"})[SECST] , where you can access the source of this document.

                For convenience, any tag that can accept a url (either :code[src] or :code[href] in HTML) does not require the attribute, just provide the URL. And, single quotes can be used for attribute values, e.g.

                :code[:a(./index.txt {target:'_tab'})[SECST]] yields :a(./index.txt {target:'_tab'})[SECST].

                You can also use document ids prefixed with a period as targets, e.g. :code[.#headings] for this section. The period prefix is necessary to distinguish from providing an id for the link itself.

                You can also specify hyperlinks with just a content value, e.g. :code[:a({target:"_tab"})[./index.txt]] renders as :a({target:"_tab"})[./index.txt].

                By default, relative URLS and ids, i.e. those starting with a period, have a target that is the current window or tab. And, those starting with a protocol, e.g. :code[https://] have a target of a new tab.

                All tags that take more than just content are fully documented in the :a({href:"#tags"})[complete list of tags].

            ]
            :section[
                :h[Lists]
                :section[
                    :h[Unordered And Ordered Lists]

                    Both ordered and unordered lists are supported using :code[ol] and :code[ul] tags with :code[li] tags.

                    :code[:ul[:li[LinkedIn] :li[Facebook] :li[Twitter]]]

                    :ul[
                        :li[LinkedIn]
                        :li[Facebook]
                        :li[Twitter]
                        ]

                    There is also a shorthand:

                    :code[
                        :ul[
                            - LinkedIn
                            - Facebook
                            - Twitter
                        ]
                    ]

                    The same shorthand also works for :code[ol]. Since SECST already knows the list is ordered, numbers are inserted. The main utility of using numbers, is to reset numbering:

                    :code[
                        :ol[
                            2. LinkedIn
                            - Facebook
                            - Twitter
                        ]
                    ]

                    :ol[
                        2. LinkedIn
                        - Facebook
                        - Twitter
                    ]

                    The standard HTML attributes for configuring ordered lists, :code[reversed], :code[start], :code[type] are :a(https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol)[also supported].

                     :code[
                        :ol({type:"a"})[
                            - LinkedIn
                            - Facebook
                            - Twitter
                        ]
                    ]

                    :ol({type:"a"})[
                        - LinkedIn
                        - Facebook
                        - Twitter
                    ]

                ]
                :section[
                    :h[Description Lists]

                    Definition lists follow the :a(https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dl)[MDN documentation] for their parallel HTML tags except non-semantic tags can't be used.

                    :code[
                        :dl[
                            :dt[Beast of Bodmin]
                            :dd[A large feline inhabiting Bodmin Moor.]

                            :dt[Morgawr]
                            :dd[A sea serpent.]

                            :dt[Owlman]
                            :dd[A giant owl-like creature.]
                        ]
                    ]

                    :dl[
                        :dt[Beast of Bodmin]
                        :dd[A large feline inhabiting Bodmin Moor.]

                        :dt[Morgawr]
                        :dd[A sea serpent.]

                        :dt[Owlman]
                        :dd[A giant owl-like creature.]
                    ]
                ]
                :section[
                    :h[Task Lists]
                ]
            ]
            :section[
                :h[Meta Tags]

                The :code[:meta[]] tag inserts values into the head of a document. It takes the form :code[:meta({name:"my-name"})[my value]], e.g. :code[:meta({name:"author"})[John Jones]] injects the HTML :transpiled[:meta({name:"author"})[John Jones]].

                Suggested meta tag names include:

                :ul({title:"suggested meta tag names"})[
                    - :em[keywords], comma separated key words, but consider using :a({href:"#hashtags"})[Hashtags]
                    - :em[description] for a description of the document
                    - :em[publisher] for the publisher of the document
                    - :em[author] for the author of the document
                    - :em[copyright] for a copyright notice of the document
                    - :em[robots] with the comma separated values of :code[index] or :code[noindex] and :code[follow] or :code[nofollow].
                ]

                Also see the section :a(.#structured-data)[Structured Data] under :a(.#seo)[SEO].
            ]
            :section[
                :h[Multimedia]
                :section[
                    :h[Images]

                    Since SECST has a focus on semantics, images must be specified with alt text braces, even if they contain no content.

                    :code[:img(static ./anywhichway_mobius_138.png)] will not transpile.

                    Images can be loaded statically at transpile time and delivered bundled into the output HTML.
                    :ul[
                        :li[The tag :code[:img(static ./anywhichway_mobius_138.png)[Test]]]
                        :li[renders as :img(static ./anywhichway_mobius_138.png)[Test]]
                        :li[instead of a reference there is a data url in :code[src] attribute of the :details[:summary[generated HTML.]  :transpiled[:img(static ./anywhichway_mobius_138.png)[Test]]]]
                    ]
                    If static loading fails, the standard link will stay in place and dynamic loading may still render the image.

                    The callout for the generated HTML above is implemented using the :a({href:"#details"})[details tag].
                ]
                :section[
                    :h[Audio]
                ]
                :section[
                    :h[Video]
                ]
            ]
        ]
        :section[
            :h[HTML Entities and Special Characters]

            :a(https://www.toptal.com/designers/htmlarrows/)[Toptal] has a great reference for this.

            Special characters and symbols can be displayed using either :code[&<character-id>]:code[;] or :code[:&[<character-id>...]]. The second form can display multiple characters or symbols, just leave off the leading "&" and trailing ";" in the listed items.

            The code :code[&#x263B;] renders as &#x263B; .

            The code :code[:&[female male #9893]] renders as :&[female male #9893].
        ]
        :section[
            :h[Other Tags]

            :section[
                :h[Inserted And Deleted]

                :code[:ins[inserted]] renders as :ins[inserted]

                :code[:del[deleted]] renders as :del[deleted]
            ]
            :section[
                :h[Details]

                The :code[:details[]] tag supports the conditional display of content. It has one required sub-tag :code[:summary[<short text>]] and the rest of the content is conditionally displayed. If a :code[summary] tag is missing, then the first word is used as the summary.

                :code[:details[:summary[See the content...] Hello there!]] renders as :details[:summary[See the content...] Hello there!]

                :code[:details[... Hello there!]] renders as :details[... Hello there!]
            ]
        ]
    ]
    :section[
        :h[Markup Without HTML Names]
        :section[
            :h[Hashtags]

            Hashtags can take the form :code[#<value>[]] or :code[#[<value>...]], or :code[:hashtag[<value>...]] where :code[<value>...] is a space separated list of words to hashtag. The short form :strong[MUST] be followed by :code[[]] to distinguish them from ids used to identify :code[:value[]] and other tags in the document.

            :code[#sects-is-great[]] renders as #sects-is-great[].

            :code[#[sects-is-great javascript-is-great]] renders as #[sects-is-great javascript-is-great].

            :code[:hashtag[sects-is-great javascript-is-great]] renders as :hashtag[sects-is-great javascript-is-great].

            Hashtags are automatically added to the meta tags in the head of a document for indexing and SEO purposes.
        ]
        :section[
            :h[Emojis]

            Emojis can take the form :code[:emoji[<emoji-name>...]], where :code[<emoji-name>...] is a space separated list of emoji names.

            There are over 1,800 emojis. If a match for the emoji name can't be found, it is displayed as plain text.

            See :a(https://missiveapp.com/open/emoji-mart)[EmjoiMart] to find just the right emoji.

            :code[:emoji[smile]] renders as :smile:.

            :code[:emoji[smile frowning jumping]] renders as :emoji[smile frowning jumping].

            Most emoji's are colored. You can use grayscale emojis by providing the attribute :code[grayscale].

            :code[:emoji(grayscale)[smile]] renders as :emoji(grayscale)[smile].

            :code[:emoji(grayscale)[smile frowning jumping]] renders as :emoji(grayscale)[smile frowning jumping].
        ]
        :section[
            :h(#tables-of-contents)[Tables of Contents]

            Tables of contents are placed at the location of the tag :code[toc[<title>]]. The :code[:toc[]] tag should occur at the same level, i.e. outside of, :code[:section[]].

            If you do not provide a title, "Table of Contents" is used. All content after the tag is analyzed for sections with a heading as the first child tag. These are used for the table.

            Levels are determined based on section nesting. You can use the heading tag :code[:h[]] that has no number and the headings will be automatically sized.

            Only the first occurrence of :code[toc] is used, others are ignored.

            After transpilation, each heading in the document will have an :&[#9783] icon preceding it. When this icon is clicked, the table of contents will hover over the heading until something in the document is clicked.

            Each heading will have up and down arrows placed after it to facilitate jumping to the previous and next section.

            The tag can take the boolean attribute :code[toggle] to collapse the table of contents into an :a(https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details)[HTML details element].
        ]
        :section[
            :h[Footnotes/Endnotes]

            Footnotes are inserted with the :code[:footnote[<footnote-text>]] tag. The content between the braces is used as the footnote and placed at the end of the document. Auto-numbering is done so that authors do not have to keep updating footnotes as the document changes. :footnote(#markdown-footnote)[Markdown does not autonumber footnotes, which makes them hard to maintain. Although, it does create back-references.]

            The source for the above footnote is:

            :code[
                :footnote(#markdown-footnote)[Markdown does not autonumber footnotes, which makes them hard to maintain. Although, it does create back-references.]
            ].

            To re-use a footnote, give the first occurrence an id. Then, give the subsequent uses an :code[href] attribute or shorthand relative URL reference using the id and leave the content section between the braces empty. This will override the anchors for numbered links generated by the SECST transpiler, but not break ordering. :footnote(.#markdown-footnote)[] If you do provide content when reusing, it will be ignored.

            The source for the second footnote is: :code[:footnote(.#markdown-footnote)].

            And, here is a third, :code[:footnote[Footnote management does not get much easier!]], that just gets numbered. :footnote[Footnote management does not get much easier!]
        ]
        :section[
            :h[forEach]

            The :code[:forEach[]] tag supports repeating content based on an iterable object, typically an array or the results of using a CSS selector to gather values from a document.

            The variables :code[item], :code[index], :code[iterable] are available to string template literals in the content.

            :code[
                :ol[
                    :forEach({iterable:["a","b","c"]})[
                        :li[${item} at ${index}]
                    ]
                ]
            ]

            renders as

            :ol[
                :forEach({iterable:["a","b","c"]})[:li[${item} at ${index}]]
            ]

            :code[
                :value(#a hidden literal)[a] :value(#b hidden literal)[b] :value(#c hidden litral)[c]
                :ol[
                    :forEach({iterable:"#a, #b, #c"})[
                        :li[${item} at ${index}]
                    ]
                ]
            ]

            renders as :value(#a hidden literal)[a] :value(#b hidden literal)[b] :value(#c hidden literal)[c]
            :ol[
                :forEach({iterable:"#a, #b, #c"})[:li[${item} at ${index}]]
            ]

        ]
        :macro({name:"profile",tag:"p",contentAllowed:true})[
            :em[${name}]
            :em[${age}]
         ]
        :section[
            :h[Macros]

            New tags can be defined in the content of any document through the use of macros.

            A macro takes a :code[name] attribute, which will be the new tag name and a :code[tag] attribute which will be the top level tag to use for replacing the macro tag.

            The content of the macro is used as a substitute for the macro. The content can contain variables that are populated based on the attributes provided to the new tag when it is used.

            An optional attribute :code[contentAllowed] specifies if instances of the macro tag can provide content to append at the end.

            :code[
                :macro({name:"profile",tag:"p",contentAllowed:true})[
                    :em[${name}]
                    :em[${age}]
                ]
                :profile({name:"joe",age:26})[Hi there ${name}!]
            ]

            renders as

            :profile({name:"joe",age:26})[Hi there ${name}!]
        ]
        :section[
            :h[Questions]

            Questions can be used to create a quiz like experience.

            :code[
                :question({type:"number",showanswer:true})[
                    :text[What is the :code[sqrt(4)]?]
                    :answer[sqrt(4)]
                ]
            ]

            renders as:

            :question({type:"number",showanswer:true})[
                :text[What is the :code[sqrt(4)]?]
                :answer[sqrt(4)]
            ]

            Clicking on the &check; or leaving the input field tests the answer. If incorrect, the question will be surrounded with a red box based on the style :code[secst-error].

            Questions take most of the same type attribute values as :a(https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input)[input]. They can also take the attribute :code[showanswer:true] if the correct answer is supposed to be shown after a user provides the wrong response.

            Questions have two required content tags, :code[:text[<question-body>]] and :code[:answer[<some-formula>]], that must be provided in order. The :code[<question-body>] can be just about any content that would normally go in a paragraph. Formulas are :a(.#values-and-formulas)[documented later]. The :code[:answer[]] tag can also take the attribute :code[literal], e.g. :code[:answer(literal)[2]].
        ]
        :section[
            :h[Theme]

            If a :code[:theme[]] tag with a URL or :code[:link]] content exists in document, then the default theme of :a(./assets/themes/secst.css {target:"_tab"})[sects.css] is not applied.

            The theme :code[:theme(./assets/themes/html.css)[]] provides only those styles dependent on the :code[.secst] class. Default browser HTML styling is used elsewhere. It is kind of ugly!

            The content of a theme can be any tag, except most have a special interpretation. The tag content is the CSS to apply to the tag, e.g. :code[:theme[:p[border:solid black 1px;]]] will put a black border around all paragraphs.

            The theme content tags permitted but not treated in a special way are :code[:link[]], :code[:style[]], and :code[:class[]] (which is unique to :code[:theme[]]).

            The tag :code[:class[]] takes a name attribute (without a prefixed period) and the content is the CSS, e.g. :code[:theme[:class({name:"italic-paragraph"})[font-style:italic;]]] creates a class :code[.italic-paragraph].

            Theme tags can exist as :a(.#body-content)[Body Content] or :a(.#block-content)[Block Content]. They should be placed as close to the top of the document as possible.

            Themes can be scoped using the scope attribute, the value of which is a CSS selector string, e.g. :code[:theme({scope:#special-section})[:p[border:solid black 1px;]]] will only put borders around paragraphs that are inside a :code[:section(#special-section)[]].


            :code[
                :theme[:class({name:"italic-paragraph"})[font-style:italic;]]
                :theme({scope:"#special-section"})[:p[border:solid black 1px;]]
                :section[
                   :p(.italic-paragraph)[Italic paragraph with no border.]
                ]
                :section(#special-section)[
                   :p[Paragraph with border.]
                ]
            ]

            renders as

            :theme[:class({name:"italic-paragraph"})[font-style:italic;]]
            :theme({scope:"#special-section"})[:p[border:solid black 1px;]]
            :section[
               :p(.italic-paragraph)[Italic paragraph with no border]
            ]
            :section(#special-section)[
               :p[Paragraph with border]
            ]
        ]
        :section[
            :h[Transpiled SECST]

            The HTML of transpiled SECST can be displayed as plain text, e.g. :code[:transpiled[:strong[test]]] renders as :transpiled[:strong[test]].

            If you desire the transpiled code to be displayed as a block, end the content between the :code[[]] with a newline.
        ]
    ]
    :section[
        :h(#mentions)[Social Media And Other Mentions]

        Several platforms provide :code[@mention] capability, but only for their users, e.g. GitHub and StackExchange. SECST allows you to mention people on any platform, although they do not get notified.

        Mentions take the form :code[@<platform>[<space separated profile-names>]]. The target for mentions will always be another browser tab, unless a :code[target] attribute is provided.

        The mark-up :code[@github[anywhichway]] renders as @github[anywhichway].

        Alternatively you can name the user and provide your own content, e.g. :code[@github({user:"anywhichway"})[AnyWhichWay]] renders as @github({user:"anywhichway"})[AnyWhichWay].

        The supported platforms are:
        :ul[
            - LinkedIn
            - Facebook
            - GitHub
            - Twitter
        ]
    ]
    :section[
        :h[Latex]

        Math and chemical formulas can be displayed using the :code[:latex[]] tag.

        :code({language:"latex"})[:latex[Lift = \frac{1}{2} \rho v^2 S C_L]] renders as :latex[Lift = \frac{1}{2} \rho v^2 S C_L]

        :code({language:"latex"})[:latex[\ce{CO2 + C -> 2 CO}]] renders as :latex[\ce{CO2 + C -> 2 CO}]

        To display the generated formula as a block, end the content between the :code[[]] with a newline.
    ]
    :section[
        :h[Mermaid Chart]

        :code[:mermaidChart[]] is a block level element, it belongs outside of paragraphs. Just provide :a(https://mermaid.js.org/intro/)[Mermaid notation] as the content.

        :code[
             :mermaidChart[
                graph LR
                A --- B
                B-->C[fa:fa-ban forbidden]
                B-->D(fa:fa-spinner);
            ]
        ]

        renders as

        :mermaidChart[
            graph LR
            A --- B
            B-->C[fa:fa-ban forbidden]
            B-->D(fa:fa-spinner);
        ]

        There are lots of charts to choose from:

        :ul[
            - :a(https://mermaid.js.org/syntax/flowchart.html)[Flowchart]
            - :a(https://mermaid.js.org/syntax/sequenceDiagram.html)[Sequence Diagram]
            - :a(https://mermaid.js.org/syntax/classDiagram.html)[Class Diagram]
            - :a(https://mermaid.js.org/syntax/stateDiagram.html)[State Diagram]
            - :a(https://mermaid.js.org/syntax/entityRelationshipDiagram.html)[Entity Relationship Diagram]
            - :a(https://mermaid.js.org/syntax/userJourney.html)[User Journey]
            - :a(https://mermaid.js.org/syntax/gantt.html)[Gannt]
            - :a(https://mermaid.js.org/syntax/pie.html)[Pie Chart]
            - :a(https://mermaid.js.org/syntax/requirementDiagram.html)[Requirement Diagram]
            - :a(https://mermaid.js.org/syntax/gitgraph.html)[Gitgraph Diagram]
            - :a(https://mermaid.js.org/syntax/c4c.html)[C4C Diagram]
            - :a(https://mermaid.js.org/syntax/mindmap.html)[Mindmaps]
        ]
    ]
    :section[
        :h(#sheet-music)[Sheet Music (ABC Notation)]

        :code[:sheetMusic[]] is a block level element, it belongs outside of paragraphs. Just provide :a(https://abcnotation.com/examples)[ABC notation] as the content.

        :code[
            :sheetMusic[
                X: 1
                T: Nokia Tune
                M: 3/4
                L: 1/8
                K: Amaj
                |: e'd' f2 g2 | c'b d2 e2 | ba c2 e2 | a6 |
            ]
        ]

        renders as

        :sheetMusic[
            X: 1
            T: Nokia Tune
            M: 3/4
            L: 1/8
            K: Amaj
            |: e'd' f2 g2 | c'b d2 e2 | ba c2 e2 | a6 |
        ]

        If you drop the :code[:] from the second position in the last line, the play button will disappear.

         :code[
            :sheetMusic[
                X:1
                T: Paper
                K:D
                | DDAA | BBA2|
            ]
        ]

        renders as

        :sheetMusic[
           X:1
           T: Paper
           K:D
           | DDAA | BBA2|
        ]

    ]
    :section[
        :h[Escaping SECST]

        SECST tagging inside of :code[:code[]] is automatically escaped. If you wish to escape it elsewhere use the tag :code[:escape[]].

        :code[:code[:strong[text]]] renders as :code[:strong[text]].

        :code[:escape[:strong[text]]] renders as :escape[:strong[text]].
    ]
]
:section[
    :h[Values and Formulas]

    SECST supports the tag :code[:value[]] for adding reactive variables and formulas to documents.
    :ul[
         - The value of a :code[:value[]] is the content, i.e the text between the :code[[]]. It should be either a literal value or a formula, e.g. :code[:value[2 * 2]] = :value[2 * 2].
         - By default values are visible. Use the boolean attribute :code[hidden]  to hide them, e.g. :code[:value(hidden)[2 * 2]] = :value(hidden)[2 * 2].
         - So that readers know the value may be computed, computed ones are rendered as disabled, readonly input. To render them as plaintext use the attribute :code[plaintext], e.g. :code[:value(plaintext)[2 * 2]] = :value(plaintext)[2 * 2].
         - To make a value editable by readers, use the attribute :code[editable], e.g. code[:value(editable)[2 * 2]] = :value(editable)[2 * 2].
         - Everything between the :code[[]] is treated as a formula for evaluation, if your readers may be entering formula like text and you do not want it evaluated, use the attribute :code[literal], e.g. :code[:value(literal)[2 *2]] = :value(literal)[2 * 2].
         - If values are not given a :code[title] attribute, e.g. :code[:value({title:"Two times two"})[2 * 2]], then the content is used as the mouseover title unless the content is provided by a URL, in which case the URL is the title, e.g. :code[:value(./sample.json type="application/json")] has a mouseover title of :code[./sample.json].
    ]

    Formulas are actually just valid JavaScript expressions that also support standard and symbolic math. SECST attempts to process most values as math expressions first:footnote[A parser is used to match potential math expressions.] and if this fails, as JavaScript.

    :ul[
        - :code[2 * 3] = :value[2 * 2],
        - :code[2^3] when used as part of a math expression:footnote[If the expression cannot be parsed as math, then 2^3 will be treated as XOR as it is in regular JavaScript.], is the same as :code[2**3], which is the same as :code[pow(2,3)], which is the same as :code[Math.pow(2,3)] = :value[2^3].
    ]

    To use a value in another formula, the value must be named with an id, e.g.

    :code[:value(#v1)[2]] + :code[:value(#v2)[2]] = :code[:value(plaintext)[$(#v1) + $(#v2)]]

    :value(#v1)[2] + :value(#v2)[2] = :value(plaintext)[$(#v1) + $(#v2)] (try changing one of the inputs)

    The code :code[$(#<some-id>)] is used to substitute named values into formulas. See :a(.#advanced-formulas)[Advanced Formulas] for use of more general CSS selectors and arrays.

    :section[
        :h[Typed Data]

        The :code[:value[]] tag supports the type attribute, valid types include :a(https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input)[those supported by the HTML input element] except :code[button], :code[file], :code[hidden], :code[image], :code[search] and :code[submit].

        The type can also be one of the mime-types :code[text/plain], :code[text/csv], :code[application/json], e.g. :code[:value(editable {type:"application/json"})[{name:"Joe"}]] as shown below in :a(.#importing-data)[Importing Data]..

        If you provide a type, e.g. :code[:value(editable {type:"number"})], then the type will be enforced when the value is edited: :value(editable {type:"number"}).

        If the value is computed, a red border will surround type check failures.
    ]
    :section[
        :h[String Manipulation]

        Strings can be combined using the :code[+] sign, e.g. :code[:value["Hello" + " " + "world!"]] = :value["Hello" + " " + "world!"].

        Strings can be manipulated using the :a(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)[standard JavaScript methods], e.g. :code[:value["Hello world!".substring(0,5)]] = :value["Hello world!".substring(0,5)].
    ]
    :section[
        :h[Basic Math]

        Math formulas include anything that can use the normal operators, :code[,%,^,*,-,+,<,<=,==,>=,>] and any functions available via :a(https://mathjs.org/docs/reference/functions.html)[MathJS].

        Formulas can include :a(https://mathjs.org/docs/datatypes/units.html#physical-constants)[physical constants], e.g :code[:value[pi * 3^2]] = :value[pi * 3^2].

        This includes these items (most links goto MathJS documentation):

        :ul[
            - :a(https://mathjs.org/docs/reference/functions.html#algebra-functions)[Algebraic functions], covered in :a(.#algebraic-functions)[more detail] later.
            - :a(https://mathjs.org/docs/reference/functions.html#artihmetic-functions)[Arithmetic functions]
            - :a(https://mathjs.org/docs/reference/functions.html#bitwise-functions)[Bitwise functions]
            - :a(https://mathjs.org/docs/reference/functions.html#combinatorics-functions)[Combinatorics functions]
            - :a(https://mathjs.org/docs/reference/functions.html#complex-functions)[Complex functions]
            - :a(https://mathjs.org/docs/reference/functions.html#geometry-functions)[Geometry functions]
            - :a(https://mathjs.org/docs/reference/functions.html#logical-functions)[Logical functions]
            - :a(https://mathjs.org/docs/reference/functions.html#matrix-functions)[Matrix functions]
            - :a(https://mathjs.org/docs/reference/functions.html#probability-functions)[Probability functions]
            - :a(https://mathjs.org/docs/reference/functions.html#relational-functions)[Relational functions]
            - :a(https://mathjs.org/docs/reference/functions.html#set-functions)[Set functions]
            - :a(https://mathjs.org/docs/reference/functions.html#special-functions)[Special functions]
            - :a(https://mathjs.org/docs/reference/functions.html#statistics-functions)[Statistics functions]
            - :a(https://mathjs.org/docs/reference/functions.html#string-functions)[String functions]
            - :a(https://mathjs.org/docs/reference/functions.html#trigonometry-functions)[Trigonometry functions]
            - :a(https://mathjs.org/docs/reference/functions.html#unit-functions)[Unit functions]
            - :a(https://mathjs.org/docs/reference/functions.html#utils-functions)[Utility functions]
        ]
        :section[
                :h[Algebraic Functions]

                You can ...
                :dl[
                    :dt[Compute derivatives:]
                    :dd[:code[:value[derivative('x^2 + x', 'x')]]]
                    :dd[:value[derivative('x^2 + x', 'x')]]
                    :dt[Simplify equations:]
                    :dd[:code[:value[simplify('x^2 + x + 3 + x^2')]]]
                    :dd[:value[simplify('x^2 + x + 3 + x^2')]]
                    :dt[Solve equations:]
                    :dd[:code[:value[solve('Math.pow(r,2)',{r:3})}]]]
                    :dd[:value[solve('Math.pow(r,2)',{r:3})]]
                    :dt[Give your readers the opportunity to provide the values for which to solve:]
                    :dd[
                        :code[
                            Math.pow(r,2)
                            where r = :value(#v1d)[0]
                            is :value[solve('Math.pow(r,2)',{r:$(#v1d)})]
                        ]
                        renders as
                        :br :br
                        Math.pow(r,2) where r = :value(#v1d)[0] is :value[solve('Math.pow(r,2)',{r:$(#v1d)})]
                        :br
                        :code[
                            The derivative of :value(#formula literal editable)[x^2 + x]
                            for :value(#variable literal editable)[x]
                            is :value[derivative($(#formula),$(#variable))]
                        ]
                        renders as
                        :br :br
                        The derivative of :value(#formula literal editable)[x^2 + x] for :value(#variable literal editable)[x] is :value[derivative($(#formula),$(#variable))]
                    ]]
                ]
            ]
    ]
    :section[
        :h[Unit Math]

        Unit math :a(https://mathjs.org/docs/datatypes/units.html)[from MathJS] allows fro conversions using the word :code[to], as well as units for numbers, e.g. :code[45 deg].

        There are many :a(https://mathjs.org/docs/datatypes/units.html#reference)[supported units].

        Here are a few examples:

        :code[:value[2 inch to cm]] = :value[2 inch to cm]

        :code[:value[90 km/h to mi/h]] = :value[90 km/h to mi/h]

        :code[:value[90 km/h to m/h]] = :value[90 km/h to m/h] :code[m/h] is meters per hour

        :code[:value[(1 week + 1 day) to days]] = value[(1 week + 1 day) to days]

        :code[:value[cos(45 deg)]] = :value[cos(45 deg)]

        :code[:value[8.314 m^3 Pa / mol / K]] = :value[8.314 m^3 Pa / mol / K]

        Remember, if you have a value that looks like a formula, but is not, you can use the attribute :code[literal], e.g.  :code[:value(literal)[2 inch to cm]] = :value(literal)[2 inch to cm].

        And, you can use :code[plaintext] to display values without any styling, e.g. :code[:value(plaintext)[90 km/h to mi/h]] = :value(plaintext)[90 km/h to mi/h].

    ]
    :section[
        :h[Importing Data]

        Values can included imported data using the :code[src] attribute and a mime type provided in the :code[type] attribute. The trailing :code[[]] are optional.

        Values with the boolean attribute :code[static] is import data at transpile time. If importing fails, another attempt is made at runtime.

        JSON is formatted for readability and :a(https://json5.org)[JSON5] is supported to make it easier to handle remote data.

        :code[
            :value(https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_hour.geojson static {type:"application/json"})
        ]

        :value(https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_hour.geojson static {type:"application/json"})

        Imported data is readonly because it is essentially a computation. To make it editable, use the attribute :code[editable].

        :code[
            :value(https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_hour.geojson editable {type:"application/json"})
        ]

        :value(https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_hour.geojson editable {type:"application/json"})

        If an id is added, the data can be used in a computation.

        :code[
            :value(#earthquake-data https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_hour.geojson {type:"application/json"})

            :value[$(#earthquake-data).metadata]
        ]

       :value(#earthquake-data https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_hour.geojson {type:"application/json"})

       :value({type:"application/json"})[$(#earthquake-data).metadata]
    ]
]
:section[
    :h(#seo)[Search Engine Optimization]

    SECST provides several features to support :abbr[SEO] Search Engine Optimization.

    :ul({title:"SEO features"})[
        :li[:a({href:"#hastags"})[hashtags]]
        :li[:a({href:"#meta-tags"})[meta tags]]
        :li[:a({href:"#structured-data"})[structured data]]
        :li[:a({href:"#server-transpilation"})[server transpilation]]
    ]
    :section[
        :h[Structured Data]

        For select tags, SECST can generate JSON-LD directly from SECST markup. Content, layout, and SEO optimization will always be in sync.

        This is best covered by an example based on :a(https://developers.google.com/search/docs/appearance/structured-data/article)[Google's documentation for a news article].

        :em[Note: Only those tags documented for :code[NewsArticle] are currently supported.]

        :section[
            :h[News Article]

            The SECST tag :code[:NewsArticle[]] can contain regular SECST as well as tags that parallel established JSON-LD schema like :code[:headline[]], :code[:author[]] and :code[:Person[]]. Where there are identical or trivially equivalent tags and attribute names, e.g. :code[image] and :code[img], the native SECST tag is used.

            :code[
                :NewsArticle[
                    :headline[Pinky Conquers The World]
                    :img(./assets/images/The_Blue_Marble.jpg {height:50})[Earth]
                    :p[
                        Today Pinky finally conquered the world! Next up will be Mars. Watch out billionaires!
                    ]
                    :author[The Brain]
                    :datePublished({format:""})[2022-12-20T08:00:00+08:00]
                ]
            ]

            Where JSON-LD names elements accept sub-types, e.g. :code[:Person[]], those can also be used:

            :code[
                :NewsArticle[
                    :headline[Pinky Conquers The World]
                    :img(./assets/images/The_Blue_Marble.jpg {height:50,align:"left"})[Earth]
                    :p[
                       Today Pinky finally conquered the world! Next up will be Mars. Watch out billionaires!
                    ]
                    :author[
                        :Person[
                            :name[The Brain]
                        ]
                    ]
                    :datePublished({format:""})[2022-12-20T08:00:00+08:00]
                ]
            ]

            renders as

            :NewsArticle[
                :headline[Pinky Conquers The World]
                :img(./assets/images/The_Blue_Marble.jpg {height:50,align:"left"})[Earth]
               :p[
                   Today Pinky finally conquered the world! Next up will be Mars. Watch out billionaires!
               ]
                :author[
                    :Person[
                        :name[The Brain]
                    ]
                ]
                :datePublished[2022-12-20T08:00:00+08:00]
            ]

            SECST knows which tags to collect into arrays for JSON-LD and how to map similar SECST or HTML semantics to JSON-LD. It ignores tags that are not part of JSON-LD schema unless they have been mapped to JSON-LD schema internally.

            Some JSON-LD attributes mapped to tags can have attributes, e.g. :code[:heading({level:2})[]].

            Also note the use of Date formatting below based on :a(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat)[Javascript date and time format functionality].

             :code[
                :NewsArticle[
                    :headline({level:2})[Pinky and The Brain Plan For Mars]
                    :img(./assets/images/mars.jpg {height:50,align:"left"})[Mars]
                    :p[
                        We, Pinky and The Brain, will soon announce plans to partner with one of the world's billionaires to take over Mars.
                    ]
                    :Author[
                        :Person[
                            :name[Pinky]
                        ],
                        :Person[
                            :name[The Brain]
                        ]
                    ]
                    :datePublished({format:"{dateStyle:'full',timeStyle:'long'}"})[2022-12-20T08:00:00+08:00]
                ]
            ]

            will generate this JSON-LD and insert it into the head of the document:

            :code[
                <script type="application/ld+json">
                {
                  "@context": "https://schema.org",
                  "@type": "NewsArticle",
                  "headline": "Pinky and The Brain Plan For Mars",
                  "image": [
                    "./assets/images/mars.jpg"
                   ],
                  "datePublished": "2022-12-20T08:00:00+08:00",
                  "author": [{
                      "@type": "Person",
                      "name": "Pinky",
                    },{
                      "@type": "Person",
                      "name": "The Brain"
                  }]
                }
                </script>
            ]

            while rendering this content

            :NewsArticle[
                :headline({level:2})[Pinky and The Brain Plan For Mars]
                :img(./assets/images/mars.jpg {height:50,align:"left"})[Mars]
                :p[
                    We, Pinky and The Brain, will soon announce plans to partner with one of the world's billionaires to take over Mars.
                ]
                :author[
                    :Person[:name[Pinky]],
                    :Person[:name[The Brain]]
                ]
                :datePublished({format:"{dateStyle:'full',timeStyle:'long'}"})[2022-12-20T08:00:00+08:00]
            ]

            The ordering of the tags inside :code[:NewsArticle[]] is only relevant to the display layout. This generates equivalent JSON-LD:

            :code[
                :NewsArticle[
                    :headline({level:4})[Pinky and The Brain Plan For Mars]
                    :author[
                        :Person[:name[Pinky]],
                        :Person[:name[The Brain]]
                    ]
                    :datePublished({format:"{dateStyle:'full',timeStyle:'long'}"})[2022-12-20T08:00:00+08:00]
                    :img(./assets/images/mars.jpg {height:50,align:"left"})[Mars]
                    :p[
                        We, Pinky and The Brain, will soon announce plans to partner with one of the world's billionaires to take over Mars.
                    ]
                ]
            ]

            but renders with a smaller heading and the attribution at the start:

             :NewsArticle[
                :headline({level:4})[Pinky and The Brain Plan For Mars]
                :author[
                    :Person[:name[Pinky]],
                    :Person[:name[The Brain]]
                ]
                :datePublished({format:"{dateStyle:'full',timeStyle:'long'}"})[2022-12-20T08:00:00+08:00]
                :p[]
                :img(./assets/images/mars.jpg {height:50,align:"left"})[Mars]
                :p[
                    We, Pinky and The Brain, will soon announce plans to partner with one of the world's billionaires to take over Mars.
                ]
            ]
            The generated HTML also contains styles, so a style sheet can be used to modify the displayed content.

            :transpiled[
                :NewsArticle[
                    :headline[Pinky and The Brain Plan For Mars]
                    :author[
                        :Person[:name[Pinky]],
                        :Person[:name[The Brain]]
                    ]
                    :datePublished({format:"{dateStyle:'full',timeStyle:'long'}"})[2022-12-20T08:00:00+08:00]
                    :p[
                        :img(./assets/images/mars.jpg {height:50,align:"left"})[Mars]
                        We, Pinky and The Brain, will soon announce plans to partner with one of the world's billionaires to take over Mars.
                    ]
                ]
            ]
        ]
    ]
    :section[
        :h[Server Transpilation]

        The reference implementation of SECST provides both client and server rendering capability.

        Content delivered from server rendering is identical to that rendered on the client except forumlas will not be resolved and output element sizes will not be adjusted for the target device. Upon loading, a SECST runtime library makes a pass through the document to compute formulas and adjust layout.
    ]
]
:section[
    :h[Styling Content]

    The ability to use styling can be configured using API options and command line arguments. See :a(#document-programming)[document programming] below. Hence, not all styling may be available in all environments that provide a SECST editor.

    Content can be styled using CSS classes loaded using the :code[style[]] tag. This tag can be used in multiple ways. If a url is provided, i.e. :code[style(./my-css-url.css)[]], then an HTML link element of type style is created behind the scenes. If CSS is placed between the square braces, i.e. :code[style[.my-class { background-color: red}]], then a style element with CSS content is created behind the scenes. If both are provided, then two elements are generated, one of each. :code[link(<some url> type='style')[]] tag.

    CSS classes are applied to content using :code[.<class-name>] inside the parenthetical configuration part of a tag, e.g. :code[:p(.my-class1 .my-class2)[My content]].

    Style can also be applied using the :code[style] attribute of a tag. The style attribute can be in string or JSON format. If in JSON format, should use camelCase versions of CSS property names. For example, :code[:p(style='width:200px;text-align:right;float:right')[My right content]]  and :code[:p({style:{width:"200px";textAlign:"right",float:"right"}}')[My right content]] produce the same output.

    :p({style:"text-align:left;float:right"})[My right content]
    :p[
    Left Content
    More left content
    ]
]
:section[
    :h[Advanced Formulas]

    :section[
        :h[Formulas With CSS Selectors and Multiple Values]

        You can use almost any :a(https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors)[CSS selector] in place of :code[#id] in :code[$(#<id>)]. The value returned will be the contents of the value property on the element or (if not available) the value attribute or the :code[innerText].

        You can use commas to select multiple identifier named elements, e.g. :code[:value[$(#id1, #id2, #id3)]] is an array of the values associated with the elements :code[:value(#id1)[3]], :code[:value(#id2)[2]], :code[:value(#id3)[1]] and will be :code[[3,2,1]].

        A value reference :code[$(css-selector)] can end in :code[[]] to indicate multiple values should be returned as an array if the selector does not contain a comma. This allows tables to be treated somewhat like spreadsheets.
    ]

    :section[
        :h[Macro List Formatting]

         The SECST formula evaluator supports supports some utility functions.
    ]
    :section[
        :h[JavaScript]

        You can use any JavaScript that is available in :a(https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API)[WebWorkers] inside a macro. A special read-only :code[document] object is also made available. This object has title, baseURI, location, and navigator properties along with a property for each meta tag name and value.
    ]
]
:section[
:h[Configuring, Extending, And Programming]
    :section[
        :h[Basic Configuration]
        :section[
            :h[Semantic Options]
        ]
        :section[
            :h[Error Handling]

            Depending on your use case, the reference transpiler can be set to do one of the following when errors are encountered.

            :ul[
                - cancel, stop processing and throw an error when the first problem is detected
                - collect, collect all the errors and return them with line numbers (if you are writing an editor)
                - leave the text version of the error generating mark-up in place (also collects the errors)
                - try to correct the errors (collects the errors and also returns a parse tree with corrected markup)
            ]
        ]
    ]
    :section[
        :h[Tag Configuration]
    ]
    :section[
        :h[Custom Tags]
    ]
    :section[
        :h[Custom Methods]
    ]
    :section[
        :h[Event Listeners]
    ]
]
:section[
    :h(#tags)[Complete List Of Tags]

    :p[
        :value(#tagSpecs hidden {globalThis:"SECST.tagSpecs"})[]
        The documentation below is generated directly from SECST source code.
    ]

    :dl[
        :forEntries({object:"#tagSpecs"})[
            :dt[:strong[${key}] ${value.htmlDocLink}]
            :dd[:em[Transformation Impacts]:
                ${value.transform && value.attributesAllowed ? "Attributes may differ from the below since the tag is transformed and transient attributes may be deleted. See the detailed documentation for the tag." : "N/A"} :br :br
            ]
            :dd[:em[Attributes Allowed]: :code[
                ${value.attributesAllowed && typeof(value.attributesAllowed)==="object" ? JSON.stringify(value.attributesAllowed,null,2).replaceAll(/["\{\}]/g,"").replaceAll(/,\n/g,"\n").substring(1) : "N/A"}]
            ]
            :dd[:em[Content Allowed]: :code({wrap:true})[
                ${value.contentAllowed ? (typeof(value.contentAllowed)==="object" ? Object.keys(value.contentAllowed).join(", ") : "plain text") : "N/A"}]
            ]
        ]
    ]
]
:section[
:h[Design Objectives]

    :em[Note, not all design objectives are fully implemented].

    :ul[
        - Easy to understand, simple syntax, semantic
        - Easy to learn, limited syntax, semantic
        - Forgiving, auto corrects nesting, table column count, etc.
        - Easy to extend, uniform syntax, declarative language elements
        li[Secure, transpiler ensures security, also see :a({href:"#security"})[Security]]
        - Performant, simple syntax reduces parser work, although iterating over tag object definitions can take time
        - Small, simple syntax reduces parser size, although tag object definitions can take up memory
        - Configurable, declarative language elements
        - Comprehensive, all of semantic HTML and more (extensible)
        - Computational, reactivity built-in, advanced math built in, simple user input mechanisms
        - Programmable, simple event handling plus API
        - Styled, code highlighting built-in, optional CSS capability
        - Portable, HTML output with asset bundling options
        li[SEO Friendly, browser or server rendering, meta tags driven by hashtags and an explicit :code[:keywords[]] tag]
    ]
]
:section[
    :h[Grammar]

    All SECST markup takes one of two forms:

    :ul[
        :li[:code[:<tag>[<content>...]] where code:[<content>] is SECST markup ans text.]
        :li[:code[:<tag>(#<id> .<class>... <unaryattribute>... <attributes>...)[<content>...]] where  :code[<id>],  :code[<class>] space separated list,  :code[<unaryattribute>] space separated list, :code[<attributes>] JSON5 object are all optional, but must be provided in that order if present.]
    ]

    In some cases, e.g. for :code[:hr], :code[:br], and :code[:value] the trailing :code[[]] are optional.

    The reference implementation uses a :a(https://pegjs.org/)[PEG parser] and this grammar:

    :value(./src/secst.peg static {type:"text/plain"})
]
:section[
:h[Architecture]
    :section[
        :h[Semantic Architecture]
        The Mozilla Development Network, a.k.a. :abbrev[MDN], documents several HTML tag content categories:

        :ul[
            - :a(https://developer.mozilla.org/en-US/docs/Web/HTML/Content_categories#metadata_content)[:em(#metadata-content)[Metadata content]], e.g. :code[<link>], :code[<style].
            - :a(https://developer.mozilla.org/en-US/docs/Web/HTML/Content_categories#flow_content)[:em(#flow-content)[Flow content]], most elements that can go inside :code[<body>].
            - :a(https://developer.mozilla.org/en-US/docs/Web/HTML/Content_categories#sectioning_content)[:em(#sectioning-content)[Sectioning Content]], e.g. :code[<section>], :code[<article>].
            - :a(https://developer.mozilla.org/en-US/docs/Web/HTML/Content_categories#heading_content)[:em(#heading-content)[Heading Content]], e.g, :code[<h1>], :code[<h2>].
            - :a(https://developer.mozilla.org/en-US/docs/Web/HTML/Content_categories#phrasing_content)[:em(#phrasing-content)[Phrasing Content]], i.e. anything that can go in a paragraph, :code[<p>].
            - :a(https://developer.mozilla.org/en-US/docs/Web/HTML/Content_categories#embedded_content)[:em(#embedded-content)[Embedded Content]], e.g. :code[<img>], :code[audio], that cab be used in the same places as flow content.
            - :a(https://developer.mozilla.org/en-US/docs/Web/HTML/Content_categories#interactive_content)[:em(#interactive-content)[Interactive Content]], i.e. that specifically designed to support user interaction, e.g. :code[<a>], :code[<input>].
            - :a(https://developer.mozilla.org/en-US/docs/Web/HTML/Content_categories#form-associated_content)[:em(#form-associated-content)[Form Associated Content]], i.e. elements that are usually seen inside a form, but might not support interaction, e.g. :code[<progress>].
        ]

        SECST uses these content types to determine what types of tags can exists inside other tags with a few enhancements or exceptions:

        :ul[
            :li[A content category :em(#text-content)[Text Content], a sub-set of :a(.#phrasing-content)[:em[Phrasing Content]] that is typically displayed inline and does not allow a tag to be nested inside itself because it would be redundant, e.g. :code[<strong>].]
            - A content category :em(#block-content)[Block Content], includes all :a(.#flow-content)[:em[Flow Content]] that is not included in :em[Text Content], e.g. :code[<table>].
            - A content category :em(#body-content)[Body Content], enumerates tags allowed in a :code[<body>], including :code[<style>] and :code[<link>] which are usually in a :code[<head>] section, but may exist in a :code[<body>].
            - All :a(.#flow-content)[:em[Flow Content]] &nbsp; :strong[MUST] be inside of :a(.#sectioning-content)[:em[Sectioning Content]]. The tags :code[:article[]] and :code[:section[]] are both :a(.#sectioning-content)[:em[Sectioning Content]] and  :a(.#flow-content)[:em[Flow Content]], so they can be nested.
            - Common cases where browsers support elements in an alternate context that makes semantic sense, e.g. :code[:progress[]] can exist outside a :code[<form>].
            - JSON-LD tags, which are treated as :a(.#sectioning-content)[:em[Sectioning Content]].
        ]

    ]
    :section[
        :h[Data Architecture]

        The relationship between tags is represented as a cyclical JSON structure. The bulk of of the structure is static, but some of it is dynamic and memoized to address its cyclical nature.

        Tags are defined with the following parts:

        :code[
            {
                attributesAllowed: { // optional
                    <attributeName>: true || "<primitive-type>" : function, // the function can validate or transform the attribute
                    ...
                },
                contentAllowed: boolean || function || {<tagName>: Tag,...}, // optional
                transform(node) { // optional
                    // a function body that can validate and manipulate the parsed tag
                    // e.g. replace SECST attribute names with valid HTML attribute names or classes, map  :code[{language:"javascript"}] to the CSS class :code[.language-javascript] for use by HighlightJS.
                    return node;
                },
                beforeMount(node) { // optional
                    // a function body that can map SECST tag names to HTML tag names that are not valid in SECST
                    // e.g. :code[transpiled] to :code[span] or :code[div]
                    // node.tag = "div"
                    return node;
                },
                toJSONLD(node) { // optional
                    const jsonld = ...
                     // a function body to override built-in JSONLD generation
                    return jsonld;
                }
                toInnerHTML(node) { // optional
                    const html = ...
                    // a function body to generate the :code[innerHTML] for a tag in place of SECST iterating over parsed content for the tag.
                    return html;
                }
                toText(node) { // optional
                    const text = ...
                    // a function body to generate text inside a :code[<span>] as a :strong[replacement] for the tag in place of SECST iterating over parsed content for the tag.
                }
                render(el,node) { // optional
                    // a function body to manipulate the element based on the parsed node in some custom manner
                }
                mounted(el,node) { // optional
                    // called when the element is added to a DOM tree that is not yet connected to a document
                    // a function body that can make adjustments to the generated element before it is rendered, e.g. call HighlightJS with the :code[innerHTML] and replace it.
                },
                connected(el,node) { // optional
                    // a function body that is called after all nodes have been added to a document in the order they were added to the document, e.g. re-compute sizes that can't be done easily with CSS
                }
                listeners: { // optional, responds to events on all occurrences of the tag
                    <event-name>(event) {
                        // function body, event.target will be the occurrence of the HTML element generated by the Tag
                    },
                    ...
                },
                ... // any other functions are added to the element generated by the tag.
            }
        ]

        Functions taking a :code[node] as an argument are free to modify the :code[node]. Where necessary, copying to support immutability is done prior to calling the function. Functions taking a :code[node] as the first argument :strong[MUST] return a :code[node].

        Functions taking a :code[node] as a second argument should not modify the node or return a value. If they do, it will be ignored.

        Parsed instances of tags, a.k.a. :code[nodes], have a structure similar to that found in a virtual DOM:

        :code[
            {
                tag:<tag-name>,
                attributes:{<attribute-name>:<attribute-value>,...}
                classNames: Array // will be changed to use a Set,
                content: [string || <node>, ...]
            }
        ]
    ]
    :section[
        :h[Project Architecture]
    ]
]
:section[
    :h[Security]

    :em[Note, not all security features are fully implemented and tested in this release]

    :ul[
    :li[Prevents DOM clobbering by ensuring ids do not conflict with key DOM components, e.g. :code[head], :code[body].]
    - Does not allow the use of variables for attribute values.
    - Executes all macros in WebWorkers that will timeout if they take too long.
    - Does not support "on" event handlers and limits the use of styling directly on HTML elements.
    :li[Sanitizes CSS content used in the :code[:style[]] tag.]
    - Only passes attributes to HTML that are valid for the associated tag
    ]

    :value(hidden)[
        style({selector:"#id1"})[
         float:right;
        ]
        listeners({selector:"#id1"})[
         attributeChanged(event) {
          console.log(event)
         },
         disconnected(event) {
          console.log(event)
         },
         click() { alert("clicked") }
        ]
    ]
]
:section[
    :h[Copyright and License]
    Copyright 2022, AnyWhichWay LLC

    License: :a(https://www.gnu.org/licenses/agpl-3.0.en.html)[AGPL-3.0]

    Visit :a(https://github.com/anywhichway/secst)[SECST on GitHub] to participate in SECST's further development.
]