5 Style
For many experiments, styling is not very important, for example, if you just want to show some words or images on the screen and measure reaction times. In other cases, however, having exactly the right color of a text may be crucial. Or you may really need borders around blocks, or show a list of items in a specific format, or use text that is outlined to the right rather than centered, and so on. In this chapter, we will tell how to accomplish this. Our approach is based on the styling framework called Cascading Style Sheets or CSS, which is the standard on the Internet. The advanced NeuroTask Scripting manual gives a little more background on this. For most experiments, you only need to know the names of the styles you wish to change, such as ‘color’ and ‘font-size’, and what values are allowed. These will be discussed in this chapter. Nearly all styling can be done with a single function, aptly called style().
5.1 Style with style()
Suppose, we would like to have a block in the right-bottom corner with a small trial counter in it that says for example ‘7/15’, meaning this is trial 7 of a total of 15 trials. In order to achieve this, we would do something like:
var b = addblock("right","bottom",10,10);
b.text("7/15");
Now, suppose furthermore you want it to be less distracting by making the font a little smaller and the font color a bit lighter. How would you do it? This is easily accomplished with the style() function, which allows you to change fonts, colors, borders, and many other style properties (often simply called ‘styles’). In this particular case you might do this:
var b = main.addblock("right","bottom",10,10);
b.text("7/15");
b.style("color","grey");
b.style("font-size","80%");
The style() function takes a style property name as the first argument and its value as the second argument.
Chaining of block function calls
The example above may also be written in a short-hand form that many people find easier, namely:
addblock("right","bottom",10,10)
.text("7/15")
.style("color","grey")
.style("font-size","80%");
This is allowed because addblock() returns a Block object, and text() and style() also return this same object. Thus when we have b.text("Hello"), where b is a block, this function call will itself return b, so we could do this:
b = b.text("Hello");
b.style("color","red");
This can also be written as
b.text("Hello").style("color","red");
or, with a different spacing, as
b.text("Hello")
.style("color","red");
Remember that white space is irrelevant in JavaScript, except within strings. Especially with style() function calls, chaining allows more compact code. Many Block functions return a Block object, with some exceptions, like setimage(), setvideo(), and a few others.
5.2 Setting font size of an entire Box
Styles also work on a Box, such as ‘main’. However, if you try to set the font-size style of a Box, it works but upon a window resize by the subject your font-size will be lost. This is because the system is constantly recalculating the font size. To solve this, you can do the following:
main.setfontsize(75);
This sets the font size of the (default) Box ‘main’ to 75% percent, a setting that will be retained on window resizing.
5.3 Queries with tag name, class and id
While you do not really need to use HTML markup in your experiments, it may come in handy from time to time. If you are going to use it, it is good to know that the style() function is powerful enough to act as a style rule for all the HTML code in a block or box.
1 var s = "<div><h2>Instruction</h2><p>Welcome to this experiment.</p></div>",
2 b = main.addblock().text(s);
This assigns a string to s with HTML code that contains a <h2> header and a paragraph, which is wrapped in <p>...</p> tags. This code in turn is put inside the newly formed block using text(). The header will be shown as large and bold in the browser. It is browser-dependent exactly how this is done (i.e., how large and how bold).
Now suppose that you want to turn the header blue rather than have it remain black. How can this be achieved? This can be done by adding the tag name, in this case ‘h2’ (without the <..>) as a third argument:
1 var s = "<div><h2>Instruction</h2><p>Welcome to this experiment.</p></div>",
2 b = main.addblock().text(s);
3
4 b.style("color","blue","h2");
This will color all <h2> headers in block b blue.
It is also possible to call the style() function on a Box object, like main:
main.style("color","blue","h2");
In this example, you would not notice a difference, but if you had several blocks within main with headers, all of these would be color blue. Blocks in other Box objects, however, would not be styled.
It is also possible to use classes in queries. For example, in case you want to create a Stroop task where color words are shown in different colors (not congruent with the color they mean), we could write:
var s = "<div class='r'>blue</div>"
+ "<div class='g'>black</div>"
+ "<div class='b'>green</div>",
b = main.addblock().text(s);
b.style("color","red",".r")
.style("color","green",".g")
.style("color","blue",".b");
Here, “.r” refers to the class “r”, which was used in the HTML code fragment. You can assign arbitrary classes in HTML and then use the style() function to fine-tune their appearance. The advanced manual has more information on this.
5.4 Color
So far, we have specified color either with color names, like ‘lightgrey’ and ‘red’. In this section, we will explain more about colors and how to specify them with NeuroTask Scripting.
Named colors
There is a surprisingly long list of color names that are officially recognized for usage on the web. They do not just include ‘red’, ‘green’, and ‘blue’, but also ‘crimson’, ‘lime,’ and ‘cadetblue’. And what about ‘slateblue’, ‘lightgoldenrodyellow’ and ‘papayawhip’? You can use all these color names in NeuroTask Scripting styles (just check the Quick Reference side panel when you are editing a script; all legal color names are listed there with a color sample). Despite the inspiring list of named colors, there are many circumstances where you need yet more shades of color. It takes a bit of getting used to, but specifying colors on the Internet is not too difficult. The two main notations for shades of color are RGB and Hex (or hexadecimal).
RGB and Hex
Each color you see on the screen is composed of red, green, and blue. On another list of color names on the web, we can see the relative portions of each of these for all named colors in two often-used ways.
The first method is called RGB, for Red, Green, Blue. Each color ‘channel’ is specified with a number from 0 to 255. This is the number of levels that fit into one byte, so a color value can be represented by three bytes. Suppose, we want to make the level-2 headings slateblue. There is a handy online color converter where you can type in various color names (and codes) to convert them. For slateblue we get rgb(106, 90, 205). We use this as follows:
b.style("color","rgb(106,90,205)");
(Spaces within rgb() specifications are not necessary, though they are allowed.) In this example, and all others below in this chapter, b refers to a block, e.g., one that displays some text.
Many people (read: programmers) find this way of specifying too cumbersome and they have come up with a shorter format, called hexadecimal or Hex for short. We will briefly explain it here, because even if you don’t want to work with it, you may still encounter these color codes frequently. Slateblue in Hex is #6a5acd, which can be entered like this:
b.style("color","#6a5acd");
The # symbol signals that this is a color, which consists of values red = 6a, green = 5a, and blue is cd. Rather than the decimal system, the hexadecimal system is used which does not run from 0 to 9 but from 0 to 15 (and thus has 16 levels). Instead of 10, 11, …, 15, hexadecimal notation uses a, b, …, f. In hexadecimal, we don’t count to 9 but to f: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f. So what does 6a mean? We must multiply 6 not with 10 but with 16 and than add a (a = 10). This gives 6×16+10=106, which is the value we entered above. Similarly, 5a is 5×16 plus a (= 10), which gives 90. And cd is c (= 12) times 16 (= 192) plus d (=13), which gives 205.
Perhaps, the easiest way to specify colors is using RGB with percentages. The example may then be specified as:
b.style("color","rgb(42%,35%,80%)");
You may also use decimal values like 42.3%.
There are several other ways of specifying colors based on hue, saturation and lightness or value, which will not discuss here even though you may use them in the style() function. There is a good Wikipedia entry about them.
Opacity and transparency
Opacity is the opposite of transparency, so that high opacity means low transparency, in other words, a quite opaque object is not very see-through. In CSS, there is an ‘opacity’ style property that can be specified like this:
b.style("opacity",0.2);
Here, some block b is made “not very opaque” (i.e., 20% opaque = 80% transparent). If b is green and overlaps an underlying block c that is red, you would be able to see c below (or ‘through’) b. Legal values for ‘opacity’ are decimal values 0.0 to 1.0 (1.0 = completely non-transparent).
5.5 Fonts and text styles
Modern CSS styles allow many aspects of text appearance to be specified: font-type or font-family, size, color, weight, decoration, underline, strike-through, kerning, etc. This is great, except that many font types and other properties do not look very similar on different browsers. From the perspective of web design, this is often not a great problem, but for the psychophysics of reading and word recognition, this is unfortunately highly relevant. We, therefore, recommend to not ‘over-style’ your stimuli.
Font family
The text() function is optimized for the Arial font type, meaning that other font types may show up a different sizes on different browsers, while with Arial we actively try to compensate for this. We may include such support for other fonts in the future.
Because not all browsers support all fonts, CSS allows specification of the font you really want and then a few fall-backs in case the font is not available on the computer. For example:
b.style("font-family","Arial, Helvetica, sans-serif");
This says: “I prefer Arial but if not available use Helvetica, and if even that is missing use the default sans-serif font on the computer”. Arial is an improved version of Helvetica, so the two fonts will be highly similar. For a more classic look you may prefer a serif font:
b.style("font-family","'Times New Roman', Georgia, serif");
The quotes around ‘Times New Roman’ are necessary here. As argued above, unless your stimuli absolutely require this, only use non-Arial fonts in instructions and other parts of your experiment where psychophysical aspects are less important.
Font size and other text style properties
Font weight: bold and bolder
To make text in a block bold, you can use the font-weight style property. For example, to make all text in a block b bold we would use this:
b.style("font-weight","bold");
Other legal values instead of bold are: normal (useful to remove bold), lighter, bolder, and the values 100, 200, 300, …, 900, where 400 is normal and 700 is bold. This makes 900 extra bold and 100 quite light (little ‘ink’ will be used).
If you only want part of a sentence to be bold, you may surround it with <b>...</b> tags, like so:
b.text("Your participation is <b>much appreciated</b>!");
Font style: italic
Setting the font style to italic means you are setting it to, well, italic:
b.style("font-style","italic");
Other legal values are normal (useful for removing italic) and oblique, which is rarely used.
The corresponding tag is <i>...</i>.
Text decoration: underline and line-through
Underlining may occasionally come in handy and can be achieved either with <u>...</u> tag or with the text-decoration property:
b.style("text-decoration","underline");
Other useful values are overline (a line above the words) and line-through (a line striking through the words).
Font size and line-height
The font-size property sets the size of the letters in a text. We highly recommend only specifying font size in percentages, because NeuroTask normalizes the font sizes such that they are near-equal for 100% sized Arial normal text. This implies that text at other percentages scales proportionally, so that the relative size of text, images and other display elements stays the same at all screen sizes.
Increasing the size of the text is done as follows:
b.style("font-size","150%");
Specifying size in percentages is also built into the text() function, as in text("Hello",150), which displays text at 150% text size.
There are several other unit types available in CSS to express font sizes, such as em, pt, px, but apart from em they do not scale well and may show great differences between browsers. You may use px (pixels), if you are absolutely sure on what particular screen size your subjects will do their experiment and if it is important to know the font size in pixels.
If you find that the lines are too close together you may change the line-height CSS property. This is one of the few CSS properties where it is better not to use units. You could use 1 for normal line-height and something like 1.5 for wider spacing (more white between lines), e.g.,
b.style("line-height","1.5");
Text align: left, right, or justify
For technical reasons that are explained in some detail in the advanced manual the style() function is not very suitable to align text. Because non-centered text alignment is quite frequent in experiments (in instructions, debriefing, etc.), we have defined a convenience function called align(), which should be used instead of the style function (which will not work as expected):
b.align("left");
Other legal values are centered (default setting), right, and justified. The latter fills out the text evenly, aiming to create straight margins left and right by adding extra space between words.
Top, left, width, height, and getshape()
To get or set the exact size of a block is trickier than you might think because there are several coordinate systems active on a web page and of course there are the usual cross-browser issues. But because in NeuroTask Scripting sizes and locations are expressed strictly in percentages we avoid most of these problems. To find out the size and location of a block, you can use the getshape() function.
In most cases, however, the shape properties left, top, width, and height (there are no other shape properties) can be obtained reliably from properties of a block, for example:
b.text("Width: " + b.width + "%, Height: " + b.height + "%");
This displays the width and height of a block, e.g., “Width: 90%, Height: 90%”.
You can also get all shape properties with a call to getshape():
var shape = b.getshape();
b.text("Width: " + shape.width + "%, Height: " + shape.height + "%");
This gives the same result as above.
The main reason for using getshape() over direct access of the shape properties on the block (i.e., with b.width), is the fact that getshape() optionally recalculates these properties. The shape properties do not normally change, even if the users resizes the window or screen orientation: the relative coordinates (in percentages) stay the same. If for some reason you know that a block’s shape properties are not synchronized anymore, call getshape(true), where true indicates that it must first recalculate and refresh (i.e., synchronize) the shape properties. After that, direct access of say width or height will give reliable values again.
Blocks also have a setshape() function, which takes the same arguments as the first four of the addblock() function. Calls to setshape() will also keep the shape properties on the Block object in sync.
5.6 Borders
Boxes, blocks and many HTML elements can be given a border using style. CSS offers quite a number of ways of specifying these. A border around an entire block b can be set like this:
b.style("border","solid thin black");
The order of solid, thin and black is unimportant. Other values for the border-style instead of solid are, for example, dotted and dashed, and there are several others.
In addition to thin, the border-width property can take values like thick, medium or a number with a unit, such as “0.5em”. The latter specification is more reliable if you want a fairly thick border, as there is no standard definition for what ‘thick’ is. Using pixels (e.g., “5px”) is a possibility but then the perceived width will depend on screen resolution: on an high-resolution display (e.g., Retina or 4K screen), there are more pixels per inch and the line will look thinner than on a low-resolution display.
For example to get a fairly thick line of 0.5em, which is red and dashed, you might do:
b.style("border","red dashed 0.5em");
If you want a really thin line, use thin or “1px”. Using something like “0.1em” may cause the calculated size to be less than one pixel (e.g., with a small font size we might have 0.1em = 0.8px) and some browsers (i.e., Chrome) then rounds this down to zero and no border will be shown. This is one of the few exceptions where it is better to use the pixel (px) unit rather than percentages or em units.
For border color you can use any of the methods defined above, in the section on color.
It is also possible change border-style, border-width, and border-color separately, or even to give each side of a block its own style. Such styling details are beyond the scope of this book, though the style() function will happily apply them. You can find more information on borders at cssportal.com, html.net, and w3schools.com.
5.7 Padding
If you find that the text in a block comes too close to the borders, you may add some padding with the padding property:
b.style("padding","2em");
This will put a space of twice the letter m between the text and the border of the block. You can set the left, top, right, and bottom padding sizes separately with padding-left, padding-top, padding-right, and padding-bottom. Other units than em, such as percentages, are allowed as well, as explained at cssportal.com. We strongly advise to only use either em or percentages in order ensure correct scaling.
It is also possible to specify a margin, which is space on the outside of the border of box, block or HTML element (like a div). Because boxes and blocks are positioned absolutely, the margin property is less useful and changing it may in fact interfere with certain centering settings. If you want to apply the margin property to text inside a block, consult an online resource like the cssportal.com. You can still use the style() function for this, as it will accept any legal CSS style property.
5.8 Preset functions versus block functions
So far, we have encountered several functions that are not tied to a block, like text() and setimage(). These functions are included to make it easier for beginners to start programming, and for expert users to save some code writing. Though they do not appear to be tied to a particular block, this is in fact not the case. At the start of a script, a default preset Box called main is created, which is square and white. In that box is a single white (transparent) block that is also square with a width and height of 90%. This block can be accessed as main.centerblock. Calls to the stand-alone text() function in fact are translated into using main.centerblock.text() with a little bit of additional styling of the font size. The code for the text() function is something like:
function text(s,size)
{
if (size === undefined)
{
size = 100;
}
main.tofront();
return main.centerblock.style("fontSize",""+ size/100 + "em").text(s);
}
Here s is the text string and size is text size in percentages. It is allowed to call this function without a second argument, like text("Thank you!"). In that case the size argument will have the value undefined. To make sure the default value of 100 is assigned to size, we check for this case and if necessary assign the default value 100.
The line main.tofront(); moves the main box all the way on top under the assumption that if you are going to display text, you will also want to see it. If there are other boxes on top of some box, its text may not be visible. But the front box is always completely visible. There is also a toback() and a tooriginal() function, which returns it to its original order.
The same effects can also be obtained by manipulating a box’s z-index CSS property, e.g.,
main.style("z-index","999");
The z-index is an imaginary axis sticking out of the web page. The higher it is, the more ‘in front’ a box will appear. This property can be used for boxes and blocks but does not work for blocks that are in different boxes: If Box A is in front of Box B, no block in B can be in front of a block in A.