Web Standards

A Poll About Pattern Libraries and Hiring

Css Tricks - Tue, 08/15/2017 - 3:49am

I was asked (by this fella on Twitter) a question about design patterns. It has an interesting twist though, related to hiring, which I hope makes for a good poll.

Note: There is a poll embedded within this post, please visit the site to participate in this post's poll.

I'll let this run for a week or two. Then (probably) instead of writing a new post with the results, I'll update this one with the results. Feel free to comment with the reasoning for your vote.

A Poll About Pattern Libraries and Hiring is a post from CSS-Tricks

(An Interview About) imgix Page Weight

Css Tricks - Tue, 08/15/2017 - 3:41am

Imgix has been a long-time display ad sponsor here on CSS-Tricks. This post is not technically sponsored, I just noticed that they released a tool for analyzing image performance at any given URL that is pretty interesting.

We know web performance is a big deal. We know that images are perhaps the largest offender in ballooning page weights across the web. We know we have tools for looking at page performance as a whole. It seems fairly new to me to have tools for specifically analyzing and demonstrating how we could have done better with images specifically. That's what this Page Weight tool is.

Clearly this is a marketing tool for them. You put in a URL, and it tells you how you could have done better, and specifically how imgix can help do that. I'm generally a fan of that. Tools with businesses behind them have the resources and motivation to stick around and get better. But as ever, something to be aware of.

I asked Brit Morgan some questions about it.

As we can see checking out the homepage for Page Weight, you drop in a URL, it analyzes all the images and gives you some information about how much more performant they could have been. What's going on behind the scenes there?

We run each image on the page through imgix, resizing to fit the image container as best we can tell, and also transform file formats, color palettes, and quality breakpoints to determine which combination provides the best size savings. Then we display that version for each image.

I see it suggests fitting the image to the container, but that only makes sense for 1x displays right? Images need to be larger than their display pixel size for visual crispness on high-density display.

Definitely. The Page Weight tool does not currently address high-DPR display differences, but our service does. We offer automated high-DPR support via Client Hints, and manually via our dpr parameter, which allows developers to set the desired value directly (useful on its own or as a fallback for Client Hint support in browsers that don't yet support that functionality). Our imgix.js front-end library also generates a comprehensive srcset (based on the defined sizes) to address whatever size/DPR the device requires.

I think most developers here are smart enough to realize this is really smart marketing for imgix. But also smart enough to realize the images are a huge deal in web performance and want to do better. What can imgix do that a developer on their own can't do? Or that is fairly impractical for a developer to do on their own?

First, it is important to note that resizing is not the only thing that imgix does, although it is a very common use case. We provide over 100 different processing parameters that enable developers to do everything from context-aware cropping to color space handling to image compositing. So adopting imgix gives a developer access to a lot of image handling flexibility without a lot of hassle, even if they’re primarily using it to support responsive design.

That said, it is not impossible to get a very simple resizing solution running on your own, and many developers start out there. Usually, this takes the form of some kind of batch script based on ImageMagick or Pillow or some other image manipulation library that creates derivative images for the different breakpoints.

For a while, that's often sufficient. But once your image library gets beyond a few hundred images, batch-based systems begin to break down in various ways. Visibility, error handling, image catalog cleaning, and adding support for new formats and devices are all things that get much harder at scale. Very large sites and sites where content changes constantly will often end up spending significant dev time on these kinds of maintenance tasks.

So really, "could you build this?" is a less useful question than "should you build this?" In other words, is image processing central enough to the value proposition of what you're building that you're willing to spend time and effort maintaining your own system to handle it? Usually, the answer is no. Most developers would rather focus on the what's important and leave images to something like imgix — a robust, scaleable system that just works.

Does the tool look at responsive images syntax in HTML? As in, which image was actually downloaded according to the srcset/sizes or picture element rules?

Not yet. That's a feature we're hoping to implement in the next version of the tool.

Can you share implementations of imgix that are particularly impressive or creative?

An interesting use we see more and more is image processing for social media. These days, many sites see the majority of their traffic coming in through social, which makes it more important than ever to make content look good in the feed. Setting OpenGraph tags is a start, but every social network has a different container size. This creates a similar problem to the one posed by mobile fragmentation, and we can help by dynamically generating social images for each network. This provides a polished presentation without adding a ton of overhead for the person maintaining the site.

Other customers are pushing even further by combining several images to create a custom presentation for social. HomeChef, a meal delivery service, does this to dynamically create polished, branded images for Pinterest from their ingredient photos.

We actually created an open source tool called Motif (GitHub Repo) to make it easier for developers to get started with dynamically generating social images through imgix.

(An Interview About) imgix Page Weight is a post from CSS-Tricks

The gig economy as student jobs

QuirksBlog - Tue, 08/15/2017 - 3:19am

For years, whenever I thought about the gig economy, I noted to myself that gigs are great for students, who like to be flexible with their time and don't need a lot of money, but not so great for others.

This is not a particularly original thought, so I didn’t pursue it any futher. That’s why it took me until last Sunday to realise that gig jobs being the same as student jobs is not at all a coincidence.

Picture a typical Silicon Valley start-up. Now ask yourself what kind of job experience the founders and employees have. They have start-up experience, obviously. Also, many will have gone to university and have taken on the odd student job. But that’s about it for most of them — certainly if they were recruited young.

Not a very diverse job experience. That shows in their products.

Uber drivers; delivery people of various sorts; warehouse employees; the average start-up employee sees clearly that these are not highly-paid professionals. Not like them, in other words.

So what are they instead? Given the start-up employee’s limited job experience, there’s only one possible answer. They are what the employee was when he was a student: flexible; doesn’t need a lot of money; happy with whatever he can find, and with great expectations ahead.

Besides, wasn’t their student time the most wonderful time of their lives? Consequence-free frolicking with the certainty of a well-paid job at the end? And weren’t these odd student jobs that they took on great for the people they met and the experiences they lived through? Let’s give others a chance to live our charmed life as well! That’s our mission! Aren’t we wonderful?

And why stop at start-ups? Don’t managers of big companies, who fire their lower-educated workers and hire them back as “freelancers” suffer from the same lack of real-world job experience?

Just a thought on a rainy Tuesday.

Using ES2017 Async Functions

Css Tricks - Mon, 08/14/2017 - 2:23am

ES2017 was finalized in June, and with it came wide support for my new favorite JavaScript feature: async functions! If you've ever struggled with reasoning about asynchronous JavaScript, this is for you. If you haven't, then, well, you're probably a super-genius.

Async functions more or less let you write sequenced JavaScript code, without wrapping all your logic in callbacks, generators, or promises. Consider this:

function logger() { let data = fetch('http://sampleapi.com/posts') console.log(data) } logger()

This code doesn't do what you expect. If you've built anything in JS, you probably know why.

But this code does do what you'd expect.

async function logger() { let data = await fetch('http:sampleapi.com/posts') console.log(data) } logger()

That intuitive (and pretty) code works, and its only two additional words!

Async JavaScript before ES6

Before we dive into async and await, it's important that you understand promises. And to appreciate promises, we need go back one more step to just plain ol' callbacks.

Promises were introduced in ES6, and made great improvements to writing asynchronous code in JavaScript. No more "callback hell", as it is sometimes affectionately referred to.

A callback is a function that can be passed into a function and called within that function as a response to any event. It's fundamental to JS.

function readFile('file.txt', (data) => { // This is inside the callback function console.log(data) }

That function is simply logging the data from a file, which isn't possible until the file is finished being read. It seems simple, but what if you wanted to read and log five different files in sequence?

Before promises, in order to execute sequential tasks, you would need to nest callbacks, like so:

// This is officially callback hell function combineFiles(file1, file2, file3, printFileCallBack) { let newFileText = '' readFile(string1, (text) => { newFileText += text readFile(string2, (text) => { newFileText += text readFile(string3, (text) => { newFileText += text printFileCallBack(newFileText) } } } }

It hard to reason about and difficult to follow. This doesn't even include error handling for the entirely possible scenario that one of the files doesn't exist.

I Promise it gets better (get it?!)

This is where a Promise can help. A Promise is a way to reason about data that doesn't yet exist, but you know it will. Kyle Simpson, author of You Don't Know JS series, is well known for giving async JavaScript talks. His explanation of promises from this talk is spot on: It's like ordering food a fast-food restaurant.

  1. Order your food.
  2. Pay for your food and receive a ticket with an order number.
  3. Wait for your food.
  4. When your food is ready, they call your ticket number.
  5. Receive the food.

As he points out, you may not be able to eat your food while you're waiting for it, but you can think about it, and you can prepare for it. You can proceed with your day knowing that food is going to come, even if you don't have it yet, because the food has been "promised" to you. That's all a Promise is. An object that represents data that will eventually exist.

readFile(file1) .then((file1-data) => { /* do something */ }) .then((previous-promise-data) => { /* do the next thing */ }) .catch( /* handle errors */ )

That's the promise syntax. Its main benefit is that it allows an intuitive way to chain together sequential events. This basic example is alright, but you can see that we're still using callbacks. Promises are just thin wrappers on callbacks that make it a bit more intuitive.

The (new) Best Way: Async / Await

A couple years ago, async functions made their way into the JavaScript ecosystem. As of last month, its an official feature of the language and widely supported.

The async and await keywords are a thin wrapper built on promises and generators. Essentially, it allows us to "pause" our function anywhere we want, using the await keyword.

async function logger() { // pause until fetch returns let data = await fetch('http://sampleapi.com/posts') console.log(data) }

This code runs and does what you'd want. It logs the data from the API call. If your brain didn't just explode, I don't know how to please you.

The benefit to this is that it's intuitive. You write code the way your brain thinks about it, telling the script to pause where it needs to.

The other advantages are that you can use try and catch in a way that we couldn't with promises:

async function logger () { try { let user_id = await fetch('/api/users/username') let posts = await fetch('/api/`${user_id}`') let object = JSON.parse(user.posts.toString()) console.log(posts) } catch (error) { console.error('Error:', error) } }

This is a contrived example, but it proves a point: catch will catch the error that occurs in any step during the process. There are at least 3 places that the try block could fail, making this by far the cleanest way to handle errors in async code.

We can also use async functions with loops and conditionals without much of a headache:

async function count() { let counter = 1 for (let i = 0; i < 100; i++) { counter += 1 console.log(counter) await sleep(1000) } }

This is a silly example, but that will run how you'd expect and it's easy to read. If you run this in the console, you'll see that the code will pause on the sleep call, and the next loop iteration won't start for one second.

The Nitty Gritty

Now that you're convinced of the beauty of async and await, lets dive into the details:

  • async and await are built on promises. A function that uses async will always itself return a promise. This is important to keep in mind, and probably the biggest "gotcha" you'll run into.
  • When we await, it pauses the function, not the entire code.
  • async and await are non-blocking.
  • You can still use Promise helpers such as Promise.all(). Here's our earlier example: async function logPosts () { try { let user_id = await fetch('/api/users/username') let post_ids = await fetch('/api/posts/<code>${user_id}') let promises = post_ids.map(post_id => { return fetch('/api/posts/${post_id}') } let posts = await Promise.all(promises) console.log(posts) } catch (error) { console.error('Error:', error) } }
  • Await can only be used in functions that have been declared Async.
  • Therefore, you can't use await in the global scope. // throws an error function logger (callBack) { console.log(await callBack) } // works! async function logger () { console.log(await callBack) }
Available now!

The async and await keywords are available in almost every browser as of June 2017. Even better, to ensure your code works everywhere, use Babel to preprocess your JavaScript into and older syntax that older browsers do support.

If you're interested in more of what ES2017 has to offer, you can see a full list of ES2017 features here.

Using ES2017 Async Functions is a post from CSS-Tricks

Long Distance

Css Tricks - Mon, 08/14/2017 - 2:23am

A podcast (turns out to be a 2-parter) from Reply All in which Alex Goldman gets a scam phone call about his iCloud account being compromised. He goes pretty far into investigating it, speaking regularly with the people who run these scams.

Especially resonant for me, as someone who also spoke directly with a hacker who's goal was doing me harm. I've long struggled with thinking rationally about stuff like this.

Direct Link to ArticlePermalink

Long Distance is a post from CSS-Tricks

Crafting Webfont Fallbacks

Css Tricks - Sun, 08/13/2017 - 1:25pm

There is a great bit in here where Glen uses Font Style Matcher to create some CSS for a fallback font that has font-size, line-height, font-weight, letter-spacing, and word-spacing adjusted so perfectly that when the web font does load, the page hardly shifts at all. Like barely noticeable FOUT. Maybe we'll call it FOCST (Flash of Carefully Styled Text).

Direct Link to ArticlePermalink

Crafting Webfont Fallbacks is a post from CSS-Tricks

How do you start a sentence with “npm”?

Css Tricks - Sat, 08/12/2017 - 4:21am

This npm. Asking this question was a fun little journey.

Right on the npm website, the very first sentence starts with "npm", and they do not capitalize it.

That's a pretty good precedent for not capitalizing it. It certainly looks awkward though, which is why I asked the question to begin with. It doesn't feel right to me to start a sentence that way, and I'm sure other some other people would look at it and see a mistake.

Their own documentation forbids capitalization as well:

Straight from Raquel Vélez, an employee:

always npm, even if starting the sentence.

(this is a common question we get a lot :-) )

— Raquel Vélez (@rockbot) August 11, 2017

But!

We don't have to.

Brand name capitalization is always at the discretion of the editor or style guide, so brands like WIRED or LEGO do not have to be capped

— Karen McGrane (@karenmcgrane) August 11, 2017

If you're following the Chicago Manual of Style, they would say:

This makes life difficult, however, for those of us who cannot bear to begin a sentence with a lowercase letter. CMOS forbids so doing (except for names like eBay)—we advise you to rewrite. Some publications simply ignore the preference.

Emphasis mine.

"Rewriting", as in, find a way not to start the sentence with the preferred-lowercase initialism.

Using npm …
The npm package manager …
Thanks to npm …
Anything to keep it capitalized as intended while not breaking basic capitalization &#x1f605;

— Nicolás Bevacqua (@nzgb) August 11, 2017

This advice holds true for other situations/companies as well:

avoid: He said that EBay is where he bought his IPod.
instead, use: He said that eBay is where he bought his iPod.

avoid: eBay is where he bought his iPod.
instead, use: He bought his iPod on eBay.

Or just burn it all down

this is why i don't capitalize ever

— jennmoneydollars (@jennschiffer) August 11, 2017

How do you start a sentence with “npm”? is a post from CSS-Tricks

More CSS Charts, with Grid & Custom Properties

Css Tricks - Fri, 08/11/2017 - 7:54am

I loved Robin's recent post, experimenting with CSS Grid for bar-charts. I've actually been using a similar approach on a client project, building a day-planner with CSS Grid. It's a different use-case, but the same basic technique: using grid layouts to visualize data.

(I recommend reading Robin's article first, since I'm building on top of his chart.)

Robin's approach relies on a large Sass loop to generate 100 potential class-names, even though less than 12 are used in the final chart. In production we'll want something more direct and performant, with better semantics, so I turned to definition lists and CSS Variables (aka Custom Properties) to build my charts.

Here's the final result:

See the Pen Bar chart in CSS grid + variables by Miriam Suzanne (@mirisuzanne) on CodePen.

Let's dig into it!

Markup First

Robin was proposing a conceptual experiment, so he left out many real-life data and accessibility concerns. Since I'm aiming for (fictional) production code, I want to make sure it will be semantic and accessible. I borrowed the year-axis idea from a comment on Robin's charts, and moved everything into a definition list. Each year is associated with a corresponding percentage in the list:

<dl class="chart"> <dt class="date">2000</dt> <dd class="bar">45%</dd> <dt class="date">2001</dt> <dd class="bar">100%</dd> <!-- etc… --> </dl>

There are likely other ways to mark this up accessibly, but a dl seemed clean and clear to me – with all the data and associated pairs available as structured text. By default, this displays year/percentage pairs in a readable format. Now we have to make it beautiful.

Grid Setup

I started from Robin's grid, but my markup requires an extra row for the .date elements. I add that to the end of my grid-template-rows, and place my date/bar elements appropriately:

.chart { display: grid; grid-auto-columns: 1fr; grid-template-rows: repeat(100, 1fr) 1.4rem; grid-column-gap: 5px; } .date { /* fill the bottom row */ grid-row-start: -2; } .bar { /* end before the bottom row */ grid-row-end: -2; }

Normally, I would use auto for that final row, but I needed an explicit height to make the background-grid work properly. Not not worth the trade-off, probably, but I was having fun.

Passing Data to CSS

At this point, CSS has no access to the relevant numbers for styling a chart. We have no hooks for setting individual bars to different heights. Robin's solution involves individual class-names for every bar-value, with a Sass to loop to create custom classes for each value. That works, but we end up with a long list of classes we may never use. Is there a way to pass data into CSS more directly?

The most direct approach might be an inline style:

<dd class="bar" style="grid-row-start: 56">45%</dd>

The start position is the full number of grid lines (one more than the number of rows, or 101 in this case), minus the total value of the given bar: 101 - 45 = 56. That works fine, but now our markup and CSS are tightly coupled. With CSS Variables, we can pass in raw data, and let the CSS decide how it is used:

<dd class="bar" style="--start: 56">45%</dd>

In the CSS we can wire that up to grid-row-start:

.bar { grid-row-start: var(--start); }

We've replaced the class-name loop, and bloated 100-class output, with a single line of dynamic CSS. Variables also remove the danger normally associated with CSS-in-HTML. While an inline property like grid-row-start will be nearly impossible to override from a CSS file, the inline variable can simply be ignored by CSS. There are no specificity/cascade issues to worry about.

Data-Driven Backgrounds

As a bonus, we can do more with the data than simply provide a grid-position – reusing it to style a fallback option, or even adjust the bar colors based on that same data:

.bar { background-image: linear-gradient(to right, green, yellow, orange, red); background-size: 1600% 100%; /* turn the start value into a percentage for position on the gradient */ background-position: calc(var(--start) * 1%) 0; }

I started with a horizontal background gradient from green to yellow, orange, and then red. Then I used background-size to make the gradient much wider than the bar – at least 200% per color (800%). Larger gradient-widths will make the fade less visible, so I went with 1600% to keep it subtle. Finally, using calc() to convert our start position (1-100) into a percentage, I can adjust the background position left-or-right based on the value – showing a different color depending on the percentage.

The background grid is also generated using variables and background-gradients. Sadly, subpixel rounding makes it a bit unreliable, but you can play with the --line-every value to change the level of detail. Take a look around, and see what other improvements you can make!

Adding Scale [without Firefox]

Right now, we're passing in a start position rather than a pure value ("56" for "45%"). That start position is based on an assumption that the overall scale is 100%. In order to make this a more flexible tool, I thought it would be fun to contain all the math, including the scale, inside CSS. Here's what it would look like:

<dl class="chart" style="--scale: 100"> <dt class="date">2000</dt> <dd class="bar" style="--value: 45">45%</dd> <dt class="date">2001</dt> <dd class="bar" style="--value: 100">100%</dd> <!-- etc… --> </dl>

Then we can calculate the --start value in CSS, before applying it.

.bar { --start: calc(var(--scale) + 1 - var(--value)); grid-row-start: var(--start); }

With both the overall scale and individual values in CSS, we can manipulate either one individually. Change the scale to 200%, and watch the chart update accordingly:

See the Pen Bar Chart with Sale - no firefox by Miriam Suzanne (@mirisuzanne) on CodePen.

Both Chrome and Safari handle it beautifully, but Firefox seems unhappy about calc values in grid-positioning. I imagine they'll get it fixed eventually. For now, we'll just have to leave some calculations out of our CSS.

Sad, but we'll get used to it. &#x1f609;

There is much more we could do, providing fallbacks for older browsers – but I do think this is a viable option with potential to be accessible, semantic, performant, and beautiful. Thanks for starting that conversation, Robin!

More CSS Charts, with Grid & Custom Properties is a post from CSS-Tricks

CSS Utility Classes and “Separation of Concerns”

Css Tricks - Fri, 08/11/2017 - 4:31am

Adam Wathan takes us on a journey through the different ways we can approach HTML and CSS. This is a really great read that I bet will resonate with a lot of you, whether or not you agree with where he ends up.

Here's a particularly interesting bit where he specifically calls out "separation of concerns" as being a straw man:

You either have separation of concerns (good!), or you don't (bad!).This is not the right way to think about HTML and CSS.

Instead, think about dependency direction. There are two ways you can write HTML and CSS:

CSS that depends on HTML ... In this model, your HTML is restyleable, but your CSS is not reusable.

HTML that depends on CSS ... In this model, your CSS is reusable, but your HTML is not restyleable.

It occurs to me that there are fairly large contingents heading in both directions with styling. One direction is headed toward tightly coupled CSS (i.e. `.vue` files with scoped styles living right next to the template HTML). The other direction is styling classes that are completely de-coupled from HTML (i.e. atomic CSS).

What seems to be least popular is loosely-coupled global styles.

Direct Link to ArticlePermalink

CSS Utility Classes and “Separation of Concerns” is a post from CSS-Tricks

Improving Conversations using the Perspective API

Css Tricks - Fri, 08/11/2017 - 3:30am

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 is a post from CSS-Tricks

“Combine the transparency of a PNG with the compression of a JPG”

Css Tricks - Thu, 08/10/2017 - 5:42am

JPG doesn't support alpha transparency. PNGs that do support alpha transparency don't compress nearly as well as JPG. SVG has masks and clipping paths, which we can use to our advantage here.

Direct Link to ArticlePermalink

“Combine the transparency of a PNG with the compression of a JPG” is a post from CSS-Tricks

?The #1 Website for Coding Challenges

Css Tricks - Thu, 08/10/2017 - 3:10am

Coderbyte is a web application built to help you practice programming and improve your algorithm skills. We offer a collection of coding challenges and web development courses that can help you prepare for an upcoming job interview or general technical assessment. All of our coding challenges can be completed directly in our online editor, and if you need help along the way you can view thousands of user-submitted code solutions as well.

View all Coding Challenges

Direct Link to ArticlePermalink

?The #1 Website for Coding Challenges is a post from CSS-Tricks

The Difference Between Explicit and Implicit Grids

Css Tricks - Thu, 08/10/2017 - 3:09am

Grid Layout finally gives us the ability to define grids in CSS and place items into grid cells. This on its own is great, but the fact that we don't have to specify each track and we don't have to place every item manually makes the new module even better. Grids are flexible enough to adapt to their items.

This is all handled by the so called explicit and implicit grid.

All code samples in this post are accompanied by images in order to display grid lines and tracks. If you want to tinker with the code yourself, I recommend you download Firefox Nightly because it currently has the best DevTools to debug grids.

Explicit Grids

We can define a fixed number of lines and tracks that form a grid by using the properties grid-template-rows, grid-template-columns, and grid-template-areas. This manually defined grid is called the explicit grid.

An explicit grid with 4 vertical tracks (columns) and 2 horizontal tracks (rows). (View Pen) .grid { display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; grid-template-rows: 100px 100px; grid-gap: 20px; } Repeating tracks

When we define grid-template-columns: 1fr 1fr 1fr 1fr; we get four vertical tracks each with a width of 1fr. We can automate that by using the repeat() notation like so grid-template-columns: repeat(4, 1fr);. The first argument specifies the number of repetitions, the second a track list, which is repeated that number of times.

A track list? Yes, you can actually repeat multiple tracks.

See the Pen CSS Grid Layout: Repeating track lists by Manuel Matuzovic (@matuzo) on CodePen.

Automatic repetition of tracks An explicit grid with 4 vertical tracks each 100px wide, generated by the repeat notation. (View Pen)

The repeat notation is quite useful, but it can be automated even further. Instead of setting a fixed number of repetitions we can use the auto-fill and auto-fit keywords.

Auto-filling tracks

The auto-fill keyword creates as many tracks as fit into the grid container without causing the grid to overflow it.

Repetition of as many vertical tracks with a width of 100px as fit into the grid container. (View Pen) .grid { display: grid; grid-template-columns: repeat(auto-fill, 100px); grid-gap: 20px; }

Note that repeat(auto-fill, 1fr); will only create one track because a single track with a width of 1fr already fills the whole grid container.

Auto-fitting tracks

The auto-fit keyword behaves the same way as auto-fill, except that after grid item placement it will only create as many tracks as needed and any empty repeated track collapses.

.grid { display: grid; grid-template-columns: repeat(auto-fit, 100px); grid-gap: 20px; }

In the example used in this section the grid will look the same with repeat(auto-fit, 100px); and repeat(4, 100px);. The difference is visible when there are more than 4 grid items.

If there are more items, auto-fit creates more columns.

The repeat notation with the auto-fit keyword creates as many tracks as needed and as many as fit into the grid container. (View Pen)

On the other hand, if a fixed number of vertical tracks is used in the repeat notation and the number of items exceeds this value more rows are added. You can read more about that in the next section: implicit grids.

If there are more items than vertical tracks, more rows are added. (View Pen)

I've used grid-template-columns in the above examples out of convenience, but all rules also apply to grid-template-rows.

.grid { display: grid; grid-template-columns: repeat(auto-fill, 100px); grid-template-rows: repeat(auto-fill, 100px); grid-gap: 20px; height: 100%; } html, body { height: 100%; } The repeat notation with the auto-fill keyword on both axes. (View Pen) Implicit Grids

If there are more grid items than cells in the grid or when a grid item is placed outside of the explicit grid, the grid container automatically generates grid tracks by adding grid lines to the grid. The explicit grid together with these additional implicit tracks and lines forms the so called implicit grid.

Two items placed outside of the explicit grid causing the creation of implicit lines and tracks (View Pen) .item:first-child { grid-column-start: -1; } .item:nth-child(2) { grid-row-start: 4; }

The widths and heights of the implicit tracks are set automatically. They are only big enough to fit the placed grid items, but it's possible to change this default behavior.

Sizing implicit tracks

The grid-auto-rows and grid-auto-columns properties give us control over the size of implicit tracks.

.grid { display: grid; grid-template-columns: repeat(4, 1fr); grid-template-rows: 100px 100px; grid-gap: 20px; grid-auto-columns: 200px; grid-auto-rows: 60px; }

Implicit tracks will now always have a width of 200px and a height of 60px, no matter if the grid item fits or not.

Fixed widths and heights for implicit tracks (View in CodePen)

You can make sized implicit tracks more flexible by specifying a range using the minmax() notation.

.grid { grid-auto-columns: minmax(200px, auto); grid-auto-rows: minmax(60px, auto); }

Implicit tracks are now at least 200px wide and 60px high, but will expand if the content demands it.

Extending the grid to the start

Implicit tracks may not just be added to the end of the explicit grid. It may also happen that the explicit grid needs to be extended to the start.

An implicit grid extended by one row and one column to the start (View Pen) .item:first-child { grid-row-end: 2; grid-row-start: span 2; } .item:nth-child(2) { grid-column-end: 2; grid-column-start: span 2; }

Each item ends on the second line and spans 2 cells (one vertically, the other horizontally). Since there is only one cell before the second line respectively another implicit track is added to the grid at the start of each side.

Automatic Placement

As already mentioned, implicit tracks are also added if the number of items exceeds the number of cells. By default, the auto-placement algorithm places items by filling each row consecutively, adding new rows as necessary. We can specify how auto-placed items get flowed into the grid by using the grid-auto-flow property.

Instead of rows, new columns are added if the number of items exceeds the number of cells (View in CodePen) .grid { display: grid; grid-template-columns: repeat(4, 1fr); grid-template-rows: 100px 100px; grid-gap: 20px; grid-auto-flow: column; }

Instead of rows, columns are being filled with items and additional implicit columns are created.

Not defining an explicit grid

Due to the fact that it's possible to automatically size cells using grid-auto-rows and grid-auto-columns it's not obligatory to define an explicit grid.

An implicit grid without explicit lines and tracks (View in CodePen) .grid { display: grid; grid-auto-columns: minmax(60px, 200px); grid-auto-rows: 60px; grid-gap: 20px; } .item:first-child { grid-row: span 2; } .item:nth-child(2) { grid-column: 1 / span 2; } .item:nth-child(5) { grid-column: 3; }

Relying solely on the implicit grid can get confusing and difficult to understand in combination with explicit placement. In this example the first item is placed auto and spans 2 rows, the second item is placed explicitly in the first column and spans 2 columns creating a second vertical track. The third and fourth item would actually both be placed automatically in the fourth row, but the fifth item is placed explicitly in the previously non-existent third column. This creates a third vertical track and due to Grids auto-placement, the third item moves up a row to fill the space.

Conclusion

This article doesn't cover everything there is to know about the explicit and implicit grid, but it should give you more than a solid understanding of the concept. Knowing why and how implicit lines and tracks a created is vital for working with Grid Layout.

You can find all the examples used in this article in a Collection on CodePen.

If you want to learn more about Grids check out The Complete Guide to Grid, Getting Started with CSS Grid, Grid By Example and A Collection of Interesting Facts about CSS Grid Layout.

The Difference Between Explicit and Implicit Grids is a post from CSS-Tricks

abovethefold.fyi

Css Tricks - Wed, 08/09/2017 - 11:28am

Novelty site with a message (and reference articles) by Rob Lafratta.

Direct Link to ArticlePermalink

abovethefold.fyi is a post from CSS-Tricks

The Fall Of The Feedback Widget, And What You Should Replace It With [Sponsored]

Usability Geek - Wed, 08/09/2017 - 9:26am
Customer sentiment is an important metric for any online product team to track. An overall negative sentiment likely means you’re hemorrhaging customers, forcing them to flee to a friendlier...
Categories: Web Standards

Removing that ugly :focus ring (and keeping it too)

Css Tricks - Wed, 08/09/2017 - 3:02am

David Gilbertson:

Removing the focus outline is like removing the wheelchair ramp from a school because it doesn't fit in with the aesthetic.

So David shows how you can remove it unless you detect that the user is tabbing, then show it. Essentially you add "user-is-tabbing" class to the body when you detect the tabbing, and use that class to remove the focus styles if it's not there (plus handle the edge cases).

Direct Link to ArticlePermalink

Removing that ugly :focus ring (and keeping it too) is a post from CSS-Tricks

The Best Way to Implement a “Wrapper” in CSS

Css Tricks - Wed, 08/09/2017 - 2:20am

Sometimes the first bit of HTML we write in a new document is an element that wraps everything else on the page. The term wrapper is common for that. We give it a class, and that class is responsible for encapsulating all visual elements on the page.

I've always struggled to with the best way to implement it. I found a related thread on StackOverflow that has more than 250,000 views, so obviously I'm not the only one wondering! I'll sum up my latest thoughts in this article.

Before we dive into it, let's first examine the difference between the "wrapper" and the "container".

"Wrapper" vs "Container"

I believe there is a difference between wrapper and container elements.

In programming languages, the word container is generally used for structures that can contain more than one element. A wrapper, on the other hand, is something that wraps around a single object to provide more functionality and interface to it.

So, in my opinion, it makes sense to have two different names because they intend different functions.

Speaking of the wrapper, it's common to think of a <div> that contains all the rest of the HTML of the document. I'm sure many of us have lived through a time where we set that to 960px in width and center aligned all our main content. Wrappers are also used for things like applying a sticky footer.

The container, on the other hand, usually intends another kind of containment. One that sometimes necessary to implement a behavior or styling of multiple components. It serves the purpose of grouping elements both semantically and visually. As an example, Bootstrap has "container classes" that house their grid system or contain various other components.

The terms wrapper and container can also mean the same thing depending on the developer and what they intend. There might be other conventions too, so the best advice is usually to implement whatever makes the most sense to you. But remember, naming is one of the most fundamental and important parts of developer activities. Naming conventions make our code more readable and predictable. Choose carefully!

Here's an example of a general page wrapper:

/** * 1. Centers the content. Yes, it's a bit opinionated. * 2. See the "width vs max-width" section * 3. See the "Additional Padding" section */ .wrapper { margin-right: auto; /* 1 */ margin-left: auto; /* 1 */ max-width: 960px; /* 2 */ padding-right: 10px; /* 3 */ padding-left: 10px; /* 3 */ } width vs max-width

Setting the width of a block-level element will prevent it from stretching out to the edges of its container (good for things like readable line lengths). Therefore, the wrapper element will take up the specified width. The problem occurs when the browser window is narrower than the specific width of the wrapper. That will trigger a horizontal scrollbar, which is almost always undesirable.

Using max-width instead, in this situation, is better for narrower browser windows. This is important when making a site usable on small devices. Here's a good example showcasing the problem.

See the Pen CSS-Tricks: The Best Way to Implement a CSS Wrapper by Kaloyan Kosev (@superKalo) on CodePen.

In terms of responsiveness, max-width is the better choice!

Additional Padding

I've seen a lot of developers forget one particular edge case. Let's say we have a wrapper with max-width set to 980px. The edge case appears when the user's device screen width is exactly 980px. The content then will exactly glue to the edges of the screen with no breathing room left.

The "no breathing room left" problem.

We usually want a bit of padding on the edges. That's why if I need to implement a wrapper with a total width of 980px, I'd do it like so:

.wrapper { max-width: 960px; /* 20px smaller, to fit the paddings on the sides */ padding-right: 10px; padding-left: 10px; /* ... */ }

Therefore, that's why adding padding-left and padding-right to your wrapper might be a good idea, especially on mobile.

Or, consider using box-sizing so that the padding doesn't change the overall width at all.

Which HTML Element to Choose

A wrapper has no semantic meaning. It simply holds all visual elements and content on the page. It's just a generic container. In terms of semantics, <div> is the best choice. The <div> also has no semantic meaning and it just a generic container.

One might wonder if maybe a <section> element could fit this purpose. However, here's what the W3C spec says:

The <section> element is not a generic container element. When an element is needed only for styling purposes or as a convenience for scripting, authors are encouraged to use the div element instead. A general rule is that the section element is appropriate only if the element's contents would be listed explicitly in the document's outline.

The <section> element carries it's own semantics. It represents a thematic grouping of content. The theme of each section should be identified, typically by including a heading (h1-h6 element) as a child of the section element.

Examples of sections would be chapters, the various tabbed pages in a tabbed dialog box, or the numbered sections of a thesis. A Web site's home page could be split into sections for an introduction, news items, and contact information.

It might not seem very obvious at first sight, but yes! The plain ol' <div> fits best for a wrapper!

Using the <body> tag vs. Using an additional <div>

It's worth mentioning that there will be some instances where one could use the <body> element as a wrapper. The following implementation will work perfectly fine:

body { margin-right: auto; margin-left: auto; max-width: 960px; padding-right: 10px; padding-left: 10px; }

And it will result in one less element in your markup because you can drop that unnecessary wrapper <div> this way.

However, I wouldn't recommend this, due to flexibility and resilience to changes. Imagine if on a later stage of the project any of these scenarios happen:

  • You need to enforce a footer to "stick" to the end of the document (bottom of the viewport when the document is short). Even if you can use the most modern way to do it - with flexbox, you need an additional wrapper <div>.
  • You need to set the background-color of the whole page. Since the body tag has a max-width, it won't be possible to use it. But then you could set the background color with the <html> tag instead of the <body> tag. Well, I don't know about that. I've read the HTML vs Body in CSS and it just sounds a bit uncommon to me. However, I haven't heard any issues or difficulties raised due to applying a background color to the <html> element. But it still does sound a bit weird, doesn't it?

I would conclude it is still best practice to have an additional <div> for implementing a CSS wrapper. This way if spec requirements change later on you don't have to add the wrapper later and deal with moving the styles around. After all, we're only talking about one extra DOM element.

The Best Way to Implement a “Wrapper” in CSS is a post from CSS-Tricks

Browser Compatibility for CSS Grid Layouts with Simple Sass Mixins

Css Tricks - Tue, 08/08/2017 - 1:54am

According to an article from A List Apart about CSS Grid, a "new era in digital design is dawning right now." With Flexbox and Grid, we have the ability to create layouts that used to be extremely difficult to implement, if not impossible. There's an entirely new system available for creating layouts, especially with Grid. However, as with most web technologies, browser support is always something of an issue. Let's look at how we can make the fundamental aspects of Grid work across the browsers that support it, including older versions of Internet Explorer that supported an older and slightly different version of Grid.

The Sales Pitch

If you visit caniuse.com, you'll see that CSS Grid is supported in current versions of all major browsers except Opera Mini. So why not start using it? Rachel Andrew wrote extensively on the subject of if it's "safe to use" or not. It is, assuming you're OK with a fallback scenario that doesn't replicate exactly what Grid can do:

If your website really needs to look the same in all browsers (whatever that means to you), you won't be able to use any features only available by using Grid. In that case, don't use Grid Layout! Use Grid Layout if you want to achieve something that you can't do in a good way with older technologies.

If you'd like to get started learning Grid, Jen Simmons has a nice collection of links and there is a reference guide here.

Grid is a major change to CSS layout. It's powerful, (fairly) easy to use, and, if you're working on open source or on a team, easy for fellow developers to read. In this article, we're going to look at how we can write Grid code to be as compatible as we can possibly make it, including some degree of fallback.

The Sauce

The main thing we want to do in this post is address browser compatibility for the fundamentals of CSS Grid. We'll cover how to construct a parent grid element and place child grid elements within.

The outliers are going to be Internet Explorer and Edge. Edge is just starting to ship the modern CSS grid syntax, and it's an evergreen browser so ultimately we won't have to worry about Edge too much, but at the time of this writing, it matters. IE 10 and 11 are rather locked in time, and both support the old syntax.

Again, Rachel Andrew has information on this old syntax, what it supports, and how to use it. The old syntax, for example, doesn't support display: grid;, we have to do display: -ms-grid;. And there are similarly prefixed for many of the properties.

Even then, many of the properties themselves are not the same. But it's okay. The differences are not that great and we can get some help from Sass. The saving grace here is that we only need vendor prefixes for IE/Edge. Any other browsers will be addressed by the "standard" properties.

First, let's define a Grid parent using a Sass @mixin:

@mixin display-grid { display: -ms-grid; display: grid; } .grid-parent { @include display-grid; }

Here's a demo of that, which also defines and lays out a simple grid:

See the Pen CSS Grid Demo 1 by Farley (@farleykreynolds) on CodePen.

This is helpful, but the grid itself isn't yet compatible with the old CSS Grid style.

Next we need to cover the differences in defining the grid parent rows and columns. Let's beef up the grid a little bit and define it to be compatible across all browsers (for example, by using -ms-grid-columns in addition to grid-template-columns):

See the Pen CSS Grid Demo 2 by Farley (@farleykreynolds) on CodePen.

The -ms- prefixed properties will work for IE/Edge and the non-prefixed properties will work for other grid-supporting browsers. This particular demo will give us the following dimensions:

  • One column 150px wide.
  • One column that takes up all available space left over by the other columns (1fr = 1 fraction of the remaining space after other elements are calculated).
  • Two columns at 100px wide apiece.
  • Three rows at 1fr tall apiece.

Check out the value for grouping on line 19. IE and Edge don't have syntax for grouping a set of rows or columns that are all the same dimensions. We can accomplish the same thing in any other browser using the repeat() function:

repeat([number of columns or rows], [width of columns or height of rows])

No vendor prefix can help us here, we'll need to write out each column manually with the old syntax.

Now we have a grid that's compatible across all browsers, but we still need to address the grid children. The following Pen illustrates how they can be made compatible:

See the Pen CSS Grid Demo 2.5 by Farley (@farleykreynolds) on CodePen.

Here's the mixin for placing the grid items in both the old and new syntax:

@mixin grid-child ($col-start, $col-end, $row-start, $row-end) { -ms-grid-column: $col-start; -ms-grid-column-span: $col-end - $col-start; -ms-grid-row: $row-start; -ms-grid-row-span: $row-end - $row-start; grid-column: #{$col-start}/#{$col-end}; grid-row: #{$row-start}/#{$row-end}; } .child { @include grid-child(2, 3, 2, 3); }

Here's what you need to know about the difference between properties for the grid children in different browsers. In most browsers, you define a grid child by the grid lines where it starts and ends. Grid lines are the lines that exist between the columns and rows you've defined. The syntax looks like this:

grid-column-start: 3; grid-column-end: 5; /* or the shorthand version: */ grid-column: 3 / 5;

This element will span column lines 3 through 5 in your grid.

In IE and Edge, you define a grid child by the line it starts on and how many rows or columns it spans (There is no shorthand version as in the previous example). The syntax looks like this:

-ms-grid-column: 3; -ms-grid-column-span: 2;

This element will start on line 3 and span 2 columns. The two code snippets above will effectively create the same element. Notice that 5 - 3 (from the first snippet) is 2, which is the column span from the IE/Edge example. This allows us to do some quick math in our @mixin and get all the needed information from four numbers. The subtraction on lines 17 and 19 sets your span number for IE/Edge.

Using @include grid-element; allows you to define a grid child for any browser using only four numbers: The column start and end, and row start and end.

So now we've got grid parents and children that work for all browsers.

Drawbacks and Fallbacks

It's an unfortunate reality that not all browsers support CSS Grid, and that the old syntax doesn't support everything from the modern syntax. For example, grid-gap and grid-auto-rows or grid-auto-columns are very useful properties in the modern syntax that there isn't any equivalent to in the old syntax.

Sometimes you can use the @supports for help. @supports works a bit like a media query, where if it matches the CSS inside it applies.

This can get very tricky though, as @supports is not supported in IE. This kind of creates a puzzle when you want to use features like grid-auto-rows or grid-gap to automate portions of the layout, as you have three scenarios now: modern grid support, old grid support with @supports, old grid support without @supports.

@supports (display: -ms-grid) { /* This will apply in Edge (old syntax), but not IE 10 or 11 */ }

For the old syntax, you'll have to place your grid children and set their margins explicitly so the CSS is recognized by IE, which would nullify the need for auto placement or grid-gap in other browsers.

The following Pen is kind of a hodge-podge, due to the issue of @supports compatibility. You can see how grid-auto-rows works, and how to set the gaps for IE/Edge where grid-gap won't work. Again, if you have to support IE, the need for explicitly setting values may nullify the need for properties that set layout styles automatically.

See the Pen Grid Demo 3 by Farley (@farleykreynolds) on CodePen.

The grid-auto-rows property will automatically generate successive rows of a specified height as the columns fill up. You can play with it in the Pen by adding more divs. Another row will be added each time you increase the number of divs past a multiple of three (the number of columns).

The grid-gap property essentially turns the lines between your grid children into gutters. You can set its value using all the usual CSS size units like rems, ems, pixels, etc. In the demo above, the properties involving nth-child set margins that replicate the gutter effect of grid-gap for IE and Edge. It's not that difficult to account for with simple grids but you can see how it could get out of hand quickly with more advanced or flexible grids.

These two properties and others can be used as a basis for some very powerful layouts. If you're responsible for supporting IE and Edge, it will come down to a judgment call on whether or not your project warrants the code it takes to account for them. It's also true that sites don't necessarily have to look the same in all browsers. And because grid layouts are so easy to construct, they're probably worth the extra time.

I think it's worth taking some time to consider whether your project would benefit from CSS Grid and use the @supports rule if it would.

Conclusion

CSS Grid is changing the way layouts on the web are constructed and how they work. Browser support is probably always going to be an issue for web technologies but the saving grace here is that it's really not that bad for CSS Grid. And even then, the differences are easily accounted for in our code. CSS Grid layouts are awesome and powerful and with a little convenience help from tools like Sass, they can also be compatible.

Browser Compatibility for CSS Grid Layouts with Simple Sass Mixins is a post from CSS-Tricks

UX Case Study: Duolingo

Usability Geek - Mon, 08/07/2017 - 12:47pm
In a blogosphere full of articles honing in on single, often granular components of user experience design, Codal‘s UX Case Study aims to examine UX from a holistic perspective, panning the...
Categories: Web Standards

The Evolution of Trust

Css Tricks - Mon, 08/07/2017 - 2:31am

Nicky Case's games are a damn treasure in this world. Most importantly, they are fun and compelling to play. They also make gameplay the vehicle for education on tricky, intricate, and important issues. Issues that would be much harder to learn about by just reading. They are also a masterclass in design: clear calls to action, clear onboarding, meaningful interactions and animations, and good copy.

This latest one is no different.

Direct Link to ArticlePermalink

The Evolution of Trust is a post from CSS-Tricks

Syndicate content
©2003 - Present Akamai Design & Development.