HTMLB

Documentation
Back to Home

HTMLB extends plain HTML4 with 11 tag families. They work in any browser that supports HTMLB and on any normal HTTP host. Full client-side, no server code required.

Tag Purpose Go to
<variable>store and recall text variablesdetails
<urlparam>read ?key=value from the URLdetails
<calc>math and text operationsdetails
<if>show or hide content by conditiondetails
<switch> / <case>multi-way choicedetails
<template> / <call> / <param>reusable blocks with parametersdetails
<moddoc>modify another element by iddetails
<include>include file, plain or HTML+HTMLB, scopeddetails
<rmws>remove whitespace around the tagdetails
<style for>style rules for elements, classes and idsdetails
<user>browser user agent and current date/timedetails
attribute expansionuse tags inside quoted attributesdetails

How to read this page: each tag shows syntax, then a table of attributes, then a short example you can copy. All tags are case-insensitive but variable names and urlparam keys are case-sensitive.

Attribute Expansion - Tags Inside Attributes

Any attribute (quoted or unquoted) can contain these self-closing tags. The browser replaces the tag with its text value before using the attribute.

Allowed inside attributes
<variable> <urlparam> <calc> <call> <param> <user>

Rules: attribute values are entity-decoded and trimmed first, then expanded. Tag names are case-insensitive. Works in quoted and unquoted attributes, on any tag including <variable> itself (for example to, name, default, a/b/c all expand nested tags first).

- Quoted: text mixed with multiple tags. Example: href="page.html?n=<urlparam name='n'/>-<calc a='1' b='2' op='+'/>".
- Unquoted: exactly one whole tag, no concatenation. Example: <call template=test html=<urlparam name=test>> works, but a=<urlparam name='x'/>-hi stays literal.
- Never <include>: not allowed inside attributes (it would leak files into URLs). Any other non-listed tag is copied verbatim, and a stray < stays literal.
- Short form (quoted only): if the quoted value ends inside a whitelisted tag before its >, a virtual close is assumed and the tag still runs. Example: href="<urlparam name='next'" means href="<urlparam name='next'/>". Only at end of value, only whitelisted tags, under 2048 chars.
- Missing variable: a pure <variable get> of a not-yet-set variable (and a bare tag with neither get nor set) stays literal for a later pass, so forward references work. Anything with set runs immediately (get wins if both present).
- Up to 64 tags per attribute, nesting depth 64. More than 64 are left as plain text. Substituted values, <calc> strings and attribute text results are cut at 64K.
- <call> in attributes is plain text: template markup is resolved, then tags are stripped, whitespace runs collapse to one space and trim, script/style/textarea content is dropped, leftover value tags (<urlparam> etc.) are evaluated inline, and your own <b> stays literal without rescanning (max 256 tags stripped).
- Works on any tag. Example: <a>, <img>, <variable> itself.
Examples
<a href="<urlparam name='next' default='index.html'/>">Next</a>

<img src="avatar.png" alt="<variable get='username'/>">

<variable set="who" to="<urlparam name='name' default='Guest'/>">
<p>Hello, <variable get="who"/>!</p>

<calc a="<param name='x'/>" b="10" op="+">

<!-- mixed text + two tags (quoted) -->
<a href="page.html?n=<urlparam name='n' default='1'/>-<calc a='1' b='2' op='+'/>">next</a>

<!-- unquoted: exactly one tag -->
<call template=test html=<urlparam name=test>>

<!-- short form: virtual close at end of quoted value -->
<a href="<urlparam name='next' default='index.html'">Next</a>

<!-- call becomes plain text (tags stripped, spaces collapsed) -->
<template name="hi" params="who">Hello <b><param name="who"/></b></template>
<a title="<call template='hi' who='Bob'/>">link</a>  <!-- title is: Hello Bob -->

<!-- never include, non-whitelisted stays verbatim -->
<a href="<b>hi</b>">link</a>  <!-- value keeps <b>hi</b> literally -->
Notes: non-whitelisted tags are copied verbatim, not stripped. A stray < that is not a tag stays literal. Tag names are case-insensitive, but variable names stay case-sensitive. Nested attributes expand first, so to="<urlparam ...>" stores the resolved text.

1. <variable> - Variables

Store text once and reuse it. Variables live only for the current page load and are case-sensitive.

Set:  <variable set="name" to="value">
Set global: <variable set="name" to="value" global>
Get:  <variable get="name">
AttributeRequiredWhat it does
setfor settingname of the variable to create or overwrite
tofor settingvalue to store. Can contain expanded tags like <urlparam>
getfor gettingname to read. Prints nothing if not found. get has priority if both get and set are present.
globalnoif present, variable escapes <include> scope and persists globally. Without it, child <include> reads parent but writes are local (function-like).
Example
<variable set="title" to="Hello World">
<h1><variable get="title"/></h1>
<p>Title is: <variable get="title"/></p>

<!-- with urlparam -->
<variable set="who" to="<urlparam name='user' default='Stranger'/>">
<p>Hello, <variable get="who"/>!</p>
Notes: setting prints nothing. Getting a missing name prints nothing. Names are case-sensitive: who and Who are different. Scope: <include type="text/html"> creates a new variable scope, child can get parent vars, but set without global is discarded on return. Use global to modify parent/global.

2. <urlparam> - Read the URL Query String

Reads ?key=value&other=123 from the page URL, or from the enclosing <include src="page.html?key=value"> query string. Handles percent-encoding and plus signs. Inside <include> without ?key it falls back to parent <include> and then main page URL.

<urlparam name="key" default="fallback" valid="a?b?c" separator="?">
AttributeRequiredWhat it does
nameyesquery key to look up (decoded before comparison, so %20 matches space)
defaultnovalue if key is missing, or if valid rejects it. If omitted, empty.
validnoallow-list. Split by separator. If value not in list and not empty, use default.
separatornostring that separates valid entries. Default is ?. Trimmed spaces, exact case match.
Examples
<!-- page.html?name=Alice  ->  Alice -->
<p>Hi <urlparam name="name" default="Guest"/></p>

<!-- allow only these pages -->
<urlparam name="page" default="home" valid="home?about?contact" separator="?">

<!-- custom separator -->
<urlparam name="color" default="blue" valid="red,green,blue" separator=",">

 <!-- inside an attribute -->
 <a href="<urlparam name='next' default='index.html'/>">Next page</a>

 <!-- with include: page.html includes header -->
 <include src="header.html?name=Bob" type="text/html">
 <!-- inside header.html: Hi <urlparam name="name" default="Guest"/> → Hi Bob -->
 <!-- header without ?name falls back to page.html?name -->
Decoding: + becomes space, %20 becomes space, %3D becomes =. Empty value (?key=&) is returned as empty and is not rejected by valid. Inside <include> the query is taken from src="?key=value" first.

3. <calc> - Math and String Operations

One tag for both numbers and text. Use op to choose the operation. Prints 0 on bad input, divide-by-zero, or invalid operator.

<calc a="10" b="3" op="+" >   <!-- math -->
<calc a="hello" op="upper">   <!-- text -->
<calc a="hello world" b="6" c="5" op="substr">
<calc a="a-b-c" b="-" c="_" op="replace">
AttributeWhat it does
oprequired. One of: + - * / % ^ (math) or len upper lower substr replace match search rand (text, case-insensitive, no aliases)
afirst value. Required for all ops. For len/upper/lower it is the string. For substr, replace, match and search it is the source text.
bsecond value. Required for math ops. For substr it is start index (characters, 0-based, negative counts from end). For replace, match and search it is the search pattern (plain words match literally, see regex note below).
cfor substr: length in characters (omit to go to end). For replace: replacement text, used literally (omit to delete matches). For search: 1-based number of the match to return (omit for the first match).
opExampleResult
+<calc a="10" b="3" op="+">13
-<calc a="10" b="3" op="-">7
*<calc a="10" b="3" op="*">30
/<calc a="10" b="4" op="/">2.5 (always decimal)
%<calc a="10" b="3" op="%">1 (fmod for decimals)
^<calc a="2" b="8" op="^">256 (pow)
len<calc a="hello" op="len">5 (UTF-8 characters, not bytes)
upper<calc a="hello" op="upper">HELLO
lower<calc a="HELLO" op="lower">hello
substr<calc a="hello world" b="6" c="5" op="substr">world
replace<calc a="a-b-c" b="-" c="_" op="replace">a_b_c (all occurrences)
match<calc a="hello123" b="\d+" op="match">1 if the pattern matches, else 0
search<calc a="a1b22c333" b="\d+" c="2" op="search">22 (2nd match, empty if none)
rand<calc a="1" b="100" op="rand">random integer 1-100 (inclusive)
More replace examples
<calc a="hello 123 world 456" b="\d+" c="#" op="replace">  <!-- hello # world # -->
<calc a="a1b22c" b="[0-9]+" op="replace">  <!-- abc (no c = delete) -->
<calc a="a.b.c" b="\." c="-" op="replace">  <!-- a-b-c (escape the dot) -->
<calc a="foo boo" b="o+" c="0" op="replace">  <!-- f0 b0 -->
How it works: a is the source, b the search pattern, c the literal replacement. Case-sensitive, byte-oriented, all occurrences replaced. Plain words match literally; these are regex and must be escaped with backslash to match literally: . ^ $ * + ? [ ] plus \d \w \s classes and [a-z] ranges. Empty or invalid b returns a unchanged.
Entities: attribute values are decoded twice (page load, then attribute), so to replace with the 4 characters &lt; write c="&amp;amp;lt;" in source. A replacement of only spaces is trimmed to empty (use a visible character instead).
Match and search (op="match" / op="search")
<calc a="hello123" b="\d+" op="match">          <!-- 1 -->
<calc a="hello" b="\d+" op="match">             <!-- 0 (no match) -->
<calc a="a1b22c333" b="\d+" op="search">       <!-- 1 (first match) -->
<calc a="a1b22c333" b="\d+" c="2" op="search">  <!-- 22 (2nd match) -->
<calc a="a1b22c333" b="\d+" c="9" op="search">  <!-- empty (only 3 matches) -->
Same patterns as replace (case-sensitive, byte-oriented substring search, same regex syntax). match prints 1 when the pattern is found, else 0. search prints the matched text itself, or empty when nothing is found. c picks which match to return, 1-based (omit for the first). Bad c (non-numeric, below 1, past the last match), missing a, or empty/invalid b prints empty (match prints 0 instead).
Random numbers (op="rand")
<calc a="1" b="6" op="rand">    <!-- dice -->
<calc a="100" b="1" op="rand">  <!-- same as 1 to 100 (auto swapped) -->
<calc b="10" op="rand">          <!-- 0 to 10 (missing a = 0) -->
<calc op="rand">                 <!-- 0 to 100 (both missing) -->
a is the lowest value, b the highest (both inclusive). Missing a means 0, missing b means 100. Empty or non-numeric bounds print 0. Range is inclusive, 64-bit values. Any page using rand is marked uncacheable.
More substr examples
<calc a="hello world" b="0" c="5" op="substr">  <!-- hello -->
<calc a="hello world" b="-5" op="substr">       <!-- world (negative start)-->
<calc a="hello world" b="6" op="substr">        <!-- world (to end) -->
<calc a="abc" b="10" op="substr">               <!-- empty (past end) -->
Numbers: integers use 64-bit, decimals use double and print with %g. Math with two integers stays integer except / and ^ which are always decimal.

4. <if> - Conditionals

Show or hide the content inside based on a value. All listed conditions must be true (AND). Use not to flip the result.

<if value="..." eq="..." gt="..." lt="..." gteq="..." lteq="..." empty not img htmlb>
  ... content shown if true ...
</if>
AttributeWhat it checks
valuerequired (unless img or htmlb is used). The value to test. Usually from a variable: value="<variable get='x'/>". If missing, hides content.
eq or equaltrue if value equals this text exactly
gttrue if value > this (numbers; falls back to decimal)
lttrue if value < this
gteqtrue if value >= this
lteqtrue if value <= this
emptypresent = true if value is empty. Combine with not to test "not empty".
notpresent = flip result of all conditions
imgpresent = true if the browser can show images (graphical mode with images on). Ignores all other checks except not. Combine with not for the text-only fallback.
htmlbpresent = always true in HTMLB browsers. Use not htmlb for fallback content shown in ordinary browsers. img and htmlb together must both be true (AND).
Examples
<!-- default = not empty -->
<if value="<variable get='name'/>">
  Hello <variable get="name"/>!
</if>

<!-- exact match -->
<if value="<urlparam name='page'/>" eq="admin">
  Secret panel
</if>

<!-- number -->
<if value="<variable get='score'/>" gteq="100">
  You win!
</if>

<!-- empty / not empty -->
<if value="<variable get='email'/>" empty>
  Please enter email
</if>
<if value="<variable get='email'/>" not empty>
  Email OK
</if>

<!-- AND: both must match -->
<if value="<variable get='role'/>" eq="editor" not empty>
  ...
</if>

<!-- text-only fallback -->
<if not img>
  .--.
 / o o \
 |  ^  |
  \_-_/
</if>
<if img>
  <img src="logo.png" alt="Logo">
</if>

<!-- fallback for browsers without HTMLB -->
<if not htmlb>
  <p>This page needs an HTMLB browser for the full version.</p>
</if>
<if htmlb>
  <p>Full HTMLB content here.</p>
</if>
If no operator is given, the test is "not empty". Multiple operators are AND. Bad numbers make numeric checks false.

5. <switch> / <case> - Multi-Way Choice

Cleaner than many <if> tags. First matching <case> is shown.

<switch value="...">
  <case if="a?b?c" separator="?"> ... </case>
  <case default> ... </case>
</switch>
TagAttributes
<switch>value - text to test (often value="<urlparam name='x'/>" or a variable)
<case>if="a?b?c" - list of values that match, split by separator (default ?, trimmed, exact case). default alone = catch-all if nothing matched before. If neither, hides content.
Examples
<switch value="<urlparam name='lang' default='en'/>">
  <case if="en">Hello!</case>
  <case if="fr">Bonjour!</case>
  <case if="es?pt" separator="?">Hola!</case>
  <case default>Hello (unknown)</case>
</switch>

<!-- with variables -->
<variable set="role" to="<urlparam name='role' default='guest'/>">
<switch value="<variable get='role'/>">
  <case if="admin">Admin panel</case>
  <case if="editor?author" separator="?">Editor tools</case>
  <case default>Guest view</case>
</switch>
Max 16 nested switches. A default shows only if no earlier case matched. The <switch> itself stays visible, so stray text inside it outside any <case> will show.

6. <template> / <call> / <param> - Reusable Blocks

Define a block once, call it many times with different values. No JavaScript.

Define: <template name="card" params="title, body"> ... </template>
Call:   <call template="card" title="Hi" body="Hello world">
Inside: <param name="title">
TagAttributes
<template>name required - template name.
params="a, b, c" optional - comma-separated param names (spaces trimmed, empty ignored). Block content is hidden until called.
<call>template required - name to call. Any other attribute is a param value: title="Hi" sets <param name="title"> to Hi. Self-closing.
<param>name required - prints the matching <call> attribute, or a <variable> with same name as fallback. Also works inside a tag attribute: <a href="<param name='url'/>">. Self-closing. A bare <param> in body position splices its value raw into the page (parsed as HTML, see below); inside attributes it stays text. Add plaintext (no value) to print the value as text even in body position: passed tags (e.g. <urlparam>) are evaluated once to fetch the value, then the result is printed literally and never re-evaluated or parsed.
Examples
<template name="card" params="title, body">
  <table border="1" cellpadding="8">
    <tr><td><b><param name="title"/></b><br><param name="body"/></td></tr>
  </table>
</template>

<call template="card" title="Hello" body="Welcome to HTMLB">
<call template="card" title="About" body="No JS needed">

<!-- param inside an attribute -->
<template name="btn" params="url, label">
  <a href="<param name='url'/>"><param name="label"/></a>
</template>
<call template="btn" url="index.html" label="Home">

<!-- nesting: template can call another -->
<template name="inner" params="x">Value: <param name="x"/></template>
<template name="outer" params="x">Outer <call template="inner" x="<param name='x'/>"></template>
 <call template="outer" x="hi">
Notes: templates are collected before page render and removed from output. Unknown template prints nothing. Up to 500 nested calls. Values over 64K are cut. A call value containing a tag (e.g. x=<calc op=rand>) is evaluated once per call: every <param> for it sees the same result. <param> outside a call prints the variable with same name.
Text by default, HTML by position
Substituted values are text by default and are never parsed as markup: direct <urlparam>, <variable get>, <calc> and <param> output print literally, and values passed through <calc> attributes stay text too. Nested HTMLB tags inside values (<urlparam>, <calc>, set <variable>) are still evaluated to their text result before splicing.
To deliberately render a value as markup, put a bare <param> in body position inside the template: its value is spliced raw into the page source and parsed as HTML. Position decides, not content. To force text even in body position, add plaintext: <param name="html" plaintext/> evaluates nested tags in the passed value once (so <urlparam> resolves), then prints that result byte-for-byte — a one-tag equivalent of the escape template below. Inside <textarea> (whose content is copied raw) the value is spliced raw so the field shows it literally.
<!-- safe default: ?test=<b>hi</b> shows literally -->
<form method="get">
  <urlparam name=test>
  <textarea name="test" value=""><urlparam name=test></textarea>
  <input type="submit"/>
</form>

<!-- opt-in: same value renders as markup -->
<template name="test" params="html">
  <param name="html"/>
  <form method="get">
    <textarea name="test" value=""><param name="html"/></textarea>
    <input type="submit"/>
  </form>
</template>
<call template=test html=<urlparam name=test>>
Security: only pass trusted or generated content to body-position params. A query value containing </textarea> would break out of the field. Escape untrusted input first with replace:
<template name="escape_html" params="html"><calc a=<param name="html"> b="<" c="&amp;amp;lt;" op="replace" /></template>
<call template="escape_html" html=<urlparam name="test"> />
<!-- ?test=<b>hi</b> prints as text: &lt;b&gt;hi&lt;/b&gt; -->

7. <moddoc> - Modify Another Element by ID

Insert or replace content inside or around any element that has an id. The <moddoc> block itself is removed.

<moddoc for="targetId" insert inside at="start"> ... </moddoc>
<moddoc for="targetId" replace> ... </moddoc>
AttributeWhat it does
forrequired. id of target element.
replace / insertchoose mode. replace deletes old inner content and puts new in its place. insert keeps old content. Default is insert. If both present, replace wins.
inside / outsideonly for insert. inside = within the target tags. outside = before/after the whole element. Default inside.
atonly for insert. at="start" = before inner content (or before element if outside). at="end" or omitted = after.
Examples
<div id="main">Original content</div>

<!-- add at end inside -->
<moddoc for="main" insert inside at="end">
  <p>Added at bottom</p>
</moddoc>

<!-- add at start inside -->
<moddoc for="main" insert inside at="start">
  <p>Added at top</p>
</moddoc>

<!-- insert outside -->
<moddoc for="main" insert outside at="start">
  <p>Before the div</p>
</moddoc>

<!-- replace everything inside -->
<moddoc for="main" replace>
  <p>Completely new content</p>
</moddoc>
If id not found, nothing happens. Multiple <moddoc> blocks run in document order. Self-closing targets (like <img id="x">) have empty inner content.

8. <include> - Include Files

Insert another file. With type="text/html" the file is parsed as HTMLB (scoped); without type it is inserted as plain text (escaped, no HTML/HTMLB).

<include src="header.html" type="text/html" />
<include src="header.html?name=Bob" type="text/html" />
<include src="notes.txt" />  <!-- plain, no type -->
<include src="https://example.com/data.html" />  <!-- plain, HTML escaped -->
AttributeWhat it does
srcrequired. Path or URL to file. Relative to current page URL (via join_urls). Supports file://, http://, https://, or plain path. May contain ?key=value query, used for <urlparam> inside the included file. Percent-decoded for local files.
typeif type="text/html" → file is parsed as HTMLB (templates, variables, conditionals). Otherwise → plain text: all < & > escaped, no HTMLB. Even text.html without type is plain.
Examples
<!-- plain vs HTMLB -->
<include src="text.txt" />  <!-- verbatim, no HTMLB -->
<include src="text.html" />  <!-- verbatim, no HTMLB even though .html -->
<include src="header.html" type="text/html" />  <!-- HTMLB expanded -->

<!-- urlparam via src query (isolated with fallback) -->
<include src="greeting.html?name=James" type="text/html" />
<!-- inside greeting.html: <urlparam name="name" default="Guest"/> → James -->
<!-- greeting without ?name falls back to parent/main ?name -->

<!-- variable scope (function-like) -->
<variable set="x" to="outer">
<include src="inner.html" type="text/html" />  <!-- inner: <variable set="x" to="inner"> -->
<variable get="x"/>  <!-- still outer (child writes don't leak) -->
<variable set="y" to="1" global>  <!-- with global → persists -->

<!-- old layout pattern (still works) -->
<include src="base.html" type="text/html" />
<moddoc for="content" replace>
  <p>Page-specific content</p>
</moddoc>
Scope: each type="text/html" include gets its own variable/urlparam scope. Child can get parent vars, but set without global is discarded on return. urlparam reads src="?query" first, then parent, then main page. Plain includes are fully escaped and support https:// (e.g. google.com) as source. Recursion up to 8 levels, 16 MB limit.

9. <rmws> - Remove Whitespace

Joins the text before and after the tag with no gap: it drops the pending space and skips the whitespace that follows it. Self-closing, no attributes. Useful inside <template> loops to glue separators without the line-break gaps left by source formatting.

<rmws>
Examples
<!-- without rmws the newline before the comma shows as a space: "0, 1" -->
<template name="loop" params="i,limit">
  <param name="i"/>
  <if value="<param name='i'/>" lt="<param name='limit'/>">
    <rmws>,
    <call template="loop" i="<calc a='<param name='i'/>' b='1' op='+'/>" limit="<param name='limit'/>">
  </if>
</template>
<call template="loop" i="0" limit="3">  <!-- 0, 1, 2 -->
Notes: the effect ends as soon as real text is printed or at a line break. No effect inside <pre>-style blocks or inside hidden content (<if> false branch).

10. <style for> - Style Rules

Define presentation rules once without CSS, they apply to the elements that follow. Unlike the block <style> tag, the self-closing form with for is processed and stored in memory.

<style for="h1" color="green" />
<style for="img" bgcolor="red" />
<style for=".note" align="center" />
<style for="#main" bgcolor="#FFFFE0" />
<style for="h1, h2, .note" color="green" />
Selector (for)Matches
h1tag name, any tag, case-insensitive (h1, img, footer, ...)
.noteone whitespace-separated class token, case-sensitive
#mainexact element id
h1, h2, .notecomma-separated list: the rule matches if any selector matches. Spaces around commas are ignored, empty entries skipped. Tags, classes and ids can be mixed.

PropertyWhat it does
colortext color: name (green) or hex (#008000). Applies to text elements and image alt text.
bgcolorbackground color. Native bgcolor attributes work only on body table tr td th (HTML standard), the rule adds it elsewhere: text and full-line background on blocks (div p footer ...).
sizefont size 1-7 on text elements, absolute (4) or relative (+1 -1). On hr it is rule thickness, on input field width in characters. Not used on textarea select option button (they use cols/rows).
facefont face name.
alignleft right center justify (plus top middle bottom on images in graphics mode).
widthonly where the element supports it: img table hr td th (percentages where supported). Ignored elsewhere, e.g. on h1.
Examples
<style for="h1" color="green" />
<h1>This heading is green</h1>
<h1 color="red">Explicit wins: this one stays red</h1>

<style for="img" width="100" />
<img src="a.png" alt="100 wide">

<style for=".note" align="center" bgcolor="yellow" />
<p class="note">Centered on yellow</p>

<style for="#main" bgcolor="#FFFFE0" />
<div id="main">Warm background</div>

<!-- unknown elements (footer header nav section ...) get a
     style scope when a rule matches, closed by </footer> -->
<style for="footer" align="center" />
<footer>Centered footer</footer>

<!-- one rule for several targets: any match applies -->
<style for="h1, h2, .note" color="green" />
<h1>Green</h1>
<h2>Green too</h2>
<p class="note">Green as well</p>
Rules: the tag must be self-closing (/>), a block <style for=...> without it hides content like plain <style>. A rule affects only elements after it. for accepts a comma-separated list (h1, h2, .a, #b); one rule then covers every listed target and counts as a single rule for last-wins resolution. When several rules match one element, the last one wins per property; an explicit attribute on the element itself always beats any rule. Rules live for the page load only. Values can contain expanded tags, e.g. color="<variable get='c'/>". Colors need a color-capable session, same as native attributes.

11. <user> - User Agent and Date/Time

Print plain-text information about the visitor's browser: the user agent string, or the current date and time in any format. Self-closing, no content. Also works inside quoted attributes.

<user what="agent" />
<user what="datetime" format="%Y-%m-%d %H:%M:%S" />
AttributeRequiredWhat it does
whatyesrequired, case-insensitive. agent prints the browser user agent: the configured fake user agent when one is set, otherwise Links (version; system). datetime prints the current local time formatted with format. Missing or any other value prints nothing.
formatnoonly for what="datetime". A strftime format string, e.g. %Y-%m-%d for the date, %H:%M for the time. Missing or empty means %Y-%m-%d %H:%M:%S. Cut at 128 characters.
Examples
<p>You are using: <user what="agent"/></p>

<p>Today is <user what="datetime" format="%Y-%m-%d"/></p>
<p>The time is <user what="datetime" format="%H:%M"/></p>
<p>Stamp: <user what="datetime"/></p>  <!-- default format -->

<!-- case does not matter -->
<p><user what="DateTime" format="%d.%m.%Y"/></p>

<!-- inside an attribute -->
<a href="log.html?agent=<user what='agent'/>">Log my visit</a>

<!-- with if: show a message in the morning -->
<if value="<user what='datetime' format='%H'/>" lt="12">
  <p>Good morning!</p>
</if>
Notes: output is plain text, never parsed as markup. Useful format codes: %Y year, %m month, %d day, %H hour, %M minute, %S second, %A weekday name. The clock is the visitor's local time and changes on every page load.

Complete Example - Putting It Together

A greeting page that uses variables, url params, calc, and templates - no JavaScript.

<template name="greeting" params="name, count">
  <p>Hello, <param name="name"/>!</p>
  <p>Name length: <calc a="<param name='name'/>" op="len"></p>
  <p>Visits: <param name="count"/></p>
</template>

<variable set="who" to="<urlparam name='user' default='Guest'/>">
<variable set="n" to="<calc a='<variable get="n"/>' b='1' op='+'>">

<call template="greeting" name="<variable get='who'/>" count="<variable get='n'/>">

<switch value="<variable get='who'/>">
  <case if="Admin"><p>Admin panel here</p></case>
  <case default><p>Guest view</p></case>
</switch>

<if value="<variable get='who'/>" not empty>
  <p>Welcome back!</p>
</if>

Limits and Notes
LimitValue
File fetch for include16 MB max, 15s timeout, 8 levels for include (4 for legacy extends)
Attribute expansion tags64 per attribute, nesting depth 64
Calc string output64 K max (cut if longer)
Template nesting500 calls deep, 64 K output per call
Switch nesting16 deep
Variable/include scope16 deep, function-like (global flag escapes)
Style rules256 max per page, values cut at 1024 chars
User datetime format128 chars max, default %Y-%m-%d %H:%M:%S when missing or empty
All tags stop on bad inputthey print 0 or nothing, never break the page

Top | Back to Home | View Source Code