Skip to content

Commit 8772cf4

Browse files
feat(vara-ui): add transitions and dark mode to Checkbox (#1690)
Co-authored-by: Nikita Yutanov <[email protected]>
1 parent 9260f69 commit 8772cf4

File tree

4 files changed

+201
-33
lines changed

4 files changed

+201
-33
lines changed
Loading
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,179 @@
1+
@use '../../utils' as *;
2+
13
.label {
4+
--color: #000;
5+
--error-color: rgba(255, 51, 51, 0.8);
6+
7+
@include darkMode() {
8+
--color: #fff;
9+
--error-color: #d73b4f;
10+
}
11+
212
display: flex;
313
align-items: center;
14+
gap: var(--gap);
15+
16+
font-size: var(--font-size);
17+
font-weight: 400;
18+
line-height: var(--line-height);
19+
color: var(--color);
20+
421
cursor: pointer;
522

6-
&.disabled {
23+
&:has(.input:disabled) {
724
pointer-events: none;
8-
opacity: 0.3;
925
}
1026
}
1127

1228
.input {
1329
appearance: none;
14-
cursor: inherit;
15-
margin: 0 10px 0 0;
30+
margin: 0;
1631
}
1732

18-
.checkbox {
19-
width: 15px;
20-
height: 15px;
21-
background-image: url("data:image/svg+xml,%3Csvg width='15' height='15' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='.5' y='.5' width='14' height='14' rx='1.5' stroke='%23909090'/%3E%3C/svg%3E");
33+
.checkbox.box {
34+
--background-color: transparent;
35+
--border-color: #d0d5dd;
36+
--disabled-background-color: #eceded;
37+
--checked-color: #00ffc4;
38+
39+
@include darkMode() {
40+
--background-color: rgba(255, 255, 255, 0.03);
41+
--border-color: rgba(255, 255, 255, 0.04);
42+
--disabled-background-color: rgba(40, 44, 48, 0.1);
43+
--checked-color: #30ffcf;
44+
}
45+
46+
width: var(--checkbox-size);
47+
height: var(--checkbox-size);
48+
49+
display: flex;
50+
align-items: center;
51+
52+
background-color: var(--background-color);
53+
border: 1px solid var(--border-color);
54+
border-radius: 2px;
55+
56+
transition: 0.25s border-color ease, 0.25s background-color ease;
57+
58+
&::before {
59+
content: '';
60+
opacity: 0;
61+
62+
width: 100%;
63+
height: 100%;
64+
65+
background-color: #000;
66+
mask: url(./assets/check.svg) center/cover no-repeat;
67+
68+
transition: 0.25s opacity ease, 0.25s background-color ease;
69+
70+
.input:checked + & {
71+
opacity: 1;
72+
}
73+
74+
.input:not(:disabled)[aria-invalid='true'] + & {
75+
background-color: #fff;
76+
}
77+
78+
.input:disabled + & {
79+
background-color: var(--border-color);
80+
}
81+
}
82+
83+
.input:not(:disabled):checked + & {
84+
background-color: var(--checked-color);
85+
border-color: var(--checked-color);
86+
}
87+
88+
.input:not(:disabled)[aria-invalid='true'] + & {
89+
border-color: var(--error-color);
90+
}
91+
92+
.input:not(:disabled):checked[aria-invalid='true'] + & {
93+
background-color: var(--error-color);
94+
}
2295

23-
&:checked {
24-
background-image: url("data:image/svg+xml,%3Csvg width='15' height='15' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='.5' y='.5' width='14' height='14' rx='1.5' stroke='%230ED3A3'/%3E%3Cpath d='M12.163 4.45a.75.75 0 0 1 0 1.058l-5.595 5.955a.75.75 0 0 1-.547.232.75.75 0 0 1-.548-.232L3.643 9.49a.75.75 0 1 1 1.088-1.027l1.32 1.402 5.047-5.37a.75.75 0 0 1 1.065-.045Z' fill='%230ED3A3'/%3E%3C/svg%3E");
96+
.input:disabled + & {
97+
background-color: var(--disabled-background-color);
2598
}
2699
}
27100

28-
.switch {
29-
width: 25px;
101+
.switch.box {
102+
--border-color: #909090;
103+
--checked-color: #0ed3a3;
104+
--disabled-color: rgba(144, 144, 144, 0.3);
105+
106+
@include darkMode() {
107+
--border-color: rgba(255, 255, 255, 0.08);
108+
--checked-color: #00ffc4;
109+
--disabled-color: rgba(255, 255, 255, 0.03);
110+
}
111+
112+
width: 26px;
30113
height: 15px;
31-
background-image: url("data:image/svg+xml,%3Csvg width='25' height='15' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='.5' y='.5' width='24' height='14' rx='7'/%3E%3Ccircle cx='7.5' cy='7.5' r='3.5' fill='%23909090'/%3E%3Crect x='.5' y='.5' width='24' height='14' rx='7' stroke='%23909090'/%3E%3C/svg%3E");
114+
padding: 0 4px;
115+
116+
display: flex;
117+
align-items: center;
118+
119+
border: 1px solid var(--border-color);
120+
border-radius: 16px;
121+
122+
transition: 0.25s border-color ease;
123+
124+
&::before {
125+
content: '';
126+
127+
width: 7px;
128+
height: 7px;
129+
130+
background-color: var(--border-color);
131+
border-radius: 50%;
132+
133+
transition: transform 0.25s ease, background-color 0.25s ease;
134+
135+
.input:checked + & {
136+
transform: translateX(9px);
137+
}
32138

33-
&:checked {
34-
background-image: url("data:image/svg+xml,%3Csvg width='25' height='15' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='17.5' cy='7.5' r='3.5' fill='%230ED3A3'/%3E%3Crect x='.5' y='.5' width='24' height='14' rx='7' stroke='%230ED3A3'/%3E%3C/svg%3E");
139+
.input:not(:disabled):checked + & {
140+
background-color: var(--checked-color);
141+
}
142+
143+
.input:not(:disabled)[aria-invalid='true'] + & {
144+
background-color: var(--error-color);
145+
}
146+
147+
.input:disabled + & {
148+
background-color: var(--disabled-color);
149+
}
150+
}
151+
152+
.input:not(:disabled):checked + & {
153+
border-color: var(--checked-color);
154+
}
155+
156+
.input:not(:disabled)[aria-invalid='true'] + & {
157+
border-color: var(--error-color);
158+
}
159+
160+
.input:disabled + & {
161+
border-color: var(--disabled-color);
35162
}
36163
}
164+
165+
.default {
166+
--gap: 12px;
167+
--font-size: 14px;
168+
--line-height: 20px;
169+
170+
--checkbox-size: 18px;
171+
}
172+
173+
.small {
174+
--gap: 10px;
175+
--font-size: 12px;
176+
--line-height: 22px;
177+
178+
--checkbox-size: 15px;
179+
}

utils/vara-ui/src/components/checkbox/checkbox.stories.ts

+17-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,23 @@ type Story = StoryObj<Type>;
77
const meta: Meta<Type> = {
88
title: 'Checkbox',
99
component: Checkbox,
10-
args: { label: 'Label' },
10+
args: {
11+
label: 'Label',
12+
disabled: false,
13+
size: 'default',
14+
type: 'checkbox',
15+
error: undefined,
16+
},
17+
argTypes: {
18+
size: {
19+
options: ['small', 'default'],
20+
control: { type: 'select' },
21+
},
22+
type: {
23+
options: ['checkbox', 'switch'],
24+
control: { type: 'select' },
25+
},
26+
},
1127
};
1228

1329
const Default: Story = {
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,27 @@
11
import { InputHTMLAttributes, forwardRef } from 'react';
22
import cx from 'clsx';
3+
34
import styles from './checkbox.module.scss';
45

5-
type Props = InputHTMLAttributes<HTMLInputElement> & {
6+
type Props = Omit<InputHTMLAttributes<HTMLInputElement>, 'size'> & {
67
label: string;
7-
type?: 'switch';
8+
type?: 'switch' | 'checkbox';
9+
size?: 'small' | 'default';
10+
error?: string;
811
};
912

10-
const Checkbox = forwardRef<HTMLInputElement, Props>(({ label, className, type, ...attrs }, ref) => {
11-
const { disabled } = attrs;
12-
13-
return (
14-
<label className={cx(styles.label, className, disabled && styles.disabled)}>
15-
<input
16-
type="checkbox"
17-
className={cx(styles.input, type === 'switch' ? styles.switch : styles.checkbox)}
18-
ref={ref}
19-
{...attrs}
20-
/>
13+
const Checkbox = forwardRef<HTMLInputElement, Props>(
14+
({ label, className, type = 'checkbox', size = 'default', error, ...attrs }, ref) => {
15+
return (
16+
<label className={cx(styles.label, className, styles[size])}>
17+
<input type="checkbox" className={styles.input} ref={ref} aria-invalid={Boolean(error)} {...attrs} />
18+
<span className={cx(styles.box, styles[type])} />
2119

22-
{label}
23-
</label>
24-
);
25-
});
20+
{label}
21+
</label>
22+
);
23+
},
24+
);
2625

2726
export { Checkbox };
2827
export type { Props as CheckboxProps };

0 commit comments

Comments
 (0)