Skip to content
107 changes: 107 additions & 0 deletions src/components/Checkbox/Checkbox.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/* Checkbox label styles */
.checkbox-label {
font-size: 1.6rem;
display: flex;
align-items: center;
font-weight: var(--checkbox-font-weight, 400);
color: var(--checkbox-color, inherit);
}

.checkbox-label--disabled {
color: var(--checkbox-disabled-color, #e5e5e5);
}

/* Checkbox input/selection styles */
.checkbox-input {
appearance: none;
background-color: var(--checkbox-bg-unchecked, #ffffff);
opacity: var(--checkbox-opacity, 1);
border: var(--checkbox-border-unchecked, 1px solid #6f6f6f);
border-radius: 0.2rem;
transform: translateY(-0.075em);
width: var(--checkbox-size, 1.6rem);
height: var(--checkbox-size, 1.6rem);
margin: 0 1.6rem 0 0;
display: grid;
place-content: center;
}

.checkbox-input::before {
content: "";
border-radius: 0.2rem;
width: var(--checkbox-size, 1.6rem);
height: var(--checkbox-size, 1.6rem);
border: var(--checkbox-border-checked, 1px solid #026AA1);
transform: scale(0);
background-color: var(--checkbox-bg, #026AA1);
background-image: var(--checkbox-checkmark, none);
background-size: 80%;
background-position: center;
background-repeat: no-repeat;
}

.checkbox-input:checked::before {
transform: scale(1);
opacity: var(--checkbox-checked-opacity, 1);
}

.checkbox-input--disabled {
opacity: 0.4;
border: var(--checkbox-disabled-border, 1px solid #d5d5d5);
}

.checkbox-input--disabled:checked::before {
opacity: 0;
}

/* TreeCheckbox specific styles */
.checkbox-label [data-slot="selection"] {
appearance: none;
background-color: var(--checkbox-bg-unchecked, #ffffff);
opacity: var(--checkbox-opacity, 1);
border: var(--checkbox-border-unchecked, 1px solid #6f6f6f);
border-radius: 0.2rem;
transform: translateY(-0.075em);
width: var(--checkbox-size, 1.6rem);
height: var(--checkbox-size, 1.6rem);
margin: 0 1.6rem 0 0;
display: grid;
place-content: center;
}

.checkbox-label [data-slot="selection"]::before {
content: "";
border-radius: 0.2rem;
width: var(--checkbox-size, 1.6rem);
height: var(--checkbox-size, 1.6rem);
border: var(--checkbox-border-checked, 1px solid #026AA1);
transform: scale(0);
background-color: var(--checkbox-bg, #026AA1);
background-image: var(--checkbox-checkmark, none);
background-size: 80%;
background-position: center;
background-repeat: no-repeat;
}

.checkbox-label[data-selected] [data-slot="selection"]::before {
transform: scale(1);
opacity: var(--checkbox-checked-opacity, 1);
}

.checkbox-label[data-indeterminate="true"] [data-slot="selection"]::before {
content: "";
position: relative;
transform: scale(1);
background-color: var(--checkbox-indeterminate-bg, #026AA1);
border: none;
background-image: var(--checkbox-indeterminate-icon, none);
}

.checkbox-label--disabled [data-slot="selection"] {
opacity: 0.4;
border: var(--checkbox-disabled-border, 1px solid #d5d5d5);
}

.checkbox-label--disabled[data-selected] [data-slot="selection"]::before {
opacity: 0;
}
68 changes: 53 additions & 15 deletions src/components/Checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
import type React from "react";
import { LabelHTMLAttributes, PropsWithChildren } from "react";
Comment thread
RoyEJohnson marked this conversation as resolved.
import { checkboxLabelStyles, checkboxInputStyles, CheckboxVariant, CheckboxSize } from "./sharedCheckboxStyles";
import styled from "styled-components";
import { checkboxVariants, CheckboxVariant, CheckboxSize } from "./sharedCheckboxStyles";
import { InputHTMLAttributes } from "react";

const StyledLabel = styled.label<{ bold: boolean; variant: CheckboxVariant; isDisabled?: boolean; }>`
${checkboxLabelStyles}
`;

// https://moderncss.dev/pure-css-custom-checkbox-style/
const StyledInput = styled.input<{ variant: CheckboxVariant; checkboxSize: CheckboxSize; isDisabled?: boolean; }>`
${checkboxInputStyles}
`;
import { colors } from "../../theme";
import classNames from "classnames";
import "./Checkbox.css";

type CheckboxProps = PropsWithChildren<
Omit<InputHTMLAttributes<HTMLInputElement>, 'type'> & {
Expand All @@ -20,11 +14,55 @@ type CheckboxProps = PropsWithChildren<
labelProps?: LabelHTMLAttributes<HTMLLabelElement>;
}>;

export const Checkbox = ({ children, disabled, variant = 'primary', bold = false, size = 1.6, labelProps, ...props }: CheckboxProps) => {
export const Checkbox = ({ children, disabled, variant = 'primary', bold = false, size = 1.6, labelProps, className, style, ...props }: CheckboxProps) => {
// Get variant styles for CSS variables
const variantStyles = disabled ? checkboxVariants.disabled : checkboxVariants[variant];

// Merge labelProps className with our label classes
const labelClassName = classNames(
'checkbox-label',
{ 'checkbox-label--disabled': disabled },
labelProps?.className
);

// Merge labelProps style with our CSS variables
const labelStyle = {
'--checkbox-font-weight': bold ? 700 : 400,
'--checkbox-color': variantStyles.color,
'--checkbox-disabled-color': colors.palette.neutralLight,
...labelProps?.style
} as unknown as React.CSSProperties;

// Merge input className
const inputClassName = classNames(
'checkbox-input',
{ 'checkbox-input--disabled': disabled },
className
);

// Merge input style with our CSS variables
const inputStyle = {
'--checkbox-size': `${size}rem`,
'--checkbox-bg-unchecked': colors.palette.white,
'--checkbox-bg': variantStyles.backgroundColor,
'--checkbox-border-unchecked': variantStyles.unCheckedBorder,
'--checkbox-border-checked': variantStyles.checkedBorder,
'--checkbox-checkmark': variantStyles.backgroundImage === 'none' ? 'none' : `url('${variantStyles.backgroundImage}')`,
'--checkbox-opacity': disabled ? '0.4' : '1',
'--checkbox-disabled-border': `1px solid ${colors.palette.pale}`,
...style
} as unknown as React.CSSProperties;

return (
<StyledLabel bold={bold} variant={variant} isDisabled={disabled} {...labelProps}>
<StyledInput {...props} type="checkbox" variant={variant} checkboxSize={size} isDisabled={disabled} disabled={disabled} />
<label {...labelProps} className={labelClassName} style={labelStyle}>
<input
{...props}
type="checkbox"
className={inputClassName}
style={inputStyle}
disabled={disabled}
/>
{children}
</StyledLabel>
</label>
);
};
92 changes: 84 additions & 8 deletions src/components/Checkbox/__snapshots__/Checkbox.spec.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,29 @@
exports[`Checkbox allows setting props on label 1`] = `
<label
aria-label="custom label"
className="sc-bczRLJ XfrOB"
className="checkbox-label"
style={
Object {
"--checkbox-color": "inherit",
"--checkbox-disabled-color": "#e5e5e5",
"--checkbox-font-weight": 700,
}
}
>
<input
className="sc-gsnTZi dskNep"
className="checkbox-input"
style={
Object {
"--checkbox-bg": "#ffffff",
"--checkbox-bg-unchecked": "#ffffff",
"--checkbox-border-checked": "1px solid #d5d5d5",
"--checkbox-border-unchecked": "1px solid #d5d5d5",
"--checkbox-checkmark": "url('data:image/svg+xml,<svg height=\\"125px\\" width=\\"125px\\" version=\\"1.1\\" id=\\"Capa_1\\" xmlns=\\"http://www.w3.org/2000/svg\\" xmlns:xlink=\\"http://www.w3.org/1999/xlink\\" viewBox=\\"0 0 17.837 17.837\\" xml:space=\\"preserve\\" fill=\\"%23000000\\"><g id=\\"SVGRepo_bgCarrier\\" stroke-width=\\"0\\"></g><g id=\\"SVGRepo_tracerCarrier\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\"></g><g id=\\"SVGRepo_iconCarrier\\"><g><path style=\\"fill:%235e5e5e;\\" d=\\"M16.145,2.571c-0.272-0.273-0.718-0.273-0.99,0L6.92,10.804l-4.241-4.27 c-0.272-0.274-0.715-0.274-0.989,0L0.204,8.019c-0.272,0.271-0.272,0.717,0,0.99l6.217,6.258c0.272,0.271,0.715,0.271,0.99,0 L17.63,5.047c0.276-0.273,0.276-0.72,0-0.994L16.145,2.571z\\"></path></g></g></svg>')",
"--checkbox-disabled-border": "1px solid #d5d5d5",
"--checkbox-opacity": "1",
"--checkbox-size": "2rem",
}
}
type="checkbox"
/>
Click Me
Expand All @@ -15,11 +34,30 @@ exports[`Checkbox allows setting props on label 1`] = `

exports[`Checkbox handles disabled state 1`] = `
<label
className="sc-bczRLJ jDFcYw"
className="checkbox-label checkbox-label--disabled"
style={
Object {
"--checkbox-color": "inherit",
"--checkbox-disabled-color": "#e5e5e5",
"--checkbox-font-weight": 400,
}
}
>
<input
className="sc-gsnTZi dJvfbo"
className="checkbox-input checkbox-input--disabled"
disabled={true}
style={
Object {
"--checkbox-bg": "#ffffff",
"--checkbox-bg-unchecked": "#ffffff",
"--checkbox-border-checked": "1px solid #d5d5d5",
"--checkbox-border-unchecked": "1px solid #d5d5d5",
"--checkbox-checkmark": "none",
"--checkbox-disabled-border": "1px solid #d5d5d5",
"--checkbox-opacity": "0.4",
"--checkbox-size": "1.6rem",
}
}
type="checkbox"
/>
Click Me
Expand All @@ -28,10 +66,29 @@ exports[`Checkbox handles disabled state 1`] = `

exports[`Checkbox handles options 1`] = `
<label
className="sc-bczRLJ XfrOB"
className="checkbox-label"
style={
Object {
"--checkbox-color": "inherit",
"--checkbox-disabled-color": "#e5e5e5",
"--checkbox-font-weight": 700,
}
}
>
<input
className="sc-gsnTZi dskNep"
className="checkbox-input"
style={
Object {
"--checkbox-bg": "#ffffff",
"--checkbox-bg-unchecked": "#ffffff",
"--checkbox-border-checked": "1px solid #d5d5d5",
"--checkbox-border-unchecked": "1px solid #d5d5d5",
"--checkbox-checkmark": "url('data:image/svg+xml,<svg height=\\"125px\\" width=\\"125px\\" version=\\"1.1\\" id=\\"Capa_1\\" xmlns=\\"http://www.w3.org/2000/svg\\" xmlns:xlink=\\"http://www.w3.org/1999/xlink\\" viewBox=\\"0 0 17.837 17.837\\" xml:space=\\"preserve\\" fill=\\"%23000000\\"><g id=\\"SVGRepo_bgCarrier\\" stroke-width=\\"0\\"></g><g id=\\"SVGRepo_tracerCarrier\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\"></g><g id=\\"SVGRepo_iconCarrier\\"><g><path style=\\"fill:%235e5e5e;\\" d=\\"M16.145,2.571c-0.272-0.273-0.718-0.273-0.99,0L6.92,10.804l-4.241-4.27 c-0.272-0.274-0.715-0.274-0.989,0L0.204,8.019c-0.272,0.271-0.272,0.717,0,0.99l6.217,6.258c0.272,0.271,0.715,0.271,0.99,0 L17.63,5.047c0.276-0.273,0.276-0.72,0-0.994L16.145,2.571z\\"></path></g></g></svg>')",
"--checkbox-disabled-border": "1px solid #d5d5d5",
"--checkbox-opacity": "1",
"--checkbox-size": "2rem",
}
}
type="checkbox"
/>
Click Me
Expand All @@ -40,10 +97,29 @@ exports[`Checkbox handles options 1`] = `

exports[`Checkbox matches snapshot 1`] = `
<label
className="sc-bczRLJ fIuhsq"
className="checkbox-label"
style={
Object {
"--checkbox-color": "inherit",
"--checkbox-disabled-color": "#e5e5e5",
"--checkbox-font-weight": 400,
}
}
>
<input
className="sc-gsnTZi ehAXiz"
className="checkbox-input"
style={
Object {
"--checkbox-bg": "#026AA1",
"--checkbox-bg-unchecked": "#ffffff",
"--checkbox-border-checked": "1px solid #026AA1",
"--checkbox-border-unchecked": "1px solid #6f6f6f",
"--checkbox-checkmark": "url('data:image/svg+xml,<svg height=\\"125px\\" width=\\"125px\\" version=\\"1.1\\" id=\\"Capa_1\\" xmlns=\\"http://www.w3.org/2000/svg\\" xmlns:xlink=\\"http://www.w3.org/1999/xlink\\" viewBox=\\"0 0 17.837 17.837\\" xml:space=\\"preserve\\" fill=\\"%23000000\\"><g id=\\"SVGRepo_bgCarrier\\" stroke-width=\\"0\\"></g><g id=\\"SVGRepo_tracerCarrier\\" stroke-linecap=\\"round\\" stroke-linejoin=\\"round\\"></g><g id=\\"SVGRepo_iconCarrier\\"><g><path style=\\"fill:%23fff;\\" d=\\"M16.145,2.571c-0.272-0.273-0.718-0.273-0.99,0L6.92,10.804l-4.241-4.27 c-0.272-0.274-0.715-0.274-0.989,0L0.204,8.019c-0.272,0.271-0.272,0.717,0,0.99l6.217,6.258c0.272,0.271,0.715,0.271,0.99,0 L17.63,5.047c0.276-0.273,0.276-0.72,0-0.994L16.145,2.571z\\"></path></g></g></svg>')",
"--checkbox-disabled-border": "1px solid #d5d5d5",
"--checkbox-opacity": "1",
"--checkbox-size": "1.6rem",
}
}
type="checkbox"
/>
Click Me
Expand Down
Loading
Loading