Archiwum kategorii: CSS

VS Code extensions for the discerning developer palate

Post pobrano z: VS Code extensions for the discerning developer palate

I am a VS Code extension snob. I like to hunt down the most obscure extensions for VS Code — the ones that nobody knows about — and impress people at parties with my knowledge of finely aged and little-known VS Code capabilities… then watch as they look around desperately for someone else to talk to. It’s like the “Sideways” of VS Code.

In my endless pursuit of the perfect VS Code setup, I reached out to my colleagues here on the Azure team and asked them to share their favorite extension in their own words. So clear your pallet and breathe in the aromatic flavors of productivity; I am your VS Code Extension Sommelier.


Christina Warren – Settings Sync

I cannot live without this extension. If you use multiple machines (especially on multiple platforms, where a sym-linked Dropbox folder won’t really work), this extension is for you. It syncs your extensions, settings file, keybinding file, launch file, snippets folder, extension settings, and workspaces folder. This means that when you login to a new machine, you can quickly get back to work with your own settings and workflow tools in just a few minutes.

👉 Get Settings SyncExtension


Shayne Boyer – Paste JSON as Code

Consuming an endpoint that produces JSON is like breathing, but no one wants to choke on the hand cranking of an object by looking back and forth between JSON and the target language. This is a long loved feature in Visual Studio for .NET developers, but now you too can copy the JSON and paste that class into the editor as your target language and save a ton of time. Currently supports C#, Go, C++, Java, TypeScript, Swift, Elm, and JSON Schema.

👉 Get Paste JSON as Code Extension


Jeremy Likness – Spell Right

I find myself authoring blog posts, articles, and documentation almost every day. After embracing the power of Markdown (it is, after all, what is used to drive our own https://docs.com), I began writing my content in Visual Studio Code. It has a built-in preview window so I can edit the Markdown source and see the rendered result side-by-side. As much as I’ve written over the years, mastering the fine art of spelling still eludes me. Maybe it’s because I’m lazy, and this extension doesn’t help at all. With Spell Right, I get to reunite with my same favorite red squiggly lines that I first met in Word. It does a great job of catching spelling mistakes in real time, then illuminates my mistakes with a handy light bulb with alternative suggestions that give me single-click corrections. It enables me to be highly productive and look like I know what I’m doing. I recommend this for anyone who uses Code to write.

👉 Get Spell Right Extension


Aaron Wislang – Go

I live in VS Code and use it for everything from code and content to its integrated terminal. This extension enables first-class support for IntelliSense, testing, refactoring and more, making Code the best place to me to write Go. And it turns out I’m not the only one who thinks so; it helped to make Code the most popular editor amongst Gophers, just ahead of vim-go, as of the Go 2017 Survey!

👉 Get Go Extension


Cecil Phillip – C# Extensions

This extension was created by one of our community members, and it’s a great companion to the official C# extension from Microsoft. The “New Class|Interface” actions make it easy to add new types, and takes some of the hassle out of fixing up the namespaces. It also comes with a few interesting refactorings like „Initialize fields from constructors,” which I use pretty often. Whenever I’m teaching a C# course, I always have my students that are using Visual Studio Code install this extension.

👉 Get C# Extension


Brian Clark – VS Live Share

Pair programming just got way better. Gone are the days where I need to set up screen sharing to review code with coworkers. Instead I fire up a live share session, invite the other party and we can all view and edit code directly from our editors. I’ve used it in a situations where I review someone else’s C# code on my machine while it runs on THEIR machine! I didn’t have anything installed on my Mac for C# and yet I could debug their code!

👉 Get VS Live Share Extension


David Smith – Rewrap

I write a lot of text, and sometimes I just want (or need) to write in a plain-text environment. Easy reflowing of text is essential. (Surprised this isn’t built in, in fact.)

👉 Get Rewrap Extension


Anthony Chu – Git Lens

At a glance, GitLens shows me contextual information from Git about the line of code and the file I’m working in. It adds some useful commands to view history and diffs, search commits, and browse local and remote branches… all without leaving VS Code.

👉 Get Git Lens Extension


Asim Hussain – AsciiDoc

I used to write with Markdown, we all make mistakes. The solution to my Markdown mistake is AsciiDoc, especially if you write a lot of code snippets as I do. Out of the box it let’s you add line numbers, annotate and highlight lines and provides an incredible amount of customization. Plus, as a bonus it also can convert your blog posts into PDFs, ePubs, Mobis which is perfect for ebooks.

Once you start with AsciiDoc it’s hard to go back to Markdown and this plugin lets you preview your AsciiDoc right inside the editor.

👉 Get AsciiDoctor Extension


Seth Juarez) – VS Code Tools For AI

With Visual Studio Code Tools for AI, I can finally use machines I need but might never have access to in order to build the next Skynet — all within the comfort of my favorite lightweight editor. We live in amazing times, friends…

👉 Get VS Code Tools For AI Extension


Alena Hall – Ionide

Ionide is an awesome Visual Studio Code extension for cross-platform F# development. It’s open-source and it was created by the F# Community. I use it every day on multiple machines I have. It runs perfectly on both my Mac and Linux machines. Ionide conveniently integrates with Paket, Project Scaffold, and you can experiment away as much as you want in F# Interactive!

👉 Get Ionide Extension


Matt Soucoup – VSCodeVim

There’s an old joke that goes: “How do you know if a developer uses vim? They’ll tell you.” Well, I use vim! But… I want more. I want to tell everybody I use vim and I want to use all the great features and extensions that VS Code offers. (I mean, look at the list here!) So that’s where VSCodeVim saves the day for me. It puts a full-featured vim emulator into my VS Code editor, letting me edit files super fast by typing esoteric commands like h, 10 k, i, and u (lots and lots of u) and I still get to use all the awesome features of VS Code.

👉 Get VSCodeVim Extension


John Papa – Docker

If you like it put a container on it. Yeah, containers are the latest craze, but in a constantly evolving containerization world, it’s nice to have great tooling make it easy to use containers. Enter the Docker extension for VS Code. It handles the complete container development and deployment lifecycle! Start by generating docker files to your project, create an image, run it, and even push it to a container registry. If you’re like me, you like to make sure you still have complete control over your code and your app, even when they are inside of containers. Accessing the files, showing logs, and debugging the running container are all essential tools for development. This extension puts all of this within your reach without having to learn the docker command line!

👉 Get Docker Extension


Suz Hinton – Arduino

My favorite extension for VS Code is Arduino. I’m pretty sure anyone who knows me wouldn’t be surprised about that. Traditionally, developing programs for Arduino-compatible micro-controller boards has been done in the Arduino IDE. It’s a powerful program which smooths over the compilation and uploading experiences for dozens of boards. It is, however, not a full code IDE. It’s missing some of the features you love, such as autocomplete, a file tree, and fine-grained tuning of the editor itself.

The good news is that the Arduino extension allows you to finally develop freely for all of your favorite micro-controller boards without leaving VS Code!

Here are some of my favorite things about the extension:

  1. It’s open source! So reporting bugs and contributing improvements is a straightforward experience.
  2. The Command Palette integration is so handy. Compile and upload your code to an Arduino with one simple shortcut.
  3. Access all the great tools from the Arduino IDE right in VS Code. Yes, that even means board / library management and the serial monitor!
  4. Scaffolding brand new Arduino projects is a command away. No more copy + pasting older project directories to get set up.

👉 Get Arduino Extension


Burke Holland – Azure Functions

Serverless is like Hansel — so hot right now. But Serverless shouldn’t be a black box. The Azure Functions extensions for VS Code puts Serverless right inside of the editor. I love it because it lets me create new Serverless projects, new functions for all of the available trigger types (http, timer, blob storage, etc.), and most importantly, I can run them locally and debug them. Not that I would ever need to debug. My code is always perfect.

👉 Get Azure Functions Extension

The post VS Code extensions for the discerning developer palate appeared first on CSS-Tricks.

​Experience a Simpler Cloud Computing Platform with DigitalOcean

Post pobrano z: ​Experience a Simpler Cloud Computing Platform with DigitalOcean

(This is a sponsored post.)

From deploying static sites and blogging platforms to managing multiple client websites, DigitalOcean provides a flexible platform for developers and their teams to deliver an unparalleled end-user experience with a lightning-fast network, pre-configured applications, and a 99.99% uptime SLA. Simply let us know your needs and our solutions engineers will provide the best options available.

Direct Link to ArticlePermalink

The post ​Experience a Simpler Cloud Computing Platform with DigitalOcean appeared first on CSS-Tricks.

On Switching Code Editors

Post pobrano z: On Switching Code Editors

I’m sure a lot of you are like me and have switched code editors a number of times. I think my first major editor was Coda. Then I moved to TextMate when I started working primarily on local. Then Sublime Text. And, most recently, VS Code. I bet your journey was different. I know lots of folks that quite love Atom, Brackets, WebStorm, and even BBedit. You do you!

For me, that’s four changes in a dozen years, or a change every three years. Moving isn’t something I do quickly. Here’s a collection of thoughts around the idea of changing editors.

When moving, I have to take time to make sure it works pretty much like the old one.

Otherwise, I’ll just end up disliking it to the point that I switch back a day or two later. It’s happened to me every time I switch. I have little false-starts after a switch where I go back to the old editor because something bugged me too much or it affected my productivity and I gave up. (Now that I know I do this, I don’t let a single false-start make me feel like the editor I’m trying is never a possibility.)

My latest switch was from Sublime Text to VS Code. I’d become become very used to the key bindings (e.g. CMD+Shift+d to duplicate a line) in Sublime Text, so thankfully VS Code has that covered.

I was amazed to find even my VIM friends happy and comfortable in VS Code. (Fun fact: we have key bindings choices in CodePen, too.)

Nothing can be too obnoxious.

In one of my first attempts at switching, I found the UI in VS code to be too cluttered and the find-in-project feature to be a little slow and awkward. Those things bugged me to the point they caused false-starts and I went back to Sublime Text.

On this last switch attempt (my 3rd or 4th maybe?) I finally have a theme I quite like (customized a smidge), found some settings to clean up the UI (I removed the whitespace indicators which were overwhelming to me, and overrode that intense blue footer with something more chill).

In working with find-in-project a bit more, I’ve grown to get used to it. I finally might even like it more than Sublime, as the sidebar approach is more consistent than opening a new tab of results. I find the jump-to-line feature works more consistently and search feels more the first-class citizen it should be.

Another factor would be Emmet. I’m quite sure that I’d be too annoyed writing HTML and CSS in an editor without Emmet, and I’d just give up and use something else that had it. Emmet isn’t even an extension in VS Code, it’s built in.

I’m cool with making small changes after a successful switch.

Once I’ve actually done it, and made the switch to full-time use, then I can make some changes. Maybe I’ll learn some new key commands. Maybe I’ll add an extension that adds functionality I’ve never had before. Maybe the editor affects some workflow thing in a way I’m now willing to try.

The new editor better have some killer feature that incentivizes me to switch.

If it’s exactly the same, why bother?

The new editor needs to be faster (or feel just as fast). Or should look better. Or it should have some awesome package that is only available on it. Ideally all of that.

In this recent switch for me, it was GitLens.

How cool is that?

It ought to have a plugin architecture.

Meaning that anyone can write code to extend the editor. I’m fairly certain that having a plugin architecture (plus a healthy amount of community momentum) is what is key to any editor’s success. Sublime’s package manager, and the subsequent built-in packages feature of VS Code, seem crucial. Not only for functionality but even just for the look of the editor. I wouldn’t use an editor with a look I hate, but I’d be tempted to if the functionality was awesome. That’s a non-issue with a plugin-based editor. Open source seems smart as well.

Careful for those GOTCHAs.

One of those was spell-checking for me. In Sublime Text, it was an option under the View menu. I had it checked all the time, and it spell-checked all the time.

This is not a thing in VS Code. That was dangerous after switching because, who knows, I may have been committing typos all over the place. Fortunately, this extension seems to be doing the trick. Thank jeepers for extensions!

Your thoughts!

I thought it might be interesting to ask what y’all think about when switching code editors. There were lots of responses. I picked out as many as I could here and focused on one thing that you mentioned.

The post On Switching Code Editors appeared first on CSS-Tricks.

What bit of advice would you share with someone new to your field?

Post pobrano z: What bit of advice would you share with someone new to your field?

The most FA of all the FAQs.

Here’s Laura Kalbag:

Find what you love. Don’t worry about needing to learn every language, technique or tool. Start with what interests you, and carve your own niche. And then use your powers for good!

And my own:

Buy a domain name. Figure out how to put an HTML file up there. Isn’t that a powerful feeling? Now you’ve got table stakes. Build something.

Definitely, go read other A Book Apart author answers because they are all great. My other favorite is just three words.

Direct Link to ArticlePermalink

The post What bit of advice would you share with someone new to your field? appeared first on CSS-Tricks.

Automate Your Workflow with Node

Post pobrano z: Automate Your Workflow with Node

You know those tedious tasks you have to do at work: Updating configuration files, copying and pasting files, updating Jira tickets.

Time adds up after a while. This was very much the case when I worked for an online games company back in 2016. The job could be very rewarding at times when I had to build configurable templates for games, but about 70% of my time was spent on making copies of those templates and deploying re-skinned implementations.

What is a reskin?

The definition of a reskin at the company was using the same game mechanics, screens and positioning of elements, but changing the visual aesthetics such as color and assets. So in the context of a simple game like ‘Rock Paper Scissors,’ we would create a template with basic assets like below.

But when we create a reskin of this, we would use different assets and the game would still work. If you look at games like Candy Crush or Angry Birds, you’ll find that they have many varieties of the same game. Usually Halloween, Christmas or Easter releases. From a business perspective it makes perfect sense.

Now… back to our implementation. Each of our games would share the same bundled JavaScript file, and load in a JSON file that had different content and asset paths. The result?

The good thing about extracting configurable values into a JSON file is that you can modify the properties without having to recompile/build the game again. Using Node.js and the original breakout game created by Mozilla, we will make a very simple example of how you can create a configurable template, and make releases from it by using the command line.

Our game

This is the game we’ll be making. Reskins of MDN Breakout, based on the existing source code.

Gameplay screen from the game MDN Breakout where you can use your paddle to bounce the ball and destroy the brick field, with keeping the score and lives.

The primary color will paint the text, paddle, ball and blocks, and the secondary color will paint the background. We will proceed with an example of a dark blue background and a light sky blue for the foreground objects.

Prerequisites

You will need to ensure the following:

We have tweaked the original Firefox implementation so that we first read in the JSON file and then build the game using HTML Canvas. The game will read in a primary color, and a secondary color from our game.json file.

{
  "primaryColor": "#fff",
  "secondaryColor": "#000"
}

We will be using example 20 from the book Automating with Node.js. The source code can be found here.

Open up a new command line (CMD for Windows, Terminal for Unix-like Operating systems) and change into the following directory once you have cloned the repository locally.

$ cd nobot-examples/examples/020

Remember the game server should be running in a separate terminal.

Our JSON file sits beside an index.html file inside a directory called template. This is the directory that we will copy from whenever we want to do a new release/copy.

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Paddle Game</title>
  <style>
    * {
      padding: 0;
      margin: 0;
    }
    canvas {
      background: #eee;
      display: block;
      margin: 0 auto;
    }
  </style>
</head>
<body>
  <canvas id="game" width="480" height="320"></canvas>
  <script type="text/javascript" src="../../core/game-1.0.0.js"></script>
</body>
</html>

You see above that every game we release will point to the same core bundle JavaScript file. Let’s have a look at our JavaScript implementation under the core directory.

Don’t look too much into the mechanics of how the game works, more so how we inject values into the game to make it configurable.

(function boot(document) {
  function runGame(config) {
    const canvas = document.getElementById('game');
    canvas.style.backgroundColor = config.secondaryColor;
    // rest of game source code gets executed... hidden for brevity
    // source can be found here: https://git.io/vh1Te
  }

  function loadConfig() {
    fetch('game.json')
      .then(response => response.json())
      .then(runGame);
  }

  document.addEventListener('DOMContentLoaded', () => {
    loadConfig();
  });
}(document));

The source code is using ES6 features and may not work in older browsers. Run through Babel if this is a problem for you.

You can see that we are waiting for the DOM content to load, and then we are invoking a method called loadConfig. This is going to make an AJAX request to game.json, fetch our JSON values, and once it has retrieved them, it will initiate the game and assign the styles in the source code.

Here is an example of the configuration setting the background color.

const canvas = document.getElementById('game');
canvas.style.backgroundColor = config.secondaryColor; // overriding color here

So, now that we have a template that can be configurable, we can move on to creating a Node.js script that will allow the user to either pass the name of the game and the colors as options to our new script, or will prompt the user for: the name of the game, the primary color, and then the secondary color. Our script will enforce validation to make sure that both colors are in the format of a hex code (e.g. #101b6b).

When we want to create a new game reskin, we should be able to run this command to generate it:

$ node new-reskin.js --gameName='blue-reskin' --gamePrimaryColor='#76cad8' --gameSecondaryColor='#10496b'

The command above will build the game immediately, because it has the three values it needs to release the reskin.

We will create this script new-reskin.js, and this file carries out the following steps:

  1. It will read in the options passed in the command line and store them as variables. Options can be read in by looking in the process object (process.argv).
  2. It will validate the values making sure that the game name, and the colors are not undefined.
  3. If there are any validation issues, it will prompt the user to re-enter it correctly before proceeding.
  4. Now that it has the values, it will make a copy of the template directory and place a copy of it into the releases directory and name the new directory with the name of the game we gave it.
  5. It will then read the JSON file just recently created under the releases directory and override the values with the values we passed (the colors).
  6. At the end, it will prompt the user to see if they would like to open the game in a browser. It adds some convenience, rather than us trying to remember what the URL is.

Here is the full script. We will walk through it afterwards.

require('colors');
const argv = require('minimist')(process.argv.slice(2));
const path = require('path');
const readLineSync = require('readline-sync');
const fse = require('fs-extra');
const open = require('opn');
const GAME_JSON_FILENAME = 'game.json';

let { gameName, gamePrimaryColor, gameSecondaryColor } = argv;

if (gameName === undefined) {
  gameName = readLineSync.question('What is the name of the new reskin? ', {
    limit: input => input.trim().length > 0,
    limitMessage: 'The project has to have a name, try again'
  });
}

const confirmColorInput = (color, colorType = 'primary') => {
  const hexColorRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
  if (hexColorRegex.test(color)) {
    return color;
  }
  return readLineSync.question(`Enter a Hex Code for the game ${colorType} color `, {
    limit: hexColorRegex,
    limitMessage: 'Enter a valid hex code: #efefef'
  });
};

gamePrimaryColor = confirmColorInput(gamePrimaryColor, 'primary');
gameSecondaryColor = confirmColorInput(gameSecondaryColor, 'secondary');

console.log(`Creating a new reskin '${gameName}' with skin color: Primary: '${gamePrimaryColor}' Secondary: '${gameSecondaryColor}'`);

const src = path.join(__dirname, 'template');
const destination = path.join(__dirname, 'releases', gameName);
const configurationFilePath = path.join(destination, GAME_JSON_FILENAME);
const projectToOpen = path.join('http://localhost:8080', 'releases', gameName, 'index.html');

fse.copy(src, destination)
  .then(() => {
    console.log(`Successfully created ${destination}`.green);
    return fse.readJson(configurationFilePath);
  })
  .then((config) => {
    const newConfig = config;
    newConfig.primaryColor = gamePrimaryColor;
    newConfig.secondaryColor = gameSecondaryColor;
    return fse.writeJson(configurationFilePath, newConfig);
  })
  .then(() => {
    console.log(`Updated configuration file ${configurationFilePath}`green);
    openGameIfAgreed(projectToOpen);
  })
  .catch(console.error);

const openGameIfAgreed = (fileToOpen) => {
  const isOpeningGame = readLineSync.keyInYN('Would you like to open the game? ');
  if (isOpeningGame) {
    open(fileToOpen);
  }
};

At the top of the script, we require the packages needed to carry out the process.

  • colors to be used to signify success or failure using green or red text.
  • minimist to make it easier to pass arguments to our script and to parse them optionally. Pass input without being prompted to enter.
  • path to construct paths to the template and the destination of the new game.
  • readline-sync to prompt user for information if it’s missing.
  • fs-extra so we can copy and paste our game template. An extension of the native fs module.
  • opn is a library that is cross platform and will open up our game in a browser upon completion.

The majority of the modules above would’ve been downloaded/installed when you ran npm install in the root of the nobot-examples repository. The rest are native to Node.

We check if the game name was passed as an option through the command line, and if it hasn’t been, we prompt the user for it.

// name of our JSON file. We store it as a constant
const GAME_JSON_FILENAME = 'game.json';

// Retrieved from the command line --gameName='my-game' etc.
let { gameName, gamePrimaryColor, gameSecondaryColor } = argv;

// was the gameName passed?
if (gameName === undefined) {
  gameName = readLineSync.question('What is the name of the new reskin? ', {
    limit: input => input.trim().length > 0,
    limitMessage: 'The project has to have a name, try again'
  });
}

Because two of our values need to be hex codes, we create a function that can do the check for both colors: the primary and the secondary. If the color supplied by the user does not pass our validation, we prompt for the color until it does.

// Does the color passed in meet our validation requirements?
const confirmColorInput = (color, colorType = 'primary') => {
  const hexColorRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
  if (hexColorRegex.test(color)) {
    return color;
  }
  return readLineSync.question(`Enter a Hex Code for the game ${colorType} color `, {
    limit: hexColorRegex,
    limitMessage: 'Enter a valid hex code: #efefef'
  });
};

We use the function above to obtain both the primary and secondary colors.

gamePrimaryColor = confirmColorInput(gamePrimaryColor, 'primary');
gameSecondaryColor = confirmColorInput(gameSecondaryColor, 'secondary');

In the next block of code, we are printing to standard output (console.log) to confirm the values that will be used in the process of building the game. The statements that follow are preparing the paths to the relevant files and directories.

The src will point to the template directory. The destination will point to a new directory under releases. The configuration file that will have its values updated will reside under this new game directory we are creating. And finally, to preview our new game, we construct the URL using the path to the local server we booted up earlier on.

console.log(`Creating a new reskin '${gameName}' with skin color: Primary: '${gamePrimaryColor}' Secondary: '${gameSecondaryColor}'`);
const src = path.join(__dirname, 'template');
const destination = path.join(__dirname, 'releases', gameName);
const configurationFilePath = path.join(destination, GAME_JSON_FILENAME);
const projectToOpen = path.join('http://localhost:8080', 'releases', gameName, 'index.html');

In the code following this explanation, we:

  • Copy the template files to the releases directory.
  • After this is created, we read the JSON of the original template values.
  • With the new configuration object, we override the existing primary and secondary colors provided by the user’s input.
  • We rewrite the JSON file so it has the new values.
  • When the JSON file has been updated, we ask the user if they would like to open the new game in a browser.
  • If anything went wrong, we catch the error and log it out.
fse.copy(src, destination)
  .then(() => {
    console.log(`Successfully created ${destination}`green);
    return fse.readJson(configurationFilePath);
  })
  .then((config) => {
    const newConfig = config;
    newConfig.primaryColor = gamePrimaryColor;
    newConfig.secondaryColor = gameSecondaryColor;
    return fse.writeJson(configurationFilePath, newConfig);
  })
  .then(() => {
    console.log(`Updated configuration file ${configurationFilePath}`green);
    openGameIfAgreed(projectToOpen);
  })
  .catch(console.error);

Below is the function that gets invoked when the copying has completed. It will then prompt the user to see if they would like to open up the game in the browser. The user responds with y or n

const openGameIfAgreed = (fileToOpen) => {
  const isOpeningGame = readLineSync.keyInYN('Would you like to open the game? ');
  if (isOpeningGame) {
    open(fileToOpen);
  }
};

Let’s see it in action when we don’t pass any arguments. You can see it doesn’t break, and instead prompts the user for the values it needs.

$ node new-reskin.js
What is the name of the new reskin? blue-reskin
Enter a Hex Code for the game primary color #76cad8
Enter a Hex Code for the game secondary color #10496b
Creating a new reskin 'blue-reskin' with skin color: Primary: '#76cad8' Secondary: '#10496b'
Successfully created nobot-examples\examples\020\releases\blue-reskin
Updated configuration file nobot-examples\examples\020\releases\blue-reskin\game.json
Would you like to open the game? [y/n]: y
(opens game in browser)

My game opens on my localhost server automatically and the game commences, with the new colors. Sweet!

Oh… I’ve lost a life already. Now if you navigate to the releases directory, you will see a new directory called blue-reskin This contains the values in the JSON file we entered during the script execution.

Below are a few more releases I made by running the same command. You can imagine if you were releasing games that could configure different: images, sounds, labels, content and fonts, you would have a rich library of games based on the same mechanics.

Even better, if the stakeholders and designers had all of this information in a Jira ticket, you could integrate the Jira API into the Node script to inject these values in without the user having to provide any input. Winning!


This is one of many examples that can be found in Automating with Node.js. In this book, we will be looking at a more advanced example using „Rock Paper Scissors” as the basis of a build tool created from scratch.

The post Automate Your Workflow with Node appeared first on CSS-Tricks.

Render Children in React Using Fragment or Array Components

Post pobrano z: Render Children in React Using Fragment or Array Components

What comes to your mind when React 16 comes up? Context? Error Boundary? Those are on point. React 16 came with those goodies and much more, but In this post, we’ll be looking at the rendering power it also introduced — namely, the ability to render children using Fragments and Array Components.

These are new and really exciting concepts that came out of the React 16 release, so let’s look at them closer and get to know them.

Fragments

It used to be that React components could only return a single element. If you have ever tried to return more than one element, you know that you’ll will be greeted with this error: Syntax error: Adjacent JSX elements must be wrapped in an enclosing tag. The way out of that is to make use of a wrapper div or span element that acts as the enclosing tag.

So instead of doing this:

class Countries extends React.Component {
  render() {
    return (
      <li>Canada</li>
      <li>Australia</li>
      <li>Norway</li>
      <li>Mexico</li>
    )
  }
}

…you have to add either an ol or ul tag as a wrapper on those list items:

class Countries extends React.Component {
  render() {
    return (
      <ul>
        <li>Canada</li>
        <li>Australia</li>
        <li>Norway</li>
        <li>Mexico</li>
      </ul>
    )
  }
}

Most times, this may not be the initial design you had for the application, but you are left with no choice but to compromise on this ground.

React 16 solves this with Fragments. This new features allows you to wrap a list of children without adding an extra node. So, instead of adding an additional element as a wrapper like we did in the last example, we can throw <React.Fragment> in there to do the job:

class Countries extends React.Component {
  render() {
    return (
      <React.Fragment>
        <li>Canada</li>
        <li>Australia</li>
        <li>Norway</li>
        <li>Mexico</li>
      </React.Fragment>
    )
  }
}

You may think that doesn’t make much difference. But, imagine a situation where you have a component that lists different items such as fruits and other things. These items are all components, and if you are making use of old React versions, the items in these individual components will have to be wrapped in an enclosing tag. Now, however, you can make use of fragments and do away with that unnecessary markup.

Here’s a sample of what I mean:

class Items extends React.Component {
  render() {
    return (
      <React.Fragment>
        <Fruit />
        <Beverages />
        <Drinks />
      </React.Fragment>
    )
  }
}

We have three child components inside of the fragment and can now create a component for the container that wraps it. This is much more in line with being able to create components out of everything and being able to compile code with less cruft.

Fragment Shorthand

There is a shorthand syntax when working with Fragments, which you can use. Staying true to its fragment nature, the syntax is like a fragment itself, leaving only only empty brackets behind.

Going back to our last example:

class Fruit extends React.Component {
  render() {
    return (
      <>
        <li>Apple</li>
        <li>Orange</li>
        <li>Blueberry</li>
        <li>Cherry</li>
      </>
    )
  }
}

Question: Is a fragment better than a container div?

You may be looking for a good reason to use Fragments instead of the container div you have always been using. Dan Abramov answered the question on StackOverflow. To summarize:

  1. It’s a tiny bit faster and has less memory usage (no need to create an extra DOM node). This only has a real benefit on very large and/or deep trees, but application performance often suffers from death by a thousand cuts. This is one less cut.
  2. Some CSS mechanisms like flexbox and grid have a special parent-child relationship, and adding divs in the middle makes it harder to maintain the design while extracting logical components.
  3. The DOM inspector is less cluttered.

Keys in Fragments

When mapping a list of items, you still need to make use of keys the same way as before. For example, let’s say we want to pass a list of items as props from a parent component to a child component. In the child component, we want to map through the list of items we have and output each item as a separate entity. Here’s how that looks:

const preload = {
  "data" : [
    {
      "name": "Reactjs",
      "url": "https://reactjs.org",
      "description": "A JavaScript library for building user interfaces",
    },
    {
      "name": "Vuejs",
      "url": "https://vuejs.org",
      "description": "The Progressive JavaScript Framework",
    },
    {
      "name": "Emberjs",
      "url": "https://www.emberjs.com",
      "description": "Ember.js is an open-source JavaScript web framework, based on the Model–view–viewmodel pattern"
    }
  ]
}

const Frameworks = (props) => {
  return (
    <React.Fragment>
      {props.items.data.map(item => (
        <React.Fragment key={item.id}>
          <h2>{item.name}</h2>
          <p>{item.url}</p>
          <p>{item.description}</p>
        </React.Fragment>
      ))}
    </React.Fragment>
  )
}

const App = () => {
  return (
    <Frameworks items={preload} />
  )
}

See the Pen React Fragment Pen by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

You can see that now, in this case, we are not making use of any divs in the Frameworks component. That’s the key difference!

Render Children Using an Array of Components

The second specific thing that came out of React 16 we want to look at is the ability to render multiple children using an array of components. This is a clear timesaver because it allows us to cram as many into a render instead of having to do it one-by-one.

Here is an example:

class Frameworks extends React.Component {
  render () {
    return (
      [
        <p>JavaScript:</p>
        <li>React</li>,
        <li>Vuejs</li>,
        <li>Angular</li>
      ]
    )
  }
}

You can also do the same with a stateless functional component:

const Frontend = () => {
  return [
    <h3>Front-End:</h3>,
    <li>Reactjs</li>,
    <li>Vuejs</li>
  ]
}

const Backend = () => {
  return [
    <h3>Back-End:</h3>,
    <li>Express</li>,
    <li>Restify</li>
  ]
}

const App = () => {
  return [
    <h2>JavaScript Tools</h2>,
    <Frontend />,
    <Backend />
  ]
}

See the Pen React Fragment 2 Pen by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

Conclusion

Like the Context API and Error Boundary feature that were introduced in React 16, rendering children components with Fragment and multiples of them with Array Components are two more awesome features you can start making use of as you build your application.

Have you started using these in a project? Let me know how in the comments so we can compare notes. 🙂

The post Render Children in React Using Fragment or Array Components appeared first on CSS-Tricks.

itty.bitty

Post pobrano z: itty.bitty

Mark this down as one of the strangest things I’ve seen in a good long while. Nicholas Jitkoff has made a tool called itty.bitty that creates websites with all of the assets being contained within their own link. You can create a website without any HTML or CSS resources at all because it’s all been base64 encoded into the URL itself.

Take this crazy looking URL, for example:

https://itty.bitty.site/#How_it_works/XQAAAAJdDAAAAAAAAAAeHMqHyTY4PyKmqfkwr6ooCXSIMxPQ7ojYR153HqZD3W+keVdvwyoyd+luwncAksxo8PWJs+831jtAVty8rDpGXmyebtxMTP3PSa4g8/593sWue8WaPjJZKXPqJ+G6ffUrBFhdclCfPRZ1gVobusxdd5g4xI5328XhoOoeADUFQZMeRzfBVzcSlEdUqLcxhD5c7kYCBo79g1FR9+rRyw0U3Jyc+5Axt5HWUqKPcEBfQpO35dwvNhCOOZQ/83PRzl51VrpqJ+zwl7UT1JUUK24g/b9zCUO6EIGg1bcZW0VBL53SSK4LE+UkJNv3WKY04UQnISAISa4WghBZjXT5ID0S61leAohaQ9Kqkv6fGfZKI+RFa0bqnsYAHjZZQce8enN9zmfqYR/KIuNnoHmpY9M0W0okWY00CsBUYMdGh1tuE9dypij8nGhJnV61wgkafLJGNsg71mZFmumJRFRdjhuoORlUot67GJomgzUTr5dksBkTokx2zOQKMVW3M1yIQfy3ylY4txUf1icCDjf+Xs2Zhcm51cFucwhLfPIMfGfvlDOh4LtSQxlTu9YfZl4+H6fBMeVed/EqzEOwXAH21SiHUWBzxU8iwSNdHa48CvP41ozlThFVKKpWP290FN8UAcKd8smsf4YKLuDFHqoMyZILv1+k4ZhqEfXKO0Ky7oHaI7xLacv0mihhxK6Kz2jYlwFIsi3E8Mo8miWTIGcyZXCpwmsYzuKDigv5ZFGkw/VhOrQV/wLyqoa707i7PKnJE9BT1mhWpzonBLME6S7afa1CB3FqcPp7+/vRJbrKH9LqXpwUgzbdIl67YqgXBls5enwQ5mWgU23HC3Es60D0mBCALFNIsm/5HPLPWBwJrkfmx/JNPvnt2YLLeopCva1nY3cINXEfpGRtm2/j/F19PWFNJ0BW98k9KqaQNYduYlQN6Xjctewe2mRNM8k9CKaVet5pJ/eWshL8iMwje5sIX8MI09TGetVqAsgw6U+2F6UcqJPXFyJrGvdxeK6P5Zi9tSLQr5hByMK9mgXUfPuAL+Sazf/jt2UTMmHtr0yCaYTgw8bu/+Bzdo+eaE3J/ZVlTPPr2alSi6SmVSWGlYbcTIQZMA5KpF4GZFGowyxQUbndHhP/J1yY1SZ9G8YBktTaoGkXL2CDo6u498VrB6f7+oS5MOe3pK2/YzghZKLtMq1yzdnFlyekKyOSjppwbwkYJlLn96JOOpFxPHC2AZmUhfRbyYLNTrEQottY5z2Y/ERJTXMhTOtkE2Ba5LIrkrR3/7zjQTY5EF2plyx8xKwd07hrzZJ5tjUL3o2Hz+TsTs7glon64LbdmEZ6gH0acX7GrzdedQPdcElyDx+ByFW6l+IXDxShmeWlOBsMh0EOT5LC2y9UO9ZsNvdq21EAIiqTEOCf8xqm88r22q/A+uDmQHNhPG9s6zjoLmIaWugZWSy4Ab9wGC0+iTriRnLiePMQayw6bJ03G5Hoq+VKIF5y7OV2ijQtUkqqjDpGwkFSjwgirudMVeVhekZf7uMcCyT3JDLaTbMUVEtji2eL3fugR+yx8WYE99rczKeSpH7KBW4Vvs2cdy+42YZkZC9Ajnm3y1g6ljqE5/a3amHTXs4SH5TutdTeXKYI9fD6MHjmSWG2G8/JRjMGYVFhKpQs93CSaINQ3vGJYki5FP9aGDWqS7RlxXnUKgImK5upLy4XgmYQxQwoMYhj5JbfGyz5jqw+JlxK8XUkr+AQApWG/9suBe6BCgoAQRdgRjkMJGVdNwbCCCNs5ri/8kDwKahl6TAI4cO5B8QNg9KF1UalldlyMd3ITCHrcKkof1ZLGpHULGHvCcTC3IFANcd1hhNwyapdQPQQjiEqSBqP63kae39ehNXpbEjLk3vPypTX73p3D5/NxBUUqJgDSkKvs2zkKSfIXproiyqFQIxJgu+NFBYppkjkxQUzPRfhaojDES5jFdPMQGGCUG+en0zDihgNSJFnim/WHnUw89LXCQS96tbuW3Ym8RoGUIOzw9IjpWq8DJq3YY26TZu3WtqHUaSEUN0BBnUjAz+SR/hI3ellqVIK069hk8zGu7cdsRlGuRtUDnVeqMPX3M9VqGd7Qw8A9dV6XbjUP0DZTp4Xiqyj2IfXOG+qGAqCfPkKzaSW//XuArU=

This link contains all of the HTML and CSS that makes up the website. The really cool thing about this is that you don’t need a server to make an itty.bitty site — once you paste the link above into the browser it’ll fetch some code from the itty bitty domain then extract and inflate the data from there.

I’m not entirely sure how useful this is but it’s certainly a novel concept to me! Go ahead and start making your own itty.bitty sites.

Direct Link to ArticlePermalink

The post itty.bitty appeared first on CSS-Tricks.

Hyperlinking Beyond the Web

Post pobrano z: Hyperlinking Beyond the Web

Hyperlinks are the oldest and the most popular feature of the web. The word hypertext (which is the ht in http/s) means text having hyperlinks. The ability to link to other people’s hypertext made the web, a web — a set of connected pages. This fundamental feature has made the web a very powerful platform and it is obvious that the world of apps needs this feature. All modern platforms support a way for apps to register a URI (custom protocol) and also have universal links (handling web links in an app).

Let’s see why we’d want to take advantage of this feature and how to do it.

Why have app links at all?

Creating URIs that apps can open provides a unique set of benefits. A URL encapsulates the entire state of the webpage (it used to before the advent of Single Page Applications (SPAs) heavy with JavaScript and even in them, it is best practice to maintain this functionality) or a web application and it is advisable to have routing in applications so that a URL navigation is never broken. URLs have allowed browsers to have consistent back and forward buttons and reload and bookmark pages. And things like Google’s search depend on webpage addresses and web applications to navigate users to the correct place. Allowing applications to open URLs provides a way to link content and functionality in your application from other applications, websites and even internally within the application, like help/tutorial pages.

Trivia! Desktop applications have had hyperlinks even before the world wide web like Hypercard on the Mac in 1987 and Windows Help in 1990. The revolutionary thing about hyperlinks in the web is that you can link content that was created and is owned by others. This concept of cross app linking was not popular in traditional platforms until the rise of smartphones.

App links over the command line

There is already a shell for the command line interface and it supports passing arguments, redirecting the results from one application to anther, and even scripting. Still, the need for having support for hyperlinks is desirable for various reasons:

  • Security: The command line script is too complicated to get right. It is very risky to expect users of technology (who are not developers) to understand how to sanitize a command line script and make sure that the script does not accidentally call something, like rm -rf ~/Documents. The command line is not sand-boxed and though it provides a power, it puts responsibility on users and is prone to exploits.
  • Browser support: The browser cannot provide the command line interface. It is built to run untrusted third party code in a sandbox and that cannot be broken. And, if we don’t respect the rules of web technology, the app would silo itself out of the Internet. That is too much to lose.
  • Mobile: In the world of mobile, a touch keyboard is not as good and intuitive as the physical ones used on the desktop. Therefore, even if command line was present, it would be very difficult to use. All the power of redirection and chaining is not as effective as it is over a keyboard-based command line. A simpler solution like URL is more desirable in this context.
  • State: Theoretically, the command line interface can provide a way to have the application available in any state. But, practically, it was built for a separate purpose: to launch the application to perform a task and then return with a code. Additionally, it was de-prioritized by the GUIs in Windows and Mac. Many applications (like Microsoft Word and Adobe Photoshop) never had full-fledged command line support. They are not even present in the environment PATH and need the full path to be launched. URIs provide a fresh way to look at connecting information as well as functionality between applications. They are a secure, modern way of inter-app communication where the user does not have to think.

Linking Terminology

Deep Link

Deep link is the concept where a specific page/functionality within the website/application can be linked to. For example, https://css-tricks.com/link/to/this/page is a deep link for this page within the broader https://css-tricks.com link. Unlike superficial links that open applications, deep links open the application in a specific state, with passed data, ready to be consumed. Custom URI (described below) were the first ways to achieve deep linking within the app and „deep linking” in many contexts is now synonymous with custom URI, though it can also mean Universal links.

Custom URI

The web always had certain custom URIs that linked to default applications, like mailto:username@host.com and tel:1234567890 for email and telephone, respectively. It was an obvious choice to extend this and all major platforms provide a way for an app to register a URI protocol — like css-tricks://<link details> — that can open a deep linked native application. It should encapsulate the entire state and provide all routing benefits to open the app if it is available on the system. These provide a good interface for inter-app communication when the application is already installed and the desire is to open the user’s preferred application (like the browser or email client) that implements the desired protocol.

Custom URIs are great for the class of applications where the user wants to have a choice to select an app to perform a certain choice, but they are not great for the task of linking the website to the native application due to the following reasons:

  • Installation: Unlike the web, native apps need installation and, if they are not installed, you have two options: send the user to the app store (which also provides a custom URI) or properly fall back. Both these tasks require additional code which the URI does not encapsulate. The app store URI would be another separate URI that we need to store. Additionally, we have to detect whether the app is installed, which requires navigating to the URI and handling the error in navigation akin to a 404 since the protocol is not registered. This is not as simple as an anchor tag (<a href="{URL}"></a>) and, therefore, is a frequent source of developer complaints.
  • Lack of a central registry: Even if everything is done perfectly right, users can still be thrown into an application that they should not have been linked to. Unlike the DNS system (which guarantees uniqueness of domain names), application platforms do not have a central repository. So, multiple apps could register csstricks as a custom URI. Now, someone else could also register the same URI as one app and if the other application is installed on the system instead of the desired one, it could launch instead. The freedom of a custom URI for app selection works against the case where we always want to open a specific application.
  • Third Party Linking: Giving a custom URI to third parties because of the issues we’ve covered above is cumbersome and verifying them is painful. It is also a new protocol. Every website has links to the web content and updating the entirety of the Internet is not possible. It may not even be desired for SEO purposes.

Therefore, if we want to give the user the experience where the app is the preferred way to view content, custom URIs are not the perfect solution. This can be mitigated with smart app banners to some extent, where the banner would be displayed when the browser gets the custom URI and the app store link to identify the application. But this will not be seamless.

Trivia! URI and URLs are slightly different by definition, although they are used interchangeably. URI stands for Uniform Resource Identifier which means that it encapsulates everything required to get a resource. URL (Uniform Resource Locator) is a special type of URI that identifies the resource on the web. So, technically, a web address is a URL but something like csstricks:// would be just a URI.

These are all possible use cases where a custom URI might make sense:

  • You need to provide a URI that many third party client apps can register. Say you have a git:// in your app and the user’s favorite git client could register that URI and clone the repo when the the link is clicked.
  • You do not have a fully featured website.
  • You need a URI that is easy for users to remember.
  • You are willing to handle all edge cases related to navigation.

You can use custom URIs in parallel to the Universal link we’ll cover next and they can benefit from exposing the custom URL for inter-app communication while leaving the Universal link for the special case of web-to-app navigation. Android officially calls custom URIs deep links.

Control flows for Custom URI, Universal Link and Instant Apps
Universal link, App Link or App URI

The solution to the problem of web-to-app redirection can be solved with the Universal link (as it’s called in Mac and iOS), App Link (as it’s called in Android) or App URI (as it’s called in Windows UWP), which are different names of the same concept. This encapsulates the logic that every website needs to write in order to detect installed apps, launch correct pages and handle navigation failures in the case of custom URLs. It is very similar to the smart app banner in the sense that you need to provide an app store link to the application on your website for verification but it removes all the redundancy. Since the existing http(s) URL already contains the entire state, registering the custom URI is redundant. The original URL could itself navigate to the app with the website providing a fallback experience in case the application is not installed.

Once you register your app with a universal link, when the app is installed, the OS goes to the Internet to figure out the set of links that the app supports. Whenever any of those links get clicked, the native app gets launched instead of the browser. Full addressing support is now available in the application where a more customized experience can be provided falling back to the browser if the application is not installed. On important distinction with universal links is that they do affect regular browsing and therefore the OS providers keep then under tight lock and key.

A few good use cases for Universal links include:

  • You have a fully featured website for a fallback.
  • The desired flow for users is from the website to the app.
  • You have already built up a lot of karma by having content from your website linked around the web.

Instant Link or Deferred Deep Link

Deferred deep links provide the missing piece to the deep links if the user goes on to install the app when the link is opened. The user can be forwarded to the app store and the app store takes care of maintaining the context when the app is eventually launched after installation. This provides continuity in the case where an app is installed. Currently this method is only supported by Android (as Google Play Instant) and it is the option where you want to require users to move from the app to get the desired functionality. The hyperlink system on the web is seamless and clicking on a link gets you to the destination almost instantly (though a lot of things happen behind the scenes). Deferred deep links provide the same functionality for apps where clicking on a link can download the app and launch it with the correct page making all the tasks on app installation as seamless as possible.

You might consider using instant links if:

  • You need the users to use the app and not the website, even if they come from the browser (except for rare cases where they are low on disk space or have turned this feature off to save bandwidth).
  • Your key pages are always up-to-date in your application.
  • OK, always use them. With the extra SEO advantages that Google throws in for instant apps, there is no reason not to enable „instant” apps for an app that has Universal links.

Now that we have a summary of what each term means, let’s see how we can go about creating these on specific platforms.

Setting up application hyperlinks

MacOS and iOS

Custom URI

Registering a custom URI in MacOS and iOS is extremely easy. In Xcode, go to the Info.plist file of the project and select the option URL Types. Create an item of type URL Schemes inside the file where you can add all of the URL schemes you wish to support.

Note: The default URL Identifier filled in by Xcode within URL Types is optional.

URL Scheme in Xcode
<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>css-tricks</string>
    </array>
  </dict>
</array>

For MacOS, you will receive calls to the AppDelegate where you can override the function:

func application(_ application: NSApplication, open urls: [URL]) {
  // Select the first URL in the list of URL to open
  let url = urls[0]; 
  // Log the entire URL
  NSLog("%@", url.absoluteString)
}
-(void)application:(NSApplication *)application openURLs:(NSArray<NSURL *> *)urls {
  NSLog(@"%@", urls[0].absoluteString);
}

In iOS, the function to receive the same call in AppDelegate is:

func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
  NSLog("%@", url.absoluteString)
  return true
}
-(BOOL)application:(UIApplication *)app openURL:(NSURL *)url options: (NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
  NSLog(@"%@", url.absoluteString);
  return true;
}

The URL type in all these cases contains the usual URL pieces like the path, the query and the search parameters.

Universal Links

Universal links are not supported in MacOS. To get universal links on iOS, you need to be registered with the Apple Developer Program or be an Apple Developer Enterprise Program member. Universal links on iOS are part of a wider concept of associated domains where the secrets of the website, like stored credentials, can be accessed from within the app and Apple does not allow regular Xcode users to dabble with universal links (if you accidentally enable the functionality for your App ID but do not put this in the entitlements, be prepared to get a validation error: ERROR ITMS-90046: „Invalid Code Signing Entitlements”).

Trivia! Universal links are a new concept and many of the Mac and Windows desktop applications are built with their own network stack, do not use the app store and may have been released years ago. Therefore, the OS does not have the control required to force all applications to follow the Universal link concept and open the app. Apple has chosen to not implement Universal Links in MacOS yet, while on Windows they only work in the Microsoft Edge browser and other UWP apps (which use the same technology).

To register for Universal links on iOS, you need to do the following things:

  1. Enable the App ID on the Apple Developer website to get the feature for associated domains.
  2. Add the associated domains to the entitlements file.
  3. Add a apple-app-site-association file to the web domain to provide ownership verification for the application.
  4. Add code to handle clicks on Universal links.

Steps 1 and 2 can be performed together in the Capabilities tab on Xcode if you are signed in and have the right profile selected (which is from an Apple Developer or Enterprise account). The Xcode UI provides an indication that the both items have been completed.

Associated Domains in the Capabilities section in Xcode

In the image above, Step 2 has been successfully completed, while Step 1 is pending. You can click on the exclamation mark to find the issue. It is important to prefix the domain with applinks: (that identifies that you need app links functionality; the same is used for shared credentials). This identifies that the App Link capability has been enabled.

The above steps can also be accomplished individually.

For Step 1, enable Associated Domains in the App ID section on the developer website (shown at the bottom of the image):

Apple Developer website to enable associated domains

For Step 2, add the following to the entitlements file:

Entitlements plist
<key>com.apple.developer.associated-domains</key>
<array>
  <string>applinks:css-tricks.com</string>
</array>

Now, for Step 3, host a association file apple-app-site-association like https://css-tricks.com/apple-app-site-association with mime type application/json with the following JSON data from the root of your website:

{
  "applinks": {
    "apps": [],
    "details": {
      "ABCDEFGHIJ.com.ccs-tricks.mobile": {
        "paths": [
          "*"
        ]
      }
    }
  }
}

The above snippet grants the app with ID ABCDEFGHIJ.com.ccs-tricks.mobile and provides access to all requests on the https://css-tricks.com domain. Note that this works with https but http is not supported. You can also use an alternative location like https://css-tricks.com/.well-known/apple-app-site-association for this. This URL is preferred as it can keep the Android, iOS and UWP association files in a safe separate folder that cannot be accidentally deleted.

Trivia! Universal links do not guarantee that the app is opened. If the user goes back to the website from the header in iOS, the OS decides to default to the website from then on until the user decides to use the app by clicking on the header again from Safari.

For Step 4, add the following code to the app:

func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool {
  if userActivity.activityType == NSUserActivityTypeBrowsingWeb {
    let url = userActivity.webpageURL!
    print(url.absoluteString)
  }
  return true
}
-(BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler {
  if(userActivity.activityType == NSUserActivityTypeBrowsingWeb) {
    NSLog(@"%@", userActivity.webpageURL.absoluteString);
  }
  return true;
}

Smart app banners on iOS are much simpler and can be added via a simple meta HTML tag in the head section like this:

<meta name="apple-itunes-app" content="app-id=123456789, app-argument=https://css-tricks.com/linkto/this/article, affiliate-data=optionalAffiliateData">

They call the same method as Custom URIs we covered earlier.

Windows (Traditional)

On the traditional Windows platform (Win32/.NET), the custom URI (called Custom Pluggable Protocol Handler) is the only feature supported. Adding a custom URI in Windows involves adding a registry entry within HKEY_CLASSES_ROOT. The following will open the app with the absolute path given when clicking on a link to css-tricks://<text>. Due to using the command line, this always opens a new instance of the app and does not send the URL to the existing running application.

Registry Entry for providing application name
Registry entry for open command
Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\css-tricks]
"URL Protocol"=""
@="CSS Tricks (Application name)"

[HKEY_CLASSES_ROOT\css-tricks\shell]

[HKEY_CLASSES_ROOT\css-tricks\shell\open]

[HKEY_CLASSES_ROOT\css-tricks\shell\open\command]
@=""C:\\Windows\\notepad.exe" "%1""

The above launches notepad with the command line argument corresponding to the URL supplied. Be aware that Notepad does not support custom URI and will not know what to do with the URL.

This behave is similar to passing this on the console and the application needs to make sure it properly distinguishes between the argument being a regular CLI or a custom URI, as shown here:

namespace ConsoleApplication1 {
  class Program {
    static void Main(string[] args) {
      if (args.Length > 0 && args[0].IndexOf("css-tricks:") == 0) {
        Console.Write(args[0]);
      }
    }
  }
}
int main(int argc, char*argv[]) {
  if (argc > 1) { // argv[0] is the file name.
    std::string word(argv[1]);
    if (word.compare(0, 11, "css-tricks:") == 0) {
      std::cout<<word;
    }
  }
  return 0;
}

Universal Windows Platform (UWP)

On the Universal Windows Platform, you can use the package manifest to register both the custom URI and the Universal link (called App URI Handler).

Custom URI

Add a protocol declaration in the package.appxmanifest:

Protocol Declaration in package.appxmanifest

The same can be achieved in code:

<Extensions>
  <uap:Extension Category="windows.protocol">
    <uap:Protocol Name="css-tricks" DesiredView="default">
      <uap:DisplayName>CSS Tricks</uap:DisplayName>
    </uap:Protocol>
  </uap:Extension>
</Extensions>

…and this can now be handled within the app.

protected override void OnActivated(IActivatedEventArgs args) {
  base.OnActivated(args);
  if (args.Kind == ActivationKind.Protocol) {
    var e = args as ProtocolActivatedEventArgs;
    System.Diagnostics.Debug.WriteLine(e.Uri.AbsoluteUri);
  }
}
// In App.xaml.h
virtual void OnActivated(Windows::ApplicationModel::Activation::IActivatedEventArgs^ e) override;

// In App.xaml.cpp
void App::OnActivated(Windows::ApplicationModel::Activation::IActivatedEventArgs ^ e) {
  if (e->Kind == Windows::ApplicationModel::Activation::ActivationKind::Protocol) {
    auto args = (ProtocolActivatedEventArgs^)e;
    auto url = args->Uri->AbsoluteUri;
  }
}
Universal Links (App URI)

App URIs are only supported in the Microsoft Edge browser. They do not work in Internet Explorer, Chrome or Firefox. App URIs also have a package entry similar to the custom URI. It is not available in the UI of Visual Studio Code 2017. The package.appxmanifest entries are almost the same:

<Extensions>
  <uap3:Extension Category="windows.appUriHandler">
    <uap3:AppUriHandler>
      <uap3:Host Name="css-tricks.com" />
    </uap3:AppUriHandler>
  </uap3:Extension>
</Extensions>

If uap3 is not available, it can be added to the Package tag where uap is also defined:

xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"

App URI is a relatively new feature of Windows and many UWP projects target older versions of windows as the minimum version. You might need to bump that up in order to support this feature.

Just like iOS, the website needs to verify the ownership of the domain for this to function. The can be done by hosting a file with mime type application/json at the root of your website, like https://css-tricks.com/windows-app-web-link or https://css-tricks.com/.well-known/windows-app-web-link over https with the content:

{
  "packageFamilyName": "YourPackageFamilyNameHere",
  "paths": ["*"],
  "excludePaths": ["/none/*", "https://cdn.css-tricks.com/robot.txt"]
}

To get the package family name, run the following in Powershell and search for your package path:

Get-AppxPackage

Handling App URIs requires the same code as custom URIs on Windows. By design, all you need to do is see the protocol field in the provided URI and write the corresponding logic.

Just like iOS, Windows users have a choice to disable opening apps. Windows provides registry settings to force apps to open (used for testing) and also a validator tool (located at %windir%\system32\AppHostRegistrationVerifier.exe) to verify if the above settings are correct.

Android

Custom URI

Android has supported custom URIs from the very beginning. This can be done with code. In AndroidManifest.xml add:

<activity android:name=".CustomUriActivity">
  <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="css-tricks" />
  </intent-filter>
</activity>

The category DEFAULT is to ensure that there is no user action needed to enable it. BROWSABLE ensures that the custom URIs work in the browser.

This can then be handled in the CustomUriActivity.java on creation:

public class CustomUriActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Intent intent = getIntent();
    if (Intent.ACTION_VIEW.equals(intent.getAction())) {
      Uri uri = intent.getData();
    }
  }
} 
Universal Links

Universal links in Android are very similar to App URIs. Android Studio provides a GUI tool to create this. Go to Tools > App Link Assistant. This will provide the three tasks needed to create app links:

App Link Assistant in Android

For Step 1, enter the URL mapping editor and click on the + button to open the URL mapping dialog:

URL Mapping dialog

Once you click OK, it will show the code that could have been used alternatively in the AndroidManifest.xml:

<activity android:name=".CustomUriActivity">
  <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="http" android:host="css-tricks.com"/>
  </intent-filter>
</activity>

Note: This exactly the same as as it is for a custom URI. In the Step 2, select the activity you assigned and it will add the code to load the action. The code is also exactly the same as was used for custom URIs above.

In the Step 3, it will generate the JSON file that needs to be added to the website as application/json mime at https://css-tricks.com/.well-known/assetlinks.json.

[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "css_tricks",
    "package_name": "com.css-tricks.android",
    "sha256_cert_fingerprints":
    ["17:CC:87:9C:C8:39:B1:89:48:E8:2E:9E:6F:FC:7D:03:69:4D:05:90:2A:84:B8:AE:5D:6B:30:97:F8:1C:2B:BF"]
  }
}]

Android Studio automates the fingerprint generation that verifies the application identity. This identity is different for debug and the release version of the application.

Instant Link

Instant apps on Android require minor changes from the App links we covered earlier. To enable instant apps, modify the intent declared above to look like this:

<activity android:name=".CustomUriActivity">
  <intent-filter android:autoVerify="true">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="http" android:host="css-tricks.com"/>
    <data android:scheme="https"/>
  </intent-filter>
</activity>

This allows both http and https URLs to be associated with the app, and the autoVerify tells the Android (Google Play) store to verify the manifest item automatically. Instant app is a Play Store/Google Chrome feature. The changes on the app are minimal to support this.

Electron

Electron apps are traditional MacOS and Windows apps with the chromium runtime. These platforms only support custom URIs that can be enabled without touching the compiled code.

For MacOS, edit info.plist as described in the MacOS section. The events are received by the app object as:

// In main.js
require('electron').app.on('open-url', (event, url) => {
  console.log(url);
});

For Windows, add the registry entry as defined in the Windows section. The app will receive the custom URL in process.argv:

// In main.js
console.log(process.argv[2])

In electron, you can load external web pages that can open URLs. If it is desired to handle only those URLs internally, that can be registered via an electron API:

require('electron').protocol.registerHttpProtocol('css-tricks', (req, cb) => {
  console.log(req.url)
});

Web

Going a complete circle, a website can also register custom URIs (like handling mailto: links via a website). Note that these only affect links provided in other websites and have nothing to do with links in other applications. The API is very restrictive and can be used only for a selected link type: web+<insert anything here>. Browsers allow a pre-defined list of top level protocols to be registered:

  • Chrome: mailto, mms, nntp, rtsp, webcal
  • Firefox: bitcoin, geo, im, irc, iris, magnet, mailto, mms, news, sip, sms, smite, ssh, tel, urn, webcal, wti, xmpp

To register a protocol, you need to provide the same domain as the website that registers it (i.e. https://www.css-tricks.com can register only https://www.css-tricks.com and not https://css-tricks.com).

if (navigator.registerProtocolHandler) {
  navigator.registerProtocolHandler("web+csstricks", "https://css-ticks.com/?s=%s", "CSS Tricks custom URI");
}

The custom URIs are not indexed by search engines and therefore there is not much use to these apart from handling the links like mailto, webcal etc., that are already present in the web at multiple places.

Wrapping Up

Adding hyperlinks to native apps is an easy and effective way to seamlessly move the user across applications with graceful handling in case the application is not installed. With these links, applications can have provide paths all over the Internet which can be an effective promotional mechanism. Links also give websites a way to provide data and launch the applications on a specific page without the security worries that plague the downloadable files or command line scripts allowing effective tutorials and bug reports. Routing and URLs provide for the core features of the World Wide Web like navigation, the back button, bookmarks and search. Hyperlinks in apps bring these learning from the (almost) 30 years of world wide web to native applications.

The post Hyperlinking Beyond the Web appeared first on CSS-Tricks.