Tag: Under

Weaving One Element Over and Under Another Element

In this post, we’re going to use CSS superpowers to create a visual effect where two elements overlap and weave together. The epiphany for this design came during a short burst of spiritual inquisitiveness where I ended up at The Bible Project’s website. They make really cool animations, and I mean, really cool animations.

My attention, however, deviated from spiritualism to web design as I kept spotting these in-and-out border illustrations.

Screenshot form The Bible Project website.

I wondered if a similar could be made from pure CSS… and hallelujah, it’s possible!

See the Pen
Over and under border design using CSS
by Preethi Sam (@rpsthecoder)
on CodePen.

The principal CSS standards we use in this technique are CSS Blend Modes and CSS Grid.

First, we start with an image and a rotated frame in front of that image.

<div class="design">   <img src="bird-photo.jpg">   <div class="rotated-border"></div> </div>
.design {   position: relative;   height: 300px;   width: 300px; }  .design > * {   position: absolute;   height: 100%;   width: 100%; }  .rotated-border {   box-sizing: border-box;   border: 15px #eb311f solid;   transform: rotate(45deg);   box-shadow: 0 0 10px #eb311f, inset 0 0 20px #eb311f; }

The red frame is created using border. Its box-sizing is set to include the border size in the dimensions of the box so that the frame is centered around the picture after being rotated. Otherwise, the frame will be bigger than the image and get pulled towards the bottom-right corner.

Then we pick a pair of opposite corners of the image and overlay their quadrants with their corresponding portion in a copy of the same image as before. This hides the red frame in those corners.

We basically need to make a cut portion of the image that looks like below to go on top of the red frame.

The visible two quadrants will lay on top of the .rotated-border element.

So, how do we alter the image so that only two quadrants of the image are visible? CSS Blend Modes! The multiply value is what we’re going to reach for in this instance. This adds transparency to an element by stripping white from the image to reveal what’s behind the element.

Chris has a nice demo showing how a red background shows through an image with the multiply blend mode.

See the Pen
Background Blending
by Chris Coyier (@chriscoyier)
on CodePen.

OK, nice, but what about those quadrants? We cover the quadrants we want to hide with white grid cells that will cause the image to bleed all the way through in those specific areas with a copy of the bird image right on top of it in the sourcecode.

<div id="design">     <img src="bird-photo.jpg">     <div class="rotated-border"></div>      <div class="blend">       <!-- Copy of the same image -->       <img src="bird-photo.jpg">       <div class="grid">         <!-- Quadrant 1: Top Left -->         <div></div>         <!-- Quadrant 2: Top Right -->         <div data-white></div>         <!-- Quadrant 3: Bottom Left -->         <div data-white></div>         <!-- Quadrant 4: Bottom Right -->         <div></div>       </div>     </div>  </div>
.blend > * {   position: absolute;   height: 100%;   width: 100%; }  /* Establishes our grid */ .grid {   display: grid;   grid: repeat(2, 1fr) / repeat(2, 1fr); }  /* Adds white to quadrants with this attribute */ [data-white]{   background-color: white; }

The result is a two-by-two grid with its top-right and bottom-left quadrants that are filled with white, while being grouped together with the image inside .blend.

To those of you new to CSS Grid, what we’re doing is adding a new .grid element that becomes a “grid” element when we declare display: grid;. Then we use the grid property (which is a shorthand that combines grid-template-columns and grid-template-rows) to create two equally spaced rows and columns. We’re basically saying, “Hey, grid, repeat two equal columns and repeat two equal rows inside of yourself to form four boxes.”

A copy of the image and a grid with white cells on top of the red border.

Now we apply the multiply blend mode to .blend using the mix-blend-mode property.

.blend { mix-blend-mode: multiply; }

The result:

As you can see, the blend mode affects all four quadrants rather than just the two we want to see through. That means we can see through all four quadrants, which reveals all of the red rotated box.

We want to bring back the white we lost in top-left and bottom-right quadrants so that they hide the red rotated box behind them. Let’s add a second grid, this time on top of .blend in the sourcecode.

<div id="design">   <img src="bird-photo.jpg">   <div class="rotated-border"></div>        <!-- A second grid  -->   <!-- This time, we're adding white to the image quandrants where we want to hide the red frame  -->   <div class="grid">     <!-- Quadrant 1: Top Left -->     <div data-white></div>     <!-- Quadrant 2: Top Right -->     <div></div>     <!-- Quadrant 3: Bottom Left -->     <div></div>     <!-- Quadrant 4: Bottom Right -->     <div data-white></div>   </div>    <div class="blend">     <img src="bird-photo.jpg">     <div class="grid">       <!-- Quadrant 1: Top Left -->       <div></div>       <!-- Quadrant 2: Top Right -->       <div data-white></div>       <!-- Quadrant 3: Bottom Left -->       <div data-white></div>       <!-- Quadrant 4: Bottom Right -->       <div></div>     </div>   </div>  </div>

The result!

Summing up, the browser renders the elements in our demo like this:
​​

  1. ​​At bottommost is the bird image (represented by the leftmost grey shape in the diagram below)
  2. ​​Then a rotated red frame
  3. ​​On top of them is a grid with top-left and bottom-right white cells (corners where we don’t want to see the red frame in the final result)
  4. ​​Followed by a copy of the bird image from before and a grid with top-right and bottom-left white cells (corners where we do want to see the red frame) – both grouped together and given the blending mode, multiply​.

You may have some questions about the approach I used in this post. Let me try to tackle those.

What about using CSS Masking instead of CSS Blend Modes?

For those of you familiar with CSS Masking – using either mask-image or clip-path – it can be an alternative to using blend mode.

I prefer blending because it has better browser support than masks and clipping. For instance, WebKit browsers don’t support SVG <mask> reference in the CSS mask-image property and they also provide partial support for clip-path values, especially Safari.

Another reason for choosing blend mode is the convenience of being able to use grid to create a simple white structure instead of needing to create images (whether they are SVG or otherwise).

Then again, I’m fully on board the CSS blend mode train, having used it for knockout text, text fragmentation effect… and now this. I’m pretty much all in on it.

Why did you use grid for the quadrants?

The white boxes needed in the demo can be created by other means, of course, but grid makes things easier for me. For example, we could’ve leaned on flexbox instead. Use what works for you.

Why use a data-attribute on the grid quadrant elements to make them white?

I used it while coding the demo without thinking much about it – I guess it was quicker to type. I later thought of changing it to a class, but left it as it is because the HTML looked neater that way… at least to me. 🙂

Is multiply the only blend mode that works for this example?

Nope. If you already know about blend modes then you probably also know you can use either screen, darken, or lighten to get a similar effect. (Both screen and lighten will need black grid cells instead of white.)

The post Weaving One Element Over and Under Another Element appeared first on CSS-Tricks.

CSS-Tricks

, , , ,

Form Validation in Under an Hour with Vuelidate

Form validation has a reputation for being tricky to implement. In this tutorial, we’ll break things down to alleviate some of that pain. Creating nice abstractions for forms is something that Vue.js excels at and Vuelidate is personally my favorite option for validations because it doesn’t require a lot of hassle. Plus, it’s really flexible, so we don’t even have to do it how I’m going to cover it here. This is just a launching point.

If you simply want to copy and paste my full working example, it’s at the end. Go ahead. I won’t tell. Then your time spent is definitely under an hour and more, like, two minutes amirite?! Ahh, the internet is a beautiful place.

You may find you need to modify the form we’re using in this post so, in that case, you can read the full thing. We’ll start with a simple case and gradually build out a more concrete example. Finally, we’ll go through how to show form errors when the user has completed the form.

Simplest case: showing the entry once you’re done with the input

First, let’s show how we’d work with Vuelidate. We’ll need to create an object called validations that will mirror the data structure of what we’re trying to capture in the form. In the simplest terms, it would look like this:

data: {   name: ‘’ }, validations: {   name: {     required   } }

This would create an object within computed properties that we can find with $ v. It looks like this in Vue DevTools:

computed properties when empty

A couple things to note here: $ v is a computed property. This is great because that means it’s cached until something updates, which is a very performant way to deal with these state changes. Check out my article here if you want more background on this concept.

Another thing to note: there are two objects — one general object about all validations (there’s only one here currently) and one about the property name in specific. This is great because if we’re looking for general information about all fields, we have that information. And if we need to gather specific data, we have that too.

Let’s take a look at what happens when we start typing in that input:

random typing shown in computed properties

We can see in data that we have… well, me typing like a lunatic. But let’s check out some of these other fields. $ dirty, in this case, refers to whether the form has been touched at all. We can also see that the $ model field is now filled in for the name object, which mirrors what’s in data.

$ error and $ invalid sound the same but are actually a little different. $ invalid is checking if it passes validation, but $ error checks both for something that’s $ invalid and whether or not it’s $ dirty (whether the form has been touched yet or not). If this all seems like a lot to parse (haha get it? parse?), don’t worry, we’ll walk through many of these pieces step by step.

Installing Vuelidate and creating our first form validation

OK, so that was a very simple example. Let’s build something real out of it. We’ll bring this into our application and this time we’ll make the field required and give it a minimum length requirement. In the Vue app, we’ll first add Vuelidate:

yarn add vuelidate

Now, let’s go into the main.js file and update it as follows:

import Vue from 'vue'; import Vuelidate from "vuelidate"; import App from './App.vue'; import store from './store';  Vue.use(Vuelidate); Vue.config.productionTip = false  new Vue({  store,  render: h => h(App) }).$ mount('#app')

Now, in whatever component holds the form element, let’s first import the validators we’ll need:

import { required, minLength } from 'vuelidate/lib/validators'

Then, we’ll put the data inside of a function so we can reuse the component. You likely know about that one. Next, we’ll put our name form field in an object, because typically, we’d want to capture all of the form data together.

We’ll also need to include the validations, which will mirror our data. We’ll use required again, but this time we’ll also add a key/value pair for the minimum length of the characters, minLength(x), which will look something like this:

<script> import { required, minLength } from 'vuelidate/lib/validators'  export default {  data() {    return {      formResponses: {        name: '',      }    }  },  validations: {    formResponses: {      name: {        required,         minLength: minLength(2)      },    }  } } </script>

Next, in the template, we’ll create a label for accessibility purposes. Instead of using what’s in the data to create the relationship in v-model, we’ll use that computed property ($ model) that we saw earlier in the validations object.

<template>  <div id="app">    <label for="fname">Name*</label>    <input id="fname" class="full" v-model="$ v.formResponses.name.$ model" type="text">  </div> </template>

Finally, beneath the form input, we’ll place some text beneath the form. We can use required attached to formResponses.name to see if it evaluates correctly and whether it’s provided at all. We can also see if there’s more than the minimum length of characters. We even have a params object that will tell us the number of characters we specified. We’ll use all of this to create informative error messages for our user.

<p class="error" v-if="!$ v.formResponses.name.required">this field is required</p> <p class="error" v-if="!$ v.formResponses.name.minLength">Field must have at least {{ $ v.formResponses.name.$ params.minLength.min }} characters.</p>

And we’ll style our error class so it’s clear at a glance that they’re errors.

.error {   color: red; }

Be a little lazy

You may have noticed in that last demo that the errors are present right away and update while typing. Personally, I don’t like to show form validations that way because I think it’s distracting and confusing. What I like to do is wait to evaluate until typing has completed. For that kind of interaction, Vue comes equipped with a modifier for v-model: v-model.lazy. This will only evaluate the two-way binding once the user has completed the task with the input.

We can now improve on our single form input like this:

<label for="fname">Name*</label> <input id="fname" class="full" v-model.lazy="$ v.formResponses.name.$ model" type="text">

Creating custom validators

Vuelidate comes with a lot of validators out of the box, which is really helpful. However, there are times when we need something a little more custom. Let’s make a custom validator for a strong password, and check that it matches with Vuelidate’s sameAs validator

The first thing we’ll do is make a label attached to an input, and the input will be type="password".

<section>   <label for="fpass1">Password*</label>   <input id="fpass1" v-model="$ v.formResponses.password1.$ model" type="password"> </section>

In our data, we’ll create password1 and password2 (which we’ll use these in a moment to validate matching passwords) in our formResponses object, and import what we need from the validators.

import { required, minLength, email, sameAs } from "vuelidate/lib/validators";  export default {  data() {    return {      formResponses: {        name: null,        email: null,        password1: null,        password2: null      }    };  },

Then, we’ll create our custom validator. In the code below you can see that we’re using regex for different types of evaluation. We’ll create a strongPassword method, passing in our password1, and then we can check it several ways with .test(), which works as you might expect: it has to pass true if it is passing and false if not.

validations: {   formResponses: {     name: {       required,       minLength: minLength(3)     },     email: {       required,       email     },     password1: {       required,       strongPassword(password1) {         return (           /[a-z]/.test(password1) && // checks for a-z           /[0-9]/.test(password1) && // checks for 0-9           /W|_/.test(password1) && // checks for special char           password1.length >= 6         );       }     },  }

I am separating out each line so you can see what’s going on, but we could also write the whole thing as a one-liner like this:

const regex = /^[a-zA-Z0-9!@#$ %^&*)(+=._-]{6,}$ /g

I prefer to break it out because it is easier to modify.

This allows us to make the error text for our validation. We can make it say whatever we like, or even take this out of a v-if and make it present on the page. Up to you!

<section>   <label for="fpass1">Password*</label>   <input id="fpass1" v-model="$ v.formResponses.password1.$ model" type="password">   <p class="error" v-if="!$ v.formResponses.password1.required">this field is required</p>   <p class="error" v-if="!$ v.formResponses.password1.strongPassword">Strong passwords need to have a letter, a number, a special character, and be more than 8 characters long.</p> </section>

Now we can check if the second password matches the first with Vuelidate’s sameAs method:

validations: {   formResponses: {     password1: {       required,       strongPassword(password1) {         return (           /[a-z]/.test(password1) && // checks for a-z           /[0-9]/.test(password1) && // checks for 0-9           /W|_/.test(password1) && // checks for special char           password1.length >= 6         );       }     },     password2: {       required,       sameAsPassword: sameAs("password1")     }   } }

And we can create our second password field:

<section>   <label for="fpass2">Please re-type your Password</label>   <input id="fpass2" v-model="$ v.formResponses.password2.$ model" type="password">   <p class="error" v-if="!$ v.formResponses.password2.required">this field is required</p>   <p class="error" v-if="!$ v.formResponses.password2.sameAsPassword">The passwords do not match.</p> </section>

Now you can see the whole thing in action all together:

Evaluate on completion

You can see how noisy that last example is until the form has been completed. In my opinion, a better route is to evaluate when the entire form is completed so the user isn’t interrupted in the process. Here’s how we can do that.

Remember when we looked at the computed properties $ v contained? It had objects for all the individual properties, but also one for all validations as well. Inside, there were three very important values:

  • $ anyDirty: if the form was touched at all or left blank
  • $ invalid: if there are any errors in the form
  • $ anyError: if there are any errors at all (even one), this will evaluate to true

You can use $ invalid, but I prefer $ anyError, because it doesn’t require us to check if it’s dirty as well.

Let’s improve on our last form. We’ll put in a submit button, and a uiState string to keep track of, well, the UI state! This is incredibly useful as we can keep track of whether we’ve attempted submission, and whether we’re ready to send what we’ve collected. We’ll also make a small style improvement: position the error on the form so that it’s not moving around to in order to show the errors.

First, let’s add a few new data properties:

data() {   return {     uiState: "submit not clicked",     errors: false,     empty: true,     formResponses: {       ...     }   } }

Now, we’ll add in a submit button at the end of the form. The .prevent modifier at the end of the @click directive acts like preventDefault, and keeps the page from reloading:

<section>   <button @click.prevent="submitForm" class="submit">Submit</button> </section>

We’ll handle some different states in the submitForm method. We’re going to use that computed property from Vuelidate ($ anyDirty) to see if the form is empty. Remember, we can gather that information from this.$ v. We used the formResponses object to hold all the form responses, so what we’ll use is this.$ v.formResponses.$ anyDirty. We’ll map that value to our “empty” data property. We’ll also do the same with errors and we’ll change the uiState to "submit clicked":

submitForm() {   this.formTouched = !this.$ v.formResponses.$ anyDirty;   this.errors = this.$ v.formResponses.$ anyError;   this.uiState = "submit clicked";   if (this.errors === false && this.formTouched === false) {     //this is where you send the responses     this.uiState = "form submitted";   } }

If the form has no errors and it’s not empty, we’ll send the responses and change the uiState to “form submitted” as well.

Now, we can handle some states for errors and empty states as well and, finally, if the form is submitted, we’ll evaluate a success.

<section>   <button @click.prevent="submitForm" class="submit">Submit</button>   <p v-if="errors" class="error">The form above has errors,     <br>please get your act together and resubmit   </p>   <p v-else-if="formTouched && uiState === 'submit clicked'" class="error">The form above is empty,     <br>cmon y'all you can't submit an empty form!   </p>   <p v-else-if="uiState === 'form submitted'" class="success">Hooray! Your form was submitted!</p> </section>

In this form, we’ve given each section relative positioning and added a little padding at the bottom. That will allow us to give absolute positioning to the error state, which will prevent the form from moving around.

.error {   color: red;    font-size: 12px;   position: absolute;   text-transform: uppercase; }

There’s one last thing we need to do: now that we’ve placed the errors in the form absolutely, they’ll stack on top of each other unless we place them next to each other instead. We also want to check if the form is in the error state, which will be true only after the submit button is clicked. This can be a useful way of doing things- we won’t show the errors until the user is done with the form, which can be less invasive. It’s up to you if you’d like to do it this way or the v-model.lazy example used in previous sections.

Our previous errors looked like this:

<section>   ...   <p class="error" v-if="!$ v.formResponses.password2.required">this field is required</p>   <p class="error" v-if="!$ v.formResponses.password2.sameAsPassword">The passwords do not match.</p>  </section>

Now, they’ll be contained together like this:

<p v-if="errors" class="error">   <span v-if="!$ v.formResponses.password1.required">this field is required.</span>   <span v-if="!$ v.formResponses.password1.strongPassword">Strong passwords need to have a letter, a number, a special character, and be more than 8 characters long.</span> </p>

To make things even easier on you, there’s a library that dynamically figures out what error to display based on your validation. Super cool! If you’re doing something simple, it’s probably too much overhead, but if you have a really complex form, it might save you time 🙂

And there we have it! Our form is validated and we have both errors and empty states when we need them, but none while we’re typing.

Sincere thanks to Damian Dulisz, one of the maintainers for Vuelidate, for proofing this article.

The post Form Validation in Under an Hour with Vuelidate appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]