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

@palmerhq/radio-group

v1.0.2

Published

An accessible [WAI-ARIA 1.1-compliant Radio Group](https://www.w3.org/TR/wai-aria-practices-1.1/#radiobutton) React component.

Downloads

254

Readme

@palmerhq/radio-group

An accessible WAI-ARIA 1.1-compliant Radio Group React component.

Installation

yarn add @palmerhq/radio-group

Or try it out in your browser on CodeSandbox

Note: This package uses Array.prototype.findIndex, so be sure that you have properly polyfilled.

Usage

import * as React from 'react';
import { RadioGroup, Radio } from '@palmerhq/radio-group';
import '@palmerhq/radio-group/styles.css'; // use the default styles

function App() {
  const [value, setValue] = React.useState<string | undefined>();

  return (
    <>
      <h3 id="color">Color</h3>
      <RadioGroup
        labelledBy="color"
        value={value}
        onChange={value => setValue(value)}
      >
        <Radio value="blue">Blue</Radio>
        <Radio value="red">Red</Radio>
        <Radio value="green">Green</Radio>
      </RadioGroup>
    </>
  );
}

Usage with Formik v2

import * as React from 'react';
import { Formik, Form, useField } from 'formik';
import { RadioGroup, Radio } from '@palmerhq/radio-group';
import '@palmerhq/radio-group/styles.css'; // use the default styles

function FRadioGroup(props) {
  const [{ onChange, onBlur, ...field }] = useField(props.name);
  return (
    <RadioGroup
      {...props}
      {...field}
      labelledBy={props.name}
      onBlur={onBlur(props.name)}
      onChange={onChange(props.name)}
    />
  );
}

function App() {
  return (
    <Formik
      initialValues={{ color: '' }}
      validationSchema={Yup.object().shape({
        color: Yup.string().required(),
      })}
      onSubmit={(values, { setSubmitting }) => {
        setTimeout(() => {
          alert(JSON.stringify(values, null, 2));
          setSubmitting(false);
        }, 500);
      }}
    >
      <Form>
        <h3 id="color">Color</h3>
        <FRadioGroup name="color">
          <Radio value="blue">Blue</Radio>
          <Radio value="red">Red</Radio>
          <Radio value="green">Green</Radio>
        </FRadioGroup>
      </Form>
    </Formik>
  );
}

API Reference

<RadioGroup />

This renders a div and will pass through all props to the DOM element. It's children must be <Radio> components.

labelledBy?: string

This should match the id you used to label the radio group.

<h3 id="color">Color</h3>
<RadioGroup labelledBy="color">
  {/* ... */}
</RadioGroup>

onChange: (value: any) => void

A callback function that will be fired with the value of the newly selected item.

import * as React from 'react';
import { RadioGroup, Radio } from '@palmerhq/radio-group';
import '@palmerhq/radio-group/styles.css'; // use the default styles

function App() {
  const [value, setValue] = React.useState<string | undefined>();

  return (
    <>
      <h3 id="color">Color</h3>
      <RadioGroup
        labelledBy="color"
        value={value}
        onChange={value => setValue(value)}
      >
        <Radio value="blue">Blue</Radio>
        <Radio value="red">Red</Radio>
        <Radio value="green">Green</Radio>
      </RadioGroup>
    </>
  );
}

children: React.ComponentType<RadioProps>[]

Required

The children of a <RadioGroup> can ONLY be <Radio> components. In order to support compliant keyboard behavior, each sibling must know the value of the whole group and so React.Children.map is used internally.

<h3 id="color">Color</h3>
<RadioGroup labelledBy="color">
  {/* ... */}
</RadioGroup>

value: any

Required

The current value of the radio group. This is shallowly compared to each value prop of the child <Radio> components to determine which item is active.

as?: React.ComponentType

Component to use a the wrapper. Default is <div>.

autoFocus?: boolean

Whether to autoFocus the selected radio option.

<Radio>

This renders a div with a data attribute data-palmerhq-radio and all the relevant perfect aria attributes. The React component will pass through all props to the DOM element.

value: any

Required

The value of the radio button. This will be set / passed back to the <RadioGroup onChange> when the item is selected.

onFocus?: () => void

Callback function for when the item is focused. When focused, a data attribute data-palmerhq-radio-focus is set to "true". You can thus apply the selector to manage focus style like so:

[data-palmerhq-radio][data-palmerhq-radio-focus='true'] {
  background: blue;
}

onBlur?: () => void

Callback function for when the item is blurred

as?: React.ComponentType

Component to use as radio. Default is <div>.

Underlying DOM Structure

For reference, the underlying HTML DOM structure are all divs and looks as follows.

<div role="radiogroup" aria-labelledby="color" data-palmerhq-radio-group="true">
  <div
    role="radio"
    tabindex="0"
    aria-checked="false"
    data-palmerhq-radio="true"
    data-palmerhq-radio-focus="false"
  >
    Red
  </div>
  <div
    role="radio"
    tabindex="-1"
    aria-checked="false"
    data-palmerhq-radio="true"
    data-palmerhq-radio-focus="false"
  >
    Green
  </div>
  <div
    role="radio"
    tabindex="-1"
    aria-checked="false"
    data-palmerhq-radio="true"
    data-palmerhq-radio-focus="false"
  >
    Blue
  </div>
</div>

Overriding Styles

These are the default styles. Copy and paste the following into your app to customize them.

[data-palmerhq-radio-group] {
  padding: 0;
  margin: 0;
  list-style: none;
}

[data-palmerhq-radio-group]:focus {
  outline: none;
}

[data-palmerhq-radio] {
  border: 2px solid transparent;
  border-radius: 5px;
  display: inline-block;
  position: relative;
  padding: 0.125em;
  padding-left: 1.5em;
  padding-right: 0.5em;
  cursor: default;
  outline: none;
}

[data-palmerhq-radio] + [data-palmerhq-radio] {
  margin-left: 1em;
}

[data-palmerhq-radio]::before,
[data-palmerhq-radio]::after {
  position: absolute;
  top: 50%;
  left: 7px;
  transform: translate(-20%, -50%);
  content: '';
}

[data-palmerhq-radio]::before {
  width: 14px;
  height: 14px;
  border: 1px solid hsl(0, 0%, 66%);
  border-radius: 100%;
  background-image: linear-gradient(to bottom, hsl(300, 3%, 93%), #fff 60%);
}

[data-palmerhq-radio]:active::before {
  background-image: linear-gradient(
    to bottom,
    hsl(300, 3%, 73%),
    hsl(300, 3%, 93%)
  );
}

[data-palmerhq-radio][aria-checked='true']::before {
  border-color: hsl(216, 80%, 50%);
  background: hsl(217, 95%, 68%);
  background-image: linear-gradient(
    to bottom,
    hsl(217, 95%, 68%),
    hsl(216, 80%, 57%)
  );
}

[data-palmerhq-radio][aria-checked='true']::after {
  display: block;
  border: 0.1875em solid #fff;
  border-radius: 100%;
  transform: translate(25%, -50%);
}

[data-palmerhq-radio][aria-checked='mixed']:active::before,
[data-palmerhq-radio][aria-checked='true']:active::before {
  background-image: linear-gradient(
    to bottom,
    hsl(216, 80%, 57%),
    hsl(217, 95%, 68%) 60%
  );
}

[data-palmerhq-radio]:hover::before {
  border-color: hsl(216, 94%, 65%);
}

[data-palmerhq-radio][data-palmerhq-radio-focus='true'] {
  border-color: hsl(216, 94%, 73%);
  background-color: hsl(216, 80%, 97%);
}

[data-palmerhq-radio]:hover {
  background-color: hsl(216, 80%, 92%);
}

Accessibility Features

  • Uses CSS attribute selectors for synchronizing aria-checked state with the visual state indicator.
  • Uses CSS :hover and :focus pseudo-selectors for styling visual keyboard focus and hover.
  • Focus indicator encompasses both radio button and label, making it easier to perceive which option is being chosen.
  • Hover changes background of both radio button and label, making it easier to perceive that clicking either the label or button will activate the radio button.

Authors


MIT License