Skip to content

Commit 4988fb3

Browse files
HBS999jer3m01
andauthored
feat: new ColorField component (#510)
* feat: new ColorWheel component --------- Co-authored-by: jer3m01 <[email protected]>
1 parent 3674cfe commit 4988fb3

21 files changed

+2330
-4
lines changed
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
.color-field {
2+
display: flex;
3+
flex-direction: column;
4+
gap: 4px;
5+
}
6+
7+
.color-field__label {
8+
color: hsl(240 6% 10%);
9+
font-size: 14px;
10+
font-weight: 500;
11+
user-select: none;
12+
}
13+
14+
.color-field__input {
15+
display: inline-flex;
16+
width: 200px;
17+
border-radius: 6px;
18+
padding: 6px 12px;
19+
font-size: 16px;
20+
outline: none;
21+
background-color: white;
22+
border: 1px solid hsl(240 6% 90%);
23+
color: hsl(240 4% 16%);
24+
transition:
25+
border-color 250ms,
26+
color 250ms;
27+
}
28+
29+
.color-field__input:hover {
30+
border-color: hsl(240 5% 65%);
31+
}
32+
33+
.color-field__input:focus-visible {
34+
outline: 2px solid hsl(200 98% 39%);
35+
outline-offset: 2px;
36+
}
37+
38+
.color-field__input[data-invalid] {
39+
border-color: hsl(0 72% 51%);
40+
color: hsl(0 72% 51%);
41+
}
42+
43+
.color-field__input::placeholder {
44+
color: hsl(240 4% 46%);
45+
}
46+
47+
.color-field__description {
48+
color: hsl(240 5% 26%);
49+
font-size: 12px;
50+
user-select: none;
51+
}
52+
53+
.color-field__error-message {
54+
color: hsl(0 72% 51%);
55+
font-size: 12px;
56+
user-select: none;
57+
}
58+
59+
[data-kb-theme="dark"] .color-field__input {
60+
background-color: hsl(240 4% 16%);
61+
border: 1px solid hsl(240 5% 34%);
62+
color: hsl(0 100% 100% / 0.9);
63+
}
64+
65+
[data-kb-theme="dark"] .color-field__input:hover {
66+
border-color: hsl(240 4% 46%);
67+
}
68+
69+
[data-kb-theme="dark"] .color-field__input[data-invalid] {
70+
border-color: hsl(0 72% 51%);
71+
color: hsl(0 72% 51%);
72+
}
73+
74+
[data-kb-theme="dark"] .color-field__input::placeholder {
75+
color: hsl(0 100% 100% / 0.5);
76+
}
77+
78+
[data-kb-theme="dark"] .color-field__label {
79+
color: hsl(240 5% 84%);
80+
}
81+
82+
[data-kb-theme="dark"] .color-field__description {
83+
color: hsl(240 5% 65%);
84+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { createSignal } from "solid-js";
2+
import { ColorField } from "../../../../packages/core/src/colors/color-field";
3+
4+
import style from "./color-field.module.css";
5+
6+
export function BasicExample() {
7+
return (
8+
<ColorField class={style["color-field"]}>
9+
<ColorField.Label class={style["color-field__label"]}>
10+
Favorite color
11+
</ColorField.Label>
12+
<ColorField.Input class={style["color-field__input"]} />
13+
</ColorField>
14+
);
15+
}
16+
17+
export function DefaultValueExample() {
18+
return (
19+
<ColorField class={style["color-field"]} defaultValue="#7f007f">
20+
<ColorField.Label class={style["color-field__label"]}>
21+
Favorite color
22+
</ColorField.Label>
23+
<ColorField.Input class={style["color-field__input"]} />
24+
</ColorField>
25+
);
26+
}
27+
28+
export function ControlledExample() {
29+
const [value, setValue] = createSignal("#7f007f");
30+
31+
return (
32+
<>
33+
<ColorField
34+
class={style["color-field"]}
35+
value={value()}
36+
onChange={setValue}
37+
>
38+
<ColorField.Label class={style["color-field__label"]}>
39+
Favorite color
40+
</ColorField.Label>
41+
<ColorField.Input class={style["color-field__input"]} />
42+
</ColorField>
43+
<p class="not-prose text-sm mt-4">Your favorite color is: {value()}</p>
44+
</>
45+
);
46+
}
47+
48+
export function DescriptionExample() {
49+
return (
50+
<ColorField class={style["color-field"]}>
51+
<ColorField.Label class={style["color-field__label"]}>
52+
Favorite color
53+
</ColorField.Label>
54+
<ColorField.Input class={style["color-field__input"]} />
55+
<ColorField.Description class={style["color-field__description"]}>
56+
Choose the color you like the most.
57+
</ColorField.Description>
58+
</ColorField>
59+
);
60+
}
61+
62+
export function ErrorMessageExample() {
63+
const [value, setValue] = createSignal("#7f007f");
64+
65+
return (
66+
<ColorField
67+
class={style["color-field"]}
68+
value={value()}
69+
onChange={setValue}
70+
validationState={value() !== "#000000" ? "invalid" : "valid"}
71+
>
72+
<ColorField.Label class={style["color-field__label"]}>
73+
Favorite color
74+
</ColorField.Label>
75+
<ColorField.Input class={style["color-field__input"]} />
76+
<ColorField.ErrorMessage class={style["color-field__error-message"]}>
77+
Hmm, I prefer black.
78+
</ColorField.ErrorMessage>
79+
</ColorField>
80+
);
81+
}
82+
83+
export function HTMLFormExample() {
84+
let formRef: HTMLFormElement | undefined;
85+
86+
const onSubmit = (e: SubmitEvent) => {
87+
e.preventDefault();
88+
e.stopPropagation();
89+
90+
const formData = new FormData(formRef);
91+
92+
alert(JSON.stringify(Object.fromEntries(formData), null, 2));
93+
};
94+
95+
return (
96+
<form
97+
ref={formRef}
98+
onSubmit={onSubmit}
99+
class="flex flex-col items-center space-y-6"
100+
>
101+
<ColorField class={style["color-field"]} name="favorite-color">
102+
<ColorField.Label class={style["color-field__label"]}>
103+
Favorite color
104+
</ColorField.Label>
105+
<ColorField.Input class={style["color-field__input"]} />
106+
</ColorField>
107+
<div class="flex space-x-2">
108+
<button type="reset" class="kb-button">
109+
Reset
110+
</button>
111+
<button type="submit" class="kb-button-primary">
112+
Submit
113+
</button>
114+
</div>
115+
</form>
116+
);
117+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
.ColorWheelRoot {
2+
position: relative;
3+
display: flex;
4+
flex-direction: column;
5+
align-items: center;
6+
user-select: none;
7+
touch-action: none;
8+
}
9+
10+
.ColorWheelTrack {
11+
position: relative;
12+
height: 160px;
13+
width: 160px;
14+
}
15+
16+
.ColorWheelThumb {
17+
display: block;
18+
width: 16px;
19+
height: 16px;
20+
border-radius: 9999px;
21+
border: 2px solid #fff;
22+
box-shadow: 0 0 0 1px #0000006b;
23+
}
24+
25+
.ColorWheelThumb:focus {
26+
outline: none;
27+
}
28+
29+
.ColorWheelLabel {
30+
display: flex;
31+
justify-content: space-between;
32+
width: 100%;
33+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import { createSignal } from "solid-js";
2+
import { ColorWheel } from "../../../../packages/core/src/colors/color-wheel";
3+
import { parseColor } from "../../../../packages/core/src/colors/utils";
4+
import style from "./color-wheel.module.css";
5+
6+
export function BasicExample() {
7+
return (
8+
<ColorWheel class={style.ColorWheelRoot} thickness={24}>
9+
<ColorWheel.Track class={style.ColorWheelTrack}>
10+
<ColorWheel.Thumb class={style.ColorWheelThumb}>
11+
<ColorWheel.Input />
12+
</ColorWheel.Thumb>
13+
</ColorWheel.Track>
14+
</ColorWheel>
15+
);
16+
}
17+
18+
export function DefaultValueExample() {
19+
return (
20+
<ColorWheel
21+
class={style.ColorWheelRoot}
22+
defaultValue={parseColor("hsl(80, 100%, 50%)")}
23+
thickness={24}
24+
>
25+
<ColorWheel.Track class={style.ColorWheelTrack}>
26+
<ColorWheel.Thumb class={style.ColorWheelThumb}>
27+
<ColorWheel.Input />
28+
</ColorWheel.Thumb>
29+
</ColorWheel.Track>
30+
</ColorWheel>
31+
);
32+
}
33+
34+
export function ThicknessExample() {
35+
return (
36+
<ColorWheel class={style.ColorWheelRoot} thickness={56}>
37+
<ColorWheel.Track class={style.ColorWheelTrack}>
38+
<ColorWheel.Thumb class={style.ColorWheelThumb}>
39+
<ColorWheel.Input />
40+
</ColorWheel.Thumb>
41+
</ColorWheel.Track>
42+
</ColorWheel>
43+
);
44+
}
45+
46+
export function ControlledValueExample() {
47+
const [value, setValue] = createSignal(parseColor("hsl(0, 100%, 50%)"));
48+
49+
return (
50+
<>
51+
<ColorWheel
52+
class={style.ColorWheelRoot}
53+
value={value()}
54+
onChange={setValue}
55+
thickness={24}
56+
>
57+
<ColorWheel.Track class={style.ColorWheelTrack}>
58+
<ColorWheel.Thumb class={style.ColorWheelThumb}>
59+
<ColorWheel.Input />
60+
</ColorWheel.Thumb>
61+
</ColorWheel.Track>
62+
</ColorWheel>
63+
<p class="not-prose text-sm mt-4">
64+
Current color value: {value().toString("hsl")}
65+
</p>
66+
</>
67+
);
68+
}
69+
70+
export function CustomValueLabelExample() {
71+
return (
72+
<ColorWheel
73+
class={style.ColorWheelRoot}
74+
thickness={24}
75+
getValueLabel={(color) =>
76+
color
77+
.toFormat("hsl")
78+
.withChannelValue("saturation", 100)
79+
.withChannelValue("lightness", 50)
80+
.withChannelValue("alpha", 1)
81+
.toString()
82+
}
83+
>
84+
<div class={style.ColorWheelLabel}>
85+
<ColorWheel.ValueLabel />
86+
</div>
87+
<ColorWheel.Track class={style.ColorWheelTrack}>
88+
<ColorWheel.Thumb class={style.ColorWheelThumb}>
89+
<ColorWheel.Input />
90+
</ColorWheel.Thumb>
91+
</ColorWheel.Track>
92+
</ColorWheel>
93+
);
94+
}
95+
96+
export function HTMLFormExample() {
97+
let formRef: HTMLFormElement | undefined;
98+
99+
const onSubmit = (e: SubmitEvent) => {
100+
e.preventDefault();
101+
e.stopPropagation();
102+
103+
const formData = new FormData(formRef);
104+
105+
alert(JSON.stringify(Object.fromEntries(formData), null, 2));
106+
};
107+
108+
return (
109+
<form
110+
ref={formRef}
111+
onSubmit={onSubmit}
112+
class="flex flex-col items-center space-y-6"
113+
>
114+
<ColorWheel class={style.ColorWheelRoot} name="hue" thickness={24}>
115+
<ColorWheel.Track class={style.ColorWheelTrack}>
116+
<ColorWheel.Thumb class={style.ColorWheelThumb}>
117+
<ColorWheel.Input />
118+
</ColorWheel.Thumb>
119+
</ColorWheel.Track>
120+
</ColorWheel>
121+
<div class="flex space-x-2">
122+
<button type="reset" class="kb-button">
123+
Reset
124+
</button>
125+
<button type="submit" class="kb-button-primary">
126+
Submit
127+
</button>
128+
</div>
129+
</form>
130+
);
131+
}

apps/docs/src/routes/docs/core.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ const CORE_NAV_SECTIONS: NavSection[] = [
8080
href: "/docs/core/components/color-channel-field",
8181
status: "new",
8282
},
83+
{
84+
title: "Color Field",
85+
href: "/docs/core/components/color-field",
86+
status: "new",
87+
},
8388
{
8489
title: "Color Slider",
8590
href: "/docs/core/components/color-slider",
@@ -90,6 +95,11 @@ const CORE_NAV_SECTIONS: NavSection[] = [
9095
href: "/docs/core/components/color-swatch",
9196
status: "new",
9297
},
98+
{
99+
title: "Color Wheel",
100+
href: "/docs/core/components/color-wheel",
101+
status: "new",
102+
},
93103
{
94104
title: "Combobox",
95105
href: "/docs/core/components/combobox",

0 commit comments

Comments
 (0)