WireText VSCode
WireText is a text format that allows you to rapidly create low fidelity user interfaces mockups. This VSCode extensions gives you a live preview of the mockups.
Features:
- A HTML/CSS templating language (inspired by PugJS) with a very lean syntax.
- A library of common user-interface components, using a hand-drawn style so that nobody mistakes your wireframes for polished layout mockups.
- As-you-type previews and error feedback.
- Automatic scrolling of the preview panel.
Installation
WireText can be installed through the VSCode Marketplace or through Open VSX. Alternatively, you can download it here and install it in VSCode using Extensions: Install from VSIX....
The components library
This section is autogenerated from the component-examples.wt example file. If you want to get started with WireText quickly, we recommend you install the extension and open that file. Contrary to this Markdown document, that will provide you with syntax highlighting and an easy way to play around.
In order to use components, we should first include the built-in components library, using include components-v1
.
include components-v1
A Phone
is a type of screen. It must have an id
, which can be used to link to it from other screens. Here's an example:
Phone id=login title="Welcome to Thingies"
Input label=Email value="test@example.com"
Input label=Password value=123456 type=password
Button content="Sign in" link=home
Screens will often have some associated notes:
Phone id=login title="Welcome to Thingies" notes="""
These are the notes for this screen.
- Notes are written in a simple *Markdown*-like rich text.
- They can be used to describe **behavior** associated with the screen.
"""
Input label="Email" value="test@example.com"
Input label=Password value=123456 type=password
Button content="Sign in" link=home
Let's define a template layout for a logged in view of our app, that'll we be able to reuse for most of our screens. Icons are provided by Remix Icons. You can look the names up here: https://remixicon.com/
define LoggedInPhone(id,title,content,up?)
Phone id={id} title={title} up={up} content={content}
bar_right =
Icon icon="settings" link=settings
Icon icon="profile" link=profiled
panel =
Button link=home content="Home" icon=home
Button link=favs content="Favourites" icon=heart
Button link=about content="About" icon=info
hr
Button content="Nederlands" icon=flag
Button content="English" icon=flag
Button content="Deutsch" icon=flag
Now use the template we just created to display a page with an empty list and an add-button:
LoggedInPhone id=home title="Home"
ActionButton link="add"
RemixIcon icon=add
.wt-empty-page "No items"
A screen with some tabs and a view inputs:
LoggedInPhone id=add title="Add an item" up=home
Tabs
Tab title=Movie selected={true}
Input label=Title value=Memento
Input label="Release year" value=2000
Button link=home_populated content=Add
Tab title=Song
Tab title=Book
Tab title="..."
A populated list:
LoggedInPhone id=home_populated title="Home"
ActionButton link="add"
RemixIcon icon=add
List
ListItem text="Fight Club" subtext="Movie, 1999" thumbnail=image
ListItem text=Memento subtext="Movie, 2000" thumbnail="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR1Ud-yvPVYheznyWLI9Vo53AFt67Q2XMZWRg&usqp=CAU" link=edit_thing
ListItem text="The Matrix" subtext="Movie, 1999" thumbnail=code
Some more input elements, and columns:
LoggedInPhone id=edit_thing title="Memento" up=home
img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR1Ud-yvPVYheznyWLI9Vo53AFt67Q2XMZWRg&usqp=CAU"
.wt-columns
CheckBox label=Favourite
CheckBox label=Owned checked=true
FieldSet legend="Thingy type"
RadioButton label=Movie name=thing_type checked={true}
RadioButton label=Book name=thing_type
RadioButton label=Other name=thing_type
TextArea label=Synopsis value="""
A man with short-term memory loss attempts to track
down his wife's murderer."""
Select label=Rating options="All | PG-13 | PG-16"
Button content=Save link=home_populated
Insert some html formatting:
LoggedInPhone id=about title="About..." up=home
html """
Lorem ipsum dolor sit <strong>amet</strong>, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Platea dictumst quisque sagittis purus sit amet volutpat consequat. Integer feugiat scelerisque varius morbi enim nunc faucibus.
<em>Quis auctor</em> elit sed vulputate mi sit amet mauris. Tincidunt tortor aliquam nulla facilisi cras fermentum odio. Sit amet nisl suscipit adipiscing bibendum est. Auctor urna nunc id cursus metus aliquam eleifend mi. Rhoncus dolor purus non enim praesent elementum facilisis leo. Sed lectus vestibulum mattis ullamcorper.
Dui ut ornare lectus sit amet est. Eros in cursus turpis massa tincidunt dui ut. Ultrices neque ornare aenean euismod elementum nisi quis eleifend. Purus non enim praesent elementum facilisis leo vel. Accumsan sit amet nulla facilisi morbi tempus iaculis urna id.
Egestas egestas fringilla phasellus faucibus scelerisque eleifend. Amet nisl purus in mollis nunc sed id semper risus. Varius duis at consectetur lorem. Amet purus gravida quis blandit. Pretium nibh ipsum consequat nisl vel pretium.
"""
The Computer template:
Computer id=wintest title=Test notes="""
The name of the last-opened file is stored, and if the file still exists when the application starts again, it should automatically be opened.
"""
MenuBar
MenuCategory category="File"
Button icon=file content=Load
Button icon=save content=Save
Button icon=close content=Exit
MenuCategory category="Edit"
MenuCategory category="Tools"
MenuCategory category="Help"
.wt-empty-page "Start by opening a file..."
The Watch template:
Watch id=watch
h3 "Favourites"
List
ListItem text="Fight Club" subtext="Movie, 1999" thumbnail=image
ListItem text=Memento subtext="Movie, 2000" thumbnail="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR1Ud-yvPVYheznyWLI9Vo53AFt67Q2XMZWRg&usqp=CAU" link=watch_view_thing
ListItem text="The Matrix" subtext="Movie, 1999" thumbnail=code
Watch id=watch_view_thing
h3 "Memento"
img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR1Ud-yvPVYheznyWLI9Vo53AFt67Q2XMZWRg&usqp=CAU"
html """<strong>Synopsis</strong> A man with short-term memory loss attempts to track down his wife's murderer."""
html """<strong>Rating</strong> PG-13"""
Popups
LoggedInPhone id=add title="Add an item" up=home
Tabs
Tab title=Movie selected={true}
Input label=Title value=Memento
Input label="Release year" value=2000
Button link=home_populated content=Add
Tab title=Song
Tab title=Book
Tab title="..."
Popup
p "Just do it?"
.wt-columns
Button content="No"
Button content="Yes"
Expect more components in the future. Please let us know what you're missing as a GitHub issue: https://gitlab.com/saxion.nl/42/wiretext-code/-/issues
The WireText templating language
This section is autogenerated from the language-tutorial.wt example file. If you want to get started with WireText quickly, we recommend you install the extension and open that file. Contrary to this Markdown document, that will provide you with syntax highlighting and an easy way to play around.
The WireText language is an HTML templating language (inspired by PugJS). The following will create a <section> containing an <h3> with some text and a <ul> with two list items (the second one being a link):
section
h3
"Title text"
ul
li
"Item one."
li
a
"Item two."
# This is a comment.
When the content block for an element has only one item, it can be added to the same line, like this:
section
h3 "Title text"
ul
li "Item one."
li a "Item two."
HTML attributes can be provided using key=value pairs immediately after the tag name. Keys and values that contain 'strange' characters need to be "quoted" as a string.
section class=intro
h3 id=my_title style="color: red;" "Title text"
ul
li "Item one."
li a target=_blank href="https://example.com/two" "Item two."
As the class attribute is quite common, there's a shortcut for setting it:
button.active "Active"
button.active.destructive "Active and destructive"
When the tag name is left out, 'div' is assumed:
.some_class "I'm a <DIV>"
Multi-line strings are supported using triple double quotes:
pre style="""
background-color: [#222](https://gitlab.com/saxion.nl/42/wiretext-code/-/issues/222);
color: #fde;
""" title="Shell script"
"""
#!/bin/sh
files=`ls ~ | wc -l`
echo You have $files files in your home directory.
"""
For styling, we recommend the use of CSS instead of 'style' attributes. WireText offers a convenient syntax for defining CSS inline:
css
.special
background-color="#cce"
padding="8px"
.special > h3
color=red
.special li a
color=green
section class=special
h3 id=my_title "Title text"
ul
li "Item one."
li a target=_blank href="https://example.com/two" "Item two."
CSS selectors can be nested, and property=value pairs can optionally be put on the same line as the selector, allowing the above CSS to be written like this:
css .special background-color="#eee" padding="8px"
> h3 color=red
li a color=green
WireText allows you to define reusable components. Component names must start with a Capital Letter.
define YourName
fieldset
legend "Who are you?"
input placeholder="First name"
input placeholder="Last name"
To show a component, you can use its name as if it were an HTML tag:
YourName
.special
YourName
Components can have parameters, defined between (parenthesis) after the tag name. They must be provided when instantiating the component the same way that HTML attributes are provided. They can be used in most contexts (including within strings) by surrounding their names with curly brackets.
define AnyName(title, pronoun)
fieldset
legend {title}
input placeholder="{pronoun} first name"
input placeholder="{pronoun} last name"
AnyName title="Who is the king of pop?" pronoun=His
AnyName title="Who am I?" pronoun=My
Parameters are required, unless they get a default value, provided by a '=' and the value after the definition.
define AnyName(title = "Who are you?", pronoun = Your)
fieldset
legend {title}
input placeholder="{pronoun} first name"
input placeholder="{pronoun} last name"
AnyName
AnyName pronoun=Thy
Expression between curly brackets cannot just be parameter names, but are actually JavaScript expressions, in a context where all parameters have been defined as local variables
define AnyName(title = "Who are you?", pronoun = Your)
fieldset
legend {title.toUpperCase()}
input placeholder="{pronoun.replace(/o/, '😁')} first name"
input placeholder="{pronoun} last name"
AnyName
Component attributes names must be valid JavaScript variable names. In case you want to use a JavaScript keyword, such as class
or in
m as an attribute, you can suffix it with and underscore in the definition and in your JavaScript expressions.
define InClass(class_, in_)
input value={in_} class={class_}
InClass in=test class=intro
WireText supports the spread operator, to receive all attributes that have not been defined as parameters into an object. This object can be passed expanded to attributes for a DOM element or another component.
define SpreadComponent(a, b, ...rest)
p "a = {a}"
p "b = {b}"
p "rest has {Object.keys(rest).length} properties"
input ...rest
SpreadComponent a=Almond b=Banana style="color: red;" value="My input value" type=password
WireText offers if/else-blocks, where the condition is provided by a JavaScript expression. Also note how the '?' suffix in the parameter list can be used for optional parameters (defaulting to an empty string).
define AnyName(title = "Who are you?", checkbox?)
fieldset
legend {title}
input placeholder="First name"
input placeholder="Last name"
if {checkbox}
label
input type=checkbox
{checkbox}
AnyName
AnyName checkbox="I agree!"
Similarly, there are for-blocks.
for person in {"he she Frank".split(" ")}
AnyName title="Who is {person}?"
JavaScript can also be used in event handlers, allowing you to hack a little bit of interactivity into your wireframes. NOTE: Expect this feature to change at any time. I'm not really happy with it yet.
button onclick={e => e.target.innerText += "!"} "Hello"
You may also run a bit of JavaScript while your page is initially constructed using a run statement. Its this
variable will refer to the parent DOM element. NOTE: Expect this feature to change at any time. I'm not really happy with it yet.
css .angry color=red text-transform=uppercase font-weight=bold
section
"Test"
run {setInterval(() => this.classList.toggle('angry'), 500)}
(Multi-line) JavaScript containing curly brackets can be wrapped in triple curly brackets. While in regular form only a single JavaScript expression is expected, which will be used as a value by WireText, triple form doesn't deliver a value by default, but you can return
one if you want.
section
"Test"
run {{{
let element = this;
function toggleAngry() {
element.classList.toggle('angry');
}
setInterval(toggleAngry, 500);
}}}
Components can also receive a block of WireText as a parameter. The default block (which resembles the content of a DOM element) is called 'content'.
define FancyFrame(content)
div style="border: 4px solid #c0c;"
div style="border: 4px solid #0cc;"
div style="border: 4px solid #cc0; padding: 4px;"
{content}
FancyFrame
"Type something: "
input
FancyFrame "test"
You can also pass multiple blocks as parameters, or pass a block to a parameter not named 'content' using this form:
css header display=flex background-color="#ccc" align-items=center
> h3 flex=1 text-align=center
define TitleBar(title, left, right)
header
.left {left}
h3 {title}
.right {right}
TitleBar title="Hello world!"
left=
a href="#" "Go back"
right=
input type=checkbox
"Dark mode"
FancyFrame
content = select
option "moon"
option "mars"
WireText allows you to include files. The path should be given as a string and relative to the path of the current file.
include "language-tutorial-include.wt"
IncludeHello
WireText standard libraries (currently only components-v1
) can be included by giving the name as an identifier:
include components-v1
Phone id=test title="My app" up=home
content=
Input label=Name
CheckBox label="Sure!"
bar_right=
RemixIcon icon=settings