Archiwum kategorii: CSS

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

Post pobrano z: Design Principles for Developers: Processes and CSS Tips for Better Web Design

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.

Moving Text on a Curved Path

Post pobrano z: Moving Text on a Curved Path

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 his 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.

Moving Text on a Curved Path

Post pobrano z: Moving Text on a Curved Path

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 his 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

Post pobrano z: Building a Full-Stack Serverless Application with Cloudflare Workers

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.

Building a Full-Stack Serverless Application with Cloudflare Workers

Post pobrano z: Building a Full-Stack Serverless Application with Cloudflare Workers

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, webhin browser extension, CSS Modules V1

Post pobrano z: Weekly Platform News: CSS font-style: oblique, webhin browser extension, CSS Modules V1

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, webhin browser extension, CSS Modules V1 appeared first on CSS-Tricks.

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

Post pobrano z: Weekly Platform News: CSS font-style: oblique, webhin browser extension, CSS Modules V1

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, webhin browser extension, CSS Modules V1 appeared first on CSS-Tricks.

Making a Realistic Glass Effect with SVG

Post pobrano z: Making a Realistic Glass Effect with SVG

I’m in love with SVG. Sure, the code can look dense and difficult at first, but you’ll see the beauty in the results when you get to know it. The bonus is that those results are in code, so it can be hooked up to a CMS. Your designers can rest easy knowing they don’t have to reproduce an effect for every article or product on your site.

Today I would like to show you how I came up with this glass text effect.

The word Kyoto that is translucent and stacked on top an image of the city.

Step 0: Patience and space

SVG can be a lot to take on, especially when you’re just starting to learn it (and if you are, Chris’ book is a good place to start). It’s practically a whole new language and, especially for people who lack design chops, there are lots of new techniques and considerations to know about. Like HTML, though, you’ll find there are a handful of tools that we can reach for to help make SVG much easier to grasp., so be patient and keep trying!

Also, give yourself space. Literally. SVG code is dense so I like to use two or three new lines to space things out. It makes the code easier to read and helps me see how different pieces are separated with less visual distraction. Oh, and use comments to mark where you are in the document, too. That can help organize your thoughts and document your findings.

I’ve made demos for each step we’re going to cover in the process of learning this glass effect as a way to help solidify the things we’re covering as we go.

OK, now that we’re mentally prepared, let’s get into the meat of it!

Step 1: Get the basic image in place

First things first: we need an image as the backdrop for our glass effect. Here we have an <svg> element and an <image> within it. This is similar to adding an <img> in HTML. You’ll notice the dimensions of the viewBox attribute and <image> element in the SVG element are the same. This ensures that the <image> is exactly the same size as the actual picture we’re linking to.

That’s a key distinction to note: we’re linking to an image. The SVG file itself does not draw a raster image, but we can reference one in the SVG code and make sure that asset is in the location we point to. If you’ve worked with Adobe InDesign before, it’s a lot like linking to an image asset in a layout — the image is in the InDesign layout, but the asset itself actually lives somewhere else.

See the Pen
SVG Glass Text Effect – basic image in place
by David Fitzgibbon (@loficodes)
on CodePen.

Step 2: Distort the image

Straightforward so far, but this is where things get complicated because we’re going to add a filter to the image we just inserted. This filter is going to distort the image. If you look closely at the difference between the demo in the last step and the one in this step, you’ll see that the edges of objects in the image are a little rough and wavy. That’s the filter at work!

First, we create another <svg> to hold filter. This means that if we ever want to reuse our filter — for example on multiple elements on the page — then we totally can!

Our first filter (#displacement) is going to distort our image. We’re going to use feTurbulence and feDisplacementMap, each explained by Sara Soueidan much better than I can in this post. Beau Jackson also wrote up a nice piece that shows how they can be used to make a cloud effect. Suffice to say, these two filters tend to go together and I like to think of them as when something needs to appear „wobbly.”

With our filter container in place, we just need to apply that filter to our image with a filter attribute on the <image>, magic!

<svg>

  <!-- more stuff -->
  
  <!-- DISTORTION IMAGE: clipped -->
  <image xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/5946/kyoto.jpg" width="1890" x=0 height="1260" y=0 clip-path="url(#clip)" filter= "url(#distortion)"></image>
  
  <!-- FILTER: wobbly effect -->
  <filter id="distortion">
    <feTurbulence type="turbulence" baseFrequency="0.05" numOctaves="2" result="turbulence"/>
    <feDisplacementMap in2="turbulence" in="SourceGraphic" scale="20" xChannelSelector="R" yChannelSelector="G"/>
  </filter>

  <!-- more stuff -->

</svg>

See the Pen
SVG Glass Text Effect – image distorted
by David Fitzgibbon (@loficodes)
on CodePen.

Step 3: Clip the text

We don’t want the entire image to be distorted though. We’re going to clip the shape of our distorted <image> to the shape of some text. This will essentially be the portion of the picture seen „through” the glass.

To do this, we need to add a <text> element in a <clip-path> and give it an id. Calling this id in the clip-path of our <image> now restricts its shape to that of our <text>. Wonderful!

See the Pen
SVG Glass Text Effect – text clipped
by David Fitzgibbon (@loficodes)
on CodePen.

Step 4: Reveal the full image

OK, so it’s bueno that we have the distorted <image> clipped to the <text>, but now the rest of the image is gone. No bueno.

We can counteract this by adding a copy of the same <image> but without the clip-path or filter attributes before our existing <image>. This is where I like to add some nice comments to keep things neat. The idea is like placing a transparent layer over what we have so far.

Illustration showing one image overlaid on the other.

I know, I know, this isn’t very neat, and we’re repeating ourselves. Ideally, we would set our filter straight on the <text> element and use the in="BackgroundImage property for feDisplacementMap to warp what’s behind the text, without the need for extra elements. Unfortunately, this has poor browser support, so we’re going to go with multiple images.

<svg>

  <!-- more stuff -->

  <!-- BACKGROUND IMAGE - visible -->
  <image xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/5946/kyoto.jpg" width="1890" x=0 height="1260" y=0 ></image>
          
  <!-- DISTORTION IMAGE - clipped -->
  <image xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/5946/kyoto.jpg" width="1890" x=0 height="1260" y=0 clip-path="url(#clip)" filter= "url(#distortion)"></image>

  <!-- more stuff -->

</svg>

See the Pen
SVG Glass Text Effect – warp complete
by David Fitzgibbon (@loficodes)
on CodePen.

Step 5: Place the text… again

Next, we’re going to duplicate our text just as we did for the image in the last step. Unfortunately, because the text is in a clip-path, it’s now not available for rendering. This is the last time we’re going to duplicate content like this, I promise!

Now we should have something that looks like a normal image with black text over it. If the distortion filter on the <image> we’ve already made is what we can see „through” the glass, then our new <text> is going to be the glass itself.

<svg>

  <!-- more stuff -->

  <!-- TEXT - clipped -->
  <clipPath id="clip">
    <text x="50%" y ="50%" dominant-baseline="middle" text-anchor="middle">KYOTO</text>
  </clipPath>
        
  <!-- TEXT - visible -->
  <text x="50%" y ="50%" dominant-baseline="middle" text-anchor="middle">KYOTO</text>

<!-- more stuff -->

</svg>

See the Pen
SVG Glass Text Effect – text in place again
by David Fitzgibbon (@loficodes)
on CodePen.

Step 6: Creating the dark edge of the text

This is where things start to get exciting, at least for me! 🤓

We want to create a dark edge along the text element which, when paired with a light edge (we’ll look at that next), will add depth to the appearance of the text against the image.

We want a new filter for our <text>, so let’s create one in our filter’s SVG element and give it an id="textFilter and link it to the filter attribute of the <text> element.

SVG works from the background to the foreground, so the first thing we’re going put in our filter is the shadow that the glass would have, as that is furthest back. I’m gonna level with you, this one is pretty complex, but we’re going to go through it one step at a time.

For this effect, we’re using four filter primitives: feMorphology, feOffset, feFlood and feComposite.

feMorphology is first. We’re using this to make the text fatter. In the demo below, comment out the next three primitives ( feOffset, feFlood, feComposite ) and play with it. I have the value radius="4" to achieve the glass effect, but see what happens if you set it to 1… or 100!

feOffset is used to move all the „pixels” in the previous primitive ( feMorphology ) across the x- or y-axis. The values dx="5" and dy="5" move the „pixels” right on the x-axis and y-axis, respectively. The higher the number, the further they move. Put in negative numbers for dx and the „pixels” will move left. Negative dy and they’ll move up! Again, the is the sort of thing you start to learn as you play around with them.

The reason I have quotes around „pixels” is because they’re not screen pixels like you might expect in CSS. Rather, they refer to the dimensions we set on the parent <svg>. I think of them as percentages. We have used these settings viewBox="0 0 1890 1260" in our example. This means our <svg> is 1890 „pixels” wide. If we set dx="189" it means we’ll move our element 10% of the way across the SVG (1890 divided by 189).

feFlood is great. If you want to fill the screen with color, this is the primitive you need! You might wonder why we can’t read our text now when we apply it. That’s because you can only see the result of the last filter primitive that was created. The result of each of the previous primitives was related to our <text> element. The result of feFlood is just like its name: a flood of color. It doesn’t know what you did before and it doesn’t care — it’s merely going to fill an area with color.

This is where some people start getting frustrated with SVG. It’s hard to work on something when you can’t see it! Trust me, as you work with SVG more you’ll get used to this. In fact, the next few steps will need us to rely on this and trust that everything is still in place.

feComposite is going to solve this issue for us. What does it do? MDN describes it as:

The SVG filter primitive performs the combination of two input images pixel-wise in image space using one of the Porter-Duff compositing operations: over, in, atop, out, xor, and lighter.

That to me is jibba-jabba. I think of it as affecting the alpha layer of in with the color/alpha of in2.

With this in place we can once again see our text spelled out and, because the color we used is slightly transparent, we can even see the distorted „glass” effect coming through. Great!

<svg>

  <!-- more stuff -->
    
  <!-- dark edge -->
  <feMorphology operator="dilate" radius="4" in="SourceAlpha" result="dark_edge_01" />
    <feConvolveMatrix order="3,3" kernelMatrix=
      "1 0 0 
      0 1 0
      0 0 1" in="dark_edge_01" result="dark_edge_02" />
    <feOffset dx="5" dy="5" in="dark_edge_02" result="dark_edge_03"/>
    <feFlood flood-color="rgba(0,0,0,.5)" result="dark_edge_04" />
    <feComposite in="dark_edge_04" in2="dark_edge_03" operator="in" result="dark_edge" />
    
  </filter>

  <!-- more stuff -->

</svg>

See the Pen
SVG Glass Text Effect – dark edge
by David Fitzgibbon (@loficodes)
on CodePen.

Step 7: Let’s do the light edge

This is essentially the same as what we literally just did, but we’re going to shift the shape up and to the left using negative dx/dy values. We’re also setting a slightly white color this time. We’re aiming for a nice depth effect.

We’re again in a position where what we can see is the most recent result from a filter primitive, but we can’t see our dark edge! feComposite isn’t what we want to use to bring them together because we don’t want the alpha of the dark edge colored by the light edge… we want to see both! Which leads us to…

<svg>
  <filter id="textFilter">

    <!-- more stuff -->

      <feMorphology operator="dilate" radius="4" in="SourceAlpha" result="light_edge_01" />
      <feConvolveMatrix order="3,3" kernelMatrix=
      "1 0 0 
        0 1 0
        0 0 1" in="light_edge_01" result="light_edge_02" />
      <feOffset dx="-2" dy="-2" in="light_edge_02" result="light_edge_03"/>
      <feFlood flood-color="rgba(255,255,255,.5)" result="light_edge_04" />
      <feComposite in="light_edge_04" in2="light_edge_03" operator="in" result="light_edge" />

    <!-- more stuff -->
  
  </filter>
</svg>

See the Pen
SVG Glass Text Effect – light edge
by David Fitzgibbon (@loficodes)
on CodePen.

Step 8: Combine the edges

feMerge! It’s a hero. It lets us take any number of primitive results and merge them, making a new image. Woohoo, we can now see both dark and light edges together!

However, we do want them to be edges rather than both filling up the entire text, so we need to remove the space that the original <text> takes up. What we need next is another feComposite to chop out the original SourceGraphic. Because we used feMorphology to fatten the letters for our edges, we can now chop the original letter shapes out of the result of our feMerge.

<svg>
  <filter id="textFilter">

    <!-- more stuff -->

    <feMerge result="edges">
      <feMergeNode in="dark_edge" />
      <feMergeNode in="light_edge" />
    </feMerge>
    <feComposite in="edges" in2="SourceGraphic" operator="out" result="edges_complete" />

  </filter>
</svg>

See the Pen
SVG Glass Text Effect – edges combined
by David Fitzgibbon (@loficodes)
on CodePen.

Now we’re starting to look like glass, with just one piece missing.

Step 9: Yes, a bevel

We have a pretty good 3D-looking glass effect. However, the letters look flat. Let’s add one more effect and make them look more rounded.

To achieve this we’re going to create a bevelled effect.

First we’re going to use feGaussianBlur. This will blur our existing filters slightly. We’re going to use this blurred result as basis to add some feSpecularLighting. As usual, feel free to play with the numbers here and see what effects you can get! The main one you might want to change is the lighting-color attribute. The image that we’re using here is slightly dark, so we’re using a bright lighting-color. If your image was very bright, this would make the letters hard to read, so you might use a darker lighting-color in that case.

<svg>
  <filter id="textFilter">
  
    <!-- more stuff -->

    <feGaussianBlur stdDeviation="5" result="bevel_blur" />
    <feSpecularLighting result="bevel_lighting" in="bevel_blur" specularConstant="2.4" specularExponent="13" lighting-color="rgba(60,60,60,.4)">
      <feDistantLight azimuth="25" elevation="40" />
    </feSpecularLighting>
    <feComposite in="bevel_lighting" in2="SourceGraphic" operator="in" result="bevel_complete" />

  </filter>
</svg>

See the Pen
SVG Glass Text Effect – bevel
by David Fitzgibbon (@loficodes)
on CodePen.

Step 10: All together now!

Finally, with all the pieces ready, we do one last feMerge to get everything in place for the finished effect!

<svg>
  <filter id="textFilter">

    <!-- more stuff -->

    <feMerge result="complete">
      <feMergeNode in="edges_complete" />
      <feMergeNode in="bevel_complete" />
    </feMerge>
  </filter>
</svg>

Here’s everything together, nicely spaced out and commented:

<!-- VISIBLE SVG -->
<svg viewBox="0 0 1890 1260">
        
  <!-- BACKGROUND IMAGE - visible -->
  <image xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/5946/kyoto.jpg" width="1890" x=0 height="1260" y=0 ></image>
    
  <!-- DISTORTION IMAGE - clipped -->
  <image xlink:href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/5946/kyoto.jpg" width="1890" x=0 height="1260" y=0 clip-path="url(#clip)" filter= "url(#distortion)"></image>
    
  <!-- TEXT - clipped -->
  <clipPath id="clip">
    <text x="50%" y ="50%" dominant-baseline="middle" text-anchor="middle">KYOTO</text>
  </clipPath>
    
  <!-- TEXT - visible -->
  <text x="50%" y ="50%" dominant-baseline="middle" text-anchor="middle" filter="url(#textFilter)">KYOTO</text>
    
</svg>

<!-- FILTERS -->
<svg>
  <filter id="distortion">
    <feTurbulence type="turbulence" baseFrequency="0.05" numOctaves="2" result="turbulence"/>
    <feDisplacementMap in2="turbulence" in="SourceGraphic" scale="20" xChannelSelector="R" yChannelSelector="G"/>
  </filter>
    
  <filter id="textFilter">
            
    <!-- dark edge -->
    <feMorphology operator="dilate" radius="4" in="SourceAlpha" result="dark_edge_01" />
    <feOffset dx="5" dy="5" in="dark_edge_01" result="dark_edge_03"/>
    <feFlood flood-color="rgba(0,0,0,.5)" result="dark_edge_04" />
    <feComposite in="dark_edge_04" in2="dark_edge_03" operator="in" result="dark_edge" />     
            
    <!-- light edge -->
    <feMorphology operator="dilate" radius="4" in="SourceAlpha" result="light_edge_01" />
    <feOffset dx="-2" dy="-2" in="light_edge_01" result="light_edge_03"/>
    <feFlood flood-color="rgba(255,255,255,.5)" result="light_edge_04" />
    <feComposite in="light_edge_04" in2="light_edge_03" operator="in" result="light_edge" />
          
    <!-- edges together -->
    <feMerge result="edges">
      <feMergeNode in="dark_edge" />
      <feMergeNode in="light_edge" />
    </feMerge>
    <feComposite in="edges" in2="SourceGraphic" operator="out" result="edges_complete" />
          
    <!-- bevel -->
    <feGaussianBlur stdDeviation="5" result="bevel_blur" />
    <feSpecularLighting result="bevel_lighting" in="bevel_blur" specularConstant="2.4" specularExponent="13" lighting-color="rgba(60,60,60,.4)">
      <feDistantLight azimuth="25" elevation="40" />
    </feSpecularLighting>
    <feComposite in="bevel_lighting" in2="SourceGraphic" operator="in" result="bevel_complete" />

    <!-- everything in place -->
    <feMerge result="complete">
              <feMergeNode in="edges_complete" />
              <feMergeNode in="bevel_complete" />
    </feMerge>

  </filter>
</svg>

See the Pen
SVG Glass Text Effect
by David Fitzgibbon (@loficodes)
on CodePen.

The post Making a Realistic Glass Effect with SVG appeared first on CSS-Tricks.

Branching Out from the Great Divide

Post pobrano z: Branching Out from the Great Divide

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:

A tree of nodes branching out, but sharing a common trunk.

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.

Branching Out from the Great Divide

Post pobrano z: Branching Out from the Great Divide

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:

A tree of nodes branching out, but sharing a common trunk.

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.