Tech News

A Handy Little System for Animated Entrances in CSS

Css Tricks - Fri, 11/26/2021 - 6:12am

I love little touches that make a website feel like more than just a static document. What if web content wouldn’t just “appear” when a page loaded, but instead popped, slid, faded, or spun into place? It might be a stretch to say that movements like this are always useful, though in some cases they can draw attention to certain elements, reinforce which elements are distinct from one another, or even indicate a changed state. So, they’re not totally useless, either.

So, I put together a set of CSS utilities for animating elements as they enter into view. And, yes, this pure CSS. It not only has a nice variety of animations and variations, but supports staggering those animations as well, almost like a way of creating scenes.

You know, stuff like this:

Which is really just a fancier version of this:

CodePen Embed Fallback

We’ll go over the foundation I used to create the animations first, then get into the little flourishes I added, how to stagger animations, then how to apply them to HTML elements before we also take a look at how to do all of this while respecting a user’s reduced motion preferences.

The basics

The core idea involves adding a simple CSS @keyframes animation that’s applied to anything we want to animate on page load. Let’s make it so that an element fades in, going from opacity: 0 to opacity: 1 in a half second:

.animate { animation-duration: 0.5s; animation-name: animate-fade; animation-delay: 0.5s; animation-fill-mode: backwards; } @keyframes animate-fade { 0% { opacity: 0; } 100% { opacity: 1; } }

Notice, too, that there’s an animation-delay of a half second in there, allowing the rest of the site a little time to load first. The animation-fill-mode: backwards is there to make sure that our initial animation state is active on page load. Without this, our animated element pops into view before we want it to.

CodePen Embed Fallback

If we’re lazy, we can call it a day and just go with this. But, CSS-Tricks readers aren’t lazy, of course, so let’s look at how we can make this sort of thing even better with a system.

Fancier animations

It’s much more fun to have a variety of animations to work with than just one or two. We don’t even need to create a bunch of new @keyframes to make more animations. It’s simple enough to create new classes where all we change is which frames the animation uses while keeping all the timing the same.

There’s nearly an infinite number of CSS animations out there. (See animate.style for a huge collection.) CSS filters, like blur(), brightness() and saturate() and of course CSS transforms can also be used to create even more variations.

But for now, let’s start with a new animation class that uses a CSS transform to make an element “pop” into place.

.animate.pop { animation-duration: 0.5s; animation-name: animate-pop; animation-timing-function: cubic-bezier(.26, .53, .74, 1.48); } @keyframes animate-pop { 0% { opacity: 0; transform: scale(0.5, 0.5); } 100% { opacity: 1; transform: scale(1, 1); } }

I threw in a little cubic-bezier() timing curve, courtesy of Lea Verou’s indispensable cubic-bezier.com for a springy bounce.

CodePen Embed Fallback Adding delays

We can do better! For example, we can animate elements so that they enter at different times. This creates a stagger that makes for complex-looking motion without a complex amount of code.

This animation on three page elements using a CSS filter, CSS transform, and staggered by about a tenth of a second each, feels really nice:

CodePen Embed Fallback

All we did there was create a new class for each element that spaces when the elements start animating, using animation-delay values that are just a tenth of a second apart.

.delay-1 { animation-delay: 0.6s; } .delay-2 { animation-delay: 0.7s; } .delay-3 { animation-delay: 0.8s; }

Everything else is exactly the same. And remember that our base delay is 0.5s, so these helper classes count up from there.

Respecting accessibility preferences

Let’s be good web citizens and remove our animations for users who have enabled their reduced motion preference setting:

@media screen and (prefers-reduced-motion: reduce) { .animate { animation: none !important; } }

This way, the animation never loads and elements enter into view like normal. It’s here, though, that is worth a reminder that “reduced” motion doesn’t always mean “remove” motion.

Applying animations to HTML elements

So far, we’ve looked at a base animation as well as a slightly fancier one that we were able to make even fancier with staggered animation delays that are contained in new classes. We also saw how we can respect user motion preferences at the same time.

Even though there are live demos that show off the concepts, we haven’t actually walked though how to apply our work to HTML. And what’s cool is that we can use this on just about any element, whether its a div, span, article, header, section, table, form… you get the idea.

Here’s what we’re going to do. We want to use our animation system on three HTML elements where each element gets three classes. We could hard-code all the animation code to the element itself, but splitting it up gives us a little animation system we can reuse.

  • .animate: This is the base class that contains our core animation declaration and timing.
  • The animation type: We’ll use our “pop” animation from before, but we could use the one that fades in as well. This class is technically optional but is a good way to apply distinct movements.
  • .delay-<number>: As we saw earlier, we can create distinct classes that are used to stagger when the animation starts on each element, making for a neat effect. This class is also optional.

So our animated elements might now look like:

<h2 class="animate pop">One!</h2> <h2 class="animate pop delay-1">Two!</h2> <h2 class="animate pop delay-2">Three!</h2>

Let’s count them in!

CodePen Embed Fallback Conclusion

Check that out: we went from a seemingly basic set of @keyframes and turned it into a full-fledged system for applying interesting animations for elements entering into view.

This is ridiculously fun, of course. But the big takeaway for me is how the examples we looked at form a complete system that can be used to create a baseline, different types of animations, staggered delays, and an approach for respecting user motion preferences. These, to me, are all the ingredients for a flexible system that easy to use, while giving us a lot with a little and without a bunch of extra cruft.

What we covered could indeed be a full animation library. But, of course, I did’t stop there and have my entire CSS file of animations in all its glory for you. There are several more types of animations in there, including 15 classes of different delays that can be used for staggering things. I’ve been using these on my own projects, but it’s still an early draft and I love feedback on it—so please enjoy and let me know what you think in the comments!

/* ========================================================================== Animation System by Neale Van Fleet from Rogue Amoeba ========================================================================== */ .animate { animation-duration: 0.75s; animation-delay: 0.5s; animation-name: animate-fade; animation-timing-function: cubic-bezier(.26, .53, .74, 1.48); animation-fill-mode: backwards; } /* Fade In */ .animate.fade { animation-name: animate-fade; animation-timing-function: ease; } @keyframes animate-fade { 0% { opacity: 0; } 100% { opacity: 1; } } /* Pop In */ .animate.pop { animation-name: animate-pop; } @keyframes animate-pop { 0% { opacity: 0; transform: scale(0.5, 0.5); } 100% { opacity: 1; transform: scale(1, 1); } } /* Blur In */ .animate.blur { animation-name: animate-blur; animation-timing-function: ease; } @keyframes animate-blur { 0% { opacity: 0; filter: blur(15px); } 100% { opacity: 1; filter: blur(0px); } } /* Glow In */ .animate.glow { animation-name: animate-glow; animation-timing-function: ease; } @keyframes animate-glow { 0% { opacity: 0; filter: brightness(3) saturate(3); transform: scale(0.8, 0.8); } 100% { opacity: 1; filter: brightness(1) saturate(1); transform: scale(1, 1); } } /* Grow In */ .animate.grow { animation-name: animate-grow; } @keyframes animate-grow { 0% { opacity: 0; transform: scale(1, 0); visibility: hidden; } 100% { opacity: 1; transform: scale(1, 1); } } /* Splat In */ .animate.splat { animation-name: animate-splat; } @keyframes animate-splat { 0% { opacity: 0; transform: scale(0, 0) rotate(20deg) translate(0, -30px); } 70% { opacity: 1; transform: scale(1.1, 1.1) rotate(15deg)); } 85% { opacity: 1; transform: scale(1.1, 1.1) rotate(15deg) translate(0, -10px); } 100% { opacity: 1; transform: scale(1, 1) rotate(0) translate(0, 0); } } /* Roll In */ .animate.roll { animation-name: animate-roll; } @keyframes animate-roll { 0% { opacity: 0; transform: scale(0, 0) rotate(360deg); } 100% { opacity: 1; transform: scale(1, 1) rotate(0deg); } } /* Flip In */ .animate.flip { animation-name: animate-flip; transform-style: preserve-3d; perspective: 1000px; } @keyframes animate-flip { 0% { opacity: 0; transform: rotateX(-120deg) scale(0.9, 0.9); } 100% { opacity: 1; transform: rotateX(0deg) scale(1, 1); } } /* Spin In */ .animate.spin { animation-name: animate-spin; transform-style: preserve-3d; perspective: 1000px; } @keyframes animate-spin { 0% { opacity: 0; transform: rotateY(-120deg) scale(0.9, .9); } 100% { opacity: 1; transform: rotateY(0deg) scale(1, 1); } } /* Slide In */ .animate.slide { animation-name: animate-slide; } @keyframes animate-slide { 0% { opacity: 0; transform: translate(0, 20px); } 100% { opacity: 1; transform: translate(0, 0); } } /* Drop In */ .animate.drop { animation-name: animate-drop; animation-timing-function: cubic-bezier(.77, .14, .91, 1.25); } @keyframes animate-drop { 0% { opacity: 0; transform: translate(0,-300px) scale(0.9, 1.1); } 95% { opacity: 1; transform: translate(0, 0) scale(0.9, 1.1); } 96% { opacity: 1; transform: translate(10px, 0) scale(1.2, 0.9); } 97% { opacity: 1; transform: translate(-10px, 0) scale(1.2, 0.9); } 98% { opacity: 1; transform: translate(5px, 0) scale(1.1, 0.9); } 99% { opacity: 1; transform: translate(-5px, 0) scale(1.1, 0.9); } 100% { opacity: 1; transform: translate(0, 0) scale(1, 1); } } /* Animation Delays */ .delay-1 { animation-delay: 0.6s; } .delay-2 { animation-delay: 0.7s; } .delay-3 { animation-delay: 0.8s; } .delay-4 { animation-delay: 0.9s; } .delay-5 { animation-delay: 1s; } .delay-6 { animation-delay: 1.1s; } .delay-7 { animation-delay: 1.2s; } .delay-8 { animation-delay: 1.3s; } .delay-9 { animation-delay: 1.4s; } .delay-10 { animation-delay: 1.5s; } .delay-11 { animation-delay: 1.6s; } .delay-12 { animation-delay: 1.7s; } .delay-13 { animation-delay: 1.8s; } .delay-14 { animation-delay: 1.9s; } .delay-15 { animation-delay: 2s; } @media screen and (prefers-reduced-motion: reduce) { .animate { animation: none !important; } }

The post A Handy Little System for Animated Entrances in CSS appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Why Bold not Black Friday?

Typography - Thu, 11/25/2021 - 10:55pm

Read the book, Typographic Firsts

We would like to give thanks this Thanksgiving to the brilliant designers who give shape to our words, so we’re taking a bold step that no other font distributor has taken before: All income goes to the foundries and designers.

The post Why Bold not Black Friday? appeared first on I Love Typography.

Welcome to the family!

Typography - Wed, 11/24/2021 - 3:00am

Read the book, Typographic Firsts

When we launched our store back in June, we did so with 40 indie foundries. Today, just five months on, we’re thrilled to announce a second cohort of 21 foundries and more than 200 font families. Joining us are established classics to new and cutting-edge foundries from around the world.

The post Welcome to the family! appeared first on I Love Typography.

Discounts on UX Courses and Bootcamps for Black Friday 2021!

Usability Geek - Wed, 11/24/2021 - 12:00am
UX designers rejoice! This Black Friday, our friends over at the Interaction Design Foundation (IxDF) have a gift for you: 25% off UX design courses on new memberships and a $200 discount on UX...
Categories: Web Standards

Which SVG technique performs best for way too many icons?

Css Tricks - Tue, 11/23/2021 - 9:21am

Tyler Sticka digs in here in the best possible way: by making a test page and literally measuring performance. Maybe 1,000 icons is a little bit of an edge case, but hey, 250 rows of data with four icons in each gets you there. Tyler covers the nuances carefully in the post. The different techniques tested: inline <svg>, same-document sprite <symbol>s, external-document sprite <symbol>s, <img> with an external source, <img> with a data URL, <img> with a filter, <div> with a background-image of an external source, <div> with a background-image of a data URL, and a <div> with a mask. Phew! That’s a lot — and they are all useful techniques in their own right.

Which technique won? Inline <svg>, unless the SVGs are rather complex, then <img> is better. That’s what I would have put my money on. I’ve been on that train for a while now.

To Shared LinkPermalink on CSS-Tricks

The post Which SVG technique performs best for way too many icons? appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

On User Tracking and Industry Standards on Privacy

Css Tricks - Mon, 11/22/2021 - 11:00am

Inspired by Eva PenzeyMoog’s new book, Jeremy highlights the widespread user tracking situation in this industry:

There was a line that really stood out to me:

The idea that it’s alright to do whatever unethical thing is currently the industry norm is widespread in tech, and dangerous.

It stood out to me because I had been thinking about certain practices that are widespread, accepted, and yet strike me as deeply problematic. These practices involve tracking users.

And ends with zero minced words:

We should stop stop tracking users because it’s wrong.

I take notice here, as I’m largely complicit when it comes to some degree of user tracking. For example, I have Google Analytics on this site. And pertinent to the topic: I have for well over a decade. I mention that not to prove that it’s OK, but almost to question it more, because it’s such a widespread long-term industry standard that is rarely questioned.

Because I have Google Analytics¹ on this site, I can take zoomed-out looks at the long-term traffic on this site. Here’s a 10-year period:

I realize that even this screenshot of a chart may be abhorrent to some, as it was collected from users who did not explicitly consent.

Or I can see how year-over-year mobile traffic on this site has gone down nearly 6%.

Weird.

I don’t send any personal information to Google Analytics. I don’t know who did what — I can only see anonymous aggregate data. Not only is it literally against Google policy to do so:

The Analytics terms of service, which all Analytics customers must adhere to, prohibits sending personally identifiable information (PII) to Analytics (such as names, social security numbers, email addresses, or any similar data), or data that permanently identifies a particular device.

… but I have a much clearer ethical line in my head there — that’s not something I’m comfortable with. Even when I’ve implemented user tracking that does tie a particular user to a particular action, it’s still anonymized such that it’s impossible for me to tell from using that tool who has done what.

But I understand that even this “anonymous” tracking is what is being questioned here. For example, just because what I send is anonymous, it doesn’t mean that attempts can’t be made to try to figure out exactly who is doing what by whoever has that data.

I’m generally fine with anonymous tracking, but the receiving parties go to great lengths to deanonymize it which may be where concern stems from.

— Cory LaViska (@claviska) November 17, 2021

Switching the focus to email, I do use MailChimp to send the email newsletter on this site, and I haven’t done anything special with the settings to increase or decrease how much tracking happens when a newsletter is sent. As such, I can see data, like how many people I send to, how many open it, and how many clicks happened:

As I write this, I’m poking around in the reporting section to see what else I can see. Ughghk, guess what? I can literally see exactly who opened the email (by the person’s email address) and which links they clicked. I didn’t even realize that until now, but wow, that’s very super personally identifiable analytics information. I’m going to look into how I can turn that off because it does cross an ethical line for me.

There is also a brand new mini-war happening with email tracking (not the first, as I remember the uproar when Gmail started proxying images through their own servers, thus “breaking” the accuracy tracker pixel images). This time, it’s Apple doing more aggressive blocking, and companies like MailChimp having to tell customers it is going to mess with their analytics:

Apple Mail in macOS Monterey Warning on the MailChimp reporting screen

I’m interested not just in the ethical concerns and my long-time complacency with industry norms, but also as someone who very literally sells advertising. I can tell you these things are true:

  • I have meetings about pricing where the decisions are based on the historical performance of what is being sold, meaning impressions and clicks.
  • The vast majority of first conversations between bag-of-money-holding advertisers and publishers like me, the very first questions I’m asked are about performance metrics.

That feels largely OK to me. When I go to the store to buy walnuts, I want to know how many walnuts I’m going to get for my dollar. I expect the store to price the walnuts based on normal economic factors, like how much they cost and the supply/demand for walnuts. The advertising buyers are the walnut buyers — they want to know what kind of performance an ad is likely to get for their dollar.

What if I said: I don’t know? I don’t know how many people see these ads. I don’t know how many people click these ads. I don’t know where they are from. I don’t know anything at all. And more, you aren’t allowed to know either. You can give me a URL to send them to, but it cannot have tracking params on it and we won’t be tracking the clicks on it.

Would I lose money? I gotta tell you readers: yes. In the short-term, anyway. It’s hard enough to land advertisers as it is. Coming off as standoffish and unwilling to tell them how many walnuts they are going to get for their dollar is going to make them roll their eyes and move on. Long-term, I bet it could be done. Tell advertisers (and the world) up front, very clearly, your stance on user tracking and how it means that you don’t have and won’t provide numbers via tracking. Lean on supply and demand entirely. Price spots at $X to start. If other people have interest in the spot, raise the price until it stops selling, lower the price if it does. I bet it could be done.

To be honest, I’m not ready to tip my apple cart yet. I have a mortgage. I have employees to pay. I absolutely do not have a war chest to dip into to ride out a major income shortage. If I lost most of my advertising income I would just… fail. Close up shop. Be forced to make other dramatic life changes to deal with it. And I just don’t want to. It doesn’t feel like rolling the dice, because that implies I might win big. But if I were to take a hardline stance with advertisers, telling them that I provide zero data, “winning big” is merely getting back to the baseline for me.

I write all this just to help me think about it. I don’t want to sound like I’m being defensive. If I come across that way, I’d blame my own inertia for following what have felt like industry standards for so long, and being indoctrinated that those practices are just fine. I don’t feel like I’m crossing major ethical boundaries at the moment, but I’d rather be someone who questions myself and takes action when appropriate rather than tying a bandana over my eyes.

  1. I have tried other analytics services, like Plausible, that are more specifically privacy-focused.

The post On User Tracking and Industry Standards on Privacy appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS “decorations”

Css Tricks - Mon, 11/22/2021 - 8:48am

A reader wrote to me the other day asking about this bit of CSS they came across in Wikipedia’s Common.css:

.mw-collapsible-leftside-toggle .mw-collapsible-toggle { /* @noflip */ float: left; /* @noflip */ text-align: left; }

What’s that @noflip business? That’s what they are calling a “CSS decorator” and I think that’s a fine term for it. Really they are just CSS comments, but clearly there is more going on here as those look like programmatic statements that have functionality.

Without some kind of CSS processing, those comments will do nothing. Off the top of my head, I’m not 100% sure what CSS processor is in use here, but I think it’s reasonable to assume that when it runs, it produces a “right-to-left” stylesheet that turns float: left into float: right and text-align: left into text-align: right.

I think it’s worth noting that it’s probably smarter these days to use the natively supported text-align: start so that you don’t have to rely on CSS processing and alternate stylesheets to help you. I don’t think there is a “logical” equivalent for float, unfortunately, but there may be a way to refactor the layout (using grid?) such that “flipping” is unnecessary. Although, wrapping elements around an element is pretty unique to float, so there might not be a simple alternative here.

Searching around a little, it seems like the source of /* @noflip */ is CSSJanus.

The repo suggests it’s made by Wikimedia, so I think that’s a solved case. It looks like the tech has made it’s way to other things, like a plugin for styled-components, a plugin for Sublime Text, and Salesforce even used it in their design system.

There is another processor called css-flip (archived, from Twitter) that looks like it did the exact same thing, and the README shows just how many properties might need this:

background-position, background-position-x, border-bottom-left-radius, border-bottom-right-radius, border-color, border-left, border-left-color, border-left-style, border-left-width, border-radius, border-right, border-right-color, border-right-style, border-right-width, border-style, border-top-left-radius, border-top-right-radius, border-width, box-shadow, clear, direction, float, left, margin, margin-left, margin-right, padding, padding-left, padding-right, right, text-align transition transition-property

It would have hugely surprised me if there wasn’t a PostCSS plugin for this, and a little searching turned up postcss-rtl, but alas, it’s also been deprecated by the owner.

This all started with talking about “CSS decorators” though, which I guess we’re defining as “CSS comments that have processor directives in them.” The one I personally use the most is this:

/* prettier-ignore */ .cool { linear-gradient( to left, pink pink 20% red 20% red ) }

I love Prettier, but if I take the time to format a bit of CSS myself for readability, I’ll chuck a /* prettier-ignore */ on the previous line so it doesn’t mess with it.

Do you use any CSS decorators in your codebases?

The post CSS “decorations” appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Responsive Layouts, Fewer Media Queries

Css Tricks - Mon, 11/22/2021 - 5:29am

We cannot talk about web development without talking about Responsive Design. It’s just a given these days and has been for many years. Media queries are a part of Responsive Design and they aren’t going anywhere. Since the introduction of media queries (literally decades ago), CSS has evolved to the points that there are a lot of tricks that can help us drastically reduce the usage of media queries we use. In some cases, I will show you how to replace multiple media queries with only one CSS declaration. These approaches can result in less code, be easier to maintain, and be more tied to the content at hand.

Let’s first take a look at some widely used methods to build responsive layouts without media queries. No surprises here — these methods are related to flexbox and grid.

Using flex and flex-wrap Live Demo

In the above demo, flex: 400px sets a base width for each element in the grid that is equal to 400px. Each element wraps to a new line if there isn’t enough room on the currently line to hold it. Meanwhile, the elements on each line grow/stretch to fill any remaining space in the container that’s leftover if the line cannot fit another 400px element, and they shrink back down as far as 400px if another 400px element can indeed squeeze in there.

Let’s also remember that flex: 400px is a shorthand equivalent to flex: 1 1 400px (flex-grow: 1, flex-shrink: 1, flex-basis: 400px).

What we have so far:

  • ✔️ Only two lines of code
  • ❌ Consistent element widths in the footer
  • ❌ Control the number of items per row
  • ❌ Control when the items wrap
Using auto-fit and minmax Live Demo

Similar to the previous method, we are setting a base width—thanks to repeat(auto-fit, minmax(400px, 1fr))—and our items wrap if there’s enough space for them. This time, though we’re reaching for CSS Grid. That means the elements on each line also grow to fill any remaining space, but unlike the flexbox configuration, the last row maintains the same width as the rest of the elements.

So, we improved one of requirements and solved another, but also introduced a new issue since our items cannot shrink below 400px which may lead to some overflow.

  • ✔️ Only one line of code
  • ✔️ Consistent element widths in the footer
  • ❌ Control the number of items per row
  • ❌ Items grow, but do not shrink
  • ❌ Control when the items wrap

Both of the techniques we just looked at are good, but we also now see they come with a few drawbacks. But we can overcome those with some CSS trickery.

Control the number of items per row

Let’s take our first example and change flex: 400px to flex: max(400px, 100%/3 - 20px).

CodePen Embed Fallback

Resize the screen and notice that each row never has more than three items, even on a super wide screen. We have limited each line to a maximum of three elements, meaning each line only contains between one and three items at any given time.

Let’s dissect the code:

  • When the screen width increases, the width of our container also increases, meaning that 100%/3 gets bigger than 400px at some point.
  • Since we are using the max() function as the width and are dividing 100% by 3 in it, the largest any single element can be is just one-third of the overall container width. So, we get a maximum of three elements per row.
  • When the screen width is small, 400px takes the lead and we get our initial behavior.

You might also be asking: What the heck is that 20px value in the formula?

It’s twice the grid template’s gap value, which is 10px times two. When we have three items on a row, there are two gaps between elements (one on each on the left and right sides of the middle element), so for N items we should use max(400px, 100%/N - (N - 1) * gap). Yes, we need to account for the gap when defining the width, but don’t worry, we can still optimize the formula to remove it!

We can use max(400px, 100%/(N + 1) + 0.1%). The logic is: we tell the browser that each item has a width equal to 100%/(N + 1) so N + 1 items per row, but we add a tiny percentage ( 0.1%)—thus one of the items wraps and we end with only N items per row. Clever, right? No more worrying about the gap!

CodePen Embed Fallback

Now we can control the maximum number of items per row which give us a partial control over the number of items per row.

The same can also be applied to the CSS Grid method as well:

CodePen Embed Fallback

Note that here I have introduced custom properties to control the different values.

We’re getting closer!

  • ✔️ Only one line of code
  • ✔️ Consistent element widths in the footer
  • ⚠️ Partial control of the number of items per row
  • ❌ Items grow, but do not shrink
  • ❌ Control when the items wrap
Items grow, but do not shrink

We noted earlier that using the grid method could lead to overflow if the base width is bigger than the container width. To overcome this we change:

max(400px, 100%/(N + 1) + 0.1%)

…to:

clamp(100%/(N + 1) + 0.1%, 400px, 100%)

Breaking this down:

  • When the screen width is big, 400px is clamped to 100%/(N + 1) + 0.1%, maintaining our control of the maximum number of items per row.
  • When the screen width is small, 400px is clamped to 100% so our items never exceed the container width.
CodePen Embed Fallback

We’re getting even closer!

  • ✔️ Only one line of code
  • ✔️ Consistent element widths in the footer
  • ⚠️ Partial control of the number of items per row
  • ✔️ Items grow and shrink
  • ❌ Control when the items wrap
Control when the items wrap

So far, we’ve had no control over when elements wrap from one line to another. We don’t really know when it happens because it depends a number of things, like the base width, the gap, the container width, etc. To control this, we are going to change our last clamp() formula, from this:

clamp(100%/(N + 1) + 0.1%, 400px, 100%)

…to:

clamp(100%/(N + 1) + 0.1%, (400px - 100vw)*1000, 100%)

I can hear you screaming about that crazy-looking math, but bear with me. It’s easier than you might think. Here’s what’s happening:

  • When the screen width (100vw) is greater than 400px, (400px - 100vw) results in a negative value, and it gets clamped down to 100%/(N + 1) + 0.1%, which is a positive value. This gives us N items per row.
  • When the screen width (100vw) is less than 400px, (400px - 100vw) is a positive value and multiplied by a big value that’s clamped to the 100%. This results in one full-width element per row.
Live Demo

Hey, we made our first media query without a real media query! We are updating the number of items per row from N to 1 thanks to our clamp() formula. It should be noted that 400px behave as a breakpoint in this case.

What about: from N items per row to M items per row?

We can totally do that by updating our container’s clamped width:

clamp(100%/(N + 1) + 0.1%, (400px - 100vw)*1000, 100%/(M + 1) + 0.1%)

I think you probably get the trick by now. When the screen width is bigger than 400px we fall into the first rule (N items per row). When the screen width is smaller than 400px, we fall into the second rule (M items per row).

Live Demo

There we go! We can now control the number of items per row and when that number should change—using only CSS custom properties and one CSS declaration.

  • ✔️ Only one line of code
  • ✔️ Consistent element widths in the footer
  • ✔️ Full control of the number of items per row
  • ✔️ Items grow and shrink
  • ✔️ Control when the items wrap
More examples!

Controlling the number of items between two values is good, but doing it for multiple values is even better! Let’s try going from N items per row to M items per row, down to one item pre row.

Our formula becomes:

clamp(clamp(100%/(N + 1) + 0.1%, (W1 - 100vw)*1000,100%/(M + 1) + 0.1%), (W2 - 100vw)*1000, 100%)

A clamp() within a clamp()? Yes, it starts to get a big lengthy and confusing but still easy to understand. Notice the W1 and W2 variables in there. Since we are changing the number of items per rows between three values, we need two “breakpoints” (from N to M, and from M to 1).

Here’s what’s happening:

  • When the screen width is smaller than W2, we clamp to 100%, or one item per row.
  • When the screen width is larger than W2, we clamp to the first clamp().
  • In that first clamp, when the screen width is smaller than W1, we clamp to 100%/(M + 1) + 0.1%), or M items per row.
  • In that first clamp, when the screen width is bigger than W1, we clamp to 100%/(N + 1) + 0.1%), or N items per row.
CodePen Embed Fallback

We made two media queries using only one CSS declaration! Not only this, but we can adjust that declaration thanks to the CSS custom properties, which means we can have different breakpoints and a different number of columns for different containers.

CodePen Embed Fallback

How many media queries do we have in the above example? Too many to count but we will not stop there. We can have even more by nesting another clamp() to get from N columns to M columns to P columns to one column. (&#x1f631;)

clamp( clamp( clamp(100%/(var(--n) + 1) + 0.1%, (var(--w1) - 100vw)*1000, 100%/(var(--m) + 1) + 0.1%),(var(--w2) - 100vw)*1000, 100%/(var(--p) + 1) + 0.1%),(var(--w3) - 100vw)*1000, 100%), 1fr)) from N columns to M columns to P columns to 1 column CodePen Embed Fallback

As I mentioned at the very beginning of this article, we have a responsive layout without any single media queries while using just one CSS declaration—sure, it’s a lengthy declaration, but still counts as one.

A small summary of what we have:

  • ✔️ Only one line of code
  • ✔️ Consistent element widths in the footer
  • ✔️ Full control of the number of items per row
  • ✔️ Items grow and shrink
  • ✔️ Control when the items wrap
  • ✔️ Easy to update using CSS custom properties
Let’s simulate container queries

Everyone is excited about container queries! What makes them neat is they consider the width of the element instead of the viewport/screen. The idea is that an element can adapt based on the width of its parent container for more fine-grain control over how elements respond to different contexts.

Container queries aren’t officially supported anywhere at the time of this writing, but we can certainly mimic them with our strategy. If we change 100vw with 100% throughout the code, things are based on the .container element’s width instead of the viewport width. As simple as that!

Resize the below containers and see the magic in play:

CodePen Embed Fallback

The number of columns change based on the container width which means we are simulating container queries! We’re basically doing that just by changing viewport units for a relative percentage value.

More tricks!

Now that we can control the number of columns, let’s explore more tricks that allow us to create conditional CSS based on either the screen size (or the element size).

Conditional background color

A while ago someone on StackOverflow asked if it is possible to change the color of an element based on its width or height. Many said that it’s impossible or that it would require a media query.

But I have found a trick to do it without a media query:

div { background: linear-gradient(green 0 0) 0 / max(0px,100px - 100%) 1px, red; }
  • We have a linear gradient layer with a width equal to max(0px,100px - 100%) and a height equal to 1px. The height doesn’t really matter since the gradient repeats by default. Plus, it’s a one color gradient, so any height will do the job.
  • 100% refers to the element’s width. If 100% computes to a value bigger than 100px, the max() gives us 0px, which means that the gradient does not show, but the comma-separated red background does.
  • If 100% computes to a value smaller than 100px, the gradient does show and we get a green background instead.

In other words, we made a condition based on the width of the element compared to 100px!

CodePen Embed Fallback

This demo supports Chrome, Edge, and Firefox at the time of writing.

The same logic can be based on an element’s height instead by rearranging where that 1px value goes: 1px max(0px,100px - 100%). We can also consider the screen dimension by using vh or vw instead of %. We can even have more than two colors by adding more gradient layers.

div { background: linear-gradient(purple 0 0) 0 /max(0px,100px - 100%) 1px, linear-gradient(blue 0 0) 0 /max(0px,300px - 100%) 1px, linear-gradient(green 0 0) 0 /max(0px,500px - 100%) 1px, red; } CodePen Embed Fallback Toggling an element’s visibility

To show/hide an element based on the screen size, we generally reach for a media query and plop a classic display: none in there. Here is another idea that simulates the same behavior, only without a media query:

div { max-width: clamp(0px, (100vw - 500px) * 1000, 100%); max-height: clamp(0px, (100vw - 500px) * 1000, 1000px); overflow: hidden; }

Based on the screen width (100vw), we either get clamped to a 0px value for the max-height and max-width (meaning the element is hidden) or get clamped to 100% (meaning the element is visible and never greater than full width). We’re avoiding using a percentage for the max-height since it fails. That’s why we’re using a big pixel value (1000px).

Notice how the green elements disappear on small screens:

CodePen Embed Fallback

It should be noted that this method is not equivalent to the toggle of the display value. It’s more of a trick to give the element 0×0 dimensions, making it invisible. It may not be suitable for all cases, so use it with caution! It’s more a trick to be used with decorative elements where we won’t have accessibility issues. Chris wrote about how to hide content responsibly.

It’s important to note that I am using 0px and not 0 inside clamp() and max(). The latter makes invalidates property. I won’t dig into this but I have answered a Stack Overflow question related to this quirk if you want more detail.

Changing the position of an element

The following trick is useful when we deal with a fixed or absolutely positioned element. The difference here is that we need to update the position based on the screen width. Like the previous trick, we still rely on clamp() and a formula that looks like this: clamp(X1,(100vw - W)*1000, X2).

Basically, we are going to switch between the X1 and X2 values based on the difference, 100vw - W, where W is the width that simulates our breakpoint.

Let’s take an example. We want a div placed on the left edge (top: 50%; left:0) when the screen size is smaller than 400px, and place it somewhere else (say top: 10%; left: 40%) otherwise.

div { --c:(100vw - 400px); /* we define our condition */ top: clamp(10%, var(--c) * -1000, 50%); left: clamp(0px, var(--c) * 1000, 40%); } Live Demo

First, I have defined the condition with a CSS custom property to avoid the repetition. Note that I also used it with the background color switching trick we saw earlier—we can either use (100vw - 400px) or (400px - 100vw), but pay attention to the calculation later as both don’t have the same sign.

Then, within each clamp(), we always start with the smallest value for each property. Don’t incorrectly assume that we need to put the small screen value first!

Finally, we define the sign for each condition. I picked (100vw - 400px), which means that this value will be negative when the screen width is smaller than 400px, and positive when the screen width is bigger than 400px. If I need the smallest value of clamp() to be considered below 400px then I do nothing to the sign of the condition (I keep it positive) but if I need the smallest value to be considered above 400px I need to invert the sign of the condition. That’s why you see (100vw - 400px)*-1000 with the top property.

OK, I get it. This isn’t the more straightforward concept, so let’s do the opposite reasoning and trace our steps to get a better idea of what we’re doing.

For top, we have clamp(10%,(100vw - 400px)*-1000,50%) so…

  • if the screen width (100vw) is smaller than 400px, then the difference (100vw - 400px) is a negative value. We multiply it with another big negative value (-1000 in this case) to get a big positive value that gets clamped to 50%: That means we’re left with top: 50% when the screen size is smaller than 400px.
  • if the screen width (100vw) is bigger than 400px, we end with: top: 10% instead.

The same logic applies to what we’re declaring on the left property. The only difference is that we multiply with 1000 instead of -1000 .

Here’s a secret: You don’t really need all that math. You can experiment until you get it perfect values, but for the sake of the article, I need to explain things in a way that leads to consistent behavior.

It should be noted that a trick like this works with any property that accepts length values (padding, margin, border-width, translate, etc.). We are not limited to changing the position, but other properties as well.

Demos!

Most of you are probably wondering if any of these concepts are at all practical to use in a real-world use case. Let me show you a few examples that will (hopefully) convince you that they are.

Progress bar

The background color changing trick makes for a great progress bar or any similar element where we need to show a different color based on progression.

CodePen Embed Fallback

This demo supports Chrome, Edge, and Firefox at the time of writing.

That demo is a pretty simple example where I define three ranges:

  • Red: [0% 30%]
  • Orange: [30% 60%]
  • Green: [60% 100%]

There’s no wild CSS or JavaScript to update the color. A “magic” background property allows us to have a dynamic color that changes based on computed values.

Editable content

It’s common to give users a way to edit content. We can update color based on what’s entered.

In the following example, we get a yellow “warning” when entering more than three lines of text, and a red “warning” if we go above six lines. This can a way to reduce JavaScript that needs to detect the height, then add/remove a particular class.

CodePen Embed Fallback

This demo supports Chrome, Edge, and Firefox at the time of writing.

Timeline layout

Timelines are great patterns for visualizing key moments in time. This implementation uses three tricks to get one without any media queries. One trick is updating the number of columns, another is hiding some elements on small screens, and the last one is updating the background color. Again, no media queries!

CodePen Embed Fallback

When the screen width is below 600px, all of the pseudo elements are removed, changing the layout from two columns to one column. Then the color updates from a blue/green/green/blue pattern to a blue/green/blue/green one.

Responsive card

Here’s a responsive card approach where CSS properties update based on the viewport size. Normally, we might expect the layout to transition from two columns on large screens to one column on small screens, where the card image is stacked either above or below the content. In this example, however, we change the position, width, height, padding, and border radius of the image to get a totally different layout where the image sits beside the card title.

CodePen Embed Fallback Speech bubbles

Need some nice-looking testimonials for your product or service? These responsive speech bubbles work just about anywhere, even without media queries.

CodePen Embed Fallback Fixed button

You know those buttons that are sometimes fixed to the left or right edge of the screen, usually for used to link up a contact for or survey? We can have one of those on large screens, then transform it into a persistent circular button fixed to the bottom-right corner on small screens for more convenient taps.

CodePen Embed Fallback Fixed alert

One more demo, this time for something that could work for those GDPR cookie notices:

CodePen Embed Fallback Conclusion

Media queries have been a core ingredient for responsive designs since the term responsive design was coined years ago. While they certainly aren’t going anywhere, we covered a bunch of newer CSS features and concepts that allow us to rely less often on media queries for creating responsive layouts.

We looked at flexbox and grid, clamp(), relative units, and combined them together to do all kinds of things, from changing the background of an element based on its container width, moving positions at certain screen sizes, and even mimicking as-of-yet-unreleased container queries. Exciting stuff! And all without one @media in the CSS.

The goal here is not to get rid or replace media queries but it’s more to optimize and reduce the amount of code especially that CSS has evolved a lot and now we have some powerful tool to create conditional styles. In other words, it’s awesome to see the CSS feature set grow in ways that make our lives as front-enders easier while granting us superpowers to control how our designs behave like we have never had before.

The post Responsive Layouts, Fewer Media Queries appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Reimagine Atomic CSS

Css Tricks - Fri, 11/19/2021 - 12:01pm

I’m not the biggest fan of Atomic CSS myself. I don’t like all the classes. I like to express my styles in CSS because I find CSS… good. But I appreciate that a lot of people seem to like it, and it does have some clear advantages, like the fact that the generated stylesheet is generally smaller than hand-rolled CSS authored other ways—plus, the available classes are like guardrails that ensure more consistent usage in a design system.

I also appreciate that innovation is happening in this space. It seems to have gone from:

Here’s a billion classes you can use, but hey, at least the CSS is still fairly small and won’t change!

To:

Yes, that, but we’re going to strip away the ones you aren’t using.

And now:

We’re going to generate the stylesheet on the fly based on what you do use.

Anthony Fu breaks down this history nicely in “Reimagine Atomic CSS” where he then throws his hat in the ring taking things one step further with UnoCSS. I can’t say I fully understand it all, but it looks like it can do anything that its predecessors can do but more, largely via rule configurations. It’s also fast (based on vite), and I’m always a fan of fast tools—especially where the goal is a tight feedback loop.

It looks rather complex to me and seems to have limited integrations. I’m not a fan of the bit that turns styles into arbitrary HTML attributes. If they were, like, JSX props, that’s fine. But I think HTML attributes that go all the way to the DOM are dangerous and should be data-* scoped.

At the same time, I always like it when people think through problems and share their thought processes for solving them like Anthony has done here. Plus, there is a playground and that’s fun.

To Shared LinkPermalink on CSS-Tricks

The post Reimagine Atomic CSS appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Coding Font Game

Css Tricks - Fri, 11/19/2021 - 11:18am

A tournament bracket UI where you pick your favorite between two coding fonts and your choices are whittled down all the way to a final winner. A clever way to suss out your own taste and arrive at a choice.

(P.S. We have our own little coding fonts website to showcase some high-quality favorites.)

Wenting Zhang documented in a newsletter that she built it the whole thing with Retool, meaning she had to write very little code directly, and instead used pre-built components. That’s kind of the point of Retool. It’s like a UI library with existing components for building stuff out quickly, but it goes the extra mile in connecting to your own data.

Five years ago, if I wanted to build this coding font game, I would have had to spend days or weeks hand-coding it in React or other frameworks. But since then, I have discovered no code and low code apps which are essentially libraries of pre-existing informational or functional things. They can be powered by a database or an API, and the interaction layer is standard, often with minimal customization needs.

The two bits of code shared in that post look… pretty complicated. So, in this case, it’s a “low code” thing where those bits of code really hone in on the core functionality and experience, but a lot of the rest of the more mundane and boilerplate code is avoided. Seems compelling to me. In the end, the whole “game” is an <iframe> pointing to the Retool widget thing.

The post Coding Font Game appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

The Many Faces of VS Code in the Browser

Css Tricks - Thu, 11/18/2021 - 10:22am

VS Code is built from web technologies (HTML, CSS, and JavaScript), but dare I say today it’s mostly used a local app that’s installed on your machine. That’s starting to shift, though, as there has been an absolute explosion of places VS Code is becoming available to use on the web. I’d say it’s kind of a big deal, as VS Code isn’t just some editor; it’s the predominant editor used by web developers. Availability on the web means being able to use it without installing software, which is significant for places, like schools, where managing all that is a pain, and computers, like Chromebooks, where you don’t really install local software at all.

It’s actually kind of confusing all the different places this shows up, so let’s look at the landscape as I see it today.

vscode.dev

It was just a few weeks ago as I write that Microsoft dropped vscode.dev. Chris Dias:

Modern browsers that support the File System Access API (Edge and Chrome today) allow web pages to access the local file system (with your permission). This simple gateway to the local machine quickly opens some interesting scenarios for using VS Code for the Web as a zero-installation local development tool

It’s just Edge and Chrome that have this API right now, but even if you can’t get it, you can still upload files, or perhaps more usefully, open a repo. If it does work, it’s basically… VS Code in the browser. It can open your local folders and it behaves largely just like your local VS Code app does.

I haven’t worked a full day in it or anything, but basic usage seems about the same. There is some very explicit permission-granting you have to do, and keyboard commands are a bit weird as you’re having to fight the browsers keyboard commands. Plus, there is no working terminal.

Other than that it feels about the same. Even stuff like “Find in Project” seems just as fast as local, even on large sites.

GitHub.dev: The whole “Press Period (.) on any GitHub Repo” Thing

You also get VS Code in the browser if you go to github.dev, but it’s not quite wired up the same.

You don’t have the opportunity here to open a local folder. Instead, you can quickly look at a GitHub repo.

But perhaps even more notably, you can make changes, save the files, then use the Source Control panel right there to commit the code or make a pull request.

You’d think vscode.dev and github.dev would merge into one at some point, but who knows.

Oh and hey, thinking of this in reverse, you can open GitHub repos on your locally installed VS Code as well directly (even without cloning it).

There is no terminal or preview in those first two, but there is with GitHub Codespaces.

GitHub Codespaces is also VS Code in the browser, but fancier. For one thing, you’re auth’d into Microsoft-land while using it, meaning it’s running all your VS Code extensions that you run locally. But perhaps a bigger deal is that you get a working terminal. When you spin it up, you see:

Welcome to Codespaces! You are on our default image.

• It includes runtimes and tools for Python, Node.js, Docker, and more. See the full list here: https://aka.ms/ghcs-default-image
• Want to use a custom image instead? Learn more here: https://aka.ms/configure-codespace

&#x1f50d; To explore VS Code to its fullest, search using the Command Palette (Cmd/Ctrl + Shift + P or F1).

&#x1f4dd; Edit away, run your app as usual, and we’ll automatically make it available for you to access.

On a typical npm-powered project, you can npm run you scripts and you’ll get a URL running the project as a preview.

This is in the same territory as Gitpod.

Gitpod is a lot like GitHub CodeSpaces in that it’s VS Code in the browser but with a working terminal. That terminal is like a full-on Docker/Linux environment, so you’ve got a lot of power there. It might even be able to mimic your production environment, assuming you’re using all things that Gitpod supports.

It’s also worth noting that Gitpod jacks in “workspaces” that run services. On that demo project above, a MongoDB instance is running on one port, and a preview server is open on another port, which you can see in a mock browser interface there. Being able to preview the project you’re working on is an absolute must and they are handling that elegantly.

Perhaps you’ll remember we used Gitpod in a video we did about DataStax Astra (jumplink) which worked out very nicely.

My (absolute) guess is that Gitpod could be acquired by Microsoft. It seems like Microsoft is headed in this exact direction and getting bought is certainly better than getting steamrolled by the company that makes the core tech that you’re using. You gotta play the “no—this is good! it validates the market! we baked you an awkward cake!” for a while but I can’t imagine it ends well.

This is also a lot like CodeSandbox or Stackblitz.

Straight up, CodeSandbox and Stackblitz also run VS Code in the browser. Or… something that leverages bits and bobs of VS Code at least (a recent episode of Syntax gets into the StackBlitz approach a bit).

You can also install VS Code on your own server.

That’s what Coder’s code-server ia. So, rather than use someone else’s web version of VS Code, you use your own.

You could run VS Code on a local server, but I imagine the big play here is that you run it on live cloud web servers you control. Maybe servers, you know, with your code running on them, so you can use this to literally edit the files on the server. Who needs VIM when you have full-blown VS Code (lolz).

We talked about the school use case, and I imagine this is compelling for that as well, since the school might not even rely on a third-party service, but host it themselves. The iPad/Chromebook use cases are relevant here, too, and perhaps even more so. The docs say “Preserve battery life when you’re on the go; all intensive tasks run on your server,” which I guess means that unlike vscode.dev where tasks like “Find in Project” are (presumably) done on your local machine, they are done by the server (maybe slower, but not slower than a $200 laptop?).

There is clearly something in the water with all this. I’m bullish on web-based IDEs. Just look at what’s happening with Figma (kicking ass), which I would argue is one-third due to the product meetings screen designers need with little bloat, one-third due to the simple team and permissions model, and one-third due to the fact that it’s built web-first.

The post The Many Faces of VS Code in the Browser appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

PHP Date and Time Recipes

Css Tricks - Thu, 11/18/2021 - 5:38am

Dealing with dates and times is one of those things that can frustrate programmers a lot. At the same time, they are fundamental to software development, used from everything from meta and how things are ordered to time-based triggers and lots in between.

Dates and times are prone to errors too. Handle them incorrectly, and they can confuse end-users and fellow programmers alike.

This is a quick guide to dealing with dates and times specifically in the PHP programming language. It’s meant to be a reference to the most common needs you’ll have, like formatting and adjusting dates. It’s simple, but it’s likely going to cover 80% of your needs.

Table of contents

This research is brought to you by support from Frontend Masters, CSS-Tricks’ official learning partner.

Need front-end development training?

Frontend Masters is the best place to get it. They have courses on all the most important front-end technologies. Interested in going full-stack? Here’s your best bet:

Take the Courses

Get the current date and time

One thing to know is that the dates and times can be represented in three forms: a timestamp (i.e. epoch time), a DateTime object, and a string.

First up, a recipe to get the current date and time:

<?php $now = new DateTime(); var_dump($now); // object(DateTime)#1 (3) { // ["date"]=> // string(26) "2021-10-13 22:25:11.790490" // ["timezone_type"]=> // int(3) // ["timezone"]=> // string(12) "Asia/Jakarta" // }

This provides a DateTime object that can be used to create a date and time string:

<?php $now = new DateTime(); echo $now->format("Y-m-d"); // 2021-10-13 echo $now->format("Y-m-d h:i:s A"); // 2021-10-13 10:10:31 PM

Intuitively, you know that Y refers to the year, m refers to the month, d refers to the day of the month, and so on. The full list of the parameters can be found in the PHP manual, but I’ll drop some of the most common ones here for reference.

Day of the monthdDay of the month. two digits with leading zeros01 – 31jDay of the month without leading zeros1 – 31SIncludes the English suffix.st, nd, rd, th (e.g. 1st, 2nd, 3rd, 4th)WeekdayDAbbreviated textual representation of a day, in three lettersSun – SatlA full textual representation of a weekday.Sunday – SaturdayMonthFA full textual representation of a month, such as January or MarchJanuary – DecemberMAbbreviated textual representation of a month, in three lettersJan – DecmNumeric representation of a month, with leading zeros01 – 12nNumeric representation of a month, without leading zeros1 – 12YearYA full numeric representation of a year, 4 digitsE.g. 1999 or 2003yA two digit representation of a yearE.g. 99 or 03TimeAUppercase Ante Meridiem and Post MeridiemAM or PMg12-hour format of an hour without leading zeros1 – 12h12-hour format of an hour with leading zeros01 – 12iMinutes with leading zeros00 – 59sSeconds with leading zeros00 – 59

The DateTime object can be converted to a timestamp:

<?php $now = new DateTime(); echo $now->getTimestamp(); // 1634139081

But we can also get the current time in timestamp without constructing a DateTime object:

<?php echo time(); // 1634139081 Construct a DateTime object of a specific time

What if we want to construct a DateTime for a particular time, like July 14th, 2011? We can pass a formatted string date to the constructor:

<?php $date = new DateTime("2011-07-14"); var_dump($date); // object(DateTime)#1 (3) { // ["date"]=> // string(26) "2011-07-14 00:00:00.000000" // ["timezone_type"]=> // int(3) // ["timezone"]=> // string(12) "Asia/Jakarta" // }

The constructor accepts other formats as well:

<?php $date = new DateTime("14-07-2011"); var_dump($date); // object(DateTime)#1 (3) { // ["date"]=> // string(26) "2011-07-14 00:00:00.000000" // ["timezone_type"]=> // int(3) // ["timezone"]=> // string(12) "Asia/Jakarta" // }

But be careful with an ambiguous format, like this:

<?php $date = new DateTime("07/14/2011"); var_dump($date); // object(DateTime)#1 (3) { // ["date"]=> // string(26) "2011-07-14 00:00:00.000000" // ["timezone_type"]=> // int(3) // ["timezone"]=> // string(12) "Asia/Jakarta" // }

You might think that everyone should be familiar with an American date format. But not everyone is and it might be interpreted differently. Not PostgreSQL.

CREATE TABLE IF NOT EXISTS public.datetime_demo ( created_at date ); insert into datetime_demo (created_at) values ('07/12/2011'); select created_at from datetime_demo; /* 2011-12-07 */

You may have thought that would return July 12th, 2011, but it was December 7th, 2011, instead. A better way is to use an explicit format:

<?php $date = DateTime::createFromFormat('m/d/y', "10/08/21"); var_dump($date); //object(DateTime)#2 (3) { // ["date"]=> // string(26) "2021-10-08 16:00:47.000000" // ["timezone_type"]=> // int(3) // ["timezone"]=> // string(12) "Asia/Jakarta" //}

What if we want to construct a DateTime object from a timestamp?

<?php $date = new DateTime(); $date->setTimestamp(1634142890); var_dump($date); //object(DateTime)#1 (3) { // ["date"]=> // string(26) "2021-10-13 23:34:50.000000" // ["timezone_type"]=> // int(3) // ["timezone"]=> // string(12) "Asia/Jakarta" // }

We don’t have to create a DateTime object if we want to convert a timestamp object to a formatted date string:

<?php echo date("Y-m-d h:i A", time()); // 2021-10-14 04:10 PM Timezones

We can create a DateTime object that includes timezone information, like if we’re dealing with Pacific Standard Time, Eastern Daylight Time, etc.

<?php $timezone = new DateTimeZone("America/New_York"); $date = new DateTime("2021-10-13 05:00", $timezone); var_dump($date); // object(DateTime)#1 (3) { // ["date"]=> // string(26) "2021-10-13 05:00:00.000000" // ["timezone_type"]=> // int(3) // ["timezone"]=> // string(16) "America/New_York" // } // Eastern Daylight Time, for example: New York $date = new DateTime("2021-10-13 05:00 EDT"); var_dump($date); // object(DateTime)#2 (3) { // ["date"]=> // string(26) "2021-10-13 05:00:00.000000" // ["timezone_type"]=> // int(2) // ["timezone"]=> // string(3) "EDT" // } $date = new DateTime("2021-10-13 05:00 -04:00"); var_dump($date); // object(DateTime)#1 (3) { // ["date"]=> // string(26) "2021-10-13 05:00:00.000000" // ["timezone_type"]=> // int(1) // ["timezone"]=> // string(6) "-04:00" // }

There are three ways to create a DateTime object with timezone information. The timezone_type accepts different values for each one.

But say we want to convert a date and time that’s displayed in New York’s timezone to display Jakarta’s timezone instead?

<?php $newYorkTimeZone = new DateTimeZone("America/New_York"); $date = new DateTime("2021-11-11 05:00", $newYorkTimeZone); echo $date->format("Y-m-d h:i A"); // 2021-11-11 05:00 AM $jakartaTimeZone = new DateTimeZone("Asia/Jakarta"); $date->setTimeZone($jakartaTimeZone); echo $date->format("Y-m-d h:i A"); // 2021-11-11 05:00 PM

When it’s 05:00 AM in New York, it’s 05:00 PM in Jakarta on the same day. Jakarta is 12 hours ahead of New York on November 11th 2021. But one month earlier, Jakarta is only 11 hours ahead of New York as shown below:

<?php $newYorkTimeZone = new DateTimeZone("America/New_York"); $date = new DateTime("2021-10-11 05:00", $newYorkTimeZone); echo $date->format("Y-m-d h:i A"); // 2021-10-11 05:00 AM $jakartaTimeZone = new DateTimeZone("Asia/Jakarta"); $date->setTimeZone($jakartaTimeZone); echo $date->format("Y-m-d h:i A"); // 2021-10-11 04:00 PM

PHP handles Daylight Saving Time for you automatically.

Localization

This is a common way to display date and time in the United States:

<?php $now = new DateTime(); echo $now->format("m/d/Y h:i A"); // 10/14/2021 03:00 PM

But someone in France might prefer something more common to their locale. C’est horrible, they’d complain. For one, nobody puts the month before month day, except the U.S. Second, France doesn’t use AM or PM — they use the 24-hour format (e.g. 14:00 instead of 2:00 PM) like the military. This is how you make a French local happy.

<?php $now = new DateTime(); echo $now->format("d/m/Y H:i"); // 14/10/2021 15:00

But this requires an intimate knowledge about a specific country or area. Instead, we can localize the date. To localize a date in PHP, first check for support for a particular language.

In this example, we’re using the French. In Ubuntu, install the French language pack:

$ sudo apt-get install language-pack-fr

Use the strftime() function to localize a date:

<?php setlocale(LC_TIME, "en_US.UTF-8"); // Check for U.S. English support echo strftime("%x %X"); // 10/14/21 03:37:59 PM setlocale(LC_TIME, "fr_FR.UTF-8"); // Check for French support echo strftime("%x %X"); // 14/10/2021 15:37:31

All of the parameters for strftime are documented in the PHP manual.

Time travel

Let’s do time travel to the past and the future. First, let’s get acquainted with DateInterval:

<?php $interval = new DateInterval("P4M1W2DT2H5M"); // P 4M 1W 2D T 2H 5M // // P = Period interval (years, months, weeks, days) // 4M = 4 months // 1W = 1 week // 2D = 2 days // // T = Time interval (hours, minutes, seconds) // 2H = 2 hours // 5M = 5 minutes

The P and T are to separate period interval and time interval. Here’s how we can travel to the future:

<?php $date = new DateTime("2021-10-14"); $interval = new DateInterval("P2D"); // 2 days $futureDate = $date->add($interval); echo $futureDate->format("Y-m-d"); // 2021-10-16

And here’s how we go back in time:

<?php $date = new DateTime("2021-10-14 10:00"); $interval = new DateInterval("PT6H"); // 6 hours $pastDate = $date->sub($interval); echo $pastDate->format("Y-m-d H:i"); // 2021-10-14 04:00

If we want to time travel with the name of the weekday, we can combine the strtotime() function and the setTimestamp() method of a DateTime object:

<?php $nextTuesday = strtotime("next tuesday"); $date = new DateTime("2021-10-14"); $date->setTimestamp($nextTuesday); echo $date->format("Y-m-d"); // 2021-10-19

See the full list of strtotime() parameters in the PHP docs.

Recurring dates and times

It’s a common feature in calendar apps to set a reminder that repeats every so often, like every two days or every week. We can use DatePeriod to represent a period of time:

<?php $start = new DateTime("2021-10-01"); $end = new DateTime("2021-11-01"); $interval = new DateInterval("P1W"); // 1 week $range = new DatePeriod($start, $interval, $end); // Starting from October 1st 2021 (inclusive), jump every 1 week // until November 1st 2021 (exclusive) foreach ($range as $date) { echo $date->format("Y-m-d") . "n"; } // 2022-10-01 // 2022-10-08 // 2022-10-15 // 2022-10-22 // 2022-10-29 How many days ago?

You know how services like Twitter will show that someone posted X number of minutes/hours/days/etc. ago? We can do the same thing by calculating how much time has elapsed between the the current time and when that action occurred.

<?php $date = new DateTime("2022-10-30"); $date2 = new DateTime("2022-10-25"); $date3 = new DateTime("2022-10-10"); $date4 = new DateTime("2022-03-30"); $date5 = new DateTime("2020-03-30"); function get_period_ago($endDate, $startDate) { $dateInterval = $endDate->diff($startDate); if ($dateInterval->invert==1) { if ($dateInterval->y > 0) { return $dateInterval->y . " years agon"; } if ($dateInterval->m > 0) { return $dateInterval->m . " months agon"; } if ($dateInterval->d > 7) { return (int)($dateInterval->d / 7) . " weeks agon"; } if ($dateInterval->d > 0) { return $dateInterval->d . " days agon"; } } } echo get_period_ago($date, $date2); // 5 days ago echo get_period_ago($date, $date3); // 2 weeks ago echo get_period_ago($date, $date4); // 7 months ago echo get_period_ago($date, $date5); // 2 years ago

After getting the DateInterval object from the diff() method, make sure that the $startDate variable is in the past by checking the invert property. Then check the y, m, and d properties.

The full list of DateInterval object properties can be found here in the PHP docs.

Where do you go from here?

Now you have a little cheatsheet of common PHP recipes for when you find yourself working with dates and times. Need to get the current date and time? Maybe you need to format a date a certain way, or include the local timezone, or compare dates. All of that is right here!

There are still more methods and functions about date and time that we haven’t discussed, of course — things like calendar-related functions and whatnot. Be sure to keep the PHP Manual’s Date and Time section close by for even more use cases and examples.

The post PHP Date and Time Recipes appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Zero Trickery Custom Radios and Checkboxes

Css Tricks - Wed, 11/17/2021 - 2:16pm

I feel like half of all “custom-designed radio buttons and checkboxes” do two things:

  1. Make them bigger
  2. Colorize them

I always think of SurveyMonkey for having big chunky radios and checkboxes. And indeed, just poking at their interface quickly, even internally, the app uses has those all over the place:

SurveyMonkey’s implementation appears to be pseudo-elements on a <label> element with icon fonts and such.

I think it’s worth noting that if that’s all you are doing, you might be able to do that with zero CSS trickery. Just… make them bigger and colorize them.

Like…

input[type="radio"], input[type="checkbox"] { width: 3em; height: 3rem; accent-color: green; }

That’ll chunk those suckers up and colorize them pretty good!

Firefox Chrome

Except (obligatory sad trombone) in Safari:

Safari

We’re so close to having some very simple CSS to accomplish the main use-case for custom checkboxes and radios. But no cigar, that is, unless you can bring yourself to just not care about the Safari UI (it is still perfectly functional, after all).

If you do need to give up and go for a completely custom design, Stephanie Eckles has got you covered:

In related news, I always think of a “toggle” UI control as a set of 2 radio buttons progressively enhanced, but it turns out <button> is the way, as Michelle Barker covers here. No native UI for slider toggle thingies, alas.

The post Zero Trickery Custom Radios and Checkboxes appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

What do you get for using a search input type?

Css Tricks - Wed, 11/17/2021 - 1:09pm

Like this: <input type="search">

  1. You get an extra-round-y appearance in Safari, which at one time matched the macOS look for search inputs, but not really anymore. I don’t hate the look, except…
  2. Safari totally ignores the font-size you set on it, so careful about that. Unless you smash off the round-y look with -webkit-appearance: none (I guess unprefixed going forward) — then you can, so probably 92% of all websites do this.
  3. You get a little × icon inside the input (when it has a value) that clears it out. This is probably the best reason to use it, and mercifiully you get to keep it even after resetting the appearance.
  4. You get the satisfaction of knowing that you’re using the semantically correct input type for the job, assuming you are building a thing that actually searches something.
  5. You get past search terms with autocomplete. Browsers store those past search terms and offer a menu for autocompleting them. I’m trying to make this a list of things that are unique to search inputs, and I can prove this happens on non-search inputs. But hey, it does make the most sense on search inputs.
  6. You get the pleasure of adding a role to the parent form (i.e. <form role="search">) because that helps assistive technology announce it as a search form.
  7. You get to remember to add a proper <label> to the input. Just using a magnifying glass icon or just a placeholder alone won’t cut it, even though that’s what a lot of designs call for visually.
  8. You get a super weird incremental attribute that sends a debounced search event to the DOM element itself. I guess that is useful for live search UX. But it’s non-standard and not in Firefox so probably don’t count on it (via Christian Shaefer).
  9. You get to live another day as a front-end developer.

The post What do you get for using a search input type? appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Cherry-Picking Commits in Git

Css Tricks - Wed, 11/17/2021 - 5:28am

This article is part of our “Advanced Git” series. Be sure to follow us on Twitter or sign up for our newsletter to hear about the next articles!

In part 5 of this series, we looked at rebasing and merging. Although there are a couple of differences between git merge and git rebase, both commands have the same goal: they integrate changes from one branch into another.

Today, we’ll look at git cherry-pick and how it allows us to integrate selected, individual commits from any branch into our current HEAD branch. This is a big difference compared to git merge or git rebase, which both integrate all new commits from the specified branch.

So why would you want to do this and pick only one commit from a branch to apply it to another one? There are different reasons, of course, but an especially helpful one is to undo changes. Let’s say you accidentally made a commit on the wrong branch. Using cherry-pick this is not a big problem: you can switch to the correct branch and then cherry pick the commit!

Advanced Git series:

Before we look at a practical example, a word of warning: don’t get too excited about cherry picking. Your primary goal should be to work on a branch level, and both git merge and git rebase were built exactly for this job. Cherry picking is meant for special occasions, not as a replacement for merging and rebasing.

Moving a commit to another branch

Here’s a real-life scenario to explain when cherry picking is the right approach. Let’s say you committed something to the master branch that was intended for the feature/newsletter instead. What now? Do you have to call your team members or your boss to explain this “mistake”?

The following screenshot of Tower, a graphical Git client for macOS and Windows, shows the problem and the commit 26bf1b48 which was accidentally made on the master branch:

Alternatively, you can examine the situation on the command line if you type git log in the terminal:

$ git log commit 26bf1b4808ba9783e4fabb19ec81e7a4c8160194 (HEAD -> master) Author: Tobias Günther Date: Fri Oct 5 09:58:03 2018 +0200 Newsletter signup page

So, the commit with the ID 26bf1b48 ended up in master, but you should’ve committed it to the branch feature/newsletter. Let’s cherry pick that particular commit and move it to the correct branch. First, you switch branches, and then you cherry pick the commit:

$ git checkout feature/newsletter Switched to branch 'feature/newsletter' $ git status On branch feature/newsletter nothing to commit, working tree clean $ git cherry-pick 26bf1b48 [feature/newsletter 7fb55d0] Newsletter signup page Author: Tobias Günther <tg@fournova.com> Date: Fri Oct 5 09:58:03 2018 +0200 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 signup.html

If you run git log again, you can see the new commit on the feature/newsletter branch:

$ git log commit 7fb55d06a8e70fdce46921a8a3d3a9de7f7fb8d7 (HEAD -> feature/newsletter) Author: Tobias Günther <tg@fournova.com> Date: Fri Oct 5 09:58:03 2018 +0200 Newsletter signup page

What happened in the background? Git created a copy of the commit with the same changes and the same commit message on the feature/newsletter branch. It is, however, a completely new commit with its own, new ID. And what about the original commit?

Cleaning up the other branch

If you check the master branch, you can still see that “wrong” commit. This means that cherry picking doesn’t “move” a picked commit from the original branch; it merely creates a copy and leaves the original untouched.

Now, in order to clean up and undo the commit, you can use git reset.

$ git checkout master Switched to branch 'master' $ git reset --hard HEAD~1 HEAD is now at 776f8ca Change about title and delete error page

That’s it — like nothing ever happened.

In case you’re using a GUI app like Tower, this is what the whole process looks like:

A tool for special cases, not the everyday integration

Whenever you can use a traditional merge or rebase, you should do so. Cherry picking should be reserved for cases when a git merge or git rebase is not possible, when you want to move only individual commits from one branch to another. Always keep in mind that git cherry-pick creates “duplicate” commits and that you should clean up afterwards.

If you want to dive deeper into advanced Git tools, feel free to check out my (free!) “Advanced Git Kit”: it’s a collection of short videos about topics like branching strategies, Interactive Rebase, Reflog, Submodules and much more.

Happy cherry picking — and see you soon for the next part in our series on “Advanced Git”!

Advanced Git series:

The post Cherry-Picking Commits in Git appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

The Gap (Design Engineering)

Css Tricks - Wed, 11/17/2021 - 5:27am

Egor Kloos describes a situation where a (purely visual) designer asks for some changes to a component. There is a misunderstanding where the (code monkey) developer implements the change exactly as requested—but really what was required was both a bug fix and a new variation to the component. Because of the skill siloing: problems.

Designers move from idea to a wireframe, a prototype, a logo, or even just a drawing. Developers move from a problem or feature to a coded solution that is solved and released. Both are creative, both are in aid of the end-user. The Design Engineer role is also creative and authors code but systematically translates a design towards implementation in a structured way. 

Design Engineering is certainly becoming at thing! You can listen to Natalya Shelburne talk about it on an episode of ShopTalk, and there is a whole book on it. I suspect it is similar-but-different (somehow?) to the term “DesignOps.” I heard Cameron asking about that the other day and got some good responses.

I find all this fascinating. In the beginning, we had the webmaster and that role did all the things. The webmaster was pulled into two, front-end and back-end developer. Design was maybe a bit lumped into front-end, but has now clearly pulled away as a very distinct role. Back-end has pulled apart, birthing DevOps as a new role. Front-end has been pulled apart the most—see “Front of the Front / Back of the Front” for more on that.

We see more and more specialists in fields like performance and accessibility. Now we’re seeing “glue” roles between the roles that we previously pulled apart. This will continue to happen as the haystack of responsibility continues to grow (and it will).

Heck, Dave and I joked the other day there might be entire jobs just focused on dealing with images. I feel fortunate that I’ve largely always worked on small teams with very talented people that very often see across and reach across silos.

To Shared LinkPermalink on CSS-Tricks

The post The Gap (Design Engineering) appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

A Clever Sticky Footer Technique

Css Tricks - Tue, 11/16/2021 - 2:49pm

Upon hearing “sticky footer” these days, I would think most people imagine a position: sticky situation where a footer element appears fixed on the screen while in the scrolling context of some parent element.

That’s not quite what I’m talking about here. “Sticky footers” were a UI concept before position: sticky existed and they mean something slightly different. The idea is that they stick to the bottom of the screen, even when the content of the page isn’t enough to push them there. But if there is enough content, they are happily pushed down.

We covered five ways to do this in years past, which included techniques that are somewhat modern, including calc(), flexbox, and CSS Grid.

Enter a sixth challenger! Reader Sílvio Rosa wrote in with this:

CodePen Embed Fallback

(Probably easiest to check out on a medium-sized desktop screen, which is kinda where sticky footers matter the most anyway.)

It’s pretty much just this:

html, body { height: 100%;} body > footer { position: sticky; top: 100vh; }

What I like about it is that it doesn’t require any special extra wrapper for non-footer content.

It’s also a little brain-bending. When I see top: 100vh; I think well that won’t work because it will push the footer outside the viewable area. But that’s the clever bit. It will do that no matter how big the footer is (no magic numbers), and then the sticky positioning will “suck it back up” to stick along the bottom edge. But it will never overlap content, so it’s happy to be pushed down below content, which is a core tenet of the sticky footer pattern.

The post A Clever Sticky Footer Technique appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Line length revisited: following the research

Css Tricks - Tue, 11/16/2021 - 11:35am

Mary Dyson produces nitty gritty research on the long-accepted notion that shorter line lengths are more legible than longer ones. The study finds that shorter lines do not necessarily lead to faster reading. If you’re looking for a definitive answer to use in your next design review debate, though, no dice. The big finding is that long lines don’t slow things down as much as previously thought, not that they’re better or worse.

But there’s so much more meat in here that I found much more interesting, mostly because I’m largely ignorant on the topic and gained a dollop of context on writing, legibility, and behavior.

Things like…

There’s a term for transitioning between lines of text

It’s return sweeps. You know, like your eye hits the Return key at the end of the line and sweeps to the start of the next line. Then, there are undershoots. The idea is that eyes may not sweep to the exact start of the next line, instead stopping a bit short.

Those little rapid eye movements between words and phrases? Those are called saccades. I had to look that up.

The impact of undershoots is what’s being challenged

The previous research we’ve relied on for years comes from 1940(!), a time when we obviously were more concerned with paper pages than bright digital displays. Longer lines, it said, increased the likelihood that eyes undershoot during a return sweep, and undershooting results in a regression that results in a 130ms to 250ms delay where the brain needs to get its bearings. The report refers to that as undersweep-fixation.

We can still process words during undersweep-fixation

This report cites a 2019 study that tried to correct undershoots by bolding the first word at the start of each new line, sort of like an anchor that naturally draws the eye closer to the left margin.

The 2019 study did find that the bolded words did decrease undershot return sweeps But despite that, reading speed did not improve. That’s the impetus for challenging the long-held assumption that shorter is better.

Mary explains further:

In seeking to reconcile why longer line lengths may not slow down reading on screen but do so when reading print, I outlined some differences, e.g. visual angle, time spent scrolling. But although physical differences between reading from screen and reading print still exist, we now have direct evidence to explain why longer lines were read at least as fast as shorter lines. Readers can process words during the brief fixation when undershooting the start of the new line. This saves time in subsequent processing. Now we might also acknowledge that there is greater consistency between the range of optimal line lengths for print and screen.

Where does this leave us today?

Well, nowhere closer to a clear answer we can use in our day-to-day work. But it’s good to dust off our collection of design and copywriting best practices and know that line length is less of a constraint than perhaps it has been.

Again, none of this tells us whether long or short lines are better. Mary ends her report by saying she cannot definitely recommend using longer lines of text because there are clear because there are still some factors at play, including:

  • Shorter lines are more effective for people with dyslexia.
  • More questions about return sweeps and undershooting need to be answered.
  • Several other studies indicate that users prefer shorter lines and judge longer lines as more difficult to read.

To Shared LinkPermalink on CSS-Tricks

The post Line length revisited: following the research appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Generate a Pull Request of Static Content With a Simple HTML Form

Css Tricks - Tue, 11/16/2021 - 5:37am

Jamstack has been in the website world for years. Static Site Generators (SSGs) — which often have content that lives right within a GitHub repo itself — are a big part of that story. That opens up the idea of having contributors that can open pull requests to add, change, or edit content. Very useful!

Examples of this are like:

Why built with a static site approach?

When we need to build content-based sites like this, it’s common to think about what database to use. Keeping content in a database is a time-honored good idea. But it’s not the only approach! SSGs can be a great alternative because…

  • They are cheap and easy to deploy. SSGs are usually free, making them great for an MVP or a proof of concept.
  • They have great security. There is nothing to hack through the browser, as all the site contains is often just static files.
  • You’re ready to scale. The host you’re already on can handle it.

There is another advantage for us when it comes to a content site. The content of the site itself can be written in static files right in the repo. That means that adding and updating content can happen right from pull requests on GitHub, for example. Even for the non-technically inclined, it opens the door to things like Netlify CMS and the concept of open authoring, allowing for community contributions.

But let’s go the super lo-fi route and embrace the idea of pull requests for content, using nothing more than basic HTML.

The challenge

How people contribute adding or updating a resource isn’t always perfectly straightforward. People need to understand how to fork your repository, how to and where to add their content, content formatting standards, required fields, and all sorts of stuff. They might even need to “spin up” the site themselves locally to ensure the content looks right.

People who seriously want to help our site sometimes will back off because the process of contributing is a technological hurdle and learning curve — which is sad.

You know what anybody can do? Use a <form>

Just like a normal website, the easy way for people to submit a content is to fill out a form and submit it with the content they want.

What if we can make a way for users to contribute content to our sites by way of nothing more than an HTML <form> designed to take exactly the content we need? But instead of the form posting to a database, it goes the route of a pull request against our static site generator? There is a trick!

The trick: Create a GitHub pull request with query parameters

Here’s a little known trick: We can pre-fill a pull request against our repository by adding query parameter to a special GitHub URL. This comes right from the GitHub docs themselves.

Let’s reverse engineer this.

If we know we can pre-fill a link, then we need to generate the link. We’re trying to make this easy remember. To generate this dynamic data-filled link, we’ll use a touch of JavaScript.

So now, how do we generate this link after the user submits the form?

Demo time!

Let’s take the Serverless site from CSS-Tricks as an example. Currently, the only way to add a new resource is by forking the repo on GitHub and adding a new Markdown file. But let’s see how we can do it with a form instead of jumping through those hoops.

The Serverless site itself has many categories (e.g. for forms) we can contribute to. For the sake of simplicity, let’s focus on the “Resources” category. People can add articles about things related to Serverless or Jamstack from there.

All of the resource files are in this folder in the repo.

Just picking a random file from there to explore the structure…

--- title: "How to deploy a custom domain with the Amplify Console" url: "https://read.acloud.guru/how-to-deploy-a-custom-domain-with-the-amplify-console-a884b6a3c0fc" author: "Nader Dabit" tags: ["hosting", "amplify"] --- In this tutorial, we’ll learn how to add a custom domain to an Amplify Console deployment in just a couple of minutes.

Looking over that content, our form must have these columns:

  • Title
  • URL
  • Author
  • Tags
  • Snippet or description of the link.

So let’s build an HTML form for all those fields:

<div class="columns container my-2"> <div class="column is-half is-offset-one-quarter"> <h1 class="title">Contribute to Serverless Resources</h1> <div class="field"> <label class="label" for="title">Title</label> <div class="control"> <input id="title" name="title" class="input" type="text"> </div> </div> <div class="field"> <label class="label" for="url">URL</label> <div class="control"> <input id="url" name="url" class="input" type="url"> </div> </div> <div class="field"> <label class="label" for="author">Author</label> <div class="control"> <input id="author" class="input" type="text" name="author"> </div> </div> <div class="field"> <label class="label" for="tags">Tags (comma separated)</label> <div class="control"> <input id="tags" class="input" type="text" name="tags"> </div> </div> <div class="field"> <label class="label" for="description">Description</label> <div class="control"> <textarea id="description" class="textarea" name="description"></textarea> </div> </div> <!-- Prepare the JavaScript function for later --> <div class="control"> <button onclick="validateSubmission();" class="button is-link is-fullwidth">Submit</button> </div> </div> </div>

I’m using Bulma for styling, so the class names in use here are from that.

Now we write a JavaScript function that transforms a user’s input into a friendly URL that we can combine as GitHub query parameters on our pull request. Here is the step by step:

  • Get the user’s input about the content they want to add
  • Generate a string from all that content
  • Encode the string to format it in a way that humans can read
  • Attach the encoded string to a complete URL pointing to GitHub’s page for new pull requests

Here is the Pen:

CodePen Embed Fallback

After pressing the Submit button, the user is taken right to GitHub with an open pull request for this new file in the right location.

Quick caveat: Users still need a GitHub account to contribute. But this is still much easier than having to know how to fork a repo and create a pull request from that fork.

Other benefits of this approach

Well, for one, this is a form that lives on our site. We can style it however we want. That sort of control is always nice to have.

Secondly, since we’ve already written the JavaScript, we can use the same basic idea to talk with other services or APIs in order to process the input first. For example, if we need information from a website (like the title, meta description, or favicon) we can fetch this information just by providing the URL.

Taking things further

Let’s have a play with that second point above. We could simply pre-fill our form by fetching information from the URL provided for the user rather than having them have to enter it by hand.

With that in mind, let’s now only ask the user for two inputs (rather than four) — just the URL and tags.

CodePen Embed Fallback

How does this work? We can fetch meta information from a website with JavaScript just by having the URL. There are many APIs that fetch information from a website, but you might the one that I built for this project. Try hitting any URL like this:

https://metadata-api.vercel.app/api?url=https://css-tricks.com

The demo above uses that as an API to pre-fill data based on the URL the user provides. Easier for the user!

Wrapping up

You could think of this as a very minimal CMS for any kind of Static Site Generator. All you need to do is customize the form and update the pre-filled query parameters to match the data formats you need.

How will you use this sort of thing? The four sites we saw at the very beginning are good examples. But there are so many other times where might need to do something with a user submission, and this might be a low-lift way to do it.

The post Generate a Pull Request of Static Content With a Simple HTML Form appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Jamstack TV

Css Tricks - Tue, 11/16/2021 - 5:35am

That’s the name of Netlify’s YouTube Channel. Love that. I linked up Rich’s talk the other day, which was a part of this past JamstackConf, but now all the talks are up on there. Rich got to talk about Svelte, but there are talks on Astro, RedwoodJS, Eleventy, Vue… all the cool kids really.

I’m subscribed, of course. I’ve been on a real YouTube bender, spending most of my non-active screen time there. I’d say half of what I watch is tech content (like JamstackTV) and the rest is watching people play video games or puzzles, watching people talk about where they live or have traveled (preferably with an AMSR-friendly voice), and all the amazing recordings musicians put up for free(?!). I find if you subscribe to enough stuff you actually like, the algorithm really kicks in and finds you more stuff you’re very likely to enjoy. We’ve got a fairly active channel ourselves!

The post Jamstack TV appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

Syndicate content
©2003 - Present Akamai Design & Development.