Tag: Them

Two Issues Styling the Details Element and How to Solve Them

In the not-too-distant past, even basic accordion-like interactions required JavaScript event listeners or some CSS… trickery. And, depending on the solution used, editing the underlying HTML could get complicated.

Now, the <details> and <summary> elements (which combine to form what’s called a “disclosure widget”) have made creation and maintenance of these components relatively trivial.

At my job, we use them for things like frequently asked questions.

Pretty standard question/answer format

There are a couple of issues to consider

Because expand-and-collapse interactivity is already baked into the <details> and <summary> HTML tags, you can now make disclosure widgets without any JavaScript or CSS. But you still might want some. Left unstyled, <details> disclosure widgets present us with two issues.

Issue 1: The <summary> cursor

Though the <summary> section invites interaction, the element’s default cursor is a text selection icon rather than the pointing finger you may expect:

We get the text cursor but might prefer the pointer to indicate interaction instead.

Issue 2: Nested block elements in <summary>

Nesting a block-level element (e.g. a heading) inside a <summary> element pushes that content down below the arrow marker, rather than keeping it inline:

Block-level elements won’t share space with the summary marker.

The CSS Reset fix

To remedy these issues, we can add the following two styles to the reset section of our stylesheets:

details summary {    cursor: pointer; }  details summary > * {   display: inline; }

Read on for more on each issue and its respective solution.

Changing the <summary> cursor value

When users hover over an element on a page, we always want them to see a cursor “that reflects the expected user interaction on that element.”

We touched briefly on the fact that, although <summary> elements are interactive (like a link or form button), its default cursor is not the pointing finger we typically see for such elements. Instead, we get the text cursor, which we usually expect when entering or selecting text on a page.

To fix this, switch the cursor’s value to pointer:

details summary {    cursor: pointer; }

Some notable sites already include this property when they style <details> elements. The MDN Web Docs page on the element itself does exactly that. GitHub also uses disclosure widgets for certain items, like the actions to watch, star and fork a repo.

GitHub uses cursor: pointer on the <summary> element of its disclosure widget menus. 

I’m guessing the default cursor: text value was chosen to indicate that the summary text can (along with the rest of a disclosure widget’s content) be selected by the user. But, in most cases, I feel it’s more important to indicate that the <summary> element is interactive.

Summary text is still selectable, even after we’ve changed the cursor value from text to pointer. Note that changing the cursor only affects appearance, and not its functionality.

Displaying nested <summary> contents inline

Inside each <summary> section of the FAQ entries I shared earlier, I usually enclose the question in an appropriate heading tag (depending on the page outline):

<details>   <summary>     <h3>Will my child's 504 Plan be implemented?</h3>   </summary>   <p>Yes. Similar to the Spring, case managers will reach out to students.</p> </details>

Nesting a heading inside <summary> can be helpful for a few reasons:

  • Consistent visual styling. I like my FAQ questions to look like other headings on my pages.
  • Using headings keeps the page structure valid for users of Internet Explorer and pre-Chromium versions of Edge, which don’t support <details> elements. (In these browsers, such content is always visible, rather than interactive.)
  • Proper headings can help users of assistive technologies navigate within pages. (That said, headings within <summary> elements pose a unique case, as explained in detail below. Some screen readers interpret these headings as what they are, but others don’t.)

Headings vs. buttons

Keep in mind that the <summary> element is a bit of an odd duck. It operates like a button in many ways. In fact, it even has implicit role=button ARIA mapping. But, very much unlike buttons, headings are allowed to be nested directly inside <summary> elements.

This poses us — and browser and assistive technology developers — with a contradiction:

  • Headings are permitted in <summary> elements to provide in-page navigational assistance.
  • Buttons strip the semantics out of anything (like headings) nested within them.

Unfortunately, assistive technologies are inconsistent in how they’ve handled this situation. Some screen-reading technologies, like NVDA and Apple’s VoiceOver, do acknowledge headings inside <summary> elements. JAWS, on the other hand, does not.

What this means for us is that, when we place a heading inside a <summary>, we can style the heading’s appearance. But we cannot guarantee our heading will actually be interpreted as a heading!

In other words, it probably doesn’t hurt to put a heading there. It just may not always help.

Inline all the things

When using a heading tag (or another block element) directly inside our <summary>, we’ll probably want to change its display style to inline. Otherwise, we’ll get some undesired wrapping, like the expand/collapse arrow icon displayed above the heading, instead of beside it.

We can use the following CSS to apply a display value of inline to every heading — and to any other element nested directly inside the <summary>:

details summary > * {    display: inline; }

A couple notes on this technique. First, I recommend using inline, and not inline-block, as the line wrapping issue still occurs with inline-block when the heading text extends beyond one line.

Second, rather than changing the display value of the nested elements, you might be tempted to replace the <summary> element’s default display: list-item value with display: flex. At least I was! However, if we do this, the arrow marker will disappear. Whoops!

Bonus tip: Excluding Internet Explorer from your styles

I mentioned earlier that Internet Explorer and pre-Chromium (a.k.a. EdgeHTML) versions of Edge don’t support <details> elements. So, unless we’re using polyfills for these browsers, we may want to make sure our custom disclosure widget styles aren’t applied for them. Otherwise, we end up with a situation where all our inline styling garbles the element.

Inline <summary> headings could have odd or undesirable effects in Internet Explorer and EdgeHTML.

Plus, the <summary> element is no longer interactive when this happens, meaning the cursor’s default text style is more appropriate than pointer.

If we decide that we want our reset styles to target only the appropriate browsers, we can add a feature query that prevents IE and EdgeHTML from ever having our styles applied. Here’s how we do that using @supports to detect a feature only those browsers support:

@supports not (-ms-ime-align: auto) {    details summary {      cursor: pointer;   }    details summary > * {      display: inline;   }    /* Plus any other <details>/<summary> styles you want IE to ignore. }

IE actually doesn’t support feature queries at all, so it will ignore everything in the above block, which is fine! EdgeHTML does support feature queries, but it too will not apply anything within the block, as it is the only browser engine that supports -ms-ime-align.

The main caveat here is that there are also a few older versions of Chrome (namely 12-27) and Safari (macOS and iOS versions 6-8) that do support <details> but don’t support feature queries. Using a feature query means that these browsers, which account for about 0.06% of global usage (as of January 2021), will not apply our custom disclosure widget styles, either.

Using a @supports selector(details) block, instead of @supports not (-ms-ime-align: auto), would be an ideal solution. But selector queries have even less browser support than property-based feature queries.

Final thoughts

Once we’ve got our HTML structure set and our two CSS reset styles added, we can spruce up all our disclosure widgets however else we like. Even some simple border and background color styles can go a long way for aesthetics and usability. Just know that customizing the <summary> markers can get a little complicated!


The post Two Issues Styling the Details Element and How to Solve Them appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , ,

SVG, Favicons, and All the Fun Things We Can Do With Them

Favicons are the little icons you see in your browser tab. They help you understand which site is which when you’re scanning through your browser’s bookmarks and open tabs. They’re a neat part of internet history that are capable of performing some cool tricks.

One very new trick is the ability to use SVG as a favicon. It’s something that most modern browsers support, with more support on the way.

Here’s the code for how to add favicons to your site:

<link rel="icon" type="image/svg+xml" href="/favicon.svg"> <link rel="alternate icon" href="/favicon.ico"> <link rel="mask-icon" href="/safari-pinned-tab.svg" color="#ff8a01">

If a browser doesn’t support a SVG favicon, it will ignore the first link element declaration and continue on to the second. This ensures that all browsers that support favicons can enjoy the experience. 

You may also notice the alternate attribute value for our rel declaration in the second line. This programmatically communicates to the browser that the favicon with a file format that uses .ico is specifically used as an alternate presentation.

Following the favicons is a line of code that loads another SVG image, one called safari-pinned-tab.svg. This is to support Safari’s pinned tab functionality, which existed before other browsers had SVG favicon support. There’s additional files you can add here to enhance your site for different apps and services, but more on that in a bit.

Here’s more detail on the current level of SVG favicon support:

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

Desktop

Chrome Firefox IE Edge Safari
80 41 No 80 TP

Mobile / Tablet

Android Chrome Android Firefox Android iOS Safari
80 No No 13.4

Why SVG?

You may be questioning why this is needed. The .ico file format has been around forever and can support images up to 256×256 pixels in size. Here are three answers for you.

Ease of authoring

It’s a pain to make .ico files. The file is a proprietary format used by Microsoft, meaning you’ll need specialized tools to make them. SVG is an open standard, meaning you can use them without any further tooling or platform lock-in.

Future-proofing

Retina? 5k? 6k? When we use a resolution-agnostic SVG file for a favicon, we guarantee that our favicons look crisp on future devices, regardless of how large their displays get

Performance

SVGs are usually very small files, especially when compared to their raster image counterparts — even more-so if you optimize them beforehand. By only using a 16×16 pixel favicon as a fallback for browsers that don’t support SVG, we provide a combination that enjoys a high degree of support with a smaller file size to boot. 

This might seem a bit extreme, but when it comes to web performance, every byte counts!

Tricks

Another cool thing about SVG is we can embed CSS directly in it. This means we can do fun things like dynamically adjust them with JavaScript, provided the SVG is declared inline and not embedded using an img element.

<svg  version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">   <style>     path { fill: #272019; }   </style>   <!-- etc. --> </svg>

Since SVG favicons are embedded using the link element, they can’t really be modified using JavaScript. We can, however, use things like emoji and media queries.

Emoji

Lea Verou had a genius idea about using emoji inside of SVG’s text element to make a quick favicon with a transparent background that holds up at small sizes.

In response, Chris Coyier whipped up a neat little demo that lets you play around with the concept.

Dark Mode support

Both Thomas Steiner and Mathias Bynens independently stumbled across the idea that you can use the prefers-color-scheme media query to provide support for dark mode. This work is built off of Jake Archibald’s exploration of SVG and media queries.

<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg">   <style>     path { fill: #000000; }     @media (prefers-color-scheme: dark) {       path { fill: #ffffff; }     }   </style>   <path d="M111.904 52.937a1.95 1.95 0 00-1.555-1.314l-30.835-4.502-13.786-28.136c-.653-1.313-2.803-1.313-3.456 0L48.486 47.121l-30.835 4.502a1.95 1.95 0 00-1.555 1.314 1.952 1.952 0 00.48 1.99l22.33 21.894-5.28 30.918c-.115.715.173 1.45.768 1.894a1.904 1.904 0 002.016.135L64 95.178l27.59 14.59c.269.155.576.232.883.232a1.98 1.98 0 001.133-.367 1.974 1.974 0 00.768-1.894l-5.28-30.918 22.33-21.893c.518-.522.71-1.276.48-1.99z" fill-rule="nonzero"/> </svg>

For supporting browsers, this code means our star-shaped SVG favicon will change its fill color from black to white when dark mode is activated. Pretty neat!

Other media queries

Dark mode support got me thinking: if SVGs can support prefers-color-scheme, what other things can we do with them? While the support for Level 5 Media Queries may not be there yet, here’s some ideas to consider:

Mockup of four SVG favicon treatments. The first treatment is a pink star with a tab title of, “SVG Favicon.” The second treatment is a hot pink star with a tab title of, “Light Level SVG Favicon.” The third treatment is a light pink star with a tab title of, “Inverted Colors SVG Favicon.” The fourth treatment is a black pink star with a tab title of, “High Contrast Mode SVG Favicon.” The tabs are screen captures from Microsoft Edge, with the browser chrome updating to match each specialized mode.
A mockup of how these media query-based adjustments could work.

Keep it crisp

Another important aspect of good favicon design is making sure they look good in the small browser tab area. The secret to this is making the paths of the vector image line up to the pixel grid, the guide a computer uses to turn SVG math into the bitmap we see on a screen. 

Here’s a simplified example using a square shape:

A crisp orange square on a white background. There is also a faint grid of gray horizontal and vertical lines that represent the pixel grid. Screenshot from Figma.

When the vector points of the square align to the pixel grid of the artboard, the antialiasing effect a computer uses to smooth out the shapes isn’t needed. When the vector points aren’t aligned, we get a “smearing” effect:

A blurred orange square on a white background. There is also a faint grid of gray horizontal and vertical lines that represent the pixel grid. Screenshot from Figma.

A vector point’s position can be adjusted on the pixel grid by using a vector editing program such as Figma, Sketch, Inkscape, or Illustrator. These programs export SVGs as well. To adjust a vector point’s location, select each node with a precision selection tool and drag it into position.

Some more complicated icons may need to be simplified, in order to look good at such a small size. If you’re looking for a good primer on this, Jeremy Frank wrote a really good two-part article over at Vidget.

Go the extra mile

In addition to favicons, there are a bunch of different (and unfortunately proprietary) ways to use icons to enhance its experience. These include things like the aforementioned pinned tab icon for Safari¹, chat app unfurls, a pinned Windows start menu tile, social media previews, and homescreen launchers.

If you’re looking for a great place to get started with these kinds of enhancements, I really like realfavicongenerator.net.

Icon output from realfavicongenerator.net arranged in a grid using CSS-Trick’s logo. There are two rows of five icons: android-chrome-192x192.png, android-chrome-384x384.png, apple-touch-icon.png, favicon-16x16.png, favicon-32x32.png, mstile-150x150.png, safari-pinned-tab.svg, favicon.ico, browserconfig.xml, and site.webmanifest.
It’s a lot, but it guarantees robust support.

A funny thing about the history of the favicon: Internet Explorer was the first browser to support them and they were snuck in at the 11th hour by a developer named Bharat Shyam:

As the story goes, late one night, Shyam was working on his new favicon feature. He called over junior project manager Ray Sun to take a look.

Shyam commented, “This is good, right? Check it in?”, requesting permission to check the code into the Internet Explorer codebase so it could be released in the next version. Sun didn’t think too much of it, the feature was cool and would clearly give IE an edge. So he told Shyam to go ahead and add it. And just like that, the favicon made its way into Internet Explorer 5, which would go on to become one of the largest browser releases the web has ever seen.

The next day, Sun was reprimanded by his manager for letting the feature get by so quickly. As it turns out, Shyam had specifically waited until later in the day, knowing that a less experienced Program Manager would give him a pass. But by then, the code had been merged in. Incidentally, you’d be surprised just how many relatively major browser features have snuck their way into releases like this.

From How We Got the Favicon by Jay Hoffmann

I’m happy to see the platform throw a little love at favicons. They’ve long been one of my favorite little design details, and I’m excited that they’re becoming more reactive to user’s needs. If you have a moment, why not sneak a SVG favicon into your project the same way Bharat Shyam did way back in 1999. 


¹ I haven’t been able to determine if Safari is going to implement SVG favicon support, but I hope they do. Has anyone heard anything?

The post SVG, Favicons, and All the Fun Things We Can Do With Them appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

The Rising Complexity of JAMstack Sites and How to Manage Them

When you add anything with user-generated content or dynamic data to a static site, the complexity of the build process can become comparable to launching a monolithic CMS. How can we add rich content to static sites without stitching together multiple third-party services?

For people in the development community static site generators are a popular choice over traditional content management systems (CMS) like WordPress. By comparison static sites are usually lightweight, highly configurable, fast, easy to use and can be deployed almost anywhere.

With static websites, no code is generated on the server; we’ve replaced databases and server-side code with APIs and build processes.

This has become known as a JAMstack, which stands for JavaScript, APIs and Markup. I have a strong persuasion towards JAMstack sites because I feel more in control of the output than I do when working with the often large and monolithic CMSs I’ve sometimes had to use on client projects.

Despite my enthusiasm, I’m often disheartened by the steep complexity curve I typically encounter about halfway through a JAMstack project. Normally the first few weeks are incredibly liberating. It’s easy to get started, there is good visible progress, everything feels lean and fast. Over time, as more features are added, the build steps become more complex, multiple APIs are added, and suddenly everything feels slow. In other words, the development experience begins to suffer.

It usually looks something like this:

A hand-drawn chart showing complexity of a project over time. It shows a complexity curve that rises steeply at the end.

One of the reasons for this steep rise in complexity is there are limits to the type of data that markdown can easily represent. Relationships are one example where static sites struggle. Relationships between pages or collections of assets (such as an image gallery) can only be represented by markdown in inefficient ways. It requires significant preprocessing to resolve anything more complicated than a simple set of tags or categories. If you’ve ever had to do it, you will also know the authoring experience of managing relationships in markdown isn’t ideal.

User-generated content is another area that can cause a steep rise in the complexity of static sites. Adding features like comments, ratings, likes or any other kind of dynamic content will require third-party services — each has its own account that needs to be managed, not to mention that adding third-party scripts can have a negative impact of page performance.

If a service doesn’t match your specific requirements, sometimes it’s possible to cobble solutions together using generic platforms like Google Forms or AirTable.

The end result is we’ve outsourced the database, fragmented the content management experience and stitched together a bundle of compromises. That’s a stark contrast from the initial ease of setting up and deploying a JAMstack site.

Although this complexity curve is not unique to JAMstack projects, adding rich features to markdown-driven sites is far more difficult than we care to admit. What happened!? A lack of complexity was one of the reasons we favored JAMstack in the first place.

We did that thing that web developers do. We moved the complexity from one space into another and congratulated ourselves 😂. Not having complexity on the server-side is good for front-end performance, but there is little incentive to optimize any further once we do this. Ridiculous build times and complicated tool-chains have become an acceptable reality for modern front-end web development.

JAMstack Plus

Before I come across as sounding too critical, I should make it clear that I absolutely love static site generators. I think they are a perfect solution for many simple sites and you should still use them. However, I feel like a simple content management layer that I own and can configure is preferable to:

  • poor content management experiences,
  • complicated integration of third-party services, and
  • inefficient build processes.

I want to combine the benefits of a CMS with static site generators.

And it seems I’m not the only one who has reached this conclusion:

The solution doesn’t need to be another third-party service or require abandoning static sites entirely. You can use a personalized content management layer and unified API to enrich a static site. It might not be as hard as you think.

The first step is to create an API for your site. You can use any headless CMS, but the challenge I’ve had with many options is they make a lot of assumptions about the type of content you want. You might not want the CMS to manage pages and posts, but rather use it to store comments or images. I find this particularly difficult with WordPress. I often feel like I’m forcing a blogging platform to be just the service I need.

The new version of KeystoneJS (Keystone 5) is an excellent alternative to more opinionated content management systems. It’s made up of tiny independent components, so you only add the parts you need. This means it doesn’t feel like modifying a blogging platform. Instead it’s like creating a personalized mini-CMS and API to work specifically with your site.

I call this approach JAMstack Plus.

To help you get started with this idea I’ve created two projects:

  1. Supermaya, a starter kit for the static site generator Eleventy.
  2. Keystone JAMstack Plus, a blog enrichment platform.

Introducing Supermaya

The first project I want to share with you is Supermaya, an Eleventy starter kit designed to help add rich features to a blog or website without a complicated build process.

It comes with the all “blog standard” features including:

  • Posts and Pages
  • Pagination
  • Tags
  • RSS feed
  • Service worker
  • Lazy loading images
  • Critical CSS (if enabled)

It also has considerate and accessible markup. If deployed correctly, it should get full scores on a lighthouse audit out-of-the-box:

Supermaya scores 100% on Lighthouse tests.

I didn’t build Supermaya specifically as a platform to add user-generated content to static sites. Instead, I started it because I was not satisfied with the way existing static site generators integrate with other build tools. That’s why all the pre-processing steps in Supermaya are built into Eleveny itself. This includes the compilation of SCSS and JavaScript. Unifying the compilation steps eliminates the need for build tools like Grunt, Gulp or Webpack running in parallel.

After this, I realized the other reason for increasing complexity on JAMstack sites was integration with third-party services, usually for user-generated content. To solve this, Supermaya has optional tie-ins with a Keystone JAMstack Plus starter-kit, which makes it easier to add user-generated content and other rich features.

You can deploy both Keystone and Supermaya together and connect them at the same time by following the instructions during installation. This will deploy Keystone to Herouku and Supermaya to Netlify, as well as configure your admin user and API URL.

Rich features are added with progressive enhancement, so if the API cannot be reached or there is a server error, the site will continue to function without noticeable degradation or delays for users.

JAMstack Plus starter kit

The Keystone JAMstack Plus starter kit allows you to add rich features to a blog including:

  • Comments
  • Claps
  • Reading list, and
  • Logins

Just like Supermaya, it can be used on its own. After it’s deployed, you’ll get access to an admin interface that allows you to create and manage content. You’ll also get a GraphQL endpoint that can be connected to Supermaya.

It’s configured with the intention of being a headless CMS for user-generated content. It expects pages and posts to be managed by a static site generator. However, with a little work — and following the examples in Supermaya — you can connect any front-end to the GraphQL API.

I’d encourage you to modify the starter-kit: Add additional features or provide content for pages directly from Keystone. If you add features that could be used by the rest of the community contribute back to the starter-kit and we can make it easier for everyone to add rich features to their sites without the need for third-party services.

Note: The automatic deploy will deploy to a free instance of Heroku. This will sleep periodically if not used which can result in slow API response times after periods of inactivity. You can upgrade to a hobby instance to avoid this.

Consider owning your own data

JAMstack and servers are not incompatible. There’s always a server (usually multiple) — it’s just a question of who owns it. If you are using any kind of third-party service, the chances are they own your account information, your content and collect user data.

Sometimes this might be an acceptable compromise compared with the overhead of deploying and managing a back-end service, but when the complexity of stitching together several APIs becomes comparable to a CMS, I believe managing a tiny configurable service that you own, can provide a better experience for users, developers and content managers. It also provides a solid platform for websites to grow beyond purely static content into more complicated and varying types of applications.

I don’t think JAMstack should defined by pushing all the complexity into the front-end build process or by compromising on developer and user experience. Instead, I think JAMstack should focus on providing lean, configurable static front-ends. These can be connected to APIs to provide user-generated data and content management services. There is no reason not to own and manage these services, if it provides the best outcome.

CSS-Tricks

, , , , ,
[Top]

Git Pathspecs and How to Use Them

When I was looking through the documentation of git commands, I noticed that many of them had an option for <pathspec>. I initially thought that this was just a technical way to say “path,” and assumed that it could only accept directories and filenames. After diving into the rabbit hole of documentation, I found that the pathspec option of git commands are capable of so much more.

The pathspec is the mechanism that git uses for limiting the scope of a git command to a subset of the repository. If you have used much git, you have likely used a pathspec whether you know it or not. For example, in the command git add README.md, the pathspec is README.md. However, it is capable of much more nuance and flexibility.

So, why should you learn about pathspecs? Since it is a part of many commands, these commands become much more powerful with an understanding of pathspecs. With git add, you can add just the files within a single directory. With git diff, you can examine just the changes made to filenames with an extension of .scss. You can git grep all files except for those in the /dist directory.

In addition, pathspecs can help with the writing of more generic git aliases. For example, I have an alias named git todo, which will search all of my repository files for the string 'todo'. However, I would like for this to show all instances of the string, even if they are not within my current working directory. With pathspecs, we will see how this becomes possible.

File or directory

The most straightforward way to use a pathspec is with just a directory and/or filename. For example, with git add you can do the following. ., src/, and README are the respective pathspecs for each command.

git add .      # add CWD (current working directory) git add src/   # add src/ directory git add README # add only README directory

You can also add multiple pathspecs to a command:

git add src/ server/ # adds both src/ and server/ directories

Sometimes, you may see a -- preceding the pathspec of a command. This is used to remove any ambiguity of what is the pathspec and what is part of the command.

Wildcards

In addition to files & directories, you can match patterns using *, ?, and []. The * symbol is used as a wildcard and it will match the / in paths — in other words, it will search through subdirectories.

git log '*.js' # logs all .js files in CWD and subdirectories git log '.*'   # logs all 'hidden' files and directories in CWD git log '*/.*' # logs all 'hidden' files and directories in subdirectories

The quotes are important, especially when using *! They prevent your shell (such as bash or ZSH) from attempting to expand the wildcards on their own. For example, let’s take a look at how git ls-files will list files with and without the quotes.

# example directory structure # # . # ├── package-lock.json # ├── package.json # └── data #     ├── bar.json #     ├── baz.json #     └── foo.json  git ls-files *.json   # package-lock.json # package.json  git ls-files '*.json'  # data/bar.json # data/baz.json # data/foo.json # package-lock.json # package.json

Since the shell is expanding the * in the first command, git ls-files receives the command as git ls-files package-lock.json package.json. The quotes ensure that git is the one to resolve the wildcard.

You can also use the ? character as a wildcard for a single character. For example, to match either mp3 or mp4 files, you can do the following.

git ls-files '*.mp?'

Bracket expressions

You can also use “bracket expressions” to match a single character out of a set. For example, if you’d like to make matches between either TypeScript or JavaScript files, you can use [tj]. This will match either a t or a j.

git ls-files '*.[tj]s'

This will match either .ts files or .js files. In addition to just using characters, there are certain collections of characters that can be referenced within bracket expressions. For example, you can use [:digit:] within a bracket expression to match any decimal digit, or you can use [:space:] to match any space characters.

git ls-files '*.mp[[:digit:]]' # mp0, mp1, mp2, mp3, ..., mp9 git ls-files '*[[:space:]]*' # matches any path containing a space

To read more about bracket expression and how to use them, check out the GNU manual.

Magic signatures

Pathspecs also have the special tool in their arsenal called “magic signatures” which unlock some additional functionality to your pathspecs. These “magic signatures” are called by using :(signature) at the beginning of your pathspec. If this doesn’t make sense, don’t worry: some examples will hopefully help clear it up.

top

The top signature tells git to match the pattern from the root of the git repository rather than the current working directory. You can also use the shorthand :/ rather than :(top).

git ls-files ':(top)*.js' git ls-files ':/*.js' # shorthand

This will list all files in your repository that have an extension of .js. With the top signature this can be called within any subdirectory in your repository. I find this to be especially useful when writing generic git aliases!

git config --global alias.js 'ls-files -- ':(top)*.js''

You can use git js anywhere within your repository to get a list of all JavaScript files in your project using this.

icase

The icase signature tells git to not care about case when matching. This could be useful if you don’t care which case the filename is — for example, this could be useful for matching jpg files, which sometimes use the uppercase extension JPG.

git ls-files ':(icase)*.jpg'

literal

The literal signature tells git to treat all of your characters literally. This would be used if you want to treat characters such as * and ? as themselves, rather than as wildcards. Unless your repository has filenames with * or ?, I don’t expect that this signature would be used too often.

git log ':(literal)*.js' # returns log for the file '*.js'

glob

When I started learning pathspecs, I noticed that wildcards worked differently than I was used to. Typically I see a single asterisk * as being a wildcard that does not match anything through directories and consecutive asterisks (**) as a “deep” wildcard that does match names through directories. If you would prefer this style of wildcards, you can use the glob magic signature!

This can be useful if you want more fine-grained control over how you search through your project’s directory structure. As an example, take a look at how these two git ls-files can search through a React project.

git ls-files ':(glob)src/components/*/*.jsx' # 'top level' jsx components git ls-files ':(glob)src/components/**/*.jsx' # 'all' jsx components

attr

Git has the ability to set “attributes” to specific files. You can set these attributes using a .gitattributes file.

# .gitattributes  src/components/vendor/*  vendored # sets 'vendored' attribute src/styles/vendor/*      vendored

Using the attr magic signature can set attribute requirements for your pathspec. For example, we might want to ignore the above files from a vendor.

git ls-files ':(attr:!vendored)*.js' # searches for non-vendored js files git ls-files ':(attr:vendored)*.js'  # searches for vendored js files

exclude

Lastly, there is the “exclude’” magic signature (shorthand of :! or :^). This signature works differently from the rest of the magic signatures. After all other pathspecs have been resolved, all pathspecs with an exclude signature are resolved and then removed from the returned paths. For example, you can search through all of your .js files while excluding the .spec.js test files.

git grep 'foo' -- '*.js' ':(exclude)*.spec.js' # search .js files excluding .spec.js git grep 'foo' -- '*.js' ':!*.spec.js' .       # shorthand for the same

Combining signatures

There is nothing limiting you from using multiple magic signatures in a single pathspec! You can use multiple signatures by separating your magic words with commas within your parenthesis. For example, you can do the following if you’d like to match from the base of your repository (using top), case insensitively (using icase), using only authored code (ignoring vendor files with attr), and using glob-style wildcards (using glob).

git ls-files -- ':(top,icase,glob,attr:!vendored)src/components/*/*.jsx'

The only two magic signatures that you are unable to combine are glob and literal, since they both affect how git deals with wildcards. This is referenced in the git glossary with perhaps my favorite sentence that I have ever read in any documentation.

Glob magic is incompatible with literal magic.


Pathspecs are an integral part of many git commands, but their flexibility is not immediately accessible. By learning how to use wildcards and magic signatures you can multiply your command of the git command line.

The post Git Pathspecs and How to Use Them appeared first on CSS-Tricks.

CSS-Tricks

,
[Top]

Could Grouping HTML Classes Make Them More Readable?

You can have multiple classes on an HTML element:

<div class="module p-2"></div>

Nothing incorrect or invalid there at all. It has two classes. In CSS, both of these will apply:

.module { } .p-2 { }
const div = document.querySelector("div"); console.log(div.classList.contains("module")); // true console.log(div.classList.contains("p-3"));    // false

But what about grouping them? All we have here is a space-separated string. Maybe that’s fine. But maybe we can make things more clear!

Years ago, Harry Roberts talked about grouping them. He wrapped groups of classes in square brackets:

<div class="[ foo  foo--bar ]  [ baz  baz--foo ]">

The example class names above are totally abstract just to demonstrate the grouping. Imagine they are like primary names and variations as one group, then utility classes as another group:

<header class="[ site-header site-header-large ]  [ mb-10 p-15 ]">

Those square brackets? Meaningless. Those are there to visually represent the groups to us developers. Technically, they are also classes, so if some sadist wrote .[ {}, it would do stuff in your CSS. But that’s so unlikely that, hopefully, the clarity from the groups outweighs it and is more helpful.

That example above groups the primary name and a variation in one group and some example utility classes in another group.

I’m not necessarily recommending that approach. They are simply groups of classes that you might have.

Here’s the same style of grouping, with different groups:

<button class="[ link-button ] [ font-base text-xs color-primary ] [ js-trigger ]" type="button" hidden>

That example has a single primary name, utility classes with different naming styles, and a third group for JavaScript specific selectors.

Harry wound up shunning this approach a few years ago, saying that the look of it was just too weird for the variety of people and teams he worked with. It caused enough confusion that the benefits of grouped classes weren’t worth it. He suggested line breaks instead:

<div class="media  media--large             testimonial  testimonial--main"> 

That seems similarly clear to me. The line breaks in HTML are totally fine. Plus, the browser will have no trouble with that and JSX is generally written with lots of line breaks in HTML anyway because of how much extra stuff is plopped onto elements in there, like event handlers and props.

Perhaps we combine the ideas of line breaks as separators and identified groups… with emojis!

See the Pen
Grouping Classes
by Chris Coyier (@chriscoyier)
on CodePen.

Weird, but fun. Emojis are totally valid there. Like the square brackets, they could also do things if someone wrote a class name for them, but that’s generally unlikely and something for a team to talk about.

Another thing I’ve seen used is data-* attributes for groups instead of classes, like…

<div    class="primary-name"   data-js="js-hook-1 js-hook-2"   data-utilities="padding-large" >

You can still select and style based on attributes in both CSS and JavaScript, so it’s functional, though slightly less convenient because of the awkward selectors like [data-js="js-hook-1"] and lack of convenient APIs like classList.

How about you? Do you have any other clever ideas for class name groups?

The post Could Grouping HTML Classes Make Them More Readable? appeared first on CSS-Tricks.

CSS-Tricks

, , , , , ,
[Top]

The Many Ways to Change an SVG Fill on Hover (and When to Use Them)

SVG is a great format for icons. Vector formats look crisp and razor sharp, no matter the size or device — and we get tons of design control when using them inline.

SVG also gives us another powerful feature: the ability to manipulate their properties with CSS. As a result, we can make quick and simple interactions where it used to take crafty CSS tricks or swapping out entire image files.

Those interactions include changing color on hover states. It sounds like such a straightforward thing here in 2019, but there are actually a few totally valid ways to go about it — which only demonstrates the awesome powers of SVG more.

First off, let’s begin with a little abbreviated SVG markup:

<svg class="icon">   <path .../> </svg>

Target the .icon class in CSS and set the SVG fill property on the hover state to swap colors.

.icon:hover {   fill: #DA4567; }

This is by far the easiest way to apply a colored hover state to an SVG. Three lines of code!

SVGs can also be referenced using an <img> tag or as a background image. This allows the images to be cached and we can avoid bloating your HTML with chunks of SVG code. But the downside is a big one: we no longer have the ability to manipulate those properties using CSS. Whenever I come across non-inline icons, my first port of call is to inline them, but sometimes that’s not an option.

I was recently working on a project where the social icons were a component in a pattern library that everyone was happy with. In this case, the icons were being referenced from an <img> element. I was tasked with applying colored :focus and :hover styles, without adjusting the markup.

So, how do you go about adding a colored hover effect to an icon if it’s not an inline SVG?

CSS Filters

CSS filters allow us to apply a whole bunch of cool, Photoshop-esque effects right in the browser. Filters are applied to the element after the browser renders layout and initial paint, which means they fall back gracefully. They apply to the whole element, including children. Think of a filter as a lens laid over the top of the element it’s applied to.

These are the CSS filters available to us:

  • brightness(<number-percentage>);
  • contrast(<number-percentage>);
  • grayscale(<number-percentage>);
  • invert(<number-percentage>);
  • opacity(<number-percentage>);
  • saturate(<number-percentage>);
  • sepia(<number-percentage>);
  • hue-rotate(<angle>);
  • blur(<length>);
  • drop-shadow(<length><color>);

All filters take a value which can be changed to adjust the effect. In most cases, this value can be expressed in either a decimal or percent units (e.g. brightness(0.5) or brightness(50%)).

Straight out of the box, there’s no CSS filter that allows us to add our own specific color.
We have hue-rotate(), but that only adjusts an existing color; it doesn’t add a color, which is no good since we’re starting with a monochromatic icon.

The game-changing bit about CSS filters is that we don’t have to use them in isolation. Multiple filters can be applied to an element by space-separating the filter functions like this:

.icon:hover {   filter: grayscale(100%) sepia(100%); }

If one of the filter functions doesn’t exist, or has an incorrect value, the whole list is ignored and no filter will be applied to the element.

When applying multiple filter functions to an element, their order is important and will affect the final output. Each filter function will be applied to the result of the previous operation.

So, in order to colorize our icons, we have to find the right combination.

To make use of hue-rotate(), we need to start off with a colored icon. The sepia() filter is the only filter function that allows us to add a color, giving the filtered element a yellow-brown-y tinge, like an old photo.

The output color is dependent on the starting tonal value:

In order to add enough color with sepia(), we first need to use invert() to convert our icon to a medium grey:

.icon:hover {   filter: invert(0.5) }

We can then add the yellow/brown tone with sepia():

.icon:hover {   filter: invert(0.5) sepia(1); }

…then change the hue with hue-rotate():

.icon:hover {   filter: invert(0.5) sepia(1) hue-rotate(200deg);                                   }

Once we have the rough color we want, we can tweak it with saturation() and brightness():

.icon:hover {   filter:      invert(0.5)     sepia(1)     hue-rotate(200deg)     saturate(4)     brightness(1); }

I’ve made a little tool for this to make your life a little easier, as this is a pretty confusing process to guesstimate.

See the Pen CSS filter example by Cassie Evans (@cassie-codes)
on CodePen.

Even with the tool, it’s still a little fiddly, not supported by Internet Explorer, and most importantly, you’re unable to specify a precise color.

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

Desktop

Chrome Opera Firefox IE Edge Safari
18* 15* 35 No 18 6*

Mobile / Tablet

iOS Safari Opera Mobile Opera Mini Android Android Chrome Android Firefox
6.0-6.1* 46 No 4.4* 71 64

So, what do we do if we need a specific hex code?

SVG Filters

If we need more precise control (and better browser support) than CSS filters can offer, then it’s time to turn to SVG.

Filters originally came from SVG. In fact, under the hood, CSS filters are just shortcuts to SVG filters with a particular set of values baked in.

Unlike CSS, the filter isn’t predefined for us, so we have to create it. How do we do this?

This is the syntax to define a filter:

<svg xmlns="<http://www.w3.org/2000/svg>" version="1.1">   <defs>     <filter id="id-of-your-filter">       ...                        ...     </filter>     ...   </defs> </svg>

Filters are defined by a <filter> element, which goes inside the <defs> section of an SVG.

SVG filters can be applied to SVG content within the same SVG document. Or, the filter can be referenced and applied to HTML content elsewhere.

To apply an SVG filter to HTML content, we reference it the same way as a CSS filter: by using the url() filter function. The URL points to the ID of the SVG filter.

.icon:hover {   filter: url('#id-of-your-filter'); }

The SVG filter can be placed inline in the document or the filter function can reference an external SVG. I prefer the latter route as it allows me to keep my SVG filters tidied away in an assets folder.

.icon:hover {   filter: url('assets/your-SVG.svg#id-of-your-filter'); }

Back to the <filter> element itself.

<filter id="id-of-your-filter">   ...                ... </filter>

Right now, this filter is empty and won’t do anything as we haven’t defined a filter primitive. Filter primitives are what create the filter effects. There are a number of filter primitives available to us, including:

  • [<feBlend>]
  • [<feColorMatrix>]
  • [<feComponentTransfer>]
  • [<feComposite>]
  • [<feConvolveMatrix>]
  • [<feDiffuseLighting>]
  • [<feDisplacementMap>]
  • [<feDropShadow>]
  • [<feFlood>]
  • [<feGaussianBlur>]
  • [<feImage>]
  • [<feMerge>]
  • [<feMorphology>]
  • [<feOffset>]
  • [<feSpecularLighting>]
  • [<feTile>]
  • [<feTurbulence>]

Just like with CSS filters, we can use them on their own or include multiple filter primitives in the <filter> tag for more interesting effects. If more than one filter primitive is used, then each operation will build on top of the previous one.

For our purposes we’re just going to use feColorMatrix, but if you want to know more about SVG filters, you can check out the specs on MDN or this (in progress, at the time of this writing) article series that Sara Soueidan has kicked off.

feColourMatrix allows us to change color values on a per-channel basis, much like channel mixing in Photoshop.

This is what the syntax looks like:

<svg xmlns="<http://www.w3.org/2000/svg>" version="1.1">   <defs>     <filter id="id-of-your-filter">       <feColorMatrix         color-interpolation-filters="sRGB"         type="matrix"         values="1 0 0 0 0                 0 1 0 0 0                 0 0 1 0 0                 0 0 0 1 0 "/>     </filter>     ...   </defs> </svg>

Let’s have a closer look at the color matrix values.

The first four columns represent the red, green and blue channels of color and the alpha (opacity) value. The rows contain the red, green, blue and alpha values in those channels.

The M column is a multiplier — we don’t need to change any of these values for our purposes here. The values for each color channel are represented as floating point numbers in the range 0 to 1.

We could write these values as a CSS RGBA color declaration like this:

rgba(255, 255, 255, 1)

The values for each color channel (red, green and blue) are stored as integers in the range 0 to 255. In computers, this is the range that one 8-bit byte can offer.

By dividing these color channel values by 255, the values can be represented as a floating point number which we can use in the feColorMatrix.

And, by doing this, we can create a color filter for any color with an RGB value!

Like teal, for example:

rgba(0, 128, 128, 1). 128%255=0.50

See the Pen
SVG filter – teal hover
by Cassie Evans (@cassie-codes)
on CodePen.

This SVG filter will only impart color to icons with a white fill, so If we have an icon with a black fill, we can use invert() to convert it to white before applying the SVG filter.

.icon:hover {   filter: invert(100%) url('assets/your-SVG.svg#id-of-your-filter'); }

If we just have a hex code, the math is a little trickier, although there are plenty of hex-to-RGBA converters out there. To help out, I’ve made a HEX to feColorMatrix converter.

See the Pen
HEX to feColorMatrix converterr
by Cassie Evans (@cassie-codes)
on CodePen.

Have a play around, and happy filtering!

The post The Many Ways to Change an SVG Fill on Hover (and When to Use Them) appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,
[Top]