Friday, 28 February 2020

Where Does SEO Belong In Your Web Design Process?

Where Does SEO Belong In Your Web Design Process?

Suzanne Scacca

We all have a role to play in the building of a website. Designers create beautiful and interactive interfaces. Copywriters create messaging that gets visitors to take notice and action. Developers bring it all together with code.

But there’s one piece of the puzzle that can’t be handed off to just one person. And that’s SEO.

If you’re building websites for clients and they’re expecting impressive outcomes in the end (i.e. high conversion rates), SEO has to be part of your process. You can’t just leave it in the hands of your writer or a dedicated SEO and assume that’s enough.

Google is a demanding overlord and we must appease it if we want our websites to reach the top of search. And what that means is taking a well-rounded approach to SEO throughout the lifecycle of the website design and development process.

If you haven’t accounted for this already or you want to make sure you’ve covered all the bases in what you currently do, this post is for you. I’m going to run through when and how SEO needs to enter the picture in your workflow. In addition, I’ve included an SEO checklist at the bottom of this post that you and your team can adapt to your workflow.

Where Does SEO Belong in Your Web Design Process?

SEO should be something you’re always thinking about and planning for, from the prospecting phase all the way to the launch date.

SEO During the Proposal Phase

When you talk to a prospect about what they need your help for, they’re going to focus on the website itself.

“I need a website so I can sell my products online/grow my presence/get new customers.”

But that’s not really what they’re asking for. What they want is a website to help their business get found in Google and to convert visitors, which requires something more complex from you than just slapping together some copy and designs.

You know that the website needs to be optimized for search if the website is to do what the client needs it to. Because of this, it’s going to impact things like cost, timeline and process flow. All of these details should be mapped out on your end as you prepare the proposal.

As for what you actually tell your clients? This is where things get tricky.

You have to somehow address SEO with your prospect 1) to set the right expectations and 2) to justify what you’re about to propose to them in terms of scope and cost. The only problem is, some clients might know the term “SEO”, but they don’t really understand what it entails.

The conversation could quickly devolve into something like this:

You: [You explain the details of your design offering and mention that SEO is part of it.]

Client: “Oh, great. I have a list of 20 keywords I want included in every page.”

You: “Well, that’s not really what search engine optimization is. If you want your website to perform well, you need to pay attention to things like caching, security and mobile-first design.”

Client: “What are you talking about? Cashing? What even is that? I thought you said you do SEO. I want to rank for these 20 keywords.”

It’s your job to get clients the results they need, not to try and explain to them the technicalities of how you do it. That’s why they’re paying you to do this, after all. So, here’s what I’d recommend for your proposal and early discussions with clients:

First, let your website do all the talking about SEO like UPQODE’s does:

UPQODE SEO Services page - list of services
UPQODE briefly details the kinds of SEO services it provides. (Image source: UPQODE) (Large preview)

This would allow you to clearly spell out the kinds of SEO you provide (which will also help those SEO-minded clients find you in the first place). It’s okay to include technical terms here so long as you briefly explain what each is in layman’s terms:

UPQODE On-Page SEO explanation for clients
UPQODE explains what’s involved in on-page SEO. (Image source: UPQODE) (Large preview)

What I like about this approach is that it enables you to say “We’re going to do X, Y and Z” while framing it in a light that the client actually understands. In this case, what you’d really be saying is “We handle all the technical stuff so your site’s visibility grows in search and, consequently, you get more traffic.”

This is how you should be talking to your prospects and laying out SEO-related details in your proposals and contracts, too.

In other words, don’t delve too much into your SEO tasks. Instead, just spell out the benefits. For example:

  • Responsive web design that looks great on all devices.
  • Web pages that load in three seconds or less for optimal visitor experiences.
  • Tightened up security to keep all your business data as well as your visitors’ data private.
  • Content that’s easy for humans to read and even easier for Google bots to understand.
  • Custom-built user pathways that turn visitors into customers in no time.

The goal is to get them onboard with your search-optimized web design services and perhaps even ongoing maintenance and support afterwards. To do that, you have to clearly convey the benefits without getting them wrapped up in the technicalities of SEO.

SEO During the Setup and Planning Phase

In the early phases of an approved project, SEO shouldn’t be far from your mind. While you might not actively be working on optimizations yet, it’s a good idea to make sure the baseline you’re working from won’t cause more work for you down the line.

Here are some things to keep in mind:

Review the Client’s Design Assets

You probably ask clients to provide you with assets, logins and even copy before a project gets underway. While this is mainly to protect yourself from scope creep, it can also come in handy for SEO.

Say your client hands over logos and other image assets that are really low quality or look super outdated. The earlier you have these assets in hand, the sooner you can let them know that they need to be replaced if they want people to take their website seriously.

If visitors don’t like the look of a website, they’re going to abandon it without fail and that high bounce rate is going to kill the site’s ranking. So, make sure your clients don’t stand in the way of the results you’re trying to help them achieve.

Work in Tandem with the Copywriter

Even if a professional copywriter is creating the content for your website, if you’re not working closely with them from the start, there could be problems.

Web design agencies often debate the merits of content-first website development vs. design-first. The truth is, they should be created together. If you don’t, you put the whole project at risk.

For starters, a writer and designer working in isolation are bound to come up with different ways to handle style and layout. But if you establish guidelines from the get-go (which you can easily do with wireframes), you can avoid any disjoint in how the copy and design are created.

Wireframe.cc wireframe for mobile page layout
Designers can use a tool like Wireframe.cc to create wireframes that both they and the writer can work out of. (Image source: Wireframe.cc) (Large preview)

You’re going to wireframe any website you build anyway, so why not provide a copy of it to the writer so you two can be on the same page from the beginning? Even better, why not collaborate with the writer? They might not know design techniques or best practices, but they can certainly provide insight on things like optimal text length, the priority of certain sales or marketing messages and so on.

Then, there’s the matter of optimizing copy for search. Ideally, you’d want to work with a copywriter who can do the following:

  • Create actionable messaging and CTAs,
  • Naturally weave target keywords into the copy,
  • Write for user intent,
  • Write in HTML,
  • Create copy that’s both readable and scannable,
  • Build a system of internal links from page to page,
  • Create meta tags and optimize URLs for search,
  • Write alt tags for images.

The more of this that gets built into the copy from the get-go, the less back-and-forth you have to do with them later. It’ll also spare you the trouble of trying to fix this on your own (which you shouldn’t attempt to do).

Design for Sales Funnels

In order to increase conversions on a site, you should be designing sales funnels into them. But sales funnels aren’t just a series of steps that you lay down for visitors on the website.

It all begins with understanding user intent outside of the site. If you know what draws visitors to your site from search engines and social media, you can design your metadata and landing pages to align with that intent.

Again, if you do the research and planning upfront with SEO in mind, you’ll have less cleanup work to do later.

SEO During Design and Development

This, obviously, is the most important and labor-intensive stage for you. And while it’s easy to get wrapped up in designing and coding, it’s important not to lose sight of SEO here.

What I’d recommend is creating a universal SEO checklist you can use from project to project. If you account for every possible optimization now, you can take the guesswork out of SEO. Plus, a checklist allows you to become better acquainted with everything that needs to be done, which will enable you to find more efficient ways of building search optimizations into your process.

To help you along, I’ve created the following SEO checklist. Feel free to copy and use for your own needs:

  • Technical SEO
    • Web hosting with 99.9%+ uptime
    • Domain with clean web history
    • SSL certificate installed
    • Firewall implemented
    • Caching enabled (page, browser, object, etc.)
    • Image compression and resizing (in-house system or automated)
    • Automated backups
    • Google Analytics account connected
    • Google Analytics goal tracking, ecommerce tracking and other special tracking enabled
    • Google Search Console account connected
    • XML sitemap set up and submitted to Google
    • Separate sitemap submitted for images and for videos
    • Robots.txt set up
    • Schema.org markup (when relevant) written
  • Design SEO
    • Information architecture mapped out
    • Responsive web design
    • Mobile-first web design
    • 1 clear CTA per page
    • Custom 404 page set up
    • All links, buttons and forms tested and working
  • On-page SEO
    • 1 unique focus keyword per page
    • Focus keyword density between 1-3%
    • 50-60 character meta title including the focus keyword
    • 150-160 character meta description including the focus keyword
    • Short but descriptive slug including the focus keyword
    • Error-free content
    • At least 1 relevant internal link per page
    • Featured image for each page
    • Descriptive alt text for each image
    • Header tags used (focus keyword included in at least 1)
    • Headers appear every 300 words or so
    • Sentences stretch no more than two lines
    • Paragraphs stretch no more than five lines
    • Duplicate content analysis
    • Plagiarism check
  • Local SEO
    • Google My Business page set up
    • Geo-specific keywords included
    • Location-specific pages created (when relevant)
    • Contact information provided (e.g. phone number, address, etc.)
  • Ongoing SEO Support
    • Web server uptime, speed and security analysis
    • Page speed testing
    • Security monitoring
    • Google blacklist monitoring
    • Keyword rank monitoring
    • Broken link checking
    • Software updates

A Few Notes

  1. Anything that’s not relevant to the kinds of clients you serve or the kinds of services you offer, delete the row.
  2. If there’s anything I’m missing, feel free to add it on (like if you specialize in e-commerce design and want to prep product pages to appear in Google Shopping results).
  3. If you’re not comfortable checking the on-page SEO stuff, hand it off to a proofreader or editor (someone who didn’t do the copywriting).

For those of you looking for additional opportunities to make money, this SEO checklist can also be used to analyze an existing website and present a prospective client with a website redesign or optimization plan.

Wrapping Up

Keyword optimization and link building are only part of the SEO puzzle. As a web designer or developer, you have a huge contribution to make here as well. Whether it’s in the quality of code written on the backend, the way images are optimized or how well-secured the site is, these are the kinds of tasks you’re responsible for managing that no writer or SEO is going to or should handle. And, without these optimizations, Google and your visitors will fail to take notice of the site you worked so hard to build for them.

Smashing Editorial
(ra, il)

from Tumblr https://ift.tt/32xh4n0

Thursday, 27 February 2020

How To Build A Music Manager With Nuxt.js And Express.js

How To Build A Music Manager With Nuxt.js And Express.js

Deven Rathore

Handling digital media assets such as audio and video in your application can be tricky because of the considerations that have to be made server-side (e.g. networking, storage and the asynchronous nature of handling file uploads). However, we can use libraries like Multer and Express.js to simplify our workflow on the backend while using Nuxt.js (Vue framework) to build out the front-end interactions.

Whenever a web client uploads a file to a server, it is generally submitted through a form and encoded as multipart/form-data. Multer is a middleware for Express.js and Node.js that makes it easy to handle this so-called multipart/form-data whenever your users upload files. In this tutorial, I will explain how you can build a music manager app by using Express.js with Multer to upload music and Nuxt.js (Vue framework) for our frontend.

Prerequisites

  • Familiarity with HTML, CSS, and JavaScript (ES6+);
  • Node.js, npm and MongoDB installed on your development machine;
  • VS code or any code editor of your choice;
  • Basic Knowledge of Express.js.

Building The Back-End Service

Let’s start by creating a directory for our project by navigating into the directory, and issuing npm init -y on your terminal to create a package.json file that manages all the dependencies for our application.

mkdir serverside && cd serverside
npm init -y

Next, install multer, express, and the other dependencies necessary to Bootstrap an Express.js app.

npm install express multer nodemon mongoose cors morgan body-parser --save

Next, create an index.js file:

touch index.js

Then, in the index.js file, we will initialize all the modules, create an Express.js app, and create a server for connecting to browsers:

const express = require("express");
const PORT = process.env.PORT || 4000;
const morgan = require("morgan");
const cors = require("cors");
const bodyParser = require("body-parser");
const mongoose = require("mongoose");
const config = require("./config/db");
const app = express();
//configure database and mongoose
mongoose.set("useCreateIndex", true);
mongoose
  .connect(config.database, { useNewUrlParser: true })
  .then(() => {
    console.log("Database is connected");
  })
  .catch(err => {
    console.log({ database_error: err });
  });
// db configuaration ends here
//registering cors
app.use(cors());
//configure body parser
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
//configure body-parser ends here
app.use(morgan("dev")); // configire morgan
// define first route
app.get("/", (req, res) => {
  res.json("Hola MEVN devs...Assemble");
});
app.listen(PORT, () => {
  console.log(`App is running on ${PORT}`);
});

We, first of all, bring in Express.js into the project and then define a port that our application will be running on. Next, we bring in the body-parser, morgan ,mongoose and the cors dependencies.

We then save the express instance in a variable called app. We can use the app instance to configure middleware in our application just as we configured the cors middleware. We also use the app instance to set up the root route that will run in the port we defined.

Let’s now create a /config folder for our database config and multer config:

mkdir config and cd config
touch multer.js && touch db.js

Then open config/db.js and add the following code to configure our database:

module.exports = {
  database: "mongodb://localhost:27017/",
  secret: "password"
};

(This is actually an object that holds the database URL and the database secret.)

Running nodemon and navigating to localhost:4000 on your browser should give you this message:

"Hola MEVN devs...Assemble"

Also, this is what your terminal should now look like:

Running Nodemon using Terminal
Terminal preview (Large preview)

Setting Up Model, Routes, And Controllers

Let’s set up a file structure by typing in the following:

mkdir api && cd api
mkdir model && cd model && touch Music.js
cd ..
mkdir controller && cd controller && touch musicController.js
cd ..
mkdir routes && cd routes && touch music.js

In our terminal, we use mkdir to create a new directory, and then cd to move into a directory. So we start by creating a directory called api and then move into the api directory.

The touch command is used to create a new file inside a directory using the terminal, while the cd command is used to move out of a directory.

Now let’s head on over to our api/model/Music.js file to create a music schema. A model is a class with which we construct documents. In this case, each document will be a piece of music with properties and behaviors as declared in our schema:

let mongoose = require("mongoose");
let musicSchema = mongoose.Schema({
  title: {
    type: String,
    required: true
  },
  music: {
    type: Object,
    required: true
  },
  artist: {
    type: String,
    required: true
  },
  created: {
    type: Date,
    default: Date.now()
  }
});
let Music = mongoose.model("Music", musicSchema);
module.exports = Music;

Let’s head over to config/multer to configure Multer:

let multer = require("multer");
const path = require("path");
const storage = multer.diskStorage({
  destination: (req, res, cb) => {
    cb(null, "./uploads");
  },
  filename: (req, file, cb) => {
    cb(null, new Date().toISOString() + file.originalname);
  }
});
const fileFilter = (req, file, cb) => {
  if (
     file.mimetype === "audio/mpeg" ||
     file.mimetype === "audio/wave" ||
     file.mimetype === "audio/wav" ||
     file.mimetype === "audio/mp3"
  ) {
    cb(null, true);
  } else {
    cb(null, false);
  }
};
exports.upload = multer({
  storage: storage,
  limits: {
    fileSize: 1024 * 1024 * 5
  },
  fileFilter: fileFilter
});

In the multer.js file, we start by setting up a folder where all the uploaded music files will be uploaded. We need to make this file static by defining that in the index.js file:

app.use('/uploads', express.static('uploads'));

After that, we write a simple validator that will check the file mimetype before uploading. We then define the multer instance by adding the storage location, the limits of each file, and the validator that we created.

Create The Necessary Routes

Now let’s create our routes. Below is the list of endpoints we will be creating.

HTTP POST /music Add new music
HTTP GET /music Get all music
HTTP DELETE /music/:blogId Delete a music

Let’s start by creating the blog route. Head over to api/routes/music.js and write the following code:

const express = require("express");
const router = express.Router();
const musicController = require("../controller/musicController");
const upload = require("../../config/multer");
router.get("/",  musicController.getAllMusics);
router.post("/", upload.upload.single("music"), musicController.addNewMusic);
router.delete("/:musicId", musicController.deleteMusic);
module.exports = router;

Note: Now whenever we make a get request to /music. the route calls the getAllMusic function that is located in the ‘controllers’ file.

Let’s move on over to api/controllers/musicController to define the controllers. We start by writing a function to get all the music in our database using the mongoose db.collection.find method which will return all the items in that collection.

After doing that, we write another function that will create a piece of new music in the database. We need to create a new music instance using the new keyword and then define the music object. After doing this, we will use the mongoose save method to add new music to the database.

In order to delete a piece of music, we need to use the mongoose remove method by simply passing the music ID as a parameter in the remove instance. This results to mongoose looking into the music collection that has that particular ID and then removing it from that collection.

let mongoose = require("mongoose");
const Music = require("../model/Music");
exports.getAllMusics = async (req, res) => {
  try {
    let music = await Music.find();
    res.status(200).json(music);
  } catch (err) {
    res.status(500).json(err);
  }
};
exports.addNewMusic = async (req, res) => {
  try {
    const music = new Music({
      title:req.body.title,
      artist:req.body.artist,
      music:req.file
    });
    
    let newMusic = await music.save();
    res.status(200).json({ data: newMusic });
  } catch (err) {
    res.status(500).json({ error: err });
  }
};
exports.deleteMusic = async (req, res) => {
  try {
    const id = req.params.musicId;
    let result = await Music.remove({ _id: id });
    res.status(200).json(result);
  } catch (err) {
    res.status(500).json(err);
  }
};

Last but not least, in order to test the routes, we need to register the music routes in our index.js file:

const userRoutes = require("./api/user/route/user"); //bring in our user routes
app.use("/user", userRoutes);

Testing The End Points

To test our endpoints, we will be using POSTMAN.

Adding New Music

To test the Add Music functionality, set the method of the request by clicking on the methods drop-down. After doing this, type the URL of the endpoint and then click on the body tab to select how you want to send your data. (In our case, we will be using the form-data method.)

So click on the form-data and set up your model key. As you set it up, give the keys some value as shown in the image below:

Testing Adding new music API using Postman
Testing Adding new music API in Postman dashboard (Large preview)

After doing this, click on ‘Send’ to make the request.

Listing All Music

To list all of the music in our database, we have to type the endpoint URL in the URL section provided. After doing this, click on the ‘Send’ button to make the request.

Testing Listing API using Postman
Testing Listing API in Postman dashboard (Large preview)

Deleting Music

To delete a piece of music, we need to pass the music id as a parameter.

Testing Delete API using Postman
Testing Delete API Postman dashboard (Large preview)

That’s it!

Building The Frontend

For our frontend, we will be using a Vue framework: Nuxt.js.

“Nuxt is a progressive framework based on Vue.js to create modern web applications. It is based on Vue.js official libraries (vue, vue-router and vuex) and powerful development tools (webpack, Babel and PostCSS).”

NuxtJS Guide

To create a new Nuxt.js application, open up your terminal and type in the following (with musicapp as the name of the app we will be building):

$ npx create-nuxt-app musicapp

During the installation process, we will be asked some questions regarding the project setup:

Project name musicapp
project description A Simple music manager app
Author name <your name>
Package manager npm
UI framework Bootstrap vue
custom ui framework none
Nuxt modules Axios,pwa (use the spacebar on your keyboard to select items)
Linting tool Prettier
test framework None
Rendering Mode Universal (SSR)
development tool Jsonconfig.json

After selecting all of this, we have to wait a little while for the project to be set up. Once it’s ready, move into the /project folder and serve the project as follows:

cd musicapp && npm run dev

Open up the project in any code editor of your choice and then open the project in the browser by accessing localhost:3000.

Preview Of Nuxt.js project
Nuxt.js Project Preview (Large preview)

Configuring Axios

We will be using axios to make an HTTP request to our back-end server. Axios is already installed in our project, so we just have to configure the baseURL- to our backend server.

To do this, open the nuxt.config.js file in the root directory and add the baseURL in the axios object.

axios: {
  baseURL:'http://localhost:4000'
},

Building The Music Manager

Setting Up The UI

Let’s start by cleaning up the UI. Open up the pages/index.vue file and remove all of the code in there with the following:

<template>
<div>Hello</div>
</template>

After doing this, you should only be able to see a “Hello” in the browser.

In the root directory, create a /partials folder. Inside the /partials folder, create a navbar.vue file and add the following code:


<template>
  <header>
    <nav class="navbar navbar-expand-lg navbar-light bg-info">
      <div class="container">
        <a class="navbar-brand" href="#">Music App</a>
        <button
          class="navbar-toggler"
          type="button"
          data-toggle="collapse"
          data-target="#navbarNav"
          aria-controls="navbarNav"
          aria-expanded="false"
          aria-label="Toggle navigation"
        >
          <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse justify-content-end" id="navbarNav">
          <ul class="navbar-nav">
            <li class="nav-item active">
              <a class="nav-link" href="#">Player</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#">Manager</a>
            </li>
          </ul>
        </div>
      </div>
    </nav>
  </header>
</template>
<style scoped>
.nav-link,
.navbar-brand {
  color: #ffff !important;
}
</style>

Note: We will be using the component to navigate through pages in our application. This is just going to be a simple component made up of Bootstrap navbar. Check out the official Bootstrap documentation for more reference.

Next, let’s define a custom layout for the application. Open the /layouts folder, replace the code in the default.vue file with the code below.

<template>
  <div>
    <navbar />
    <nuxt />
  </div>
</template>
<script>
import navbar from '@/partial/navbar'
export default {
  components: {
    navbar
  }
}
</script>

We import the navbar into this layout, meaning that all the pages in our application will have that navbar component in it. (This is going to be the component that all other component in our application will be mounted.)

After this, you should be able to see this in your browser:

Nuxt.js Navbar component after modification
Nuxt.js Navbar component (Large preview)

Now let’s setup the UI for our manager. To do this, we need to create a /manager folder within the components folder and then add a file into the folder named manager.vue.

In this file, add the following code:

<template>
  <section class="mt-5">
    <div class="container mb-4">
      <div class="row">
        <div class="col-md-12">
          <div class="card">
            <div class="card-body">
              <div class="card-title mb-4">
                <h4>Add Music</h4>
              </div>
              <form>
                <div class="form-group">
                  <label for="title">Title</label>
                  <input type="text" class="form-control" />
                </div>
                <div class="form-group">
                  <label for="artist">Artist</label>
                  <input type="text" class="form-control" />
                </div>
                <div class="form-group">
                  <label for="artist">Music</label>
                  <div class="custom-file">
                    <input type="file" class="custom-file-input" id="customFile" />
                    <label class="custom-file-label" for="customFile">Choose file</label>
                  </div>
                </div>
                <div class="form-group">
                  <button class="btn btn-primary">Submit</button>
                </div>
              </form>
            </div>
          </div>
        </div>
      </div>
    </div>
    <div class="container">
      <div class="row">
        <div class="col-md-12">
          <div class="card bg-light p-1 showdow-sm">
            <div class="card-title">
              <button class="btn btn-info m-3">Add Music</button>
            </div>
            <div class="card-body">
              <table class="table">
                <thead>
                  <tr>
                    <th scope="col">#</th>
                    <th scope="col">Title</th>
                    <th scope="col">Artist</th>
                    <th scope="col">Date created</th>
                    <th scope="col">Action</th>
                  </tr>
                </thead>
                <tbody>
                  <tr>
                    <td>1</td>
                    <td>Demo Title</td>
                    <td>Wisdom.vue</td>
                    <td>12/23/13</td>
                    <td>
                      <button class="btn btn-info">Delete</button>
                    </td>
                  </tr>
                </tbody>
              </table>
            </div>
          </div>
        </div>
      </div>
    </div>
  </section>
</template>

Note: This is just a simple Bootstrap template for adding music into our application. The form will define a table template that will list all os the music that can be found in our database.

After defining this component, we need to register it in the /pages folder to initailize routing.

Nuxt.js doesn’t have a ‘router.js’ file like Vue.js. It uses the pages folder for routing. For more details, visit the Nuxt.js website.

To register the component, create a /manager folder within the /pages folder and create an index.vue file. Then, place the following code inside the file:

<template>
  <div>
    <manager />
  </div>
</template>
<script>
import manager from '@/components/manager/manager'
export default {
  components: {
    manager
  }
}
</script>

This is the component that will render in our pages route.

After doing this, head over to your browser and navigate to /manager — you should be seeing this:

UI of music Manager
Music manager UI (Large preview)

Listing All Music

Let’s continue by creating a function that will fetch all of the music. This function will be registered in the created life cycle hook, so that whenever the component is created, the function will be called.

Let’s start by creating a variable in the vue instance that will hold all of the music:

allmusic = [];
musicLoading: false,

Then, define a getAllMusics function and add the following code:

async getAllMusics() {
    this.musicLoading = true
    try {
      let data = await this.$axios.$get('/music')
      this.allmusic = data
      this.musicLoading = false
    } catch (err) {
      this.musicLoading = false
      swal('Error', 'Error Fetting Musics', 'error')
    }
  }

Next, register within the created life cycle hook:

created() {
    this.getAllMusics()
  }

Outputting The Data

Now it’s time to output all of the songs on the table which we’ve created earlier:

<table class="table">
              <thead>
                <tr>
                  <th scope="col">#</th>
                  <th scope="col">Title</th>
                  <th scope="col">Artist</th>
                  <th scope="col">Date created</th>
                  <th scope="col">Action</th>
                </tr>
              </thead>
              <div
                v-if="musicLoading"
                class="spinner-border"
                style="width: 3rem; height: 3rem;"
                role="status"
              >
                <span class="sr-only">Loading...</span>
              </div>
              <tbody v-else>
                <tr v-for="(music, index) in allmusic" :key="index">
                  <td></td>
                  <td></td>
                  <td></td>
                  <td></td>
                  <td>
                    <button class="btn btn-info" @click="deleteMusic(music._id)">Delete</button>
                  </td>
                </tr>
              </tbody>
            </table>

Remember that table we created earlier? Well, we will need to loop through the response we get back from our backend to list all of the music received back from the database.

Adding Music

To add a new piece of music we need to make an HTTP request to the back-end server with the music details. To do this, let’s start by modifying the form and handling of the file uploads.

On the form, we need to add an event listener that will listen to the form when it is submitted. On the input field, we add a v- model to bind the value to the input field.

<form @submit.prevent="addNewMusic">
            <div class="form-group">
              <label for="title">Title</label>
              <input type="text" v-model="musicDetails.title" class="form-control" />
            </div>
            <div class="form-group">
              <label for="artist">Artist</label>
              <input type="text" v-model="musicDetails.artist" class="form-control" />
            </div>
            <div class="form-group">
              <label for="artist">Music</label>
              <div class="custom-file">
                <input
                  type="file"
                  id="customFile"
                  ref="file"
                  v-on:change="handleFileUpload()"
                  class="custom-file-input"
                />
                <label class="custom-file-label" for="customFile">Choose file</label>
              </div>
            </div>
            <div class="form-group">
               <button class="btn btn-primary" :disabled="isDisabled">
                <span
                  class="spinner-border spinner-border-sm"
                  v-if="addLoading"
                  role="status"
                  aria-hidden="true"
                ></span>Submit
              </button>
            </div>
          </form>

And the script section should look like this:

<script>
export default {
  data() {
    return {
      musicDetails: {
        title: '',
        artist: '',
        music: ''
      },
      allmusic = [],
        musicLoading: false,
      isValid: false;
      addLoading: false,
    }
  },
  computed: {
    isDisabled: function() {
      if (
        this.musicDetails.title === '' ||
        this.musicDetails.artist === '' ||
        this.musicDetails.music === ''
      ) {
        return !this.isValid
      }
    }
  },
  methods: {
    handleFileUpload() {
      this.musicDetails.music = this.$refs.file.files[0]
      console.log(this.musicDetails.music.type)
    },
    addNewMusic() {
      let types = /(\.|\/)(mp3|mp4)$/i
      if (
        types.test(this.musicDetails.music.type) ||
        types.test(this.musicDetails.music.name)
      ) {
        console.log('erjkb')
      } else {
        alert('Invalid file type')
        return !this.isValid
      }
    }
  }
}
</script>

We will define a function that will send a request to our back-end service to create any new music that has been added to the list. Also. we need to write a simple validation function that will check for the file type so that the users can only upload files with an extention of .mp3 and .mp4.

It’s important to define a computed property to make sure that our input field isn’t empty. We also need to add a simple validator that will make sure the the file we are trying to upload is actually a music file.

Let’s continue by editing the addMusic function to make a request to our back-end service. But before we do this, let’s first install sweetalert which will provide us with a nice modal window. To do this this, open up your terminal and type in the following:

npm i sweetalert

After installing the package, create a sweetalert.js file in the /plugins folder and add this:

import Vue from 'vue';
import swal from 'sweetalert';

Vue.prototype.$swal = swal;

Then, register the plugin in the nuxt.config.js file inside the plugin instace like this:

plugins: [
    {
      src: '~/plugins/sweetalert'
    }
  ],

We have now successfully configured sweetalert in our application, so we can move on and edit the addmusic function to this:

addNewMusic() {
    let types = /(\.|\/)(mp3|mp4)$/i
    if (
      types.test(this.musicDetails.music.type) ||
      types.test(this.musicDetails.music.name)
    ) {
      let formData = new FormData()
      formData.append('title', this.musicDetails.title)
      formData.append('artist', this.musicDetails.artist)
      formData.append('music', this.musicDetails.music)
      this.addLoading = true
      this.$axios
        .$post('/music', formData)
        .then(response => {
          console.log(response)
          this.addLoading = false
          this.musicDetails = {}
          this.getAllMusics() // we will create this function later
          swal('Success', 'New Music Added', 'success')
        })
        .catch(err => {
          this.addLoading = false
          swal('Error', 'Something Went wrong', 'error')
          console.log(err)
        })
    } else {
      swal('Error', 'Invalid file type', 'error')
      return !this.isValid
    }
  },

Let’s write a simple script that will toggle the form, i.e it should only display when we want to add new music.

We can do this by editing the ‘Add Music’ button in the table that displays all of the music that can be found:

<button
    class="btn btn-info m-3"
    @click="initForm">
    
</button>

Then, add a state that will hold the state of the form in the data property:

addState: false

After doing this, let’s define the initForm function:

initForm() {
    this.addState = !this.addState
  },

And then add v-if="addState" to the div that holds the form:

<div class="card" v-if="addState">

Deleting Music

To delete music, we need to call the delete endpoint and pass the music id as a param. Let’s add a click event to the ‘Delete’ button that will trigger the function to delete a function:

<button class="btn btn-info" @click="deleteMusic(music._id)">Delete</button>

The delete function will be making an HTTP request to our back-end service. After getting the music ID from the deleteMusic function parameter, we will add the ID in the URL that we are using to send the request. This specifies the exact piece of music that ought to be removed from the database.

deleteMusic(id) {
    swal({
      title: 'Are you sure?',
      text: 'Once deleted, you will not be able to recover this Music!',
      icon: 'warning',
      buttons: true,
      dangerMode: true
    }).then(willDelete => {
      if (willDelete) {
        this.$axios
          .$delete('/music/' + id)
          .then(response => {
            this.getAllMusics()
            swal('Poof! Your Music file has been deleted!', {
              icon: 'success'
            })
          })
          .catch(err => {
            swal('Error', 'Somethimg went wrong', 'error')
          })
      } else {
        swal('Your Music file is safe!')
      }
    })
  }

With all of this, we have just built our music manager. Now it’s time to build the music player.

Let’s start by creating a new folder in the components folder named /player. Then, create a player.vue file within this folder and add this:

<template>
  <section>
    <div class="container">
      <div class="row">
        <div class="col-md-12">
          <h3 class="text-center">Player</h3>
        </div>
      </div>
    </div>
  </section>
</template>
<script>
export default {
  data() {
    return {}
  }
}
</script>
<style  scoped>
</style>

Next, let’s import this component into the index.vue file in the /pages folder. Replace the code in index.vue file to this:

<template>
  <div>
    <player />
  </div>
</template>
<script>
import player from '@/components/player/player'
export default {
  components: {
    player
  }
}
</script>

Let’s configure routing in our navbar component to enable routing between our pages.

To route in a Nuxt.js application, the nuxt-link is used after which you have specified the page for that route to a particular instance. So let’s edit the code in the partials/navbar component to this:

<template>
  <header>
    <nav class="navbar navbar-expand-lg navbar-light bg-info">
      <div class="container">
        <nuxt-link to="/" class="navbar-brand">Music App</nuxt-link>
        <button
          class="navbar-toggler"
          type="button"
          data-toggle="collapse"
          data-target="#navbarNav"
          aria-controls="navbarNav"
          aria-expanded="false"
          aria-label="Toggle navigation"
        >
          <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse justify-content-end" id="navbarNav">
          <ul class="navbar-nav">
            <li class="nav-item active">
              <nuxt-link to="/" class="nav-link">Player</nuxt-link>
            </li>
            <li class="nav-item">
              <nuxt-link to="/manager" class="nav-link">Manager</nuxt-link>
            </li>
          </ul>
        </div>
      </div>
    </nav>
  </header>
</template>
<style scoped>
.nav-link,
.navbar-brand {
  color: #ffff !important;
}
</style>

With this, we can navigate through our pages by using the navbar.

Building The Player

Before we begin, we need to extend Webpack to load audio files. Audio files should be processed by file-loader. This loader is already included in the default Webpack configuration, but it is not set up to handle audio files.

To do this, go to the nuxt.config.js file and modify the build object to this:

build: {
    extend(config, ctx) {
      config.module.rules.push({
        test: /\.(ogg|mp3|mp4|wav|mpe?g)$/i,
        loader: 'file-loader',
        options: {
          name: '\[path\][name].[ext]'
        }
      })
    }
  }

Next, let’s write a function that will get all songs and then use the Audio constructor to play the first song in the allMusic array.

For starters, let’s modify our player.vue file to this:

<template>
  <section v-if="allMusic">
    <div class="container">
      <div class="row">
        <div class="col-md-12">
          <h3 class="text-center">Player</h3>
        </div>
      </div>
      <div class="row">
        <div class="col-md-6">
          <span> - </span>
        </div>
      </div>
    </div>
  </section>
</template>
<script>
export default {
  data() {
    return {
      current: {
        title: '',
        artist: ''
      },
      song: true,
      isplaying: false,
      allMusic: null,
      index: 0,
      player: ''
    }
  },
  methods: {
     async initPlayer() {
      if (this.allMusic !== []) {
        this.current = await this.allMusic[this.index]
        this.player.src = `http://localhost:4000/${this.current.music.path}`
      } else {
        this.song = true
      }
    },
      async getAllSongs() {
        try {
        let response = await this.$axios.$get('/music')
        console.log(response)
        if (response === []) {
          this.song = true
          this.current = null
        } else {
          this.song = false
          this.allMusic = response
        }
        await this.initPlayer()
      } catch (err) {
        this.current = null
        console.log(err)
      }
    }
  },
  created() {
 if (process.client) {
      this.player = new Audio()
    }
    this.getAllSongs()
  }
}
</script>
<style  scoped>
</style>

Once the file is served, the music will play in the background and then you should be able to see this in your browser:

UI of Music player
Music player UI (Large preview)

To stop the music, all you need to do is comment out the await player.play() in the initPlayer function.

Creating The Player UI

Let’s now define our music player UI by replacing the template in our player.vue file with the following:

<template>
  <section v-if="allMusic">
    <div class="container">
      <div class="row mb-5">
        <div class="col-md-12">
          <h3 class="text-center">Player</h3>
        </div>
      </div>
      <div class="row mt-5">
        <div class="col-md-6">
          <img
            src="https://images.pexels.com/photos/3624281/pexels-photo-3624281.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500"
            class="image"
          />
          <div class="card player_card">
            <div class="card-body">
              <h6 class="card-title">
                <b> - </b>
              </h6>
              <div>
                <i class="fas fa-backward control mr-4"></i>
                <i class="fas fa-play play"></i>
                <i class="fas fa-pause play"></i>
                <i class="fas fa-forward control ml-4"></i>
              </div>
            </div>
          </div>
        </div>
        <div class="col-md-6">
          <div class="card shadow">
            <table class="table">
              <thead>
                <tr>
                  <th scope="col">#</th>
                  <th scope="col">Title</th>
                  <th scope="col">Artist</th>
                  <th scope="col">Action</th>
                </tr>
              </thead>
              <tbody>
                <tr>
                  <th scope="row">1</th>
                  <td>Mark</td>
                  <td>Otto</td>
                  <td>
                    <button class="btn btn-primary">Play</button>
                  </td>
                </tr>
              </tbody>
            </table>
          </div>
        </div>
      </div>
    </div>
  </section>
</template>

Then, add the following style into the style section:

<style  scoped>
.image {
  border-radius: 5px !important;
  position: relative;
  height: 300px;
  width: 100%;
}
.player_card {
  text-align: center;
  bottom: 20px;
  margin: 0px 40px;
}
.text-muted {
  font-size: 15px;
}
.play {
  font-size: 40px;
}
.control {
  font-size: 25px;
}
</style>

After modifying this, the player should look like this:

Music player final UI
Final UI of music player (Large preview)

Adding The Play Function

We’ll continue by displaying the music description on the table. In order to do this, replace the table with the code below:

<table class="table">
              <thead>
                <tr>
                  <th scope="col">#</th>
                  <th scope="col">Title</th>
                  <th scope="col">Artist</th>
                  <th scope="col">Action</th>
                </tr>
              </thead>
              <tbody>
                <tr v-for="(music,index) in allMusic" :key="index">
                  <th scope="row"></th>
                  <td></td>
                  <td></td>
                  <td>
                    <button class="btn btn-primary">Play</button>
                  </td>
                </tr>
              </tbody>
            </table>

We don’t want to display the ‘Play’ and ‘Pause’ icons at the same time. Instead, we want a situation that when the song is playing, the ‘Pause’ icon is displayed. Also, when the song is paused, the play icon should be displayed.

To achieve this, we need to set a isPlaying state to the false instance and then use this instance to toggle the icons. After that, we will add a function to our ‘Play’ icon.

isplaying:false

After doing this, modify your ‘Play’ and ‘Pause’ icon to this:

<i class="fas fa-play play" v-if="!isplaying" @click="play"></i>
<i class="fas fa-pause play" v-else></i>

With all this let’s define the play method:

play(song) {
      console.log(song)
      if (song) {
        this.current = song
        this.player.src = `http://localhost:4000/${this.current.music.path}`
      }
      this.player.play()
      this.isplaying = true
    },

We, first of all, get the current song and pass it into the function parameter. We then define the JavaScript Audio() instance. Next, we check if the song is null: If it isn’t, we set this.current to the song we passed in the parameter, and then we call the Audio player instance. (Also, don’t forget that we have to set the isPlaying state to true when the music is playing.)

Adding The Pause Function

To pause a song, we will use the Audio pause method. We need to add a click event to the pause icon:

<i class="fas fa-pause play" @click="pause" v-else></i>

And then define the function in the methods instance:

pause() {
      this.player.pause()
      this.isplaying = false
    },

Playing A Song From The Music List

This is quite simple to implement. All we have to do is add a click event that will change the song parameter in the play method to the song we just created.

Simply modify the play button on the music list table to this:

<button class="btn btn-primary" @click="play(music)">Play</button>

And there you have it!

Adding The Next Function

To add the next function, we need to increment the index by one. To do this, add a click event to the next icon:

@click="next"

And then define the prev function in the methods instance:

next() {
      this.index++
      if (this.index > this.allMusic.length - 1) {
        this.index = 0
      }
       this.current = this.allMusic[this.index]
      this.play(this.current)
    },

This conditional is responsible for replaying all of the songs whenever the last song in the list has been played.

Adding The previous Function

This is actually the opposite of the next function, so let’s add a click event to the previous function:

@click="prev"

Next, we define the previous function:

prev() {
      this.index--
      if (this.index 

Our music player app is now complete!

Conclusion

In this article, we looked at how we can build a music manager with Nuxt.js and Express.js. Along the way, we saw how Multer streamlines the process of handling file uploads and how to use Mongoose to interact without a database. Finally, we used Nuxt.js to build the client app which gives it a fast and snappy feel.

Unlike other frameworks, building an application with Nuxt.js and Express.js is quite easy and fast. The cool part about Nuxt.js is the way it manages your routes and makes you structure your apps better.

  • You can access more information about Nuxt.js here.
  • You can access the source code on Github here
Smashing Editorial
(dm, il)

from Tumblr https://ift.tt/2Pw9NP3

Wednesday, 26 February 2020

What Does A Foldable Web Actually Mean?

What Does A Foldable Web Actually Mean?

Frederick O'Brien

After years of talk, experimentation, and plateauing smartphone sales, foldable devices are finally entering the market. Samsung, Huawei, and Motorola have all released phones with foldable screens, and tinkering away behind the scenes the likes of Apple aren’t far behind. The ‘foldable web’ is coming.

Its devices are taking various forms, from laptops to phones to newfangled dual-screen hybrids. There is no catch-all definition for this new class of gizmos, but most fit into one of two categories. ‘Foldables’ are devices in which the screen literally folds, while on ‘dual-screens’ the screens are separate but can be used as one. Where web design is concerned the two types will likely play by similar rules. Should the technology take off in a big way then web design could be looking at its biggest shakeup in well over a decade.

Google Trends info for ‘foldable phones’ that have skyrocketed late 2018
Searches for ‘foldable phones’ have skyrocketed late 2018. (Data source: Google Trends) (Large preview)

It all sounds very exciting, but what does it actually mean? The ‘foldable web’ will bring with it new challenges, new opportunities, and, in all likelihood, new syntax. The web could be in for its biggest shakeup since the smartphone. Users and coders alike have gotten rather used to the playing field: desktop and mobile with a sprinkling of tablets. Not any more. If you thought you knew responsive design before you ain’t seen nothing yet.

Further Reading

New Web Standards, New Experiences, And New Problems

Flexible screen technology has been researched since the 1970s but has only been developed in earnest since the turn of the millennium. It’s only in the last couple of years that consumer devices have started to enter the market — in all shapes and sizes.

Some, like the Galaxy Z Flip, mimic an old-school flip-phone. Others, like the Huawei Mate X, have the screen(s) wrap around the outside of the phone. Plenty more are built like electronic books, with two interior displays becoming one when the device is fully opened. Oftentimes there is a separate, smaller screen on the outside so users don’t have to unfold it when they use it.

A picture of the Huawei Mate X and Samsung Galaxy Z Flip
The Huawei Mate X and Samsung Galaxy Z Flip. (Note: Dramatic shadows are not included with most foldable phones.) (Large preview)

The hardware hiccups are well documented and are being worked through. Foldable devices are coming. That is not the focus. Here the focus is on how the technology will impact web developers, UX designers, and anyone else whose business it is to deliver quality browsing experiences.

Updates To CSS And JavaScript

New hardware means updated software. Microsoft has been particularly responsive to the arrival of foldable tech, in part because the company is working on its own foldable devices. Three Microsoft developers — Bogdan Brinza, Daniel Libby, and Zouhir Chahoud — have published an explainer in which they propose a new JavaScript API and a CSS media query. Chahoud expanded on this with a GitHub post on February 3rd.

They highlight potential issues with foldable devices, including:

  • Variety of hardware in the foldable market.
    Some devices are seamless while others aren’t, and their shapes vary wildly. The Windows Surface Duo and Galaxy Fold are both shaped like books - one with a seam and one without. The seamless Motorola Razr harkens back to the flip-phones of old, with the two ‘halves’ of the screen closer to squares than rectangles. Consider as well that it’s surely only a matter of time before a three-part foldable appears on the scene. With so much variety in the shape and size of foldables it’s important to target a _class _of device rather than specific hardware.
  • ‘Fold area’ functionality.
    The miracle of foldable screens has required a few sacrifices. A major one is the potential awkwardness of the screen(s) near the fold. Content positioned on or across the seam of a partially folded screen may be difficult to view or interact with. Books and magazines tend to avoid printing content across their folds; the same will likely hold true for foldable screens. What’s more, some usability tests have suggested touchscreen responsiveness isn’t as reliable on foldables.

In an attempt to address these issues and others, Brinza, Libby, and Chahoud have proposed a spanning CSS media feature, which can test whether the browser window is being displayed across two screens or across a fold. If it is, then content can then be positioned relative to the fold or seam. This plays into the ongoing evolution of responsive design, which increasingly has to account for more than screen size.

Accordingly, environment variables have also been proposed, providing a way to recognise segment size and orientations. Such additions would effectively allow websites to shape themselves across three dimensions. The same page can behave differently when it’s flat than when it’s an L shape.

The CSS suggestions are accompanied by a new Window Segments enumeration for JavaScript API, which would allow sites to behave more dynamically. For example, what’s displayed could change depending on whether the screen is bent or not, or behave differently depending on whether users touch one half of the display or the other. The new JavaScript API also improves functionality on non-Document Object Model targets where CSS is not available, such as Canvas2d or WebGL.

Websites would be able to shape themselves around a device’s seam. Mockups from the explainer by Bogdan Brinza, Daniel Libby, and Zouhir Chahoud. (Large preview)

These proposals don’t account for more than two screens or segments, but for now the tech seems to be headed that way. Should these proposals be implemented they would add a new layer to responsive web design. The time may soon come when we can no longer assume sites need only function in single rectangular spaces. New CSS and JavaScript specifications like those proposed by Brinza, Libby, and Chahoud would give developers a way of doing something about it.

Chahoud doesn’t expect many teething problems:

“We view dual-screen and foldable devices as another responsive web design target, something web developers have been doing for years with CSS specific to phones, desktops, tablets,and so on.”

If new web primitives stay ahead of the tech, developers will be able to focus on improving the functionality of their sites.

A New Fold And Dual-Screen Experiences

What does that improved functionality involve? One of the main takeaways is that there’s a new fold in town. While ‘above the fold’ has been around for as long as there’s been scrolling (a throwback to newspaper design) developers will soon have to contend with folds in the middle of the page.

At the very least this will likely mean adjusting content so users don’t have to interact with anything on the fold. If touch controls are limited at the fold, or the device is partially folded, it makes sense to reposition certain elements so that they sit on one half of the screen or the other.

Foldables offer new opportunities for dual-screen mobile designs. Mockup from the explainer by Bogdan Brinza, Daniel Libby, and Zouhir Chahoud. (Large preview)
“I think there are a lot of opportunities not only in the increased real estate but also in the ‘defined’ real-estate,” Chahoud says. “The fold (whether the device is seamless or has a seam) splits the screen into two nicely defined display regions and creators can organize specific content per region.”

On the more ambitious end of the spectrum, foldable devices effectively mean a mini dual-screen setup, in which the two halves of the display can be used for different things. Indeed, when you boil down the foldable web it bears an uncanny resemblance to devices like the Nintendo DS — a single device with two screens working together. Tech has advanced massively since then, to the point where the two displays can be seamlessly connected, but the core experience is very similar.

Nintendo DS
I have seen the future, and it looks a bit like 2004. (Large preview)

In terms of web design, this allows for content to be presented in a more app-like way. Chahoud says: “I believe designs targeting dual-screen or foldable devices will be a two-column grid at the base, representing the available logical or physical display regions.” Samsung Developer documentation goes even further, suggesting the secondary display can itself be split into two, providing three separate ‘screens’ in all.

Samsung Developer mockups of multiple windows on foldable devices
Why stop at two screens? The foldable web could be a world of multiple windows. Mockup from Samsung Developers documentation for the Galaxy Fold. (Large preview)

On a cooking website, this might mean having the recipe on one screen and the ingredients on the other. On a news website, it might mean having article copy on one screen and related reading on the other. It depends, as ever, on the content. At its most ambitious the foldable web could function like dual mobile screens.

Tidying Up

For many, the rise of foldable devices won’t be a game-changer so much as a modest improvement for user experience. Steve Krug, author of Don’t Make Me Think! A Common Sense Approach to Web Usability, sees the foldable web as an evolution rather than a revolution. “Unfoldable phones always struck me as the next reasonable step,” he says. Not because of dual-screen capabilities, but because they make tablet experiences more portable, fulfilling the desire for “a tablet I can carry in my pocket.”

Phablet, as well as being a top contender for worst portmanteau of all time (it narrowly lost to ‘guesstimate’ in the 2019 World Championships), may well find a new home. Rather than meaning a phone so large it’s basically a tablet, a phablet will be one or the other depending on whether the device is open, closed, or somewhere inbetween.

“There are classes of apps that would benefit from split-screen or different aspect ratios, but for the most part those devices are not going to bring you anything new if you just want a larger screen to watch videos on.”

— Steve Krug

In many cases, the ‘foldable web’ will simply mean better-optimizing sites for tablet-sized displays. At present tablets only have around 3% market share worldwide (compared to 52% for mobile and 45% for desktop). If foldable devices make a dent in that they will be much harder to ignore.

When the likes of Apple release a foldable device, it’s safe to say it will sell like hotcakes. As more foldable tech enters the market, web design will need to up its responsiveness just to maintain existing functionality. At the very least there’s going to be some tidying up to do.

Further Reading

Be Flexible

So what does the foldable web mean? In short, it’s up to you. The trend likely marks the next step in responsive design. With the aid of new CSS and JavaScript features, developers will be able to build multi-screen experiences where before there was the single, uninterrupted rectangles of desktop, mobile, and tablet.

How far those experiences can go remains to be seen. It’s safe to assume the ‘foldable web’ won’t arrive readymade. There is no guarantee that devices will take off like smartphones did, especially while most of them still cost upwards of 2,000 dollars. There will be teething on the hardware side, a period of turbulence after which tech will likely settle into reliable styles.

It is the role of developers and designers to push these platforms as far as they can. The foldable web is an opportunity to give websites fluidity and functionality that wasn’t possible before. It means making websites more responsive than ever.

It also marks a unique opportunity to explore uncharted territory. Though not a seismic change, the foldable web is probably the biggest change to the status quo since the iPhone. What that means as far as syntax goes is very much up for grabs. Web standards aren’t concocted in smoke-filled back rooms. Now is the time for getting involved, offering feedback, making suggestions, and experimenting.

Here are some resources for getting involved.

Mobile-first design is about to get more complicated, but also more exciting. The foldable web could be the first time handheld devices feel expansive rather than restrictive. For some websites, it will mean tweaks, while for others, wholesale redesigns. The scope of what’s possible depends on the innovation of developers.

So, what do you think is possible?

Smashing Editorial
(ra, il)

from Tumblr https://ift.tt/37YseC7