Front End Web Development

What React Does (and Doesn’t Do)

Css Tricks - Wed, 03/04/2020 - 1:39pm

With a name as big as React, it's bound to cause some Stream-Crossing Confusion, as I like to call it. How do you center a <div> in React? Dave Ceddia:

React cares exactly zero about styling. Think of it as generating the barebones HTML. React will put elements on the page, but everything after that is the job of CSS: how they appear, what they look like, how they’re positioned, and how centered or uncentered they are.

“How to center a div in React” is… not a React problem. It’s a CSS problem. You don’t need “react” in your Google query. Once you figure it out, use React to apply the right CSS class name to your components

How do you center a <div> in WordPress? How do you center a <div> in Vue? On one hand, they are broken questions. Those technologies don't have anything to do with centering things. Centering on the web is something CSS does. On the other hand, higher-level tech is sometimes involved. Maybe there is some Gutenberg thing in WordPress that handles centering on this particular site that you should use for editorial consistency. Maybe there is some styling library being used which means you can't write regular CSS — you have to write the way the library wants you to write it. Complicated, all this "making websites" stuff.

Direct Link to ArticlePermalink

The post What React Does (and Doesn’t Do) appeared first on CSS-Tricks.

How We Created a Static Site That Generates Tartan Patterns in SVG

Css Tricks - Wed, 03/04/2020 - 5:26am

Tartan is a patterned cloth that’s typically associated with Scotland, particularly their fashionable kilts. On tartanify.com, we gathered over 5,000 tartan patterns (as SVG and PNG files), taking care to filter out any that have explicit usage restrictions.

The idea was cooked up by Sylvain Guizard during our summer holidays in Scotland. At the very beginning, we were thinking of building the pattern library manually in some graphics software, like Adobe Illustrator or Sketch. But that was before we discovered that the number of tartan patterns comes in thousands. We felt overwhelmed and gave up… until I found out that tartans have a specific anatomy and are referenced by simple strings composed of the numbers of threads and color codes.

Tartan anatomy and SVG

Tartan is made with alternating bands of colored threads woven at right angles that are parallel to each other. The vertical and horizontal bands follow the same pattern of colors and widths. The rectangular areas where the horizontal and vertical bands cross give the appearance of new colors by blending the original ones. Moreover, tartans are woven with a specific technique called twill, which results in visible diagonal lines. I tried to recreate the technique with SVG rectangles as threads here:

CodePen Embed Fallback

Let’s analyze the following SVG structure:

<svg viewBox="0 0 280 280" width="280" height="280" x="0" y="0" xmlns="http://www.w3.org/2000/svg"> <defs> <mask id="grating" x="0" y="0" width="1" height="1"> <rect x="0" y="0" width="100%" height="100%" fill="url(#diagonalStripes)"/> </mask> </defs> <g id="horizontalStripes"> <rect fill="#FF8A00" height="40" width="100%" x="0" y="0"/> <rect fill="#E52E71" height="10" width="100%" x="0" y="40"/> <rect fill="#FFFFFF" height="10" width="100%" x="0" y="50"/> <rect fill="#E52E71" height="70" width="100%" x="0" y="60"/> <rect fill="#100E17" height="20" width="100%" x="0" y="130"/> <rect fill="#E52E71" height="70" width="100%" x="0" y="150"/> <rect fill="#FFFFFF" height="10" width="100%" x="0" y="220"/> <rect fill="#E52E71" height="10" width="100%" x="0" y="230"/> <rect fill="#FF8A00" height="40" width="100%" x="0" y="240"/> </g> <g id="verticalStripes" mask="url(#grating)"> <rect fill="#FF8A00" width="40" height="100%" x="0" y="0" /> <rect fill="#E52E71" width="10" height="100%" x="40" y="0" /> <rect fill="#FFFFFF" width="10" height="100%" x="50" y="0" /> <rect fill="#E52E71" width="70" height="100%" x="60" y="0" /> <rect fill="#100E17" width="20" height="100%" x="130" y="0" /> <rect fill="#E52E71" width="70" height="100%" x="150" y="0" /> <rect fill="#FFFFFF" width="10" height="100%" x="220" y="0" /> <rect fill="#E52E71" width="10" height="100%" x="230" y="0" /> <rect fill="#FF8A00" width="40" height="100%" x="240" y="0" /> </g> </svg>

The horizontalStripes group creates a 280x280 square with horizontal stripes. The verticalStripes group creates the same square, but rotated by 90 degrees. Both squares start at (0,0) coordinates. That means the horizontalStripes are completely covered by the verticalStripes; that is, unless we apply a mask on the upper one.

<defs> <mask id="grating" x="0" y="0" width="1" height="1"> <rect x="0" y="0" width="100%" height="100%" fill="url(#diagonalStripes)"/> </mask> </defs>

The mask SVG element defines an alpha mask. By default, the coordinate system used for its x, y, width, and height attributes is the objectBoundingBox. Setting width and height to 1 (or 100%) means that the mask covers the verticalStripes resulting in just the white parts within the mask being full visible.

Can we fill our mask with a pattern? Yes, we can! Let’s reflect the tartan weaving technique using a pattern tile, like this:

In the pattern definition we change the patternUnits from the default  objectBoundingBox to userSpaceOnUse so that now, width and height are defined in pixels.

<svg width="0" height="0"> <defs> <pattern id="diagonalStripes" x="0" y="0" patternUnits="userSpaceOnUse" width="8" height="8"> <polygon points="0,4 0,8 8,0 4,0" fill="white"/> <polygon points="4,8 8,8 8,4" fill="white"/> </pattern> </defs> </svg> CodePen Embed Fallback Using React for tartan weaving

We just saw how we can create a manual “weave” with SVG. Now let’s automatize this process with React. 

The SvgDefs component is straightforward — it returns the defs markup.

const SvgDefs = () => { return ( <defs> <pattern id="diagonalStripes" x="0" y="0" width="8" height="8" patternUnits="userSpaceOnUse" > <polygon points="0,4 0,8 8,0 4,0" fill="#ffffff" /> <polygon points="4,8 8,8 8,4" fill="#ffffff" /> </pattern> <mask id="grating" x="0" y="0" width="1" height="1"> <rect x="0" y="0" width="100%" height="100%" fill="url(#diagonalStripes)" /> </mask> </defs> ) }

We will represent a tartan as an array of stripes. Each stripe is an object with two properties: fill (a hex color) and size (a number).

const tartan = [ { fill: "#FF8A00", size: 40 }, { fill: "#E52E71", size: 10 }, { fill: "#FFFFFF", size: 10 }, { fill: "#E52E71", size: 70 }, { fill: "#100E17", size: 20 }, { fill: "#E52E71", size: 70 }, { fill: "#FFFFFF", size: 10 }, { fill: "#E52E71", size: 10 }, { fill: "#FF8A00", size: 40 }, ]

Tartans data is often available as a pair of strings: Palette and Threadcount that could look like this:

// Palette O#FF8A00 P#E52E71 W#FFFFFF K#100E17 // Threadcount O/40 P10 W10 P70 K/10.

I won’t cover how to convert this string representation into the stripes array but, if you are interested, you can find my method in this Gist.

The SvgTile component takes the tartan array as props and returns an SVG structure.

const SvgTile = ({ tartan }) => { // We need to calculate the starting position of each stripe and the total size of the tile const cumulativeSizes = tartan .map(el => el.size) .reduce(function(r, a) { if (r.length > 0) a += r[r.length - 1] r.push(a) return r }, []) // The tile size const size = cumulativeSizes[cumulativeSizes.length - 1] return ( <svg viewBox={`0 0 ${size} ${size}`} width={size} height={size} x="0" y="0" xmlns="http://www.w3.org/2000/svg" > <SvgDefs /> <g id="horizontalStripes"> {tartan.map((el, index) => { return ( <rect fill={el.fill} width="100%" height={el.size} x="0" y={cumulativeSizes[index - 1] || 0} /> ) })} </g> <g id="verticalStripes" mask="url(#grating)"> {tartan.map((el, index) => { return ( <rect fill={el.fill} width={el.size} height="100%" x={cumulativeSizes[index - 1] || 0} y="0" /> ) })} </g> </svg> ) } CodePen Embed Fallback Using a tartan SVG tile as a background image

On tartanify.com, each individual tartan is used as a background image on a full-screen element. This requires some extra manipulation since we don’t have our tartan pattern tile as an SVG image. We're also unable to use an inline SVG directly in the background-image property.

Fortunately, encoding the SVG as a background image does work:

.bg-element { background-image: url('data:image/svg+xml;charset=utf-8,<svg>...</svg>'); }

Let’s now create an SvgBg component. It takes the tartan array as props and returns a full-screen div with the tartan pattern as background.

We need to convert the SvgTile React object into a string. The ReactDOMServer object allows us to render components to static markup. Its method renderToStaticMarkup is available both in the browser and on the Node server. The latter is important since later we will server render the tartan pages with Gatsby.

const tartanStr = ReactDOMServer.renderToStaticMarkup(<SvgTile tartan={tartan} />)

Our SVG string contains hex color codes starting with the # symbol. At the same time, # starts a fragment identifier in a URL. It means our code will break unless we escape all of those instances. That’s where the built-in JavaScript encodeURIComponent function comes in handy.

const SvgBg = ({ tartan }) => { const tartanStr = ReactDOMServer.renderToStaticMarkup(<SvgTile tartan={tartan} />) const tartanData = encodeURIComponent(tartanStr) return ( <div style={{ width: "100%", height: "100vh", backgroundImage: `url("data:image/svg+xml;utf8,${tartanData}")`, }} /> ) } CodePen Embed Fallback Making an SVG tartan tile downloadable

Let’s now download our SVG image.

The SvgDownloadLink component takes svgData (the already encoded SVG string) and fileName as props and creates an anchor (<a>) element. The download attribute prompts the user to save the linked URL instead of navigating to it. When used with a value, it suggests the name of the destination file.

const SvgDownloadLink = ({ svgData, fileName = "file" }) => { return ( <a download={`${fileName}.svg`} href={`data:image/svg+xml;utf8,${svgData}`} > Download as SVG </a> ) } CodePen Embed Fallback Converting an SVG tartan tile to a high-res PNG image file

What about users that prefer the PNG image format over SVG? Can we provide them with high resolution PNGs?

The PngDownloadLink component, just like SvgDownloadLink, creates an anchor tag and has the tartanData and fileName as props. In this case however, we also need to provide the tartan tile size since we need to set the canvas dimensions.

const Tile = SvgTile({tartan}) // Tartan tiles are always square const tartanSize = Tile.props.width

In the browser, once the component is ready, we draw the SVG tile on a <canvas> element. We’ll use the canvas toDataUrl() method that returns the image as a data URI. Finally, we set the date URI as the href attribute of our anchor tag.

Notice that we use double dimensions for the canvas and double scale the ctx. This way, we will output a PNG that’s double the size, which is great for high-resolution usage.

const PngDownloadLink = ({ svgData, width, height, fileName = "file" }) => { const aEl = React.createRef() React.useEffect(() => { const canvas = document.createElement("canvas") canvas.width = 2 * width canvas.height = 2 * height const ctx = canvas.getContext("2d") ctx.scale(2, 2) let img = new Image() img.src = `data:image/svg+xml, ${svgData}` img.onload = () => { ctx.drawImage(img, 0, 0) const href = canvas.toDataURL("image/png") aEl.current.setAttribute("href", href) } }, []) return ( <a ref={aEl} download={`${fileName}.png`} > Download as PNG </a> ) } CodePen Embed Fallback

For that demo, I could have skipped React's useEffect hook and the code would worked fine. Nevertheless, our code is executed both on the server and in the browser, thanks to Gatsby. Before we start creating the canvas, we need to be sure that we are in a browser. We should also make sure the anchor element is ”ready” before we modify its attribute. 

Making a static website out of CSV with Gatsby

If you haven’t already heard of Gatsby, it’s a free and open source framework that allows you to pull data from almost anywhere and generate static websites that are powered by React.

Tartanify.com is a Gatsby website coded by myself and designed by Sylvain. At the beginning of the project, all we had was a huge CSV file (seriously, 5,495 rows), a method to convert the palette and threadcount strings into the tartan SVG structure, and an objective to give Gatsby a try.

In order to use a CSV file as the data source, we need two Gatsby plugins: gatsby-transformer-csv and gatsby-source-filesystem. Under the hood, the source plugin reads the files in the /src/data folder (which is where we put the tartans.csv file), then the transformer plugin parses the CSV file into JSON arrays.

// gatsby-config.js module.exports = { /* ... */ plugins: [ 'gatsby-transformer-csv', { resolve: 'gatsby-source-filesystem', options: { path: `${__dirname}/src/data`, name: 'data', }, }, ], }

Now, let’s see what happens in the gatsby-node.js file. The file is run during the site-building process. That’s where we can use two Gatsby Node APIs: createPages and onCreateNode. onCreateNode is called when a new node is created. We will add two additional fields to a tartan node: its unique slug and a unique name. It is necessary since the CSV file contains a number of tartan variants that are stored under the same name.

// gatsby-node.js // We add slugs here and use this array to check if a slug is already in use let slugs = [] // Then, if needed, we append a number let i = 1 exports.onCreateNode = ({ node, actions }) => { if (node.internal.type === 'TartansCsv') { // This transforms any string into slug let slug = slugify(node.Name) let uniqueName = node.Name // If the slug is already in use, we will attach a number to it and the uniqueName if (slugs.indexOf(slug) !== -1) { slug += `-${i}` uniqueName += ` ${i}` i++ } else { i = 1 } slugs.push(slug) // Adding fields to the node happen here actions.createNodeField({ name: 'slug', node, value: slug, }) actions.createNodeField({ name: 'Unique_Name', node, value: uniqueName, }) } }

Next, we create pages for each individual tartan. We want to have access to its siblings so that we can navigate easily. We will query the previous and next edges and add the result to the tartan page context.

// gatsby-node.js exports.createPages = async ({ graphql, actions }) => { const { createPage } = actions const allTartans = await graphql(` query { allTartansCsv { edges { node { id fields { slug } } previous { fields { slug Unique_Name } } next { fields { slug Unique_Name } } } } } `) if (allTartans.errors) { throw allTartans.errors } allTartans.data.allTartansCsv.edges.forEach( ({ node, next, previous }) => { createPage({ path: `/tartan/${node.fields.slug}`, component: path.resolve(`./src/templates/tartan.js`), context: { id: node.id, previous, next, }, }) } ) }

We decided to index tartans by letters and create paginated letter pages. These pages list tartans with links to their individual pages. We display a maximum of 60 tartans per page, and the number of pages per letter varies. For example, the letter “a” will have have four pages: tartans/a, tartans/a/2, tartans/a/3 and tartans/a/4. The highest number of pages (15) belongs to “m” due to a high number of traditional names starting with “Mac.”

The tartans/a/4 page should point to tartans/b as its next page and tartans/b should point to tartans/a/4 as its previous page.

We will run a for of loop through the letters array ["a", "b", ... , "z"] and query all tartans that start with a given letter. This can be done with filter and regex operator:

allTartansCsv(filter: { Name: { regex: "/^${letter}/i" } })

The previousLetterLastIndex variable will be updated at the end of each loop and store the number of pages per letter. The /tartans/b page need to know the number of a pages (4) since its previous link should be tartans/a/4.

// gatsby-node.js const letters = "abcdefghijklmnopqrstuvwxyz".split("") exports.createPages = async ({ graphql, actions }) => { const { createPage } = actions // etc. let previousLetterLastIndex = 1 for (const letter of letters) { const allTartansByLetter = await graphql(` query { allTartansCsv(filter: {Name: {regex: "/^${letter}/i"}}) { nodes { Palette fields { slug Unique_Name } } totalCount } } `) if (allTartansByLetter.errors) { throw allTartansByLetter.errors } const nodes = allTartansByLetter.data.allTartansCsv.nodes const totalCountByLetter = allTartansByLetter.data.allTartansCsv.totalCount const paginatedNodes = paginateNodes(nodes, pageLength) paginatedNodes.forEach((group, index, groups) => { createPage({ path: index > 0 ? `/tartans/${letter}/${index + 1}` : `/tartans/${letter}`, component: path.resolve(`./src/templates/tartans.js`), context: { group, index, last: index === groups.length - 1, pageCount: groups.length, letter, previousLetterLastIndex, }, }) }) previousLetterLastIndex = Math.ceil(totalCountByLetter / pageLength) } }

The paginateNode function returns an array where initial elements are grouped by pageLength. 

const paginateNodes = (array, pageLength) => { const result = Array() for (let i = 0; i < Math.ceil(array.length / pageLength); i++) { result.push(array.slice(i * pageLength, (i + 1) * pageLength)) } return result }

Now let’s look into the tartan template. Since Gatsby is a React application, we can use the components that we were building in the first part of this article.

// ./src/templates/tartan.js import React from "react" import { graphql } from "gatsby" import Layout from "../components/layout" import SvgTile from "../components/svgtile" import SvgBg from "../components/svgbg" import svgAsString from "../components/svgasstring" import SvgDownloadLink from "../components/svgdownloadlink" import PngDownloadLink from "../components/pngdownloadlink" export const query = graphql` query($id: String!) { tartansCsv(id: { eq: $id }) { Palette Threadcount Origin_URL fields { slug Unique_Name } } } ` const TartanTemplate = props => { const { fields, Palette, Threadcount } = props.data.tartansCsv const {slug} = fields const svg = SvgTile({ palette: Palette, threadcount: Threadcount, }) const svgData = svgAsString(svg) const svgSize = svg.props.width return ( <Layout> <SvgBg svg={svg} /> {/* title and navigation component comes here */} <div className="downloads"> <SvgDownloadLink svgData={svgData} fileName={slug} /> <PngDownloadLink svgData={svgData} size={svgSize} fileName={slug} /> </div> </Layout> ) } export default TartanTemplate

Finally let’s focus on the tartans index pages (the letter pages).

// ./src/templates/tartans.js import React from "react" import Layout from "../components/layout" import {Link} from "gatsby" import TartansNavigation from "../components/tartansnavigation" const TartansTemplate = ({ pageContext }) => { const { group, index, last, pageCount, letter, previousLetterLastIndex, } = pageContext return ( <Layout> <header> <h1>{letter}</h1> </header> <ul> {group.map(node => { return ( <li key={node.fields.slug}> <Link to={`/tartan/${node.fields.slug}`}> <span>{node.fields.Unique_Name}</span> </Link> </li> ) })} </ul> <TartansNavigation letter={letter} index={index} last={last} previousLetterLastIndex={previousLetterLastIndex} /> </Layout> ) } export default TartansTemplate

The TartansNavigation component adds next-previous navigation between the index pages.

// ./src/components/tartansnavigation.js import React from "react" import {Link} from "gatsby" const letters = "abcdefghijklmnopqrstuvwxyz".split("") const TartansNavigation = ({ className, letter, index, last, previousLetterLastIndex, }) => { const first = index === 0 const letterIndex = letters.indexOf(letter) const previousLetter = letterIndex > 0 ? letters[letterIndex - 1] : "" const nextLetter = letterIndex < letters.length - 1 ? letters[letterIndex + 1] : "" let previousUrl = null, nextUrl = null // Check if previousUrl exists and create it if (index === 0 && previousLetter) { // First page of each new letter except "a" // If the previous letter had more than one page we need to attach the number const linkFragment = previousLetterLastIndex === 1 ? "" : `/${previousLetterLastIndex}` previousUrl = `/tartans/${previousLetter}${linkFragment}` } else if (index === 1) { // The second page for a letter previousUrl = `/tartans/${letter}` } else if (index > 1) { // Third and beyond previousUrl = `/tartans/${letter}/${index}` } // Check if `nextUrl` exists and create it if (last && nextLetter) { // Last page of any letter except "z" nextUrl = `/tartans/${nextLetter}` } else if (!last) { nextUrl = `/tartans/${letter}/${(index + 2).toString()}` } return ( <nav> {previousUrl && ( <Link to={previousUrl} aria-label="Go to Previous Page" /> )} {nextUrl && ( <Link to={nextUrl} aria-label="Go to Next Page" /> )} </nav> ) } export default TartansNavigation Final thoughts

Let’s stop here. I tried to cover all of the key aspects of this project. You can find all the tartanify.com code on GitHub. The structure of this article reflects my personal journey — understanding the specificity of tartans, translating them into SVG, automating the process, generating image versions, and discovering Gatsby to build a user-friendly website. It was maybe not as fun as our Scottish journey itself &#x1f609;, but I truly enjoyed it. Once again, a side project proved to be the best way to dig into new technology.

The post How We Created a Static Site That Generates Tartan Patterns in SVG appeared first on CSS-Tricks.

How I think about solving problems

Css Tricks - Wed, 03/04/2020 - 5:26am

Nicholas C. Zakas:

Eventually, I settled on a list of questions I would ask myself for each problem as it arose. I found that asking these questions, in order, helped me make the best decision possible:

1) Is this really a problem?
2) Does the problem need to be solved?
3) Does the problem need to be solved now?
4) Does the problem need to be solved by me?
5) Is there a simpler problem I can solve instead?

We've talked about what it takes to be a senior developer before, and I'd say this kind of thinking should be on that list as well.

Direct Link to ArticlePermalink

The post How I think about solving problems appeared first on CSS-Tricks.

4 Ways to Animate the Color of a Text Link on Hover

Css Tricks - Tue, 03/03/2020 - 12:20pm

Let’s create a pure CSS effect that changes the color of a text link on hover… but slide that new color in instead of simply swapping colors.

There are four different techniques we can use to do this. Let’s look at those while being mindful of important things, like accessibility, performance, and browser support in mind.

Let’s get started!

Technique 1: Using background-clip: text

At the time of writing, the background-clip: text property is an experimental feature and is not supported in Internet Explorer 11 and below.

This technique involves creating knockout text with a hard stop gradient. The markup consists of a single HTML link (<a>) element to create a hyperlink:

<a href="#">Link Hover</a>

We can start adding styles to the hyperlink. Using overflow: hidden will clip any content outside of the hyperlink during the hover transition:

a { position: relative; display: inline-block; font-size: 2em; font-weight: 800; color: royalblue; overflow: hidden; }

We will need to use a linear gradient with a hard stop at 50% to the starting color we want the link to be as well as the color that it will change to:

a { /* Same as before */ background: linear-gradient(to right, midnightblue, midnightblue 50%, royalblue 50%); }

Let’s use background-clip to clip the gradient and the text value to display the text. We will also use the background-size and background-position properties to have the starting color appear:

a { /* Same as before */ background-clip: text; -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-size: 200% 100%; background-position: 100%; }

Finally, let’s add the transition CSS property and :hover CSS pseudo-class to the hyperlink. To have the link fill from left to right on hover, use the background-position property:

a { /* Same as before */ transition: background-position 275ms ease; } a:hover { background-position: 0 100%; } CodePen Embed Fallback

While this technique does achieve the hover effect, Safari and Chrome will clip text decorations and shadows, meaning they won’t be displayed. Applying text styles, such as an underline, with the text-decoration CSS property will not work. Perhaps consider using other approaches when creating underlines.

Technique 2: Using width/height

This works by using a data attribute containing the same text as the one in the <a> tag and setting the width (filling the text from left-to-right or right-to-left) or height (filling the text from top-to-bottom or bottom-to-top), from 0% to 100% on hover.

Here is the markup:

<a href="#" data-content="Link Hover">Link Hover</a>

The CSS is similar to the previous technique minus the background CSS properties. The text-decoration property will work here:

a { position: relative; display: inline-block; font-size: 2em; color: royalblue; font-weight: 800; text-decoration: underline; overflow: hidden; }

This is when we need to use the content from the data-content attribute. It will be positioned above the content in the <a> tag. We get to use the nice little trick of copying the text in the data attribute and displaying it via the attr() function on the content property of the element’s ::before pseudo-element.

a::before { position: absolute; content: attr(data-content); /* Prints the value of the attribute */ top: 0; left: 0; color: midnightblue; text-decoration: underline; overflow: hidden; transition: width 275ms ease; }

To keep the text from wrapping to the next line, white-space: nowrap will be applied. To change the link fill color, set the value for the color CSS property using the ::before pseudo-element and having the width start at 0:

a::before { /* Same as before */ width: 0; white-space: nowrap; }

Increase the width to 100% to the ::before pseudo element to complete the text effect on hover:

a:hover::before { width: 100%; } CodePen Embed Fallback

While this technique does the trick, using the width or height properties will not produce a performant CSS transition. It is best to use either the transform or opacity properties to achieve a smooth, 60fps transition.

Using the text-decoration CSS property can allow for different underline styles to appear in the CSS transition. I created a demo showcasing this using the next technique: the clip-path CSS property.

CodePen Embed Fallback Technique 3: Using clip-path

For this technique, we will be using the clip-path CSS property with a polygon shape. The polygon will have four vertices, with two of them expanding to the right on hover:

The markup is the same as the previous technique. We will use a ::before pseudo-element again, but the CSS is different:

a::before { position: absolute; content: attr(data-content); color: midnightblue; text-decoration: underline; clip-path: polygon(0 0, 0 0, 0% 100%, 0 100%); transition: clip-path 275ms ease; }

Unlike the previous techniques, text-decoration: underline must be declared to the ::before pseudo-element for the color to fill the underline on hover.

Now let’s look into the CSS for the clip-path technique:

clip-path: polygon(0 0, 0 0, 0% 100%, 0 100%);

The polygon’s vertices of the clip-path property are set in percentages to define coordinates by the order written:

  • 0 0 = top left
  • 0 0 = top right
  • 100% 0 = bottom right
  • 0 100% = bottom left

The direction of the fill effect can be changed by modifying the coordinates. Now that we have an idea for the coordinates, we can make the polygon expand to the right on hover:

a:hover::before { clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); } CodePen Embed Fallback

This technique works pretty well, but note that support for the clip-path property varies between browsers. Creating a CSS transition with clip-path is a better alternative than using the width/height technique; however, it does affect the browser paint.

Technique 4: Using transform

The markup for this technique uses a masking method with a <span> element. Since we will be using duplicated content in a separate element, we will use aria-hidden="true" to improve accessibility — that will hide it from screen readers so the content isn’t read twice:

<a href="#"><span data-content="Link Hover" aria-hidden="true"></span>Link Hover</a>

The CSS for the <span> element contains a transition that will be starting from the left:

span { position: absolute; top: 0; left: 0; overflow: hidden; transform: translateX(-100%); transition: transform 275ms ease; }

Next, we need to get the <span> to slide the right like this:

To do this, we will use the translateX() CSS function and set it to 0:

a:hover span {   transform: translateX(0); }

Then, we will use the ::before pseudo-element for the <span>, again using the data-content attribute we did before. We’ll set the position by translating it 100% along the x-axis.

span::before {  display: inline-block;   content: attr(data-content);   color: midnightblue;   transform: translateX(100%);   transition: transform 275ms ease;   text-decoration: underline; }

Much like the <span> element, the position of the ::before pseudo-element will also be set to  translateX(0):

a:hover span::before {   transform: translateX(0); } CodePen Embed Fallback

While this technique is the the most cross-browser compatible of the bunch, it requires more markup and CSS to get there. That said, using the transform CSS property is great for performance as it does not trigger repaints and thus produces smooth, 60fps CSS transitions.

There we have it!

We just looked at four different techniques to achieve the same effect. Although each has its pros and cons, you can see that it’s totally possible to slide in a color change on text. It’s a neat little effect that makes links feel a little more interactive.

The post 4 Ways to Animate the Color of a Text Link on Hover appeared first on CSS-Tricks.

Just Dropping Some Type Links

Css Tricks - Tue, 03/03/2020 - 12:20pm

I've had a bunch of tabs open that just so happen to all be related to typography, so I figured I'd give myself the mental release of closing them by blogging them. How's that for a blog post format for ya: whatever random tabs you've had open for far too long.

  • Times New Roman is popular on the web in the sense that it's a default font installed on most computers and safe to use without having to load any web fonts. But it's also the default font that (all?) web browsers use if you don't declare a font-family at all so, in that sense, it sometimes feels like a site is broken on accident when Times is used. Typewolf has a nice list of alternatives if you like the vibe but need something different.
  • Speaking of Times, err, The New York Times profiles TypeThursday with a pretty funny correction where they got the typeface name wrong.
  • In the last month of 2019, Tyopgraphica published their favorite typefaces of 2018. Fern grabs me.
  • Una Kravets has a "designing in the browser" video about typography on the Chrome Developers channel on YouTube. About 11 minutes in, she gets into variable fonts which are just incredible. I know they are capable of all sorts of amazing things (even animation), but I remain the most excited about performance: loading one font and having tons of design control.
  • Florens Verschelde's "A short history of body copy sizes on the Web" gets into how typical font size usage has crept up and up over the years.
  • Alina Sava makes the argument that font licensing is ill. Primarily: pricing web fonts based on page views. As someone who works on some high-traffic / fairly-low-profit websites, it's hard to disagree.
  • Matej Latin covers five fonts designed for coding that have ligatures. Ya know, instead of != you get ?, but only visually rather than actually changing the characters that are used as I did there. Ligatures are a neat trick to use (another trick: ligatures as icons), but Matthew Butterick says "hell no": The ligature introduces an ambiguity that wasn’t there before. I find it notable that Operator Mono chose not to go there. I feel like I overheard a discussion about it once but can't dig it up now. I know there is a way to add them, and it's a little surprising to me that's legal.
  • Trent popped some new fonts on his blog and shared his font shopping list.
  • You might have noticed some new fonts around here on CSS-Tricks as well, as of a few weeks ago. I just wanted to freshen up the place as I was getting sick of looking at system fonts (they started looking bad to me on Catalina, which is something Andy Baio pointed out is a Chrome Bug, but still). The CSS-Tricks logo has long been Gotham Rounded, so I went back to Hoefler&Co. for the font choices here to kinda stay in the family. The headers use Ringside, monospace content uses Operator Mono, and the body uses Sentinel.

The post Just Dropping Some Type Links appeared first on CSS-Tricks.

Vue.js: The Documentary

Css Tricks - Tue, 03/03/2020 - 7:33am

Hey how cool! A documentary about Vue! Good timing as it looks like VueConf is happening right now. (Reminder we have a site for conferences to tell you stuff like that).

Sarah appears in it (about 21:13) and talks about CSS-Tricks for a second, so we're officially super famous now and I have already booked sailing lessons. The series Sarah mentioned is here.

I'll embed it in the body of the post here.

As it's on the Honeypot YouTube channel, so I imagine it's Honeypot that pays for and produces these things. They did the same for GraphQL and I enjoyed that one as well. I hope they keep doing them. I'd love to see them for other tech projects that went massive like jQuery, Bootstrap, and npm.

The post Vue.js: The Documentary appeared first on CSS-Tricks.

What I Like About Craft CMS

Css Tricks - Tue, 03/03/2020 - 5:18am

Looking at the CMS scene today, there are upwards of 150 options to choose from — and that’s not including whatever home-grown custom alternatives people might be running. The term “Content Management System” is broad and most site builders fit into the CMS model. Craft CMS, a relatively new choice in this field (launched in 2013) stands out to me.

My team and I have been using Craft CMS for the past two years to develop and maintain a couple of websites. I would like to share my experience using this system with you.

Note that this review is focused on our experience with using Craft and as such, no attempt has been made to compare it to other available options. For us, using Craft has been a very positive experience and we leave it up to you, the reader, to compare it to other experiences that you may have had.

First, a quick introduction to Craft

Craft is the creation of Pixel & Tonic, a small software development company based out of Oregon. Founded by Brandon Kelly, known for premium ExpressionEngine add-ons. While developing some of the most used add-ons, Pixel & Tonic set out to build their own CMS, known as "Blocks." This was all the way in 2010, during its development the name was changed to Craft CMS.

Looking at the market we can see that Craft is well adopted. At the time of writing this article, there are around ~70 000 websites using Craft.

Showing market growth over the five year period.

Craft was set out to make life enjoyable for developers and content managers. In 2015, Craft proved this by winning the Best CMS for Developers award by CMSCritics. Over the years, Craft has won multiple awards that prove that Craft is on the right path.

When I am asked where Craft fits in the overall CMS landscape, I say it's geared toward small-to-medium-sized businesses where there is a staff of content managers that don't require a completely custom solution.

At the heart of things, Craft is a CMS in the same vein as WordPress and other traditional offerings — just with a different flavor and approach to content management that makes it stand out from others, which is what we're covering next.

Craft's requirements

Server requirements for a Craft setup are simple and standard. Craft requires the following:

  • PHP 7.0+
  • MySQL 5.5+ with InnoDB, MariaDB 5.5+, or PostgreSQL 9.5+
  • At least 256MB of memory allocated to PHP
  • At least 200MB of free disk space

Out of the box, you can get Craft up and running fast. You don’t need an extensive PHP or Database background to get started. Hell, you can get away with little-to-no PHP knowledge at all. That makes both the barrier to entry and the time from installation to development extremely small.

It’s both simple and complex at the same time

Craft is unique in that it is both a simple and a complex CMS.

You can use Craft to design and develop complex sites that and are built with and rely heavily on PHP, databases, and query optimizations. 

However, you can also use Craft to design and develop simple sites where you do none of those things.

This was one of the main selling points for me. It’s simple to get up and going with very little, but if you need to do something more complex, you can. And it never feels like you are “hacking” it do anything it wasn’t meant to.

Craft abstracted all the field creation and setup to the admin panel. You only need to point it to the right Twig and then use the fields you connected. Furthermore, it provides localization and multi-site management out of the box with no need for plugins. This is essentially what makes it different from other content management systems. You can create the structure, fields and all the forms without ever touching any code.

Some CMSs like to make a lot of decisions for you and sometimes that leads to unnecessary bloat. Front- and back-end performance is super important to me and, as such, I appreciate that Craft doesn’t leave a lot of that up to me, should I need it. It provides a full customization experience that supports beginners right out of the box, but doesn’t constrain folks at the professional level.

Craft’s templating engine

Some developers are not keen on this, but Craft uses Twig as its template engine. The word “use” should be emphasized as a requirement, as there is no option of writing raw PHP anywhere inside the template. Here are my thoughts on that:

  1. It is standardized in a way that, when I look at my team's Pull Requests, I don’t expect to see 100 lines of custom PHP that make no sense. I only see the code related to templating.
  2. Twig is already powerful enough that it will cover nearly all use cases while being extensible for anything else.

Let’s say you’re not digging Twig or you would rather use one of the latest technologies (hello static site generators!). Craft’s templating system isn’t the only way to get content out of Craft. As of Craft 3.3, it provides a “headless” mode and GraphQL built-in with Craft's Pro features. That means that you can use tools like Gatsby or Gridsome to build static sites with the comfort of Craft CMS. That brings Craft in line with the like of WordPress that provides its own REST API for fetching content to use somewhere else.

There's a fully functional GraphQL editor built right inside the Craft UI.

Speaking of REST, there is even an option for that in Craft if, say, you are not a fan of GraphQL. The Element API is a REST read-only API that is available via the first-party Element API plugin. Again, Craft comes with exactly what you need at a minimum and can be extended to do more.

Craft’s extensibility

This brings me to my next point: Craft CMS is super extensible. It is built on the Yii Framework, a well-known PHP framework that is robust and fast. This is important, as all the extensibility is either through modules or plugins written in Yii and Craft API. Modules are a concept passed down from Yii modules and they provide a way to extend core functionality without changing the source. On the other hand, plugins are a Craft concept and they do the same thing as modules, but can be installed, disabled and removed. If you would like to read more about this, you can find it in Craft’s docs.

Both modules and plugins have full access to Craft and Yii’s API. This is a huge bonus, as you can benefit from Yii’s community and documentation. Once you get used to Yii, writing plugins is easy and enjoyable. My team has built multiple custom plugins and modules over the last two years, like a Pardot form integration, a Google reCAPTCHA integration, custom search behavior, and others.  Essentially, the sky is the limit. 

Writing plugins and modules is covered in the docs but I think this is where Craft's system has room to grow. I would recommend opening a well-known plugin on GitHub to get a sense of how it’s done because I’ve found that to be much more helpful than the docs.

Initially, you may find this aspect of the system difficult, but once you understand the structure, it does get easier, because the code structure essentially consists of models, views, and controllers. It is like building a small MVC app inside your CMS. Here is an example of a plugin structure I’ve worked with:

. ??? assetbundles ??? controllers ??? migrations ??? models ??? records ??? services ??? templates ? ??? _layouts ? ??? _macros ??? translations ? ??? en ??? variables ??? icon-mask.svg ??? icon.svg ??? Plugin.php

If you don’t feel like writing PHP and tinkering with Yii/Craft, you can always download plugins from the official Craft plugin store. There is a variety of plugins, from image to building on top of the WYSIWYG editor. One of many things that Craft got right is that you can try paid plugins in development mode as much as you like rather than having to first make a purchase.

The Craft plugins screen.

During the course of two years, we have tried multiple plugins, there are a few that I not only recommend, but have found myself using for almost every project.

  • ImageOptimize - This is a must for performance enthusiasts as it provides a way to automatically transform uploaded images to responsive images with compression and convert to more modern formats.
  • Navigation - Craft doesn’t come with navigation management built right in, even though you technically can do it with custom fields. But Verbb did an awesome job with this simple plugin and for us it’s one of the very first plugins we reach for on any given project.
  • Seomatic - This is what is the Yoast SEO plugin is to WordPress: an out of the box solution for all your SEO needs.
  • Redactor - This is a must and should be included in every project. Craft doesn’t come with a WYSIWYG editor out of the box but, with Redactor, you get a Redactor field that includes one.
  • Super Table - This powerful plugin gives you an option to create repeatable fields. You can use built-in Craft field types to create a structure (table) and the content manager creates rows of content. It reminds me of ACF Repeater for WordPress.
Craft’s author experience

While we’ve covered the development experience so far, the thing that Craft got extremely right — to the point of blowing other CMSs out of the water, in my view — is the author's experience. A CMS can do all kinds of wonderful things, but it has to be nice to write in at the end of the day.

Craft provides a straightforward set of options to configure the site right in the admin.

The whole concept of the CMS is that it is built with two simple things; Fields and Sections, where fields are added to sections and entries are created by content managers.

Craft's default post editor is simple and geared toward blog posts. Need more fields or sections? Those are available to configure in the site settings, making for a more open-ended tool for any type of content publishing.

One of the neatest author features is version control. "Wait, what?" you ask. Yes, all content is version controlled in a way that lets authors track changes and roll back to previous versions for any reason at all.

Craft shows revisions for each entry.

At any point in time, you can go back to any revision and use is as a current one. You don't know how much you need this feature until you've tried it. For me, it brings a sense of security that you can't lose someone's edit or changes, same a with Git and developers.

The fun doesn't stop here because Craft nailed one of the hardest things (in my opinion) about content management and that is localization. People still find this hard in 2020 and usually give up because it is both difficult to implement and properly present to authors in the UI.

You can create as many sites as you want.

Oh, and you can host multiple websites in a single Craft 3 instance. You can define one or more sites at different domains, different versions of the entry content and using a different set of templates. Like with everything in Craft, it is made so simple and open-ended (in a good way) that it is up to you what the other sites are going to be. You can create a site with the same language but different content or create a site with another language, solving the localization problem.

All the features above are already built-in inside Craft which for me is a must for a good author experience. As soon as you start patching the essential author functionality with plugins, great author experience is lost. This is because usually when you want to add functionality there are multiple plugins (ways) to do it, which aids a different author experience on the same platform but different instances.

Craft’s community

It’s worth underscoring the importance of having a community of people you can to turn to. To some degree, you’re probably reading this post because you enjoy learning from others in the development community. It’ no difference with CMSs and Craft has an awesome community of developers.

Craft's Stack Exchange is full of questions and answers, but a lot of the information needs to be updated to reflect Craft 3.

At the same time, the community is still small (compared to, say, WordPress) and doesn’t have a long track record — though there are many folks in the community who have been around quite a while having come from ExpressionEngine.  It’s not just because Craft itself is relatively new to the market. It’s also because not everyone posts on the Craft CMS Stack Exchange to the extent thatmany of the older answers haven’t even been updated for Craft 3. You’ll actually find most of the community hanging out on Discord, where even the creators of Craft, Pixel & Tonic, are active and willing to answer questions. It is also very helpful when you see Craft core members and big plugin creators, like Andrew from nystudio107 (shout out to a great performance freak), are there to assist almost 24/7.

Craft's discord has always someone to help you. Even the core team responds often.

One thing I also want to touch on is the limited learning resources available but, then again, you hardly need them. As I said earlier, the combination of Craft and Twig is simple enough that you won’t need a full course on how to build a blog.

Craft's conference, Dot All, is a great resource all its own. Chris attended last year with a great presentation, which is available to the public.

And, lastly, Craft uses and enforces open source. For me, open source is always a good thing because you expose your code to more people (developers). Craft did this right. The whole platform and also plugins are open source.

Pricing

This is the elephant in the room because there are mixed feelings about charging for content management systems. And yes, Craft has a simple pricing model

  1. It’s free for a single user account, small website.
  2. It’s $299 per project for the first year of updates. It’s only $59 each year after that, but they don't force you to pay for updates and you can enable license updates for an additional year at any time at the same price.
Craft's Solo version is totally capable of meeting the needs of many websites, but the paid Pro option is a cost-effective upgrade for advanced features and use cases.

I consider this pricing model fair and not particularly expensive — at least to the point of being prohibitive. Craft offers a free license for a small website you can build for a friend or a family member. On the other hand, Craft is more of a professional platform that is used to build mid-size business websites and as such their license cost is negligible. In most cases, developers (or agencies) will eat up the initial cost so that clients don’t have to worry about this pricing.

Oh, and kudos to Craft for providing an option to try the Pro version for free on a local domain. This also applies to all plugins.

Conclusion

To conclude, I would like to thank Craft CMS and the Pixel & Tonic team for an awesome and fun ride. Craft has satisfied almost all our needs and we will continue to use it for future projects. It’s flexible to fit each project and feel like CMS built for that use case.

It boils down Craft for me is a content management framework. Out of the box, it is nothing more than nuts and bolts that needs to be assembled to the user's needs. This is the thing that makes Craft stand out and why it provides both great author and developer experience.

As you saw in the licensing model it is free for a single user, try it out and leave your feedback in the comments.

The post What I Like About Craft CMS appeared first on CSS-Tricks.

Making Things Better: Redefining the Technical Possibilities of CSS

Css Tricks - Tue, 03/03/2020 - 5:18am

(This is a sponsored post.)

Robin recently lamented the common complaint that CSS is frustrating. There are misconceptions about what it is and what it does. There are debates about what kind of language it is. There are even different views on where it should be written.

Rachel Andrew has a new talk from An Event Apart DC 2019 available that walks us back; back to the roots of the issues we used to have with CSS and the "hacks" we used to overcome them. CSS has changed a lot over the past few years and, while those changes have felt complex and confusing at times, they are designed to solve what we have always wanted CSS to do.

The full hour it takes to watch the talk is well worth the time. Here are a few nuggets that stood out. First off, some de-bunking of common CSS complaints:

  • You never know how tall anything is on the web. Floats never solved this because they only bring things next to each other instead of knowing about the items around them. New layout methods, including CSS Grid and Flexbox, actually look at our elements and help them behave consistently.
  • Flexbox is weird and unintuitive. It's not the layout method you might think it is. If we view it as a way to line things up next to each other, it's going to feel weird and behavior weirdly as well. But if we see it for what it is - a method that looks at differently sized elements and returns the most logical layout - it starts to make sense. It assigns space, rather than squishing things into a box.

Rachel continues by giving us a peek into the future of what CSS wants to do for us:

  • CSS wants to avoid data loss. New alignment keywords like safe and unsafe will give us extra control to define whether we want CSS to aggressively avoid content that's unintentionally hidden or allow it to happen.
.container { display: flex; flex-direction: column; /* Please center as long as it doesn't result in overflow */ align-items: safe center; }
  • CSS wants to help us get real with overflow. Themin-content and max-content keywords make it possible to create boxes that are wide enough for the content but not wider, and boxes that are as big as the content can be.
.container { width: min-content; /* Allow wrapping */ }
  • CSS wants to lay things out logically. The web is not left-to-right. Grid and Flexbox quietly introduced a way of thinking start-to-end that is direction agnostic. That has brought about a new specification for Logical Properties and Values.
  • CSS wants to make content more portable. CSS Regions let us flow content from one element into another. While it's probably a flawed comparison, it's sorta like the pipes in old school Mario Bros. games where jumping in one pipe at one location will plop your character out of another pipe in another location... but we get to define those sources ourselves and without angry plants trying to eat us along the way.

Anyway, these are merely scratching the surface of what Rachel covers in her talk. It's a good reminder that An Event Apart has an entire library of valuable talks from amazing speakers and that attending an AEA event is an invaluable experience worth checking out. Rachel's talk was from last year's Washington D.C. event and, as it turns out, the very next event is taking place there this April 13-15. If you can't make that one, there are several others throughout the year across the United States.

Oh, and of course we have a discount code for you! Use AEACP for $100 off any show.

Direct Link to ArticlePermalink

The post Making Things Better: Redefining the Technical Possibilities of CSS appeared first on CSS-Tricks.

Considerations for Creating a Card Component

Css Tricks - Mon, 03/02/2020 - 10:04am

Here's a Card component in React:

const Card = props => { return( <div className="card"> <h2>{props.title}</h2> <p>{props.content}</p> </div> ) }

It might be pretty useful! If you end up using this thing hundreds of times, now you have the ability to refactor a little bit of HTML across your app very easily. You already have that power in CSS because of the class name there, but now you have HTML control too. Feel it.

But wait. Maybe this is limiting... an <h2>? What if that really should have been an <h4> in some usages? What's the approach there? Maybe an API of sorts?

const Card = props => { return( <div className="card"> {props.type === "big" && <h2>{props.title}</h2>} {props.type !== "big" && <h4>{props.title}</h4>} <p>{props.content}</p> </div> ) }

Or maybe we force a level to be passed in?

const Card = props => { const HeaderTag = `h${props.level}`; return( <div className="card"> <HeaderTag>{props.title}</HeaderTag> <p>{props.content}</p> </div> ) }

Or maybe that header is its own component?

And a forced paragraph tag wrapper around that content? That's a little limiting, isn't it? Maybe that should be a <div> so that it could take arbitrary HTML inside it, like multiple paragraphs.

const Card = props => { return( <div className="card"> <WhateverHeader>{props.title}</WhateverHeader> <div>{props.content}</div> </div> ) }

Actually, why even ask for content with props? It's probably easier to deal with a child component, especially if what is coming over is HTML.

const Card = props => { return( <div className="card"> <WhateverHeader>{props.title}</WhateverHeader> {children} </div> ) }

There are more assumptions we could challenge too. Like card only for a class name... shouldn't that be more flexible?

const Card = props => { const classes = `card ${props.className}`; return( <div className={classes}> <WhateverHeader>{props.title}</WhateverHeader> {children} </div> ) }

I'm still forcing card there. We could drop that so that it isn't assumed, or build another aspect of the Card API providing a way to opt-out of it.

Even the <div> wrapper is presumptuous. Perhaps that tag name could be passed in so that you could make it into a <section> or <article> or whatever you want.

Maybe it's better to assume nothing actually, making our card like this:

const Card = () => { return( <> {children} </> ) }

That way anything you want to change, you have the freedom to change. At least then it's flexibility while being relaxed about it, rather than this kind of "flexibility":

<Card parentTag="article" headerLevel="3" headerTitle="My Card" contentWrapper="div" cardVariation="extra-large" contentContent="" this="" little="" piggy="" went="" to="" market="" />

That kind of extreme-API-zying just happens sometimes when you're grasping for control and flexibility at the same time.

A component model with no guidance can lead to over-componentization also, like perhaps:

const Card = props => { return( <CardWrapperTheme> <CardWrapper> <CardTitle /> <CardContent /> <CardFooter /> </CardWrapper> </CardWrapperTheme> ) }

There might be perfectly good reasons to do that, or it might be the result of componentizing because it's "free" and just feels like that's how things are done in an architecture that supports it.

There is a balance. If a component is too strict, it runs the risk of that people won't use them because they don't give them what they need. And if they're too loose, people might not use them because they don't provide any value, and, even if they did use them, they don't offer any cohesiveness.

I don't have any answers here, I just find it fascinating.

The post Considerations for Creating a Card Component appeared first on CSS-Tricks.

Unfortunately, clip-path: path() is Still a No-Go

Css Tricks - Mon, 03/02/2020 - 4:54am

I was extremely excited when I first heard that clip-path: path() was coming to Firefox. Just imagine being able to easily code a breathing box like the one below with just one HTML element and very little CSS without needing SVG or a huge list of points inside the polygon function!

Chris was excited about the initial implementation, too.

How fun would this be:

Breathing box.

I decided to give it a try. I went on CodePen, dropped a <div> in the HTML panel, gave it dimensions in viewport units so that it scales nicely, added a background so that I could see it. Then I went on MDN to check out some usage examples... and my fluffy cloud of a dream began to crash!

Note that clip-path: path() only works in Firefox 63-70 with the layout.css.clip-path-path.enabled flag set to true in about:config and in Firefox 71+ without needing to enable any flag. (Source: MDN.)

These were the examples I found:

path('M0 200L0 110A110 90 0 0 1 240 100L 200 340z') path('M.5 1C.5 1 0 .7 0 .3A.25 .25 1 1 1 .5 .3 .25 .25 1 1 1 1 .3C1 .7 .5 1 .5 1Z')

What are those coordinates? The sad answer is pixel values! Those are used because the path() function takes an SVG <path> string as an argument which — like the value of the SVG d attribute on a <path> element — only contains one kind of coordinate value: unit-less pixels. In the SVG case, these pixels scale with the viewBox of the <svg> element but they don't scale at all inside the CSS path() function!

This means the element always gets clipped to the same fixed area if we have a responsive element with a path() value for the clip-path property. For example, consider a square .box whose edge length is 35vw. We clip it to a heart using the path() function:

clip-path: path('M256 203C150 309 150 309 44 203 15 174 15 126 44 97 73 68 121 68 150 97 179 68 227 68 256 97 285 126 285 174 256 203')

This heart shape stays the same size while the dimensions of our actual .box element changes with the viewport:

The issue with a fixed pixel path().

This is bad news here in 2020, where responsive design is the standard, not the exception. Save for the odd case where the element we want to clip actually has a fixed pixel size, the path() function is completely useless! We're still better off using an actual SVG today, or even a polygon() approximation value for clip-path. In short, path() is still in need of improvement, despite getting off the ground.

Amelia Bellamy-Royds has suggested two possibilities here:

Option 1: Allow calc() values/units inside path data. This would probably be done while extending SVG path syntax in general.

Option 2: Specify viewBox in clip-path declaration, scale path to fit.

I personally prefer the first option. The only advantage the second one offers over using SVG is the fact that we don't have to include an actual SVG. That said, including an actual SVG is always going to have better support.

The first option, however, could be a huge improvement over using SVG — at least enough of an improvement to justify using clip-path on an HTML element instead of including an SVG inside it. Let's consider the breathing box at the top of this post. Using SVG, we have the following markup:

<svg viewBox='-75 -50 150 100'> <path/> </svg>

Note that the viewBox is set such that the 0,0 point is dead in the middle. This means we've got to make the coordinates of the top-left corner (i.e. first two viewBox values) equal to minus half the viewBox dimensions (i.e. the last two viewBox values).

In SCSS, we set the edge length ($l) of the initial square box as the smallest viewBox dimension (which is the smallest of the last two values). This is 100 in our case.

We start the path from the top-left corner of our square box. This means a move to (M) command to this point, with coordinates that are both equal to minus half the length of the edge.

We then go down to the bottom-left corner. This requires drawing a vertical line with a length that equals an edge length ($l) and goes down, in the positive direction of the y axis. So, we'll use the v command.

Next, we go to the bottom-right corner. We'll draw a horizontal line with a length that equals an edge length ($l) and goes right, in the positive direction of the x axis. We'll use the h command to make that happen.

Going to the top-right corner means drawing another vertical line of with a length equal to the edge length ($l), so we will use the v command again — only this time, the difference is the fact that we go in the opposite direction of the y axis, meaning we use the same coordinates, but with a minus sign.

Putting it all together, we have the SCSS that allows us to create the initial square box:

.box { d: path('M#{-.5*$l},#{-.5*$l} v#{$l} h#{$l} v#{-$l}'); fill: darkorange }

The generated CSS (where $l is replaced with 100) looks like this:

.box { d: path('M-50,-50 v100 h100 v-100'); fill: darkorange; }

The result can be seen in the interactive demo below where hovering a part of the path data highlights the corresponding part in the resulting SVG and the other way around:

See the Pen by thebabydino (@thebabydino) on CodePen.

However, if we want the lateral edges to breathe, we can't use straight lines. Let's replace those with quadratic Bézier (q) ones. The end point remains the same, which is one edge length down along the same vertical line. We travel by 0,#{$l} to get there.

But what about the control point we need to specify before that? We place the point to be vertically midway between the start and end points, meaning we go down to it by half of we travel to get to the end point.

And let's say that, horizontally, we place it by a quarter of an edge length to the side in one direction or the other. If we want the lines to protrude to widen the box or squeeze them in to narrow it, we need to do something like this:

d: path('M#{-.5*$l},#{-.5*$l} q#{-.25*$l},#{.5*$l} 0,#{$l} h#{$l} v#{-$l}'); /* swollen box */ d: path('M#{-.5*$l},#{-.5*$l} q#{.25*$l},#{.5*$l} 0,#{$l} h#{$l} v#{-$l}'); /* squished box */

This compiles to the following CSS:

d: path('M-50,-50 q-25,50 0,100 h100 v-100'); /* swollen box */ d: path('M-50,-50 q25,50 0,100 h100 v-100'); /* squished box */

The interactive demo below shows how this path works. You can hover over path data components to see them highlighted on the SVG graphic. You can also toggle between the swollen and squished versions.

See the Pen by thebabydino (@thebabydino) on CodePen.

This is only the left edge. We need to do the same thing for the right edge as well. The difference here is that we're going from the bottom-right corner to the top-right corner instead, which is up (in the negative direction of the y axis). We'll place the control point outside the box to get the wide ox effect, which also means placing it to the right of its endpoints (in the positive direction of the x axis). Meanwhile, we'll place the control point inside to get the narrow box effect, which means placing it to the left of its endpoints (in the negative direction of the x axis).

d: path('M#{-.5*$l},#{-.5*$l} q#{-.25*$l},#{.5*$l} 0,#{$l} h#{$l} q#{.25*$l},#{-.5*$l} 0,#{-$l}'); /* swollen box */ d: path('M#{-.5*$l},#{-.5*$l} q#{.25*$l},#{.5*$l} 0,#{$l} h#{$l} q#{-.25*$l},#{-.5*$l} 0,#{-$l}'); /* squished box */

The above SCSS generates the CSS below:

d: path('M-50,-50 q-25,50 0,100 h100 q25,-50 0,100'); /* swollen box */ d: path('M-50,-50 q25,50 0,100 h100 q-25,-50 0,-100'); /* squished box */

See the Pen by thebabydino (@thebabydino) on CodePen.

In order to get the breathing effect, we animate between the swollen state and the squished state:

.box { d: path('M#{-.5*$l},#{-.5*$l} q#{-.25*$l},#{.5*$l} 0,#{$l} h#{$l} q#{.25*$l},#{-.5*$l} 0,#{-$l}'); /* swollen box */ animation: breathe .5s ease-in-out infinite alternate } @keyframes breathe { to { d: path('M#{-.5*$l},#{-.5*$l} q#{.25*$l},#{.5*$l} 0,#{$l} h#{$l} q#{-.25*$l},#{-.5*$l} 0,#{-$l}'); /* squished box */ } }

Since the only thing that differs between the two states is the sign of the horizontal difference to the control points (the sign of the first number after the quadratic Bézier curve q command), we can simplify things with a mixin:

@mixin pdata($s: 1) { d: path('M#{-.5*$l},#{-.5*$l} q#{-.25*$s*$l},#{.5*$l} 0,#{$l} h#{$l} q#{.25*$s*$l},#{-.5*$l} 0,#{-$l}') } .box { @include pdata(); animation: breathe .5s ease-in-out infinite alternate } @keyframes breathe { to { @include pdata(-1) } }

This is pretty much what I'm doing for the actual breathing box demo, though the motion is slightly more discreet. Still, this does absolutely nothing for the generated CSS — we still have two long, ugly and almost identical paths in the compiled code.

However, if we were able to use a <div>, clipped with a clip-path: path() that supported all sorts of values, including calc() values inside, then we could make the sign a custom property --sgn, which we could then animate between -1 and 1 with the help of Houdini.

div.box { width: 40vmin; height: 20vmin; background: darkorange; --sgn: 1; clip-path: path(M 25%,0% q calc(var(--sgn)*-25%),50% 0,100% h 50% q calc(var(--sgn)*25%),-50% 0,-100%); animation: breathe .5s ease-in-out infinite alternate } @keyframes breathe { to { --sgn: -1 } }

Being able to do this would make a whole world of difference. Our element would scale nicely with the viewport and so would the breathing box we clip out of it. And, most importantly, we wouldn't need to repeat this clipping path in order to get the two different versions of it (the swollen one and the squished one), because using the sign custom property (--sgn) inside a calc() value would do the trick. As it is right now, however, clip-path: path() is pretty much useless.

The post Unfortunately, clip-path: path() is Still a No-Go appeared first on CSS-Tricks.

Selectors Explained

Css Tricks - Mon, 03/02/2020 - 4:54am

Have you ever found yourself either writing a CSS selector that winds up looking confusing as heck, or seen one while reading through someone's code? That happened to me the other day.

Here's what I wrote:

.site-footer__nav a:hover > svg ellipse:first-child { }

At the end of it, I honestly couldn't even explain what it does to myself. LOL, that probably means there was a better way to write it.

But Hugo Giraudel has this handy new tool that will explain any selector you throw at it.

Here's how it explained mine:

An <ellipse> element provided it is the first child of its parent somewhere
… within a <svg> element
… itself directly within an <a> element provided it is hovered
… itself somewhere
… within an element with class site-footer__nav.

Bravo! It even spits out the specificity of the selector to boot. &#x1f44f;

Direct Link to ArticlePermalink

The post Selectors Explained appeared first on CSS-Tricks.

HTML: The Inaccessible Parts

Css Tricks - Sat, 02/29/2020 - 4:48am

<input type="number">, <input type="date">, <input type="search">, <select multiple>, <progress>, <meter>, <dialog>, <details><summary>, <video>, <div onclick>, <div aria-label>, <a href><div>Block Links</div></a>, aria-controls, role="tablist"

&#x1f62c;&#x1f62c;&#x1f62c;&#x1f62c;&#x1f62c;&#x1f62c;&#x1f62c;&#x1f62c;&#x1f62c;&#x1f62c;&#x1f62c;&#x1f62c;




Direct Link to ArticlePermalink

The post HTML: The Inaccessible Parts appeared first on CSS-Tricks.

A Follow-Up to PHP Templating

Css Tricks - Fri, 02/28/2020 - 11:18am

Not long ago, I posted about PHP templating in just PHP (which is basically HEREDOC syntax). I'm literally using that technique for some super basic templating I needed to do on this very WordPress site. The main pushback was that this kind of thing can be an XSS vulnerability. In my case, it's not, because I'm not using it for anything other than an abstraction convenience for my own hand-written strings.

Since then, we've had a couple of good articles about templating and I've seen some other approaches. I thought I'd make a quick link dump of them.

  • Chris Geelhoed took a different approach than I did, passing data to a function then using a require statement for a template file that expects global variables you set right before the require.
  • If you're into the idea of using Twig as a PHP templating engine on your WordPress site, check out Timber. TJ Fogarty has written about this for us.
  • If Timber is a little heavy-handed, check out Sprig from Russell Heimlich. I really like this approach!
  • Jonathan Land shared how you can use Vue (inline) templates to do your templating, even in WordPress-land.
  • Charlie Walter wrote about many ways to approach PHP templating in WordPress, like in Jade, Mustache, and Twig, as well as some interesting combinations.
  • It was the first I've heard of this, but a templating language called TinyButStrong seems to fit the bill and looks like it's actively developed.

The post A Follow-Up to PHP Templating appeared first on CSS-Tricks.

How to Customize the WooCommerce Cart Page on a WordPress Site

Css Tricks - Fri, 02/28/2020 - 5:26am

A standard e-commerce site has a few common pages. There are product pages, shop pages that list products, and let’s not forget pages for the user account, checkout flow and cart.

WooCommerce makes it a trivial task to set these up on a WordPress site because it provides templates for them and create the pages for you right out of the box. This is what makes it easy to get your store up and running in a few minutes just by setting up some products and your payment processing details. WooCommerce is very helpful that way.

But this isn’t a post extolling the virtues of WooCommerce. Instead, let’s look at how we can customize parts of it. Specifically, I want to look at the cart. WooCommerce is super extensible in the sense that it provides a ton of filters and actions that can be hooked into, plus a way to override the templates in a WordPress theme. The problem is, those take at least some intermediate-level dev chops which may not be feasible for some folks. And, at least in my experience, the cart page tends to be the most difficult to grok and customize.

Let’s look at how to change the WooCommerce cart page by implementing a different layout. This is how a standard default cart page looks:

We’ll go for something like this instead:

Here’s what’s different:

  • We’re adopting a two-column layout instead of the single full-width layout of the default template. This allows us to bring the “Cart totals” element up top so it is more visible on larger screens.
  • We’re adding some reassurance for customers by including benefits below the list of products in the cart. This reminds the customer the value they’re getting with their purchase, like free shipping, easy exchanges, customer support and security.
  • We’re including a list of frequently asked questions beneath the list of products in an accordion format. This helps the customer get answers to questions about their purchase without have to leave and contact support.

This tutorial assumes that you have access to your theme files. If you don’t feel comfortable logging in to your hosting server and going to the file manager, I would suggest you install the plugin WP File Manager. With just the free version, you can accomplish everything explained here.

First, let’s roll our own template

One of the many benefits of WooCommerce is that it gives us pre-designed and coded templates to work with. The problem is that those template files are located in the plugin folder. And if the plugin updates in the future (which it most certainly will), any changes we make to the template will get lost. Since directly editing plugin files is a big ol’ no-no in WordPress, WooCommerce lets us modify the files by making copies of them that go in the theme folder.

It’s a good idea to use a child theme when making these sorts of changes, especially if you are using a third-party theme. That way, any changes made to the theme folder aren’t lost when theme updates are released.

To do this, we first have to locate the template we want to customize. That means going into the site’s root directory (or wherever you keep your site files if working locally, which is a great idea) and open up the /wp-content where WordPress is installed. There are several folders in there, one of which is /plugins. Open that one up and then hop over to the /woocommerce folder. That’s the main directory for all-things-WooCommerce. We want the cart.php file, which is located at:

/wp-content/plugins/woocommerce/templates/cart/cart.php

Let’s open up that file in a code editor. One of the first things you’ll notice is a series of comments on top of the file:

/** * Cart Page * * This template can be overridden by copying it to yourtheme/woocommerce/cart/cart.php. // highlight * * HOWEVER, on occasion WooCommerce will need to update template files and you * (the theme developer) will need to copy the new files to your theme to * maintain compatibility. We try to do this as little as possible, but it does * happen. When this occurs the version of the template file will be bumped and * the readme will list any important changes. * * @see https://docs.woocommerce.com/document/template-structure/ * @package WooCommerce/Templates * @version 3.8.0 */

The highlighted line is exactly what we’re looking for — instructions on how to override the file! How kind of the WooCommerce team to note that up front for us.

Let’s make a copy of that file and create the file path they suggest in the theme:

/wp-content/themes/[your-theme]/woocommerce/cart/cart.php

Drop the copied file there and we’re good to start working on it.

Next, let’s add our own markup

The first two things we can tackle are the benefits and frequently asked questions we identified earlier. We want to add those to the template.

Where does our markup go? Well, to make the layout look the way we laid it out at the beginning of this post, we can start below the cart’s closing table </table> , like this:

</table> <!-- Custom code here --> <?php do_action( 'woocommerce_after_cart_table' ); ?>

We won’t cover the specific HTML that makes these elements. The important thing is knowing where that markup goes.

Once we’ve done that, we should end up with something like this:

Now we have all the elements we want on the page. All that’s left is to style things up so we have the two-column layout.

Alright, now we can override the CSS

We could’ve add more markup to the template to create two separate columns. But the existing markup is already organized nicely in a way that we can accomplish what we want with CSS… thanks to flexbox!

The first step involves making the .woocommerce  element a flex container. It’s the element that contains all our other elements, so it makes for a good parent. To make sure we’re only modifying it in the cart and not other pages (because other templates do indeed use this class), we should scope the styles to the cart page class, which WooCommerce also readily makes available.

.woocommerce-cart .woocommerce {   display: flex; }

These styles can go directly in your theme’s style.css file. That’s what WooCommerce suggests. Remember, though, that there are plenty of ways to customize styles in WordPress, some safer and more maintainable than others.

We have two child elements in the .woocommerce element, perfect for our two-column layout: .woocommerce-cart-form and .cart-collaterals. This is the CSS we need to split things up winds up looking something like this:

/* The table containing the list of products and our custom elements */ .woocommerce-cart .woocommerce-cart-form { flex: 1 0 70%; /* 100% at small screens; 70% on larger screens */ margin-right: 30px; } /* The element that contains the cart totals */ .woocommerce-cart .cart-collaterals { flex: 1 0 30%; /* 100% at small screens; 30% on larger screens */ margin-left: 30px; } /* Some minor tweak to make sure the cart totals fill the space */ .woocommerce-cart .cart-collaterals .cart_totals { width: 100%; padding: 0 20px 70px; }

That gives us a pretty clean layout:

It looks more like Amazon’s cart page and other popular e-commerce stores, which is not at all a bad thing.

Best practice: Make the most important elements stand out

One of the problems I have with WooCommerce’s default designs is that all the buttons are designed the same way. They’re all the same size and same background color.

Look at all that blue!

There is no visual hierarchy on the action users should take and, as such, it’s tough to distinguish, say, how to update the cart from proceeding to checkout. The next thing we ought to do is make that distinction clearer by changing the background colors of the buttons. For that, we write the following CSS:

/* The "Apply Coupon" button */ .button[name="apply_coupon"] {   background-color: transparent;   color: #13aff0; } /* Fill the "Apply Coupon" button background color and underline it on hover */ .button[name="apply_coupon"]:hover {   background-color: transparent;   text-decoration: underline; } ? /* The "Update Cart" button */ .button[name="update_cart"] {   background-color: #e2e2e2;   color: #13aff0; } /* Brighten up the button on hover */ .button[name="update_cart"]:hover {   filter: brightness(115%); }

This way, we create the following hierarchy: 

  1. The “Proceed to checkout” is pretty much left as-is, with the default blue background color to make it stand out as it is the most important action in the cart.
  2. The “Update cart” button gets a grey background that blends in with the white background of the page. This de-prioritizes it.
  3. The “Apply coupon” is less a button and more of a text link, making it the least important action of the bunch.

The full CSS that you have to add to make this design is here:

@media(min-width: 1100px) {   .woocommerce-cart .woocommerce {     display: flex;   }   .woocommerce-cart .woocommerce-cart-form {     flex: 1 0 70%;     margin-right: 30px;   }       .woocommerce-cart .cart-collaterals {     flex: 1 0 30%;     margin-left: 30px;   } } ? .button[name="apply_coupon"] {   background-color: transparent;   color: #13aff0; } ? .button[name="apply_coupon"]:hover {   text-decoration: underline;   background-color: transparent;   color: #13aff0; } ? .button[name="update_cart"] {   background-color: #e2e2e2;   color: #13aff0; } ? .button[name="update_cart"]:hover {   background-color: #e2e2e2;   color: #13aff0;   filter: brightness(115%); } That’s a wrap!

Not too bad, right? It’s nice that WooCommerce makes itself so extensible, but without some general guidance, it might be tough to know just how much leeway you have to customize things. In this case, we saw how we can override the plugin’s cart template in a theme directory to future-proof it from future updates, and how we can override styles in our own stylesheet. We could have also looked at using WooCommerce hooks, the WooCommerce API, or even using WooCommerce conditions to streamline customizations, but perhaps those are good for another post at another time.

In the meantime, have fun customizing the e-commerce experience on your WordPress site and feel free to spend a little time in the WooCommerce docs — there are lots of goodies in there, including pre-made snippets for all sorts of things.

The post How to Customize the WooCommerce Cart Page on a WordPress Site appeared first on CSS-Tricks.

Where to Learn WordPress Theme Development

Css Tricks - Fri, 02/28/2020 - 5:25am

Over a decade ago, I did a little three-part video series on Designing for WordPress. Then I did other series with the same spirit, like videocasting the whole v10 redesign, a friend's website, and even writing a book. Those are getting a little long in the tooth though. You might still learn from watching them if you're getting into WordPress theme development, but there will be moments that feel very aged (old UI's and old versions of software). All the code still works though, because WordPress is great at backward compatibility. I still hear from people who found those videos very helpful for them.

But since time has pressed on, and I was recently asked what resources I would suggest now, I figured I'd have a look around and see what looks good to me.

Do you like how I plopped the WordPress logo over some stock art I bought that features both a computer and a chalkboard, by which to evoke a feeling of "learning"? So good. I know. Who are we talking to?

There's a spectrum of WordPress developers, from people who don't know any code at all or barely touch it, to hardcore programming nerds building custom everything.

  1. Pick out a theme that looks good, use it.
  2. &#x1f937;‍♂️
  3. &#x1f937;‍♂️
  4. &#x1f937;‍♂️
  5. &#x1f937;‍♂️
  6. Hardcore programmer nerd.

I can't speak to anybody on either edge of that spectrum. There is this whole world of people in the middle. They can code, but they aren't computer science people. They are get the job done people. Maybe it's something like this:

  1. Pick out a theme that will work, use it.
  2. Start with a theme, customize it a bit using built-in tools.
  3. Start with a theme, hack it up with code to do what you need it to do.
  4. Start from scratch, build out what you need.
  5. Start from scratch, build a highly customized site.
  6. Hardcore programmer nerd.

I've always been somewhere around #4, and I think that's a nice sweet spot. I try to let off-the-shelf WordPress and big popular plugins do the heavy lifting, but I'll bring-my-own front-end (HTML, CSS, and JavaScript) and customize what I have to. I'm making templates. I'm writing queries. I'm building blocks. I'm modularizing where I can.

I feel powerful in that zone. I can build a lot of sites that way, almost by myself. So where are the resources today that help you learn this kind of WordPress theme development? Lemme see what I can find.

Wing it, old school

There is something to be said for learning by doing. Trial by fire. I've learned a lot under these circumstances in my life.

The trick here is to get WordPress installed on a live server and then play with the settings, plugins, customizer, and edit the theme files themselves to make the site do things. You'll find HTML in those theme files — hack it up! You'll see PHP code spitting out content. Can you tell what and how to manipulate it? You'll find a CSS file in the theme — edit that sucker!

Editing a WordPress theme and seeing what happens

The official documentation can help you somewhat here:

To some degree, I'm a fan of doing it live (on a production website) because it lends a sense of realness to what you are doing when you are a beginner. The stakes are high there, giving you a sense of the power you have. When I make these changes, they are for anyone in the world with an internet connection to see.

I did this in my formative years by buying a domain name and hosting, installing WordPress on that hosting, logging into it with SFTP credentials, and literally working on the live files. I used Coda, which is still a popular app, and is being actively developed into a new version of itself as I write.

This is Nova, a MacOS code editor from Panic that has SFTP built-in.

Hopefully, the stakes are real but low. Like you're working on a pet project or your personal site. At some point, hacking on production sites becomes too dangerous of an idea. One line of misplaced PHP syntax can take down the entire site.

If you're working on something like a client site, you'll need to upgrade that workflow.

Modern winging it

The modern, healthy, standard way for working on websites is:

  1. Work on them locally.
  2. Use version control (Git), where new work is done in branches of the master branch.
  3. Deployment to the production website is done when code is pushed to the master branch, like your development branch is merged in.

I've done a recent video on this whole workflow as I do it today. My toolset is:

  • Work locally with Local by Flywheel.
  • My web hosting is also Flywheel, but that isn't required. It could be anything that gives you SFTP access and runs what WordPress needs: Apache, PHP, and MySQL. Disclosure, Flywheel is a sponsor here, but because I like them and their service :).
  • Code is hosted on a private repo on GitHub.
  • Deployment to the Flywheel hosting is done by Buddy. Buddy watches for pushes to the master branch and moves the files over SFTP to the production site.
Local by Flywheel

Now that you have a local setup, you can go nuts. Do whatever you want. You can't break anything on the live site, so you're freer to make experimental changes and just see what happens.

When working locally, it's likely you'll be editing files with a code editor. I'd say the most popular choice these days is the free VS Code, but there is also Atom and Sublime, and fancier editors like PhpStorm.

The freedom of hacking on files is especially apparent once you've pushed your code up to a Git repo. Once you've done that, you have the freedom of reverting files back to the state of the last push.

I use the Git software Tower, and that lets me can see what files have changed since I last committed code. If I've made a mistake, caused a problem, or done something I don't like — even if I don't remember exactly what I changed — I can discard those changes back to their last state. That's a nice level of freedom.

When I do commit code, to master or by merging a branch into master, that's when Buddy kicks in and deploys the changes to the production site.

CSS-Tricks itself is a WordPress site, which has continuously evolved over 13 years. But like, where do you start?

We're talking about WordPress theme development here, so you start with a theme. Themes are literally folders of files in your WordPress installation.

root - /wp-content/ - /themes/ - /theme-name/

WordPress comes with some themes right out of the box. As I write, the Twenty Twenty theme ships with WordPress, and it's a nice one! You could absolutely start your theme hackin' on that.

Themes tend to have some opinions about how they organize themselves and do things, and Twenty Twenty is no different. I'd say, perhaps controversially, that there is no one true way to organize your theme, so long as it's valid code and does things the "WordPress" way. This is just something you'll have to get a feel for as you make themes.

Starter themes

Starter themes were a very popular way to start building a theme from scratch in my day. I don't have a good sense if that's still true, but the big idea was a theme with all the basic theme templates you'll need (single blog post pages, a homepage, a 404 page, search results page, etc.) with very little markup and no styling at all. That way you have an empty canvas from which to build out all your HTML, CSS, and JavaScript yourself to your liking. Sorta like you're building any other site from scratch with these core technologies, only with some PHP in there spitting out content.

There was a theme called Starkers that was popular, but it's dead now. I made one called BLANK myself but haven't touched that in a long time. In looking around a bit, I found some newer themes with this same spirit. Here's the best three I found:

I can't personally vouch for them, but they've all been updated somewhat recently and look like pretty good starting points to me. I'd give them a shot in the case that I was starting from absolute scratch on a project. I'd be tempted to download one and then spruce it up exactly how I like it and save that as my own starter in case I needed to do it again.

It feels worth mentioning that a lot of web development isn't starting from scratch, but rather working on existing projects. In that case, the process is still getting a local environment set up; you just aren't starting from scratch, but with the existing theme. I'd suggest duplicating the theme and changing the name while you hack on it, so even if you deploy it, it doesn't affect the live theme. Others might suggest using the starter as a "parent" theme, then branching off into a "child" theme.

To get your local development environment all synced up with exactly what the production website is like, I think the best tool is WP DB Migrate Pro, which can yank down the production database to your local site and all the media files (paid product and a paid add-on, worth every penny).

Fancier Starter Themes

Rather than starting from absolute scratch, there are themes that come with sensible defaults and even modern build processes for you start with. The idea is that building a site with essentially raw HTML, CSS, and JavaScript, while entirely doable, just doesn't have enough modern conveniences to be comfortable.

Here are some.

  • Morten Rand-Hendriksen has a project called WP Rig that has all sorts of developer tools built into it. A Gulp-based build process spins up a BrowserSync server for auto updating. JavaScript gets processed in Babel. CSS gets processed in PostCSS, and code is linted. He teaches WordPress with it.
  • Roots makes a theme called Sage that comes with a templating engine, your CSS framework of choice, and fancy build process stuff.
  • Ignition has a build process and all sorts of helpers.
  • Timber comes with a templating engine and a bunch of code helpers.

I think all these are pretty cool, but are also probably not for just-starting-out beginner developers.

Books

This is tough because of how many there are. In a quick Google search, I found one site selling fifteen WordPress books as a bundle for $9.99. How would you even know where to start? How good can they be for that rock bottom price? I dunno.

I wrote a book with Jeff Starr ages ago called Digging Into WordPress. After all these years, Jeff still keeps the book up to date, so I'd say that's a decent choice! Jeff has other books like The Tao of WordPress and WordPress Themes In Depth.

A lot of other books specifically about WordPress theme development are just fairly old. 2008-2015 stuff. Again, not that there isn't anything to be learned there, especially as WordPress doesn't change that rapidly, but still, I'd want to read a book more recent that half a decade old. Seems like a big opportunity for a target audience as large as WordPress users and developers. Or if there is already stuff that I'm just not finding, lemme know in the comments.

Perhaps learning is shifting so much toward online that people don't write books as much...

Online learning courses

Our official learning partner Frontend Masters has one course on WordPress focused on JavaScript and WordPress, so that might not be quite perfect for learning the basics of theme development. Still, fascinating stuff.

Here's some others that looked good to me while looking around:

Zac's course looks like the most updated and perhaps the best option there.

A totally different direction for theme Development

One way to build a site with WordPress is not to use WordPress themes at all! Instead, you can use the WordPress API to suck data out of WordPress and build a site however the heck you please.

This idea of decoupling the CMS and the front end you build is pretty neat. It's often referred to as using a "headless" CMS. It's not for everyone. (One big reason is that, in a way, it doubles your technical debt.). But it can bring a freedom to both the CMS and the front end to evolve independently.

The post Where to Learn WordPress Theme Development appeared first on CSS-Tricks.

Data-driven Jamstack with Sourcebit

Css Tricks - Fri, 02/28/2020 - 3:57am

Think of building sites with Gatsby as an hourglass shape.

Gatsby itself is right in the middle. The wide funnel at the top represents the fact that Gatsby can take in data from all sorts of sources. The data could be in markdown files, from a headless CMS or some other API, from a hosted database, or pretty much whatever.

The wide funnel at the bottom represents that the output from Gatsby is static files, so those files can go anywhere. Netlify, GitHub Pages, ZEIT, S3, whatever.

Gatsby does a bunch of neat stuff (just the fact that it's in React I'm sure is appealing to a wide swath of developers), but it seems to me the secret sauce is how it works with any data source.

If you were going to widen that hourglass shape into a, uhhh, pipe, you'd build a tool that connects arbitrary data sources with arbitrary static site generators. It appears that is what Stackbit is doing with Sourcebit. It has a two-sided plugin model (Sources: e.g. Contentful or Sanity; Targets: e.g. Jekyll or Hugo) with the goal of connecting any data source with any site-building tool that needs that data.

I would think contributors to all projects in both the data source and site builder arenas would be interested in seeing this succeed, including Gatsby.

Direct Link to ArticlePermalink

The post Data-driven Jamstack with Sourcebit appeared first on CSS-Tricks.

Why is CSS Frustrating?

Css Tricks - Thu, 02/27/2020 - 1:02pm

Here’s a great thread by Kevin Powell that's making the rounds. He believes so many folks see CSS as a frustrating and annoying language:

That's just as unintuitive as JS starting to count at 0, but since you learned that and accept it, it's fine.

The real issue isn't with CSS. If you struggle with it, or ever call it unintuitive, it's probably because you don't really understand how CSS is meant to work.

— Kevin Powell (@KevinJPowell) February 24, 2020

Why do people respect JavaScript or other languages enough to learn them inside-out, and yet constantly dunk on CSS? Well, all this reminds me of what Jeremy Keith wrote a while back when he argued that CSS is simple, but not easy:

Unlike a programming language that requires knowledge of loops, variables, and other concepts, CSS is pretty easy to pick up. Maybe it’s because of this that it has gained the reputation of being simple. It is simple in the sense of “not complex”, but that doesn’t mean it’s easy. Mistaking “simple” for “easy” will only lead to heartache.

I think that’s what’s happened with some programmers coming to CSS for the first time. They’ve heard it’s simple, so they assume it’s easy. But then when they try to use it, it doesn’t work. It must be the fault of the language, because they know that they are smart, and this is supposed to be easy. So they blame the language. They say it’s broken. And so they try to “fix” it by making it conform to a more programmatic way of thinking.

There have been time where I’ve sat down with engineers to pair with them about a tricky layout issue and the CSS is treated as being beneath them — as if the language is somehow too unpredictable to warrant learning and mastering. Perhaps this has something to do with the past, where we’ve spent years fighting the way browsers render things differently. But this is mostly a solved problem. I can’t remember the last time I fought against browsers like that.

Instead, I reckon the biggest issue that engineers face — and the reason why they find it all so dang frustrating — is that CSS forces you to face the webishness of the web. Things require fallbacks. You need to take different devices into consideration, and all the different ways of seeing a website: mobile, desktop, no mouse, no keyboard, etc. Sure, you have to deal with that when writing JavaScript, too, but it’s easier to ignore. You can’t ignore your the layout of your site being completely broken on a phone.

Side note: We have a guide to centering in CSS not because CSS is busted and dumb, but because there’s so many variables to the extent that a simple question like, “How do I center text?” is actually much more complicated than it appears. There’s so much context that’s missed.

This reminds me of one of my favorite blog posts of all time, where Brandon Smith argues that CSS is awesome and we should respect the language and learn how it works under the hood:

CSS is hard because its properties interact, often in unexpected ways. Because when you set one of them, you're never just setting that one thing. That one thing combines and bounces off of and contradicts with a dozen other things, including default things that you never actually set yourself.

One rule of thumb for mitigating this is, never be more explicit than you need to be. Web pages are responsive by default. Writing good CSS means leveraging that fact instead of overriding it. Use percentages or viewport units instead of a media query if possible. Use min-width instead of width where you can. Think in terms of rules, in terms of what you really mean to say, instead of just adding properties until things look right. Try to get a feel for how the browser resolves layout and sizing, and make your changes and additions on top of that judiciously. Work with CSS, instead of against it.

The post Why is CSS Frustrating? appeared first on CSS-Tricks.

When CSS Blocks

Css Tricks - Thu, 02/27/2020 - 1:02pm

Tim Kadlec:

One particular pattern [for loading non-critical CSS] I’ve seen is the preload/polyfill pattern. With this approach, you load any stylesheets as preloads instead, and then use their onload events to change them back to a stylesheet once the browser has them ready.

So you're trying to make your stylesheet more async, but it causes two big problems:

  1. You've kicked up the priority of the downloading higher than any other asset.
  2. You've blocked the HTML parser too (because of the polyfill as an inline script).

Firefox does something fancy to avoid problem #2 in this particular case, but it affects every other browser.

I've never had good luck with fancy techniques to trick the browser into theoretically better downloading/rendering patterns. I'm kind of a stylesheets in the head, scripts at the end of the body kinda guy, but I know the web is a complicated place. In fact, in a quick peek, I see that Jetpack is inserting an inline script into my <head>, so that would affect my loading too, except they load it with an obfuscated type until later scripts execute and change it, probably to avoid this exact problem.

Anyway, Tim's advice:

• If you’re using loadCSS with the preload/polyfill pattern, switch to the print stylesheet pattern instead.

• If you have any external stylesheets that you’re loading normally (that is, as a regular stylesheet link), move any and all inline scripts that you can above it in the markup

• Inline your critical CSS for the fastest possible start render times.

The print pattern being:

<link rel="stylesheet" href="/path/to/my.css" media="print" onload="this.media='all'">

Direct Link to ArticlePermalink

The post When CSS Blocks appeared first on CSS-Tricks.

Instant GraphQL Backend with Fine-grained Security Using FaunaDB

Css Tricks - Thu, 02/27/2020 - 7:31am

GraphQL is becoming popular and developers are constantly looking for frameworks that make it easy to set up a fast, secure and scalable GraphQL API. In this article, we will learn how to create a scalable and fast GraphQL API with authentication and fine-grained data-access control (authorization). As an example, we’ll build an API with register and login functionality. The API will be about users and confidential files so we’ll define advanced authorization rules that specify whether a logged-in user can access certain files. 

By using FaunaDB’s native GraphQL and security layer, we receive all the necessary tools to set up such an API in minutes. FaunaDB has a free tier so you can easily follow along by creating an account at https://dashboard.fauna.com/. Since FaunaDB automatically provides the necessary indexes and translates each GraphQL query to one FaunaDB query, your API is also as fast as it can be (no n+1 problems!).

Setting up the API is simple: drop in a schema and we are ready to start. So let’s get started!  

The use-case: users and confidential files

We need an example use-case that demonstrates how security and GraphQL API features can work together. In this example, there are users and files. Some files can be accessed by all users, and some are only meant to be accessed by managers. The following GraphQL schema will define our model:

type User { username: String! @unique role: UserRole! } enum UserRole { MANAGER EMPLOYEE } type File { content: String! confidential: Boolean! } input CreateUserInput { username: String! password: String! role: UserRole! } input LoginUserInput { username: String! password: String! } type Query { allFiles: [File!]! } type Mutation { createUser(input: CreateUserInput): User! @resolver(name: "create_user") loginUser(input: LoginUserInput): String! @resolver(name: "login_user") }

When looking at the schema, you might notice that the createUser and loginUser Mutation fields have been annotated with a special directive named @resolver. This is a directive provided by the FaunaDB GraphQL API, which allows us to define a custom behavior for a given Query or Mutation field. Since we’ll be using FaunaDB’s built-in authentication mechanisms, we will need to define this logic in FaunaDB after we import the schema. 

Importing the schema

First, let’s import the example schema into a new database. Log into the FaunaDB Cloud Console with your credentials. If you don’t have an account yet, you can sign up for free in a few seconds.

Once logged in, click the "New Database" button from the home page:

Choose a name for the new database, and click the "Save" button: 

Next, we will import the GraphQL schema listed above into the database we just created. To do so, create a file named schema.gql containing the schema definition. Then, select the GRAPHQL tab from the left sidebar, click the "Import Schema" button, and select the newly-created file: 

The import process creates all of the necessary database elements, including collections and indexes, for backing up all of the types defined in the schema. It automatically creates everything your GraphQL API needs to run efficiently. 

You now have a fully functional GraphQL API which you can start testing out in the GraphQL playground. But we do not have data yet. More specifically, we would like to create some users to start testing our GraphQL API. However, since users will be part of our authentication, they are special: they have credentials and can be impersonated. Let’s see how we can create some users with secure credentials!

Custom resolvers for authentication

Remember the createUser and loginUser mutation fields that have been annotated with a special directive named @resolver. createUser is exactly what we need to start creating users, however the schema did not really define how a user needs to created; instead, it was tagged with a @resolver tag.

By tagging a specific mutation with a custom resolver such as @resolver(name: "create_user") we are informing FaunaDB that this mutation is not implemented yet but will be implemented by a User-defined function (UDF). Since our GraphQL schema does not know how to express this, the import process will only create a function template which we still have to fill in.

A UDF is a custom FaunaDB function, similar to a stored procedure, that enables users to define a tailor-made operation in Fauna’s Query Language (FQL). This function is then used as the resolver of the annotated field. 

We will need a custom resolver since we will take advantage of the built-in authentication capabilities which can not be expressed in standard GraphQL. FaunaDB allows you to set a password on any database entity. This password can then be used to impersonate this database entity with the Login function which returns a token with certain permissions. The permissions that this token holds depend on the access rules that we will write.

Let’s continue to implement the UDF for the createUser field resolver so that we can create some test users. First, select the Shell tab from the left sidebar:

As explained before, a template UDF has already been created during the import process. When called, this template UDF prints an error message stating that it needs to be updated with a proper implementation. In order to update it with the intended behavior, we are going to use FQL's Update function.

So, let’s copy the following FQL query into the web-based shell, and click the "Run Query" button:

Update(Function("create_user"), { "body": Query( Lambda(["input"], Create(Collection("User"), { data: { username: Select("username", Var("input")), role: Select("role", Var("input")), }, credentials: { password: Select("password", Var("input")) } }) ) ) });

Your screen should look similar to:

The create_user UDF will be in charge of properly creating a User document along with a password value. The password is stored in the document within a special object named credentials that is encrypted and cannot be retrieved back by any FQL function. As a result, the password is securely saved in the database making it impossible to read from either the FQL or the GraphQL APIs. The password will be used later for authenticating a User through a dedicated FQL function named Login, as explained next.

Now, let’s add the proper implementation for the UDF backing up the loginUser field resolver through the following FQL query:

Update(Function("login_user"), { "body": Query( Lambda(["input"], Select( "secret", Login( Match(Index("unique_User_username"), Select("username", Var("input"))), { password: Select("password", Var("input")) } ) ) ) ) });

Copy the query listed above and paste it into the shell’s command panel, and click the "Run Query" button:

The login_user UDF will attempt to authenticate a User with the given username and password credentials. As mentioned before, it does so via the Login function. The Login function verifies that the given password matches the one stored along with the User document being authenticated. Note that the password stored in the database is not output at any point during the login process. Finally, in case the credentials are valid, the login_user UDF returns an authorization token called a secret which can be used in subsequent requests for validating the User’s identity.

With the resolvers in place, we will continue with creating some sample data. This will let us try out our use case and help us better understand how the access rules are defined later on.

Creating sample data

First, we are going to create a manager user. Select the GraphQL tab from the left sidebar, copy the following mutation into the GraphQL Playground, and click the "Play" button:

mutation CreateManagerUser { createUser(input: { username: "bill.lumbergh" password: "123456" role: MANAGER }) { username role } }

Your screen should look like this:

Next, let’s create an employee user by running the following mutation through the GraphQL Playground editor:

mutation CreateEmployeeUser { createUser(input: { username: "peter.gibbons" password: "abcdef" role: EMPLOYEE }) { username role } }

You should see the following response:

Now, let’s create a confidential file by running the following mutation:

mutation CreateConfidentialFile { createFile(data: { content: "This is a confidential file!" confidential: true }) { content confidential } }

As a response, you should get the following:

And lastly, create a public file with the following mutation:

mutation CreatePublicFile { createFile(data: { content: "This is a public file!" confidential: false }) { content confidential } }

If successful, it should prompt the following response:

Now that all the sample data is in place, we need access rules since this article is about securing a GraphQL API. The access rules determine how the sample data we just created can be accessed, since by default a user can only access his own user entity. In this case, we are going to implement the following access rules: 

  1. Allow employee users to read public files only.
  2. Allow manager users to read both public files and, only during weekdays, confidential files.

As you might have already noticed, these access rules are highly specific. We will see however that the ABAC system is powerful enough to express very complex rules without getting in the way of the design of your GraphQL API.

Such access rules are not part of the GraphQL specification so we will define the access rules in the Fauna Query Language (FQL), and then verify that they are working as expected by executing some queries from the GraphQL API. 

But what is this "ABAC" system that we just mentioned? What does it stand for, and what can it do?

What is ABAC?

ABAC stands for Attribute-Based Access Control. As its name indicates, it’s an authorization model that establishes access policies based on attributes. In simple words, it means that you can write security rules that involve any of the attributes of your data. If our data contains users we could use the role, department, and clearance level to grant or refuse access to specific data. Or we could use the current time, day of the week, or location of the user to decide whether he can access a specific resource. 

In essence, ABAC allows the definition of fine-grained access control policies based on environmental properties and your data. Now that we know what it can do, let’s define some access rules to give you concrete examples.

Defining the access rules

In FaunaDB, access rules are defined in the form of roles. A role consists of the following data:

  • name —  the name that identifies the role
  • privileges?—?specific actions that can be executed on specific resources?
  • membership?—?specific identities that should have the specified privileges

Roles are created through the CreateRole FQL function, as shown in the following example snippet:

CreateRole({ name: "role_name", membership: [ // ... ], privileges: [ // ... ] })

You can see two important concepts in this role; membership and privileges. Membership defines who receives the privileges of the role and privileges defines what these permissions are. Let’s write a simple example rule to start with: “Any user can read all files.”

Since the rule applies on all users, we would define the membership like this: 

membership: { resource: Collection("User") }

Simple right? We then continue to define the "Can read all files" privilege for all of these users.

privileges: [ { resource: Collection("File"), actions: { read: true } } ]

The direct effect of this is that any token that you receive by logging in with a user via our loginUser GraphQL mutation can now access all files. 

This is the simplest rule that we can write, but in our example we want to limit access to some confidential files. To do that, we can replace the {read: true} syntax with a function. Since we have defined that the resource of the privilege is the "File" collection, this function will take each file that would be accessed by a query as the first parameter. You can then write rules such as: “A user can only access a file if it is not confidential”. In FaunaDB’s FQL, such a function is written by using Query(Lambda(‘x’, … <logic that users Var(‘x’)>)).

Below is the privilege that would only provide read access to non-confidential files: 

privileges: [ { resource: Collection("File"), actions: { // Read and establish rule based on action attribute read: Query( // Read and establish rule based on resource attribute Lambda("fileRef", Not(Select(["data", "confidential"], Get(Var("fileRef")))) ) ) } } ]

This directly uses properties of the "File" resource we are trying to access. Since it’s just a function, we could also take into account environmental properties like the current time. For example, let’s write a rule that only allows access on weekdays. 

privileges: [ { resource: Collection("File"), actions: { read: Query( Lambda("fileRef", Let( { dayOfWeek: DayOfWeek(Now()) }, And(GTE(Var("dayOfWeek"), 1), LTE(Var("dayOfWeek"), 5)) ) ) ) } } ]

As mentioned in our rules, confidential files should only be accessible by managers. Managers are also users, so we need a rule that applies to a specific segment of our collection of users. Luckily, we can also define the membership as a function; for example, the following Lambda only considers users who have the MANAGER role to be part of the role membership. 

membership: { resource: Collection("User"), predicate: Query( // Read and establish rule based on user attribute Lambda("userRef", Equals(Select(["data", "role"], Get(Var("userRef"))), "MANAGER") ) ) }

In sum, FaunaDB roles are very flexible entities that allow defining access rules based on all of the system elements' attributes, with different levels of granularity. The place where the rules are defined — privileges or membership — determines their granularity and the attributes that are available, and will differ with each particular use case.

Now that we have covered the basics of how roles work, let’s continue by creating the access rules for our example use case!

In order to keep things neat and tidy, we’re going to create two roles: one for each of the access rules. This will allow us to extend the roles with further rules in an organized way if required later. Nonetheless, be aware that all of the rules could also have been defined together within just one role if needed.

Let’s implement the first rule: 

“Allow employee users to read public files only.”

In order to create a role meeting these conditions, we are going to use the following query:

CreateRole({ name: "employee_role", membership: { resource: Collection("User"), predicate: Query( Lambda("userRef", // User attribute based rule: // It grants access only if the User has EMPLOYEE role. // If so, further rules specified in the privileges // section are applied next. Equals(Select(["data", "role"], Get(Var("userRef"))), "EMPLOYEE") ) ) }, privileges: [ { // Note: 'allFiles' Index is used to retrieve the // documents from the File collection. Therefore, // read access to the Index is required here as well. resource: Index("allFiles"), actions: { read: true } }, { resource: Collection("File"), actions: { // Action attribute based rule: // It grants read access to the File collection. read: Query( Lambda("fileRef", Let( { file: Get(Var("fileRef")), }, // Resource attribute based rule: // It grants access to public files only. Not(Select(["data", "confidential"], Var("file"))) ) ) ) } } ] })

Select the Shell tab from the left sidebar, copy the above query into the command panel, and click the "Run Query" button:

Next, let’s implement the second access rule:

“Allow manager users to read both public files and, only during weekdays, confidential files.”

In this case, we are going to use the following query:

CreateRole({ name: "manager_role", membership: { resource: Collection("User"), predicate: Query( Lambda("userRef", // User attribute based rule: // It grants access only if the User has MANAGER role. // If so, further rules specified in the privileges // section are applied next. Equals(Select(["data", "role"], Get(Var("userRef"))), "MANAGER") ) ) }, privileges: [ { // Note: 'allFiles' Index is used to retrieve // documents from the File collection. Therefore, // read access to the Index is required here as well. resource: Index("allFiles"), actions: { read: true } }, { resource: Collection("File"), actions: { // Action attribute based rule: // It grants read access to the File collection. read: Query( Lambda("fileRef", Let( { file: Get(Var("fileRef")), dayOfWeek: DayOfWeek(Now()) }, Or( // Resource attribute based rule: // It grants access to public files. Not(Select(["data", "confidential"], Var("file"))), // Resource and environmental attribute based rule: // It grants access to confidential files only on weekdays. And( Select(["data", "confidential"], Var("file")), And(GTE(Var("dayOfWeek"), 1), LTE(Var("dayOfWeek"), 5)) ) ) ) ) ) } } ] })

Copy the query into the command panel, and click the "Run Query" button:

At this point, we have created all of the necessary elements for implementing and trying out our example use case! Let’s continue with verifying that the access rules we just created are working as expected...

Putting everything in action

Let’s start by checking the first rule: 

“Allow employee users to read public files only.”

The first thing we need to do is log in as an employee user so that we can verify which files can be read on its behalf. In order to do so, execute the following mutation from the GraphQL Playground console:

mutation LoginEmployeeUser { loginUser(input: { username: "peter.gibbons" password: "abcdef" }) }

As a response, you should get a secret access token. The secret represents that the user has been authenticated successfully:

At this point, it’s important to remember that the access rules we defined earlier are not directly associated with the secret that is generated as a result of the login process. Unlike other authorization models, the secret token itself does not contain any authorization information on its own, but it’s just an authentication representation of a given document.

As explained before, access rules are stored in roles, and roles are associated with documents through their membership configuration. After authentication, the secret token can be used in subsequent requests to prove the caller’s identity and determine which roles are associated with it. This means that access rules are effectively verified in every subsequent request and not only during authentication. This model enables us to modify access rules dynamically without requiring users to authenticate again.

Now, we will use the secret issued in the previous step to validate the identity of the caller in our next query. In order to do so, we need to include the secret as a Bearer Token as part of the request. To achieve this, we have to modify the Authorization header value set by the GraphQL Playground. Since we don’t want to miss the admin secret that is being used as default, we’re going to do this in a new tab.

Click the plus (+) button to create a new tab, and select the HTTP HEADERS panel on the bottom left corner of the GraphQL Playground editor. Then, modify the value of the Authorization header to include the secret obtained earlier, as shown in the following example. Make sure to change the scheme value from Basic to Bearer as well:

{ "authorization": "Bearer fnEDdByZ5JACFANyg5uLcAISAtUY6TKlIIb2JnZhkjU-SWEaino" }

With the secret properly set in the request, let’s try to read all of the files on behalf of the employee user. Run the following query from the GraphQL Playground: 

query ReadFiles { allFiles { data { content confidential } } }

In the response, you should see the public file only:

Since the role we defined for employee users does not allow them to read confidential files, they have been correctly filtered out from the response!

Let’s move on now to verifying our second rule:

“Allow manager users to read both public files and, only during weekdays, confidential files.”

This time, we are going to log in as the employee user. Since the login mutation requires an admin secret token, we have to go back first to the original tab containing the default authorization configuration. Once there, run the following query:

mutation LoginManagerUser { loginUser(input: { username: "bill.lumbergh" password: "123456" }) }

You should get a new secret as a response:

Copy the secret, create a new tab, and modify the Authorization header to include the secret as a Bearer Token as we did before. Then, run the following query in order to read all of the files on behalf of the manager user:

query ReadFiles { allFiles { data { content confidential } } }

As long as you’re running this query on a weekday (if not, feel free to update this rule to include weekends), you should be getting both the public and the confidential file in the response:

And, finally, we have verified that all of the access rules are working successfully from the GraphQL API!

Conclusion

In this post, we have learned how a comprehensive authorization model can be implemented on top of the FaunaDB GraphQL API using FaunaDB's built-in ABAC features. We have also reviewed ABAC's distinctive capabilities, which allow defining complex access rules based on the attributes of each system component.

While access rules can only be defined through the FQL API at the moment, they are effectively verified for every request executed against the FaunaDB GraphQL API. Providing support for specifying access rules as part of the GraphQL schema definition is already planned for the future.

In short, FaunaDB provides a powerful mechanism for defining complex access rules on top of the GraphQL API covering most common use cases without the need for third-party services.

The post Instant GraphQL Backend with Fine-grained Security Using FaunaDB appeared first on CSS-Tricks.

Animated Matryoshka Dolls in CSS

Css Tricks - Thu, 02/27/2020 - 5:23am

Here’s a fun one. How might we create a set of those cool Matryoshka dolls where they nest inside one another... but in CSS?

I toyed with this idea in my head for a little while. Then, I saw a tweet from CSS-Tricks and the article image had the dolls. I took that as a sign! It was time to put fingers to the keyboard.

Our goal here is to make these fun and interactive, where we can click on a doll to open it up and reveal another, smaller doll. Oh, and stick with just CSS for the functionality. And while we’re at it, let’s replace the dolls with our own character, say a CodePen bear. Something like this:

We won't dwell on making things pretty to start. Let's get some markup on the page and thrash out the mechanics first.

We can’t have an infinite amount of dolls. When we reach the innermost doll, it'd be nice to be able to reset the dolls without having to do a page refresh. A neat trick for this is to wrap our scene in an HTML form. That way we can use an input and set the type attribute to reset to avoid using any JavaScript.

<form> <input type="reset" id="reset"/> <label for="reset" title="Reset">Reset</label> </form>

Next, we need some dolls. Or bears. Or something to start with. The key here will be to use the classic checkbox hack and any associated form labels. As a note, I’m going to use Pug to handle the markup because it supports loops, making things a little easier. But, you can certainly write the HTML by hand. Here’s the start with form fields and labels that set up the checkbox hack.

CodePen Embed Fallback

Try clicking some of the inputs and hitting the Reset input. They all become unchecked. Nice, we’ll use that.

We have some interactivity but nothing is really happening yet. Here’s the plan:

  1. We’ll only show one checkbox at a time
  2. Checking a checkbox should reveal the label for the next checkbox.
  3. When we get to the last checkbox, there our only option should be to reset the form.

The trick will be to make use of the CSS adjacent sibling combinator (+).

input:checked + label + input + label {   display: block; }

When a checkbox is checked, we need to show the label for the next doll, which will be three siblings along in the DOM. How do we make the first label visible? Give it an explicit display: block via inline styles in our markup. Putting this together, we have something along these lines:

CodePen Embed Fallback

Clicking each label reveals the next. Hold on, the last label isn’t shown! That’s correct. And that’s because the last label doesn’t have a checkbox. We need to add a rule that caters to that last label.

input:checked + label + input + label, input:checked + label + label { display: block; } CodePen Embed Fallback

Cool. We're getting somewhere. That's the basic mechanics. Now things are going to get a little trickier. 

Basic styling

So, you might be thinking, “Why aren’t we hiding the checked label?” Good question! But, if we hide it straight away, we won’t have any transition between the current doll and the next. Before we start animating our dolls, let’s create basic boxes that will represent a doll. We can style them up so they mimic the doll outline without the detail.

.doll { color: #fff; cursor: pointer; height: 200px; font-size: 2rem; left: 50%; position: absolute; text-align: center; top: 50%; transform: translate(-50%, -50%); width: 100px; } .doll:nth-of-type(even) { background: #00f; } .doll:nth-of-type(odd) { background: #f00; } CodePen Embed Fallback

Clicking one doll instantly reveals the next one and, when we’ve reached the last doll, we can reset the form to start again. That’s what we want here.

The mechanics

We are going to animate the dolls based on a center point. Our animation will consist of many steps:

  1. Slide the current doll out to the left.
  2. Open the doll to reveal the next one.
  3. Move the next doll where the current one started.
  4. Make the current doll fade out.
  5. Assign the next doll as the current doll.

Let’s start by sliding the current doll out to the left. We apply an animation when we click a label. Using the :checked pseudo-selector we can target the current doll. At this point, it’s worth noting that we are going to use CSS variables to control animation speed and behavior. This will make it easier to chain animations on the labels.

:root { --speed: 0.25; --base-slide: 100; --slide-distance: 60; } input:checked + label { animation: slideLeft calc(var(--speed) * 1s) forwards; } @keyframes slideLeft { to { transform: translate(calc((var(--base-slide) * -1px) + var(--slide-distance) * -1%), 0); } }

That looks great. But there’s an issue. As soon as we click a label, we could click it again and reset the animation. We don't want that to happen.

How can we get around this? We can remove pointer events from a label once it’s been clicked.

input:checked + label { animation: slideLeft calc(var(--speed) * 1s) forwards; pointer-events: none; }

Great! Now once we have started, we can’t stop the animation chain from happening.

CodePen Embed Fallback

Next up, we need to crack open the doll to reveal the next one. This is where things get tricky because we are going to need some extra elements, not only to create the effect that the doll is opening up, but also to reveal the next doll inside of it. That's right: we need to duplicate the inner doll. The trick here is to reveal a "fake" doll that we swap for the real one once we've animated it. This also means delaying the reveal of the next label.

Now our markup updates labels so that they contains span elements.

<label class="doll" for="doll--1"> <span class="doll doll--dummy"></span> <span class="doll__half doll__half--top">Top</span> <span class="doll__half doll__half--bottom">Bottom</span> </label>

These will act as the “dummy” doll as well as the lid and base for the current doll.

.doll { color: #fff; cursor: pointer; height: 200px; font-size: 2rem; position: absolute; text-align: center; width: 100px; } .doll:nth-of-type(even) { --bg: #00f; --dummy-bg: #f00; } .doll:nth-of-type(odd) { --bg: #f00; --dummy-bg: #00f; } .doll__half { background: var(--bg); position: absolute; width: 100%; height: 50%; left: 0; } .doll__half--top { top: 0; } .doll__half--bottom { bottom: 0; } .doll__dummy { background: var(--dummy-bg); height: 100%; width: 100%; position: absolute; top: 0; left: 0; }

The lid requires three translations to create the opening effect: one to pop it up, one to move it left and then one to pop it down.

@keyframes open { 0% { transform: translate(0, 0); } 33.333333333333336% { transform: translate(0, -100%); } 66.66666666666667% { transform: translate(-100%, -100%); } 100% { transform: translate(-100%, 100%); } }

Next is where we can use CSS custom properties to handle changing values. Once the doll has slid over to the left, we can open it. But how do we know how long to delay it from opening until that happens? We can use the --speed custom property we defined earlier to calculate the correct delay.

It looks a little quick if we use the --speed value as it is, so let’s multiply it by two seconds:

input:checked + .doll { animation: slideLeft calc(var(--speed) * 1s) forwards; pointer-events: none; } input:checked + .doll .doll__half--top { animation: open calc(var(--speed) * 2s) calc(var(--speed) * 1s) forwards; // highlight }

Much better:

CodePen Embed Fallback

Now we need to move the inner “dummy” doll to the new position. This animation is like the open animation in that it consists of three stages. Again, that’s one to move up, one to move right, and one to set down. It's like the slide animation, too. We are going to use CSS custom properties to determine the distance that the doll moves.

:root { // Introduce a new variable that defines how high the dummy doll should pop out. --pop-height: 60; } @keyframes move { 0% { transform: translate(0, 0) translate(0, 0); } 33.333333333333336% { transform: translate(0, calc(var(--pop-height) * -1%)) translate(0, 0); } 66.66666666666667% { transform: translate(0, calc(var(--pop-height) * -1%)) translate(calc((var(--base-slide) * 1px) + var(--slide-distance) * 1%), 0); } 100% { transform: translate(0, calc(var(--pop-height) * -1%)) translate(calc((var(--base-slide) * 1px) + var(--slide-distance) * 1%), calc(var(--pop-height) * 1%)); } }

Almost there! 

CodePen Embed Fallback

The only thing is that the next doll is available as soon as we click a doll. that means we can spam click our way through the set.

Technically, the next doll shouldn’t show until the “fake” one has moved into place. It’s only once the “fake” doll is in place that we can hide it and reveal the real one. That means we going to use zero-second scale animations! That's right. We can play pretend by delaying two zero-second animations and using animation-fill-mode.

@keyframes appear { from { transform: scale(0); } }

We actually only need one set of @keyframes. because we can re-use what we have to create the opposite movement with animation-direction: reverse. With that in mind, all our animations get applied something like this:

// The next doll input:checked + .doll + input + .doll, // The last doll (doesn't have an input) input:checked + .doll + .doll { animation: appear 0s calc(var(--speed) * 5s) both; display: block; } // The current doll input:checked + .doll, // The current doll that isn't the first. Specificity prevails input:checked + .doll + input:checked + .doll { animation: slideLeft calc(var(--speed) * 1s) forwards; pointer-events: none; } input:checked + .doll .doll__half--top, input:checked + .doll + input:checked + .doll .doll__half--top { animation: open calc(var(--speed) * 2s) calc(var(--speed) * 1s) forwards; } input:checked + .doll .doll__dummy, input:checked + .doll + input:checked + .doll .doll__dummy { animation: move calc(var(--speed) * 2s) calc(var(--speed) * 3s) forwards, appear 0s calc(var(--speed) * 5s) reverse forwards; }

Note how important the variables are, especially where we are chaining animations. That gets us almost where we need to be.

CodePen Embed Fallback

I can hear it now: "They're all the same size!" Yep. That's the missing piece. They need to scale down. The trick here is to adjust the markup again and make use of CSS custom properties yet again.

<input id="doll--0" type="checkbox"/> <label class="doll" for="doll--0" style="display: block; --doll-index: 0;"> <span class="doll__dummy-container"> <span class="doll__dummy"></span> </span> //highlight <span class="doll__container"> <span class="doll__half doll__half--top"></span> <span class="doll__half doll__half--bottom"></span> </span> </label>

We just introduced a CSS custom property inline that tells us the index of the doll. We can use this to generate a scale of each half as well as the fake inner doll. The halves will have to scale to match the actual doll size, but the fake inner doll scale will need to match that of the next doll. Tricky!

We can apply these scales inside the containers so that our animations are not affected.

:root { --scale-step: 0.05; } .doll__container, .doll__dummy-container { height: 100%; left: 0; position: absolute; top: 0; width: 100%; } .doll__container { transform: scale(calc(1 - ((var(--doll-index)) * var(--scale-step)))); transform-origin: bottom; } .doll__dummy { height: 100%; left: 0; position: absolute; top: 0; transform: scale(calc(1 - ((var(--doll-index) + 1) * var(--scale-step)))); transform-origin: bottom center; width: 100%; }

Note how the .doll__dummy class uses var(--doll-index) + 1) to calculate the scale so that it matches the next doll.  &#x1f44d;

Lastly, we re-assign the animation to the .doll__dummy-container class instead of the .doll__dummy class.

input:checked + .doll .doll__dummy-container, input:checked + .doll + input:checked + .doll .doll__dummy-container { animation: move calc(var(--speed) * 2s) calc(var(--speed) * 3s) forwards, appear 0s calc(var(--speed) * 5s) reverse forwards; }

Here's a demo where the containers have been given a background color to see what’s happening.

CodePen Embed Fallback

We can see that, although the content size changes, they remain the same size. This makes for consistent animation behavior and makes the code easier to maintain.

Finishing touches

Wow, things are looking pretty slick! All we need are some finishing touches and we are done!

The scene starts to look cluttered because we’re stacking the “old” dolls off to the side when a new one is introduced. So let's slide a doll out of view when the next one is revealed to clean that mess up.

@keyframes slideOut { from { transform: translate(calc((var(--base-slide) * -1px) + var(--slide-distance) * -1%), 0); } to { opacity: 0; transform: translate(calc((var(--base-slide) * -1px) + var(--slide-distance) * -2%), 0); } } input:checked + .doll, input:checked + .doll + input:checked + .doll { animation: slideLeft calc(var(--speed) * 1s) forwards, slideOut calc(var(--speed) * 1s) calc(var(--speed) * 6s) forwards; pointer-events: none; }

The new slideOut animation fades the doll out while it translates to the left. Perfect.  &#x1f44d;

That’s it for the CSS trickery we need to make this effect work. All that’s left style the dolls and the scene.

We have many options to style the dolls. We could use a background image, CSS illustration, SVG, or what have you. We could even throw together some emoji dolls that use random inline hues!

CodePen Embed Fallback

Let’s go with inline SVG.

I’m basically using the same underlying mechanics we’ve already covered. The difference is that I’m also generating inline variables for hue and lightness so the bears sport different shirt colors.

CodePen Embed Fallback

There we have it! Stacking dolls — err, bears — with nothing but HTML and CSS! All the code for all the steps is available in this CodePen collection. Questions or suggestions? Feel free to reach out to me here in the comments.

The post Animated Matryoshka Dolls in CSS appeared first on CSS-Tricks.

Syndicate content
©2003 - Present Akamai Design & Development.