emGee Software Solutions Custom Database Applications

Share this

CSS-Tricks

Tips, Tricks, and Techniques on using Cascading Style Sheets.
Updated: 1 day 1 hour ago

The possibilities of the color-adjust property

Tue, 08/14/2018 - 09:22

The Open Web continues to show up in places we would have never originally expected to find it: our phones, televisions, watches, books, video game consoles, fast food menus, gas pumps, elevators, cars—even our refrigerators.

By not making too many or too strict assumptions about how the web should be used, it remains flexible and adaptable. These qualities have allowed it to outperform closed technologies like Flash and Silverlight.

With the web’s growth comes new features to better accommodate its new form factors and use cases. One feature I’m excited about is the color-adjust property, proposed in CSS Color Module Level 4. It is an acknowledgement that the web will continue to show up on devices that have less-than-stellar displays.

There are two values for color-adjust: economy and exact. A value of exact tells the browser it shouldn’t make adjustments to the colors declared in the stylesheet:

.card { background-color: #98b3c7; border-bottom: 0.25rem solid #7c92a3; color: #f3f3f3; color-adjust: exact; ... }

The color-adjust: exact; declaration in this example forces the browser to render all colors as accurately as possible on anything with a class of card applied to it. Accurate meaning being as close as possible based on the host device’s best ability.

The description for the economy value in the specification reads as, “The user agent should make adjustments to the page’s styling as it deems necessary and prudent for the output device.” It places trust in the browser’s hands, allowing it to make adjustments to color values as it sees fit.

Best ability

Handing control over to a browser might seem a little scary at first. As an industry, we’re really great at bikeshedding the heck out of color systems. And that’s a great thing! The use of color, including proper contrast ratios, is an incredibly important aspect of design, and can oftentimes make or break a product.

But we need to understand that our platonically ideal design might not be able to be experienced in the real world as intended. Not everyone owns a device that outputs to a Retina display with a luxurious DCI-P3 color space; nor do they always have perfect vision or ideal lighting conditions. In these kinds of circumstances, it’s better to bend, not break.

We now live in a Mobile, Small, Portrait, Slow, Interlace, Monochrome, Coarse, Non-Hover, First world. Limited color displays aren’t as rare as you think, and are probably only going to get more commonplace as time goes on. I’d especially like to call attention to the rise of internet use by low income populations and emerging markets. With that comes cheaper devices with lower-quality displays.

Browser support

At the time of this article’s publishing, color-adjust has been supported since Firefox 48 (and Android Firefox 60):

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

DesktopChromeOperaFirefoxIEEdgeSafari19*15*48No186*Mobile / TabletiOS SafariOpera MobileOpera MiniAndroidAndroid ChromeAndroid Firefox12NoNo676760

Chrome and Safari, both WebKit browsers, require a vendor-prefixed declaration of -webkit-print-color-adjust. Curiously, -webkit-print signals that their implementation of this property is only intended for print. While the W3C documentation does mention use cases for printing, it is phrased in such a way as to not be limited to it.

People still print webpages! Paper doesn’t require a data plan, nor does its connection drop when you go underground. Just yesterday, I saw someone on the train who was using a collection of printed sites to study for their next exam. And here’s your galaxy brain moment: printed pages are just limited color displays.

I’d also be remiss if I didn’t mention situations where print styles are missing or poorly-authored, potentially forcing a printer to waste ink in an attempt to do what the stylesheet asks of it. Printer ink is hideously expensive—minding this does your (or your IT department’s) budget a solid.

Potential uses

Before we get any further, I want to state the following is all personal theorizing based off the phrasing in the W3C specification—targeting, but not limited to printing.

I think color-adjust could be one of those properties that could find a home explicitly declared in the body selector, where it can best take advantage of the Cascade:

body { color-adjust: economy; ... }

This declaration says, “Every time I declare color in this website, use the values I specify. If you can’t, that’s cool—do what you think is best.” That’s a lot better than the browser trying to literally interpret styling instructions at all cost, potentially rendering the page as completely illegible.

You could declare color-adjust in a more specific way, say nested in a @supports at-rule inside of a print media query, but that’s unnecessary extra work. It would fail to accommodate things like High Contrast Mode and the upcoming color gamut media feature. Better to embrace the unknown and cast a wide net.

I’m also very curious to see how color-adjust could work in conjunction with other browser capabilities, say the Ambient Light Sensor API (RIP Battery Status API). It’d be neat if there were opportunities to experiment with other specialized display modes—macOS’ Night Shift, Increased Contrast, Grayscale, and Reduce Transparency options all come to mind.

A note about accessibility

I’m wondering if software (browser preference toggle or extension, bookmarklet, etc.) could be written to override what the device’s hardware reports itself as being. Much like User Agent spoofing, it could “trick” a browser into thinking it has a limited color display, using economy to force better contrast between text and background color. This would be a lot like some browser’s reading modes, only page layout would be better preserved.

That being said, I don’t think color-adjust is a silver bullet for all color-related accessibility concerns. We can’t always know the device and context our websites and web apps will show up in, including what colors color-adjust would ultimately render as. Because of this, it’s still important to mind your color contrast ratios.

Bending, not breaking

color-adjust feels like a natural extension of Jen Simmons’ Intrinsic Web Design: fluid and squishy UI, proportional sizing, media queries as needed, and simple declarations that do the heavy lifting.

The beauty of the CSS Cascade means you can gracefully create intent, then adjust as needed. color-adjust's documentation specifically mentions a situation where it could be useful to ensure a table’s zebra striping is is retained when printed to make it easier to read. Such a tweak can be scoped to a single selector, without having to spend time undoing it for every other component.

body { color-adjust: economy; ... } tr { color-adjust: exact; }

The beauty of CSS’ fault tolerance means browsers that don’t understand this declaration will ignore it and continue parsing the rest of of the stylesheet. Browsers that do support it can take advantage of it, without any complicated build tool configuration or dangerous User Agent sniffing.

It is important to make our web sites and web apps design adapt to the user’s environment and circumstances, and not the other way around. Good user experiences meet the user where they are, not where we hope they’ll be.

The post The possibilities of the color-adjust property appeared first on CSS-Tricks.

Categories: Web Technologies

An Event Apart

Tue, 08/14/2018 - 09:21

Just the other day in a Slack group I hang out in someone asked "what web design events is everyone going to and loving?" An Event Apart is always my immediate answer. I've gotten to speak a number of An Event Apart events, which is an incredible honor and always a good time. So from that perspective, I love it. I can tell you that it's the most well-run conference I go to that gets all the details right.

But I've also attended An Event Apart as a paying attendee and loved that. There is something about An Event Apart that gets the speakers to really bring their A-game, and you get to watch that A-game over and over for a couple of days.

Upcoming Events in 2018: About AEA

Learn from the best: An Event Apart’s speakers aren’t merely industry leaders with years of experience and deep insight. They’re also engaging, inspiring thinkers who are passionate about sharing what they’ve learned and helping you level up your skills.

And because our editorial team works closely with those speakers, you’ll find that each presentation sheds light on the next—that the event is not merely a collection of disparate talks, but a curated, cutting-edge curriculum for the modern designer, coder, and strategist. A curriculum that continues after the conference ends, with slides, articles, videos, and other resources shared freely on our website.

At An Event Apart, you won’t just learn what’s happening right now. You’ll also look ahead to what’s next. Topics people heard about on our stage before the rest of the industry knew they were coming: responsive web design, mobile-first practices, content strategy, responsive content strategy, and, now, intrinsic web design and variable fonts.

To design a better experience, attend a better-designed UX conference.

AEA Discount Code

Make sure to use AEACP to get $100 of any 2 or 3 day event!

The post An Event Apart appeared first on CSS-Tricks.

Categories: Web Technologies

Simple Interactive Pie Chart with CSS Variables and Houdini Magic

Mon, 08/13/2018 - 06:57

I got the idea for doing something of the kind when I stumbled across this interactive SVG pie chart. While the SVG code is as compact as it gets (a single <circle> element!), using strokes for creating pie chart slices is problematic as we run into rendering issues on Windows for Firefox and Edge. Plus, in 2018, we can accomplish a lot more with a lot less JavaScript!

AI got the following result down to a single HTML element for the chart and very little JavaScript. The future should completely eliminate the need for any JavaScript, but more on that later.

The final pie chart result.

Some of you may remember Lea Verou's Missing Slice talk—my solution is based on her technique. This article dissects how it all works, showing what we can do in terms of graceful degradation and other ways this technique can be put to use.

The HTML

We use Pug to generate the HTML from a data object that contains unitless percentage values for the past three years:

- var data = { 2016: 20, 2017: 26, 2018: 29 }

We make all our elements reside in a .wrap element. Then, we loop through our data object and, for each of its properties, we create a radio input with a corresponding label. After these, we add a .pie element to the mix.

- var darr = [], val; .wrap - for(var p in data) { - if(!val) val = data[p]; input(id=`o${p}` name='o' type='radio' checked=val == data[p]) label(for=`o${p}`) #{p} - darr.push(`${data[p]}% for year ${p}`) - } .pie(aria-label=`Value as pie chart. ${darr.join(', ')}.` role='graphics-document group')

Note that we also made sure only the first radio input is checked.

Passing custom properties to the CSS

I normally don't like putting styles in HTML but, in this particular case, it's a very useful way to pass custom property values to the CSS and ensure that we only need to update things in one place if we need to change any of our data points—the Pug code. The CSS remains the same.

The trick is to set a unitless percentage --p on the .pie element for every radio input that might be checked:

style - for(var p in data) { | #o#{p}:checked ~ .pie { --p: #{data[p]} } - }

We use this percentage for a conic-gradient() on the .pie element after making sure neither of its dimensions (including border and padding) are 0:

$d: 20rem; .wrap { width: $d; } .pie { padding: 50%; background: conic-gradient(#ab3e5b calc(var(--p)*1%), #ef746f 0%); }

Note that this requires native conic-gradient() support since the polyfill doesn't work with CSS variables. At the moment, this limits support to Blink browsers with the Experimental Web Platform features flag enabled, though things are bound to get better.

The Experimental Web Platform features flag enabled in Chrome.

We now have a working skeleton of our demo—picking a different year via the radio buttons results in a different conic-gradient()!

The basic functionality we've been after (live demo, Blink browsers with flag enabled only). Displaying the value

The next step is to actually display the current value and we do this via a pseudo-element. Unfortunately, number-valued CSS variables cannot be used for the value of the content property, so we get around this by using the counter() hack.

.pie:after { counter-reset: p var(--p); content: counter(p) '%'; }

We've also tweaked the color and font-size properties so that our pseudo-element is a bit more visible:

Displaying the value on the chart (live demo, Blink browsers with flag enabled only). Smoothing things out

We don't want abrupt changes between values, so we smooth things out with the help of a CSS transition. Before we can transition or animate the --p variable, we need to register it in JavaScript:

CSS.registerProperty({ name: '--p', syntax: '<integer>', initialValue: 0, inherits: true });

Note that using <number> instead of <integer> causes the displayed value to go to 0 during the transition as our counter needs an integer. Thanks to Lea Verou for helping me figure this out!

Also note that explicitly setting inherits is mandatory. This wasn't the case until recently.

This is all the JavaScript we need for this demo and, in the future, we shouldn't even need this much as we'll be able to register custom properties from the CSS.

With that out of the way, we can add a transition on our .pie element.

.pie { /* same styles as before */ transition: --p .5s; }

And that's it for the functionality! All done with one element, one custom variable, and a sprinkle of Houdini magic!

Interactive pie chart (live demo, Blink browsers with flag enabled only). Prettifying touches

While our demo is functional, it looks anything but pretty at this point. So, let's take care of that while we're at it!

Making the pie... a pie!

Since the presence of :after has increased the height of its .pie parent, we absolutely position it. And since we want our .pie element to look more like an actual pie, we make it round with border-radius: 50%.

Rounding up our pie (live demo, Blink browsers with flag enabled only).

We also want to display the percentage value in the middle of the dark pie slice.

In order to do this, we first position it dead in the middle of the .pie element. By default, the :after pseudo-element is displayed after its parent's content. Since .pie has no content in this case, the top-left corner of the :after pseudo-element is in the top-left corner of the parent's content-box. Here, the content-box is a 0x0 box in the center of the padding-box. Remember that we've set the padding of .pie to 50%—a value that's relative to the wrapper width for both the horizontal and the vertical direction!

This means the top-left corner of :after is in the middle of its parent, so a translate(-50%, -50%) on it shifts it to the left by half its own width and up by half its own height, making its own mid-point coincide with that of .pie.

Remember that %-valued translations are relative to the dimensions of the element they're applied on along the corresponding axis. In other words, a %-valued translation along the x-axis is relative to the element's width, a %-valued translation along the y-axis is relative to its height and a %-valued translation along the z-axis is relative to its depth, which is always 0 because all elements are flat two-dimensional boxes with 0 depth along the third axis.

Positioning the value label in the middle of the pie (live demo, Blink browsers with flag enabled only).

Next, we rotate the value such that the positive half of its x-axis splits the dark slice into two equal halves and then translate it by half a pie radius along this now-rotated x-axis.

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

What we need to figure out is how much to rotate the :after pseudo-element so that its x-axis splits the dark slice into two equal halves. Let's break that down!

Initially, the x-axis is horizontal, pointing towards the right, so in order to have it in the desired direction, we first need to rotate it so that it points up and going along the starting edge of the slice. Then it needs to rotate clockwise by half a slice.

In order to get the axis to point up, we need to rotate it by -90deg. The minus sign is due to the fact that positive values follow a clockwise direction and we're going the other way.

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

Next, we need to rotate it by half a slice.

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

But how much is half a slice?

Well, we already know what percentage of the pie this slice represents: it's our custom property, --p. If we divide that value by 100 and then multiply it by 360deg (or 1turn, it doesn't matter what unit is used), we get the central angle of our dark slice.

After the -90deg rotation, we need to rotate :after by half this central angle in the clockwise (positive) direction.

This means we apply the following transform chain:

translate(-50%, -50%) rotate(calc(.5*var(--p)/100*1turn - 90deg)) translate(.25*$d);

The last translation is by a quarter of $d, which is the wrapper width and gives us the .pie diameter as well. (Since the content-box of .pie is a 0x0 box, it has no border and both its left and right padding are 50% of its wrapper parent width.) The .pie radius is half its diameter, meaning that half the radius is a quarter of the diameter ($d).

Now the value label is positioned where we want it to be:

Positioning the value label in the middle of the slice (live demo, Blink browsers with flag enabled only).

However, there's still one problem: we don't want it to be rotated, as that can look really awkward and neck-bending at certain angles. In order to fix this, we revert the rotation at the end. To make things easier for ourselves, we store the rotation angle in a CSS variable that we'll call --a:

--a: calc(.5*var(--p)/100*1turn - 90deg); transform: translate(-50%, -50%) rotate(var(--a)) translate(.25*$d) rotate(calc(-1*var(--a)));

Much better!

Positioning the value label in the middle of the slice, now horizontal (live demo, Blink browsers with flag enabled only). Layout

We want to have the whole assembly in the middle of the screen, so we solve this with a neat little grid trick:

body { display: grid; place-items: center center; margin: 0; min-height: 100vh }

Alright, this puts the entire .wrap element in the middle:

Positioning the whole assembly in the middle (live demo, Blink browsers with flag enabled only).

The next step is to place the pie chart above the radio buttons. We do this with a flexbox layout on the .wrap element:

.wrap { display: flex; flex-wrap: wrap-reverse; justify-content: center; width: $d; } Placing the pie chart above the radio buttons (live demo, Blink browsers with flag enabled only). Styling the radio buttons

...or more accurately, we're styling the radio button labels because the first thing that we do is hide the radio inputs:

[type='radio'] { position: absolute; left: -100vw; } After hiding the radio buttons (live demo, Blink browsers with flag enabled only).

Since this leaves us with some very ugly labels that are very hard to distinguish from one another, let's give each one some margin and padding so they don't look so crammed together, plus backgrounds so that their clickable areas are clearly highlighted. We can even add box and text shadows for some 3D effects. And, of course, we can create a separate case for when their corresponding inputs are :checked.

$c: #ecf081 #b3cc57; [type='radio'] { /* same as before */ + label { margin: 3em .5em .5em; padding: .25em .5em; border-radius: 5px; box-shadow: 1px 1px nth($c, 2); background: nth($c, 1); font-size: 1.25em; text-shadow: 1px 1px #fff; cursor: pointer; } &:checked { + label { box-shadow: inset -1px -1px nth($c, 1); background: nth($c, 2); color: #fff; text-shadow: 1px 1px #000; } } }

We've also blown up the font-size a bit and set a border-radius to smooth out the corners:

After styling the radio button labels (live demo, Blink browsers with flag enabled only). Final prettifying touches

Let's set a background on the body, tweak the font of the whole thing and add a transition for the radio labels:

The final pie chart result (live demo, Blink browsers with flag enabled only). Graceful degradation

While our demo now looks good in Blink browsers with the flag enabled, it looks awful in all other browsers...and that's most browsers!

First off, let's put our work inside a @supports block that checks for native conic-gradient() support so the browsers that support it will render the pie chart. This includes our conic-gradient(), the padding that gives the pie equal horizontal and vertical dimensions, the border-radius that makes the pie circular, and the transform chain that positions the value label in the middle of the pie slice.

.pie { @supports (background: conic-gradient(tan, red)) { padding: 50%; border-radius: 50%; background: conic-gradient(var(--stop-list)); --a: calc(.5*var(--p)/100*1turn - 90deg); --pos: rotate(var(--a)) translate(#{.25*$d}) rotate(calc(-1*var(--a))); } } }

Now, let's construct a bar chart as a fallback for all other browsers using linear-gradient(). We want our bar to stretch across the .wrap element so that the horizontal padding is still 50%, but vertically as a narrow bar. We still want the chart to be tall enough to fit the value label. This means we will go with smaller vertical padding. We will also decrease the border-radius, since 50% would give us an ellipse and what we need is a rectangle with slightly rounded corners.

The fallback will also replace conic-gradient() with a left-to-right linear-gradient(). Since both the linear-gradient() creating the fallback bar chart and the conic-gradient() creating the pie chart use the same stop list, we can store it in a CSS variable (--stop-list) so that we don't even have it repeated in the compiled CSS.

Finally, we want the value label in the middle of the bar for the fallback since we don't have pie slices anymore. This means we store all the post-centering positioning into a CSS variable (--pos) whose value is nothing in the no conic-gradient() support case and the transform chain:

.pie { padding: 1.5em 50%; border-radius: 5px; --stop-list: #ab3e5b calc(var(--p)*1%), #ef746f 0%; background: linear-gradient(90deg, var(--stop-list)); /* same as before */ &:after { transform: translate(-50%, -50%) var(--pos, #{unquote(' ')}); /* same as before */ } @supports (background: conic-gradient(tan, red)) { padding: 50%; border-radius: 50%; background: conic-gradient(var(--stop-list)); --a: calc(.5*var(--p)/100*1turn - 90deg); --pos: rotate(var(--a)) translate(#{.25*$d}) rotate(calc(-1*var(--a))); } } }

We also switch to using a flexbox layout on the body (since, as clever as it may be, the grid one is messed up in Edge).

body { display: flex; align-items: center; justify-content: center; /* same as before */ }

This all gives us a bar chart fallback for the browsers not supporting conic-gradient().

The fallback for the final pie chart result (live demo). Responsifying it all

The one problem we still have is that, if the viewport is narrower than the pie diameter, things don't look so good anymore.

CSS variables and media queries to the rescue!

We set the diameter to a CSS variable (--d) that gets used to set the pie dimensions and the position of the value label in the middle of our slice.

.wrap { --d: #{$d}; width: var(--d); /* same as before */ @media (max-width: $d) { --d: 95vw } }

Below certain viewport widths, we also decrease the font-size, the margin for our <label> elements, and we don't position the value label in the middle of the dark pie slice anymore, but rather in the middle of the pie itself:

.wrap { /* same as before */ @media (max-width: 265px) { font-size: .75em; } } [type='radio'] { /* same as before */ + label { /* same as before */ @media (max-width: 195px) { margin-top: .25em; } } } .pie{ /* same as before */ @media (max-width: 160px) { --pos: #{unquote(' ')} } }

This gives us our final result: a responsive pie chart in browsers supporting conic-gradient() natively. And, even though that's sadly just Blink browsers with the Experimental Web Platform features flag enabled for now, we have a solid fallback that renders a responsive bar chart for all other browsers. We also animate between values—again, that's just Blink browsers with the Experimental Web Platform features flag enabled at this point.

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

Bonus: radial progress!

We can also apply this concept to build a radial progress indicator like the one below (inspired by this Pen):

The radial progress and its fallback.

The technique is pretty much the same, except we leave the value label dead in the middle and set the conic-gradient() on the :before pseudo-element. This is because we use a mask to get rid of everything, except for a thin outer ring and, if we were to set the conic-gradient() and the mask on the element itself, then the mask would also hide the value label inside and we want that visible.

On clicking the <button>, a new value for our unitless percentage (--p) is randomly generated and we transition smoothly between values. Setting a fixed transition-duration would create a really slow transition between two close values (e.g. 47% to 49%) and a really fast transition when moving between values with a larger gap in between (e.g. 3% to 98%). We get around this by making the transition-duration depend on the absolute value of the difference between the previous value of --p and its newly generated value.

[id='out'] { /* radial progress element */ transition: --p calc(var(--dp, 1)*1s) ease-out; } const _GEN = document.getElementById('gen'), _OUT = document.getElementById('out'); _GEN.addEventListener('click', e => { let old_perc = ~~_OUT.style.getPropertyValue('--p'), new_perc = Math.round(100*Math.random()); _OUT.style.setProperty('--p', new_perc); _OUT.style.setProperty('--dp', .01*Math.abs(old_perc - new_perc)); _OUT.setAttribute('aria-label', `Graphical representation of generated percentage: ${new_perc}% of 100%.`) }, false);

This gives us a nice animated radial progress indicator for browsers supporting all the new and shiny features. We have a linear fallback for other browsers:

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

The post Simple Interactive Pie Chart with CSS Variables and Houdini Magic appeared first on CSS-Tricks.

Categories: Web Technologies

The Cost of JavaScript in 2018

Fri, 08/10/2018 - 08:40

Even though we mentioned it earlier, I thought this outstanding post by Addy Osmani all about the performance concerns of JavaScript was still worth digging into a little more.

In that post, Addy touches on all aspects of perf work and how we can fix some of the most egregious issues, from setting up a budget to “Time-to-Interactive” measurements and auditing your JavaScript bundles.

Embrace performance budgets and learn to live within them. For mobile, aim for a JS budget of < 170KB minified/compressed. Uncompressed this is still ~0.7MB of code. Budgets are critical to success, however, they can’t magically fix perf in isolation. Team culture, structure and enforcement matter. Building without a budget invites performance regressions and failure.

Super specific and super practical!

Surprisingly, Addy mentions that “the median webpage today currently ships about 350KB of minified and compressed JavaScript,” which seems like an awful lot lower than I’d expected, if I'm being honest. The stat that scares me most is that the median webpage takes around fifteen whole seconds until it’s interactive. And pulling all that JS into a Web Worker or caching with Service Workers won't even make up that time to interaction. Yikes.

Another key point: not all bytes are equal. For example, 200KB of JavaScript is not equal to a 200KB JPG image file:

A JPEG image needs to be decoded, rasterized, and painted on the screen. A JavaScript bundle needs to be downloaded and then parsed, compiled, executed —and there are a number of other steps that an engine needs to complete. Just be aware that these costs are not quite equivalent.

Direct Link to ArticlePermalink

The post The Cost of JavaScript in 2018 appeared first on CSS-Tricks.

Categories: Web Technologies

Switch font color for different backgrounds with CSS

Fri, 08/10/2018 - 06:58

Ever get one of those, "I can do that with CSS!" moments while watching someone flex their JavaScript muscles? That’s exactly the feeling I got while watching Dag-Inge Aas & Ida Aalen talk at CSSconf EU 2018.

They are based in Norway, where WCAG accessibility is not a just good practice, but actually required by law (go, Norway!). As they were developing a feature that allows user-selectable color theming for their main product, they faced a challenge: automatically adjusting the font color based on the selected background color of the container. If the background is dark, then it would be ideal to have a white text to keep it WCAG contrast compliant. But what happens if a light background color is selected instead? The text is both illegible and fails accessibility.

They used an elegant approach and solved the issue using the "color" npm package, adding conditional borders and automatic secondary color generation while they were at it.

But that’s a JavaScript solution. Here’s my pure CSS alternative.

The challenge

Here is the criteria I set out to accomplish:

  • Change the font color to either black or white depending on the background color
  • Apply the same sort of logic to borders, using a darker variation of the base color of the background to improve button visibility, only if background is really light
  • Automatically generate a secondary, 60º hue-rotated color
Working with HSL colors and CSS variables

The easiest approach I could think for this implies running HSL colors. Setting the background declarations as HSL where each parameter is a CSS custom property allows for a really simple way to determine lightness and use it as a base for our conditional statements.

:root { --hue: 220; --sat: 100; --light: 81; } .btn { background: hsl(var(--hue), calc(var(--sat) * 1%), calc(var(--light) * 1%)); }

This should allow us to swap the background to any color we’d like at runtime by changing the variables and running an if/else statement to change the foreground color.

Except... we don’t have if/else statements on CSS… or do we?

Introducing CSS conditional statements

Since the introduction of CSS variables, we also got conditional statements to go with them. Or sort of.

This trick is based on the fact that some CSS parameters get capped to a min and max value. For instance, think opacity. The valid range is 0 to 1, so we normally keep it there. But we can also declare an opacity of 2, 3, or 1000, and it will be capped to 1 and interpreted as such. In a similar fashion, we can even declare a negative opacity value, and get it capped to 0.

.something { opacity: -2; /* resolves to 0, transparent */ opacity: -1; /* resolves to 0, transparent */ opacity: 2; /*resolves to 1, fully opaque */ opacity: 100; /* resolves to 1, fully opaque */ } Applying the trick to our font color declaration

The lightness parameter of an HSL color declaration behaves in a similar way, capping any negative value to 0 (which results in black, whatever the hue and saturation happens to be) and anything above 100% is capped to 100% (which is always white).

So, we can declare the color as HSL, subtract the desired threshold from the lightness parameter, then multiply by 100% to force it to overshoot one of the limits (either sub-zero or higher than 100%). Since we need negative results to resolve in white and positive results to resolve in black, we also have to invert it multiplying the result by -1.

:root { --light: 80; /* the threshold at which colors are considered "light." Range: integers from 0 to 100, recommended 50 - 70 */ --threshold: 60; } .btn { /* Any lightness value below the threshold will result in white, any above will result in black */ --switch: calc((var(--light) - var(--threshold)) * -100%); color: hsl(0, 0%, var(--switch)); }

Let’s review that bit of code: starting from a lightness of 80 and considering a 60 threshold, the subtraction results in 20, which multiplied by -100%, results in -2000% capped to 0%. Our background is lighter than the threshold, so we consider it light and apply black text.

If we had set the --light variable as 20, the subtraction would have resulted in -40, which multiplied by -100% would turn 4000%, capped to 100%. Our light variable is lower than the threshold, therefore we consider it a "dark" background and apply white text to keep a high contrast.

Generating a conditional border

When the background of an element becomes too light, it can get easily lost against a white background. We might have a button and not even notice it. To provide a better UI on really light colors, we can set a border based on the very same background color, only darker.

A light background with a dark border based on that background color.

To achieve that, we can use the same technique, but apply it to the alpha channel of a HSLA declaration. That way, we can adjust the color as needed, then have either fully transparent or fully opaque.

:root { /* (...) */ --light: 85; --border-threshold: 80; } .btn { /* sets the border-color as a 30% darker shade of the same color*/ --border-light: calc(var(--light) * 0.7%); --border-alpha: calc((var(--light) - var(--border-threshold)) * 10); border: .1em solid hsla(var(--hue), calc(var(--sat) * 1%), var(--border-light), var(--border-alpha)); }

Assuming a hue of 0 and saturation at 100%, the above code will provide a fully opaque, pure red border at 70% the original lightness if the background lightness is higher than 80, or a fully transparent border (and therefore, no border at all) if it’s darker.

Setting the secondary, 60º hue-rotated color

Probably the simplest of the challenges. There are two possible approaches for it:

  1. filter: hue-rotate(60): This is the first that comes to mind, but it’s not the best solution, as it would affect the color of the child elements. If necessary, it can be reversed with an opposite rotation.
  2. HSL hue + 60: The other option is getting our hue variable and adding 60 to it. Since the hue parameter doesn’t have that capping behavior at 360 but instead wraps around (as any CSS <angle> type does), it should work without any issues. Think 400deg=40deg, 480deg=120deg, etc.

Considering this, we can add a modifier class for our secondary-colored elements that adds 60 to the hue value. Since self-modifying variables are not available in CSS (i.e. there’s no such thing as --hue: calc(var(--hue) + 60) ), we can add a new auxiliary variable for our hue manipulation to our base style and use it in the background and border declaration.

.btn { /* (...) */ --h: var(--hue); background: hsl(var(--h), calc(var(--sat) * 1%), calc(var(--light) * 1%)); border:.1em solid hsla(var(--h), calc(var(--sat) * 1%), var(--border-light), var(--border-alpha)); }

Then re-declare its value in our modifier:

.btn--secondary { --h: calc(var(--hue) + 60); }

Best thing about this approach is that it’ll automatically adapt all our calculations to the new hue and apply them to the properties, because of CSS custom properties scoping.

And there we are. Putting it all together, here’s my pure CSS solution to all three challenges. This should work like a charm and save us from including an external JavaScript library.

See the Pen CSS Automatic switch font color depending on element background.... FAIL by Facundo Corradini (@facundocorradini) on CodePen.

Except it doesn’t. Some hues get really problematic (particularly yellows and cyans), as they are displayed way brighter than others (e.g. reds and blues) despite having the same lightness value. In consequence, some colors are treated as dark and given white text despite being extremely bright.

What in the name of CSS is going on?

Introducing perceived lightness

I’m sure many of you might have noticed it way ahead, but for the rest of us, turns out the lightness we perceive is not the same as the HSL lightness. Luckily, we have some methods to weigh in the hue lightness and adapt our code so it responds to hue as well.

To do that, we need to take into consideration the perceived lightness of the three primary colors by giving each a coefficient corresponding to how light or dark the human eye perceives it. This is normally referred to as luma.

There are several methods to achieve this. Some are better than others in specific cases, but not one is 100% perfect. So, I selected the two most popular, which are good enough:

  • sRGB Luma (ITU Rec. 709): L = (red * 0.2126 + green * 0.7152 + blue * 0.0722) / 255
  • W3C method (working draft): L = (red * 0.299 + green * 0.587 + blue * 0.114) / 255
Implementing luma-corrected calculations

The first obvious implication from going with a luma-corrected approach is that we cannot use HSL, since CSS doesn’t have native methods to access the RGB values of an HSL declaration.

So, we need to switch to an RBG declaration for the backgrounds, calculate the luma from whatever method we choose, and use it on our foreground color declaration, which can (and will) still be HSL.

:root { /* theme color variables to use in RGB declarations */ --red: 200; --green: 60; --blue: 255; /* the threshold at which colors are considered "light". Range: decimals from 0 to 1, recommended 0.5 - 0.6 */ --threshold: 0.5; /* the threshold at which a darker border will be applied. Range: decimals from 0 to 1, recommended 0.8+ */ --border-threshold: 0.8; } .btn { /* sets the background for the base class */ background: rgb(var(--red), var(--green), var(--blue)); /* calculates perceived lightness using the sRGB Luma method Luma = (red * 0.2126 + green * 0.7152 + blue * 0.0722) / 255 */ --r: calc(var(--red) * 0.2126); --g: calc(var(--green) * 0.7152); --b: calc(var(--blue) * 0.0722); --sum: calc(var(--r) + var(--g) + var(--b)); --perceived-lightness: calc(var(--sum) / 255); /* shows either white or black color depending on perceived darkness */ color: hsl(0, 0%, calc((var(--perceived-lightness) - var(--threshold)) * -10000000%)); }

For the conditional borders, we need to turn the declaration into a RGBA, and once again, use the alpha channel to make it either fully transparent or fully opaque. Pretty much the same thing as before, only running RGBA instead of HSLA. The darker shade is obtained by subtracting 50 from each channel.

There's a really weird bug on WebKit on iOS that will show a black border instead of the appropriate color if using variables in the RGBA parameters of the shorthand border declaration. The solution is declaring the border in longhand.
Thanks Joel for pointing out the bug!

.btn { /* (...) */ /* applies a darker border if the lightness is higher than the border threshold */ --border-alpha: calc((var(--perceived-lightness) - var(--border-threshold)) * 100); border-width: .2em; border-style: solid; border-color: rgba(calc(var(--red) - 50), calc(var(--green) - 50), calc(var(--blue) - 50), var(--border-alpha)); }

Since we lost our initial HSL background declaration, our secondary theme color needs to be obtained via hue rotation:

.btn--secondary { filter: hue-rotate(60deg); }

This is not the best thing in the world. Besides applying the hue rotation to potential child elements as previously discussed, it means the switch to black/white and border visibility on the secondary element will depend on the main element’s hue and not on its own. But as far as I can see, the JavaScript implementation has the same issue, so I’ll call it close enough.

And there we have it, this time for good.

See the Pen CSS Automatic WCAG contrast font-color depending on element background by Facundo Corradini (@facundocorradini) on CodePen.

A pure CSS solution that achieves the same effect as the original JavaScript approach, but significantly cuts on the footprint.

Browser support

IE is excluded due to the use of CSS variables. Edge doesn’t have that capping behavior we used throughout. It sees the declaration, deems it nonsense, and discards it altogether as it would any broken/unknown rule. Every other major browser should work.

The post Switch font color for different backgrounds with CSS appeared first on CSS-Tricks.

Categories: Web Technologies

Five interesting ways to use Sanity.io for image art direction

Thu, 08/09/2018 - 06:57

When we saw Chris put up a list of cloud-hosted data-stores, we couldn't resist letting him know that we also had one of those, only ours is a fully featured CMS that come with a rich query language and an open source, real time, collaborative authoring tool that you can tailor to your specific needs using React. It's called Sanity.io.

“Add us to your list!” we asked Chris. “No, your stuff is interesting, can’t you write about you,” he replied. “Maybe something that would be useful for people working with images.” Challenge accepted!

Systems like Sanity wants to free your content from the specific page it happens to be sitting on, so that you can flow it through APIs. That way you can reuse your painstakingly crafted content anywhere you need it.

So, what does this mean for images?

Images are the odd ones out. We can capture documentation articles, pricing plans or project portfolios as potentially complex, but ultimately graspable data-structures that machines can query and process in many useful ways. But images? They're really just bags of pixels sitting at the end of a CDN, aren't they?

We came up with some ideas anyway because everyone needs images and images should be just as willing to travel to new and exciting places and services as the rest of your structured stuff.

So, here we are with a medium-sized bag of image tricks you can pull off with Sanity.

1. Grab the palette from image metadata

Ever tried to overlay text over a random image submitted by a user? How did that work out? We've usually ended submitting our images to filters till they're predictable and skulked home with a moody expression.

We thought it would be nice to ship an image palette with your pictures so you can select between dark and light typography as well as pair visual elements with image color. This makes legibility so much better and prevents clashes between the colors of images and text.

See the Pen Access image colors as metadata in Sanity by Knut Melvær (@kmelve) on CodePen.

2. Get low-quality image placeholders

However fast and nearby your CDN is, it still takes time to transmit large image files. So, we included a Low-Quality Image Placeholder (LQIP) with your image metadata. It's a base64 encoded string of your photo at 20px width. Getting LQIP in the first request lets you bake a proxy image right into the HTML.

<figure style={{ backgroundImage: `url(${mainImage.metadata.lqip})`, paddingTop: `calc(100% / ${mainImage.metadata.dimensions.aspectRatio})` }} > <img src={`${mainImage.url}`} /> </figure>

A challenge with image placeholders is being able to predict the dimensions of the image in a fluid layout. In this example, we used the aspect ratio that also comes in the metadata object to calculate the padding-top trick. That means that you don't have to calculate the aspect ratio yourself, and e.g. output it as a custom property combined with CSS variables.

See the Pen Get Low Quality Image Placeholders out of the box by Knut Melvær (@kmelve) on CodePen.

3. Use on-demand image transforms

You should be able to store archival originals and get the image in whatever resolution and format you need. Go ahead, upload your 268 megapixel TIFF, and ask for a 128x128 cropped JPEG with a 50% blur.

The transforms are generated from URL-parameters and generated on the first request. We cache the result locally and on a CDN so many requests for the same result image will be super snappy.

Take this image of the Carina Nebula. We uploaded a 29.566 x 12.960 version of it. Let’s say we wanted a PNG version with a 500px width. Attach ?w=500&fm=png to the image URL, and there it is:

Source File

Not only is it hard to hear screaming in space, it's also hard to judge directions. But let’s say you need to rotate the image. Well, you can append &or=90 and it's tilted 90 degrees (and scale it down to 128px):

Source File

Not to ruffle any feathers at NASA, but if you want to simulate spherical aberration on your new 1.5bn USD telescope, you can do that by adding ?blur=30 to the image-url!

Source File

The image pipeline is good for your SVGs as well. Say you need to offer a JPG Open Graph (og:image) to Facebook or you have an urgent need to add this SVG of a forklift as a Slack emoji:

Source File

Get the 128x128 PNG version you need, by appending the URL parameters w=128&h=128&fm=png just like this.

You also have control of background color, with bg=<hexcolor> Let's say we, for some reason, wanted this forklift on a lovely purple background.

And, finally, if you want to link to this image and trigger a download for the user, you can append dl=<suggested file-name> to make it happen.

Download the Image

It's not always as fun as this to mess around with URL parameters, so we made a JavaScript library that makes it a bit easier. Install it with npm install --save @sanity/image-url and learn how to use it (we use it in the demos throughout this post).

4. Crop and scale to fit around a focus point

Those pesky images with their fixed aspect ratios. In addition to not knowing the pixel density of the output device, you often don't even know what crop would be best suited for the layout. Even on a single website, you'll often use the same image in different layouts, and often need to serve them up to third parties, like Facebook, that expects specific aspect ratios.

Custom crop and hotspot in Sanity Studio

To ease the pain of dealing with this, we've let content editors set hotspots and crops for images. We have also made a JavaScript package that does all the hard work of making the correct image transform URLs for you. Simply pass an image asset and the size you want and we'll figure out the crop and scale.

See the Pen Custom crop and hotspots by Knut Melvær (@kmelve) on CodePen.

5. Make a real-time photomap

If this isn't turning it up to 11, it's at least a good 10.5.

We can extract EXIF and geo-location data from an image. EXIF-data contains information about the camera state when the photo was taken (e.g. exposure, aperture, lens, if the flash went off etc). Due to privacy concerns (we take GDPR-compliance super seriously) we have turned the feature off by default, but if you set [options: { metadata: ['exif', 'location' ] } in your schema options, they will be included.

We have made a tiny demo that lets you upload an image and, if it has location data (e.g. typically those you take with your phone), it will automatically pop up in the map — because the API is real-time with listeners!

See the Pen Shared Realtime Photo Map by Knut Melvær (@kmelve) on CodePen.

If you want to test out Sanity and these features yourself, you can get started with writing npm i -g @sanity/cli && sanity init. Even though the CLI guides you through the installation process, it's worth checking out the documentation (if not for the illustrations alone) and there are always people that want to help out with the nitty gritty in our Slack group.

The post Five interesting ways to use Sanity.io for image art direction appeared first on CSS-Tricks.

Categories: Web Technologies

New on Wufoo: Form Manager Beta, File Manager Beta, Entry Manager Beta

Thu, 08/09/2018 - 06:54

Wufoo really is firing on all cylinders lately! As you may know, I've been using Wufoo here on this site, and pretty much every other site I've ever made, to power the web forms for over a decade. That's a dang long time, which more than proves to me Wufoo is a form solution to trust. But also a product that improves!

There is a new Form Manager, Entry Manager, and File Manager.

Enable the beta stuff through the Account menu:

The new Form Manager has a modern and clean look. You can tell this has been a massive cleanup and give them better place to iterate from:

The Entry Manager is the biggest upgrade so far. You can view more entries at a time and navigate between entires much easier. I quite like how each entry takes you through kind of a ghosted version of the form itself, seeing exactly what someone entered:

The Files Manager is a brand new thing! Wufoo forms can collect files easily, so if that's a thing you use, it might be mighty handy for you to be able to browse and manage those from the manager.

Those are great improvements for you for getting around and doing stuff within Wufoo, but of course, the most important thing is that you can build powerful forms very easily on Wufoo and integrate them anywhere you need to.

The post New on Wufoo: Form Manager Beta, File Manager Beta, Entry Manager Beta appeared first on CSS-Tricks.

Categories: Web Technologies

Here’s how I recreated theory11’s login form — how would you do it?

Wed, 08/08/2018 - 14:11

I ran across a super cool design for a login form over on the website theory11.com. Actually, the whole site and the products they make are incredibly well designed, there's just something about the clean and classy form that really got me. Plus, it just so happened that the CodePen Challenge that coming week was based on forms, so I took a few minutes and tried slapping it together.

Fadeout vector pattern

One of the things I thought was super classy was the way that vector wallpaper-eque pattern was not only there but faded out sort of radially. I didn't try to match the pattern exactly—I just grabbed one from the Assets Panel in CodePen and dropped it onto the <html> element as a SVG data URL background-image with a low fill-opacity. Then a radial gradient sits on top and creates the fading effect—a radial gradient with the same base background color that fades away.

:root { --gray: rgb(14, 19, 25); --gray--t: rgba(14, 19, 25, 0); } html { background: var(--gray) url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="28" height="49" viewBox="0 0 28 49"%3E%3Cg fill-rule="evenodd"%3E%3Cg id="hexagons" fill="%239C92AC" fill-opacity="0.1" fill-rule="nonzero"%3E%3Cpath d="M13.99 9.25l13 7.5v15l-13 7.5L1 31.75v-15l12.99-7.5zM3 17.9v12.7l10.99 6.34 11-6.35V17.9l-11-6.34L3 17.9zM0 15l12.98-7.5V0h-2v6.35L0 12.69v2.3zm0 18.5L12.98 41v8h-2v-6.85L0 35.81v-2.3zM15 0v7.5L27.99 15H28v-2.31h-.01L17 6.35V0h-2zm0 49v-8l12.99-7.5H28v2.31h-.01L17 42.15V49h-2z"/%3E%3C/g%3E%3C/g%3E%3C/svg%3E'); } body { margin: 0; height: 100vh; background: radial-gradient( ellipse at bottom center, var(--gray--t), var(--gray) 90% ); } Double border

If you peek at the double border code on theory11's site, you'll see it's done with a single 2px solid border on a parent and another on the child element, with a bit of padding to space them out. Perfectly fine way to do it, of course. But it reminded me of the fact that you can literally do double as a border style. You have very little control over the spacing, but hey, it's close!

form { border: 7px double #AA8B59; } What about them corner boxes?

I had fun building the rest of it out, but I stopped short of dealing with those corner boxes. I thought about it though! My first thought was psuedo elements, as those are wonderful little tools for adding extra flair without any extra HTML. Except... you only get two of those and we need four here. Turns out that's how they do it—they get four because they use both the parent and the child (to get the border).

Peter Schmalfeldt took a crack at doing it that way:

See the Pen theory11 sign in by Peter Schmalfeldt (@manifestinteractive) on CodePen.

Dan Wilson took an entirely different approach by doing it with multiple background gradients:

See the Pen theory11 sign in (add corner boxes with background gradients) by Dan Wilson (@danwilson) on CodePen.

Another possibility would have been border-image. I find use cases for border-image rather rare, and the syntax pretty hard to grok, but this is kind of the perfect situation for it. The property uses "9 slice scaling," so imagine an image being cut up like a tic-tac-toe board. Then each of those areas can repeat or stretch (or variations of those). A graphic like this brown shape:

Nine-sliced:

And all those non-corner sections repeating directionally to make whatever middle space is needed.

SVG has similar potential. Mike Riethmuller has a great post about that. I'll steal his demo here:

See the Pen Flexy SVG by Chris Coyier (@chriscoyier) on CodePen.

See how different parts of that SVG stretch differently when the viewport is resized? I'm sure that could be used for our form design here as well.

I find it fun how many ways there are to do something like this. There's the ol' fashioned way, where those corners are just some <div>s you position and style up as needed. Or you could get extra super fancy and use Houdini / Paint API to do it!

The post Here’s how I recreated theory11’s login form — how would you do it? appeared first on CSS-Tricks.

Categories: Web Technologies

Chrome 69

Wed, 08/08/2018 - 14:10

Chrome 69 is notable for us CSS developers:

  • Conic gradients (i.e. background: conic-gradient(red, green, blue);): We've got lots of interesting articles about conic gradients here, and here's some use cases and a polyfill from Lea Verou.
  • Logical box model properties: margin, padding, and border all get an upgrade for more use cases. Think of how we have margin-left now — the "left" part doesn't make much sense when we switch directions. Now, we'll have margin-inline-start for that. The full list is margin-{block,inline}-{start,end}, padding-{block,inline}-{start,end} and border-{block,inline}-{start,end}-{width,style,color}. Here's Rachel Andrew with Understanding Logical Properties And Values.
  • Scroll snap points (i.e. scroll-snap-type: x mandatory;): What once required JavaScript intervention is now happily in CSS. We've been covering this for years. Goes a long way in making carousels way less complicated.
  • Environment variables (i.e. env(safe-area-inset-top);): Apple introduced "the notch" with the iPhone X and dropped some proprietary CSS for dealing with it. The community quickly stepped in and now we have env() for browsers to ship stuff like this.

I guess we can give this version number a well-deserved nice.

Direct Link to ArticlePermalink

The post Chrome 69 appeared first on CSS-Tricks.

Categories: Web Technologies

Browser painting and considerations for web performance

Wed, 08/08/2018 - 07:00

The process of a web browser turning HTML, CSS, and JavaScript into a finished visual representation is quite complex and involves a good bit of magic. Here’s a simplified set of steps the browser goes through:

  1. Browser creates the DOM and CSSOM.
  2. Browser creates the render tree, where the DOM and styles from the CSSOM are taken into account (display: none elements are avoided).
  3. Browser computes the geometry of the layout and its elements based on the render tree.
  4. Browser paints pixel by pixel to create the visual representation we see on the screen.

In this article, I'd like to focus on the last part: painting.

All of those steps combined is a lot of work for a browser to do on load... and actually, not just on load, but any time the DOM (or CSSOM) is changed. That’s why many web developers tend to partially solve this by using some sort of frontend framework, such as React which, apart from many other advantages, can help to highly optimize changes in the DOM to avoid unnecessary recalculating or rendering.

You may have heard terms such as state, component rendering, or immutability. All of those have something to do with the optimization of DOM changes, or in other words, to only make changes to the DOM when it's necessary.

To give an example, the state of a web application may change, and that would lead to a change in UI. However, certain (or many) components are not affected by this change. What React helps to do is limit the writing to the DOM for elements that are actually affected by a change in state and ultimately limit the rendering to the smallest part of the web application possible:

DOM/CSSOM → render tree → layout → painting

However, browser painting is special in its own way, as it can happen even without any changes to the DOM and/or CSSOM.

Example of page performance summary

The diagram above was generated using Chrome's performance panel in DevTools (more on that later) and it shows how much time was taken by each task in the browser in the recorded time (0-7.12s) after reloading of a page. As you can see, painting takes a significant part, and that's not automatically a bad thing. In this particular example, the increased painting is caused by a combination of animated GIFs on the page and canvas drawing (at 60fps), where both don't cause any changes to the DOM or its styles, while still triggering painting.

Another good example of a feature that may cause painting without any outside intervention is the CSS animation property, and compared to animated GIF or canvas, it is probably more common on the web. An animation is usually triggered by user input, like hover, but thanks to animation and @keyframes rules, we can even create quite complex animations running constantly on the page without much of an effort, which is pretty amazing.

What some might not realize, is that those animations can easily get out of hand and constantly trigger painting, and that can cost us a lot of processing power. Of course, there are some rules that can be used to avoid painting. Most obvious is limiting manipulation of elements to CSS transform and opacity properties, which by default don't trigger paint, unless some special circumstances are in place, such as animating an SVG path.

Paint flashing

You likely know that Chrome has DevTools. What you might not know about is a little shortcut (Shift+Cmd+P on Mac or Control+Shift+P on PC) which can be used inside DevTools to bring up a little search bar and command menu.

Command Menu

I've started digging around it, and apart from many other useful and incredibly interesting options, a render panel caught my attention.

Render Panel

At the first sight, you can see some interesting options that can be very helpful when it comes to debugging animation on the web, like an FPS meter.

FPS meter

Layer borders and paint flashing are also interesting tools. Layer borders are used to display the borders of layers as they are rendered by the browser so that any transformation or change in size is easily recognizable. Paint flashing serves to highlight areas of the webpage where the browser is forced to repaint.

Paint flashing

After discovering paint flashing, the first thing I did was check it out on a project of mine. In most places, there was no trouble. For instance, any movement triggered by scroll on the website was powered by the CSS transform property, which as we covered, doesn't cause painting. The painting was present where one would expect it to be, like changes in text color on hover, but that's not something that should be much of a concern due to its area and presence only on hover of the element. To sum it up, you can always find something to improve, even if you wrote the code yesterday…

But one thing was a slap in the face.

It doesn't matter how experienced or careful you are, you can — and most likely will — make a mistake. We're just people and some would argue that fixing your own bugs is most of the job when it comes to development. However, for a bug to be fixable, we need to be aware of it… and that's exactly where the render panel helps.

Case study

Let's take a closer look at the actual issue. The design came in with the request for a noisy background. That kind of effect that old TVs had when there was no signal.

It is known that GIFs have many issues, where performance is certainly one of them, so I definitely couldn't use that for a whole page background. If you'd like to read some more on why to avoid GIFs, here is a good resource with a bunch of reasons.

Using JavaScript is definitely an option in this case. Displaying or hiding elements with a slightly moved background was the first thing that came to my mind, and using canvas could help too. However, all of this seemed a little overkill for simply having a background. I decided to go for a CSS-only approach.

My solution was to take a small “noisy” PNG image as a background-image, enable background-repeat and throw it over a one-color background. How did I achieve the noise effect? With infinite CSS animation! By setting the background-position to different value over the period of 200 milliseconds. Here's how that turned out:

See the Pen MXoddr by Georgy Marchuk (@gmrchk) on CodePen.

Can you guess the problem? It seemed like a quite an elegant solution to me, and I was excited about my achievement of making it through without a crappy GIF and even not a single line of JavaScript. Just simple CSS that is optimized in browsers these days.

Well, the paint flashing showed something completely different. The layer of the size of the window was constantly repainting, without the user even doing anything. You can see the paint flashing in the demo above if you enable it in the render panel (note that paint flashing doesn't show up in embedded pen).

Without paint flashing (left) vs. with paint flashing (right)

That certainly doesn't play well with the performance of the website and drains laptop batteries like there's no tomorrow.

CPU usage for the animation done with background-position (top) and transform (bottom)

All of this CPU usage could have been avoided by replacing the changes to background-position using transform or opacity.

See the Pen XYOYGm by Georgy Marchuk (@gmrchk) on CodePen.

The problem

I've been doing web development for a while and I knew very well that animating a background is never a good idea. This felt like a rookie mistake. People make mistakes... but that's not the whole story. The website was all laggy and uncomfortable to navigate. How did I miss it?

Something that certainly plays a big role is the fact that I am (and you may be as well) a little spoiled when it comes to development equipment. I have a nice, powerful computer for work and access to speedy internet. Unless we write some really crappy code, anything we write runs quite smoothly in our eyes. But that's not always the case for our users.

A similar problem applies to many other things — like display size. Using a little exaggeration, while we are developing on 27” display with 4K resolution and getting the designs primarily for 1920x1080, our visitors come in mainly from 1366x768 laptops and have a completely different workflow when it comes to using a computer.

Conclusion

While this article started off as a piece about painting, its main topic is really much more about being mindful of the impact our code has on the painting process or performance in general. While painting serves as a good example of something that can be problematic and easily missed, it's more of a disconnect between developer and user that is the issue.

The web is a place of many environments, where the developer's environment is often far different than the user's. While there is no need to change our ways or switch to lazy computers, it definitely helps to see our work the way it is seen by others from time to time. My suggestion is: when you come home from work and have a little free time, try to pick up your old computer and check your work there, to get a little closer to what your users experience.

If you don't have this kind of computer around, tools like render panel can turn out to be awfully handy.

The post Browser painting and considerations for web performance appeared first on CSS-Tricks.

Categories: Web Technologies

Short note on the accessibility of styled form controls

Mon, 08/06/2018 - 11:13

Styling and accessibility are often at odds with each other. Scott O'Hara has this repo that shows how the two can work really well together in the context of form controls.

The trade-offs between native and styled controls reminds me of Eric Bailey's case study on focus styles:

A common misconception is that the focus style can only use the outline property. It’s worth noting that :focus is a selector like any other, meaning that it accepts the full range of CSS properties. I like to play with background color, underlining, and other techniques that don’t adjust the component’s current size, so as to not shift page layout when the selector is activated.

The nice thing about Scott's repo is that it serves as a baseline that can be pulled into a project and customized from there. Sort of like WTF, forms? but with a clear—ahem—focus on accessibility. Styles are considered, but less the point.

While we're on the topic, let's not let Dave Rupert's "Nutrition Cards for Accessible Components" go unnoticed. It also shipped recently and—aside from being a clever idea—is a truly useful thing to reference the accessibility expectations of specific components. Definitely worth a bookmark. &#x1f516;

Direct Link to ArticlePermalink

The post Short note on the accessibility of styled form controls appeared first on CSS-Tricks.

Categories: Web Technologies

Let’s make a form that puts current location to use in a map!

Mon, 08/06/2018 - 06:51

I love shopping online. I can find what I need and get most things for a decent price. I am Nigerian currently working and studying in India, and two things I dread when shopping online are:

  1. Filling out a credit card form
  2. Filling out shipping and billing address forms

Maybe I’m just lazy, but these things are not without challenges! For the first one, thanks to payment processing services like PayPal and e-wallets, I neither have to type in my 12-digit credit card number for every new e-commerce site I visit, nor have to save my credit card details with them.

For the second, the only time-saving option given by most shopping websites is to save your shipping address, but you still have to fill the form (arrghh!). This is where the challenge is. I've had most of my orders returned because my address (which I thought was the right address) could not be located or confirmed by the app for one reason or another.

Address inputs are challenging

Getting a user's address through an input form is a clean solution but can also be a herculean task to the user. Here's why:

  • If the user is new in a particular city, they might not know their full address
  • If the user wants to ship to a new address which isn't saved (e.g shipping to a workplace or a friend’s address instead of the saved home address)
  • If the user resides in a city with very difficult address systems
  • If the user is plain lazy like me
A potential solution: get the address automatically

Getting the user's address by the tap/click of a button. Yup, that's easy! The UX is great as it saves the user both the time and effort of filling out some form. It will also save the store owner time, effort, and even money in some cases, as there'll likely be a reduction in the number of incorrectly placed orders or shipments.

Let’s build a mini app that gets a user's address and shows it on a Google Map interface using vanilla JavaScript. The concept is as follows:

  1. Get the HTML button and listen for a click event
  2. Get the user's location (latitude and longitude) on a button click
  3. Show the user’s location on a Google map
  4. Pass the latitude and longitude to the Google Maps Geocode API URL
  5. Display the returned address (or list of addresses) on the UI for the user to select one
  6. Listen for map events and repeat steps 4 and 5
  7. Pre-fill the form with the address data the user selects
Getting started and setting up

To build this app, we’re going to use the Materialize CSS framework to save us some time fussing with styles. Materialize is a modern responsive front-end framework based on Google’s Material Design system. The beta version works with vanilla JavaScript.

A basic setup using Materialize’s CSS and JavaScript files in a document is like this:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Address Locator</title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-beta/css/materialize.min.css"> <link rel="stylesheet" href="css/main.css"> </head> <body> <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-beta/js/materialize.min.js"></script> <script src="js/main.js"></script> </body> </html>

We’re also going to use the Google Maps API for displaying the map and getting the user's human-readable address. We’ll need an API key to do this. Here’s how to get one:

  1. Sign in to your Google Developer's Console Account
  2. Create a new project or select an existing one
  3. Click on "Enable APIs and Services"
  4. Select the “Maps Javascript API” option
  5. Click "Enable" on the new page that comes up. Go back to the previous page and do a search for "Geocoding API," click on it and enable it as well
  6. Then, on the right nav of the page, click on Credentials, copy the API key on the page and save it to a file

Now, let’s update our document to show the map and let the user know it can be used to get their current location as the address. Also, we will add a form that’s pre-filled with the address the user selects.

... <body> <div class="container"> <h3>Shipping Address</h3> <p>You can click the button below to use your current location as your shipping address</p> <div id="map"> </div> <button id="showMe" class="btn">Use My Location</button> <form id="shippingAddress"> <div id="locationList"></div> <br> <div class="input-field"> <textarea class="input_fields materialize-textarea" id="address" type="text"></textarea> <label class="active" for="address">Address (Area and Street)</label> </div> <div class="input-field"> <input class="input_fields" id="locality" type="text"> <label class="active" for="locality">Locality</label> </div> <div class="input-field"> <input class="input_fields" id="city" type="text"> <label class="active" for="city">City/District/Town</label> </div> <div class="input-field"> <input class="input_fields" id="postal_code" type="text"> <label class="active" for="pin_code">Pin Code</label> </div> <div class="input-field"> <input class="input_fields" id="landmark" type="text"> <label class="active" for="landmark">Landmark</label> </div> <div class="input-field"> <input class="input_fields" id="state" type="text"> <label class="active" for="State">State</label> </div> </form> <!-- You could add a fallback address gathering form here --> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-beta/js/materialize.min.js"></script> <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY"></script> <script src="js/main.js"></script> </body> </html>

While we are here, let's style things a bit to make this look a little better:

.container { width: 50%; max-width: 800px; } #map { height: 50vh; margin-bottom: 10px; display: none; } #locationList .card { padding: 10px; } #toast-container { top: 50%; bottom: unset; } .toast { background-color: rgba(0, 0, 0, 0.8); } @media only screen and (max-width: 768px) { .container { width: 80%; } }

This CSS hides the map until we are ready to view it. Our app should look like this:

Let’s plan this out

Our app will be making use of the HTML5 Geolocation API to determine our user's current location as well as Google's Geocode API with a technique called Reverse Geocoding. The Geocode API takes a human-readable address and changes it into geographical (latitudinal and longitudinal) coordinates and marks the spot in the map.

Reverse Geocoding does the reverse. It takes the latitude and longitude and converts them to human-readable addresses. Geocoding and Reverse Geocoding are well documented.

Here’s how our app will work:

  1. The user clicks on the "Use My Location" button
  2. The user is located with the HTML5 Geolocation API (navigator.geolocation)
  3. We get the user's geographic coordinates
  4. We pass the coordinates to the geocode request API
  5. We display the resulting addresses to the user

Most times, the geocode returns more than one address, so we would have to show the user all the returned addresses and let them choose the most accurate one.

Phew! Finally, we can get to the fun part of actually writing the JavaScript. Let’s go through each of the steps we outlined.

Step 1: Clicking the button

In our main.js file, let's get a reference to the HTML button. While we are at it, we’ll set up some other variables we’ll need, like our API key.

//This div will display Google map const mapArea = document.getElementById('map'); //This button will set everything into motion when clicked const actionBtn = document.getElementById('showMe'); //This will display all the available addresses returned by Google's Geocode Api const locationsAvailable = document.getElementById('locationList'); //Let's bring in our API_KEY const __KEY = 'YOUR_API_KEY'; //Let's declare our Gmap and Gmarker variables that will hold the Map and Marker Objects later on let Gmap; let Gmarker; //Now we listen for a click event on our button actionBtn.addEventListener('click', e => { // hide the button actionBtn.style.display = "none"; // call Materialize toast to update user M.toast({ html: 'fetching your current location', classes: 'rounded' }); // get the user's position getLocation(); });

When the click handler for our button runs, it:

  1. Hides the button
  2. Alerts the user that we are getting their current location (a “toast” in Materialize is like a popup notification)
  3. Calls the getLocation function
Step 2: Get the user's location (latitude and longitude)

When our getLocation function is invoked, we need to do some more work. First, let’s check if we can even use the Geolocation API:

getLocation = () => { // check if user's browser supports Navigator.geolocation if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(displayLocation, showError, options); } else { M.toast({ html: "Sorry, your browser does not support this feature... Please Update your Browser to enjoy it", classes: "rounded" }); } }

When we have support, it calls geolocation's getCurrentPosition method. If it doesn't have support, then the user is alerted that there's no browser support.

If there is support, then the getCurrentLocation method is used to get the current location of the device. The syntax is like this:

navigator.geolocation.getCurrentPosition(*success, error, [options]*)
  • success : This is a callback function that takes a position as its only parameter. For us, our success callback function is the displayLocation function.
  • error : [optional] This is a callback function that takes a PositionError as its sole input parameter. You can read more about this here. Our error callback function is the showError function.
  • options : [optional] This is an object which describes the options property to be passed to the getCurrentPosition method. You can read more about this here. Our options parameter is the options object.

Before writing our displayLocation function, let's handle the showError function and options object:

// Displays the different error messages showError = (error) => { mapArea.style.display = "block" switch (error.code) { case error.PERMISSION_DENIED: mapArea.innerHTML = "You denied the request for your location." break; case error.POSITION_UNAVAILABLE: mapArea.innerHTML = "Your Location information is unavailable." break; case error.TIMEOUT: mapArea.innerHTML = "Your request timed out. Please try again" break; case error.UNKNOWN_ERROR: mapArea.innerHTML = "An unknown error occurred please try again after some time." break; } } //Makes sure location accuracy is high const options = { enableHighAccuracy: true }

Now, let’s write the code for our displayLocation function inside our main.js file:

displayLocation = (position) => { const lat = position.coords.latitude; const lng = position.coords.longitude; }

We now have our user's latitude and longitude and we can view them in the console by writing the code below inside displayLocation:

console.log( `Current Latitude is ${lat} and your longitude is ${lng}` ); Step 3: Show the user’s current location on a Google Map

To do this, we will be adding these lines of code to our displayLocation function.

const latlng = {lat, lng} showMap(latlng); createMarker(latlng); mapArea.style.display = "block";

The first line takes our lat and lng values and encapsulates it in the latlng object literal. This makes it easy for us to use in our app.

The second line of code calls a showMap function which accepts a latlng argument. In here, we get to instantiate our Google map and render it in our UI.

The third line invokes a createMarker function which also accepts our object literal (latlng) as its argument and uses it to create a Google Maps Marker for us.

The fourth line makes the mapArea visible so that our user can now see the location.

displayLocation = (position) => { const lat = position.coords.latitude; const lng = position.coords.longitude; const latlng = { lat, lng } showMap(latlng); createMarker(latlng); mapArea.style.display = "block"; }

Now, let's get to creating our functions. We will start with the showMap function.

showMap = (latlng) => { let mapOptions = { center: latlng, zoom: 17 }; Gmap = new google.maps.Map(mapArea, mapOptions); }

The showMap function creates a mapOptions objects that contain the map center (which is the lat and lng coordinates we got from displayLocation) and the zoom level of the map. Finally, we create an instance of the Google Maps class and pass it on to our map. In fewer words, we instantiate the Google Maps class.

To create a map instance, we specify two parameters in the constructor: the div the map will be displayed and the mapOptions. In our case, our div is called mapArea and our mapOptions is called mapOptions. After this, our created map will show up, but without a marker. We need a marker so the user can identify their current position on the map.

Let's create our marker using the createMarker function:

createMarker = (latlng) => { let markerOptions = { position: latlng, map: Gmap, animation: google.maps.Animation.BOUNCE, clickable: true }; Gmarker = new google.maps.Marker(markerOptions); }

A few things to note in this code:

  1. The position property positions the marker at the specified latlng
  2. The map property specifies the map instance where the marker should be rendered (in our case, it’s Gmap)
  3. The animation property adds a little BOUNCE to our marker
  4. The clickable property set to true means our marker can be clicked
  5. Finally, we instantiate the Marker class in our Gmarker instance variable

So far, our user's location has been fetched, the map has rendered and the user can see their current location on the map. Things are looking good! &#x1f57a;

Step 4: Pass the latitude and longitude to the Geocode API

Google's Geocoding API will be used to convert our user's numeric geographical coordinates to a formatted, human-readable address using the reverse geocoding process we covered earlier.

The URL takes this form:

https://maps.googleapis.com/maps/api/geocode/outputFormat?parameters

...where the outputFormat may either be a json or xml which determines the the format used to deliver the data. The parameters part is a list of parameters needed for the request.

Our request URL will look like this:

https://maps.googleapis.com/maps/api/geocode/json?latlng=${latlng}&key=${__KEY}

Let's go ahead and connect to the API. We would do this in a function called getGeolocation.

getGeolocation = (lat, lng) => { const latlng = lat + "," + lng; fetch( `https://maps.googleapis.com/maps/api/geocode/json?latlng=${latlng}&key=${__KEY}` ) .then(res => res.json()) .then(data => console.log(data.results)); }

The getGeolocation function takes two arguments ( lat and lng } concatenates them to form a new latlng variable that is passed to the URL.

Using the Fetch API (more on this here), we add the new latlng and __KEY into the Geocode request URL. Then, on the response object we get back, we pass the .json method to resolve the promise with JSON. Finally, we log the response in our console.

To make use of our newly created function, we have to call it in the displayLocation function. So let's update our displayLocation function to contain the getGeolocation function call:

displayLocation = (position) => { const lat = position.coords.latitude; const lng = position.coords.longitude; const latlng = { lat, lng } showMap(latlng); createMarker(latlng); mapArea.style.display = "block"; getGeolocation(lat, lng)// our new function call }

The returned data should look something like this:

{ "results" : { "address_components": { "long_name": "1600", "short_name": "1600", "types": ["street_number"] }, { "long_name": "Amphitheatre Pkwy", "short_name": "Amphitheatre Pkwy", "types": ["route"] }, { "long_name": "Mountain View", "short_name": "Mountain View", "types": ["locality", "political"] }, { "long_name": "Santa Clara County", "short_name": "Santa Clara County", "types": ["administrative_area_level_2", "political"] }, { "long_name": "California", "short_name": "CA", "types": ["administrative_area_level_1", "political"] }, { "long_name": "United States", "short_name": "US", "types": ["country", "political"] }, { "long_name": "94043", "short_name": "94043", "types": ["postal_code"] } ], "formatted_address": "1600 Amphitheatre Parkway, Mountain View, CA 94043, USA", "geometry": { "location": { "lat": 37.4224764, "lng": -122.0842499 }, "location_type": "ROOFTOP", "viewport": { "northeast": { "lat": 37.4238253802915, "lng": -122.0829009197085 }, "southwest": { "lat": 37.4211274197085, "lng": -122.0855988802915 } } }, "place_id": "ChIJ2eUgeAK6j4ARbn5u_wAGqWA", "types": ["street_address"] } ], "status" : "OK" } Step 5: Display the returned address(es) for the user to select

At this stage, we have made a request to Google's Geocoding API and have gotten our result logged in the console. Now, we have to display the results in a UI for our user. This requires two things:

  1. Create a new function that handles creating HTML elements
  2. Update our getGeolocation function to make the function call

Let's create the function that would take care of creating the HTML elements and updating the DOM.

populateCard = (geoResults) => { geoResults.map(geoResult => { // first create the input div container const addressCard = document.createElement('div'); // then create the input and label elements const input = document.createElement('input'); const label = document.createElement('label'); // then add materialize classes to the div and input addressCard.classList.add("card"); input.classList.add("with-gap"); // add attributes to them label.setAttribute("for", geoResult.place_id); label.innerHTML = geoResult.formatted_address; input.setAttribute("name", "address"); input.setAttribute("type", "radio"); input.setAttribute("value", geoResult.formatted_address); input.setAttribute("id", geoResult.place_id); addressCard.appendChild(input); addressCard.appendChild(label) return ( // append the created div to the locationsAvailable div locationsAvailable.appendChild(addressCard) ); }) }

In this function, we iterate through our results and create some HTML elements (div , input and a label), append the input and the label to the div and finally append the new div to a parent div (which is locationsAvailable). Once we get the result from our API call, our DOM will be created and displayed to the user.

Next, we update our getGeolocation function to call our populateCard function by replacing the last line of getGeolocation with this:

.then(data => populateCard(data.results));

...which means our updated function should look this:

getGeolocation = (lat, lng) => { const latlng = lat + "," + lng; fetch( `https://maps.googleapis.com/maps/api/geocode/json?latlng=${latlng}&key=${__KEY}` ) .then(res => res.json()) .then(data => populateCard(data.results)); }

At this point, everything should be working fine. Our user clicks a button, gets a location displayed on the map along with a list of addresses that match the current location.

Step 6: Listen for map events and repeat steps 4 and 5

If our user decides to move the map or marker, nothing happens to the UI — new addresses aren’t not displayed and everything remains static. We’ve got to fix this, so let's make our app dynamic by listening for map events. You can read all about Google Map Events here.

There are three events we want to listen for:

  1. drag: This is fired once the user starts dragging and continues to drag the map
  2. dragend: This is fired once the user stops dragging the map
  3. idle: This is fired once every event has been fired and the map is idle

Quick Question: Why are these events best suited for our app?

Quick Answer: The first two events will make sure that our map marker stays in the center of the map during the drag event while the idle event will make a geocoding request with the new coordinates.

To listen for these events we have to update the showMap function with the following:

Gmap.addListener('drag', function () { Gmarker.setPosition(this.getCenter()); // set marker position to map center }); Gmap.addListener('dragend', function () { Gmarker.setPosition(this.getCenter()); // set marker position to map center }); Gmap.addListener('idle', function () { Gmarker.setPosition(this.getCenter()); // set marker position to map center if (Gmarker.getPosition().lat() !== lat || Gmarker.getPosition().lng() !== lng) { setTimeout(() => { updatePosition(this.getCenter().lat(), this.getCenter().lng()); // update position display }, 2000); } });

As explained above, the first two event listeners simply ensure that the marker remains in the center of our map. Pay closer attention to the idle event listener because that is where the action is.

Once the idle event is fired, the marker goes to the center, then a check is done to find out if the current position of the marker is the same with the lat or lng values received by the displayLocation function. If it is not the same, then we call the updatePosition function after two seconds of idleness.

Having said that, we have to make a few updates to the showMap function. First, on the function header, we have to include more parameters and on the showMap function call. We need to add the new arguments there, too. Our showMap function should look like this:

showMap = (latlng, lat, lng) => { let mapOptions = { center: latlng, zoom: 17 }; Gmap = new google.maps.Map(mapArea, mapOptions); Gmap.addListener('drag', function () { Gmarker.setPosition(this.getCenter()); // set marker position to map center }); Gmap.addListener('dragend', function () { Gmarker.setPosition(this.getCenter()); // set marker position to map center }); Gmap.addListener('idle', function () { Gmarker.setPosition(this.getCenter()); // set marker position to map center if (Gmarker.getPosition().lat() !== lat || Gmarker.getPosition().lng() !== lng) { setTimeout(() => { updatePosition(this.getCenter().lat(), this.getCenter().lng()); // update position display }, 2000); } }); }

And our displayLocation function should look like this:

displayLocation = (position) => { const lat = position.coords.latitude; const lng = position.coords.longitude; const latlng = { lat, lng } showMap(latlng, lat, lng); //passed lat and lng as the new arguments to the function createMarker(latlng); mapArea.style.display = "block"; getGeolocation(lat, lng); }

Having listened for the map events, let's repeat Step 4 and Step 5.

We start by writing our updatePosition function. This function will perform only one action for now, which is to pass the new lat and lng values to the getGeolocation function:

updatePosition = (lat, lng) => { getGeolocation(lat, lng); }

After getting the new position and fetching addresses, our DOM should re-render for the user, right? Well, it doesn't. And to fix that, we create a function that will force the DOM to re-render:

// check if the container has a child node to force re-render of dom function removeAddressCards(){ if (locationsAvailable.hasChildNodes()) { while (locationsAvailable.firstChild) { locationsAvailable.removeChild(locationsAvailable.firstChild); } } }

It checks if the locationsAvailable div has any childNodes, and if it does, it deletes them before creating new address cards. The populateCard function is now updated to this:

populateCard = (geoResults) => { // check if a the container has a child node to force re-render of dom removeAddressCards(); geoResults.map(geoResult => { // first create the input div container const addressCard = document.createElement('div'); // then create the input and label elements const input = document.createElement('input'); const label = document.createElement('label'); // then add materialize classes to the div and input addressCard.classList.add("card"); input.classList.add("with-gap"); // add attributes to them label.setAttribute("for", geoResult.place_id); label.innerHTML = geoResult.formatted_address; input.setAttribute("name", "address"); input.setAttribute("type", "radio"); input.setAttribute("value", geoResult.formatted_address); input.setAttribute("id", geoResult.place_id); addressCard.appendChild(input); addressCard.appendChild(label); return ( locationsAvailable.appendChild(addressCard); ); }) }

We are done and are now able to fully get and display the user's address!

Step 7: Pre-fill the form with the address data the user selects

The final step is to fill the form with the address the user selects. We need to add a click event listener to the address card and pass the address as argument to the callback function.

Here’s how we add the event listener in the populateCard function:

input.addEventListener('click', () => inputClicked(geoResult));

You should note that the geoResult argument in the above callback is the selected address object from the results array. That said, update the populateCard function to accommodate our new line of code.

The inputClicked function uses a series of if statements to assign values to our form elements. so before working on it, let’s bring our form elements into the equation:

const inputAddress = document.getElementById('address'), inputLocality = document.getElementById('locality'), inputPostalCode = document.getElementById('postal_code'), inputLandmark = document.getElementById('landmark'), inputCity = document.getElementById('city'), inputState = document.getElementById('state');

Having done this, let us now work on pre-filling the form with the address_components in the inputClicked function.

inputClicked = result => { result.address_components.map(component => { const types = component.types if (types.includes('postal_code')) { inputPostalCode.value = component.long_name } if (types.includes('locality')) { inputLocality.value = component.long_name } if (types.includes('administrative_area_level_2')) { inputCity.value = component.long_name } if (types.includes('administrative_area_level_1')) { inputState.value = component.long_name } if (types.includes('point_of_interest')) { inputLandmark.value = component.long_name } }); inputAddress.value = result.formatted_address; // to avoid labels overlapping pre-filled input contents M.updateTextFields(); // removes the address cards from the UI removeAddressCards(); }

The above block of code iterates over the clicked (or selected) address component, checks the types of components and finally assigns them to the input fields if they match.

M.updateTextFields() function is from Materialize and it ensures that the label does not overlap with the input fields values and the removeAddressCards() function removes the address cards from the UI.

With this, we are done with our app and have saved our users lots of typing and headaches! Surely they will thank us for implementing such a hassle free solution.

This whole UX experiment can be seen as a shortcut that will help the user pre-fill the shipping address form. We should clearly state here that the returned addresses isn’t always 100% accurate. But that’s why we allow the address to be manually edited in the UI.

Demo!

See the Pen Prefill Address Form with Geolocation and Google Maps by CSS-Tricks (@css-tricks) on CodePen.

The post Let’s make a form that puts current location to use in a map! appeared first on CSS-Tricks.

Categories: Web Technologies

Using data in React with the Fetch API and axios

Fri, 08/03/2018 - 07:15

If you are new to React, and perhaps have only played with building to-do and counter apps, you may not yet have run across a need to pull in data for your app. There will likely come a time when you’ll need to do this, as React apps are most well suited for situations where you’re handling both data and state.

The first set of data you may need to handle might be hard-coded into your React application, like we did for this demo from our Error Boundary tutorial:

See the Pen error boundary 0 by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

What if you want to handle data from an API? That's the purpose of this tutorial. Specifically, we'll make use of the Fetch API and axios as examples for how to request and use data.

The Fetch API

The Fetch API provides an interface for fetching resources. We'll use it to fetch data from a third-party API and see how to use it when fetching data from an API built in-house.

Using Fetch with a third-party API

See the Pen React Fetch API Pen 1 by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

We will be fetching random users from JSONPlaceholder, a fake online REST API for testing. Let's start by creating our component and declaring some default state.

class App extends React.Component { state = { isLoading: true, users: [], error: null } render() { <React.Fragment> </React.Fragment> } }

There is bound to be a delay when data is being requested by the network. It could be a few seconds or maybe a few milliseconds. Either way, during this delay, it’s good practice to let users know that something is happening while the request is processing.

To do that we'll make use of isLoading to either display the loading message or the requested data. The data will be displayed when isLoading is false, else a loading message will be shown on the screen. So the render() method will look like this:

render() { const { isLoading, users, error } = this.state; return ( <React.Fragment> <h1>Random User</h1> // Display a message if we encounter an error {error ? <p>{error.message}</p> : null} // Here's our data check {!isLoading ? ( users.map(user => { const { username, name, email } = user; return ( <div key={username}> <p>Name: {name}</p> <p>Email Address: {email}</p> <hr /> </div> ); }) // If there is a delay in data, let's let the user know it's loading ) : ( <h3>Loading...</h3> )} </React.Fragment> ); }

The code is basically doing this:

  1. De-structures isLoading, users and error from the application state so we don't have to keep typing this.state.
  2. Prints a message if the application encounters an error establishing a connection
  3. Checks to see if data is loading
  4. If loading is not happening, then we must have the data, so we display it
  5. If loading is happening, then we must still be working on it and display "Loading..." while the app is working

For Steps 3-5 to work, we need to make the request to fetch data from an API. This is where the JSONplaceholder API will come in handy for our example.

fetchUsers() { // Where we're fetching data from fetch(`https://jsonplaceholder.typicode.com/users`) // We get the API response and receive data in JSON format... .then(response => response.json()) // ...then we update the users state .then(data => this.setState({ users: data, isLoading: false, }) ) // Catch any errors we hit and update the app .catch(error => this.setState({ error, isLoading: false })); }

We create a method called fetchUser() and use it to do exactly what you might think: request user data from the API endpoint and fetch it for our app. Fetch is a promise-based API which returns a response object. So, we make use of the json() method to get the response object which is stored in data and used to update the state of users in our application. We also need to change the state of isLoading to false so that our application knows that loading has completed and all is clear to render the data.

The fact that Fetch is promise-based means we can also catch errors using the .catch() method. Any error encountered is used a value to update our error's state. Handy!

The first time the application renders, the data won't have been received — it can take seconds. We want to trigger the method to fetch the users when the application state can be accessed for an update and the application re-rendered. React's componentDidMount() is the best place for this, so we'll place the fetchUsers() method in it.

componentDidMount() { this.fetchUsers(); } Using Fetch With Self-Owned API

So far, we’ve looked at how to put someone else’s data to use in an application. But what if we’re working with our own data in our own API? That’s what we’re going to cover right now.

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

I built an API which is available on GitHub. The JSON response you get has been placed on AWS — that’s what we will use for this tutorial.

As we did before, let's create our component and set up some default state.

class App extends React.Component { state = { isLoading: true, posts: [], error: null } render() { <React.Fragment> </React.Fragment> } }

Our method for looping through the data will be different from the one we used before but only because of the data’s structure, which is going to be different. You can see the difference between our data structure here and the one we obtained from JSONPlaceholder.

Here is how the render() method will look like for our API:

render() { const { isLoading, posts, error } = this.state; return ( <React.Fragment> <h1>React Fetch - Blog</h1> <hr /> {!isLoading ? Object.keys(posts).map(key => <Post key={key} body={posts[key]} />) : <h3>Loading...</h3>} </React.Fragment> ); }

Let's break down the logic

{ !isLoading ? Object.keys(posts).map(key => <Post key={key} body={posts[key]} />) : <h3>Loading...</h3> }

When isLoading is not true, we return an array, map through it and pass the information to the Post component as props. Otherwise, we display a "Loading..." message while the application is at work. Very similar to before.

The method to fetch posts will look like the one used in the first part.

fetchPosts() { // The API where we're fetching data from fetch(`https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/posts.json`) // We get a response and receive the data in JSON format... .then(response => response.json()) // ...then we update the state of our application .then( data => this.setState({ posts: data, isLoading: false, }) ) // If we catch errors instead of a response, let's update the app .catch(error => this.setState({ error, isLoading: false })); }

Now we can call the fetchPosts method inside a componentDidMount() method

componentDidMount() { this.fetchPosts(); }

In the Post component, we map through the props we received and render the title and content for each post:

const Post = ({ body }) => { return ( <div> {body.map(post => { const { _id, title, content } = post; return ( <div key={_id}> <h2>{title}</h2> <p>{content}</p> <hr /> </div> ); })} </div> ); };

There we have it! Now we know how to use the Fetch API to request data from different sources and put it to use in an application. High fives. ✋

axios

OK, so we’ve spent a good amount of time looking at the Fetch API and now we’re going to turn our attention to axios.

Like the Fetch API, axios is a way we can make a request for data to use in our application. Where axios shines is how it allows you to send an asynchronous request to REST endpoints. This comes in handy when working with the REST API in a React project, say a headless WordPress CMS.

There’s ongoing debate about whether Fetch is better than axios and vice versa. We’re not going to dive into that here because, well, you can pick the right tool for the right job. If you’re curious about the points from each side, you can read here and here.

Using axios with a third-party API

See the Pen React Axios 1 Pen by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

Like we did with the Fetch API, let's start by requesting data from an API. For this one, we’ll fetch random users from the Random User API.

First, we create the App component like we’ve done it each time before:

class App extends React.Component { state = { users: [], isLoading: true, errors: null }; render() { return ( <React.Fragment> </React.Fragment> ); } }

The idea is still the same: check to see if loading is in process and either render the data we get back or let the user know things are still loading.

To make the request to the API, we'll need to create a function. We’ll call the function getUsers(). Inside it, we'll make the request to the API using axios. Let's see how that looks like before explaining further.

getUsers() { // We're using axios instead of Fetch axios // The API we're requesting data from .get("https://randomuser.me/api/?results=5") // Once we get a response, we'll map the API endpoints to our props .then(response => response.data.results.map(user => ({ name: `${user.name.first} ${user.name.last}`, username: `${user.login.username}`, email: `${user.email}`, image: `${user.picture.thumbnail}` })) ) // Let's make sure to change the loading state to display the data .then(users => { this.setState({ users, isLoading: false }); }) // We can still use the `.catch()` method since axios is promise-based .catch(error => this.setState({ error, isLoading: false })); }

Quite different from the Fetch examples, right? The basic structure is actually pretty similar, but now we’re in the business of mapping data between endpoints.

The GET request is passed from the API URL as a parameter. The response we get from the API contains an object called data and that contains other objects. The information we want is available in data.results, which is an array of objects containing the data of individual users.

Here we go again with calling our method inside of the componentDidMount() method:

componentDidMount() { this.getUsers(); }

Alternatively, you can do this instead and basically combine these first two steps:

componentDidMount() { axios .get("https://randomuser.me/api/?results=5") .then(response => response.data.results.map(user => ({ name: `${user.name.first} ${user.name.last}`, username: `${user.login.username}`, email: `${user.email}`, image: `${user.picture.thumbnail}` })) ) .then(users => { this.setState({ users, isLoading: false }); }) .catch(error => this.setState({ error, isLoading: false })); }

If you are coding locally from your machine, you can temporarily edit the getUsers() function to look like this:

getUsers() { axios .get("https://randomuser.me/api/?results=5") .then(response => console.log(response)) .catch(error => this.setState({ error, isLoading: false })); }

Your console should get something similar to this:

We map through the results array to obtain the information we need for each user. The array of users is then used to set a new value for our users state. With that done, we can then change the value of isLoading.

By default, isLoading is set to true. When the state of users is updated, we want to change the value of isLoading to false since this is the cue our app is looking for to make the switch from "Loading..." to rendered data.

render() { const { isLoading, users } = this.state; return ( <React.Fragment> <h2>Random User</h2> <div> {!isLoading ? ( users.map(user => { const { username, name, email, image } = user; return ( <div key={username}> <p>{name}</p> <div> <img src={image} alt={name} /> </div> <p>{email}</p> <hr /> </div> ); }) ) : ( <p>Loading...</p> )} </div> </React.Fragment> ); }

If you log the users state to the console, you will see that it is an array of objects:

The empty array shows the value before the data was obtained. The returned data contains only the name, username, email address and image of individual users because those are the endpoints we mapped out. There is a lot more data available from the API, of course, but we’d have to add those to our getUsers method.

Using axios with your own API

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

You have seen how to use axios with a third-party API but we can look at what it’s like to request data from our own API, just like we did with the Fetch API. In fact, let’s use same JSON file we used for Fetch so we can see the difference between the two approaches.

Here is everything put together:

class App extends React.Component { // State will apply to the posts object which is set to loading by default state = { posts: [], isLoading: true, errors: null }; // Now we're going to make a request for data using axios getPosts() { axios // This is where the data is hosted .get("https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/posts.json") // Once we get a response and store data, let's change the loading state .then(response => { this.setState({ posts: response.data.posts, isLoading: false }); }) // If we catch any errors connecting, let's update accordingly .catch(error => this.setState({ error, isLoading: false })); } // Let's our app know we're ready to render the data componentDidMount() { this.getPosts(); } // Putting that data to use render() { const { isLoading, posts } = this.state; return ( <React.Fragment> <h2>Random Post</h2> <div> {!isLoading ? ( posts.map(post => { const { _id, title, content } = post; return ( <div key={_id}> <h2>{title}</h2> <p>{content}</p> <hr /> </div> ); }) ) : ( <p>Loading...</p> )} </div> </React.Fragment> ); } }

The main difference between this method and using axios to fetch from a third-party is how the data is formatted. We’re getting straight-up JSON this way rather than mapping endpoints.

The posts data we get from the API is used to update the value of the component's posts state. With this, we can map through the array of posts in render(). We then obtain the id, title and content of each post using ES6 de-structuring, which is then rendered to the user.

Like we did before, what is displayed depends on the value of isLoading. When we set a new state for posts using the data obtained from the API, we had to set a new state for isLoading, too. Then we can finally let the user know data is loading or render the data we’ve received.

async and await

Another thing the promise-based nate of axios allows us to do is take advantage of is async and await . Using this, the getPosts() function will look like this.

async getPosts() { const response = await axios.get("https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/posts.json"); try { this.setState({ posts: response.data.posts, isLoading: false }); } catch (error) { this.setState({ error, isLoading: false }); } } Base instance

With axios, it’s possible to create a base instance where we drop in the URL for our API like so:

const api = axios.create({ baseURL: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/posts.json" });

...then make use of it like this:

async getPosts() { const response = await api.get(); try { this.setState({ posts: response.data.posts, isLoading: false }); } catch (error) { this.setState({ error, isLoading: false }); } }

Simply a nice way of abstracting the API URL.

Now, data all the things!

As you build React applications, you will run into lots of scenarios where you want to handle data from an API. Hopefully you know feel armed and ready to roll with data from a variety of sources with options for how to request it.

Want to play with more data? Sarah recently wrote up the steps for creating your own serverless API from a list of public APIs.

The post Using data in React with the Fetch API and axios appeared first on CSS-Tricks.

Categories: Web Technologies

VS Code extensions for the discerning developer palate

Thu, 08/02/2018 - 07:10

I am a VS Code extension snob. I like to hunt down the most obscure extensions for VS Code — the ones that nobody knows about — and impress people at parties with my knowledge of finely aged and little-known VS Code capabilities… then watch as they look around desperately for someone else to talk to. It’s like the “Sideways” of VS Code.

In my endless pursuit of the perfect VS Code setup, I reached out to my colleagues here on the Azure team and asked them to share their favorite extension in their own words. So clear your pallet and breathe in the aromatic flavors of productivity; I am your VS Code Extension Sommelier.

Christina Warren - Settings Sync

I cannot live without this extension. If you use multiple machines (especially on multiple platforms, where a sym-linked Dropbox folder won’t really work), this extension is for you. It syncs your extensions, settings file, keybinding file, launch file, snippets folder, extension settings, and workspaces folder. This means that when you login to a new machine, you can quickly get back to work with your own settings and workflow tools in just a few minutes.

&#x1f449; Get Settings SyncExtension

Shayne Boyer - Paste JSON as Code

Consuming an endpoint that produces JSON is like breathing, but no one wants to choke on the hand cranking of an object by looking back and forth between JSON and the target language. This is a long loved feature in Visual Studio for .NET developers, but now you too can copy the JSON and paste that class into the editor as your target language and save a ton of time. Currently supports C#, Go, C++, Java, TypeScript, Swift, Elm, and JSON Schema.

&#x1f449; Get Paste JSON as Code Extension

Jeremy Likness - Spell Right

I find myself authoring blog posts, articles, and documentation almost every day. After embracing the power of Markdown (it is, after all, what is used to drive our own https://docs.com), I began writing my content in Visual Studio Code. It has a built-in preview window so I can edit the Markdown source and see the rendered result side-by-side. As much as I’ve written over the years, mastering the fine art of spelling still eludes me. Maybe it’s because I’m lazy, and this extension doesn’t help at all. With Spell Right, I get to reunite with my same favorite red squiggly lines that I first met in Word. It does a great job of catching spelling mistakes in real time, then illuminates my mistakes with a handy light bulb with alternative suggestions that give me single-click corrections. It enables me to be highly productive and look like I know what I’m doing. I recommend this for anyone who uses Code to write.

&#x1f449; Get Spell Right Extension

Aaron Wislang - Go

I live in VS Code and use it for everything from code and content to its integrated terminal. This extension enables first-class support for IntelliSense, testing, refactoring and more, making Code the best place to me to write Go. And it turns out I’m not the only one who thinks so; it helped to make Code the most popular editor amongst Gophers, just ahead of vim-go, as of the Go 2017 Survey!

&#x1f449; Get Go Extension

Cecil Phillip - C# Extensions

This extension was created by one of our community members, and it’s a great companion to the official C# extension from Microsoft. The “New Class|Interface” actions make it easy to add new types, and takes some of the hassle out of fixing up the namespaces. It also comes with a few interesting refactorings like "Initialize fields from constructors,” which I use pretty often. Whenever I’m teaching a C# course, I always have my students that are using Visual Studio Code install this extension.

&#x1f449; Get C# Extension

Brian Clark - VS Live Share

Pair programming just got way better. Gone are the days where I need to set up screen sharing to review code with coworkers. Instead I fire up a live share session, invite the other party and we can all view and edit code directly from our editors. I’ve used it in a situations where I review someone else’s C# code on my machine while it runs on THEIR machine! I didn’t have anything installed on my Mac for C# and yet I could debug their code!

&#x1f449; Get VS Live Share Extension

David Smith - Rewrap

I write a lot of text, and sometimes I just want (or need) to write in a plain-text environment. Easy reflowing of text is essential. (Surprised this isn’t built in, in fact.)

&#x1f449; Get Rewrap Extension

Anthony Chu - Git Lens

At a glance, GitLens shows me contextual information from Git about the line of code and the file I'm working in. It adds some useful commands to view history and diffs, search commits, and browse local and remote branches... all without leaving VS Code.

&#x1f449; Get Git Lens Extension

Asim Hussain - AsciiDoc

I used to write with Markdown, we all make mistakes. The solution to my Markdown mistake is AsciiDoc, especially if you write a lot of code snippets as I do. Out of the box it let’s you add line numbers, annotate and highlight lines and provides an incredible amount of customization. Plus, as a bonus it also can convert your blog posts into PDFs, ePubs, Mobis which is perfect for ebooks.

Once you start with AsciiDoc it’s hard to go back to Markdown and this plugin lets you preview your AsciiDoc right inside the editor.

&#x1f449; Get AsciiDoctor Extension

Seth Juarez) - VS Code Tools For AI

With Visual Studio Code Tools for AI, I can finally use machines I need but might never have access to in order to build the next Skynet — all within the comfort of my favorite lightweight editor. We live in amazing times, friends...

&#x1f449; Get VS Code Tools For AI Extension

Alena Hall - Ionide

Ionide is an awesome Visual Studio Code extension for cross-platform F# development. It’s open-source and it was created by the F# Community. I use it every day on multiple machines I have. It runs perfectly on both my Mac and Linux machines. Ionide conveniently integrates with Paket, Project Scaffold, and you can experiment away as much as you want in F# Interactive!

&#x1f449; Get Ionide Extension

Matt Soucoup - VSCodeVim

There’s an old joke that goes: “How do you know if a developer uses vim? They’ll tell you.” Well, I use vim! But… I want more. I want to tell everybody I use vim and I want to use all the great features and extensions that VS Code offers. (I mean, look at the list here!) So that’s where VSCodeVim saves the day for me. It puts a full-featured vim emulator into my VS Code editor, letting me edit files super fast by typing esoteric commands like h, 10 k, i, and u (lots and lots of u) and I still get to use all the awesome features of VS Code.

&#x1f449; Get VSCodeVim Extension

John Papa - Docker

If you like it put a container on it. Yeah, containers are the latest craze, but in a constantly evolving containerization world, it’s nice to have great tooling make it easy to use containers. Enter the Docker extension for VS Code. It handles the complete container development and deployment lifecycle! Start by generating docker files to your project, create an image, run it, and even push it to a container registry. If you’re like me, you like to make sure you still have complete control over your code and your app, even when they are inside of containers. Accessing the files, showing logs, and debugging the running container are all essential tools for development. This extension puts all of this within your reach without having to learn the docker command line!

&#x1f449; Get Docker Extension

Suz Hinton - Arduino

My favorite extension for VS Code is Arduino. I'm pretty sure anyone who knows me wouldn't be surprised about that. Traditionally, developing programs for Arduino-compatible micro-controller boards has been done in the Arduino IDE. It's a powerful program which smooths over the compilation and uploading experiences for dozens of boards. It is, however, not a full code IDE. It's missing some of the features you love, such as autocomplete, a file tree, and fine-grained tuning of the editor itself.

The good news is that the Arduino extension allows you to finally develop freely for all of your favorite micro-controller boards without leaving VS Code!

Here are some of my favorite things about the extension:

  1. It's open source! So reporting bugs and contributing improvements is a straightforward experience.
  2. The Command Palette integration is so handy. Compile and upload your code to an Arduino with one simple shortcut.
  3. Access all the great tools from the Arduino IDE right in VS Code. Yes, that even means board / library management and the serial monitor!
  4. Scaffolding brand new Arduino projects is a command away. No more copy + pasting older project directories to get set up.

&#x1f449; Get Arduino Extension

Burke Holland - Azure Functions

Serverless is like Hansel — so hot right now. But Serverless shouldn’t be a black box. The Azure Functions extensions for VS Code puts Serverless right inside of the editor. I love it because it lets me create new Serverless projects, new functions for all of the available trigger types (http, timer, blob storage, etc.), and most importantly, I can run them locally and debug them. Not that I would ever need to debug. My code is always perfect.

&#x1f449; Get Azure Functions Extension

The post VS Code extensions for the discerning developer palate appeared first on CSS-Tricks.

Categories: Web Technologies

​Experience a Simpler Cloud Computing Platform with DigitalOcean

Thu, 08/02/2018 - 07:07

(This is a sponsored post.)

From deploying static sites and blogging platforms to managing multiple client websites, DigitalOcean provides a flexible platform for developers and their teams to deliver an unparalleled end-user experience with a lightning-fast network, pre-configured applications, and a 99.99% uptime SLA. Simply let us know your needs and our solutions engineers will provide the best options available.

Direct Link to ArticlePermalink

The post ​Experience a Simpler Cloud Computing Platform with DigitalOcean appeared first on CSS-Tricks.

Categories: Web Technologies

On Switching Code Editors

Wed, 08/01/2018 - 07:05

I'm sure a lot of you are like me and have switched code editors a number of times. I think my first major editor was Coda. Then I moved to TextMate when I started working primarily on local. Then Sublime Text. And, most recently, VS Code. I bet your journey was different. I know lots of folks that quite love Atom, Brackets, WebStorm, and even BBedit. You do you!

For me, that's four changes in a dozen years, or a change every three years. Moving isn't something I do quickly. Here's a collection of thoughts around the idea of changing editors.

When moving, I have to take time to make sure it works pretty much like the old one.

Otherwise, I'll just end up disliking it to the point that I switch back a day or two later. It's happened to me every time I switch. I have little false-starts after a switch where I go back to the old editor because something bugged me too much or it affected my productivity and I gave up. (Now that I know I do this, I don't let a single false-start make me feel like the editor I'm trying is never a possibility.)

My latest switch was from Sublime Text to VS Code. I'd become become very used to the key bindings (e.g. CMD+Shift+d to duplicate a line) in Sublime Text, so thankfully VS Code has that covered.

I was amazed to find even my VIM friends happy and comfortable in VS Code. (Fun fact: we have key bindings choices in CodePen, too.)

Nothing can be too obnoxious.

In one of my first attempts at switching, I found the UI in VS code to be too cluttered and the find-in-project feature to be a little slow and awkward. Those things bugged me to the point they caused false-starts and I went back to Sublime Text.

On this last switch attempt (my 3rd or 4th maybe?) I finally have a theme I quite like (customized a smidge), found some settings to clean up the UI (I removed the whitespace indicators which were overwhelming to me, and overrode that intense blue footer with something more chill).

In working with find-in-project a bit more, I've grown to get used to it. I finally might even like it more than Sublime, as the sidebar approach is more consistent than opening a new tab of results. I find the jump-to-line feature works more consistently and search feels more the first-class citizen it should be.

Another factor would be Emmet. I'm quite sure that I'd be too annoyed writing HTML and CSS in an editor without Emmet, and I'd just give up and use something else that had it. Emmet isn't even an extension in VS Code, it's built in.

I'm cool with making small changes after a successful switch.

Once I've actually done it, and made the switch to full-time use, then I can make some changes. Maybe I'll learn some new key commands. Maybe I'll add an extension that adds functionality I've never had before. Maybe the editor affects some workflow thing in a way I'm now willing to try.

The new editor better have some killer feature that incentivizes me to switch.

If it's exactly the same, why bother?

The new editor needs to be faster (or feel just as fast). Or should look better. Or it should have some awesome package that is only available on it. Ideally all of that.

In this recent switch for me, it was GitLens.

How cool is that? It ought to have a plugin architecture.

Meaning that anyone can write code to extend the editor. I'm fairly certain that having a plugin architecture (plus a healthy amount of community momentum) is what is key to any editor's success. Sublime's package manager, and the subsequent built-in packages feature of VS Code, seem crucial. Not only for functionality but even just for the look of the editor. I wouldn't use an editor with a look I hate, but I'd be tempted to if the functionality was awesome. That's a non-issue with a plugin-based editor. Open source seems smart as well.

Careful for those GOTCHAs.

One of those was spell-checking for me. In Sublime Text, it was an option under the View menu. I had it checked all the time, and it spell-checked all the time.

This is not a thing in VS Code. That was dangerous after switching because, who knows, I may have been committing typos all over the place. Fortunately, this extension seems to be doing the trick. Thank jeepers for extensions!

Your thoughts!

I thought it might be interesting to ask what y'all think about when switching code editors. There were lots of responses. I picked out as many as I could here and focused on one thing that you mentioned.

The post On Switching Code Editors appeared first on CSS-Tricks.

Categories: Web Technologies

Getting to Know a Legacy Codebase

Tue, 07/31/2018 - 13:44

Harry Roberts talks about some methods for getting comfy with a new ("specifically CSS") code base. Harry's done this a lot as someone who parachutes into new code bases regularly as a consultant. But I think this is also quite interesting for people starting a new job. So much web development work is working on existing sites, not green fielding new ones.

Direct Link to ArticlePermalink

The post Getting to Know a Legacy Codebase appeared first on CSS-Tricks.

Categories: Web Technologies

Framer X

Tue, 07/31/2018 - 13:35

Framer X is a brand new app that’s about to be released and this quick demo reel takes us on a tour through some of the changes to the previous app—it all looks super exciting.

As a designer, I’m most interested in the prototyping tools and being able to quickly explore complex scene transitions between one state and another. But as a developer, I’m interested in how it all ties into React. The website describes it like so:

Use actual React in your projects to create interactive components from scratch. Want more control? Create custom UI in the properties panel for your components.

I can imagine a wonderful near-future where it’s possible to tie Framer X into a design system so that folks on a team can use all the real life React components without having to worry if they’re up-to-date or not.

Direct Link to ArticlePermalink

The post Framer X appeared first on CSS-Tricks.

Categories: Web Technologies

The trick to viewport units on mobile

Tue, 07/31/2018 - 06:58

Viewport units have always been controversial and some of that is because of how mobile browsers have made things more complicated by having their own opinions about how to implement them.

Case in point: should the scrollbar be taken into account for the vw unit? What about a site's navigation or page controls — should those count in the calculation? Then there are physical attributes of the devices themselves (hello, notch!) that can't be overlooked.

First, a little context

The spec is pretty vague about how viewport units should be calculated. With mobile devices, we're often concerned with the vertical height, so let's look specifically at viewport height (vh):

vh unit
Equal to 1% of the height of the initial containing block.

So yeah, no clear guidance there when it comes to handling device and browser-specific differentiations.

vh was initially calculated by the current viewport of your browser. If you opened your browser and started to load a website, 1vh was equal to 1% of your screen height, minus the browser interface.

But! If you start scrolling, it's a different story. Once you get past a piece of the browser interface, like the address bar, the vh value would update and the result was an awkward jump in the content.

Safari for iOS was one of the first mobile browsers to update their implementation by choosing to define a fixed value for the vh based on the maximum height of the screen. By doing so, the user would not experience jumps on the page once the address bar went out of view. Chrome's mobile browser followed suit around a year ago.

As of this writing, there is a ticket to address this in Firefox Android.

While using a fixed value is nice, it also means that you cannot have a full-height element if the address bar is in view. The bottom of your element will be cropped.

An element gets cropped at the bottom when the address bar is in view (left) but what we want is the full thing (right). CSS Custom Properties: The trick to correct sizing

The idea struck me that CSS Custom Properties and a few lines of JavaScript might be the perfect way to get the consistent and correct sizing I needed.

In JavaScript, you can always get the value of the current viewport by using the global variable window.innerHeight. This value takes the browser's interface into account and is updated when its visibility changes. The trick is to store the viewport value in a CSS variable and apply that to the element instead of the vh unit.

Let's say our CSS custom variable is --vh for this example. That means we will want to apply it in our CSS like this:

.my-element { height: 100vh; /* Fallback for browsers that do not support Custom Properties */ height: calc(var(--vh, 1vh) * 100); }

OK, that sets us up. Now let's get the inner height of the viewport in JavaScript:

// First we get the viewport height and we multiple it by 1% to get a value for a vh unit let vh = window.innerHeight * 0.01; // Then we set the value in the --vh custom property to the root of the document document.documentElement.style.setProperty('--vh', `${vh}px`);

So, we told JS to grab the height of the viewport and then drilled it down into 1/100th of that total so we have a value to assign as our viewport height unit value. Then we politely asked JS to create the CSS variable (--vh) at the :root.

As a result, we can now use --vh as our height value like we would any other vh unit, multiply it by 100 and we have the full height we want.

See the Pen Viewport Height on Mobile (no resize on update) by CSS-Tricks (@css-tricks) on CodePen.

Whoa, there! One more little detail.

While our work might look done at this point, those of you with an astute eye for detail may have caught that the JavaScript fires but never updates the size of our element when the viewport's height changes. Go ahead and try resizing the demo above.

We can update the value of --vh by listening to the window resize event. This is handy in case the user rotates the device screen, like from landscape to portrait, or the navigation moves out of view on scroll.

// We listen to the resize event window.addEventListener('resize', () => { // We execute the same script as before let vh = window.innerHeight * 0.01; document.documentElement.style.setProperty('--vh', `${vh}px`); });

⚠️ Updating the value of --vh will trigger a repaint of the page and the user may experience a jump as a result. Because of this, I’m not advising that this trick should be used for every project or to replace all usage of the vh unit but only when you may need your users to have an exact viewport unit value.

Also, you may want to implement a debounce method for the resize event to avoid triggering to many events while the user is resizing their browser's window. You can learn more about it with this article: Debouncing and Throttling Explained Through Examples

See the Pen Correct Viewport Height on Mobile by CSS-Tricks (@css-tricks) on CodePen.

You can now resize the demo above and notice that the CSS variable is updated accordingly.

While I recently used this technique on a project and it really helped, you should always think twice when replacing the browser’s default behaviors. (For example, this comes up a lot with ::focus.) Also, browsers tend to update very fast these days, so beware that today's solution may not work tomorrow.

In the meantime, I hope this article helps! &#x1f44b;

The post The trick to viewport units on mobile appeared first on CSS-Tricks.

Categories: Web Technologies

abc to SVG

Mon, 07/30/2018 - 14:56

Here's a little example that Jeremy Keith used to use in his talks. It's stuck with me as one of the coolest examples of progressive enhancement and Technology Being CoolTM around.

There is this musical notation format called abc. They don't capitalize it. Kinda like npm, which I guess makes sense as it isn't an acronym. But it is the name of something so it's super awkward. Ughakdhk. Anyway.

The format itself is really cool. It's super simple and text-based. Here's the example from their homepage:

X:1 T:Speed the Plough M:4/4 C:Trad. K:G |:GABc dedB|dedB dedB|c2ec B2dB|c2A2 A2BA| GABc dedB|dedB dedB|c2ec B2dB|A2F2 G4:| |:g2gf gdBd|g2f2 e2d2|c2ec B2dB|c2A2 A2df| g2gf g2Bd|g2f2 e2d2|c2ec B2dB|A2F2 G4:|

A little like YAML, I suppose. That's the music for that whole song. It's not loaded with all the possibilities of sheet music; it's just notes and timing. Enough to communicate a folk/traditional song, which is primarily what it's used for.

You could probably get used to reading it as-is, but I don't think that's what it's really for. My guess is that the format is more about being:

  1. Standardized (it is, and there are 570,000 tunes in it, so I guess that worked)
  2. Text (I bet that entire database of songs is surprisingly tiny)
  3. Ready to be converted into whatever

The conversion part being the fun part! You can see it happening right on the homepage for the abc format.

Lonesome Moonlight Waltz, stored in abc format on the site, is being displayed as sheet music in PNG format and being converted into a an audio format playable on the web. Basically MIDI barfed out as MP3.

PNG is fine, but wouldn't that be so much nicer as SVG? Of course it would.

All of this is relevant to Jeremy because he has a website called The Session, which is dedicated to Irish music. He stores tunes on the site, and thus appropriately uses the abc format. On The Session, you can click a Sheet Music button and it will convert abc into SVG and display that.

That sheet music looks great in the crisp SVG format.

The SVG conversion is made possible entirely in JavaScript by an open source library. That's the progressive enhancement part. Store and ship the basic format, and let the browser enhance the experience, if it can (it can).

That's all. Just cool.

The JavaScript library that The Session actually uses is abcjs by Paul Rosen and Gregory Dyke. The other one linked to above is Jef Moine's similar abc2svg.

The post abc to SVG appeared first on CSS-Tricks.

Categories: Web Technologies

Pages