Feature

Using Feature Queries in CSS

There’s a tool in CSS that you might not have heard of yet. It’s powerful. It’s been there for a while. And it’ll likely become one of your favorite new things about CSS.

Behold, the @supports rule. Also known as Feature Queries.

With @supports, you can write a small test in your CSS to see whether or not a particular “feature” (CSS property or value) is supported, and apply a block of code (or not) based on the answer. Like this:

@supports (display: grid) {
   // code that will only run if CSS Grid is supported by the browser 
 }

If the browser understands display: grid, then all of the styling inside the brackets will be applied. Otherwise all of that styling will be skipped.

Now, there seems to be a bit of confusion about what Feature Queries are for. This is not some kind of external verification that analyzes whether or not a browser has properly implemented a CSS property. If you are looking for that, look elsewhere. Feature Queries ask the browser to self-report on whether or not a certain CSS property/value is supported, and use the answer to decide whether or not to apply a block of CSS. If a browser has implemented a feature improperly or incompletely, @supports won’t help you. If the browser is misreporting what CSS it supports, @supports won’t help you. It’s not a magic wand for making browser bugs disappear.

That said, I’ve found @supports to be incredibly helpful. The @supports rule has repeatedly let me use new CSS far earlier than I could without it.

For years, developers have used Modernizr to do what Feature Queries do — but Modernizr requires JavaScript. While the scripts might be tiny, CSS architected with Modernizr requires the JavaScript file to download, to execute, and to complete before the CSS is applied. Involving JavaScript will always be slower than only using CSS. Requiring JavaScript opens up the possibility of failure — what happens if the JavaScript doesn’t execute? Plus, Modernizr requires an additional layer of complexity that many projects just can’t handle. Feature Queries are faster, more robust, and much simpler to use.

You might notice the syntax of a Feature Query is a lot like a Media Query. I think of them as cousins.

@supports (display: grid) {
  main {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  }
}

Now most of the time, you do not need such a test in your CSS. For example, you can write this code without testing for support:

aside {
  border: 1px solid black;
  border-radius: 1em;
}

If a browser understands border-radius, then it will put rounded corners on the aside box. If it doesn’t, it will skip that line of code and move on, leaving the edges of the box to be square. There is no reason to run a test or use a Feature Query. This is just how CSS works. It’s a fundamental principle in architecting solid, progressively-enhanced CSS. Browsers simply skip over code they don’t understand, without throwing an error.

a screenshot of border radius effect in  old vs new browsers

Most browsers will display border-radius: 1em as the result on the right. Internet Explorer 6, 7 and 8, however, will not round the corners, and you’ll see the result on the left. Check out this example at codepen.io/jensimmons/pen/EydmkK

You do not need a Feature Query for this.

So when do you want to use @supports? A Feature Query is a tool for bundling together CSS declarations so that they’ll run as a group under certain conditions. Use a Feature Query when you want to apply a mix of old and new CSS, but only when the new CSS is supported.

Let’s look at an example using the Initial Letter property. This new property initial-letter tells the browser to make the element in question bigger — like for a drop cap. Here, the first letter of the first word in a paragraph is being told to be the size of four lines of text. Fabulous. Oh, but I would also like to make that letter bold, and put a bit of margin on its right side, and hey, let’s make it a nice orange color. Cool.

  p::first-letter {
     -webkit-initial-letter: 4;
     initial-letter: 4;
     color: #FE742F;
     font-weight: bold;
     margin-right: 0.5em;
  }
a screenshot of this example Initial Letter in Safari 9

Here’s what our initial-letter example looks like in Safari 9.

Now let’s see what will happen in all the other browsers…

a screenshot of this Initial Letter example in other browsers

Oh, no. This looks terrible in all the other browsers.

Well, that’s not acceptable. We don’t want to change the color of the letter, or add a margin, or make it bold unless it’s also going to be made bigger by the Initial Letter property. We need a way to test and see whether or not the browser understands initial-letter, and only apply the change to color, weight, and margin if it does. Enter the Feature Query.

@supports (initial-letter: 4) or (-webkit-initial-letter: 4) {
  p::first-letter {
     -webkit-initial-letter: 4;
     initial-letter: 4;
     color: #FE742F;
     font-weight: bold;
     margin-right: 0.5em;
  }
}

Notice, you do need to test a full string with both the property and value. This confused me at first. Why am I testing initial-letter: 4 ? Is the value of 4 important? What if I put 17? Does it need to match the value that is further down in my code?

The @supports rule tests a string that contains both the property and value because sometimes it’s the property that needs the test, and sometimes it’s the value. For the initial-letter example, it doesn’t really matter what you put for the value. But consider @supports (display: grid) and you’ll see the need for both. Every browser understands display. Only experimental browsers understand display: grid (at the moment).

Back to our example: Currently initial-letter is only supported in Safari 9, and it requires a prefix. So I’ve written the prefix, making sure to also include the unprefixed version, and I’ve written the test to look for one or the other. Yes, you can have or, and, and not statements in your Feature Queries.

Here’s the new result. The browsers that understand initial-letter show a giant bolded, orange drop-cap. The other browsers act like the drop cap doesn’t exist — the same way they would if I’d waited to use this feature until more browsers had support for it. (We are currently implementing Initial Letter in Firefox, by the way.)

a before and after comparison

The screenshot on the left is from Safari 9. All other browsers show the result on the right. You can see this code in action at codepen.io/jensimmons/pen/ONvdYL

Organizing Your Code

Now you might be tempted to use this tool to cleanly fork your code into two branches. “Hey browser, if you understand Viewport Units, do this, and if you do not understand them, do this other thing.” That feels nice and tidy.

@supports (height: 100vh) {
  // my layout that uses viewport height
}
@supports not (height: 100vh) {
  // the alternative layout for older browsers
}
// WE MIGHT WISH. BUT THIS IS BAD CODE.

This is not a good idea — at least not yet. Do you see the problem?

Well, not all browsers support Feature Queries. And the browsers that do not understand @supports will skip over both blocks of code. That’s probably bad.

Does this mean we can’t use Feature Queries until 100% of browsers support them? Nope. We can, and we should use Feature Queries today. Simply do not write your code like the last example.

How do we do this right? Well, in much the same way we used Media Queries before they were 100% supported. And well, actually it’s easier to use Feature Queries in this transitional period than it was to use Media Queries. You just have to be smart about it.

You want to structure your code knowing that the oldest browsers won’t support Feature Queries or the feature you are testing for. I’ll show you how.

(Of course, sometime in the far future, once 100% of the browsers have Feature Queries, we can make heavier use of @supports not and organize our code in that way. But it’ll be many years until we get there.)

Support for Feature Queries

So how far back are Feature Queries supported?

Well @supports has worked in Firefox, Chrome, and Opera since mid–2013. It also works in every version of Edge. Safari shipped it in Fall 2015, in Safari 9. Feature Queries are not supported in any version of Internet Explorer, Opera Mini, Blackberry Browser, or UC Browser.

a screenshot from Can I Use showing support for Feature Queries

Looking up support for Feature Queries on Can I Use

You might think the fact Internet Explorer doesn’t have support for Feature Queries is a big problem. Actually, it’s usually not. I’ll show you why in a moment. I believe the biggest hurdle is Safari 8. We need to keep an eye out for what happens there.

Let’s look at another example. Say we have some layout code we want to apply that requires using object-fit: cover in order to work properly. For the browsers that don’t understand object-fit, we want to apply different layout CSS.

a screenshot from Can I Use showing support for Object-fit

Looking up support for Object Fit on Can I Use

So let’s write:

div {
  width: 300px;
  background: yellow;
  // some complex code for a fallback layout
}
@supports (object-fit: cover) {
  img {
    object-fit: cover;
  }
  div {
    width: auto;
    background: green;
   // some other complex code for the fancy new layout
  }
}

So what happens? Feature Queries is either supported or not, and the new feature object-fit: cover is either supported or not. Combine those, and we get four possibilities:

Feature Query Support? Feature support? What Happens? Is This What We Want?
Supports Feature Queries Supports the feature in question
Supports Feature Queries Does not support the feature
Does not support Feature Queries Does not support the feature
Does not support Feature Queries Supports the feature in question

Situation 1: Browsers that support Feature Queries, and support the feature in question

Firefox, Chrome, Opera, and Safari 9 all support object-fit and support @supports, so this test will run just fine, and the code inside this block will be applied. Our image will be cropped using object-fit: cover, and our div background will be green.

Situation 2: Browsers that support Feature Queries, and do not support the feature in question

Edge does not support object-fit, but it does support @supports, so this test will run and fail, preventing the code block from being applied. The image will not have object-fit applied, and the div will have a yellow background.

This is what we want.

Situation 3: Browsers that do not support Feature Queries, and do not support the feature in question

This is where our classic nemesis Internet Explorer appears. IE does not understand @supports and it does not understand object-fit. You might think this means we can’t use a Feature Query — but that’s not true.

Think about the result we want. We want IE to skip over this entire block of code. And that’s exactly what will happen. Why? Because when it reaches @supports, it doesn’t recognize the syntax, and it skips to the end.

It might be skipping the code “for the wrong reasons” — it skips over the code because it doesn’t understand @supports, instead of because it doesn’t understand object-fit — but who cares! We still get exactly the result we want.

This same thing happens with the Blackberry Browser and UC Browser for Android. They don’t understand object-fit, nor @supports, so we are all set. It works out great.

The bottom line — anytime you use a Feature Query in a browser that does not support Feature Queries, it’s fine as long as that browser also does not support the feature you are testing.

Think through the logic of your code. Ask yourself, what happens when the browser skips over this code? If that’s what you want, you are all set.

Situation 4: Browsers that support do not Feature Queries, yet do support the feature in question

The problem is this fourth combination — when the test proposed by a Feature Query doesn’t run, but the browser does support that feature and should run that code.

For example, object-fit is supported by Safari 7.1 (on Mac) and 8 (Mac and iOS) — but neither browser supports Feature Queries. The same is true for Opera Mini — it will support object-fit but not @supports.

What happens? Those browsers get to this block of code, and instead of using the code, applying object-fit:cover to the image and turning the background color of the div green, it skips the whole block of code, leaving yellow as the background color.

And this is not really what we want.

Feature Query Support? Feature support? What Happens? Is This What We Want?
Supports Feature Queries Supports the feature in question CSS is applied Yes
Supports Feature Queries Does not support the feature CSS is not applied Yes
Does not support Feature Queries Does not support the feature CSS is not applied Yes
Does not support Feature Queries Supports the feature in question CSS is not applied No, likely not.

Of course, it depends on the particular use case. Perhaps this is a result we can live with. The older browser gets an experience planned for older browsers. The web page still works.

But much of the time, we will want that browser to be able to use any feature that it does support. This is why Safari 8 is likely the biggest problem when it comes to Feature Queries, not Internet Explorer. There are many newer properties that Safari 8 does support — like Flexbox. You probably don’t want to block Safari 8 from these properties. That’s why I rarely use @supports with Flexbox, or when I have, I’ve written at least three forks in my code, one with a not. (Which gets complicated fast, so I’m not even going to try to explain it here.)

If you are using a feature that has better support in older browsers than Feature Queries, then think through all of the combinations as you write your code. Be sure not to exclude browsers from getting something you want them to get.

Meanwhile, it’s easy to use @supports with the newest CSS features — CSS Grid for example, and Initial Letter. No browser will ever support CSS Grid without supporting Feature Queries. We don’t have to worry about our fourth, problematic combination with the newest features, which makes Feature Queries incredibly useful as we go forward.

All of this means that while IE11 will likely be around for many years to come, we can use Feature Queries liberally with the newest advances in CSS.

Best Practices

So now we realize why we can’t write our code like this:

@supports not (display: grid) {
    // code for older browsers
    // DO NOT COPY THIS EXAMPLE
}
@supports (display: grid) {
    // code for newer browsers
    // DID I SAY THIS IS REALLY BAD?
}

If we do, we’ll stop the older browsers from getting the code they need.

Instead, structure your code like this:

// fallback code for older browsers

@supports (display: grid) {
    // code for newer browsers
    // including overrides of the code above, if needed
}

This is exactly the strategy we applied to using Media Queries when supporting old versions of IE. This strategy is what gave rise to the phrase “mobile first”.

I expect CSS Grid to land in browsers in 2017, and I bet we will use Feature Queries quite a lot when implementing future layouts. It’s going to be much less of a hassle, and much faster, than involving JavaScript. And @supports will let us doing interesting and complex things for browsers that support CSS Grid, while providing layout options for the browser that don’t.

Feature Queries have been around since mid–2013. With the imminent release of Safari 10, I believe it’s past time for us to add @supports to our toolbox.

View full post on Mozilla Hacks – the Web developer blog

VN:F [1.9.22_1171]
Rating: 0.0/10 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)

Implementing Air Mozilla’s Related Events Feature

Editor’s note: It’s not often that we hear from web developers and software engineers at the start their careers here on the Hacks blog. This post is a great reminder of what it’s like.

Mozilla participates in Outreachy, and offers internship opportunities to bring women and other under-represented groups into the world of free and open source software. The application process for the upcoming round will open on September 22, 2015. The application deadline is October 26. Internship dates will be December 7, 2015 to March 7, 2016. If you’ve been thinking of applying, read this, be like Gloria – and go for it!

How I became a Mozilla intern

It was a spring night in Greece when I got the news I’d been selected by Mozilla to be an Outreachy intern. Outreachy is a project of the Software Freedom Conservancy that helps people from groups underrepresented in FOSS (free and open source software) to get involved by offering focused internship opportunities with a number of open source software organizations. That night, I was the happiest person in my town, I believe. I remember rushing to say a big thank you to my mentor Peter Bengtsson, before I got too overwhelmed with happiness to the point that I forgot to be grateful. It was a two-way street. I chose Mozilla and Mozilla chose me.

I started contributing to Air Mozilla in March 2015, by solving beginner-friendly bugs. Initially I didn’t want to apply at all, because there were too many wonderful contributors, and in some way I felt overwhelmed by how rigorous the process was. I told several people I won’t apply because I am afraid I won’t be getting it, and sometimes it is okay to be afraid, but it is not okay to be afraid of something you are capable of achieving to the point of sabotaging yourself.

After several pep talks, I went to view the list of the companies and decided to apply only for Mozilla. It will be a hit or miss, I told myself. I noticed it listed “Python” and I was aware that I didn’t know Python that well as it was something I self-studied briefly in the past, but I told myself to go ahead and try. Sometimes you need to do hard things to get the experience you want. Every expert was once a newbie. I went to check Bugzilla to see what I could fix. One bug looked very easy, so I thought I should work on that. That’s how it all started. Feedback after feedback, perseverance, pull, merge, merge conflicts, and getting a deeper understanding of how the code base functions made me able to contribute a lot more. One thing I loved about interning with open source is the fact that everything is open. I was asked to blog about what I worked on, and I didn’t need to hide anything that concerned it. Well… that doesn’t mean I shared my passwords too though! Passwords need to be protected and secret for a reason.

Getting to work on Air Mozilla

The internship day officially started on May 25th. I was told that after discussions I am being assigned to add a new feature. That new feature will act as a recommendation system suggesting similar events to Air Mozilla viewers while they view a specific event. Air Mozilla is a platform for Mozilla’s online multimedia presence that provides live and pre-recorded shows, interviews, news snippets, tutorial videos, and features about the Mozilla community.

That sounded fascinating. I wasn’t nervous at all, it seemed pretty interesting and I always wondered how those things worked, that’s until I started getting really stuck down the road. Then I realized the complexity of it. I had to either build my own algorithms and computations to do the job, or find an open source tool we could use instead of reinventing the wheel. After several days of research we decided Elastic Search seemed like a good fit. Elastic Search, is a search server. It has a function called “More Like This” and that helps you search for similar events based on your set parameters. I went and read the documentation of Elastic Search but to be honest, I had a very hard time navigating it. Mostly because I couldn’t find enough examples on how to use it with Python. At some point we decided pyelasticsearch might make things easier; it’s a Python library version of Elastic Search that has the functionalities we were looking for.

For a short recap, Elastic Search creates a mapping and indexes all the events on Air Mozilla. When you visit an event page, with the “More Like This” feature, we ask it to search the indexed events that are similar to the event you are currently viewing and return back the title, id, tags and its similarity scores on the back-end. For events to be considered related, their tags, channel and titles must be very much alike, but that’s not where we stop.

We also need to be able to set someone’s level of access: a person who is logged in as a Mozilla employee will have more access than an anonymous guest would. A logged in volunteer contributor will have more access than an anonymous guest, but less than a Mozilla employee. You get the idea. Each event has its own access level setting. Some events are private and limited to employees, while the majority of Air Mozilla events are accessible to the public or to members of the entire Mozilla community. Regardless, the Related Events feature lets people see a list of similar events, filtered for their level of access, with the most similar “related events” appearing on top.

Implementing Elastic Search

In the code below we are using the more_like_this (mlt) query. We break the tags and the titles into different queries because we want to boost the relevance score of each one to a different degree. In this case we want a similar title to have a higher relevance score than the similar tags. The field parameter is used to determine which field we are trying to run mlt against, the docs parameter is the documents we are trying to find similar events for. You can read more about this at the More Like This Query page.

mlt_query1 = { 
    'more_like_this': { 
        'fields': ['title'], 
        'docs': [ 
            { 
                '_index': index, 
                '_type': doc_type, 
                '_id': event.id 
            }], 
        'min_term_freq': 1, 
        'max_query_terms': 20, 
        'min_doc_freq': 1, 
        'boost': 1.0, 
    } 
} 
mlt_query2 = { 
    'more_like_this': { 
        'fields': ['tags'], 
        'docs': [ 
            { 
                '_index': index, 
                '_type': doc_type, 
                '_id': event.id 
            }], 
        'min_term_freq': 1, 
        'max_query_terms': 20, 
        'min_doc_freq': 1, 
        'boost': -0.5, 
    }
} 

 


Below we are asking that one or more of the given mlt queries should match the documents, i.e., list of events:

query_ = { 
    'bool': { 
        'should': [mlt_query1, mlt_query2], 
    } 
} 

 


Below, with request.user.is_active we are checking if the user is logged in and what their access level is. If the user is logged in as a volunteer the events that appear must not contain events that are restricted to Mozilla employees only.

If the user is logged in and is not a volunteer, that means they are Mozilla paid staff members. Their “related events” results should contain all events that are similar to the event we are currently viewing.

In the case of an anonymous user, the “related events” results must contain only events that are accessible to everyone.

if request.user.is_active: 
  query = { 
    'fields': fields, 
    'query': query_ 
  } 
else: 
  query = { 
    'fields': fields, 
    'query': query_, 
    "filter": { 
      "bool": { 
        "must": { 
          "term": {"privacy": Event.PRIVACY_PUBLIC} 
        } 
      } 
    } 
  } 

In the code above you see we used the more_like_this query along with a filter. More_like_this does return similar events based on title, tags and channels but it doesn’t ensure that the events returned are filtered by access level. So this is where “filter” comes in. It makes sure to filter the relevant events and allow only the ones the user can view based on their level of access.

With the use of cron jobs I completely delete and re-index the index for the week, and every 10 minutes we re-index (without first deleting) all events that have changed in the last 10 minutes.

AJAX queries were used to get the thumbnails and links of the related events. AJAX loads asynchronously, so running those queries doesn’t slow the pageload. When I view an event, I want it to load the page fully without waiting for Elastic Search to finish running the queries. Elastic Search queries run pretty fast but it still takes some time. Sometimes, it can be slow. The relevant events section appears under the events’ details. Ideally, someone watching an event won’t mind if there is a slight delay in the loading of the related events while they view the event, however a page that loads slowly as a whole can be frustrating. Through AJAX our Elastic Search queries can run in the background after the page has loaded.

In the screenshots below, you can see how it works. I have added red arrows to the title of event we are on and the title of the first recommended event. If you compare both pictures you can see how the events we are viewing, and/or recommending, relate to each other.

Example of Related Events for volunteers and staff

A second example of Related Events

Examples of related events for signed-in users

 

4 key things that make you realize how much you enjoyed your internship:

  • Your mentor made sure you took the right steps to learn a lot.
  • You feel that every portion of it, even the frustrating moments, made you grow not only in skills but as a person.
  • You were extremely happy you got it.
  • You are extremely sad it is ending.

That’s exactly how it’s been for me. I want to take a moment to thank Outreachy, my mentor Peter, and the other Mozillians I met on IRC and in email during my internship. Thank you for being helpful, welcoming, and for contributing to my learning experience.

What made the pensive ending of my internship bright and merry is a motto Mozilla takes pride in: Once a Mozillian, Always a Mozillian.

View full post on Mozilla Hacks – the Web developer blog

VN:F [1.9.22_1171]
Rating: 0.0/10 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)

Feature: Silverlight, HTML5, and Microsoft’s opaque development strategy

For reasons that are not immediately clear to me, it seems that a lot of developers who attended Microsoft’s recent PDC event were surprised to hear that the company now sees HTML5 as the way forward for developing rich Internet applications—and not, as they had been expecting, Silverlight. Their surprise surprises me, because past statements by the company had already made this repositioning …

View full post on web development – Yahoo! News Search Results

VN:F [1.9.22_1171]
Rating: 0.0/10 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)