Front End Web Development

Front-end development is not a problem to be solved

Css Tricks - Tue, 11/27/2018 - 5:16am

HTML and CSS are often seen as a burden.

This is a feeling I’ve noticed from engineers and designers I’ve worked with in the past, and it’s a sentiment that’s a lot more transparent with the broader web community at large. You can hear it in Medium posts and on indie blogs, whether in conversations about CSS, web performance, or design tools.

The sentiment is that front-end development is a problem to be solved: “if we just have the right tools and frameworks, then we might never have to write another line of HTML or CSS ever again!” And oh boy what a dream that would be, right?

Well, no, actually. I certainly don’t think that front-end development is a problem at all.

What's behind this feeling? Well, designers want tools that let them draw pictures and export a batch of CSS and HTML files like Dreamweaver promised back in the day. On the other end, engineers don’t want to sweat accessibility, web performance or focus states among many, many other things. There’s simply too many edge cases, too many devices, and too many browsers to worry about. The work is just too much.

Consequently, I empathize with these feelings as a designer/developer myself, but I can’t help but get a little upset when I read about someone's relationship with Bootstrap or design systems, frameworks or CSS-in-JS solutions — and even design tools like Sketch or Figma. It appears that we treat front-end development as a burden, or something we want to void altogether by abstracting it with layers of tools.

We should see front-end development as a unique skillset that is critical to the success of any project.

I believe that’s why frameworks and tools like Bootstrap are so popular; not necessarily because they’re a collection of helpful components, but a global solution that corrects an inherent issue. And when I begin to see “Bootstrap” in multiple resumés for front-end applications, I immediately assume that we're going to be at odds with our approaches to design and development.

Bootstrap isn’t a skill though — front-end development is.

And this isn’t me just being a curmudgeon... I hope. I genuinely want tools that help us make better decisions, that help us build accessible, faster, and more beautiful websites in a way that pushes the web forward. That said, I believe the communities built up around these tools encourage designing and developing in a way that's ignorant of front-end skills and standards.

What’s the point in learning about vanilla HTML, CSS and JavaScript if they wind up becoming transpiled by other tools and languages?

Don’t get me wrong — I don’t think there’s anything wrong with Bootstrap, or CSS-in-JS, or CSS Modules, or fancy design tools. But building our careers around the limitations of these tools is a minor tragedy. Front-end development is complex because design is complex. Transpiling our spoken language into HTML and CSS requires vim and nuance, and always will. That’s not going to be resolved by a tool but by diligent work over a long period of time.

I reckon HTML and CSS deserve better than to be processed, compiled, and spat out into the browser, whether that’s through some build process, app export, or gigantic framework library of stuff that we half understand. HTML and CSS are two languages that deserve our care and attention to detail. Writing them is a skill.

I know I’m standing on a metaphorical soapbox here, and perhaps I’m being a tad melodramatic, but front-end development is not a problem to be solved. It’s a cornerstone of the web, and it’s not going away any time soon.

Is it?

The post Front-end development is not a problem to be solved appeared first on CSS-Tricks.

FUIF: Responsive Images by Design

Css Tricks - Tue, 11/27/2018 - 5:11am

Jon Sneyers:

One of the main motivations for FUIF is to have an image format that is responsive by design, which means it’s no longer necessary to produce many variants of the same image: low-quality placeholders, thumbnails, many downscaled versions for many display resolutions. A single file, truncated at different offsets, can do the same thing.

FLIF isn't anywhere near ready to use, but it's a fascinating idea. I love the idea that the format stores the image data in such a way that you request just first few kilobytes of the file and to essentially get a low-quality version, then you request more as needed. See this little demo from Eric Portis that shows it off somewhat via a Service Worker and a progressive JPG.

If this idea ever does get legs and support in browsers, Cloudinary is super well suited to take advantage of that, as they serve the best image format for the current browser — and that is massive for image performance.

Direct Link to ArticlePermalink

The post FUIF: Responsive Images by Design appeared first on CSS-Tricks.

CSS Grid in IE: Duplicate area names now supported!

Css Tricks - Mon, 11/26/2018 - 5:06am

Autoprefixer is now up to version 9.3.1 and there have been a lot of updates since I wrote the original three-part CSS Grid in IE series — the most important update of which is the new grid-areas system. This is mostly thanks to Bogdan Dolin, who has been working like crazy to fix loads of Autoprefixer issues. Autoprefixer’s grid translations were powerful before, but they have gotten far more powerful now!

Article Series:
  1. Debunking common IE Grid misconceptions
  2. CSS Grid and the new Autoprefixer
  3. Faking an auto-placement grid with gaps
  4. Duplicate area names now supported! (This Post)
How Autoprefixer supports duplicate area names

In Part 2 of the series, I pointed out why Autoprefixer wasn’t able to handle duplicate area names that were used across multiple selectors.

To summarize, this is the example I gave in the article:

.grid-alpha { grid-template-areas: "delta echo"; } .grid-beta { grid-template-areas: "echo delta"; } .grid-cell { /* What column does .grid-cell go in? */ -ms-grid-column: ???; grid-area: echo; }

We thought that since Autoprefixer didn’t have access to the DOM, there was no way of knowing what grid the grid cell belonged to and thus which column the grid cell should go in.

However, I realized something. Grid cells are only ever affected by their direct parent element (ignoring display: contents). That meant that if the grid cell exists, it will always be a direct child of the grid template element. This gave me an idea for how we can solve the grid-area name conflict! 😁

.grid-alpha { grid-template-areas: "delta echo"; } .grid-beta { /* Uh oh, duplicate area names! */ grid-template-areas: "echo delta"; } .grid-cell { /* We will use the first occurrence by default */ -ms-grid-column: 2; grid-area: echo; } /* We have detected a conflict! Prefix the class with the parent selector. */ .grid-beta > .grid-cell { /* NO MORE CONFLICT! */ -ms-grid-column: 1; }

The entire grid-areas system needed to be re-written to achieve this, but it was totally worth the effort.

The way this new system works is that .grid-cell will default to using the first grid-template-areas property that it comes across. When it hits a conflicting grid template, it will create a new rule placing the parent selector in front of the child selector like so:

[full parent selector] > [direct child selector]

This is what we know purely from looking at the CSS and knowing how CSS Grid works:

  • A grid cell must be a direct descendant of the parent grid container for CSS Grid to work (assuming display: contents isn’t used).
  • grid-beta > .grid-cell will only work on .grid-cell elements that have been placed directly inside a .grid-beta element.
  • .grid-beta > .grid-cell will have no effect on .grid-cell elements placed directly inside .grid-alpha elements.
  • .grid-beta > .grid-cell will have no effect on .grid-cell elements nested deeply inside .grid-beta elements.
  • .grid-beta > .grid-cell will override the styling of the lonely .grid-cell CSS rule both because of rule order and specificity.

Because of those reasons, Autoprefixer can pretty safely resolve these conflicts without having any access to the DOM.

That last point in the list can be a little bit dangerous. It increases specificity which means that it may cause some IE styles to override others in a way that is different from how modern overrides are working. Since the generated rules only hold IE-specific grid styles, and they only apply under very specific circumstances, this is unlikely to cause an issue in 99.999% of circumstances. There is still potential for edge cases though.

If you ever find yourself needing to increase the specificity of the grid cell selector, here’s how to go about it.

Instead of writing this:

.grid { grid-template-areas: "area-name"; } .grid-cell { grid-area: area-name; }

...we write the rule like this:

.grid { grid-template-areas: "area-name"; } .grid > .grid-cell { grid-area: area-name; }

Autoprefixer will retain the selector and output something like this:

.grid { grid-template-areas: "area-name"; } .grid > .grid-cell { -ms-grid-column: 1; -ms-grid-row: 1; grid-area: area-name; } The exciting new possibilities!

So why is duplicate area name support so exciting?

Well, for one, it was just plain annoying when accidentally using a duplicate area name in older versions of Autoprefixer. It would break in IE and, in older versions of Autoprefixer, it would fail silently.

The main reason it is exciting though is that it opens up a whole new world of IE-friendly CSS Grid possibilities!

Use modifier classes to adjust a grid template

This was the use case that really made me want to get duplicate area name support into Autoprefixer. Think about this. You have a typical site with a header, a footer, a main area, and a sidebar down either side.

See the Pen Basic website layout by Daniel Tonon (@daniel-tonon) on CodePen.

Sometimes we want both sidebars, sometimes we want one sidebar, and sometimes we want no sidebars. This was very difficult to manage back when Autoprefixer didn’t support duplicate area names since we couldn't share area names across multiple grid templates. We would have to do something like this for it to work:

/* Before Duplicate area names were supported - n = no side-bars - f = first sidebar only - s = second sidebar only - fs = first and second sidebars */ .main-area { display: grid; grid-template: "content-n" / 1fr; } .main-area.has-first-sidebar { grid-template: "first-sb-f content-f" / 300px 1fr; } .main-area.has-second-sidebar { grid-template: "content-s second-sb-s" / 1fr 300px; } .main-area.has-first-sidebar.has-second-sidebar { grid-template: "first-sb-fs content-fs second-sb-fs" / 200px 1fr 200px; } .main-area > .content { grid-area: content-n; /* no side-bars */ } .main-area > .sidebar.first { grid-area: first-sb-f; /* first sidebar only */ } .main-area > .sidebar.second { grid-area: second-sb-s; /* second sidebar only */ } .main-area.has-first-sidebar > .content { grid-area: content-f; /* first sidebar only */ } .main-area.has-second-sidebar > .content { grid-area: content-s; /* second sidebar only */ } .main-area.has-first-sidebar.has-second-sidebar > .content { grid-area: content-fs; /* first and second sidebars */ } .main-area.has-first-sidebar.has-second-sidebar > .sidebar.first { grid-area: first-sb-fs; /* first and second sidebars */ } .main-area.has-first-sidebar.has-second-sidebar > .sidebar.second { grid-area: second-sb-fs; /* first and second sidebars */ } Autoprefixer translation /* Before Duplicate area names were supported - n = no sidebars - f = first sidebar only - s = second sidebar only - fs = first and second sidebars */ .main-area { display: -ms-grid; display: grid; -ms-grid-rows: auto; -ms-grid-columns: 1fr; grid-template: "content-n" / 1fr; } .main-area.has-first-sidebar { -ms-grid-rows: auto; -ms-grid-columns: 300px 1fr; grid-template: "first-sb-f content-f" / 300px 1fr; } .main-area.has-second-sidebar { -ms-grid-rows: auto; -ms-grid-columns: 1fr 300px; grid-template: "content-s second-sb-s" / 1fr 300px; } .main-area.has-first-sidebar.has-second-sidebar { -ms-grid-rows: auto; -ms-grid-columns: 200px 1fr 200px; grid-template: "first-sb-fs content-fs second-sb-fs" / 200px 1fr 200px; } .main-area > .content { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: content-n; /* no side-bars */ } .main-area > .sidebar.first { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: first-sb-f; /* first sidebar only */ } .main-area > .sidebar.second { -ms-grid-row: 1; -ms-grid-column: 2; grid-area: second-sb-s; /* second sidebar only */ } .main-area.has-first-sidebar > .content { -ms-grid-row: 1; -ms-grid-column: 2; grid-area: content-f; /* first sidebar only */ } .main-area.has-second-sidebar > .content { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: content-s; /* second sidebar only */ } .main-area.has-first-sidebar.has-second-sidebar > .content { -ms-grid-row: 1; -ms-grid-column: 2; grid-area: content-fs; /* first and second sidebars */ } .main-area.has-first-sidebar.has-second-sidebar > .sidebar.first { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: first-sb-fs; /* first and second sidebars */ } .main-area.has-first-sidebar.has-second-sidebar > .sidebar.second { -ms-grid-row: 1; -ms-grid-column: 3; grid-area: second-sb-fs; /* first and second sidebars */ }

🤢 Oh yeah, and don’t forget media queries! 🤮

That example was based off actual code that I had to write for an actual project… so yeah, I really wanted to get duplicate area name support into Autoprefixer.

Now that we do have duplicate area name support, we can simplify that code down to something much nicer 😊

/* Duplicate area names now supported! This code will work perfectly in IE with Autoprefixer 9.3.1 */ .main-area { display: grid; grid-template: "content" / 1fr; } .main-area.has-first-sidebar { grid-template: "first-sb content" / 300px 1fr; } .main-area.has-second-sidebar { grid-template: "content second-sb" / 1fr 300px; } .main-area.has-first-sidebar.has-second-sidebar { grid-template: "first-sb content second-sb" / 200px 1fr 200px; } .content { grid-area: content; } .sidebar.first { grid-area: first-sb; } .sidebar.second { grid-area: second-sb; } Autoprefixer translation .main-area { display: -ms-grid; display: grid; -ms-grid-rows: auto; -ms-grid-columns: 1fr; grid-template: "content" / 1fr; } .main-area.has-first-sidebar { -ms-grid-rows: auto; -ms-grid-columns: 300px 1fr; grid-template: "first-sb content" / 300px 1fr; } .main-area.has-second-sidebar { -ms-grid-rows: auto; -ms-grid-columns: 1fr 300px; grid-template: "content second-sb" / 1fr 300px; } .main-area.has-first-sidebar.has-second-sidebar { -ms-grid-rows: auto; -ms-grid-columns: 200px 1fr 200px; grid-template: "first-sb content second-sb" / 200px 1fr 200px; } .content { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: content; } .main-area.has-first-sidebar > .content { -ms-grid-row: 1; -ms-grid-column: 2; } .main-area.has-second-sidebar > .content { -ms-grid-row: 1; -ms-grid-column: 1; } .main-area.has-first-sidebar.has-second-sidebar > .content { -ms-grid-row: 1; -ms-grid-column: 2; } .sidebar.first { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: first-sb; } .main-area.has-first-sidebar.has-second-sidebar > .sidebar.first { -ms-grid-row: 1; -ms-grid-column: 1; } .sidebar.second { -ms-grid-row: 1; -ms-grid-column: 2; grid-area: second-sb; } .main-area.has-first-sidebar.has-second-sidebar > .sidebar.second { -ms-grid-row: 1; -ms-grid-column: 3; }

With that CSS, we are now able to use the has-first-sidebar and has-second-sidebar classes on the grid element to modify what grid template the browser uses. We no longer have to worry about defining exactly what grid grid cells are placed in.

Assign components reusable area names

One of the limitations of being forced to use unique area names was that components could not be given a single, consistent re-usable area name. We were forced to come up with unique area names for every grid.

Now that Autoprefixer has duplicate area name support, that limitation is gone. We can provide every component with a single area name that is based on their component name. We can then reference that one area name whenever we want to place that component inside a grid layout.

/* header.scss */ .header { grid-area: header; } /* nav.scss */ .nav { grid-area: nav; } /* sidebar.scss */ .sidebar { grid-area: sidebar; } /* content.scss */ .content { grid-area: content; } /* subscribe.scss */ .subscribe { grid-area: subscribe; } /* footer.scss */ .footer { grid-area: footer; } /* layout.scss */ .single-sidebar-layout { display: grid; grid-template-areas: "header header" "nav content" "subscribe content" "footer footer"; } .double-sidebar-layout { display: grid; grid-template-areas: "header header header" "nav content sidebar" "nav subscribe sidebar" "footer footer footer"; } Autoprefixer translation /* header.scss */ .header { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 2; grid-area: header; } .double-sidebar-layout > .header { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column-span: 3; } /* nav.scss */ .nav { -ms-grid-row: 2; -ms-grid-column: 1; grid-area: nav; } .double-sidebar-layout > .nav { -ms-grid-row: 2; -ms-grid-row-span: 2; -ms-grid-column: 1; } /* sidebar.scss */ .sidebar { -ms-grid-row: 2; -ms-grid-row-span: 2; -ms-grid-column: 3; grid-area: sidebar; } /* content.scss */ .content { -ms-grid-row: 2; -ms-grid-row-span: 2; -ms-grid-column: 2; grid-area: content; } .double-sidebar-layout > .content { -ms-grid-row: 2; -ms-grid-column: 2; } /* subscribe.scss */ .subscribe { -ms-grid-row: 3; -ms-grid-column: 1; grid-area: subscribe; } .double-sidebar-layout > .subscribe { -ms-grid-row: 3; -ms-grid-column: 2; } /* footer.scss */ .footer { -ms-grid-row: 4; -ms-grid-column: 1; -ms-grid-column-span: 2; grid-area: footer; } .double-sidebar-layout > .footer { -ms-grid-row: 4; -ms-grid-column: 1; -ms-grid-column-span: 3; } /* layout.scss */ .single-sidebar-layout { display: -ms-grid; display: grid; grid-template-areas: "header header" "nav content" "subscribe content" "footer footer"; } .double-sidebar-layout { display: -ms-grid; display: grid; grid-template-areas: "header header header" "nav content sidebar" "nav subscribe sidebar" "footer footer footer"; }

Here’s what we’ve got. Note that this should be viewed in IE.

See the Pen component-area-name technique by Daniel Tonon (@daniel-tonon) on CodePen.

Duplicate area name limitation

There is one fairly common use case that Autoprefixer still can’t handle at the moment. When the parent selector of the grid cell does not match up with the grid template selector, it tries to resolve a duplicate area name:

.grand-parent .mother { grid-template-areas: "child"; } .grand-parent .father { grid-template-areas: "child child"; } /* This will work */ .grand-parent .mother .child { grid-area: child; } /* This does not work because: - ".uncle" != ".grand-parent .mother" - ".uncle" != ".grand-parent .father" - "child" is a duplicate area name */ .uncle .child { grid-area: child; }

Here is a more realistic scenario of the current limitation in Autoprefixer’s algorithm:

.component .grid { display: grid; grid-template-areas: "one two"; grid-template-columns: 1fr 1fr; } /* This rule triggers duplicate area name conflicts. */ .component.modifier .grid { grid-template-areas: "one ... two"; grid-template-columns: 1fr 1fr 1fr; } /* This does not work because: - ".component" != ".component .grid" - ".component" != ".component.modifier .grid" - area names "one" and "two" both have duplicate area name conflicts */ .component .cell-one { grid-area: one; } .component .cell-two { grid-area: two; }

There are really only three ways of resolving this conflict at the moment.

Option 1: Remove the parent selector from child elements

Without any safeguards in place for scoping styles to a particular component, this is by far the most dangerous way to resolve the issue. I don’t recommend it.

.component .grid { display: grid; grid-template-areas: "one two"; grid-template-columns: 1fr 1fr; } .component.modifier .grid { grid-template-areas: "one ... two"; grid-template-columns: 1fr 1fr 1fr; } .cell-one { grid-area: one; } .cell-two { grid-area: two; } Autoprefixer translation .component .grid { display: -ms-grid; display: grid; grid-template-areas: "one two"; -ms-grid-columns: 1fr 1fr; grid-template-columns: 1fr 1fr; } .component.modifier .grid { grid-template-areas: "one ... two"; -ms-grid-columns: 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr; } .cell-one { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: one; } .component.modifier .grid > .cell-one { -ms-grid-row: 1; -ms-grid-column: 1; } .cell-two { -ms-grid-row: 1; -ms-grid-column: 2; grid-area: two; } .component.modifier .grid > .cell-two { -ms-grid-row: 1; -ms-grid-column: 3; } Option 2: Go back to using unique area names

This solution is pretty ugly, but if you have no control over the HTML, then this is probably the best way of handling the issue.

.component .grid { display: grid; grid-template-areas: "one two"; grid-template-columns: 1fr 1fr; } .component .cell-one { grid-area: one; } .component .cell-two { grid-area: two; } .component.modifier .grid { grid-template-areas: "modifier_one ... modifier_two"; grid-template-columns: 1fr 1fr 1fr; } .component.modifier .cell-one { grid-area: modifier_one; } .component.modifier .cell-two { grid-area: modifier_two; } Autoprefixer translation .component .grid { display: -ms-grid; display: grid; grid-template-areas: "one two"; -ms-grid-columns: 1fr 1fr; grid-template-columns: 1fr 1fr; } .component .cell-one { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: one; } .component .cell-two { -ms-grid-row: 1; -ms-grid-column: 2; grid-area: two; } .component.modifier .grid { grid-template-areas: "modifier_one ... modifier_two"; -ms-grid-columns: 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr; } .component.modifier .cell-one { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: modifier_one; } .component.modifier .cell-two { -ms-grid-row: 1; -ms-grid-column: 3; grid-area: modifier_two; } Option 3: Use a BEM-style naming convention

Yep, if you use the BEM naming convention (or something similar), this will practically never be an issue for you. This is easily the preferred way of dealing with the issue if it’s an option.

.component__grid { display: grid; grid-template-areas: "one two"; grid-template-columns: 1fr 1fr; } .component__grid--modifier { grid-template-areas: "one ... two"; grid-template-columns: 1fr 1fr 1fr; } .component__cell-one { grid-area: one; } .component__cell-two { grid-area: two; } Autoprefixer translation .component__grid { display: -ms-grid; display: grid; grid-template-areas: "one two"; -ms-grid-columns: 1fr 1fr; grid-template-columns: 1fr 1fr; } .component__grid--modifier { grid-template-areas: "one ... two"; -ms-grid-columns: 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr; } .component__cell-one { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: one; } .component__grid--modifier > .component__cell-one { -ms-grid-row: 1; -ms-grid-column: 1; } .component__cell-two { -ms-grid-row: 1; -ms-grid-column: 2; grid-area: two; } .component__grid--modifier > .component__cell-two { -ms-grid-row: 1; -ms-grid-column: 3; }

I have some issues with BEM, but with a few tweaks to the syntax, it can be a really great way to control CSS specificity and scoping.

Other Autoprefixer updates

While I’m here, I’ll give you an update on a couple of other developments that have occurred in Autoprefixer since I wrote the original CSS Grid in IE series.

Before we dive in though, here is how to set things up in CodePen in case you want to follow along with your own experiments.

Using Autoprefixer Grid translations in CodePen

CodePen has already upgraded to version 9.3.1 of Autoprefixer. You will now easily be able to integrate IE-friendly CSS Grid styles into your pens. First, make sure that Autoprefixer is enabled in the CodePen settings (Settings > CSS > [Vendor Prefixing] > Autoprefixer).

Step 1: Turn on Autoprefixer in CodePen

Then add the all new /* autoprefixer grid: on */ control comment to the top of the CSS Panel. I will dive into this awesome new feature a bit more later.

Step 2: Add the control comment

You can now write modern IE-friendly CSS Grid code and CodePen will automatically add in all of the IE prefixes for you.

"View compiled CSS" button The compiled CSS view

In order to actually test your pens in IE though, you will need to view the pen in "Debug Mode" (Change View > Debug Mode). The pen will need to be saved before you will have access to this view.

Enabling Debug Mode for testing in IE

If you’d like to try out a tool that shows you real-time Autoprefixer output, try the online tool just for that. Input CSS on the left. It will output Autoprefixer’s translated CSS on the right as you type.

autoprefixer.github.io New Autoprefixer control comments

We don’t always have the ability to alter the Autoprefixer configuration settings. If you have ever tried to do some IE-friendly CSS Grid experiments in CodePen in the past, you will know the pain of not having direct access to the Autoprefixer settings. There are also some frameworks like Create React App and Angular that don’t allow users to alter the Autoprefixer settings. There was also a bit of a skill barrier that prevented some users from using Grid because they were unsure of how to enable Grid translations.

This limitation was causing pain for many users but then Andrey Alexandrov submitted a pull request that introduced a new control comment. This new control comment gave users the ability to easily turn grid translations on and off from inside the CSS file. This was far easier than doing it through configuration settings. It was also accessible to all users no matter how they compile their CSS.

Adding /* autoprefixer grid: on */ will enable grid translations for that entire block while /* autoprefixer grid: off */ will disable grid translations for that block. Only the first grid control comment in a block will be applied.

/* Globally enable grid prefixes */ /* autoprefixer grid: on */ .grid { display: grid; } .non-ie .grid { /* Turn off grid prefixes but only for this block */ /* autoprefixer grid: off */ display: grid; /* Grid control comments affect the whole block. This control comment is ignored */ /* autoprefixer grid: on */ grid-column: 1; }

The above code translates into the following (I’ve filtered out the explanation comments):

/* autoprefixer grid: on */ .grid { display: -ms-grid; display: grid; } .non-ie .grid { /* autoprefixer grid: off */ display: grid; /* autoprefixer grid: on */ grid-column: 1; } @supports can disable grid translations

The new control comments aren’t the only way to selectively prevent Autoprefixer from outputting grid translation code.

Autoprefixer will never be able to support implicit grid auto-placement. If you use an @supports statement that checks for something like grid-auto-rows: 0, Autoprefixer will not output Grid translations for code entered inside that statement.

.prefixed .grid { display: -ms-grid; display: grid; } /* Checking grid-auto-* support prevents prefixes */ @supports (grid-auto-rows: 0) { .modern .grid { display: grid; } } /* Checking basic grid support still outputs prefixes */ @supports (display: grid) { .still-prefixed .grid { display: -ms-grid; display: grid; } }

To support IE, Autoprefixer can generate something like 50 lines of extra CSS sometimes. If you are writing Grid code inside an @supports statement, you will want IE translations to be turned off to reduce the weight of your CSS. If you don’t want to use @supports (grid-auto-rows: 0) then you can use a control comment inside the @supports statement instead.

@supports (display: grid) { .prefixed .grid { display: -ms-grid; display: grid; } } @supports (display: grid) { /* autoprefixer grid: off */ .modern .grid { display: grid; } } Autoprefixer now inherits grid-gaps

In the original article series, I said that Autoprefixer was unable to inherit grid-gap values. In version 9.1.1, grid-gap values were able to inherit through media queries. In version 9.3.1, they have become inheritable through more specific selectors as well.

.grid { display: grid; grid-gap: 20px; /* grid-gap is stated here */ grid-template: "one two" / 1fr 1fr; } @media (max-width: 600px) { .grid { /* grid-gap is now inhereited here */ grid-template: "one" "two" / 1fr; } } .grid.modifier { /* grid-gap is now inhereited here as well */ grid-template: "one" "two" / 1fr; } .one { grid-area: one; } .two { grid-area: two; } Autoprefixer translation .grid { display: -ms-grid; display: grid; grid-gap: 20px; /* grid-gap is stated here */ -ms-grid-rows: auto; -ms-grid-columns: 1fr 20px 1fr; grid-template: "one two" / 1fr 1fr; } @media (max-width: 600px) { .grid { /* grid-gap is now inhereited here */ -ms-grid-rows: auto 20px auto; -ms-grid-columns: 1fr; grid-template: "one" "two" / 1fr; } } .grid.modifier { /* grid-gap is now inherited here as well */ -ms-grid-rows: auto 20px auto; -ms-grid-columns: 1fr; grid-template: "one" "two" / 1fr; } .one { -ms-grid-row: 1; -ms-grid-column: 1; grid-area: one; } .grid.modifier > .one { -ms-grid-row: 1; -ms-grid-column: 1; } .two { -ms-grid-row: 1; -ms-grid-column: 3; grid-area: two; } .grid.modifier > .two { -ms-grid-row: 3; -ms-grid-column: 1; } @media (max-width: 600px) { .one { -ms-grid-row: 1; -ms-grid-column: 1; } .two { -ms-grid-row: 3; -ms-grid-column: 1; } } grid-template: span X; now works

In Part 2 this series, I mentioned that the following syntax doesn’t work:

.grid-cell { grid-column: span 2; }

This was fixed in version 9.1.1. It now translates into the following:

.grid-cell { -ms-grid-column-span: 2; grid-column: span 2; } New warning for mixing manual and area based placement

We had a user complain that Autoprefixer wasn’t handling grid areas correctly. Upon further investigation, we realized that they were applying both a grid-area and a grid-column setting within the same CSS rule.

.grid { display: grid; grid-template-areas: "a b"; } /* This doesn't work very well */ .grid-cell { grid-column: 2; grid-area: a; }

Either use grid-area on its own, or us grid-column & grid-row. Never use both at the same time.

If you are curious, this is what Autoprefixer outputs for the above code:

.grid { display: -ms-grid; display: grid; grid-template-areas: "a b"; } /* This doesn't work very well */ .grid-cell { -ms-grid-row: 1; -ms-grid-column: 1; -ms-grid-column: 2; grid-column: 2; grid-area: a; }

There is one exception to this. You may need a grid cell to overlap other grid cells in your design. You want to use grid-template-areas for placing your grid cells due to how much easier Autoprefixer makes the overall cell placement. You can't really create cell overlaps using grid-template-areas though. To create this overlap, you can use grid-[column/row]-end: span X; after the grid-area declaration to force the overlap.

.grid { display: grid; grid-template-areas: "a . ." "a b b" "a . ."; grid-template-columns: 1fr 1fr 1fr; grid-template-rows: 1fr 1fr 1fr; } .grid-cell-a { grid-area: a; /* place cell span after the grid-area to force an overlap */ grid-column-end: span 2; }

See the Pen column-span + grid-area experiment by Daniel Tonon (@daniel-tonon) on CodePen.

However if you have declared grid gaps in your grid, you will need to write the column/row span prefix manually while taking the extra columns/rows generated by Autoprefixer into consideration (IE doesn't support grid-gap). There is an issue for this on GitHub.

.grid { display: grid; grid-template-areas: "a . ." "a b c" "a . ."; grid-template-columns: 1fr 1fr 1fr; grid-template-rows: 1fr 1fr 1fr; grid-gap: 10px; } .grid-cell-a { grid-area: a; /* IE column span added manually */ -ms-grid-column-span: 3; /* IE spans 2 columns + the gap */ grid-column-end: span 2; /* Modern browsers only span 2 columns */ }

See the Pen column-span + grid-area experiment 2 by Daniel Tonon (@daniel-tonon) on CodePen.

This technique currently produces a warning message in Autoprefixer that can't easily be hidden. An issue for this is on GitHub.

New warning when using [align/justify/place]-[content/items]

Unfortunately. IE does not have the ability to align grid cells from the parent container. The only way to align grid cells in IE is by using the -ms-grid-row-align and the -ms-grid-column-align properties. These translate to align-self and justify-self in modern day grid syntax.

The following properties will trigger a warning in Autoprefixer if used in conjunction with a CSS Grid property:

  • align-items
  • align-content
  • justify-items
  • justify-content
  • place-items
  • place-content

The align-content, justify-content and place-content properties are pretty much impossible to replicate in IE. The align-items, justify-items and place-items properties, on the other hand, are quite easy to replicate using their self equivalents on the child elements (place-self support was added in 9.3.0).

/* [align/justify/place]-items is *NOT* IE friendly */ .align-justify-items { display: grid; align-items: start; justify-items: end; } .place-items { display: grid; place-items: start end; } /*[align/justify/place]-self *IS* IE friendly */ .align-justify-grid { display: grid; } .align-justify-grid > * { align-self: start; justify-self: end; } .place-grid { display: grid; } .place-grid > * { place-self: start end; } Autoprefixer translation /* [align/justify/place]-items is *NOT* IE friendly */ .align-justify-items { display: -ms-grid; display: grid; align-items: start; justify-items: end; } .place-items { display: -ms-grid; display: grid; place-items: start end; } /*[align/justify/place]-self *IS* IE friendly */ .align-justify-grid { display: -ms-grid; display: grid; } .align-justify-grid > * { -ms-grid-row-align: start; align-self: start; -ms-grid-column-align: end; justify-self: end; } .place-grid { display: -ms-grid; display: grid; } .place-grid > * { -ms-grid-row-align: start; -ms-grid-column-align: end; place-self: start end; }

.grid { [align/justify/place]-items } and .grid > * { [align/justify/place]-self } are generally quite interchangeable with one another. This substitution doesn’t always work, but in most cases, the two methods tend to act in much the same way.

Below is a pen demonstrating the difference between the IE friendly alignment method and the IE unfriendly alignment method. In modern browsers they look identical, but in IE one looks the same as the modern browsers and one does not.

See the Pen place-items experiment by Daniel Tonon (@daniel-tonon) on CodePen.

That’s all, folks

I hope you have enjoyed reading about the awesome new improvements the Autoprefixer community has made to the project over the past few months. The new control comments make enabling and disabling grid translations an absolute breeze. The new grid-areas system is also exciting. I love all the new IE-friendly CSS Grid options that the new areas system opens up, and I think you will too. 🙂

Article Series:
  1. Debunking common IE Grid misconceptions
  2. CSS Grid and the new Autoprefixer
  3. Faking an auto-placement grid with gaps
  4. Duplicate area names now supported! (This Post)

The post CSS Grid in IE: Duplicate area names now supported! appeared first on CSS-Tricks.

You might not need a loop

Css Tricks - Mon, 11/26/2018 - 5:02am

Ire Aderinokun has written a nifty piece using loops and when we might consider replacing it with another method, say .map() and .filter(). I particularly like what she has to say here:

As I mentioned earlier, loops are a great tool for a lot of cases, and the existence of these new methods doesn't mean that loops shouldn't be used at all.

I think these methods are great because they provide code that is in a way self-documenting. When we use the filter() method instead of a for loop, it is easier to understand at first glance what the purpose of the logic is.

However, these methods have very specific use cases and may be overkill if their full value isn't being used. An example of this is the map() method, which can technically be used to replace almost any arbitrary loop. If in our first example, we only wanted to modify the original articles array and not create a new, modified, amazingArticles, using this method would be unnecessary. It's important to use the method that suits each scenario, to make sure that we aren't over- or under-performing.

If you’re interested in digging more into this subject, Adan Giese wrote a great post about the .filter() method a short while ago that’s definitely worth checking out. Oh, and speaking of lots of different ways to approach loops, Chris compiled a list of options for looping over querySelectorAll NodeLists where forEach is just one of many options.

Direct Link to ArticlePermalink

The post You might not need a loop appeared first on CSS-Tricks.

The Current State of Styling Scrollbars

Css Tricks - Fri, 11/23/2018 - 5:41am

If you need to style your scrollbars right now, one option is to use a collection of ::webkit prefixed CSS properties.

See the Pen CSS-Tricks Almanac: Scrollbars by Chris Coyier (@chriscoyier) on CodePen.

Sadly, that doesn't help out much for Firefox or Edge, or the ecosystem of browsers around those.

But if that's good enough for what you need, you can get rather classy with it:

See the Pen Custom Scrollbar styling by Devstreak (@devstreak) on CodePen.

There are loads of them on CodePen to browse. It's a nice thing to abstract with a Sass @mixin as well.

There is good news on this front! The standards bodies that be have moved toward a standardizing methods to style scrollbars, starting with the gutter (or width) of them. The main property will be scrollbar-gutter and Geoff has written it up here. Hopefully Autoprefixer will help us as the spec is finalized and browsers start to implement it so we can start writing the standardized version and get any prefixed versions from that.

But what if we need cross-browser support?

If styled scrollbars are a necessity (and I don't blame you), then you'll probably have to reach for a JavaScript solution. There must be dozens of libraries for that. I ran across simplebar and it looks like a pretty modern one with easy instantiation. Here's a demo of that:

See the Pen simple-bar by Chris Coyier (@chriscoyier) on CodePen.

Here's another one called simple-scrollbar:

See the Pen simple-scroll-bar by Chris Coyier (@chriscoyier) on CodePen.

Das Surma did a very fancy tutorial that creates a custom scrollbar that doesn't actually require any JavaScript when scrolling (good for perf), but does require some JavaScript setup code.

Custom scrollbars are extremely rare and that’s mostly due to the fact that scrollbars are one of the remaining bits on the web that are pretty much unstylable (I’m looking at you, date picker). You can use JavaScript to build your own, but that’s expensive, low fidelity and can feel laggy. In this article we will leverage some unconventional CSS matrices to build a custom scroller that doesn’t require any JavaScript while scrolling, just some setup code.

I'll embed a copy here:

See the Pen Custom Scrollbar Example from Google Chrome Labs by Chris Coyier (@chriscoyier) on CodePen.

The post The Current State of Styling Scrollbars appeared first on CSS-Tricks.

State of Houdini (Chrome Dev Summit 2018)

Css Tricks - Thu, 11/22/2018 - 4:19am

Here’s a great talk by Das Surma where he looks into what Houdini is and how much of it is implemented in browsers. If you’re unfamiliar with that, Houdini is a series of technologies and APIs that gives developers low level access to how CSS properties work in a fundamental way. Check out Ana Tudor's deep dive into its impact on animations for some incredible examples of it in practice.

What I particularly like about this video is the way Das mentions the CSS Paint API which lets you do a bunch of bizarre things with CSS, such as creating "squircle" shapes and changing how borders work. It looks wonderfully robust and it should give us super powers in the near future. Ruth John wrote up this extensive overview on the topic earlier this year and it's worth a read as well.

Direct Link to ArticlePermalink

The post State of Houdini (Chrome Dev Summit 2018) appeared first on CSS-Tricks.

Add instant awesomeness to your interfaces with this insanely large icon set

Css Tricks - Thu, 11/22/2018 - 4:18am

(This is a sponsored post.)

When you need to add icons to your interface, the whole process can really suck. “Should I use all these default bootstrap icons? Maybe I’ll just use the same Google Material icons for the hundredth time?”

Some important yet often overlooked things to consider when choosing an icon set includes, the size of the icons, style, consistency, and quantity. It’s frustrating to find icons that only cover half of the use cases you need.

We constantly felt this frustration too and decided to do something about it. This ultimately led to creating Streamline icon set.

Now in version 3.0, Streamline contains a whopping 30,000 icons in three distinct weights, similar to a font family. There are tons of options to pick the perfect icon for any interface you’re working with, whether it’s a big web application, documentation, or a marketing site.

"I own several icon sets but the one that I return to over and over is the copious Streamline pack, which almost always seems to have just the pictogram I need when I dig into its catalog."

—Khoi Vinh, Adobe

Easy to Use

Streamline has also been meticulously organized into easy-to-navigate categories. You can see all of the categories in our handy dandy web-based icon viewer.

If you’re an IconJar user, you can also search for icons by name and drag and drop them into your project folder. We’re currently under development on this functionality for our web viewer too.

Every Streamline Icon pack comes with the following file types: .svg, .iconjar, .sketch, .fig, .ai, .pdf, .png, .xd.

So now matter how you like to work with icons, you have the file types you need.

"Streamline 3.0 is one of the most versatile and detailed icon packs I’ve ever used. The structure and hierarchy make it super easy to work with. This is an amazing product. Bravo, Vincent."

—Stephanie Walter, UX & UI Designer

Optimized SVG

The SVG versions of Streamline is already dev-ready with proper viewbox tags in place and currentColor set as the color properties for all strokes and fills. You can pop in Streamline using your favorite SVG technique and start changing the color of the icons with CSS right out of the gate.

See the Pen QJQjMm by Matt D. Smith (@mds) on CodePen.

Weights

Every weight—light, regular, and bold—was designed with a very consistent style to give you tons of consistency within your interface.

Light

The classic Streamline style with bits of detail here and there. Designed with 1px stroke on a 24px grid. The Light icons are great for interfaces that need lots of fun personality. They also work well scaled up to 48px as small illustrations.

Regular

A new minimal and geometric style. Designed with a 1.5px stroke on a 24px grid. These are perfect to use on clean and modern web interfaces.

Bold

A new solid style akin to the latest iOS guidelines. Designed with fills and a 2px stroke on a 24px grid. The bold style gives a little more punch for an iOS style interface.

Put Streamline to work for you

There are two different package types available—Essential and Ultimate.

Essential contains 14 categories all related to interfaces and web design, whereas the Ultimate pack contains all 53 categories, including things like Pets, Weather, Finance, Outdoors, Transportation, and so much more.

👉 Check out the Streamline site to soak in all of the icon glory.

"Vincent's icons are unique, versatile, and easy to work with. I've found them to be super useful across a range of projects."

—Daniel Burka, Resolve to Save Lives

🤓 Some nerdy facts about the Streamline site:

  • Initials designs created in Figma
  • Coded from scratch with .pug, .sass, and .js
  • CodeKit for compiling all of the code
  • Grunt to create a sprite with all of the SVG assets
  • Animations created in After Effects, exported from AE with Lottie into an icon-animation.json format, and added to the site using bodymovin.js
  • Scrollmagic.js was used to manipulate the DOM based on scroll positions for the large icon parallax sections
  • jQuery.js was used to make our lives easier since we’re building a marketing site and not a full-scale application

Direct Link to ArticlePermalink

The post Add instant awesomeness to your interfaces with this insanely large icon set appeared first on CSS-Tricks.

Swipeable card stack using Vue.js and interact.js

Css Tricks - Wed, 11/21/2018 - 4:53am

I recently had an opportunity to work on a fantastic research and development project at Netguru. The goal of project (codename: "Wordguru") was to create a card game that anyone can play with their friends. You can see the outcome here.

One element of the development process was to create an interactive card stack. The card stack had a set of requirements, including:

  • It should contain a few cards from the collection.
  • The first card should be interactive.
  • The user should be able to swipe the card in different directions that indicate an intent to accept, reject or skip the card.

This article will explain how to create that and make it interactive using Vue.js and interact.js. I created an example for you to refer to as we go through the process of creating a component that is in charge of displaying that card stack and a second component that is responsible for rendering a single card and managing user interactions in it.

View Demo

Step 1: Create the GameCard component in Vue

Let’s start by creating a component that will show a card, but without any interactions just yet. We’ll call this file GameCard.vue and, in the component template, we’ll render a card wrapper and the keyword for a specific card. This is the file we’ll be working in throughout this post.

// GameCard.vue <template> <div class="card" :class="{ isCurrent: isCurrent }" > <h3 class="cardTitle">{{ card.keyword }}</h3> </div> </template>

In the script section of the component, we receive the prop card that contains our card content as well as an isCurrent prop that gives the card a distinct look when needed.

export default { props: { card: { type: Object, required: true }, isCurrent: { type: Boolean, required: true } } }, Step 2: Create the GameCardStack component in Vue

Now that we have a single card, let's create our card stack.

This component will receive an array of cards and render the GameCard for each card. It's also going to mark the first card as the current card in the stack so a special styling is applied to it.

// GameCardsStack.vue <template> <div class="cards"> <GameCard v-for="(card, index) in cards" :key="card" :card="card" :is-current="index === 0" /> </div> </template> <script> import GameCard from "@/components/GameCard"; export default { components: { GameCard }, props: { cards: { type: Array, required: true } } }; </script>

Here’s what we’re looking at so far, using the styles pulled from the demo:

At this point, our card looks complete, but isn't very interactive. Let's fix that in the next step!

Step 3: Add interactivity to GameCard component

All our interactivity logic will live in the GameCard component. Let's start by allowing the user to drag the card. We will use interact.js to deal with dragging.

We’ll set the interactPosition initial values to 0 in the script section. These are the values that indicate a card’s order in the stack when it’s moved from its original position.

<script> import interact from "interact.js"; data() { return { interactPosition: { x: 0, y: 0 }, }; }, // ... </script>

Next, we create a computed property that’s responsible for creating a transform value that’s applied to our card element.

// ... computed: { transformString() { const { x, y } = this.interactPosition; return `translate3D(${x}px, ${y}px, 0)`; } }, // ...

In the mounted lifecycle hook, we make use of the interact.js and its draggable method. That method allows us to fire a custom function each time the element is dragged (onmove). It also exposes an event object that carries information about how far the element is dragged from its original position. Each time user drags the card, we calculate a new position of the card and set it on the interactPosition property. That triggers our transformString computed property and sets new value of transform on our card.

We use the interact onend hook that allows us to listen when the user releases the mouse and finishes the drag. At this point, we will reset the position of our card and bring it back to its original position: { x: 0, y: 0 }.

We also need to make sure to remove the card element from the Interactable object before it gets destroyed. We do that in the beforeDestroy lifecycle hook by using interact(target).unset(). That removes all event listeners and makes interact.js completely forget about the target.

// ... mounted() { const element = this.$refs.interactElement; interact(element).draggable({ onmove: event => { const x = this.interactPosition.x + event.dx; const y = this.interactPosition.y + event.dy; this.interactSetPosition({ x, y }); }, onend: () => { this.resetCardPosition(); } }); }, // ... beforeDestroy() { interact(this.$refs.interactElement).unset(); }, // ... methods: { interactSetPosition(coordinates) { const { x = 0, y = 0 } = coordinates; this.interactPosition = {x, y }; }, resetCardPosition() { this.interactSetPosition({ x: 0, y: 0 }); }, }, // ...

We need to add one thing in our template to make this work. As our transformString computed property returns a string, we need to apply it to the card component. We do that by binding to the :style attribute and then passing the string to the transform property.

<template> <div class="card" :class="{ isCurrent: isCurrent }" :style="{ transform: transformString }" > <h3 class="cardTitle">{{ card.keyword }}</h3> </div> </template>

With that done, we have created interaction with our card — we can drag it around!

You may have noticed that the behavior isn’t very natural, specifically when we drag the card and release it. The card immediately returns to its original position, but it would be more natural if the card would go back to initial position with animation to smooth the transition.

That’s where transition comes into play! But adding it to our card introduces another issue: there’s a lag in the card following as it follows the cursor because transition is applied to the element at all times. We only want it applied when the drag ends. We can do that by binding one more class (isAnimating) to the component.

<template> <div class="card" :class="{ isAnimating: isInteractAnimating, isCurrent: isCurrent }" > <h3 class="cardTitle">{{ card.keyword }}</h3> </div> </template>

We can add and remove the animation class by changing the isInteractAnimating property.

The animation effect should be applied initially and we do that by setting our property in data.

In the mounted hook where we initialize interact.js, we use one more interact hook (onstart) and change the value of isInteractAnimating to false so that the animation is disabled when the during the drag.

We’ll enable the animation again in the onend hook, and that will make our card animate smoothly to its original position when we release it from the drag.

We also need to update transformString computed property and add a guard to recalculate and return a string only when we are dragging the card.

data() { return { // ... isInteractAnimating: true, // ... }; }, computed: { transformString() { if (!this.isInteractAnimating) { const { x, y } = this.interactPosition; return `translate3D(${x}px, ${y}px, 0)`; } return null; } }, mounted() { const element = this.$refs.interactElement; interact(element).draggable({ onstart: () => { this.isInteractAnimating = false; }, // ... onend: () => { this.isInteractAnimating = true; }, }); },

Now things are starting to look nice!

Our card stack is ready for second set of interactions. We can drag the card around, but nothing is actually happening — the card is always coming back to its original place, but there is no way to get to the second card.

This will change when we add logic that allows the user to accept and rejecting cards.

Step 4: Detect when the card is accepted, rejected, or skipped

The card has three types of interactions:

  • Accept card (on swipe right)
  • Reject card (on swipe left)
  • Skip card (on swipe down)

We need to find a place where we can detect if the card was dragged from its initial position. We also want to be sure that this check will happen only when we finish dragging the card so the interactions do not conflict with the animation we just finished.

We used that place earlier smooth the transition during animation — it's the onend hook provided by the interact.draggable method.

Let's jump into the code.

First, we need to store our threshold values. Those values are the distances as the card is dragged from its original position and allows us to determine if the card should be accepted, rejected, or skipped. We use X axis for right (accept) and left (reject), then use the Y axis for downward movement (skip).

We also set coordinates where we want to place a card after it gets accepted, rejected or skipped (coordinates out of user's sight).

Since those values will not change, we will keep them in the static property of our component, which can be accessed with this.$options.static.interactYThreshold.

export default { static: { interactYThreshold: 150, interactXThreshold: 100 },

We need to check if any of our thresholds were met in our onend hook and then fire the appropriate method that happened. If no threshold is met, then we reset the card to its initial position.

mounted() { const element = this.$refs.interactElement; interact(element).draggable({ onstart: () => {...}, onmove: () => {...}, onend: () => { const { x, y } = this.interactPosition; const { interactXThreshold, interactYThreshold } = this.$options.static; this.isInteractAnimating = true; if (x > interactXThreshold) this.playCard(ACCEPT_CARD); else if (x < -interactXThreshold) this.playCard(REJECT_CARD); else if (y > interactYThreshold) this.playCard(SKIP_CARD); else this.resetCardPosition(); } }); }

OK, now we need to create a playCard method that’s responsible for handling those interactive actions.

Step 5: Establish the logic to accept, reject, and skip cards

We will create a method that accepts a parameter telling us the user’s intended action. Depending on that parameter, we will set the final position of the current card and emit the accept, reject, or skip event. Let's go step by step.

First, our playCard method will remove the card element from the Interactable object so that it stops tracking drag events. We do that by using interact(target).unset().
Secondly, we set the final position of the active card depending on the user's intention. That new position allows us to animate the card and remove it from the user's view.

Next, we emit an event up to the parent component so we can deal with our cards (e.g. change the current card, load more cards, shuffle the cards, etc.). We want to follow the DDAU principle that states a component should refrain from mutating data it doesn't own. Since our cards are passed down to our component, it should emit an event up to the place from where those cards come.

Lastly, we hide the card that was just played and add a timeout that allow the card to animate out of view.

methods: { playCard(interaction) { const { interactOutOfSightXCoordinate, interactOutOfSightYCoordinate, } = this.$options.static; this.interactUnsetElement(); switch (interaction) { case ACCEPT_CARD: this.interactSetPosition({ x: interactOutOfSightXCoordinate, }); this.$emit(ACCEPT_CARD); break; case REJECT_CARD: this.interactSetPosition({ x: -interactOutOfSightXCoordinate, }); this.$emit(REJECT_CARD); break; case SKIP_CARD: this.interactSetPosition({ y: interactOutOfSightYCoordinate }); this.$emit(SKIP_CARD); break; } this.hideCard(); }, hideCard() { setTimeout(() => { this.isShowing = false; this.$emit("hideCard", this.card); }, 300); }, interactUnsetElement() { interact(this.$refs.interactElement).unset(); this.interactDragged = true; }, }

And, there we go!

Summary

Let's recap what we just accomplished:

  • First we created a component for a single card.
  • Next we created another component that renders the cards in a stack.
  • Thirdly, we implemented interact.js to allow interactive dragging.
  • Then we detected when the user wants takes an action with the current card.
  • Finally, we established the to handle those actions.

Phew, we covered a lot! Hopefully this gives you both a new trick in your toolbox as well as a hands-on use case for Vue. And, if you’ve ever had to build something similar, please share in the comments because it would be neat to compare notes.

The post Swipeable card stack using Vue.js and interact.js appeared first on CSS-Tricks.

Exclusions will hopefully be like more powerful grid-friendly floats

Css Tricks - Wed, 11/21/2018 - 4:49am

Exclusions (which are currently in a "working draft" spec as I write) are kinda like float in that they allow inline content to wrap around an element. But not exactly a float. Chen Hui Jing has an excellent explanation:

An exclusion element is a block-level element which is not a float, and generates an exclusion box. An exclusion element establishes a new block formatting context.

An element becomes an exclusion when its wrap-flow property is computed to something other than its initial value of auto. When an element becomes an exclusion, inline content will wrap around the exclusion areas, but within their own formatting contexts.

Source: Chen's article

Support is limited to Edge and IE (again, as I write):

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

DesktopChromeOperaFirefoxIEEdgeSafariNoNoNo10*12*NoMobile / TabletiOS SafariOpera MobileOpera MiniAndroidAndroid ChromeAndroid FirefoxNoNoNoNoNoNo

Chen makes a great case for why they are useful, but another round of discussion has cropped up lately as well. Rob Weychert documents a simple layout situation in which an image is floated left and text is wrapping around it:

Source: Rob's article

As those light red bars indicate, Rob has set up some display: grid; columns to align elements in the article to those axes. A classic "editorial layout" indeed. But there really is no good mechanism to place that image onto the grid and maintain the wrapping. By placing both the content and the image into separate grid items, you don't get the wrapping. You can use float, but that's not using the grid.

Rachel Andrew chimed in that the answer is CSS exclusions. While Rob's example ultimately had to use floats, Rachel re-did it with exclusions. Exclusions make the code much simpler.

/* with floats, replicating exactly what the grid is doing */ img { float: left; width: calc( 3 * ((100% - 50px - 15em) / 6) + 50px + 2em ); } /* with exclusions, using the grid */ img { grid-row: 2; grid-column: 1 / 3; -ms-wrap-flow: both; }

Perhaps we can chime in with thumbs up on Rachel's call to see what's up with the status of the spec and with other author use cases.

The post Exclusions will hopefully be like more powerful grid-friendly floats appeared first on CSS-Tricks.

Prettier & Beautify

Css Tricks - Tue, 11/20/2018 - 1:02pm

Aww, what a cute blog post title, right?

Prettier is an "opinionated code formatter." I highly suggest using it. They have a document going deeper into the reasons, but their three marketing bullet points say it best:

  • You press save and code is formatted
  • No need to discuss style in code review
  • Saves you time and energy

But Prettier doesn't do all languages. Notably HTML. It's great at JSX, and I've gotten really used to enjoying that. But then when I switch to a Rails .erb template or a WordPress site where I'm editing a .php file, or even a plain ol' .html file... no Prettier.

They have a development branch for it, but I haven't tried that yet. For now, I was compelled to try an HTML prettification tool that's been out in the wild and through the ringer. The VS Code plugin Beautify does it and has 6.5 million installs, so that seemed like a good bet.

By using this plugin, that means you can configure it, rather than use VS Code's internal version of it which is apparently non-configurable.

The problem is...

The first time I tried this, I found that it made my Prettier stop working. That was unacceptable to me as I want Prettier to be the top priority formatter. I'm not sure if it was running both of them but placing Prettier last, if it was running Beautify on its own instead, or exactly what, but I couldn't figure it out at the time.

The trick for me was to tell it to only care about certain file types!

In my settings.json:

{ ... "beautify.language": { "html": ["html", "php", "erb"], "css": [], "js": [] } }

Now I can have Prettier doing all the languages it does, and fall back to Beautify for the HTML stuff that Prettier doesn't do yet. So far so good.

The post Prettier & Beautify appeared first on CSS-Tricks.

Push and ye shall receive

Css Tricks - Tue, 11/20/2018 - 4:42am

Sometimes the seesaw of web tech is fascinating. Service workers have arrived, and beyond offline networking (read Jeremy's book) which is possibly their best feature, they can enable push notifications via the Push API.

I totally get the push (pun intended) to make that happen. There is an omnipresent sentiment that we want the web to win, as there should be in this industry. Losing on the web means losing to native apps on all the different platforms out there. Native apps aren't evil or anything — they are merely competitive and exclusionary in a way the web isn't. Making the web a viable platform for any type of "app" is a win for us and a win for humans.

One of the things native apps do well is push notifications which gives them a competitive advantage. Some developers choose native for stuff like that. But now that we actually have them on the web, there is pushback from the community and even from the browsers themselves. Firefox supports them, then rolled out a user setting to entirely block them.

We're seeing articles like Moses Kim's Don't @ me:

Push notifications are a classic example of good UX intentions gone bad because we know no bounds.

Very few people are singing the praises of push notifications. And yet! Jeremy Keith wrote up a great experiment by Sebastiaan Andeweg. Rather than an obnoxious and intrusive push notification...

Here’s what Sebastiaan wanted to investigate: what if that last step weren’t so intrusive? Here’s the alternate flow he wanted to test:

  1. A website prompts the user for permission to send push notifications.
  2. The user grants permission.
  3. A whole lot of complicated stuff happens behinds the scenes.
  4. Next time the website publishes something relevant, it fires a push message containing the details of the new URL.
  5. The user’s service worker receives the push message (even if the site isn’t open).
  6. The service worker fetches the contents of the URL provided in the push message and caches the page. Silently.

It worked.

Imagine a PWA podcast app that works offline and silently receives and caches new podcasts. Sweet. Now we need a permissions model that allows for silent notifications.

The post Push and ye shall receive appeared first on CSS-Tricks.

Magic Printed

Typography - Tue, 11/20/2018 - 2:01am

Magic Printed Today we tend to associate magic either with the sleight of hand tricks performed by magicians and illusionists or with the fictional universes of Game of Thrones and Harry Potter. Belief in magic predates any form of religion and in ancient times the belief that magic could be used to do harm led […]

The post Magic Printed appeared first on I Love Typography.

Why can’t we use Functional CSS and regular CSS at the same time?

Css Tricks - Mon, 11/19/2018 - 4:48am

Harry Nicholls recently wrote all about simplifying styles with functional CSS and you should definitely check it out. In short, functional CSS is another name for atomic CSS or using “helper” or “utility” classes that would just handle padding or margin, background-color or color, for example.

Harry completely adores the use of adding multiple classes like this to an element:

So what I'm trying to advocate here is taking advantage of the work that others have done in building functional CSS libraries. They're built on solid foundations in design, people have spent many hours thinking about how these libraries should be built, and what the most useful classes will be.

And it's not just the classes that are useful, but the fundamental design principles behind Tachyons.

This makes a ton of sense to me. However, Chris notes that he hasn’t heard much about the downsides of a functional/atomic CSS approach:

What happens with big redesigns? Is it about the same, time- and difficulty-wise, or do you spend more time tearing down all those classes? What happens when you need a style that isn't available? Write your own? Or does that ruin the spirit of all this and put you in dangerous territory? How intense can all the class names get? I can think of areas I've styled that have three or more media queries that dramatically re-style an element. Putting all that information in HTML seems like it could get awfully messy. Is consistency harder or easier?

This also makes a ton of sense to me, but here’s the thing: I’m a big fan of both methods and even combine them in the same projects.

Before you get mad, hear me out

At Gusto, the company I work for today, I’ve been trying to design a system that uses both methods because I honestly believe that they can live in harmony with one another. Each solve very different use cases for writing CSS.

Here’s an example: let’s imagine we’re working in a big ol’ React web app and our designer has handed off a page design where a paragraph and a button need more spacing beneath them. Our code looks like this:

<p>Item 1 description goes here</p> <Button>Checkout item</Button>

This is just the sort of problem for functional CSS to tackle. At Gusto, we would do something like this:

<div class="margin-bottom-20px"> <p>Item 1 description goes here</p> <button>Checkout item</button> </div>

In other words, we use functional classes to make layout adjustments that might be specific to a particular feature that we’re working on. However! That Button component is made up of a regular ol’ CSS file. In btn.scss, we have code like this which is then imported into our btn.jsx component:

.btn { padding: 10px 15px; margin: 0 15px 10px; // rest of the styles go here }

I think making brand new CSS files for custom components is way easier than trying to make these components out of a ton of classes like margin-*, padding-*, etc. Although, we could be using functional styles in our btn.jsx component instead like this:

const Button = ({ onClick, className, children }) => { return ( <button className='padding-top-10px padding-bottom-10px padding-left-15px padding-right-15px margin-bottom-none margin-right-15px margin-left-15px margin-bottom-10px ${className}')} onClick={onClick} > {children} </button> ); };

This isn’t a realistic example because we're only dealing with two properties and we’d probably want to be styling this button’s background color, text color, hover states, etc. And, yes, I know these class names are a little convoluted but I think my point still stands even if you combine vertical and horizontal classes together.

So I reckon that we solve the following three issues with functional CSS by writing our custom styles in a separate CSS file for this particular instance:

  1. Readability
  2. Managing property dependencies
  3. Avoiding the painful fact that visual design doesn’t like math

As you can see in the earlier code example, it’s pretty difficult to read and immediately see which classes have been applied to the button. More classes means more difficulty to scan.

Secondly, a lot of CSS property/value pairs are written in relation to one another. Say, for example, position: relative and position: absolute. In our stylesheets, I want to be able to see these dependencies and I believe it’s harder to do that with functional CSS. CSS often depends on other bits of CSS and it’s important to see those connections with comments or groupings of properties/values.

And, finally, visual design is an issue. A lot of visual design requires imperfect numbers that don’t properly scale. With a functional CSS system, you’ll probably want a system of base 10, or base 8, where each value is based on that scale. But when you’re aligning items together visually, you may need to do so in a way that it won’t align to those values. This is called optical adjustment and it’s because our brains are, well, super weird. What makes sense mathematically often doesn’t visually. So, in this case, we'd need to add more bottom padding to the button to make the text feel like it’s positioned in the center. With a functional CSS approach it’s harder to do stuff like that neatly, at least in my experience.

In those cases where you need to balance readability, dependencies, and optical adjustments, writing regular CSS in a regular old-fashioned stylesheet is still my favorite thing in the world. But functional CSS still solves a ton of other problems very eloquently.

For example, what we’re trying to prevent with functional classes at Gusto is creating tons of stylesheets that do a ton of very specific or custom stuff. Going back to that earlier example with the margin beneath those two elements for a second:

<div className='margin-bottom-20px'> <p>Item 1 description goes here</p> <Button>Checkout item</Button> </div>

In the past our teams might have written something like this instead:

<div className='cool-feature-description-wrapper'> <p>Item 1 description goes here</p> <button>Checkout item</button> </div>

A new CSS file called cool_feature_description_wrapper.scss would need to be created in our application like so:

.cool-feature-description-wrapper { margin-bottom: 20px; }

I would argue that styles like this make our code harder to understand, harder to read, and encourages diversions from our library of components. By replacing this with a class from our library of functional classes, it’s suddenly much easier to read, and to change in the future. It also solves a custom solution for our particular needs without forking our library of styles.

So, I haven’t read much about balancing both approaches this way, although I assume someone has covered this in depth already. I truly believe that a combination of these two methods is much more useful than trying to solve all problems with a single bag of tricks.

I know, right? Nuanced opinions are the worst.

The post Why can’t we use Functional CSS and regular CSS at the same time? appeared first on CSS-Tricks.

GraphQL is Everywhere!

Css Tricks - Fri, 11/16/2018 - 9:22am

I find GraphQL extremely fun and empowering tech to work with, even as a novice just getting started. You've probably heard the elevator pitch before: it allows you to ask for exactly the data you need whenever you need it (probably at the component level), and it arrives as lovely JSON data for your usage.

I see it used as part of modern website builds all the dang time. The overall vibe is, "I want to do whatever I want on the front end, and that actually allows for more back-end choices as well." And by "whatever" on the front end, that generally means a fancy SPA-ish JavaScript-powered thing or a static-site generator-ish thing.

Here's a quick smattering of articles that are everywhere these days. Instead of the actual article titles, I'll rename with the stack parts.

GraphQL is certainly in the new-and-hip category, but as ever, everything old is new again. Check out Query by Example, a language from the 1970's:

.....Name: Bob ..Address: .....City: ....State: TX ..Zipcode:

Resulting SQL:

SELECT * FROM Contacts WHERE Name='Bob' AND State='TX';

The post GraphQL is Everywhere! appeared first on CSS-Tricks.

An Overview of Render Props in React

Css Tricks - Fri, 11/16/2018 - 5:05am

An Overview of Render Props in React

Using render props in React is a technique for efficiently re-using code. According to the React documentation, "a component with a render prop takes a function that returns a React element and calls it instead of implementing its own render logic." To understand what that means, let’s take a look at the render props pattern and then apply it to a couple of light examples.

The render props pattern

In working with render props, you pass a render function to a component that, in turn, returns a React element. This render function is defined by another component, and the receiving component shares what is passed through the render function.

This is what this looks like:

class BaseComponent extends Component { render() { return <Fragment>{this.props.render()}</Fragment>; } }

Imagine, if you will, that our App is a gift box where App itself is the bow on top. If the box is the component we are creating and we open it, we’ll expose the props, states, functions and methods needed to make the component work once it’s called by render().

The render function of a component normally has all the JSX and such that form the DOM for that component. Instead, this component has a render function, this.props.render(), that will display a component that gets passed in via props.

Example: Creating a counter

See the Pen React Render Props by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

Let’s make a simple counter example that increases and decreases a value depending on the button that is clicked.

First, we start by creating a component that will be used to wrap the initial state, methods and rendering. Creatively, we’ll call this Wrapper:

class Wrapper extends Component { state = { count: 0 }; // Increase count increment = () => { const { count } = this.state; return this.setState({ count: count + 1 }); }; // Decrease count decrement = () => { const { count } = this.state; return this.setState({ count: count - 1 }); }; render() { const { count } = this.state; return ( <div> {this.props.render({ increment: this.increment, decrement: this.decrement, count: count })} </div> ); } }

In the Wrapper component, we specify the methods and state what gets exposed to the wrapped component. For this example, we need the increment and decrement methods. We have our default count set as 0. The logic is to either increment or decrement count depending on the method that is triggered, starting with a zero value.

If you take a look at the return() method, you’ll see that we are making use of this.props.render(). It is through this function that we pass methods and state from the Wrapper component so that the component that is being wrapped by it will make use of it.

To use it for our App component, the component will look like this:

class App extends React.Component { render() { return ( <Wrapper render={({ increment, decrement, count }) => ( <div> <div> <h3>Render Props Counter</h3> </div> <div> <p>{count}</p> <button onClick={() => increment()}>Increment</button> <button onClick={() => decrement()}>Decrement</button> </div> </div> )} /> ); } } Example: Creating a data list

The gain lies in the reusable power of render props, let’s create a component that can be used to handle a list of data which is obtainable from an API.

See the Pen React Render Props 2 by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

What do we want from the wrapper component this time? We want to pass the source link for the data we want to render to it, then make a GET request to obtain the data. When the data is obtained we then set it as the new state of the component and render it for display.

class Wrapper extends React.Component { state = { isLoading: true, error: null, list: [] }; fetchData() { axios.get(this.props.link) .then((response) => { this.setState({ list: response.data, isLoading: false }); }) .catch(error => this.setState({ error, isLoading: false })); } componentDidMount() { this.setState({ isLoading: true }, this.fetchData); } render() { return this.props.render(this.state); } }

The data link will be passed as props to the Wrapper component. When we get the response from the server, we update list using what is returned from the server. The request is made to the server after the component mounts.

Here is how the Wrapper gets used:

class App extends React.Component { render() { return ( <Wrapper link="https://jsonplaceholder.typicode.com/users" render={({ list, isLoading, error }) => ( <div> <h2>Random Users</h2> {error ? <p>{error.message}</p> : null} {isLoading ? ( <h2>Loading...</h2> ) : ( <ul>{list.map(user => <li key={user.id}>{user.name}</li>)}</ul> )} </div> )} /> ); } }

You can see that we pass the link as a prop, then we use ES6 de-structuring to get the state of the Wrapper component which is then rendered. The first time the component loads, we display loading text, which is replaced by the list of items once we get a response and data from the server.

The App component here is a class component since it does not manage state. We can transform it into a functional stateless component.

const App = () => { return ( <Wrapper link="https://jsonplaceholder.typicode.com/users" render={({ list, isLoading, error }) => ( <div> <h2>Random Users</h2> {error ? <p>{error.message}</p> : null} {isLoading ? ( <h2>Loading...</h2> ) : ( <ul>{list.map(user => <li key={user.id}>{user.name}</li>)}</ul> )} </div> )} /> ); } That’s a wrap!

People often compare render props with higher-order components. If you want to go down that path, I suggest you check out this post as well as this insightful talk on the topic by Michael Jackson.

The post An Overview of Render Props in React appeared first on CSS-Tricks.

CSS Animations and Transitions in Email

Css Tricks - Thu, 11/15/2018 - 12:55pm

We don't generally think of CSS animations or transitions inside of email, or really any movement at all outside of an awkward occasional GIF. But there is really no reason you can't use them inside HTML emails, particularly if you do it in a progressive enhancement-friendly way. Like, you could style a link with a hover state and a shaking animation, but if the animation (or even the hover) doesn't work, it's still a functional link. Heck, you can use CSS grid in email, believe it or not.

Jason Rodriguez just wrote Understanding CSS Animations in Email: Transitions and Keyframe Animations that covers some of the possibilities. On the supported list of email clients that support CSS transitions and keyframe animations is Apple Mail, Outlook, and AOL mail, among others.

Other things to look at:

The post CSS Animations and Transitions in Email appeared first on CSS-Tricks.

Scaling CSS: Two Sides of a Spectrum

Css Tricks - Thu, 11/15/2018 - 5:16am

The subject of scaling CSS came up a lot in a recent ShopTalk Show with Ben Frain. Ben has put a lot of thought into the subject, even writing a complete book on it, Enduring CSS, which is centered around a whole ECSS methodology.

He talked about how there are essentially two solutions for styling at scale:

  1. Total isolation
  2. Total abstraction

Total isolation is some version of writing styles scoped to some boundary that you've set up (like a component) in which those styles don't leak in or out.

Total abstraction is some version of writing styles that are global, yet so generic and re-usable, that they have no unintended side effects.

Total isolation might come from <style scoped> in a .vue file, CSS modules in which CSS class selectors and HTML class attributes are dynamically generated gibberish, or a CSS-in-JS project, like glamerous. Even strictly-followed naming conventions like BEM can be a form of total isolation.

Total abstraction might come from a project, like Tachyons, that gives you a fixed set of class names to use for styling (Tailwind is like a configurable version of that), or a programmatic tool (like Atomizer) that turns specially named HTML class attributes into a stylesheet with exactly what it needs.

It's the middle ground that has problems. It's using a naming methodology, but not holding strictly to it. It's using some styles in components, but also having a global stylesheet that does random other things. Or, it's having lots of developers contributing to a styling system that has no strict rules and mixes global and scoped styles. Any stylesheet that grows and grows and grows. Fighting it by removing some unused styles isn't a real solution (and here's why).

Note that the web is a big place and not all projects need a scaling solution. A huge codebase with hundreds of developers that needs to be maintained for decades absolutely does. My personal site does not. I've had my fair share of styling problems, but I've never been so crippled by them that I've needed to implement something as strict as Atomic CSS (et al.) to get work done. Nor at at any job I've had so far. I see the benefits though.

Imagine the scale of Twitter.com over a decade! Nicolas has a great thread where he compares Twitter's PWA against Twitter's legacy desktop website.

The legacy site's CSS is what happens when hundreds of people directly write CSS over many years. Specificity wars, redundancy, a house of cards that can't be fixed. The result is extremely inefficient and error-prone styling that punishes users and developers alike.

The post Scaling CSS: Two Sides of a Spectrum appeared first on CSS-Tricks.

Why monday.com is the Universal Team Management Tool for Your Team

Css Tricks - Thu, 11/15/2018 - 4:52am

(This is a sponsored post.)

This platform is perfect for teams sized at 2-to-200 — and gives every employee the same level of transparency.

Every project management tool seeks to do the same instrumental thing: keep teams connected, on task and on deadline to get major initiatives done. But the market is getting pretty crowded, and for good reason — no platform seems to have gotten the right feel for what people need to see, and how that information should be displayed so that it’s both actionable/relevant and contextualized.

That’s why monday.com is worth a shot. The platform is based off a simple, but powerful idea: that as humans, we like to feel like we’re contributing to part of a greater/effort good — an idea that sometimes gets lost in the shuffle as we focus on the details of getting stuff done. So projects are put onto a task board (think of it like a digital whiteboard), where everyone can have the same level of visibility into anyone else who’s contributing a set of tasks. That transparency breaks down the silos between teams that cause communication errors and costly project mistakes — and it’s a beautiful, simple way to connect people to the processes that drive forward big business initiatives.

Whether you’re part of a tech-forward team or not, monday.com is a welcome relief to cumbersome Excel files, messy (physical) whiteboards, or meetings that waste time when actual work could be completed. The scalable, intuitive structure can effectively work for a team of two, or an international team of 2,000+ — and a beautiful, color-coded board lays out tasks you can cleanly see and tag for various stages of completion. That way, employees can see exactly what needs to be done (and who needs to do it), while managers can optimize their time re-allocating resources as necessary to optimize processes. It’s a win-win.

monday.com also allows teams to communicate within the platform, cutting down on the amount of laborious sifting through various email threads to figure out a workflow. Messages can be sent inside of tasks — so all the communication is contextualized before meeting resolution or seeking it. The platform also supports uploads, so documents and videos can be added to facilitate more collaboration, and integration with other productivity apps. So if your team is already using tools like Slack, Google Calendar, Dropbox, Microsoft Excel, Trello, and Jira, there’s specific, clean shortcuts to integrate the information from those platforms into monday.com. And even beyond team communication and management, you can use monday.com for client-facing exchanges, so all your messages are consolidated into a single place.

The platform recently raised $50M in funding, and received nods from the likes of Forbes, Entrepreneur, Business Insider, and more for its ability to empower international teams to do better work together. Best of all, unlike other team management software, which can be pricey and time-intensive to scope, test and run, you can try monday.com today — for free.

What can this app do?
  • Creating and managing a project’s milestones
  • Creating and assigning tasks
  • Attaching files to any project’s table projects on the go.
  • Using mobile applications to manage projects
  • Communicating with your team members
  • Updating team using the news feed
  • Keeping clients in the loop
  • Organizing the organization into teams
  • Creating detailed project charts and reports
  • Tracking the time your team members spend on tasks
  • Managing a project's financials
  • Website as well as a desktop app for Mac and Windows

monday.com to make every user feel empowered and part of something bigger than their own individual tasks, and as a result, to boost collective productivity and transparency.

Direct Link to ArticlePermalink

The post Why monday.com is the Universal Team Management Tool for Your Team appeared first on CSS-Tricks.

Keyboard-Only Focus Styles

Css Tricks - Wed, 11/14/2018 - 5:01am

Like Eric Bailey says, if it's interactive, it needs a focus style. Perhaps your best bet? Don't remove the dang outlines that focusable elements have by default. If you're going to rock a button { outline: 0; }, for example, then you'd better do a button:focus { /* something else very obvious visually */ }. I handled a ticket just today where a missing focus style was harming a user who relies on visual focus styles to navigate the web.

But those focus styles are most useful when tabbing or otherwise navigating with a keyboard, and less so when they are triggered by a mouse click. Now we've got :focus-visible! Nelo writes:

TLDR; :focus-visible is the keyboard-only version of :focus.

Also, the W3C proposal mentions that :focus-visible should be preferred over :focus except on elements that expect a keyboard input (e.g. text field, contenteditable).

(Also see his article for a good demo on why mouse clicking and focus styles can be at odds, beyond a general dislike of fuzzy blue outlines.)

Browser support for :focus-visible is pretty rough:

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

DesktopChromeOperaFirefoxIEEdgeSafariNoNo4*NoNoNoMobile / TabletiOS SafariOpera MobileOpera MiniAndroidAndroid ChromeAndroid FirefoxNoNoNoNoNo63*

But it does have Firefox support, and as Lea Verou says:

... once Chrome ships its implementation it will explode in a matter of 1-2 months.

That's generally how things go these days. Once two major browsers have support — and one of them is Chrome — that's a huge enough slice of the web that can start using it. Especially when it can be done as safely as this property.

Safely, as in, there is an official polyfill, meaning you can nuke default focus styles and just use :focus-visible styles:

/* Remove outline for non-keyboard :focus */ *:focus:not(.focus-visible) { outline: none; } /* Optional: Customize .focus-visible */ .focus-visible { outline: lightgreen solid 2px; }

But, as Patrick H. Lauke documented, you can do it even without the polyfill, using careful selector usage and un-doing styles as needed:

button:focus { /* Some exciting button focus styles */ } button:focus:not(:focus-visible) { /* Undo all the above focused button styles if the button has focus but the browser wouldn't normally show default focus styles */ } button:focus-visible { /* Some even *more* exciting button focus styles */ }

Seems like a nice improvement for the web.

The post Keyboard-Only Focus Styles appeared first on CSS-Tricks.

Dark modes with CSS

Css Tricks - Wed, 11/14/2018 - 5:00am

With the introduction of dark mode in macOS, Safari Technology Preview 68 has released a new feature called prefers-color-scheme which lets us detect whether the user has dark mode enabled with a media query.

That’s right. If this becomes a little bit more supported in other browsers, then we might potentially soon have a way to toggle on night modes with a few lines of CSS!

Recently Mark Otto described how we can start using prefers-color-scheme today in order to create themes that dynamically adjust to the new user setting. And the neat thing about this post is that Mark sort of frames it as an accessibility issue and shows how he uses it on his own website to adjust images so that they’re not too bright for the user:

@media (prefers-color-scheme: dark) { img { opacity: .75; transition: opacity .5s ease-in-out; } img:hover { opacity: 1; } }

In the code above, Mark detects whether the user has dark mode enabled with the media query and then makes the images darker so that they match a dark background. This reminds me of an excellent post by Marcin Wichary where he explores a similar technique and goes one step further by adding all sorts of filters to make sure they have a much higher contrast.

Andy Clarke also wrote up some thoughts about how to take this fancy new CSS feature and how we might apply a dark theme across our website. He describes how to pick colors so our light/dark themes are consistent in terms of branding and how we might want to use a lighter font-weight for darker backgrounds. He writes:

Designing for dark mode shouldn’t stop with choosing darker colours. You should also consider altering typography styles to maintain readability for people who use dark mode. Light text against dark backgrounds appears higher in contrast than when the same colours are used in reverse, so to make your dark mode designs easier to read you’ll need to add more white/dark space to your text.

If your fonts offer a lighter weight, using that for your dark mode design will open up the letterforms and make them appear further apart...

What was that? It sure sounded like the joyous applause of typography nerds and designers everywhere!

The post Dark modes with CSS appeared first on CSS-Tricks.

Syndicate content
©2003 - Present Akamai Design & Development.