Skip to content

Instantly share code, notes, and snippets.

@brigand
Created November 29, 2018 22:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brigand/c01be7e7343ad62e85fe43a2d7f08e63 to your computer and use it in GitHub Desktop.
Save brigand/c01be7e7343ad62e85fe43a2d7f08e63 to your computer and use it in GitHub Desktop.
Accessible Custom Radio Input for React
const defaultItemRender = (props, active, children) => (
<FlexCol className={[s.label, active && s.active]} {...props}>
{children}
</FlexCol>
);
export default class RadioSet extends React.Component<Props> {
baseId = `Radio-${++idCounter}`;
render() {
const activeIndexes = [].concat(this.props.activeIndexes);
const hidden = [];
const ui = [];
this.props.items.forEach((item, i) => {
const active = activeIndexes.includes(i);
const id = `${this.baseId}-${String(i).padStart(3, '0')}`;
const className = [s.label, active && s.active];
const uiProps = {
tag: 'label',
onMouseDown: (event) => {
event.preventDefault();
},
key: `${id}::label`,
tabIndex: -1,
htmlFor: id,
};
if (typeof item === 'function') {
ui.push(item(uiProps, active));
} else {
ui.push(
defaultItemRender(
uiProps,
active,
<React.Fragment>
{item}
<Box className={s.activeAccent} />
</React.Fragment>,
),
);
}
hidden.push(
<input
checked={active}
type="radio"
name={this.props.name || this.baseId}
onChange={() => this.props.onSelect(i)}
key={`${id}::input`}
className={`${s.input} sr-only`}
value={i}
id={id}
tabIndex={0}
/>,
);
});
const Comp = this.props.direction === 'column' ? FlexCol : FlexRow;
const restProps = omit(this.props, [
'name',
'items',
'activeIndexes',
'onSelect',
'direction',
'tt',
]);
return (
<fieldset className={[s.root, s.fieldset].filter(Boolean).join(' ')}>
<Comp tag="div" data-tt={this.props.tt} {...restProps}>
{hidden}
{ui}
</Comp>
</fieldset>
);
}
}
@import '../scss/colors';
.root {
}
.fieldset {
display: block;
width: 100%;
}
.label {
/* put display properties here */
position: relative;
cursor: pointer;
}
.activeAccent {
/* in this example, we display an underline on the active radio */
/* styles omitted */
}
.activeAccent::after {
/* styles omitted */
}
@for $i from 1 through 10 {
/* focus styles */
.input:nth-of-type(#{$i}):focus ~ .label:nth-of-type(#{$i}) {
box-shadow: 0 0 3px 1px $r-secondary, inset 0 0 3px $r-secondary;
}
/* active styles */
.input:nth-of-type(#{$i}):checked ~ .label:nth-of-type(#{$i}) {
color: $r-secondary;
.activeAccent {
opacity: 1;
color: inherit;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment