Lesson 29 of 45 · Web pages

Forms

A form is how a page listens. This lesson covers every form element and every input type, the attributes that make the browser check what was typed, and how to read the values in your own code. Each idea has a live example, and the examples that react to the visitor really work in the preview.

The form and the submit

A form holds controls and knows where to send them. action is the address that receives the data and method is how it travels: get puts the data into the address and suits searches, post puts it into the body of the request and is the right choice for everything that changes something or is private. When the form has a file input, add enctype = "multipart/form-data". The other form attributes are in the reference below: autocomplete, novalidate, target, rel, accept charset and name.

A real send would leave the page, and the previews in this lesson must stay on it. So most examples here listen to the button and decide for themselves what happens. The click attribute of a submit button is a function that runs when the button is pressed. It receives the event, and dom.PreventDefault stops the default action, which is sending the form. That also skips the browser's checks, so the function asks for them: reportValidity checks every field and shows the browser's message at the first bad one, and as bool turns its answer into true or false. After that you read each field with dom.GetValue(dom.ById("id")) and use the text as you like. The event is the plain i64 parameter of the function.

On a page of your own you can give the same function to the submit attribute of the form. The browser then runs its checks first and calls your function only when they pass, so reportValidity is not needed.

web-forms-signup.salam
import dom

func Join(e: i64):
    dom.PreventDefault(e)
    if dom.Call0(dom.ById("sign"), "reportValidity") as bool:
        name := dom.GetValue(dom.ById("name"))
        mail := dom.GetValue(dom.ById("mail"))
        dom.SetText(dom.ById("out"), "Welcome, " + name + ". We will write to " + mail + ".")
    end
end

page:
    title = "Sign up"
    global: selector = "body" font family = "system-ui, sans-serif" margin = "1.5rem" max width = "24rem" end
    global: selector = "label" display = block margin = "0.8rem 0 0.2rem" end
    global: selector = "input" padding = 6 width = "100%" box sizing = "border-box" end
    form: id = "sign"
        label: for = "name" content = "Your name" end
        input: id = "name" name = "name" required = true placeholder = "Ada Lovelace" end
        label: for = "mail" content = "Email" end
        input: id = "mail" name = "mail" type = email required = true placeholder = "ada@example.com" end
        button: type = submit click = Join content = "Join" margin = "1rem 0" padding = "6px 18px" end
    end
    paragraph: id = "out" color = "#0f766e" end
end

Leave the fields empty and press Join: the browser shows its message and nothing else happens, because reportValidity answered no. Write something that is not an address in the Email field and it refuses again. Only a complete form gets past the if, which is why you can trust the values inside it, at least as far as the browser checks go.

Browser checks are for the visitor's comfort, not for security. Anyone can send a request without your page. Whatever your server receives has to be checked on the server again.

Labels

Every control needs a label. Connect them in one of two ways. Give the control an id and write the same id in for of the label, as the sign up example does: Salam checks at compile time that an element with that id exists, so a typo cannot silently break the link. Or put the control inside the label, as the pizza example further down does, and no for is needed. Either way a click on the label text focuses the control or ticks the checkbox, and a screen reader reads the label when the control gets the focus.

The placeholder attribute is not a label. It is a hint in the empty box, it vanishes when the visitor types and many people find the grey text hard to read. Use it for an example of the format, and keep the label.

Input types

input is one element with many faces. Its type decides the face, the keyboard a phone shows, and what the browser checks. Without a type it is a text box. The table lists every type that Salam names, with its Persian name, which you use in Persian files.

TypePersianWhat the visitor getsWhat the browser checks
textمتنA one line box.Only minlength, maxlength and pattern.
passwordگذرواژهA box that hides the characters.The same as text. Set autocomplete to current-password or new-password.
emailایمیلA box and a phone keyboard with the @ key.The value looks like name@host. With multiple it is a comma separated list.
numberعددA box with up and down arrows and a number keyboard.It is a number, within min and max, on the step grid.
telتلفنA box and the phone keypad.Nothing, because numbers differ so much. Add a pattern.
urlنشانیA box and a keyboard with slash and dot.A full address with a scheme, such as https://.
searchجستجوA box, often with a clear button and a search key on the keyboard.Nothing.
checkboxتیکA square that is on or off.With required it has to be ticked. Sent only when ticked.
radioرادیوییA round dot. Controls with the same name form a group of which one is chosen.With required one of the group must be chosen.
submitارسالA button that sends the form. value is its label.Runs the checks of all the other fields.
buttonدکمهA button that does nothing until you give it a click handler.Nothing.
resetبازنشانیA button that puts every field back to its starting value.Nothing.
dateتاریخA calendar picker. The value is written 2026-10-26.A real date, within min and max.
timeزمانA time picker. The value is written 14:30.A real time, within min and max.
colorرنگ ورودیA colour picker. The value is written #ff8800.Nothing. It always has a colour.
rangeبازهA slider between min and max.Nothing. It always has a value.
fileپروندهA button that opens the file chooser. accept and multiple shape it.With required a file must be chosen.
hiddenپنهانNothing. It sends a value the visitor does not see.Nothing. Do not trust the value: the visitor can change it.
textareaناحیه متنA multi line box, the same as the text area element.The same as text area.
jalali dateتاریخ شمسیA box that writes Persian digits and slashes by itself.A real Jalali date. See the section on it below.

Other HTML types, such as datetime-local, month, week and image, are not in Salam's list but you can write them as quoted strings, for example type = "month". The image type is a submit button drawn as a picture, and it uses url for the picture, alt for its text and width and height.

web-forms-types.salam
page:
    title = "Input types"
    global: selector = "body" font family = "system-ui, sans-serif" margin = "1.5rem" end
    form:
        display = grid
        grid template columns = "7rem 12rem"
        gap = "0.6rem 1rem"
        label: for = "n" content = "Number" end
        input: id = "n" type = number end
        label: for = "p" content = "Phone" end
        input: id = "p" type = tel end
        label: for = "u" content = "Web page" end
        input: id = "u" type = url end
        label: for = "q" content = "Search" end
        input: id = "q" type = search end
        label: for = "d" content = "Date" end
        input: id = "d" type = date end
        label: for = "t" content = "Time" end
        input: id = "t" type = time end
        label: for = "c" content = "Colour" end
        input: id = "c" type = color end
        label: for = "v" content = "Volume" end
        input: id = "v" type = range end
    end
end

The form above is a grid with two columns, set with display = grid and grid template columns, so that every label stands to the left of its control. gap is the space between rows and columns. Open it on a phone and each control brings its own keyboard.

Letting the browser check

Most of the checking comes from attributes on the control. required forbids an empty value. minlength and maxlength bound the number of characters, and pattern takes a regular expression that the whole value must match. For numbers and dates, min, max and step set the range and the grid of allowed values. size only changes how wide a text box looks, and does not check anything. readonly keeps a value from being edited but still sends it, while disabled also keeps it from being sent. autofocus puts the cursor in a field when the page opens, multiple allows several files or addresses, and autocomplete tells the browser what the field holds so that it can offer the saved value.

Besides the message that the browser shows, your code can ask each control about its validity. The example reads it on every keystroke with the input event. dom.GetProp(field, "validity") gives an object whose properties are true or false: valueMissing when a required field is empty, tooShort when it is shorter than minlength, patternMismatch when it does not match the pattern, and rangeUnderflow and rangeOverflow when a number is outside min and max. dom.EventTarget(e) tells the shared handler which field it was called for.

web-forms-validate.salam
import dom

func Check(e: i64):
    field := dom.EventTarget(e)
    v := dom.GetProp(field, "validity")
    mut text := "Looks good."
    if dom.GetBool(v, "valueMissing"): text = "This field is required."
    else dom.GetBool(v, "tooShort"): text = "Use at least 3 characters."
    else dom.GetBool(v, "patternMismatch"): text = "Letters only, no spaces."
    else dom.GetBool(v, "rangeUnderflow"): text = "You must be 18 or older."
    else dom.GetBool(v, "rangeOverflow"): text = "Please enter an age up to 99."
    end
    dom.SetText(dom.ById(dom.GetStr(field, "id") + "-note"), text)
end

page:
    title = "Validation"
    global: selector = "body" font family = "system-ui, sans-serif" margin = "1.5rem" end
    style: content = "input:user-invalid { border: 2px solid #dc2626 } input:user-valid { border: 2px solid #16a34a }" end
    form:
        label: for = "user" content = "Username" end
        input: id = "user" required = true minlength = 3 pattern = "[A-Za-z]+" input = Check end
        paragraph: id = "user-note" content = "At least 3 letters." end
        label: for = "age" content = "Age" end
        input: id = "age" type = number required = true min = 18 max = 99 input = Check end
        paragraph: id = "age-note" content = "From 18 to 99." end
    end
end

The two border colours come from a CSS rule written with the page wide style element: input:user-invalid and input:user-valid. These match only after the visitor has touched the field and left it, so a form that is still empty does not show red from the first second. The older :invalid matches at once and looks angry on a fresh form.

One handler can serve many fields. dom.EventTarget(e) is the control that fired the event, and dom.GetStr(field, "id") is its id, so the message for the field named user goes into the element with the id user-note.

Longer text

For text that may take several lines use text area. It has rows and cols for its starting size, maxlength and minlength for the length, and wrap: with soft long lines only wrap on the screen, with hard the line breaks are also put into the text that is sent, which needs cols. The text between the tags in HTML is the starting text, and in Salam it is content. An input with type = "textarea" is the same element written differently.

A counter that tells the visitor how many characters are left is a small kindness when you set a limit. The example reads textLength from the box, which counts characters, not bytes, so it is right for Persian text as well.

web-forms-textarea.salam
import dom

const LIMIT := 80

func Count:
    used := dom.GetNum(dom.ById("bio"), "textLength") as i64
    dom.SetText(dom.ById("left"), "" + (LIMIT - used) + " characters left")
end

page:
    title = "Long text"
    global: selector = "body" font family = "system-ui, sans-serif" margin = "1.5rem" end
    label: for = "bio" content = "About you" end
    text area:
        id = "bio"
        name = "bio"
        rows = 4
        cols = 40
        maxlength = LIMIT
        placeholder = "A few words about yourself"
        input = Count
        display = block
        margin = "0.4rem 0"
    end
    small: id = "left" content = "80 characters left" end
end

Checkboxes, radio buttons and groups

A checkbox is a yes or no. Several checkboxes with the same name are sent as a list of the ticked ones. A radio is one choice out of a set: all radio buttons that share a name belong together and the browser keeps one of them ticked. value is the text that is sent when the control is chosen, and checked chooses it from the start. To read a choice in code, use dom.GetBool(control, "checked").

Wrap a related set in a field set with a legend. A sighted visitor sees a box with a title. A screen reader announces the legend as the question when the visitor enters the group, and then reads each option, which a bare row of radio buttons cannot do. The form in the example reacts to the change event, which fires on the form for every change of any control inside it.

web-forms-choices.salam
import dom

func Show:
    mut size := "small"
    if dom.GetBool(dom.ById("s-large"), "checked"): size = "large" end
    mut extras := ""
    if dom.GetBool(dom.ById("x-cheese"), "checked"): extras = extras + " cheese" end
    if dom.GetBool(dom.ById("x-olives"), "checked"): extras = extras + " olives" end
    dom.SetText(dom.ById("out"), "Size: " + size + ". Extras:" + extras)
end

page:
    title = "Pizza order"
    global: selector = "body" font family = "system-ui, sans-serif" margin = "1.5rem" end
    global: selector = "label" display = block margin = "0.3rem 0" end
    form: change = Show
        field set:
            legend: content = "Size" end
            label: input: id = "s-small" type = radio name = "size" value = "small" checked = true end span: content = " Small" end end
            label: input: id = "s-large" type = radio name = "size" value = "large" end span: content = " Large" end end
        end
        field set:
            legend: content = "Extras" end
            label: input: id = "x-cheese" type = checkbox name = "extra" value = "cheese" end span: content = " Cheese" end end
            label: input: id = "x-olives" type = checkbox name = "extra" value = "olives" end span: content = " Olives" end end
        end
    end
    paragraph: id = "out" content = "Size: small. Extras:" end
end

A field set can also be switched off in one go: disabled on the set disables every control inside. The next example reads a checkbox and sets disabled with dom.SetBool.

web-forms-fieldset.salam
import dom

func Toggle:
    same := dom.GetBool(dom.ById("same"), "checked")
    dom.SetBool(dom.ById("ship"), "disabled", same)
end

page:
    title = "Grouped fields"
    global: selector = "body" font family = "system-ui, sans-serif" margin = "1.5rem" max width = "26rem" end
    global: selector = "fieldset input" display = block margin = "0.4rem 0" padding = 5 end
    form:
        field set:
            legend: content = "Billing address" end
            input: name = "street" placeholder = "Street" end
            input: name = "town" placeholder = "Town" end
        end
        label: input: id = "same" type = checkbox change = Toggle end span: content = " Ship to the same address" end end
        field set: id = "ship"
            legend: content = "Shipping address" end
            input: name = "street2" placeholder = "Street" end
            input: name = "town2" placeholder = "Town" end
        end
    end
end

Choosing from a list

select is a drop-down list, and option is each line of it. The visitor sees the text of the option, and the form sends its value, so give a short code that your server understands. selected picks the starting option and disabled greys one out: a first line that is disabled and selected makes a good prompt, such as Choose a city. option group puts a heading over several options and needs a label. multiple turns the list into a box where several lines can be chosen, and size says how many lines are visible.

web-forms-select.salam
import dom

func Pick:
    dom.SetText(dom.ById("out"), "You chose: " + dom.GetValue(dom.ById("city")))
end

page:
    title = "Drop-down list"
    global: selector = "body" font family = "system-ui, sans-serif" margin = "1.5rem" end
    label: for = "city" content = "Where do you live? " end
    select: id = "city" name = "city" change = Pick
        option: value = "none" content = "Choose a city" disabled = true selected = true end
        option group: label = "Europe"
            option: value = "oslo" content = "Oslo" end
            option: value = "rome" content = "Rome" end
        end
        option group: label = "Asia"
            option: value = "tokyo" content = "Tokyo" end
            option: value = "seoul" content = "Seoul" disabled = true end
        end
    end
    paragraph: id = "out" content = "You chose: none" end
end

When the visitor should also be allowed to type something that is not in your list, use a data list. It is a list of suggestions that has no look of its own. Give it an id and name it in the list attribute of a text input: the browser then offers the matching suggestions while the visitor types.

web-forms-datalist.salam
import dom

func Echo:
    dom.SetText(dom.ById("out"), "Searching for " + dom.GetValue(dom.ById("lang")))
end

page:
    title = "Suggestions"
    global: selector = "body" font family = "system-ui, sans-serif" margin = "1.5rem" end
    label: for = "lang" content = "Favourite language " end
    input: id = "lang" list = "langs" placeholder = "Start typing" input = Echo end
    data list: id = "langs"
        option: value = "Salam" end
        option: value = "Python" end
        option: value = "Rust" end
        option: value = "Scheme" end
    end
    paragraph: id = "out" content = "Searching for" end
end

Buttons

A button has a type. A submit button sends the form, a reset button puts all fields back, and a button button does nothing until a script gives it something to do. Inside a form a button without a type is a submit button, which is how many forms get sent by accident: always write the type.

A submit button with a name and a value tells you which one was pressed, so one form can have Save and Delete. The buttons can also override the form for one press: form action, form method, form enctype, form target and form novalidate replace the form's own attribute. A Save draft button with form novalidate sends a half filled form. With form = "id" a button may stand outside the form it belongs to. In a submit handler on a real page, dom.GetProp(e, "submitter") is the button that was used. In the example each button has its own click handler instead, which reads the button's value with dom.GetStr(dom.EventTarget(e), "value"). The reset button below the form belongs to it through form = "note".

web-forms-buttons.salam
import dom

func Pressed(e: i64):
    dom.PreventDefault(e)
    dom.SetText(dom.ById("out"), "Pressed: " + dom.GetStr(dom.EventTarget(e), "value"))
end

page:
    title = "Buttons"
    global: selector = "body" font family = "system-ui, sans-serif" margin = "1.5rem" end
    global: selector = "button" padding = "6px 12px" margin = "0.4rem 0.4rem 0 0" end
    form: id = "note"
        input: id = "t" name = "title" placeholder = "Title" end
        button: type = submit name = "do" value = "save" click = Pressed content = "Save" end
        button: type = submit name = "do" value = "draft" click = Pressed content = "Save draft" end
        button: type = reset content = "Clear" end
        button: type = button value = "help" click = Pressed content = "Help" end
    end
    button: type = reset form = "note" content = "Clear from outside" end
    paragraph: id = "out" content = "Nothing pressed yet." end
end

Two more groups of attributes belong to buttons. popover target and popover target action show or hide an element that has the popover attribute, with no code. command and commandfor give a command such as show-modal to a dialog. Both are newer, so check the browsers your visitors use.

Showing results

Three elements show values instead of asking for them. output holds a result, like the number beside a slider; its for lists the controls it is calculated from, and Salam checks the ids. progress is a bar for how much of a job is done, with value out of max. meter shows a value on a known scale, and low, high and optimum tell the browser which part of the scale is good: the bar turns green, yellow or red.

web-forms-gauges.salam
import dom

func Move:
    level := dom.GetValue(dom.ById("level"))
    dom.SetText(dom.ById("shown"), level + " %")
    dom.SetStr(dom.ById("bar"), "value", level)
    dom.SetStr(dom.ById("gauge"), "value", level)
end

page:
    title = "Output, progress, meter"
    global: selector = "body" font family = "system-ui, sans-serif" margin = "1.5rem" end
    global: selector = "progress, meter" display = block width = "16rem" margin = "0.6rem 0" end
    form:
        label: for = "level" content = "Battery level " end
        input: id = "level" type = range min = 0 max = 100 value = 40 input = Move end
        output: id = "shown" for = "level" content = "40 %" end
    end
    progress: id = "bar" value = 40 max = 100 end
    meter: id = "gauge" value = 40 min = 0 max = 100 low = 25 high = 75 optimum = 100 end
end

Drag the slider and all three change. progress and meter look alike and mean different things. A task that is going to finish is a progress. A reading that goes up and down, such as battery charge, is a meter.

Files

A file input opens the file chooser. accept filters what it offers, with MIME types such as image/png, wildcards such as image/* and extensions such as .pdf. It is a convenience: a visitor can still pick another file, so check the type on the server. multiple allows several files. A form that sends files must be a post form with enctype = "multipart/form-data". In code, dom.GetProp(input, "files") is the list of chosen files, dom.CountOf counts them and dom.ItemAt picks one.

web-forms-file.salam
import dom

func Listed:
    files := dom.GetProp(dom.ById("pics"), "files")
    n := dom.CountOf(files)
    mut text := "No files chosen."
    if n > 0: text = "Chosen: " + dom.GetStr(dom.ItemAt(files, 0), "name") end
    if n > 1: text = text + " and " + (n - 1) + " more" end
    dom.SetText(dom.ById("out"), text)
end

page:
    title = "File upload"
    global: selector = "body" font family = "system-ui, sans-serif" margin = "1.5rem" end
    form: method = post enctype = "multipart/form-data"
        label: for = "pics" content = "Pictures " end
        input: id = "pics" name = "pics" type = file accept = "image/png, image/jpeg, .webp" multiple = true change = Listed end
    end
    paragraph: id = "out" content = "No files chosen." end
end

The Jalali date

Salam has an input type of its own for Persian dates, jalali date (in Persian files تاریخ شمسی). It is a text box that types Persian digits whatever keyboard the visitor has, adds the slashes, and tells the browser the date is invalid when the month or the day does not exist, which also respects leap years. Latin and Arabic digits that the visitor pastes are converted. The box shows the digits ۱۴۰۵/۰۸/۰۴, and the same date with Latin digits, 1405/08/04, is kept in the data-value attribute for your code to read with dom.GetAttr.

web-forms-jalali.salam
import dom

func Changed:
    day := dom.GetAttr(dom.ById("birth"), "data-value")
    dom.SetText(dom.ById("out"), "Stored value: " + day)
end

page:
    title = "Persian date"
    global: selector = "body" font family = "system-ui, sans-serif" margin = "1.5rem" end
    label: for = "birth" content = "Birthday (Jalali) " end
    input: id = "birth" name = "birth" type = "jalali date" placeholder = "1380/01/01" change = Changed end
    paragraph: id = "out" content = "Stored value:" end
end

The value that a real form send puts on the wire is the text as the box shows it, with Persian digits. Convert on the server, or read data-value in a submit handler.

Reference: every form element

Each element is shown with an example, and the attributes that belong only to that element are listed under it. The attributes that every element has are in the lesson on the head and global attributes, and the events, such as submit, input and change, are in the lesson on keys and form events. The examples are checked by the compiler, so you can paste any of them into a page: block. Persian names work in any file, and the Persian lessons use them.

form (Persian: فرم; HTML: <form>)

A form collects what the visitor types or picks and sends it to an address. Inputs, buttons and the other form controls go inside it. A form can also run code instead of sending: write a submit handler that calls dom.PreventDefault and reads the fields yourself, as the examples in this lesson do. Forms cannot be nested.

form: action = "/search" method = get
    label: for = "q" content = "Search" end
    input: id = "q" name = "q" type = search end
    button: type = submit content = "Go" end
end
AttributeWhat it doesExample
name
نام
A name for the form, so that scripts and the browser can find it among the forms of the page. It is not sent to the server. [string]form: name = "login" input: name = "user" placeholder = "User" end end
target
هدف
Where the answer of the server is shown: same (the current window, the default), new (a new window or tab), parent or top for pages inside frames. The HTML names _self, _blank, _parent and _top work too. [enum: target]form: action = "/report" target = new button: type = submit content = "Open the report" end end
rel
رابطه
Says how the target page relates to this one, as a list of words separated by spaces. With target = new write noopener, so that the new page cannot reach back into yours. [token (list)]form: action = "/pay" target = new rel = "noopener noreferrer" button: type = submit content = "Pay" end end
autocomplete
تکمیل خودکار
on lets the browser fill the fields from what it remembers, off asks it not to. The default is on. Each field can override the form with its own autocomplete. [string]form: autocomplete = "off" input: name = "code" placeholder = "One use code" end end
action
مقصد ارسال
The address that receives the data when the form is sent. Without it the form is sent to the address of the page itself. [url]form: action = "/subscribe" method = post input: type = email name = "mail" end button: type = submit content = "Subscribe" end end
method
روش
How the data travels. get puts it in the address, which suits searches because the result can be bookmarked. post puts it in the body of the request, which is right for anything that changes data or is private. dialog sends nothing and just closes the dialog that holds the form. [enum: form_method]form: action = "/login" method = post input: type = password name = "secret" end button: type = submit content = "Log in" end end
enctype
رمزگذاری
How a post form packs its data. The default application/x-www-form-urlencoded is right for text. Choose multipart/form-data when the form has a file input, and text/plain only for debugging. [enum: form_enctype]form: method = post enctype = "multipart/form-data" input: type = file name = "photo" end button: type = submit content = "Upload" end end
novalidate
بدون اعتبارسنجی
A boolean. When it is true the browser does not check required, pattern and the other rules when the form is sent. Use it when you validate in your own code. [bool]form: novalidate = true input: name = "mail" type = email required = true end button: type = submit content = "Send anyway" end end
accept charset
نویسه‌گان پذیرفته
The text encodings the server accepts, separated by spaces. The default is the encoding of the page, and since pages are UTF-8 you rarely need it. [charset]form: accept charset = "UTF-8" input: name = "word" placeholder = "Any word" end end

input (Persian: ورودی; HTML: <input>)

A single form control. Its type decides what it is: a text box, a checkbox, a colour picker, a button and so on, 20 kinds in all that Salam names (see the table in the lesson). It has no content and no children. Give it a name, because a control without a name is not sent, and a label, because a control without a label is a puzzle for a screen reader.

label: for = "city" content = "City" end
input: id = "city" name = "city" placeholder = "Oslo" end

Void element, it cannot have children

AttributeWhat it doesExample
width
عرض
The width in pixels. It only has an effect on type = "image". For every other kind use the CSS width. [ulength]input: type = "image" url = "go.png" alt = "Go" width = 80 end
height
ارتفاع
The height in pixels. Like width it only matters for type = "image". [ulength]input: type = "image" url = "go.png" alt = "Go" height = 40 end
type
نوع
The kind of control. The names Salam knows are text (the default), password, email, number, tel, url, search, checkbox, radio, submit, button, reset, date, time, color, range, file, hidden, textarea and jalali date. Other HTML types, such as datetime-local, month, week and image, can be written as quoted strings. [enum: input_type]input: type = range min = 0 max = 10 value = 5 end
name
نام
The key under which the value is sent. Several radio buttons with the same name form one group, and the browser lets only one of them be checked. A control without a name is not sent at all. [string]input: name = "email" type = email end
value
مقدار
The starting value of the field, and what is sent. For a checkbox or a radio button it is the text that is sent when the box is checked. For buttons it is the label. [string]input: name = "nick" value = "guest" end
placeholder
جانگهدار
A short hint shown inside an empty field and gone when the visitor starts typing. It is not a label: it disappears, it is often low in contrast, and not all screen readers read it. Use a label for the name and the placeholder for an example. [string]input: name = "phone" type = tel placeholder = "0912 345 6789" end
required
الزامی
A boolean. The form cannot be sent while the field is empty (or, for a checkbox, unchecked), and the browser says so. It also sets the :required state for CSS. [bool]input: name = "name" required = true placeholder = "Required" end
checked
تیک خورده
A boolean for checkboxes and radio buttons: whether the control starts checked. Reading it later in code is done with dom.GetBool(el, "checked"). [bool]input: type = checkbox name = "news" checked = true end
disabled
غیرفعال
A boolean. A disabled control cannot be focused or changed, is drawn greyed out and is not sent with the form. If you want a field that is only unchangeable but still sent, use readonly. [bool]input: name = "plan" value = "Free" disabled = true end
url
نشانی
The address of the picture of an image button. It is written src in HTML. Add alt too. [url]input: type = "image" url = "send.png" alt = "Send" end
form
فرم
The id of the form this control belongs to. It lets a control stand outside its form in the page, or be part of a form far away. [idref]form: id = "signup" end input: name = "mail" type = email form = "signup" end
multiple
چندتایی
A boolean for file and email: the visitor may choose several files, or write several addresses separated by commas. [bool]input: type = file name = "docs" multiple = true end
size
اندازه
The visible width of a text box, counted in characters. It does not limit what can be typed (that is maxlength). For real layout use CSS width. [uint]input: name = "zip" size = 6 placeholder = "12345" end
autofocus
تمرکز خودکار
A boolean. The control gets the keyboard focus when the page opens. Use it on one field only, and only where the visitor expects it, such as a search page. [bool]input: type = search name = "q" autofocus = true end
autocomplete
تکمیل خودکار
Tells the browser what the field holds so it can fill it in. Use the standard words: name, email, tel, username, current-password, new-password, one-time-code, postal-code, or off. Password managers depend on these. [string]input: type = password name = "pw" autocomplete = "new-password" end
maxlength
بیشینه طول
A whole number. The most characters the visitor can type. The browser stops them at that point, so tell them the limit in the label or a counter. [uint]input: name = "tag" maxlength = 12 placeholder = "12 characters at most" end
minlength
کمینه طول
A whole number. The fewest characters that are accepted. A shorter value that the visitor typed makes the field invalid, and the form is not sent. An empty field is allowed unless it is also required. [uint]input: name = "user" minlength = 3 placeholder = "3 characters or more" end
readonly
فقط خواندنی
A boolean. The visitor can select and copy the text but not change it, and it is still sent with the form. Compare disabled. [bool]input: name = "id" value = "A-1042" readonly = true end
dirname
نام جهت
The name of an extra field that carries the text direction (ltr or rtl) of what was typed. Useful when you store text in several languages. The usual name is the field name plus .dir. [token]input: name = "comment" dirname = "comment.dir" end
accept
پذیرش
For file: the kinds of files the dialog offers, as a list of MIME types such as image/png, wildcards such as image/*, or extensions such as .pdf. It is a hint for the visitor, not a check, so verify files on the server. [string (list)]input: type = file name = "doc" accept = "application/pdf, .docx" end
form action
مقصد ارسال فرم
On a submit button or an image button: sends the form to a different address than the form's own action, only when this button is used. [url]form: action = "/save" input: type = submit value = "Save" end input: type = submit value = "Save and close" form action = "/save-close" end end
form method
روش فرم
On a submit button: overrides the form's method for this button only, with get, post or dialog. [enum: form_method]form: action = "/find" method = post input: type = submit value = "Search" form method = get end end
form enctype
رمزگذاری فرم
On a submit button: overrides the form's enctype for this button only. [enum: form_enctype]form: method = post input: type = submit value = "Upload" form enctype = "multipart/form-data" end end
form novalidate
بدون اعتبارسنجی فرم
On a submit button: skips the browser's checks when this button is used. The classic use is a Save draft button next to a Send button. [bool]form: input: name = "title" required = true end input: type = submit value = "Save draft" form novalidate = true end end
form target
هدف فرم
On a submit button: shows the answer in a different window than the form's target says, for this button only. [enum: target]form: action = "/print" input: type = submit value = "Preview" form target = new end end
list
فهرست پیشنهادها
The id of a data list whose options are offered as suggestions while the visitor types. The visitor may still type something else. Salam checks that the id exists. [idref]input: name = "fruit" list = "fruits" end data list: id = "fruits" option: value = "Apple" end option: value = "Plum" end end
max
بیشینه
The largest accepted value for number, range, date and time. For dates write it as text: 2026-12-31. [string]input: type = number name = "qty" max = 10 end
min
کمینه
The smallest accepted value for number, range, date and time. [string]input: type = number name = "age" min = 18 end
pattern
الگو
A regular expression the whole value must match, for text, search, tel, url, email and password. Describe the rule in a title or next to the field, since the browser's own message is generic. [string]input: name = "zip" pattern = "[0-9]{5}" title = "Five digits" end
step
گام
The step between valid numbers or times. The default for number is 1, so 2.5 is refused unless you write step = "0.5" or step = "any". It is counted from min. [string]input: type = number name = "price" step = "0.5" min = 0 end
popover target
هدف واشو
On a button type input: the id of an element that has the popover attribute. Pressing the button shows or hides it, with no script. [idref]input: type = button value = "Tip" popover target = "tip" end box: id = "tip" popover = "auto" content = "Drink water." end
alpha
شفافیت رنگ
A boolean for color: the colour picker may also choose transparency. Only newer browsers support it. [bool]input: type = color name = "tint" alpha = true end
colorspace
فضای رنگ
For color: the colour space of the value, either limited-srgb (the default) or display-p3, which has more vivid colours on wide gamut screens. Only newer browsers support it. [enum: color_space]input: type = color name = "tint" colorspace = "display-p3" end
popover target action
کنش واشو
Together with popover target: what the button does to the popover. toggle (the default) shows or hides it, show only shows it and hide only hides it. [enum: popover_action]input: type = button value = "Close tip" popover target = "tip" popover target action = hide end box: id = "tip" popover = "auto" content = "Drink water." end
alt
جایگزین
The text that stands for the picture of an image button when the picture is missing, and that screen readers read. [string]input: type = "image" url = "send.png" alt = "Send the form" end
content
محتوا
Another way to write the starting text of the field: it becomes the value. Handy for a text typed straight into the page, and it is what a textarea input uses for its text. [string]input: name = "greeting" content = "Hello" end

textarea input (Persian: ورودی; HTML: <textarea>)

The form of text area that is written as an input with type = "textarea". It produces the same <textarea> tag, and its text comes from content. Prefer text area, which has the attributes that belong to a multi-line box (rows, cols, wrap).

input: type = "textarea" name = "note" content = "Write here" end

text area (Persian: متن بلند; HTML: <textarea>)

A box for text with several lines: a comment, a message, an address. The text inside is its starting content. The visitor can drag the corner to resize it unless CSS resize says otherwise. Line breaks the visitor types are sent as they are.

text area: name = "message" rows = 3 cols = 30 content = "Dear team," end
AttributeWhat it doesExample
name
نام
The key under which the text is sent. [string]text area: name = "bio" end
form
فرم
The id of the form this box belongs to, when it stands outside that form. [idref]form: id = "post" end text area: name = "body" form = "post" end
required
الزامی
A boolean. The form cannot be sent while the box is empty. [bool]text area: name = "reason" required = true placeholder = "Why?" end
disabled
غیرفعال
A boolean. The box cannot be edited or focused and is not sent. [bool]text area: name = "old" disabled = true content = "Archived text" end
autofocus
تمرکز خودکار
A boolean. The box gets the keyboard focus when the page opens. [bool]text area: name = "draft" autofocus = true end
autocomplete
تکمیل خودکار
on or off (or a standard word such as street-address): whether the browser may suggest earlier text. [string]text area: name = "address" autocomplete = "street-address" end
rows
ردیف‌ها
A whole number: how many lines of text are visible without scrolling. For exact sizes use CSS height. [uint]text area: name = "note" rows = 6 end
cols
ستون‌ها
A whole number: how many characters wide the box is. For exact sizes use CSS width, and width = "100%" for a box that fills its parent. [uint]text area: name = "note" cols = 50 end
maxlength
بیشینه طول
A whole number: the most characters the visitor can type. [uint]text area: name = "bio" maxlength = 200 end
minlength
کمینه طول
A whole number: the fewest characters accepted when the box is not empty. [uint]text area: name = "review" minlength = 20 end
placeholder
جانگهدار
A hint shown while the box is empty. It does not replace a label. [string]text area: name = "msg" placeholder = "Write your message" end
readonly
فقط خواندنی
A boolean. The text can be selected but not changed, and it is still sent. [bool]text area: name = "terms" readonly = true content = "Terms of use" end
wrap
شکستن خط
soft (the default) wraps long lines on the screen only. hard also puts real line breaks into the sent text at the edge of the box, and needs cols. [enum: wrap]text area: name = "poem" cols = 30 wrap = hard end
dirname
نام جهت
The name of an extra field that carries the text direction of what was typed, like the same attribute of input. [token]text area: name = "note" dirname = "note.dir" end

label (Persian: برچسب; HTML: <label>)

The caption of a form control. A click on the label focuses or toggles its control, which makes small checkboxes much easier to hit, and screen readers read it when the control is focused. Connect them with for (the id of the control) or by putting the control inside the label.

label: for = "mail" content = "Email" end
input: id = "mail" type = email end
AttributeWhat it doesExample
for
برای
The id of the control this label names. Salam checks at compile time that an element with that id exists in the page, so a typo is an error instead of a silent loss. [idref]label: for = "pw" content = "Password" end input: id = "pw" type = password end

button (Persian: دکمه; HTML: <button>)

A button with any content: text, or an image next to text. Inside a form its default type is submit, which sends the form. For a button that only runs code, say type = button or the form is sent by accident.

button: type = button content = "Click me" end
AttributeWhat it doesExample
type
نوع
submit sends the form, reset puts every field back to its starting value and button does nothing by itself. Without a type, a button in a form is a submit button. [enum: button_type]button: type = reset content = "Clear the form" end
name
نام
With value: the pair that is sent when this button was the one pressed. It lets a form with several submit buttons know which one the visitor used. [string]button: type = submit name = "action" value = "save" content = "Save" end
value
مقدار
The value sent under the button's name when the button is pressed. In a script, dom.GetStr(button, "value") reads it. [string]button: type = submit name = "action" value = "delete" content = "Delete" end
disabled
غیرفعال
A boolean. The button cannot be pressed and is drawn greyed out. A common use is to keep Send disabled until the form is valid. [bool]button: type = submit disabled = true content = "Send" end
form
فرم
The id of the form this button belongs to, so the button can stand outside the form, for instance in a toolbar. [idref]form: id = "note" end button: type = submit form = "note" content = "Save the note" end
autofocus
تمرکز خودکار
A boolean. The button gets the keyboard focus when the page opens. [bool]button: type = button autofocus = true content = "Start" end
form action
مقصد ارسال فرم
On a submit button: sends the form to this address instead of the form's own action. [url]form: action = "/save" button: type = submit content = "Save" end button: type = submit form action = "/publish" content = "Publish" end end
form method
روش فرم
On a submit button: sends the form with this method instead of the form's method. [enum: form_method]form: action = "/item" method = post button: type = submit content = "Save" end button: type = submit form method = get content = "Preview" end end
form enctype
رمزگذاری فرم
On a submit button: packs the data in this way instead of the form's enctype. [enum: form_enctype]form: method = post button: type = submit form enctype = "multipart/form-data" content = "Send with files" end end
form novalidate
بدون اعتبارسنجی فرم
On a submit button: skips the browser's checks when this button is pressed, as in a Save draft button. [bool]form: input: name = "title" required = true end button: type = submit form novalidate = true content = "Save draft" end end
form target
هدف فرم
On a submit button: shows the answer in this window instead of the form's target. [enum: target]form: action = "/preview" button: type = submit form target = new content = "Preview in a new tab" end end
popover target
هدف واشو
The id of an element with the popover attribute that this button shows or hides, with no script. [idref]button: popover target = "menu" content = "Menu" end box: id = "menu" popover = "auto" content = "Home, About, Contact" end
popover target action
کنش واشو
With popover target: toggle (the default), show or hide. [enum: popover_action]button: popover target = "menu" popover target action = show content = "Open the menu" end box: id = "menu" popover = "auto" content = "Home, About, Contact" end
command
فرمان
A command the button gives to the element named by commandfor, with no script: for example show-modal and close for a dialog. A newer feature, so check browser support before relying on it. [string]button: command = "show-modal" commandfor = "box" content = "Open" end dialog: id = "box" content = "Hello" end
commandfor
هدف فرمان
The id of the element that receives the command. Salam checks that the id exists. [idref]button: command = "close" commandfor = "box" content = "Close" end dialog: id = "box" open = true content = "Hello" end

select (Persian: انتخاب; HTML: <select>)

A drop-down list: the visitor picks one of the options, or several with multiple. The value that is sent is the value of the chosen option, or its text when it has none. Use it for four or more choices; for two or three, radio buttons show the options without a click.

select: name = "fruit"
    option: value = "apple" content = "Apple" end
    option: value = "pear" content = "Pear" end
end
AttributeWhat it doesExample
name
نام
The key under which the chosen value is sent. [string]select: name = "size" option: value = "s" content = "Small" end end
form
فرم
The id of the form the list belongs to, when it stands outside that form. [idref]form: id = "order" end select: name = "size" form = "order" option: value = "s" content = "Small" end end
multiple
چندتایی
A boolean. The list becomes a box that shows several options at once, and the visitor can select more than one with Ctrl or Shift (or by tapping, on a phone). [bool]select: name = "toppings" multiple = true size = 3 option: value = "cheese" content = "Cheese" end option: value = "olives" content = "Olives" end option: value = "corn" content = "Corn" end end
required
الزامی
A boolean. The form cannot be sent until an option with a non-empty value is chosen, so make the first option a prompt with value = "". [bool]select: name = "country" required = true option: value = "" content = "Choose a country" end option: value = "no" content = "Norway" end end
disabled
غیرفعال
A boolean. The whole list is greyed out, cannot be changed and is not sent. [bool]select: name = "plan" disabled = true option: content = "Free" end end
size
اندازه
A whole number: how many options show at once. Anything above 1 turns the drop-down into a scrolling box. [uint]select: name = "day" size = 4 option: content = "Mon" end option: content = "Tue" end option: content = "Wed" end option: content = "Thu" end option: content = "Fri" end end
autofocus
تمرکز خودکار
A boolean. The list gets the keyboard focus when the page opens. [bool]select: name = "lang" autofocus = true option: content = "English" end end
autocomplete
تکمیل خودکار
A standard word such as country or off: lets the browser pick the option from what it knows about the visitor. [string]select: name = "country" autocomplete = "country" option: value = "no" content = "Norway" end end

option (Persian: گزینه; HTML: <option>)

One choice in a select, an option group or a data list. Its text is what the visitor sees, and its value is what is sent. In a data list the value is the suggestion.

select: name = "fruit"
    option: value = "apple" content = "Apple" end
end

Only inside: select, option group, data list

AttributeWhat it doesExample
value
مقدار
The text that is sent when this option is chosen. Without it the text of the option is sent. Give a short code, not a translated sentence, so that your server does not depend on the page language. [string]select: name = "lang" option: value = "fa" content = "Persian" end end
label
برچسب
A shorter text to show instead of the text inside the option. It is rarely needed. In a data list it is the explanation shown next to the suggestion. [string]select: name = "unit" option: value = "km" label = "km" content = "kilometres" end end
disabled
غیرفعال
A boolean. The option is shown greyed out and cannot be chosen. Good for a prompt line like Choose a city, or for something sold out. [bool]select: name = "city" option: value = "" content = "Choose a city" disabled = true selected = true end option: value = "oslo" content = "Oslo" end end
selected
انتخاب‌شده
A boolean. The option is the chosen one when the page opens. Without it the first option is chosen. [bool]select: name = "size" option: value = "s" content = "Small" end option: value = "m" content = "Medium" selected = true end end

option group (Persian: گروه گزینه; HTML: <optgroup>)

A named group of options inside a select. The visitor sees the name as a bold line that cannot be chosen. It must have a label, and the compiler stops without one.

select: name = "city"
    option group: label = "Europe"
        option: value = "oslo" content = "Oslo" end
        option: value = "rome" content = "Rome" end
    end
end

Required: label · Only inside: select

AttributeWhat it doesExample
label
برچسب
The name of the group, shown above its options. It is required. [string]select: name = "city" option group: label = "Asia" option: value = "tokyo" content = "Tokyo" end end end
disabled
غیرفعال
A boolean. Every option of the group is greyed out and cannot be chosen. [bool]select: name = "city" option group: label = "Closed" disabled = true option: value = "x" content = "Atlantis" end end end

data list (Persian: فهرست پیشنهاد; HTML: <datalist>)

A list of suggestions for a text input. Link it with the input's list attribute. Unlike select it does not force a choice: the visitor may type something that is not in the list. It is invisible by itself.

input: name = "browser" list = "browsers" end
data list: id = "browsers"
    option: value = "Firefox" end
    option: value = "Chrome" end
end

field set (Persian: گروه فیلد; HTML: <fieldset>)

Groups related controls in a box, such as the lines of an address or a set of radio buttons. A screen reader announces the legend when the visitor enters the group, which is how a bare list of radio buttons gets its question.

field set:
    legend: content = "Delivery" end
    label: input: type = radio name = "d" end span: content = " Home" end end
    label: input: type = radio name = "d" end span: content = " Office" end end
end
AttributeWhat it doesExample
name
نام
A name for the group, for scripts. It is not sent. [string]field set: name = "address" legend: content = "Address" end input: name = "street" end end
form
فرم
The id of the form the group belongs to, when it stands outside that form. The group's controls are part of that form. [idref]form: id = "f1" end field set: form = "f1" legend: content = "Extra" end input: name = "extra" end end
disabled
غیرفعال
A boolean. Every control inside is disabled at once, apart from those in the legend. Switch it in a script with dom.SetBool(el, "disabled", true), as the lesson example does. [bool]field set: disabled = true legend: content = "Locked" end input: name = "locked" value = "Cannot change" end end

legend (Persian: عنوان گروه; HTML: <legend>)

The title of a field set. It must be the first child of the group and is drawn on its border.

field set:
    legend: content = "Payment" end
    input: name = "card" placeholder = "Card number" end
end

Only inside: field set

output (Persian: خروجی; HTML: <output>)

The result of a calculation or of the visitor's action, such as the value next to a slider. Screen readers announce changes in it politely. Set its text from a script with dom.SetText.

input: id = "a" type = number value = 2 end
output: for = "a" content = "4" end
AttributeWhat it doesExample
name
نام
A name for the result, for scripts. An output is never sent with the form. [string]output: name = "total" content = "0" end
form
فرم
The id of the form this result belongs to, when it stands outside that form. [idref]form: id = "calc" end output: form = "calc" content = "0" end
for
برای
The ids of the controls that the result is calculated from, separated by spaces. It only documents the link for assistive technology. Salam checks that every id exists. [idref (list)]input: id = "w" type = number end input: id = "h" type = number end output: for = "w h" content = "0" end

progress (Persian: پیشرفت; HTML: <progress>)

A bar for how far a task has got, such as an upload. Without value it shows an endless busy animation. For a measurement that is not progress, such as disk use, take meter. Put a text next to it as well, since the bar alone says little to a screen reader.

progress: value = 30 max = 100 end
AttributeWhat it doesExample
value
مقدار
A number from 0 to max: how much is done. Leave it out for an unknown amount. [float]progress: value = 70 max = 100 end
max
بیشینه
The number that means finished. The default is 1, so a value of 0.3 is thirty percent. [float]progress: value = 3 max = 5 end

meter (Persian: سنجه; HTML: <meter>)

A gauge for a value inside a known range: battery level, a score, how full a disk is. Browsers colour it green, yellow or red from low, high and optimum. It is not for progress: use progress for that.

meter: value = 6 min = 0 max = 10 low = 3 high = 8 optimum = 10 end

Required: value

AttributeWhat it doesExample
value
مقدار
The current value. It must lie between min and max; otherwise the browser clips it. This is a required attribute. [float]meter: value = 0.6 end
max
بیشینه
The top of the range. The default is 1. [float]meter: value = 70 max = 100 end
min
کمینه
The bottom of the range. The default is 0. [float]meter: value = 15 min = -10 max = 40 end
low
حد پایین
The upper limit of the low part of the range. A value below it counts as low. [float]meter: value = 20 max = 100 low = 30 end
high
حد بالا
The lower limit of the high part of the range. A value above it counts as high. [float]meter: value = 90 max = 100 high = 80 end
optimum
بهینه
The best value. When it lies in the high part, high values are drawn green; when it lies in the low part, low values are. This is how disk use (low is good) and a score (high is good) look different. [float]meter: value = 90 max = 100 low = 30 high = 80 optimum = 0 end

Try it yourself

  • Make a contact form with a name, an email, a topic in a select and a message in a text area with a character counter. Show the collected values in a paragraph when it is sent.
  • In the validation example, add a Repeat password field and report an error until the two passwords are the same. Read both with dom.GetValue.
  • Build a small order form with a number of items (min = 1), a delivery choice in radio buttons and a total shown in an output.
  • Give the Jalali date field a required and see what the browser does when it is empty and when the date is 1405/13/40.