google – CSS-Tricks https://css-tricks.com Tips, Tricks, and Techniques on using Cascading Style Sheets. Mon, 14 Nov 2022 20:35:38 +0000 en-US hourly 1 https://wordpress.org/?v=6.1.1 https://i0.wp.com/css-tricks.com/wp-content/uploads/2021/07/star.png?fit=32%2C32&ssl=1 google – CSS-Tricks https://css-tricks.com 32 32 45537868 Auto Dark Theme https://css-tricks.com/auto-dark-theme/ https://css-tricks.com/auto-dark-theme/#comments Mon, 20 Dec 2021 18:34:47 +0000 https://css-tricks.com/?p=359017 There are a number of ways to approach a dark mode for your website, but essentially you get all the styles ready for it and then apply them when the user has indicated they want them, whether by direct …


Auto Dark Theme originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
There are a number of ways to approach a dark mode for your website, but essentially you get all the styles ready for it and then apply them when the user has indicated they want them, whether by direct choice or a system-level preference. If your website doesn’t have a dark mode, well, then, it doesn’t have a dark mode.

Chrome is testing changing that, and forcing your website to have a dark mode if a user has indicated they prefer them at the system level. André Cipriani Bandarra:

With this feature, the browser applies an automatically generated dark theme to light themed sites, when the user has opted into dark themes in the operating system. Users can opt-out of dark themes by either disabling the option on the OS level or in a specific setting in Chrome.

A little scary, I think. But hey let’s give it a test to see. They’ve built in the ability to test this right into Chrome DevTools.

Just looking at my own site quick…

Emulated dark theme on left, normal on right. 😭

The homepage looks like crap, as it didn’t flip out the explicit white background color. Not sure why, as it does on article pages where it looks much better:

The link blue feels too dark to me, but otherwise it kinda makes me want to build a real dark theme.

The WordPress admin famously has no dark mode, so here’s a look at that:

Kinda good?

I have mixed feelings about it. On one hand, users should have control over what they want to see in their browsers on their computers. Nobody is claiming accessibility features like high contrast, invert colors, or reduced motion modes shouldn’t exist. They directly affect website rendering. This could be considered an accessibility feature. On the other hand, this can cause accessibility problems (case in point: the contrast issues visible right here in that first screenshot).

This is just an Origin Trial for now, so I imagine there is plenty of time before this ships and for problems to get ironed out. But the web is huge so I imagine the edge cases here are absolutely bonkers and this is going to be very hard to perfect. It’s not even clear if this is just a Chrome-on-Android thing or if it will spread wider than that. It’s also not clear that if it leaves your site alone if you have a hand-crafted dark mode, or if it still messes with the design. Me, I’d probably use their mechanism for opt-ing out until I could get the details right.


Auto Dark Theme originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/auto-dark-theme/feed/ 6 359017
The UI fund https://css-tricks.com/the-ui-fund/ https://css-tricks.com/the-ui-fund/#respond Wed, 15 Dec 2021 15:18:29 +0000 https://css-tricks.com/?p=359107 Google is handing out bucks for CSS-related projects, so you might as well know about it! Nicole Sullivan:

All of us who work on the web regularly benefit from the work of people who create specifications, tools, demos, tutorials, and


The UI fund originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Google is handing out bucks for CSS-related projects, so you might as well know about it! Nicole Sullivan:

All of us who work on the web regularly benefit from the work of people who create specifications, tools, demos, tutorials, and polyfills. Many of these resources are side projects, made available and supported free of charge. They exist because someone saw a need, created something to meet it, then shared it with the rest of the ecosystem.

We want to recognize and help support this work and invest in the CSS ecosystem.

Ya know how esbuild has seriously shaken things up for the JavaScript processing world? Maybe we need a cssbuild? It would process imports and do bundling (something we generally rely on Sass for). The point would be extreme speed. Maybe it would be plugin-based and compatible with the PostCSS API so that existing PostCSS plugins would work on it. Maybe it could make sourcemaps and do modification. Maybe it would run your Sass, too, I dunno. But something to spark the CSS ecosystem like that could be cool.

Or apply for money to do extreme accessibility testing and implementation on a public design system.

To Shared LinkPermalink on CSS-Tricks


The UI fund originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/the-ui-fund/feed/ 0 359107
What is Your Page Title on a Google Search Engine Results Page? https://css-tricks.com/what-is-your-page-title-on-a-google-search-engine-results-page/ https://css-tricks.com/what-is-your-page-title-on-a-google-search-engine-results-page/#comments Thu, 23 Sep 2021 18:11:15 +0000 https://css-tricks.com/?p=352279 Whatever Google wants it to be. I always thought it was exactly what your <title> element was. Perhaps in lieu of that, what the first <h1> on the page is. But recently I noticed some pages on this site that …


What is Your Page Title on a Google Search Engine Results Page? originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Whatever Google wants it to be. I always thought it was exactly what your <title> element was. Perhaps in lieu of that, what the first <h1> on the page is. But recently I noticed some pages on this site that were showing a title on SERPs that was a string that appeared nowhere at all in the source of the page.

When I first noticed it, I tweeted my basic findings…

The thread has more info than what unfurled here.

This is a known thing. Apparently, they’ve been doing this for a long time (~10 years), but it’s the first I’ve noticed it. And it’s undergone a recent change:

The article is pretty clear about things:

[…] we think our new system is producing titles that work better for documents overall, to describe what they are about, regardless of the particular query.

Also, while we’ve gone beyond HTML text to create titles for over a decade, our new system is making even more use of such text. In particular, we are making use of text that humans can visually see when they arrive at a web page. We consider the main visual title or headline shown on a page, content that site owners often place within <H1> tags or other header tags, and content that’s large and prominent through the use of style treatments.

Other text contained in the page might be considered, as might be text within links that point at pages.

The change is in response to people having sucky <title> text. Like it’s too long, too jacked up with SEO garbage (irony!), or are just plain non-descriptive.

I’m not entirely sure how much I care just yet.

Part of me thinks, well, google.com isn’t the web. As important as it is, it’s a proprietary product by a private company and they can do whatever they want within the bounds of the law. In this case, it’s clear the intention is to help: to provide titles that are more clear than what the original page has.

Part of me thinks, well, that sucks that, as site owners, we have no control. If Google wanted to change the SERP title for every results to this website to “CSS-Tricks is a stupid website, never visit it,” they could and that’s that.

Part of me connects this kind of work to AMP. AMP was basically saying, “Y’all are absolutely horrible at building performant mobile websites, so we’re going to build a strict set of rules such that you can’t screw it up anymore, and dangling a carrot of better SERP placement if you buy into the rules.” This way of creating page titles is basically saying, “Y’all are absolutely horrible at providing good titles, so we’re going to title your pages for you so you can’t screw it up anymore and we can improve our SERPs.”

Except with AMP, you had to put in the development hours to make it happen. It was opt-in, even if the carrot was un-ignorable by content companies. This doesn’t carry the risk of burning development hours, but it’s also not something we get to opt into.


What is Your Page Title on a Google Search Engine Results Page? originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/what-is-your-page-title-on-a-google-search-engine-results-page/feed/ 3 352279
WooCommerce With Apple Pay and Google Pay https://css-tricks.com/woocommerce-with-apple-pay-and-google-pay/ https://css-tricks.com/woocommerce-with-apple-pay-and-google-pay/#comments Tue, 10 Aug 2021 14:54:56 +0000 https://css-tricks.com/?p=345799 (This is a sponsored post.)

Got a WooCommerce store? It behooves you to offer a variety of payment methods. Just anecdotally, I’m sure both you and me have been annoyed and even abandoned purchases when a merchant, online or …


WooCommerce With Apple Pay and Google Pay originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
(This is a sponsored post.)

Got a WooCommerce store? It behooves you to offer a variety of payment methods. Just anecdotally, I’m sure both you and me have been annoyed and even abandoned purchases when a merchant, online or otherwise, doesn’t take the payment method we want to pay with. That’s just straight-up lost sales for the merchant. But you don’t have to entirely trust anecdotal evidence, there is data you can pour into, suggesting 7% of abandonment is from missing payment methods.

I’d suggest, at a minimum, you take credit cards and PayPal. There are a variety of payment gateways you can explore (and it’s worth doing so), including a number that take credit cards. The best bet there is WooCommerce Payments — supported in many big countries. It’s Stripe-backed, so it’s a lot like using the Stripe gateway anyway, except way better as it’s loaded with useful features like the fact that you manage all your payments directly in your WordPress dashboard, and Instant Deposits.

The PayPal plugin is free, so that’s kind of a no-brainer, and I’m just talking the basic integration that kicks people over to PayPal.com to pay. Some people like that, as it lets them use their PayPal account online where they may already carry a balance for online purchases and transfers.

The very next step? Apple Pay and Google Pay. Why? Like PayPal, some people strongly prefer it (including me) because of how quick and familiar it makes the checkout process. The Apple Pay and Google Pay functionality in WooCommerce goes so far as to even allow skipping the whole traditional cart and checkout process. That might allow you to make up even more than that 7% based on improved UX.

How does Apple Pay and Google Pay work on WooCommerce? Well if you’re already using WooCommerce Payments, like you should, you’re already almost there.

Enabling Apple Pay and Google Pay on WooCommerce

Apple Pay is supported via the Stripe plugin or the Square plugin, but I’d say it’s easiest with WooCommerce Payments. Under Settings > Payments, you’ll see a checkbox for “Enable express checkouts” — flip that on and you’ll be enabling both Apple Pay and Google Pay — and will have an opportunity to pick where you want them to appear.

There are a handful of prerequisites, like having an HTTPS site, but with eCommerce in general, that is not optional and you’ve probably already got it in place.

One thing I experienced when activating it is this warning:

I was able to download the domain association file from the Strip docs, give it to my WordPress Host (Flywheel), and they manually installed it for me and it worked fine.

No “account”

With PayPal, you need a PayPal account for yourself to make it work. That’s not the case with Apple Pay and Google Pay where you don’t have an account and they don’t keep a balance — they just kick that money directly over to WooCommerce Payments and you have access to that money like you would any other WooCommerce Payments transaction.

Example transaction

Here’s an order that came in (I get email notifications for orders):

Notice I can see right in the email that Apple Pay was used.

I can see the order in my dashboard like any other, and have the ability to refund it directly from there and other actions:

I barely even notice it. What payment gateway someone chooses is of little consequence to me once it’s all set up.

The user experience

Apple Pay works on Safari, both on iOS and macOS. If a user both is using one of those browsers and has Apple Pay set up, they’ll see the special buttons show up on your store:

Press that button, and the user sees this immediate checkout step:

The user can change credit cards (that they have set up in Apple Pay), changing shipping address, and then if they approve it, it’s instantly done.

It’s a pretty satisfying user experience, I must say.

Even moreso on a mobile phone, where it feels like things like Apple Pay and Google Pay were really designed to shine. Here’s Apple Pay:

Google Pay works on Android phones nicely, but also works in desktop Chrome.

I did learn one super weird little caveat with Google Pay and desktop Chrome though! Cards that are in your desktop Chrome autofill area in settings that literally say “Google Pay” next to them don’t actually work for the WooCommcere Google Pay buttons. Only credit cards that are kinda manually added in there without that little label work. Just a little thing to be aware of when testing:

This is a rather compelling reason to use WooCommerce for eCommerce. I feel like I got this feature for free. I basically checked a box in settings, and it makes a material positive impact on my business.


WooCommerce With Apple Pay and Google Pay originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/woocommerce-with-apple-pay-and-google-pay/feed/ 4 345799
Using Google Drive as a CMS https://css-tricks.com/using-google-drive-as-a-cms/ https://css-tricks.com/using-google-drive-as-a-cms/#comments Thu, 22 Jul 2021 14:44:26 +0000 https://css-tricks.com/?p=344580 We’re going to walk through the technical process of hooking into Google Drive’s API to source content on a website. We’ll examine the step-by-step implementation, as well as how to utilize server-side caching to avoid the major pitfalls to avoid …


Using Google Drive as a CMS originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
We’re going to walk through the technical process of hooking into Google Drive’s API to source content on a website. We’ll examine the step-by-step implementation, as well as how to utilize server-side caching to avoid the major pitfalls to avoid such as API usage limits and image hotlinking. A ready-to-use npm package, Git repo, and Docker image are provided throughout the article.

But… why?

At some point in the development of a website, a crossroads is reached: how is content managed when the person managing it isn’t technically savvy? If the content is managed by developers indefinitely, pure HTML and CSS will suffice — but this prevents wider team collaboration; besides, no developer wants to be on the hook for content updates in perpetuity.

So what happens when a new non-technical partner needs to gain edit access? This could be a designer, a product manager, a marketing person, a company executive, or even an end customer.

That’s what a good content management system is for, right? Maybe something like WordPress. But this comes with its own set up of disadvantages: it’s a new platform for your team to juggle, a new interface to learn, and a new vector for potential attackers. It requires creating templates, a format with its own syntax and idiosyncrasies. Custom or third-party plugins may need to be to vetted, installed, and configured for unique use cases — and each of these is yet another source of complexity, friction, technical debt, and risk. The bloat of all this setup may end up cramping your tech in a way which is counterproductive to the actual purpose of the website.

What if we could pull content from where it already is? That’s what we’re getting at here. Many of the places where I have worked use Google Drive to organize and share files, and that includes things like blog and landing page content drafts. Could we utilize Google Drive’s API to import a Google Doc directly into a site as raw HTML, with a simple REST request?

Of course we can! Here’s how we did it where I work.

What you’ll need

Just a few things you may want to check out as we get started:

Authenticating with the Google Drive API

The first step is to establish a connection to Google Drive’s API, and for that, we will need to do some kind of authentication. That’s a requirement to use the Drive API even if the files in question are publicly shared (with “link sharing” turned on). Google supports several methods of doing this. The most common is OAuth, which prompts the user with a Google-branded screen saying, “[So-and-so app] wants to access your Google Drive” and waits for user consent — not exactly what we need here, since we’d like to access files in a single central drive, rather than the user’s drive. Plus, it’s a bit tricky to provide access to only particular files or folders. The https://www.googleapis.com/auth/drive.readonly scope we might use is described as:

See and download all your Google Drive files.

That’s exactly what it says on the consent screen. This is potentially alarming for a user, and more to the point, it is a potential security weakness on any central developer/admin Google account that manages the website content; anything they can access is exposed through the site’s CMS back end, including their own documents and anything shared with them. Not good!

Enter the “Service account”

Instead, we can make use of a slightly less common authentication method: a Google service account. Think of a service account like a dummy Google account used exclusively by APIs and bots. However, it behaves like a first-class Google account; it has its own email address, its own tokens for authentication, and its own permissions. The big win here is that we make files available to this dummy service account just like any other user — by sharing the file with the service account’s email address, which looks something like this:

google-drive-cms-example@npm-drive-cms.iam.gserviceaccount.com

When we go to display a doc or sheet on the website, we simply hit the “Share” button and paste in that email address. Now the service account can see only the files or folders we’ve explicitly shared with it, and that access can be modified or revoked at any time. Perfect!

Creating a service account

A service account can be created (for free) from the Google Cloud Platform Console. That process is well documented in Google’s developer resources, and in addition it’s described in step-by-step detail in the companion repo of this article on GitHub. For the sake of brevity, let’s fast-forward to immediately after a successful authentication of a service account.

The Google Drive API

Now that we’re in, we’re ready to start tinkering with what the Drive API is capable of. We can start from a modified version of the Node.js quickstart sample, adjusted to use our new service account instead of client OAuth. That’s handled in the first several methods of the driveAPI.js we are constructing to handle all of our interactions with the API. The key difference from Google’s sample is in the authorize() method, where we use an instance of jwtClient rather than the oauthClient used in Google’s sample:

authorize(credentials, callback) {
  const { client_email, private_key } = credentials;

  const jwtClient = new google.auth.JWT(client_email, null, private_key, SCOPES)

  // Check if we have previously stored a token.
  fs.readFile(TOKEN_PATH, (err, token) => {
    if (err) return this.getAccessToken(jwtClient, callback);
    jwtClient.setCredentials(JSON.parse(token.toString()));
    console.log('Token loaded from file');
    callback(jwtClient);
  });
}

Node.js vs. client-side

One more note about the setup here — this code is intended to be called from server-side Node.js code. That’s because the client credentials for the service account must be kept secret, and not exposed to users of our website. They are kept in a credentials.json file on the server, and loaded via fs.readFile inside of Node.js. It’s also listed in the .gitignore to keep the sensitive keys out of source control.

Fetching a doc

After the stage is set, loading raw HTML from a Google Doc becomes fairly simple. A method like this returns a Promise of an HTML string:

getDoc(id, skipCache = false) {
  return new Promise((resolve, reject) => {
    this.drive.files.export({
      fileId: id,
      mimeType: "text/html",
      fields: "data",
    }, (err, res) => {
      if (err) return reject('The API returned an error: ' + err);
      resolve({ html: this.rewriteToCachedImages(res.data) });
      // Cache images
      this.cacheImages(res.data);
    });
  });
}

The Drive.Files.export endpoint does all the work for us here. The id we’re passing in is just what shows up in your browsers address bar when you open the doc, which is shown immediately after https://docs.google.com/document/d/.

Also notice the two lines about caching images — this is a special consideration we’ll skip over for now, and revisit in detail in the next section.

Here’s an example of a Google document displayed externally as HTML using this method.

Fetching a sheet

Fetching Google Sheets is almost as easy using Spreadsheets.values.get. We adjust the response object just a little bit to convert it to a simplified JSON array, labeled with column headers from the first row of the sheet.

getSheet(id, range) {
  return new Promise((resolve, reject) => {
    this.sheets.spreadsheets.values.get({
      spreadsheetId: id,
    range: range,
  }, (err, res) => {
    if (err) return reject('The API returned an error: ' + err);
    // console.log(res.data.values);
    const keys = res.data.values[0];
    const transformed = [];
    res.data.values.forEach((row, i) => {
      if(i === 0) return;
      const item = {};
      row.forEach((cell, index) => {
        item[keys[index]] = cell;
      });
       transformed.push(item);
      });
      resolve(transformed);
    });
  });
}

The id parameter is the same as for a doc, and the new range parameter here refers to a range of cells to fetch values from, in Sheets A1 notation.

Example: this Sheet is read and parsed in order to render custom HTML on this page.

…and more!

These two endpoints already get you very far, and forms the backbone of a custom CMS for a website. But, in fact, it only taps the surface of Drive’s potential for content management. It’s also capable of:

  • listing all files in a given folder and display them in a menu,
  • importing complex media from a Google Slides presentation, and
  • downloading and caching custom files.

The only limits here are your creativity, and the constraints of the full Drive API documented here.

Caching

As you’re playing with the various kinds of queries that the Drive API supports, you may end up receiving a “User Rate Limit Exceeded” error message . It’s fairly easy to hit this limit through repeated trial-and-error testing during the development phase, and at first glance, it seems as if it would represent a hard blocker for our Google Drive-CMS strategy.

This is where caching comes in — every time we fetch a new version of any file on Drive, we cache it locally (aka server-side, within the Node.js process). Once we do that, we only need to check the version of every file. If our cache is out of date, we fetch the newest version of the corresponding file, but that request only happens once per file version, rather than once per user request. Instead of scaling by the number of people who use the website, we can now scale by the number of updates/edits on Google Drive as our limiting factor. Under the current Drive usage limits on a free-tier account, we could support up to 300 API requests per minute. Caching should keep us well within this limit, and it could be optimized even further by batching multiple requests.

Handling images

The same caching method is applied to images embedded inside Google Docs. The getDoc method parses the HTML response for any image URLs, and makes a secondary request to download them (or fetches them directly from cache if they’re already there). Then it rewrites the original URL in the HTML. The result is that static HTML; we never use hotlinks to Google image CDNs. By the time it gets to the browser, the images have already been pre-cached.

Respectful and responsive

Caching ensures two things: first, that we are being respectful of Google’s API usage limits, and truly utilize Google Drive as a front end for editing and file management (what the tool is intended for), rather than leaching off of it for free bandwidth and storage space. It keeps our website’s interaction with Google’s APIs to the bare minimum necessary to refresh the cache as needed.

The other benefit is one that the users of our website will enjoy: a responsive website with minimal load times. Since cached Google Docs are stored as static HTML on our own server, we can fetch them immediately without waiting for a third-party REST request to complete, keeping website load times to a minimum.

Wrapping in Express

Since all this tinkering has been in server-side Node.js, we need a way for our client pages to interact with the APIs. By wrapping the DriveAPI into its own REST service, we can create a middleman/proxy service which abstracts away all the logic of caching/fetching new versions, while keeping the sensitive authentication credentials safe on the server side.

A series of express routes, or the equivalent in your favorite web server, will do the trick, with a series of routes like this:

const driveAPI = new (require('./driveAPI'))();
const express = require('express');
const API_VERSION = 1;
const router = express.Router();

router.route('/getDoc')
.get((req, res) => {
  console.log('GET /getDoc', req.query.id);
  driveAPI.getDoc(req.query.id)
  .then(data => res.json(data))
  .catch(error => {
    console.error(error);
    res.sendStatus(500);
  });
});

// Other routes included here (getSheet, getImage, listFiles, etc)...

app.use(`/api/v${API_VERSION}`, router);

See the full express.js file in the companion repo.

Bonus: Docker Deployment

For deployment to production, we can can run the Express server alongside your existing static web server. Or, if it’s convenient, we could easily wrap it in a Docker image:

FROM node:8
# Create app directory
WORKDIR /usr/src/app
# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
COPY package*.json ./
RUN npm install
# If you are building your code for production
# RUN npm ci --only=production
# Bundle app source
COPY . .
CMD [ "node", "express.js" ]

…or use this pre-built image published on Docker Hub.

Bonus 2: NGINX Google OAuth

If your website is public-facing (accessible by anyone on the internet), then we’re done! But for our purposes where I work at Motorola, we are publishing an internal-only documentation site that needs additional security. That means Link Sharing is turned off on all our Google Docs (they also happened to be stored in an isolated and dedicated Google Team Drive separated from all other company content).

We handled this additional layer of security as early as possible at the server level, using NGINX to intercept and reverse-proxy all requests before they even make it to the Express server or any static content hosted by the website. For this, we use Cloudflare’s excellent Docker image to present a Google sign-on screen to all employees accessing any website resources or endpoints (both the Drive API Express server and the static content alongside it). It seamlessly integrates with the corporate Google account and single-sign-on they already have — no extra account needed!

Conclusion

Everything we just covered in this article is exactly what we’ve done where I work. It’s a lightweight, flexible, and decentralized content management architecture, in which the raw data lives where Google Drive, where our team already works, using a UI that’s already familiar to everyone. It all gets tied together into the website’s front end which retains the full flexibility of pure HTML and CSS in terms of control over presentation, and with minimal architectural constraints. A little extra legwork from you, the developer, creates a virtually seamless experience for both your non-dev collaborators and your end users alike.

Will this sort of thing work for everyone? Of course not. Different sites have different needs. But if I were to put together a list of use cases for when to use Google Drive as a CMS, it would look something like this:

  • An internal site with between a few hundred to a few thousand daily users — If this had been the front page of the global company website, even a single request for file version metadata per user might approach that Drive API usage limit. Further techniques could help mitigate that — but it’s the best fit for small to medium-size websites.
  • A single-page app — This setup has allowed us to query the version numbers of every data source in a single REST request, one time per session, rather than one time per page. A non-single-page app could use the same approach, perhaps even making use of cookies or local storage to accomplish the same “once per visit” version query, but again, it would take a little extra legwork.
  • A team that’s already using Google Drive — Perhaps most important of all, our collaborators were pleasantly surprised that they could contribute to the website using an account and workflow they already had access to and were comfortable using, including all of the refinements of Google’s WYSIWYG experience, powerful access management, and the rest.

Using Google Drive as a CMS originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/using-google-drive-as-a-cms/feed/ 8 344580
Weekly Platform News: Internet Explorer Mode, Speed Report in Search Console, Restricting Notification Prompts https://css-tricks.com/weekly-platform-news-internet-explorer-mode-speed-report-in-search-console-restricting-notification-prompts/ https://css-tricks.com/weekly-platform-news-internet-explorer-mode-speed-report-in-search-console-restricting-notification-prompts/#comments Thu, 14 Nov 2019 21:48:35 +0000 https://css-tricks.com/?p=298946 In this week’s roundup: Internet Explorer finds its way into Edge, Google Search Console touts a new speed report, and Firefox gives Facebook’s notification the silent treatment.

Let’s get into the news!

Edge browser with new Internet Explorer mode launches…


Weekly Platform News: Internet Explorer Mode, Speed Report in Search Console, Restricting Notification Prompts originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
In this week’s roundup: Internet Explorer finds its way into Edge, Google Search Console touts a new speed report, and Firefox gives Facebook’s notification the silent treatment.

Let’s get into the news!

Edge browser with new Internet Explorer mode launches in January

Microsoft expects to release the new Chromium-based Edge browser on January 15, on both Windows and macOS. This browser includes a new Internet Explorer mode that allows Edge to automatically and seamlessly render tabs containing specific legacy content (e.g., a company’s intranet) using Internet Explorer’s engine instead of Edge’s standard engine (Blink).

Here’s a sped-up excerpt from Fred Pullen’s presentation that shows the new Internet Explorer mode in action:

(via Kyle Pflug)

Speed report experimentally available in Google Search Console

The new Speed report in Google’s Search Console shows how your website performs for real-world Chrome users (both on mobile and desktop). Pages that “pass a certain threshold of visits” are categorized into fast, moderate, and slow pages.

Tip: After fixing a speed issue, use the “Validate fix” button to notify Google Search. Google will verify the fix and re-index the pages if the issue is resolved.

(via Google Webmasters)

Facebook’s notification prompt will disappear in Firefox

Firefox will soon start blocking notification prompts on websites that request the notification permission immediately on page load (Facebook does this). Instead of the prompt, a small “speech balloon” icon will be shown in the URL bar.

Websites will still be able to show a notification prompt in Firefox as long as they request permission in response to a user interaction (a click, tap, or key press).

(via Marcos Càceres)

More news…

Read more news in my weekly newsletter for web developers. Pledge as little as $2 per month to get the latest news from me via email every Monday.

More News →


Weekly Platform News: Internet Explorer Mode, Speed Report in Search Console, Restricting Notification Prompts originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/weekly-platform-news-internet-explorer-mode-speed-report-in-search-console-restricting-notification-prompts/feed/ 2 298946
Material Theming: Making Material Your Own! https://css-tricks.com/material-theming-making-material-your-own/ https://css-tricks.com/material-theming-making-material-your-own/#comments Mon, 03 Jun 2019 14:15:26 +0000 http://css-tricks.com/?p=288127 The web is a beautiful, expressive medium that’s evolved over time as trends and technology have changed. Moments of delight and flair are what set companies apart from one another. At the same time, today’s top products rely on scalable, …


Material Theming: Making Material Your Own! originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
The web is a beautiful, expressive medium that’s evolved over time as trends and technology have changed. Moments of delight and flair are what set companies apart from one another. At the same time, today’s top products rely on scalable, component-based design systems to efficiently develop a coherent brand. And it’s more important than ever to have accessibility and a solid user experience baked in from the start! But these two worlds — creative web design and systematic web design — don’t need to be at arms. The beautiful gooey center of the web design world is where we can find a way to meld creative web design in with our systems, and luckily, you can have both!

One of those design systems is called Material Design (the team I just joined at Google!). Material is built on years of research and best practices to give designers and developers access to creating beautiful, accessible UIs with the least amount of work possible. But in its initial launch, one thing was missing from the equation: the ability to express a brand’s personality in an easy-to-implement way. The team heard two key pieces of feedback:

  1. Material wasn’t stylistically flexible enough to meet the needs of all products, brands, and users.
  2. It wasn’t easy enough to apply and build brand experiences systematically across products.

And as of last year (specifically, I/O 2018), Material announced new theming capabilities! Theming allows developers and designers to benefit from all the parts of Material that make it a world-class design system — and make it their own! In other words: You can customize the look and feel of Material Components by applying global changes to your product’s color, shape, and typography.

Material Components are built for multiple systems like Android, iOS, and Flutter, and we’re going to focus on the web for this post (this is CSS-Tricks after all). Let’s start off with a base login template using Material Components for the web to see how easy implementing a theme atop this will be:

About theming

The current theming implementations allow for color, typography, and shape adjustments that trickle down to every component in your product. These three subsystems may not sound like many options, but together they bring a big impact to the design, and are a great springboard to jump off of with more design changes on a more granular level.

Color

The first themable option is color. There’s a great color picker tool on Material.io for you to be able to see contrast and aid your color selection process. Since this is CSS-Tricks, let’s write some CSS (okay, more like SCSS), to get started with an example. In a few lines of code in our my-theme.scss file, we can entirely change the look and feel of this login screen:

We’ve set:

$mdc-theme-primary: #26418f;
$mdc-theme-secondary: #d1c4e9;
$mdc-theme-background: #fdf6f9;

Even though we have not specified a $mdc-theme-on-primary, the system knows to make this white now (using a Sass contrast function) to help us ensure accessible color contrast. We can also override the text color on the primary and secondary themes with $mdc-theme-on-primary and $mdc-theme-on-secondary. However, if no value is explicitly set, these will either be black or white based on the background. See the color picker for more information.

Typography

Material sets up a base typography scale, which can be customized with theming. You can adjust the typography for each individual headline level and apply it throughout your product in two ways:

  1. Using CSS classes to apply the styles. I.e. <h1 class="mdc-typography--headline1">
  2. Using mixins to extend the styles from a header component onto another (i.e. h1 {@include mdc-typography(headline1);}). You can see an example of this in our starter kit example on Glitch.

A quick way to change the typeface is by using Google Fonts — a really nice directory of free web fonts. We’re going to make this easy for ourselves and pick out a Google font called Josefin Sans. In order to use this in our project, we’ll need to import it, and then can set the base typography to use it:

@import url('https://fonts.googleapis.com/css?family=Josefin+Sans');
$mdc-typography-font-family: unquote("Josefin Sans, sans-serif");

We use unquote here because the typography in Material is set in a map. We can specify more styles like so:

$mdc-typography-styles-button: (
 font-size: 14px,
 font-weight: 600,
 letter-spacing: 0.05em,
);

Now our login page looks like this:

Shape

Shape is another way to be expressive with your code! What do I mean by shape? In Material, shape affects the corner radius of components, such as buttons, cards, and sheets. In product design, shape helps to define your brand. Is it angular and machine-like, or is it more rounded and organic? Changing shape trickles down through the rest of the system. Here are some examples of shape in Material:

The other cool thing about shape in Material Components is that you don’t have to have the same shape on each corner, or even the same component at different sizes. There are small, medium, and large components in Material, and you can apply different styles for these different component types, and you can mix it up and specify each corner, one-by-one, in a space-separated list of values just as you would with border-radius.

MDC Web does not currently support cut corners like the other platforms do since it is a non-trivial operation with current web standards. This is another reason I personally love Houdini and see a lot of impact for it in future CSS development!

In the example we’re working with, we have both large and small components. The large components are the text field inputs, and the small component is the “login” button. We can apply different shapes to these with:

$mdc-shape-small-component-radius: 12px 4px;
$mdc-shape-large-component-radius: 8px;

And voilà! We have transformed our application in 12 lines of code.

Themes, themes, everywhere!

Now here’s the part where you get creative. There are so many ways to mix and match the elements of color, typography, and shape to really make your brand stand out. Here are four very distinct example apps that use color theory, typography, and shape to differentiate their products, each based on the baseline Material Components:

Themes IRL

Google itself customizes and extends upon the baseline Material Components with its own products. This is called the Google Material Theme, and it was defined by a number of product teams, including Gmail, Google News, Google Play, and Google Home:

But they are not the only company to use Material Design and its principles. A few companies have also extended Material Components in their own large-scale products.

Lyft, a ridesharing service, is just one example. They used an extended FAB component to highlight key actions, and designed it with a gradient to give it a unique feel that users were still familiar with. Lyft also leveraged Material’s elevation system in their own product.

Anchor, a free app that helps people record, distribute, and host podcasts, is another great example of making Material your own. They showcase their bold color palette, and integrate it with choice chips to delineate display options, a list of cards for content selection, and an extended FAB anchored to the bottom to remain persistently available for user actions.

Get started

Now you have everything you need to make own Material Theme with Material Components for the web. It’s easy to start building your web project with our Material Starter Kit, and exploring theming with the Build a Material theme Glitch project we’ve just released. Change around the variables in my-theme.scss to explore how they can customize the individual components. Then, you can open any template, paste those variables into the my-theme.scss, and start building.

TL;DR: If you thought that Material Design wasn’t for you in the past, it’s a great time to start exploring. Happy theming!


Material Theming: Making Material Your Own! originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/material-theming-making-material-your-own/feed/ 6 288127
Weekly Platform News: Feature Policy, Signed Exchanges, iOS browsers https://css-tricks.com/weekly-platform-news-feature-policy-signed-exchanges-ios-browsers/ https://css-tricks.com/weekly-platform-news-feature-policy-signed-exchanges-ios-browsers/#comments Fri, 10 May 2019 14:22:12 +0000 http://css-tricks.com/?p=287593 👋 Hey folks! This is the first edition of a new weekly update we’ll be posting that covers timely news at the intersection of development standards and the tools that make them available on the web. We often talk about


Weekly Platform News: Feature Policy, Signed Exchanges, iOS browsers originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
👋 Hey folks! This is the first edition of a new weekly update we’ll be posting that covers timely news at the intersection of development standards and the tools that make them available on the web. We often talk about the pace of change in our industry. It’s fast and touches everything from the HTML, CSS and JavaScript we write to the landscape of browsers that renders them. Please help us welcome Šime Vidas, who will be keeping us all on the up and up with curated updates from his own blog of regular development updates, webplatform.news.

Feature Policy in Chrome

Andrew Betts: Websites can use the HTTP Feature-Policy response header to prevent third parties from secretly using powerful features such as geolocation, and to disable certain bad practices (e.g. document.write, parser-blocking JavaScript, un-optimized images).

This allows good practices to be more easily rewarded. … Search results could be badged with some approving “fast” logomark or (more controversially perhaps) get a higher result ranking if they disallow themselves certain policy-controlled behaviors.

Feature Policy is an emerging technology. See featurepolicy.info for more information about individual policies and their level of support in browsers.

Signed exchanges on Google Search

The mobile version of Google Search includes AMP results on search results pages. When the user taps on an AMP result, the AMP page loads from Google’s domain (google.com) and is displayed in the AMP Viewer.

Google Search now supports an alternative: If a website signs its AMP pages, and the visitor uses Chrome for Android, then tapping on an AMP result instead loads the signed version of the AMP page from Google’s servers, but to the user it appears as if they have navigated to the website normally.

The technology that enables this is called Signed HTTP Exchanges (SXG). See the announcement on Google Webmaster Central Blog for more details. The specification describes the following use case:

In order to speed up loading but still maintain control over its content, an HTML page in a particular origin “O.com” could tell clients to load its sub-resources from an intermediate content distributor that’s not authoritative, but require that those resources be signed by “O.com” so that the distributor couldn’t modify the resources.

Websites can add support for signed exchanges by running AMP Packager on the server side. Cloudflare has launched a free feature called “AMP Real URL” that fully automates the signing process for AMP pages served from its CDN.

Alternative iOS browsers

Henrik Joreteg: On iOS, several important APIs are limited to Safari and are not available in any of the alternative iOS browsers. These include service workers, web payments, and camera access.

Chrome for iOS supports web payments via a custom implementation. I’ve created a browser support table on HTML5test that highlights the differences between some of the popular iOS browsers.


Weekly Platform News: Feature Policy, Signed Exchanges, iOS browsers originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/weekly-platform-news-feature-policy-signed-exchanges-ios-browsers/feed/ 4 287593
Chrome Lite Pages https://css-tricks.com/chrome-lite-pages/ https://css-tricks.com/chrome-lite-pages/#comments Tue, 19 Mar 2019 21:47:06 +0000 http://css-tricks.com/?p=284811 The Chrome team announced a new feature called Lite Pages that can be activated by flipping on the Data Saver option on an Android device:

Chrome on Android’s Data Saver feature helps by automatically optimizing web pages to make them


Chrome Lite Pages originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
The Chrome team announced a new feature called Lite Pages that can be activated by flipping on the Data Saver option on an Android device:

Chrome on Android’s Data Saver feature helps by automatically optimizing web pages to make them load faster. When users are facing network or data constraints, Data Saver may reduce data use by up to 90% and load pages two times faster, and by making pages load faster, a larger fraction of pages actually finish loading on slow networks. Now, we are securely extending performance improvements beyond HTTP pages to HTTPS pages and providing direct feedback to the developers who want it.

To show users when a page has been optimized, Chrome now shows in the URL bar that a Lite version of the page is being displayed.

All of this is pretty neat but I think the name Lite Pages is a little confusing as it’s in no way related to AMP and Tim Kadlec makes that clear in his notes about the new feature:

Lite pages are also in no way related to AMP. AMP is a framework you have to build your site in to reap any benefit from. Lite pages are optimizations and interventions that get applied to your current site. Google’s servers are still involved, by as a proxy service forwarding the initial request along. Your URL’s aren’t tampered with in any way.

A quick glance at this seems great! We don’t have to give up ownership of our URLs, like with AMP, and we don’t have to develop with a proprietary technology — we can let Chrome be Chrome and do any performance things that it wants to do without turning anything on or off or adding JavaScript.

But wait! What kind of optimizations does a Lite Page make and how do they affect our sites? So far, it can disable scripts, replace images with placeholders and stop the loading of certain resources, although this is all subject to change in the future, I guess.

The optimizations only take effect when the loading experience for users is particularly bad, as the announcement blog post states:

…they are applied when the network’s effective connection type is “2G” or “slow-2G,” or when Chrome estimates the page load will take more than 5 seconds to reach first contentful paint given current network conditions and device capabilities.

It’s probably important to remember that the reason why Google is doing this isn’t to break our designs or mess with our websites — they’re doing this because there are serious performance concerns with the web, and those concerns aren’t limited to developing nations.


Chrome Lite Pages originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/chrome-lite-pages/feed/ 4 284811
Google Labs Web Components https://css-tricks.com/google-labs-web-components/ https://css-tricks.com/google-labs-web-components/#comments Mon, 10 Dec 2018 15:21:59 +0000 http://css-tricks.com/?p=279927 I think it’s kinda cool to see Google dropping repos of interesting web components. It demonstrates the possibilities of cool new web features and allows them to ship them in a way that’s compatible with entirely web standards.

Here’s one: …


Google Labs Web Components originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
I think it’s kinda cool to see Google dropping repos of interesting web components. It demonstrates the possibilities of cool new web features and allows them to ship them in a way that’s compatible with entirely web standards.

Here’s one: <two-up>

I wanted to give it a try, so I linked up their example two-up-min.js script in a Pen and used the element by itself to see how it works. They expose the component’s styling with custom properties, which I’d say is a darn nice use case for those.

<two-up&rt; by Chris Coyier (@chriscoyier) on CodePen.


Google Labs Web Components originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/google-labs-web-components/feed/ 12 279927
Flutter: Google’s take on cross platform https://css-tricks.com/flutter-googles-take-on-cross-platform/ https://css-tricks.com/flutter-googles-take-on-cross-platform/#comments Wed, 05 Sep 2018 14:01:21 +0000 http://css-tricks.com/?p=275725 Flutter is a mobile SDK that, at its core, is about empowering everyone to build beautiful mobile apps. Whether you come from the world of web development or native mobile development, Flutter makes it easier to create mobile apps in …


Flutter: Google’s take on cross platform originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
Flutter is a mobile SDK that, at its core, is about empowering everyone to build beautiful mobile apps. Whether you come from the world of web development or native mobile development, Flutter makes it easier to create mobile apps in a familiar, simplified way, without ever giving up control to the framework.

As of this writing, Google AdWords and Alibaba are both using Flutter in production. You can see more examples of who’s using Flutter (including the app I’ve worked on) on Flutter’s website on the showcase page.

Right now, there’s a lot of buzz about Flutter. The question I see most often is, “Flutter or React Native…which one should I use?” Like all things in programming, its all about the tradeoffs you’re willing to make.

I’m going to try to convince you that Flutter is the best option for mobile app development. I believe it’s better than any other cross platform framework, and it’s possibly better than native development — but more on that in a bit.

Before that though, let me walk (quickly) through what Flutter is, and what it is not, starting with the Dart programming language.

What’s Dart?

Dart is a programming language created by Google and was used to write Flutter. Dart was created, more or less, because Google wanted a language that was “better” than JavaScript to write server side and front-end code. From what I understand, the main issue they had with JavaScript is how slowly it updates with new features since it relies on a huge committee for approvals and several browser vendors to implement it.

After a series of decisions about whether to take on JavaScript directly or not, Google decided to make a language that semantically fit inside of JavaScript. In other words, every single thing you write in Dart can compile to JavaScript. This is why they didn’t just use Java — it’s semantically huge.

Here’s a leaked email chain from Google from 2010. It’s the “coming to Jesus” moment that they decided they needed to do something about JavaScript.

The fundamentals of Dart are similar to all high-level languages. That said, programming languages are, as it turns out, hard to learn.

There’s good news, though. Dart excels at being a “safe” language to learn. Google didn’t set out to create anything innovative with Dart. They were seeking to make a language that was simple, productive and could be compiled into JavaScript.

There is nothing particularly exciting about its syntax, and no special operators that will throw you through a loop. In Dart (unlike JavaScript), there is one way to say true: True. There is one way to say false: False.

In JavaScript, this coerces to True:

if (3) { ... }

In Dart, that would blow up your program. Dart is, at its core, a productive, predictable, and simple language.

This is important, because writing an app in Flutter is simply writing Dart. Flutter is, underneath it all, a library of Dart classes. There is no markup language involved or JSX-style hybrid language. Every bit of front-end code is written in Dart. No HTML. No CSS.

Why does Flutter use Dart?

If you’re coming from literally any other background (and you’re like me), you’ve probably complained about the fact that Flutter uses Dart, and not JavaScript. (Developers are, believe it or not, opinionated.)

And there are reasons to be skeptical of this choice. It’s not one of the hot languages of today. It’s not even one of the top 25 most used languages. What gives? Is Google just using it because it’s their language? I’d imagine that played a role, but there are practical reasons, too.

  • Dart supports both Just In Time (JIT) compiling and Ahead of Time (AOT) compiling.
    • The AOT compiler changes Dart into efficient native code. This makes Flutter fast (a win for the user and the developer), but it also means that almost all of the framework is written in Dart. For you, the developer, that means you customize everything.
    • Dart’s optional JIT compiling allows hot-reloading to exist. Fast development and iteration is a key to the joy of using Flutter. When you save code in your text editor, your app is updated in your simulator in less than a second.
  • Dart is Object Oriented. This makes it easy to write visual user-experiences exclusively with Dart, with no need for a markup language.

  • Dart is a productive and predictable language. It’s easy to learn and it feels familiar. Whether you come from a dynamic language or a static language, you can get up and running with ease.
  • And yes, I’d image that it is extremely appealing to use a language made by the same company, because the Flutter team could work closely with the Dart team to implement new needed features.

Flutter vs. React Native (and other options)

Before I offer up my unsolicited opinions on your other options, I want to make this crystal clear: Flutter is not the answer 100% of the time. It’s a tool and we should choose the right tool for the job at hand. That said, I’d only argue that it’s something you should strongly consider in the future.

Native development (iOS and Android)

Your first choice is to write native apps for iOS and Android. This gives you maximum control, debugging tools, and (potentially) a very performant app. At a company, this likely means you have to write everything twice; once for each platform. You likely need different developers on different teams with different skillsets that can’t easily help each other.

React Native, WebViews, and other cross-platform JavaScript options

Your second option: cross-platform, JavaScript-based tools such as WebViews and React Native. These aren’t bad options. The problems you experience with native development disappear. Every front-end web developer on your team can chip in and help — all they need are some modern JavaScript skills. This is precisely why large companies such as AirBnb, Facebook, and Twitter have used React Native on core products. (AirBnb recently announced that it would stop using React Native, because of some of the issues I’ll describe below.)

The first “mobile apps” to be built cross platform are simply WebViews that run on WebKit (a browser rendering engine). These are literally just embedded web pages. The problem with this is basically that manipulating the DOM is very expensive and doesn’t perform well enough to make a great mobile experience.

Some platforms have solved this problem by building the “JavaScript bridge.” This bridge lets JavaScript talk directly to native widgets.

This is much more performant than WebViews, because you eliminate the DOM from the equation, but it’s still not ideal. Every time your app needs to talk directly to the rendering engine, it has to be compiled to native code to “cross the bridge.” On a single interaction, the bridge must be crossed twice: once from platform to app, and then back from app to platform.

Flutter differs because it uses its own rendering engine, Skia, which is the same rendering engine used in Chrome. Skia can communicate with Flutter apps. As a result, Flutter accepts local events directly, rather than having to first compile them into JavaScript. This is essentially possible because Flutter compiles to native ARM code. This is the secret to its success. When your app is fired up on a user’s device, it’s entirely running in the language that the device’s operating system expects.

The JavaScript bridge is a marvel of modern programming, to be sure, but it presents three big problems.

The first problem is that debugging is hard. When there’s an error in the runtime compiler, that error has to be traced back across the JavaScript bridge and found in the JavaScript code. It may be in markup or CSS-like syntax as well. The debugger itself may not work as well as we’d like it to.

A second bigger issue, though, is performance. The JavaScript bridge is very expensive. Every time something in the app is tapped, that event must be sent across the bridge to your JavaScript app. The result, for lack of better term, is jank.

The third big problem, according to AirBnb, is that they found themselves having to dip down into the native code more often than they wanted to, which was a problem for their teams comprised mostly of JavaScript developers. (The jury is still out on this issue with Flutter, but I can say that I’ve never once had to try and write native code at my job. Some members of my team have created plugins in Objective-C and Java.)

The immediate benefits of Flutter

It’s likely, since you’re reading this article, that you’re interested in Flutter… but you might be skeptical. I admire how thorough you are in vetting technology.

Your reasons for being skeptical are fair. It’s a new technology. That means breaking changes in the API. It means missing support for important features (such as Google Maps). It seems possible that Google could abandon it altogether one day.

And, despite the fact that you believe Dart is great language, that doesn’t change the fact that Dart isn’t widely used, and many third-party libraries that you want may not exist.

I would argue against all those points, though. The API unlikely to change, as the Google uses Flutter internally on major revenue-generating apps, including Google AdWords. Dart has recently moved into version 2, which means it will likely be a while until it changes much. It will likely be years until breaking changes are introduced which, in a computer world, is practically forever.

Yes, there are indeed missing features, but Flutter gives you the complete control to add your own native plugins. In fact, many of the most important operating system plugins already exist, such as a map plugin, camera, location services, and device storage. The Dart and Flutter ecosystem and community already exists. It’s much smaller than the JavaScript community, of course, but I would argue that it’s concise. I see people every day contributing to existing packages, rather than creating new ones.

Now, let’s talk about Flutter’s specific benefits.

No JavaScript bridge

This is a major bottleneck in development and in your application’s performance. Again, it leads to jank. Scrolling isn’t smooth, it’s not always performant, and it’s hard to debug.

Flutter compiles to actual native code and is rendered using Skia. The app itself is running in native, so there’s no reason to convert Dart to native. This means that it doesn’t lose any of the performance or productivity when it’s running on a user’s device.

Compile time

If you’re coming from native development, one of your major pains is the development cycle. iOS is infamous for its insane compile times. In Flutter, a full compile generally takes less than 30 seconds, and incremental compiles are sub-seconds, thanks to hot-reload. At my day job, we develop features for our mobile client first because Flutter’s development cycle allows us to move so quickly. Only when we’re sure of our implementation do we go write those features in the web client.

Write once, test once, deploy everywhere

Not only do you get to write your app one time and deploy to iOS and Android, you also only have to write your tests once. Dart unit testing is quite easy, and Flutter includes a library for testing Widgets.

Code sharing

I’m going to be fair here: I suppose this is technically possible in JavaScript as well. But, it’s certainly not possible in native development. With Flutter and Dart, your web and mobile apps can share all the code, except each client’s views. (Of course, only if you’re using Dart for your web apps.) You can quite easily use dependency injection to run an AngularDart app and Flutter app with the same models and controllers.

And of course, even if you don’t want to share code between your web app and your mobile app, you’re sharing all your code between the iOS and Android apps.

In practical terms, this means that you are super productive. I mentioned that we develop our mobile features first at my day job. Because we share business logic between web and mobile, once the mobile feature is implemented, we only have to write views that expect that same controller data.

Productivity and collaboration

Gone are the days of separate teams for iOS and Android. In fact, whether your use Dart or JavaScript in your web apps, Flutter development is familiar enough that all your teams will be unified. It’s not a stretch by any means to expect a JavaScript web developer to also effectively develop in Flutter and Dart. If you believe me here, then it follows that your new unified team will be three times more productive.

Code maintenance

Nothing is more satisfying then fixing a bug once and having it corrected on all your clients. Only in very specific cases is there a bug in a iOS app produced with Flutter that is not also in the Android version (and vice versa). In 100% of these cases, these bugs aren’t bugs, but cosmetic issues because Flutter follows the device OS design systems in it’s built-in widgets. Because these are issues like text sizing or alignment, they are trivial in the context of using engineering time to fix.

Flutter for JavaScript developers

Since you’re reading CSS-Tricks, I’d be willing to bet you’re a web developer. If you’ve used any of today’s hottest frameworks (e.g. React, Angular, Vue, etc.), then you’ll be happy to know that picking up Flutter is easy.

Flutter is completely reactive, so the same mindset and paradigm that you’re used to with React carries over to Flutter. You’re essentially building a ton of small, reusable components (called Widgets in Flutter) just like React. These widgets are complete with lifecycle methods, and they’re written in classes. If you’ve used this syntax in React:

const MyComponent extends React.Component {
  //...
  render(){}
}

…then you’ll pick up Flutter with no problem. This is how you do the same in Flutter:

class MyWidget extends StatelessWidget {
  //...
  build(){}
}

And, just like React, Flutter favors composition over inheritance. For example, if you want to make a special AddToCartButton in React, you’d build a button with special functions and styles in JSX. That’s exactly how you do it in Flutter (minus the JSX).

Finally, the layout system in Flutter is similar to CSS rules we’re familiar with, like flexbox and absolute positioning.

This is also where a big difference in making views in Flutter comes in, though. In Flutter, literally everything is a Widget. There are some obvious, concrete Widgets, like Text, Button, and AppBar. But Animations and Layout declarations are also Widgets. To center text, you wrap a Text Widget in a Center Widget. To add padding, there’s a Padding Widget.

Imagine breaking down a React app to the smallest possible reusable components you could make. For example, what if you made a higher-order React component that simply took a prop “padding” and all it did was add that amount of padding to whatever was nested within it. That’s how Flutter works, because there is no CSS or markup.

In this sample picture, here are a few layout widgets that you might use, but you can’t ‘see’ as the user:

That may seem like a ton of monotonous work, but Flutter comes with many, many Widgets built right in (such as Padding and Center) so you don’t have to waste time doing that yourself.

These are some of the most common widgets:

Final note

TL;DR: should you try Flutter?

If you want to make buttery smooth mobile apps in a familiar style, then yes! The performance and developer experience are both completely held in tact in Flutter. Its animations tick at 60fps, and it has a bundle of built-in Cupertino-style and Material Design-style Widgets. Or, long story short: it’s incredible how quick you can be productive in Flutter, without sacrificing native performance.

If you want to try Flutter today, here are a couple great places to start:

The Flutter docs are truly some of the best I’ve ever seen, and they’ll teach you everything you need to know.


Flutter: Google’s take on cross platform originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/flutter-googles-take-on-cross-platform/feed/ 15 275725
Let’s make a form that puts current location to use in a map! https://css-tricks.com/lets-make-a-form-that-puts-current-location-to-use-in-a-map/ https://css-tricks.com/lets-make-a-form-that-puts-current-location-to-use-in-a-map/#comments Mon, 06 Aug 2018 13:51:38 +0000 http://css-tricks.com/?p=274428 I love shopping online. I can find what I need and get most things for a decent price. I am Nigerian currently working and studying in India, and two things I dread when shopping online are:

  1. Filling out a credit


Let’s make a form that puts current location to use in a map! originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
I love shopping online. I can find what I need and get most things for a decent price. I am Nigerian currently working and studying in India, and two things I dread when shopping online are:

  1. Filling out a credit card form
  2. Filling out shipping and billing address forms

Maybe I’m just lazy, but these things are not without challenges! For the first one, thanks to payment processing services like PayPal and e-wallets, I neither have to type in my 12-digit credit card number for every new e-commerce site I visit, nor have to save my credit card details with them.

For the second, the only time-saving option given by most shopping websites is to save your shipping address, but you still have to fill the form (arrghh!). This is where the challenge is. I’ve had most of my orders returned because my address (which I thought was the right address) could not be located or confirmed by the app for one reason or another.

Address inputs are challenging

Getting a user’s address through an input form is a clean solution but can also be a herculean task to the user. Here’s why:

  • If the user is new in a particular city, they might not know their full address
  • If the user wants to ship to a new address which isn’t saved (e.g shipping to a workplace or a friend’s address instead of the saved home address)
  • If the user resides in a city with very difficult address systems
  • If the user is plain lazy like me

A potential solution: get the address automatically

Getting the user’s address by the tap/click of a button. Yup, that’s easy! The UX is great as it saves the user both the time and effort of filling out some form. It will also save the store owner time, effort, and even money in some cases, as there’ll likely be a reduction in the number of incorrectly placed orders or shipments.

Let’s build a mini app that gets a user’s address and shows it on a Google Map interface using vanilla JavaScript. The concept is as follows:

  1. Get the HTML button and listen for a click event
  2. Get the user’s location (latitude and longitude) on a button click
  3. Show the user’s location on a Google map
  4. Pass the latitude and longitude to the Google Maps Geocode API URL
  5. Display the returned address (or list of addresses) on the UI for the user to select one
  6. Listen for map events and repeat steps 4 and 5
  7. Pre-fill the form with the address data the user selects

Getting started and setting up

To build this app, we’re going to use the Materialize CSS framework to save us some time fussing with styles. Materialize is a modern responsive front-end framework based on Google’s Material Design system. The beta version works with vanilla JavaScript.

A basic setup using Materialize’s CSS and JavaScript files in a document is like this:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Address Locator</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-beta/css/materialize.min.css">
  <link rel="stylesheet" href="css/main.css">
</head>
<body>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-beta/js/materialize.min.js"></script>
  <script src="js/main.js"></script>
</body>
</html>

We’re also going to use the Google Maps API for displaying the map and getting the user’s human-readable address. We’ll need an API key to do this. Here’s how to get one:

  1. Sign in to your Google Developer’s Console Account
  2. Create a new project or select an existing one
  3. Click on “Enable APIs and Services”
  4. Select the “Maps Javascript API” option
  5. Click “Enable” on the new page that comes up. Go back to the previous page and do a search for “Geocoding API,” click on it and enable it as well
  6. Then, on the right nav of the page, click on Credentials, copy the API key on the page and save it to a file

Now, let’s update our document to show the map and let the user know it can be used to get their current location as the address. Also, we will add a form that’s pre-filled with the address the user selects.

...

<body>
  <div class="container">
    <h3>Shipping Address</h3>
    <p>You can click the button below to use your current location as your shipping address</p>
    <div id="map">
    </div>
    <button id="showMe" class="btn">Use My Location</button>
    <form id="shippingAddress">
      <div id="locationList"></div>
      <br>
      <div class="input-field">
        <textarea class="input_fields materialize-textarea" id="address" type="text"></textarea>
        <label class="active" for="address">Address (Area and Street)</label>
      </div>
      <div class="input-field">
        <input class="input_fields" id="locality" type="text">
        <label class="active" for="locality">Locality</label>
      </div>
      <div class="input-field">
        <input class="input_fields" id="city" type="text">
        <label class="active" for="city">City/District/Town</label>
      </div>
      <div class="input-field">
        <input class="input_fields" id="postal_code" type="text">
        <label class="active" for="pin_code">Pin Code</label>
      </div>
      <div class="input-field">
        <input class="input_fields" id="landmark" type="text">
        <label class="active" for="landmark">Landmark</label>
      </div>
      <div class="input-field">
        <input class="input_fields" id="state" type="text">
        <label class="active" for="State">State</label>
      </div>
    </form>
    <!-- You could add a fallback address gathering form here -->
  </div>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-beta/js/materialize.min.js"></script>
  <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY"></script>
  <script src="js/main.js"></script>
</body>

</html>

While we are here, let’s style things a bit to make this look a little better:

.container {
  width: 50%;
  max-width: 800px;
}

#map {
  height: 50vh;
  margin-bottom: 10px;
  display: none;
}

#locationList .card {
  padding: 10px;
}

#toast-container {
  top: 50%;
  bottom: unset;
}

.toast {
  background-color: rgba(0, 0, 0, 0.8);
}

@media only screen and (max-width: 768px) {
  .container {
    width: 80%;
  }
}

This CSS hides the map until we are ready to view it. Our app should look like this:

Let’s plan this out

Our app will be making use of the HTML5 Geolocation API to determine our user’s current location as well as Google’s Geocode API with a technique called Reverse Geocoding. The Geocode API takes a human-readable address and changes it into geographical (latitudinal and longitudinal) coordinates and marks the spot in the map.

Reverse Geocoding does the reverse. It takes the latitude and longitude and converts them to human-readable addresses. Geocoding and Reverse Geocoding are well documented.

Here’s how our app will work:

  1. The user clicks on the “Use My Location” button
  2. The user is located with the HTML5 Geolocation API (navigator.geolocation)
  3. We get the user’s geographic coordinates
  4. We pass the coordinates to the geocode request API
  5. We display the resulting addresses to the user

Most times, the geocode returns more than one address, so we would have to show the user all the returned addresses and let them choose the most accurate one.

Phew! Finally, we can get to the fun part of actually writing the JavaScript. Let’s go through each of the steps we outlined.

Step 1: Clicking the button

In our main.js file, let’s get a reference to the HTML button. While we are at it, we’ll set up some other variables we’ll need, like our API key.

//This div will display Google map
const mapArea = document.getElementById('map');

//This button will set everything into motion when clicked
const actionBtn = document.getElementById('showMe');

//This will display all the available addresses returned by Google's Geocode Api
const locationsAvailable = document.getElementById('locationList');

//Let's bring in our API_KEY
const __KEY = 'YOUR_API_KEY';

//Let's declare our Gmap and Gmarker variables that will hold the Map and Marker Objects later on
let Gmap;
let Gmarker;

//Now we listen for a click event on our button
actionBtn.addEventListener('click', e => {
  // hide the button 
  actionBtn.style.display = "none";
  // call Materialize toast to update user 
  M.toast({ html: 'fetching your current location', classes: 'rounded' });
  // get the user's position
  getLocation();
});

When the click handler for our button runs, it:

  1. Hides the button
  2. Alerts the user that we are getting their current location (a “toast” in Materialize is like a popup notification)
  3. Calls the getLocation function

Step 2: Get the user’s location (latitude and longitude)

When our getLocation function is invoked, we need to do some more work. First, let’s check if we can even use the Geolocation API:

getLocation = () => {
  // check if user's browser supports Navigator.geolocation
  if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(displayLocation, showError, options);
  } else {
    M.toast({ html: "Sorry, your browser does not support this feature... Please Update your Browser to enjoy it", classes: "rounded" });
  }
}

When we have support, it calls geolocation’s getCurrentPosition method. If it doesn’t have support, then the user is alerted that there’s no browser support.

If there is support, then the getCurrentLocation method is used to get the current location of the device. The syntax is like this:

navigator.geolocation.getCurrentPosition(*success, error, [options]*)
  • success : This is a callback function that takes a position as its only parameter. For us, our success callback function is the displayLocation function.
  • error : [optional] This is a callback function that takes a PositionError as its sole input parameter. You can read more about this here. Our error callback function is the showError function.
  • options : [optional] This is an object which describes the options property to be passed to the getCurrentPosition method. You can read more about this here. Our options parameter is the options object.

Before writing our displayLocation function, let’s handle the showError function and options object:

// Displays the different error messages
showError = (error) => {
  mapArea.style.display = "block"
  switch (error.code) {
    case error.PERMISSION_DENIED:
      mapArea.innerHTML = "You denied the request for your location."
      break;
    case error.POSITION_UNAVAILABLE:
      mapArea.innerHTML = "Your Location information is unavailable."
      break;
    case error.TIMEOUT:
      mapArea.innerHTML = "Your request timed out. Please try again"
      break;
    case error.UNKNOWN_ERROR:
      mapArea.innerHTML = "An unknown error occurred please try again after some time."
      break;
  }
}
//Makes sure location accuracy is high
const options = {
  enableHighAccuracy: true
}

Now, let’s write the code for our displayLocation function inside our main.js file:

displayLocation = (position) => {
  const lat = position.coords.latitude;
  const lng = position.coords.longitude;
}

We now have our user’s latitude and longitude and we can view them in the console by writing the code below inside displayLocation:

console.log( `Current Latitude is ${lat} and your longitude is ${lng}` );

Step 3: Show the user’s current location on a Google Map

To do this, we will be adding these lines of code to our displayLocation function.

const latlng = {lat, lng}
showMap(latlng);
createMarker(latlng);
mapArea.style.display = "block";

The first line takes our lat and lng values and encapsulates it in the latlng object literal. This makes it easy for us to use in our app.

The second line of code calls a showMap function which accepts a latlng argument. In here, we get to instantiate our Google map and render it in our UI.

The third line invokes a createMarker function which also accepts our object literal (latlng) as its argument and uses it to create a Google Maps Marker for us.

The fourth line makes the mapArea visible so that our user can now see the location.

displayLocation = (position) => {
  const lat = position.coords.latitude;
  const lng = position.coords.longitude;
  const latlng = { lat, lng }
  showMap(latlng);
  createMarker(latlng);
  mapArea.style.display = "block";
}

Now, let’s get to creating our functions. We will start with the showMap function.

showMap = (latlng) => {
  let mapOptions = {
    center: latlng,
    zoom: 17
  };
  Gmap = new google.maps.Map(mapArea, mapOptions);
}

The showMap function creates a mapOptions objects that contain the map center (which is the lat and lng coordinates we got from displayLocation) and the zoom level of the map. Finally, we create an instance of the Google Maps class and pass it on to our map. In fewer words, we instantiate the Google Maps class.

To create a map instance, we specify two parameters in the constructor: the div the map will be displayed and the mapOptions. In our case, our div is called mapArea and our mapOptions is called mapOptions. After this, our created map will show up, but without a marker. We need a marker so the user can identify their current position on the map.

Let’s create our marker using the createMarker function:

createMarker = (latlng) => {
  let markerOptions = {
    position: latlng,
    map: Gmap,
    animation: google.maps.Animation.BOUNCE,
    clickable: true
  };
  Gmarker = new google.maps.Marker(markerOptions);
}

A few things to note in this code:

  1. The position property positions the marker at the specified latlng
  2. The map property specifies the map instance where the marker should be rendered (in our case, it’s Gmap)
  3. The animation property adds a little BOUNCE to our marker
  4. The clickable property set to true means our marker can be clicked
  5. Finally, we instantiate the Marker class in our Gmarker instance variable

So far, our user’s location has been fetched, the map has rendered and the user can see their current location on the map. Things are looking good! 🕺

Step 4: Pass the latitude and longitude to the Geocode API

Google’s Geocoding API will be used to convert our user’s numeric geographical coordinates to a formatted, human-readable address using the reverse geocoding process we covered earlier.

The URL takes this form:

https://maps.googleapis.com/maps/api/geocode/outputFormat?parameters

…where the outputFormat may either be a json or xml which determines the the format used to deliver the data. The parameters part is a list of parameters needed for the request.

Our request URL will look like this:

https://maps.googleapis.com/maps/api/geocode/json?latlng=${latlng}&key=${__KEY}

Let’s go ahead and connect to the API. We would do this in a function called getGeolocation.

getGeolocation = (lat, lng) => {
  const latlng = lat + "," + lng;
  fetch( `https://maps.googleapis.com/maps/api/geocode/json?latlng=${latlng}&key=${__KEY}` )
    .then(res => res.json())
    .then(data => console.log(data.results));
}

The getGeolocation function takes two arguments ( lat and lng } concatenates them to form a new latlng variable that is passed to the URL.

Using the Fetch API (more on this here), we add the new latlng and __KEY into the Geocode request URL. Then, on the response object we get back, we pass the .json method to resolve the promise with JSON. Finally, we log the response in our console.

To make use of our newly created function, we have to call it in the displayLocation function. So let’s update our displayLocation function to contain the getGeolocation function call:

displayLocation = (position) => {
  const lat = position.coords.latitude;
  const lng = position.coords.longitude;
  const latlng = { lat, lng }
  showMap(latlng);
  createMarker(latlng);
  mapArea.style.display = "block";
  getGeolocation(lat, lng)// our new function call
}

The returned data should look something like this:

{
  "results" : 
    {
      "address_components": 
        {
          "long_name": "1600",
          "short_name": "1600",
          "types": ["street_number"]
        },
        {
          "long_name": "Amphitheatre Pkwy",
          "short_name": "Amphitheatre Pkwy",
          "types": ["route"]
        },
        {
          "long_name": "Mountain View",
          "short_name": "Mountain View",
          "types": ["locality", "political"]
        },
        {
          "long_name": "Santa Clara County",
          "short_name": "Santa Clara County",
          "types": ["administrative_area_level_2", "political"]
        },
        {
          "long_name": "California",
          "short_name": "CA",
          "types": ["administrative_area_level_1", "political"]
        },
        {
          "long_name": "United States",
          "short_name": "US",
          "types": ["country", "political"]
        },
        {
          "long_name": "94043",
          "short_name": "94043",
          "types": ["postal_code"]
        }
      ],
      "formatted_address": "1600 Amphitheatre Parkway, Mountain View, CA 94043, USA",
      "geometry": {
        "location": {
          "lat": 37.4224764,
          "lng": -122.0842499
        },
        "location_type": "ROOFTOP",
        "viewport": {
          "northeast": {
            "lat": 37.4238253802915,
            "lng": -122.0829009197085
          },
          "southwest": {
            "lat": 37.4211274197085,
            "lng": -122.0855988802915
          }
        }
      },
      "place_id": "ChIJ2eUgeAK6j4ARbn5u_wAGqWA",
      "types": ["street_address"]
    }
  ],
  "status" : "OK"
}

Step 5: Display the returned address(es) for the user to select

At this stage, we have made a request to Google’s Geocoding API and have gotten our result logged in the console. Now, we have to display the results in a UI for our user. This requires two things:

  1. Create a new function that handles creating HTML elements
  2. Update our getGeolocation function to make the function call

Let’s create the function that would take care of creating the HTML elements and updating the DOM.

populateCard = (geoResults) => {
  geoResults.map(geoResult => {
    // first create the input div container
    const addressCard = document.createElement('div');
    // then create the input and label elements
    const input = document.createElement('input');
    const label = document.createElement('label');
    // then add materialize classes to the div and input
    addressCard.classList.add("card");
    input.classList.add("with-gap");
    // add attributes to them
    label.setAttribute("for", geoResult.place_id);
    label.innerHTML = geoResult.formatted_address;
    input.setAttribute("name", "address");
    input.setAttribute("type", "radio");
    input.setAttribute("value", geoResult.formatted_address);
    input.setAttribute("id", geoResult.place_id);
    addressCard.appendChild(input);
    addressCard.appendChild(label)
    return (
      // append the created div to the locationsAvailable div
      locationsAvailable.appendChild(addressCard)
    );
  })
}

In this function, we iterate through our results and create some HTML elements (div , input and a label), append the input and the label to the div and finally append the new div to a parent div (which is locationsAvailable). Once we get the result from our API call, our DOM will be created and displayed to the user.

Next, we update our getGeolocation function to call our populateCard function by replacing the last line of getGeolocation with this:

.then(data => populateCard(data.results));

…which means our updated function should look this:

getGeolocation = (lat, lng) => {
  const latlng = lat + "," + lng;
  fetch( `https://maps.googleapis.com/maps/api/geocode/json?latlng=${latlng}&key=${__KEY}` )
    .then(res => res.json())
    .then(data => populateCard(data.results));
}

At this point, everything should be working fine. Our user clicks a button, gets a location displayed on the map along with a list of addresses that match the current location.

Step 6: Listen for map events and repeat steps 4 and 5

If our user decides to move the map or marker, nothing happens to the UI — new addresses aren’t not displayed and everything remains static. We’ve got to fix this, so let’s make our app dynamic by listening for map events. You can read all about Google Map Events here.

There are three events we want to listen for:

  1. drag: This is fired once the user starts dragging and continues to drag the map
  2. dragend: This is fired once the user stops dragging the map
  3. idle: This is fired once every event has been fired and the map is idle

Quick Question: Why are these events best suited for our app?

Quick Answer: The first two events will make sure that our map marker stays in the center of the map during the drag event while the idle event will make a geocoding request with the new coordinates.

To listen for these events we have to update the showMap function with the following:

Gmap.addListener('drag', function () {
  Gmarker.setPosition(this.getCenter()); // set marker position to map center
});
Gmap.addListener('dragend', function () {
  Gmarker.setPosition(this.getCenter()); // set marker position to map center
});
Gmap.addListener('idle', function () {
  Gmarker.setPosition(this.getCenter()); // set marker position to map center
  if (Gmarker.getPosition().lat() !== lat || Gmarker.getPosition().lng() !== lng) {
    setTimeout(() => {
      updatePosition(this.getCenter().lat(), this.getCenter().lng()); // update position display
    }, 2000);
  }
});

As explained above, the first two event listeners simply ensure that the marker remains in the center of our map. Pay closer attention to the idle event listener because that is where the action is.

Once the idle event is fired, the marker goes to the center, then a check is done to find out if the current position of the marker is the same with the lat or lng values received by the displayLocation function. If it is not the same, then we call the updatePosition function after two seconds of idleness.

Having said that, we have to make a few updates to the showMap function. First, on the function header, we have to include more parameters and on the showMap function call. We need to add the new arguments there, too. Our showMap function should look like this:

showMap = (latlng, lat, lng) => {
  let mapOptions = {
    center: latlng,
    zoom: 17
  };
  Gmap = new google.maps.Map(mapArea, mapOptions);
  Gmap.addListener('drag', function () {
    Gmarker.setPosition(this.getCenter()); // set marker position to map center
  });
  Gmap.addListener('dragend', function () {
    Gmarker.setPosition(this.getCenter()); // set marker position to map center
  });
  Gmap.addListener('idle', function () {
    Gmarker.setPosition(this.getCenter()); // set marker position to map center
    if (Gmarker.getPosition().lat() !== lat || Gmarker.getPosition().lng() !== lng) {
      setTimeout(() => {
        updatePosition(this.getCenter().lat(), this.getCenter().lng()); // update position display
      }, 2000);
    }
  });
}

And our displayLocation function should look like this:

displayLocation = (position) => {
  const lat = position.coords.latitude;
  const lng = position.coords.longitude;
  const latlng = { lat, lng }
  showMap(latlng, lat, lng); //passed lat and lng as the new arguments to the function
  createMarker(latlng);
  mapArea.style.display = "block";
  getGeolocation(lat, lng);
}

Having listened for the map events, let’s repeat Step 4 and Step 5.

We start by writing our updatePosition function. This function will perform only one action for now, which is to pass the new lat and lng values to the getGeolocation function:

updatePosition = (lat, lng) => {
  getGeolocation(lat, lng);
}

After getting the new position and fetching addresses, our DOM should re-render for the user, right? Well, it doesn’t. And to fix that, we create a function that will force the DOM to re-render:

// check if the container has a child node to force re-render of dom
function removeAddressCards(){
  if (locationsAvailable.hasChildNodes()) {
    while (locationsAvailable.firstChild) {
      locationsAvailable.removeChild(locationsAvailable.firstChild);
    }
  }
}

It checks if the locationsAvailable div has any childNodes, and if it does, it deletes them before creating new address cards. The populateCard function is now updated to this:

populateCard = (geoResults) => {
  // check if a the container has a child node to force re-render of dom
  removeAddressCards();
  
  geoResults.map(geoResult => {
    // first create the input div container
    const addressCard = document.createElement('div');
    // then create the input and label elements
    const input = document.createElement('input');
    const label = document.createElement('label');
    // then add materialize classes to the div and input
    addressCard.classList.add("card");
    input.classList.add("with-gap");
    // add attributes to them
    label.setAttribute("for", geoResult.place_id);
    label.innerHTML = geoResult.formatted_address;
    input.setAttribute("name", "address");
    input.setAttribute("type", "radio");
    input.setAttribute("value", geoResult.formatted_address);
    input.setAttribute("id", geoResult.place_id);
    addressCard.appendChild(input);
    addressCard.appendChild(label);
    return (
      locationsAvailable.appendChild(addressCard);
    );
  })
}

We are done and are now able to fully get and display the user’s address!

Step 7: Pre-fill the form with the address data the user selects

The final step is to fill the form with the address the user selects. We need to add a click event listener to the address card and pass the address as argument to the callback function.

Here’s how we add the event listener in the populateCard function:

input.addEventListener('click', () => inputClicked(geoResult));

You should note that the geoResult argument in the above callback is the selected address object from the results array. That said, update the populateCard function to accommodate our new line of code.

The inputClicked function uses a series of if statements to assign values to our form elements. so before working on it, let’s bring our form elements into the equation:

const inputAddress = document.getElementById('address'),
  inputLocality = document.getElementById('locality'),
  inputPostalCode = document.getElementById('postal_code'),
  inputLandmark = document.getElementById('landmark'),
  inputCity = document.getElementById('city'),
  inputState = document.getElementById('state');

Having done this, let us now work on pre-filling the form with the address_components in the inputClicked function.

inputClicked = result => {
  result.address_components.map(component => {
    const types = component.types
    if (types.includes('postal_code')) {
      inputPostalCode.value = component.long_name
    }
    if (types.includes('locality')) {
      inputLocality.value = component.long_name
    }
    if (types.includes('administrative_area_level_2')) {
      inputCity.value = component.long_name
    }
    if (types.includes('administrative_area_level_1')) {
      inputState.value = component.long_name
    }
    if (types.includes('point_of_interest')) {
      inputLandmark.value = component.long_name
    }
  });
  inputAddress.value = result.formatted_address;
  // to avoid labels overlapping pre-filled input contents
  M.updateTextFields();
  // removes the address cards from the UI
  removeAddressCards();
}

The above block of code iterates over the clicked (or selected) address component, checks the types of components and finally assigns them to the input fields if they match.

M.updateTextFields() function is from Materialize and it ensures that the label does not overlap with the input fields values and the removeAddressCards() function removes the address cards from the UI.

With this, we are done with our app and have saved our users lots of typing and headaches! Surely they will thank us for implementing such a hassle free solution.

This whole UX experiment can be seen as a shortcut that will help the user pre-fill the shipping address form. We should clearly state here that the returned addresses isn’t always 100% accurate. But that’s why we allow the address to be manually edited in the UI.

Demo!

See the Pen Prefill Address Form with Geolocation and Google Maps by CSS-Tricks (@css-tricks) on CodePen.


Let’s make a form that puts current location to use in a map! originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/lets-make-a-form-that-puts-current-location-to-use-in-a-map/feed/ 9 274428
Improving Conversations using the Perspective API https://css-tricks.com/improving-conversations-using-perspective-api/ https://css-tricks.com/improving-conversations-using-perspective-api/#comments Fri, 11 Aug 2017 13:30:47 +0000 http://css-tricks.com/?p=257367 I recently came across an article by Rory Cellan-Jones about a new technology from Jigsaw, a development group at Google focused on making people safer online through technology. At the time they’d just released the first alpha version of …


Improving Conversations using the Perspective API originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
I recently came across an article by Rory Cellan-Jones about a new technology from Jigsaw, a development group at Google focused on making people safer online through technology. At the time they’d just released the first alpha version of what they call The Perspective API. It’s a machine learning tool that is designed to rate a string of text (i.e. a comment) and provide you with a Toxicity Score, a number representing how toxic the text is.

The system learns by seeing how thousands of online conversations have been moderated and then scores new comments by assessing how “toxic” they are and whether similar language had led other people to leave conversations. What it’s doing is trying to improve the quality of debate and make sure people aren’t put off from joining in.

As the project is still in its infancy it doesn’t do much more than that. Still, we can use it!

Starting with the API

To get started with using the API, you’ll need to request API access from their website. I managed to get access within a few days. If you’re interested in playing with this yourself, know that you might need to wait it out until they email you back. Once you get the email saying you have access, you’ll need to log in to the Google Developer Console and get your API key. Create your credentials with the amount of security you’d like and then you’re ready to get going!

Now you’ll need to head over to the documentation on GitHub to learn a bit more about the project and find out how it actually works. The documentation includes lots of information about what features are currently available and what they’re ultimately designed to achieve. Remember: the main point of the API is to provide a score of how toxic a comment is, so to do anything extra with that information will require some work.

Getting a Score with cURL

Let’s use PHP’s cURL command to make the request and get the score. If you’re not used to cURL, don’t panic; it’s relatively simple to get the hang of. If you want to try it within WordPress, it’s even easier because there are a native WordPress helper functions you can use. Let’s start with the standard PHP method.

Whilst we walk through this, it’s a good idea to have the PHP documentation open to refer to. To understand the fundamentals of cURL, we’ll go through a couple of the core options we may need to use.

$params = array(
  'comment' => array(
    'text' => 'what a stupid question...',
    'languages' => array(
      'en'
    ),
    'requestedAttributes' => array(
      'TOXICITY' => ''
    )
  )
);

$params = json_encode($params);

$req = curl_init();
curl_setpot($req, 'CURLOPT_URL', 'https://commentanalyzer.googleapis.com/v1alpha1/comments:analyze');
curl_setpot($req, 'CURLOPT_POSTFIELDS', $params);
curl_setopt($req, CURLOPT_HTTPHEADER, array('Content-Type: application/json');
curl_exec($req);
curl_close($req);

The above seven lines simply perform different actions when you want to make a cURL request to a server. You’ll need to initialize the cURL request, set the options for the request, execute it, then close the connection. You’ll then get your comment data back from the server in the form of JSON data which is handy for a number reasons.

Send An Ajax Request

As you get the response from the API in JSON format, you can also make an Ajax request to the API as well. This is handy if you don’t want to dive too much into PHP and the method of using cURL requests. An example of an Ajax request (using jQuery) would look something like the following:

$.ajax({

        data: {
                comment: {
                        text: "this is such a stupid idea!!"
                },
                languages: ["en"],
                requestedAttributes: {
                        TOXICITY: {}
                }
        },
        type: 'post',
        url: 'https://commentanalyzer.googleapis.com/v1alpha1/comments:analyze?key=YOUR-API-KEY',
        success: function(response) {

                console.log(response);

        }

});

The data we get back is now logged to the console ready for us to debug it. Now we can decode the JSON data into an array and do something with it. Make sure you include your API key at the end of the URL in the Ajax request too otherwise it won’t work! Without it; you’ll get an error about your authentication being invalid. Also, you don’t have to stop here. You could also take the example above a step further and log the score in a database as soon as you’ve got the data back, or provide feedback to the user on the front-end in the form of an alert.

The WordPress Way

If you’re using WordPress (which is relevant here since WordPress has comment threads you might want to moderate) and you want to make a cURL request to the Perspective API, then it’s even simpler. Using the Toxic Comments plugin as an example, you can do the following instead thanks to WordPress’ exhaustive built-in functions. You won’t need to do any of the following if you use the plugin, but it’s worth explaining what the plugin does behind the scenes to achieve what we want to do here.

$request = wp_remote_post($arguments, $url);

This will make a post request to the external resource for us without doing much leg work for it. There are other functions that you can use too, like a get request but we don’t need to think about that right now. You then need to use another function to get the requested data back from the server. Yes, you’re completely right. WordPress has a function for that:

$data = wp_remote_retrieve_body($request);

So that’s great, but how do we actually use the API to get the data we want? Well, to start with if you just want to get the overall toxicity score, you’ll need to use the following URL which will ask the API to read the comment and score it. It also has your API key at the end which you need to authenticate your request. Make sure you change it to yours!

https://commentanalyzer.googleapis.com/v1alpha1/comments:analyze?key=YOUR-API-KEY

It looks quite plain and if you visit it, it’ll take you to a 404 page. But if you make a cURL request to it, either through your favorite CMS or via a simple PHP script, you’ll end up getting data that might look similar to this:

{
  "attributeScores": {
    "TOXICITY": {
      "summaryScore": {
        "value": 0.567890,
        "type": "PROBABILITY"
      }
    }
  },
  "languages": [
    "en"
  ]
}

The score you’ll get back from the API will be a number as a decimal. So if a comment gets a score of 50% toxicity, the score you’ll actually get back from the API will be 0.5. You can then use this score to manipulate the way the comment is stored and shown to the end user by marking it as spam or creating a filter to let users show less or more toxic comments, much like Google has done in their example.

There are other bits of useful data you may want to look into as well. Things such as the context of the comment which can help you understand the intent of the comment without reading it firsthand.

Ultimately, with this kind of data we can expect to receive, it makes it possible to filter out certain comments with particular intent and provide a nicer comment area where trolls can often take over. Over time when the API becomes more developed, we should expect the scoring to be more robust and more accurate on the analysis of the comment we send it.

Privacy and Censorship

This is a pretty hot topic these days. I can imagine some pushback on this, particularly because it involves sending your data to Google to have it analyzed and judged Google computers, which ultimately does have effect on your voice and ability to use it. Personally, I think the idea behind this is great and it works very well in practice. But when you think about it’s implementation on popular news websites and social media platforms, you can see how privacy and censorship could be a concern.

The Perspective API makes a great effort to score comments based on a highly complex algorithm, but it seems that there is still a long way to go yet in the fight to maintain more civil social spaces online.

Until then, play around with the API and let me know what you think! If you’re not up for writing something from scratch, there are some public client libraries available now in both Node and Python so go for it! Also, remember to err on the side of caution as the API is still in an alpha phase for now so things may break. If you’re feeling lazy, check out the quick start guide.


Improving Conversations using the Perspective API originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/improving-conversations-using-perspective-api/feed/ 4 257367
PWA Directory https://css-tricks.com/pwa-directory/ Tue, 25 Apr 2017 11:18:59 +0000 http://css-tricks.com/?p=254156 The other day I was watching an interview with Ade Oshineye where he discussed his work on the PWA Directory at Google, a showcase of progressive web apps. And it’s pretty neat!

It lists a whole bunch of PWAs out …


PWA Directory originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
The other day I was watching an interview with Ade Oshineye where he discussed his work on the PWA Directory at Google, a showcase of progressive web apps. And it’s pretty neat!

It lists a whole bunch of PWAs out there and you can filter them by Lighthouse metrics – that’s the auditing tool from Google that scores a web app and gives us developers the ability to improve them.

To Shared LinkPermalink on CSS-Tricks


PWA Directory originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
254156
The Average Web Page (Data from Analyzing 8 Million Websites) https://css-tricks.com/average-web-page-data-analyzing-8-million-websites/ https://css-tricks.com/average-web-page-data-analyzing-8-million-websites/#comments Mon, 15 Aug 2016 13:23:07 +0000 http://css-tricks.com/?p=244360 The following is a guest post by Catalin Rosu, who along with some colleagues, dug up a ton of data about the HTML content of web sites. This is the most recent study of its kind and wildly fascinating


The Average Web Page (Data from Analyzing 8 Million Websites) originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
The following is a guest post by Catalin Rosu, who along with some colleagues, dug up a ton of data about the HTML content of web sites. This is the most recent study of its kind and wildly fascinating to see the results. I find it especially fun to compare the top results to what I would have guessed would have won.

We’ve all been there. We try to improve our HTML code making it clean, beautiful, and readable. We do this in pursuit of better semantics and better accessibility, so that everyone can use it. It’s our top priority. And we always have questions:

  • What is the best way to structure the markup?
  • How are others doing it?

Questions like these were running through my mind. I wondered about how people write markup these days, as new web technologies emerge. So, I teamed up with a few of my colleagues at AWRCloud and we came up with a data set of over 8 million pages from Google top twenty results.

The studies that came before this one

Back in 2005, Ian Hickson, the editor of HTML5 specification, made an analysis of a sample of slightly over a billion documents, looking to see what the web is made of. A billion is an enormous number, but to Google, nothing is impossible. With this huge amount of documents, he extracted valuable information about popular class names, elements, attributes, and related metadata. The outstanding results were later published as Web Authoring Statistics, which is still the most powerful web authoring study ever made.

More recently, in 2008, the Opera Metadata Analysis and Mining Application crawler, MAMA, ended up analyzing about 3.5 million URLs. Brian Wilson, the author of this impressive work, expanded the study by publishing results detailing page structures, including HTML, CSS, and JavaScript.

One of the analyses from Web Authoring Statistics that later proved vital for the work in progress HTML5 development, was a list of the most popular class names in those HTML documents. The Opera MAMA crawler also searched for the most common class names and in addition to Google’s results and they’ve published relevant results on the popular ID attribute values given to elements as well.

What does this study add to the conversation?

The data for this study comes from 8,021,323 index pages gathered from the top twenty Google results for about 30 million keywords, chosen by keyword volume. Meaning: we had 30 million keywords. We ran a Google search for each of them and took the URLs for the top 20 results and added them to the list and removed the duplicates.

We can only assume that the relevance of these web pages to the general web population is very high. That is based on the likelihood these are popular and high-trafficked websites commensurate to their search result positions.

How fresh is this data?

The latest data set is from May 20th, 2016.

This new study will never surpass the former study Google made back in 2005. It’s not about overcoming Opera’s great study either. It’s about finding new and relevant insights on the actual markup used by the most popular and successful web pages on the internet.

So, how does the average HTML page look like nowadays? Take a look at the screenshots below and check out the study for the full statistics.

The Stats

Following our study, we find that the average website index page uses twenty six different different element types.

Most website use 26 different HTML elements, give or take a few, with a curious peak at 9.

The twenty six elements used on the most pages, ordered by frequency:

Unsurprisingly, <head> and <html> are used on all sites. Slightly surprising with <body> at 99% – perhaps a very large website with a weird mistake? The table elements at the bottom of the list are still on a surprising nearly-third of all websites.

Among the document type declarations that specify which version of (X)HTML a page is using, the latest HTML5 doctype is clearly leading the way.

Nearly two thirds of all websites declare themselves at HTML5.

If we look at all the elements that are specifically about telling browser or search engines about the site and how to style it, we found about 175 million elements, and here’s how they broke down:

The breakdown of the 105 million elements for content sectioning looks like this:

<h3>s are the most popular heading elements and overall content sectioning element.

Of the billion text content elements:

The <div> has a commanding lead.

What’s the future of web?

Us web developers and web content creators are curious and interested in usage, statistics, and browser support. These are the things that led to the class names findings back in 2005, names known today as the most popular HTML5 tags.

The web is evolving fast. This isn’t new, but it can feel overwhelming. The trends are changing from year to year and as a web content creator, it requires motivation and effort to stay up to date. Think about how the markup and the average web page looked like ten years ago and how a modern web page looks like today.

We also used the study to look at emerging technologies like Web Components. While Web Components allows authors to create arbitrarily named elements, we can look for standards elements used in the creation of Web Components.

Nobody can predict the future. We can only guess how the average web page will look like ten years from now on. Next time we run this study (we’re considering quarterly), will we see things like Web Components rise?

And again, the complete data set is here.


The Average Web Page (Data from Analyzing 8 Million Websites) originally published on CSS-Tricks, which is part of the DigitalOcean family. You should get the newsletter.

]]>
https://css-tricks.com/average-web-page-data-analyzing-8-million-websites/feed/ 40 244360