npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@washingtonpost/wpds-input-text

v1.24.0

Published

WPDS InputText

Downloads

1,367

Readme

InputText

Handling browser autofill

It is now standard practice for browsers to save users information and use it to autofill forms for them to provide users with the best experience.

Reasons this can be tricky:

  • Browsers override our styles with theirs in these situations. This means they add a blue or yellow background on the inputs that have been autofilled and that could clash with our styles.
  • Autofilling a form does not trigger any events on the input. This can cause issues if, for instance, you have a floating label that is triggered solely by JS like we do.

Tackling the first problem isn't too difficult. All we have to do is apply the styles that we want targeting the pseudo element that the browsers add. We use -webkit-box-shadow to override the yellow/blue background color the browser adds to the input. We also need to set the color of the font - especially so that you can read the text in dark mode.

"&:-webkit-autofill": {
    "-webkit-box-shadow": `0 0 0 100px ${theme.colors.secondary} inset`,
    "-webkit-text-fill-color": `${theme.colors.primary}`,
  },

The second problem is trickier to solve. How can we make our label float when no event is triggered on the autofill?

Approaches we can take:

  • Use an interval/timeout to check whether the pseudo class has been applied.
    • This is not the best approach because it can make the page slower. Also,We are working on a component level, which means that each input component on the form would have one of these timers. Imagine a page full of inputs with each input having a never-ending interval! The horror!
  • Use the :has selector.
    • This could work, but it's not the best solution. We would have to poll the input to see if the selector has been applied. Additionally, the :has selector is still a draft and not fully supported by all browsers.
  • Listen for an animation change and use that to trigger label.
    • This is the solution we ultimately went with. A more thorough explanation can be found below, but essentially add an animation to the autofill pseudo class selectors and use JS to listen for the change.

Our solution

From all the possible approaches, we opted to go with listening to the animation. This solution was adapted from a solution Klarna UI has used in the past.

To start, we had to add the animation on the autofill pseudo selectors. We created two animations and called them inside the autofill selectors. Note: We need to add these animations to the globalCss because stitches will change the class names and we won't be able to match them inside of our component. We couldn't just add them to globalStyles, however, because not all teams have adopted and/or are using our global styles.

export const globalInputAutoFillTriggerAnimations = globalCss({
  "@keyframes jsTriggerAutoFillStart": {
    from: {
      alpha: 1,
    },
  },
  "@keyframes jsTriggerAutoFillCancel": {
    from: {
      alpha: 1,
    },
  },
});
export const unstyledInputStyles = {
  "&:-webkit-autofill": {
    "-webkit-box-shadow": `0 0 0 100px ${theme.colors.secondary} inset`,
    "-webkit-text-fill-color": `${theme.colors.primary}`,
    // used to trigger JS so that we can do the label shrinking
    animation: "jsTriggerAutoFillStart 200ms",
  },

  "&:not(:-webkit-autofill)": {
    // used to trigger JS so that we can stop the label shrinking
    animation: "jsTriggerAutoFillCancel 200ms",
  },

  "@reducedMotion": {
    animation: "none",
  },
};

In our component we now have to create an event listener to listen to these animations. We need to check the animationName to make sure that it matches either the start or cancel, so that our label floats or stays normal accordingly.

For this component, we also accept a reference from outside and have an interal reference to keep track of our listener. We have to make sure that we don't forget about the external reference.

export const InputText = () => {
  // This useEffect checks whether we have an external reference.
  // If so, then we take it into account
  useEffect(() => {
    if (!ref) return;

    if (typeof ref === "function") {
      ref(internalRef.current);
    } else {
      ref.current = internalRef.current;
    }
  }, [ref, internalRef]);

  useEffect(() => {
    const element = internalRef.current;

    const onAnimationStart = (e) => {
      // This switch case will not work due to the way stitches does classes
      switch (e.animationName) {
        case "jsTriggerAutoFillStart":
          return setIsAutofilled(true);
        case "jsTriggerAutoFillCancel":
          return setIsAutofilled(false);
      }
    };

    element?.addEventListener("animationstart", onAnimationStart, false);

    // don't forget to clean up your listener
    return () => {
      element?.removeEventListener("animationstart", onAnimationStart, false);
    };
  });
};

Some extra resources:

  • https://medium.com/@brunn/detecting-autofilled-fields-in-javascript-aed598d25da7
  • https://stackoverflow.com/questions/11708092/detecting-browser-autofill?page=1&tab=scoredesc#tab-top
  • https://github.com/mui/material-ui/issues/22488
  • https://github.com/mui/material-ui/issues/14427
  • http://webagility.com/posts/the-ultimate-list-of-hacks-for-chromes-forced-yellow-background-on-autocompleted-inputs
  • https://stackoverflow.com/questions/22631943/change-font-color-of-autofill-input-field
  • https://developer.mozilla.org/en-US/docs/Web/CSS/:has