Developer News

The Differing Perspectives on CSS-in-JS

Css Tricks - Tue, 08/13/2019 - 1:00pm

Some people outright hate the idea of CSS-in-JS. Just that name is offensive. Hard no. Styling doesn't belong in JavaScript, it belongs in CSS, a thing that already exists and that browsers are optimized to use. Separation of concerns. Anything else is a laughable misstep, a sign of not learning from the mistakes of the past (like the <font> tag and such.)

Some people outright love the idea of CSS-in-JS. The co-location of templates and functionality, à la most JavaScript frameworks, has proven successful to them, so wrapping in styles seems like a natural fit. Vue's single file components are an archetype here.

(Here's a video on CSS-in-JS I did with Dustin Schau if you need a primer.)

Brent Jackson thinks you should definitely learn it, but also offers some pragmatic points on what it does and doesn't do:

What does CSS-in-JS do?

  • Let you author CSS in JavaScript syntax
  • Colocate styles with components
  • Take advantage of native JS syntax features
  • Take advantage of anything from the JS ecosystem

What does CSS-in-JS not rid you of needing to understand:

  • How styles are applied to the DOM
  • How inheritance works
  • How CSS properties work
  • How CSS layout works

CSS-in-JS doesn't absolve you of learning CSS. Mostly, anyway.

I've heard lots of pushback on CSS-in-JS in the vein of "you people are reaching for CSS-in-JS because you don't understand CSS" or "You're doing this because you're afraid of the cascade. I already know how to scope CSS." I find that stuff to be more poking across the isles that isn't particularly helpful.

Laura buns has a wonderfully two-sided article titled "The web without the web" part of which is about React and CSS-in-JS:

I hate React because CSS-in-JS approaches by default encourage you to write completely self-contained one off components rather than trying to build a website UI up as a whole.

You don't need to use CSS-in-JS just because you use React, but it is popular, and that's a very interesting and fair criticism. If you scope everything, aren't you putting yourself at higher risk of inconsistency?

I've been, so far, a fan of CSS modules in that it's about as light as you get when it comes to CSS-in-JS, only handling scoping and co-location and that's about it. I use it with Sass so we have access to mixins and variables that help consistency, but I could see how it could allow a slide into dangerous too-many-one-offs territory.

And yet, they would be disposable one-offs. Code-splittable one-offs. Everything exists in balance.

Laura goes on to say she likes CSS-in-JS approaches for some of the power and flexibility it offers:

I like the way CSS-in-JS gives you enough abstraction to still use tricks like blind owl selectors while also giving you the full power of using JS to do stuff like container queries.

Martin Hofmann created a site comparing BEM vs. Emotion that looks at one little "alert" component. I like how it's an emotionless (literally, not referencing the library) comparison that looks at syntax. BEM has some advantages, notably, requiring no tooling and is easily sharable to any web project. But the Emotion approach is cleaner in many ways and looks easier to handle.

I'd like to see more emotionless comparisons of the technologies. Choice A does these three things well but is painful here and here, while choice B does these other things well and solves a few other pain points.

We recently linked up Scott Jehl's post that looks into loading CSS asynchronously. Scott's opening line:

One of the most impactful things we can do to improve page performance and resilience is to load CSS in a way that does not delay page rendering.

It's notable that an all-in CSS-in-JS approach gets this ability naturally, as styling is bundled into JavaScript. It's bundled at a cost. A cost to performance. But we get some of that cost back if we're eliminating other render-blocking things. That's interesting stuff worthy of more data, at least.

I might get my butt kicked for this, but I'm a bit less interested in conversations that try to blame CSS-in-JS for raising the barrier to entry in the industry. That's a massive thing to consider, but we aren't talking about shutting down CSS here and forcing everyone to some other language. We're talking about niche libraries for certain types of projects at certain scales.

I think it's worth taking a look at CSS-in-JS ideas if...

  • You're working on a component-heavy JavaScript project anyway.
  • You're already co-locating templates, functionality, and data queries.
  • You think you can leverage it without harming user experience, like gaining speed back elsewhere.
  • Your team is comfortable with the required tech, as in, you aren't pushing away talent.

Max Stoiber is an unabashed fan. His post on the topic talks about the confidence this style brings him and the time he saves in finding what he needs, both things I've found to be true. But he also thinks the approach is specifically for JavaScript framework apps.

If you are using a JavaScript framework to build a web app with components, CSS-in-JS is probably a good fit. Especially if you are part of a team where everybody understands basic JavaScript.

I'd love to hear y'all thoughts on this in the comments. Have you worked out your feelings on all this? Madly in love? Seething with dislike? I'd be most interested in hearing success stories or failure stories on real projects.

The post The Differing Perspectives on CSS-in-JS appeared first on CSS-Tricks.

All the New ES2019 Tips and Tricks

Css Tricks - Tue, 08/13/2019 - 4:32am

The ECMAScript standard has been updated yet again with the addition of new features in ES2019. Now officially available in node, Chrome, Firefox, and Safari you can also use Babel to compile these features to a different version of JavaScript if you need to support an older browser.

Let’s look at what’s new!

Object.fromEntries

In ES2017, we were introduced to Object.entries. This was a function that translated an object into its array representation. Something like this:

let students = { amelia: 20, beatrice: 22, cece: 20, deirdre: 19, eloise: 21 } Object.entries(students) // [ // [ 'amelia', 20 ], // [ 'beatrice', 22 ], // [ 'cece', 20 ], // [ 'deirdre', 19 ], // [ 'eloise', 21 ] // ]

This was a wonderful addition because it allowed objects to make use of the numerous functions built into the Array prototype. Things like map, filter, reduce, etc. Unfortunately, it required a somewhat manual process to turn that result back into an object.

let students = { amelia: 20, beatrice: 22, cece: 20, deirdre: 19, eloise: 21 } // convert to array in order to make use of .filter() function let overTwentyOne = Object.entries(students).filter(([name, age]) => { return age >= 21 }) // [ [ 'beatrice', 22 ], [ 'eloise', 21 ] ] // turn multidimensional array back into an object let DrinkingAgeStudents = {} for (let [name, age] of overTwentyOne) { DrinkingAgeStudents[name] = age; } // { beatrice: 22, eloise: 21 }

Object.fromEntries is designed to remove that loop! It gives you much more concise code that invites you to make use of array prototype methods on objects.

let students = { amelia: 20, beatrice: 22, cece: 20, deirdre: 19, eloise: 21 } // convert to array in order to make use of .filter() function let overTwentyOne = Object.entries(students).filter(([name, age]) => { return age >= 21 }) // [ [ 'beatrice', 22 ], [ 'eloise', 21 ] ] // turn multidimensional array back into an object let DrinkingAgeStudents = Object.fromEntries(overTwentyOne); // { beatrice: 22, eloise: 21 }

It is important to note that arrays and objects are different data structures for a reason. There are certain cases in which switching between the two will cause data loss. The example below of array elements that become duplicate object keys is one of them.

let students = [ [ 'amelia', 22 ], [ 'beatrice', 22 ], [ 'eloise', 21], [ 'beatrice', 20 ] ] let studentObj = Object.fromEntries(students); // { amelia: 22, beatrice: 20, eloise: 21 } // dropped first beatrice!

When using these functions make sure to be aware of the potential side effects.

Support for Object.fromEntries Chrome Firefox Safari Edge 75 67 12.1 No

&#x1f50d; We can use your help. Do you have access to testing these and other features in mobile browsers? Leave a comment with your results — we'll check them out and include them in the article.

Array.prototype.flat

Multi-dimensional arrays are a pretty common data structure to come across, especially when retrieving data. The ability to flatten it is necessary. It was always possible, but not exactly pretty.

Let’s take the following example where our map leaves us with a multi-dimensional array that we want to flatten.

let courses = [ { subject: "math", numberOfStudents: 3, waitlistStudents: 2, students: ['Janet', 'Martha', 'Bob', ['Phil', 'Candace']] }, { subject: "english", numberOfStudents: 2, students: ['Wilson', 'Taylor'] }, { subject: "history", numberOfStudents: 4, students: ['Edith', 'Jacob', 'Peter', 'Betty'] } ] let courseStudents = courses.map(course => course.students) // [ // [ 'Janet', 'Martha', 'Bob', [ 'Phil', 'Candace' ] ], // [ 'Wilson', 'Taylor' ], // [ 'Edith', 'Jacob', 'Peter', 'Betty' ] // ] [].concat.apply([], courseStudents) // we're stuck doing something like this

In comes Array.prototype.flat. It takes an optional argument of depth.

let courseStudents = [ [ 'Janet', 'Martha', 'Bob', [ 'Phil', 'Candace' ] ], [ 'Wilson', 'Taylor' ], [ 'Edith', 'Jacob', 'Peter', 'Betty' ] ] let flattenOneLevel = courseStudents.flat(1) console.log(flattenOneLevel) // [ // 'Janet', // 'Martha', // 'Bob', // [ 'Phil', 'Candace' ], // 'Wilson', // 'Taylor', // 'Edith', // 'Jacob', // 'Peter', // 'Betty' // ] let flattenTwoLevels = courseStudents.flat(2) console.log(flattenTwoLevels) // [ // 'Janet', 'Martha', // 'Bob', 'Phil', // 'Candace', 'Wilson', // 'Taylor', 'Edith', // 'Jacob', 'Peter', // 'Betty' // ]

Note that if no argument is given, the default depth is one. This is incredibly important because in our example that would not fully flatten the array.

let courseStudents = [ [ 'Janet', 'Martha', 'Bob', [ 'Phil', 'Candace' ] ], [ 'Wilson', 'Taylor' ], [ 'Edith', 'Jacob', 'Peter', 'Betty' ] ] let defaultFlattened = courseStudents.flat() console.log(defaultFlattened) // [ // 'Janet', // 'Martha', // 'Bob', // [ 'Phil', 'Candace' ], // 'Wilson', // 'Taylor', // 'Edith', // 'Jacob', // 'Peter', // 'Betty' // ]

The justification for this decision is that the function is not greedy by default and requires explicit instructions to operate as such. For an unknown depth with the intention of fully flattening the array the argument of Infinity can be used.

let courseStudents = [ [ 'Janet', 'Martha', 'Bob', [ 'Phil', 'Candace' ] ], [ 'Wilson', 'Taylor' ], [ 'Edith', 'Jacob', 'Peter', 'Betty' ] ] let alwaysFlattened = courseStudents.flat(Infinity) console.log(alwaysFlattened) // [ // 'Janet', 'Martha', // 'Bob', 'Phil', // 'Candace', 'Wilson', // 'Taylor', 'Edith', // 'Jacob', 'Peter', // 'Betty' // ]

As always, greedy operations should be used judiciously and are likely not a good choice if the depth of the array is truly unknown.

Support for Array.prototype.flat Chrome Firefox Safari Edge 75 67 12 No Chrome Android Firefox Android iOS Safari IE Mobile Samsung Internet Android Webview 75 67 12.1 No No 67 Array.prototype.flatMap

With the addition of flat we also got the combined function of Array.prototype.flatMap. We've actually already seen an example of where this would be useful above, but let's look at another one.

What about a situation where we want to insert elements into an array. Prior to the additions of ES2019, what would that look like?

let grades = [78, 62, 80, 64] let curved = grades.map(grade => [grade, grade + 7]) // [ [ 78, 85 ], [ 62, 69 ], [ 80, 87 ], [ 64, 71 ] ] let flatMapped = [].concat.apply([], curved) // now flatten, could use flat but that didn't exist before either // [ // 78, 85, 62, 69, // 80, 87, 64, 71 // ]

Now that we have Array.prototype.flat we can improve this example slightly.

let grades = [78, 62, 80, 64] let flatMapped = grades.map(grade => [grade, grade + 7]).flat() // [ // 78, 85, 62, 69, // 80, 87, 64, 71 // ]

But still, this is a relatively popular pattern, especially in functional programming. So having it built into the array prototype is great. With flatMap we can do this:

let grades = [78, 62, 80, 64] let flatMapped = grades.flatMap(grade => [grade, grade + 7]); // [ // 78, 85, 62, 69, // 80, 87, 64, 71 // ]

Now, remember that the default argument for Array.prototype.flat is one. And flatMap is the equivalent of combing map and flat with no argument. So flatMap will only flatten one level.

let grades = [78, 62, 80, 64] let flatMapped = grades.flatMap(grade => [grade, [grade + 7]]); // [ // 78, [ 85 ], // 62, [ 69 ], // 80, [ 87 ], // 64, [ 71 ] // ] Support for Array.prototype.flatMap Chrome Firefox Safari Edge 75 67 12 No Chrome Android Firefox Android iOS Safari IE Mobile Samsung Internet Android Webview 75 67 12.1 No No 67 String.trimStart and String.trimEnd

Another nice addition in ES2019 is an alias that makes some string function names more explicit. Previously, String.trimRight and String.trimLeft were available.

let message = " Welcome to CS 101 " message.trimRight() // ' Welcome to CS 101' message.trimLeft() // 'Welcome to CS 101 ' message.trimRight().trimLeft() // 'Welcome to CS 101'

These are great functions, but it was also beneficial to give them names that more aligned with their purpose. Removing starting space and ending space.

let message = " Welcome to CS 101 " message.trimEnd() // ' Welcome to CS 101' message.trimStart() // 'Welcome to CS 101 ' message.trimEnd().trimStart() // 'Welcome to CS 101' Support for String.trimStart and String.trimEnd Chrome Firefox Safari Edge 75 67 12 No Optional catch binding

Another nice feature in ES2019 is making an argument in try-catch blocks optional. Previously, all catch blocks passed in the exception as a parameter. That meant that it was there even when the code inside the catch block ignored it.

try { let parsed = JSON.parse(obj) } catch(e) { // ignore e, or use console.log(obj) }

This is no longer the case. If the exception is not used in the catch block, then nothing needs to be passed in at all.

try { let parsed = JSON.parse(obj) } catch { console.log(obj) }

This is a great option if you already know what the error is and are looking for what data triggered it.

Support for Optional Catch Binding Chrome Firefox Safari Edge 75 67 12 No Function.toString() changes

ES2019 also brought changes to the way Function.toString() operates. Previously, it stripped white space entirely.

function greeting() { const name = 'CSS Tricks' console.log(`hello from ${name}`) } greeting.toString() //'function greeting() {\nconst name = \'CSS Tricks\'\nconsole.log(`hello from ${name} //`)\n}'

Now it reflects the true representation of the function in source code.

function greeting() { const name = 'CSS Tricks' console.log(`hello from ${name}`) } greeting.toString() // 'function greeting() {\n' + // " const name = 'CSS Tricks'\n" + // ' console.log(`hello from ${name}`)\n' + // '}'

This is mostly an internal change, but I can’t help but think this might also make the life easier of a blogger or two down the line.

Support for Function.toString Chrome Firefox Safari Edge 75 60 12 - Partial 17 - Partial

And there you have it! The main feature additions to ES2019.

There are also a handful of other additions that you may want to explore. Those include:

Happy JavaScript coding!

The post All the New ES2019 Tips and Tricks appeared first on CSS-Tricks.

Site Monetization with Coil (and Removing Ads for Supporters)

Css Tricks - Mon, 08/12/2019 - 12:50pm

I've tried a handful of websites based on "tip with micropayments" in the past. They come and go. That's fine. From a publisher perspective, it's low-commitment. I've never earned a ton, but it was typically enough to be worth it.

Now Bruce has me trying Coil. It's compelling to me for a couple reasons:

  • The goal is to make it based on an actual web standard(!)
  • Coil is nicely designed. It's the service that readers actually subscribe to and a browser extension (for Chrome and Firefox) that pays publishers.
  • The money ends up in a Stronghold account1. I don't know much about those, but it was easy enough to set up and is also nicely designed.
  • Everything is anonymous. I don't have access to, know anything about, or store anything from the users who end up supporting the site with these micropayments.
  • Even though everyone is anonymous, I can still do things for the supporters, like not show ads.

It's a single tag on your site.

After signing up with Coil and having a Stronghold account, all you really need to do is put a <meta> tag in the <head> of your site. Here's mine:

<meta name="monetization" content="$pay.stronghold.co/1a1b91b23306ab547228c43af27ac0f2411">

Readers who have an active Coil subscription and are using the Coil browser extension will start sending micropayments to you, the publisher. Pretty cool.

Non-monetized site. Monetized site (and payments successful) Cash money

I've already made a dollar!

Since everything is anonymous, I didn't set up any logic to prevent injecting the meta tag if an admin is viewing the site. I bet it's mostly me paying myself. And Bruce.

The big hope is that this becomes a decent source of revenue once this coerces a web standard and lots of users choose to do it. My guess is it'll take years to get there if it does indeed become a winning player.

It's interesting thinking about the global economy as well. A dollar to me isn't the same as a dollar to everyone around the world. Less money goes a lot further in some parts of the world. This has the potential to unlock an income stream that perhaps things like advertising aren't as good at accounting for. I hear people who work in advertising talking about "bad geos" which literally means geographic places where advertisers avoid sending ad dollars.

Reward users for being supporters

Like I mentioned, this is completely anonymous. You can't exactly email people a free eBook or whatever for leaving a donation. But the browser itself can know if the current user is paying you or not.

It's essentially like... user isn't paying you:

document.monetization === undefined

User might be paying you, oh wait, hold on a second:

document.monetization && document.monetization.state === 'pending'

User is paying you:

document.monetization && document.monetization.state === 'started'

You can do whatever you want with that. Perhaps you can generate a secure download link on the fly if you really wanted to do something like give away an eBook or do some "subscriber only" content or whatever.

Not showing ads to supporters

Ads are generally powered by JavaScript anyway. In the global JavaScript for this site, I literally already have a function called csstricks.getAds(); which kicks off the process. That allows me to wrap that function call in some logic in case there are situations I don't even wanna bother kicking off the ad process, just like this.

if (showAdsLogic) { csstricks.getAds(); }

It's slightly tricky though, as document.monetization.state === 'started' doesn't just happen instantaneously. Fortunately, an event fires when that value changes:

if (document.monetization) { document.monetization.addEventListener("monetizationstart", event => { if (!document.monetization.state === "started") { getAds(); } }); } else { getAds(); }

And it can get a lot fancier: validating sessions, doing different things based on payment amounts, etc. Here's a setup from their explainer:

if (document.monetization) { document.monetization.addEventListener("monetizationstart", event => { // User has an open payment stream // Connect to backend to validate the session using the request id const { paymentPointer, requestId } = event.detail; if (!isValidSession(paymentPointer, requestId)) { console.error("Invalid requestId for monetization"); showAdvertising(); } }); document.monetization.addEventListener("monetizationprogress", event => { // A payment has been received // Connect to backend to validate the payment const { paymentPointer, requestId, amount, assetCode, assetScale } = event.detail; if ( isValidPayment(paymentPointer, requestId, amount, assetCode, assetScale) ) { // Hide ads for a period based on amount received suspendAdvertising(amount, assetCode, assetScale); } }); // Wait 30 seconds and then show ads if advertising is no longer suspended setTimeout(maybeShowAdvertising, 30000); } else { showAdvertising(); }

I'm finding the monetizationstart event takes a couple of seconds to fire, so it does take a while to figure out if a user is actively monetizing. A couple of seconds is quite a while to wait before starting to fetch ads, so I'm not entirely sure the best approach there. You might want to kick off the ad requests right away, then choose to inject them or not (or hide them or not) based on the results. Depending on how those ads are tracked, that might present false impressions or harm your click-through rate. Your mileage may vary.

How does the web standard stuff factor in?

Here's the proposal. I can't pretend to understand it all, but I would think the gist of it is that you wouldn't need a browser extension at all, because the concept is baked into the browser. And you don't need Coil either; it would be just one option among others.

1 I'm told more "wallets" are coming soon and that Stronghold won't be the only option forever.

The post Site Monetization with Coil (and Removing Ads for Supporters) appeared first on CSS-Tricks.

In Search of a Stack That Monitors the Quality and Complexity of CSS

Css Tricks - Mon, 08/12/2019 - 4:30am

Many developers write about how to maintain a CSS codebase, yet not a lot of them write about how they measure the quality of that codebase. Sure, we have excellent linters like StyleLint and CSSLint, but they only help at preventing mistakes at a micro level. Using a wrong color notation, adding a vendor prefix when you’re already using Autoprefixer, writing a selector in an inconsistent way... that kind of thing.

We’re constantly looking for ways to improve the way we write CSS: OOCSS, BEM, SMACSS, ITCSS, utility-first and more. But where other development communities seem to have progressed from just linters to tools like SonarQube and PHP Mess Detector, the CSS community still lacks tooling for deeper inspection than shallow lint rules. For that reason I have created Project Wallace, a suite of tools for inspecting and enforcing CSS quality.

What is Project Wallace?

At the core, Project Wallace is a group of tools that includes a command line interface, linter, analysis, and reporting

Here’s a quick rundown of those tools.

Command Line Interface

This lets you run CSS analytics on the command line and get statistics for any CSS that you feed it.

Example output for projectwallace.com Constyble Linter

This is a linter designed specifically for CSS. Based on the analytics that Wallace generates, you can set thresholds that should not be exceeded. For example, a single CSS rule should not contain more than 10 selectors, or the average selector complexity should not be higher than three.

Analysis

Extract-CSS does exactly what the name says: Extract all the CSS from a webpage, so we can send it over to projectwallace.com for analysis.

Reporting

All analysis from Extract CSS is sent over to projectwallace.com where a dashboard contains all of the reporting of that data. It’s similar to CSS Stats, but it tracks more metrics and stores the results over time and shows them in a dashboard. It also shows the differences between to points in time, and many, many more features.

A complexity analysis generated by projectwallace.com Analyzing CSS complexity

There aren’t many articles about CSS complexity but the one that Harry Roberts (csswizardry) wrote got stuck in my brain. The gist of it is that every CSS selector is basically a bunch of if-statements, which reminded me of taking computer science courses where I had to manually calculate cyclomatic complexity for methods. Harry’s article made perfect sense to me in the sense that we can write a module that calculates the complexity of a CSS selector — not to be confused with specificity, of course, because that’s a whole different can of worms when it comes to complexity.

Basically, complexity in CSS can appear in many forms, but here are the ones that I pay closest attention to when auditing a codebase:

The cyclomatic complexity of CSS selectors

Every part of a selector means another if-statement for the browser. Longer selectors are more complex than shorter ones. They are harder to debug, slower to parse for the browser and harder to override.

.my-selector {} /* 1 identifier */ .my #super [complex^="selector"] > with ~ many :identifiers {} /* 6 identifiers */ Declarations per ruleset (cohesion)

A ruleset with many declarations is more complex than a ruleset with a few declarations. The popularity of functional CSS frameworks like Tailwind and Tachyons is probably due to the relative "simplicity" of the CSS itself.

/* 1 rule, 1 declaration => cohesion = 1 */ .text-center { text-align: center; } /* 1 rule, 8 declarations => cohesion = (1 / 8) = 0.125 */ .button { background-color: blue; color: white; padding: 1em; border: 1px solid; display: inline-block; font-size: normal; font-weight: bold; text-decoration: none; } The number of source code lines

More code means more complexity. Every line of code that is written needs to be maintained and, as such, is included in the reporting.

Average selectors per rule

A rule usually contains 1 selector, but sometimes there are more. That makes it hard to delete certain parts of the CSS, making it more complex.

All of these metrics can be linted with Constyble, the CSS complexity linter that Project Wallace uses in its stack. After you’ve defined a baseline for your metrics, it’s a matter of installing Constyble and setting up a config file. Here’s an example of a config file that I’ve pulled directly from the Constyble readme file:

{ // Do not exceed 4095 selectors, otherwise IE9 will drop any subsequent rules "selectors.total": 4095, // We don't want ID selectors "selectors.id.total": 0, // If any other color than these appears, report an error! "values.colors.unique": ["#fff", "#000"] }

The cool part is that Constyble runs on your final CSS, so it does its thing only after all of your preprocessed work from Sass, Less, PostCSS or whatever you use. That way, we can do smart checks for the total amount of selectors or average selector complexity — and just like any linter, you can make this part of a build step where your build fails if there are any issues.

Takeaways from using Project Wallace

After using Project Wallace for a while now, I’ve found that it’s great for tracking complexity. But while it is mainly designed to do that, it’s also a great way to find subtle bugs in your CSS that linters may not find because of they’re checking preprocessed code. Here’s a couple of interesting things that I found:

  • I’ve stopped counting the amount of user stories in our sprints where we had to fix inconsistent colors on a website. Projects that are several years old and people entering and leaving the company: it’s a recipe for getting each and every brand color wrong on a website. Luckily, we implemented Constyble and Project Wallace to get stakeholder buy-in, because we were able to proof that the branding for our customer was spot on for newer projects. Constyble stops us from adding colors that are not in the styleguide.
    A color graph proving that our color game is spot on. Only a handful of colors and only those that originate from the client’s styleguide or in the codebase.
  • I have installed Project Wallace webhooks at all the projects that I worked on at one of my former employers. Any time that new CSS is added to a project, it sends the CSS over to projectwallace.com and it’s immediately visible in the projects’ dashboard. This makes it pretty easy to spot when a particular selector of media query was added to the CSS.
    "Hey, where did that orange go?" An example diff from projectwallace.com.
  • The CSS-Tricks redesign earlier this year meant a massive drop in complexity and filesize. Redesigns are awesome to analyze. It gives you the opportunity to take a small peek behind the scenes and figure out what and how the authors changed their CSS. Seeing what parts didn’t work for the site and new parts that do might teach you a thing or two about how rapidly CSS is moving forward.
  • A large international company based in the Netherlands once had more than 4,095 selectors in a single CSS file. I knew that they were growing aggressively in upcoming markets and that they had to support Internet Explorer 8+. IE9 stops reading all CSS after 4,095 selectors and so a good chunk of their CSS wasn’t applied in old IE browsers. I sent them an email and they verified the issue and fixed it right away by splitting the CSS into two files.
  • GitLab currently uses more than 70 unique font sizes. I’m pretty sure their typography system is complex, but this seems a little overly ambitious. Maybe it is because of some third party CSS, but that’s hard to tell.
    A subset of the 70+ unique font-sizes used at GitLab.
  • When inheriting a project from other developers, I take a look at the CSS analytics just to get a feel about the difficult bits of the project. Did they use !important a lot? Is the average ruleset size comprehensible, or did they throw 20+ declarations at each one of them? What is the average selector length, will they be hard to override? Not having to resort to .complex-selector-override\[class\][class][class]...[class] would be nice.
  • A neat trick for checking that your minification works is to let Constyble check that the Lines of Code metric is not larger than 1. CSS minification means that all CSS is put on a single line, so the Lines of Code should be equal to 1!
  • A thing that kept happening in another project of mine was that the minification broke down. I had no idea, until a Project Wallace diff showed me how a bunch of colors were suddenly written like #aaaaaa instead of #aaa. This isn’t a bad thing necessarily, but it happened for so many colors at the same time, that something had to be out of order. A quick investigation showed me that I made a mistake in the minification.
  • StackOverflow has four unique ways of writing the color white. This isn’t necessarily a bad thing, but it may be an indication of a broken CSS minifier or inconsistencies in the design system.
  • Facebook.com has more than 650 unique colors) in their CSS. A broken design system is starting to sound like a possibility for them, too.
  • A project for a former employer of mine showed input[type=checkbox]:checked+.label input[type=radio]+label:focus:after as the most complex selector. After inspecting carefully, we saw that this selector targets an input nested in another input. That’s not possible to do in HTML, and we figured that we must have forgotten a comma in our CSS. No linter warned us there.
  • Nesting in CSS preprocessors is cool, but can lead to buggy things, like @media (max-width: 670px) and (max-width: 670px), as I found in Syntax.fm.

This is the tip of the iceberg when it comes to Project Wallace. There is so much more to learn and discover once you start analyzing your CSS. Don’t just look at your own statistics, but also look at what others are doing.

I have used my Constyble configs as a conversation piece with less experienced developers to explain why their build failed on complex chunks of CSS. Talking with other developers about why we’re avoiding or promoting certain ways of writing CSS is helpful in transferring knowledge. And it helps me keep my feet on the ground too. Having to explain something that I’ve been doing for years to a PHP developer who just wanted to help out makes me re-think why I’m doing things the way I do.

My goal is not to tell anyone what is right or what is wrong in CSS, but to create the tools so that you can verify what works for you and your peers. Project Wallace is here to help us make sense of the CSS that we write.

The post In Search of a Stack That Monitors the Quality and Complexity of CSS appeared first on CSS-Tricks.

Moving Text on a Curved Path

Css Tricks - Fri, 08/09/2019 - 11:56am

There was a fun article in The New York Times the other day describing the fancy way Elizabeth Warren and her staff let people take a selfie with Warren. But... the pictures aren't actually selfies because they are taken by someone else. The article has this hilarious line of text that wiggles by on a curved line as you scroll down the page.

Let's look at how they did it.

Movie:

The curved line is drawn in SVG as a <path>, and the <text> is set upon it by a <textPath>:

<svg width="100%" height="160px" viewBox="0 0 1098.72 89.55"> <path id="curve" fill="transparent" d="M0.17,0.23c0,0,105.85,77.7,276.46,73.2s243.8-61.37,408.77-54.05c172.09,7.64,213.4,92.34,413.28,64.19"></path> <text width="100%" style="transform:translate3d(0,0,0);"> <textPath style="transform:translate3d(0,0,0);" alignment-baseline="top" xlink:href="#curve">*The pictures are not technically selfies.</textPath> </text> </svg>

The movement trick happens by adjusting the startOffset attribute of the textPath element.

I'm not 100% sure how they did it, but we can do some quick hacky math by watching the scroll position of the page and setting that attribute in a way that makes it move about as fast and far as we want.

const textPath = document.querySelector("#text-path"); const h = document.documentElement, b = document.body, st = 'scrollTop', sh = 'scrollHeight'; document.addEventListener("scroll", e => { let percent = (h[st]||b[st]) / ((h[sh]||b[sh]) - h.clientHeight) * 100; textPath.setAttribute("startOffset", (-percent * 40) + 1200) });

Here's a demo:

See the Pen
Selfie Crawl
by Chris Coyier (@chriscoyier)
on CodePen.

Text on a curved line is a cool design look for any number of reasons! It just isn't seen that much on the web, so when it is used, it stands out.

See the Pen
CodePen Challenge: Hearthstone Card
by wheatup (@wheatup)
on CodePen.

The post Moving Text on a Curved Path appeared first on CSS-Tricks.

Building a Full-Stack Serverless Application with Cloudflare Workers

Css Tricks - Fri, 08/09/2019 - 4:42am

One of my favorite developments in software development has been the advent of serverless. As a developer who has a tendency to get bogged down in the details of deployment and DevOps, it's refreshing to be given a mode of building web applications that simply abstracts scaling and infrastructure away from me. Serverless has made me better at actually shipping projects!

That being said, if you're new to serverless, it may be unclear how to translate the things that you already know into a new paradigm. If you're a front-end developer, you may have no experience with what serverless purports to abstract away from you – so how do you even get started?

Today, I'll try to help demystify the practical part of working with serverless by taking a project from idea to production, using Cloudflare Workers. Our project will be a daily leaderboard, called "Repo Hunt" inspired by sites like Product Hunt and Reddit, where users can submit and upvote cool open-source projects from GitHub and GitLab. You can see the final version of the site, published here.

Workers is a serverless application platform built on top of Cloudflare's network. When you publish a project to Cloudflare Workers, it's immediately distributed across 180 (and growing) cities around the world, meaning that regardless of where your users are located, your Workers application will be served from a nearby Cloudflare server with extremely low latency. On top of that, the Workers team has gone all-in on developer experience: our newest release, at the beginning of this month, introduced a fully-featured command line tool called Wrangler, which manages building, uploading, and publishing your serverless applications with a few easy-to-learn and powerful commands.

The end result is a platform that allows you to simply write JavaScript and deploy it to a URL – no more worrying about what "Docker" means, or if your application will fall over when it makes it to the front page of Hacker News!

If you're the type that wants to see the project ahead of time, before hopping into a long tutorial, you're in luck! The source for this project is available on GitHub. With that, let's jump in to the command-line and build something rad.

Installing Wrangler and preparing our workspace

Wrangler is the command-line tool for generating, building, and publishing Cloudflare Workers projects. We've made it super easy to install, especially if you've worked with npm before:

npm install -g @cloudflare/wrangler

Once you've installed Wrangler, you can use the generate command to make a new project. Wrangler projects use "templates" which are code repositories built for re-use by developers building with Workers. We maintain a growing list of templates to help you build all kind of projects in Workers: check out our Template Gallery to get started!

In this tutorial, we'll use the "Router" template, which allows you to build URL-based projects on top of Workers. The generate command takes two arguments: first, the name of your project (I'll use repo-hunt), and a Git URL. This is my favorite part of the generate command: you can use all kinds of templates by pointing Wrangler at a GitHub URL, so sharing, forking, and collaborating on templates is super easy. Let's run the generate command now:

wrangler generate repo-hunt https://github.com/cloudflare/worker-template-router cd repo-hunt

The Router template includes support for building projects with webpack, so you can add npm modules to your project, and use all the JavaScript tooling you know and love. In addition, as you might expect, the template includes a Router class, which allows you to handle routes in your Worker, and tie them to a function. Let's look at a simple example: setting up an instance of Router, handling a GET request to /, and returning a response to the client:

// index.js const Router = require('./router') addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)) }) async function handleRequest(request) { try { const r = new Router() r.get('/', () => new Response("Hello, world!")) const resp = await r.route(request) return resp } catch (err) { return new Response(err) } }

All Workers applications begin by listening to the fetch event, which is an incoming request from a client to your application. Inside of that event listener, it's common practice to call a handleRequest function, which looks at the incoming request and determines how to respond. When handling an incoming fetch event, which indicates an incoming request, a Workers script should always return a Response back to the user: it's a similar request/response pattern to many web frameworks, like Express, so if you've worked with web frameworks before, it should feel quite familiar!

In our example, we'll make use of a few routes: a "root" route (/), which will render the homepage of our site; a form for submitting new repos, at /post, and a special route for accepting POST requests, when a user submits a repo from the form, at /repo.

Building a route and rendering a template

The first route that we'll set up is the "root" route, at the path /. This will be where repos submitted by the community will be rendered. For now, let's get some practice defining a route, and returning plain HTML. This pattern is common enough in Workers applications that it makes sense to understand it first, before we move on to some more interesting bits!

To begin, we'll update index.js to set up an instance of a Router, handle any GET requests to /, and call the function index, from handlers/index.js (more on that shortly):

// index.js const Router = require('./router') const index = require('./handlers/index') addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)) }) function handleRequest(request) { try { const r = new Router() r.get('/', index) return r.route(request) } catch (err) { return new Response(err) } }

As with the example index.js in the previous section, our code listens for a fetch event, and responds by calling the handleRequest function. The handleRequest function sets up an instance of Router, which will call the index function on any GET requests to /. With the router setup, we route the incoming request, using r.route, and return it as the response to the client. If anything goes wrong, we simply wrap the content of the function in a try/catch block, and return the err to the client (a note here: in production applications, you may want something more robust here, like logging to an exception monitoring tool).

To continue setting up our route handler, we'll create a new file, handlers/index.js, which will take the incoming request and return a HTML response to the client:

// handlers/index.js const headers = { 'Content-Type': 'text/html' } const handler = () => { return new Response("Hello, world!", { headers }) } module.exports = handler

Our handler function is simple: it returns a new instance of Response with the text "Hello, world!" as well as a headers object that sets the Content-Type header to text/html – this tells the browser to render the incoming response as an HTML document. This means that when a client makes a GET request to the route /, a new HTML response will be constructed with the text "Hello, world!" and returned to the user.

Wrangler has a preview function, perfect for testing the HTML output of our new function. Let's run it now to ensure that our application works as expected:

wrangler preview

The preview command should open up a new tab in your browser, after building your Workers application and uploading it to our testing playground. In the Preview tab, you should see your rendered HTML response:

With our HTML response appearing in browser, let's make our handler function a bit more exciting, by returning some nice looking HTML. To do this, we'll set up a corresponding index "template" for our route handler: when a request comes into the index handler, it will call the template and return an HTML string, to give the client a proper user interface as the response. To start, let's update handlers/index.js to return a response using our template (and, in addition, set up a try/catch block to catch any errors, and return them as the response):

// handlers/index.js const headers = { 'Content-Type': 'text/html' } const template = require('../templates/index') const handler = async () => { try { return new Response(template(), { headers }) } catch (err) { return new Response(err) } } module.exports = handler

As you might imagine, we need to set up a corresponding template! We'll create a new file, templates/index.js, and return an HTML string, using ES6 template strings:

// templates/index.js const template = () => { return <code><h1>Hello, world!</h1>` } module.exports = template

Our template function returns a simple HTML string, which is set to the body of our Response, in handlers/index.js. For our final snippet of templating for our first route, let's do something slightly more interesting: creating a templates/layout.js file, which will be the base "layout" that all of our templates will render into. This will allow us to set some consistent styling and formatting for all the templates. In templates/layout.js:

// templates/layout.js const layout = body => ` <!doctype html> <html> <head> <meta charset="UTF-8"> <title>Repo Hunt</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.5/css/bulma.min.css"> </head> <body> <div class="container"> <div class="navbar"> <div class="navbar-brand"> Repo Hunt Find cool open-source projects daily </div> <div class="navbar-menu"> <div class="navbar-end"> <div class="navbar-item"> Post a repository </div> </div> </div> </div> <div class="section"> ${body} </div> </div> </body> </html> ` module.exports = layout

This is a big chunk of HTML code, but breaking it down, there's only a few important things to note: first, this layout variable is a function! A body variable is passed in, intended to be included inside of a div right in the middle of the HTML snippet. In addition, we include the Bulmahttps://bulma.io) CSS frameworkhttps://bulma.io), for a bit of easy styling in our project, and a navigation bar, to tell users *what* this site is, with a link to submit new repositories.

To use our layout template, we'll import it in templates/index.js, and wrap our HTML string with it:

// templates/index.js const layout = require('./layout') const template = () => { return layout(`<h1>Hello, world!</h1>`) } module.exports = template

With that, we can run wrangler preview again, to see our nicely rendered HTML page, with a bit of styling help from Bulma:

Storing and retrieving data with Workers KV

Most web applications aren't very useful without some sort of data persistence. Workers KV is a key-value store built for use with Workers – think of it as a super-fast and globally distributed Redis. In our application, we'll use KV to store all of the data for our application: each time a user submits a new repository, it will be stored in KV, and we'll also generate a daily array of repositories to render on the home page.

A quick note: at the time of writing, usage of Workers KV requires a paid Workers plan. Read more in the "Pricing" section of the Workers docs here.

Inside of a Workers application, you can refer to a pre-defined KV namespace, which we'll create inside of the Cloudflare UI, and bind to our application once it's been deployed to the Workers application. In this tutorial, we'll use a KV namespace called REPO_HUNT, and as part of the deployment process, we'll make sure to attach it to our application, so that any references in the code to REPO_HUNT will correctly resolve to the KV namespace.

Before we hop into creating data inside of our namespace, let's look at the basics of working with KV inside of your Workers application. Given a namespace (e.g. REPO_HUNT), we can set a key with a given value, using put:

const string = "Hello, world!" REPO_HUNT.put("myString", string)

We can also retrieve the value for that key, by using async/await and waiting for the promise to resolve:

const getString = async () => { const string = await REPO_HUNT.get("myString") console.log(string) // "Hello, world!" }

The API is super simple, which is great for web developers who want to start building applications with the Workers platform, without having to dive into relational databases or any kind of external data service. In our case, we'll store the data for our application by saving:

  1. A repo object, stored at the key repos:$id, where $id is a generated UUID for a newly submitted repo.
  2. A day array, stored at the key $date (e.g. "6/24/2019"), containing a list of repo IDs, which indicate the submitted repos for that day.

We'll begin by implementing support for submitting repositories, and making our first writes to our KV namespace by saving the repository data in the object we specified above. Along the way, we'll create a simple JavaScript class for interfacing with our store – we'll make use of that class again, when we move on to rendering the homepage, where we'll retrieve the repository data, build a UI, and finish our example application.

Allowing user-submitted data

No matter what the application is, it seems that web developers always end up having to write forms. In our case, we'll build a simple form for users to submit repositories.

At the beginning of this tutorial, we set up index.js to handle incoming GET requests to the root route (`/). To support users adding new repositories, we'll add another route, GET /post, which will render a form template to users. In index.js:

// index.js // ... const post = require('./handlers/post') // ... function handleRequest(request) { try { const r = new Router() r.get('/', index) r.get('/post', post) return r.route(request) } catch (err) { return new Response(err) } }

In addition to a new route handler in index.js, we'll also add handlers/post.js, a new function handler that will render an associated template as an HTML response to the user:

// handlers/post.js const headers = { 'Content-Type': 'text/html' } const template = require('../templates/post') const handler = request => { try { return new Response(template(), { headers }) } catch (err) { return new Response(err) } } module.exports = handler

The final piece of the puzzle is the HTML template itself – like our previous template example, we'll re-use the layout template we've built, and wrap a simple three-field form with it, exporting the HTML string from templates/post.js:

// templates/post.js const layout = require('./layout') const template = () => layout(` <div> <h1>Post a new repo</h1> <form action="/repo" method="post"> <div class="field"> <label class="label" for="name">Name</label> <input class="input" id="name" name="name" type="text" placeholder="Name" required></input> </div> <div class="field"> <label class="label" for="description">Description</label> <input class="input" id="description" name="description" type="text" placeholder="Description"></input> </div> <div class="field"> <label class="label" for="url">URL</label> <input class="input" id="url" name="url" type="text" placeholder="URL" required></input> </div> <div class="field"> <div class="control"> <button class="button is-link" type="submit">Submit</button> </div> </div> </form> </div> <code>) module.exports = template

Using wrangler preview, we can navigate to the path /post and see our rendered form:

If you look at the definition of the actual form tag in our template, you'll notice that we're making a POST request to the path /repo. To receive the form data, and persist it into our KV store, we'll go through the process of adding another handler. In index.js:

// index.js // ... const create = require('./handlers/create') // ... function handleRequest(request) { try { const r = new Router() r.get('/', index) r.get('/post', post) r.post('/repo', create) return r.route(request) } catch (err) { return new Response(err) } }

When a form is sent to an endpoint, it's sent as a query string. To make our lives easier, we'll include the qs library in our project, which will allow us to simply parse the incoming query string as a JS object. In the command line, we'll add qs simply by using npm. While we're here, let's also install the node-uuid package, which we'll use later to generate IDs for new incoming data. To install them both, use npm's install --save subcommand:

npm install --save qs uuid

With that, we can implement the corresponding handler function for POST /repo. In handlers/create.js:

// handlers/create.js const qs = require('qs') const handler = async request => { try { const body = await request.text() if (!body) { throw new Error('Incorrect data') } const data = qs.parse(body) // TODOs: // - Create repo // - Save repo // - Add to today's repos on the homepage return new Response('ok', { headers: { Location: '/' }, status: 301 }) } catch (err) { return new Response(err, { status: 400 }) } } module.exports = handler

Our handler function is pretty straightforward — it calls text on the request, waiting for the promise to resolve to get back our query string body. If no body element is provided with the request, the handler throws an error (which returns with a status code of 400, thanks to our try/catch block). Given a valid body, we call parse on the imported qs package, and get some data back. For now, we've stubbed out our intentions for the remainder of this code: first, we'll create a repo, based on the data. We'll save that repo, and then add it to the array of today's repos, to be rendered on the home page.

To write our repo data into KV, we'll build two simple ES6 classes, to do a bit of light validation and define some persistence methods for our data types. While you could just call REPO_HUNT.put directly, if you're working with large amounts of similar data, it can be nice to do something like new Repo(data).save() - in fact, we'll implement something almost exactly like this, so that working with a Repo is incredibly easy and consistent.

Let's define store/repo.js, which will contain a Repo class. With this class, we can instantiate new Repo objects, and using the constructor method, we can pass in data, and validate it, before continuing to use it in our code.

// store/repo.js const uuid = require('uuid/v4') class Repo { constructor({ id, description, name, submitted_at, url }) { this.id = id || uuid() this.description = description if (!name) { throw new Error(`Missing name in data`) } else { this.name = name } this.submitted_at = submitted_at || Number(new Date()) try { const urlObj = new URL(url) const whitelist = ['github.com', 'gitlab.com'] if (!whitelist.some(valid => valid === urlObj.host)) { throw new Error('The URL provided is not a repository') } } catch (err) { throw new Error('The URL provided is not valid') } this.url = url } save() { return REPO_HUNT.put(`repos:${this.id}`, JSON.stringify(this)) } } module.exports = Repo

Even if you aren't super familiar with the constructor function in an ES6 class, this example should still be fairly easy to understand. When we want to create a new instance of a Repo, we pass the relevant data to constructor as an object, using ES6's destructuring assignment to pull each value out into its own key. With those variables, we walk through each of them, assigning this.$key (e.g. this.name, this.description, etc) to the passed-in value.

Many of these values have a "default" value: for instance, if no ID is passed to the constructor, we'll generate a new one, using our previously-saved uuid package's v4 variant to generate a new UUID, using uuid(). For submitted_at, we'll generate a new instance of Date and convert it to a Unix timestamp, and for url, we'll insure that the URL is both valid *and* is from github.com or gitlab.com to ensure that users are submitting genuine repos.

With that, the save function, which can be called on an instance of Repo, inserts a JSON-stringified version of the Repo instance into KV, setting the key as repos:$id. Back in handlers/create.js, we'll import the Repo class, and save a new Repo using our previously parsed data:

// handlers/create.js // ... const Repo = require('../store/repo') const handler = async request => { try { // ... const data = qs.parse(body) const repo = new Repo(data) await repo.save() // ... } catch (err) { return new Response(err, { status: 400 }) } } // ...

With that, a new Repo based on incoming form data should actually be persisted into Workers KV! While the repo is being saved, we also want to set up another data model, Day, which contains a simple list of the repositories that were submitted by users for a specific day. Let's create another file, store/day.js, and flesh it out:

// store/day.js const today = () => new Date().toLocaleDateString() const todayData = async () => { const date = today() const persisted = await REPO_HUNT.get(date) return persisted ? JSON.parse(persisted) : [] } module.exports = { add: async function(id) { const date = today() let ids = await todayData() ids = ids.concat(id) return REPO_HUNT.put(date, JSON.stringify(ids)) } }

Note that the code for this isn't even a class — it's an object with key-value pairs, where the values are functions! We'll add more to this soon, but the single function we've defined, add, loads any existing repos from today's date (using the function today to generate a date string, used as the key in KV), and adds a new Repo, based on the id argument passed into the function. Back inside of handlers/create.js, we'll make sure to import and call this new function, so that any new repos are added immediately to today's list of repos:

// handlers/create.js // ... const Day = require('../store/day') // ... const handler = async request => { try { // ... await repo.save() await Day.add(repo.id) return new Response('ok', { headers: { Location: '/' }, status: 301 }) } catch (err) { return new Response(err, { status: 400 }) } } // ...

Our repo data now persists into KV and it's added to a listing of the repos submitted by users for today's date. Let's move on to the final piece of our tutorial, to take that data, and render it on the homepage.

Rendering data

At this point, we've implemented rendering HTML pages in a Workers application, as well as taking incoming data, and persisting it to Workers KV. It shouldn't surprise you to learn that taking that data from KV, and rendering an HTML page with it, our homepage, is quite similar to everything we've done up until now. Recall that the path / is tied to our index handler: in that file, we'll want to load the repos for today's date, and pass them into the template, in order to be rendered. There's a few pieces we need to implement to get that working – to start, let's look at handlers/index.js:

// handlers/index.js // ... const Day = require('../store/day') const handler = async () => { try { let repos = await Day.getRepos() return new Response(template(repos), { headers }) } catch (err) { return new Response(`Error! ${err} for ${JSON.stringify(repos)}`) } } // ...

While the general structure of the function handler should stay the same, we're now ready to put some genuine data into our application. We should import the Day module, and inside of the handler, call await Day.getRepos to get a list of repos back (don't worry, we'll implement the corresponding functions soon). With that set of repos, we pass them into our template function, meaning that we'll be able to actually render them inside of the HTML.

Inside of Day.getRepos, we need to load the list of repo IDs from inside KV, and for each of them, load the corresponding repo data from KV. In store/day.js:

// store/day.js const Repo = require('./repo') // ... module.exports = { getRepos: async function() { const ids = await todayData() return ids.length ? Repo.findMany(ids) : [] }, // ... }

The getRepos function reuses our previously defined todayData function, which returns a list of ids. If that list has *any* IDs, we want to actually retrieve those repositories. Again, we'll call a function that we haven't quite defined yet, importing the Repo class and calling Repo.findMany, passing in our list of IDs. As you might imagine, we should hop over to store/repo.js, and implement the accompanying function:

// store/repo.js class Repo { static findMany(ids) { return Promise.all(ids.map(Repo.find)) } static async find(id) { const persisted = await REPO_HUNT.get(`repos:${id}`) const repo = JSON.parse(persisted) return persisted ? new Repo({ ...repo }) : null } // ... }

To support finding all the repos for a set of IDs, we define two class-level or static functions, find and findMany which uses Promise.all to call find for each ID in the set, and waits for them all to finish before resolving the promise. The bulk of the logic, inside of find, looks up the repo by its ID (using the previously-defined key, repos:$id), parses the JSON string, and returns a newly instantiated instance of Repo.

Now that we can look up repositories from KV, we should take that data and actually render it in our template. In handlers/index.js, we passed in the repos array to the template function defined in templates/index.js. In that file, we'll take that repos array, and render chunks of HTML for each repo inside of it:

// templates/index.js const layout = require('./layout') const dateFormat = submitted_at => new Date(submitted_at).toLocaleDateString('en-us') const repoTemplate = ({ description, name, submitted_at, url }) => `<div class="media"> <div class="media-content"> <p> ${name} </p> <p> ${description} </p> <p> Submitted ${dateFormat(submitted_at)} </p> </div> </div> ` const template = repos => { const renderedRepos = repos.map(repoTemplate) return layout(` <div> ${ repos.length ? renderedRepos.join('') : `<p>No repos have been submitted yet!</p>` } </div> `) } module.exports = template

Breaking this file down, we have two primary functions: template (an updated version of our original exported function), which takes an array of repos, maps through them, calling repoTemplate, to generate an array of HTML strings. If repos is an empty array, the function simply returns a p tag with an empty state. The repoTemplate function uses destructuring assignment to set the variables description, name, submitted_at, and url from inside of the repo object being passed to the function, and renders each of them into fairly simple HTML, leaning on Bulma's CSS classes to quickly define a media object layout.

And with that, we're done writing code for our project! After coding a pretty comprehensive full-stack application on top of Workers, we're on the final step: deploying the application to the Workers platform.

Deploying your site to workers.dev

Every Workers user can claim a free Workers.dev subdomain, after signing up for a Cloudflare account. In Wrangler, we've made it super easy to claim and configure your subdomain, using the subdomain subcommand. Each account gets one Workers.dev subdomain, so choose wisely!

wrangler subdomain my-cool-subdomain

With a configured subdomain, we can now deploy our code! The name property in wrangler.toml will indicate the final URL that our application will be deployed to: in my codebase, the name is set to repo-hunt, and my subdomain is signalnerve.workers.dev, so my final URL for my project will be repo-hunt.signalnerve.workers.dev. Let's deploy the project, using the publish command:

wrangler publish

Before we can view the project in browser, we have one more step to complete: going into the Cloudflare UI, creating a KV namespace, and binding it to our project. To start this process, log into your Cloudflare dashboard, and select the "Workers" tab on the right side of the page.

Inside of the Workers section of your dashboard, find the "KV" menu item, and create a new namespace, matching the namespace you used in your codebase (if you followed the code samples, this will be REPO_HUNT).

In the listing of KV namespaces, copy your namespace ID. Back in our project, we'll add a `kv-namespaces` key to our `wrangler.toml`, to use our new namespace in the codebase:

# wrangler.toml [[kv-namespaces]] binding = "REPO_HUNT" id = "$yourNamespaceId"

To make sure your project is using the new KV namespace, publish your project one last time:

wrangler publish

With that, your application should be able to successfully read and write from your KV namespace. Opening my project's URL should show the final version of our project — a full, data-driven application without needing to manage any servers, built entirely on the Workers platform!

What's next?

In this tutorial, we built a full-stack serverless application on top of the Workers platform, using Wrangler, Cloudflare's command-line tool for building and deploying Workers applications. There's a ton of things that you could do to continue to add to this application: for instance, the ability to upvote submissions, or even to allow comments and other kinds of data. If you'd like to see the finished codebase for this project, check out the GitHub repo!

The Workers team maintains a constantly growing list of new templates to begin building projects with – if you want to see what you can build, make sure to check out our Template Gallery. In addition, make sure to check out some of the tutorials in the Workers documentation, such as building a Slack bot, or a QR code generator.

If you went through the whole tutorial (or if you're building cool things you want to share), I'd love to hear about how it went on Twitter. If you’re interested in serverless and want to keep up with any new tutorials I’m publishing, make sure to join my newsletter and subscribe to my YouTube channel!

The post Building a Full-Stack Serverless Application with Cloudflare Workers appeared first on CSS-Tricks.

Weekly Platform News: CSS font-style: oblique, webhint browser extension, CSS Modules V1

Css Tricks - Thu, 08/08/2019 - 1:12pm

In this week's roundup, variable fonts get oblique, a new browser extension for linting, and the very first version of CSS Modules.

Use font-style: oblique on variable fonts

Some popular variable fonts have a 'wght' (weight) axis for displaying text at different font weights and a 'slnt' (slant) axis for displaying slanted text. This enables creating many font styles using a single variable font file (e.g., see the "Variable Web Typography" demo page).

You can use font-style: oblique instead of the lower-level font-variation-settings property to display slanted text in variable fonts that have a 'slnt' axis. This approach works in Chrome, Safari, and Firefox.

/* BEFORE */ h2 { font-variation-settings: "wght" 500, "slnt" 4; } /* AFTER */ h2 { font-weight: 500; font-style: oblique 4deg; }

See the Pen
Using font-style: oblique on variable fonts
by Šime Vidas (@simevidas)
on CodePen.

The new webhint browser extension

The webhint linting tool is now available as a browser devtools extension for Chrome, Edge, and Firefox (read Microsoft’s announcement). Compared to Lighthouse, one distinguishing feature of webhint are its cross-browser compatibility hints.

In other news...
  • CSS Modules V1 is a new proposal from Microsoft that would extend the JavaScript modules infrastructure to allow importing a CSSStyleSheet object from a CSS file (e.g., import styles from "styles.css";) (via Thomas Steiner)
  • Web apps installed in the desktop version of Chrome can be uninstalled on the about:apps page (right-click on an app’s icon to reveal the Remove... option) (via Techdows)
  • Because of AMP’s unique requirements, larger news sites such as The Guardian should optimally have two separate codebases (one for the AMP pages and one for the regular website) (via The Guardian)

Read more news in my new, weekly Sunday issue. Visit webplatform.news for more information.

The post Weekly Platform News: CSS font-style: oblique, webhint browser extension, CSS Modules V1 appeared first on CSS-Tricks.

Design Principles for Developers: Processes and CSS Tips for Better Web Design

Css Tricks - Thu, 08/08/2019 - 4:25am

It is technically true that anyone can cook. But there’s a difference between actually knowing how to prepare a delicious meal and hoping for the best as you throw a few ingredients in a pot. Just like web development, you might know the ingredients—<span>, background-color, .heading-1—but not everyone knows how to turn those ingredients into a beautiful, easy-to-use website.

Whenever you use HTML and CSS, you are designing—giving form and structure to content so it can be understood by someone else. People have been designing for centuries and have developed principles along the way that are applicable to digital interfaces today. These principles manifest in three key areas: how words are displayed (typography), how content is arranged (spacing), and how personalty is added (color). Let’s discover how to use each of these web design ingredients through the mindset of a developer with CSS properties and guidelines to take the guesswork out of web design.

Table of Contents Typography

Websites that are easy to read don’t happen by mistake. In fact, Taimur Abdaal wrote an entire article on the topic that’s chock-full of advice for developers working with type. We’re going to focus specifically on two fundamental principles of design that can help you display words in a more visually pleasing and easy-to-read way: repetition and hierarchy.

Use repetition for consistency and maintainability

Repetition comes fairly naturally on the web thanks to the importance of reusability in software. For example, CSS classes allow you to define a particular style for text and then reuse that style across the site. This results in repeating, consistent text styles for similar content which helps users navigate the site.

If, for example, you are working on styles for a new paragraph, first consider if there is existing content that has a similar style and try to use the same CSS class. If not, you can create a new class with a generic name that can be repeated elsewhere in your site. Think .paragraph--emphasize as opposed to .footer__paragraph--emphasize or .heading-1 as opposed to .hero__site-title. The first examples can be used across your site as opposed to the second which are scoped to specific components. You can even add a prefix like text- to indicate that the class is used specifically for text styles. This method will reduce the CSS file size and complexity while making it much easier to update global styles in the future.

Left: The black text is similar but uses a slightly different font size and line height. Right: The black text uses the same styles and therefore can use the same CSS class. Reducing the amount of CSS needed and adding repetition and consistency.

In design, there are endless ways to experiment with styles. Designers can sometimes go a little crazy with their font styles by creating numerous slight variations of similar styles. However, in code, it’s valuable to restrict text styles to a minimum. Developers should urge designers to combine similar styles in order to reduce code weight and increase reusability and consistency.

These headings look very similar but are slightly different and would require three separate CSS classes to style them. They could probably be combined into one and styled with a single class. Hierarchy provides a clear visual order to content

Hierarchy is something you really only notice when it’s not there. In typography, hierarchy refers to the visual difference between various pieces of text. It’s the distinction between headings, paragraphs, links, and other text styles. This distinction is made by choosing different fonts, colors, size, capitalization, and other properties for each type of text content. Good hierarchy makes complex information easier to digest and guides users through your content.

Left: Poor hierarchy. There’s not much differentiation in the size or color of the text to help users digest the content. Right: Better hierarchy that uses more variety in font size, color, and spacing to help users quickly navigate the content.

Out of the box, HTML provides some hierarchy (like how the font size of headings gets smaller as you go from <h1> to <h6>), but CSS opens the door for even more creativity. By giving <h1> tags an even larger font size, you can quickly establish greater difference in size between heading levels—and therefore more hierarchy. To create more variety, you can also change the color, text-align, and text-transform properties.

A comparison of the way HTML headings look without styles versus adding more contrast with CSS. A note on choosing fonts

With typography, we need to make sure it is as easy to read as possible. The greatest overall factor in readability is the font you choose—which is a huge topic. There are many factors that determine how "readable" a font is. Some fonts are made specifically to be used as headings or short lines of text; these are called "display" fonts, and they often have more personality than fonts designed to be used for text. Unique flourishes and quirks make display fonts harder to read at small sizes and when part of a large paragraph. As a rule of thumb, use a more straightforward font for text and only use display fonts for headings.

Left: Examples of display fonts that work better as headings. Right: Examples of text fonts that are more readable and can be used for headings, paragraphs, and any other text that needs to be easy to read.

If you’re in a pinch and need a readable font, try Google Fonts. Add a paragraph of text to the preview field and size it roughly how it will display on your website. You can then narrow down your results to serif or sans-serif and scan the list of fonts for one that is easy to read. Roboto, Noto Sans, Merriweather, and PT Serif are all very readable options.

CSS properties for better readability
  • The main paragraph font-size should be between 16pxand 18px (1em and 1.25em) depending on the font you choose.
  • Manually set line-height (the vertical space between two lines of text) to make your text less cramped and easier to read. Start with line-height: 1.25 (that is 1.25 times the font-size) for headings and at least 1.5 for paragraphs (but no more than 1.9) and adjust from there. The longer the line of text, the larger the line-height should be. To keep your text flexible, avoid adding a unit to your line-height. Without a unit the line-height you set will be proportional to your font-size. For example, line-height: 1.5 and font-size: 18px would give you a line height of 27 pixels. If you changed your font size to font-size: 16px on smaller screens, the computed line height would then change to 24 pixels automatically.
Left: line-height is 1.1 for the heading and 1.2 for the paragraph, which is roughly the default setting. Right: line-height is 1.25 for the headings and 1.5 for the paragraph.
  • Pay attention to how many characters are in a line of text and aim for 45 and 75 characters long (including punctuation and spaces). Doing so reduces reading fatigue for your users by limiting the eye and head movement needed to follow a line of text. With the variable nature of the web, it’s impossible to completely control line length, but you can use max-width values and breakpoints to prevent lines of text from getting too long. Generally speaking, the shorter the line of text, the faster it will be to scan for quick reading. And don’t worry too much about counting the characters in every line. Once you do it a few times, you’ll develop a sense for what looks right.
Top: line length of around 125 characters. Bottom: line length of around 60 characters. Spacing

After looking at typography, you can take a step back and examine the layout, or spacing, of your content. Movement and proximity are two design principles that relate to spacing.

Movement is about content flow

Movement refers to how your eye moves through the page or the flow of the page. You can use movement to direct a user’s eye in order to tell a story, point to a main action item, or encourage them to scroll. This is done by structuring the content within individual components and then arranging those components to form the layout of the page. By paying attention to how your eye moves through content, you can help users know where to look as they scan the page.

Unlike books, which tend to have very linear structure, websites can be more creative with their layout—in literally endless ways. It is important to make sure you are intentional with how you layout content and do so in a way which guides users through your content as easily as possible.

Three potential ways to arrange a heading, image, and button.

Consider these three examples above. Which is the easiest to follow? The arrangement on the left draws your eye off the screen to the left due to how the image is positioned which makes it hard to find the button. In the center option, it’s easy to skip over the headline because the image is too large in comparison. On the right, the heading draws your attention first and the image is composed so that it points to the main action item—the button.

White space is a helpful tool for creating strong movement, but it’s easy to use too much or too little. Think about how you are using it to direct the user’s eye and divide your content. When used well, users won’t notice the whitespace itself but will be able to better focus on the content you are presenting. For example, you can use whitespace to separate content (instead of a colored box) which results in a less cluttered layout.

Left: Using a graphic element to separate content and aligning images in the center. Right: Using whitespace to separate content and aligning images on the left to let the whitespace flow better around groups of related content and create a cleaner layout. Proximity establishes relationships

When objects are closer together, they are perceived as being related. By controlling spacing around elements, you can imply relationships between them. It can be helpful to create a system for spacing to help build consistency through repetition and avoid the use of random numbers. This system is based off the default browser font size (1rem or 16px) and uses distinct values that cover most scenarios:

  • 0.25rem (4px)
  • 0.5rem (8px)
  • 1rem (16px)
  • 2rem (32px)
  • 4rem (64px)

You can use Sass or CSS variables so that the values are kept consistent across the project. A system might look like this—but use whatever you’re comfortable with because naming things is hard:

  • $space-sm
  • $space-med
  • $space-lg
  • $space-xl
  • $space-xxl
Left: A component with uneven spacing between elements. Right: A component that uses consistent spacing. Color conveys personality and calls attention

Color greatly affects a website’s personality. When used well, it gives pages life and emotion; used poorly, it can distract from the content, or worse, make it inaccessible. Color goes hand in hand with most design principles. It can be used to create movement by directing users’ eyes and can be used to create emphasis by calling attention to the most important action items.

A note on choosing colors

With color, it can be hard to know where to start. To help, you can use a four-step process to guide your color choices and build a color palette for the site.

Step 1: Know your mood

You have to know the mood or attitude of your site and brand before choosing colors. Look at your content and decide what you are trying to communicate. Is it funny, informative, retro, loud, somber? Typically, you can boil down the mood of your site to a few adjectives. For example, you might summarize The North Face as adventurous and rugged while Apple would be minimalistic and beautiful.

Step 2: Find your main color

With your mood in mind, try to visualize a color that represents it. Start with the color’s saturation (how intense the color is) and brightness (how close the color is to white or black). If your mood is upbeat or flashy, a lighter (more saturated) color is probably best. If your mood is serious or reserved, a darker (less saturated) color is better.

Next, choose a hue. Hue refers to what most people think of as colors—where does is fall on the rotation of the color wheel? The hue of a color is what gives it the most meaning. People tend to associate hues with certain ideas. For instance, red is often associated with power or danger and green relates to money or nature. It can be helpful to look at similar websites or brands to see what colors they use—although you don’t need to follow their lead. Don’t be afraid to experiment!

Color wheel showing saturation and brightness versus hue. Step 3: Add supporting colors

Sometimes, two or three main colors are needed, but this is not necessary. Think about the colors of different brands. Some use a single color, and others have a main color and one or two that support it. Coca-Cola uses its distinct red. IKEA is mostly blue with some yellow. Tide is orange with some blue and yellow. Depending on your site’s mood, you might need a few colors. Try using a tool like Adobe Color or Coolors), both of which allow you to add a main color and then try different color relationships, like complementary or monochromatic, to quickly see if any work well.

Step 4: Expand your palette

Now that you’ve narrowed things down and found your main color(s), it’s time to expand your scope with a palette that gives your project versatility and constraint—here’s a methodology I’ve found helpful. Tints and shades are the trick here. Tints are made by mixing your main color(s) with white, and shades are made by mixing with black. You can quickly create an organized system with Sass color functions:

$main-color: #9AE799; $main-color-lightest: lighten($main-color, 20%); $main-color-lighter: lighten($main-color, 15%); $main-color-light: lighten($main-color, 10%); $main-color-dark: darken($main-color, 40%); $main-color-darker: darken($main-color, 50%); $main-color-darkest: darken($main-color, 60%); A palette of color options created with Sass color functions. Make sure to use percent values for the functions that create distinct colors—not too similar to the main color.

To round out your palette, you’ll need a few more colors, like a white and black. Try creating a "rich black" using a dark, almost black shade of your main color and, on the other end of the spectrum, pick a few light grays that are tinted with your main color. Tinting the white and black adds a little more personality to your page and helps create a cohesive look and feel.

Top: Basic white, gray, and black. Bottom: Tinted white, grays, and black to match the main color.

Last but not least, if you are working on an interactive product, you should add colors for success, warning, and error states. Typically a green, yellow, and red work for these but consider how you can adjust the hue to make them fit better with your palette. For example, if your mood is friendly and your base color is green, you might want to desaturate the error state colors to make the red feel less negative.

You can do this with the mix Sass color function by giving it your base color, the default error color, and the percentage of base color that you want to mix in with the error color. Adding desaturate functions helps tone down the colors:

$success: mix($base-color, desaturate(green, 50%), 50%); $warning: mix($base-color, desaturate(yellow, 30%), 5%); $error: mix($base-color, desaturate(red, 50%), 20%); Top: Default colors for success, warning, and error states. Bottom: Tinted and desaturated colors for the success, warning and error states.

When it comes to the web, there’s one color principle that you have to pay extra attention to: contrast. That’s what we’ll cover next.

Contrast

Color contrast—the difference in saturation, brightness, and hue between two colors—is an important design principle for ensuring the web is accessible to those with low vision or color blindness. By ensuring there is enough contrast between your text and whatever is behind it on your site will be more accessible for all sighted users. When looking at accessibility, be sure to follow the color contrast guidelines provided by W3C’s Web Content Accessibility Guidelines (WCAG). There are many tools that can help you follow these guidelines, including the inspect panel in Chrome’s dev tools.

By clicking on the color property in the Chrome Inspect tool, you can see the contrast ratio and whether it is passing.

Now, it’s time to put these principles to practice! You can use these processes and CSS tips to help take the guesswork out of design and create better solutions. Start with what you are familiar with, and eventually, the design principles mentioned here will become second nature.

If you’re looking for even more practical tips, Adam Wathan and Steve Schoger wrote about some of their favorites.

The post Design Principles for Developers: Processes and CSS Tips for Better Web Design appeared first on CSS-Tricks.

Get the Best Domain Name for your New Website

Css Tricks - Thu, 08/08/2019 - 4:22am

(This is a sponsored post.)

If you're on CSS-Tricks, we can probably bet that you're in the process of building a really cool website. You've spent your time creating content, applying appropriate UX design techniques, coding it to perfection, and now you're about ready to launch it to the world.

A great website deserves a domain name that represents all that you've built. With Hover, you have the flexibility to choose a domain name that truly reflects that. We offer not only the go-to domain name extensions, like .com and .org, or the familiar country code domain extensions, like .uk or .us, or .ca, but also the more niche extensions. We have .dev for developers, .design for designers, and .dog for your dog (yes, really!).

We have hundreds of domain names to choose from and all eligible domains come with free Whois privacy protection. We're proud of the modern UX/UI and fabulous customer service we offer our customers. Find your next domain name with Hover!

Get Started

Direct Link to ArticlePermalink

The post Get the Best Domain Name for your New Website appeared first on CSS-Tricks.

Using Immer for React State Management

Css Tricks - Wed, 08/07/2019 - 4:38am

We make use of state to keep track of application data. States change as users interact with an application. When this happens, we need to update the state that is displayed to the user, and we do this using React’s setState.

Since states are not meant to be updated directly (because React’s state has to be immutable), things can get really complicated as states become more complex. They become difficult to understand and follow.

This is where Immer comes in and that’s what we’re going to look at in this post. Using Immer, states can be simplified and much easier to follow. Immer makes use of something called "draft" which you can think of as the copy of your state, but not the state itself. It’s as though Immer hit CMD+C on the state and then cmd+V’d it somewhere else where it can be safely viewed without disturbing the original copy. Any updates you need to make happen on the draft, and the parts of the current state that change on the draft is updated.

Let’s say your application’s state looks like this;

this.state = { name: 'Kunle', age: 30, city: 'Lagos, country: 'Nigeria' }

This user happens to be celebrating his 31st birthday and which means we need to update the age value. With Immer running behind the scenes, a replica of this state will be made.

Now imagine the replica is made and handed over to a messenger, who gives the newly copied version of the state to Kunle. It means there are now two copies available — the current state and the draft copy that was handed over. Kunle then changes the age on the draft to 31. The messenger then returns to the application with the draft, compares both versions, and only updates the age since that’s the only part of the draft that changed.

It does not break the idea of an immutable state, as the current state does not get updated directly. Immer basically makes it convenient to work with immutable state.

Let’s look at an example of this at work

Say you want to build a traffic light for your community, you can give it a shot using Immer for your state updates.

See the Pen
Traffic Light Example with Reactjs
by CarterTsai (@CarterTsai)
on CodePen.

Using Immer, the component will look like this:

const {produce} = immer class App extends React.Component { state = { red: 'red', yellow: 'black', green: 'black', next: "yellow" } componentDidMount() { this.interval = setInterval(() => this.changeHandle(), 3000); } componentWillUnmount() { clearInterval(this.interval); } handleRedLight = () => { this.setState( produce(draft => { draft.red = 'red'; draft.yellow = 'black'; draft.green = 'black'; draft.next = 'yellow' }) ) } handleYellowLight = () => { this.setState( produce(draft => { draft.red = 'black'; draft.yellow = 'yellow'; draft.green = 'black'; draft.next = 'green' }) ) } handleGreenLight = () => { this.setState( produce(draft => { draft.red = 'black'; draft.yellow = 'black'; draft.green = 'green'; draft.next = 'red' }) ) } changeHandle = () => { if (this.state.next === 'yellow') { this.handleYellowLight() } else if (this.state.next === 'green') { this.handleGreenLight() } else { this.handleRedLight() } } render() { return ( <div className="box"> <div className="circle" style={{backgroundColor: this.state.red}}></div> <div className="circle" style={{backgroundColor: this.state.yellow}}></div> <div className="circle" style={{backgroundColor: this.state.green}}></div> </div> ); } };

produce is the default function we get from Immer. Here, we pass it as a value to the setState() method. The produce function takes a function which accepts draft as an argument. It is inside this function that we can then set the draft copy with which we want to update our state.

If that looks complicated, there is another way to write this. First, we create a function.

const handleLight = (state) => { return produce(state, (draft) => { draft.red = 'black'; draft.yellow = 'black'; draft.green = 'green'; draft.next = 'red' }); }

We are passing the current state of the application, and the function which accepts draft as arguments to the produce function. To make use of this inside our component, we do this;

handleGreenLight = () => { const nextState = handleLight(this.state) this.setState(nextState) } Another example: A shopping list

If you have been working with React for a while now, then you’re not a stranger to the spread operator. With Immer, you need not make use of the spread operator, especially when working with an array in your state.

Let’s explore that a little further by creating a shopping list application.

See the Pen
immer 2 - shopping list
by Kingsley Silas Chijioke (@kinsomicrote)
on CodePen.

Here’s the component we’re working with:

class App extends React.Component { constructor(props) { super(props) this.state = { item: "", price: 0, list: [ { id: 1, name: "Cereals", price: 12 }, { id: 2, name: "Rice", price: 10 } ] } } handleInputChange = e => { this.setState( produce(draft => { draft[event.target.name] = event.target.value })) } handleSubmit = (e) => { e.preventDefault() const newItem = { id: uuid.v4(), name: this.state.name, price: this.state.price } this.setState( produce(draft => { draft.list = draft.list.concat(newItem) }) ) }; render() { return ( <React.Fragment> <section className="section"> <div className="box"> <form onSubmit={this.handleSubmit}> <h2>Create your shopping list</h2> <div> <input type="text" placeholder="Item's Name" onChange={this.handleInputChange} name="name" className="input" /> </div> <div> <input type="number" placeholder="Item's Price" onChange={this.handleInputChange} name="price" className="input" /> </div> <button className="button is-grey">Submit</button> </form> </div> <div className="box"> { this.state.list.length ? ( this.state.list.map(item => ( <ul> <li key={item.id}> <p>{item.name}</p> <p>${item.price}</p> </li> <hr /> </ul> )) ) : <p>Your list is empty</p> } </div> </section> </React.Fragment> ) } } ReactDOM.render( <App />, document.getElementById('root') );

As items are added to the list, we need to update the state of the list to reflect those new items. To update the state of list using setState(), we’ll have to do this:

handleSubmit = (e) => { e.preventDefault() const newItem = { id: uuid.v4(), name: this.state.name, price: this.state.price } this.setState({ list: [...this.state.list, newItem] }) };

If you have to update multiple states in the application, you’ll have to do a ton of spreading to create a new state using the old state and the additional value. Which can look more complex as the number of changes increases. With Immer, it becomes very easy to do that, as we did in the example above.

What if we want to add a function that gets called as a callback after the state update?In this case, let’s say we are keeping a tally of the number of items in the list and the total price of all the items.

See the Pen
immer 3 - shopping list
by Kingsley Silas Chijioke (@kinsomicrote)
on CodePen.

Say we want to calculate the amount that will be spent based on the price of items in the list, we can have the handleSubmit function look like this:

handleSubmit = (e) => { e.preventDefault() const newItem = { id: uuid.v4(), name: this.state.name, price: this.state.price } this.setState( produce(draft => { draft.list = draft.list.concat(newItem) }), () => { this.calculateAmount(this.state.list) } ) };

First, we create an object using the data entered by the user, which we then assign to newItem. To update our application’s state, we make use of .concat() which will return a new array that's comprised of the previous items and the new item. This updated copy is now set as the value of draft.list, which can then be used by Immer to update the state of the application.

The callback function gets called after the state update. It’s important to note that it makes use of the updated state.

The function we want to call will look like this:

calculateAmount = (list) => { let total = 0; for (let i = 0; i < list.length; i++) { total += parseInt(list[i].price, 10) } this.setState( produce(draft => { draft.totalAmount = total }) ) } Let’s look at Immer hooks

use-immer is a hook that allows you to manage state in your React application. Let’s see this in action using a classic counter example.

import React from "react"; import {useImmer} from "use-immer"; const Counter = () => { const [count, updateCounter] = useImmer({ value: 0 }); function increment() { updateCounter(draft => { draft.value = draft.value +1; }); } return ( <div> <h1> Counter {count.value} </h1> <br /> <button onClick={increment}>Increment</button> </div> ); } export default Counter;

useImmer is similar to useState. The function returns the state and an updater function. When the component loads at first, the value of the state (which is count in this example), is the same as the value passed to useImmer. Using the updater function which is returned, we can then create an increment function to increase the value of the count.

There is also a useReducer-like hook for Immer.

import React, { useRef } from "react"; import {useImmerReducer } from "use-immer"; import uuidv4 from "uuid/v4" const initialState = []; const reducer = (draft, action) => { switch (action.type) { case "ADD_ITEM": draft.push(action.item); return; case "CLEAR_LIST": return initialState; default: return draft; } } const Todo = () => { const inputEl = useRef(null); const [state, dispatch] = useImmerReducer(reducer, initialState); const handleSubmit = (e) => { e.preventDefault() const newItem = { id: uuidv4(), text: inputEl.current.value }; dispatch({ type: "ADD_ITEM", item: newItem }); inputEl.current.value = ""; inputEl.current.focus(); } const handleClear = () => { dispatch({ type: 'CLEAR_LIST' }) } return ( <div className='App'> <header className='App-header'> <ul> {state.map(todo => { return <li key={todo.id}>{todo.text}</li>; })} </ul> <form onSubmit={handleSubmit}> <input type='text' ref={inputEl} /> <button type='submit' > Add Todo </button> </form> <button onClick={handleClear} > Clear Todos </button> </header> </div> ); } export default Todo;

useImmerReducer takes in a reducer function and the initial state, and it returns both state and the dispatch function. We can then loop through the state to display the items we have. We dispatch an action when submitting a todo item and clearing the list of them. The dispatched action has a type which we use in determining what to do in the reducer function.
In the reducer function, we make use of draft like we did before, instead of state. With that, we have a convenient way of manipulating the state of our application.

You can find the code used in the above example on GitHub.

That’s a look at Immer!

Going forward, you can begin to make use of Immer in your next project, or even slowly begin to use it in the current project you’re working on. It has proven to aid in making state management convenient.

The post Using Immer for React State Management appeared first on CSS-Tricks.

Quick Gulp Cache Busting

Css Tricks - Wed, 08/07/2019 - 4:05am

You should for sure be setting far-out cache headers on your assets like CSS and JavaScript (and images and fonts and whatever else). That tells the browser "hang on to this file basically forever." That way, when navigating from page to page on a site — or revisiting it, or refreshing the page — the browser doesn't have to download it again which produces way faster page loads. It's massively important for web performance, so do it!

But how do you force the browser to get a fresh version of the file? Well, there are a bunch of ways. Check out that blog post for more. But here's one that I've used just recently that I wanted to document.

The trick is to change the query string

There was an era where the prevailing wisdom was that changing the query string wasn't enough, but even then it, the reasons it wouldn't work were pretty much edge cases. These days, changing the query string is fine (assuming you don't change the default behavior, services like Cloudflare let you do it).

So, one day you ship it like this in your HTML:

<link rel="stylesheet" href="style.css?v=1" />

Then you change that query string to break the cache when you need to:

<link rel="stylesheet" href="style.css?v=2" />

The HTML, by the way, is either not cached or cached for a much shorter amount of time, so changes to HTML will be seen.

I sometimes do it by hand

For many years, I busted cache on this very site by setting a PHP variable and using it to break assets, like...

<?php $ver = 1.0; ?> <link rel="stylesheet" href="style.css?v=<?php echo $ver; ?>" /> <link rel="stylesheet" href="some-other-asset.css?v=<?php echo $ver; ?>" />

Hey that works, but it was me hand-manipulating that variable. I would sometimes forget to do it, and even if I did remember, I sort of resented having to do it.

Automating version busting with Gulp

I'm using a Gulp-powered build process at the moment on this site, which does all the classic stuff: Sass, Babel, file concatenation, live reloading...

It occurred to me I might as well have Gulp do the query-string changing whenever changes are made to CSS or JavaScript. JavaScript has a .replace() method, and that's available in Node/Gulp easily with the gulp-replace plugin.

I make a task. When I call it, it looks in my header filer for the string cache_bust= plus some value, and replaces it with a new randomized string based on the date and time.

gulp.task("cache-bust-css", function() { var cbString = new Date().getTime(); return gulp .src(["header.php"]) .pipe( replace(/cache_bust=\d+/g, function() { return "cache_bust=" + cbString; }) ) .pipe(gulp.dest(".")); });

I do the same thing in a separate task when JavaScript files are editing and compiled.

It's still a little dumb

Notice that I'm changing the query string on all the CSS assets every time any of them changes. That's not as efficient as it could be. I should probably be changing the query string only on the changed files.

I'll get to it eventually. Sometimes you just gotta baby-step your way to better solutions over time.

This is just one way! There are other Gulp plugins just for this. Other build systems have different approaches. This approached happened to work well for me and my exact needs at the time. Feel free to share your strategy!

The post Quick Gulp Cache Busting appeared first on CSS-Tricks.

Let’s Give Grunt Tasks the Marie Kondo Organization Treatment

Css Tricks - Tue, 08/06/2019 - 10:27am

We live in an era of webpack and npm scripts. Good or bad, they took the lead for bundling and task running, along with bits of Rollup, JSPM and Gulp. But let's face it. Some of your older projects are still using good ol' Grunt. While it no longer glimmers as brightly, it does the job well so there's little reason to touch it.

Though, from time to time, you wonder if there’s a way to make those projects better, right? Then start from "Organizing Your Grunt Tasks" article and come back. I'll wait. That’ll set the stage for this post and then we'll take it further together to create a solid organization of Grunt tasks.

Automatic Speed Daemon task loading

It’s no fun writing loading declarations for each task, like this:

grunt.loadNpmTasks('grunt-contrib-clean') grunt.loadNpmTasks('grunt-contrib-watch') grunt.loadNpmTasks('grunt-csso') grunt.loadNpmTasks('grunt-postcss') grunt.loadNpmTasks('grunt-sass') grunt.loadNpmTasks('grunt-uncss') grunt.initConfig({})

It's common to use load-grunt-tasks to load all tasks automatically instead. But what if I tell you there is a faster way?

Try jit-grunt! Similar to load-grunt-tasks, but even faster than native grunt.loadNpmTasks.

The difference can be striking, especially in projects with large codebases.

Without jit-grunt

loading tasks 5.7s ???????? 84% assemble:compile 1.1s ?? 16% Total 6.8s

With jit-grunt

loading tasks 111ms ? 8% loading assemble 221ms ?? 16% assemble:compile 1.1s ???????? 77% Total 1.4s

1.4 seconds doesn't really make it a Speed Daemon... so I kinda lied. But still, it's six times faster than the traditional way! If you're curious how that's possible, read about the original issue which led to the creation of jit-grunt.

How is jit-grunt used? First, install:

npm install jit-grunt --save

Then replace all tasks load statements with a single line:

module.exports = function (grunt) { // Intead of this: // grunt.loadNpmTasks('grunt-contrib-clean') // grunt.loadNpmTasks('grunt-contrib-watch') // grunt.loadNpmTasks('grunt-csso') // grunt.loadNpmTasks('grunt-postcss') // grunt.loadNpmTasks('grunt-sass') // grunt.loadNpmTasks('grunt-uncss') // Or instead of this, if you've used `load-grunt-tasks` // require('load-grunt-tasks')(grunt, { // scope: ['devDependencies', 'dependencies'] // }) // Use this: require('jit-grunt')(grunt) grunt.initConfig({}) }

Done!

Better configs loading

In the last example, we told Grunt how to load tasks itself, but we didn't quite finish the job. As “Organizing Your Grunt Tasks" suggests, one of the most useful things we're trying to do here is split up a monolithic Gruntfile into smaller standalone files.

If you read the mentioned article, you'll know it's better to move all task configuration into external files. So, instead of a large single gruntfile.js file:

module.exports = function (grunt) { require('jit-grunt')(grunt) grunt.initConfig({ clean: {/* task configuration goes here */}, watch: {/* task configuration goes here */}, csso: {/* task configuration goes here */}, postcss: {/* task configuration goes here */}, sass: {/* task configuration goes here */}, uncss: {/* task configuration goes here */} }) }

We want this:

tasks ?? postcss.js ?? concat.js ?? cssmin.js ?? jshint.js ?? jsvalidate.js ?? uglify.js ?? watch.js ?? sass.js gruntfile.js

But that will force us to load each external configuration into gruntfile.js manually, and that takes time! We need a way to load our configuration files automatically.

We’ll use load-grunt-configs for that purpose. It takes a path, grabs all of the configuration files there and gives us a merged config object which we use for Grunt config initialization.

Here how it works:

module.exports = function (grunt) { require('jit-grunt')(grunt) const configs = require('load-grunt-configs')(grunt, { config: { src: 'tasks/.js' } }) grunt.initConfig(configs) grunt.registerTask('default', ['cssmin']) }

Grunt can do the same thing natively! Take a look at grunt.task.loadTasks (or it's alias grunt.loadTasks).

Use it like this:

module.exports = function (grunt) { require('jit-grunt')(grunt) grunt.initConfig({}) // Load all your external configs. // It's important to use it _after_ Grunt config has been initialized, // otherwise it will have nothing to work with. grunt.loadTasks('tasks') grunt.registerTask('default', ['cssmin']) }

Grunt will automatically load all js or coffee config files from the specified directory. Nice and clean! But, if you'll try to use it, you'll notice it does nothing. How is that? We still need to do one more thing.

Let's look into our gruntfile.js code once again, this time without the comments:

module.exports = function (grunt) { require('jit-grunt')(grunt) grunt.initConfig({}) grunt.loadTasks('tasks') grunt.registerTask('default', ['cssmin']) }

Notice that grunt.loadTasks loads files from tasks directory, but never assigns it to our actual Grunt config.

Compare it with a way load-grunt-configs works:

module.exports = function (grunt) { require('jit-grunt')(grunt) // 1. Load configs const configs = require('load-grunt-configs')(grunt, { config: { src: 'tasks/.js' } }) // 2. Assign configs grunt.initConfig(configs) grunt.registerTask('default', ['cssmin']) }

We initialize our Grunt config before actually loadings tasks configuration. If you are getting a strong feeling that it will make us end up with empty Grunt config — you're totally right. You see, on contrary to the load-grunt-configs, grunt.loadTasks just imports files into gruntfile.js. It does nothing more.

Woah! So, how do we make use of it? Let's explore!

First, create a file inside directory tasks named test.js

module.exports = function () { console.log("Hi! I'm an external task and I'm taking precious space in your console!") }

Let's run Grunt now:

$ grunt

We'll see printed to the console:

> Hi! I'm an external task and I'm taking precious space in your console!

So, upon importing grunt.loadTasks, every function is executed as it loads files. That's nice, but what's the use of it for us? We still can't do a thing we actually want — to configure our tasks.

Hold my beer because there is a way to command Grunt from within external configuration files! Using grunt.loadTasks upon importing provides current Grunt instance as a function first argument and also binds it to this.

So, we can update our Gruntfile:

module.exports = function (grunt) { require('jit-grunt')(grunt) grunt.initConfig({ // Add some value to work with testingValue: 123 }) grunt.loadTasks('tasks') grunt.registerTask('default', ['cssmin']) }

...and change the external config file tasks/test.js:

// Add `grunt` as first function argument module.exports = function (grunt) { // Now, use Grunt methods on `grunt` instance grunt.log.error('I am a Grunt error!') // Or use them on `this` which does the same this.log.error('I am a Grunt error too, from the same instance, but from `this`!') const config = grunt.config.get() grunt.log.ok('And here goes current config:') grunt.log.ok(config) }

Now, let’s run Grunt again:

$ grunt

And what we'll get:

> I am Grunt error! > I am Grunt error too, from the same instance, but from `this`! > And here goes current config: > { testingValue: 123 }

See how we accessed native Grunt methods from an external file and were even able to retrieve the current Grunt config? Are you thinking about that too? Yeah, the full power of Grunt is already there, right at our fingertips in each file!

If you are wondering why methods inside external files can affect our main Grunt instance, it is because of a referencing. grunt.loadTasks passing this and grunt to our current Grunt instance — not a copy of it. By invoking methods on that reference, we're able to read and mutate our main Grunt configuration file.

Now, we need to actually configure something! One last thing...

This time, let’s make configuration loading work for real

Alright, we’ve come a long way. Our tasks are loaded automatically and faster. We learned how to load external configs with native Grunt method. But our task configs are still not quite there because they do not end up in Grunt config.

But we’re almost there! We learned that we can use any Grunt instance methods in imported files using grunt.loadTasks. They are available on grunt and this instances.

Among many other methods, there is a precious grunt.config method. It allows us to set a value in an existing Grunt config. The main one, which we initialized in our Gruntfile... remember that one?

What's important is the way we can define tasks configurations. Exactly what we need!

// tasks/test.js module.exports = function (grunt) { grunt.config('csso', { build: { files: { 'style.css': 'styles.css' } } }) // same as // this.config('csso', { // build: { // files: { 'style.css': 'styles.css' } // } // }) }

Now let's update Gruntfile to log the current config. We need to see what we did, after all:

module.exports = function (grunt) { require('jit-grunt')(grunt) grunt.initConfig({ testingValue: 123 }) grunt.loadTasks('tasks') // Log our current config console.log(grunt.config()) grunt.registerTask('default', ['cssmin']) }

Run Grunt:

$ grunt

...and here’s what we see:

> { testingValue: 123, csso: { build: { files: { 'style.css': 'styles.css' } } } }

grunt.config sets csso value when imported, so the CSSO task is now configured and ready to run when Grunt is invoked. Perfect.

Note that if you used load-grunt-configs previously, you had a code like that, where each file exports a configuration object:

// tasks/grunt-csso.js module.exports = { target: { files: { 'style.css': 'styles.css' } } }

That needs to be changed to a function, as described above:

// tasks/grunt-csso.js module.exports = function (grunt) { grunt.config('csso', { build: { files: { 'style.css': 'styles.css' } } }) }

OK, one more one more last thing... this time for real!

Taking external config files to the next level

We learned a lot. Load tasks, load external configuration files, define a configuration with Grunt methods... that's fine, but where's the profit?

Hold my beer again!

By this time, we’ve externalized all our task configuration files. So, the our project directory looks something like this:

tasks ?? grunt-browser-sync.js ?? grunt-cache-bust.js ?? grunt-contrib-clean.js ?? grunt-contrib-copy.js ?? grunt-contrib-htmlmin.js ?? grunt-contrib-uglify.js ?? grunt-contrib-watch.js ?? grunt-csso.js ?? grunt-nunjucks-2-html.js ?? grunt-postcss.js ?? grunt-processhtml.js ?? grunt-responsive-image.js ?? grunt-sass.js ?? grunt-shell.js ?? grunt-sitemap-xml.js ?? grunt-size-report.js ?? grunt-spritesmith-map.mustache ?? grunt-spritesmith.js ?? grunt-standard.js ?? grunt-stylelint.js ?? grunt-tinypng.js ?? grunt-uncss.js ?? grunt-webfont.js gruntfile.js

That keeps Gruntfile relatively small and things seem to be well organized. But do you get a clear picture of the project just by glancing into this cold and lifeless list of tasks? What actually do they do? What's the flow?

Can you tell that Sass files are going through grunt-sass, then grunt-postcss:autoprefixer, then grunt-uncss, and finally through grunt-csso? Is it obvious that the clean task is cleaning the CSS or that grunt-spritesmith is generating a Sass file which should be picked up too, as grunt-watch watches over changes?

Seems like things are all over the place. We may have gone too far with externalization!

So, finally... now what if tell you that there’s yet a better way would be group configs... based on features? Instead of a not-so-helpful list of tasks, we'll get a sensible list of features. How about that?

tasks ?? data.js ?? fonts.js ?? icons.js ?? images.js ?? misc.js ?? scripts.js ?? sprites.js ?? styles.js ?? templates.js gruntfile.js

That tells me a story! But how could we do that?

We already learned about grunt.config. And believe it or not, you can use it multiple times in a single external file to configure multiple tasks at once! Let’s see how it works:

// tasks/styles.js module.exports = function (grunt) { // Configuring Sass task grunt.config('sass', { build: {/* options */} }) // Configuring PostCSS task grunt.config('postcss', { autoprefix: {/* options */} }) }

One file, multiple configurations. Quite flexible! But there is an issue we missed.

How should we deal with tasks such as grunt-contrib-watch? Its configuration is a whole monolithic thing with definitions for each task that are unable to be split.

// tasks/grunt-contrib-watch.js module.exports = function (grunt) { grunt.config('watch', { sprites: {/* options */}, styles: {/* options */}, templates: {/* options */} }) }

We can't simply use grunt.config to set watch configuration in each file, as it will override the same watch configuration in already imported files. And leaving it in a standalone file sounds like a bad option too — after all, we want to keep all related things close.

Fret not! grunt.config.merge to the rescue!

While grunt.config explicitly sets and overrides any existing values in Grunt config, grunt.config.merge recursively merges values with existing values in other Grunt config files giving us a single Grunt config. A simple, but effective way to keep related things together.

An example:

// tasks/styles.js module.exports = function (grunt) { grunt.config.merge({ watch: { templates: {/* options */} } }) } // tasks/templates.js module.exports = function (grunt) { grunt.config.merge({ watch: { styles: {/* options */} } }) }

This will produce a single Grunt config:

{ watch: { styles: {/* options */}, templates: {/* options */} } }

Just what we needed! Let's apply this to the real issue — our styles-related configuration files. Replace our three external task files:

tasks ?? grunt-sass.js ?? grunt-postcss.js ?? grunt-contrib-watch.js

...with a single tasks/styles.js file that combines them all:

module.exports = function (grunt) { grunt.config('sass', { build: { files: [ { expand: true, cwd: 'source/styles', src: '{,**/}*.scss', dest: 'build/assets/styles', ext: '.compiled.css' } ] } }) grunt.config('postcss', { autoprefix: { files: [ { expand: true, cwd: 'build/assets/styles', src: '{,**/}*.compiled.css', dest: 'build/assets/styles', ext: '.prefixed.css' } ] } }) // Note that we need to use `grunt.config.merge` here! grunt.config.merge({ watch: { styles: { files: ['source/styles/{,**/}*.scss'], tasks: ['sass', 'postcss:autoprefix'] } } }) }

Now it's much easier to tell just by glancing into tasks/styles.js that styles have three related tasks. I'm sure you can imagine extending this concept to other grouped tasks, like all the things you might want to do with scripts, images, or anything else. That gives us a reasonable configuration organization. Finding things will be much easier, trust me.

And that's it! The whole point of what we learned.

That’s a wrap

Grunt is no longer the new darling it once was when it first hit the scene. But to date, it is a straightforward and reliable tool that does its job well. With proper handling, it gives even fewer reasons to swap it for something newer.

Let's recap what we can do to organize our tasks efficiently:

  1. Load tasks using jit-grunt instead of load-grunt-tasks. It's same but insanely faster.
  2. Move specific task configurations out from Gruntfile and into external config files to keep things organized.
  3. Use native grunt.task.loadTasks to load external config files. It's simple but powerful as it exposes all Grunt capabilities.
  4. Finally, think about a better way to organize your config files! Group them by feature or domain instead of the task itself. Use grunt.config.merge to split complex tasks like watch.

And, for sure, check Grunt documentation. It’s still worth a read after all these years.

If you’d like to see a real-world example, check out Kotsu, a Grunt-based starter kit and static website generator. You'll find even more tricks in there.

Got better ideas about how to organize Grunt configs even better? Please share them in the comments!

The post Let’s Give Grunt Tasks the Marie Kondo Organization Treatment appeared first on CSS-Tricks.

SSCCE

Css Tricks - Tue, 08/06/2019 - 4:28am

You know what a "reduced test case" is, right? We've talked about it here. I imagine the concept is useful in many walks of life, but in the world of front-end development, you can think of it like:

A reduced test case is a demo/example page you create which reproduces the problem you are having with the least amount of code possible. Only the HTML needed to show the problem content. Only CSS that is related to that reduced HTML. Only the JavaScript related to the problem functionality at hand.

The point of making these is typically troubleshooting. It's both for you and for anyone else looking at the trouble. The less unrelated code anyone has to wade through to understand the problem, the better. The secret trick is that while making a reduced test case, you often figure out the problem in the process. ;)

I recently found out another acronym for all this: SSCCE.

That stands for "Short, Self Contained, Correct (Compilable), Example." Sleuthing the meta tags, it looks like the credit is to Andrew Thompson and last updated in 2014. That landing page describes them wonderfully and explains what you need to do to create one.

It covers this classic situation where you discover the problem isn't where you think it is:

If trimming a very large amount if code for others to see, you might trim out a part of it early on that you think is not related to the problem, yet the problem is fixed.

...and closes with the most important thing:

We are not proposing that every single problem needs a SSCCE in order to be solved. It will, however, make people much more likely to help, and will therefore increase the chance of finding a solution.

I'd say it's a dramatic difference. The chances of me trying to help on a small bug where the bug is buried within a giant pile of unfamiliar code? Very low. The chances of me trying to help on a small bug with a SSCCE? High.

However, I think I still prefer "reduced test case" since the term is more self-explanatory.

The post SSCCE appeared first on CSS-Tricks.

Using Your Domain with a Netlify-Hosted Site

Css Tricks - Tue, 08/06/2019 - 4:25am

Netlify has their own docs for Custom Domains, so if you're looking for horse's mouth technical docs on this stuff, that should be treated as the source of truth. But I'd like to take a crack at it from a slightly different angle, where we look at where you are and what you wanna do, and the point you in the right direction.

Do you NOT own the domain yet?

If that's the case, you can buy it directly through Netlify if you want. There is a big advantage there in that it automatically gets set up to work perfectly, so it's easier.

You don't have to buy your domain through Netlify though, you can buy it wherever you like. There is nothing you can't do with Netlify if you choose to buy a domain elsewhere.

Easiest possible method: register the domain with Netlify.

Alternative: If you anticipate some hosting/DNS churn, like you think you might ultimately need to host elsewhere, it probably makes more sense to manage to domain somewhere agnostic where you can re-point DNS stuff wherever you need to. It's not that you can't manage the DNS on Netlify and point it elsewhere, it just doesn't make loads of sense. Also, there is something to be said (lower cognitive load) managing all your domains in one place if you can.

What if you already own the domain?

No problem. The rest of this article deals with that scenario.

Are you cool pointing the nameservers at Netlify?

If you are, this is the best way. It means you don't have to fiddle with subdomains and the CDN features work the best. The requires you to go into the DNS administration area of wherever you bought the domain and change the nameservers there. Every domain registrant will be a bit different in where and how you do that.

Changing nameservers on GoDaddy to point to Netlify. Can't or don't want to point nameservers at Netlify?

I would guess that the main reason you might have this preference is that you use Cloudflare, or perhaps something else similar that gives you fancy performance and security advantages by going through their proxy. Netlify actually says that you don't need Cloudflare with them, because many of the main advantages of Cloudflare Netlify already provides.

Technology is a moving place though, and it's entirely possible that you need or really want some feature that a proxy service like Cloudflare provides.

If you need to do this, you've got two options:

  • Use the www. version of your domain and a CNAME record to point to Netlify (you can "CNAME flatten" it to remove www. if you really want).
  • Point the A record to Netlify's load balancer IP address. I'd list it here, but you're better off getting it from their docs in case it changes.
Are you just dealing with a subdomain anyway?

This is actually a lot easier. If this is the case, you probably already own the domain name anyway, and you should stick with your existing DNS host. All you need to do is CNAME the subdomain to the Netlify domain. Here's an example where the root domain is hosted elsewhere and uses Cloudflare for DNS, but the subdomain is Netlify hosted and points with a CNAME.

If you care about the Netlify domain sitting there with the same content on it, you can always redirect it. Also, you might need to "gray cloud" (turn off the Cloudflare proxy) for those subdomains so Netlify can issue SSL certificates for it.

The post Using Your Domain with a Netlify-Hosted Site appeared first on CSS-Tricks.

Let Mavo Shine in Building Interactive Web Applications

Css Tricks - Mon, 08/05/2019 - 6:36pm

As you could guess from the title, this tutorial is dedicated to Mavo: a new, approachable way to create complex, reactive, persistent web applications just by writing HTML and CSS, without a single line of JavaScript and no server backend.

&#x1f407; Follow the white rabbit!

Mavo is developed in the Haystack Group at MIT CSAIL and led by Lea Verou).

The app we will build together is a flashcard study app for foreign language learners. It is a fully-fledged CRUD application that lets you:

  • Create, delete, update flashcards and re-arrange them via drag and drop.
  • Import and export flashcards.
  • Evaluate how well you've learned the word on a flashcard.

Here is what our finished application looks like:

In the tutorial, I will guide you through the entire process of building the app.

At the end of some steps, I provide suggestions for you to experiment with Mavo—to learn a bit more—and to make some enhancements to the application we are building.

Are you ready? Let's get started! &#x1f600;

Static template

See the Pen
01. Static template
by Dmitry Sharabin (@dsharabin)
on CodePen.

In order to illustrate how Mavo enhances standard HTML, we will first create a purely static HTML page and then use Mavo to turn this static HTML into a fully-functional web application.

Assume we have the following HTML code inside <body>:

<header> <h1>Flashcards</h1> </header> <main> <article> <p>Word or phrase</div> <p>Translation</div> </article> </main>

In that code, the <article> element represents a single flashcard.

Let's add some styling in order to make our HTML look more like an actual flashcards app. You can take a look at the source code and play with it here.

Getting started with Mavo

Right now, all we have is a static template. It's time to add functionality, so it can actually work like a flashcard application. Here comes Mavo!

In order to use Mavo, we first need to include its JavaScript and CSS files in our page’s <head> section:

<head> ... <script src="https://get.mavo.io/mavo.min.js"></script> <link rel="stylesheet" href="https://get.mavo.io/mavo.css"> ... </head> &#x1f407; Follow the white rabbit!

Perhaps you need to support older browsers, or want to be able to read the code? You can customize which build and version of Mavo you are using here by answering a few questions. Defining a Mavo app

To enable Mavo functionality on an HTML structure, we must use the mv-app attribute on the element that contains our Mavo app (which may even be the <body> or <html> element, that's fine!). Its value is an ID for our app that should be unique in the page.

&#x1f407; Follow the white rabbit!

If we use mv-app without a value and there is no id or name attribute on the same element, a name such as mavo1, mavo2, etc. will be automatically generated.

However, it is strongly recommended to name your Mavo apps, because the name is used in a number of places.

Considering that the <main> element is representing our Mavo app, let's add the mv-app attribute to it and give our app the ID "flashcards":

<main mv-app="flashcards"> ... </main> The property attribute

See the Pen
02. The property attribute
by Dmitry Sharabin (@dsharabin)
on CodePen.

It's time to tell Mavo what elements of our app are important, i.e., which elements we want to be editable and be saved.

Now we have two such elements, and they are the <p> elements. Let's add the property attribute to those elements to tell Mavo they contain data. Elements with the property attribute are called properties.

&#x1f407; Follow the white rabbit!

We can put the property attribute on any HTML5 element, and Mavo knows how to make it editable. For example, for a <span> you edit its contents, but a <time> lets you edit its date/time via an appropriate date/time picker.

You can also expand this set of rules and make elements editable in new ways (e.g., rich text), via plugins.

Keep in mind that the value of the property attribute should describe the element, similarly to an id or class attribute:

... <p property="source">Word or phrase</div> <p property="translation">Translation</div> ... &#x1f407; Follow the white rabbit!

If you already have a class, id, or itemprop attribute that describes the element sufficiently well, you can use property without a value, e.g., <p property class="source">.

Notice any changes in our app? The Mavo toolbar with an Edit button appeared at the top of the page. The Edit button lets the user switch between read and edit modes. Now our app is in read mode. That means we can't edit the data on the page.

&#x1f407; Follow the white rabbit!

The Mavo toolbar is fully customizable, as is almost all of the UI generated by Mavo: you can change its placement, remove its default styling, add custom button elements, or use your own HTML elements for it, among other things.

We will see one example of such customization later on in this tutorial.

Visit this section of the Mavo website to learn more.

Now lets us switch to edit mode by clicking the Edit button. What has changed? The text of the Edit button becomes Editing to indicate that we are in edit mode. If you hover over the paragraphs, Mavo communicates that you can click to edit them by highlighting them in yellow. Go ahead! Click the text and edit it. Wow! We can change the content of the page right in place!

&#x1f4bb; Let's get our hands dirty!

Assume that in addition to the word and its translation, the flashcard should have an example of the word's usage in a sentence. Enhance the app by adding the appropriate elements to the flashcard.

The mv-multiple attribute

See the Pen
03. The mv-multiple attribute
by Dmitry Sharabin (@dsharabin)
on CodePen.

At this point, we have only one flashcard in our app. That's not very useful! For a working flashcard application, we need the ability to add, delete, and rearrange flashcards. How can we do that? We could create more flashcards by adding more <article> elements to our code, but then how does an end user create and delete flashcards?

Fortunately, Mavo has something to offer that makes this a breeze: the mv-multiple attribute, which tells Mavo that certain elements can be multiplied. It converts the element it’s used on to an editable collection of items and generates (customizable) UI for adding, deleting, and rearranging items.

&#x1f407; Follow the white rabbit!

If mv-multiple is used on an element without a property attribute, Mavo automatically adds property="collection" to it (or collection2, collection3, etc. so that the name is unique). However, it's recommended to also use a property attribute, to name your collection and make sure its data is preserved if the HTML changes.

Let's use the mv-multiple attribute in our app to convert our lonely flashcard into a collection of flashcards:

<article property="flashcard" mv-multiple> ... </article> &#x1f407; Follow the white rabbit!

It is also possible to specify the property name as a value of mv-multiple, like so: <article mv-multiple="flashcard">.

The mv-multiple attribute goes on the element that will be multiplied, not the container of the collection. It's a common mistake for people to do <ul mv-multiple> instead of <li mv-multiple> and can often go undetected for a while until the element is inspected or the styling makes it obvious.

Now switch the app to edit mode. Note that below the flashcard, there is now an Add flashcard button. Let's give it a try: create a few flashcards with the help of that button. Now we can dynamically add new elements right in the app, even though there are no corresponding elements in the HTML. But that is not all!

&#x1f407; Follow the white rabbit!

Note that the property attribute on <article> does not actually make the entire <article> element editable, but instead acts as a grouping element. This happens when you use the property attribute on elements that contain other properties.

Try hovering over a flashcard and notice the three buttons that appear near its top right corner for adding, deleting and rearranging elements via a drag and drop handle. And by hovering over any item bar button, we can understand which flashcard they correspond: Mavo highlights it. Isn't that amazing?

&#x1f407; Follow the white rabbit!

You can customize any UI element generated by Mavo, e.g., you can create your own drag handle by using an mv-drag-handle class. &#x1f407; Follow the white rabbit!

The buttons added by Mavo to every item in a collection are also keyboard accessible. Even reordering: you can focus on the drag handle and use the arrow keys to move the item. The mv-storage attribute

See the Pen
04. The mv-storage attribute
by Dmitry Sharabin (@dsharabin)
on CodePen.

Now that we have the basic UI in place, let's try the following:

  • Switch to edit mode (if you haven't already done so).
  • Edit the first flashcard's source word and translation. Add a couple more flashcards too.
  • Switch the app back to read mode.
  • And finally... refresh the page.

What?! Where did our data go? Wasn't Mavo supposed to save it? What happened?

Actually, we never told Mavo if or where to store our data!

To do so, we need to use the mv-storage attribute. What options do we have? Well, Mavo opens great possibilities for us, and Mavo plugins open up even more!

In our application, we are going to store the data in the browser’s localStorage, which is one of the simplest options available, so it's good for our first Mavo app. We just need to add the attribute mv-storage with the value local to the element with the mv-app attribute (also called the Mavo root).

<main mv-app="flashcards" mv-storage="local"> ... </main>

Have a look at the Mavo toolbar. Notice something? Another button appeared—the Save button.

Try to edit the app data one more time. Note that the Save button is now highlighted. Hover over the Save button, and Mavo will highlight the properties with the unsaved data. Isn't that cool?

Click the Save button and refresh the page (there is no need to switch to read mode before refreshing the page). Is your data still there? Great! We are one step closer to our goal—a fully-fledged flashcard application.

The mv-autosave attribute

Now we have to click the Save button every time we need our data to be saved? That may be safer, to prevent destroying valuable data, but it can often be inconvenient. Can we just save the data automatically? Sure! To save the data automatically every time it is changed, we can use the mv-autosave attribute on our Mavo root. Its value is the number of seconds to throttle saving by. Let's add mv-autosave="3" to the root element of our app:

<main mv-app="flashcard" mv-storage="local" mv-autosave="3"> ... </main> &#x1f407; Follow the white rabbit!

If mv-autosave="3", Mavo can only save at most once every three seconds. This can be especially useful for backends which keep a change history (e.g., GitHub, Dropbox) to prevent flooding which would render that history useless.

To disable throttling and save immediately, we can use mv-autosave="0" or just mv-autosave, which will also remove the Save button from the UI (since it serves no purpose in that case).

Change the data one more time and have a look at the Save button. See? In the beginning, it was highlighted but after 3 seconds–it is not. All our data is now saved automatically!

So, now the main part of our app would look like that:

<main mv-app="flashcards" mv-storage="local" mv-autosave="3"> <article property="flashcard" mv-multiple> <p property="source">Word or phrase</div> <p property="translation">Translation</div> </article> </main> &#x1f4bb; Let's get our hands dirty!

We are almost done with the alpha version of our app. Now it’s your turn to make the app even better. No worries, you have all the knowledge you need.

Enhance the app so as flashcards could be organized by end users in different groups related to various topics, e.g., the users could gather all the flashcards corresponding to clothing in one group, all the flashcards associated with kitchen utensils in the other one, etc.

&#x1f4a1; Hints!

There are many ways to achieve that goal, and it's up to you to decide what to follow. However, I'd like you to think about some questions before proceeding:

  1. What HTML element would you use as a grouping element? It would be convenient for the users if they could see the name of the group of flashcards (topic name) and could collapse the group up to the title.
  2. What Mavo attribute(s) are you going to add to that element, if any? Will the element be a property or a collection?
  3. Will end users be able to add new topics, delete and rearrange them, change the title of a topic and move flashcards between different topics?

What if you decide not to organize flashcards in groups, but instead just label them with tags that correspond to various topics? Well, that is perfectly fine. The solution with tags is also appropriate. For the sake of practice, try to accomplish that approach too. The mv-bar attribute

See the Pen
05. The mv-bar attribute
by Dmitry Sharabin (@dsharabin)
on CodePen.

As our app stores the data locally, by default, the users of the app won't be able to share their cards with other users. Wouldn't it be great if we would let them export their flashcards and import somebody else's flashcards? Thankfully, these features are already implemented in Mavo, and we can very easily add them to our app!

The mv-bar attribute controls which buttons are going to appear in the toolbar, if any. It’s typically specified on the Mavo root (an element with the mv-app attribute). Buttons are represented by their ids (which are very logical): edit, import, export, etc.

As we only want to add a few buttons to the default set, we can use the so-called relative syntax, which allows us to add and remove buttons from the default set without having to explicitly list everything out. All we need is to start the mv-bar attribute's value with the with keyword.

By doing that, we would get the following:

<main mv-app="flashcards" mv-storage="local" mv-autosave="3" mv-bar="with import export"> ... </main> &#x1f4bb; Let's get our hands dirty!

Give those features a try: add some flashcards, try to export them in a file. Then delete existing flashcards and import the flashcards from the previously exported file.

Expressions and MavoScript

See the Pen
06. Expressions and MavoScript
by Dmitry Sharabin (@dsharabin)
on CodePen.

Let's now add some statistics to our app, such as the number of flashcards! Sounds interesting? I hoped so. &#x1f600;

To do that, we need to learn something new about Mavo.

We can dynamically refer to the value of any property we have defined, anywhere in our Mavo app (including in HTML attributes), by putting its name inside brackets, like this: [propertyName]. This is an example of a simple expression, which allows us to dynamically calculate things, reactively as they change.

&#x1f407; Follow the white rabbit!

Mavo’s expression syntax is called MavoScript. It is similar to spreadsheet formulas and lets us perform computations and other operations (with numbers, texts, lists, etc.), but is designed to be more readable and to accommodate nested relations. You can learn more about Mavo expressions and MavoScript in the documentation.

Now let’s experiment and add a [source] expression inside the flashcard property, e.g., between two properties: the source and the translation.

... <p property="source">Word or phrase</div> [source] <p property="translation">Translation</div> ...

What has changed in our app? The value of the flashcard source property is now shown on the page twice.

Switch to edit mode and try to change the value of the source property. Can you see that? The page content updates while you are changing the property value! That’s why I said earlier that Mavo lets us develop reactive web applications.

That's indeed cool, but unfortunately, in our case, it's not really helpful: we can't use this expression to count the number of flashcards—we would always have only one value.

What if we put the [source] expression outside the flashcard property? We will have something like that:

... [source] <article property="flashcard" mv-multiple> ... </article> ...

How does this differ from the previous case? To see the difference add some flashcards if you haven't done so yet. Now instead of one value we have a list of comma separated values: the source property of all flashcards. That's exactly we were looking for: the number of items in the list corresponds the number of flashcards in the app.

Makes sense? Well, yes, but it wouldn't it be more logical if we would count the number of flashcards, not the number of values of its source property? After all, a flashcard added exists even before we fill in its source or translation. I suggest you do the following: let's substitute the [source] expression with [flashcard]:

... [flashcard] <article property="flashcard" mv-multiple> ... </article> ...

Noticed the difference? We still have a list, but its values are not simple values but objects, i.e., complex values containing all data that pertains to each flashcard. The good news is that the number of these objects is equal to the number of flashcards, since there is one for each flashcard, even when it's completely empty. So, right now we have an object for each flashcard, but how do we count them and display the total count?

Now let's get familiar with the MavoScript functions and find the one that would let us count the number of flashcards. Remember, we have a list of flashcards, so we need to find a function that would let us count the number of items in a list. And here it is—the count() function does exactly that!

But how can we use functions in expressions? Are there any rules we need to be aware of? Well, yes, there is a couple:

  1. Expressions are denoted by brackets.
  2. Do not nest brackets.

Let's try using the count() function to count the number of flashcards:

... [count(flashcard)] <article property="flashcard" mv-multiple> ... </article> ...

And that's exactly what we were aiming for—now we have some statistics in our app! Isn't that cool?

&#x1f4bb; Let's get our hands dirty!

I hope you've already warmed up and ready to continue experimenting with Mavo.

Improve the application so that the statistics are displayed not only for the total number of flashcards in the app but also for the number of flashcards in each topic separately if there are any topics.

&#x1f4a1;Hint!
Want to filter a list based on some criteria? The where operator will help. The self-evaluation feature

We already have an application that lets us create, edit and store multiple flashcards. But how do we keep track of which ones we have already learned and which ones we need to practice more? Any respectable flashcards application needs a self-evaluation feature. Let's investigate how we can add that!

Suppose in our app we have two buttons for the self-evaluation: the Bad and the Good. What exactly do we want to happen every time an end user clicks the buttons? Well, the idea is rather simple:

  • Clicking the Bad button would indicate the user hasn't learned the word yet and we want our app to move the corresponding flashcard to the beginning of the list so they could see it as soon as possible after launching the app.
  • Clicking the Good button would indicate the user has learned the word and the corresponding flashcard needs to move to the end of the list to let them work with other flashcards which they haven't learned yet.

“Are you sure we can do that without JavaScript?” you may ask. Yep! Mavo is extremely powerful and is able to equip us with all the tools we need!

Now when we know what we are going to implement, let's set the UI in place first and then move on to the next step. Our markup would look something like this:

... <article property="flashcard" mv-multiple> ... <section> <h2>Evaluate Yourself</h2> <button>Bad</button> <button>Good</button> </section> </article> ... The mv-action attribute

See the Pen
07. The mv-action attribute
by Dmitry Sharabin (@dsharabin)
on CodePen.

Mavo actions allow us to create our very own controls that modify data in custom ways when the user interacts with them. Sounds promising right?

To define a custom action we need to use the mv-action attribute on an appropriate element inside our Mavo app. The action is performed every time the element is clicked. That’s exactly what we were looking for.

&#x1f407; Follow the white rabbit!

For <form> elements a custom action is performed when the form is submitted.

The value of the mv-action attribute is an expression. We can use any of the expression functions and syntax that MavoScript provides to us, as well as a few more to facilitate data manipulation, such as add(), set(), move(), or delete(). It is important to note that unlike normal expressions which are evaluated reactively, these expressions are only evaluated each time the action is triggered.

&#x1f407; Follow the white rabbit!

Mavo expects the value of the mv-action attribute would be an expression, so there is no need to enclose it in brackets: mv-action="expression". Moreover, if we include them, they will be considered part of the expression.

So, we need to move flashcards inside the collection, and Mavo has an appropriate function that lets us do that—the move() function. Its first argument refers to the item we are moving, and the second is its position in the collection. Bear in mind that elements of the collection are numbered starting from 0.

&#x1f407; Follow the white rabbit!

Want to learn more about the move function (and its variants), and about custom actions in general, see the documentation.

Let's implement the first point of the outline we discussed earlier: while self-evaluating, an end user clicks the Bad button and the corresponding flashcard moves to the beginning of the collection, i.e., it becomes the first one. So in the code, we have:

... <article property="flashcard" mv-multiple> ... <button mv-action="move(flashcard, 0)">Bad</button> ... </article> ...

Pay attention that in the mv-action attribute we refer to the flashcard property inside the property itself, since we want to deal with the current flashcard.

If we try to implement the second point of the outline, we will face a problem. Can you suggest what problem exactly will it be?

Let's remember that if an end user clicks the Good button the corresponding flashcard moves to the end of the collection, i.e., it becomes the last one. To make a flashcard last in the collection we need to know the number of items in it.

Thankfully, a bit earlier we've already solved that task and implemented the corresponding feature. But could we use that solution to solve our current problem? Unfortunately, we can't: as we already know, we can refer to the collection of flashcards by its name (and evaluate its size) only outside the flashcard property. But in our case, we need to do that inside it: the Good button for which we need to write an expression is inside the flashcard property.

What should we do then? I'm glad you ask. Mavo has the solution.

Using the meta element to hold intermediate values

See the Pen
08. The <meta> element
by Dmitry Sharabin (@dsharabin)
on CodePen.

So, on the one hand, we know that the [count(flashcards)] expression gives us the number of flashcards if it is evaluated outside the flashcard property. On the other hand, we need to use that value inside the flashcard property.

To solve that dilemma, we need to evaluate the number of flashcards outside the flashcard property and somehow hold the result to be able to use it elsewhere in the app, precisely inside the flashcard property. For cases like that, in Mavo, there are so-called computed properties.

To hold an intermediate result so we can refer to it, we need an HTML element in our code. It is recommended to use the <meta> element for that purpose, like so: <meta property="propertyName" content="[expression]">. The advantage of using this element is that it is hidden outside edit mode, both semantically and visually.

&#x1f407; Follow the white rabbit!

Bear in mind that computed properties are not saved by default.

Now let's add the flashcardCount computed property in our app. Remember, we must place it outside the flashcard property, but then we can refer to it from anywhere:

... <meta property="flashcardCount" content="[count(flashcard)]"> <article property="flashcard" mv-multiple> ... </article> ...

Only one step left to finish the implementation of the self-evaluation feature: if an end user clicks the Good button the corresponding flashcard moves to the end of the collection, i.e., it becomes the last one. Let's add the relevant action in the code of our app:

... <meta property="flashcardCount" content="[count(flashcard)]"> <article property="flashcard" mv-multiple> ... <button mv-action="move(flashcard, flashcardCount)">Good</button> </article> ...

We are done! Congratulations! &#x1f600;

&#x1f4bb; Let's get our hands dirty!

There is another way to solve that task: with the help of the $all special property. The $all property represents a collection itself if it is placed inside the collection. So there is no need to use any computed property in this case. Try to implement that solution on your own.

There is only one more tiny thing left that we need to fix. Remember the part where we added some stats to our app? Remember the expression we built to evaluate the number of flashcards in the app: [count(flashcard)]? Instead, we can (and should) now use the computed property we defined. Make the appropriate changes in the app. Takeaways

So what have we learned so far? Let's recap. In order to turn any static HTML page into a Mavo app we need to:

  1. Include the Mavo JavaScript and CSS files in the page’s <head> section.
  2. Add the mv-app attribute to the Mavo root element.
  3. Tell Mavo what elements of our app are important by adding the property attribute to them.
  4. Place the mv-multiple attribute on the element that will be multiplied and converted into a collection.
  5. Tell Mavo where to store our data by adding mv-storage attribute to the Mavo root.
  6. Decide whether Mavo should save our data automatically or not. If yes, add the mv-autosave attribute to the Mavo root.

    We also know that:

  7. The Mavo toolbar is fully-customizable. The mv-bar attribute controls which buttons are going to appear there.
  8. Expressions let us present the current value of properties in other elements and perform computations. An expression value (and type) vary depending on the place the expression takes in the code. Mavo’s expression syntax is called MavoScript.
  9. Custom actions allow creating controls that modify data in custom ways. To define a custom action set the mv-action attribute on an appropriate element inside a Mavo app
  10. Properties whose values are expressions are called computed properties. To hold an intermediate result to be able to refer to it elsewhere in the app, it is recommended to use the <meta> element.
Instead of an epilogue

So we built our app. Is it already perfect? Of course not, nothing is! There are so many things that can be improved, and so many features that can be added (with the help of Mavo, we can even make our app multilingual!). Go ahead, enhance it more, don't hesitate to try something new!

What we've learned so far about Mavo is just the tip of the iceberg, and there is so much more. I encourage you to give it a closer look, by reading the documentation, by examining examples (on the Mavo site, or on CodePen: made by Lea Verou and a few made by myself), and by creating new stuff! Good luck! &#x1f609;

Acknowledgments

I want to thank two great people. First of all, my huge thanks go to Lea Verou, who not only inspired me to write this tutorial (and helped me make it happen) but also inspires me all the time by the way she makes the world of web development a better place. I've never met such a gifted human being, and I am happy having an opportunity to make some stuff with her!

I also thank James Moore. The examples he uses in his course "Functional Programming for Beginners with JavaScript” on Udemy pushed me to make my very own version of a flashcard study app. He is a wonderful teacher!

The post Let Mavo Shine in Building Interactive Web Applications appeared first on CSS-Tricks.

Can you nest @media and @support queries?

Css Tricks - Mon, 08/05/2019 - 4:50am

Yes, you can, and it doesn't really matter in what order. A CSS preprocessor is not required. It works in regular CSS.

This works:

@supports(--a: b) { @media (min-width: 1px) { body { background: red; } } }

And so does this, the reverse nesting of the above:

@media (min-width: 1px) { @supports(--a: b) { body { background: green; } } }

You can keep going with the nesting, if it ever makes sense:

@media (min-width: 2px) { @media (min-width: 1px) { @supports (--a: b) { @supports (display: flex) { body { background: pink; } } } } }

There is probably a point where the logic of that gets out of hand, so careful. But hey, it works. Presumably, you can "infinitely" nest at-rules.

To look at nested code like that looks a little like a CSS preprocessor is in use, but that's not the case here. Regular ol' CSS handles this just fine, and in fact, CSS preprocessors don't meaningfully manipulate it at all (tested Sass, Less, and Stylus).

The post Can you nest @media and @support queries? appeared first on CSS-Tricks.

The Real Dark Web

Css Tricks - Mon, 08/05/2019 - 4:50am

Here’s a wonderful reminder from Charlie Owen that everyone in the web design industry isn’t using the latest and greatest technology. And that’s okay! Charlie writes:

Most web developers are working on very "boring" teams. They're producing workhorse products that serve the organisation needs. They aren't trying to innovate. They aren't trying to impress the next recruiter with their CV. They simply want to get paid, produce a solution that works, and go home.

Yet they are told, mostly implicitly, sometimes directly, that if they're not innovating and using the latest tools that they are somehow a failure. This feeling of failure for not implementing the latest tech permeates our industry.

This feeling needs to end.

I feel that this is a big problem for our community because there are a small number of folks on the bleeding edge that happen to be quite loud about their work (even here at CSS-Tricks) – and that’s great! We all need to learn from folks that are doing this work. However, we need to remind ourselves that this is a very small number of folks and not every project has to be a technical marvel to be a success.

Take Michelle Barker's personal site, for example. It's slick but is also dependency-free so she could focus on writing the languages she loves: HTML and CSS. Not a bad goal, nor a bad outcome.

This also harkens back to something Chris mentioned when discussing complexity in web development:

There are more developers these days working on in-house teams or agencies with big-ticket clients. That is, more and more developers on large-scope, long-scale, highly-complex jobs. So that's where their minds are at. Big complicated problems with big complicated solutions. That's what gets talked about. That's what gets blogged about. That's what gets argued about. That's the topic at a lot of conferences I've been to.

While you certainly can make a soup-of-the-day website with an index.html file and FTP, blog posts about that are fewer and farther between and don't get as many claps.

It's not so much that we need cheers to validate our work; it's merely recognizing that not everything has to be on the bleeding edge of technology. There's something to be said about "simple and boring" projects.

Perhaps the real thing to fear is less about what we're sharing and more about what we're not sharing.

Direct Link to ArticlePermalink

The post The Real Dark Web appeared first on CSS-Tricks.

Branching Out from the Great Divide

Css Tricks - Fri, 08/02/2019 - 8:51am

I like the term Front-End Developer. It's encapsulates the nature of your job if your concerns are:

  • Building UIs for web browsers
  • The spectrum of devices and platforms those web browsers run on
  • The people who use those web browsers and related assistive technology

The breadth of knowledge for all-things front-end development has gotten super deep. I've found that front-end developers that have stretched themselves to the point they are thinking of themselves as full-stack developers more and more. I think that's kinda cool and empowering, but it doesn't mean that everyone needs to go that wide.

Brad Frost referred to sides of the spectrum as "back of the front" and "front of the front." I once drew the line, in The Great Divide, as heavy JavaScript vs. not. These distinctions aren't to divide people, but to acknowledge the spectrum and that there are people all over it.

In a new article called "Frontend Design, React, and a Bridge over the Great Divide," Brad makes the point that the role of "Front-End Designer" exists on the spectrum right smack in the middle between design and development, where "development" refers to the back-end or deeper JavaScript stuff.

The jobs?

  • Crafting semantic HTML markup
  • Creating CSS code
  • Authoring JavaScript that primarily manipulates objects in the DOM
  • Testing across browsers and devices
  • Optimizing the performance of front-end code
  • Working with designers
  • Working with back-end and application developers

That sounds like the "traditional" explanation of a front-end developer to me — if there is such a thing — but it makes sense to rename that role since front-end development is the term that got so wide.

Brad adds these responsibilties to the list:

  • Create a library of presentational UI components
  • Author and document a robust, intuitive component API for each presentational component
  • Determine how flexible or rigid the component library should be
  • Maintain the presentational components as a product

That's where I think this metaphor comes in:

Me, Brad and a slew of you out there are front-end developers. We work in browsers and we care about the users and where and how they interact with those browsers. We do the things on Brad's first list like craft HTML and CSS, work with designs and do testing. We share that common trunk of skills on the tree above.

But Brad is more of a systems designer than I am. His dot lands somewhere differently on that tree. I don't know if I'm particularly skilled at anything, but my dot definitely falls elsewhere on that tree. Perhaps on an entirely different branch, as I quite like working with JavaScript tooling and logic and APIs and such. The bulk of Brad's article is about React and finding a place in the realm of front-end development where the job isn't ignoring React, but working with it in such a way that doesn't mean every other aspect of development doesn't have to come along for the ride.

The post Branching Out from the Great Divide appeared first on CSS-Tricks.

Using Netlify Forms and Netlify Functions to Build an Email Sign-Up Widget

Css Tricks - Fri, 08/02/2019 - 4:13am

Building and maintaining your own website is a great idea. Not only do you own your platform, but you get to experiment with web technologies along the way. Recently, I dug into a concept called serverless functions, starting with my own website. I’d like to share the results and what I learned along the way, so you can get your hands dirty, too!

But first, a 1-minute intro to serverless functions

A serverless function (sometimes called a lambda function or cloud function) is a piece of code that you can write, host, and run independently of your website, app, or any other code. Despite the name, serverless functions do, indeed, run on a server; but it’s a server you don’t have to build or maintain. Serverless functions are exciting because they take a lot of the legwork out of making powerful, scalable, apps.

There’s lots of great information on serverless functions out there, and a great place to start is CSS Trick’s own guide: The Power of Serverless Front-End Developers.

The Challenge: Build a Mailing List Sign Up Form

I started my journey with a challenge: I wanted to have an email list sign-up form on my site. The rules are as follows:

  • It should work without JavaScript. I’d like to see how much I can get by with just CSS and HTML. Progressive enhancements are OK.
  • It shouldn’t require external dependencies. This is a learning project, so I want to write 100% of the code if possible.
  • It should use serverless functions. Instead of sending data to my email list service client-side, let’s do it server(less)-side!
Meet the team: 11ty, Netlify, and Buttondown

My website is built using a static site framework called 11ty. 11ty allows me to write templates and components in HTML, so that’s how we’ll build our email form. (Chris recently wrote a great article about his experience with 11ty if you’re interested in learning more.)

The website is then deployed using a service called Netlify) and it is the key player on our team here: the point guard, the quarterback, the captain. That’s because Netlify has three features that work together to produce serverless excellence:

  • Netlify can deploy automatically from a GitHub repo. This means I can write my code, create a pull request, and instantly see if my code works. While there are tools to test serverless functions locally, Netlify makes it super easy to test live.
  • Netlify Forms handles any form submissions my site gets. This is one part of the serverless equation: instead of writing code to collect submissions, I’ll configure the HTML with a few simple attributes and let Netlify handle the rest.
  • Netlify Functions allows me to take action with the data from my forms. I’ll write some code to send emails off to my email list provider, and tell Netlify when to run that code.

Finally, I’ll manage my email list with a service called Buttondown. It’s a no-frills email newsletter provider, with an easy-to-use API.

Bonus: for personal sites like mine, 11ty, Netlify, and Buttondown are free. You can’t beat that.

The form

The HTML for my email subscription form is very minimal, with a few extras for Netlify Forms to work.

<form class="email-form" name="newsletter" method="POST" data-netlify="true" netlify-honeypot="bot-field"> <div hidden aria-hidden="true"> <label> Don’t fill this out if you're human: <input name="bot-field" /> </label> </div> <label for="email">Your email address</label> <div> <input type="email" name="email" placeholder="Email" id="email" required /> <button type="submit">Subscribe</button> </div> </form>

First, I set the data-netlify attribute to true to tell Netlify to handle this form.

The first input in the form is named bot-field. This tricks robots into revealing themselves: I tell Netlify to watch for any suspicious submissions by setting the netlify-honeypot attribute to bot-field. I then hide the field from humans using the html hidden and aria-hidden values — users with and without assistive technology won’t be able to fill out the fake input.

If the form gets submitted with anything in the bot-field input, Netlify knows it’s coming from a robot, and ignores the input. In addition to this layer of protection, Netlify automatically filters suspicious submissions with Askimethttps://www.netlify.com/blog/2019/02/12/improved-netlify-forms-spam-filtering-using-akismet/). I don’t have to worry about spam!

The next input in the form is named email. This is where the email address goes! I’ve specified the input-type as email, and indicated that is required; this means that the browser will do all my validation for me, and won’t let users submit anything other than a valid email address.

Progressive enhancement with JavaScript

One neat feature of Netlify Forms is the ability to automatically redirect users to a “thank you” page when they submit a form. But ideally, I’d like to keep my users on the page. I wrote a short function to submit the form without a redirect.

const processForm = form => { const data = new FormData(form) data.append('form-name', 'newsletter'); fetch('/', { method: 'POST', body: data, }) .then(() => { form.innerHTML = `<div class="form--success">Almost there! Check your inbox for a confirmation e-mail.</div>`; }) .catch(error => { form.innerHTML = `<div class="form--error">Error: ${error}</div>`; }) }

When I provide the content of my email form to this function via the form value, it submits the form using JavaScript’s built-in Fetch API. If the function was successful, it shows a pleasant message to the user. If the function hits a snag, it’ll tell my users that something went wrong.

This function is called whenever a user clicks the “submit” button on the form:

const emailForm = document.querySelector('.email-form') if (emailForm) { emailForm.addEventListener('submit', e => { e.preventDefault(); processForm(emailForm); }) }

This listener progressively enhances the default behavior of the form. This means that if the user has JavaScript disabled, the form still works!

The serverless function

Now that we have a working email submission form, it’s time to do some automation with a serverless function.

The way Netlify functions work is as follows:

  1. Write the function in a JavaScript file in your project.
  2. Tell Netlify where to look for your function via the netlify.toml file in your project.
  3. Add any environment variables you’ll need via Netlify’s admin interface. An environment variable is something like an API key that you need to keep secret.

That’s it! The next time you deploy your site, the function will be ready to go.

The function for my site is going to be in the functions folder, so I have the following in my netlify.toml file:

[build] base = "." functions = "./functions"

Then, I’ll add a file in the functions folder called submission-created.js. It’s important to name the file submission-created so that Netlify knows to run it every time a new form submission occurs. A full list of events you can script against can be found in Netlify’s documentation. If you’ve correctly named and configured your function, you should see it on Netlify’s Functions dashboard:

Netlify’s Functions dashboard shows I’ve correctly configured my submission-created function

The content in submission-created.js looks like this:

require('dotenv').config() const fetch = require('node-fetch') const { EMAIL_TOKEN } = process.env exports.handler = async event => { const email = JSON.parse(event.body).payload.email console.log(`Recieved a submission: ${email}`) return fetch('https://api.buttondown.email/v1/subscribers', { method: 'POST', headers: { Authorization: `Token ${EMAIL_TOKEN}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ email }), }) .then(response => response.json()) .then(data => { console.log(`Submitted to Buttondown:\n ${data}`) }) .catch(error => ({ statusCode: 422, body: String(error) })) }

Let’s look at this line-by-line.

Line 1 includes a library called dotenv. This will help me use environment variables. Environment variables are useful to hold information that I don’t want to make public, like an API key. If I’m running my project locally, I set my environment variables with a .env file in the repo, and make sure it’s listed my .gitignore file. In order to deploy on Netlify, I also set up environment variables in Netlify’s web interface.

On line 2, I add a small library called node-fetch. This allows me to use Javascript’s Fetch API in node, which is how we’ll send data to Buttondown. Netlify automatically includes this dependency, as long as it’s listed in my project’s package.json file.

On line 3, I import my API key from the environment variables object, process.env.

Line 4 is where the function is defined. The exports.handler value is where Netlify expects to find our function, so we define it there. The only input we’ll need is the event value, which will contain all of the data from the form submission.

After retrieving the email address from the event value using JSON.parse, I’m ready to send it off to Buttondown. Here’s where I use the node-fetch library I imported earlier: I send a POST request to https://api.buttondown.email/v1/subscribers, including my API key in the header. Buttondown’s API doesn’t have many features, so it doesn’t take long to read through the documentation if you’d like to learn more.

The body of my POST request consists of the email address we retrieved.

Then (using the neat .then() syntax), I collect the response from Buttondown’s server. I do this so I can diagnose any issues that are happening with the process — Netlify makes it easy to check your function’s logs, so use console.log often!

Deploying the function

Now that I’ve written my function, configured my netlify.toml file, and added my environment variables, everything is ready to go. Deploying is painless: just set up Netlify’s GitHub integration, and your function will be deployed when your project is pushed.

Netlify projects can also be tested locally using Netlify Dev. Depending on the complexity of your code, it can be faster to develop locally: just run npm i netlify -g, then netlify dev. Netlify Dev will use the netlify.toml file to configure and run the project locally, including any serverless functions. Neat, right? One caveat: Netlify Dev currently can’t trigger serverless functions on form submissions, so you’ll have to test that using preview builds.

An idea for the future

Buttondown’s API has a few possible responses when I submit a new email. For instance, if a user submits an email that’s already subscribed to the list, I’d love to be able to tell them as soon as they submit the form.

Conclusion

All in all, I only had to write about 50 lines of code to have a functional email newsletter sign-up form available on my website. I wrote it all in HTML, CSS, and JavaScript, without having to fret with the server side of the equation. The form handles spam, and my readers get a nice experience whether they have JavaScript enabled or not.

The post Using Netlify Forms and Netlify Functions to Build an Email Sign-Up Widget appeared first on CSS-Tricks.

Weekly Platform News: Preventing Image Loads with the Picture Element, the Web We Want, Svg Styles Are Not Scoped

Css Tricks - Thu, 08/01/2019 - 11:07am

In this week's week roundup of browser news, a trick for loading images conditionally using the picture element, your chance to tell bowser vendors about the web you want, and the styles applied to inline SVG elements are, well, not scoped only to that SVG.

Let's turn to the headlines...

Preventing image loads with the picture element

You can use the <picture> element to prevent an image from loading if a specific media query matches the user’s environment (e.g., if the viewport width is larger or smaller than a certain length value). [Try out the demo:

See the Pen
voZENR
by Šime Vidas (@simevidas)
on CodePen.

<picture> <!-- show 1?1 transparent image if viewport width ? 40em --> <source media="(max-width: 40em)" srcset="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" /> <!-- load only image if viewport width > 40em --> <img src="product-large-screen.png" /> </picture>

(via Scott Jehl)

The Web We Want

The Web We Want (webwewant.fyi) is a new collaboration between browser vendors that aims to collect feedback from web developers about the current state of the web. You can submit a feature request on the website ("What do you want?"") and get a chance to present it at an event (An Event Apart, Smashing Conference, etc.).

(via Aaron Gustafson)

In other news
  • Firefox supports a non-standard Boolean parameter for the location.reload method that can be used to hard-reload the page (bypassing the browser’s HTTP cache) [via Wilson Page]
  • If you use inline <svg> elements that itself have inline CSS code (in <style> elements), be aware that those styles are not scoped to the SVG element but global, so they affect other SVG elements as well [via Sara Soueidan]
  • XSS Auditor, a Chrome feature that detects cross-site scripting vulnerabilities, has been deemed ineffective and will be removed from Chrome in a future version. You may still want to set the HTTP X-Xss-Protection: 1; mode=block header for legacy browsers [via Scott Helme]

Read more news in my new, weekly Sunday issue. Visit webplatform.news for more information.

The post Weekly Platform News: Preventing Image Loads with the Picture Element, the Web We Want, Svg Styles Are Not Scoped appeared first on CSS-Tricks.

Syndicate content
©2003 - Present Akamai Design & Development.