Loading...

Follow CSS-Tricks on Feedspot

Continue with Google
Continue with Facebook
or

Valid
CSS-Tricks by Robin Rendle - 22h ago

Every Layout is a new work-in-progress website and book by Heydon Pickering and Andy Bell that explains how to make common layout patterns with CSS. They describe a lot of the issues when it comes to the design of these layouts, such as responsive problems and making sure we all write maintainable code, and then they’ve provided a handy generator at the end of each article to create our own little frameworks for dealing with these things.

They also have a complementary blog and one of the posts called "Algorithmic Design" caught my eye:

We make many of our biggest mistakes as visual designers for the web by insisting on hard coding designs. We break browsers’ layout algorithms by applying fixed positions and dimensions to our content.

Instead, we should be deferential to the underlying algorithms that power CSS, and we should think in terms of algorithms as we extrapolate layouts based on these foundations. We need to be leveraging selector logic, harnessing flow and wrapping behavior, and using calculations to adapt layout to context.

The tools for flexible, robust, and efficient web layout are there. We are just too busy churning out CSS to use them.

I’m looking forward to seeing where this project goes and how many more layouts these two end up documenting.

Direct Link to Article

The post Every Layout appeared first on CSS-Tricks.

Read Full Article
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 
CSS-Tricks by Daniel Tonon - 1d ago

The sectioning elements in HTML5 are <nav>, <aside>, <article>, and <section>. <body> is also kind of a sectioning element since all content lying inside of it is part of the default document section.

Here is a brief explanation of each sectioning element and how they are used:

  • <nav> - Equivalent to role="navigation". Major site navigation that consistently appears frequently across the site. Examples include the primary navigation, secondary navigation, and in-page navigation.
  • <aside> - Equivalent to role="complimentary". Content that is only tangentially related (or not related) to the main content. Think of something like a sidebar with supplementary information, a note within an article, or the outer container for a list of related articles at the bottom of a blog post.
  • <article> - Equivalent to role="article". Content that is self-contained in that it makes sense on its own when taken out of context. That could mean a widget, a blog post or even a comment within a blog post.
  • <section> - Equivalent to role="region". Content that needs extra context from its parent sectioning element to make sense. This is a generic sectioning element that is used whenever it doesn’t make sense to use the other more semantic ones.

Contents

This is a very long article that I suspect you will want to come back to and reference multiple times. To make that easier, here is a list of all the article headings:

Jump to article heading
  1. When to use <nav>
    1. A <nav> is unnecessary around a search form
    2. Don’t use the word "nav" or "navigation" in the label
    3. Questions to ask yourself
    4. A <nav> doesn’t have to be a list of links
  2. Avoid nesting an <aside> inside an <aside>
  3. Article is like "Block"; Section is like "Element"
  4. Don’t swap <div> for <section>
  5. Headers and footers
    1. What goes inside headers?
    2. What goes inside footers?
  6. Sectioning elements and the document outline algorithm
    1. No browser supports the document outline algorithm
  7. Sectioning content
  8. The <main> element
  9. You need to label your sections. Here are three methods.
    1. Method 1: Add an aria-label attribute
      1. The aria-label translation issue
      2. Positives
      3. Negatives
    2. Method 2: Add a <h#> element to it
      1. Heading placement
      2. Only one heading of the highest level per sectioning element
      3. The heading always comes first
      4. Making visually hidden section labels out of headings
      5. Headings are well-supported by structure analysis tools
      6. Positives
      7. Negatives
    3. Method 3: Use an aria-labelledby attribute
      1. Labels can be hidden without CSS
      2. Major portability issue
      3. No need to place the label near the sectioning element
      4. Turn non-heading elements into section labels
      5. Positives
      6. Negatives
  10. Only use one method at a time
  11. Adding section labels to our example layout
  12. Making Heading 1 be the first heading
  13. Concerns with the simplified outline algorithm spec
  14. Using aria on the example layout sectioning elements
    1. Using aria-label
    2. Using aria-labelledby
    3. Results of using aria
  15. What happens when you need h7?
  16. Does your site have a good structure?
  17. Download and use a screen reader
When to use <nav>

The <nav> element only ever needs to be used once per navigation block. Sub-navigation that is already contained inside a <nav> element does not need to be wrapped in a second <nav> element.

<nav aria-label="Primary">
  <ul>
    <li><a href="#">Primary link</a></li>
    <li><a href="#">Primary link</a></li>
    <li>
      <a href="#">Primary link</a>
      <!-- <nav> element is *not* needed again here -->
      <ul>
        <li><a href="#">Secondary link</a></li>
        <li><a href="#">Secondary link</a></li>
      </ul>
    </li>
  </ul>
</nav>

The <nav> element is intended for only major navigation blocks. "Major" is a very subjective term though. html5doctor.com has a pretty good explanation of when it is and isn’t appropriate to use <nav>, keep in mind that the following are opinions and not official W3C rulings:

The key phrase is ‘major’ navigation. We could debate all day about the definition of ‘major’, but to me it means:

  • Primary navigation
  • Site search
  • Secondary navigation (arguably)
  • In-page navigation (within a long article, for example)

While there isn’t any right or wrong here, a straw poll coupled with my own interpretation tells me that the following shouldn’t be enclosed by <nav>:

  • Pagination controls
  • Social links (although there may be exceptions where social links are the major navigation, in sites like About me or Flavours, for example)
  • Tags on a blog post
  • Categories on a blog post
  • Tertiary navigation
  • Fat footers

html5doctor.com (strikethrough mine)

Breadcrumbs are another piece of content that should be wrapped in a <nav> element as evidenced by this W3C breadcrumb HTML example.

A <nav> is unnecessary around a search form

I disagree with HTML5 Doctor’s opinion that a site search form should be wrapped in a <nav> element (thus why I crossed it out). <nav> is intended to be wrapped around navigation links, not a form. The site search actually has its own special role that defines it as a search landmark. If you add role="search" to the search <form> element, it is announced to screen reader users as a search landmark. Screen reader users will also be able to navigate to it when navigating via landmarks. If there are multiple search forms on the page, they should be differentiated using aria-label or aria-labelledby (more details on these attributes later). Don’t include the word "search" in the aria label though — that’s like saying "image of" in image alt text; it’s unnecessary. Instead, focus on what the search form is searching through. So, for the global site search, giving it aria-label="site" would be appropriate.

<!-- <nav> is not needed on a search form. -->
<!-- role="search" is enough -->
<form role="search" aria-label="site">
  <label>
    <span>Search</span>
    <input type="search" />
  </label>
  <buton type="submit">Submit</button>
</form>

A role="search" form won’t appear in a document outline but I think this is okay considering search forms are often small and self-contained. It still gets the majority of benefits that you get from using sectioning elements. Adding a sectioning element to the mix bombards the screen reader user with messages telling them that it is a search form (one for the sectioning element, one for the search role, and one for the search input label).

Don’t use the word "nav" or "navigation" in the label

Like with role="search", adding "navigation" to the label of a <nav> element only results in a screen reader saying "navigation" twice.

<nav aria-label="primary navigation">
  <!-- Screen reader: "primary navigation navigation landmark" -->
</nav>

<nav aria-label="primary">
  <!-- Screen reader: "primary navigation landmark" -->
</nav>
Questions to ask yourself

That same HTML5 Doctor article lists two questions that you can ask yourself to help you figure out if something should be wrapped in a <nav> or not:

  • Would another sectioning element also be appropriate? If yes, maybe use that instead.
  • Would you add a link to it in a "skip to" block for accessibility? If not, then it might not be worth using a <nav> element.

In those cases where the navigation is too minor to justify the use of the <nav> element, <section> is most likely the element that you should use instead.

A <nav> doesn’t have to be a list of links

The most common use case for a <nav> is to wrap it around a list of links but it doesn’t have to be a list of links. If your navigation works in a different sort of way, you can still use the <nav> element.

<!-- Example taken from the <nav> element specification -->
<!-- https://html.spec.whatwg.org/multipage/sections.html#the-nav-element:the-nav-element-5 -->
<nav>
  <h1>Navigation</h1>
  <p>You are on my home page. To the north lies <a href="/blog">my
  blog</a>, from whence the sounds of battle can be heard. To the east
  you can see a large mountain, upon which many <a
  href="/school">school papers</a> are littered. Far up thus mountain
  you can spy a little figure who appears to be me, desperately
  scribbling a <a href="/school/thesis">thesis</a>.</p>
  <p>To the west are several exits. One fun-looking exit is labeled <a
  href="https://games.example.com/">"games"</a>. Another more
  boring-looking exit is labeled <a
  href="https://isp.example.net/">ISP&#x2122;</a>.</p>
  <p>To the south lies a dark and dank <a href="/about">contacts
  page</a>. Cobwebs cover its disused entrance, and at one point you
  see a rat run quickly out of the page.</p>
</nav>

In this same vein, it’s okay to have small bits like intro text in the <nav> element as long as the primary focus of the content is on the navigation links. Introductory content is best placed inside a <header> in the <nav> element. I’ll go into more depth on headers and footers later.

<nav>
  <header>
    <h2>In this section</h2>
    <p>This is some intro text describing what you will find in this section.</p>
  </header>
  <ul>
    <li><a href="#">Sub section one</a></li>
    <li><a href="#">Sub section two</a></li>
  </ul>
</nav>
Avoid nesting an <aside> inside an <aside>

In the same way that <nav> shouldn’t really ever be nested inside another <nav> element, <aside> elements also tend not to be nested inside each other. <aside> is used to represent content that is tangentially related to the content around it. That means placing an aside inside an aside is basically announcing a tangent away from something that in itself is a tangent away from the main content.

<!-- Don't do this -->
<aside aria-label="Side bar">

  <aside>
    <h2>Share</h2>
    <ul>
      <!-- List of social media links -->
    </ul>    
  </aside>

  <aside>
    <h2>Recommendations:</h2>
    <ul>
      <li>
        <article>
          <h2><a href="#">Related article title</a></h2>
          <p>Article description</p>
        </article>
      </li>
      <!-- List of recommended articles continues -->
    </ul>
  </aside>

</aside>

If you have a sidebar that has multiple sections, don’t nest <aside> elements inside of <aside> elements like in the example above. Instead, make the sidebar a single <aside> and then use <section> (or another appropriate sectioning element) to create the different sections.

<!-- Do this instead -->
<aside aria-label="Side bar">

  <section>
    <h2>Share</h2>
    <ul>
      <!-- List of social media links -->
    </ul>    
  </section>

  <section>
    <h2>Recommended articles:</h2>
    <ul>
      <li>
        <article>
          <h2><a href="#">Related article title</a></h2>
          <p>Article description</p>
        </article>
      </li>
      <!-- List of recommended articles continues -->
    </ul>
  </section>

</aside>
Article is like "Block"; Section is like "Element"

<section> and <article> are easy to get confused with one another. If you are familiar with "Block Element Modifier" (BEM) syntax, then an easy way to think of the difference between the two is that an <article> is a bit like the "B" (or "Block") in BEM. It is a container that stores self-contained content that still makes sense when placed in a different context. Individual tweets on Twitter and each list item on a search results page would be considered <article> elements.

<section> is like the "E" (or "Element") in BEM. It is a sub-section that requires context from its parent sectioning element to make sense. <section> is a generic catch-all sectioning element that you use when it doesn’t make sense to use the other sectioning elements. So, if in doubt, go with <section>.

Note that if something is styled as a "Block" in BEM, that doesn't automatically mean that it is an <article> element. Same goes for BEM "Elements" and <section> elements. The element of something should be based on the meaning of the content, not how the content looks.

Comments sections

Something that may surprise people is that individual comments on a blog post are also considered articles, even though they are in reply to the main blog post. The <article> element wrapping around the main blog post should also wrap around the comments section though. This is to represent that the comments go with the main article.

<article>
  <h1>I am an awesome blog post!</h1>
  <p>I am some body text content.</p>

  <section>
    <h2>Comments</h2>
    <ul>
      <li>
        <article>
          <h2>Username</h2>
          <p>This is the comment body text.</p>
          <footer>
            <p>
              Meta data like post date makes sense
              in either the header or the footer.
            </p>
          </footer>
        </article>
      </li>
    </ul>
  </section>
</article>
Don’t swap <div> for <section>

Just because we have these fancy sectioning elements now, it doesn’t mean that the good old <div> element has lost all of its usefulness. <div> has no semantic meaning, so it is quite useful whenever we are altering the HTML purely for the sake of styling purposes.

Let’s say that we have a blog post contained inside an <article> element that we need to wrap in something for the sake of styling purposes.

<!-- I need a wrapper element -->
<article>
  <h1>I am a blog post</h1>
  <p>I am some content</p>
</article>

Reaching for the <section> element in this circumstance is not the right thing to do.

<!-- Do not do this -->
<section >
  <article>
    <h1>I am a blog post</h1>
    <p>I am some content</p>
  </article>
</section>

Though <section> is technically a generic element, <div> is the far more appropriate option in this circumstance. This new wrapping container is not meant to have any semantic meaning behind it and that is exactly what <div> is designed to be used for.

<!-- Use a <div> if the element is only used for styling purposes -->
<div >
  <article>
    <h1>I am a blog post</h1>
    <p>I am some content</p>
  </article>
</div>

Another way to remember this: if you can’t think of a meaningful heading to apply to a <section>, then it probably shouldn’t be a <section>.

Headers and footers

Although they don’t necessarily need to, sectioning elements may contain a single <header> and a single <footer> with the header being at the top of the section and the footer being at the bottom.

Sectioning elements can be nested inside one another as many times as is needed based on the content.

The header and footer in a sectioning element can also contain sectioning elements.

The one major restriction around nesting sectioning elements is that headers and footers cannot be nested inside other headers and footers.

Nesting headers and footers inside one another is not allowed. What goes inside headers?

Headers are used for introductory content. Appropriate things to include in <header> elements include (but are not limited to):

  • The heading element (<h1>-<h6>)
  • An introductory paragraph or statement.
  • A profile picture
  • A logo
  • A search form
  • Primary navigation
  • Author’s name
  • Post/updated date
  • Meta data
  • Social media links
What goes inside footers?

Footer elements primarily hold things like meta data and minor supporting content. Appropriate things to include in <footer> elements include (but are not limited to):

  • Copyright information
  • Legalities
  • Footnotes
  • Low priority site navigation
  • Author’s name
  • Post/updated date
  • Meta data
  • Social media links

You will notice that there is some cross over between the header and the footer in terms of content that is appropriate to both. This is mostly because meta-type content fits well in either..

Read Full Article
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

That’s what I like to tell people. I’ve seen too many websites die off, often damaging the company along the way because the technical debt of hosting and maintaining the website is too much in the long term. For a few examples, there is the domain name itself to handle and the tricky DNS settings to go along with it. There is choosing and setting up web hosting, which often requires more long-term maintenance than many folks would like. There are SSL certificates that need to be handled and renewed. You’d better make sure security and backups and handled well, lest you risk the entire site.

Lots of stuff to think about!

Building and working on a website is hard enough without all this stress. These things are even hard enough work for seasoned web people, and often just too much entirely for people, projects, and companies that just want a dang website.

You know what? Do it on WordPress.com and worry about nothing. Just build your website and know that a great company has got your back on everything else.

To be clear, I’ve been working on websites for decades. I know a lot about what it takes to run them and what can break them. To anyone that wants to learn those things too, that’s great. I would never try to that away from anybody. And there are plenty of projects out there that need to do things that WordPress.com can’t do. But there are also a lot more projects out there suffering from forgotten web chores and abandoned responsibilities that would be and could be happily chugging along on WordPress.com.

Signing up for a WordPress.com site is not just easy, but even includes a free tier. It's pretty incredible how quick it is to get a site online. And, if you're at all familiar with publishing content on WordPress, then you know how simple it is to start cranking out content — and if you're new to WordPress, well, you're in for a treat because the editing experience is just plain delightful, especially with the new Gutenberg interface.

So, yes, regardless your skill level, type or business, team, or whatever, WordPress.com is an excellent resource and is the right call. Does it fit all use cases? No, but nothing does. I like the idea of choosing the right tool for the job and WordPress.com can certainly be the right choice for a good number of projects.

Oh and hey, as chance has it, the awesome WordPress Jetpack plugin happens to be having a 20% promotion. That's pretty awesome. If you've been following us for some time, then you know that we love Jetpack and use it right here on CSS-Tricks — from comment moderation to image optimization to social integrations and many things in between. Use coupon code JPSAVE20 at checkout.

The post If you can build a site with WordPress.com, you should build your site on WordPress.com. appeared first on CSS-Tricks.

Read Full Article
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

In a previous post, we saw how to manage state using Unstated. As you might recall, Unstated uses React’s built-in setState to allow you create components that can consume state by subscribing to a provider — like the React’s Context API.

Well, we’re going to build off that last post by looking at Unstated Next, a library that author Jamie Kyle identifies as the “spiritual successor" to his Unstated project. Unstated Next provides both React Hooks and the Context API for managing state. Unstated was a minimal abstraction to the idea of React Hooks before they were a fully-fledged thing. But now that Hooks in React are so good, that abstraction is unnecessary and Unstated Next simply incorporates them while providing an API to share state and logic with the Hooks.

We’re going to specifically look at how to manage state in both single and multiple components using Unstated Next. It might be helpful to check out the previous post on Unstated prior to moving ahead, but it’s not totally necessary.

Example: A minimal form component

To start off, we’ll create a tiny React application for a form that merely contains a text input for a person’s name and a button to submit it. When the button is clicked, we’ll display the name as a paragraph element above the form. The source code for this example is available on GitHub.

This is going to be a Bootstrap React application which we can spin up by using Create React App. Let’s install that and then change directories into the project folder.

npx create-react-app unstated-next-form
cd unstated-next-form>

We need to add Unstated Next as a dependency:

## yarn
yarn add unstated-next

## npm
npm install --save unstated-next

We’ll be making use of React Hooks and createContainer from Unstated Next, so let’s import those into the App component:

// src/App.js
import React, { useState } from 'react';
import { createContainer } from "unstated-next";

Next, we will create a custom hook. We’ll have our state in it, which we can create using useState:

// src/App.js
// ...same as before

const useForm = () => {
  const [input, setValue] = useState("");
  const [name, setName] = useState("Barney Stinson");

  const handleInput = event => {
    setValue(event.target.value);
  };

  const updateName = event => {
    event.preventDefault();
    setName(input);
    setValue("");
  };

  return {
    input,
    name,
    handleInput,
    updateName,
  };
};

We have two states defined here. input will be used for keeping track of values entered into the text input and it will be updated using the handleInput method. name will be updated when the button is clicked, which will trigger the updateName method.

OK, now we can create a container by passing our custom hook as a parameter to the createContainer() method.

// src/App.js
// ...same as before

const FormContainer = createContainer(useForm);

This will create a container which we can use across our application. Yeah, you read that right, but let’s take one step at a time. We’re starting with this one component to see how it works with Unstated Next.

Now, let’s create a Form component that looks like this.

// src/App.js
// ...same as before

const Form = () => {
  const form = FormContainer.useContainer();
  return (
    <div>
      <p>Hello! {form.name}</p>
      <div>
        <input
          type="text"
          value={form.input}
          onChange={form.handleInput}
        />
        <button onClick={form.updateName}>Save</button>
      </div>
    </div>
  );
};

We’re assigning the variable form to the value obtained from calling FormContainer.useContainer(). The value contains the states and methods defined in the custom hook we created above. With that, we can make use of the state and methods provided — but for that to happen, we have to wrap the Form component in a provider.

const App = () => (
  <Form.Provider>
    <Form />
  </Form.Provider>
)

With what you have learned so far, try building a minimal to-do application using Unstated Next. If you get stuck, feel free to check this repository to see how I made mine.

Example: Sharing state across multiple components

OK, so you got a hint earlier that we can use our form container anywhere we’d like. One of the benefits of using Unstated Next is that it makes it possible to share state across multiple components. To see how this works, we’ll build a tiny app that uses the form features we made above and also makes it possible to create to-do tasks using the same state. The name of the user can be updated in the form component, and this update will also be reflected in the to-do component. Two birds of a feather!

There’s a repo for this example as well, so feel free to clone or download it as we plow ahead.

Let’s spin up a new project and install the necessary dependencies:

npx create-react-app unstated-next-app
cd unstated-next-app
yarn unstated-next shortid

The state for the application will live in a separate file. We want to have the states for the form and to-do components in the store, and the methods needed for updating them too. Create a store.js file inside the src directory and make it look like this;

// src/store.js
import { useState } from "react";
import shortid from "shortid"
import { createContainer } from 'unstated-next'
export const useStore = () => {
  // Construct a list that contains two default tasks
  const list = [
    { id: 1, title: 'Write code' },
    { id: 2, title: 'Buy milk' }
  ]
  const [input, setValue] = useState("");
  // Let's set a legen -- wait for it -- dary default name that updates on form submit
  const [name, setName] = useState("Barney Stinson");
  const [todos, addTodo] = useState(list);
  const [item, setTodo] = useState("");
  const handleInput = event => {
    setValue(event.target.value);
  };
  const updateName = event => {
    event.preventDefault();
    setName(input);
    setValue("");
  };
  const handleTodo = event => {
    setTodo(event.target.value);
  };
  const handleSubmit = event => {
    event.preventDefault();
    const value = {
      id: shortid.generate(),
      title: item
    }
    addTodo(todos.concat(value));
    setTodo("");
  };
  return {
    input,
    name,
    handleInput,
    updateName,
    todos,
    item,
    handleTodo,
    handleSubmit
  };
}
export const StoreContainer = createContainer(useStore)

We make use of useState() to create the states we need. The methods are defined and all of this happens inside the custom hook, useStore(). We create the StoreContainer and then pass useStore() as a parameter to createContainer(). With that, we can make use of the StoreContainer in the necessary components where want to make use of the state and methods we have defined.

Starting with the form section, create a file called form.js and it should look like what I have below;

// src/form.js
import React from "react";
import { StoreContainer} from "./store";

const FormComponent = () => {
  const form = StoreContainer.useContainer();
  return (
    <div>
      <p>Hello! {form.name}</p>
      <div>
        <input type="text" value={form.input} onChange={form.handleInput} />
        <button onClick={form.updateName}>Change Name</button>
      </div>
    </div>
  );
};
export default FormComponent;

We’re using StoreContainer to access the state and methods we need. We’ll do the same thing for the task component which you can create in a todo.js file.

// src/todo.js
import React from "react";
import { StoreContainer } from "./store";

const TodoComponent = () => {
  const todo = StoreContainer.useContainer();
  return (
    <div>
      <p>Add Todos</p>
      <input type="text" value={todo.item} onChange={todo.handleTodo} />
      <button onClick={todo.handleSubmit}>Add</button>
      <div>
        <p>Dear {todo.name}, here are your current tasks;</p>
        {todo.todos.map((item) => {
          return (
            <ul key={item.id}>
              <li>{item.title}</li>
            </ul>
          );
        })}
      </div>
    </div>
  );
};
export default TodoComponent;

You can see that todo.name can only be updated in the FormComponent. That’s because we need a way to provide the state in both components. That’s why we’re going to turn again to Provider and add one in the App component just like we did in the previous example.

import React from 'react';
import TodoContainer from "./todo";
import FormContainer from "./form";
import { StoreContainer } from "./store"

function App() {
  return (
    <div className="App">
      <StoreContainer.Provider>
        <FormContainer />
        <TodoContainer />
      </StoreContainer.Provider>
    </div>
  );
}
export default App;

There we go! By adding the provider, data can be taken from the form component, stored in the provider, and passed back to the task list. 💥

The post Managing State in React using Unstated-Next appeared first on CSS-Tricks.

Read Full Article
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 
CSS-Tricks by Chris Coyier - 5d ago

I see VuePress just went 1.0. Explained simply, it's a static site generator based on Vue. But of course, you work in Vue, which means you work in components.

All the modern JavaScript frameworks are component-based. Even when they disagree with each other about specific things (like how Svelte requires compilation), they all seem to agree on the model of working in components. React is all components. A popular static site generator for React is Next.js. The Vue version of that is Nuxt.js.

Then there is Gatsby which is all React. (Listen to our latest ShopTalk Show as we discuss it.) Gridsome seems like the most 1-to-1 comparison in Vue-land, the notable comparison being how they both are designed to suck in data from any source. Components though, of course. I'm not sure there is a flagship Angular-based static site generator, but they are out there, and Angular is components all the way down.

Components are so ubiquitous that perhaps you don't even think about it anymore. But you might feel it, particularly if you jump back and forth between projects that aren't component-driven. WordPress development, generally, I feel, isn't component driven. Sure, you've got your header.php and footer.php files and such. You can break those apart however you want, but it's rather ad-hoc. You aren't explicitly building components and feeding those components local data and testing them as such. (You can get a lot closer with something like Timber.)

Building front-ends out of server-side code is absolutely fine. Server-side rendering is rife with advantages. But server-side languages don't seem to have embraced components the way JavaScript has. And since everyone seems to like components (front-end devs obviously love it, designers think that way anyway, back-end devs understand it...) it's no surprise to me to see this surge of beloved projects build server-side (or build-time) generated sites from JavaScript, simply because it's component-based and components are just a good idea.

The post Components, yo. appeared first on CSS-Tricks.

Read Full Article
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

Šime posts regular content for web developers on webplatform.news.

In this week's roundup, Chrome is adding an install option for Progressive Web Apps, Opera GX comes to Windows, the ECMAScript proposals get an update, and CSS Scroll Snap is coming to a Firefox browser near you.

An install icon is coming to Chrome on desktop

Pete LePage: The next version of Chrome will automatically show an install icon in the address bar on desktop if the site meets Chrome’s PWA "installability" criteria. You can listen for the appinstalled event to detect if the user installed your PWA.

Opera GX is available on Windows

Maciej Kocemba: The preview version of Opera GX for Windows is now available. This is a special version of Opera that lets users limit how much CPU and RAM is available to the browser.

Updated ECMAScript proposals

Azu The JavaScript optional chaining operator (obj?.prop) and null-ish coalescing operator (x ?? y) proposals have been moved to Stage 2 of the TC39 process. (See Web Platform News issue 902 for more information about the TC39 process.)

// BEFORE
let text = response.settings && response.settings.headerText;
if (text == null) text = "Hello, world!";

// AFTER
let text = response.settings?.headerText ?? "Hello, world!";
CSS Scroll Snap is coming to Firefox

Šime Vidas: CSS Scroll Snap is supported in Chrome, Safari, and the next version of Firefox. Scroll snapping works well on touch screen devices but there are some usability issues on desktop platforms.

The post Weekly Platform News: CSS Scroll Snap, Opera GX, PWA Install Icon appeared first on CSS-Tricks.

Read Full Article
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

Greek mythology tells the story of Zeus creating the cloud nymph, Nephele. Like other Greek myths, this tale gets pretty bizarre and X-rated. Here’s a very abridged, polite version.

Nephele, we are told, was created by Zeus in the image of his own beautiful wife. A mortal meets Nephele, falls in love with her and, together, they take an adult nap™. Finally, in a strange twist, the cloud gives birth to half-human half-horse Centaur babies.

Weird, right? Personally, I can’t make heads or tails of it. Thankfully, the process for creating clouds in the browser is much more straightforward and far less risqué.

Yuan Chuan’s clouds detail. (Demo)

Recently, I discovered that developer Yuan Chuan has realized code-generated, photorealistic clouds. For me, this notion in the browser had long been the stuff of myth.

With one glance at the code in this pen we can imagine that convincing individual clouds are achievable through the use of CSS box-shadow with a <filter> element containing two SVG filters as its complement.

The photorealism we want is achieved with a delicate mix of feTurbulence and feDisplacementMap. These SVG filters are powerful, complex and offer very exciting features (including an Oscar winning algorithm)! However, under the hood, their complexity can be a bit intimidating.

While the physics of SVG filters is beyond the scope of this article, there is ample documentation available on MDN and w3.org. A very informative page on feTurbulence and feDisplacement is freely available (and offered as a chapter of this amazing book).

For this article, we will focus on learning to use these SVG filters to get spectacular results. We don’t need to delve too deeply into what is happening behind the scenes algorithmically, much in the way an artist isn’t required know the molecular structure of paint to render a stunning landscape.

Instead, let's pay close attention to small handful of SVG attributes that are essential for drawing convincing clouds in the browser. Their use will enable us to bend these powerful filters to our will and learn how to customize them with precision in our own projects.

Let’s start with some basics

The CSS box-shadow property has five values that deserve close attention:

box-shadow: <offsetX> <offsetY> <blurRadius> <spreadRadius> <color>;

Let’s crank these values up (probably higher than what any sane developer would so that this shadow becomes a player on the stage in its own right.

(Demo)
#cloud-square {
  background: turquoise;
  box-shadow: 200px 200px 50px 0px #000;
  width: 180px;
  height: 180px;
}

#cloud-circle {
  background: coral;
  border-radius: 50%;
  box-shadow: 200px 200px 50px 0px #000;
  width: 180px;
  height: 180px;
}

You’ve either made or seen shadow puppets, right?

Credit: Double-M

In the same way that a hand changes shape to alter the shadow, a "source shape" in the our HTML can move and morph to move and alter the shape of a shadow rendered in the browser. box-shadow duplicates the "morphing" features on the original size and border-radius. SVG filters get applied to both the element and its shadow.

<svg width="0" height="0"> 
  <filter id="filter">
    <feTurbulence type="fractalNoise" baseFrequency=".01" numOctaves="10" />
    <feDisplacementMap in="SourceGraphic" scale="10" />
  </filter>
</svg>

This is the markup for our SVG so far. It won’t render because we haven’t defined anything visual (not to mention the zero width and height). It’s sole purpose is to hold a filter that we feed our SourceGraphic (aka our <div>). Our source <div> and its shadow are both being distorted independently by the filter.

We’ll add the essential CSS rule linking the HTML element (`#cloud-circle`) to the SVG filter using its ID:

#cloud-circle {
  filter: url(#filter);
  box-shadow: 200px 200px 50px 0px #fff;
}
Et Voilà!

OK, so admittedly, adding the SVG filter is pretty underwhelming.

(Demo)

No worries! We have only just scratched the surface and have a lot more good stuff to look at.

Experimenting with the feDisplacementMap scale attribute

A few un-scientific experiments with this one attribute can yield dramatic results. For the moment, let’s keep the all values in feTurbulence constant and simply adjust the scale attribute of DisplacementMap.

As scale increases (by increments of 30) our source <div> becomes distorted and casts a shadow to mirror the stochastic form in which clouds appear in the sky.

<feDisplacementMap in="SourceGraphic" scale="180"/>
The scale attribute incremented by values of 30. (Demo)

OK, we’re getting somewhere! Let’s change the colors a bit to produce a more convincing cloud and to "sell" the effect.

body {
  background: linear-gradient(165deg, #527785 0%, #7FB4C7 100%);
}

#cloud-circle {
    width: 180px;
    height: 180px;
    background: #000;
    border-radius: 50%;
    filter: url(#filter);
    box-shadow: 200px 200px 50px 0px #fff;
}

Now we’re getting closer to a realistic cloud effect!

Modifying the box-shadow blur value

The following suite of images shows the influence that the blur value has on box-shadow. Here, blur is increased by 10 pixels incrementally.

The cloud becomes "softer" as the blur value increases.

To give our cloud a bit of a cumulus-like effect, we can widen our source <div> a bit.

#cloud-circle {
  width: 500px;
  height: 275px;
  background: #000;
  border-radius: 50%;
  filter: url(#filter);
  box-shadow: 200px 200px 60px 0px #fff;
}
Great, now the source element is getting in the way. 😫

Wait! We’ve widened the source element and now it’s in the way of our of the white shadow we’re calling a cloud. Let’s "re-cast" the shadow at a greater distance so that our cloud is no longer obscured by the source image. (Think of this as moving your hand away further from the wall so it doesn’t block the view of your shadow puppet.)

This is nicely achieved with a bit of CSS positioning. The <body> is the parent element for our cloud, which is statically positioned by default. Let’s "tuck" our source <div> up and out of the way with some absolute positioning. Initially, that will reposition our shadow as well, so we’ll also need to increase the distance of the shadow from the element and nudge the element a bit more.

#cloud-circle {
  width: 500px;
  height: 275px;
  background: #000;
  border-radius: 50%;
  filter: url(#filter);
  box-shadow: 400px 400px 60px 0px #fff; /* Increase shadow offset */
  position: absolute; /* Take the parent out of the document flow */
  top: -320px; /* Move a little down */
  left: -320px; /* Move a little right */
}

Yes! We've arrived at a pretty persuasive cloud.

See the Pen
by Beau Haus (@beauhaus)
on CodePen.

What is painted to the browser is a pretty decent depiction of a cloud–But, I’m not sure…does this cloud really do justice the cloud nymph, Nephele? I'm sure we can do better!

Conveying depth with layers

Here’s what we want:

Credit: pcdazero

From the look of the depth, texture and richness of the clouds in this photograph, one thing is clear: Zeus went to art school. At the very least, he must have read the The Universal Principles of Design which illustrates a powerful–yet, deceptively ordinary–concept:

[...] lighting bias plays a significant role in the interpretation of depth and naturalness, and can be manipulated in a variety of ways by designers...Use the level of contrast between light and dark areas to vary the appearance of depth.

This passage provides for us a hint as to how to we can vastly improve our own code-generated cloud. We can render our cloud with a good deal of fidelity to the clouds in our reference image by stacking layers of differing form, size and color on top of each other. All that takes is calling our filter as many times as we want layers.

<svg width="0" height="0">
    <!-- Back Layer -->
    <filter id="filter-back">
      <feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="4" />
      <feDisplacementMap  in="SourceGraphic" scale="170" />
    </filter>
    <!-- Middle Layer -->
    <filter id="filter-mid">
      <feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="2" />
      <feDisplacementMap  in="SourceGraphic" scale="150" />
    </filter>
    <!-- Front Layer -->
    <filter id="filter-front">
      <feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="2" />
      <feDisplacementMap  in="SourceGraphic" scale="100" />
    </filter>
</svg>

Applying our layers will afford us an opportunity to explore feTurbulence and realize its versatility. We’ll choose the smoother type available to us: fractalNoise with numOctaves cranked up to 6.

<feTurbulence type="fractalNoise" baseFrequency="n" numOctaves="6"/>

What does all that mean? For now, let’s focus specifically on the baseFrequency attribute. Here’s what we get as we increase the value of n:

The lower the value, the rounder and fuzzier we get. The higher the value, the rounder and more rigid we get.

Words like turbulence, noise, frequency, and octave may seem odd and even confusing. But fear not! It’s actually perfectly accurate to analogize this filter’s effects to sound waves. We may equate a low frequency (baseFrequency=0.001) with a low, muffled noise and a rising frequency (baseFrequency=0.1) with a higher, crisper pitch.

We can see that our sweet spot for a cumulus-like effect may lie comfortably around the ~0.005 and ~0.01 range for the baseFrequency.

Adding detail with numOctaves

Incrementing numOctaves allows us to render our image in extremely granular detail. This requires a great deal of calculation, so be warned: high values are a significant performance hit. Try to resist the temptation to pump up this value unless your browser is wearing a helmet and knee-pads.

The higher the value we put into numOctaves the more granular detail give to our cloud.

The good news is that we don’t have to crank this value too high in order to produce detail and delicacy. As the array of images above shows, we can satisfy ourselves with a numOctavesvalue of 4 or 5.

Here’s the result

See the Pen
by Beau Haus (@beauhaus)
on CodePen.

Infinite variety with the seed attribute

There is much to say about the seed attribute as it offers a hint into the magic happening behind the scenes. But, for our purposes, the utility of seed can be reduced to four words: "different value, different shape."

The Perlin Noise function (mentioned earlier) uses this value as the starting point for its random number generator. Choosing not to include this attribute will default seed to zero. When included, however, whatever value we give seed, we don’t need to worry about a performance hit.

Different seed values produce different shapes.

The GIF above represents some of what seed has to offer. Keep in mind that each of those clouds is a layered, composite cloud. (While I have tweaked attributes for each layer, I have kept their respective seed values uniform.)

Credit: Brockenhexe

Here, with a close look at the reference image, I've layered 3 cloud-<div>s (of differing in opacity) onto a single base div. Through trial and error and punching in arbitrary seed values, I eventually arrived at a shape resembling the shape of the cloud in the photograph.

Read Full Article
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 
CSS-Tricks by Chris Coyier - 6d ago

Lindsey Kopacz has a wonderful blog about accessibility. I've seen a number of her articles making the rounds lately and I was like, dang I better make sure I'm subscribed. For example:

Regarding that last one, I remember learning from Sara Soueidan that a good tip for this to position them over the new custom checkboxes and hide them via opacity instead of hiding the native checkboxes by clipping them away. That covers the scenario of people exploring a touch screen for native interactive elements.

The post A11Y with Lindsey appeared first on CSS-Tricks.

Read Full Article
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

Take this:

<ol>
  <li>Get hungry</li>
  <li>Order pizza</li>
  <li>Eat pizza</li>
</ol>

That HTML ends up in the DOM that way (and thus how it is is exposed to assistive technology), and by default, those list items are also visually shown in that order. In most layout situations, the visual order will match that DOM order. Do nothing, and the list items will flow in the block direction of the document. Apply flexbox, and it will flow in the inline direction of the document.

But flexbox and grid also allow you to muck it up. Now take this:

ol { 
  display: flex;
  flex-direction: row-reverse;
}

In this case, the DOM order still makes sense, but the visual order is all wrong. It's not just row-reverse. There are a number of flexbox and grid properties that can get involved and confuse things: the order property, flowing items into columns instead of rows, and positioning items specifically in unusual orders, among others. Even absolute positioning could cause the same trouble.

Manuel Matuzovic says:

If the visual order and the DOM order don’t match, it can irritate and confuse users up to a point where the experience is so bad that the site is unusable.

Rachel Andrew highlights this issue (including things we've published) as a big issue, and hopes we can get tools at the CSS level to help.

I think this is something we sorely need to address at a CSS level. We need to provide a way to allow the tab and reading order to follow the visual order. Source order is a good default, if you are taking advantage of normal flow, a lot of the time following the source is exactly what you want. However not always, not at every breakpoint. If we don’t give people a solution for this, we will end up with a mess. We’ve given people these great tools, and now I feel as if I’m having to tell people not to use them.

Direct Link to Article

The post Grid, content re-ordering and accessibility appeared first on CSS-Tricks.

Read Full Article
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

End-to-end tests are written to assert the flow of an application from start to finish. Instead of handling the tests yourself — you know, manually clicking all over the application — you can write a test that runs as you build the application. That’s what we call continuous integration and it’s a beautiful thing. Write some code, save it, and let tooling do the dirty work of making sure it doesn’t break anything.

>Cypress is just one end-to-end testing framework that does all that clicking work for us and that’s what we’re going to look at in this post. It’s really for any modern JavaScript library, but we’re going to integrate it with React in the examples.

Let’s set up an app to test

In this tutorial, we will write tests to cover a todo application I’ve built. You can clone the repository to follow along as we plug it into Cypress.

git clone git@github.com:kinsomicrote/cypress-react-tutorial.git

Navigate into the application, and install the dependencies:

cd cypress-react-tutorial
yarn install

Cypress isn’t part of the dependencies, but you can install it by running this:

yarn add cypress --dev

Now, run this command to open Cypress:

node_modules/.bin/cypress open

Typing that command to the terminal over and over can get exhausting, but you can add this script to the package.json file in the project root:

"cypress": "cypress open"

Now, all you have to do is do npm run cypress once and Cypress will be standing by at all times. To have a feel of what the application we’ll be testing looks like, you can start the React application by running yarn start.

We will start by writing a test to confirm that Cypress works. In the cypress/integration folder, create a new file called init.spec.js. The test asserts that true is equal to true. We only need it to confirm that’s working to ensure that Cypress is up and running for the entire application.

describe('Cypress', () => {
  it('is working', () => {
    expect(true).to.equal(true)
  })
})

You should have a list of tests open. Go there and select init.spec.js.

That should cause the test to run and pop up a screen that shows the test passing.

While we’re still in init.spec.js, let’s add a test to assert that we can visit the app by hitting http://localhost:3000 in the browser. This’ll make sure the app itself is running.

it('visits the app', () => {
  cy.visit('http://localhost:3000')
})

We call the method visit() and we pass it the URL of the app. We have access to a global object called cy for calling the methods available to us on Cypress.

To avoid having to write the URL time and again, we can set a base URL that can be used throughout the tests we write. Open the cypress.json file in the home directory of the application and add define the URL there:

{
  "baseUrl": "http://localhost:3000"
}

You can change the test block to look like this:

it('visits the app', () => {
  cy.visit('/')
})

...and the test should continue to pass. 🤞

Testing form controls and inputs

The test we’ll be writing will cover how users interact with the todo application. For example, we want to ensure the input is in focus when the app loads so users can start entering tasks immediately. We also want to ensure that there’s a default task in there so the list is not empty by default. When there are no tasks, we want to show text that tells the user as much.

To get started, go ahead and create a new file in the integration folder called form.spec.js. The name of the file isn’t all that important. We’re prepending "form" because what we’re testing is ultimately a form input. You may want to call it something different depending on how you plan on organizing tests.

We’re going to add a describe block to the file:

describe('Form', () => {
  beforeEach(() => {
    cy.visit('/')
  })

  it('it focuses the input', () => {
    cy.focused().should('have.class', 'form-control')
  })
})

The beforeEach block is used to avoid unnecessary repetition. For each block of test, we need to visit the application. It would be redundant to repeat that line each time beforeEach ensures Cypress visits the application in each case.

For the test, let’s check that the DOM element in focus when application first loads has a class of form-control. If you check the source file, you will see that the input element has a class called form-control set to it, and we have autoFocus as one of the element attributes:

<input
  type="text"
  autoFocus
  value={this.state.item}
  onChange={this.handleInputChange}
  placeholder="Enter a task"
  className="form-control"
/>

When you save that, go back to the test screen and select form.spec.js to run the test.

The next thing we’ll do is test whether a user can successfully enter a value into the input field.

it('accepts input', () => {
  const input = "Learn about Cypress"
  cy.get('.form-control')
    .type(input)
    .should('have.value', input)
})

We’ve added some text ("Learn about Cypress") to the input. Then we make use of cy.get to obtain the DOM element with the form-control class name. We could also do something like cy.get('input') and get the same result. After getting the element, cy.type() is used to enter the value we assigned to the input, then we assert that the DOM element with class form-control has a value that matches the value of input.

In other words:

Our application should also have two todos that have been created by default when the app runs. It’s important we have a test that checks that they are indeed listed.

What do we want? In our code, we are making use of the list item (<li>) element to display tasks as items in a list. Since we have two items listed by default, it means that the list should have a length of two at start. So, the test will look something like this:

it('displays list of todo', () => {
  cy.get('li')
    .should('have.length', 2)
})

Oh! And what would this app be if a user was unable to add a new task to the list? We’d better test that as well.

it('adds a new todo', () => {
  const input = "Learn about cypress"
  cy.get('.form-control')
    .type(input)
    .type('{enter}')
    .get('li')
    .should('have.length', 3)
})

This looks similar to what we wrote in the last two tests. We obtain the input and simulate typing a value into it. Then, we simulate submitting a task that should update the state of the application, thereby increasing the length from 2 to 3. So, really, we can build off of what we already have!

Changing the value from three to two will cause the test to fail — that’s what we’d expect because the list should have two tasks by default and submitting once should produce a total of three.

You might be wondering what would happen if the user deletes either (or both) of the default tasks before attempting to submit a new task. Well, we could write a test for that as well, but we’re not making that assumption in this example since we only want to confirm that tasks can be submitted. This is an easy way for us to test the basic submitting functionality as we develop and we can account for advanced/edge cases later.

The last feature we need to test is the deleting tasks. First, we want to delete one of the default task items and then see if there is one remaining once the deletion happens. It’s the same sort of deal as before, but we should expect one item left in the list instead of the three we expected when adding a new task to the list.

it('deletes a todo', () => {
  cy.get('li')
    .first()
    .find('.btn-danger')
    .click()
    .get('li')
    .should('have.length', 1)
})

OK, so what happens if we delete both of the default tasks in the list and the list is completely empty? Let’s say we want to display this text when no more items are in the list: "All of your tasks are complete. Nicely done!"

This isn’t too different from what we have done before. You can try it out first then come back to see the code for it.

it.only('deletes all todo', () => {
  cy.get('li')
    .first()
    .find('.btn-danger')
    .click()
    .get('li')
    .first()
    .find('.btn-danger')
    .click()
    .get('.no-task')
    .should('have.text', 'All of your tasks are complete. Nicely done!')
})

Both tests look similar: we get the list item element, target the first one, and make use of cy.find() to look for the DOM element with a btn-danger class name (which, again, is a totally arbitrary class name for the delete button in this example app). We simulate a click event on the element to delete the task item.

I’m checking for "No task!" in this particular test. Testing network requests

Network requests are kind of a big deal because that’s often the source of data used in an application. Say we have a component in our app that makes a request to the server to obtain data which will be displayed to user. Let’s say the component markup looks like this:

class App extends React.Component {
  state = {
    isLoading: true,
    users: [],
    error: null
  };
  fetchUsers() {
    fetch(`https://jsonplaceholder.typicode.com/users`)
      .then(response => response.json())
      .then(data =>
        this.setState({
          users: data,
          isLoading: false,
        })
      )
      .catch(error => this.setState({ error, isLoading: false }));
  }
  componentDidMount() {
    this.fetchUsers();
  }
  render() {
    const { isLoading, users, error } = this.state;
    return (
      <React.Fragment>
        <h1>Random User</h1>
        {error ? <p>{error.message}</p> : null}
        {!isLoading ? (
          users.map(user => {
            const { username, name, email } = user;
            return (
              <div key={username}>
                <p>Name: {name}</p>
                <p>Email Address: {email}</p>
                <hr />
              </div>
            );
          })
        ) : (
          <h3>Loading...</h3>
        )}
      </React.Fragment>
    );
  }
}

Here, we are making use of the JSON Placeholder API as an example. We can have a test like this to test the response we get from the server:

describe('Request', () => {
  it('displays random users from API', () => {
    cy.request('https://jsonplaceholder.typicode.com/users')
      .should((response) => {
        expect(response.status).to.eq(200)
        expect(response.body).to.have.length(10)
        expect(response).to.have.property('headers')
        expect(response).to.have.property('duration')
      })
  })
})

The benefit of testing the server (as opposed to stubbing it) is that we are certain the response we get is the same as that which a user will get. To learn more about network requests and how you can stub network requests, see this page in the Cypress documentation.

Running tests from the command line

Cypress tests can run from the terminal without the provided UI:

./node_modules/.bin/cypress run

...or

npx cypress run

Let’s run the form tests we wrote:

npx cypress run --record --spec "cypress/integration/form.spec.js"

Terminal should output the results right there with a summary of what was tested.

There’s a lot more about using Cypress with the command line in the documentation.

That’s a wrap!

Tests are something that either gets people excited or scared, depending on who you talk to. Hopefully what we’ve looked at in this post gets everyone excited about implementing tests in an application and shows how relatively straightforward it can be. Cypress is an excellent tool and one I’ve found myself reaching for in my own work, but there are others as well. Regardless of what tool you use (and how you feel about tests), hopefully you see the benefits of testing and are more compelled to give them a try.

Related resources

The post Using Cypress to Write Tests for a React Application appeared first on CSS-Tricks.

Read Full Article

Read for later

Articles marked as Favorite are saved for later viewing.
close
  • Show original
  • .
  • Share
  • .
  • Favorite
  • .
  • Email
  • .
  • Add Tags 

Separate tags by commas
To access this feature, please upgrade your account.
Start your free month
Free Preview