Skip to content

Input components ​

Controls people press, type into or drag. Most read and write a state name through bind=.

Generated from the component definitions in src/components, the same source the parser validates against and the model guide is built from. Every element also accepts if=, tip=. Each example below is rendered live by the SayShape engine.

button ​

A pressable button that runs actions. Use emit: to tell the app what the user chose.

PropTypeDescription
"text"text, requiredThe button label.
onactionWhat happens when pressed, e.g. inc:count, set:done:true, emit:book. Separate several with ;
variantprimary / secondary / dangerprimary (filled), secondary (outlined) or danger.
disabledconditionExpression; when true the button is greyed out and can't be pressed.
sayshape
button "Book now" on=emit:book
button "Back" variant=secondary on=dec:step
Rendered by the SayShape engineOpen in playground

Try button interactively →

Pressable text for a secondary action, lighter than a button.

PropTypeDescription
"text"text, requiredThe link text.
onaction, requiredWhat happens when pressed, e.g. emit:size_guide.
sayshape
link "Size guide" on=emit:size_guide
Rendered by the SayShape engineOpen in playground

Try link interactively →

check ​

A checkbox that toggles a true/false state value.

PropTypeDescription
"text"text, requiredThe label next to the box.
bindstate, requiredThe state name this checkbox reads and writes.
sayshape
state agreed=false
check "I agree to the terms" bind=agreed
Rendered by the SayShape engineOpen in playground

Try check interactively →

switch ​

An on/off switch with a label, for settings that take effect right away.

PropTypeDescription
"text"text, requiredThe label for the setting.
bindstate, requiredThe true/false state name this switch reads and writes.
onactionActions to run after it is switched, e.g. emit:changed. Separate several with ;
sayshape
state updates=true
switch "Email me order updates" bind=updates
Rendered by the SayShape engineOpen in playground

Try switch interactively →

radio ​

One choice among several. Give each radio in a group the same bind= and its own value=.

PropTypeDescription
"text"text, requiredThe label for this choice.
bindstate, requiredThe state name the group shares; it holds the chosen value.
valuestring, requiredWhat bind= is set to when this radio is chosen.
onactionActions to run after it is chosen, e.g. emit:changed. Separate several with ;
sayshape
state size=m
radio "Small" bind=size value=s
radio "Medium" bind=size value=m
radio "Large" bind=size value=l
Rendered by the SayShape engineOpen in playground

Try radio interactively →

select ​

A field that opens a list to pick one option from, for choices like a department, status or filter; compact where radio buttons would take too much room. bind= holds the chosen option's value; with multiple=true, a list of the chosen values, for filters like regions to show.

PropTypeDescription
"text"textThe label above the field.
itemsexpression, requiredThe options: a list of records, or of text or numbers. At most 200.
valuenameFor records, the field holding each option's value (text or a number), e.g. value=id. Options without one, or repeating one, are left out.
labelnameFor records, the field shown for each option, e.g. label=name. Default: the value.
bindstate, requiredState that holds the chosen value, like department or t.status.
multiplebooleanPick several options: bind= holds a list of their values, starting from a list in state like regions=["West", "East"] (or [] for none), and the list stays open while choosing. filter(sales, 'region', regions) keeps the items whose field is any chosen value; an empty list keeps none, so show everything then: {len(regions) ? filter(sales, 'region', regions) : sales}.
placeholderstringShown while nothing (or a value no option has) is chosen.
unavailablenameA field that, when true, greys an option out so it can't be chosen, e.g. unavailable=full.
disabledconditionExpression; when true the field is greyed out and doesn't open.
onactionActions to run after an option is chosen, with bind= already set, e.g. emit:changed. Separate several with ;
sayshape
state teams=[{id: "eng", name: "Engineering"}, {id: "sales", name: "Sales"}] team=""
select "Team" items=teams value=id label=name bind=team placeholder="Choose a team"
Rendered by the SayShape engineOpen in playground

Try select interactively →

date-picker ​

A field that opens a month calendar to pick one date, kept in bind= as YYYY-MM-DD text ("" for none), or with range=true a start and an end. For deadlines, bookings or report filters.

PropTypeDescription
"text"textThe label above the field.
bindstate, requiredState that holds the date as YYYY-MM-DD, like due or t.date. Start it as "" for no date.
minstringThe earliest date that can be chosen, YYYY-MM-DD, included.
maxstringThe latest date that can be chosen, YYYY-MM-DD, included.
rangebooleanPick a start and an end date: bind= holds {from, to} as YYYY-MM-DD, starting from a record in state like period={from: "", to: ""}. The calendar offers Last 7 days and Last 30 days. filter(orders, 'date', period) keeps the items from the start to the end, both included, and all of them while it's empty.
placeholderstringShown while no date is chosen.
requiredbooleanWhen true, the calendar has no Clear button.
disabledconditionExpression; when true the field is greyed out and doesn't open.
onactionActions to run after the date changes, with bind= already set, e.g. emit:changed. Separate several with ;
sayshape
state due=""
date-picker "Due date" bind=due min="2026-01-01" max="2027-12-31"
Rendered by the SayShape engineOpen in playground

Try date-picker interactively →

stepper ​

A number with minus and plus buttons, for small counts like a quantity.

PropTypeDescription
"text"textAn optional label on the left.
bindstate, requiredThe number state this stepper reads and writes.
minnumberSmallest value (default 0).
maxnumberLargest value (no limit if left out).
stepnumberHow much each press changes the value (default 1).
onactionActions to run after the value changes, e.g. emit:changed. Separate several with ;
sayshape
state qty=2
stepper "Quantity" bind=qty min=1 max=10
Rendered by the SayShape engineOpen in playground

Try stepper interactively →

slider ​

Picks a number in a range by dragging a handle, or with the arrow keys. Good for prices, distances and amounts.

PropTypeDescription
"text"textAn optional label above the slider. Show the value with an {expression}.
bindstate, requiredThe number state this slider reads and writes.
minnumberSmallest value (default 0).
maxnumberLargest value (default 100).
stepnumberThe value moves in steps of this size (default 1).
onactionActions to run after the user lets go, or after each arrow key, e.g. emit:changed. Separate several with ;
sayshape
state maxPrice=120
slider "Up to ${maxPrice}" bind=maxPrice min=20 max=300 step=10
Rendered by the SayShape engineOpen in playground

Try slider interactively →

input ​

A text field people type into, such as a name, an email, a search, a new item, or with lines= a note or a message. Each keystroke writes its bind= state, and required= and kind=email or number say what's wrong once it's been left.

PropTypeDescription
"text"textThe label above the field. Leave it out only when the screen makes the purpose obvious.
bindstate, requiredThe state name or path it reads and writes, like task or t.title.
placeholderstringA hint shown while the field is empty, like e.g. Sunscreen.
kindtext / email / number / password / searchtext, email, number, password or search. Picks the phone keyboard and autofill; number writes a number, or null while empty. email and number also say when what's typed isn't one.
linesnumberRows of text, 1 to 12: more than 1 is a box for a note or a message, where Enter starts a new line (on= then runs only from a button).
requiredconditionWhen true, an empty field says Required once it's been left.
errorstringYour own message, shown under the field once it's been left, while it isn't empty, e.g. error="{len(pw) < 8 ? 'At least 8 characters' : ''}".
onactionActions to run when Enter is pressed, after the value is written, e.g. emit:changed. Separate several with ;
disabledconditionExpression; when true the field is greyed out and can't be typed in.
maxnumberLongest value in characters (at most 10000; 2000 when lines= is more than 1).
sayshape
state task="" todos=[]
input "New item" bind=task placeholder="e.g. Sunscreen" on="add:todos:{task};set:task:''"
text "{plural(len(todos), 'item')}" tone=muted
Rendered by the SayShape engineOpen in playground

Try input interactively →

segmented ​

Two to six short options in one track, one chosen, like Week, Month and Year, or Chart and Table. Show different content for each with if=.

PropTypeDescription
bindstate, requiredThe state name holding the chosen option's text.
labelstringWhat the choice is about, read by screen readers, e.g. label="Period". Default: the text just above it.
optionsstring, requiredThe options, comma-separated, e.g. options="Week,Month,Year". At most 6, one or two words each.
onactionActions to run after the choice changes, e.g. emit:changed. Separate several with ;
sayshape
state view="Chart"
segmented bind=view options="Chart,Table"
text "Showing the {lower(view)}" tone=muted
Rendered by the SayShape engineOpen in playground

Try segmented interactively →

A button that opens a short list of actions, for what can be done to one item (edit, duplicate, delete) without a row of buttons. Each menu-item runs its own on=.

PropTypeDescription
"text"textThe button's words, e.g. Actions. Leave it out for a small ⋯ button.
labelstringWhat a screen reader calls a ⋯ button with no text, e.g. label="Order actions". Default: More actions.
variantsecondary / primarysecondary (outlined) or primary (filled).
disabledconditionWhen true, it can't be opened.
alignstart / endWhere it sits in its room: start, or end (the right). Default: end for a ⋯ menu, start for one with words.

Children: menu-item.

sayshape
menu "Actions"
  menu-item "Edit" icon=file on=emit:edit
  menu-item "Duplicate" icon=copy on=emit:duplicate
  menu-item "Delete" icon=x-circle tone=bad on=emit:delete
Rendered by the SayShape engineOpen in playground

Try menu interactively →

One action in a menu's list.

PropTypeDescription
"text"text, requiredWhat it does, e.g. Duplicate.
onaction, requiredWhat happens when it's chosen, e.g. emit:duplicate or set:confirm:true. Separate several with ;
iconstringAn icon before the text, by name from icon's list, e.g. icon=check-circle.
tonedefault / badbad for an action that removes or can't be undone, drawn in the bad color.
disabledconditionWhen true, it's shown but can't be chosen.

Only inside: menu.

sayshape
menu "Actions"
  menu-item "Duplicate" icon=copy on=emit:duplicate
Rendered by the SayShape engineOpen in playground

Try menu-item interactively →

SayShape v0.1 is an early prototype under the MIT License. Status · GitHub