Newer posts are loading.
You are at the newest post.
Click here to check if anything new just came in.

February 15 2020

23:16

“CSS4” Update

Since I first chimed in on the CSS4¹ thing, there's been tons of more discussion on it. I'm going to round up my favorite thoughts from others here. There is an overwhelming amount of talk about this, so I'm going to distill it here down as far as I can, hopefully making it easier to follow.

  • Jen Simmons kicked off a discussion on GitHub under the CSS Working Group draft repo. The best we have for developers trying to stay current on CSS right now is "just keep up" and that's not working. Better to "draw a line" around certain things and say "Here, this. This part is ready. This part is done."
  • Michelle Barker says it's hard to prioritize what to learn without guidance.
  • Nicole Sullivan thinks if developers can "tick the checkbox" of CSS4, it would "drive developers to upgrade their skills, businesses to upgrade their tech stacks, and browsers to focus on cross-browser compat."
  • Dave Rupert sees value in the Perceived Velocity through Version Numbers.
  • Natalya Shelburne says that, in "a time-scarce world", engineers she works with don't feel like new CSS is production-ready and don't prioritize learning it.
  • Rachel Andrew thinks that the browser support of features, particularly the complex and nuanced nature of subfeatures, is a barrier. Since we don't know anyone else's browser support requirements, it's irresponsible to suggest blanket features to learn/use.
  • Brian Kardell brought up that JavaScript is "versioned" yearly and it seems to work over there (Although Shawn Wang rightly mentioned that we should probably be asking them what has worked and what hasn't). Personally, I prefer the idea of CSS2020 over CSS4 (I just like the synchronicity with JavaScript), and Brain Kardell thinks just because we use a year in the name doesn't mean we have to do it every year. Chen Hui Jing mentioned that Babel throws a wrench in things a bit. Polyfilling new JavaScript is a different beast than polyfilling new CSS and that affects expectations.
  • Miriam thinks we need to settle on a list of features, but that it should be criteria-based. I suppose criteria would be status of spec, browser support ("2 major browsers" is the most common theme), and some kind of importance modifier. My first article took a stab at a feature list and here's another thought. PPK agrees that flexbox is too old, and favors grid and custom properties. Timothy Miller has a few ideas.
  • fantasai doesn't think this message should come from the CSSWG.
  1. The more we keep calling it CSS4, the harder it will be to call it anything else. If that's how it shakes out, fine, but my preference is to mimic JavaScript "versioning". I'm going to stop calling it CSS4 after this until a name settles in better.

The post “CSS4” Update appeared first on CSS-Tricks.

14:33
The webinar software – how it can help you?
00:31

Creating a Details Element That Opens But Never Closes

The <details> and <summary> elements in HTML are useful for making content toggles for bits of text. By default, you see the <summary> element with a toggle triangle (▶︎) next to it. Click that to expand the rest of the text inside the <details> element.

But let's say you want to be able to click it open and that's that. Interactivity over. I saw this used in one of those "Read more" article designs, where you click that "Read more" button and the article expands, but there is no going back.

I'll preface this by saying that I'm not sure that this is a great idea in general. Removing controls just doesn't feel great, nor does slapping too much important content within a <details> element. But, hey, the web is a big place and you never know what you might need. The fact that this can be done in a few lines of HTML/CSS is compelling and might reduce the need for heavier solutions.

The main trick here is to hide the summary when details is open.

details[open] summary {
  display: none;
}

That's it really.

CodePen Embed Fallback

The post Creating a Details Element That Opens But Never Closes appeared first on CSS-Tricks.

February 14 2020

15:24

While You Weren’t Looking, CSS Gradients Got Better

One thing that caught my eye on the list of features for Lea Verou's conic-gradient() polyfill was the last item:

Supports double position syntax (two positions for the same color stop, as a shortcut for two consecutive color stops with the same color)

Surprisingly, I recently discovered most people aren't even aware that double position for gradient stops is something that actually exists in the spec, so I decided to write about it.

According to the spec:

Specifying two locations makes it easier to create solid-color "stripes" in a gradient, without having to repeat the color twice.

I completely agree, this was the first thing I thought of when I became aware of this feature.

Let's say we want to get the following result: a gradient with a bunch of equal width vertical stripes (which I picked up from an earlier post by Chris):

Screenshot. Shows 8 vertical rainbow stripes, from left to right: violet, magenta, red, orange, yellow, yellowish green, teal, blue. Desired gradient result.

The hex values are: #5461c8, #c724b1, #e4002b, #ff6900, #f6be00, #97d700, #00ab84 and #00a3e0.

Let's first see how we'd CSS this without using double stop positions!

We have eight stripes, which makes each of them one-eighth of the gradient width. One eighth of 100% is 12.5%, so we go from one to the next at multiples of this value.

This means our linear-gradient() looks as follows:

linear-gradient(90deg, 
             #5461c8 12.5% /* 1*12.5% */, 
  #c724b1 0, #c724b1 25%   /* 2*12.5% */, 
  #e4002b 0, #e4002b 37.5% /* 3*12.5% */, 
  #ff6900 0, #ff6900 50%   /* 4*12.5% */, 
  #f6be00 0, #f6be00 62.5% /* 5*12.5% */, 
  #97d700 0, #97d700 75%   /* 6*12.5% */, 
  #00ab84 0, #00ab84 87.5% /* 7*12.5% */, 
  #00a3e0 0)

Note that we don't need to repeat stop position % values because, whenever a stop position is smaller than a previous one, we automatically have a sharp transition. That's why it's always safe to use 0 (which is always going to be smaller than any positive value) and have #c724b1 25%, #e4002b 0 instead of #c724b1 25%, #e4002b 25%, for example. This is something that can make our life easier in the future if, for example, we decide we want to add two more stripes and make the stop positions multiples of 10%.

Not too bad, especially compared to what gradient generators normally spit out. But if we decide one of those stripes in the middle doesn't quite fit in with the others, then changing it to something else means updating in two places.

Again, not too bad and nothing we can't get around with a little bit of help from a preprocessor:

$c: #5461c8 #c724b1 #e4002b #ff6900 #f6be00 #97d700 #00ab84 #00a3e0;

@function get-stops($c-list) {
  $s-list: ();
  $n: length($c-list);
  $u: 100%/$n;
	
  @for $i from 1 to $n {
    $s-list: $s-list, 
             nth($c-list, $i) $i*$u, 
             nth($c-list, $i + 1) 0
  }

  @return $s-list
}

.strip {
  background: linear-gradient(90deg, get-stops($c)))
}

This generates the exact CSS gradient we saw a bit earlier and now we don't have to modify anything in two places anymore.

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

However, even if a preprocessor can save us from typing the same thing twice, it doesn't eliminate repetition from the generated code.

And we may not always want to use a preprocessor. Leaving aside the fact that some people are stubborn or have an irrational fear or hate towards preprocessors, it sometimes feels a bit silly to use a loop.

For example, when we barely have anything to loop over! Let's say we want to get a much simpler background pattern, such as a diagonal hashes one, which I'd imagine is a much more common use case than an over-the-top rainbow one that's probably not a good fit on most websites anyway.

Screenshot. Shows a pattern of diagonal light grey hashes on a white background. Desired hashes result

This requires using repeating-linear-gradient() and this means a bit of repetition, even if we don't have the same long list of hex values as we did before:

repeating-linear-gradient(-45deg, 
    #ccc /* can't skip this, repeating gradient won't work */, 
    #ccc 2px, 
    transparent 0, 
    transparent 9px /* can't skip this either, tells where gradient repetition starts */)

Here, we cannot ditch the first and last stops because those are precisely what indicate how the gradient repeats within the rectangle defined by the background-size.

If you want to understand why it's better to use repeating-linear-gradient() instead of a plain old linear-gradient() combined with the proper background-size in order to create such hashes, check out this other article I wrote a while ago.

This is precisely where such feature comes to the rescue — it allows us to avoid repetition in the final CSS code.

For the rainbow stripes case, our CSS becomes:

linear-gradient(90deg, 
    #5461c8 12.5%, 
    #c724b1 0 25%, 
    #e4002b 0 37.5%, 
    #ff6900 0 50%, 
    #f6be00 0 62.5%, 
    #97d700 0 75%, 
    #00ab84 0 87.5%, 
    #00a3e0 0)

And to recreate the hashes, we only need:

repeating-linear-gradient(-45deg, 
    #ccc 0 2px, 
    transparent 0 9px)

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

What about support? Well, glad you asked! It actually happens to be pretty good! It works in Safari, Chromium browsers (which now includes Edge as well!) and Firefox. Pre-Chromium Edge and maybe some mobile browsers could still hold you back, but if you don't have to worry about providing support for every browser under the sun or it's fine to provide a fallback, go ahead and start using this!

The post While You Weren’t Looking, CSS Gradients Got Better appeared first on CSS-Tricks.

15:24

A Complete Guide to Links and Buttons

There is a lot to know about links and buttons in HTML. There is markup implementation and related attributes, styling best practices, things to avoid, and the even-more-nuanced cousins of the link: buttons and button-like inputs.

Let's take a look at the whole world of links and buttons, and all the considerations at the HTML, CSS, JavaScript, design, and accessibility layers that come with them. There are plenty of pitfalls and bad practices to avoid along the way. By covering it, we’ll have a complete good UX implementation of both elements.

Quick guidelines on when to use each:

  • Are you giving a user a way to go to another page or a different part of the same page? Use a link (<a href="/somewhere">link</a>)
  • Are you making a JavaScript-powered clickable action? Use a button (<button type="button">button</button>)
  • Are you submitting a form? Use a submit input (<input type="submit" value="Submit">)

Links

Links are one of the most basic, yet deeply fundamental and foundational building blocks of the web. Click a link, and you move to another page or are moved to another place within the same page.

Table of Contents

HTML implementation

A basic link

<a href="https://css-tricks.com">CSS-Tricks</a>

That's a link to a "fully qualified" or "absolute" URL.

A relative link

You can link "relatively" as well:

<!-- Useful in navigation, but be careful in content that may travel elsewhere (e.g. RSS) -->
<a href="/pages/about.html">About</a>

That can be useful, for example, in development where the domain name is likely to be different than the production site, but you still want to be able to click links. Relative URLs are most useful for things like navigation, but be careful of using them within content — like blog posts — where that content may be read off-site, like in an app or RSS feed.

A jump link

Links can also be "hash links" or "jump links" by starting with a #:

<a href="#section-2">Section Two</a>
<!-- will jump to... -->
<section id="section-2"></section>

Clicking that link will "jump" (scroll) to the first element in the DOM with an ID that matches, like the section element above.

💥 Little trick: Using a hash link (e.g. #0) in development can be useful so you can click the link without being sent back to the top of the page like a click on a # link does. But careful, links that don't link anywhere should never make it to production.

💥 Little trick: Jump-links can sometimes benefit from smooth scrolling to help people understand that the page is moving from one place to another.

It's a fairly common UI/UX thing to see a "Back to top" link on sites, particularly where important navigational controls are at the top but there is quite a bit of content to scroll (or otherwise navigate) through. To create a jump link, link to the ID of an element that is at the top of the page where it makes sense to send focus back to.

<a href="#top-of-page">Back to Top</a>

Jump links are sometimes also used to link to other anchor (<a>) elements that have no href attribute. Those are called "placeholder" links:

<a id="section-2"></a>
<h3>Section 2</h3>

There are accessibility considerations of these, but overall they are acceptable.

Disabled links

A link without an href attribute is the only practical way to disable a link. Why disable a link? Perhaps it's a link that only becomes active after logging in or signing up.

a:not[href] {
  /* style a "disabled" link */
}

When a link has no href, it has no role, no focusability, and no keyboard events. This is intentional. You could think of it like a <span>.

Do you need the link to open in a new window or tab?

You can use the target attribute for that, but it is strongly discouraged.

<a href="https://css-tricks.com" target="_blank" rel="noopener noreferrer">
  CSS-Tricks
</a>

The bit that makes it work is target="_blank", but note the extra rel attribute and values there which make it safer and faster.

Making links open in new tabs is a major UX discussion. We have a whole article about when to use it here. Summarized:

Don't use it:

  • Because you or your client prefer it personally.
  • Because you're trying to beef up your time on site metric.
  • Because you're distinguishing between internal and external links or content types.
  • Because it's your way out of dealing with infinite scroll trickiness.

Do use it:

  • Because a user is doing something on the current page, like actively playing media or has unsaved work.
  • You have some obscure technical reason where you are forced to (even then you're still probably the rule, not the exception).

Need the link to trigger a download?

The download attribute on a link will instruct the browser to download the linked file rather than opening it within the current page/tab. It's a nice UX touch.

<a href="/files/file.pdf" download>Download PDF</a>

The rel attribute

This attribute is for the relationship of the link to the target.

The rel attribute is also commonly used on the <link> element (which is not used for creating hyperlinks, but for things like including CSS and preloading). We're not including rel values for the <link> element here, just anchor links.

Here are some basic examples:

<a href="/page/3" rel="next">Next</a>
<a href="/page/1" rel="prev">Previous</a>

<a href="http://creativecommons.org/licenses/by/2.0/" rel="license">cc by 2.0</a>

<a href="/topics/" rel="directory">All Topics</a>
  • rel="alternate": Alternate version of the document.
  • rel="author": Author of the document.
  • rel="help": A resource for help with the document.
  • rel="license": License and legal information.
  • rel="manifest": Web App Manifest document.
  • rel="next": Next document in the series.
  • rel="prev": Previous document in the series.
  • rel="search": A document meant to perform a search in the current document.

There are also some rel attributes specifically to inform search engines:

  • rel="sponsored": Mark links that are advertisements or paid placements (commonly called paid links) as sponsored.
  • rel="ugc": For not-particularly-trusted user-generated content, like comments and forum posts.
  • rel="nofollow": Tell the search engine to ignore this and not associate this site with where this links to.

And also some rel attributes that are most security-focused:

  • rel="noopener": Prevent a new tab from using the JavaScript window.opener feature, which could potentially access the page containing the link (your site) to perform malicious things, like stealing information or sharing infected code. Using this with target="_blank" is often a good idea.
  • rel="noreferrer": Prevent other sites or tracking services (e.g. Google Analytics) from identifying your page as the source of clicked link.

You can use multiple space-separated values if you need to (e.g. rel="noopener noreferrer")

And finally, some rel attributes come from the microformats standard, like:

  • rel="directory": Indicates that the destination of the hyperlink is a directory listing containing an entry for the current page.
  • rel="tag": Indicates that the destination of that hyperlink is an author-designated "tag" (or keyword/subject) for the current page.
  • rel="payment": Indicates that the destination of that hyperlink provides a way to show or give support for the current page.
  • rel="help": States that the resource linked to is a help file or FAQ for the current document.

ARIA roles

The default role of a link is link, so you do not need to do:

<a role="link" href="/">Link</a>

You'd only need that if you were faking a link, which would be a weird/rare thing to ever need to do, and you'd have to use some JavaScript in addition to this to make it actually follow the link.

<span class="link" tabindex="0" role="link" data-href="/">
  Fake accessible link created using a span
</span>

Just looking above you can see how much extra work faking a link is, and that is before you consider that is breaks right-clicking, doesn't allow opening in a new tab, doesn't work with Windows High Contrast Mode and other reader modes and assistive technology. Pretty bad!

A useful ARIA role to indicate the current page, like:

<a href="/" aria-current="page">Home</a>
<a href="/contact">Contact</a>
<a href="/about">About/a></a>

Should you use the title attribute?

Probably not. Save this for giving an iframe a short, descriptive title.

<a title="I don't need to be here" href="/">
  List of Concerts
</a>

title provides a hover-triggered UI popup showing the text you wrote. You can't style it, and it's not really that accessible.

Hover-triggered is the key phrase here. It's unusable on any touch-only device. If a link needs more contextual information, provide that in actual content around the link, or use descriptive text the link itself (as opposed to something like "Click Here").

Icon-only links

If a link only has an icon inside it, like:

<a href="/">😃</a>

<a href="/">
  <svg> ... </svg>
</a>

That isn't enough contextual information about the link, particularly for accessibility reasons, but potentially for anybody. Links with text are almost always more clear. If you absolutely can't use text, you can use a pattern like:

<a href="/">
  <!-- Hide the icon from assistive technology -->
  <svg aria-hidden="true" focusable="false"> ... </svg>
  <!-- Acts as a label that is hidden from view -->
  <span class="visually-hidden">Useful link text</span>
</a>

visually-hidden is a class used to visually hide the label text with CSS:

.visually-hidden {
  border: 0;
  clip: rect(0 0 0 0);
  height: 1px;
  margin: -1px;
  overflow: hidden;
  padding: 0;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

Unlike aria-label, visually hidden text can be translated and will hold up better in specialized browsing modes.

Links around images

Images can be links if you wrap them in a link. There is no need to use the alt text to say the image is a link, as assistive technology will do that already.

<a href="/buy/puppies/now">
  <img src="puppy.jpg" alt="A happy puppy.">
</a>

Links around bigger chunks of content

You can link a whole area of content, like:

<a href="/article/">
  <div class="card">
    <h2>Card</h2>
    <img src="..." alt="...">
    <p>Content</p>
  </div>
</a>

But it's slightly weird, so consider the UX of it if you ever do it. For example, it can be harder to select the text, and the entire element needs styling to create clear focus and hover states.

Take this example, where the entire element is wrapped in a link, but there are no hover and focus states applied to it.

CodePen Embed Fallback

If you need a link within that card element, well, you can't nest links. You could get a little tricky if you needed ot, like using a pseudo-element on the link which is absolutely positioned to cover the whole area.

Additionally, this approach can make really long and potentially confusing announcements for screen readers. Even though links around chunks of content is technically possible, it's best to avoid doing this if you can.

Styling and CSS considerations

Here's the default look of a link:

The default User-Agent styling of a link.

It's likely you'll be changing the style of your links, and also likely you'll use CSS to do it. I could make all my links red in CSS by doing:

a {
  color: red;
}

Sometimes selecting and styling all links on a page is a bit heavy-handed, as links in navigation might be treated entirely differently than links within text. You can always scope selectors to target links within particular areas like:

/* Navigation links */
nav a { }

/* Links in an article */
article a { }

/* Links contained in an element with a "text" class */
.text a { }

Or select the link directly to style.

.link {
  /* For styling <a class="link" href="/"> */
}

a[aria-current="page"] {
  /* You'll need to apply this attribute yourself, but it's a great pattern to use for active navigation. */
}

Link states

Links are focusable elements. In other words, they can be selected using the Tab key on a keyboard. Links are perhaps the most common element where you'll very consciously design the different states, including a :focus state.

  • :hover: For styling when a mouse pointer is over the link.
  • :visited: For styling when the link has been followed, as best as the browser can remember. It has limited styling ability due to security.
  • :link: For styling when a link has not been visited.
  • :active: For styling when the link is pressed (e.g. the mouse button is down or the element is being tapped on a touch screen).
  • :focus: Very important! Links should always have a focus style. If you choose to remove the default blue outline that most browsers apply, also use this selector to re-apply a visually obvious focus style.

These are chainable like any pseudo-class, so you could do something like this if it is useful for your design/UX.

/* Style focus and hover states in a single ruleset */
a:focus:hover { }

You can style a link to look button-like

Perhaps some of the confusion between links and buttons is stuff like this:

Very cool "button" style from Katherine Kato.

That certainly looks like a button! Everyone would call that a button. Even a design system would likely call that a button and perhaps have a class like .button { }. But! A thing you can click that says "Learn More" is very much a link, not a button. That's completely fine, it's just yet another reminder to use the semantically and functionally correct element.

Color contrast

Since we often style links with a distinct color, it's important to use a color with sufficient color contrast for accessibility. There is a wide variety of visual impairments (see the tool WhoCanUse for simulating color combinations with different impairments) and high contrast helps nearly all of them.

Perhaps you set a blue color for links:

The blue link is #2196F3.

While that might look OK to you, it's better to use tools for testing to ensure the color has a strong enough ratio according to researched guidelines. Here, I'll look at Chrome DevTools and it will tell me this color is not compliant in that it doesn't have enough contrast with the background color behind it.

Chrome DevTools is telling us this link color does not have enough contrast.

Color contrast is a big consideration with links, not just because they are often colored in a unique color that needs to be checked, but because they have all those different states (hover, focus, active, visited) which also might have different colors. Compound that with the fact that text can be selected and you've got a lot of places to consider contrast. Here's an article about all that.

Styling "types" of links

We can get clever in CSS with attribute selectors and figure out what kind of resource a link is pointing to, assuming the href value has useful stuff in it.

/* Style all links that include .pdf at the end */
a[href$=".pdf"]::after {
  content: " (PDF)";
}

/* Style links that point to Google */
a[href*="google.com"] {
  color: purple;
}

Styling links for print

CSS has an "at-rule" for declaring styles that only take effect on printed media (e.g. printing out a web page). You can include them in any CSS like this:

@media print {
  /* For links in content, visually display the link */ 
  article a::after { 
    content: " (" attr(href) ")";
  }
}

Resetting styles

If you needed to take all the styling off a link (or really any other element for that matter), CSS provides a way to remove all the styles using the all property.

.special-area a {
  all: unset;
  all: revert;
  
  /* Start from scratch */
  color: purple;
}

You can also remove individual styles with keywords. (Again, this isn't really unique to links, but is generically useful):

a {
  /* Grab color from nearest parent that sets it */
  color: inherit;

  /* Wipe out style (turn black) */
  color: initial;

  /* Change back to User Agent style (blue) */
  color: revert;
}

JavaScript considerations

Say you wanted to stop the clicking of a link from doing what it normally does: go to that link or jump around the page. In JavaScript, you can usepreventDefault to prevent jumping around.

const jumpLinks = document.querySelectorAll("a[href^='#']");

jumpLinks.forEach(link => {
 link.addEventListener('click', event => {
    event.preventDefault();
    // Do something else instead, like handle the navigation behavior yourself
  });
});

This kind of thing is at the core of how "Single Page Apps" (SPAs) work. They intercept the clicks so browsers don't take over and handle the navigation.

SPAs see where you are trying to go (within your own site), load the data they need, replace what they need to on the page, and update the URL. It's an awful lot of work to replicate what the browser does for free, but you get the ability to do things like animate between pages.

Another JavaScript concern with links is that, when a link to another page is clicked, the page is left and another page loads. That can be problematic for something like a page that contains a form the user is filling out but hasn't completed. If they click the link and leave the page, they lose their work! Your only opportunity to prevent the user from leaving is by using the beforeunload event.

window.addEventListener("beforeunload", function(event) {
  // Remind user to save their work or whatever.
});

A link that has had its default behavior removed won't announce the new destination. This means a person using assistive technology may not know where they wound up. You'll have to do things like update the page's title and move focus back up to the top of the document.

JavaScript frameworks

In a JavaScript framework, like React, you might sometimes see links created from something like a <Link /> component rather than a native <a> element. The custom component probably creates a native <a> element, but with extra functionality, like enabling the JavaScript router to work, and adding attributes like aria-current="page" as needed, which is a good thing!

Ultimately, a link is a link. A JavaScript framework might offer or encourage some level of abstraction, but you're always free to use regular links.

Accessibility considerations

We covered some accessibility in the sections above (it’s all related!), but here are some more things to think about.

  • You don’t need text like “Link” or “Go to” in the link text itself. Make the text meaningful (“documentation” instead of “click here”).
  • Links already have an ARIA role by default (role="link") so there's no need to explicitly set it.
  • Try not to use the URL itself as the text (<a href="google.com">google.com</a>)
  • Links are generally blue and generally underlined and that's generally good.
  • All images in content should have alt text anyway, but doubly so when the image is wrapped in a link with otherwise no text.

Unique accessible names

Some assistive technology can create lists of interactive elements on the page. Imagine a group of four article cards that all have a "Read More", the list of interactive elements will be like:

  • Read More
  • Read More
  • Read More
  • Read More

Not very useful. You could make use of that .visually-hidden class we covered to make the links more like:

<a href="/article">
  Read More
  <span class="visually-hidden">
    of the article "Dancing with Rabbits".
  <span>
</a>

Now each link is unique and clear. If the design can support it, do it without the visually hidden class to remove the ambiguity for everyone.

Buttons

Buttons are for triggering actions. When do you use the <button> element? A good rule is to use a button when there is “no meaningful href.” Here’s another way to think of that: if clicking it doesn’t do anything without JavaScript, it should be a <button>.

A <button> that is within a <form>, by default, will submit that form. But aside from that, button elements don't have any default behavior, and you'll be wiring up that interactivity with JavaScript.

Table of Contents

HTML implementation

<button>Buy Now</button>

Buttons inside of a <form> do something by default: they submit the form! They can also reset it, like their input counterparts. The type attributes matter:

<form action="/" method="POST">
  <input type="text" name="name" id="name">
  <button>Submit</button>

  <!-- If you want to be more explicit... -->
  <button type="submit">Submit</button>

  <!-- ...or clear the form inputs back to their initial values -->
  <button type="reset">Reset</button>

  <!-- This prevents a `submit` action from firing which may be useful sometimes inside a form -->
  <button type="button">Non-submitting button</button>
</form>

Speaking of forms, buttons have some neat tricks up their sleeve where they can override attributes of the <form> itself.

<form action="/" method="get">

  <!-- override the action -->
  <button formaction="/elsewhere/" type="submit">Submit to elsewhere</button>

  <!-- override encytype -->
  <button formenctype="multipart/form-data" type="submit"></button>

  <!-- override method -->
  <button formmethod="post" type="submit"></button>

  <!-- do not validate fields -->
  <button formnovalidate type="submit"></button>

  <!-- override target e.g. open in new tab -->
  <button formtarget="_blank" type="submit"></button>

</form>

Autofocus

Since buttons are focusable elements, we can automatically focus on them when the page loads using the autofocus attribute:

<div class="modal">

  <h2>Save document?</h2>

  <button>Cancel</button>
  <button autofocus>OK</button>
</div>

Perhaps you'd do that inside of a modal dialog where one of the actions is a default action and it helps the UX (e.g. you can press Enter to dismiss the modal). Autofocusing after a user action is perhaps the only good practice here, moving a user's focus without their permission, as the autofocus attribute is capable of, can be a problem for screen reader and screen magnifier users.

Note thatautofocus may not work if the element is within an <iframe sandbox> for security reasons.

Disabling buttons

To prevent a button from being interactive, there is a disabled attribute you can use:

<button disabled>Pay Now</button>
<p class="error-message">Correct the form above to submit payment.</p>

Note that we've included descriptive text alongside the disabled button. It can be very frustrating to find a disabled button and not know why it's disabled. A better way to do this could be to let someone submit the form, and then explain why it didn't work in the validation feedback messaging.

Regardless, you could style a disabled button this way:

/* Might be good styles for ANY disabled element! */
button[disabled] {
  opacity: 0.5;
  pointer-events: none;
} 

We'll cover other states and styling later in this guide.

Buttons can contain child elements

A submit button and a submit input (<input type="submit">) are identical in functionality, but different in the sense that an input is unable to contain child elements while a button can.

<button>
   <svg aria-hidden="true" focusable="false">
     <path d="..." />
   </svg>
   <span class="callout">Big</span>
   Sale!
</button>

<button type="button">
  <span role="img" aria-label="Fox">
    🦊
  </span>
  Button
</button>

Note the focusable="false" attribute on the SVG element above. In that case, since the icon is decorative, this will help assistive technology only announce the button's label.

Styling and CSS considerations

Buttons are generally styled to look very button-like. They should look pressable. If you're looking for inspiration on fancy button styles, you'd do well looking at the CodePen Topic on Buttons.

1, 2, 3, 4, 5, 6

Cross-browser/platform button styles

How buttons look by default varies by browser and platform.

Just on macOS: Chrome, Safari, and Firefox (they look the same)
Add border: 0; to those same buttons as above, and we have different styles entirely.

While there is some UX truth to leaving the defaults of form elements alone so that they match that browser/platform's style and you get some affordance for free, designers typically don't like default styles, particularly ones that differ across browsers.

Resetting the default button style

Removing all the styles from a button is easier than you think. You'd think, as a form control, appearance: none; would help, but don't count on that. Actually all: revert; is a better bet to wipe the slate clean.

You can see how a variety of properties are involved

And that's not all of them. Here's a consolidated chunk of what Normalize does to buttons.

button {
  font-family: inherit; /* For all browsers */
  font-size: 100%; /* For all browsers */
  line-height: 1.15; /* For all browsers */
  margin: 0; /* Firefox and Safari have margin */
  overflow: visible; /* Edge hides overflow */
  text-transform: none; /* Firefox inherits text-transform */
  -webkit-appearance: button; /* Safari otherwise prevents some styles */
}

button::-moz-focus-inner {
  border-style: none;
  padding: 0;
}

button:-moz-focusring {
  outline: 1px dotted ButtonText;
}

A consistent .button class

In addition to using reset or baseline CSS, you may want to have a class for buttons that gives you a strong foundation for styling and works across both links and buttons.

.button {
  border: 0;
  border-radius: 0.25rem;
  background: #1E88E5;
  color: white;
  font-family: -system-ui, sans-serif;
  font-size: 1rem;
  line-height: 1.2;
  white-space: nowrap;
  text-decoration: none;
  padding: 0.25rem 0.5rem;
  margin: 0.25rem;
  cursor: pointer;
}

Check out this Pen to see why all these properties are needed to make sure it works correctly across elements.

Button states

Just as with links, you'll want to style the states of buttons.

button:hover { }
button:focus { }
button:active { }
button:visited { } /* Maybe less so */

You may also want to use ARIA attributes for styling, which is a neat way to encourage using them correctly:

button[aria-pressed="true"] { }
button[aria-pressed="false"] { }

Link-styled buttons

There are always exceptions. For example, a website in which you need a button-triggered action within a sentence:

<p>You may open your <button>user settings</button> to change this.</p>

We've used a button instead of an anchor tag in the above code, as this hypothetical website opens user settings in a modal dialog rather than linking to another page. In this situation, you may want to style the button as if it looks like a link.

This is probably rare enough that you would probably make a class (e.g. .link-looking-button) that incorporates the reset styles from above and otherwise matches what you do for anchor links.

Breakout buttons

Remember earlier when we talked about the possibility of wrapping entire elements in links? If you have a button within another element, but you want that entire outer element to be clickable/tappable as if it's the button, that's a "breakout" button. You can use an absolutely-positioned pseudo-element on the button to expand the clickable area to the whole region. Fancy!

CodePen Embed Fallback

JavaScript considerations

Even without JavaScript, button elements can be triggered by the Space and Enter keys on a keyboard. That's part of what makes them such appealing and useful elements: they are discoverable, focusable, and interactive with assistive technology in a predictable way.

Perhaps any <button> in that situation should be inserted into the DOM by JavaScript. A tall order! Food for thought. 🤔

"Once" handlers

Say a button does something pretty darn important, like submitting a payment. It would be pretty scary if it was programmed such that clicking the button multiple times submitted multiple payment requests. It is situations like this where you would attach a click handler to a button that only runs once. To make that clear to the user, we'll disable the button on click as well.

document.querySelector("button").addEventListener('click', function(event) {
  event.currentTarget.setAttribute("disabled", true);
}, {
    once: true
});

Then you would intentionally un-disable the button and reattach the handler when necessary.

Inline handlers

JavaScript can be executed by activating a button through code on the button itself:

<button onclick="console.log('clicked');">
  Log it.
</button>

<button onmousedown="">
</button>

<button onmouseup="">
</button>

That practice went from being standard practice to being a faux pas (not abstracting JavaScript functionality away from HTML) to, eh, you need it when you need it. One advantage is that if you're injecting this HTML into the DOM, you don't need to bind/re-bind JavaScript event handlers to it because it already has one.

JavaScript frameworks

It's common in any JavaScript framework to make a component for handling buttons, as buttons typically have lots of variations. Those variations can be turned into an API of sorts. For example, in React:

const Button = ({ className, children }) => {
  const [activated, setActivated] = React.useState(false);
  return (
    <button
      className={`button ${className}`}
      aria-pressed={activated ? "true" : "false")
      onClick={() => setActivated(!activated)}
    >
      {children}
    </button>
  );
};

In that example, the <Button /> component ensures the button will have a button class and handles a toggle-like active class.

Accessibility considerations

The biggest accessibility consideration with buttons is actually using buttons. Don't try to replicate a button with a <div> or a <span>, which is, unfortunately, more common than you might think. It's very likely that will cause problems. (Did you deal with focusability? Did you deal with keyboard events? Great. There's still probably more stuff you're forgetting.)

Focus styles

Like all focusable elements, browsers apply a default focus style, which is usually a blue outline.

Focus styles on Chrome/macOS

While it's arguable that you should leave that alone as it's a very clear and obvious style for people that benefit from focus styles, it's also not out of the question to change it.

What you should not do is button:focus { outline: 0; } to remove it. If you ever remove a focus style like that, put it back at the same time.

button:focus {
  outline: 0; /* Removes the default blue ring */

  /* Now, let's create our own focus style */
  border-radius: 3px;
  box-shadow: 0 0 0 2px red;
}
Custom focus style

The fact that a button may become focused when clicked and apply that style at the same time is offputting to some. There is a trick (that has limited, but increasing, browser support) on removing focus styles from clicks and not keyboard events:

:focus:not(:focus-visible) { 
  outline: 0; 
}

ARIA

Buttons already have the role they need (role="button"). But there are some other ARIA attributes that are related to buttons:

  • aria-pressed: Turns a button into a toggle, between aria-pressed="true" and aria-pressed="false". More on button toggles, which can also be done with role="switch" and aria-checked="true".
  • aria-expanded: If the button controls the open/closed state of another element (like a dropdown menu), you apply this attribute to indicate that like aria-expanded="true".
  • aria-label: Overrides the text within the button. This is useful for labeling buttons that otherwise don't have text, but you're still probably better off using a visually-hidden class so it can be translated.
  • aria-labelledby: Points to an element that will act as the label for the button.

For that last one:

<button aria-labelledby="buttonText">
  Time is running out! 
  <span id="buttonText">Add to Cart</span>
</button>

Deque has a deeper dive blog post into button accessibility that includes much about ARIA.

Dialogs

If a button opens a dialog, your job is to move the focus inside and trap it there. When closing the dialog, you need to return focus back to that button so the user is back exactly where they started. This makes the experience of using a modal the same for someone who relies on assistive technology as for someone who doesn't.

Focus management isn't just for dialogs, either. If clicking a button runs a calculation and changes a value on the page, there is no context change there, meaning focus should remain on the button. If the button does something like "move to next page," the focus should be moved to the start of that next page.

Size

Don't make buttons too small. That goes for links and any sort of interactive control. People with any sort of reduced dexterity will benefit.

The classic Apple guideline for the minimum size for a touch target (button) is 44x44pt.

Here's some guidelines from other companies. Fitt's Law tells us smaller targets have greater error rates. Google even takes button sizes into consideration when evaluating the SEO of a site.

In addition to ample size, don't place buttons too close each other, whether they're stacked vertically or together on the same line. Give them some margin because people experiencing motor control issues run the risk of clicking the wrong one.

Activating buttons

Buttons work by being clicked/touched, pressing the Enter key, or pressing the Space key (when focused). Even if you add role="button" to a link or div, you won't get the spacebar functionality, so at the risk of beating a dead horse, use <button> in those cases.

The post A Complete Guide to Links and Buttons appeared first on CSS-Tricks.

14:00
This Week In Web Design – February 14, 2020
14:00

Designing Ethically, Optimizing Videos, And Shining The Spotlight On Our SmashingConf Speakers

Designing Ethically, Optimizing Videos, And Shining The Spotlight On Our SmashingConf Speakers

Designing Ethically, Optimizing Videos, And Shining The Spotlight On Our SmashingConf Speakers

Iris Lješnjanin
2020-02-14T14:00:00+00:002020-02-14T16:35:56+00:00

An important part of our job is staying up to date, and we know how difficult it can be. Technologies don’t really change that fast — coding languages take a long time to be specified and implemented. But the ideas surrounding these technologies and the things we can do with them are constantly evolving, and hundreds of blog posts and articles are published every day. There’s no way you can read all of those but you’ll still have to keep up to date.

Fear not, we’ve got your backs! Our bi-weekly Smashing Podcast has you covered with a variety of topics across multiple levels of expertise.

A shout-out and big thank you to both Drew McLellan and Bethany Andrew for making the episodes so brilliantly witty and informative!

Topple the cat wearing headphones and holding a coffee-to-go cup while listening to the Smashing Podcast Catching up with what’s new in the web industry doesn’t mean you have to be tied up to a chair and desk! Do as Topple the Cat does it: grab your headphones and stretch those legs! You can subscribe and tune in anytime with any of your favorite apps.

A Lovely New Addition To The Smashing Books

We’re so proud to be introducing a new book to the Smashing bookshelf — a cover so eloquently designed and a book that covers topics that are very close to our hearts: ethics and privacy.

The “The Ethical Design Handbook” is our new guide on ethical design for digital products that respect customer choices and are built and designed with ethics in mind. It’s full of practical guidelines on how to make ethical decisions to influence positive change and help businesses grow in a sustainable way.

The cover of the Ethical Design HandbookWritten by Trine Falbe, Martin Michael Frederiksen and Kim Andersen. You can download a free PDF excerpt (5 MB) (also available as ePUB and Kindle) and get a sneakpeek beforehand!

Of course, you can jump right over to the table of contents and see for yourself, but be sure to pre-order the book while you can! There’s still a discount available before the official release — we’ll start shipping printed hardcover copies in the first two weeks of March! Stay tuned!

Learning And Networking, The Smashing Way

Our SmashingConfs are known to be friendly, inclusive events where front-end developers and designers come together to attend live sessions and hands-on workshops. From live designing to live debugging, we want you to ask speakers anything — from naming conventions to debugging strategies. For each talk, we’ll have enough time to go into detail, and show real examples from real work on the big screen.

We like to bring you closer to folks working in the web industry, and so every once in a while we interview the speakers who share the stage! For SmashingConf Austin, the spotlight shined on:

  • Miriam Suzanne, who’ll be talking about the wonderful new world of CSS, new techniques and possibilities.
  • Zach Leatherman, who’ll let us in on everything we need to know about type,font performance tooling and general workflow when it comes to web fonts.
  • Rémi Parmentier, who’ll bring us closer to the good ol’ HTML Email, common techniques, state of things and what you can achieve with HTML Email today (if you are willing enough to explore its unconventional world).
Four different illustrations designed by Ricardo Gimenes dressed up in different costumes representing each city where the SmashingConf will take place this year This year, we’re organizing SmashingConf in four cities — see you there?

Shining The Spotlight On Optimizing Video Files

Mark your calendars! In less than two weeks (Feb. 25, we’ll be hosting a Smashing TV webinar with Doug Sillars who’ll be sharing several possible scenarios to optimize video files for fast and efficient playback on the web. Join us at 17:00 London time — we’d love to hear your thoughts and experiences you’ve had in your career!

Smashing TV illustration with Topple the Cat holding a video camera Smashing TV is a series of webinars and live streams packed with practical tips for designers and developers. Think of it as some sort of a Hollywood blockbuster with cats, but only better, because it’s for people like you!

Ricardo Gimenes, the talent behind the illustrations on all things SmashingWe often get asked who the creative talent behind the illustrations is: It’s the one-and-only Ricardo Gimenes, someone we’re ever so proud to have in our team!

Trending Topics On SmashingMag

We publish a new article every day on various topics that are current in the web industry. Here are some that our readers seemed to enjoy the most and have recommended further:

  • How To Create Maps With React And Leaflet
    by Shajia Abidi
    Leaflet is a very powerful tool, and we can create a lot of different kinds of maps. This tutorial will help you understand how to create an advanced map along with the help of React and Vanilla JS.
  • Understanding CSS Grid: Grid Template Areas
    by Rachel Andrew
    In a new series, Rachel Andrew breaks down the CSS Grid Layout specification. This time, she takes us through grid-template-areas and how it can be used to place items.
  • How To Create A Headless WordPress Site On The JAMstack
    by Sarah Drasner & Geoff Graham
    In this post, Sarah and Geoff set up a demo site and tutorial for headless WordPress — including a starter template! They explain how to set up a Vue application with Nuxt, pulling in the posts from our application via the WordPress API.
  • Magic Flip Cards: Solving A Common Sizing Problem
    by Dan Halliday
    In this article, Dan reviews the standard approach to creating animated flip cards and introduces an improved method which solves its sizing problem.

Best Picks From Our Newsletter

With the start of a brand-new decade, we decided to start off with topics dedicated to web performance. There are so many talented folks out there working on brilliant projects, and we’d love to spread the word and give them the credit they deserve!

Note: A huge thank you to Cosima Mielke for writing and preparing these posts!

Tiny Helpers For Web Developers

Minifying an SVG, extracting CSS from HTML, or checking your color palette for accessibility — we all know those moments when we need a little tool to help us complete a task quickly and efficiently. If you ever find yourself in such a situation again, Tiny Helpers might have just the tool you’re looking for.

Tiny Helpers

Maintained by Stefan Judis, Tiny Helpers is a collection of free, single-purpose online tools for web developers. The tools cover everything from APIs, accessibility, and color, to fonts, performance, regular expressions, SVG, and unicode. And if you know of a useful tool that isn’t featured yet, you can submit a pull request with your suggestion. One for the bookmarks.

Real-World Color Palette Inspiration

There are a lot of fantastic sites out there that help you find inspiring color palettes. However, once you have found a palette you like, the biggest question is still left unanswered: how should you apply the colors to your design? Happy Hues is here to help.

Happy Hues

Designed by Mackenzie Child, Happy Hues gives you color palette inspiration while acting as a real-world example for how the colors could be used in your design. Just change the palette, and the Happy Hues site changes its colors to show you what your favorite palette looks like in an actual design. Clever!

Free Usability Heuristics Posters

Back in 1994, Jakob Nielsen wrote an article for Nielsen Norman Group, outlining general principles for interface design: the 10 usability heuristics. Today, decades later, these heuristics still serve as a checklist for interface designers. A fact that inspired the folks at Agente Studio to create a set of posters dedicated to them.

10 Usability Heuristics for User Interface Design

Each of the ten beautifully-designed posters illustrates and explains one of Nielsen’s heuristics. The posters are CC-licensed and can be downloaded and printed for free after you shared the page on social media. JPEG and EPS formats are available.

A Guide To Fighting Online Tracking

It’s no secret that we’re being tracked online. And while we can’t stop all of it, there are things we can do to fight back.

Illustration of a woman sweeping data away Image credit: Rose Wong

In his New York Times article, Tim Herrera dives deeper into the data companies collect about us and how they share it with third parties, into “secret scores” and shocking third-party reports that list our delivery service orders and private Airbnb messages from years ago. Besides being a good reminder to be more wary of handing out our data, the article features links to tools and practical tips for preventing advertiser tracking. A must-read.

The Illustrated Children’s Guide To Kubernetes

Have you ever tried to explain software engineering to a child or to a friend, colleague, or relative who isn’t that tech-savvy? Well, finding easy words to explain a complex concept can be a challenge. A challenge that “The Illustrated Children’s Guide to Kubernetes” masters beautifully.

The Illustrated Children’s Guide to Kubernetes

Designed as a storybook and available to be read online or as a PDF, the free guide tells the story of a PHP app named Phippy who wished she had her own environment, just her and a webserver she could call home. On her journey, she meets Captain Kube who gives her a new home on his ship Kubernetes. A beautiful metaphor to explain the core concept of Kubernetes.

Generator Of Geometric Shapes

To stand out from the crowd of a myriad of websites out there, we can define one unique thing, the signature, that brings a bit of personality into our digital products. Perhaps it’s a little glitch effect, or a pencil scribble, a game or unusual shapes. Or, it could be a set of seemingly random geometric flow lines.

Flow Lines Generator

Flow Lines Generator produces random geometric lines, and we can adjust the formulas and distances between the shapes drawn, and then export the outcome as SVG. Perhaps every single page on your site could have a variation of these lines in some way? It might be enough to stand out from the crowd, mostly because nobody else has that exact visual treatment. It might be worth looking at!

Git From Beginner To Advanced

Most of us will be dealing with Git regularly, sometimes running Git commands from terminal, and sometimes using a visual tool to pull, push, commit and merge. If you feel like you’d like to supercharge your skills and gain a few Git superpowers, where do you start?

Git Cheat Sheet

Mike Riethmueller has published a quick guide to Git, from beginner to advanced, explaining how Git works, how to configure it, aliases, important commands, staging/unstaging files, managing merge conflicts, stashing and a few advanced tips. Need more advanced stuff? Harry Roberts has published “Little Things I Like To Do With Git”, Atlassian has Advanced Git Tutorials, Bruno Passos lists useful git commands, and GitHub provides a Git Cheat Sheet PDF.

The Museum Of Obsolete Media

Do you remember the days when you listened to a music cassette on your Walkman, watched your favorite movie on video tape instead of streaming it, or stored your data on a floppy disk? The media we considered state-of-the-art back then, is obsolete today. And, well, a lot of other formats shared the same fate in the past.

The Museum Of Obsolete Media

In his Museum of Obsolete Media, Jason Curtis collects examples of media that went out of use, not just the ones you might remember, but also real curiosities and treasures dating back as far as to the middle of the 19th century. Things like the “carte de visite”, “Gould Moulded Records”, or “Magnabelt” for example. A fascinating trip back in time.

Each and every issue of the Smashing Newsletter is written and edited with love and care. No third-party mailings or hidden advertising — you’ve got our word.

Smashing Editorial(cm, vf, ra, il)

February 13 2020

15:13

Why JavaScript is Eating HTML

Web development is always changing. One trend in particular has become very popular lately, and it fundamentally goes against the conventional wisdom about how a web page should be made. It is exciting for some but frustrating for others, and the reasons for both are difficult to explain.

A web page is traditionally made up of three separate parts with separate responsibilities: HTML code defines the structure and meaning of the content on a page, CSS code defines its appearance, and JavaScript code defines its behavior. On teams with dedicated designers, HTML/CSS developers and JavaScript developers, this separation of concerns aligns nicely with job roles: Designers determine the visuals and user interactions on a page, HTML and CSS developers reproduce those visuals in a web browser, and JavaScript developers add the user interaction to tie it all together and “make it work.” People can work on one piece without getting involved with all three.

In recent years, JavaScript developers have realized that by defining a page’s structure in JavaScript instead of in HTML (using frameworks such as React), they can simplify the development and maintenance of user interaction code that is otherwise much more complex to build. Of course, when you tell someone that the HTML they wrote needs to be chopped up and mixed in with JavaScript they don’t know anything about, they can (understandably) become frustrated and start asking what the heck we’re getting out of this.

As a JavaScript developer on a cross-functional team, I get this question occasionally and I often have trouble answering it. All of the materials I’ve found on this topic are written for an audience that is already familiar with JavaScript — which is not terribly useful to those who focus on HTML and CSS. But this HTML-in-JS pattern (or something else that provides the same benefits) will likely be around for a while, so I think it’s an important thing that everyone involved in web development should understand.

This article will include code examples for those interested, but my goal is to explain this concept in a way that can be understood without them.

Background: HTML, CSS, and JavaScript

To broaden the audience of this article as much as possible, I want to give a quick background on the types of code involved in creating a web page and their traditional roles. If you have experience with these, you can skip ahead.

HTML is for structure and semantic meaning

HTML (HyperText Markup Language) code defines the structure and meaning of the content on a page. For example, this article's HTML contains the text you're reading right now, the fact that it is in a paragraph, and the fact that it comes after a heading and before a CodePen.

Let’s say we want to build a simple shopping list app. We might start with some HTML like this:

CodePen Embed Fallback

We can save this code in a file, open it in a web browser, and the browser will display the rendered result. As you can see, the HTML code in this example represents a section of a page that contains a heading reading “Shopping List (2 items),” a text input box, a button reading “Add Item,” and a list with two items reading “Eggs” and “Butter.” In a traditional website, a user would navigate to an address in their web browser, then the browser would request this HTML from a server, load it and display it. If there are already items in the list, the server could deliver HTML with the items already in place, like they are in this example.

Try to type something in the input box and click the “Add Item” button. You’ll notice nothing happens. The button isn’t connected to any code that can change the HTML, and the HTML can’t change itself. We’ll get to that in a moment.

CSS is for appearance

CSS (Cascading Style Sheets) code defines the appearance of a page. For example, this article's CSS contains the font, spacing, and color of the text you're reading.

You may have noticed that our shopping list example looks very plain. There is no way for HTML to specify things like spacing, font sizes, and colors. This is where CSS (Cascading Style Sheets) comes in. On the same page as the HTML above, we could add CSS code to style things up a bit:

CodePen Embed Fallback

As you can see, this CSS changed the font sizes and weights and gave the section a nice background color (designers, please don’t @ me; I know this is still ugly). A developer can write style rules like these and they will be applied consistently to any HTML structure: if we add more <section>, <button> or <ul> elements to this page, they will have the same font changes applied.

The button still doesn’t do anything, though: that’s where JavaScript comes in.

JavaScript is for behavior

JavaScript code defines the behavior of interactive or dynamic elements on a page. For example, the embedded CodePen examples in this article are powered by JavaScript.

Without JavaScript, to make the Add Item button in our example work would require us to use special HTML to make it submit data back to the server (<form action="...">, if you’re curious). Then the browser would discard the entire page and reload an updated version of the entire HTML file. If this shopping list was part of a larger page, anything else the user was doing would be lost. Scrolled down? You’re back at the top. Watching a video? It starts over. This is how all web applications worked for a long time: any time a user interacted with a webpage, it was as if they closed their web browser and opened it again. That’s not a big deal for this simple example, but for a large complex page which could take a while to load, it’s not efficient for either the browser or the server.

If we want anything to change on a webpage without reloading the entire page, we need JavaScript (not to be confused with Java, which is an entirely different language… don’t get me started). Let’s try adding some:

CodePen Embed Fallback

Now when we type some text in the box and click the “Add Item” button, our new item is added to the list and the item count at the top is updated! In a real app, we would also add some code to send the new item to the server in the background so that it will still show up the next time we load the page.

Separating JavaScript from the HTML and CSS makes sense in this simple example. Traditionally, even more complicated interactions would be added this way: HTML is loaded and displayed, and JavaScript runs afterwards to add things to it and change it. As things get more complex, however, we start needing to keep better track of things in our JavaScript.

If we were to keep building this shopping list app, next we’d probably add buttons for editing or removing items from the list. Let’s say we write the JavaScript for a button that removes an item, but we forget to add the code that updates the item total at the top of the page. Suddenly we have a bug: after a user removes an item, the total on the page won’t match the list! Once we notice the bug, we fix it by adding that same totalText.innerHTML line from our “Add Item” code to the “Remove Item” code. Now we have the same code duplicated in more than one place. Later on, let’s say we want to change that code so that instead of “(2 items)” at the top of the page it reads “Items: 2.” We’ll have to make sure we update it in all three places: in the HTML, in the JavaScript for the “Add Item” button, and in the JavaScript for the “Remove Item” button. If we don’t, we’ll have another bug where that text abruptly changes after a user interaction.

In this simple example, we can already see how quickly these things can get messy. There are ways to organize our JavaScript to make this kind of problem easier to deal with, but as things continue to get more complex, we’ll need to keep restructuring and rewriting things to keep up. As long as HTML and JavaScript are kept separate, a lot of effort can be required to make sure everything is kept in sync between them. That’s one of the reasons why new JavaScript frameworks, like React, have gained traction: they are designed to show the relationships between things like HTML and JavaScript. To understand how that works, we first need to understand just a teeny bit of computer science.

Two kinds of programming

The key concept to understand here involves the distinction between two common programming styles. (There are other programming styles, of course, but we’re only dealing with two of them here.) Most programming languages lend themselves to one or the other of these, and some can be used in both ways. It's important to grasp both in order to understand the main benefit of HTML-in-JS from a JavaScript developer's perspective.

  • Imperative programming: The word "imperative" here implies commanding a computer to do something. A line of imperative code is a lot like an imperative sentence in English: it gives the computer a specific instruction to follow. In imperative programming, we must tell the computer exactly how to do every little thing we need it to do. In web development, this is starting to be considered "the old way" of doing things and it's what you do with vanilla JavaScript, or libraries like jQuery. The JavaScript in my shopping list example above is imperative code.
    • Imperative: “Do X, then do Y, then do Z”.
    • Example: When the user selects this element, add the .selected class to it; and when the user de-selects it, remove the .selected class from it.
  • Declarative programming: This is a more abstract layer above imperative programming. Instead of giving the computer instructions, we instead "declare" what we want the results to be after the computer does something. Our tools (e.g. React) figure out the how for us automatically. These tools are built with imperative code on the inside that we don't have to pay attention to from the outside.
    • Declarative: “The result should be XYZ. Do whatever you need to do to make that happen.”
    • Example: This element has the .selected class if the user has selected it.

HTML is a declarative language

Forget about JavaScript for a moment. Here's an important fact: HTML on its own is a declarative language. In an HTML file, you can declare something like:

<section>
  <h1>Hello</h1>
  <p>My name is Mike.</p>
</section>

When a web browser reads this HTML, it will figure out these imperative steps for you and execute them:

  1. Create a section element
  2. Create a heading element of level 1
  3. Set the inner text of the heading element to “Hello”
  4. Place the heading element into the section element
  5. Create a paragraph element
  6. Set the inner text of the paragraph element to “My name is Mike”
  7. Place the paragraph element into the section element
  8. Place the section element into the document
  9. Display the document on the screen

As a web developer, the details of how a browser does these things is irrelevant; all that matters is that it does them. This is a perfect example of the difference between these two kinds of programming. In short, HTML is a declarative abstraction wrapped around a web browser's imperative display engine. It takes care of the "how" so you only have to worry about the "what." You can enjoy life writing declarative HTML because the fine people at Mozilla or Google or Apple wrote the imperative code for you when they built your web browser.

JavaScript is an imperative language

We’ve already looked at a simple example of imperative JavaScript in the shopping list example above, and I mentioned how the complexity of an app’s features has ripple effects on the effort required to implement them and the potential for bugs in that implementation. Now let’s look at a slightly more complex feature and see how it can be simplified by using a declarative approach.

Imagine a webpage that contains the following:

  • A list of labelled checkboxes, each row of which changes to a different color when it is selected
  • Text at the bottom like "1 of 4 selected" that should update when the checkboxes change
  • A "Select All" button which should be disabled if all checkboxes are already selected
  • A "Select None" button which should be disabled if no checkboxes are selected

Here’s an implementation of this in plain HTML, CSS and imperative JavaScript:

CodePen Embed Fallback

There isn’t much CSS code here because I’m using the wonderful PatternFly design system, which provides most of the CSS for my example. I imported their CSS file in the CodePen settings.

All the small things

In order to implement this feature with imperative JavaScript, we need to give the browser several granular instructions. This is the English-language equivalent to the code in my example above:

  • In our HTML, we declare the initial structure of the page:
    • There are four row elements, each containing a checkbox. The third box is checked.
    • There is some summary text which reads "1 of 4 selected."
    • There is a "Select All" button which is enabled.
    • There is a "Select None" button which is disabled.
  • In our JavaScript, we write instructions for what to change when each of these events occurs:
    • When a checkbox changes from unchecked to checked:
      • Find the row element containing the checkbox and add the .selected CSS class to it.
      • Find all the checkbox elements in the list and count how many are checked and how many are not checked.
      • Find the summary text element and update it with the checked number and the total number.
      • Find the "Select None" button element and enable it if it was disabled.
      • If all checkboxes are now checked, find the "Select All" button element and disable it.
    • When a checkbox changes from checked to unchecked:
      • Find the row element containing the checkbox and remove the .selected class from it.
      • Find all the checkbox elements in the list and count how many are checked and not checked.
      • Find the summary text element and update it with the checked number and the total number.
      • Find the "Select All" button element and enable it if it was disabled.
      • If all checkboxes are now unchecked, find the "Select None" button element and disable it.
    • When the "Select All" button is clicked:
      • Find all the checkbox elements in the list and check them all.
      • Find all the row elements in the list and add the .selected class to them.
      • Find the summary text element and update it.
      • Find the "Select All" button and disable it.
      • Find the "Select None" button and enable it.
    • When the "Select None" button is clicked:
      • Find all the checkbox elements in the list and uncheck them all.
      • Find all the row elements in the list and remove the .selected class from them.
      • Find the summary text element and update it.
      • Find the "Select All" button and enable it.
      • Find the "Select None" button and disable it.

Wow. That's a lot, right? Well, we better remember to write code for each and every one of those things. If we forget or screw up any of those instructions, we will end up with a bug where the totals don't match the checkboxes, or a button is enabled that doesn't do anything when you click it, or a row ends up with the wrong color, or something else we didn’t think of and won’t find out about until a user complains.

The big problem here is that there is no single source of truth for the state of our app, which in this case is “which checkboxes are checked?” The checkboxes know whether or not they are checked, of course, but, the row styles also have to know, the summary text has to know, and each button has to know. Five copies of this information are stored separately all around the HTML, and when it changes in any of those places the JavaScript developer needs to catch that and write imperative code to keep the others in sync.

This is still only a simple example of one small component of a page. If that sounds like a headache, imagine how complex and fragile an application becomes when you need to write the whole thing this way. For many complex modern web applications, it’s not a scalable solution.

Moving towards a single source of truth

Tools, like React, allow us to use JavaScript in a declarative way. Just as HTML is a declarative abstraction wrapped around the web browser’s display instructions, React is a declarative abstraction wrapped around JavaScript.

Remember how HTML let us focus on the structure of a page and not the details of how the browser displays that structure? Well, when we use React, we can focus on the structure again by defining it based on data stored in a single place. When that source of truth changes, React will update the structure of the page for us automatically. It will take care of the imperative steps behind the scenes, just like the web browser does for HTML. (Although React is used as an example here, this concept is not unique to React and is used by other frameworks, such as Vue.)

Let's go back to our list of checkboxes from the example above. In this case, the truth we care about is simple: which checkboxes are checked? The other details on the page (e.g. what the summary says, the color of the rows, whether or not the buttons are enabled) are effects derived from that same truth. So, why should they need to have their own copy of this information? They should just use the single source of truth for reference, and everything on the page should "just know" which checkboxes are checked and conduct themselves accordingly. You might say that the row elements, summary text, and buttons should all be able to automatically react to a checkbox being checked or unchecked. (See what I did there?)

Tell me what you want (what you really, really want)

In order to implement this page with React, we can replace the list with a few simple declarations of facts:

  • There is a list of true/false values called checkboxValues that represents which boxes are checked.
    • Example:  checkboxValues = [false, false, true, false]
    • This list represents the truth that we have four checkboxes, and that the third one is checked.
  • For each value in checkboxValues, there is a row element which:
    • has a CSS class called .selected if the value is true, and
    • contains a checkbox, which is checked if the value is true.
  • There is a summary text element that contains the text "{x} of {y} selected" where {x} is the number of true values in checkboxValues and {y} is the total number of values in checkboxValues.
  • There is a "Select All" button that is enabled if there are any false values in checkboxValues.
  • There is a "Select None" button that is enabled if there are any true values in checkboxValues.
  • When a checkbox is clicked, its corresponding value changes in checkboxValues.
  • When the "Select All" button is clicked, it sets all values in checkboxValues to true.
  • When the "Select None" button is clicked, it sets all values in checkboxValues to false.

You'll notice that the last three items are still imperative instructions ("When this happens, do that"), but that's the only imperative code we need to write. It's three lines of code, and they all update the single source of truth. The rest of those bullets are declarations ("there is a...") which are now built right into the definition of the page's structure. In order to do this, we write our elements in a special JavaScript syntax provided by React called JSX, which resembles HTML but can contain JavaScript logic. That gives us the ability to mix logic like "if" and "for each" with the HTML structure, so the structure can be different depending on the contents of checkboxValues at any given time.

Here’s the same shopping list example as above, this time implemented with React:

CodePen Embed Fallback

JSX is definitely weird. When I first encountered it, it just felt wrong. My initial reaction was, “What the heck is this? HTML doesn’t belong in JavaScript!” I wasn’t alone. That said, it’s not HTML, but rather JavaScript dressed up to look like HTML. It is also quite powerful.

Remember that list of 20 imperative instructions above? Now we have three. For the price of defining our HTML inside our JavaScript, the rest of them come for free. React just does them for us whenever checkboxValues changes.

With this code, it is now impossible for the summary to not match the checkboxes, or for the color of a row to be wrong, or for a button to be enabled when it should be disabled. There is an entire category of bugs which are now impossible for us to have in our app: sources of truth being out of sync. Everything flows down from the single source of truth, and we developers can write less code and sleep better at night. Well, JavaScript developers can, at least…

It's a trade-off

As web applications become more complex, maintaining the classic separation of concerns between HTML and JavaScript comes at an increasingly painful cost. HTML was originally designed for static documents, and in order to add more complex interactive functionality to those documents, imperative JavaScript has to keep track of more things and become more confusing and fragile.

The upside: predictability, reusability and composition

The ability to use a single source of truth is the most important benefit of this pattern, but the trade-off gives us other benefits, too. Defining elements of our page as JavaScript code means that we can turn chunks of it into reusable components, preventing us from copying and pasting the same HTML in multiple places. If we need to change a component, we can make that change in one place and it will update everywhere in our application (or in many applications, if we’re publishing reusable components to other teams).

We can take these simple components and compose them together like LEGO bricks, creating more complex and useful components, without making them too confusing to work with. And if we’re using components built by others, we can easily update them when they release improvements or fix bugs without having to rewrite our code.

The downside: it’s JavaScript all the way down

All of those benefits do come at a cost. There are good reasons people value keeping HTML and JavaScript separate, and to get these other benefits, we need to combine them into one. As I mentioned before, moving away from simple HTML files complicates the workflow of someone who didn’t need to worry about JavaScript before. It may mean that someone who previously could make changes to an application on their own must now learn additional complex skills to maintain that autonomy.

There can also be technical downsides. For example, some tools like linters and parsers expect regular HTML, and some third-party imperative JavaScript plugins can become harder to work with. Also, JavaScript isn’t the best-designed language; it’s just what we happen to have in our web browsers. Newer tools and features are making it better, but it still has some pitfalls you need to learn about before you can be productive with it.

Another potential problem is that when the semantic structure of a page is broken up into abstract components, it can become easy for developers to stop thinking about what actual HTML elements are being generated at the end. Specific HTML tags like <section> and <aside> have specific semantic meanings that are lost when using generic tags like <div> and <span>, even if they look the same visually on a page. This is especially important for accessibility. For example, these choices will impact how screen reader software behaves for visually impaired users. It might not be the most exciting part, but JavaScript developers should always remember that semantic HTML is the most important part of a web page.

Use it if it helps you, not because it’s “what’s hot right now”

It’s become a trend for developers to reach for frameworks on every single project. Some people are of the mindset that separating HTML and JavaScript is obsolete, but this isn’t true. For a simple static website that doesn’t need much user interaction, it’s not worth the trouble. The more enthusiastic React fans might disagree with me here, but if all your JavaScript is doing is creating a non-interactive webpage, you shouldn’t be using JavaScript. JavaScript doesn’t load as fast as regular HTML, so if you’re not getting a significant developer experience or code reliability improvement, it’s doing more harm than good.

You also don’t have to build your entire website in React! Or Vue! Or Whatever! A lot of people don’t know this because all the tutorials out there show how to use React for the whole thing. If you only have one little complex widget on an otherwise simple website, you can use React for that one component. You don’t always need to worry about webpack or Redux or Gatsby or any of the other crap people will tell you are “best practices” for your React app.

For a sufficiently complex application, declarative programming is absolutely worth the trouble. It is a game changer that has empowered developers the world over to build amazing, robust and reliable software with confidence and without having to sweat the small stuff. Is React in particular the best possible solution to these problems? No. Will it just be replaced by the next thing? Eventually. But declarative programming is not going anywhere, and the next thing will probably just do it better.

What’s this I’ve heard about CSS-in-JS?

I’m not touching that one.

The post Why JavaScript is Eating HTML appeared first on CSS-Tricks.

15:13

The Unseen Performance Costs of Modern CSS-in-JS Libraries

This article is full of a bunch of data from Aggelos Arvanitakis. But lemme just focus on his final bit of advice:

Investigate whether a zero-runtime CSS-in-JS library can work for your project. Sometimes we tend to prefer writing CSS in JS for the DX (developer experience) it offers, without a need to have access to an extended JS API. If you app doesn’t need support for theming and doesn’t make heavy and complex use of the css prop, then a zero-runtime CSS-in-JS library might be a good candidate.

"Zero-runtime" meaning you author your styles in a CSS-in-JS syntax, but what is produced is .css files like any other CSS preprocessor would produce. This shifts the tool into a totally different category. It's a developer tool only, rather than a tool where the user of the website pays the price of using it.

The flagship zero-runtime CSS-in-JS library is Linaria. I think the syntax looks really nice.

import { css } from 'linaria';
import fonts from './fonts';

const header = css`
  text-transform: uppercase;
  font-family: ${fonts.heading};
`;

<h1 className={header}>Hello world</h1>;

I'm also a fan of the just do scoping for me ability that CSS modules brings, which can be done zero-runtime style.

Direct Link to ArticlePermalink

The post The Unseen Performance Costs of Modern CSS-in-JS Libraries appeared first on CSS-Tricks.

15:12

A Headless CMS for You and Your Web Development Agency

(This is a sponsored post.)

Storyblok is a headless but component-based CMS with a built-in live-preview. You can use it for building fast and reliable websites and power native apps with your favorite technology.

Let us start with the basics and what a headless CMS is:

A headless content management system is a back-end only content management system (CMS) built from the ground up as a content repository that makes content accessible via a RESTful or GraphQL API for display on any device.

At Storyblok, you can select from a large amount of already existing tutorials that will get you started. Jump into the free plan and start modeling your content. Each space can have an unlimited amount of content types and components that you can define.

Storyblok also enables you with more built-in tools like the free image optimization service to speed up your website performance and overall progress.

As a web development agency, you can also sign-up for the partner program. This will allow you and your team to have an unlimited amount of free staff members. You will be able to create as many development spaces for pitches and proof of concepts as you like. The best thing, you will not only get those users and spaces for free - you will also earn a monthly revenue share for every subscription your customers do at Storyblok.

Try Storyblok yourself

Direct Link to ArticlePermalink

The post A Headless CMS for You and Your Web Development Agency appeared first on CSS-Tricks.

11:00

Setting Up API Mocking With Mirage JS And Vue.js

Setting Up API Mocking With Mirage JS And Vue.js

Setting Up API Mocking With Mirage JS And Vue.js

Kelvin Omereshone
2020-02-13T11:00:00+00:002020-02-13T14:36:43+00:00

In the era of SPA and the JAMstack, there has always been a separation of concern between the APIs and front-end development. Almost all JavaScript projects that can be found out in the wild interact with a web service or API and either use it for authentications or getting user-related data.

So, whenever you’re working on a project and the necessary API still hasn’t been implemented by the back-end team or you need to quickly test a feature, you have some of the following options:

  • You could proxy to a locally running version of your actual backend which, in most cases as a front-end developer, you wouldn’t have.
  • You could comment out actual request and replace with mock data. (This is okay but not so great as you would need to undo that to get to production and you might not be able to deal with network states and latency.)

What Is API Mocking?

API mocking is an imitation or a simulation of an actual API. It is mostly done in order to intercept requests that are supposed to be made to an actual backend API but this mocking exist on your frontend.

Why is API Mocking Important

API mocking is significantly important in a lot of ways:

  1. It makes for a very good front-end development experience not to depend on production APIs before building out features.
  2. You could share your entire frontend and it would work without depending on an actual backend API.

What Is Mirage JS?

Mirage JS was created 5 years ago and was pretty much used in the Ember community before Sam Selikoff officially announced its release on the 24th of January, 2020 on Twitter.

Mirage JS solves the pain point for testing backend APIs without depending on those APIs. It allows for seamless front-end development experience by mocking production APIs.

Mirage JS is an API mocking library for Vue.js, React, Angular and Ember frameworks

What Makes Mirage JS A Better Choice?

There have been other options for API mocking (such as Axios interceptors, Typicode’s JSON server, and so on) but what I think is pretty interesting about Mirage is that it doesn’t get in the way of your development process (as you would see when we set it up with Vue in a bit). It is lightweight and yet powerful.

It comes with battery included out of the box that allows you to replicate real production API consumption scenarios like simulating a slow network with its timing option.

Getting Started With Mirage JS And Vue.js

So now that you know what Mirage JS is and why it’s important to your front-end development workflow, let’s look at setting it up with the progressive web framework: Vue.js.

Creating A Green-Field (Clean Installation) Vue Project

Using the Vue CLI, create a fresh Vue.js project by going into the directory you want the project to be created in and running (in your terminal):

vue create miragejs-demo-vue 

The above command would set up a new Vue project which you can now cd into and run either yarn serve or npm run serve.

#Installing Mirage JS

Now let’s install Mirage JS as a development dependency in our Vue.js project by running the following command:

yarn add -D miragejs

Or if you are using NPM, run this:

npm install --save-dev miragejs

And that’s it! Mirage JS is now installed in our Vue.js project.

Let’s Mock Out Something

With Mirage JS installed, let’s see how we configure it to talk to Vue and mock out a basic todos (an API that returns a list of todos) API.

Define Your Server

To get started, we need to create a server.js file in the /src directory of our Vue.js project. After that, add the following:

import { Server, Model } from 'miragejs'

export function makeServer({ environment = "development" } = {}) {

let server = new Server({
  environment,

    models: {
      todo: Model,
    },

  seeds(server) {
  server.create("todo", { content: "Learn Mirage JS" })
  server.create("todo", { content: "Integrate With Vue.js" })
  },

  routes() {

    this.namespace = "api"

    this.get("/todos", schema => {
      return schema.todos.all()
    })
    
  },
  })

  return server
}

Code Explained

Firstly, the server.js file is how you set up Mirage JS to create a new instance of it’s mock (fake) server which will intercept all API calls you make in your app matching the routes that you define.

Now, I agree the above may be overwhelming at first, but let’s take a closer look at what’s going on here:

import { Server, Model } from 'miragejs'

From the above code snippet, we are importing Server and Model from miragejs.

  • Server
    This is a class exposed by Mirage to helps us instantiate a new instance of a Mirage JS server to “serve” as our fake server.
  • Model
    Another class exposed by Mirage to help in creating models(a model determines the structure of a Mirage JS database entry) powered by Mirage’s ORM.
export function makeServer({ environment = "development" } = {}) {}

The above basically export a function called makeServer from the src/server.js. You could also note we are passing in an environment parameter and setting Mirage’s environment mode to development (you would see us passing test environment later on in this article).

The Body Of makeServer

Now we are doing a couple of things in the makeServer body. Let’s take a look:

let server = new Server({})

We are instantiating a new instance of the Server class and passing it a configuration option. The content of the configuration options help set up mirage:

{
  environment,

  models: {
    todo: Model,
  },

  seeds(server) {
  server.create("todo", { content: "Learn Mirage JS" })
  server.create("todo", { content: "Integrate With Vue.js" })
  },

  routes() {

    this.namespace = "api"

    this.get("/todos", schema => {
      return schema.todos.all()
    })
  },
  
  }

Firstly we are passing the environment parameter we initialized in the function definition.

models: {
    todo: Model,
  },

The next option which is the models option takes an object of the different models we want Mirage to mock.

In the above, we simply want a todo model which we are instantiating from the Model class.

seeds(server) {
server.create("todo", { content: "Learn Mirage JS" })
server.create("todo", { content: "Integrate With Vue.js" })
},

The next option is the seeds method which takes in a parameter called server. The seeds method helps create seeds(seeds are initial data or an entry into Mirage’s database) for our models. In our case to create the seeds for the todo model we do:

server.create("todo", { content: "Learn Mirage JS" })
server.create("todo", { content: "Integrate With Vue.js" })

so the server has a create method which expects as the first argument a string which corresponds to the name of the model, then an object which will contain the properties or attributes of a particular seed.

routes() {

    this.namespace = "api"

    this.get("/todos", schema => {
      return schema.todos.all()
    })
  },

finally, we have the routes method which defines the various route(routes are our mock API endpoints) Mirage JS is going to mock. Let’s look at the body of the method:

this.namespace = "api"

this line sets up the namespace for all routes that means our todo route can now be accessed from /api/todos.

this.get("/todos", schema => {
  return schema.todos.all()
})

The above creates a get route and it’s handler using the this.get() method. The get() method expects the route i.e “/todos” and a handler function that takes in schema as an argument. The schema object is how you interact with Mirage’s ORM which is powered by Mirage JS in-memory database.

Finally:

return schema.todos.all()

We are returning a list of all our todos, using the schema object made possible by Mirage’s ORM.

src/main.js

So we are done setting up src/server.js but Vue doesn’t know about it(at least not yet). So let’s import it in our main.js file like so:

import { makeServer } from "./server"

Then we call the makeServer function like so:

if (process.env.NODE_ENV === "development") {
  makeServer()
}

The above if conditional is a guard to make sure mirage only runs in development.

Set up Complete!

Now we’ve set up Miragejs with Vue. Let’s see it in action. In our App.vue file, we would clear out the content and replace with the below snippet:

<template>
  <ul id="todos">
    <li v-for="todo in todos" v-bind:key="todo.id">{{ todo.content }}</li>
  </ul>
</template>

<script>
  export default {
    name: 'app',

    data() {
      return {
        todos: []
      }
    },

    created() {
      fetch("/api/todos")
        .then(res => res.json())
        .then(json => {
          this.todos = json.todos
        })
    }
  }
</script>

If you are familiar with Vue.js, the above would be nothing new but for the sake of being total, what we are doing is making an API request using fetch when our App.vue component is created, then we pass in the returned data to the todos array in our component state. Afterward, we are using a v-for to iterate the todos array and displaying the content property of each todo.

Where Is The Mirage JS Part?

If you notice, in our App.vue component, we didn’t do anything specific to Mirage, we are just making an API call as we would normally do. This feature of Mirage is really great for DX cause under the hood, Mirage would intercept any requests that match any of the routes defined in src/server.js while you are in development.

This is pretty handy cause no work would be needed in your part to switch to an actual production server when you are in a production environment provided the routes match your production API endpoints.

So restart your Vue dev server via yarn serve to test Mirage JS.

You should see a list of two todos. One thing you’d find pretty interesting is that we did not need to run a terminal command to start up Mirage because it removes that overhead by running as a part of your Vue.js application.

Mirage JS And Vue test-utils

If you are already using Vue Test-utils in your Vue application, you’d find it exciting to know that Mirage can easily work with it to mock network requests. Let’s see an example set up using our todos application.

We would be using Jest for our unit-testing. So if you are following along, you could pretty much use the Vue CLI to install the @vue/unit-jest plugin like so:

vue add @vue/unit-jest

The above will install @vue/cli-plugin-unit-jest and @vue/test-utils development dependencies while also creating a tests directory and a jest.config.js file. It will also add the following command in our package.json scripts section (pretty neat):

"test:unit": "vue-cli-service test:unit"

Let’s Test!

We would update our App.vue to look like this:

<!-- src/App.vue -->
<template>
  <div v-if="serverError" data-testid="server-error">
    {{ serverError }}
  </div>

  <div v-else-if="todos.length === 0" data-testid="no-todos">
    No todos!
  </div>

  <div v-else>
    <ul id="todos">
      <li
        v-for="todo in todos"
        v-bind:key="todo.id"
        :data-testid="'todo-' + todo.id"
      >
        {{ todo.content }}
      </li>
    </ul>
  </div>
</template>

<script>
  export default {
    name: "app",

    data() {
      return {
        todos: [],
        serverError: null,
      }
    },

    created() {
      fetch("/api/todos")
        .then(res => res.json())
        .then(json => {
          if (json.error) {
            this.serverError = json.error
          } else {
            this.todos = json.todos
          }
        })
    },
  }
</script>

Nothing really epic is going on in the above snippet; we are just structuring to allow for the network testing we would be implementing with our unit test.

Although Vue CLI has already added a /tests folder for us, I find it to be a much better experience when my tests are placed close to the components they are testing. So create a /__tests__ folder in src/ and create an App.spec.js file inside it. (This is also the recommended approach by Jest.)

// src/__tests__/App.spec.js
import { mount } from "@vue/test-utils"
import { makeServer } from "../server"
import App from "../App.vue"

let server

beforeEach(() => {
  server = makeServer({ environment: "test" })
})

afterEach(() => {
  server.shutdown()
})

So to set up our unit testing, we are importing the mount method from @vue/test-utils, importing the Miragejs server we created earlier and finally importing the App.vue component.

Next, we are using the beforeEach life cycle function to start the Mirage JS server while passing in the test environment. (Remember, we set by default the environment to be development.)

Lastly, we are shutting down the server using server.shutdown in the afterEach lifecycle method.

Our Tests

Now let’s flesh out our test (we would be adopting the quickstart section of the Mirage js docs. So your App.spec.js would finally look like this:

// src/__tests__/App.spec.js

import { mount } from "@vue/test-utils"
import { makeServer } from "./server"
import App from "./App.vue"

let server

beforeEach(() => {
  server = makeServer({ environment: "test" })
})

it("shows the todos from our server", async () => {
  server.create("todo", { id: 1, content: "Learn Mirage JS" })
  server.create("todo", { id: 2, content: "Integrate with Vue.js" })

  const wrapper = mount(App)

  // let’s wait for our vue component to finish loading data
  // we know it’s done when the data-testid enters the dom.
  await waitFor(wrapper, '[data-testid="todo-1"]')
  await waitFor(wrapper, '[data-testid="todo-2"]')

  expect(wrapper.find('[data-testid="todo-1"]').text()).toBe("Learn Mirage JS")
  expect(wrapper.find('[data-testid="todo-2"]').text()).toBe("Integrate with Vue.js")
})

it("shows a message if there are no todo", async () => {
  // Don’t create any todos

  const wrapper = mount(App)
  await waitFor(wrapper, '[data-testid="no-todos"]')

  expect(wrapper.find('[data-testid="no-todos"]').text()).toBe("No todos!")
})

// This helper method returns a promise that resolves
// once the selector enters the wrapper’s dom.
const waitFor = function(wrapper, selector) {
  return new Promise(resolve => {
    const timer = setInterval(() => {
      const todoEl = wrapper.findAll(selector)
      if (todoEl.length > 0) {
        clearInterval(timer)
        resolve()
      }
    }, 100)
  })
}

afterEach(() => {
  server.shutdown()
})

Note: We are using a helper here (as defined in the Mirage JS docs). It returns a promise that allows us to know when the elements we are testing are already in the DOM.

Now run yarn test:unit.

All your tests should pass at this point.

Testing Different Server State With Mirage JS

We could alter our Mirage JS server to test for different server states. Let’s see how.

// src/__tests__/App.spec.js
import { Response } from "miragejs"

First, we import the Response class from Mirage, then we create a new test scenario like so:

it("handles error responses from the server", async () => {
  // Override Mirage’s route handler for /todos, just for this test
  server.get("/todos", () => {
    return new Response(
      500,
      {},
      {
        error: "The database is taking a break.",
      }
    )
  })

  const wrapper = mount(App)

  await waitFor(wrapper, '[data-testid="server-error"]')

  expect(wrapper.find('[data-testid="server-error"]').text()).toBe(
    "The database is taking a break."
  )
})

Run your test and it all should pass.

Conclusion

This article aimed to introduce you to Mirage JS and show you how it makes for a better front-end development experience. We saw the problem Mirage JS created to address (building production-ready front-end without any actual backend API) and how to set it up with Vue.js.

Although this article scratched the surface of what Mirage JS can do, I believe it is enough to get you started.

  • You could go through the docs as well as join the Mirage JS discord server.
  • The supporting repo for this article is available on GitHub.

References

Smashing Editorial(dm, il)

February 12 2020

23:09

2020 Stack

In an article with the most clickbaity article ever, Joe Honton does a nice job of talking about the evolving landscape of web development. "Full-stack" perhaps had its day as a useful term, but since front-end development touches so many parts of the stack now, it's not a particularly useful term. Joe himself did a lot to popularize it, so it does feel extra meaningful coming from him.

Plus the spectrum of how much there is to know is so wide we can't all know it all, so to get things done, we take what we do know and slot ourselves into cross-functional teams.

Since no one person can handle it all, the 2020 stack must be covered by a team. Not a group of individuals, but a true team. That means that when one person is falling behind, another will pick up the slack. When one person has superior skills, there’s a mechanism in place for mentoring the others. When there’s a gap in the team’s knowledge-base, they seek out and hire a team member who’s smarter than all of them.

So the "2020 Stack" is essentially "know things and work on teams" more so than any particular combination of technologies. That said, Joe does have opinions on technologies, including writing HTML in some weird GraphQL looking syntax that I'd never seen before.

Direct Link to ArticlePermalink

The post 2020 Stack appeared first on CSS-Tricks.

16:39

Toward Responsive Elements

Hot news from Brian Kardell, regarding what we've been referring to as "container queries", the most hotly requested feature in CSS:

There does seem to be some general agreement on at least one part of what I am going to call instead "Responsive Design for Components" and that is that flipping the problem on its head is better.

The flipping here sounds like instead of doing it as media query, we do it at the property level.

.foo {
  display: grid;
  grid-template-columns: switch(
    (available-inline-size > 1024px) 1fr 4fr 1fr;
    (available-inline-size > 400px) 2fr 1fr;
    (available-inline-size > 100px) 1fr;
    default 1fr;
  );
}

I think this is still in the ideation phase, and other people are ideating as well on different ideas, but this seems worth sharing to me as it sounds like it has traction and is doable because it isn't plagued with well that's just not how browsers work that came up a lot with container queries.

Brian also talks about "lightspeed progress" lately, for example:

Consider that we shifted the stalemate conversation and ResizeObserver was envisioned, incubated, speced, tested, agreed upon, iterated on (we got things wrong!) and implemented in all browsers in about 2 years

Two years is smokin' in standards.

Direct Link to ArticlePermalink

The post Toward Responsive Elements appeared first on CSS-Tricks.

15:13

Building an Images Gallery using PixiJS and WebGL

Sometimes, we have to go a little further than HTML, CSS and JavaScript to create the UI we need, and instead use other resources, like SVG, WebGL, canvas and others.

For example, the most amazing effects can be created with WebGL, because it’s a JavaScript API designed to render interactive 2D and 3D graphics within any compatible web browser, allowing GPU-accelerated image processing.

That said, working with WebGL can be very complex. As such, there’s a variety of libraries that to make it relatively easier, such as PixiJSThree.js, and Babylon.js, among others.We’re going to work with a specific one of those, PixiJS, to create a gallery of random images inspired by this fragment of a Dribbble shot by Zhenya Rynzhuk.

This looks hard, but you actually don’t need to have advanced knowledge of WebGL or even PixiJS to follow along, though some basic knowledge of Javascript (ES6) will come in handy. You might even want to start by getting familiar with  the basic concept of fragment shaders used in WebGL, with The Book of Shaders as a good starting point.

With that, let’s dig into using PixiJS to create this WebGL effect!

Initial setup

Here’s what we’ll need to get started:

  1. Add the PixiJS library as a script in the HTML.
  2. Have a <canvas> element (or add it dynamically from Javascript), to render the application.
  3. Initialize the application with new PIXI.Application(options).

See, nothing too crazy yet. Here’s the JavaScript we can use as a boilerplate:

// Get canvas view
const view = document.querySelector('.view')
let width, height, app

// Set dimensions
function initDimensions () {
  width = window.innerWidth
  height = window.innerHeight
}

// Init the PixiJS Application
function initApp () {
  // Create a PixiJS Application, using the view (canvas) provided
  app = new PIXI.Application({ view })
  // Resizes renderer view in CSS pixels to allow for resolutions other than 1
  app.renderer.autoDensity = true
  // Resize the view to match viewport dimensions
  app.renderer.resize(width, height)
}

// Init everything
function init () {
  initDimensions()
  initApp()
}

// Initial call
init()

When executing this code, the only thing that we will see is a black screen as well as a message like this in the if we open up the console:
PixiJS 5.0.2 - WebGL 2 - http://www.pixijs.com/.

We are ready to start drawing on the canvas using PixiJS and WebGL!

Creating the grid background with a WebGL Shader

Next we will create a background that contains a grid, which will allow us to clearly visualize the distortion effect we’re after. But first, we must know what a shader is and how it works.I recommended The Book of Shaders earlier as a starting point to learn about them and this is those concepts will come into play. If you have not done it yet, I strongly recommend that you review that material, and only then continue here.

We are going to create a fragment shader that prints a grid background on the screen:

// It is required to set the float precision for fragment shaders in OpenGL ES
// More info here: https://stackoverflow.com/a/28540641/4908989
#ifdef GL_ES
precision mediump float;
#endif

// This function returns 1 if `coord` correspond to a grid line, 0 otherwise
float isGridLine (vec2 coord) {
  vec2 pixelsPerGrid = vec2(50.0, 50.0);
  vec2 gridCoords = fract(coord / pixelsPerGrid);
  vec2 gridPixelCoords = gridCoords * pixelsPerGrid;
  vec2 gridLine = step(gridPixelCoords, vec2(1.0));
  float isGridLine = max(gridLine.x, gridLine.y);
  return isGridLine;
}

// Main function
void main () {
  // Coordinates for the current pixel
  vec2 coord = gl_FragCoord.xy;
  // Set `color` to black
  vec3 color = vec3(0.0);
  // If it is a grid line, change blue channel to 0.3
  color.b = isGridLine(coord) * 0.3;
  // Assing the final rgba color to `gl_FragColor`
  gl_FragColor = vec4(color, 1.0);
}

This code is drawn from a demo on Shadertoy,  which is a great source of inspiration and resources for shaders.

In order to use this shader, we must first load the code from the file it is in and — only after it has been loaded correctly— we will initialize the app.

// Loaded resources will be here
const resources = PIXI.Loader.shared.resources

// Load resources, then init the app
PIXI.Loader.shared.add([
  'shaders/backgroundFragment.glsl'
]).load(init)

Now, for our shader to work where we can see the result, we will add a new element (an empty Sprite) to the stage, which we will use to define a filter. This is the way PixiJS lets us execute custom shaders like the one we just created.

// Init the gridded background
function initBackground () {
  // Create a new empty Sprite and define its size
  background = new PIXI.Sprite()
  background.width = width
  background.height = height
  // Get the code for the fragment shader from the loaded resources
  const backgroundFragmentShader = resources['shaders/backgroundFragment.glsl'].data
  // Create a new Filter using the fragment shader
  // We don't need a custom vertex shader, so we set it as `undefined`
  const backgroundFilter = new PIXI.Filter(undefined, backgroundFragmentShader)
  // Assign the filter to the background Sprite
  background.filters = [backgroundFilter]
  // Add the background to the stage
  app.stage.addChild(background)
}

And now we see the gridded background with blue lines. Look closely because the lines are a little faint against the dark background color.

CodePen Embed Fallback

The distortion effect 

Our background is now ready, so let's see how we can add the desired effect (Cubic Lens Distortion) to the whole stage, including the background and any other element that we add later, like images. For this, need to create a new filter and add it to the stage. Yes, we can also define filters that affect the entire stage of PixiJS!

This time, we have based the code of our shader on this awesome Shadertoy demo that implements the distortion effectusing different configurable parameters.

#ifdef GL_ES
precision mediump float;
#endif

// Uniforms from Javascript
uniform vec2 uResolution;
uniform float uPointerDown;

// The texture is defined by PixiJS
varying vec2 vTextureCoord;
uniform sampler2D uSampler;

// Function used to get the distortion effect
vec2 computeUV (vec2 uv, float k, float kcube) {
  vec2 t = uv - 0.5;
  float r2 = t.x * t.x + t.y * t.y;
  float f = 0.0;
  if (kcube == 0.0) {
    f = 1.0 + r2 * k;
  } else {
    f = 1.0 + r2 * (k + kcube * sqrt(r2));
  }
  vec2 nUv = f * t + 0.5;
  nUv.y = 1.0 - nUv.y;
  return nUv;
}

void main () {
  // Normalized coordinates
  vec2 uv = gl_FragCoord.xy / uResolution.xy;

  // Settings for the effect
  // Multiplied by `uPointerDown`, a value between 0 and 1
  float k = -1.0 * uPointerDown;
  float kcube = 0.5 * uPointerDown;
  float offset = 0.02 * uPointerDown;
  
  // Get each channel's color using the texture provided by PixiJS
  // and the `computeUV` function
  float red = texture2D(uSampler, computeUV(uv, k + offset, kcube)).r;
  float green = texture2D(uSampler, computeUV(uv, k, kcube)).g;
  float blue = texture2D(uSampler, computeUV(uv, k - offset, kcube)).b;
  
  // Assing the final rgba color to `gl_FragColor`
  gl_FragColor = vec4(red, green, blue, 1.0);
}

We are using two uniforms this time. Uniforms are variables that we pass to the shader via JavaScript:

  • uResolution: This is a JavaScript object thaincludes {x: width, y: height}. This uniform allows us to normalize the coordinates of each pixel in the range [0, 1].
  • uPointerDown: This is a float in the range [0, 1], which allows us to animate the distortion effect, increasing its intensity proportionally.

Let's see the code that we have to add to our JavaScript to see the distortion effect caused by our new shader:

// Target for pointer. If down, value is 1, else value is 0
// Here we set it to 1 to see the effect, but initially it will be 0
let pointerDownTarget = 1
let uniforms

// Set initial values for uniforms
function initUniforms () {
  uniforms = {
    uResolution: new PIXI.Point(width, height),
    uPointerDown: pointerDownTarget
  }
}

// Set the distortion filter for the entire stage
const stageFragmentShader = resources['shaders/stageFragment.glsl'].data
const stageFilter = new PIXI.Filter(undefined, stageFragmentShader, uniforms)
app.stage.filters = [stageFilter]

We can already enjoy our distortion effect!

CodePen Embed Fallback

This effect is static at the moment, so it’s not terribly fun just yet. Next, we'll see how we can make the effect dynamically respond to pointer events.

Listening to pointer events

PixiJS makes it’s surprisingly simple to listen to events, even multiple events that respond equally to mouse and touch interactions. In this case, we want our animation to work just as well on desktop as on a mobile device, so we must listen to the events corresponding to both platforms.

PixiJs provides an interactive attribute that lets us do just that. We apply it to an element and start listening to events with an API similar to jQuery:

// Start listening events
function initEvents () {
  // Make stage interactive, so it can listen to events
  app.stage.interactive = true

  // Pointer & touch events are normalized into
  // the `pointer*` events for handling different events
  app.stage
    .on('pointerdown', onPointerDown)
    .on('pointerup', onPointerUp)
    .on('pointerupoutside', onPointerUp)
    .on('pointermove', onPointerMove)
}

From here, we will start using a third uniform (uPointerDiff), which will allow us to explore the image gallery using drag and drop. Its value will be equal to the translation of the scene as we explore the gallery. Below is the code corresponding to each of the event handling functions:

// On pointer down, save coordinates and set pointerDownTarget
function onPointerDown (e) {
  console.log('down')
  const { x, y } = e.data.global
  pointerDownTarget = 1
  pointerStart.set(x, y)
  pointerDiffStart = uniforms.uPointerDiff.clone()
}

// On pointer up, set pointerDownTarget
function onPointerUp () {
  console.log('up')
  pointerDownTarget = 0
}

// On pointer move, calculate coordinates diff
function onPointerMove (e) {
  const { x, y } = e.data.global
  if (pointerDownTarget) {
    console.log('dragging')
    diffX = pointerDiffStart.x + (x - pointerStart.x)
    diffY = pointerDiffStart.y + (y - pointerStart.y)
  }
}

We still will not see any animation if we look at our work, but we can start to see how the messages that we have defined in each event handler function are correctly printed in the console.

Let's now turn to implementing our animations!

Animating the distortion effect and the drag and drop functionality

The first thing we need to start an animation with PixiJS (or any canvas-based animation) is an animation loop. It usually consists of a function that is called continuously, using requestAnimationFrame, which in each call renders the graphics on the canvas element, thus producing the desired animation.

We can implement our own animation loop in PixiJS, or we can use the utilities included in the library. In this case, we will use the add method of app.ticker, which allows us to pass a function that will be executed in each frame. At the end of the init function we will add this:

// Animation loop
// Code here will be executed on every animation frame
app.ticker.add(() => {
  // Multiply the values by a coefficient to get a smooth animation
  uniforms.uPointerDown += (pointerDownTarget - uniforms.uPointerDown) * 0.075
  uniforms.uPointerDiff.x += (diffX - uniforms.uPointerDiff.x) * 0.2
  uniforms.uPointerDiff.y += (diffY - uniforms.uPointerDiff.y) * 0.2
})

Meanwhile, in the Filter constructor for the background, we will pass the uniforms in the stage filter. This allows us to simulate the translation effect of the background with this tiny modification in the corresponding shader:

uniform vec2 uPointerDiff;

void main () {
  // Coordinates minus the `uPointerDiff` value
  vec2 coord = gl_FragCoord.xy - uPointerDiff;

  // ... more code here ...
}

And now we can see the distortion effect in action, including the drag and drop functionality for the gridd background. Play with it!

CodePen Embed Fallback

Randomly generate a masonry grid layout

To make our UI more interesting, we can randomly generate the sizing and dimensions of the grid cells. That is, each image can have different dimensions, creating a kind of masonry layout.

Let’s use Unsplash Source, which will allow us to get random images from Unsplash and define the dimensions we want. This will facilitate the task of creating a random masonry layout, since the images can have any dimension that we want, and therefore, generate the layout beforehand.

To achieve this, we will use an algorithm that executes the following steps:

  1. We will start with a list of rectangles.
  2. We will select the first rectangle in the list divide it into two rectangles with random dimensions, as long as both rectangles have dimensions equal to or greater than the minimum established limit. We’ll add a check to make sure it’s possible and, if it is, add both resulting rectangles to the list.
  3. If the list is empty, we will finish executing. If not, we’ll go back to step two.

I think you’ll get a much better understanding of how the algorithm works in this next demo. Use the buttons to see how it runs: Next  will execute step two, All will execute the entire algorithm, and Reset will reset to step one.

CodePen Embed Fallback

Drawing solid rectangles

Now that we can properly generate our random grid layout, we will use the list of rectangles generated by the algorithm to draw solid rectangles in our PixiJS application. That way, we can see if it works and make adjustments before adding the images using the Unsplash Source API.

To draw those rectangles, we will generate a random grid layout that is five times bigger than the viewport and position it in the center of the stage. That allows us to move with some freedom to any direction in the gallery.

// Variables and settings for grid
const gridSize = 50
const gridMin = 3
let gridColumnsCount, gridRowsCount, gridColumns, gridRows, grid
let widthRest, heightRest, centerX, centerY, rects

// Initialize the random grid layout
function initGrid () {
  // Getting columns
  gridColumnsCount = Math.ceil(width / gridSize)
  // Getting rows
  gridRowsCount = Math.ceil(height / gridSize)
  // Make the grid 5 times bigger than viewport
  gridColumns = gridColumnsCount * 5
  gridRows = gridRowsCount * 5
  // Create a new Grid instance with our settings
  grid = new Grid(gridSize, gridColumns, gridRows, gridMin)
  // Calculate the center position for the grid in the viewport
  widthRest = Math.ceil(gridColumnsCount * gridSize - width)
  heightRest = Math.ceil(gridRowsCount * gridSize - height)
  centerX = (gridColumns * gridSize / 2) - (gridColumnsCount * gridSize / 2)
  centerY = (gridRows * gridSize / 2) - (gridRowsCount * gridSize / 2)
  // Generate the list of rects
  rects = grid.generateRects()
}

So far, we have generated the list of rectangles. To add them to the stage, it is convenient to create a container, since then we can add the images to the same container and facilitate the movement when we drag the gallery.

Creating a container in PixiJS is like this:

let container

// Initialize a Container element for solid rectangles and images
function initContainer () {
  container = new PIXI.Container()
  app.stage.addChild(container)
}

Now we can now add the rectangles to the container so they can be displayed on the screen.

// Padding for rects and images
const imagePadding = 20

// Add solid rectangles and images
// So far, we will only add rectangles
function initRectsAndImages () {
  // Create a new Graphics element to draw solid rectangles
  const graphics = new PIXI.Graphics()
  // Select the color for rectangles
  graphics.beginFill(0xAA22CC)
  // Loop over each rect in the list
  rects.forEach(rect => {
    // Draw the rectangle
    graphics.drawRect(
      rect.x * gridSize,
      rect.y * gridSize,
      rect.w * gridSize - imagePadding,
      rect.h * gridSize - imagePadding
    )
  })
  // Ends the fill action
  graphics.endFill()
  // Add the graphics (with all drawn rects) to the container
  container.addChild(graphics)
}

Note that we have added to the calculations a padding (imagePadding) for each rectangle. In this way the images will have some space among them.

Finally, in the animation loop, we need to add the following code to properly define the position for the container:

// Set position for the container
container.x = uniforms.uPointerDiff.x - centerX
container.y = uniforms.uPointerDiff.y - centerY

And now we get the following result:

CodePen Embed Fallback

But there are still some details to fix, like defining limits for the drag and drop feature. Let’s add this to the onPointerMove event handler, where we effectively check the limits according to the size of the grid we have calculated:

diffX = diffX > 0 ? Math.min(diffX, centerX + imagePadding) : Math.max(diffX, -(centerX + widthRest))
diffY = diffY > 0 ? Math.min(diffY, centerY + imagePadding) : Math.max(diffY, -(centerY + heightRest))

Another small detail that makes things more refined is to add an offset to the grid background. That keeps the blue grid lines in tact. We just have to add the desired offset (imagePadding / 2 in our case) to the background shader this way:

// Coordinates minus the `uPointerDiff` value, and plus an offset
vec2 coord = gl_FragCoord.xy - uPointerDiff + vec2(10.0);

And we will get the final design for our random grid layout:

CodePen Embed Fallback

Adding images from Unsplash Source

We have our layout ready, so we are all set to add images to it. To add an image in PixiJS, we need a Sprite, which defines the image as a Texture of it. There are multiple ways of doing this. In our case, we will first create an empty Sprite for each image and, only when the Sprite is inside the viewport, we will load the image, create the Texture and add it to the Sprite. Sound like a lot? We’ll go through it step-by-step.

To create the empty sprites, we will modify the initRectsAndImages function. Please pay attention to the comments for a better understanding:

// For the list of images
let images = []

// Add solid rectangles and images
function initRectsAndImages () {
  // Create a new Graphics element to draw solid rectangles
  const graphics = new PIXI.Graphics()
  // Select the color for rectangles
  graphics.beginFill(0x000000)
  // Loop over each rect in the list
  rects.forEach(rect => {
    // Create a new Sprite element for each image
    const image = new PIXI.Sprite()
    // Set image's position and size
    image.x = rect.x * gridSize
    image.y = rect.y * gridSize
    image.width = rect.w * gridSize - imagePadding
    image.height = rect.h * gridSize - imagePadding
    // Set it's alpha to 0, so it is not visible initially
    image.alpha = 0
    // Add image to the list
    images.push(image)
    // Draw the rectangle
    graphics.drawRect(image.x, image.y, image.width, image.height)
  })
  // Ends the fill action
  graphics.endFill()
  // Add the graphics (with all drawn rects) to the container
  container.addChild(graphics)
  // Add all image's Sprites to the container
  images.forEach(image => {
    container.addChild(image)
  })
}

So far, we only have empty sprites. Next, we will create a function that's responsible for downloading an image and assigning it as Texture to the corresponding Sprite. This function will only be called if the Sprite is inside the viewport so that the image only downloads when necessary.

On the other hand, if the gallery is dragged and a Sprite is no longer inside the viewport during the course of the download, that request may be aborted, since we are going to use an AbortController (more on this on MDN). In this way, we will cancel the unnecessary requests as we drag the gallery, giving priority to the requests corresponding to the sprites that are inside the viewport at every moment.

Let's see the code to land the ideas a little better:

// To store image's URL and avoid duplicates
let imagesUrls = {}

// Load texture for an image, giving its index
function loadTextureForImage (index) {
  // Get image Sprite
  const image = images[index]
  // Set the url to get a random image from Unsplash Source, given image dimensions
  const url = `https://source.unsplash.com/random/${image.width}x${image.height}`
  // Get the corresponding rect, to store more data needed (it is a normal Object)
  const rect = rects[index]
  // Create a new AbortController, to abort fetch if needed
  const { signal } = rect.controller = new AbortController()
  // Fetch the image
  fetch(url, { signal }).then(response => {
    // Get image URL, and if it was downloaded before, load another image
    // Otherwise, save image URL and set the texture
    const id = response.url.split('?')[0]
    if (imagesUrls[id]) {
      loadTextureForImage(index)
    } else {
      imagesUrls[id] = true
      image.texture = PIXI.Texture.from(response.url)
      rect.loaded = true
    }
  }).catch(() => {
    // Catch errors silently, for not showing the following error message if it is aborted:
    // AbortError: The operation was aborted.
  })
}

Now we need to call the loadTextureForImage function for each image whose corresponding Sprite is intersecting with the viewport. In addition, we will cancel the fetch requests that are no longer needed, and we will add an alpha transition when the rectangles enter or leave the viewport.

// Check if rects intersects with the viewport
// and loads corresponding image
function checkRectsAndImages () {
  // Loop over rects
  rects.forEach((rect, index) => {
    // Get corresponding image
    const image = images[index]
    // Check if the rect intersects with the viewport
    if (rectIntersectsWithViewport(rect)) {
      // If rect just has been discovered
      // start loading image
      if (!rect.discovered) {
        rect.discovered = true
        loadTextureForImage(index)
      }
      // If image is loaded, increase alpha if possible
      if (rect.loaded && image.alpha < 1) {
        image.alpha += 0.01
      }
    } else { // The rect is not intersecting
      // If the rect was discovered before, but the
      // image is not loaded yet, abort the fetch
      if (rect.discovered && !rect.loaded) {
        rect.discovered = false
        rect.controller.abort()
      }
      // Decrease alpha if possible
      if (image.alpha > 0) {
        image.alpha -= 0.01
      }
    }
  })
}

And the function that verifies if a rectangle is intersecting with the viewport is the following:

// Check if a rect intersects the viewport
function rectIntersectsWithViewport (rect) {
  return (
    rect.x * gridSize + container.x <= width &&
    0 <= (rect.x + rect.w) * gridSize + container.x &&
    rect.y * gridSize + container.y <= height &&
    0 <= (rect.y + rect.h) * gridSize + container.y
  )
}

Last, we have to add the checkRectsAndImages function to the animation loop:

// Animation loop
app.ticker.add(() => {
  // ... more code here ...

  // Check rects and load/cancel images as needded
  checkRectsAndImages()
})
CodePen Embed Fallback

Our animation is nearly ready!

Handling changes in viewport size

When initializing the application, we resized the renderer so that it occupies the whole viewport, but if the viewport changes its size for any reason (for example, the user rotates their mobile device), we should re-adjust the dimensions and restart the application.

// On resize, reinit the app (clean and init)
// But first debounce the calls, so we don't call init too often
let resizeTimer
function onResize () {
  if (resizeTimer) clearTimeout(resizeTimer)
  resizeTimer = setTimeout(() => {
    clean()
    init()
  }, 200)
}
// Listen to resize event
window.addEventListener('resize', onResize)

The clean function will clean any residuals of the animation that we were executing before the viewport changed its dimensions:

// Clean the current Application
function clean () {
  // Stop the current animation
  app.ticker.stop()
  // Remove event listeners
  app.stage
    .off('pointerdown', onPointerDown)
    .off('pointerup', onPointerUp)
    .off('pointerupoutside', onPointerUp)
    .off('pointermove', onPointerMove)
  // Abort all fetch calls in progress
  rects.forEach(rect => {
    if (rect.discovered && !rect.loaded) {
      rect.controller.abort()
    }
  })
}

In this way, our application will respond properly to the dimensions of the viewport, no matter how it changes. This gives us the full and final result of our work!

CodePen Embed Fallback

Some final thoughts

Thanks for taking this journey with me! We walked through a lot but we learned a lot of concepts along the way and walked out with a pretty neat piece of UI. You can check the code on GitHub, or play with demos on CodePen.

If you have worked with WebGL before (with or without using other libraries), I hope you saw how nice it is working with PixiJS. It abstracts the complexity associated with the WebGL world in a great way, allowing us to focus on what we want to do rather than the technical details to make it work.

Bottom line is that PixiJS brings the world of WebGL closer for front-end developers to grasp, opening up a lot of possibilities beyond HTML, CSS and JavaScript.

The post Building an Images Gallery using PixiJS and WebGL appeared first on CSS-Tricks.

15:13

Neumorphism.io

I was sort of making fun of neumorphism the other day. I don't actually care that much. I mostly just think it only works on some flat colored backgrounds, mostly really light colors, and somehow that feels weirdly limiting. (nope, sure, sure, nope 🤷‍♂️).

Anyway, I think this "generator" from Adam Giebl (Adam Gieblio maybe?) is really quite cool.

Direct Link to ArticlePermalink

The post Neumorphism.io appeared first on CSS-Tricks.

14:30
LearnDash Review: Excellent Learning Management System (LMS) Plugin For WordPress
11:30

Understanding CSS Grid: Grid Template Areas

Understanding CSS Grid: Grid Template Areas

Understanding CSS Grid: Grid Template Areas

Rachel Andrew
2020-02-12T11:30:00+00:002020-02-12T13:07:02+00:00

When using CSS Grid Layout, you can always place items from one grid line to another. However, there is an alternate way of describing your layout, one that is visual in nature. In this article, we will learn how to use the grid-template-areas property to define placement on the grid and find out how the property really works.

In case you missed the previous articles in this series, you can find them over here:

Describing Layout With grid-template-areas

The grid-template-areas property accepts one or more strings as a value. Each string (enclosed in quotes) represents a row of your grid. You can use the property on a grid that you have defined using grid-template-rows and grid-template-columns, or you can create your layout in which case all rows will be auto-sized.

The following property and value describe a grid with four areas — each spanning two column tracks and two row tracks. An area is caused to span multiple tracks by repeating the name in all of the cells that you would like it to cover:

grid-template-areas: "one one two two"
                     "one one two two"
                     "three three four four"
                     "three three four four";

Items are placed into the layout by being named with an indent using the grid-area property. Therefore, if I want to place an element with a class of test into the area of the grid named one, I use the following CSS:

.test {
  grid-area: one;
}

You can see this in action in the CodePen example shown below. I have four items (with classes one to four); these are assigned to the relevant grid area using the grid-area property and therefore display on the grid in the correct boxes.

See the Pen Simple grid-template-areas example by Rachel Andrew (@rachelandrew) on CodePen.

See the Pen Simple grid-template-areas example by Rachel Andrew (@rachelandrew) on CodePen.

If you use the Firefox Grid Inspector, then you can see the area names and the grid lines demonstrating that each item does indeed span two row and two column tracks — all without doing any line-based positioning on the item itself.

A grid of four items with the Firefox Grid Inspector highlighting the lines Each items spans two rows and two columns

Rules For Using grid-template-areas

There are a few rules when creating a layout in this way. Breaking the rules will make the value invalid and therefore your layout will not happen. The first rule is that you must describe a complete grid, i.e. every cell on your grid must be filled.

If you do want to leave a cell (or cells) as empty space, you do this by inserting a . or series such as ... with no space between them.

Therefore, if I change the value of grid-template-areas as follows:

grid-template-areas: "one one two two"
                     "one one two two"
                     ". . four four"
                     "three three four four";

I now have two cells with no content in them. Item three only displays in the last row of the grid.

One item now only spans one row with the Firefox Grid Inspector highlighting the lines There is now whitespace in the grid

You can only define each area once, meaning that you can’t use this property to copy content into two places on the grid! So the following value would be invalid and cause the entire property to be ignored as we have duplicated the area three:

grid-template-areas: "one one three three"
                     "one one two two"
                     "three three four four"
                     "three three four four";

You can’t create a non-rectangular area, so the property can’t be used to create an ‘L’ or ‘T’ shaped area — making the following value also invalid:

grid-template-areas: "one one two two"
                     "one one one one"
                     "three three four four"
                     "three three four four";

Formatting The Strings

I like to display the value of grid-template-areas as I have above (with each string representing a row below the row before). This gives me a visual representation of what the layout will be.

To help with this, it is valuable to add additional whitespace characters between each cell, and also to use multiple . characters denoting empty cells.

In the value below, I have used multiple whitespace characters between smaller words, and also multiple . characters so the empty cells line up:

grid-template-areas: "one   one   two  two"
                     "one   one   two  two"
                     "..... ..... four four"
                     "three three four four";

That said, it is also completely valid to have all of the strings on one line, so we could write our example as follows:

grid-template-areas: "one one two two" "one one two two" "three three four four" "three three four four";

Explaining grid-template-areas And grid-area

The reason that each area needs to be a complete rectangle is that it needs to be the same shape that you could create by using line-based placement. If we stick with our example above, we could make this layout with grid lines as in the next CodePen. Here I have created my grid as before. This time, however, I used grid lines to create the positioning using the longhand grid-column-start, grid-column-end, grid-row-start and grid-row-end properties.

See the Pen Grid Line placement by Rachel Andrew (@rachelandrew) on CodePen.

See the Pen Grid Line placement by Rachel Andrew (@rachelandrew) on CodePen.

Note: If you read my previous article “Understanding CSS Grid: Grid Lines” you will know that it is possible to use grid-area as a shorthand for declaring all four lines at once.

This means that we could also create our layout with the following order of lines:

  • grid-row-start
  • grid-column-start
  • grid-row-end
  • grid-column-end
.one {
  grid-area: 1 / 1 / 3 / 3;
}

.two {
  grid-area: 1 / 3 / 3 / 5;
}

.three {
  grid-area: 3 / 1 / 5 / 3;
}

.four {
  grid-area: 3 / 3 / 5 / 5;
}

The grid-area property is interesting as it can take line numbers and line names. It is also important to understand the different way it behaves when in each mode.

Using grid-area With Line Numbers

If you use the grid-area property with line numbers, then the lines are assigned in the order described above.

If you miss off any values — therefore providing 1, 2 or 3 line numbers — missing values are set to auto which means that the area will span 1 track (that being the default). So the following CSS would place an item grid-row-start: 3 with all other values set to auto, therefore, the item would be auto-placed in the first available column track, and span one row track and one column track.

grid-area: 3;

Using grid-area With Idents

If you use an ident (which is what a named area is called in Grid Layout), then the grid-area property also takes four lines. If you have named lines on your grid as described in “Understanding CSS Grid: Creating A Grid Container”, then you can use these named lines in the same way as numbered lines.

However, what happens when you miss off some lines is different to when you use idents and not numbers.

Below, I have created a grid with named lines and used grid-area to place an item (missing off the final value):

.grid {
  display: grid;
  grid-template-columns:
      [one-start three-start] 1fr 1fr
      [one-end three-end two-start four-start] 1fr 1fr [two-end four-end];
  grid-template-rows:
    [one-start two-start] 100px 100px
    [one-end two-end three-start four-start] 100px 100px [three-end four-end];;
}

.two {
  grid-area: two-start / two-start / two-end;
}

This means that we are missing the line name for grid-column-end. The spec says that in this situation, grid-column-end should use a copy of grid-column-start. If grid-column-end and grid-column-start are identical, then the end line is thrown away, and essentially the value is set to auto so we span one track as in the numbered version.

The same thing happens if we miss off the third value grid-row-end; it becomes the same as grid-row-start and therefore becomes auto.

Take a look at the next CodePen example of how each grid-area is used and how this then changes the layout of the item:

See the Pen Missing idents in grid-area by Rachel Andrew (@rachelandrew) on CodePen.

See the Pen Missing idents in grid-area by Rachel Andrew (@rachelandrew) on CodePen.

This then explains why grid-area works with a single value ident representing an area name.

When we create a named area with the grid-template-areas property, the edge of each area can be referenced by a line name which is the same as the area name you used. In our case, we could take our area named one and place our item using named lines as follows:

.one {
  grid-row-start: one;
  grid-row-end: one;
  grid-column-start: one;
  grid-row-end: one;
}

If the line is a -start line, then one resolves to the start end of the column or row. If it is an -end line, then one resolves to the end line of the column or row.

This means that when we say grid-area: one, we have omitted the last three values for the grid-area shorthand; they all end up being copies of the first value — all in our case become one and the item is placed just as with our longhand usage.

The way that naming works in Grid Layout is clever and enables some interesting things, which I have written about in my previous articles “Naming Things In CSS Grid Layout” and “Editorial Design Patterns With CSS Grid And Named Columns”.

Layering Items When Using grid-template-areas

Only one name can occupy each cell when using grid-template-areas, however, you can still add additional items to the grid after doing your main layout in this way. You can use the line numbers as usual.

In the below CodePen example, I have added an additional item and positioned it using line-based positioning over the items already positioned:

See the Pen Placing an item with line numbers by Rachel Andrew (@rachelandrew) on CodePen.

See the Pen Placing an item with line numbers by Rachel Andrew (@rachelandrew) on CodePen.

You can also use lines names defined when creating your usual columns or rows. Even better, you’ll have some line names created by the formation of the areas. We’ve already seen how you can get four line names with the name of the area. You also get a line on the start edge of each area with -start appended to the name of the area, and a line at the end edge of each area with -end appended.

Therefore, the area named one has start edge lines named one-start and end edge lines named one-end.

You can then use these implicit line names to place an item on the grid. This can be useful if you are redefining the grid at different breakpoints as long as you always want the placed item to come after a certain line name.

See the Pen Placing an item with implicit line names by Rachel Andrew (@rachelandrew) on CodePen.

See the Pen Placing an item with implicit line names by Rachel Andrew (@rachelandrew) on CodePen.

Using Grid Template Areas In Responsive Design

I often work with building up components in a component library and I find that using grid-template-areas can be helpful in terms of being able to see exactly what a component will look like from the CSS. It is also very straightforward to redefine the component at different breakpoints by redefining the value of grid-template-areas sometimes in addition to changing the number of available column tracks.

In the CSS below, I have defined a single column layout for my component. Next, at a minimum width of 600px, I redefine the number of columns and also the value of grid-template-areas in order to create a layout with two columns. The nice thing about this approach is that anyone looking at this CSS can see how the layout works!

.wrapper {
  background-color: #fff;
  padding: 1em;
  display: grid;
  gap: 20px;
  grid-template-areas:
    "hd"
    "bd"
    "sd"
    "ft";

}

@media (min-width: 600px) {
  .wrapper {
    grid-template-columns: 3fr 1fr;
    grid-template-areas:
      "hd hd"
      "bd sd"
      "ft ft";
  }
}

header { grid-area: hd; }
article {grid-area: bd; }
aside { grid-area: sd; }
footer { grid-area: ft; }

Accessibility

You need to be aware when using this method that it is very easy to move things around and cause the problem of disconnecting the visual display from the underlying source order. Anyone tabbing around the site, or who is watching the screen while having the content spoken, will be using the order that things are in the source. By moving the display from that order, you could create a very confusing, disconnected experience. Don’t use this method to move things around without also ensuring that the source is in a sensible order and matching the visual experience.

Summary

That’s the lowdown on using the grid-template-area and grid-area properties to create layouts. If you haven’t used this layout method before, give it a try. I find that it is a lovely way to experiment with layouts and often use it when prototyping a layout — even if for one reason or another we will ultimately use a different method for the production version.

Overflow And Data Loss In CSS

CSS is designed to keep your content readable. Let’s explore situations in which you might encounter overflow in your web designs and how CSS has evolved to create better ways to manage and design around unknown amounts of content. Read article →

Smashing Editorial(il)

February 11 2020

23:09

The Hooks of React Router

React Router 5 embraces the power of hooks and has introduced four different hooks to help with routing. You will find this article useful if you are looking for a quick primer on the new patterns of React Router. But before we look at hooks, we will start off with a new route rendering pattern.

Before React Router 5

// When you wanted to render the route and get router props for component
<Route path="/" component={Home} />


// Or when you wanted to pass extra props
<Route path="/" render={({ match }) => <Profile match={match} mine={true} />}>

When using the component syntax, route props (match, location and history) are implicitly being passed on to the component. But it has to be changed to render once you have extra props to pass to the component. Note that adding an inline function to the component syntax would lead to the component re-mounting on every render.

After React Router 5

<Route path="/">
  <Home />
</Route>

Note that there is no implicit passing of any props to the Home component. But without changing anything with the Route itself, you can add any extra props to the Home component. You can no longer make the mistake of re-mounting the component on every render and that's the best kind of API.

But now route props are not being passed implicitly, so how do we access match, history or location? Do we have to wrap all components with withRouter? That is where the hooks steps in.

Note that hooks were introduced in 16.8 version of React, so you need to be above that version to use them.

useHistory

  • Provides access to the history prop in React Router
  • Refers to the history package dependency that the router uses
  • A primary use case would be for programmatic routing with functions, like push, replace, etc.
import { useHistory } from 'react-router-dom';

function Home() {
  const history = useHistory();
  return <button onClick={() => history.push('/profile')}>Profile</button>;
}

useLocation

  • Provides access to the location prop in React Router
  • It is similar to window.location in the browser itself, but this is accessible everywhere as it represents the Router state and location.
  • A primary use case for this would be to access the query params or the complete route string.
import { useLocation } from 'react-router-dom';

function Profile() {
  const location = useLocation();
  useEffect(() => {
    const currentPath = location.pathname;
    const searchParams = new URLSearchParams(location.search);
  }, [location]);
  return <p>Profile</p>;
}

Since the location property is immutable, useEffect will call the function every time the route changes, making it perfect to operate on search parameters or current path.

useParams

  • Provides access to search parameters in the URL
  • This was possible earlier only using match.params.
import { useParams, Route } from 'react-router-dom';

function Profile() {
  const { name } = useParams();
  return <p>{name}'s Profile</p>;
}

function Dashboard() {
  return (
    <>
      <nav>
        <Link to={`/profile/ann`}>Ann's Profile</Link>
      </nav>
      <main>
        <Route path="/profile/:name">
          <Profile />
        </Route>
      </main>
    </>
  );
}

useRouteMatch

  • Provides access to the match object
  • If it is provided with no arguments, it returns the closest match in the component or its parents.
  • A primary use case would be to construct nested paths.
import { useRouteMatch, Route } from 'react-router-dom';

function Auth() {
  const match = useRouteMatch();
  return (
    <>
      <Route path={`${match.url}/login`}>
        <Login />
      </Route>
      <Route path={`${match.url}/register`}>
        <Register />
      </Route>
    </>
  );
}

You can also use useRouteMatch to access a match without rendering a Route. This is done by passing it the location argument.

For example, consider that you need your own profile to be rendered at /profile and somebody else's profile if the URL contains the name of the person /profile/dan or /profile/ann. Without using the hook, you would either write a Switch, list both routes and customize them with props. But now, using the hook we can do this:

import {
  Route,
  BrowserRouter as Router,
  Link,
  useRouteMatch,
} from 'react-router-dom';

function Profile() {
  const match = useRouteMatch('/profile/:name');

  return match ? <p>{match.params.name}'s Profile</p> : <p>My own profile</p>;
}

export default function App() {
  return (
    <Router>
      <nav>
        <Link to="/profile">My Profile</Link>
        <br />
        <Link to={`/profile/ann`}>Ann's Profile</Link>
      </nav>
      <Route path="/profile">
        <Profile />
      </Route>
    </Router>
  );
}

You can also use all the props on Route like exact or sensitive as an object with useRouteMatch.

Wrapping up

The hooks and explicit Route comes with a hidden advantage in itself. After teaching these techniques at multiple workshops, I have come to the realization that these help avoid a lot of confusion and intricacies that came with the earlier patterns. There are suddenly far fewer unforced errors. They will surely help make your router code more maintainable and you will find it way easier to upgrade to new React Router versions.

The post The Hooks of React Router appeared first on CSS-Tricks.

15:14

How to Fetch and Parse RSS Feeds in JavaScript

Say you have an RSS feed like this one. The goal is to request that RSS feed, parse it, and do something useful with the data in it. RSS is XML, and XML is arguably not as easy to work with than JSON. While a lot of APIs offer JSON responses, it's less typical for RSS, although it does exist.

Let's get it done.

First, it's probably smart to validate the feed. That way you know at least you're working with a working response.

Then we'll need to make a network request to the URL the RSS feed lives at. Let's use JavaScript's native fetch API since that's the most widely applicable. It definitely works in the browser, and it looks like Node has a very popular implementation.

What we'll do is:

  1. Call the URL
  2. First parse the response as text
  3. Then parse the text with DOMParser()
  4. Then use the data like we would if we had a normal DOM reference
const RSS_URL = `https://codepen.io/picks/feed/`;

fetch(RSS_URL)
  .then(response => response.text())
  .then(str => new window.DOMParser().parseFromString(str, "text/xml"))
  .then(data => console.log(data))

We can do our work in that function. RSS is sorta like HTML in that it is nested elements. Our data will be something like this:

<rss>
  <channel>
    <title>Feed Title</title>
    <item>
       <link>https://codepen.io/billgil/pen/ewqWzY</link>
       <title>A sad rain cloud</title>
       <dc:creator>Bill Gilmore</dc:creator>
    </item>
    <!-- a bunch more items -->
  </channel>
</rss>

So, we can querySelectorAll for those <item>elements and loop over them to do what we please. Here, I'll make a bunch of <article> elements as a template and then plop them onto a webpage:

fetch(RSS_URL)
  .then(response => response.text())
  .then(str => new window.DOMParser().parseFromString(str, "text/xml"))
  .then(data => {
    console.log(data);
    const items = data.querySelectorAll("item");
    let html = ``;
    items.forEach(el => {
      html += `
        <article>
          <img src="${el.querySelector("link").innerHTML}/image/large.png" alt="">
          <h2>
            <a href="${el.querySelector("link").innerHTML}" target="_blank" rel="noopener">
              ${el.querySelector("title").innerHTML}
            </a>
          </h2>
        </article>
      `;
    });
    document.body.insertAdjacentHTML("beforeend", html);
  });

Here's a demo of that working:

CodePen Embed Fallback

I've always thought jQuery made for a nice Ajax library, plus it has some helpers all around. This is how you'd do it in jQuery.

const RSS_URL = `https://codepen.io/picks/feed/`;

$.ajax(RSS_URL, {
  accepts: {
    xml: "application/rss+xml"
  },

  dataType: "xml",

  success: function(data) {
    $(data)
      .find("item")
      .each(function() {
        const el = $(this);

        const template = `
          <article>
            <img src="${el.find("link").text()}/image/large.png" alt="">
            <h2>
              <a href="${el
                .find("link")
                .text()}" target="_blank" rel="noopener">
                ${el.find("title").text()}
              </a>
            </h2>
          </article>
        `;

        document.body.insertAdjacentHTML("beforeend", template);
      });
  }
});

CodePen Embed Fallback

If you're going to do this for real on a production site, I'd say it's a smidge weird to rely on a third-party API (and I consider RSS an API) to render important stuff on your site. I'd probably make the request server-side on some kind of timer (CRON), cache it, then have your front-end use data from that cache. Safer and faster.

The post How to Fetch and Parse RSS Feeds in JavaScript appeared first on CSS-Tricks.

15:14

SFTP & Database Access on WordPress.com

(This is a sponsored post.)

Wait what?

That's right, direct access to the files and data storage that power your site on WordPress.com, just like you have if you self-host a WordPress site. You can read their announcement here. Note this is for Business and eCommerce plans only.

All you have to do is flip it on in the settings (Manage > Hosting Configuration) and it'll give you the SFTP credentials.

SFTP and database access can be configured right in the WordPress Manage settings.

Here's me using an SFTP client and logging in no problem, and what I have access to:

Notice I can sneak right into super important folders like plugins, themes, and uploads. That's right:

  • You can upload and use your own themes
  • You can upload and use your own plugins
  • You can mass upload assets if you need to

All that stuff is 🤯mind-blowing to me. It used to be pretty locked in my mind that you use WordPress.com if you want to use WordPress and get all the speed, security, and lack of any worry, but at the cost of some lack of control. Now that all this control is there too, the line gets a lot fuzzier on when to self-host and when not to. You'll have to make that call for yourself.

And I haven't even mentioned that there is a button in the settings to pop open phpMyAdmin, giving you direct access to the database as well.

There are some limitations, which I think make a lot of sense.

For example, when I started Email is Good, I chose the TwentySixteen theme because I think it works really nicely for a straightforward blog like this site is. But when I log in via SFTP, I can't directly edit the theme. Instead, I see a symlink there in the themes folder, which I can't touch. That symlink is to an official copy of the TwentySixteen theme, which WordPress.com themselves will keep up to date. If I want to customize it, I could upload my own copy of that theme (or any other theme), and then I'm on my own to update it.

If this weren't enough, there's one more small but key thing WordPress.com makes easy: switching PHP versions. That's something that often takes a support request or requires more than a simple shared hosting plan, but it readily accessible in the WordPress.com site settings.

None of this is required, of course. You don't ever have to fiddle with any of this stuff to run a WordPress.com site. This is for people who need deeper control and like to hack on stuff (like me). There is also no prescribed way to use this. You could just pop in over SFTP to edit a few little things in a template or asset. Or, you could have a full-blown load dev environment for your site and deploy it over SFTP on demand.

Direct Link to ArticlePermalink

The post SFTP & Database Access on WordPress.com appeared first on CSS-Tricks.

Older posts are this way If this message doesn't go away, click anywhere on the page to continue loading posts.
Could not load more posts
Maybe Soup is currently being updated? I'll try again automatically in a few seconds...
Just a second, loading more posts...
You've reached the end.

Don't be the product, buy the product!

Schweinderl