Category: Design

On the `dl`

Blogging about HTML elements¹? *chefs kiss*

Here’s Ben Myers on the (aptly described) “underrated” Definition List (<dl>) element in HTML:

You might have also seen lists of name–value pairs to describe lodging amenities, or to list out individual charges in your monthly rent, or in glossaries of technical terms. Each of these is a candidate to be represented with the <dl> element.

Element
Definition List
Coolness factor
10/10
Versatility
7/10

Ben says he’s satisfied with HTML semantics, even when the benefits of using them are theoretical. But in the case of <dl>, there are at least some tangible screen reader benefits, like the fact that the number of items in the list is announced, as expected (for the most part), like ordered and unordered lists. Although that makes you curious what number it announces, doesn’t it? Is it the number of children, regardless of type? Just the <dt> elements?

Speaking of children, this might look weird:

<dl>   <div>     <dt>Title</dt>     <dd>Designing with Web Standards</dd>   </div>   <div>     <dt>Author</dt>     <dd>Jeffrey Zeldman</dd>     <dd>Ethan Marcotte</dd>   </div>   <div>     <dt>Publisher</dt>     <dd>New Riders Pub; 3rd edition (October 19, 2009)</dd>   </div> </dl>

But those intermediary <div>s that group things together are cool now. They’re awfully handy when you want to style the groupings as “rows” or do something like add a border below each group. No <div>s for ordered or unordered list though, just definition lists. Lucky sacks. What’s next? Is <hgroup> gonna make a comeback?

  1. I remember Jen Kramer did 30 days of HTML not long ago, and that was fun.

The post On the `dl` appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

Building a Form in PHP Using DOMDocument

Templating makes the web go round. The synthesis of data and structure into content. It’s our coolest superpower as developers — grab some data, then make it work for us, in whatever presentation we need. An array of objects can become a table, a list of cards, a chart, or whatever we think is most useful to the user. Whether the data is our own blog posts in Markdown files, or on-the-minute global exchange rates, the markup and resulting UX are up to us as front-end developers.

PHP is an amazing language for templating, providing many ways to merge data with markup. Let’s get into an example of using data to build out an HTML form in this post.

Want to get your hands dirty right away? Jump to the implementation.

In PHP, we can inline variables into string literals that use double quotes, so if we have a variable $ name = 'world', we can write echo "Hello, {$ name}", and it prints the expected Hello, world. For more complex templating, we can always concatenate strings, like: echo "Hello, " . $ name . ".".

For the old-schoolers, there’s printf("Hello, %s", $ name). For multiline strings, you can use Heredoc (the one that starts like <<<MYTEXT). And, last but certainly not least, we can sprinkle PHP variables inside HTML, like <p>Hello, <?= $ name ?></p>.

All of these options are great, but things can get messy when a lot of inline logic is required. If we need to build compound HTML strings, say a form or navigation, the complexity is potentially infinite, since HTML elements can nest inside each other.

What we’re trying to avoid

Before we go ahead and do the thing we want to do, it’s worth taking a minute to consider what we don’t want to do. Consider the following abridged passage from the scripture of WordPress Core, class-walker-nav-menu.php, verses 170-270:

<?php // class-walker-nav-menu.php // ... $  output .= $  indent . '<li' . $  id . $  class_names . '>'; // ... $  item_output  = $  args->before; $  item_output .= '<a' . $  attributes . '>'; $  item_output .= $  args->link_before . $  title . $  args->link_after; $  item_output .= '</a>'; $  item_output .= $  args->after; // ... $  output .= apply_filters( 'walker_nav_menu_start_el', $  item_output, $  item, $  depth, $  args ); // ... $  output .= "</li>{$  n}";

In order to build out a navigation <ul> in this function, we use a variable, $ output, which is a very long string to which we keep adding stuff. This type of code has a very specific and limited order of operations. If we wanted to add an attribute to the <a>, we must have access to $ attributes before this runs. And if we wanted to optionally nest a <span> or an <img> inside the <a>, we’d need to author a whole new block of code that would replace the middle of line 7 with about 4-10 new lines, depending on what exactly we want to add. Now imagine you need to optionally add the <span> , and then optionally add the <img>, either inside the <span> or after it. That alone is three if statements, making the code even less legible.

It’s very easy to end up with string spaghetti when concatenating like this, which is as fun to say as it is painful to maintain.

The essence of the problem is that when we try to reason about HTML elements, we’re not thinking about strings. It just so happens that strings are what the browser consumes and PHP outputs. But our mental model is more like the DOM — elements are arranged into a tree, and each node has many potential attributes, properties, and children.

Wouldn’t it be great if there were a structured, expressive way to build our tree?

Enter…

The DOMDocument class

PHP 5 added the DOM module to it’s roster of Not So Strictly Typed™ types. Its main entry point is the DOMDocument class, which is intentionally similar to the Web API’s JavaScript DOM. If you’ve ever used document.createElement or, for those of us of a certain age, jQuery’s $ ('<p>Hi there!</p>') syntax, this will probably feel quite familiar.

We start out by initializing a new DOMDocument:

$  dom = new DOMDocument();

Now we can add a DOMElement to it:

$  p = $  dom->createElement('p');

The string 'p' represents the type of element we want, so other valid strings would be 'div', 'img' , etc.

Once we have an element, we can set its attributes:

$  p->setAttribute('class', 'headline');

We can add children to it:

$  span = $  dom->createElement('span', 'This is a headline'); // The 2nd argument populates the element's textContent $  p->appendChild($  span);

And finally, get the complete HTML string in one go:

$  dom->appendChild($  p); $  htmlString = $  dom->saveHTML(); echo $  htmlString;

Notice how this style of coding keeps our code organized according to our mental model — a document has elements; elements can have any number of attributes; and elements nest inside one another without needing to know anything about each other. The whole “HTML is just a string” part comes in at the end, once our structure is in place.

The “document” here is a bit different from the actual DOM, in that it doesn’t need to represent an entire document, just a block of HTML. In fact, if you need to create two similar elements, you could save a HTML string using saveHTML(), modify the DOM “document” some more, and then save a new HTML string by calling saveHTML() again.

Getting data and setting the structure

Say we need to build a form on the server using data from a CRM provider and our own markup. The API response from the CRM looks like this:

{   "submit_button_label": "Submit now!",   "fields": [     {       "id": "first-name",       "type": "text",       "label": "First name",       "required": true,       "validation_message": "First name is required.",       "max_length": 30     },     {       "id": "category",       "type": "multiple_choice",       "label": "Choose all categories that apply",       "required": false,       "field_metadata": {         "multi_select": true,         "values": [           { "value": "travel", "label": "Travel" },           { "value": "marketing", "label": "Marketing" }         ]       }     }   ] }

This example doesn’t use the exact data structure of any specific CRM, but it’s rather representative.

And let’s suppose we want our markup to look like this:

<form>   <label class="field">     <input type="text" name="first-name" id="first-name" placeholder=" " required>     <span class="label">First name</span>     <em class="validation" hidden>First name is required.</em>   </label>   <label class="field checkbox-group">     <fieldset>       <div class="choice">         <input type="checkbox" value="travel" id="category-travel" name="category">         <label for="category-travel">Travel</label>       </div>       <div class="choice">         <input type="checkbox" value="marketing" id="category-marketing" name="category">         <label for="category-marketing">Marketing</label>       </div>     </fieldset>     <span class="label">Choose all categories that apply</span>   </label> </form>

What’s that placeholder=" "? It’s a small trick that allows us to track in CSS whether the field is empty, without needing JavaScript. As long as the input is empty, it matches input:placeholder-shown, but the user doesn’t see any visible placeholder text. Just the kind of thing you can do when we control the markup!

Now that we know what our desired result is, here’s the game plan:

  1. Get the field definitions and other content from the API
  2. Initialize a DOMDocument
  3. Iterate over the fields and build each one as required
  4. Get the HTML output

So let’s stub out our process and get some technicalities out of the way:

<?php function renderForm ($  endpoint) {   // Get the data from the API and convert it to a PHP object   $  formResult = file_get_contents($  endpoint);   $  formContent = json_decode($  formResult);   $  formFields = $  formContent->fields;    // Start building the DOM   $  dom = new DOMDocument();   $  form = $  dom->createElement('form');    // Iterate over the fields and build each one   foreach ($  formFields as $  field) {     // TODO: Do something with the field data   }    // Get the HTML output   $  dom->appendChild($  form);   $  htmlString = $  dom->saveHTML();   echo $  htmlString; }

So far, we’ve gotten the data and parsed it, initialized our DOMDocument and echoed its output. What do we want to do for each field? First off, let’s build the container element which, in our example, should be a <label>, and the labelling <span> which is common to all field types:

<?php // ... // Iterate over the fields and build each one foreach ($  formFields as $  field) {   // Build the container `<label>`   $  element = $  dom->createElement('label');   $  element->setAttribute('class', 'field');    // Reset input values   $  label = null;    // Add a `<span>` for the label if it is set   if ($  field->label) {     $  label = $  dom->createElement('span', $  field->label);     $  label->setAttribute('class', 'label');   }    // Add the label to the `<label>`   if ($  label) $  element->appendChild($  label); }

Since we’re in a loop, and PHP doesn’t scope variables in loops, we reset the $ label element on each iteration. Then, if the field has a label, we build the element. At the end, we append it to the container element.

Notice that we set classes using the setAttribute method. Unlike the Web API, there unfortunately is no special handing of class lists. They’re just another attribute. If we had some really complex class logic, since It’s Just PHP™, we could create an array and then implode it:
$ label->setAttribute('class', implode($ labelClassList)).

Single inputs

Since we know that the API will only return specific field types, we can switch over the type and write specific code for each one:

<?php // ... // Iterate over the fields and build each one foreach ($  formFields as $  field) {   // Build the container `<label>`   $  element = $  dom->createElement('label');   $  element->setAttribute('class', 'field');    // Reset input values   $  input = null;   $  label = null;    // Add a `<span>` for the label if it is set   // ...    // Build the input element   switch ($  field->type) {     case 'text':     case 'email':     case 'telephone':       $  input = $  dom->createElement('input');       $  input->setAttribute('placeholder', ' ');       if ($  field->type === 'email') $  input->setAttribute('type', 'email');       if ($  field->type === 'telephone') $  input->setAttribute('type', 'tel');       break;   } }

Now let’s handle text areas, single checkboxes and hidden fields:

<?php // ... // Iterate over the fields and build each one foreach ($  formFields as $  field) {   // Build the container `<label>`   $  element = $  dom->createElement('label');   $  element->setAttribute('class', 'field');    // Reset input values   $  input = null;   $  label = null;    // Add a `<span>` for the label if it is set   // ...    // Build the input element   switch ($  field->type) {     //...       case 'text_area':       $  input = $  dom->createElement('textarea');       $  input->setAttribute('placeholder', ' ');       if ($  rows = $  field->field_metadata->rows) $  input->setAttribute('rows', $  rows);       break;      case 'checkbox':       $  element->setAttribute('class', 'field single-checkbox');       $  input = $  dom->createElement('input');       $  input->setAttribute('type', 'checkbox');       if ($  field->field_metadata->initially_checked === true) $  input->setAttribute('checked', 'checked');       break;      case 'hidden':       $  input = $  dom->createElement('input');       $  input->setAttribute('type', 'hidden');       $  input->setAttribute('value', $  field->field_metadata->value);       $  element->setAttribute('hidden', 'hidden');       $  element->setAttribute('style', 'display: none;');       $  label->textContent = '';       break;   } }

Notice something new we’re doing for the checkbox and hidden cases? We’re not just creating the <input> element; we’re making changes to the container <label> element! For a single checkbox field we want to modify the class of the container, so we can align the checkbox and label horizontally; a hidden <input>‘s container should also be completely hidden.

Now if we were merely concatenating strings, it would be impossible to change at this point. We would have to add a bunch of if statements regarding the type of element and its metadata in the top of the block. Or, maybe worse, we start the switch way earlier, then copy-paste a lot of common code between each branch.

And here is the real beauty of using a builder like DOMDocument — until we hit that saveHTML(), everything is still editable, and everything is still structured.

Nested looping elements

Let’s add the logic for <select> elements:

<?php // ... // Iterate over the fields and build each one foreach ($  formFields as $  field) {   // Build the container `<label>`   $  element = $  dom->createElement('label');   $  element->setAttribute('class', 'field');    // Reset input values   $  input = null;   $  label = null;    // Add a `<span>` for the label if it is set   // ...    // Build the input element   switch ($  field->type) {     //...       case 'select':       $  element->setAttribute('class', 'field select');       $  input = $  dom->createElement('select');       $  input->setAttribute('required', 'required');       if ($  field->field_metadata->multi_select === true)         $  input->setAttribute('multiple', 'multiple');            $  options = [];            // Track whether there's a pre-selected option       $  optionSelected = false;            foreach ($  field->field_metadata->values as $  value) {         $  option = $  dom->createElement('option', htmlspecialchars($  value->label));              // Bail if there's no value         if (!$  value->value) continue;              // Set pre-selected option         if ($  value->selected === true) {           $  option->setAttribute('selected', 'selected');           $  optionSelected = true;         }         $  option->setAttribute('value', $  value->value);         $  options[] = $  option;       }            // If there is no pre-selected option, build an empty placeholder option       if ($  optionSelected === false) {         $  emptyOption = $  dom->createElement('option');              // Set option to hidden, disabled, and selected         foreach (['hidden', 'disabled', 'selected'] as $  attribute)           $  emptyOption->setAttribute($  attribute, $  attribute);         $  input->appendChild($  emptyOption);       }            // Add options from array to `<select>`       foreach ($  options as $  option) {         $  input->appendChild($  option);       }   break;   } }

OK, so there’s a lot going on here, but the underlying logic is the same. After setting up the outer <select>, we make an array of <option>s to append inside it.

We’re also doing some <select>-specific trickery here: If there is no pre-selected option, we add an empty placeholder option that is already selected, but can’t be selected by the user. The goal is to place our <label class="label"> as a “placeholder” using CSS, but this technique can be useful for all kinds of designs. By appending it to the $ input before appending the other options, we make sure it is the first option in the markup.

Now let’s handle <fieldset>s of radio buttons and checkboxes:

<?php // ... // Iterate over the fields and build each one foreach ($  formFields as $  field) {   // Build the container `<label>`   $  element = $  dom->createElement('label');   $  element->setAttribute('class', 'field');    // Reset input values   $  input = null;   $  label = null;    // Add a `<span>` for the label if it is set   // ...    // Build the input element   switch ($  field->type) {     // ...       case 'multiple_choice':       $  choiceType = $  field->field_metadata->multi_select === true ? 'checkbox' : 'radio';       $  element->setAttribute('class', "field {$  choiceType}-group");       $  input = $  dom->createElement('fieldset');            // Build a choice `<input>` for each option in the fieldset       foreach ($  field->field_metadata->values as $  choiceValue) {         $  choiceField = $  dom->createElement('div');         $  choiceField->setAttribute('class', 'choice');              // Set a unique ID using the field ID + the choice ID         $  choiceID = "{$  field->id}-{$  choiceValue->value}";              // Build the `<input>` element         $  choice = $  dom->createElement('input');         $  choice->setAttribute('type', $  choiceType);         $  choice->setAttribute('value', $  choiceValue->value);         $  choice->setAttribute('id', $  choiceID);         $  choice->setAttribute('name', $  field->id);         $  choiceField->appendChild($  choice);              // Build the `<label>` element         $  choiceLabel = $  dom->createElement('label', $  choiceValue->label);         $  choiceLabel->setAttribute('for', $  choiceID);         $  choiceField->appendChild($  choiceLabel);              $  input->appendChild($  choiceField);       }   break;   } }

So, first we determine if the field set should be for checkboxes or radio button. Then we set the container class accordingly, and build the <fieldset>. After that, we iterate over the available choices and build a <div> for each one with an <input> and a <label>.

Notice we use regular PHP string interpolation to set the container class on line 21 and to create a unique ID for each choice on line 30.

Fragments

One last type we have to add is slightly more complex than it looks. Many forms include instruction fields, which aren’t inputs but just some HTML we need to print between other fields.

We’ll need to reach for another DOMDocument method, createDocumentFragment(). This allows us to add arbitrary HTML without using the DOM structuring:

<?php // ... // Iterate over the fields and build each one foreach ($  formFields as $  field) {   // Build the container `<label>`   $  element = $  dom->createElement('label');   $  element->setAttribute('class', 'field');    // Reset input values   $  input = null;   $  label = null;    // Add a `<span>` for the label if it is set   // ...    // Build the input element   switch ($  field->type) {     //...       case 'instruction':       $  element->setAttribute('class', 'field text');       $  fragment = $  dom->createDocumentFragment();       $  fragment->appendXML($  field->text);       $  input = $  dom->createElement('p');       $  input->appendChild($  fragment);       break;   } }

At this point you might be wondering how we found ourselves with an object called $ input, which actually represents a static <p> element. The goal is to use a common variable name for each iteration of the fields loop, so at the end we can always add it using $ element->appendChild($ input) regardless of the actual field type. So, yeah, naming things is hard.

Validation

The API we are consuming kindly provides an individual validation message for each required field. If there’s a submission error, we can show the errors inline together with the fields, rather than a generic “oops, your bad” message at the bottom.

Let’s add the validation text to each element:

<?php // ... // Iterate over the fields and build each one foreach ($  formFields as $  field) {   // build the container `<label>`   $  element = $  dom->createElement('label');   $  element->setAttribute('class', 'field');    // Reset input values   $  input = null;   $  label = null;   $  validation = null;    // Add a `<span>` for the label if it is set   // ...    // Add a `<em>` for the validation message if it is set   if (isset($  field->validation_message)) {     $  validation = $  dom->createElement('em');     $  fragment = $  dom->createDocumentFragment();     $  fragment->appendXML($  field->validation_message);     $  validation->appendChild($  fragment);     $  validation->setAttribute('class', 'validation-message');     $  validation->setAttribute('hidden', 'hidden'); // Initially hidden, and will be unhidden with Javascript if there's an error on the field   }    // Build the input element   switch ($  field->type) {     // ...   } }

That’s all it takes! No need to fiddle with the field type logic — just conditionally build an element for each field.

Bringing it all together

So what happens after we build all the field elements? We need to add the $ input, $ label, and $ validation objects to the DOM tree we’re building. We can also use the opportunity to add common attributes, like required. Then we’ll add the submit button, which is separate from the fields in this API.

<?php function renderForm ($  endpoint) {   // Get the data from the API and convert it to a PHP object   // ...    // Start building the DOM   $  dom = new DOMDocument();   $  form = $  dom->createElement('form');    // Iterate over the fields and build each one   foreach ($  formFields as $  field) {     // Build the container `<label>`     $  element = $  dom->createElement('label');     $  element->setAttribute('class', 'field');        // Reset input values     $  input = null;     $  label = null;     $  validation = null;        // Add a `<span>` for the label if it is set     // ...        // Add a `<em>` for the validation message if it is set     // ...        // Build the input element     switch ($  field->type) {       // ...     }        // Add the input element     if ($  input) {       $  input->setAttribute('id', $  field->id);       if ($  field->required)         $  input->setAttribute('required', 'required');       if (isset($  field->max_length))         $  input->setAttribute('maxlength', $  field->max_length);       $  element->appendChild($  input);          if ($  label)         $  element->appendChild($  label);          if ($  validation)         $  element->appendChild($  validation);          $  form->appendChild($  element);     }   }      // Build the submit button   $  submitButtonLabel = $  formContent->submit_button_label;   $  submitButtonField = $  dom->createElement('div');   $  submitButtonField->setAttribute('class', 'field submit');   $  submitButton = $  dom->createElement('button', $  submitButtonLabel);   $  submitButtonField->appendChild($  submitButton);   $  form->appendChild($  submitButtonField);    // Get the HTML output   $  dom->appendChild($  form);   $  htmlString = $  dom->saveHTML();   echo $  htmlString; }

Why are we checking if $ input is truthy? Since we reset it to null at the top of the loop, and only build it if the type conforms to our expected switch cases, this ensures we don’t accidentally include unexpected elements our code can’t handle properly.

Hey presto, a custom HTML form!

Bonus points: rows and columns

As you may know, many form builders allow authors to set rows and columns for fields. For example, a row might contain both the first name and last name fields, each in a single 50% width column. So how would we go about implementing this, you ask? By exemplifying (once again) how loop-friendly DOMDocument is, of course!

Our API response includes the grid data like this:

{   "submit_button_label": "Submit now!",   "fields": [     {       "id": "first-name",       "type": "text",       "label": "First name",       "required": true,       "validation_message": "First name is required.",       "max_length": 30,       "row": 1,       "column": 1     },     {       "id": "category",       "type": "multiple_choice",       "label": "Choose all categories that apply",       "required": false,       "field_metadata": {         "multi_select": true,         "values": [           { "value": "travel", "label": "Travel" },           { "value": "marketing", "label": "Marketing" }         ]       },       "row": 2,       "column": 1     }   ] }

We’re assuming that adding a data-column attribute is enough for styling the width, but that each row needs to be it’s own element (i.e. no CSS grid).

Before we dive in, let’s think through what we need in order to add rows. The basic logic goes something like this:

  1. Track the latest row encountered.
  2. If the current row is larger, i.e. we’ve jumped to the next row, create a new row element and start adding to it instead of the previous one.

Now, how would we do this if we were concatenating strings? Probably by adding a string like '</div><div class="row">' whenever we reach a new row. This kind of “reversed HTML string” is always very confusing to me, so I can only imagine how my IDE feels. And the cherry on top is that thanks to the browser auto-closing open tags, a single typo will result in a gazillion nested <div>s. Just like fun, but the opposite.

So what’s the structured way to handle this? Thanks for asking. First let’s add row tracking before our loop and build an additional row container element. Then we’ll make sure to append each container $ element to its $ rowElement rather than directly to $ form.

<?php function renderForm ($  endpoint) {   // Get the data from the API and convert it to a PHP object   // ...    // Start building the DOM   $  dom = new DOMDocument();   $  form = $  dom->createElement('form');    // init tracking of rows   $  row = 0;   $  rowElement = $  dom->createElement('div');   $  rowElement->setAttribute('class', 'field-row');    // Iterate over the fields and build each one   foreach ($  formFields as $  field) {     // Build the container `<label>`     $  element = $  dom->createElement('label');     $  element->setAttribute('class', 'field');     $  element->setAttribute('data-row', $  field->row);     $  element->setAttribute('data-column', $  field->column);          // Add the input element to the row     if ($  input) {       // ...       $  rowElement->appendChild($  element);       $  form->appendChild($  rowElement);     }   }   // ... }

So far we’ve just added another <div> around the fields. Let’s build a new row element for each row inside the loop:

<?php // ... // Init tracking of rows $  row = 0; $  rowElement = $  dom->createElement('div'); $  rowElement->setAttribute('class', 'field-row');  // Iterate over the fields and build each one foreach ($  formFields as $  field) {   // ...   // If we've reached a new row, create a new $  rowElement   if ($  field->row > $  row) {     $  row = $  field->row;     $  rowElement = $  dom->createElement('div');     $  rowElement->setAttribute('class', 'field-row');   }    // Build the input element   switch ($  field->type) {     // ...       // Add the input element to the row       if ($  input) {         // ...         $  rowElement->appendChild($  element);          // Automatically de-duped         $  form->appendChild($  rowElement);       }   } }

All we need to do is overwrite the $ rowElement object as a new DOM element, and PHP treats it as a new unique object. So, at the end of every loop, we just append whatever the current $ rowElement is — if it’s still the same one as in the previous iteration, then the form is updated; if it’s a new element, it is appended at the end.

Where do we go from here?

Forms are a great use case for object-oriented templating. And thinking about that snippet from WordPress Core, an argument might be made that nested menus are a good use case as well. Any task where the markup follows complex logic makes for a good candidate for this approach. DOMDocument can output any XML, so you could also use it to build an RSS feed from posts data.

Here’s the entire code snippet for our form. Feel free it adapt it to any form API you find yourself dealing with. Here’s the official documentation, which is good for getting a sense of the available API.

We didn’t even mention DOMDocument can parse existing HTML and XML. You can then look up elements using the XPath API, which is kinda similar to document.querySelector, or cheerio on Node.js. There’s a bit of a learning curve, but it’s a super powerful API for handling external content.

Fun(?) fact: Microsoft Office files that end with x (e.g. .xlsx) are XML files. Don’t tell the marketing department, but it’s possible to parse Word docs and output HTML on the server.

The most important thing is to remember that templating is a superpower. Being able to build the right markup for the right situation can be the key to a great UX.


The post Building a Form in PHP Using DOMDocument appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , ,
[Top]

Grainy Gradients

Browse through Dribbble or Behance, and you’ll find designers using a simple technique to add texture to an image: noise. Adding noise makes otherwise solid colors or smooth gradients, such as shadows, more realistic. But despite designers’ affinity for texture, noise is rarely used in web design.

In this article, we’ll generate colorful noise to add texture to a gradient with only a small amount of CSS and SVG. Alright, let’s make some noise!

Illustration by Hank Washington on Behance

Illustration by Jordan Kay on Dribbble

Interactive playground

Check it out here. The quickest way to understand what’s happening is to play with the parameters that make up the layers.

The trick: SVG noise and CSS gradients

The core technique in this article is built on top of a Stack Overflow answer by Chris Pachl to the question: Can you add noise to a CSS gradient?

The trick is to use an SVG filter to create the noise, then apply that noise as a background. Layer it underneath a gradient, boost the brightness and contrast, and that’s it — you have gradient that gradually dithers away.

The key ingredients

Here’s what we’re working with under the hood:

  • SVG turbulence: This is our noise filter.
  • Background with gradient and SVG: Next, we drop that filter into CSS as a background image that combines the filter with a CSS gradient.
  • Boost brightness and contrast: Then we turn to CSS filter to increase the brightness and contrast of the noise.
  • Blend gradients: Finally, we optionally use mix-blend-mode to further filter out colors and blend gradients together.

Let’s go into detail on each of these parts.

Using SVG turbulence

Within the realm of SVG, we can define filters, and one such filter lets us create Perlin noise. It’s called <feTurbulence> and we can define attributes, such as whether it is “turbulence” or “noise” and how fine or coarse it is. Bence Szabó explains it in much more detail as he demonstrates how it can be used to create patterns.

<svg viewBox="0 0 200 200" xmlns='http://www.w3.org/2000/svg'>   <filter id='noiseFilter'>     <feTurbulence        type='fractalNoise'        baseFrequency='0.65'        numOctaves='3'        stitchTiles='stitch' />   </filter>    <rect width='100%' height='100%' filter='url(#noiseFilter)' /> </svg>

This SVG example creates a filter and renders a <rect> element that we can use for our grainy gradients. Notice that the SVG <filter> is defined separately from the <rect>, and the <rect> simply references it.

Play around with changing some of the properties of <feTurbulence>

We’re going to save this SVG as a separate file. We reference an external link to grab the SVG in the demos throughout in this article. In practice, though, you would reference a local file or your own CDN. It doesn’t work to reference the SVG by its id in CSS, for some quirky reason, but you can inline the SVG, as we show in the playground demo. We don’t do this in the demos for legibility reasons.

Creating a CSS background with SVG and a gradient

After we have the SVG file stored somewhere we can reference it by a URL or path, we can now use it in a CSS background, combined with a gradient.

.noise {   /* ... */   background:     linear-gradient(to right, blue, transparent),     url(https://grainy-gradients.vercel.app/noise.svg); }

Order matters here. In this particular example, we want a solid color (i.e. no noise) to transition into noise and then into another color. We also want one end of the gradient to be transparent so that the noise shows through.

Like this:

However, this isn’t particularly nice because the noise is too muddled. We need to fray it and make it grainier. We can do that by…

Boosting the brightness and contrast

Adding a CSS filter makes the noise more stark, pushing the most faded colors towards white or black. The filter applies to the entire <div>, so the leftmost blue is a different blue than the pure blue we started with.

.noise {   /* ... */   background:      linear-gradient(to right, blue, transparent),      url(https://grainy-gradients.vercel.app/noise.svg);   filter: contrast(170%) brightness(1000%);   }

You can play around with how contrast and brightness affect the gradient. Boosting the brightness and contrast pushes out the muddled grays in the follow demo.

The noise is not uniform in color

If you zoom in, you’ll notice that the noise is made up of many colors. The SVG filter was colorful to begin with, and increasing the brightness and contrast emphasized certain colors. Although hardly noticeable, if this confetti is unwelcome, we can continue to filter out colors with CSS blending (i.e. mix-blend-mode and background-blend-mode ).

CSS blending

Let’s make a grainy gradient that transitions between two colors. CSS blending allows us to stack layers of color. In the next example, we’re adding another <div> to the markup, positioning it over the original gradient, then applying mix-blend-mode: multiply; to smooth things out.

<section>   <div class="isolate">     <div class="noise"></div>     <div class="overlay"></div>   </div> </section>
.noise {   /* ... */   background:      linear-gradient(20deg, rebeccapurple, transparent),      url(https://grainy-gradients.vercel.app/noise.svg);    contrast(170%) brightness(1000%); } .overlay {   /* ... */   background: moccasin;   mix-blend-mode: multiply; }

We can use the CSS isolation property to create a new stacking context and choose what gets blended. If we were to leave out isolation in the next example, the gradient and overlay would blend with the background color. Try it in the Pen and comment out that line!

/* Same as before */  .isolate {   isolation: isolate;   /* ... */ }

Some use cases

We’ve looked at a pretty simple example of how to make a noisy gradient, but where might you use one? Let’s consider several use cases.

Light and shadows, with grain

Where do gradients naturally occur? Light and shadows, for one. We can take advantage of the CSS property mix-blend-mode to smoothly blend gradients and selectively filter the colors we want to see in the noise.

In the “shadow” example, we create a dark gradient, and invert it to create the effect in the “light” example. In both cases, mix-blend-mode allows us to blend it with other gradients.

Holographic foil

The drastic brightness and contrast boost creates a rainbow effect that’s reminiscent of holographic foil.

Taking things further

Try the playground and mess around with the different parameters to see how they affect the texture.

Beyond that, here are some ways to continue fiddling with this technique:

  • Use a different SVG: All of the gradients in this article use the same SVG, but you can toy with the parameters that generates the noise to adjust the coarseness as well as the look and feel in the playground.
  • Try different gradients: Besides linear-gradient, CSS offers four more types of gradients. Can you name them? (Here’s one.)
  • Add more layers: With CSS blending, you can stack as many any layers as you’d like and blend them down.
  • Apply different SVG filters: There are all kinds of filters, including Gaussian blur and different types of lighting. Plus, they can be referenced in a CSS filter and applied to any element in addition to SVG.

What else can you think of? Please let us know what you discover in the comments.

Browser support

We can’t escape talking about browser support here. The core of this technique is supported by all modern browsers. As you might expect, it does not work in Internet Explorer. That said, Internet Explorer does support SVG as a background in CSS (just not with the actual CSS filter property).

SVG as a CSS background image

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

Chrome Firefox IE Edge Safari
5 24 9 16 5

Mobile / Tablet

Android Chrome Android Firefox Android iOS Safari
93 92 3 4.2-4.3

CSS filter effects

This browser support data is from Caniuse, which has more detail. A number indicates that browser supports the feature at that version and up.

Desktop

Chrome Firefox IE Edge Safari
18* 35 No 79 6*

Mobile / Tablet

Android Chrome Android Firefox Android iOS Safari
93 92 4.4* 6.0-6.1*

I’ve also noticed that Blink-based browsers (e.g. Safari) and WebKit-based one (e.g. Chrome) implement mix-blend-mode slightly differently, so please be sure to test across browsers if using CSS blending. In my own projects, I’ve used browser-specific media queries to manually reconcile the visual differences with small tweaks to CSS.


That’s it! Now that you have a grasp of SVG filters and how to combine them with CSS filters as a background, you have yet another neat visual effect to add depth and texture to a design.


The post Grainy Gradients appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

,
[Top]

Social Image Generator + Jetpack

I feel like my quest to make sure this site had pretty sweet (and automatically-generated) social media images (e.g. Open Graph) came to a close once I found Social Image Generator.

The trajectory there was that I ended up talking about it far too much on ShopTalk, to the point it became a common topic in our Discord (join via Patreon), Andy Bell pointed me at Daniel Post’s Social Image Generator and I immediately bought and installed it. I heard from Daniel over Twitter, and we ended up having long conversations about the plugin and my desires for it. Ultimately, Daniel helped me code up some custom designs and write logic to create different social media image designs depending on the information it had (for example, if we provide quote text, it uses a special design for that).

As you likely know, Automattic has been an awesome and long time sponsor for this site, and we often promote Jetpack as a part of that (as I’m a heavy user of it, it’s easy to talk about). One of Jetpack’s many features is helping out with social media. (I did a video on how we do it.) So, it occurred to me… maybe this would be a sweet feature for Jetpack. I mentioned it to the Automattic team and they were into the idea of talking to Daniel. I introduced them back in May, and now it’s September and… Jetpack Acquires WordPress Plugin Social Image Generator

“When I initially saw Social Image Generator, the functionality looked like a ideal fit with our existing social media tools,’ said James Grierson, General Manager of Jetpack. ‘I look forward to the future functionality and user experience improvements that will come out of this acquisition. The goal of our social product is to help content creators expand their audience through increased distribution and engagement. Social Image Generator will be a key component of helping us deliver this to our customers.”

Daniel will also be joining Jetpack to continue developing Social Image Generator and integrating it with Jetpack’s social media features.

Rob Pugh

Heck yeah, congrats Daniel. My dream for this thing is that, eventually, we could start building social media images via regular WordPress PHP templates. The trick is that you need something to screenshot them, like Puppeteer or Playwright. An average WordPress install doesn’t have that available, but because Jetpack is fundamentally a service that leverages the great WordPress cloud to do above-and-beyond things, this is in the realm of possibility.

WP Tavern also covered the news:

Automattic is always on the prowl for companies that are doing something interesting in the WordPress ecosystem. The Social Image Generator plugin expertly captured a new niche with an interface that feels like a natural part of WordPress and impressed our chief plugin critic, Justin Tadlock, in a recent review.

“Automattic approached me and let me know they were fans of my plugin,” Post said. “And then we started talking to see what it would be like to work together. We were actually introduced by Chris Coyier from CSS-Tricks, who uses both our products.”

Sarah Gooding

Just had to double-toot my own horn there, you understand.


The post Social Image Generator + Jetpack appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , ,
[Top]

Design Deals for the Week

[Top]

Create Anything That You Can Imagine With Litho, The Multipurpose Elementor WordPress Theme

[Top]

Learn How To Pick Your WordPress Plugins Safely

[Top]

“Just in Time” CSS

I believe acss.io is the first usage of “Atomic CSS” where the point of it is to be a compiler. You write CSS like this:

<div class="C(#fff) P(20px)">   text </div>

And it will generate CSS like:

.C(#333) {   color: #333; } .P(20px) {   padding: 20px; }

(Or something like that.)

The point is that it only generates the CSS that you actually need, because you asked for it, and no more. The result is far less CSS than you’d see in an average stylesheet.

That complication process is what has come to be known as “Just in Time” CSS.

The popular Tailwind framework supports it. It kind of flips the mental model of Tailwind on its head, to me. Rather than providing a huge pile of CSS utility classes to use — then “purging” what is unused — it only creates what it needs to begin with.

I’d say “Just in Time” is a concept that is catching on. I just saw Assembler CSS and it leans into it big time. Rather than classes, you do stuff like:

<div x-style="grid; gap:1rem; grid-rows:1; grid-cols:1; sm|grid-cols:3">   <button x-style="^button:red">Submit</button> </div>

I’m pretty torn on this stuff. Some part of me likes how you can get styling done without ever leaving your templates. And I especially like the extremely minimal CSS output since CSS is a blocking resource. Another part of me doesn’t like that it’s a limited abstraction of CSS itself, so you’re at the mercy of the tool to support things that CSS can do natively. It also makes HTML a bit harder to look at — although I certainly got over that with JSX inline event handlers and such.


The post “Just in Time” CSS appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

,
[Top]

EyeDropper API Dropped

I had no idea this was coming but it’s pretty neat! Chrome has shipped the EyeDropper API in v95 (Canary, as I write).

I like how it can grab a color from anywhere on the screen, not just within the browser window. Maybe Figma can make use of it, as I’ve been frustrated in the past how you can only snag colors from the Figma working area, not anywhere on the screen.

As a way to connect this to some other newfangled tech, check out this Pen from Andrew Walpole (in Chrome 95+):

It uses the EyeDropper API, but petite-vue is the app framework in use, and to my eyes, it is an awfully elegant expression of an interactive app. This new version of Vue is quite new meant to be used directly in the browser. I see it capturing some devs’ attention. Here are Dave Rupert’s thoughts as well as what Andrew Welch’s thinks about it.


The post EyeDropper API Dropped appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

,
[Top]

Fun Times Styling Checkbox States

We might leave a text input unstyled. We might leave a link unstyled. Even a button. But checkboxes… we don’t leave them alone. That’s why styling checkboxes never gets old.

Although designing checkboxes is not that complicated, we also don’t have to settle for simple background color changes, or adding and removing borders, to indicate state changes. We also don’t have to pull out any fancy design skills — that we don’t possess — to make this work. I’ll show you how.

Basics

In the following demos, the checkboxes pretty much have the same three-stack layout — at the bottom is a checkbox, and on top of it are two stacked elements, or pseudo-elements. The checkbox is indicated as checked or unchecked depending on which of the two is visible.

If you look at the CSS code in the pens you’ll notice all the layouts — including the one for the checkboxes — are grids. You can use other layouts that feel right for your use case (and learn more in the CSS-Tricks Grid Guide). Additional notes on code and design alternatives are at the end of the source code inside the pens.

In addition, any elements stacked on top of the checkbox have pointer-events: none so they don’t prevent users from clicking or tapping the checkbox.

Let’s now get to the first method.

Idea 1: Blended backgrounds as a state

Blending in CSS is a versatile technique. Manipulating colors relative to two or more elements or backgrounds can be handy in contexts you might not have thought of.

One such instance is the checkbox.

<input id="un" type="checkbox"> <label for="un">un</label> <!-- more checkboxes --> 
input[type=checkbox]::before, input[type=checkbox]::after {   mix-blend-mode: hard-light;   pointer-events: none;   /* more style */ } input[type=checkbox]::before {   background: green;   content: '✓';   color: white;   /* more style */ } input[type=checkbox]::after {   background: blue;   content: '⨯';   /* more style */ } input[type=checkbox]:checked::after {   mix-blend-mode: unset;   color: transparent; }

In this demo, I’ve styled the checkbox’s pseudo-elements green and blue, stacked them up, and gave them each a mix-blend-mode value. This means the background of each element blends with its backdrop.

I used the hard-light value, which emulates the result of either multiply or screen depending on if the top color is darker or lighter. You can learn in depth about different blend modes over at MDN.

When the box is checked, the ::after pseudo-element’s mix blend mode value is unset, resulting in a different visual.

Idea 2: Make a 3D animation

Animating a block of color is fun. Make them seem 3D and it’s even better. CSS has the means to render elements along an emulated 3D space. So using that, we make a 3D box and rotate it to indicate the checkbox state change.

<div class="c-checkbox">   <input type="checkbox" id="un">   <!-- cube design -->   <div><i></i><i></i><i></i><i></i></div> </div> <label for="un">un</label> <!-- more checkboxes -->
.c-checkbox > div {   transition: transform .6s cubic-bezier(.8, .5, .2, 1.4);   transform-style: preserve-3d;   pointer-events: none;   /* more style */ } /* front face */ .c-checkbox > div > i:first-child {   background: #ddd;   transform:  translateZ( -10px ); } /* back face */ .c-checkbox > div > i:last-child {   background: blue;   transform:  translateZ( 10px ); } /* side faces */ .c-checkbox > div > i:nth-of-type(2), .c-checkbox > div > i:nth-of-type(3) {   transform: rotateX(90deg)rotateY(90deg);   position: relative;   height: 20px;   top: 10px; } .c-checkbox > div > i:nth-of-type(2) {   background: navy;   right: 20px; } .c-checkbox > div > i:nth-of-type(3) {   background: darkslategray;   left: 20px; }

The <div> after the checkbox becomes a container of a 3D space — its child elements can be placed along the x, y and z axes — after it’s given transform-style: preserve-3d;.

Using the transform property, we place two <i> elements (grey and blue colored) with some distance between them across the z-axis. Two more are wedged between them, covering their left and right sides. It’s like a cardboard box that’s covered except at the top and bottom.

When the checkbox is checked, this grey and blue box is rotated sideways to face the other side. Since I’ve already added a transition to the <div>, its rotation is animated.

input:checked + div {    transform: rotateY( 180deg );  }

Idea 3: Playing with border radius

Changing a checked box’s border radius? Not that fun. Changing also the border radius of other boxes near it? Now we have something.

<input type="checkbox" id="un"> <label for="un">un</label> <!-- more rows of checkboxes -->
input {   background: #ddd;   border-radius: 20px;   /* more style */ } input:not(:first-of-type)::before {   content: '';       transform: translateY(-60px); /* move up a row */   pointer-events: none; } input:checked + * + input::before, input:last-of-type:checked {   border-radius: 20px;   background: blue; } input:checked + * + input:checked + * + input::before {   border-top-left-radius: unset !important;   border-top-right-radius: unset !important; } input:checked::before {   border-bottom-left-radius: unset !important;   border-bottom-right-radius: unset !important; } /* between the second-last and last boxes */  input:nth-of-type(4):checked + * + input:checked {   border-top-left-radius: unset;   border-top-right-radius: unset; }

If you’d just interacted with the demo before, you’ll notice that when you click or tap a checkbox, it not only can change its own borders but also the borders of the boxes after and before it.

Now, we don’t have selectors that can select elements prior, only the ones after. So what we did to control the appearance of a preceding box is use the pseudo-element of a checkbox to style the box before it. With exception of the first box, every other box gets a pseudo-element that’s moved to the top of the box before it.

Let’s say boxes A, B and C are one after another. If I click B, I can change the appearance of A by styling B’s pseudo-element, B by styling C’s pseudo-element, and C by styling D’s pseudo-element.

From B, the pseudo-elements of B, C and D are accessible — as long as the next element selector can be used between them in the layout.

The four corners of each checkbox are initially rounded when checked and unchecked. But if a box is checked, the following box’s top corners and preceding box’s bottom corners are straightened (by overriding and removing their border radii).

Idea 4: Using a CSS mask

Toggles, switches… they are also checkboxes as far as the code goes. So we can style the boxes as toggles for this one, and it’s done with a CSS mask, which Chris has written about before. But in a nutshell, it’s a technique where we use an image to filter out portions of its backdrop.

<input type="checkbox"> <div class="skin one"></div> <div class="skin two"></div>
.one.skin {   background: no-repeat center -40px url('photo-1584107662774-8d575e8f3550?w=350&q=100'); } .two.skin {   background: no-repeat center -110px url('photo-1531430550463-9658d67c492d?w=350&q=100');   --mask: radial-gradient(circle at 45px 45px , rgba(0,0,0,0) 40px, rgba(0,0,0,1) 40px);   mask-image: var(--mask); -webkit-mask-image: var(--mask); }

Two skins (displaying landscape photos) are on top of a checkbox. The topmost one gets a mask-image that’s in the shape of a typical toggle switch — a transparent circle at the left, and the rest is a fully opaque color. Through the transparent circle we see the photo below while the rest of the mask image shows the photo at the top.

When a checkbox is clicked, the transparent circle is moved to the right, so we see the image at the top through the circle while the rest shows the photo at the bottom.

input:checked ~ .two.skin {   --mask: radial-gradient(circle at 305px 45px, rgba(0,0,0,1) 40px, rgba(0,0,0,0) 40px);   mask-image: var(--mask); -webkit-mask-image: var(--mask); }

Idea 5: Using box shadow

Let’s end with the simplest — but what I consider to be the most effective — method of them all: an animated inset box-shadow.

<input id="un" type="checkbox"> <label for="un">un</label>
input {   transition: box-shadow .3s;   background: lightgrey;   /* more style */ } input:checked {    box-shadow: inset 0 0 0 20px blue; }

There are some CSS properties that can be animated by default and one of them is box-shadow. This type of subtle animation goes well with a minimalist theme.


That’s it! I hope this sparks some inspiration the next time you find yourself working with checkboxes. CSS gives us so many possibilities to indicate state changes, so have a little fun and please share if you have any interesting ideas.


The post Fun Times Styling Checkbox States appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , ,
[Top]