16px or Larger Text Prevents iOS Form Zoom
Strategy

16px or Larger Text Prevents iOS Form Zoom


This was a great “Today I Learned” for me from Josh W. Comeau. If the font-size of an <input> is 16px or larger, Safari on iOS will focus into the input normally. But as soon as the font-size is 15px or less, the viewport will zoom into that input. Presumably, because it considers that type too small and wants you to see what you are doing. So it zooms in to help you. Accessibility. If you don’t want that, make the font big enough.

Here’s Josh’s exact Pen if you want to have a play yourself.

In general, I’d say I like this feature. It helps people see what they are doing and discourages super-tiny font sizes. What is a slight bummer — and I really don’t blame anyone here — is that not all typefaces are created equal in terms of readability at different sizes. For example, here’s San Francisco versus Caveat at 16px.

San Francisco on the left, Cavet on the right. Caveat looks visually much smaller even though the font-size is the same.

You can view that example in Debug Mode to see for yourself and change the font size to see what does and doesn’t zoom.





Source link

Strategy

Creating Colorful, Smart Shadows


Ever wanted to learn how to create a shadow effect that inherits some of the colors from the foreground element? Read or watch below to find out how!

I was walking through Home Depot (aka the Toys “R” Us for big kids!) the other day, and they had a massive display to showcase all the smart lightbulbs they had for sale. One of the display items was for a series of bulbs behind a TV that would project in the background an approximation of the colors being shown on the screen in the foreground. It was something similar to the following:

Notice what is going on behind the TV. The colors shown in the foreground on the screen are projected as a colorful shadow behind the TV itself. As the colors on the screen change, so do the colors projected in the background. Really cool, right?

Naturally, after seeing this, my first thought was whether a colorful shadow smart enough to mimic the foreground colors can be created using web technologies. It turns out, it totally can using just CSS. In this article we’ll look at how to go about creating this effect.

Onwards!

Making it Real

As you will see in the following sections, creating this colorful shadow using CSS may seem like a daunting task at first. As we start getting into it and breaking this nugget of a task into smaller pieces, you’ll see that it is all quite digestible. In the next few sections, what we are going to create is the following example:

What you should see is a picture of sushi with a colorful shadow appearing behind. Just to highlight that we are doing this all live, the shadow is animated with a pulsing effect. With the working example out of the way, let’s dive into the implementation and look at how HTML and CSS make this all come alive.

Displaying our Image

The HTML for getting our sushi image to appear is nothing too fancy:

<div class="parent">
  <div class="colorfulShadow sushi"></div>
</div>

We have a parent div element, and it contains a child div element that is responsible for showing the sushi. The way we display the sushi is by specifying it as a background image, and that is handled by the following .sushi style rule:

.sushi {
  margin: 100px;
  width: 150px;
  height: 150px;
  background-image: url("https://www.kirupa.com/icon/1f363.svg");
  background-repeat: no-repeat;
  background-size: contain;
}

In this style rule, we specify the size of our div to be 150 by 150 pixels, and we set the background-image and related properties on it. If take stock of where we are right now, the HTML and CSS we have seen will give us something that looks as follows:

It’s Shadow Time

Now that we have our image appearing, all that remains is the even more fun part of us defining the shadow. The way we are going to define the shadow is by specifying a child pseudo-element (using after) that will do three things:

  1. Be positioned directly behind our image
  2. Inherit the same background image as the parent element
  3. Rely on filters to apply the colorful drop-shadow effect

These three things are done by the following two style rules:

.colorfulShadow {
  position: relative;
}

.colorfulShadow::after {
  content: "";
  width: 100%;
  height: 100%;
  position: absolute;
  background: inherit;
  background-position: center center;
  filter: drop-shadow(0px 0px 10px rgba(0, 0, 0, 0.50)) blur(20px);
  z-index: -1;
}

Take a few moments to walk through what is going on here. Pay close attention to each property and its value. Some notable callouts here are the background and filter properties. The background property has a value of inherit, which means its value will be the background of the parent:

.colorfulShadow::after {
  content: "";
  width: 100%;
  height: 100%;
  position: absolute;
  background: inherit;
  background-position: center center;
  filter: drop-shadow(0px 0px 10px rgba(0, 0, 0, 0.50)) blur(20px);
  z-index: -1;
}

The filter property has two filters defined: drop-shadow and blur:

.colorfulShadow::after {
  content: "";
  width: 100%;
  height: 100%;
  position: absolute;
  background: inherit;
  background-position: center center;
  filter: drop-shadow(0px 0px 10px rgba(0, 0, 0, 0.50)) blur(20px);
  z-index: -1;
}

Our drop-shadow filter is set to display a black shadow with 50% opacity, and our blur filter will blur our pseudoelement by 20px. The combination of these two filters is what ends up creating the colorful shadow that will now appear behind our sushi image when these two style rules get applied:

At this point, we are pretty much adone. For completeness, if you want the animation where the colorful drop shadow scales in and out, the following additions to our existing CSS will get you there:

.colorfulShadow {
  position: relative;
}

.colorfulShadow::after {
  content: "";
  width: 100%;
  height: 100%;
  position: absolute;
  background: inherit;
  background-position: center center;
  filter: drop-shadow(0px 0px 10px rgba(0, 0, 0, 0.50)) blur(20px);
  z-index: -1;

  /* animation time! */
  animation: oscillate 1s cubic-bezier(.17, .67, .45, 1.32) infinite alternate;
}

@keyframes oscillate {
  from {
    transform: scale(1, 1);
  }

  to {
    transform: scale(1.3, 1.3);
  }
}

If you want some interactivity without having a constantly looping animation, you can also use a CSS transition to change how the shadow behaves on some action like hover. The difficult part is to just treat the pseudo element just like you would any other element that you would explicitly define in HTML or dynamically create using JavaScript. The only difference is that this element is created fully using just CSS!

Conclusion

Pseudo elements allow us to use CSS to accomplish some of the element creation tasks that historically have been within HTML’s and JavaScript’s domain. For our colorful and smart drop shadow, we relied on the parent element to have a background image set. This allowed us to easily define a child pseudoelement that both inherited the parent’s background image details but also allowed us to set a bunch of properties on it for the blur and drop shadow effect. While all of this is good and we minimized a lot of copying & pasting, this approach isn’t very flexible.

What if I want to apply a shadow like this to an element that isn’t just empty with a background image? What if I have an HTML element like a button or combobox that I want to have this drop shadow effect applied to? One solution is to rely on JavaScript to duplicate the appropriate elements in the DOM, position them below the foreground elements, apply the filters, and call it a day. While this works, I always shudder a bit at duplicating DOM elements given how heavy weight the process is. Too bad JavaScript doesn’t have the equivalent of renderTargetBitmap where any visual you provide is easily turned into a bitmap that you can then do whatever you want with! 🥶

Got a question or just want to chat? Comment below or drop by our forums (they are actually the same thing!) where a bunch of the friendliest people you’ll ever run into will be happy to help you out!


When Kirupa isn’t busy writing about himself in 3rd person, he is practicing social distancing…even on his Twitter, Facebook, and LinkedIn profiles.

Hit Subscribe to get cool tips, tricks, selfies, and more personally hand-delivered to your inbox.





Source link

Clear Browsing History UI
Strategy

Using Selenium To Clear Browsing Data In Chrome


When working with pages in several automating projects I noticed that the fields like login ID, search fields, etc., are getting saved on the page. Thus, when my Selenium test runs, I need to put additional code to clear those fields if those are populated. I needed a way to clear the browsing data through my test before I went to the page.

Clear Browsing History UI

I attempted a few solutions from here and here. However, these worked on older versions of the Chrome browser. Next, I tried out the solution from here. It worked in part but I needed to also set the time range as AllTime.

A longer version of the final completed code is here. It basically traverses through the DOM which is tricky because of the nested shadow roots. This version is easy to understand but it is long and it’s a bit flaky as well.Screenshot of Code

I optimized the code and came up with a shorter version as below:

In the above code, we bring up the ClearBrowserData modal and then start getting the shadow root elements. Our Clear Data button is nested in the shadow root structure so a normal XPath will not be able to find it. I have also added a call to set the TimeRange as well. The extension methods written for IWebElement and IWebDriver used above are as below:

The following function sets the Time range to AllTime. We can change the SelectByValue call to select other values in the list.

I put the ClearBrowserCache method in a BrowserHelper.cs method in my framework. These methods work with Chrome 90.0.4430.85 (Official Build) (64-bit).

Call the above ClearBrowserCache method in your setup method as below:



Source link

r/webdev - How to implement a hover effect on a button?
Strategy

How to implement a hover effect on a button? : webdev


I have the following design:

r/webdev - How to implement a hover effect on a button?

The button on the left is the normal one without effects, it was pretty easy to implement nothing fancy here, the container was 52px by 52px.

The button on the right is the same button, but with a slight hover effect applied to it. The hover effect (I don’t know if you can see) is a border that extends from the middle to the edges of the container with a specific border radius, how would this be implemented?

Here’s my markup:

<button class="rbtn rbtn-close">
    <i class="fas fa-times-circle"></i>
</button>

Thanks in advance



Source link

How to Visualize a Social Network in Python With a Graph Dat...
Strategy

How to Visualize a Social Network in Python With a Graph Dat…


When you think about a web application, a graph database doesn’t usually spring to mind. Instead, most people just take the familiar route of using a SQL database to store information. While this is perfectly acceptable for most use cases, there are some instances where we could see tremendous benefits by using a graph database. In this tutorial, I will show you how to make a basic web application using Flask that stores all of its information in a graph database. To be more precise, we are using Memgraph DB, an in-memory database that can easily handle a lot of information and perform read/write instructions quite quickly.

Our use case is a Social Network Graph (in the code referred to as SNG for convenience) representing users and the connections between them. Usually, such a graph would contain millions of relationships and the algorithms that are performed on them don’t do well with data being stored in relational databases.

In this tutorial, I will show you, step-by-step, how to build a simple Python web application from the bottom up so you get a basic understanding of the technologies that are used. You can also find all of the code here if you don’t want to work on it as you go through the tutorial. If, at any point in this tutorial, you have a question or something is not working for you, feel free to post on StackOverflow with the tag memgraphdb.

Prerequisites

Because we are building a complete web application, there is a number of tools that you will need to install before we begin:

  • Poetry: a tool for dependency management and packaging in Python. It allows you to declare the libraries your project depends on and it will manage (install/update) them for you.
  • Flask: a very powerful web framework that provides you with tools, libraries, and technologies used in web development. A Flask application can be as small as a single web page or as complex as a management interface.
  • Docker and Compose: an open platform for developing, shipping, and running applications. It enables us to separate our application from our infrastructure (host machine). If you are installing Docker on Windows, Compose will be already included. For Linux and macOS visit this site.
  • Memgraph DB: a native, fully distributed in-memory graph database built to handle real-time use-cases at enterprise scale. Follow the Docker Installation instructions on the Quick Start page. While it’s completely optional, I encourage you to also install Memgraph Lab so you can execute openCypher queries on the database directly and see visualized results.

Creating the Project Structure and Handling Dependencies

Sometimes, standard packaging systems and dependency management in Python can be confusing for beginners so we decided to use Poetry.

To start building our project structure choose a working directory and run:

poetry new sng-demo

Now you should have a directory with the following content:

sng-demo
├── pyproject.toml
├── README.rst
├── sng_demo
│  └── __init__.py
└── tests
   ├── __init__.py
   └── test_poetry_demo.py

In this tutorial, we won’t use the testing functionalities so go on ahead and delete the directory tests as well as the file README.rst.

Now we need to add the dependencies for our project. Given that we are going to run the app inside a Docker container we don’t need the dependencies installed locally, only inside the container. Copy the files project.toml and poetry.lock and place them in the root directory of the project. The only other thing we need to do about dependency management is to tell Docker how to run Poetry on startup so it can install/update all the necessary dependencies inside the container.

Dockerizing an Application

In the root directory of the project create two files, Dockerfile and docker-compose.yml. At the beginning of the Dockerfile, we specify the Python version and instruct the container to install CMake, poetry, mgclient, and pymgclient. Poetry is necessary to manage our dependencies inside the container, while CMake and mgclient are required for pymgclient, the Python driver for Memgraph DB.
You don’t have to focus too much on this part, just copy the code to your Dockerfile:

Next, we define the working directory with:

The second command will enable us to cache the project requirements and only reinstall them when pyproject.toml or poetry.lock are changed.

We don’t need to create a virtual environment, because our application is already isolated by being in a Docker container. To disable it, virtualenvs.create needs to be set to false.

The second line in the command ensures that Poetry asks us no interactive questions while installing/updating dependencies and it makes the output more log friendly.

This is where we essentially create all the directories and files inside of our container. The EXPOSE command informs Docker that the container listens on the specified network port at runtime.

Next, we need to create a docker-compose.yml file. Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration. For our project, we need two services. One is the web application (sng_demo) and the other a database instance (memgraph).

If you followed the instructions on how to setup Memgraph DB with Docker correctly you only need to add the following code to your docker-compose.yml file to run the container:

When it comes to the ports key, there is an important distinction between the HOST_PORT and the CONTAINER_PORT. The first number in the key is the HOST_PORT and it can be used to connect from your host machine to the service (for example with Memgraph Lab). The second number specifies the CONTAINER_PORT, which is used for service-to-service communication. More precisely, our service sng_db can use this port to access the service memgraph and connect to the database.

The environment key contains MG_HOST and MG_PORT which represent environment variables in the service’s container. They store the memgraph service address and port which are needed to establish a database connection. The depends_on key is used to start services in dependency order because we need the database to start before the web application.

The build key allows us to tell Compose where to find the build instructions as well as the files and/or folders used during the build process. By using the volumes key, we bypass the need to constantly restart our image to load new changes to it from the host machine.

Finally, we have a dockerized project that utilizes Poetry! This approach is great for development because it enables us to run our project on completely different operating systems and environments without having to worry about compatibility issues.

Web Development With Flask

Flask is very simple to use, so why not create a Hello World! page to try out our Docker+Poetry setup.
In the project root directory, create a file called app.py with the following code:

First, we imported the Flask class and then created an instance of it. The route() decorator tells Flask what URL should trigger our function. Now, we need to tell Docker how to run our app. This can be done by creating a simple script in the project root directory. Let’s call it start.sh:

Setting FLASK_ENV to development will enable the debug mode. This makes Flask use an interactive debugger and reloader.

Setting FLASK_APP to app.py specifies how to start the application.

We need to tell Docker when and how to run this script so put the following code in your Dockerfile after the line EXPOSE 5000 :

The command chmod +x makes the script executable by setting the right permission.

To execute the script, add the following command after the line ENTRYPOINT [ "poetry", "run" ]:

That’s it! Our first web page is ready, so let’s start our app to make sure we don’t have any errors. 

In the project root directory execute:

The first build will take some time, because Docker has to download and install a lot of dependencies.
After it finishes run:

The URL of our web application is http://localhost:5000/. When you open it there should be the message, Hello World!, which means that the app is up and running.

Now it’s time to create a more complex web page that will contain our Social Network Graph. In the project root directory create a folder called templates and in it a file with the name base.html. This will be our base HTML template for other pages. Copy the code:

We also need to create an HTML file for our actual landing site that utilizes this base file and an accompanying JavaScript file. Create the HTML file in the same location with the name index.html and copy the following code into it:

In the project root directory, create a folder called static with one subfolder called js and another called css. The js folder will contain all of the needed local JavaScript files while the css folder will contain all the CSS stylesheets. In the js folder create a file called index.js and in the css folder one called style.css. Just leave them empty for now.

If you want to find out more about web development with Flask I suggest you try out this tutorial.

Your current project structure should like this:

sng-demo
├── sng_demo
│  └── __init__.py
├── templates
│  ├── base.html
│  └── index.html
├── static
│  ├── css
│  │  └── style.css
│  └── js
│     └── index.js
├── app.py
├── docker-compose.yml
├── Dockerfile
├── poetry.lock
├── pyproject.toml
└── start.sh

The Data Model and Database Connection

In the app directory sng-demo, create a folder called database. This folder will contain all of the modules that we need to communicate with the database. You can find them here and just copy their contents. They are closely related to the database driver and if you wish to examine them a bit more I suggest you look up the driver documentation here. In the app directory sng-demo, create the module db_operations.py. This is where all the custom database related commands will be located.
The sng_demo directory should look like this:

sng_demo
├── __init__.py
├── db_operations.py
└── database
   ├── __init__.py
   ├── memgraph.py
   ├── connection.py
   └── models.py

We will use a very simple data model that can be easily upgraded later on.

There is only one node with the label User and each User has two properties, a numerical id and a string name. Nodes are connected with edges of the type FRIENDS:

There are several methods to populate our database (more on that here) but we will be doing it manually by executing openCypher queries so you can get a better understanding of how to communicate with the database. You will find all the necessary queries to populate the database in the files data_big.txt and data_small.txt. The former just has a larger dataset than the latter.

In the project root directory, create a folder called resources and place the files in it. Now you can add an import method to your web application.

In the module db_operations.py add the following import and method:

The method clear() deletes any data that might have been left in the database before populating it.
The method populate_database() reads all of the openCypher queries in the specified file and executes them.

In the module app.py change the imports and method index() to:

Now every time we refresh our index page the database is cleared and repopulated with new data. While this is not suitable for the production stage, it is highly useful during development because it will enable us to make changes in the data without having to restart the whole application or work directly on the database.

If you want to examine the graph before proceeding, I suggest you open Memgraph Lab and run the query MATCH (n1)-[e:FRIENDS]-(n2) RETURN n1,n2,e;.

The result should be:

We also need a method in our app to fetch all the relevant data from the database when a client requests it.
Let’s call it get_graph() and place it in the db_operations.py module:

First, we need to execute the openCypher query MATCH (n1)-[e:FRIENDS]-(n2) RETURN n1,n2,e; and return its results from the database. These results will contain all the edges in the graph as well as all the nodes that are connected to those edges. Nodes that don’t have connections will not be returned and that’s ok for now.

The results (the object relationships) are in the form of a generator which we can iterate over and access its contents by using the node/edge names specified in our initial query (n1,n2, and e).

We also need to check if a node has already been appended to the node_objects list, because multiple edges can contain (point to or from) the same node. All of the objects are stored in key-value pairs suitable for later JSON conversion.

The final result is a JSON object containing:

  • links: all the relationships that are in the graph as pairs of source and target id properties.
  • nodes: all the nodes from the graph that form relationships with other nodes.

In your app.py module, add the following method:

This method is responsible for responding to POST requests from the client. It returns the graph data that we fetched from the server in the previous method.

Now let’s do something with this data! Copy the contents for your index.js file from here and the style.css file from here.

We also need to add the actual SVG graphic to our page so change the index.html file to:

I won’t go into much detail about how to use D3.js, so if you want to find out more I encourage you to visit their website.

In short, we fetch all the nodes and edges from the database and add them to an SVG element. The visual representation of the graph is made by simulating how physical forces act on particles (charge and gravity). You can drag and drop the nodes, hover over them to see the value of their name property, zoom in and out of the graph, and move the SVG graphic.

Additional Functionalities

Go ahead and copy the file query.js to the directory static/js and query.html to the directory templates. You can find the updated base.html file here. Copy the necessary methods from the db_operations.py module and app.py module.

After you made the changes, just open http://localhost:5000/query/ and see the results. 

This page will make your life easier if you want to debug the data being fetched from the server. It returns all the nodes or edges and shows them in a JSON highlighted format.

Your current project structure should like this:

sng-demo
├── resources
│  ├── data_big.py
│  └── data_small.txt
├── sng_demo
│  ├── __init__.py
│  ├── db_operations.py
│  └── database
│     ├── __init__.py
│     ├── memgraph.py
│     ├── connection.py
│     └── models.py
├── templates
│  ├── base.html
│  ├── index.html
│  └── query.html
├── static
│   ├── css
│   │  └── style.css
│   └── js
│      ├── index.js
│      └── query.js
├── app.py
├── docker-compose.yml
├── Dockerfile
├── poetry.lock
├── pyproject.toml
└── start.sh

Conclusion

Even though graph databases have been around for a long time, they are still not considered a mainstream tool in software development. Relational database-management systems model data as a set of predetermined structures. Complex joins and self-joins are necessary when the dataset becomes too inter-related. Modern datasets require technically complex queries which are often very inefficient in real-time scenarios.

Graph databases offer powerful data modeling and analysis capabilities for many real-world problems such as social networks, business relationships, dependencies, shipping, logistics… and they have been adopted by many of the world’s leading tech companies. With this tutorial, I hope to shed some light on how easy it is to integrate a graph database in your development process and I encourage you to try it out yourself.

As I said at the beginning, feel free to ask us any questions about this tutorial or Memgraph in general on StackOverflow with the tag memgraphdb or on our official forum. Good luck with your coding!



Source link

A Complete Guide to Custom Properties
Strategy

A Complete Guide to Custom Properties


A custom property is most commonly thought of as a variable in CSS.

.card {
  --spacing: 1.2rem;
  padding: var(--spacing);
  margin-bottom: var(--spacing);
}

Above, --spacing is the custom property with 1.2rem as the value and var(--spacing) is the variable in use.

Perhaps the most valuable reason to use them: not repeating yourself (DRY code). In the example above, I can change the value 1.2rem in one place and have it affect two things. This brings something programming languages do to CSS.

There is a good bit to know about custom properties, so let’s get into it.

Why care about CSS Custom Properties?

  1. They help DRY up your CSS. That is “Don’t Repeat Yourself.” Custom properties can make code easier to maintain because you can update one value and have it reflected in multiple places. Careful though, overdoing abstraction can make have the opposite effect and make code less understandable.
  2. They are particularly helpful for things like creating color themes on a website.
  3. They unlock interesting possibilities in CSS. In no small part because they cascade.
  4. The fact that they can be updated in JavaScript opens up even more interesting possibilities.

Naming custom properties

Custom properties must be within a selector and start with two dashes (--):

/* Nope, not within a selector */
--foo: 1;

body {
  /* No, 0 or 1 dash won't work */
  foo: 1;
  -foo: 1; 

  /* Yep! */
  --foo: 1;

  /* OK, but they're different properties */
  --FOO: 1;
  --Foo: 1;
  
  /* Totally fine */
  --mainColor: red;
  --main-color: red;

  /* Special characters are a no */
  [email protected]: red;
  --black&blue: black;
  --black^2: black;
}

Best to stick with letters, numbers, and dashes while making sure the custom property is defined inside of a valid selector.

Properties as properties

You can set the value of a custom property with another custom property:

html {
  --red: #a24e34;
  --green: #01f3e6;
  --yellow: #f0e765;

  --error: var(--red);
  --errorBorder: 1px dashed var(--red);
  --ok: var(--green);
  --warning: var(--yellow);
}

Some people like doing it this way because it allows the name of a custom property to be descriptive and then used in another property with a more functional name, again helping keep things DRY. It can even help make the functional names more readable and understandable.

Valid values for custom properties

Custom properties are surprisingly tolerant when it comes to the values they accept.

Here are some basic examples that you’d expect to work, and do.

body {
  --brand-color: #990000;
  --transparent-black: rgba(0, 0, 0, 0.5);
  
  --spacing: 0.66rem;
  --max-reading-length: 70ch;
  --brandAngle: 22deg;

  --visibility: hidden;
  --my-name: "Chris Coyier";
}

See that? They can be hex values, color functions, units of all kinds, and even strings of text.

But custom properties don’t have to be complete values like that. Let’s look at how useful it can be to break up valid CSS values into parts we can shove into custom properties.

Breaking up values

You can use custom properties to break up multi-part values.

Let’s imagine you’re using a color function, say rgba(). Each color channel value in there can be its own custom property. That opens up a ton of possibilities, like changing the alpha value for a specific use case, or perhaps creating color themes.

Splitting colors

Take HSL color, for example. We can split it up into parts, then very easily adjust the parts where we want. Maybe we’re working with the background color of a button. We can update specific parts of its HSL makeup when the button is hovered, in focus, or disabled, without declaring background on any of those states at all.

button {
  --h: 100;
  --s: 50%;
  --l: 50%;
  --a: 1;

  background: hsl(var(--h) var(--s) var(--l) / var(--a));
}
button:hover { /* Change the lightness on hover */
  --l: 75%;
}
button:focus { /* Change the saturation on focus */
  --s: 75%;
}
button[disabled] {  /* Make look disabled */
  --s: 0%;
  --a: 0.5;
}

By breaking apart values like that, we can control parts of them in a way we never could before. Just look at how we didn’t need to declare all of the HSL arguments to style the hover, focus and disabled state of a button. We simply overrode specific HSL values when we needed to. Pretty cool stuff!

Shadows

box-shadow doesn’t have a shorthand property for controlling the shadow’s spread on its own. But we could break out the box-shadow spread value and control it as a custom property (demo).

button {
  --spread: 5px;
  box-shadow: 0 0 20px var(--spread) black;
}
button:hover {
  --spread: 10px;
}

Gradients

There is no such thing as a background-gradient-angle (or the like) shorthand for gradients. With custom properties, we can change just change that part as if there was such a thing.

body {
  --angle: 180deg;
  background: linear-gradient(var(--angle), red, blue);
}
body.sideways {
  --angle: 90deg;
}

Comma-separated values (like backgrounds)

Any property that supports multiple comma-separated values might be a good candidate for splitting values too, since there is no such thing as targeting just one value of a comma-separated list and changing it alone.

/* Lots of backgrounds! */
background-image:
  url(./img/angles-top-left.svg),
  url(./img/angles-top-right.svg),
  url(./img/angles-bottom-right.svg),
  url(./img/angles-bottom-left.svg),
  url(./img/bonus-background.svg);

Say you wanted to remove just one of many multiple backgrounds at a media query. You could do that with custom properties like this, making it a trivial task to swap or override backgrounds.

body {
  --bg1: url(./img/angles-top-left.svg);
  --bg2: url(./img/angles-top-right.svg);
  --bg3: url(./img/angles-bottom-right.svg);
  --bg4: url(./img/angles-bottom-left.svg);
  --bg5: url(./img/bonus-background.svg);
  
  background-image: var(--bg1), var(--bg2), var(--bg3), var(--bg4);
}
@media (min-width: 1500px) {
  body {
    background-image: var(--bg1), var(--bg2), var(--bg3), var(--bg4), var(--bg5);
  }
}

Grids

We’re on a roll here, so we might as well do a few more examples. Like, hey, we can take the grid-template-columns property and abstract its values into custom properties to make a super flexible grid system:

.grid {
  display: grid;
  --edge: 10px;
  grid-template-columns: var(--edge) 1fr var(--edge);
}
@media (min-width: 1000px) {
  .grid {
     --edge: 15%;
   }
}

Transforms

CSS will soon get individual transforms but we can get it sooner with custom properties. The idea is to apply all the transforms an element might get up front, then control them individually as needed:

button {
  transform: var(--scale, scale(1)) var(--translate, translate(0));
}
button:active {
  --translate: translate(0, 2px);
}
button:hover {
  --scale: scale(0.9);
}

Concatenation of unit types

There are times when combining parts of values doesn’t work quite how you might hope. For example, you can’t make 24px by smashing 24 and px together. It can be done though, by multiplying the raw number by a number value with a unit.

body {
  --value: 24;
  --unit: px;
  
  /* Nope */
  font-size: var(--value) + var(--unit);
  
  /* Yep */
  font-size: calc(var(--value) * 1px);

  /* Yep */
  --pixel_converter: 1px;
  font-size: calc(var(--value) * var(--pixel_converter));
}

Using the cascade

The fact that custom properties use the cascade is one of the most useful things about them.

You’ve already seen it in action in many of the examples we’ve covered, but let’s put a point on it. Say we have a custom property set pretty “high up” (on the body), and then set again on a specific class. We use it on a specific component.

body {
  --background: white;
}
.sidebar {
  --background: gray;
}
.module {
  background: var(--background);
}

Then say we’ve got practical HTML like this:

<body> <!-- --background: white -->

  <main>
    <div class="module">
      I will have a white background.
    </div>
  <main>

  <aside class="sidebar"> <!-- --background: gray -->
    <div class="module">
      I will have a gray background.
    </div>
  </aside>

</body>
Three CSS rulesets, one for a body, sidebar and module. the background custom property is defined as white on body and gray on sidebar. The module calls the custom property and shows an orange arrow pointing to the custom property defined in the sidebar since it is the nearest ancestor.
For the second module, .sidebar is a closer ancestor than body, thus --background resolves to gray there, but white in other places.

The “module” in the sidebar has a gray background because custom properties (like many other CSS properties) inherit through the HTML structure. Each module takes the --background value from the nearest “ancestor” where it’s been defined in CSS.

So, we have one CSS declaration but it’s doing different things in different contexts, thanks to the cascade. That’s just cool.

This plays out in other ways:

button {
  --foo: Default;
}
button:hover {
  --foo: I win, when hovered;
  /* This is a more specific selector, so re-setting 
     custom properties here will override those in `button` */
}

Media queries don’t change specificity, but they often come later (or lower) in the CSS file than where the original selector sets a value, which also means a custom property will be overridden inside the media query:

body {
  --size: 16px;
  font-size: var(--size);
}
@media (max-width: 600px) {
  body {
    --size: 14px;
  } 
}

Media queries aren’t only for screen sizes. They can be used for things like accessibility preferences. For example, dark mode:

body {
  --bg-color: white; 
  --text-color: black;

  background-color: var(--bg-color);
  color: var(--text-color);
}

/* If the user's preferred color scheme is dark */
@media screen and (prefers-color-scheme: dark) {
  body {
    --bg-color: black;
    --text-color: white;
  }
}

The :root thing

You’ll often see custom properties being set “at the root.” Here’s what that means:

:root {
  --color: red;
}

/* ...is largely the same as writing: */
html {
  --color: red;
}

/* ...except :root has higher specificity, so remember that! */

There is no particularly compelling reason to define custom properties like that. It’s just a way of setting custom properties as high up as they can go. If you like that, that’s totally fine. I find it somehow more normal-feeling to apply them to the html or body selectors when setting properties I intend to make available globally, or everywhere.

There is also no reason you need to set variables at this broad of a scope. It can be just as useful, and perhaps more readable and understandable, to set them right at the level you are going to use them (or fairly close in the DOM tree).

.module {
  --module-spacing: 1rem;
  --module-border-width: 2px;

  border: var(--module-border-width) solid black;
}

.module + .module {
  margin-top: var(--module-spacing);
}

Note that setting a custom property on the module itself means that property will no longer inherit from an ancestor (unless we set the value to inherit). Like other inherited properties, there are sometimes reasons to specify them in place (at the global level), and other times we want to inherit them from context (at the component level). Both are useful. What’s cool about custom properties is that we can define them in one place, inherit them behind the scenes and apply them somewhere completely different. We take control of the cascade!

Combining with !important

You can make an !important modifier within or outside of a variable.

.override-red {
  /* this works */
  --color: red !important;  
  color: var(--color);

  /* this works, too */
  --border: red;
  border: 1px solid var(--border) !important;
}

Applying !important to the --color variable, makes it difficult to override the value of the --color variable, but we can still ignore it by changing the color property. In the second example, our --border variable remains low-specificity (easy to override), but it’s hard to change how that value will be applied to the border itself.

Custom property fallbacks

The var() function is what allows for fallback values in custom properties.

Here we’re setting a scale() transform function to a custom property, but there is a comma-separated second value of 1.2. That 1.2 value will be used if --scale is not set.

.bigger {
  transform: scale(var(--scale, 1.2));
}

After the first comma, any additional commas are part of the fallback value. That allows us to create fallbacks with comma-separated values inside them. For example, we can have one variable fall back to an entire stack of fonts:

html {
  font-family: var(--fonts, Helvetica, Arial, sans-serif);
}

We can also provide a series of variable fallbacks (as many as we want), but we have to nest them for that to work:

.bigger {
  transform: scale(var(--scale, var(--second-fallback, 1.2));
}

If --scale is undefined, we try the --second-fallback. If that is also undefined, we finally fall back to 1.2.

Using calc() and custom properties

Even more power of custom properties is unlocked when we combine them with math!

This kind of thing is common:

main {
  --spacing: 2rem;
}

.module {
  padding: var(--spacing);
}

.module.tight {
  /* divide the amount of spacing in half */
  padding: calc(var(--spacing) / 2)); 
}

We could also use that to calculate the hue of a complementary color:

html {
  --brand-hue: 320deg;
  --brand-color: hsl(var(--brand-hue), 50%, 50%);
  --complement: hsl(calc(var(--brand-hue) + 180deg), 50%, 50%);
}

calc() can even be used with multiple custom properties:

.slider {
  width: calc(var(--number-of-boxes) * var(--width-of-box));
}

Deferring the calc()

It might look weird to see calculous-like math without a calc():

body {
  /* Valid, but the math isn't actually performed just yet ... */
  --font-size: var(--base-font-size) * var(--modifier);

  /* ... so this isn't going to work */
  font-size: var(--font-size);
}

The trick is that as long as you eventually put it in a calc() function, it works fine:

body {
  --base-font-size: 16px;
  --modifier: 2;
  --font-size: var(--base-font-size) * var(--modifier);

  /* The calc() is "deferred" down to here, which works */
  font-size: calc(var(--font-size));
}

This might be useful if you’re doing quite a bit of math on your variables, and the calc() wrapper becomes distracting or noisy in the code.

@property

The @property “at-rule” in CSS allows you to declare the type of a custom property, as well its as initial value and whether it inherits or not.

It’s sort of like you’re creating an actual CSS property and have the ability to define what it’s called, it’s syntax, how it interacts with the cascade, and its initial value.

@property --x {
  syntax: '<number>';
  inherits: false;
  initial-value: 42;
}
Valid Types
  • length
  • number
  • percentage
  • length-percentage
  • color
  • image
  • url
  • integer
  • angle
  • time
  • resolution
  • transform-list
  • transform-function
  • custom-ident (a custom identifier string)

This means that the browser knows what kind of value it is dealing with, rather than assuming everything is a string. That means you can animate things in ways you couldn’t otherwise.

For example, say you have a star-shaped icon that you want to spin around with @keyframes and rotate with a transform. So you do this:

.star {
  --r: 0deg;
  transform: rotate(var(--r));
  animation: spin 1s linear infinite;
}

@keyframes spin {
  100% {
    --r: 360deg;
  }
}

That actually won’t work, as the browser doesn’t know that 0deg and 360deg are valid angle values. You have to define them as an <angle> type with @property for that to work.

@property --r {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}

.star {
  --r: 0deg;
  transform: rotate(var(--r));
  animation: spin 1s linear infinite;
}

@keyframes spin {
  100% {
    --r: 360deg;
  }
}
Demo

Commas in values

This can be a smidge confusing. Maybe not so much this:

html {
  --list: 1, 2, 3;
}

But below, you’ll need a sharp eye to realize the fallback value is actually 1.2, 2. The first comma separates the fallback, but all the rest is part of the value.

html {
  transform: scale(var(--scale, 1.2, 2));
}

Learn more about fallbacks above ⮑

Advanced usage

The Raven is a technique that emulates container queries using math and custom properties. Be prepared, this goes from 0-100 in complexity right out of the gate!

Demo

Resize this demo to see a grid of inline-block elements change number of columns from 4 to 3 to 1.

Here’s a few more favorite examples that show off advanced usage of custom properties:

The initial and whitespace trick

Think of @media queries and how when one thing changes (e.g. the width of the page) you can control multiple things. That’s kind of the idea with this trick. You change one custom property and control multiple things.

The trick is that the value of initial for a custom property will trigger a fallback, while an empty whitespace value will not. For the sake of explanation, it let’s define two globally-scoped custom properties, ON and OFF:

:root {
  --ON: initial;
  --OFF: ;
}

Say we have a “dark” variation class which sets a number of different properties. The default is --OFF, but can be flipped to --ON whenever:

.module {
  --dark: var(--OFF);
}

.dark { /* could be a media query or whatever */
  --dark: var(--ON);
}

Now you can use --dark to conditinally set values that apply only when you’ve flipped --dark to --ON. Demo:

Lea Verou has a great writeup that covers all of this.

Inline styles

It’s totally legit to set a custom property in HTML with an inline style.

<div style="--color: red;"></div>

That will, like any inline style, have a very high level of specificity.

This can be super useful for when the HTML might have access to some useful styling information that would be too weird/difficult to put into a static CSS file. A good example of that is maintaining the aspect ratio of an element:

<div style="--aspect-ratio: 16 / 9;"></div>

Now I can set up some CSS to make a box of that exact size wherever I need to. The full writeup on that is here, but here’s CSS that uses trickery like the ol’ padded box applied to a pseudo element which pushes the box to the desired size:

[style*="--aspect-ratio"] > :first-child {
  width: 100%;
}
[style*="--aspect-ratio"] > img {  
  height: auto;
} 
@supports (--custom: property) {
  [style*="--aspect-ratio"] {
    position: relative;
  }
  [style*="--aspect-ratio"]::before {
    content: "";
    display: block;
    padding-bottom: calc(100% / (var(--aspect-ratio)));
  }  
  [style*="--aspect-ratio"] > :first-child {
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
  }  
}

But hey, these days, we have a native aspect-ratio property in CSS, so setting that in the inline style might make more sense going forward.

<div style="aspect-ratio: 16 / 9;"></div>

Hovers and pseudos

There is no way to apply a :hover style (or other pseudo classes/elements) with inline styles. That is, unless we get tricky with custom properties. Say we want custom hover colors on some boxes — we can pass that information in as a custom property:

<div style="--hover-color: red;"><div>
<div style="--hover-color: blue;"><div>
<div style="--hover-color: yellow;"><div>

Then use it in CSS which, of course, can style a link’s hover state:

div:hover {
  background-color: var(--hover-color);
}

/* And use in other pseudos! */
div:hover::after {
  content: "I am " attr(style);
  border-color: var(--hover-color);
}

Custom properties and JavaScript

JavaScript can set the value of a custom property.

element.style.setProperty('--x', value);

Here’s an example of a red square that is positioned with custom properties, and JavaScript updates those custom property values with the mouse position:

Typically you think of JavaScript passing values to CSS to use, which is probably 99% of usage here, but note that you can pass things from CSS to JavaScript as well. As we’ve seen, the value of a custom property can be fairly permissive. That means you could pass it a logical statement. For example:

html {
  --logic: if (x > 5) document.body.style.background = "blue";
}

Then grab that value and execute it in JavaScript:

const x = 10;

const logic = getComputedStyle(document.documentElement).getPropertyValue(
  "--logic"
);

eval(logic);

Custom properties are different than preprocessor variables

Say you’re already using Sass, Less, or Stylus. All those CSS preprocessors offer variables and it’s one of the main reasons to have them as part of your build process.

// Variable usage in Sass (SCSS)
$brandColor: red;

.marketing {
  color: $brandColor;
}

So, do you even need to bother with native CSS custom properties then? Yes, you should. Here’s why in a nutshell:

  • Native CSS custom properties are more powerful then preprocessor variables. Their integration with the cascade in the DOM is something that preprocessor variables will never be able to do.
  • Native CSS custom properties are dynamic. When they change (perhaps via JavaScript, or with a media query), the browser repaints what it needs to. Preprocessor variables resolve to a value when they’re compiled and stay at that value.
  • Going with a native feature is good for the longevity of your code. You don’t need to preprocess native CSS.

I cover this in much more detail in the article “What is the difference between CSS variables and preprocessor variables?”

To be totally fair, there are little things that preprocessor variables can do that are hard or impossible with custom properties. Say you wanted to strip the units off a value for example. You can do that in Sass but you’ll have a much harder time with custom properties in CSS alone.

Can you preprocess custom properties?

Kinda. You can do this, with Sass just to pick one popular preprocessor:

$brandColor: red;
body {
  --brandColor: $brandColor;
}

All that’s doing is moving a Sass variable to a custom property. That could be useful sometimes, but not terribly. Sass will just make --brandColor: red; there, not process the custom property away.

If a browser doesn’t support custom properties, that’s that. You can’t force a browser to do what custom properties do by CSS syntax transformations alone. There might be some kind of JavaScript polyfill that parses your CSS and replicates it, but I really don’t suggest that.

The PostCSS Custom Properties plugin, though, does do CSS syntax transforms to help. What it does is figure out the value to the best of it’s ability, and outputs that along with the custom property. So like:

:root {
  --brandColor: red;
}
body {
  color: var(--brandColor);
}

Will output like this:

:root {
  --brandColor: red;
}
body {
  color: red;
  color: var(--brandColor);
}

That means you get a value that hopefully doesn’t seem broken in browsers that lack custom property support, but does not support any of the fancy things you can do with custom properties and will not even attempt to try. I’m a bit dubious about how useful that is, but I think this is about the best you can do and I like the spirit of attempting to not break things in older browsers or newer browsers.

Availiability

Another thing that is worth noting about the difference between is that with a CSS preprocessor, the variables are available only as you’re processing. Something like $brandColor is meaningless in your HTML or JavaScript. But when you have custom properties in use, you can set inline styles that use those custom properties and they will work. Or you can use JavaScript to figure out their current values (in context), if needed.

Aside from some somewhat esoteric features of preprocessor variables (e.g. some math possibilities), custom properties are more capable and useful.

Custom properties and Web Components (Shadow DOM)

One of the most common and practical ways to style of Web Components (e.g. a <custom-component> with shadow DOM) is by using custom properties as styling hooks.

The main point of the shadow DOM is that it doesn’t “leak” styles in or out of it, offering style isolation in a way that nothing else offers, short of an <iframe>. Styles do still cascade their way inside, I just can’t select my way inside. This means custom properties will slide right in there.

Here’s an example:

Another common occurrence of the shadow DOM is with SVG and the <use> element.

Video: “CSS Custom Properties Penetrate the Shadow DOM”

Browser support

Desktop

Chrome Firefox IE Edge Safari
49 31 No 16 10

Mobile / Tablet

Android Chrome Android Firefox Android iOS Safari
90 87 90 10.0-10.2

You can preprocess for deeper browser support, with heavy limitations.

@supports

If you would like to write conditional CSS for when a browser supports custom properties or not:

@supports (--custom: property) {
  /* Isolated CSS for browsers that DOES support custom properties, assuming it DOES support @supports */
}

@supports not (--custom: property) {
  /* Isolated CSS for browsers that DON'T support custom properties, assuming it DOES support @supports */
}

Credit

Thanks to Miriam Suzanne for co-authoring this with me!



Source link

Can I get some advice on product card layout please?
Strategy

Can I get some advice on product card layout please?


Can I get some advice on product card layout please?

https://preview.redd.it/ukmpbikkj1x61.png?width=499&format=png&auto=webp&s=57f60eca0a484904ad1cafe288543edb3d6280cd

https://preview.redd.it/hl2eokkkj1x61.png?width=255&format=png&auto=webp&s=604a09d1e3b5d919f6924db569f22200cb7fa089

Can I get some advice on card layout please? I'm pretty new to web-design/web-dev and I would be very grateful if some could give me some advice/ideas. Thank you very much.

submitted by /u/Shinhosuck1973
[comments]



Source link

Revealing Module Pattern Structure
Strategy

Structure JavaScript Code – DZone Web Dev


Introduction

Modern JavaScript frameworks like Angular, Vue, etc. have a built-in mechanism to structure JavaScript code. When not using these frameworks, we can use simple techniques to structure our JavaScript. In this post, I will show you one of them using the Revealing Module pattern.

This pattern is very popular and there are a great many resources online utilizing this design and here I will try to share a simple implementation of the same.

Why Structure JavaScript Code

Encapsulated and modular-based JavaScript code promotes reusability and is better for maintenance.

To avoid spaghetti JavaScript all over the code base, a Revealing Module pattern shall be used to structure JavaScript code.

Following are some of the benefits of using this pattern:

  • “Modularize” code into re-usable objects.
  • Variable/functions taken out of the global namespace.
  • Expose only public members.
  • ‘Cleaner’ way to expose public members.

Revealing Module Structure

Here is an example of kind of the standard format and structure for the Revealing Module pattern.

Revealing Module Pattern Structure

Initial Setup

Before we dive into JavaScript code, let’s talk about the initial development environment setup. I am using a very basic JavaScript project for the demo, it just has a single HTML page, some CSS styles, and a basic dev server. For more information, please check the Basic Front-end Dev. Environment Setup post. This will set up a starting point for us to move forward.

At this point, I have some HTML and an empty app.js file.

HTML in app.js File

App JavaScript Code

Let’s start with the application itself. Following the structure mentioned above, here is the shell for our application.

Now, let’s add some functionality to our app:

Here, I added one property, title and two functions, displayTitle() and displaySite(). Then in the public API section, I exposed the title property and one function. Notice that I did not expose the displaySite function and this illustrates a private function that shall not be accessible from outside of this code block.

Next, I added the following code in the document.ready in index.html as shown below:

Now, if you run the npm start command and go to browser page, you will see that the first two lines works and the third line app.displaySite() does not work, which is expected.

So, this is the general idea of this pattern. It helps us organize our JavaScript code while not polluting the global namespace.

Next, let’s see how we can use this pattern to consolidate AJAX calls.

Consolidating AJAX Calls

In order to eliminate scattered Ajax calls throughout the code, we can use Revealing Module Pattern to consolidate Ajax calls.

Developers can create a data-services script and use Ajax promises for success and failure scenarios via callbacks.

Typically a project will have more than one data service and not repeat the boilerplate Ajax code. we can refactor the actual Ajax calling part into another JavaScript object called, ajaxService and, you guessed it right, we can use the Revealing Module pattern for this service as well.

ajaxService will be a low-level implementation detail and it will handle all types of Ajax calls. While Data-Services will use the ajaxService to perform data-related operations.

ajaxService Implementation

Here is a simple implementation of ajaxService (ajaxService.js). It just has one method which takes some input and returns a promise which calling code can then use for making Ajax calls.

DataService Implementation

I created another JavaScript file called dataService.js with the following code:

It is also using the Revealing Module pattern and the code is simple. This service uses ajaxService internally. Note that I am using a publicly available test backend on the internet and making Ajax calls to it. You can use your own REST API by replacing the endpoints (URLs).

App.js Code Update

I created new methods in app.js file and these methods use dataService for their operations.

New Methods in app.js File

Application Bootstrap

I also referenced these JavaScript files in the index.html page and updated the ready function as shown below:

Updating index.html

and here is the console output of these operations:

Console Operation Output

Summary

The Revealing Module pattern can help structure JavaScript code. It has limitations, but for simple codebases, this can work very well. You can download the demo code from this git repo. Let me know if you have any comments or questions. Till next time, Happy Coding!



Source link

How to Create Actions for Selected Text With the Selection A...
Strategy

How to Create Actions for Selected Text With the Selection A…


Click, drag, release: you’ve just selected some text on a webpage — probably to copy and paste it somewhere or to share it. Wouldn’t it be cool if selecting that text revealed some options that make those tasks easier? That’s what a selection menu does.

You may already be familiar with selection menus if you’ve ever used an online editor. When you select text, options to format the selection might float above it. In fact, I’m writing this draft in an editor that does exactly this.

Let’s see how we can create a selection menu like this using JavaScript’s Selection API. The API gives us access to the space and content of the selected area on a webpage. This way we can place the selection menu exactly above the selected text and get access to the selected text itself.

Here’s an HTML snippet with some sample text:

<article>
  <h1>Select over the text below</h1> 
  <p>Cascading Style Sheets (CSS) is a style sheet language used for describing the presentation of a document written in a markup language such as HTML. CSS is a cornerstone technology of the World Wide Web, alongside HTML and JavaScript. CSS is designed to enable the separation of presentation and content, including layout, colors, and fonts. This separation can improve content accessibility, provide more flexibility and control in the specification of presentation characteristics. </p>
</article>
<template><span id="control"></span></template>

There’s a <template> tag at the end there. The <span> inside it is our selection menu control. Anything inside a <template> tag is not rendered on the page until it’s later added to the page with JavaScript. We’ll add the selection menu control to the page when user selects text. And when the user selects that text, our selection menu will prompt the user to tweet it.

Here’s the CSS to style it:

#control {
    background-image: url("data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 width='40px' height='40px'><foreignObject width='40px' height='40px'><div xmlns='http://www.w3.org/1999/xhtml' style='width:40px;height:40px;line-height:40px;text-align:center;color:transparent;text-shadow: 0 0 yellow, 2px 4px black, -1px -1px black;font-size:35px;'>💬</div></foreignObject></svg>");
  cursor: pointer;
  position: absolute;
  width: 40px;
  height: 40px;
}
#control::before{
  background-color: black;
  color: white;
  content: " tweet this! ";
  display: block;
  font-weight: bold;
  margin-left: 37px;
  margin-top: 6px;
  padding: 2px;
  width: max-content;
  height: 20px;
}

Check out this article to learn how I used an emoji (💬) for the background image.

So far, the sample text is ready, and the selection menu control has been styled. Let’s move on to the JavaScript. When a selection is made, we’ll get the size and position of the selected area on the page. We then use those measurements to assign the position of the selection menu control at the top-middle of the selected area.

var control = document.importNode(document.querySelector('template').content, true).childNodes[0];
document.querySelector('p').onpointerup = () => {
  let selection = document.getSelection(), text = selection.toString();
  if (text !== "") {
    let rect = selection.getRangeAt(0).getBoundingClientRect();
    control.style.top = `calc(${rect.top}px - 48px)`;
    control.style.left = `calc(${rect.left}px + calc(${rect.width}px / 2) - 40px)`;
    control['text']= text; 
    document.body.appendChild(control);
  }
}

In this code, we first get a copy of the selection menu control inside <template>, then assign it to the control variable.

Next, we write the handler function for the onpointerup event of the element carrying the sample text. Inside the function, we get the selection and the selected string using document.getSelection(). If the selected string is not empty, then we get the selected area’s size and position, via getBoundingClientRect(), and place it in the rect variable.

Using rect, we calculate and assign the top and left positions of the control. This way, the selection menu control is placed a little above the selected area and centered horizontally. We’re also assigning the selected string to a user-defined property of control. This will later be used to share the text.

And, finally, we add control to the webpage using appendChild(). At this point, if we select some of the sample text on the page, the selection menu control will appear on the screen.

Now we get to code what happens when the selection menu control is clicked. In other words, we’re going to make it so that the text is tweeted when the prompt is clicked.

control.addEventListener('pointerdown', oncontroldown, true);

function oncontroldown(event) {
  window.open(`https://twitter.com/intent/tweet?text=${this.text}`)
  this.remove();
  document.getSelection().removeAllRanges();
  event.stopPropagation();
}

When the control is clicked, a tab opens with Twitter’s “New Tweet” page, complete with the selected text ready to go.

After the tweet prompt, the selection menu control is no longer needed and is removed, along with any selection made on the page. The way that the pointerdown event cascades further down the DOM tree is also stopped at this point.

We also need an event handler for the onpointerdown event of the page:

document.onpointerdown = ()=> {    
  let control = document.querySelector('#control');
  if (control !== null) {control.remove();document.getSelection().removeAllRanges();}
}

Now the control and any selection made on the page are removed when clicking anywhere on the page but the selection menu control.

Demo

Here’s a more prettified version that Chris put together:

And here’s an example showing more than one control in the selection menu:

About that <template>

It’s not totally necessary that we use it. Instead, you can also try simply hiding and showing the control some other way, like the hidden HTML attribute or the CSS display. You can also build a selection menu control in the JavaScript itself. The coding choices will depend on how efficiently you execute them, and their fallbacks, if needed, as well as how they fit in with your application.

Some UI/UX advice

While this is a nice effect, there are a couple of things to consider when using it to ensure a good user experience. For example, avoid injecting your own text into the text selection — you know, like appending a link back to your site in the auto-generated tweet. It’s intrusive and annoying. If there’s any reason to do that, like adding the source citation, let the user see a preview of the final text before posting it. Otherwise, the user might be confused or surprised by the addition.

One more thing: It’s best if the menu control is out of the way. We don’t want it covering up too much of the surrounding content. That sort of thing adds up to CSS “data loss” and we want to avoid that.

Bottom line: Understand why your users need to select text on your website and add the controls in a way that gets out of the way of what they’re trying to do.





Source link

Windows Desktop, Mobile and Web Displays
Strategy

Flutter 2.0 State Management Introduction With Provider 5.0


With Flutter 2.0, you can build apps on mobile, web and desktop.  Graphics performance is fantastic and the development tools are great. The main barrier to learning Flutter is an understanding of state management.  This tutorial covers the Provider package, one of the most popular and easiest tools to manage state in Flutter.

Windows Desktop, Mobile and Web Displays

A video version of this tutorial is available. Code and image files are on GitHub. 

When to Use Provider

If your application is on a single screen with limited interaction with outside data sources and APIs, you should use the built-in setState method of Flutter.  You do not need to use a provider for simple applications.  If your application has multiple screens with multiple API calls, you should use the provider.

How Provider Works

Provider makes it easier to set widgets to listen a variable and wait for a change. You can also use the provider to send a notification to a listener when data changes.

For our simplified example, we have two screens. The first screen is a list of thumbnails.

List of Thumbnails

Each thumbnail is 9KB with a size of 320×160.  The thumbnails are designed to be fast to load over a network.  To make the tutorial easier, the thumbnails and full-size images are stored locally.  The thumbnails and images are in equirectangular format to provide a virtual 360°  experience. When a thumbnail is tapped, the application goes to another screen that displays a full-size image with 360° navigation.

Full-sized Image with 360 Navigation

ChangeNotifier and notifyListeners

The core concept of a provider is that a widget listens for changes.  To help manage the changes and notifications to the listeners, we use the ChangeNotifer Flutter class.  In the example below, the class ImageNotifier manages changes to a variable called image.

When a person presses the thumbnail button on screen 1, the updateImage() method is run. On screen 2, the image is displayed with the getter image.

ChangeNotifierProvider

Up to this point, we haven’t used provider.  ChangeNotifier is built into Flutter.

Our first step to using a provider starts in the main() method.  It’s the first thing you run in runApp(). In the main.dart file, we need to wrap ChangeNotifierProvider around your main app.  I’ve called the main app MyApp().

Change, Notification, and Provide

Let’s review the core concepts up to this point.

  1. Data changes.  In our example, we’re changing an image. The change starts by clicking on the thumbnail.  The image variable is changed with the updateImage() method we created.
  2. Create a notification of the change. In our example, our notification is that the full resolution image has changed. The notification is created with notifyListeners() which is also in the updateImage() method.
  3. Provide the notification to the widgets that are listening for the specific change.

Although these concepts are fairly easy to understand, it is different from a procedural programming paradigm.

Procedural Process

Let’s look at an alternative process with psuedo-code.

On screen 1

buttonPress(image=imageName) 

On screen 2

displayImage(imageName)

The process above is easier to understand for simple applications.  It becomes increasingly difficult to manage as the application gets more complex.

Provider Process

The provider process adds a few additional steps. More importantly, it requires a change in thinking about how the image is displayed.  Instead of the button changing the imageName variable in screen 2 directly, the button talks to an intermediary that sends a notification to any widget that has subscribed to the change.

Screen 1

buttonPress(updateImage(imageName))

Change Notification Manager

updateImage(imageName)

notifyListeners(imageName)

Screen 2

displayImage(imageName)

Updating the Image

From screen 1, we need to call the updateImage() method.  The code may look a bit confusing at first, but you’ll quickly get used to it with just a little bit of practice.

context.read<ImageNotifier>().updateImage(...

ImageNotifier is the name of the class we created that extends ChangeNotifier, which is built into Flutter.

The full code for the widget of the thumbnail is shown below.

Displaying the Image

The image is displayed with a similar process of reading from the context.

context.read<ImageNotifier>().image

The last word in the line, image, is the getter from ImageNotifier.

I’ve wrapped the image in Panorama() to get the 360° navigation.

Running the Code

You need to have Flutter 2.0 and Dart 2.12. 

Select the Device

Note, to run Windows, you will need Visual Studio (not just VS Code) installed.  To run macOS and iOS, you’ll need XCode.  To run Android, you need an Android Virtual Device.  The easiest way to get the AVD is to install Android Studio.

Select the Device

Desktop App

More information on building for Windows desktop is available in my previous DZone article, Build Great Windows Desktop Apps With Flutter.

To quickly gain practice modifying the example code for this tutorial, you can add a new variable to image_notifier.dart.

Remember to add the following for each new variable:

  1. Private variable initialization.  Note that the example uses Dart 2.12 with sound null safety. You need to assign a default value to all variables or explicitly tell dart the variable can be null.  You cannot do something like String _caption;
  2. Getter.
  3. Setter with notifyListeners.

image_notifier.dart Screenshot

If you prefer to start with a completely blank editor and a new Flutter project, go through the steps in the video I mentioned at the start of the article.

Other State Management Techniques

There are many other Flutter state management techniques.  Refer to the Flutter official documents for more options.  Provider is a great way for you to get started.

Summary

Flutter is a wonderful framework for rapidly building mobile, desktop, and web apps.  There are many options for state management.  If your app has more than a few screens and data storage options, you should use a state management package instead of relying on setState(). Provider is one of the most popular packages for state management.  It’s easy to use once you understand the process of making a change, creating a notification of the change, providing the change, and listening for the change.

The best way to really understand the concept of state management is to implement it.  Open up VS Code and starting coding right now!



Source link