Tag: Create

Create Amazingly Stable Tests Your Way — Coded and Code-Less

Testim’s end-to-end test automation delivers the speed and stability of AI-based codeless tests, with the power of code. You get the flexibility to record or code tests, run on third-party grids, fit your workflow and tools including CI, Git and more. Join the Dev Kit beta to start writing stable tests in code.

About Testim

At Testim, we too are developers and testers, striving to release quality software faster. We built Testim because writing stable end-to-end tests was just too hard. We were the first to build an AI-based functional test automation solution. Since we launched in 2014, we’ve been adding features, improving quality, and proving our solution with customers every day.

Create tests your way

There are two ways to create tests in Testim — record them using the Testim Extension or code them in your IDE. Actually, there’s a third and recommended approach for those who want to code—record the test, export it as code and modify it in your IDE.

When you record a test, each UI action leverages our AI-based platform to analyze the DOM, weigh the attributes associated with an element and create Smart Locators that uniquely identify elements. If attributes such as the color, class, text or location of an element are changed by development, our AI will still be able to uniquely identify the element so that your test doesn’t fail.

Customize tests

Testim has many features built into the visual test editor to help you customize your tests such as validation of text, email, PDFs, or conditions and loops. You can also insert JavaScript into any step so that you can handle nearly any UI situation.

If you are into JavaScript, you can also export your test as code and customize, debug, or refactor it your IDE; the choice is yours.

Execute your cross-browser tests

When your tests are ready, you can run on-demand or schedule a test run within Testim. We also make it really easy to run your test following a CI action. Tests can be parallelized and across all browsers on our test cloud or any Selenium-compatible test cloud.

Report results

Regardless of how your tests were created (code or codeless) when they run, you will see the results in Testim so you can troubleshoot. If you find an application defect, our bug capture tool makes it really easy to create a bug report, complete with screenshots, video and the test steps to recreate it. Pop it into Jira or Trello to submit your report in minutes. Our reporting shows test run history, including managerial reports so you can show all of the great work you’ve been doing and that releases are ready to ship.

Testim is the fastest way to create your most resilient end-to-end tests, your way—codeless, coded or both. For more information go to www.testim.io.

The post Create Amazingly Stable Tests Your Way — Coded and Code-Less appeared first on CSS-Tricks.


, , , , ,

Hey, let’s create a functional calendar app with the JAMstack

Hey, let’s create a functional calendar app with the JAMstack

I’ve always wondered how dynamic scheduling worked so I decided to do extensive research, learn new things, and write about the technical part of the journey. It’s only fair to warn you: everything I cover here is three weeks of research condensed into a single article. Even though it’s beginner-friendly, it’s a healthy amount of reading. So, please, pull up a chair, sit down and let’s have an adventure.

My plan was to build something that looked like Google Calendar but only demonstrate three core features:

  1. List all existing events on a calendar
  2. Create new events
  3. Schedule and email notification based on the date chosen during creation. The schedule should run some code to email the user when the time is right.

Pretty, right? Make it to the end of the article, because this is what we’ll make.

A calendar month view with a pop-up form for creating a new event as an overlay.

The only knowledge I had about asking my code to run at a later or deferred time was CRON jobs. The easiest way to use a CRON job is to statically define a job in your code. This is ad hoc — statically means that I cannot simply schedule an event like Google Calendar and easily have it update my CRON code. If you are experienced with writing CRON triggers, you feel my pain. If you’re not, you are lucky you might never have to use CRON this way.

To elaborate more on my frustration, I needed to trigger a schedule based on a payload of HTTP requests. The dates and information about this schedule would be passed in through the HTTP request. This means there’s no way to know things like the scheduled date beforehand.

We (my colleagues and I) figured out a way to make this work and — with the help of Sarah Drasner’s article on Durable Functions — I understood what I needed learn (and unlearn for that matter). You will learn about everything I worked in this article, from event creation to email scheduling to calendar listings. Here is a video of the app in action:

You might notice the subtle delay. This has nothing to do with the execution timing of the schedule or running the code. I am testing with a free SendGrid account which I suspect have some form of latency. You can confirm this by testing the serverless function responsible without sending emails. You would notice that the code runs at exactly the scheduled time.

Tools and architecture

Here are the three fundamental units of this project:

  1. React Frontend: Calendar UI, including the UI to create, update or delete events.
  2. 8Base GraphQL: A back-end database layer for the app. This is where we will store, read and update our date from. The fun part is you won’t write any code for this back end.
  3. Durable Functions: Durable functions are kind of Serverless Functions that have the power of remembering their state from previous executions. This is what replaces CRON jobs and solves the ad hoc problem we described earlier.

See the Pen
by Chris Nwamba (@codebeast)
on CodePen.

The rest of this post will have three major sections based on the three units we saw above. We will take them one after the other, build them out, test them, and even deploy the work. Before we get on with that, let’s setup using a starter project I made to get us started.

Project Repo

Getting Started

You can set up this project in different ways — either as a full-stack project with the three units in one project or as a standalone project with each unit living in it’s own root. Well, I went with the first because it’s more concise, easier to teach, and manageable since it’s one project.

The app will be a create-react-app project and I made a starter for us to lower the barrier to set up. It comes with supplementary code and logic that we don’t need to explain since they are out of the scope of the article. The following are set up for us:

  1. Calendar component
  2. Modal and popover components for presenting event forms
  3. Event form component
  4. Some GraphQL logic to query and mutate data
  5. A Durable Serverless Function scaffold where we will write the schedulers

Tip: Each existing file that we care about has a comment block at the top of the document. The comment block tells you what is currently happening in the code file and a to-do section that describes what we are required to do next.

Start by cloning the starter form Github:

git clone -b starter --single-branch https://github.com/christiannwamba/calendar-app.git

Install the npm dependencies described in the root package.json file as well as the serverless package.json:

npm install

Orchestrated Durable Functions for scheduling

There are two words we need to get out of the way first before we can understand what this term is — orchestration and durable.

Orchestration was originally used to describe an assembly of well-coordinated events, actions, etc. It is heavily borrowed in computing to describe a smooth coordination of computer systems. The key word is coordinate. We need to put two or more units of a system together in a coordinated way.

Durable is used to describe anything that has the outstanding feature of lasting longer.

Put system coordination and long lasting together, and you get Durable Functions. This is the most powerful feature if Azure’s Serverless Function. Durable Functions based in what we now know have these two features:

  1. They can be used to assemble the execution of two or more functions and coordinate them so race conditions do not occur (orchestration).
  2. Durable Functions remember things. This is what makes it so powerful. It breaks the number one rule of HTTP: stateless. Durable functions keep their state intact no matter how long they have to wait. Create a schedule for 1,000,000 years into the future and a durable function will execute after one million years while remembering the parameters that were passed to it on the day of trigger. That means Durable Functions are stateful.

These durability features unlock a new realm of opportunities for serverless functions and that is why we are exploring one of those features today. I highly recommend Sarah’s article one more time for a visualized version of some of the possible use cases of Durable Functions.

I also made a visual representation of the behavior of the Durable Functions we will be writing today. Take this as an animated architectural diagram:

Shows the touch-points of a serverless system.

A data mutation from an external system (8Base) triggers the orchestration by calling the HTTP Trigger. The trigger then calls the orchestration function which schedules an event. When the time for execution is due, the orchestration function is called again but this time skips the orchestration and calls the activity function. The activity function is the action performer. This is the actual thing that happens e.g. “send email notification”.

Create orchestrated Durable Functions

Let me walk you through creating functions using VS Code. You need two things:

  1. An Azure account
  2. VS Code

Once you have both setup, you need to tie them together. You can do this using a VS Code extension and a Node CLI tool. Start with installing the CLItool:

 npm install -g azure-functions-core-tools  # OR  brew tap azure/functions brew install azure-functions-core-tools 

Next, install the Azure Function extension to have VS Code tied to Functions on Azure. You can read more about setting up Azure Functions from my previous article.

Now that you have all the setup done, let’s get into creating these functions. The functions we will be creating will map to the following folders.

Folder Function
schedule Durable HTTP Trigger
scheduleOrchestrator Durable Orchestration
sendEmail Durable Activity

Start with the trigger.

  1. Click on the Azure extension icon and follow the image below to create the schedule function

    Shows the interface steps going from Browse to JavaScript to Durable Functions HTTP start to naming the function schedule.
  2. Since this is the first function, we chose the folder icon to create a function project. The icon after that creates a single function (not a project).
  3. Click Browse and create a serverless folder inside the project. Select the new serverless folder.
  4. Select JavaScript as the language. If TypeScript (or any other language) is your jam, please feel free.
  5. Select Durable Functions HTTP starter. This is the trigger.
  6. Name the first function as schedule

Next, create the orchestrator. Instead of creating a function project, create a function instead.

  1. Click on the function icon:

  2. Select Durable Functions orchestrator.
  3. Give it a name, scheduleOrchestrator and hit Enter.
  4. You will be asked to select a storage account. Orchestrator uses storage to preserve the state of a function-in-process.
  5. Select a subscription in your Azure account. In my case, I chose the free trial subscription.
  6. Follow the few remaining steps to create a storage account.

Finally, repeat the previous step to create an Activity. This time, the following should be different:

  • Select Durable Functions activity.
  • Name it sendEmail.
  • No storage account will be needed.

Scheduling with a durable HTTP trigger

The code in serverless/schedule/index.js does not need to be touched. This is what it looks like originally when the function is scaffolded using VS Code or the CLI tool.

const df = require("durable-functions"); module.exports = async function (context, req) {   const client = df.getClient(context);   const instanceId = await client.startNew(req.params.functionName, undefined, req.body);   context.log(`Started orchestration with ID = '$ {instanceId}'.`);   return client.createCheckStatusResponse(context.bindingData.req, instanceId); };

What is happening here?

  1. We’re creating a durable function on the client side that is based on the context of the request.
  2. We’re calling the orchestrator using the client’s startNew() function. The orchestrator function name is passed as the first argument to startNew() via the params object. A req.body is also passed to startNew() as third argument which is forwarded to the orchestrator.
  3. Finally, we return a set of data that can be used to check the status of the orchestrator function, or even cancel the process before it’s complete.

The URL to call the above function would look like this:


Where functionName is the name passed to startNew. In our case, it should be:


It’s also good to know that you can change how this URL looks.

Orchestrating with a Durable Orchestrator

The HTTP trigger startNew call calls a function based on the name we pass to it. That name corresponds to the name of the function and folder that holds the orchestration logic. The serverless/scheduleOrchestrator/index.js file exports a Durable Function. Replace the content with the following:

const df = require("durable-functions"); module.exports = df.orchestrator(function* (context) {   const input = context.df.getInput()   // TODO -- 1      // TODO -- 2 });

The orchestrator function retrieves the request body from the HTTP trigger using context.df.getInput().

Replace TODO -- 1 with the following line of code which might happen to be the most significant thing in this entire demo:

yield context.df.createTimer(new Date(input.startAt))

What this line does use the Durable Function to create a timer based on the date passed in from the request body via the HTTP trigger.

When this function executes and gets here, it will trigger the timer and bail temporarily. When the schedule is due, it will come back, skip this line and call the following line which you should use in place of TODO -- 2.

return yield context.df.callActivity('sendEmail', input);

The function would call the activity function to send an email. We are also passing a payload as the second argument.

This is what the completed function would look like:

const df = require("durable-functions");  module.exports = df.orchestrator(function* (context) {   const input = context.df.getInput()        yield context.df.createTimer(new Date(input.startAt))        return yield context.df.callActivity('sendEmail', input); });

Sending email with a durable activity

When a schedule is due, the orchestrator comes back to call the activity. The activity file lives in serverless/sendEmail/index.js. Replace what’s in there with the following:

const sgMail = require('@sendgrid/mail'); sgMail.setApiKey(process.env['SENDGRID_API_KEY']);  module.exports = async function(context) {   // TODO -- 1   const msg = {}   // TODO -- 2   return msg; };

It currently imports SendGrid’s mailer and sets the API key. You can get an API Key by following these instructions.

I am setting the key in an environmental variable to keep my credentials safe. You can safely store yours the same way by creating a SENDGRID_API_KEY key in serverless/local.settings.json with your SendGrid key as the value:

{   "IsEncrypted": false,   "Values": {     "AzureWebJobsStorage": "<<AzureWebJobsStorage>",     "FUNCTIONS_WORKER_RUNTIME": "node",     "SENDGRID_API_KEY": "<<SENDGRID_API_KEY>"   } }

Replace TODO -- 1 with the following line:

const { email, title, startAt, description } = context.bindings.payload;

This pulls out the event information from the input from the orchestrator function. The input is attached to context.bindings. payload can be anything you name it so go to serverless/sendEmail/function.json and change the name value to payload:

{   "bindings": [     {       "name": "payload",       "type": "activityTrigger",       "direction": "in"     }   ] }

Next, update TODO -- 2 with the following block to send an email:

const msg = {   to: email,   from: { email: 'chris@codebeast.dev', name: 'Codebeast Calendar' },   subject: `Event: $ Hey, let’s create a functional calendar app with the JAMstack`,   html: `<h4>$ Hey, let’s create a functional calendar app with the JAMstack @ $ {startAt}</h4> <p>$ {description}</p>` }; sgMail.send(msg);  return msg;

Here is the complete version:

const sgMail = require('@sendgrid/mail'); sgMail.setApiKey(process.env['SENDGRID_API_KEY']);  module.exports = async function(context) {   const { email, title, startAt, description } = context.bindings.payload;   const msg = {     to: email,     from: { email: 'chris@codebeast.dev', name: 'Codebeast Calendar' },     subject: `Event: $ Hey, let’s create a functional calendar app with the JAMstack`,     html: `<h4>$ Hey, let’s create a functional calendar app with the JAMstack @ $ {startAt}</h4> <p>$ {description}</p>`   };   sgMail.send(msg);    return msg; };

Deploying functions to Azure

Deploying functions to Azure is easy. It’s merely a click away from the VS Code editor. Click on the circled icon to deploy and get a deploy URL:

Still with me this far in? You’re making great progress! It’s totally OK to take a break here, nap, stretch or get some rest. I definitely did while writing this post.

Data and GraphQL layer with 8Base

My easiest description and understanding of 8Base is “Firebase for GraphQL.” 8Base is a database layer for any kind of app you can think of and the most interesting aspect of it is that it’s based on GraphQL.

The best way to describe where 8Base fits in your stack is to paint a picture of a scenario.

Imagine you are a freelance developer with a small-to-medium scale contract to build an e-commerce store for a client. Your core skills are on the web so you are not very comfortable on the back end. though you can write a bit of Node.

Unfortunately, e-commerce requires managing inventories, order management, managing purchases, managing authentication and identity, etc. “Manage” at a fundamental level just means data CRUD and data access.

Instead of the redundant and boring process of creating, reading, updating, deleting, and managing access for entities in our backend code, what if we could describe these business requirements in a UI? What if we can create tables that allow us to configure CRUD operations, auth and access? What if we had such help and only focus on building frontend code and writing queries? Everything we just described is tackled by 8Base

Here is an architecture of a back-end-less app that relies on 8Base as it’s data layer:

Create an 8Base table for events storage and retrieval

The first thing we need to do before creating a table is to create an account. Once you have an account, create a workspace that holds all the tables and logic for a given project.

Next, create a table, name the table Events and fill out the table fields.

We need to configure access levels. Right now, there’s nothing to hide from each user, so we can just turn on all access to the Events table we created:

Setting up Auth is super simple with 8base because it integrates with Auth0. If you have entities that need to be protected or want to extend our example to use auth, please go wild.

Finally, grab your endpoint URL for use in the React app:

Testing GraphQL Queries and mutation in the playground

Just to be sure that we are ready to take the URL to the wild and start building the client, let’s first test the API with a GraphQL playground and see if the setup is fine. Click on the explorer.

Paste the following query in the editor.

query {   eventsList {     count     items {       id       title       startAt       endAt       description       allDay       email     }   } }

I created some test data through the 8base UI and I get the result back when I run they query:

You can explore the entire database using the schema document on the right end of the explore page.

Calendar and event form interface

The third (and last) unit of our project is the React App which builds the user interfaces. There are four major components making up the UI and they include:

  1. Calendar: A calendar UI that lists all the existing events
  2. Event Modal: A React modal that renders EventForm component to create a component
  3. Event Popover: Popover UI to read a single event, update event using EventForm or delete event
  4. Event Form: HTML form for creating new event

Before we dive right into the calendar component, we need to setup React Apollo client. The React Apollo provider empowers you with tools to query a GraphQL data source using React patterns. The original provider allows you to use higher order components or render props to query and mutate data. We will be using a wrapper to the original provider that allows you query and mutate using React Hooks.

In src/index.js, import the React Apollo Hooks and the 8base client in TODO -- 1:

import { ApolloProvider } from 'react-apollo-hooks'; import { EightBaseApolloClient } from '@8base/apollo-client';

At TODO -- 2, configure the client with the endpoint URL we got in the 8base setup stage:

const URI = 'https://api.8base.com/cjvuk51i0000701s0hvvcbnxg';  const apolloClient = new EightBaseApolloClient({   uri: URI,   withAuth: false });

Use this client to wrap the entire App tree with the provider on TODO -- 3:

ReactDOM.render(   <ApolloProvider client={apolloClient}>     <App />   </ApolloProvider>,   document.getElementById('root') );

Showing events on the calendar

The Calendar component is rendered inside the App component and the imports BigCalendar component from npm. Then :

  1. We render Calendar with a list of events.
  2. We give Calendar a custom popover (EventPopover) component that will be used to edit events.
  3. We render a modal (EventModal) that will be used to create new events.

The only thing we need to update is the list of events. Instead of using the static array of events, we want to query 8base for all store events.

Replace TODO -- 1 with the following line:

const { data, error, loading } = useQuery(EVENTS_QUERY);

Import the useQuery library from npm and the EVENTS_QUERY at the beginning of the file:

import { useQuery } from 'react-apollo-hooks'; import { EVENTS_QUERY } from '../../queries';

EVENTS_QUERY is exactly the same query we tested in 8base explorer. It lives in src/queries and looks like this:

export const EVENTS_QUERY = gql`   query {     eventsList {       count       items {         id         ...       }     }   } `;

Let’s add a simple error and loading handler on TODO -- 2:

if (error) return console.log(error);   if (loading)     return (       <div className="calendar">         <p>Loading...</p>       </div>     );

Notice that the Calendar component uses the EventPopover component to render a custom event. You can also observe that the Calendar component file renders EventModal as well. Both components have been setup for you, and their only responsibility is to render EventForm.

Create, update and delete events with the event form component

The component in src/components/Event/EventForm.js renders a form. The form is used to create, edit or delete an event. At TODO -- 1, import useCreateUpdateMutation and useDeleteMutation:

import {useCreateUpdateMutation, useDeleteMutation} from './eventMutationHooks'
  • useCreateUpdateMutation: This mutation either creates or updates an event depending on whether the event already existed.
  • useDeleteMutation: This mutation deletes an existing event.

A call to any of these functions returns another function. The function returned can then serve as an even handler.

Now, go ahead and replace TODO -- 2 with a call to both functions:

const createUpdateEvent = useCreateUpdateMutation(   payload,   event,   eventExists,   () => closeModal() ); const deleteEvent = useDeleteMutation(event, () => closeModal());

These are custom hooks I wrote to wrap the useMutation exposed by React Apollo Hooks. Each hook creates a mutation and passes mutation variable to the useMutation query. The blocks that look like the following in src/components/Event/eventMutationHooks.js are the most important parts:

useMutation(mutationType, {   variables: {     data   },   update: (cache, { data }) => {     const { eventsList } = cache.readQuery({       query: EVENTS_QUERY     });     cache.writeQuery({       query: EVENTS_QUERY,       data: {         eventsList: transformCacheUpdateData(eventsList, data)       }     });     //..   } });

Call the Durable Function HTTP trigger from 8Base

We have spent quite some time in building the serverless structure, data storage and UI layers of our calendar app. To recap, the UI sends data to 8base for storage, 8base saves data and triggers the Durable Function HTTP trigger, the HTTP trigger kicks in orchestration and the rest is history. Currently, we are saving data with mutation but we are not calling the serverless function anywhere in 8base.

8base allows you to write custom logic which is what makes it very powerful and extensible. Custom logic are simple functions that are called based on actions performed on the 8base database. For example, we can set up a logic to be called every time a mutation occurs on a table. Let’s create one that is called when an event is created.

Start by installing the 8base CLI:

npm install -g 8base

On the calendar app project run the following command to create a starter logic:

8base init 8base

8base init command creates a new 8base logic project. You can pass it a directory name which in this case we are naming the 8base logic folder, 8base — don’t get it twisted.

Trigger scheduling logic

Delete everything in 8base/src and create a triggerSchedule.js file in the src folder. Once you have done that, drop in the following into the file:

const fetch = require('node-fetch');  module.exports = async event => {   const res = await fetch('<HTTP Trigger URL>', {     method: 'POST',     body: JSON.stringify(event.data),     headers: { 'Content-Type': 'application/json' }   })   const json = await res.json();   console.log(event, json)   return json; };

The information about the GraphQL mutation is available on the event object as data.

Replace <HTTP Trigger URL> with the URL you got after deploying your function. You can get the URL by going to the function in your Azure URL and click “Copy URL.”

You also need to install the node-fetch module, which will grab the data from the API:

npm install --save node-fetch

8base logic configuration

The next thing to do is tell 8base what exact mutation or query that needs to trigger this logic. In our case, a create mutation on the Events table. You can describe this information in the 8base.yml file:

functions:   triggerSchedule:     handler:       code: src/triggerSchedule.js     type: trigger.after     operation: Events.create

In a sense, this is saying, when a create mutation happens on the Events table, please call src/triggerSchedule.js after the mutation has occurred.

We want to deploy all the things

Before anything can be deployed, we need to login into the 8Base account, which we can do via command line:

8base login

Then, let’s run the deploy command to send and set up the app logic in your workspace instance.

8base deploy

Testing the entire flow

To see the app in all its glory, click on one of the days of the calendar. You should get the event modal containing the form. Fill that out and put a future start date so we trigger a notification. Try a date more than 2-5 mins from the current time because I haven’t been able to trigger a notification any faster than that.

Yay, go check your email! The email should have arrived thanks to SendGrid. Now we have an app that allows us to create events and get notified with the details of the event submission.

The post Hey, let’s create a functional calendar app with the JAMstack appeared first on CSS-Tricks.


, , , ,

Using the Little-Known CSS element() Function to Create a Minimap Navigator

W3C’s CSS Working Group often gives us brilliant CSS features to experiment with. Sometimes we come across something so cool that sticks a grin on our face, but it vanishes right away because we think, “that’s great, but what do I do with it?” The element() function was like that for me. It’s a CSS function that takes an element on the page and presents it as an image to be displayed on screen. Impressive, but quixotic.

Below is a simple example of how it works. It’s currently only supported in Firefox, which I know is a bummer. But stick with me and see how useful it can be.

<div id="ele">   <p>Hello World! how're you?<br>I'm not doing that<br>great. Got a cold &#x1F637;</p> </div> <div id="eleImg"></div>
#eleImg {   background: -moz-element(#ele) no-repeat center / contain; /* vendor prefixed */ }

The element() function (with browser prefix) takes the id value of the element it’ll translate into an image. The output looks identical to the appearance of the given element on screen.

When I think of element()’s output, I think of the word preview. I think that’s the type of use case that gets the most out of it: where we can preview an element before it’s shown on the page. For example, the next slide in a slideshow, the hidden tab, or the next photo in a gallery. Or… a minimap!

A minimap is a mini-sized preview of a long document or page, usually shown at on one side of the screen or another and used to navigate to a corresponding point on that document.

You might have seen it in code editors like Sublime Text.

The minimap is there on the right.

CSS element() is useful in making the “preview” part of the minimap.

Down below is the demo for the minimap, and we will walk through its code after that. However, I recommend you see the full-page demo because minimaps are really useful for long documents on large screens.

If you’re using a smartphone, remember that, according to the theory of relativity, minimaps will get super mini in mini screens; and no, that’s not really what the theory of relativity actually says, but you get my point.

See the Pen Minimap with CSS element() & HTML input range by Preethi Sam (@rpsthecoder) on CodePen.

A screenshot of the final result of the demo. It consists of a large block of styled paragraph text with a

If you’re designing the minimap for the whole page, like for a single page website, you can use the document body element for the image. Otherwise, targeting the main content element, like the article in my demo, also works.

<div id="minimap"></div> <div id="article"> <!-- content --> </div>
#minimap {   background: rgba(254,213,70,.1) -moz-element(#article) no-repeat center / contain;   position: fixed; right: 10px; top: 10px; /* more style */ }

For the minimap’s background image, we feed the id of the article as the parameter of element() and, like with most background images, it’s styled to not repeat (no-repeat) and fit inside (contain) and at center of the box (center) where it’s displayed.

The minimap is also fixed to the screen at top right of the viewport.

Once the background is ready, we can add a slider on top of it and it will serve to operate the minimap scrolling. For the slider, I went with input: range, the original, uncomplicated, and plain HTML slider.

<div id="minimap">   <input id="minimap-range" type="range" max="100" value="0"> </div>
#minimap-range {   /* Rotating the default horizontal slider to vertical */   transform: translateY(-100%) rotate(90deg);   transform-origin: bottom left;   background-color: transparent;  /* more style */ }  #minimap-range::-moz-range-thumb {   background-color: dodgerblue;    cursor: pointer; /* more style */ }  #minimap-range::-moz-range-track{   background-color: transparent; }

Not entirely uncomplicated because it did need some tweaking. I turned the slider upright, to match the minimap, and applied some style to its pseudo elements (specifically, the thumb and track) to replace their default styles. Again, we’re only concerned about Firefox at the moment since we’re dealing with limited support.

All that’s left is to couple the slider’s value to a corresponding scroll point on the page when the value is changed by the user. That takes a sprinkle of JavaScript, which looks like this:

onload = () => {   const minimapRange = document.querySelector("#minimap-range");   const minimap = document.querySelector("#minimap");   const article = document.querySelector("#article");   const $  = getComputedStyle.bind();      // Get the minimap range width multiplied by the article height, then divide by the article width, all in pixels.   minimapRange.style.width = minimap.style.height =      parseInt($ (minimapRange).width) * parseInt($ (article).height) / parseInt($ (article).width) + "px";      // When the range changes, scroll to the relative percentage of the article height       minimapRange.onchange = evt =>      scrollTo(0, parseInt($ (article).height) * (evt.target.value / 100)); };

The dollar sign ($ ) is merely an alias for getComputedStyle(), which is the method to get the CSS values of an element.

It’s worth noting that the width of the minimap is already set in the CSS, so we really only need to calculate its height. So, we‘re dealing with the height of the minimap and the width of the slider because, remember, the slider is actually rotated up.

Here’s how the equation in the script was determined, starting with the variables:

  • x1 = height of minimap (as well as the width of the slider inside it)
  • y1 = width of minimap
  • x2 = height of article
  • y2 = width of article
x1/y1 = x2/y2 x1 = y1 * x2/y2      height of minimap = width of minimap * height of article / width of article

And, when the value of the slider changes (minimapRange.onchange), that’s when the ScrollTo() method is called to scroll the page to its corresponding value on the article. 💥

Fallbacks! We need fallbacks!

Obviously, there are going to be plenty of times when element() is not supported if we were to use this at the moment, so we might want to hide the minimap in those cases.

We check for feature support in CSS:

@supports (background: element(#article)) or (background: -moz-element(#article)){   /* fallback style */ }

…or in JavaScript:

if(!CSS.supports('(background: element(#article)) or (background: -moz-element(#article))')){   /* fallback code */ }

If you don’t mind the background image being absent, then you can still keep the slider and apply a different style on it.

There are other slick ways to make minimaps that are floating out in the wild (and have more browser support). Here’s a great Pen by Shaw:

See the Pen
Mini-map Progress Tracker & Scroll Control
by Shaw (@shshaw)
on CodePen.

There are also tools like pagemap and xivimap that can help. The element() function is currently specced in W3C’s CSS Image Values and Replaced Content Module Level 4. Defintely worth a read to fully grasp the intention and thought behind it.

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.


Chrome Opera Firefox IE Edge Safari
No No 4* No No No

Mobile / Tablet

iOS Safari Opera Mobile Opera Mini Android Android Chrome Android Firefox
No No No No No 64*

Psst! Did you try selecting the article text in the demo? See what happens on the minimap. 😉

The post Using the Little-Known CSS element() Function to Create a Minimap Navigator appeared first on CSS-Tricks.


, , , , , ,

Create Smart WordPress Forms in Less Than 5 Minutes with WPForms

(This is a sponsored post.)

Most online form solutions are either too complex or too expensive.

We believe you shouldn’t have to spend hours creating online forms for your business. That’s why we built WPForms, a drag and drop WordPress form builder that’s both EASY and POWERFUL.

WPForms allows you to create beautiful contact forms, email subscription forms, payment forms, smart survey forms, and basically every other type of form for your website within minutes!

Since launching in 2016, WPForms has taken the market by surprise with it’s pre-built templates and smart workflows. Currently, WPForms is being used on over 1 million WordPress websites.

WPForms also maintains a 4.9 out of 5 star rating average on WordPress.org with over 3,000 five-star ratings.

WPForms Features

Here are the features that makes WPForms the most powerful and user-friendly WordPress form plugin in the market.

  • Drag & Drop Form Builder – create smart forms in minutes without writing any code.
    150+ Pre-Made Form Templates – start with our form templates to save time, and customize them as needed.
    Smart Conditional Logic – customize your forms based on user interaction to collect the most relevant information.
  • Entry Management – See all your leads right inside your WordPress dashboard with Geo-location data, and other powerful features.
  • Instant Form Notifications – Get email notifications when a user submits a form. You can smart routing to send the inquiry to the right person in your team.
  • Spam Protection – Our smart captcha and honeypot automatically prevents all spam submissions. You can also add custom CAPTCHA questions as well.
  • Email Marketing Integrations – Integrate with your email marketing service using our native integration for MailChimp, AWeber, Constant Contact, Drip, and countless others. You can use also use our Zapier integration to connect your forms to over 1000+ other apps.
  • Payment Forms – Create online payments using Stripe or PayPal. Recurring payment support is built-in as well.
  • Surveys & Polls – Easily create smart survey forms and analyze your data with our interactive reports. Our WordPress survey plugin is by far the best solution in the industry.
  • Form Abandonment – Unlock more leads by capturing partial form submissions.
  • Form Permissions – Lock your forms and manage access control with password, logged-in users, and other restrictions.
  • Signature – Allow users to sign your online forms with their mouse or touch screen.
  • Post Submission – Allow users to submit guest blog posts and other content directly to your WordPress site without logging into the dashboard. Great for user-submitted content.
  • User Registration – Create custom user registration and login forms for WordPress with just a few clicks. Also include advanced features like profile field mapping, spam registration protection, etc.
  • All Advanced Fields You Need – From radio button to file uploads to multi-page forms to Likert scale, we have all the fields you need.
  • Easy to Customize – Completely customize your WordPress forms with section dividers, HTML blocks, and custom CSS. WPForms also comes with hooks and filters for developers to extend and create custom functionality.

Smart business owners, designers, and developers love WPForms, and you will too!

Unlike other form solutions that charge a monthly subscription which increases as your business grows, WPForms allows you to create unlimited forms and get unlimited entries for a small yearly fee.

Get Started with WPForms Pro Today

Bonus: CSS Tricks users can get 50% off by using the coupon: SAVE50

Need more social proof? Read WPForms user reviews to see why over 1 million websites choose WPForms to build powerful WordPress forms.

If you’re just starting out and don’t need all the advanced features, then you can try our free WPForms Lite plugin for creating a simple contact form. You can simply download it from your WordPress site by searching for WPForms in the plugin search of your WordPress site.

Direct Link to ArticlePermalink

The post Create Smart WordPress Forms in Less Than 5 Minutes with WPForms appeared first on CSS-Tricks.


, , , , , , ,