Skip to content
This repository was archived by the owner on Feb 18, 2026. It is now read-only.

Commit d8ce494

Browse files
committed
docs(examples): improve color editor example components
1 parent c6a12f9 commit d8ce494

227 files changed

Lines changed: 1454 additions & 1144 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs-src/components/basic-button/copyToClipboard.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export const copyToClipboard =
1212
container: HTMLElement,
1313
messages: { [COPY_ERROR]?: string; [COPY_SUCCESS]?: string },
1414
): Effect<ComponentProps, Component<BasicButtonProps>> =>
15-
(host, button) =>
15+
(_, button) =>
1616
on('click', () => {
1717
const label = button.label
1818
let status: CopyStatus = COPY_SUCCESS
@@ -38,4 +38,4 @@ export const copyToClipboard =
3838
status === COPY_SUCCESS ? 1000 : 3000,
3939
)
4040
})()
41-
})(host, button)
41+
})(_, button)

docs-src/components/form-colorgraph/form-colorgraph.css

Lines changed: 20 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -46,31 +46,6 @@ form-colorgraph {
4646
}
4747
}
4848

49-
/* .knob span {
50-
display: inline-block;
51-
background: rgba(0 0 0 / var(--opacity-dimmed));
52-
color: white;
53-
font-size: var(--font-size-xs);
54-
position: absolute;
55-
left: var(--input-height);
56-
padding: var(--space-xxs);
57-
border-radius: var(--space-xxs);
58-
transform: translateY(-50%);
59-
margin-left: var(--space-xs);
60-
}
61-
62-
.knob span:before {
63-
position: absolute;
64-
left: calc(-1 * var(--space-xs));
65-
top: calc(50% - var(--space-xs));
66-
content: "";
67-
width: 0;
68-
height: 0;
69-
border: var(--space-xs) solid transparent;
70-
border-left-width: 0;
71-
border-right-color: rgba(0 0 0 / var(--opacity-dimmed));
72-
} */
73-
7449
& ol {
7550
list-style: none;
7651
margin: 0;
@@ -128,8 +103,9 @@ form-colorgraph {
128103
display: grid;
129104
align-items: center;
130105
column-gap: var(--space-xs);
131-
grid-template-areas: "label buttons" "input buttons";
106+
grid-template-areas: "label label" "input buttons" "error error";
132107
grid-template-columns: 1fr auto;
108+
max-width: 12rem;
133109
}
134110

135111
& label,
@@ -143,6 +119,7 @@ form-colorgraph {
143119
display: block;
144120
font-size: var(--font-size-s);
145121
color: var(--color-text);
122+
margin-bottom: var(--space-xxs);
146123
}
147124

148125
& input {
@@ -181,7 +158,18 @@ form-colorgraph {
181158
grid-area: input;
182159
display: inline-flex;
183160
align-items: center;
184-
column-gap: var(--space-xxs);
161+
}
162+
163+
.error {
164+
grid-area: error;
165+
margin: var(--space-xs) 0 0;
166+
font-size: var(--font-size-xs);
167+
line-height: var(--line-height-s);
168+
color: color-mix(in srgb, var(--color-text) 50%, var(--color-error));
169+
170+
&:empty {
171+
display: none;
172+
}
185173
}
186174

187175
.slider {
@@ -227,36 +215,9 @@ form-colorgraph {
227215
);
228216
}
229217

230-
/* .tooltip {
231-
display: inline-block;
232-
font-size: var(--font-size-xs);
233-
background: rgba(0 0 0 / var(--opacity-dimmed));
234-
color: white;
235-
position: absolute;
236-
top: calc(-1 * var(--input-height));
237-
left: calc(0.5 * var(--input-height) - 2px);
238-
transform: translateX(-50%);
239-
padding: var(--space-xxs);
240-
border-radius: var(--space-xxs);
241-
margin-bottom: var(--space-xs);
242-
}
243-
244-
.tooltip:before {
245-
position: absolute;
246-
bottom: calc(-1 * var(--space-xs));
247-
left: calc(50% - var(--space-xs));
248-
content: "";
249-
width: 0;
250-
height: 0;
251-
border: var(--space-xs) solid transparent;
252-
border-bottom-width: 0;
253-
border-top-color: rgba(0 0 0 / var(--opacity-dimmed));
254-
} */
255-
256218
.buttons {
257219
grid-area: buttons;
258220
display: flex;
259-
flex-direction: column-reverse;
260221
align-items: center;
261222

262223
& button {
@@ -292,31 +253,24 @@ form-colorgraph {
292253
}
293254

294255
&:first-of-type {
295-
border-radius: 0 0 50% 50%;
296-
border-top-width: 0;
256+
border-radius: var(--space-xs) 0 0 var(--space-xs);
257+
border-right-width: 0;
297258
}
298259

299260
&:last-of-type {
300-
border-radius: 50% 50% 0 0;
261+
border-radius: 0 var(--space-xs) var(--space-xs) 0;
301262
}
302263
}
303264
}
304-
305-
input::-webkit-textfield-decoration-container {
306-
height: 100%;
307-
}
308-
309-
input::-webkit-inner-form-spinbutton {
310-
appearance: none;
311-
}
312265
}
313266

314-
@container (width > 18rem) {
267+
@container (width > 27rem) {
315268
form-colorgraph {
316269
display: grid;
317270
grid-template-areas: "graph graph graph" "slider slider slider" "lightness chroma hue";
318271
grid-template-columns: 1fr 1fr 1fr;
319272
gap: var(--space-s) var(--space-m);
273+
align-items: start;
320274

321275
.graph {
322276
grid-area: graph;

docs-src/components/form-colorgraph/form-colorgraph.html

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,27 +30,45 @@
3030
<span class="unit">%</span>
3131
</div>
3232
<div class="buttons">
33-
<button type="button" class="decrement" aria-label="Decrement">
33+
<button
34+
type="button"
35+
class="decrement"
36+
aria-label="Decrement lightness"
37+
>
3438
3539
</button>
36-
<button type="button" class="increment" aria-label="Increment">
40+
<button
41+
type="button"
42+
class="increment"
43+
aria-label="Increment lightness"
44+
>
3745
+
3846
</button>
3947
</div>
48+
<p class="error" aria-live="assertive" id="lightness-error"></p>
4049
</div>
4150
<div class="chroma">
4251
<label for="chroma">Chroma</label>
4352
<div class="input">
4453
<input id="chroma" name="chroma" type="number" />
4554
</div>
4655
<div class="buttons">
47-
<button type="button" class="decrement" aria-label="Decrement">
56+
<button
57+
type="button"
58+
class="decrement"
59+
aria-label="Decrement chroma"
60+
>
4861
4962
</button>
50-
<button type="button" class="increment" aria-label="Increment">
63+
<button
64+
type="button"
65+
class="increment"
66+
aria-label="Increment chroma"
67+
>
5168
+
5269
</button>
5370
</div>
71+
<p class="error" aria-live="assertive" id="chroma-error"></p>
5472
</div>
5573
<div class="hue">
5674
<label id="hue-label" for="hue">Hue</label>
@@ -59,12 +77,13 @@
5977
<span class="unit">°</span>
6078
</div>
6179
<div class="buttons">
62-
<button type="button" class="decrement" aria-label="Decrement">
80+
<button type="button" class="decrement" aria-label="Decrement hue">
6381
6482
</button>
65-
<button type="button" class="increment" aria-label="Increment">
83+
<button type="button" class="increment" aria-label="Increment hue">
6684
+
6785
</button>
6886
</div>
87+
<p class="error" aria-live="assertive" id="hue-error"></p>
6988
</div>
7089
</form-colorgraph>

docs-src/components/form-colorgraph/form-colorgraph.ts

Lines changed: 69 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
setAttribute,
1010
setProperty,
1111
setStyle,
12+
setText,
1213
state,
1314
} from '../../..'
1415
import { asOklch } from '../../functions/parser/asOklch'
@@ -19,9 +20,9 @@ export type FormColorgraphAxis = 'l' | 'c' | 'h'
1920

2021
export type FormColorgraphProps = {
2122
color: Oklch
22-
lightness: number
23-
chroma: number
24-
hue: number
23+
readonly lightness: number
24+
readonly chroma: number
25+
readonly hue: number
2526
stepDown: (axis: FormColorgraphAxis, bigStep?: boolean) => void
2627
stepUp: (axis: FormColorgraphAxis, bigStep?: boolean) => void
2728
}
@@ -52,18 +53,20 @@ export default component<FormColorgraphProps>(
5253
},
5354
(el, { all, first, useElement }) => {
5455
// Required elements
55-
const lInput = useElement(
56-
'input[name="lightness"]',
57-
'Add an <input[name="lightness"]> element to control the lightness of the color.',
58-
)
59-
const cInput = useElement(
60-
'input[name="chroma"]',
61-
'Add an <input[name="chroma"]> element to control the chroma of the color.',
62-
)
63-
const hInput = useElement(
64-
'input[name="hue"]',
65-
'Add an <input[name="hue"]> element to control the hue of the color.',
66-
)
56+
const inputs = {
57+
l: useElement(
58+
'input[name="lightness"]',
59+
'Add an <input[name="lightness"]> element to control the lightness of the color.',
60+
),
61+
c: useElement(
62+
'input[name="chroma"]',
63+
'Add an <input[name="chroma"]> element to control the chroma of the color.',
64+
),
65+
h: useElement(
66+
'input[name="hue"]',
67+
'Add an <input[name="hue"]> element to control the hue of the color.',
68+
),
69+
}
6770
const graph = useElement(
6871
'.graph',
6972
'Add a <.graph> element as a container for the color graph.',
@@ -82,18 +85,23 @@ export default component<FormColorgraphProps>(
8285
)
8386

8487
// Initialize
85-
lInput.min = '0'
86-
lInput.max = '100'
87-
lInput.step = 'any'
88-
cInput.min = '0'
89-
cInput.max = '0.4'
90-
cInput.step = 'any'
91-
hInput.min = '0'
92-
hInput.max = '360'
93-
hInput.step = 'any'
88+
for (const [key, input] of Object.entries(inputs)) {
89+
input.min = '0'
90+
input.max = key === 'l' ? '100' : key === 'c' ? '0.4' : '360'
91+
input.step = 'any'
92+
}
9493
slider.setAttribute('aria-valuemin', '0')
9594
slider.setAttribute('aria-valuemax', '360')
9695

96+
// Internal states
97+
const canvasSize = state(graph.getBoundingClientRect().width)
98+
const trackWidth = computed(() => canvasSize.get() - 2 * TRACK_OFFSET)
99+
const errors = {
100+
l: state(''),
101+
c: state(''),
102+
h: state(''),
103+
}
104+
97105
// Step methods
98106
const getValue = (axis: FormColorgraphAxis) => {
99107
return axis === 'l'
@@ -104,13 +112,22 @@ export default component<FormColorgraphProps>(
104112
}
105113
const commit = (color: Oklch) => {
106114
el.color = color
115+
for (const key of ['l', 'c', 'h']) {
116+
errors[key].set('')
117+
}
107118
emitEvent('color-change', 'color')
108119
}
109120
const setToNearestStep = (axis: FormColorgraphAxis, value: number) => {
110121
const nearest =
111122
Math.round(value / AXIS_STEP[axis]) * AXIS_STEP[axis]
112-
if (nearest >= 0 && nearest <= AXIS_MAX[axis])
113-
commit({ ...el.color, [axis]: nearest })
123+
if (nearest < 0 || nearest > AXIS_MAX[axis]) return
124+
const color = { ...el.color, [axis]: nearest }
125+
if (inP3Gamut(color)) {
126+
commit(color)
127+
} else {
128+
inputs[axis].setCustomValidity('Color out of gamut')
129+
errors[axis].set(inputs[axis].validationMessage)
130+
}
114131
}
115132
el.stepDown = (axis: FormColorgraphAxis, bigStep = false) => {
116133
setToNearestStep(axis, getValue(axis) - getStep(axis, bigStep))
@@ -119,10 +136,6 @@ export default component<FormColorgraphProps>(
119136
setToNearestStep(axis, getValue(axis) + getStep(axis, bigStep))
120137
}
121138

122-
// Internal states
123-
const canvasSize = state(graph.getBoundingClientRect().width)
124-
const trackWidth = computed(() => canvasSize.get() - 2 * TRACK_OFFSET)
125-
126139
// Helper functions
127140
const formatNumber = (axis: FormColorgraphAxis, value: number) => {
128141
const v = axis === 'l' ? value * 100 : value
@@ -199,6 +212,16 @@ export default component<FormColorgraphProps>(
199212
}
200213
},
201214
all('input', [
215+
setProperty('ariaInvalid', target => {
216+
const axis = getAxis(target)
217+
return axis ? String(!!errors[axis].get()) : 'false'
218+
}),
219+
setAttribute('aria-errormessage', target => {
220+
const axis = getAxis(target)
221+
return axis && errors[axis].get()
222+
? `${target.id}-error`
223+
: null
224+
}),
202225
setProperty('value', target => {
203226
const axis = getAxis(target)
204227
return axis ? formatNumber(axis, el.color[axis] ?? 0) : '0'
@@ -207,8 +230,22 @@ export default component<FormColorgraphProps>(
207230
const axis = getAxis(target)
208231
if (!axis) return
209232
const value = target.valueAsNumber
210-
const newColor = { ...el.color, [axis]: value }
211-
if (inP3Gamut(newColor)) commit(newColor)
233+
const newColor = {
234+
...el.color,
235+
[axis]: axis === 'l' ? value / 100 : value,
236+
}
237+
if (inP3Gamut(newColor)) {
238+
commit(newColor)
239+
} else {
240+
target.setCustomValidity('Color out of gamut')
241+
errors[axis].set(target.validationMessage)
242+
}
243+
}),
244+
]),
245+
all('.error', [
246+
setText(target => {
247+
const axis = getAxis(target)
248+
return axis ? errors[axis].get() : ''
212249
}),
213250
]),
214251
first('.graph', [

0 commit comments

Comments
 (0)