Building our first screen

Layers, text, images and input


If you just want to copy-paste and run, I put the entire code at the bottom. You can then continue with adding flow.


Let's start building a screen to fill our PageComponent with! Since one of the main advantages of building a coded prototype is that you're able to include real data, store it over time and use it to personalize a user's experience, it makes sense to implement a way to identify who this user actually is. Therefore, we're gonna have to start with one of the more boring types of screens to make: a login screen (yay)! To keep our code clean, let's create a separate file for each screen we will have, as well as for the PageComponent itself. In Atom, right click on the src folder in your project and select New File. As a filename, you can put src/pagecomponent.js.

Creating a new file in Atom

Let's take the code we had in index.js before, cut it and paste it into our new pagecomponent.js file:

var pages = new PageComponent({
  backgroundColor: "rgb(255, 0, 0)",
  width: Screen.width,
  height: Screen.height,
  scrollHorizontal: false,
  scrollVertical: false
});

export default pages;

We now have all the functionality related to our PageComponent combined in one file. Note the last line we added: export default pages, this ensures that the variable pages is made available to other files that want to use it. Let's create another new file: right click again on the src folder and select New file, name this one login.js. In this file, we can use the PageComponent we've created by importing it like so:

import pages from './pagecomponent.js';

We can now use the properties and functions of our pages variable, which is the PageComponent object we created. Let's continue with building our first screen!

Much like your favorite design tool, FramerJS works with Layer objects. They are your primary tool to create layouts, since they can take on all kinds of shapes and colors, and can contain other objects (such as other layers). Like the PageComponent, we can create layers with all kinds of properties, like so:

import pages from './pagecomponent.js';

// The page itself
var login_page = new Layer({
  width: pages.width,
  height: pages.height,
  backgroundColor: "rgb(255, 255, 255)",
  parent: pages.content
});

export default login_page;

This should look familiar, we are setting properties such as the width and height of the Layer object, and its background color (in this case, white). What's new here is that we are basing these properties for the layer from existing properties of our PageComponent: its width and height, to ensure that our screen fills the entire display of the device. In other words, this layer is shown in fullscreen. One more new thing is the last line, where we set this Layer's parent to the property content of the PageComponent; by doing so, we add this layer (screen) to the collection of pages (screens) that is managed by the PageComponent. This will allow us later on to transition between pages or screens.

Our index.js, the main starting point of our prototype, should now be empty since we moved all its contents into pagecomponent.js. What we want to happen upon starting the prototype is that the PageComponent is loaded, with the login page visible inside of it. To achieve this, all we have to do is import the login screen, which will in turn import the PageComponent automatically, from the index.js file:

import login_page from './login.js';

After saving this file, the prototype should refresh and you will be presented with an empty white screen!

Let's add some more components to our new screen, underneath the login_page but keeping the export default login_page; line at the bottom. We're starting with another, nicely centered layer that will contain the elements for logging in: a username and password field and a button to perform the login action:

// The (central) container displaying the required fields
var login_container = new Layer({
  width: login_page.width * .8,
  height: 260,
  backgroundColor: "rgb(255, 255, 255)",
  borderWidth: 2,
  parent: login_page
});

This layer has a fixed height of 260 pixels, while its width is set to 80% of the entire login screen (and thus, since the login is shown fullscreen, 80% of the device's size). The login_page is the parent of this layer, since this area is contained within the login screen. A border of 2 pixels is added to make it stand out from the background. If you now save the file, the prototype will refresh and you should be able to see our newly created layer:

A login screen with one layer on top

Now we want to center this layer, regardless of the size of the device. This can be done by calling upon a function of the Layer object:

// Center it, both horizontally and vertically
login_container.center();

The container layer, centered

It does look a bit too wide on a normal-sized laptop screen, so let's add some code that limits the maximum width of this container to 300px:

// If our screen is very wide, we limit the size of our container to 300px
if (login_container.width > 300) {
  login_container.width = 300;
}

This piece of code is what is called a conditional statement, which means that anything that is put between the curly brackets is only executed if the condition in the parentheses above is satisfied. So in this case, if the width of the login_container (which is 80% of the screen's width) is larger than 300px, we set the width to 300px. What this means in practice is that on your big laptop screen the container will assume its maximum width of 300px (the if-statement will be evaluated as true, so the code within curly brackets is executed), while on your smaller mobile phone screen 80% of its screen size will actually be less than 300px, so it will assume that size instead (the if-statement is false, code is not executed). A bit of responsive design! If you've followed the instructions, your container might now be mis-aligned horizontally. That is because we first did login_container.center() (still with the old width) and only then resized the container, making it smaller. So to have it nicely centered, we need to flip the order of the two bits of code:

// If our screen is very wide, we limit the size of our container to 300px
if (login_container.width > 300) {
  login_container.width = 300;
}

// Center it, both horizontally and vertically
// Center after resizing, otherwise it uses the old width to calculate the center!
login_container.center();

Images and text

It's time to add some branding to our app by putting our logo at the top. Adding an image is actually very straight-forward, as it is just another Layer. First, find or create your amazing logo, or borrow the one I made specifically for this purpose (got enthousiastic after discovering my design program has a "Donut Tool"...):

Example of logo

This file needs to go into the /public/images/ folder within your project, so not in /src/. There should already be some images there. Back in our login.js file, I added it to the container like so:

// The amazing logo
var login_logo = new Layer({
  y: 0,
  width: 200,
  height: 80,
  image: "images/sprinkles.svg",
  parent: login_container
});

You might have to play around with the width and height in case you're using a different image. What's interesting to note here is that, because we add login_container as a parent, the positioning of this Layer becomes relative to that parent. So if we put its y-position as 0, we're actually putting it at the top within its parent. Let's also center this logo, but only horizontally:

// Center this one, but only horizontally
login_logo.centerX();

Now let's add some instructional text, indicating that people should provide us with their e-mail address in order to log in:

// Instruction to put e-mail address
var text_email = new TextLayer({
  x: 20,
  y: 102,
  color: "rgb(0, 0, 0)",
  fontSize: 14,
  fontFamily: "Arial",
  fontWeight: "bold",
  text: "Your e-mail:",
  parent: login_container
});

Not much new here, other than the fact that we set some properties that are specific to the text (fontSize, fontFamily and fontWeight) and of course the content of the text itself. In this case, the color property refers to the color of the text and not the background of the Layer. Moving on!

Input layers

A quirky thing about FramerJS is that it doesn't seem to include any input fields for us to use, where users can actually enter their details. Fortunately, Adria Jimenez was kind enough to create a plug-in that does exactly what we need. I took the liberty of making some changes to it, specifically I destroyed the on-screen keyboard (since we'll be working with either a laptop, or a device that has the on-screen keyboard built in) and I changed the way you can set a property (padding) so that it is more consistent with how you would do this on a normal Layer.

Start by downloading the module here, again by either using your Git client or by simply clicking on the "Clone or download" button and then on "Download ZIP". From this zip-file, we only need the file input.coffee. Create a new folder in your project, under src, and call it modules. Add the input.coffee file there, and your project should look something like this:

Project overview after adding your first module

Before we can use our newly acquired module, we need to first include it in a similar way to how we included our PageComponent, although using a slightly different notation because the module is written in CoffeeScript. All the way at the top of your login.js file, put this:

var InputModule = require("./modules/input.coffee");

Now we're good to go! After your text_email field, add our e-mail input field:

// Field to enter e-mail address
var input_email = new InputModule.Input({
  x: 140,
  y: 95,
  width: login_container.width * .4,
  height: 30,
  padding: {
    left: 10
  },
  borderWidth: 1,
  fontSize: 14,
  fontFamily: "Arial",
  placeholder: "test@test.com",
  placeholderColor: "rgb(200, 200, 200)",
  type: "email",
  parent: login_container
});

Note that instead of a Layer we're now creating an Input object, from the InputModule we just downloaded and included. However, it behaves much like a Layer in that we provide it with the properties we need and it will automatically show up on our screen:

Our screen with a piece of text and an input text area

The property padding adds some extra space within the layer, in between the edge of the layer and its contents (so in this case, area around the text). You can supply it with a simple number, so for example if I would put padding: 10 it would add 10 pixels of padding on all sides. But, as shown in the example above, you can also specify padding only on one or some sides by providing a dictionary. Sides that are not mentioned in the dictionary, so in our case all but the left side, are then automatically set to 0px. An extra feature we're using, specific to the Input object, is the placeholder: temporary text that can be used as a hint, in this case "test@test.com", that will be shown until the user clicks on the text area and starts to type something. Finally, by setting the type to email, we suggest mobile devices and tablets to use a different on-screen keyboard than the regular one: the @-sign will be easily accessible and the first letter you type will be lowercase by default, as you will find out when we start testing on those devices!

Okay, we're almost there. Let's add another TextLayer and Input for our password:

// Instruction to put password
var text_password = new TextLayer({
  x: 20,
  y: 147,
  fontSize: 14,
  fontFamily: "Arial",
  fontWeight: "bold",
  color: "rgb(0, 0, 0)",
  text: "Your password:",
  parent: login_container
});

// Field to enter password
var input_password = new InputModule.Input({
  x: 140,
  y: 140,
  width: login_container.width * .4,
  height: 30,
  borderWidth: 1,
  padding: {
    left: 10
  },
  fontSize: 14,
  fontFamily: "Arial",
  placeholder: "",
  type: "password",
  parent: login_container
});

Looking great! Note how we set the type of this Input to "password" to make sure characters that are typed don't show up in the input field. Now let's add a button to actually be able to log in:

// The login button
var button_login = new TextLayer({
  x: 140,
  y: 190,
  width: login_container.width * 0.4,
  height: 50,
  padding: {
    top: 16
  },
  backgroundColor: "rgb(65, 114, 193)",
  color: "rgb(255, 255, 255)",
  text: "Login",
  fontFamily: "Arial",
  fontSize: 14,
  fontWeight: "bold",
  textAlign: "center",
  parent: login_container
});

This button is simply another TextLayer, but this time with a background color to make it stand out and look like an actual button. Another small addition is the property textAlign which aligns the text horizontally within this TextLayer. Finally, we need a way for new users to create an account, so let's add a small link for that (in the form of a simple TextLayer) to the bottom of our container, centered horizontally:

// Link to create a new account
var create_account_text = new TextLayer({
  y: login_container.y + login_container.height + 10,
  width: 300,
  color: "rgb(15, 108, 200)",    
  fontFamily: "Arial",
  fontSize: 14,
  color: "rgb(65, 114, 193)",
  text: "No account? Click here to register!",
  textAlign: "center",
  parent: login_page
});

create_account_text.centerX();

The login screen finished!

We'll add functionality to the login button and the link to create an account later on. That's all, our first screen is done! If it all went well, and your screen looks like the example above, let's start adding flow to the prototype.

I'll list the contents of my entire files below:

index.js

import login_page from './login.js';

pagecomponent.js

var pages = new PageComponent({
  backgroundColor: "rgb(255, 0, 0)",
  width: Screen.width,
  height: Screen.height,
  scrollHorizontal: false,
  scrollVertical: false
});

export default pages;

login.js

var InputModule = require("./modules/input.coffee");

import pages from './pagecomponent.js';

// The page itself
var login_page = new Layer({
  width: pages.width,
  height: pages.height,
  backgroundColor: "rgb(255, 255, 255)",
  parent: pages.content
});

// The (central) container displaying the required fields
var login_container = new Layer({
  width: login_page.width * .8,
  height: 260,
  backgroundColor: "rgb(255, 255, 255)",
  borderWidth: 2,
  parent: login_page
});

// If our screen is very wide, we limit the size of our container to 300px
if (login_container.width > 300) {
  login_container.width = 300;
}

// Center it, both horizontally and vertically
// Center after resizing, otherwise it uses the old width to calculate the center!
login_container.center();

// The amazing logo
var login_logo = new Layer({
  y: 0,
  width: 200,
  height: 80,
  image: "images/sprinkles.svg",
  parent: login_container
});

// Center this one, but only horizontally
login_logo.centerX();

// Instruction to put e-mail address
var text_email = new TextLayer({
  x: 20,
  y: 102,
  color: "rgb(0, 0, 0)",
  fontSize: 14,
  fontFamily: "Arial",
  fontWeight: "bold",
  text: "Your e-mail:",
  parent: login_container
});

// Field to enter e-mail address
var input_email = new InputModule.Input({
  x: 140,
  y: 95,
  width: login_container.width * .4,
  height: 30,
  padding: {
    left: 10
  },
  borderWidth: 1,
  fontSize: 14,
  fontFamily: "Arial",
  placeholder: "test@test.com",
  placeholderColor: "rgb(200, 200, 200)",
  type: "email",
  parent: login_container
});

// Instruction to put password
var text_password = new TextLayer({
  x: 20,
  y: 147,
  fontSize: 14,
  fontFamily: "Arial",
  fontWeight: "bold",
  color: "rgb(0, 0, 0)",
  text: "Your password:",
  parent: login_container
});

// Field to enter password
var input_password = new InputModule.Input({
  x: 140,
  y: 140,
  width: login_container.width * .4,
  height: 30,
  borderWidth: 1,
  padding: {
    left: 10
  },
  fontSize: 14,
  fontFamily: "Arial",
  placeholder: "",
  type: "password",
  parent: login_container
});

// The login button
var button_login = new TextLayer({
  x: 140,
  y: 190,
  width: login_container.width * 0.4,
  height: 50,
  padding: {
    top: 16
  },
  backgroundColor: "rgb(65, 114, 193)",
  color: "rgb(255, 255, 255)",
  text: "Login",
  fontFamily: "Arial",
  fontSize: 14,
  fontWeight: "bold",
  textAlign: "center",
  parent: login_container
});

// Link to create a new account
var create_account_text = new TextLayer({
  y: login_container.y + login_container.height + 10,
  width: 300,
  color: "rgb(15, 108, 200)",    
  fontFamily: "Arial",
  fontSize: 14,
  color: "rgb(65, 114, 193)",
  text: "No account? Click here to register!",
  textAlign: "center",
  parent: login_page
});

create_account_text.centerX();

export default login_page;




Add a comment