Tag: Styling

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

, , , , ,

Styling Code In and Out of Blocks

We’ll get to that, but first, a long-winded introduction.

I’m still not in a confident place knowing a good time to use native web components. The templating isn’t particularly robust, so that doesn’t draw me in. There is no state management, and I like having standard ways of handling that. If I’m using another library for components anyway, seems like I would just stick with that. So, at the moment, my checklist is something like:

  • Not using any other JavaScript framework that has components
  • Templating needs aren’t particularly complex
  • Don’t need particularly performant re-rendering
  • Don’t need state management

I’m sure there is tooling that helps with these things and more (the devMode episode with some folks from Stencil was good), but if I’m going to get into tooling-land, I’d be extra tempted to go with a framework, and probably not framework plus another thing with a lot of overlap.

The reasons I am tempted to go with native web components are:

  • They are native. No downloads of frameworks.
  • The Shadow DOM is a true encapsulation in a way a framework can’t really do.
  • I get to build my own HTML element that I use in HTML, with my own API design.

It sorta seems like the sweet spot for native web components is design system components. You build out your own little API for the components in your system, and people can use them in a way that is a lot safer than just copy and paste this chunk of HTML. And I suppose if consumers of the system wanted to BYO framework, they could.

So you can use like <our-tabs active-tab="3"> rather than <div class="tabs"> ... <a href="#3" class="tab-is-active">. Refactoring the components certainly gets a lot easier as changes percolate everywhere.

I’ve used them here on CSS-Tricks for our <circle-text> component. It takes the radius as a parameter and the content via, uh, content, and outputs an <svg> that does the trick. It gave us a nice API for authoring that abstracted away the complexity.

So!

It occurred to me a “code block” might be a nice use-case for a web component.

  • The API would be nice for it, as you could have attributes control useful things, and the code itself as the content (which is a great fallback).
  • It doesn’t really need state.
  • Syntax highlighting is a big gnarly block of CSS, so it would be kinda cool to isolate that away in the Shadow DOM.
  • It could have useful functionality like a “click to copy” button that people might enjoy having.

Altogether, it might feel like a yeah, I could use this kinda component.

This probably isn’t really production ready (for one thing, it’s not on npm or anything yet), but here’s where I am so far:

Here’s a thought dump!

  • What do you do when a component depends on a third-party lib? The syntax highlighting here is done with Prism.js. To make it more isolated, I suppose you could copy and paste the whole lib in there somewhere, but that seems silly. Maybe you just document it?
  • Styling web components doesn’t feel like it has a great story yet, despite the fact that Shadow DOM is cool and useful.
  • Yanking in pre-formatted text to use in a template is super weird. I’m sure it’s possible to do without needing a <pre> tag inside the custom element, but it’s clearly much easier if you grab the content from the <pre>. Makes the API here just a smidge less friendly (because I’d prefer to use the <code-block> alone).
  • I wonder what a good practice is for passing along attributes that another library needs. Like is data-lang="CSS" OK to use (feels nicer), and then convert it to class="language-css" in the template because that’s what Prism wants? Or is it better practice to just pass along attributes as they are? (I went with the latter.)
  • People complain that there aren’t really “lifecycle methods” in native web components, but at least you have one: when the thing renders: connectedCallback. So, I suppose you should do all the manipulation of HTML and such before you do that final shadowRoot.appendChild(node);. I’m not doing that here, and instead am running Prism over the whole shadowRoot after it’s been appended. Just seemed to work that way. I imagine it’s probably better, and possible, to do it ahead of time rather than allow all the repainting caused by injecting spans and such.
  • The whole point of this is a nice API. Seems to me thing would be nicer if it was possible to drop un-escaped HTML in there to highlight and it could escape it for you. But that makes the fallback actually render that HTML which could be bad (or even theoretically insecure). What’s a good story for that? Maybe put the HTML in HTML comments and test if <!-- is the start of the content and handle that as a special situation?

Anyway, if you wanna fork it or do anything fancier with it, lemme know. Maybe we can eventually put it on npm or whatever. We’ll have to see how useful people think it could be.


The post Styling Code In and Out of Blocks appeared first on CSS-Tricks.

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

CSS-Tricks

, ,
[Top]

Styling Comment Threads

Comment threads are one of those things that look really simple when executed right. When designing them yourself, you may find that they are rather deceptively simple. There is a lot that goes into designing nice and usable comment threads, and in this article, I will try my best to walk you through the steps to building a comment thread, that is great to look at, and a joy to use.

What makes a good comment thread?

Before diving into designing and writing code, let’s break down what actually makes a good comment thread. A good comment thread will have the following characteristics:

  1. Comments are readable, and all the important data-points are visible to the user at a glance. This includes the author, number of votes, timestamp, and the content.
  2. Different parts of comments are visually distinct, so that the user is immediately able to understand what is what. This is especially important for the action buttons (e.g. reply, report), and links.
  3. There must be visual cues for hierarchies between the comments. This is a requirement for having nested comments, i.e. one comment being a reply to another one.
  4. There should be an easy way to quickly scroll to a comment, especially to the parent comment from its children (i.e. replies).
  5. Comments should also have a toggle feature which allows the users to hide and show the comment and its replies.

As you can see, that’s quite a lot to consider! There are also some nice-to-haves that we won’t cover in this article, but are certainly good enhancements:

  1. Users should be able to flag a comment so a moderator is aware of inappropriate content.
  2. A comment can be up- or down-voted as a way of signaling helpful and unhelpful comments.
  3. Only the first few comments are displayed and the user is able to load more comments.

The above features would require at least a bit of JavaScript to pull off. Moreover, depending on the tech stack, these features could just as likely be implemented server side, especially keeping track of the number of votes and flag status of comments. That is why we will focus only on styling the comment threads in this article. With that out of the way, let’s knock out the first set of points and design our comment thread.

A basic comment thread

A comment by itself has a pretty simple structure. Here’s a skeleton of a single comment:

A single comment structure

So far, so good. Notice how the replies have an extra margin to the left. This is meant to satisfy the visual hierarchy (point #3 above). The markup for the above structure could look something like this:

<div class="comment">    <!-- Comment heading start -->   <div class="comment-heading">      <!-- Comment voting start -->     <div class="comment-voting">       <button type="button">Vote up</button>       <button type="button">Vote down</button>     </div>     <!-- Comment voting end -->      <!-- Comment info (author, # of votes, time added) start -->     <div class="comment-info">       <a href="#" class="comment-author">{{ author }}</a>       <p>         {{ # of votes }} • {{ time_added }}       </p>     </div>     <!-- Comment info (author, # of votes, time added) end -->   </div>   <!-- Comment heading end -->    <!-- Comment body start -->   <div class="comment-body">     <p>       {{ comment_content }}     </p>     <button type="button">Reply</button>     <button type="button">Flag</button>   </div>   <!-- Comment body end -->    <!-- Replies start -->   <div class="replies">     <!-- ... -->   </div>   <!-- Replies end -->  </div>

There is nothing to explain here — just a bunch of containers with some basic elements. So instead, let’s look at a real-life example, with a comment that also has a reply.

Looks pretty nice, right? The above example satisfies the first three points, so we are already on our way. A few things to understand about the code above:

  • The replies are given an extra margin to the left side to provide a visual cue of the hierarchy.
  • The vote up and vote down buttons are using HTML character codes for the icons.
  • The class .sr-only hides elements on all devices except on screen readers. This makes the voting buttons accessible. If you are using a framework, like Bootstrap or Halfmoon, this class comes automatically packed in.

Adding links that jump to comments

Now that we have a basic comment thread going, let’s add a feature to help users quickly scroll to a comment. As mentioned above, this is especially useful when someone wants to jump to the parent comment of a reply.

In order to build it, we need to decide what these links look like. While this is entirely subjective, one particular design I really like is a clickable “border” on the left hand side of the comment, something like this:

Link (marked in red) that allows users to jump to a comment

In order to accommodate the link, we are pushing the comment body to the right and aligning it with the replies. This design also has the added benefit of reinforcing the hierarchy between the comments, because you can simply look at the number of border links to the left and determine the level of nesting of the comment you are currently reading. And of course, you can also immediately jump to any upper level by clicking on the border links.

To actually create these border links, we need to add anchor elements (<a href="...">) inside each of our comments, and style these anchor elements to look like the borders that can be clicked. Let’s add them to our first code example above.

Here are a few things to understand about the changes made:

  • Anchor links are added inside each of the comments, and these are styled to look like borders on the left-hand side.
  • The body of the comment is now aligned with the replies.
  • The .comment-heading (which contains the votes, author, and time added) has a fixed height of 50px. Therefore, by giving the border links the properties of position:absolute, top: 50px, and height: calc(100% - 50px), we are ensuring that they will start right below the heading, and go all the way down to the end of the comment. If you are not familiar with the calc() function, you can read this cool guide by Chris.
  • The border links have a clipped background, and are also given a width of 12px along with a border-width of 4px on the left and right. This means that while the visible area is only 4px wide, the actual clickable area is 12px wide. A wider surface is to help the users have an easier time actually positioning their pointers on the link and clicking it, because 4px is a little too narrow, but anything wider would look off visually.

With all that, we have knocked out the first four of the points mentioned above. Let’s add more comments to the code example to see how it would look.

Allowing users to hide/show comments with a click

At this point, we have a pretty darn satisfactory comment thread. This design by itself can work for quite a lot of real life use cases. However, let’s go one step farther and add our toggle feature, i.e. hiding and showing comments with a click.

The quickest and easiest way to allow users to hide and show comments with a click is to make use of the <details> and <summary> elements. To put it simply, the visibility of the entire <details> can be toggled by clicking on the <summary>. There is no JavaScript involved, and these tags are supported by ~96% of browsers at this moment. Once again, if you are unfamiliar with these concepts, you can learn more in yet another article from Chris.

Anyway, to actually implement this, we need to make the following changes to our code:

  • Change comments from <div> to <details>, i.e. all the elements with the class .comment will now be a <details> element.
  • Wrap the comment heading (.comment-heading) inside of a <summary> tag.
  • Provide a visual cue to the user to tell them whether a comment is hidden or not.

Seems easy enough. Anyway, here’s our new implementation:

Here are the final things to understand about the changes made:

  • The comments are now <details>, and they are all given the open attribute, so they are visible (i.e. open) by default.
  • The comment headings are now wrapped inside of <summary> tags. The default arrow is also removed.
  • The cue for the visibility status of the comments is mainly created using small text at the right of the comment. The content of this text changes depending on whether the parent <details> element has the open attribute or not. The text itself is a simple pseudo-element created using the ::after selector. Moreover, closed comments also have a border on the bottom to show the users that there is more to see.
  • At the very end of the CSS code, you will find some styles inside the @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {...} selector. This is a hack that only targets Internet Explorer. You see, IE does not support <details>, so they are always open by default there. By removing the text and resetting the cursor, IE users will see a regular comment thread, only without the ability to actually toggle the comment visibility. So, no harm, no foul.

Honorable mentions

Before we end this article, let’s talk about a few more things that is worth considering when designing comment threads in real-life applications.

What if the comment thread has no comments to show?

This may be a very simple problem to solve, but it is often easy to overlook these simple things. If a comment thread does not have any comments (empty state), we need to communicate that clearly to the user. A simple paragraph containing the line “There are no comments yet.” along with a form containing a text box and submit button can go a very long way, and should be the bare minimum when dealing with empty states. If you want to go the extra mile, you can also have a nice image (that communicates the message) accompanying the form.

How to handle the form for replying to a comment?

When it comes to the form for replying to a comment, different websites have different implementations. Some use the old fashioned way of redirecting users to a new page which contains the form — a simple text box with a submit button. Others open up a form right within the comment thread itself, usually with a simple toggle. The latter paradigm obviously requires JavaScript, but it is more more popular these days. For instance, in our example above, we could have a simple form which can be toggled by clicking on the Reply button, like so:

In the above example, we added simple forms inside the comment bodies, and gave them the class .d-none by default, which sets display: none; and hides them from view. Thanks to the simple event listener, any button with the attributes data-toggle="reply-form" and data-target="{{ comment_reply_form_id }} can be clicked to toggle the visibility of the forms. This is a very simple example of handling the reply forms with ease.

Where to place a new reply after a user is done posting it?

Let’s say a user replies to a comment using a form similar to the one shown above. Do you show it above the existing replies or below it? The answer is that it should always be shown above the other replies right after the user posts it for the first time. When a person fills out a form and submits it, they want immediate feedback to tell them that it worked. Therefore, by placing the new reply above the others, we are providing this feedback to the user without them needing to scroll down. On subsequent loads, you can of course arrange your comment replies according to whatever algorithm you see fit for your website.

Handling Markdown and code blocks

Many websites, particularly developer blogs, need to support markdown and code blocks in their comments. This is a much bigger discussion, perhaps warranting a dedicated article on this topic. However, for the sake of this article, let’s just say that there are plenty of Markdown editors out there that you can attach to a text box quite easily. Most of them work with JavaScript, so they should be fairly easy to integrate in our examples. One such plugin is markdown-it, which has a permissive MIT license. You can also look into WYSIWYG editors, which also serve a very similar purpose when it comes to comments on the web.

Spam prevention and user authentication

If you give users a form to provide their inputs, you can guarantee that you will find spam coming your way, so this is obviously an issue to address when building comment threads. A great way to reduce spam is to use services like reCAPTCHA from Google. For instance, in our example above, a reCAPTCHA box could be placed right below the Submit button in the reply forms. This would protect our website from abuse.

Another way to prevent spam is to only allow authenticated users to post comments, i.e. a user must have an account and be logged in to post a comment. Every comment would obviously be linked to an account, so this has the benefit of allowing moderators to handle users who continuously post spam or low effort content. In terms of handling it in the UI, a great way of doing it is by redirecting users to a login page when they click on the Reply or Post comment button if they are not logged in. Once they complete the authentication process, we can simply redirect them back to the comment thread and open up the form.


And we are done! We have fulfilled all five of our points, and have designed a nice-looking comment thread that is highly usable and accessible, with some cool features like jumping to comments, and togging the visibility of each comment. We also talked about forms inside comment threads, and discussed other things to consider in real-life applications. The bulk of our comment thread works using only CSS (no JavaScript), which goes to show you how far CSS has actually come.


The post Styling Comment Threads appeared first on CSS-Tricks.

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

CSS-Tricks

, ,
[Top]

Comparing Styling Methods in 2020

Over on Smashing, Adebiyi Adedotun Lukman covers all these styling methods. It’s in the context of Next.js, which is somewhat important as Next.js has some specific ways you work with these tools, is React and, thus, is a components-based architecture. But the styling methods talked about transcend Next.js, and can apply broadly to lots of websites.

Here are my hot takes on the whole table-of-contents of styling possibilities these days.

  • Regular CSS. If you can, do. No build tooling is refreshing. It will age well. The only thing I really miss without any other tooling is nesting media queries.
  • Sass. Sass has been around the block and is still a hugely popular CSS preprocessor. Sass is built into tons of other tools, so choosing Sass doesn’t always mean the same thing. A simple Sass integration might be as easy as a sass --watch src/style.scss dist/style.css npm script. Then, once you’ve come to terms with the fact that you have a build process, you can start concatenating files, minifying, busting cache, and all this stuff that you’re probably going to end up doing somehow anyway.
  • Less & Stylus. I’m surprised they aren’t more popular since they’ve always been Node and work great with the proliferation of Node-powered build processes. Not to mention they are nice feature-rich preprocessors. I have nothing against either, but Sass is more ubiquitous, more actively developed, and canonical Sass now works fine in Node-land,
  • PostCSS. I’m not compelled by PostCSS because I don’t love having to cobble together the processing features that I want. That also has the bad side effect of making the process of writing CSS different across projects. Plus, I don’t like the idea of preprocessing away modern features, some of which can’t really be preprocessed (e.g. custom properties cannot be preprocessed). But I did love Autoprefixer when we really needed that, which is based on PostCSS.
  • CSS Modules. If you’re working with components in any technology, CSS modules give you the ability to scope CSS to that component, which is an incredibly great idea. I like this approach wherever I can get it. Your module CSS can be Sass too, so we can get the best of both worlds there.
  • CSS-in-JS. Let’s be real, this means “CSS-in-React.” If you’re writing Vue, you’re writing styles the way Vue helps you do it. Same with Svelte. Same with Angular. React is the un-opinionated one, leaving you to choose between things like styled-components, styled-jsx, Emotion… there are a lot of them. I have projects in React and I just use Sass+CSS Modules and think that’s fine, but a lot of people like CSS-inJS approaches too. I get it. You get the scoping automatically and can do fancy stuff like incorporate props into styling decisions. Could be awesome for a design system.

If you want to hear some other hot takes on this spectrum, the Syntax FM fellas sounded off on this recently.


The post Comparing Styling Methods in 2020 appeared first on CSS-Tricks.

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

CSS-Tricks

, , ,
[Top]

Styling Complex Labels

Danielle Romo covers the HTML pattern you need when you have a wordy <label> with fancy styling for an <input type="radio">.

The trick? The ol’ <span class="hidden-visually"> that contains the label that you want to be read, and a <span aria-hidden="true"> with the visual-only content.


I think it’s interesting how often people are landing on this pattern. Have you seen Ethan’s The World-Wide Work? The drop-cap pattern he talks about here lands on essentially the same pattern.

<span aria-hidden="true">   Markup for the visual experience only,   where you can (somewhat safely) use markup    that would be crap for screen readers.   </span>  <span class="visually-hidden">   Markup for the read experience only, that   you keep very clean on purpose. </span>

That class is like this.

Direct Link to ArticlePermalink


The post Styling Complex Labels appeared first on CSS-Tricks.

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

CSS-Tricks

, ,
[Top]

Responsive Styling Using Attribute Selectors

One of the challenges we face when implementing class-based atomic styling is that it often depends on a specific breakpoint for context.

<div class="span-12"></div> <!-- we want this for small screens  --> <div class="span-6"></div>  <!-- we want this for medium screens --> <div class="span-4"></div>  <!-- we want this for large screens  -->

It’s common to use a prefix to target each breakpoint:

<div class="sm-span-12 md-span-6 lg-span-4"></div>

This works well until we start adding multiple classes. That’s when it becomes difficult to keep a track what relates to what and where to add, remove. or change stuff.

<div class="   sm-span-12    md-span-6    lg-span-4    sm-font-size-xl    md-font-size-xl    lg-font-size-xl    md-font-weight-500    lg-font-weight-700"> </div>

We can try to make it more readable by re-grouping:

<div class="   sm-span-12    sm-font-size-xl  
   md-span-6    md-font-size-xl    md-font-weight-500  
   lg-span-4    lg-font-size-xl    lg-font-weight-700"> </div>

We can add funky separators (invalid class names will be ignored):

<div class="   [    sm-span-12     sm-font-size-xl    ],[    md-span-6     md-font-size-xl     md-font-weight-500    ],[    lg-span-4     lg-font-size-xl     lg-font-weight-700   ]"> </div>

But this still feels messy and hard to grasp, at least to me.

We can get a better overview and avoid implementation prefixes by grouping attribute selectors instead of actual classes:

<div    sm="span-12 font-size-lg"   md="span-6 font-size-xl font-weight-500"   lg="span-4 font-size-xl font-weight-700" > </div>

These aren’t lost of classes but a whitespace-separated list of attributes we can select using [attribute~="value"], where ~= requires the exact word to be found in the attribute value in order to match.

@media (min-width: 0) {  [sm~="span-1"] { /*...*/ }                [sm~="span-2"] { /*...*/ }     /* etc. */  } @media (min-width: 30rem) {  [md~="span-1"] { /*...*/ }     [md~="span-2"] { /*...*/ }     /* etc. */    } @media (min-width: 60rem) {  [lg~="span-1"] { /*...*/ }     [lg~="span-2"] { /*...*/ }     /* etc. */    }

It may be a bit odd-looking but I think translating atomic classes to  attributes is fairly straightforward (e.g. .sm-span-1 becomes [sm~="span-1"]). Plus, attribute selectors have the same specificity as classes, so we lose nothing there. And, unlike classes, attributes can be written without escaping special characters, like /+.:?.

That’s all! Again, this is merely an idea that aims to make switching declarations in media queries easier to write, read and manage. It’s definitely not a proposal to do away with classes or anything like that.

The post Responsive Styling Using Attribute Selectors appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

Styling Layout Wrappers In CSS

Two things that strike me often about the web are how many ways there are to go about the same thing and how many considerations go into even the most seemingly simple things.

Working with wrapper elements is definitely on both those lists. Wrappers (or containers or whatever) are so common — especially when establishing grid layouts and boundaries for the elements inside them — that it’s easy to take them for granted and reach for them without stepping back to consider how they work, why we use them, and how to use them effectively.

Ahmed Shadeed wrote up the most exhaustive article on wrappers I’ve ever read. He provides a brief overview of them before diving into a bunch of considerations and techniques for working with them, including:

  • When to use them
  • How to size them
  • Positioning them
  • Adding margin and padding
  • Working with CSS grid and other display values
  • Breaking out of the wrapper
  • Using CSS custom properties

If you take the images from the article, it tells a pretty cool story.

Direct Link to ArticlePermalink

The post Styling Layout Wrappers In CSS appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

Styling in the Shadow DOM With CSS Shadow Parts 

Safari 13.1 just shipped support for CSS Shadow Parts. That means the ::part() selector is now supported in Chrome, Edge, Opera, Safari, and Firefox. We’ll see why it’s useful, but first a recap on shadow DOM encapsulation…

The benefits of shadow DOM encapsulation

I work at giffgaff where we have a wide variety of CSS code that has been written by many different people in many different ways over the years. Let’s consider how this might be problematic. 

Naming collisions

Naming collisions between classes can easily crop up in CSS. One developer might create a class name like .price. Another developer (or even the same one) might use the same class name, without knowing it.

CSS won’t alert you to any error here. Now, any HTML elements with this class will receive the styling intended for two completely different things.

Shadow DOM fixes this problem. CSS-in-JS libraries, like Emotion and styled-components, also solve this issue in a different way by generating random class names, like .bwzfXH. That certainly does help avoid conflicts! However, CSS-in-JS doesn’t prevent anybody from breaking your component in other ways. For example…

Base styles and CSS resets

Styles can be applied using HTML element selectors like <button> and <div>. These styles could break a component. Shadow DOM is the only thing that offers (almost) full encapsulation — you can rest assured that your component will look the same, even in a messy  !important  strewn codebase because each component is encapsulated.

/* This will have no effect on buttons inside shadow DOM */ button { background-color: lime !important; }

I wouldn’t say it’s good practice to style elements this way, but it happens. Even it does, those styles will have no effect on the shadow DOM.

It’s worth noting that inheritable styles like color, font and line-height are still inherited in a shadow DOM. To prevent that, use all: initial  or, preferably, all: revert once it has better browser support.

Let’s look at a common example of CSS applied directly to HTML elements. Consider this code from Eric Meyer’s reset

html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed,  figure, figcaption, footer, header, hgroup,  menu, nav, output, ruby, section, summary, time, mark, audio, video {   margin: 0;   padding: 0;   border: 0;   font-size: 100%;   font: inherit;   vertical-align: baseline; }

What if the component we’re working with makes use of the user agent’s default values for margin and padding? This reset might cause it to appear broken since those defaults are effectively wiped out.

Shadow DOM is a way to avoid these problems. Shadow DOM allows us to feel fully confident that a component will render as expected, regardless of what codebase it ends up in. Equally, none of the code meant only for a component can inadvertently affect anything else — all without resorting to onerous class naming conventions. Shadow DOM offers a level of encapsulation that can’t be achieved any other way.

Encapsulation is great, but we also want our components to be themeable and customizable. That’s been made far easier with the ::part selector. 

Styling shadow DOM with ::part()

Until now, the only way for CSS to modify the styling of a custom element from outside of the shadow DOM was to use CSS custom properties. In a strict design system where you only want to allow limited changes, that might be ideal. If you want your component to be more versatile, it creates a problem. Every CSS property you want to offer up for styling needs to be defined using a custom property. Just the sound of that seems tedious.

The situation is compounded further if we want to style a component differently based on pseudo-classes, like :hover. Basically, we end up with loads of custom properties. Let’s look at an example from Ionic, an open source set of web components. Just look at all the custom properties defined on the Ionic button component.

Go ahead, I’ll wait.

I counted 23 custom properties. Needless to say, that’s less than ideal.

Here’s an example using ::part() to style the element instead. 

In this Pen, I’m simply changing the color, border and background-color properties, but I could use whatever I want without being constrained by what custom properties have been defined. Notice that I can also style different states of the part using pseudo-classes, like :hover and :focus.

The entire component in this button example is being exposed for styling, but if your web component consists of multiple HTML elements, you can expose only selected parts of the component to this sort of styling — hence the name ::part. This stops users of the component from styling any arbitrary element inside the shadow tree. It is up the the component author to expose the parts of the component they explicitly want to. Other parts of the component can be kept visually uniform or make use of custom properties for a more minimal customizability. 

So, how do we set this up for our own components? Let’s look at using ::part to make certain elements of a web component eligible for styling. All we do is add a part attribute on the element we want to be exposed.

<div part="box">...</div>   <button>Click me</button>

In this example the div is customizable with the full gamut of CSS — any CSS property can be changed. The button, however, is locked down — it cannot be visually changed by anybody except the component author.

And the same way an HTML element can have multiple classes, an element can have multiple part names: 

<div part="box thing">...</div>

So that’s what we get with ::part: by exposing “parts” of an element we can provide some flexibility in how a web component is used while exercising protection in other areas. Whether it’s your design system, a component library, or what have you, the fact that CSS Shadow Parts are becoming mainstream gives us yet another exciting tool to work with.

The post Styling in the Shadow DOM With CSS Shadow Parts  appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

Custom Styling Form Inputs With Modern CSS Features

It’s entirely possible to build custom checkboxes, radio buttons, and toggle switches these days, while staying semantic and accessible. We don’t even need a single line of JavaScript or extra HTML elements! It’s actually gotten easier lately than it has been in the past. Let’s take a look.

Here’s where we’ll end up:

Things sure have gotten easier than they were!

The reason is that we can finally style the ::before and ::after pseudo-elements on the <input> tag itself. This means we can keep and style an <input> and won’t need any extra elements. Before, we had to rely on the likes of an extra <div> or <span>, to pull off a custom design.

Let’s look at the HTML

Nothing special here. We can style our inputs with just this HTML:

<!-- Checkbox --> <input type="checkbox">  <!-- Radio --> <input type="radio">  <!-- Switch --> <input type="checkbox" class="switch">

That’s it for the HTML part, but of course it’s recommended to have name and id attributes, plus a matching <label> element:

<!-- Checkbox --> <input type="checkbox" name="c1" id="c1"> <label for="c1">Checkbox</label>  <!-- Radio --> <input type="radio" name="r1" id="r1"> <label for="r1">Radio</label>  <!-- Switch --> <input type="checkbox" class="switch" name="s1" id="s1"> <label for="s1">Switch</label>

Getting into the styling 

First of all, we check for the support of appearance: none;, including it’s prefixed companions. The appearance property is key because it is designed to remove a browser’s default styling from an element. If the property isn’t supported, the styles won’t apply and default input styles will be shown. That’s perfectly fine and a good example of progressive enhancement at play.

@supports(-webkit-appearance: none) or (-moz-appearance: none) {   input[type='checkbox'],   input[type='radio'] {     -webkit-appearance: none;     -moz-appearance: none;   } }

As it stands today, appearance  is a working draft, but here’s what support looks like:

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
82* 74* No 79* TP*

Mobile / Tablet

Android Chrome Android Firefox Android iOS Safari
79* 68* 76* 13.3*

Like links, we’ve gotta consider different interactive states with form elements. We’ll consider these when styling our elements:

  • :checked
  • :hover
  • :focus
  • :disabled

For example, here’s how we can style our toggle input, create the knob, and account for the :checked state:

/* The toggle container */ .switch {   width: 38px;   border-radius: 11px; }  /* The toggle knob */ .switch::after {   left: 2px;   top: 2px;   border-radius: 50%;   width: 15px;   height: 15px;   background: var(--ab, var(--border));   transform: translateX(var(--x, 0)); }  /* Change color and position when checked */ .switch:checked {   --ab: var(--active-inner);   --x: 17px; }  /* Drop the opacity of the toggle knob when the input is disabled */ .switch:disabled:not(:checked)::after {   opacity: .6; }

We are using the <input> element like a container. The knob inside of the input is created with the ::after pseudo-element. Again, no more need for extra markup!

If you crack open the styles in the demo, you’ll see that we’re defining some CSS custom properties because that’s become such a nice way to manage reusable values in a stylesheet:

@supports(-webkit-appearance: none) or (-moz-appearance: none) {   input[type='checkbox'],   input[type='radio'] {     --active: #275EFE;     --active-inner: #fff;     --focus: 2px rgba(39, 94, 254, .25);     --border: #BBC1E1;     --border-hover: #275EFE;     --background: #fff;     --disabled: #F6F8FF;     --disabled-inner: #E1E6F9;   } }

But there’s another reason we’re using custom properties — they work well for updating values based on the state of the element! We won’t go into full detail here, but here’s an example how we can use custom properties for different states.

/* Default */ input[type='checkbox'], input[type='radio'] {   --active: #275EFE;   --border: #BBC1E1;   border: 1px solid var(--bc, var(--border)); }  /* Override defaults */ input[type='checkbox']:checked, input[type='radio']:checked {   --b: var(--active);   --bc: var(--active); }    /* Apply another border color on hover if not checked & not disabled */ input[type='checkbox']:not(:checked):not(:disabled):hover, input[type='radio']:not(:checked):not(:disabled):hover {   --bc: var(--border-hover); }

For accessibility, we ought to add a custom focus style. We are removing the default outline because it can’t be rounded like the rest of the things we’re styling. But a border-radius along with a box-shadow can make for a rounded style that works just like an outline.

input[type='checkbox'], input[type='radio'] {   --focus: 2px rgba(39, 94, 254, .25);   outline: none;   transition: box-shadow .2s; }  input[type='checkbox']:focus, input[type='radio']:focus {   box-shadow: 0 0 0 var(--focus); }

It’s also possible to align and style the <label> element which directly follows the <input> element in the HTML:

<input type="checkbox" name="c1" id="c1"> <label for="c1">Checkbox</label>
input[type='checkbox'] + label, input[type='radio'] + label {   display: inline-block;   vertical-align: top;   /* Additional styling */ }  input[type='checkbox']:disabled + label, input[type='radio']:disabled + label {     cursor: not-allowed; }

Here’s that demo again:

Hopefully, you’re seeing how nice it is to create custom form styles these days. It requires less markup, thanks to pseudo-elements that are directly on form inputs. It requires less fancy style switching, thanks to custom properties. And it has pretty darn good browser support, thanks to @supports.

All in all, this is a much more pleasant developer experience than we’ve had to deal with in the past!

The post Custom Styling Form Inputs With Modern CSS Features appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,
[Top]

Thinking Through Styling Options for Web Components

Where do you put styles in web components?

I’m assuming that we’re using the Shadow DOM here as, to me, that’s one of the big draws of a web component: a platform thing that is a uniquely powerful thing the platform can do. So this is about defining styles for a web component in a don’t-leak-out way, and less so a way to get global styles to leak in (although that’s very interesting as well, which can be done via custom properties which we’ll look at later in the article).

If you’re building the template inside the JavaScript — which is nice because of template literals and how we can sprinkle our data into the template nicely — you need access to those styles in JavaScript.

const template = `   <style>$  {styles}</style>   <div class="$  {class}">     <h2>$  Thinking Through Styling Options for Web Components</h2>     $  {content}   </div> `;

Where does that style variable come from? Maybe also a template literal?

const style = `   :host {     background: white;   }   h2 {     font: 900 1.5rem/1.1 -system-ui, sans-serif;   } `;

I guess that’s fine, but it makes for a big messy block of code just dunked somewhere in the class where you’re trying to build this web component.

Another way is to <template> the template and make a <style> block part of it.

<template id="card-template">   <style>     :host {       background: white;     }     h2 {       font: 900 1.5rem/1.1 -system-ui, sans-serif;     }   </style>    <div id="card-hook">     <h2 id="title-hook"></h2>     <p id="desc-hook"></p>   </div> </template>

I can see the appeal with this because it keeps HTML in HTML. What I don’t love about it is that you have to do a bunch of manual shadowRoot.querySelector("#title-hook").innerHTML = myData.title; work in order to flesh out that template. That doesn’t feel like a convenient template. I also don’t love that you need to just chuck this template somewhere in your HTML. Where? I dunno. Just chuck it in there. Chuck it.

The CSS is moved out of the JavaScript too, but it just moved from one awkward location to another.

If we wanted to keep the CSS in a CSS file, we can sorta do that like this:

<template id="card-template">   <style>     @import "/css/components/card.css";   </style>    <div id="card-hook">     <h2 id="title-hook"></h2>     <p id="desc-hook"></p>   </div> </template>

(The use of <link rel="import" type="css" href=""> is deprecated, apparently.)

Now we have @import which is an extra HTTP Request, and notorious for being a performance hit. An article by Steven Lambert says it clocked in at half a second slower. Not ideal. I don’t suppose it would be much better to do this instead:

class MyComponent extends HTMLElement {        constructor() {     super();     this.attachShadow({ mode: "open" });      fetch('/css/components/card.css')       .then(response => response.text())       .then(data => {         let node = document.createElement('style');         node.innerHTML = data;         document.body.appendChild(node);       });   }    // ... }

Seems like that would potentially be a Flash-of-Unstyled-Web-Component? I guess I should get off my butt and test it.

Now that I’m digging into this again, it seems like ::part has gotten some steam (explainer). So I can do…

const template = `   <div part="card">     <h2>$  Thinking Through Styling Options for Web Components</h2>     $  {content}   </div> `;

…then write styles in a global stylesheet that only apply inside that Shadow DOM, like:

my-card::part(card) {   background: black;   color: white; }

…which has a smidge of browser support, but maybe not enough?

These “part” selectors can only touch the exact element it’s connected to. You’d have to do all your styling by applying a part name to every single DOM node and then styling each entirely on its own. That’s no fun, particularly because the appeal of the Shadow DOM is this isolated styling environment in which we’re supposed to be able to write looser CSS selectors and not be worried our h2 { } style is going to leak all over the place.

Looks like if native CSS modules becomes a thing, that will be the most helpful thing that could happen.

import styles from './styles.css';  class MyElement extends HTMLElement {   constructor() {     this.attachShadow({mode: open});     this.shadowRoot.adoptedStyleSheets = [styles];   } }

I’m not sure, however, if this is any sort of performance boost. Seems like it would be a wash between this and @import. I have to say I prefer the clarity and syntax with native CSS modules. It’s nice to be writing JavaScript when working with JavaScript.

Constructable Stylesheets also look helpful for sharing a stylesheet across multiple components. But the CSS modules approach looks like it could also do that since the stylesheet has already become a variable at that point.

The post Thinking Through Styling Options for Web Components appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]