Focus for input type range
luangjokaj opened this issue Β· comments
Describe the issue
I have been playing around with Stylex lately and I absolutely love it. I am already re-writing our design system based on this technology. So first thing first congrats on that π―
I am currently styling input ranges (<input type="range" />
). I can style the :hover
and :active
states without any problem, but for some reason :focus
doesn't behave the same way.
As we know input ranges are a bit more complex to be styled, as we need to target also pseudo-elements to make it look consistent across browsers. In this case i am using:
- ::-webkit-slider-runnable-track
- ::-moz-range-track
- ::-webkit-slider-thumb
- ::-moz-range-thumb
Expected behavior
The expected behaviour is that :focus
should work just the same way as as :hover
, or `:active.
Steps to reproduce
":hover": {
"::-webkit-slider-runnable-track": {
border: `solid 2px ${colors.secondary}`, // β
this works
},
},
":focus": {
border: "solid 2px red", // β
this works
"::-webkit-slider-runnable-track": {
border: `solid 2px ${colors.secondary}`, // β doesn't work
boxShadow: `0 0 0 4px ${colors.secondaryLight}`, // β doesn't work
},
},
":active": {
"::-webkit-slider-runnable-track": {
border: `solid 2px ${colors.secondary}`, // β
this works
boxShadow: `0 0 0 2px ${colors.secondaryLight}`, // β
this works
},
},
Test case
No response
Additional comments
Here is the entire components with styles included:
"use client";
import React from "react";
import * as stylex from "@stylexjs/stylex";
import { genericStyles } from "./generic";
import { colors } from "../../tokens.stylex";
interface InputProps
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "size"> {
className?: string;
label?: string;
size?: "default" | "big";
error?: boolean;
success?: boolean;
fullWidth?: boolean;
}
const styles = stylex.create({
range: {
padding: 0,
height: 10,
fontSize: 0,
boxShadow: "none",
border: "none",
background: "transparent",
"::-webkit-slider-runnable-track": {
width: "100%",
cursor: "pointer",
borderRadius: 25,
border: `solid 2px ${colors.grayLight}`,
transition: "all 0.3s ease",
boxShadow: `0 0 0 0 ${colors.secondaryLight}`,
},
"::-webkit-slider-thumb": {
appearance: "none",
padding: 0,
margin: 0,
cursor: "pointer",
background: colors.secondary,
border: "0 solid transparent",
borderRadius: "50%",
transition: "all 0.3s ease",
},
"::-moz-range-track": {
width: "100%",
cursor: "pointer",
borderRadius: 25,
border: `solid 2px ${colors.grayLight}`,
transition: "all 0.3s ease",
boxShadow: `0 0 0 0 ${colors.secondaryLight}`,
},
"::-moz-range-thumb": {
appearance: "none",
padding: 0,
margin: 0,
cursor: "pointer",
background: colors.secondary,
border: "0 solid transparent",
borderRadius: "50%",
transition: "all 0.3s ease",
},
},
default: {
minWidth: 130,
height: 32,
"::-webkit-slider-runnable-track": {
height: 10,
},
"::-webkit-slider-thumb": {
width: 22,
height: 22,
marginTop: -8,
},
"::-moz-range-track": {
height: 6,
},
"::-moz-range-thumb": {
width: 22,
height: 22,
},
},
big: {
minWidth: 200,
height: 32,
"::-webkit-slider-runnable-track": {
height: 14,
},
"::-webkit-slider-thumb": {
width: 32,
height: 32,
marginTop: -11,
},
"::-moz-range-track": {
height: 10,
},
"::-moz-range-thumb": {
width: 32,
height: 32,
},
},
actions: {
":hover": {
"::-webkit-slider-runnable-track": {
border: `solid 2px ${colors.secondary}`,
},
"::-moz-range-track": {
border: `solid 2px ${colors.secondary}`,
},
},
":focus": {
"::-webkit-slider-runnable-track": {
border: `solid 2px ${colors.secondary}`,
boxShadow: `0 0 0 4px ${colors.secondaryLight}`,
},
"::-webkit-slider-thumb": {
boxShadow: `0 0 0 4px ${colors.secondaryLight}`,
},
"::-moz-range-track": {
border: `solid 2px ${colors.secondary}`,
boxShadow: `0 0 0 4px ${colors.secondaryLight}`,
},
"::-moz-range-thumb": {
boxShadow: `0 0 0 4px ${colors.secondaryLight}`,
},
},
":active": {
"::-webkit-slider-runnable-track": {
border: `solid 2px ${colors.secondary}`,
boxShadow: `0 0 0 2px ${colors.secondaryLight}`,
},
"::-webkit-slider-thumb": {
boxShadow: `0 0 0 2px ${colors.secondaryLight}`,
},
"::-moz-range-track": {
border: `solid 2px ${colors.secondary}`,
boxShadow: `0 0 0 2px ${colors.secondaryLight}`,
},
"::-moz-range-thumb": {
boxShadow: `0 0 0 2px ${colors.secondaryLight}`,
},
},
},
disabled: {
cursor: "not-allowed",
"::-webkit-slider-runnable-track": {
background: colors.grayLight,
cursor: "not-allowed",
},
"::-webkit-slider-thumb": {
background: colors.gray,
cursor: "not-allowed",
},
"::-moz-range-track": {
background: colors.grayLight,
cursor: "not-allowed",
},
"::-moz-range-thumb": {
background: colors.gray,
cursor: "not-allowed",
},
},
fullWidth: {
width: "100%",
},
});
export default function RangeSlider({
className,
label,
size = "default",
error,
success,
fullWidth,
disabled,
...props
}: InputProps) {
return (
<span {...stylex.props(fullWidth && styles.fullWidth)}>
<input
className={className}
type="range"
{...stylex.props(
genericStyles.resetButton,
styles.range,
!disabled && styles.actions,
styles[size],
fullWidth && styles.fullWidth,
disabled && styles.disabled,
)}
{...props}
disabled={disabled}
/>
{label && <label htmlFor={props.id}>{label}</label>}
</span>
);
}
What you're trying to achieve is essentially a descendent selector which isn't officially supported. This kind of case is what is what we are still trying to solve.
You will need to use CSS variables as a workaround for this.