Bruck is a lo-fi prototyping system made with web components. Quickly create and comment on interface layouts. The output is screen reader accessible, and responsive without you having to author breakpoints.
Warning: Bruck is experimental, and in its very early stages of development.
You can mock up real or dummy content with a set of primitive layout components- Clone this repository.
- (No
npm install
because there's no dependencies, unless you want to contribute — then you may need the Jest stuff) - Write HTML with the help of the components (below) in the
index.html
and split-screeneditor.html
files. Check out a demo indemo.html
. - Serve by using something like http-server at the root
- That's pretty much it. Requests and contribution offers welcome.
(Used in conjunction with the utility classes.)
The <t-ext>
component lets you generate a paragraph of dummy text. Each word is represented as a line. 'Words' wrap like any other text content.
words |
A comma separated range e.g. Default: |
---|
<t-ext words="50,75"></t-ext>
Just the lorem ipsum text underlying the emblematic line-words.
The <w-ords>
component lets you generate sets of words, picked at random from lorem ipsum. It is an inline element, so can be used to generate text inside a <p>
or <h2>
etc.
count |
The number of words. Can be a set integer e.g. Default: |
---|---|
capitalize |
Capitalizes the first letter of each word (Boolean) |
sentence |
Capitalizes the first letter of the first word, and appends a period followed by a space (Boolean) |
repeat |
How many times to repeat the pattern (i.e. how many sentences to produce). Can be a set integer e.g. |
The following will produce a paragraph or 3-4 sentences.
<p>
<w-ords sentence repeat="3,4"></w-ords>
</p>
Just the lorem ipsum text verbatim.
The <i-mage>
element creates an accessible dummy/placeholder image (with a X-through style generated via Houdini's Paint API). The available horizontal space is filled unless minWidth
and maxWidth
props are supplied.
ratio |
The expression of an aspect ratio e.g. Default: |
---|---|
caption |
The text for a caption, which is placed in a Default: nothing ( |
minWidth |
Any CSS Default: |
maxWidth |
Any CSS Default: |
<i-mage ratio="3:4"></i-mage>
"Image with [ratio] aspect ratio".
The simplest of components: just wraps some content with some padding, and a border if you want it.
pad |
Padding for all sides. A point on the modular scale ( Default: |
---|---|
border |
Whether to apply a border (Boolean) Default: |
<b-ox pad="2" border>
<h3>My box</h3>
<t-ext></t-ext>
</b-ox>
"['Bordered'?] box containing [# of child elements] elements"
The <s-tack>
component lets you inject whitespace between flow elements. Wrap it around a set of elements and separate them visually. Critically, it accepts a repeat
prop that lets you multiply its contents a set number of times.
gap |
A point on the modular scale ( Default: |
---|---|
repeat |
The number of times the content (light DOM children) will be repeated. Can be an integer e.g. Default: |
lines |
Boolean: Whether or not to divide items with horizontal lines/borders Default: omitted (no lines) |
<s-tack repeat="3">
<t-ext></t-ext>
</s-tack>
"Column of [# of child elements] elements"
The <g-rid>
element let's you easily compose a responsive grid using CSS's Grid algorithm. Like <s-tack>
it lets you repeat the contents/children you supply. Useful for quickly mocking up a set of card components or similar.
gap |
A point on the modular scale ( Default: |
---|---|
repeat |
The number of times the content (light DOM children) will be repeated. Can be an integer e.g. Default: |
itemWidth |
The 'ideal' width of the grid item as a CSS length value e.g. Default: |
<g-rid repeat="10">
<b-ox border>
<h2>Card title</h2>
<t-ext words="25,50"></t-ext>
</b-ox>
</g-rid>
"Grid of [# of child elements] elements, each [itemWidth] wide"
The <c-luster>
component uses Flexbox to let you cluster items around the horizontal center of their context. Items wrap where there is no room for them all on one line, maintaining responsiveness.
gap |
The gap between clustered items. A point on the modular scale ( Default: |
---|---|
align |
Maps to the Flexbox Default: |
<c-luster align="bottom">
<div>Will Something on the left</div>
<div>Something in the center</div>
<div>something on the right</div>
</c-luster>
"Set of [# of child elements] centrally grouped elements"
The <s-pread>
component uses Flexbox to let you separate items horizontally. Items wrap where there is no room for them all on one line, maintaining responsiveness.
gap |
The minimum gap between spread-out items. A point on the modular scale ( Default: |
---|---|
spaces |
Where the spaces occur. One of Default: |
align |
Maps to the Flexbox Default: |
<s-pread gap="2">
<div>Will be pushed to the left</div>
<div>Will appear in the center</div>
<div>Will be pushed to the right</div>
</s-pread>
The <s-idebar>
component wraps two child elements, with one as the designated 'sidebar' and the other taking up the remaining space. If more than two child elements are supplied, construction ceases and an error is returned.
The designated sidebar only appears as a sidebar where it is narrower than its sibling. Otherwise, Flexbox reorganizes the two elements into a single column (uses the flex-grow: 999
hack).
gap |
The gap between the sidebar and adjacent element. A point on the modular scale ( Default: |
---|---|
width |
The width of the sidebar in contexts wide enough that it can be a sidebar. Any CSS length, e.g. Default: |
to |
Which child element (left or right) to be the sidebar. Values can only be 'left' or 'right'; If anything different is supplied, 'left' is used by default. Default: |
Creates a sidebar of the second element (introduced with the <h2>
heading). The gap between the sidebar and the adjacent content matches the second point on the modular scale (one point higher than the default).
<s-idebar to="right" gap="2">
<div>
<h1>The main content</h1>
<!-- etc -->
</div>
<div>
<h2>The sidebar content</h2>
<!-- etc -->
</div>
</s-idebar>
"Content with a [left || right] sidebar"
The <l-ine>
component configures child elements inline, with the option to add a separator. The separator can be any character or HTML.
between |
Some HTML to separate each item e.g. |
---|---|
gap |
A point on the modular scale ( Default: |
justify |
Maps to the Flexbox Default: |
<l-ine gap="2">
<a href="#index">Home</a>
<a href="#about">About</a>
<a href="#faq">FAQ</a>
</l-ine>
"Line of [# of child elements] elements"
The <i-con>
component inserts an inline SVG icon, by name, from the icons folder (using fetch). The size of the icon is determined by its parent's font-size
, or you can use the u-h1
— u-h6
utility classes on the <i-con>
itself.
name (required) |
The name of the icon, matching the filename (without extension) from the icons folder. |
---|---|
label |
A string to describe the SVG as an image. Not necessary if the icon has accompanying, descriptive text (for example, alongside a Default: omitted (no label or image role) |
<i-con name="tick" label="Correct!"></i-con>
None by default (it is assumed the icon will be accompanied by text). Or you can supply a label via the label
prop. This will also include the img
ARIA role.
The <p-rogress>
component displays a series of steps in a continuum to indicate how far along the user is in a process.
steps (required) |
A comma-separated set of labels for he consecutive steps, e.g. `steps="one,two,three"`. |
---|---|
current |
The label of the current step, which takes Default: the first step (whatever it is named) |
<p-rogress steps="address,payment details,confirmation" current="confirmation"></p-rogress>
"Progress indicator of [# of steps] steps" (in addition to list semantics and aria-current
).
The <c-enter>
component simply creates a centralized column (with horizontal margins set to auto
). The max-width
is set to the --measure
custom property value (under css/lib/variables.css) by default.
maxWidth |
A CSS Default: the root |
---|
<c-enter>
<s-tack gap="2">
<h2>A centrally aligned document section</h2>
<s-tack repeat="5">
<t-ext></t-ext>
</s-tack>
</s-tack>
</c-enter>
NA
A basic collapsible section, as you might find in an accordion. The first child (light DOM) element is use to form a button. This is wrapped in a heading, for which the level can be adjusted using the level
prop.
level |
An integer between 1 Default: |
---|---|
open |
An integer between 1 |
<d-rawer>
<any-element>Collapsible section title</any-element>
<p>This content appears when the handle is clicked...</p>
<p>...and so does this content.</p>
</d-rawer>
"Collapsible section"
The <s-creen>
element lets you define whole screens (like those defined within a routed Single Page Application). You can link between screens using either the <g-o>
call-to-action component, or regular links pointing to hash fragments that match the screens' id
s.
The <s-creen>
with the index
id
is displayed on page load.
id (required) |
A valid |
---|---|
label |
A string describing the screen (think Default: the screen's |
<s-creen id="index">
<h1>The default screen</h1>
<g-o to="about">About</g-o>
</s-creen>
<s-creen id="about">
<h1>The about screen</h1>
<a href="#index">Home</a>
</s-creen>
The value of the label
prop (or the id
value as a fallback).
A call-to-action type component, specifically for linking between <s-creen>
elements.
to (required) |
A valid |
---|
<g-o to="index">Home</g-o>
The text content of the link.
The <f-low>
component lets you group elements as a navigable sequence of steps, such as sections in a form. On hover and focus, previous and next buttons are exposed to change steps.
hideControls |
Boolean. Whether to show the previous and next buttons. Default: |
---|
prev() |
Show the previous element |
---|---|
next() |
Show the next element |
goTo(index) |
Show the element with the supplied index |
<f-low>
<s-tack>
<h2>Step 1</h2>
<t-ext></t-ext>
</s-tack>
<s-tack>
<h2>Step 2</h2>
<t-ext></t-ext>
</s-tack>
<s-tack>
<h2>Step 3</h2>
<t-ext></t-ext>
</s-tack>
</f-low>
"Sequence of [# of child elements] steps"
Allows you to write a comment for any part of the interface, simply by wrapping it and supplying text for the wording
prop. The comment is revealed by pressing a '?' button that is revealed on both hover and focus.
wording |
A basic string e.g. Default: 'TBD' |
---|
<c-omment wording="A set of card components">
<g-rid repeat="10">
<div>
<h2>Card title</h2>
<t-ext words="25,50"></t-ext>
</div>
</g-rid>
</c-omment>
The comment wrapper is identified as a region. The buttons to open and close the comment are suitably labeled.
The <c-lone>
lets you instantiate content from a named <template>
. It's the easiest way to reuse compound components.
of (required) |
The |
---|
<template id="image-and-text">
<i-mage maxWidth="20rem" ratio="6:9"></i-mage>
<t-ext words="20,40"></t-ext>
</template>
<c-lone of="image-and-text"></c-lone>
NA (whatever the cloned elements provide)
The <i-nput>
component is an abstraction of a basic type="text"
input/label, with accessible labeling built in.
label |
A string for the `` element e.g. Default: |
---|---|
name (required) |
A valid |
value |
Optionally prepopulate a value, e.g. |
<i-nput name="favoriteAnimal" label="Your favorite animal"></i-nput>
Standard <label>
and <input>
elements are used (and associated with one another) and convey the standard/expected semantics to assistive technologies.
The <t-extarea>
component is an abstraction of a basic <textarea>
with accessible labeling built in.
label |
A string for the `` element e.g. Default: |
---|---|
name (required) |
A valid |
value |
Optionally prepopulate the |
<t-extarea name="favoriteAnimal" label="Tell us about your favorite animal"></t-extarea>
Standard <label>
and <textarea>
elements are used (and associated with one another) and convey the standard/expected semantics to assistive technologies.
The <s-elect>
component is an abstraction of a basic <select>
/<option>
s pattern, with accessible labeling built in.
label |
A string for the `` element e.g. Default: |
---|---|
name (required) |
A valid |
options (required) |
A comma-separated list of available options, e.g. Note: Both |
selected |
The string that represents the selected option (if any), e.g. Default: No item is selected |
<s-elect label="Your favorite animal" name="favoriteAnimal" options="dog, cat, fish, aardvark" selected="aardvark"></s-elect>
Standard <label>
and <select>
elements are used (and associated with one another) and convey the standard/expected semantics to assistive technologies.
The <c-heckbox>
component is an abstraction of a basic type="checkbox"
input/label, with accessible labeling built in.
label |
A string for the `` element, e.g. Default: |
---|---|
name (required) |
A valid |
checked |
Boolean: whether the checkbox is checked by default |
<c-heckbox name="likesAnimals" label="Do you like animals?" checked></c-heckbox>
Standard <label>
and <input>
elements are used (and associated with one another) and convey the standard/expected semantics to assistive technologies.
The <r-adios>
component is an abstraction of a basic radio group pattern (using <fieldset>
and <legend>
for the group labeling).
legend |
A string for the `` element e.g. Default: |
---|---|
name (required) |
A valid |
options (required) |
A comma-separated list of available options, e.g. Note: Both |
checked |
The string that represents the checked option (if any), e.g. Default: No item is checked |
<r-adios label="Your favorite animal" name="favoriteAnimal" options="dog, cat, fish, aardvark" checked="aardvark"></r-adios>
Standard <fieldset>
, <legend>
, <label>
, <input>
and <select>
elements are used (and associated with one another) and convey the standard/expected semantics to assistive technologies.
A set of utility classes for global styling. Each is prefixed with u-
.
With class="u-invert"
a CSS filter inverts the colors of the element in hand.
For realigning text.
For emulating the font sizes of semantic headings without introducing heading elements (mapped to h1—h6 sizes).
Rounds the edges of an element with a 50%
border radius (making square elements circles).
Sometimes you'll want to work with dummy text (like <w-ords>
and <t-ext>
). Other times, you'll want to add real content using basic elements like <h2>
and <p>
. But you can also work with data.
This file defines a global data object and is found in the root of the /js folder. You can add properties to the data object as you wish. For example, I might add a people
property defining an array of usernames:
// in js/data.js
export default {
people: [
{
firstName: 'Lara',
lastName: 'Hogan'
},
{
firstName: 'Hulk',
lastName: 'Hogan'
},
{
firstName: 'Paul',
lastName: 'Hogan'
},
{
firstName: 'Heydon',
lastName: 'Pickering'
}
]
}
Using the <o-utput>
component, you can template the data. Note that this
refers to the property identified using the mandatory property
attribute (property="people"
here).
<o-utput property="people">
<b-ox>
<g-rid>
{{% for (let name in this) { %}}
<s-tack>
<i-mage ratio="5:9"></i-mage>
<h3 class="u-text-center">{{% this[name].lastName %}}, {{% this[name].firstName %}}</h3>
{{% this[name].firstName === 'Heydon' ? '(me!)' : '' %}}
</s-tack>
{{% } %}}
</g-rid>
</b-ox>
</o-utput>
There you have it: a set of user 'cards' laid out in a <g-rid>
.
The <m-odel>
component wraps a form and lets you change or add data to the global data object. It too takes a property
attribute (i.e. it can only affect one property at a time, and only works to one level of depth).
<m-odel property="people">
<form>
<s-tack>
<i-nput label="First name" name="firstName"></i-nput>
<i-nput label="Last name" name="lastName"></i-nput>
<button type="submit">Submit</button>
</s-tack>
</form>
</m-odel>
When the submit button is pressed, a FormData
object of values is created based on the form fields' name
attributes. In this case, the object would be:
{
firstName: /* value from `name="firstName"` */,
lastName: /* value from `name="lastName"` */
}
If the window.data.people
property's value is an object, it will be overridden with the new data. If it is an array, the data will be pushed as a new item (as in the people
example). If the <m-odel>
contains just one field, the FormData
object will be flattened into a simple string of that single value. That is:
// This would be avoided...
people: [
{
person: 'Heydon Pickering'
}
]
// ...in preference of this:
people: [
'Heydon Pickering'
]
(Note: In this scenario, the field's name
attribute would actually be immaterial because the first and only property of the FormData
object is taken, whatever it is called.)
Whenever a <m-odel>
changes the data, a 'stored'
event is fired, alerting any <o-utput>
elements of the new data so they can rerender accordingly. This event also fires on page load, rendering out the original data.
Be careful not to allow the use of <m-odel>
or the creation of <o-utput>
on domains that expose personal/sensitive data. You may invite XSS.
A set of global functions to be used with inline handlers (onClick
etc). It is recommended these are used primarily on <button>
elements, since they are accessible. Each action is prefixed with the actions
namespace, like onClick="actions.nameOfAction(arg1, arg2)"
.
Shows an element by its id
(removes the hidden
property).
Shows the element with the id
'myElem':
<button onClick="action.show('myElem')">SHow the element</button>
Hides an element by its id
(adds the hidden
property).
Hides the element with the id
'myElem':
<button onClick="action.hide('myElem')">SHow the element</button>
Toggles the visibility of an element by its id
(toggles the hidden
property).
Toggles the visibility of the element with the id
'myElem':
<button onClick="action.showHide('myElem')">SHow the element</button>
Shows the previous element/slide/step within a <f-low>
component. Must be called from inside the <f-low>
component, with this
as the only argument.
<button onClick="action.flowPrev(this)">Previous</button>
Shows the next element/slide/step within a <f-low>
component. Must be called from inside the <f-low>
component, with this
as the only argument.
<button onClick="action.flowNext(this)">Previous</button>
Shows the next element/slide/step within a <f-low>
component. Must be called from inside the <f-low>
component, with this
as the first argument. The second argument is the index of the element/slide/step (not zero-based; the first item is 1
).
Shows the third element/slide/step within the <f-low>
.
<button onClick="action.flowGoTo(this, 3)">Previous</button>