How to Deal with Confrontational Feedback

Sadly, some clients can be just as sensitive, and triggered as online trolls; You recommend an update to a logo they designed themselves (even though you didn’t know that at the time); You disagree with their suggestion that paying for backlinks is a legit marketing strategy; You ask them to clarify what “I don’t like it” means in response to the mockups you delivered.

Before you know it, they’re providing irrelevant feedback, slinging insults at you, or poo-pooing every action you take. It’s not really professional or fair, but it is what it is, and now you’re left having to deal with it.

I recently had someone respond to an article I wrote, calling the design concept I was proposing “evil” and “s***”. It’s not like that hasn’t happened before. I’ve had my content written off as “stupid”, “pointless”, and once had someone feel the need to tell me they wouldn’t read an article I wrote because dating apps (the subject of the piece) only cater to “the attractive 1% of the population”. It’s not just trolls that will direct illogical and unfair criticisms at creatives either. I managed web design and marketing projects for years, and encountered a number of clients who were more than happy to personally attack our team, or voice unhelpful feedback when they weren’t satisfied.

So, today, I want to look at how web designers, and creatives in general, can more effectively handle unfair or unhelpful criticism of their work. Wherever your unfair or negative criticisms come from, keep the following in mind before you respond to any of them:

1. Understand Where the Negativity Comes From

Let’s be honest: People are super sensitive these days and it’s very easy to “trigger” highly emotional and volatile responses.

Social media is partially to blame for this as it makes it easy for people to hide behind their screens and avatars as they spew hate speech, abusive comments, and generally try to stir up trouble.

You also have to consider the state of the world — on a global scale as well as the personal worlds we build for ourselves. I think it’s a lot easier for some people to nitpick about something trite or something that goes against their personal beliefs than it is to deal with real problems in the world.

This doesn’t justify or excuse any unfair comments made of your work. But it helps to understand where the underlying anger, jealousy, or nastiness is coming from.

2. Remove Yourself from the Criticism

When people make comments about you or your work that’s irrelevant, illogical, or mean-spirited, it’s a tricky situation to be in. The same thing goes for clients who give vague, unhelpful, or hurtful feedback like “I could’ve done that myself” or “You don’t get it”.

Unfortunately, the response you have to these kinds of unfair criticisms could end up hurting you in the long run if you:

  • Get defensive and fight back, potentially compromising positive relationships you worked hard to build;
  • Take it personally and let it feed into insecurities you’ve tried to tuck away “Why should anyone pay me to build their website?”;
  • Become fearful of taking risks and pushing boundaries and, thus, become stifled creatively.

Instead of letting your emotions run amok, you need to remove the “you” from the criticism. This goes for any kind of feedback you receive.

You are not the one under the microscope here; it’s your work that’s under scrutiny. If you can take yourself out of the equation, then the response you give becomes less about defending your personal integrity, skills, etc., which makes you more likely to respond calmly and professionally.

3. Choose the Best Response for the Situation

Whether a comment has come from a troll or a bad client, you need to quickly work through your options.

Here’s what you can do to determine the best response:

Is the Argument Clear?

For now, don’t focus on the validity of the argument. You just need to establish if the commenter is thinking clearly or being driven solely by raw emotion.

If the argument is something like “This is stupid” or “This isn’t good”, you know it’s going to be like pulling teeth to get a clear explanation of the grievance. The comment is vague and hurtful for a reason.

If it comes from a troll, it’s probably not worth responding to. They’re just looking for a fight. If it comes from a client, you will need to respond. The best thing to do is to ask, “Why?” Again, you don’t want to get personal. Just focus on getting them to give you specific details or examples of what isn’t good and what they actually want.

Is the Feedback Relevant?

Nothing is more frustrating than putting something out there, only to receive feedback on something else entirely… or something that doesn’t matter.

I recently wrote an article on a controversial subject, so I expected a lot of heated debate around it. There’s absolutely nothing wrong with that. What I hadn’t expected, though, was that someone would try to reduce everything I’d written to a spelling error in the piece.

Now, spelling and grammatical errors happen. When someone is nice enough to call them to my attention, I make sure they’re fixed right away. However, this person wasn’t looking for that. The comment was phrased so as to make my argument seem invalid because of one typo.

This is the kind of commenter that doesn’t want you to say, “Hey, thanks for pointing that out! We fixed the error.” They want you to open up the conversation and give them the floor to point out more issues with what you’ve done.

When you receive this kind of feedback or comment — one made for the sake of meanness or to demonstrate dominance over you — it’s probably best to leave it be. They just want to nitpick for nitpicking’s sake.

Is it Worth Debating?

Let’s say that you’ve received a comment that you don’t agree with, but is valid enough in its own right. Is it worth responding to?

If it sounds as though they genuinely want to have a conversation or healthy debate, then yes, it’s worth responding. That’s part of the reason why art is created: to get people thinking and talking.

Just remember to check yourself before you respond. This isn’t about you. You can certainly disagree with what they have to say and you can bring data to back up your argument, but you don’t need to be defensive.

Here’s what I always remind myself before I engage with someone who has a counter-argument for something I created:

There’s a lot to learn when you step outside the bubble of your work and look at it from someone else’s perspective.

I actually find that responding to feedback and comments makes me better at my job and I think this is something that would be especially helpful for web designers. Why? For starters, it helps me figure out what people like and dislike (especially as the market changes). I also enjoy getting to learn more about other people’s perspectives as it almost always inspires future work of mine.

Wrap-Up

Don’t be afraid to put yourself out there and to take risks in your work. I know it’s hard when one voice comes through so clearly, casting doubt on your body of work or your talent overall. But that’s just one person who’s made up their mind to be mean-spirited or unfair.

If you’re ever feeling down about something someone said and you can’t shake the comment, take a step back and look over the good feedback you’ve received. These are the people who appreciate what you do. And even when the feedback isn’t always positive, they’re the ones who’ve chosen to share feedback that’s productive because they want to help you become better at your job.

These are the people worth listening and responding to.

 

Featured image via Unsplash.

Source
p img {display:inline-block; margin-right:10px;}
.alignleft {float:left;}
p.showcase {clear:both;}
body#browserfriendly p, body#podcast p, div#emailbody p{margin:0;}

Getting Acquainted With Svelte, the New Framework on the Block

For the last six years, Vue, Angular, and React have run the world of front-end component frameworks. Google and Facebook have their own sponsored frameworks, but they might leave a bitter taste for anyone who advocates for an open and unbiased web. Vue is another popular framework that has multiple sponsors, but isn’t run by a single corporation, which may be attractive to some folks.

There’s another player in the framework space that’s gaining attention and operates very much in the same spirit as Vue as far adopting an open MIT license: Svelte.

Svelte has been covered here on CSS-Tricks before, like Ollie Williams’ excellent overview of how it can be used to write more convenient, component-based CSS. This article is going to zoom out a bit and provide a little more context about Svelt, as well as how it differentiates itself from other frameworks, and how to implement it in your own projects.

What makes Svelte different?

I can confidently say that Svelte has been the easiest JavaScript component library to learn and start putting to use in a productive way.

— Jeff Delaney, from Svelte Realtime Todo List with Firebase

OK, so Svelte is a JavaScript component library. But so is React. And Angular. And Vue. What makes Svelte stand out from the bunch?

Svelte is trying to do a few things that are different from the rest:

  1. All the code is compiled ahead of time.
  2. There is no virtual DOM.
  3. CSS scoping is baked in.

Let’s break those down a bit because they significantly distinguish Svelte from other front-end frameworks.

All the code is compiled ahead of time.

Svelte is a compiler, meaning that the code in Svelte files gets converted from an easier-to-write hybrid language that mixes HTML, JavaScript, and CSS into lower-level optimized JavaScript, HTML, and CSS files.

This is very similar to the way C# gets compiled down to bytecode, or how Typescript compiles down to JavaScript. But where traditional compilers tend to go down to one language, Svelte mixes all three.

This makes writing code a lot more flexible, and benefits the client (web browser) as the computation is done when the application is built, not on every browser when the web app is visited.

There is no Virtual DOM.

A DOM (or Document Object Model) is an interface that defines the logical structure of a webpage. It takes HTML and converts it to a structure that can be manipulated and accessed. Chris has a classic post that thoroughly explains it.

The Virtual DOM extends the concept of a DOM by creating a “second” DOM in memory. Like the DOM, this is manipulated and accessed by traditional frameworks (e.g. Angular, Vue, and React). At build, this second “virtual” DOM gets consolidated with the actual DOM, allowing the UI to render.

And what about the Shadow DOM? Well, the Shadow DOM is technically part of the “real” DOM, just in the shadows. As such it is a great tool for isolating chunks of code that don’t leak into or conflict with other elements on the page — a little bit like (but at the same time almost nothing like) an iframe. The shadow DOM is sorta the crux for most component-based front-end frameworks because they leverage the siloed nature of the Shadow DOM to serve specific code to specific elements.

While that isn’t exactly a key selling point of Svelte, it is possible to work with the Shadow DOM experimentally. The Shadow DOM hasn’t really quite caught on in progressive web practices, which is a shame, and probably due to the confusion between drafts and lack of support from IE and Edge.

So, where am I going with all this? The difference between Svelte and other JavaScript frameworks is the lack of a Virtual DOM. That’s important because it contributes to faster apps — faster than frameworks using a Virtual DOM. Yes, the Virtual DOM can be super fast because it only updates parts of the DOM when needed, but as applications grow, the impact of a duplicate DOM stored in memory can have an overall negative impact on performance.

Svelte takes a different approach and does a lot of these heavy calculations at build time. All that heavy lifting in advance, which allows Svelte to surgically insert changes only where needed.

CSS scoping is baked in.

Svelte has built-in styling, which is essential in other modern frameworks. The different between CSS in Svelte and CSS in other frameworks is that Svelte takes the CSS from each component and spits it out to a separate CSS file on build.

A personal gripe I have with most CSS-in-JS approaches is that it seems like an over-engineered solution. Svelte’s approach keeps things lean, vanilla, and encapsulated — while keeping everything where it should be.

For those who love preprocessors, there are plugins, whether it for Sass, Less or Gulp. But since Svelte is still in its infancy, I would recommend using plain ol’ CSS with a minified CSS framework of your choice so you can utilize Svelte’s handy dandy component scoping. 

You could just as easily keep to your usual styling preferences and completely forgo Svelte’s CSS builder. However, I’d argue that is a massive shame, as Svelte’s solution has been extremely clean and enjoyable, at lease in my experience. But anyone who has to work with IE11 (😬) and even older browsers will know that normalizing styles is a must. This is a good place to stop and check out Ollie’s post because he dives much deeper into Svelte’s styling features and advantages.

How Svelte stacks up to other frameworks

We just looked at what how Svelte has a different approach for compiling, interacting with DOM and writing CSS. You might be wondering: how does Svelte compare to other popular frameworks?

There are plenty of comparisons already out there, but suffice to say that Svelte is pretty darn fast. But speed isn’t the only basis for comparison. Instead, let’s do a side-by-side that looks at a broader overview in a format much loved by the development community: a table!

SvelteVueReact Angular (2+)
What is itCompilerFrameworkFrameworkFramework
First CommitNov. 16, 2016 Jul. 29, 2013May 24, 2013Sep. 18, 2014
BackingOpen sourceMultiple SponsorsFacebookGoogle
Community¹SmallLargeMassiveLarge
Satisfaction288%87%89%38%

Svelte is in a strong position considering its late entrance and small community. Developer satisfaction is high, while the big three have been seeing recent declines. The Svelte community is small, but growing, and the code is open source which is a huge plus for the overall web community.

Let’s look at an example of using Svelte

I hope that I have convinced you that Svelte is worth at least a try. If so, let’s fire up the terminal and try a real-world examples of an everyday use case: implementing the Intersection Observer. If you’ve ever run a Lighthouse report, it may have been shouted at you for not using passive scroll events. That may be the most boring sentence I have written in my life, but it’s scores points for performance and isn’t overly complicated to do with the Intersection Observer in Svelte.

Let’s skip all the installation and setup stuff because we can avoid it with REPL, the online editor Svelte uses to demonstrate the framework on its site. The standard “Hello world” boilerplate is in there. Go ahead and download the ZIP file of the app, in the upper-right corner of the screen.

Now, unzip the file and cd into the folder from the terminal and run  npm -i to initialize the project. Once that’s done, do npm run build and you’ll get a copy of your lightweight miniature Svelte “Hello, world!” app.

Now we can get into the actual task of adding the IntersectionObserver.

First, we import the code that has already kindly been written by the Svelte team. It’s in the source code of the svelte.dev git repo (the inner cogs of which make for fascinating reading).

<script> import { onMount } from 'svelte'; export let once = false; export let top = 0; export let bottom = 0; export let left = 0; export let right = 0; let intersecting = false; let container; onMount(() => { if (typeof IntersectionObserver !== 'undefined') { const rootMargin = `${bottom}px ${left}px ${top}px ${right}px`; const observer = new IntersectionObserver(entries => { intersecting = entries[0].isIntersecting; if (intersecting && once) { observer.unobserve(container); } }, { rootMargin }); observer.observe(container); return () => observer.unobserve(container); } function handler() { const bcr = container.getBoundingClientRect(); intersecting = ( (bcr.bottom + bottom) > 0 && (bcr.right + right) > 0 && (bcr.top - top) < window.innerHeight && (bcr.left - left) < window.innerWidth ); if (intersecting && once) { window.removeEventListener('scroll', handler); } } window.addEventListener('scroll', handler); return () => window.removeEventListener('scroll', handler); });
</script> <style> div { width: 100%; height: 100%; }
</style> <div bind:this={container}> <slot {intersecting}></slot>
</div>

Stick this in a file called IntersectionObserver.svelte in a src/components folder. Then, reference it from the main Svelte file: App.svelte.

import IntersectionObserver from "../components/IntersectionObserver.svelte";

Now that we have the Intersection Observer available as a component, we can wrap other elements with it.

<IntersectionObserver let:intersecting top={400}>
 {#if intersecting}
    <section>
      This message will Show if it is intersecting
    </section>
  {:else}
    <section>
      This message won't Show if it is intersecting
    </section>
 {/if}
</IntersectionObserver>

That’s really it! You can see how the Intersection Observer component allows us to use <IntersectionObserver>  like a wrapper and define where the intersection should trigger, which is 400 pixels from the top in this example. As a reminder, this is all being exported as vanilla JavaScript! Super performant, no funny business. We’re sandwiching JavaScript and HTML together which is cool because we can see what the Intersection Observer is directly affecting, leaving no ambiguity and without being penalized for performance.

The OnMount function is necessary to tell Svelte that this code needs to run within the browser, as the Intersection Observer can’t be figured out ahead of time.

We’ll need to add some styling so that we can experience the observer in action, and we can do that directly in your App.svelte file. This might look super familiar if you have worked with any of the other front-end frameworks:

<style>
  .somesection {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100vh;
  }
  
  .somesection.even{
    background: #ccc;
  }
        
  .content{
    text-align: center;
    width: 350px;
  }
</style>

Finally, we can copy and paste our Intersection Observer element four times to create more intersections. That gives us a mini web app that reactively adds and removes content as it comes into view — perfect to use with media, like lazy-loading. Check out a demo of the final result and be sure to crack open DevTools to see the Intersection Observer

Some final thoughts

My personal recommendation is to give Svelte a try. We’ve only scratched the surface of the framework in this article, but having converted my personal website to Svelte, I can confidently say that it is a pleasure to work with. It is performant, has a brilliant VSCode linter, and best of all, is easy to use. It may be small and new on the block, but I have a keen feeling that it is the relief from bloated “Goliath” frameworks, the “David” that frontend-ers have been looking for.

So should you use Svelte in a real project? Comparing risk and reward definitely comes into play. The community is smaller than other frameworks, meaning you’re likely to find less support and fewer tutorials to guide your along. At the same time, Svelte is in its third generation, meaning most of the gremlins should have been driven away, leaving a lean and reliable framework.

As with anything new, common sense rules, try it out with something non-commercial, take it for a spin, and see how you go.

Is there anything else? Funny you should ask! There are two co-projects that live in the Svelte Ecosystem: Sapper and Native. Sapper is a framework that utilizes Svelte for building full web applications, including routing, service workers, and all the good stuff you need to get started. I have used it to rebuild my personal website, and so far, I am a fan. Svelte Native is the most experimental of the Svelte projects, a NativeScript mobile app builder that utilizes Svelte under the hood. I confess that is where my knowledge on the subject ends. Luckily, it has a website with further information.

What do you think? Have you given Svelte a try? Do you think it stacks up to other frameworks? Let’s discuss it in the comments!

  1. Based on a mix of Github Contributions, NPM Downloads and StackOverflow topics
  2. State of JS review 2019

The post Getting Acquainted With Svelte, the New Framework on the Block appeared first on CSS-Tricks.

Django Highlights: User Models And Authentication (Part 1)

Django Highlights: User Models And Authentication (Part 1)

Django Highlights: User Models And Authentication (Part 1)

Philip Kiely

2020-02-06T11:00:00+00:00 2020-02-07T00:09:51+00:00

There are two types of websites: static and dynamic. Django is a framework for developing dynamic websites. While a static website is one that solely presents information, there is no interaction (beyond simple page requests) that gets registered to a server. In a static website, the server sends HTML, CSS, and JavaScript to a client and that’s it. More capabilities require a dynamic website, where the server stores information and responds to user interaction beyond just serving pages. One major reason to develop a dynamic site is to authenticate users and restrict content.

Writing, deploying, and administering a static website is about an order of magnitude easier, cheaper, and more secure than a dynamic site. Thus, you should only create a dynamic website if the dynamic paradigm’s additional capabilities are necessary for your project. Django simplifies and streamlines the process of creating a dynamic site with its built-in components. As one of the primary components of a dynamic web application, the “user account” object, like the wheel, is tempting to re-invent, but the standard shape is appropriate for most uses. Django provides a powerful out-of-the-box user model, and in this article, we’ll walk through the best way to provide secure, intuitive user authentication flows.

Getting Set Up

If you’d like to create your own Django application to experiment with the concepts in this article, you can create a directory (and preferably a virtual environment) and then run the following commands:

pip install django
django-admin startproject PROJECTNAME
cd PROJECTNAME
python manage.py startapp APPNAME
python manage.py migrate
python manage.py runserver

If you’re looking for a walkthrough of creating your first Django project, Django’s own website provides a great one. In this article, we’re not using an example project, but the concepts discussed will apply to almost every Django application.

Standard User Model

Fundamentally, the concept of a user account exists for two reasons: access control and personalized application state. Access control is the idea that resources on a system are only available to some users. Personalized state is heavily dependent on the purpose of the application but may include settings, data, or any other records specific to an individual user. The Django stock User model provides sensible approaches to both use cases.

Initially, there are two types of users in a Django application: superuser accounts and regular users. Superusers have every attribute and privilege of regular accounts, but also have access to the Django admin panel, a powerful application that we’ll explore in detail in a future article. Essentially, superusers can create, edit, or delete any data in the application, including other user accounts.

Fundamentally, the concept of a user account exists for two reasons: access control and personalized application state.

To create a superuser on your own Django application, run:

python manage.py createsuperuser

The other benefit of a user account is storing personalized data to the database. By default, Django only requires a username and password but provides optional fields for users to enter their first name, last name, and email address. You can read a complete model reference on the Django website. We’ll discuss extending this model below.

Security And Reliability

Django includes substantial password management middleware with the user model. User passwords are required to be at least 8 characters, not entirely numbers, not match too closely to the username, and not be on a list of the 20,000 most common passwords. The Django stock forms validate these requirements. When a password is sent to the server, it is encrypted before it is stored, by default using the PBKDF2 algorithm with a SHA256 hash. Overall, the default password system provides robust security without any effort from the developer. Unless you have specific expertise and a compelling reason to change the way passwords are handled in your application, don’t modify this behavior.

One other convenient built-in is the requirement that usernames are unique. If you think about it, if there were two users with the username “djangofan1” and the server received a login request for that username it would not know which user was trying to access the application. Unfortunately for them, the second user to try to register with “djangofan1” will have to pick a different name, perhaps “djangofan2”. This uniqueness constraint is enforced at the database level but is again verified by the forms that Django provides.

Finally, a note on deleting users. While deleting users is a possibility, most complex applications will have a number of resources tied to each user account. If you want to effectively delete a user without removing those attached objects, set the user’s is_active field to false instead. Then, those other resources remain associated with the account rather than being deleted themselves, and the user account is able to be re-activated at any time by a superuser. Note that this does not free up the username, but setting the account as inactive in conjunction with changing the username to a random, unique value could achieve the same effect. Finally, if your site policies or applicable local law require that user accounts be fully deletable, the is_active approach will not be sufficient.

Unless you have specific expertise and a compelling reason to change the way passwords are handled in your application, don’t modify this behavior.

Standard Use

When you want to register a new user account, the view for doing so will probably look something like the following in views.py:

from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login
from django.contrib.auth.forms import UserCreationForm def signup(request): if request.user.is_authenticated: return redirect('/') if request.method == 'POST': form = UserCreationForm(request.POST) if form.is_valid(): form.save() username = form.cleaned_data.get('username') password = form.cleaned_data.get('password1') user = authenticate(username=username, password=password) login(request, user) return redirect('/') else: return render(request, 'signup.html', {'form': form}) else: form = UserCreationForm() return render(request, 'signup.html', {'form': form})

Let’s break that down:

  • If the user is already signed in, we’ll redirect them away from the signup page.
  • If the request method is POST, that means that the form for creating a user has already been filled out and it’s time to create a user.
    • First, construct the form object on the backend with the user-provided data.
    • If the form is valid, create the user and log them in, then send them to the main page.
    • Otherwise, dump them back on the user creation page with information about what data was invalid (for example, they requested a username already in use).
  • Otherwise, the user is accessing the page for the first time and should be met with the form for creating a new account.

Now examining account signin:

from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login
from django.contrib.auth.forms import AuthenticationForm def signin(request): if request.user.is_authenticated: return render(request, 'homepage.html') if request.method == 'POST': username = request.POST['username'] password = request.POST['password'] user = authenticate(request, username=username, password=password) if user is not None: login(request, user) return redirect('/') else: form = AuthenticationForm(request.POST) return render(request, 'signin.html', {'form': form}) else: form = AuthenticationForm() return render(request, 'signin.html', {'form': form})

Another breakdown:

  • If the user is already signed in, we’ll redirect them away from the sign-in page.
  • If the request method is POST, that means that the form for signing in has been filled and it’s time to authenticate the user to an account.
    • First, authenticate the user with the user-provided data
    • If the username and password correspond to an account, log the user in
    • Otherwise, bring them back to the sign-in page with their form information pre-filled
  • Otherwise, the user is accessing the page for the first time and should be met with the form for logging in.

Finally, your users may eventually want to sign out. The basic code for this request is simple:

from django.shortcuts import render, redirect
from django.contrib.auth import logout def signout(request): logout(request) return redirect('/')

Once the user has been logged in to their account, and until they log out on that device, they are having a “session.” During this time, subsequent requests from their browser will be able to access account-only pages. A user can have multiple sessions active at the same time. By default, sessions do not time out.

While a user has an active session on their device, they will register as True for the request.user.is_authenticated check. Another way to restrict pages to logged-in users only is the @login_required decorator above a function. There are multiple other ways of achieving the same, detailed here.

If this level of configuration is more than you want to perform, there is an even more out-of-the-box approach to user management. These stock authentication views provide standard routes, views, and forms for user management and can be modified by assigning them to custom URLs, passing custom templates, or even subclassing the views for more control.

Extending The User Model

Generally, you need to expand the user model to provide finer-grained access controls or store more user data per account. We’ll explore several common cases below.

Dropping Fields From The User Model

Contrary to this section’s header, I do not actually recommend making changes directly to the user model or associated database schema! The generic user model is established in your database by default during the setup of a new Django project. So much is tied to the default user model that changing it could have unexpected effects in your application (especially if you’re using third-party libraries), accordingly, adding or removing fields is not recommended and is not made easy by the framework.

By default, the only two fields that are required for a user are username and password. If you don’t want to use any of the other fields, simply ignore their existence, as a user can be created without a first name, last name, or email address. There is a collection of default fields like last_login and date_joined that can also be ignored if you don’t want them. If you had an actual technical constraint that required dropping optional fields from the user model, you would already know about it and wouldn’t need this article.

A common reason you might want to drop a field in the user model is to drop the username in favor of the email as a unique identifier. In that case, when creating the user from form data or authenticating a request, simply enter the email address as the username in addition to its use in the email field. The username field will still enforce the uniqueness constraint when usernames are formatted as email addresses. Strings are strings, they will be treated the same.

So much is tied to the default user model that changing it could have unexpected effects in your application, especially if you’re using third-party libraries.

Adding Fields With A Profile

Similarly, if you want to store extra information about your users, you should not attempt to modify the default user model. Even for a single field, define your own object with a one-to-one relationship with the existing users. This extra model is often called the Profile. Say you wanted to store a middle name and date of birth (dob) for each user. The Profile would be defined as follows in models.py:

from django.db import models
from django.contrib.auth.models import User class Profile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) middle_name = models.CharField(max_length=30, blank=True) dob = models.DateField(null=True, blank=True)

Custom Access Control

Your application may have required a distinction between different types of users for accessing information and functions. Django provides a system for creating custom fine-grained access control: permissions and groups.

Permissions are objects that determine access to resources. A user can have one or more permissions. For example, they might have read access to a table of products and write access to a table of customers. The exact implementation of permission varies substantially by application but Django’s approach makes it intuitive to define permissions for data and assign those permissions to users.

Applications for enterprise and other large organizations often implement role-based access control. Essentially, a user can have various roles, each of which has certain permissions. Django’s tool for implementing this pattern is the Group. A group can have any number of permissions, which can each be assigned to any number of groups. A user then gains permissions not directly but by their membership to groups, making the application easier to administer.

Overview: Integrating A Payment Provider

The precise process of integrating a payment processor varies substantially by application and provider. However, I’ll cover the process in general terms.

One of the key advantages of outsourcing payments is that the provider is responsible for storing and validating highly regulated data such as credit card information. The service then shares some unique user token with your application along with data about their payment history. Like adding the profile, you can create a model associated with the core User and store the data in that model. As with passwords, it’s important to follow the given integration with the provider to avoid storing sensitive information improperly.

Overview: Social Sign-In

The other broad, complex field I want to briefly touch upon is social sign-in. Large platforms like Facebook and Google, as well as smaller sites like GitHub, provide APIs for using their services to authenticate users. Similar to a payment provider, this creates a record in your database linking an account to an account in their database.

You might want to include social auth in your site to make it easier for users to sign up without creating a new set of login credentials. If your application solely targets customers who already use a specific site that provides a social auth option, it might help you attract users from that market by lowering the barrier of entry. Furthermore, if your application intends to access the user’s data from a third-party service, linking the accounts during authentication simplifies the process of granting permissions.

However, there are downsides to using social auth. API changes or outages from the third party provider could interrupt your application’s uptime or development activities. In general, external dependencies add complexity to the application. Finally, it’s worth considering the data collection and use policies of third parties that you are integrating with and make sure that they align with your and your users’ expectations.

If you do decide that social authentication is right for your application, fortunately, there’s a library for that. Python Social Auth for Django is a package for enabling the capabilities of Python’s social auth ecosystem in Django projects.

Wrapping Up

As universal as the user account is, its widespread use can lead to resolving the same problems needlessly. Hopefully, this article has shown you the range of powerful features available in Django and given you an understanding of the basic responsibilities of an application when creating user accounts.

Django Highlights is a series introducing important concepts of web development in Django. Each article is written as a stand-alone guide to a facet of Django development intended to help front-end developers and designers reach a deeper understanding of “the other half” of the codebase. These articles are mostly constructed to help you gain an understanding of theory and convention but contain some code samples, which are written in Django 3.0. Several concepts that we touched on in this article, including templates, admin users, models, and forms, will be explored individually in detail in future articles in this series.

Smashing Editorial (dm, il)
Browser Version Release Spectrum

Whenever a browser upgrades versions, it’s a little marketing event, and rightly so. Looks like for Firefox it’s about once a month, Chrome is ~6 weeks, and Safari is once a year.

Chrome 80 just dropped, as they say, and we get a video and blog post. What strikes me about releases like this these days is that there isn’t big flagship features that we’re all collectively interested in as developers. Maybe a little great divide stuff going on, but more broadly, just different front-end developers (the people who care about browsers) work on different things and so care about different things.

Let me try to illustrate that with a feature rundown of this release:

  • Content Indexing is a way to access metadata of files your browser has cached (that you specifically cached). Seems like a great idea, but I’ve never really dealt with offline stuff like this seriously so it’s not really in my wheelhouse.
  • Web Workers can use ES modules now. That’s cool, I didn’t even know they couldn’t.
  • Optional chaining in JavaScript, like obj?.name?.first. Love it. Super useful. Probably the feature that most JavaScript developers are applauding. But Chrome is first out of the gate here, so if you’re into it, you’d better be Babel-ing. We also get ?? .
  • Origin Trials are like feature flags I guess, except you opt-in from a website and not just on your local browser. Totally new to me, but sounds like they have been effective in gathering and refining new APIs.
  • Periodic background sync. Jeremy laid out how useful this in his article where he called it “silent push”. You can also schedule notifications, making them more resilient to offline situations.
  • HTTP content tries to auto-upgrade to HTTPS if the site is HTTPS. Great idea. I use the Cloudflare setting for this, but makes sense it moves to the browser level.
  • JavaScript can gzip streams. I understand what gzip is, but feel like this is over my head. There is a bunch of other stuff totally outside my wheelhouse too, like Decoding Encrypted Media and others.
  • Contact Picker API. Very much like this, in the same way I like the Web Payments API. If I can build a UI that helps users fill out forms faster and more accurately, that’s great. That’s why I use 1Password. I use it just as much for filling out address and credit card forms as I do for passwords.
  • SameSite cookies. Scares me. I know that we need to update our cookies on CodePen to make sure it has this value but I haven’t researched it deep enough yet, and 80 is already shipping.
  • Actual CSS stuff! line-break: anywhere; and overflow-wrap: anywhere; I can’t seem to grok the difference. This stuff is already very complicated.
  • Nothing HTML related. Poor HTML never gets nuthin’.

And then the two that really caught my eye!

  • SVG favicons. Awesome. We already ship one for CodePen because it looks so nice on Safari. Although Safari supports it with <link rel="mask-icon" href="..."> and Chrome is supporting it with <link rel="icon" href="..."> so I’m not quite sure what to do there. I guess since Firefox supports SVG with rel="icon", just ship SVG for both?
  • Text fragments. How cool. I had no idea this was coming. People have been talking about this for at least a decade. The idea is linking to things on a page without needing a name or id to link down to, just using text. The syntax is funky:
https://site.com/#:~:text=Links to first occurance of this text.

Here’s a video from Stefan Judis:

https://platform.twitter.com/widgets.js

Especially notable to me: it links you to the middle of the page, not headbutting the top. I much prefer this.

The post Browser Version Release Spectrum appeared first on CSS-Tricks.

3 Lessons UX Designers Can Take From Netflix

If we look at this from a design perspective, there’s definitely something about the way the user experiences are designed that makes them more attractive than other movie or TV viewing options. Especially Netflix.

Today, I want to put the spotlight on Netflix and give you 3 lessons you can take away from the platform’s design and features.

1. Make Onboarding Painless

Obviously, Netflix is a household name, so it doesn’t need to mince words on its website.

While you won’t be able to get away with a navigation-less website, what you can do to emulate the Netflix UX is to deliver just as brief and benefits-driven of a message above-the-fold.

Unlimited movies, TV shows, and more. Watch anywhere. Cancel anytime.

It perfectly sums up what users get while also taking the risk and fear out of it with “Cancel anytime.” Can you do the same? Totally.

While you’re at it, build a shortcut to the conversion point (e.g. newsletter subscription, SaaS purchase, schedule an appointment, etc.) in the same banner. Most of your visitors will need some time to educate themselves, but this will at least shorten the signup process for those who are ready to take action.

When that happens, make sure your conversion funnel is streamlined, too.

In the first step of Netflix’s signup process, it lets customers know how many steps there are while reiterating the benefits. The interface is distraction-free and easy to follow.

Next, users see plan options. Again, the UI is simple and easy to follow. The table comparing the features and value of each plan is a nice touch, too.

The final step is just as minimally designed. With a clean and clear interface, and a benefits-driven message, there’s no reason a user should have any problems getting through this process nor should they have any doubts along the way.

2. Use Your Data to Create a More Personal UX

Every year, it seems like we have a new law that sends web designers and business owners scrambling to strengthen their website privacy and security policies. And while it might feel like we’re losing control over all that big data we’ve gained access to in recent years, that’s not really the case.

What’s happening is that consumers want businesses to more carefully protect their data. Plain and simple.

There’s nothing in these laws that’s telling us to stop collecting user data. If that happened, I think consumers would be just as outraged. Personalization is one of those things consumers actually look for in the user experience — and the better a website can deliver on it, the more loyal they’ll be as customers.

As far as being responsible with user data, that’s up to you and your clients to manage. As for using the data you’re given, Netflix has shown us a number of ways to use only the most necessary data points to create a very personal experience.

First, you need to start collecting data that’ll help you refine the experience. Netflix empowers customers to help with this here:

With each movie or show’s page, users can:

  • Add it to their personal viewing list;
  • Rate it with a thumbs up or thumbs down.

Netflix uses this information to provide helpful recommendations throughout the platform.

The first spot it does this is here:

When customers are rooting around for a new movie or show to watch, this percentage should give them a clue as to how much they’ll like or dislike it. This, in turn, encourages them to rate more programs so that Netflix’s ranking algorithm can become more attuned to their preferences.

The second spot Netflix provides personalized recommendations is the main page. It actually uses this page in a couple of ways to deliver custom suggestions to users.

The first is with “Because You Watched” categories:

If a user spends enough time with a particular product, service, or content on your site, there’s a good chance they’ll like similar ones. So, this is a great way to build those suggestions into the UX.

The other way Netflix uses this page to deliver a personalized experience is through its categories. Note the categories I was shown above:

  • Totally Awesome 80’s;
  • Violent Asian Action;
  • True Bromance.

I have a history of watching movies and shows in these highly specific categories, so it’s pretty awesome to see these aggregated lists ready to go for me. If you can deliver a tailor-made list of recommendations, you’ll find it much easier to keep customers engaged with your product.

3. A/B Test All New Features

I’ve been a Netflix customer since 2007, so I’ve seen it go through a ton of changes over the years. WebDesigner Depot has, too:

From branding to layouts, and pricing to features, Netflix always seems to be switching things up. But here’s the thing: Netflix always implements changes that are meant to enhance the user experience. And when they don’t? It simply rolls the platform back to the way its customers preferred it.

One of the first times I remember this happening was with Max, Netflix’s talking bot:

This wasn’t a feature that was shoved onto users. It would sit in its dedicated space, waiting to be interacted with. Max would then welcome you back and ask what you’re in the mood to watch. You could pick a genre or you could let the bot provide recommendations based on how you rate other movies.

In all honesty, I was on the fence about Max. It was entertaining and I loved finding hidden gems through it. However, there were too many nights where I’d use Max hoping to find the perfect movie… only to abandon it and find something on my own.

That’s why it was no surprise when Max quietly slipped away. I have a feeling other users were just as ambivalent about it as I was.

There are a number of lessons, UX or otherwise, you can take away from this:

  • Be careful of trying the latest AI fads, they’re just too costly to invest in without hard data that proves that’s what your users want;
  • Give a new feature enough time to build up steam and provide you with reliable metrics — I remember Max being available for about six months, that’s more than enough time to gather user feedback and decide if a feature is worth keeping or not;
  • Personalization is great, but not necessarily if it’s at the expense of your customers’ time, sometimes the simpler feature is better.

Max isn’t the only example of Netflix playing around with its features. Do any of you recognize this?

This appears when the opening credits and theme song play at the start of a TV show. There’s really not a lot of value in sitting through this every time, and I’m willing to bet that Netflix saw that most of its users were manually fast-forwarding through them when it decided to try out this feature.

Here’s another recent feature that I think has some staying power:

While streaming services are responsible for the epidemic of binge-watching, it’s not necessarily in their best interest to allow customers to do so. Think of this “Are you still watching?” wake-up call as a form of ethical design.

This feature has been around for over a year, and it’s still going strong.

Bottom line? It’s really important to research your users when you’re in the process of building a website. However, there’s nothing more valuable than real user input from a live website.

Whether you plan to roll out a new feature or simply want to test the validity of one that exists, don’t run on assumptions. Use the new data coming in every day to further improve your design and features.

Invaluable Lessons UX Designers Can Take from Netflix

Although Netflix’s market share is slowly being chipped away at by the competition, it continues to reign supreme when it comes to streaming video services. I don’t see that changing anytime in the future either, considering how how long it’s demonstrated its willingness to innovate alongside evolving consumer needs.

And that’s really the key point I want to make in this post. While I could’ve pointed out its dramatic color palette or use of a responsive layout, we already are familiar with these concepts. The most important UX lessons we should be taking away from Netflix are the ones here.

Source
p img {display:inline-block; margin-right:10px;}
.alignleft {float:left;}
p.showcase {clear:both;}
body#browserfriendly p, body#podcast p, div#emailbody p{margin:0;}

Magic Flip Cards: Solving A Common Sizing Problem

Magic Flip Cards: Solving A Common Sizing Problem

Magic Flip Cards: Solving A Common Sizing Problem

Dan Halliday

2020-02-05T10:30:00+00:00 2020-02-06T00:08:00+00:00

What are the chances your next client will use the word interactive while introducing their project? In my experience, the answer is 100%, so I’m always looking for robust CSS techniques to help me deliver the various features and effects that come up when discussing this goal.

A little piece of interactivity I’m asked to implement again and again is flip cards — blocks of content that turn about when hovered or tapped to reveal content on their reverse side. It’s a neat effect that encourages playful browsing, and another way to show more information without navigating away from the page. But the standard method has a problem when it comes to accommodating different card content lengths.

In this tutorial, we’re going to build a flip card grid which solves that problem with some CSS basics — transforms, flex, and grid. You’ll need to be familiar with these, and it will help to have a good grasp of CSS positioning techniques. We will cover:

  • How flip cards are usually implemented using absolute positioning;
  • The sizing problem that absolute positioning introduces; and
  • A general solution for automatic sizing of overlaid content.

Creating A Basic Flip Card

With good modern browser support for three-dimensional transforms, creating a basic flip card is relatively straightforward. The usual method is to place the front and back card faces in a parent container, and absolutely position the back face so it can match the size of the front face. Add an x-axis transform to the back face to make it appear reversed, add another to the card itself on hover, and we’re in business.

.cards { display: grid;
} .card { perspective: 40rem;
} .card-body { transform-style: preserve-3d; transition: var(--time) transform; .card:hover & { transform: rotateX(-180deg); } } .card-front, .card-back { backface-visibility: hidden;
} .card-back { position: absolute; top: 0; right: 0; bottom: 0; left: 0; transform: rotateX(-180deg);
}

A standard flip card implementation using absolute positioning (see the Pen “[Magic Flip Cards 1: The Standard Implementation](https://codepen.io/smashingmag/pen/JjdPJvo)” by Dan Halliday)

A standard flip card implementation using absolute positioning (see the Pen “Magic Flip Cards 1: The Standard Implementation” by Dan Halliday)

What Could Go Wrong?

Our standard solution has a big problem, though: it doesn’t work when the back face needs more space than the front face provides. Giving the card a large, fixed size is one solution, but that approach is also guaranteed to fail at some point for some set of screen sizes.

How the standard flip card implementation fails with longer back content
This is how the standard flip card implementation fails with longer back content. (Large preview)

Design comps naturally feature neat-looking boxes with text that fits perfectly. But when starting development, it can be hard to get a page and card layout that works for the real content. And when displaying dynamic content from a CMS, it can be impossible! Even with word or character limits, there’s often no solution that works reliably across all devices.

How the standard flip card implementation fails with longer back content (see the Pen “[Magic Flip Cards 2: How Absolute Positioning Fails](https://codepen.io/smashingmag/pen/QWbLMLz)” by Dan Halliday)

How the standard flip card implementation fails with longer back content (see the Pen “Magic Flip Cards 2: How Absolute Positioning Fails” by Dan Halliday)

We should always strive to create layout implementations that tolerate a wide range of content lengths. But it’s not easy! I’ve often had occasion to fall back to using fixed sizing and position, whether due to time constraints, insufficient browser support, a weak reference design, or just my own inexperience.

Over the years, I’ve learned that a good iterative process and healthy dialogue with the designer can help a lot when wrangling these issues, and often you can meet somewhere in the middle to get a robust layout with some interactivity. But back to the task at hand — can it be done?

Thinking Outside the Box

In fact, it is possible to size the cards based on both the front and back content, and it’s not as hard as it seems at first. We just need to be methodical and persistent!

Constraining the Problem

Let’s start by making a list of our layout’s requirements. Trying to write down precisely what you want might seem like a chore, but it’s a great way to uncover constraints that can be simplified to solve a problem. Let’s say:

  • We want to see one or more rectangular cards, arranged in a single-column or multi-column grid;
  • We want the cards to flip over on hover or tap to reveal a second set of content on the back face;
  • We want the cards to always be big enough to show all their front and back content, regardless of content length or styling; and
  • In the case of multiple columns, ideally, we want all the cards to be the same size so that the rows align nicely.

Thinking through these requirements, we can notice a couple of things that simplify the problem:

  • If the cards are going to be presented in a grid, we have a constraint on their width — that is, their widths are functions of the viewport or grid container rather than their own content;
  • Given that we know a card’s width (as a percentage of its parent, at least), we’ve solved for the horizontal dimension and we just need to have the card’s height expand to fits the taller of its front or back face; and
  • If we can do that and each card is vertically self-sized, we can use CSS Grid’s grid-auto-rows to make all rows of cards as tall as the tallest card.

Figuring Out The Card Trick

So, how do we self-size the cards? Now we’ve simplified our problem, we’re within reach of the solution.

Forget, for a moment, the idea of putting content on top of other content, and focus on our new requirement: a parent that is as tall as its tallest child. That’s easy! Using columns, we can cause a parent to expand to the height of its tallest child. Then, we just need to employ a little sleight of hand to get the children aligned:

  1. Set the children to be the same width as their parent
  2. Allow the second child to overflow to the right
  3. Transform it leftwards back into its proper place
.cards { display: grid;
} .card-body { display: flex;
} .card-front, .card-back { min-width: 100%; mix-blend-mode: multiply; // Preview both faces
} .card-back { transform: translate(-100%, 0);
}

Vertical sizing by fixed horizontal overflow (see the Pen “[Magic Flip Cards 3: Vertical Sizing by Fixed Horizontal Overflow](https://codepen.io/smashingmag/pen/ExjYvjP)” by Dan Halliday)

Vertical sizing by fixed horizontal overflow (see the Pen “Magic Flip Cards 3: Vertical Sizing by Fixed Horizontal Overflow” by Dan Halliday)

If this approach seems obvious, rest assured that I spent many hours going through some really terrible ideas before thinking of it. At first, I had planned to print a hidden duplicate version of the back face text inside the front face to expand the card to the correct size. And when I did think of using column overflow, I was originally cropping the right-hand column using overflow:hidden, and transforming it only at the last moment when the hover started, as I hadn’t yet realized I could just keep it transformed from the beginning and use another method such as opacity or backface-visibility to turn it on and off as needed.

In other words, obvious solutions are the result of hard work! If you feel like you’ve been bashing your head against your desk for hours on a layout problem, it is important to take a step back and decide whether you are spending your client’s time wisely: whether to suggest they alter the design, and whether to pursue the solution in your own time as a learning exercise when the pressure’s off. But when you do come up with simple methods, never feel stupid because it took a long time. Now, let’s review our complete solution.

.cards { display: grid;
} .card { perspective: 40rem;
} .card-body { display: flex; transform-style: preserve-3d; transition: var(--time) transform; .card:hover & { transform: rotateX(-180deg); }
} .card-front, .card-back { backface-visibility: hidden; min-width: 100%;
} .card-back { transform: rotateX(-180deg) translate(-100%, 0);
}

The complete magic flip cards solution (see the Pen “[Magic Flip Cards 4: The Complete Solution](https://codepen.io/smashingmag/pen/xxGKLZO)” by Dan Halliday)

The complete magic flip cards solution (see the Pen “Magic Flip Cards 4: The Complete Solution” by Dan Halliday)

Are There Any Caveats?

The solution works well generally, with just a few minor caveats to bear in mind:

  • The cards must be present in a grid layout or in some other context where their widths are not content-dependent.
  • Cards require a content wrapper of some sort (our card-body) so that the hover area doesn’t change during animations. If the card itself is animated, you’ll see some glitching as the animation rapidly stops and restarts.
  • Styling such as backgrounds and box-shadows are best placed directly on the front and back faces, as any effects on the card itself will not be animated. Beware of styling such as box-shadows on the card body, as naturally they will get flipped upside down.
  • Cards’ front and back faces need their box-sizing property set to border-box if they have their own padding, owing to their min-width requirement, otherwise they will overflow.
  • Safari still requires -webkit-backface-visibility, in its vendor-prefixed form.

Adding Some Polish

Now we’ve solved the hard problem, let’s look at a couple of tweaks we can make to get the whole interaction working as smoothly as possible.

First, check whether the cards overlap while flipping. This will depend on whether you’re using multiple columns, the width of the column gutter, the orientation of the flip, and the perspective value of the card, but it is likely to happen. You can increase the duration of the animation to see things more clearly. When hovering, it looks unnatural for the hovered card to flip underneath its later neighbors, so we need to put it on top using z-index. Easy enough, but watch out! We need to wait until the outgoing animation is complete before restoring the z-index. Enter transition-delay:

.card { transition: z-index; transition-delay: var(--time); z-index: 0; &:hover { transition-delay: 0s; z-index: 1; }
}

Next, consider creating an active state for the cards. I usually try and make cards like these link to somewhere relevant — even if not specified by the designer — as elements with hover effects like this feel very tappable so it’s good to provide a destination for readers who try their luck. I like a short, subtle scale transform, as it works reasonably well whether or not the second half of the animation is cut off by loading of the destination page (I’d love for browsers to complete in-flight animations cleanly before navigation, though I’m sure that would be far more difficult to implement in practice than it sounds).

This is also a great opportunity to think about how accessible our cards’ back content is. Our markup is concise and well-ordered so we’ve covered screen readers and other use cases that ignore styling, but how about keyboard users? If we’re going to make the cards themselves anchors, they will receive focus as keyboard users tab through the page. Let’s reuse the card’s hover state as a focus state, and the back content will appear naturally during keyboard browsing.

.card { transition: z-index, transform calc(var(--time) / 4); transition-delay: var(--time), 0s; z-index: 0; &:hover { transition-delay: 0s; z-index: 1; } &:active { transform: scale(0.975); }
} .card-body { .card:hover &, .card:focus & { transform: rotateX(-180deg); }
}

Finally, don’t forget that now the card automatically scales to fit its content, you can use pretty much any alignment and spacing techniques you like inside the front and back containers. Use flex alignment to center titles, add padding, even put another grid inside the card. This is the beauty of good layout solutions that scale with their content — reduced coupling of children to parents, and the modularity that allows you to focus on one thing at a time.

.card-front, .card-back { display: flex; align-items: center; background-color: white; box-shadow: 0 5px 10px black; border-radius: 0.25rem; padding: 1.5rem;
}

Wrapping Up

I hope you find this CSS technique useful! Why not try some variations on the animation, such as a scale effect, or a simple cross-fade? The technique isn’t limited to the cards form factor either. It can be used anywhere where the responsibility for vertical sizing falls to more than one element. Imagine a magazine website featuring large photographs with overlaid captions — you could use it to accommodate both images with tall aspect ratios, and long dynamic text.

Above all, remember the benefits of taking some time to really think hard about whether there’s a way of implementing a design that looks as if it would only work with fixed sizing and position. Often, there is, and no matter how tricky the problem may seem at first, writing down all your requirements, putting some time aside to create a minimal test case, and stepping through it methodically is always the best bet.

Smashing Editorial (ra, il)
CSS4

Tab Atkins in 2012:

There has never been a CSS4. There will never be a CSS4. CSS4 is not a thing that exists.

Rachel Andrew in 2016:

While referring to all new CSS as CSS3 worked for a short time, it doesn’t reflect the reality of where CSS is today. If you read something about CSS3 Selectors, then what is actually being described is something that is part of the CSS Selectors Level 3 specification. In fact CSS Selectors is one of the specifications that is marked as completed and a Recommendation. The CSS Working Group is now working on Selectors Level 4 with new proposed features plus the selectors that were part of Level 3 (and CSS 1 and 2). It’s not CSS4, but Level 4 of a single specification. One small part of CSS.

Jen Simmons in 2018:

Many people are waiting for CSS4 to come out. Where is it? When will it arrive? The answer is never. CSS4 will never happen. It’s not actually a thing.


So CSS3 was a unique one-off opportunity. Rather than one big spec, break them into parts and start them all off at “Level 3” but then let them evolve separately. That was very on purpose, so things could move quicker independently.

The problem? It was almost too effective. CSS3, and perhaps to a larger degree, “HTML5”, became (almost) household names. It was so successful, it’s leaving us wanting to pull that lever again. It was successful on a ton of levels:

  • It pushed browser technology forward, particularly on technologies that had been stale for too long.
  • It got site owners to think, “hey maybe it’s a good time to update our website.
  • It got educators to think, “hey maybe it’s a good time to update our curriculums.

It was good for the web overall, good for websites taking advantage of it, and there was money to be made along the way. I bet it would be staggering to see how much money was made in courses and conferences waving the CSS3 flag.

Peter-Paul Koch in 2020:

I am proposing that we web developers, supported by the W3C CSS WG, start saying “CSS4 is here!” and excitedly chatter about how it will hit the market any moment now and transform the practice of CSS.

Of course “CSS4” has no technical meaning whatsoever. All current CSS specifications have their own specific versions ranging from 1 to 4, but CSS as a whole does not have a version, and it doesn’t need one, either.

Regardless of what we say or do, CSS 4 will not hit the market and will not transform anything. It also does not describe any technical reality.

Then why do it? For the marketing effect.

I think he’s probably right. If we all got together on it, it could have a similar good-for-everybody bang the way CSS3 did.

If it’s going to happen, what will give it momentum is if there is a single clear message about what it is. CSS3 was like:

  • border-radius
  • gradients
  • animations and transitions
  • transforms
  • box-shadow

Oh gosh, it’s hard to remember now. But at the time it was a pretty clear set of things that represented what there was to learn and they were all fairly exciting.

What would we put under the CSS4 flag?

  • Flexbox
  • Grid maybe?
  • Everything new with color (like this and this)
  • Independent transforms
  • Variable fonts
  • Offset paths
  • Let’s get nesting done!
  • Houdini stuff?
  • Shadow DOM selectors?

Lemme just say I will personally spearhead this thing if container queries can get done and we make that a part of it.

What else? Wanna refute anything on my list?

The post CSS4 appeared first on CSS-Tricks.

Meet “The Ethical Design Handbook”: How To Leave Dark Patterns Behind

Meet “The Ethical Design Handbook”: How To Leave Dark Patterns Behind

Meet “The Ethical Design Handbook”: How To Leave Dark Patterns Behind

Vitaly Friedman

2020-02-04T13:00:00+00:00 2020-02-05T00:08:29+00:00

Over the past twenty years, user privacy has become merely a commodity on the web: there, but hardly ever respected — and often swiftly discarded. No wonder ad-blockers have gained traction, browsers have introduced tracking protection, and new legislation in form of GDPR and CCPA brought regulations for data collection.

We need to craft better digital products that respect customer’s choices without hurting business KPIs. And we need to do so by taming data collection and abandoning dark patterns, from hidden checkboxes to ambiguous copywriting. How do we get there?

That’s the question we wanted to answer. Meet Ethical Design Handbook, a new Smashing book full of practical techniques and blueprints on how companies ridden with shady practices can shift towards better, sustainable design. Download a free PDF excerpt (1 MB).


Print + eBook

{ “sku”: “ethical-design-handbook”, “type”: “Book”, “price”: “39.00”, “sales_price”: “29.00”, “prices”: [{ “amount”: “39.00”, “currency”: “USD”, “items”: [ {“amount”: “29.00”, “type”: “Book”}, {“amount”: “10.00”, “type”: “E-Book”} ] }, { “amount”: “39.00”, “currency”: “EUR”, “items”: [ {“amount”: “29.00”, “type”: “Book”}, {“amount”: “10.00”, “type”: “E-Book”} ] }, { “amount”: “29.00”, “currency”: “USD”, “items”: [ {“amount”: “21.00”, “type”: “Book”}, {“amount”: “8.00”, “type”: “E-Book”} ] }, { “amount”: “29.00”, “currency”: “EUR”, “items”: [ {“amount”: “21.00”, “type”: “Book”}, {“amount”: “8.00”, “type”: “E-Book”} ] } ] } $
29.00 $
39.00

Quality hardcover. Free worldwide shipping, starting early March. 100 days money-back-guarantee.

eBook

{ “sku”: “ethical-design-handbook”, “type”: “E-Book”, “price”: “19.00”, “sales_price”: “14.90”, “prices”: [{ “amount”: “19.00”, “currency”: “USD” }, { “amount”: “19.00”, “currency”: “EUR” }, { “amount”: “14.90”, “currency”: “USD” }, { “amount”: “14.90”, “currency”: “EUR” } ] } $
14.90 $
19.00

DRM-free, of course. ePUB, Kindle, PDF.
Included with Smashing Membership.

About The Book

When we set out to write this book, we wanted to develop a new type of working framework to empower people to practice ethical design in their business, in their teams, and with their products. The reason was simple: too many products we use today have become machines for tricking customers into decisions they never intended to make. That’s not quite the web we want to see around us.


Many business models thrive on ingeniously deceptive and manipulative digital products. Not because they have to; mostly because it has become an accepted norm as everybody else seems to be doing it as well. But what happens when the norm is shattered?

  • What happens if you can’t get access to personal data that’s been feeding the machine all this time?
  • What if you can’t track customers wandering from one website to another?
  • What happens when ad-blocking becomes mainstream and your advertising scripts are dismissed by crafty blockers?
  • How should the role and responsibilties of marketing team change with new regulations, such as GDPR and CCPA?
  • What if your competitors discover an alternative business model way before you do?
  • What competitive advantages can your business gain by focusing on privacy and transparency?

The Ethical Design Handbook explores alternative, compliant and sustainable strategies. The book explores how companies and organizations, small and large, can move towards ethical design and become more healthy and profitable along the way. It’s a practical guide for everyone who works with digital products as a designer, developer, product manager, lawyer or in management.

The book features interface design examples that take ethical design principles into the account. Large preview.

Table Of Contents

Introduction
+
1. The need for ethics in design
+
2. Creating positive change
+
3. Respect-driven design
+
4. The business of ethical design
+
5. Ethical design best practices
+
6. Getting started
+

In the book, you’ll learn how to:

  1. Define and explain what ethical design is,
  2. Justify and prove a business case for ethical decisions,
  3. Measure and track the impact of ethical design,
  4. Grow a sustainable business on ethical principles,
  5. Strike the balance between data collection and ethics,
  6. Embed ethical design into your workflow,
  7. Get started with ethical transformation.

368 pages. The eBook is already available (PDF, ePUB, Amazon Kindle). We’ll ship printed copies early March 2020. Written by Trine Falbe, Martin Michael Frederiksen and Kim Andersen.


Print + eBook

{ “sku”: “ethical-design-handbook”, “type”: “Book”, “price”: “39.00”, “sales_price”: “29.00”, “prices”: [{ “amount”: “39.00”, “currency”: “USD”, “items”: [ {“amount”: “29.00”, “type”: “Book”}, {“amount”: “10.00”, “type”: “E-Book”} ] }, { “amount”: “39.00”, “currency”: “EUR”, “items”: [ {“amount”: “29.00”, “type”: “Book”}, {“amount”: “10.00”, “type”: “E-Book”} ] }, { “amount”: “29.00”, “currency”: “USD”, “items”: [ {“amount”: “21.00”, “type”: “Book”}, {“amount”: “8.00”, “type”: “E-Book”} ] }, { “amount”: “29.00”, “currency”: “EUR”, “items”: [ {“amount”: “21.00”, “type”: “Book”}, {“amount”: “8.00”, “type”: “E-Book”} ] } ] } $
29.00 $
39.00

Quality hardcover. Free worldwide shipping, starting early March. 100 days money-back-guarantee.

eBook

{ “sku”: “ethical-design-handbook”, “type”: “E-Book”, “price”: “19.00”, “sales_price”: “14.90”, “prices”: [{ “amount”: “19.00”, “currency”: “USD” }, { “amount”: “19.00”, “currency”: “EUR” }, { “amount”: “14.90”, “currency”: “USD” }, { “amount”: “14.90”, “currency”: “EUR” } ] } $
14.90 $
19.00

DRM-free, of course. ePUB, Kindle, PDF.
Included with Smashing Membership.

About The Authors

Trine FalbeTrine Falbe is a human-centered UX strategist, designer and teacher who works in the intersection between people and business. Trine is deeply passionate about ethical design and designing for children, and she is also a keynote speaker at conferences and a UX advisor in strategic projects.

Martin Michael FrederiksenAs a serial entrepreneur since the very first browser, Martin Michael Frederiksen was born with a practical appreciation for the crossroads between business and digital development. He has published the books Cross Channel and the CEO’s Guide to IT Projects That Cannot Fail. He works as an independent consultant for businesses that need a devil’s advocate when trying out new strategies and ideas.

Kim AndersenAfter training at an international advertising agency, Kim Andersen quickly left print media for digital design. Owing to his amazing memory he always leaves design meetings with an empty notebook, only to attend the following meeting armed with drawings where nothing has been forgotten and everything is drawn in great detail. He owns the digital design studio Onkel Kim, where he can be hired to do design tasks, preferably the most difficult and complex ones where the brain is working overtime.

Technical Details

The book features scorecards and blueprints, applicable to your work right away. Large view.

Community Matters ❤️

With The Ethical Design Handbook, we’ve tried to create a very focused handbook with applicable, long-living solutions and strategies to introduce a positive change in companies ridden with dark patterns and questionable practices.

There is quite a bit of work to do on the web, but our hope is that with this book, you will be equipped with enough tooling to slowly move a company towards a more sustainable and healthy digital footprint.

Producing a book takes quite a bit of time, and we couldn’t pull it off without the support of our wonderful community. A huge shout-out to Smashing Members for their ongoing support in our adventures. As a result, the eBook is and always will be free for Smashing Members. Plus, Members get a friendly discount when purchasing their printed copy.

Stay smashing, and thank you for your ongoing support, everyone!

The Ethical Design Handbook

Print + eBook

{ “sku”: “ethical-design-handbook”, “type”: “Book”, “price”: “39.00”, “sales_price”: “29.00”, “prices”: [{ “amount”: “39.00”, “currency”: “USD”, “items”: [ {“amount”: “29.00”, “type”: “Book”}, {“amount”: “10.00”, “type”: “E-Book”} ] }, { “amount”: “39.00”, “currency”: “EUR”, “items”: [ {“amount”: “29.00”, “type”: “Book”}, {“amount”: “10.00”, “type”: “E-Book”} ] }, { “amount”: “29.00”, “currency”: “USD”, “items”: [ {“amount”: “21.00”, “type”: “Book”}, {“amount”: “8.00”, “type”: “E-Book”} ] }, { “amount”: “29.00”, “currency”: “EUR”, “items”: [ {“amount”: “21.00”, “type”: “Book”}, {“amount”: “8.00”, “type”: “E-Book”} ] } ] } $
29.00 $
39.00

Quality hardcover. Free worldwide shipping, starting early March. 100 days money-back-guarantee.

eBook

{ “sku”: “ethical-design-handbook”, “type”: “E-Book”, “price”: “19.00”, “sales_price”: “14.90”, “prices”: [{ “amount”: “19.00”, “currency”: “USD” }, { “amount”: “19.00”, “currency”: “EUR” }, { “amount”: “14.90”, “currency”: “USD” }, { “amount”: “14.90”, “currency”: “EUR” } ] } $
14.90 $
19.00

DRM-free, of course. ePUB, Kindle, PDF.
Included with Smashing Membership.

More Smashing Books

Promoting best practices and providing you with practical tips to master your daily coding and design challenges has always been (and will be) at the core of everything we do at Smashing. In the past few years, we were very lucky to have worked together with some talented, caring people from the web community to publish their wealth of experience as printed books that stand the test of time. Alla, Adam and Heydon are some of these people. Have you checked out their books already?

How To Create A Headless WordPress Site On The JAMstack

How To Create A Headless WordPress Site On The JAMstack

How To Create A Headless WordPress Site On The JAMstack

Sarah Drasner & Geoff Graham

2020-02-04T11:00:00+00:00 2020-02-09T00:38:05+00:00

In the first article of this series, we walked through Smashing Magazine’s journey from WordPress to the JAMstack. We covered the reasons for the change, the benefits that came with it, and hurdles that were encountered along the way.

Like any large engineering project, the team came out the other end knowing more about the spectrum of successes and failures within the project. In this post, we’ll set up a demo site and tutorial for what our current recommendations would be for a WordPress project at scale: retaining a WordPress dashboard for rich content editing, while migrating the Front End Architecture to the JAMstack to benefit from better security, performance, and reliability.

We’ll do this by setting up a Vue application with Nuxt, and use WordPress in a headless manner — pulling in the posts from our application via the WordPress API. The demo is here, and the open-source repo is here.

(Large preview)

Deploy to Netlify buttonIf you wish to skip all the steps below, we’ve prepared a template for you. You can hit the deploy button below and modify it to your needs.

What follows is a comprehensive tutorial of how we set this all up. Let’s dig in!

Enter The WordPress REST API

One of the most interesting features of WordPress is that it includes an API right out of the box. It’s been around since late 2016 when it shipped in WordPress 4.7 and with it came opportunities to use WordPress new ways. What sort of ways? Well, the one we’re most interested in covering today is how it allows for the separation of the WordPress content and the Front End. Where building a WordPress theme in PHP was once the only way to develop an interface for a WordPress-powered site, the REST API ushered in a new era where the content management powers of WordPress could be extended for use outside the root WordPress directory on a server — whether that be an app, a hand-coded site, or even different platforms altogether. We’re no longer tethered to PHP.

This model of development is called a Headless CMS. It’s worth mentioning that Drupal and most other popular content management systems out there also offer a headless model, so a lot of what we show in this article isn’t just specific to WordPress.

In other words, WordPress is used purely for its content management interface (the WordPress admin) and the data entered into it is syndicated anywhere that requests the data via the API. It means your same old site content can now be developed as a static site, progressive web app, or any other way while continuing the use of WordPress as the engine for creating content.

Getting Started

Let’s make a few assumptions before we dive right in:

  • WordPress is already up and running.
    Going over a WordPress install is outside what we want to look at in this article and it’s already well documented.
  • We have content to work with.
    The site would be nothing without feeding it some data from the WordPress REST API.
  • The front-end is developed in Vue.
    We could use any number of other things, like React, Jekyll, Hugo, or whatever. We just happen to really like Vue and, truth be told, it’s likely the direction the Smashing Magazine project would have gone if they could start the process again.
  • We’re using Netlify.
    It was the platform Smashing migrated to, and is straightforward to work with. Full disclosure, Sarah works there. She also works there because she loves their service. 🙂

Setting Up Vue With Nuxt

Like WordPress, there’s already stellar documentation for setting up a Vue project, and Sarah has a Frontend Masters course (all the materials are free and open source on her GitHub). No need to rehash that here.

But what we’re actually going to do is create our app using NuxtJS. It adds a bunch of features to a typical Vue project (e.g. bundling, hot reloading, server-side rendering, and routing to name a few) that we’d otherwise have to piece together. In other words, it gives us a nice head start.

Again, setting up a NuxtJS project is super well documented, so still no need to get into that in this post. What is worth getting into is the project directory itself. It’d be nice to know what we’re getting into and where the API needs to go.

Learn how to set up a Nuxt app from scratch — it might be helpful to watch if you’re completely new to it. (Watch on Vimeo)

We’ll create the project with this command:

npx create-nuxt-app <project-name>

Here’s the general structure for a standard Nuxt project, leaving out some files for brevity:

[root-directory]
├── .nuxt
├── assets
├── components ├── AppNavigation.vue //any components you will reuse └── AppFooter.vue
├── dist
├── layouts └── default.vue //this gives you a standard layout, you can make many if you like, such as blog.vue, etc. We typically put our navs and footers here
├── middleware
├── node_modules
├── pages
├── index.vue //any .vue components we put in here will automatically become routed pages! └── about.vue
├── plugins
├── static
| └── index.html
├── store └── index.js //we’ll put any state we need to share around the application in here, including the calls to the REST API to update the data. This is called a Vuex store. By creating the index page, Nuxt registers it.
└── nuxt.config.js

That’s about it for our Vue/Nuxt installation! We’ll create a component that fetches data from a WordPress site in just a bit, but this is basically what we’re working with.

Static Hosting

Before we hook the Vue app up with Netlify, let’s create a repository for the project. One of the benefits of a service like Netlify is that it can trigger a deploy when changes are pushed to the master (or some other) branch of a repository. We’ll definitely want that. Git is automatically initialized in a new Vue installation, so we get to skip that step. All we need to do is create a new repository on GitHub and push the local project to master. Anything in caps is something you will replace with your own information.

### Add files
git add . ### Add remote origin
git remote add origin git@github.com:USERNAME/REPONAME.git ### Push everything to the remote repo
git push -u origin master

Now, we’re going to head over to our Netlify account and hook things up. First, let’s add a new site from the Sites screen. If this is your first time using Netlify, it will ask you to give it the authorization to read repositories from your GitHub (or GitLab, or BitBucket) account. Let’s select the repository we set up.

(Large preview)

Netlify will confirm the branch we want to use for deployments. There’s also a spot to tell Netlify what we use for the build task that compiles our site for production and which directory to look at.

We’ll be prompted for our build command and directory. For Nuxt it’s:

  • Build command: yarn generate or npm run generate
  • Directory: dist

Check out the full instructions for deploying a Vue app to Netlify for more details. Now we can deploy! 🎉

Setting Up The Store

No, we’re not getting into e-commerce or anything. Nuxt comes equipped with the ability to use a Vuex store out of the gate. This provides a central place where we can store data for components to call and consume. You can think of it like the “brains” of the application.

The /store directory is empty by default. Let’s create a file in there to start making a place where we can store data for the index page. Let’s creatively call that index.js. Sarah has a VS Code extension with shortcuts that make this setup fairly trivial. With that installed (assuming you’re using VS Code, of course) we can type vstore2 and it spits out everything we need:

export const state = () => ({ value: 'myvalue'
}) export const getters = { getterValue: state => { return state.value }
} export const mutations = { updateValue: (state, payload) => { state.value = payload }
} export const actions = { updateActionValue({ commit }) { commit('updateValue', payload) }
}

Basically, the setup is as follows:

  • state holds the posts, or whatever info we need to store.
  • mutations will hold functions that will update the state. Mutations are the only thing that can update state actions cannot.
  • actions can make asynchronous API calls. We’ll use this to make the call to WordPress API and then commit a mutation to update the state. First, we’ll check if there’s any length to the posts array in state, which means we already called the API, so we don’t do it again.

Right off, we can nix the getters block because we won’t be using those right now. Next, we can replace the value: ‘myValue’ in the state with an empty array that will be reserved for our posts data:posts: []. This is where we’re going to hold all of our data! This way, any component that needs the data has a place to grab it.

The only way we can update the state is with mutations, so that’s where we’re headed next. Thanks to the snippet, all we need to do is update the generic names in the block with something more specific to our state. So, instead of updateValue, let’s go with updatePosts; and instead of state.value, let’s do state.posts. What this mutation is doing is taking a payload of data and changing the state to use that payload.

Now let’s look at the actions block. Actions are how we’re able to work with data asynchronously. Asynchronous calls are how we’ll fetch data from the WordPress API. Let’s update the boilerplate values with our own:

/*
this is where we will eventually hold the data for all of our posts
*/
export const state = () => ({ posts: []
})
/*
this will update the state with the posts
*/
export const mutations = { updatePosts: (state, posts) => { state.posts = posts }
}
/* actions is where we will make an API call that gathers the posts,
and then commits the mutation to update it
*/
export const actions = { //this will be asynchronous async getPosts({ state, commit }) { //the first thing we’ll do is check if there’s any length to the posts array in state, which means we already called the API, so don’t do it again. if (state.posts.length) return }
}

If that errors along the way, we’ll catch those errors and log them to the console. In production apps, we would also check if the environment was development before logging to the console.

Next, in that action we set up we’re going to try to get the posts from the API:

export const actions = { async getPosts({ state, commit }) { if (state.posts.length) return try { let posts = await fetch( `https://css-tricks.com/wp-json/wp/v2/posts?page=1&per_page=20&_embed=1` ).then(res => res.json()) posts = posts .filter(el => el.status === "publish") .map(({ id, slug, title, excerpt, date, tags, content }) => ({ id slug, title, excerpt, date, tags, content })) commit("updatePosts", posts) } catch (err) { console.log(err) }
}

You might have noticed we don’t just take all of the information and store it, we’re filtering out only what we need. We do this because WordPress does indeed store a good deal of data for each and every post, only some of which might be needed for our purposes. If you’re familiar with REST APIs, then you might already know that they typically return everything. For more information about this, you can check out a great post by Sebastian Scholl on the topic.

That’s where the .filter() method comes in. We can use it to fetch just the schema we need which is a good performance boost. If we head back to our store, we can filter the data in posts and use .map() to create a new array of that data.

Let’s do this so that we only get published posts (because we don’t want drafts showing up in our feed), the Post ID (for distinguishing between posts), the post slug (good for linking up posts), the post title (yeah, kinda important), and the post excerpt (for a little preview of the content), and some other things like tags. We can drop this in the try block right before the commit is made.

This will give us data that looks like this:

posts: [ { content:Object protected:false rendered:"<p>Fluid typography is the idea ..." date:"2019-11-29T08:11:40" excerpt:Object protected:false rendered:"<p>Fluid typography is the idea ..." id:299523 slug:"simplified-fluid-typography" tags:Array[1] 0:963 title:Object rendered:"Simplified Fluid Typography" }, …
]

OK, so we’ve created a bunch of functions but now they need to be called somewhere in order to render the data. Let’s head back into our index.vue file in the /pages directory to do that. We can make the call in a script block just below our template markup.

Let’s Render Them!

In this case, we want to create a template that renders a loop of blog posts. You know, the sort of page that shows the latest 10 or so posts. We already have the file we need, which is the index.vue file in the /pages directory. This is the file that Nuxt recognizes as the “homepage” of the app. We could just as easily create a new file if we wanted the feed of posts somewhere else, but we’re using this since we’re dealing with a site that’s based around a blog. Let’s open that file, clear out what’s already there and drop our own template markup in there.

We’ll dispatch this action, and render the posts:

<template>
<div class="posts"> <main> <h2>Posts</h2> <!-- here we loop through the posts --> <div class="post" v-for="post in posts" :key="post.id"> <h3> <!-- for each one of them, we’ll render their title, and link off to their individual page --> <a :href="`blog/${post.slug}`">{{ post.title.rendered }}</a> </h3> <div v-html="post.excerpt.rendered"></div> <a :href="`blog/${post.slug}`" class="readmore">Read more ⟶</a> </div> </main> </div>
</template> <script>
export default {
computed: { posts() { return this.$store.state.posts; }, },
created() { this.$store.dispatch("getPosts");
},
};
</script>
(Large preview)

In the created lifecycle method, you see we’re kicking off that action that will fetch the posts from the API. Then we’ll store those posts we get in a computed property called posts. Then in the template, we loop through all the posts, and render the title and the excerpt from each one, linking off to an individual post page for the whole post (think like single.php) that we haven’t built yet. So let’s do that part now!

Creating Individual Post Pages Dynamically

Nuxt has a great way of creating dynamic pages, with minimal code, you can set up a template for all of your individual posts.

First, we need to create a directory, and in there, put a page with an underscore, based on how you will render it. In our case, it will be called blog, and we’ll use the slug data we brought in from the API, with an underscore. Our directory will then look like this:

index.vue blog/ _slug.vue
<script>
export default {
computed: { posts() { return this.$store.state.posts; }
},
created() { this.$store.dispatch("getPosts");
}
};
</script>

We’ll dispatch the getPosts request, just in case they enter the site via one of the individual pages. We’ll also pull in the posts data from the store.

We also have to make sure this page knows which post we’re referring to. The way we’ll do this is to store this particular slug with this.$route.params.slug. Then we can find the particular post and store it as a computed property using filter:

computed: { ... post() { return this.posts.find(el => el.slug === this.slug); }
},
data() { return { slug: this.$route.params.slug };
},

Now that we have access to the particular post, in the template, we’ll render the title, and also the content. Due to the fact that the content is a string that has HTML elements already included that we want to use, we’ll use the vue directive v-html to render that output.

<template>
<main class="post individual"> <h1>{{ post.title.rendered }}</h1> <section v-html="post.content.rendered"></section>
</main>
</template>
(Large preview)

The last thing we have to do is let Nuxt know that it needs to generate all of these dynamic routes. In our nuxt.config.js file, we’ll let nuxt know when we use the generate command (which allows nuxt to build statically), to use a function to create the routes. We’ll call our function dynamicRoutes.

generate: { routes: dynamicRoutes
},

Next, we’ll install axios by running yarn add axios at the top of the file we’ll import it. Then we’ll create a function that will generate an array of posts based on the slugs we retrieve from the API. I cover this in more detail in this post.

import axios from "axios"
let dynamicRoutes = () => {
return axios .get("https://css-tricks.com/wp-json/wp/v2/posts?page=1&per_page=20") .then(res => { return res.data.map(post => `/blog/${post.slug}`) })
}

This will create an array that looks something like this:

export default { generate: { routes: [ '/blog/post-title-one', '/blog/post-title-two', '/blog/post-title-three' ] }
}

And we’re off to the races! Now let’s deploy it and see what we’ve got.

Create The Ability To Select Via Tags

The last thing we’re going to do is select posts by tag. It works very similarly for categories, and you can create all sorts of functionality based on your data in WordPress, we’re merely showing one possible path here. It’s worth exploring the API reference to see all that’s available to you.

It used to be that when you gathered the tags data from the posts, it would tell you the names of the tags. Unfortunately, in v2 of the API, it just gives you the id, so you have to then make another API call to get the actual names.

The first thing we’ll do is create another server-rendered plugin to gather the tags just as we did with the posts. This way, it will do this all at build time and be rendered for the end-user immediately (yay JAMstack!)

export default async ({ store }) => { await store.dispatch("getTags")
}

Next, we’ll create a getTags action, where we pass in the posts. The API call will look very similar, but we have to pass in the tags in this format, where after include UTM we pass in the IDs, comma-separated, like this:

https://css-tricks.com/wp-json/wp/v2/tags?include=1,2,3

In order to format it like that, we’ll have to take the posts and extract all the ids. We’ll do so with a .reduce() method:

async getTags({ state, commit }, posts) { if (state.tags.length) return let allTags = posts.reduce((acc, item) => { return acc.concat(item.tags); }, []) allTags = allTags.join() try { let tags = await fetch( `https://css-tricks.com/wp-json/wp/v2/tags?page=1&per_page=40&include=${allTags}` ).then(res => res.json()) tags = tags.map(({ id, name }) => ({ id, name })) commit("updateTags", tags) } catch (err) { console.log(err) }
}

Just like the posts, we’ll need a place in state to store the tags, and a mutation to update them:

export const state = () => ({ posts: [], tags: []
}) export const mutations = { updatePosts: (state, posts) => { state.posts = posts }, updateTags: (state, tags) => { state.tags = tags }
}

Now, in our index.vue page, we can bring in the tags from the store and display all of them

computed: { tags() { return this.$store.state.tags; },
} <aside> <h2>Categories</h2> <div class="tags-list"> <ul> <li v-for="tag in tags" :key="tag.id"> <a>{{ tag.name }}</a> </li> </ul> </div> </aside>

This will give us this visual output:

(Large preview)

Now, this is fine, but we might want to filter the posts based on which one we selected. Fortunately, computed properties in Vue make small work of this.

<template>
<div class="posts"> <aside> <h2>Categories</h2> <div class="tags-list"> <ul> <li @click="updateTag(tag)" v-for="tag in tags" :key="tag.id"> <a>{{ tag.name }}</a> </li> </ul> </div> </aside>
</div>
</template> <script>
export default {
data() { return { selectedTag: null }
},
methods: { updateTag(tag) { if (!this.selectedTag) { this.selectedTag = tag.id } else { this.selectedTag = null } }
}, ...
};
</script>

First, we’ll store a data property that allows us to store the selectedTag. We’ll start it off with a null value.

In the template, when we click on the tag, we’ll execute a method that will pass in which tag it is, named updateTag. We’ll use that to either set selectedTag to the tag ID or back to null, for when we’re done filtering.

From there, we’ll change our v-for directive that displays the post from"post in posts" to "post in sortedPosts". We’ll create a computed property called sortedPosts. If the selectedTag is set to null, we’ll just return all the posts, but otherwise we’ll return only the posts filtered by the selectedTag:

<template>
<main> <h2>Posts</h2> <div class="post" v-for="post in sortedPosts" :key="post.id"> </div> </main>
</template>
<script>
computed: { sortedPosts() { if (!this.selectedTag) return this.posts return this.posts.filter(el => el.tags.includes(this.selectedTag)) }
},
</script>

Now the last thing we want to do to polish off the application is style the selected tag just a little differently, and let the user know you can deselect it.

<template>
<div class="posts"> <aside> <h2>Categories</h2> <div class="tags-list"> <ul> <li @click="updateTag(tag)" v-for="tag in tags" :key="tag.id" :class="[tag.id === selectedTag ? activeClass : '']"> <a>{{ tag.name }}</a> <span v-if="tag.id === selectedTag">✕</span> </li> </ul> </div> </aside>
</div>
</template> <script>
export default {
data() { return { selectedTag: null, activeClass: 'active' }
},
</script>

And there you have it! All the benefits of a rich content editing system like WordPress, with the performance and security benefits of JAMstack. Now you can decouple the content creation from your development stack and use a modern JavaScript framework and the rich ecosystem in your own app!

Deploy to Netlify buttonOnce again, if you wish to skip all these steps and deploy the template directly, modifying it to your needs, we set this up for you. You can always refer back here if you need to understand how it’s built.

There are a couple of things we didn’t cover that are out of the scope of the article (it’s already quite long!)

Smashing Editorial (dm, ra, il)
The Three Types of Code

Every time I start a new project, I organize the code I’m looking at into three types, or categories if you like. And I think these types can be applied to any codebase, any language, any technology or open source project. Whether I’m writing HTML or CSS or building a React component, thinking about these different categories has helped me figure out what to refactor and prioritize, and what to leave alone for now.

Those categories: Boring Code, Salt Mine Code, and Radioactive Code.

Let me explain.

Boring Code

Boring code is when it makes perfect sense when you read it. There’s no need to refactor it, and it performs its function in a way that doesn’t make you want to throw yourself into a river. Boring code is good code. It doesn’t do a kick-flip and it’s not trying to impress you. You can use it without having to write even more code or engineer hacks on top of it. Boring code does exactly what it says on the tin and never causes any surprises.

This function makes sense, this prop is clearly named, this React component is straightforward. There are no loops within loops, no mental gymnastics required here.

However, boring code is near impossible to write because our understanding of it is almost always incomplete when we start tackling a problem. Just look at how many considerations can go into a styling a simple paragraph for contrast. To write boring code, we must be diligent, we must endlessly refactor, and we must care for the codebase beyond a paycheck at the end of the month.

Boring code is good because boring code is kind.

Salt Mine Code

This is the type of code that’s bonkers and makes not a lick of sense. It’s the sort of code that we can barely read but it’s buried so deep in the codebase that it’s near impossible to change anyway. However! It’s not leaking into other parts of our code, so we can mostly ignore it. It might not be pretty, and we probably don’t want to ever look at it so long as we live, but it’s not actively causing any damage.

It’s this type of code that we can mostly forget about. It’s the type of code that is dangerous if opened up and tampered with, but for now, everything is okay.

The trouble is buried deep.

Radioactive Code

Radioactive code is the real problem at the heart of every engineering team. It’s the let’s-not-go-to-work-today sort of code. It’s the stuff that is not only bad but is actively poisoning our codebase and making everything worse over time. Imagine a codebase as a nuclear reactor; radioactive code is the stuff that’s breached the container and is now leaking into every part of our codebase.

An example? For us at Gusto and on the design systems team, I would consider our form components to be radioactive. Each component causes more problems because we can never use the component as is; we have to hack it to get what we want. Each time anyone uses this code they have to write even more code on top of it, making things worse over time, and it encourages everyone on the team to do the same.

In our design system, when we want to add a class name to the div that wraps a form element, we must use the formFieldClass prop in one component, and wrapperClass in another. There is a propType called isDefaultLayout and everyone sets it to false and writes custom CSS classes on top of it. In other words, not only does radioactive code make it hard for us to understand all this nonsense code, it makes it increasingly difficult to understand other parts of the codebase, too. Because the file we’re looking at right now has dependencies on eight different things that we cannot see. The result of removing this radioactive code means changing everything else that depends upon it.

In other words, radioactive code — like our form components — makes it impossible for the codebase to be trusted.

Radioactive code is not only bad for us and our codebase, but it is also bad for our team. It encourages bad habits, cruelty in Slack threads, not to mention that it causes friction between team members that is hard to measure. Radioactive code also encourages other teams in a company to go rogue and introduce new technologies into a codebase when the problem of radioactive code is not the tech itself. Anyone can write this type of code, regardless of the language or the system or the linting when they’re not paying enough attention to the problem. Or when they’re trying to be a little too smart. Or when they’re trying to impress someone.

How do we fix radioactive code? Well, we must draw a circle around it and contain the madness that’s leaking into other parts of the codebase. Then we must do something utterly heroic: we must make it boring.

The post The Three Types of Code appeared first on CSS-Tricks.

Privacy Settings
We use cookies to enhance your experience while using our website. If you are using our Services via a browser you can restrict, block or remove cookies through your web browser settings. We also use content and scripts from third parties that may use tracking technologies. You can selectively provide your consent below to allow such third party embeds. For complete information about the cookies we use, data we collect and how we process them, please check our Privacy Policy
Youtube
Consent to display content from Youtube
Vimeo
Consent to display content from Vimeo
Google Maps
Consent to display content from Google