People Problems

Just the other day, Jeremy Keith wrote that problems with performance work isn’t only a matter of optimization and fixing code, but also tackling people problems:

It struck me that there’s a continuum of performance challenges. On one end of the continuum, you’ve got technical issues. These can be solved with technical solutions. On the other end of the continuum, you’ve got human issues. These can be solved with discussions, agreement, empathy, and conversations (often dreaded or awkward).

I think that, as developers, we tend to gravitate towards the technical issues. That’s our safe space. But I suspect that bigger gains can be reaped by tackling the uncomfortable human issues.

This was definitely shocking to learn when I joined a company a few years ago and found that there was a mountain of performance work that I couldn’t do alone. I started trying to teach folks about performance, as well as holding office hours and hopping onto projects and teams that needed help. But I realized that all this work didn’t help. The website I was working on in my spare time was getting slower, despite my best efforts.

Frustrated and exhausted, one day I sat back in my chair and realized that I couldn’t do all this work alone. The real problem was this: there’s no incentive for folks to care. If performance magically improved by ten thousand percent, no one in the company would have noticed. Customers would have noticed, but we all probably wouldn’t have. Except me, because I’m a nerd.

In Ethan Marcotte’s latest talk, he describes this people problem when it comes to design systems:

Creating modular components isn’t the primary goal or even the primary benefit of creating a design system. And what’s more, a focus on process and people always leads to more sustainable systems.

Design systems are about good, quality front-end code just like performance is, too. But if people within an organization are not incentivized to use the components within a library or talk to the design systems team, then that’s where things quickly get bonkers.

I’d maybe simplify this people problem a bit: the codebase is easy to change, but the incentives within a company are not. And yet it’s the incentives that drive what kind of code gets written — what is acceptable, what needs to get fixed, how people work together. In short, we cannot be expected to fix the code without fixing the organization, too.

The most obvious incentives are money and performance ratings, or even hiring a person or team dedicated to this particular line of work. Improving visibility into performance problems and celebrating big wins is another thing that can be done to shift the balance and make folks care more about this whole new area of expertise. But these things really have to come from the top down; not from the the bottom up. At least that’s been true in my experience.

My point here is that there’s no single solution to fix the incentive problem in large organizations. It sounds silly, but in order to make that website, the biggest hurdles to overcome are those incentives. If no one cares about performance work today, then shouting and screaming and being a jerk about it won’t help at all.

Trust me, I have been that jerk.


The post People Problems appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

,

How to be productive as a designer and not waste time procrastinating

[Top]

Full Bleed

We’ve covered techniques before for when you want a full-width element within a constrained-width column, like an edge-to-edge image within a narrower column of text. There are loads of techniques.

Perhaps my favorite is this little utility class:

.full-width {   width: 100vw;   position: relative;   left: 50%;   right: 50%;   margin-left: -50vw;   margin-right: -50vw; }

That works as long as the column is centered and you don’t mind having to hide overflow-x on the column (or the body) as this can trigger horizontal overflow otherwise.

There was a little back and forth on some other ideas lately…

Josh Comeau blogged that you could set up a three-column grid, and mostly place content in the middle column, but then have the opportunity to bust out of it:

.wrapper {   display: grid;   grid-template-columns:     1fr     min(65ch, 100%)     1fr; } .wrapper > * {   grid-column: 2; } .full-bleed {   width: 100%;   grid-column: 1 / -1; }

I think this is clever. I’d probably use it. But I admit there are bits that feel weird to me. For instance…

  • Now everything within the container is a grid element. Not a huge deal, but the elements will behave slightly differently. No margin collapsing, for one.
  • You have to apply the default behavior you want to every single element. Rather than elements naturally stacking on top of each other, you have to select them and tell them where to go and let them stack themselves. Feels a little less like just going with the web’s grain. Then you still need a utility class to do the full bleed behavior.

What I really like about the idea is that it gives you this literal grid to work with. For example, your left spacer could be half the width of the right and that’s totally fine. It’s setting up that space to be potentially used, like Ethan talked about in his article on constrained grids.

Kilian Valkhof responded to the article with this idea:

body > *:not(img):not(video) {    position: relative;   max-width: 40rem;   margin: auto; }

Also very clever. This constrains the width of everything (in whatever container, and it wouldn’t have to be the body) except the elements you want to bust out (which could be a utility class there too, and not necessarily images and videos).

Again, to me, this feeling that I have to select every single element and provide it this fundamental information about layout feels slightly weird. Not like “don’t use it” weird, just not something I’m used to doing. Historically, I’m more comfortable sizing and positioning a container and letting the content in that container lay itself out without much further instruction.

You know what I like the most? That we have so many powerful layout tools in CSS and we have conversations about the pros and cons of pulling off exactly what we’re going for.


The post Full Bleed appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

,
[Top]

Layoutit Grid: Learning CSS Grid Visually With a Generator

Layoutit Grid is an interactive open source CSS Grid generator. It lets you draw your designs and see the code as you go. You can interact with the code, add or remove track lines and drag them around to change the sizing — and you get to see the CSS and HTML change in real time!

Animated gif of the tool which is split into three columns: one that sets the number of grid rows and columns, one to name and visualize the layout, and the last to see the code.
Add some tracks and see how they’re made in CSS

When you are done with a layout, you can create a CodePen or grab the code to jumpstart your next project. The tool brings the code to the forefront, helping you learn CSS grid as you work directly with it.

CSS Grid is a whole new way of thinking about layouts

We can now create robust responsive layouts for our web experiences. We can finally learn to design with a coherent set of layout tools instead of memorizing piles of hacks to force elements into position.

Now, I’m not saying a generator like this excuses us from knowing the code we write. We should all learn how CSS Grid and Flexbox work. Even if your stronghold is JavaScript, having a solid foundation in CSS knowledge is a powerful ally when communicating your ideas. When sharing a prototype for a component, a UX interaction, or even an algorithm in an online sandbox, the way in which your work is presented can make a big the difference. The ability to develop proper layouts — and define the styles that create them — is fundamental.

Crafting layouts in CSS should not be a daunting task. CSS Grid is actually quite fun to use! For example, using named grid areas feels like an ASCII art version of drawing a design on a piece of paper. Lets create the layout of a photos app, a feed of pics and the people in them side by side for its main content and the typical header, footer and a config sidebar.

.photos-app {   /* For our app layout, lets place things in a grid */   display: grid;   /* We want 3 columns and 3 rows, and these are the responsive      track sizes using `fr` (fraction of the remaining space) */   grid-template-columns: 20% 1fr 1fr;   grid-template-rows: 0.5fr 1.7fr 0.3fr;   /* Let's separate our tracks a bit */   gap: 1em;   /* We now have 3x3 cells, here is where each section is placed */   grid-template-areas:     "header header header"  /* a header stretching in the top row */     "config photos people"  /* a left sidebar, and our app content */     "footer footer footer"; /* and a footer along the bottom row  */ } 
 .the-header {   /* In each section, let's define the name we use to refence the area */   grid-area: "header"; }

This is just a small subset of what you can build with CSS Grid. The spec is quite flexible. Areas can also be placed directly using line numbers or names, or they can be placed implicitly by the browser, with the content distributed inside the grid automatically. And the spec continues to grow with additions, like subgrid.

At the same time, working with grids can be difficult, just like anything that requires a new way of thinking. It takes a lot of time to wrap our heads around this sort of thing. And one way to help do that is to…

Learn while playing

When you are learning CSS Grid, it is easy to feel intimidated by its notation and semantics. Until you develop some muscle memory for it, kickstarting the learning process with visual and interactive tools can be an excellent way to overcome that early trepidation. A lot of us have used generators while learning how to create shadows, gradients, Markdown tables, and so on. Generators, if built with care, are great learning aids.

Let’s use Layoutit Grid to recreate the same design in our example.

Generators like this aren’t meant to be leaned on forever; they’re a stepping stone. This particular one helps you experience the power of CSS Grid by materializing your designs in a few clicks along with the code to make it happen. This gives you the early wins that you need to push forward with the learning process. For some of us, generators permanently remain in our toolboxes. Not because we do not know how to craft the layouts by hand, but because having the visual feedback loop help us to quickly convert our ideas into code. So we keep playing with them.

Sarah Drasner has also created a CSS Grid generator that’s worth checking out as well.

Learn by building

Leniolabs recently open-sourced Layoutit Grid and added new features, like interactive code views, area edition, history and offline support. And there are several more in the making.

If you have ideas to improve the tool, get in touch! Open an issue and let’s discuss it on GitHub. Going into meta territory, you can also learn about the CSS Grid spec by helping us build the tool. 

We use the app to keep track of best practices in creating performant interactive web experiences. It is now powered by the newly released Vue 3 using <script setup> components and built with Vite, a new dev tool that doesn’t bundle the app while developing, which gives us instant feedback during development. If you are curious and want to build with us, fork the repo and let’s learn together!


The post Layoutit Grid: Learning CSS Grid Visually With a Generator appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , ,
[Top]

A’ Design Award Winners for Graphic Design and Visual Communication

[Top]

10+ Examples of Responsive Websites That Show Us How to Get It Right

[Top]

How to Create a Realistic Motion Blur with CSS Transitions

Before we delve into making a realistic motion blur in CSS, it’s worth doing a quick dive into what motion blur is, so we can have a better idea of what we’re trying to reproduce.

Have you ever taken a photo of something moving quickly, especially under low light, and it turned into a blurry streak? Or maybe the whole camera shook and the whole shot became a series of streaks? This is motion blur, and it’s a byproduct of how a camera works.

Motion Blur 101

Imagine a camera. It’s got a shutter, a door that opens to let light in, and then closes to stop the light from coming in. From the time it opens, to when it closes, is a single photograph, or a single frame of a moving image.

A blurry man wearing a red shirt on a blue bike speeding through the forest.
Real motion blur in action (Photo: Kevin Erdvig, Unsplash)

If the subject of the frame is moving during the time the shutter is open, we end up taking a photo of an object moving through the frame. On film, this shows up as being a steady smear, with the subject being in an infinite number of places between its starting point to its end. The moving object also ends up being semi-transparent, with parts of the background visible behind it.

What computers do to fake this is model several subframes, and then composite them together at a fraction of the opacity. Putting lots of copies of the same object in slightly different places along the path of motion creates a pretty convincing facsimile of a motion blur.

Video compositing apps tend to have settings for how many subdivisions their motion blur should have. If you set this value really low, you can see exactly how the technique works, like this, a frame of an animation of a simple white dot at four samples per frame:

Four overlapping white opaque circles on a black background.
Four samples per frame.
Twelve overlapping white opaque circles on a black background.
Here is 12 samples per frame.
Thirty-two overlapping white opaque circles on a black background.
And by the time we’re at 32 samples per frame, it’s pretty close to fully real, especially when seen at multiple frames per second.

The number of samples it takes to make a convincing motion blur is entirely relative to the content. Something small with sharp edges that’s moving super fast will need a lot of subframes; but something blurry moving slowly might need only a few. In general, using more will create a more convincing effect.

Doing this in CSS

In order to approximate this effect in CSS, we need to create a ton of identical elements, make them semi-transparent, and offset their animations by a tiny fraction of a second.

First, we’ll set up the base with the animation we want using a CSS transition. We’ll go with a simple black dot, and assign it a transform on hover (or tap, if you’re on mobile). We’ll also animate the border radius and color to show the flexibility of this approach.

Here is the base animation without motion blur:

Now, let’s make 20 identical copies of the black dot, all placed in the exact same place with absolute positioning. Each copy has an opacity of 10%, which is a little more than might be mathematically correct, but I find we need to make them more opaque to look solid enough.

The next step is where the magic happens. We add a slightly-increasing transition-delay value for each clone of our dot object. They’ll all run the exact same animation, but they’ll each be offset by three milliseconds. 

The beauty of this approach is that it creates a pseudo-motion blur effect that works for a ton of different animations. We can throw color changes on there, scaling transitions, odd timings, and the motion blur effect still works.

Using 20 object clones will work for plenty of fast and slow animations, but fewer can still produce a reasonable sense of motion blur. You may need to tweak the number of cloned objects, their opacity, and the amount of transition delay to work with your particular animation. The demo we just looked at has the blur effect slightly overclocked to make it more prominent.


Eventually, with the progress of computer power, I expect that some of the major browsers might start offering this effect natively. Then we can do away with the ridiculousness of having 20 identical objects. In the meantime, this is a reasonable way to approximate a realistic motion blur.


The post How to Create a Realistic Motion Blur with CSS Transitions appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , ,
[Top]

Let’s Create Our Own Authentication API with Nodejs and GraphQL

Authentication is one of the most challenging tasks for developers just starting with GraphQL. There are a lot of technical considerations, including what ORM would be easy to set up, how to generate secure tokens and hash passwords, and even what HTTP library to use and how to use it. 

In this article, we’ll focus on local authentication. It’s perhaps the most popular way of handling authentication in modern websites and does so by requesting the user’s email and password (as opposed to, say, using Google auth.)

Moreover, This article uses Apollo Server 2, JSON Web Tokens (JWT), and Sequelize ORM to build an authentication API with Node.

Handling authentication

As in, a log in system:

  • Authentication identifies or verifies a user.
  • Authorization is validating the routes (or parts of the app) the authenticated user can have access to. 

The flow for implementing this is:

  1. The user registers using password and email
  2. The user’s credentials are stored in a database
  3. The user is redirected to the login when registration is completed
  4. The user is granted access to specific resources when authenticated
  5. The user’s state is stored in any one of the browser storage mediums (e.g. localStorage, cookies, session) or JWT.

Pre-requisites

Before we dive into the implementation, here are a few things you’ll need to follow along.

Dependencies 

This is a big list, so let’s get into it:

  • Apollo Server: An open-source GraphQL server that is compatible with any kind of GraphQL client. We won’t be using Express for our server in this project. Instead, we will use the power of Apollo Server to expose our GraphQL API.
  • bcryptjs: We want to hash the user passwords in our database. That’s why we will use bcrypt. It relies on Web Crypto API‘s getRandomValues interface to obtain secure random numbers.
  • dotenv: We will use dotenv to load environment variables from our .env file. 
  • jsonwebtoken: Once the user is logged in, each subsequent request will include the JWT, allowing the user to access routes, services, and resources that are permitted with that token. jsonwebtokenwill be used to generate a JWT which will be used to authenticate users.
  • nodemon: A tool that helps develop Node-based applications by automatically restarting the node application when changes in the directory are detected. We don’t want to be closing and starting the server every time there’s a change in our code. Nodemon inspects changes every time in our app and automatically restarts the server. 
  • mysql2: An SQL client for Node. We need it connect to our SQL server so we can run migrations.
  • sequelize: Sequelize is a promise-based Node ORM for Postgres, MySQL, MariaDB, SQLite and Microsoft SQL Server. We will use Sequelize to automatically generate our migrations and models. 
  • sequelize cli: We will use Sequelize CLI to run Sequelize commands. Install it globally with yarn add --global sequelize-cli  in the terminal.

Setup directory structure and dev environment

Let’s create a brand new project. Create a new folder and this inside of it:

yarn init -y

The -y flag indicates we are selecting yes to all the yarn init questions and using the defaults.

We should also put a package.json file in the folder, so let’s install the project dependencies:

yarn add apollo-server bcrpytjs dotenv jsonwebtoken nodemon sequelize sqlite3

Next, let’s add Babeto our development environment:

yarn add babel-cli babel-preset-env babel-preset-stage-0 --dev

Now, let’s configure Babel. Run touch .babelrc in the terminal. That creates and opens a Babel config file and, in it, we’ll add this:

{   "presets": ["env", "stage-0"] }

It would also be nice if our server starts up and migrates data as well. We can automate that by updating package.json with this:

"scripts": {   "migrate": " sequelize db:migrate",   "dev": "nodemon src/server --exec babel-node -e js",   "start": "node src/server",   "test": "echo "Error: no test specified" && exit 1" },

Here’s our package.json file in its entirety at this point:

{   "name": "graphql-auth",   "version": "1.0.0",   "main": "index.js",   "scripts": {     "migrate": " sequelize db:migrate",     "dev": "nodemon src/server --exec babel-node -e js",     "start": "node src/server",     "test": "echo "Error: no test specified" && exit 1"   },   "dependencies": {     "apollo-server": "^2.17.0",     "bcryptjs": "^2.4.3",     "dotenv": "^8.2.0",     "jsonwebtoken": "^8.5.1",     "nodemon": "^2.0.4",     "sequelize": "^6.3.5",     "sqlite3": "^5.0.0"   },   "devDependencies": {     "babel-cli": "^6.26.0",     "babel-preset-env": "^1.7.0",     "babel-preset-stage-0": "^6.24.1"   } }

Now that our development environment is set up, let’s turn to the database where we’ll be storing things.

Database setup

We will be using MySQL as our database and Sequelize ORM for our relationships. Run sequelize init (assuming you installed it globally earlier). The command should create three folders: /config /models and /migrations. At this point, our project directory structure is shaping up. 

Let’s configure our database. First, create a .env file in the project root directory and paste this:

NODE_ENV=development DB_HOST=localhost DB_USERNAME= DB_PASSWORD= DB_NAME=

Then go to the /config folder we just created and rename the config.json file in there to config.js. Then, drop this code in there:

require('dotenv').config() const dbDetails = {   username: process.env.DB_USERNAME,   password: process.env.DB_PASSWORD,   database: process.env.DB_NAME,   host: process.env.DB_HOST,   dialect: 'mysql' } module.exports = {   development: dbDetails,   production: dbDetails }

Here we are reading the database details we set in our .env file. process.env is a global variable injected by Node and it’s used to represent the current state of the system environment.

Let’s update our database details with the appropriate data. Open the SQL database and create a table called graphql_auth. I use Laragon as my local server and phpmyadmin to manage database tables.

What ever you use, we’ll want to update the .env file with the latest information:

NODE_ENV=development DB_HOST=localhost DB_USERNAME=graphql_auth DB_PASSWORD= DB_NAME=<your_db_username_here>

Let’s configure Sequelize. Create a .sequelizerc file in the project’s root and paste this:

const path = require('path') 
 module.exports = {   config: path.resolve('config', 'config.js') }

Now let’s integrate our config into the models. Go to the index.js in the /models folder and edit the config variable.

const config = require(__dirname + '/../../config/config.js')[env]

Finally, let’s write our model. For this project, we need a User model. Let’s use Sequelize to auto-generate the model. Here’s what we need to run in the terminal to set that up:

sequelize model:generate --name User --attributes username:string,email:string,password:string

Let’s edit the model that creates for us. Go to user.js in the /models folder and paste this:

'use strict'; module.exports = (sequelize, DataTypes) => {   const User = sequelize.define('User', {     username: {       type: DataTypes.STRING,     },     email: {       type: DataTypes.STRING,       },     password: {       type: DataTypes.STRING,     }   }, {});   return User; };

Here, we created attributes and fields for username, email and password. Let’s run a migration to keep track of changes in our schema:

yarn migrate

Let’s now write the schema and resolvers.

Integrate schema and resolvers with the GraphQL server 

In this section, we’ll define our schema, write resolver functions and expose them on our server.

The schema

In the src folder, create a new folder called /schema and create a file called schema.js. Paste in the following code:

const { gql } = require('apollo-server') const typeDefs = gql`   type User {     id: Int!     username: String     email: String!   }   type AuthPayload {     token: String!     user: User!   }   type Query {     user(id: Int!): User     allUsers: [User!]!     me: User   }   type Mutation {     registerUser(username: String, email: String!, password: String!): AuthPayload!     login (email: String!, password: String!): AuthPayload!   } ` module.exports = typeDefs

Here we’ve imported graphql-tag from apollo-server. Apollo Server requires wrapping our schema with gql

The resolvers

In the src folder, create a new folder called /resolvers and create a file in it called resolver.js. Paste in the following code:

const bcrypt = require('bcryptjs') const jsonwebtoken = require('jsonwebtoken') const models = require('../models') require('dotenv').config() const resolvers = {     Query: {       async me(_, args, { user }) {         if(!user) throw new Error('You are not authenticated')         return await models.User.findByPk(user.id)       },       async user(root, { id }, { user }) {         try {           if(!user) throw new Error('You are not authenticated!')           return models.User.findByPk(id)         } catch (error) {           throw new Error(error.message)         }       },       async allUsers(root, args, { user }) {         try {           if (!user) throw new Error('You are not authenticated!')           return models.User.findAll()         } catch (error) {           throw new Error(error.message)         }       }     },     Mutation: {       async registerUser(root, { username, email, password }) {         try {           const user = await models.User.create({             username,             email,             password: await bcrypt.hash(password, 10)           })           const token = jsonwebtoken.sign(             { id: user.id, email: user.email},             process.env.JWT_SECRET,             { expiresIn: '1y' }           )           return {             token, id: user.id, username: user.username, email: user.email, message: "Authentication succesfull"           }         } catch (error) {           throw new Error(error.message)         }       },       async login(_, { email, password }) {         try {           const user = await models.User.findOne({ where: { email }})           if (!user) {             throw new Error('No user with that email')           }           const isValid = await bcrypt.compare(password, user.password)           if (!isValid) {             throw new Error('Incorrect password')           }           // return jwt           const token = jsonwebtoken.sign(             { id: user.id, email: user.email},             process.env.JWT_SECRET,             { expiresIn: '1d'}           )           return {            token, user           }       } catch (error) {         throw new Error(error.message)       }     }   }, 
 } module.exports = resolvers

That’s a lot of code, so let’s see what’s happening in there.

First we imported our models, bcrypt and  jsonwebtoken, and then initialized our environmental variables. 

Next are the resolver functions. In the query resolver, we have three functions (me, user and allUsers):

  • me query fetches the details of the currently loggedIn user. It accepts a user object as the context argument. The context is used to provide access to our database which is used to load the data for a user by the ID provided as an argument in the query.
  • user query fetches the details of a user based on their ID. It accepts id as the context argument and a user object. 
  • alluser query returns the details of all the users.

user would be an object if the user state is loggedIn and it would be null, if the user is not. We would create this user in our mutations. 

In the mutation resolver, we have two functions (registerUser and loginUser):

  • registerUser accepts the username, email  and password of the user and creates a new row with these fields in our database. It’s important to note that we used the bcryptjs package to hash the users password with bcrypt.hash(password, 10). jsonwebtoken.sign synchronously signs the given payload into a JSON Web Token string (in this case the user id and email). Finally, registerUser returns the JWT string and user profile if successful and returns an error message if something goes wrong.
  • login accepts email and password , and checks if these details match with the one that was supplied. First, we check if the email value already exists somewhere in the user database.
models.User.findOne({ where: { email }}) if (!user) {   throw new Error('No user with that email') }

Then, we use bcrypt’s bcrypt.compare method to check if the password matches. 

const isValid = await bcrypt.compare(password, user.password) if (!isValid) {   throw new Error('Incorrect password') }

Then, just like we did previously in registerUser, we use jsonwebtoken.sign to generate a JWT string. The login mutation returns the token and user object.

Now let’s add the JWT_SECRET to our .env file.

JWT_SECRET=somereallylongsecret

The server

Finally, the server! Create a server.js in the project’s root folder and paste this:

const { ApolloServer } = require('apollo-server') const jwt =  require('jsonwebtoken') const typeDefs = require('./schema/schema') const resolvers = require('./resolvers/resolvers') require('dotenv').config() const { JWT_SECRET, PORT } = process.env const getUser = token => {   try {     if (token) {       return jwt.verify(token, JWT_SECRET)     }     return null   } catch (error) {     return null   } } const server = new ApolloServer({   typeDefs,   resolvers,   context: ({ req }) => {     const token = req.get('Authorization') || ''     return { user: getUser(token.replace('Bearer', ''))}   },   introspection: true,   playground: true }) server.listen({ port: process.env.PORT || 4000 }).then(({ url }) => {   console.log(`🚀 Server ready at $ https://css-tricks.com/lets-create-our-own-authentication-api-with-nodejs-and-graphql/`); });

Here, we import the schema, resolvers and jwt, and initialize our environment variables. First, we verify the JWT token with verify. jwt.verify accepts the token and the JWT secret as parameters.

Next, we create our server with an ApolloServer instance that accepts typeDefs and resolvers.

We have a server! Let’s start it up by running yarn dev in the terminal.

Testing the API

Let’s now test the GraphQL API with GraphQL Playground. We should be able to register, login and view all users — including a single user — by ID.

We’ll start by opening up the GraphQL Playground app or just open localhost://4000 in the browser to access it.

Mutation for register user

mutation {   registerUser(username: "Wizzy", email: "ekpot@gmail.com", password: "wizzyekpot" ){     token   } }

We should get something like this:

{   "data": {     "registerUser": {       "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTUsImVtYWlsIjoiZWtwb3RAZ21haWwuY29tIiwiaWF0IjoxNTk5MjQwMzAwLCJleHAiOjE2MzA3OTc5MDB9.gmeynGR9Zwng8cIJR75Qrob9bovnRQT242n6vfBt5PY"     }   } }

Mutation for login 

Let’s now log in with the user details we just created:

mutation {   login(email:"ekpot@gmail.com" password:"wizzyekpot"){     token   } }

We should get something like this:

{   "data": {     "login": {       "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTUsImVtYWlsIjoiZWtwb3RAZ21haWwuY29tIiwiaWF0IjoxNTk5MjQwMzcwLCJleHAiOjE1OTkzMjY3NzB9.PDiBKyq58nWxlgTOQYzbtKJ-HkzxemVppLA5nBdm4nc"     }   } }

Awesome!

Query for a single user

For us to query a single user, we need to pass the user token as authorization header. Go to the HTTP Headers tab.

Showing the GraphQL interface with the HTTP Headers tab highlighted in red in the bottom left corner of the screen,

…and paste this:

{   "Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTUsImVtYWlsIjoiZWtwb3RAZ21haWwuY29tIiwiaWF0IjoxNTk5MjQwMzcwLCJleHAiOjE1OTkzMjY3NzB9.PDiBKyq58nWxlgTOQYzbtKJ-HkzxemVppLA5nBdm4nc" }

Here’s the query:

query myself{   me {     id     email     username   } }

And we should get something like this:

{   "data": {     "me": {       "id": 15,       "email": "ekpot@gmail.com",       "username": "Wizzy"     }   } }

Great! Let’s now get a user by ID:

query singleUser{   user(id:15){     id     email     username   } }

And here’s the query to get all users:

{   allUsers{     id     username     email   } }

Summary

Authentication is one of the toughest tasks when it comes to building websites that require it. GraphQL enabled us to build an entire Authentication API with just one endpoint. Sequelize ORM makes creating relationships with our SQL database so easy, we barely had to worry about our models. It’s also remarkable that we didn’t require a HTTP server library (like Express) and use Apollo GraphQL as middleware. Apollo Server 2, now enables us to create our own library-independent GraphQL servers!

Check out the source code for this tutorial on GitHub.


The post Let’s Create Our Own Authentication API with Nodejs and GraphQL appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , ,
[Top]

How to Recreate the Ripple Effect of Material Design Buttons

When I first discovered Material Design, I was particularly inspired by its button component. It uses a ripple effect to give users feedback in a simple, elegant way.

How does this effect work? Material Design’s buttons don’t just sport a neat ripple animation, but the animation also changes position depending on where each button is clicked.

We can achieve the same result. We’ll start with a concise solution using ES6+ JavaScript, before looking at a few alternative approaches.

HTML

Our goal is to avoid any extraneous HTML markup. So we’ll go with the bare minimum:

<button>Find out more</button>

Styling the button

We’ll need to style a few elements of our ripple dynamically, using JavaScript. But everything else can be done in CSS. For our buttons, it’s only necessary to include two properties.

button {   position: relative;   overflow: hidden; }

Using position: relative allows us to use position: absolute on our ripple element, which we need to control its position. Meanwhile, overflow: hidden prevents the ripple from exceeding the button’s edges. Everything else is optional. But right now, our button is looking a bit old school. Here’s a more modern starting point:

/* Roboto is Material's default font */ @import url('https://fonts.googleapis.com/css2?family=Roboto&display=swap');  button {   position: relative;   overflow: hidden;   transition: background 400ms;   color: #fff;   background-color: #6200ee;   padding: 1rem 2rem;   font-family: 'Roboto', sans-serif;   font-size: 1.5rem;   outline: 0;   border: 0;   border-radius: 0.25rem;   box-shadow: 0 0 0.5rem rgba(0, 0, 0, 0.3);   cursor: pointer; }

Styling the ripples

Later on, we’ll be using JavaScript to inject ripples into our HTML as spans with a .ripple class. But before turning to JavaScript, let’s define a style for those ripples in CSS so we have them at the ready:

span.ripple {   position: absolute; /* The absolute position we mentioned earlier */   border-radius: 50%;   transform: scale(0);   animation: ripple 600ms linear;   background-color: rgba(255, 255, 255, 0.7); }

To make our ripples circular, we’ve set the border-radius to 50%. And to ensure each ripple emerges from nothing, we’ve set the the default scale to 0. Right now, we won’t be able to see anything because we don’t yet have a value for the top, left, width, or height properties; we’ll soon be injecting these properties with JavaScript.

As for our CSS, the last thing we need to add is an end state for the animation:

@keyframes ripple {   to {     transform: scale(4);     opacity: 0;   } }

Notice that we’re not defining a starting state with the from keyword in the keyframes? We can omit from and CSS will construct the missing values based on those that apply to the animated element. This occurs if the relevant values are stated explicitly — as in transform: scale(0) — or if they’re the default, like opacity: 1.

Now for the JavaScript

Finally, we need JavaScript to dynamically set the position and size of our ripples. The size should be based on the size of the button, while the position should be based on both the position of the button and of the cursor.

We’ll start with an empty function that takes a click event as its argument:

function createRipple(event) {   // }

We’ll access our button by finding the currentTarget of the event.

const button = event.currentTarget;

Next, we’ll instantiate our span element, and calculate its diameter and radius based on the width and height of the button.

const circle = document.createElement("span"); const diameter = Math.max(button.clientWidth, button.clientHeight); const radius = diameter / 2;

We can now define the remaining properties we need for our ripples: the left, top, width and height.

circle.style.width = circle.style.height = `$ {diameter}px`; circle.style.left = `$ {event.clientX - (button.offsetLeft + radius)}px`; circle.style.top = `$ {event.clientY - (button.offsetTop + radius)}px`; circle.classList.add("ripple"); 

Before adding our span element to the DOM, it’s good practice to check for any existing ripples that might be leftover from previous clicks, and remove them before executing the next one.

const ripple = button.getElementsByClassName("ripple")[0];  if (ripple) {   ripple.remove(); }

As a final step, we append the span as a child to the button element so it is injected inside the button.

button.appendChild(circle);

With our function complete, all that’s left is to call it. This could be done in a number of ways. If we want to add the ripple to every button on our page, we can use something like this:

const buttons = document.getElementsByTagName("button"); for (const button of buttons) {   button.addEventListener("click", createRipple); }

We now have a working ripple effect!

Taking it further

What if we want to go further and combine this effect with other changes to our button’s position or size? The ability to customize is, after all, one of the main advantages we have by choosing to recreate the effect ourselves. To test how easy it is to extend our function, I decided to add a “magnet” effect, which causes our button to move towards our cursor when the cursor’s within a certain area.

We need to rely on some of the same variables defined in the ripple function. Rather than repeating code unnecessarily, we should store them somewhere they’re accessible to both methods. But we should also keep the shared variables scoped to each individual button. One way to achieve this is by using classes, as in the example below:

Since the magnet effect needs to keep track of the cursor every time it moves, we no longer need to calculate the cursor position to create a ripple. Instead, we can rely on cursorX and cursorY.

Two important new variables are magneticPullX and magneticPullY. They control how strongly our magnet method pulls the button after the cursor. So, when we define the center of our ripple, we need to adjust for both the position of the new button (x and y) and the magnetic pull.

const offsetLeft = this.left + this.x * this.magneticPullX; const offsetTop = this.top + this.y * this.magneticPullY;

To apply these combined effects to all our buttons, we need to instantiate a new instance of the class for each one:

const buttons = document.getElementsByTagName("button"); for (const button of buttons) {   new Button(button); }

Other techniques

Of course, this is only one way to achieve a ripple effect. On CodePen, there are lots of examples that show different implementations. Below are some of my favourites.

CSS-only

If a user has disabled JavaScript, our ripple effect doesn’t have any fallbacks. But it’s possible to get close to the original effect with just CSS, using the :active pseudo-class to respond to clicks. The main limitation is that the ripple can only emerge from one spot — usually the center of the button — rather than responding to the position of our clicks. This example by Ben Szabo is particularly concise:

Pre-ES6 JavaScript

Leandro Parice’s demo is similar to our implementation but it’s compatible with earlier versions of JavaScript: 

jQuery 

This example use jQuery to achieve the ripple effect. If you already have jQuery as a dependency, it could help save you a few lines of code. 

React

Finally, one last example from me. Although it’s possible to use React features like state and refs to help create the ripple effect, these aren’t strictly necessary. The position and size of the ripple both need to be calculated for every click, so there’s no advantage to holding that information in state. Plus, we can access our button element from the click event, so we don’t need refs either.

This React example uses a createRipple function identical to that of this article’s first implementation. The main difference is that — as a method of the Button component — our function is scoped to that component. Also, the onClick event listener is now part of our JSX:


The post How to Recreate the Ripple Effect of Material Design Buttons appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , ,
[Top]

Run Gulp as You Open a VS Code Project

When I open my local project for this very site, there is a 100% chance that I need to run this command before anything else: gulp. I set that up fresh less than a year ago so I’m on the latest-and-greatest stuff and have my workflow just how I like it. I did a few more tweaks a few months later to make things a smidge nicer (even adding a fancy fun little dock icon!).

That’s when I learned about VS Code Tasks. Generically, the can just run command line tasks that you configure whenever you choose to run them by name. But I’m particularly compelled by the idea that they can run when you open a project.

It’s this easy to run Gulp:

{   "version": "2.0.0",   "tasks": [     {       "label": "Run Gulp",       "command": "gulp",       "type": "shell",       "runOptions": {         "runOn": "folderOpen"       }     }   ] }

Except… that started to fail on my machine. I use nvm to manage Node versions, and despite my best effort to nvm alias default to the the correct version of Node that works nicely with Gulp, the Node version was always wrong, and thus running gulp would fail. The trick is to run nvm use first (which sets the correct version from my .nvmrc file), then gulp runs fine.

That works fine in a fresh terminal window, but for some reason, even making the command run two tasks like this (chaining them with a semicolon):

"command": "nvm use; gulp",

…it would still fail. It didn’t know what nvm meant. I don’t know what the heart of the problem is exactly (why one terminal doesn’t know the same things that another terminal does), but I did manage to sort out that the global nvm has a shell script with one job: defining the nvm command. So you “source” that, as they say, and then the nvm command works.

So my final setup is:

{   "version": "2.0.0",   "tasks": [     {       "label": "Run Gulp",       "command": ". ~/.nvm/nvm.sh; nvm use; gulp",       "type": "shell",       "runOptions": {         "runOn": "folderOpen"       }     }   ] } 

And that, dear readers, runs Gulp perfectly when I open my CSS-Tricks project, which is exactly what I wanted.

High five to Jen Luker who went on this journey with me and helped me get it to the finish line. 🤚


The post Run Gulp as You Open a VS Code Project appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , ,
[Top]