Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
6cf3c75
Initial plan
Copilot Jan 29, 2026
dca7304
Add CSS appearance: base-select styling for customizable select elements
Copilot Jan 29, 2026
62d8d53
Add 1px gap between option elements in base-select dropdown
Copilot Jan 29, 2026
6672921
Fix overflow: hidden blocking base-select anchor positioning
Copilot Jan 29, 2026
45ff98a
Add button and selectedcontent elements for base-select activation
Copilot Jan 30, 2026
8e61926
Add appearance: base-select to ::picker(select) pseudo-element
Copilot Jan 30, 2026
6003e7b
Fix text wrapping regression in base-select dropdown buttons
Copilot Jan 30, 2026
cbfa93c
Fix picker styling: hr margins, remove optgroup::before, restore chec…
Copilot Jan 30, 2026
ed72f9f
Fix picker styling: checkmark position, shadow, hide picker-icon, but…
Copilot Jan 30, 2026
983a4a1
Add order: 2 to checkmark for correct inline-end positioning
Copilot Jan 30, 2026
23f2089
Fix action button icons visibility and select sizing in base-select mode
Copilot Jan 30, 2026
2d894fb
Remove background-image: none from base-select styling
Copilot Jan 31, 2026
d3a78c6
Remove HR elements from selects and hide disabled options in base-sel…
Copilot Jan 31, 2026
28290a0
Fix Svelte reactivity with base-select by adding explicit oninput han…
Copilot Jan 31, 2026
156347a
Add meaningful icons to action menu options with accessibility support
Copilot Jan 31, 2026
73e741e
Fix Move to top/bottom icons to use vertical align icons
Copilot Jan 31, 2026
b8728ba
Update option spacing and add padding: 0 for selects inside buttons
Copilot Jan 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/components/ColorPicker.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,9 @@ let gamut = $derived(whatsTheGamutDamnit($picker_value))
<div class="hd-color-picker" style="accent-color: {$picker_value}; --contrast-color: {bg_overlay}; --counter-contrast-color: {text_overlay}">
<div class="preview" style={`--user-color:${$picker_value}`}>
<select class="colorspace" onchange={spaceChange} title="Colorspace" style="--icon-arrow-up: url(https://api.iconify.design/ic:keyboard-arrow-up.svg?color={text_overlay}); --icon-arrow-down: url(https://api.iconify.design/ic:keyboard-arrow-down.svg?color={text_overlay});">
<button>
<selectedcontent></selectedcontent>
</button>
<optgroup label="Standard">
<option value="srgb">rgb</option>
<option>srgb-linear</option>
Expand Down
5 changes: 4 additions & 1 deletion src/components/ConicPosition.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@
<div class="label-select-combo">
<label>Position</label>
<NamedDirections id="conic-position" bind:selected={$conic_named_position} mode="position" />
<select name="conic-position" bind:value={$conic_named_position}>
<select name="conic-position" bind:value={$conic_named_position} oninput={(e) => $conic_named_position = e.currentTarget.value}>
<button>
<selectedcontent></selectedcontent>
</button>
<option disabled>--</option>
{#each gradient_positions as pos}
<option value={pos}>{pos}</option>
Expand Down
182 changes: 166 additions & 16 deletions src/components/Gradient.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -560,17 +560,15 @@ let user_gradient = $derived(gensyntax[$gradient_type](
<button class="global-actions">
<select tabindex="-1" onchange={globalAction}>
<option disabled selected>Global Actions</option>
<hr>
<option>Start new</option>
<option>Import gradient</option>
<option>Copy modern CSS</option>
<option>Copy classic CSS</option>
<option><svg aria-hidden="true" viewBox="0 0 24 24"><path fill="currentColor" d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg> Start new</option>
<option><svg aria-hidden="true" viewBox="0 0 24 24"><path fill="currentColor" d="M19 19H5V5h7V3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83l1.41 1.41L19 6.41V10h2V3h-7z"/></svg> Import gradient</option>
<option><svg aria-hidden="true" viewBox="0 0 24 24"><path fill="currentColor" d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg> Copy modern CSS</option>
<option><svg aria-hidden="true" viewBox="0 0 24 24"><path fill="currentColor" d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg> Copy classic CSS</option>
<!-- <option disabled>Reset all stops to auto</option> -->
<hr>
<!-- <option disabled>Toggle light & dark</option> -->
<option>Tips & tricks</option>
<option>Help & feedback</option>
<option>GitHub</option>
<option><svg aria-hidden="true" viewBox="0 0 24 24"><path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10s10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/></svg> Tips & tricks</option>
<option><svg aria-hidden="true" viewBox="0 0 24 24"><path fill="currentColor" d="M11 18h2v-2h-2v2zm1-16C6.48 2 2 6.48 2 12s4.48 10 10 10s10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8s8 3.59 8 8s-3.59 8-8 8zm0-14c-2.21 0-4 1.79-4 4h2c0-1.1.9-2 2-2s2 .9 2 2c0 2-3 1.75-3 5h2c0-2.25 3-2.5 3-5c0-2.21-1.79-4-4-4z"/></svg> Help & feedback</option>
<option><svg aria-hidden="true" viewBox="0 0 24 24"><path fill="currentColor" d="M12 2A10 10 0 0 0 2 12c0 4.42 2.87 8.17 6.84 9.5c.5.08.66-.23.66-.5v-1.69c-2.77.6-3.36-1.34-3.36-1.34c-.46-1.16-1.11-1.47-1.11-1.47c-.91-.62.07-.6.07-.6c1 .07 1.53 1.03 1.53 1.03c.87 1.52 2.34 1.07 2.91.83c.09-.65.35-1.09.63-1.34c-2.22-.25-4.55-1.11-4.55-4.92c0-1.11.38-2 1.03-2.71c-.1-.25-.45-1.29.1-2.64c0 0 .84-.27 2.75 1.02c.79-.22 1.65-.33 2.5-.33c.85 0 1.71.11 2.5.33c1.91-1.29 2.75-1.02 2.75-1.02c.55 1.35.2 2.39.1 2.64c.65.71 1.03 1.6 1.03 2.71c0 3.82-2.34 4.66-4.57 4.91c.36.31.69.92.69 1.85V21c0 .27.16.59.67.5C19.14 20.16 22 16.42 22 12A10 10 0 0 0 12 2z"/></svg> GitHub</option>
</select>
</button>
</div>
Expand Down Expand Up @@ -622,7 +620,7 @@ let user_gradient = $derived(gensyntax[$gradient_type](
display: grid;
border-radius: var(--radius-3);
box-shadow: var(--shadow-3);
overflow: hidden;
overflow: clip;
}

@media (prefers-color-scheme: light) {
Expand Down Expand Up @@ -743,7 +741,7 @@ let user_gradient = $derived(gensyntax[$gradient_type](
display: grid;
place-content: center;
inline-size: 100cqi;
overflow: hidden;
overflow: clip;
position: relative;
}

Expand Down Expand Up @@ -997,6 +995,161 @@ let user_gradient = $derived(gensyntax[$gradient_type](
cursor: not-allowed;
}

/* -----------------------------------------------
* Customizable Select (base-select) Styles
* ----------------------------------------------- */
@supports (appearance: base-select) {
:global(select) {
appearance: base-select;
}

/* Style the button that activates the picker */
:global(select > button) {
all: unset;
display: flex;
align-items: center;
gap: 0.5ch;
cursor: pointer;
white-space: nowrap;
}

/* Style the selected content display */
:global(select selectedcontent) {
display: inline;
white-space: nowrap;
}

/* Hide the picker icon */
:global(select::picker-icon) {
display: none;
}

/* Action button selects: fit to parent and hide content so parent icon shows */
:global(.global-actions > select),
:global(.layer-actions > select),
:global(.stop-actions > select) {
position: absolute;
inset: 0;
inline-size: 100%;
block-size: 100%;
opacity: 0;
}

/* Selects inside buttons should have no padding */
:global(button > select) {
padding: 0;
}

:global(select::picker(select)) {
appearance: base-select;
--_picker-bg-light: #fff;
--_picker-bg-dark: var(--surface-3);
--_picker-bg: var(--_picker-bg-light);

background: var(--_picker-bg);
border: 1px solid var(--surface-4);
border-radius: var(--radius-2);
box-shadow: var(--shadow-6);
padding: var(--size-1);
margin-block-start: var(--size-1);

/* Transitions */
opacity: 0;
transform: translateY(-8px) scale(0.96);
transition:
opacity 150ms var(--ease-3),
transform 150ms var(--ease-3),
overlay 150ms var(--ease-3) allow-discrete,
display 150ms var(--ease-3) allow-discrete;
}

:global(select:open::picker(select)) {
opacity: 1;
transform: translateY(0) scale(1);
}

@starting-style {
:global(select:open::picker(select)) {
opacity: 0;
transform: translateY(-8px) scale(0.96);
}
}

/* Hide disabled options in base-select mode */
:global(select option[disabled]) {
display: none;
}

:global(select option) {
display: flex;
align-items: center;
gap: var(--size-3);
padding: var(--size-2) var(--size-2);
border-radius: var(--radius-2);
cursor: pointer;
transition:
background-color 100ms var(--ease-2),
color 100ms var(--ease-2);
}

/* Style icons inside options */
:global(select option > svg) {
inline-size: 1.25em;
block-size: 1.25em;
flex-shrink: 0;
}

:global(select option:not(:last-child)) {
margin-block-end: 1px;
}

:global(select option:hover) {
background-color: var(--surface-2);
}

:global(select option:checked),
:global(select option:focus) {
background-color: var(--link);
color: white;
}

/* Position checkmark on inline-end */
:global(select option::checkmark) {
order: 2;
margin-inline-start: auto;
padding-inline-start: var(--size-2);
}

:global(select optgroup) {
padding-block: var(--size-1);
}

:global(select optgroup:last-of-type) {
padding-block-end: 0;
}

:global(select optgroup:not(:first-of-type)) {
border-block-start: 1px solid var(--surface-3);
margin-block-start: var(--size-1);
padding-block-start: var(--size-2);
}

@media (prefers-color-scheme: dark) {
:global(select::picker(select)) {
--_picker-bg: var(--_picker-bg-dark);
border-color: var(--surface-4);
}

:global(select option:hover) {
background-color: var(--surface-4);
}

:global(select optgroup:not(:first-of-type)) {
border-block-start-color: var(--surface-4);
}
}
}

:global(.sr-only) {
inline-size: 0;
block-size: 0;
Expand Down Expand Up @@ -1062,7 +1215,7 @@ let user_gradient = $derived(gensyntax[$gradient_type](
.global-actions {
position: relative;
inline-size: var(--size-7);
overflow: hidden;
overflow: clip;
border-radius: var(--radius-round);
padding-inline: 0;
aspect-ratio: 1;
Expand All @@ -1078,11 +1231,8 @@ let user_gradient = $derived(gensyntax[$gradient_type](
--_bg: transparent;
}

.global-actions:not(:active,:focus) select {
opacity: 0;
}

.global-actions > select {
position: absolute;
inset: 0;
}
</style>
5 changes: 4 additions & 1 deletion src/components/GradientColorSpace.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
</a>
Color Space
</label>
<select name="colorspace" id="in-colorspace" bind:value={$gradient_space}>
<select name="colorspace" id="in-colorspace" bind:value={$gradient_space} oninput={(e) => $gradient_space = e.currentTarget.value}>
<button>
<selectedcontent></selectedcontent>
</button>
<optgroup label="Default">
<option selected>oklab</option>
</optgroup>
Expand Down
18 changes: 10 additions & 8 deletions src/components/GradientStops.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -330,13 +330,11 @@
<button class="stop-actions" use:tooltip={{content: "Actions", offset: 15}}>
<select tabindex="-1" onchange={(e) => colorAction(e,i)}>
<option disabled selected>Color Stop Actions</option>
<hr>
<option>Duplicate</option>
<option>Copy CSS color</option>
<option>Random color</option>
<hr>
<option>Reset</option>
<option disabled={colorStopCount() <= 1}>Remove</option>
<option><svg aria-hidden="true" viewBox="0 0 24 24"><path fill="currentColor" d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg> Duplicate</option>
<option><svg aria-hidden="true" viewBox="0 0 24 24"><path fill="currentColor" d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg> Copy CSS color</option>
<option><svg aria-hidden="true" viewBox="0 0 24 24"><path fill="currentColor" d="M17.65 6.35A7.958 7.958 0 0 0 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0 1 12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg> Random color</option>
<option><svg aria-hidden="true" viewBox="0 0 24 24"><path fill="currentColor" d="M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6s-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8s-3.58-8-8-8z"/></svg> Reset</option>
<option disabled={colorStopCount() <= 1}><svg aria-hidden="true" viewBox="0 0 24 24"><path fill="currentColor" d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg> Remove</option>
</select>
</button>
<div class="drag-handle" use:tooltip={{content: 'Drag to reorder'}} draggable="true" ondragstart={(e) => beginDrag(e, i)} role="button" aria-label="Drag to reorder" tabindex="0"></div>
Expand Down Expand Up @@ -438,6 +436,10 @@
aspect-ratio: 1;
border: none;
box-shadow: 0 0 0 var(--_highlight-size) var(--_highlight);
background-image: url(https://api.iconify.design/mdi:dots-vertical.svg?color=%23adb5bd);
background-position: center;
background-repeat: no-repeat;
background-size: 75%;
}

/* Drag handle to initiate reordering */
Expand Down Expand Up @@ -477,7 +479,7 @@
--icon-arrow-down: url(https://api.iconify.design/mdi:dots-vertical.svg?color=%23adb5bd);
--icon-arrow-up: url(https://api.iconify.design/mdi:dots-vertical.svg?color=%23adb5bd);
position: absolute;
inset-inline-end: -1.1ch;
inset: 0;
}

.chip {
Expand Down
5 changes: 4 additions & 1 deletion src/components/HueInterpolation.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
</svg>
Hue Path
</label>
<select name="colorspace" id="in-colorspace" bind:value={$gradient_interpolation}>
<select name="colorspace" id="in-colorspace" bind:value={$gradient_interpolation} oninput={(e) => $gradient_interpolation = e.currentTarget.value}>
<button>
<selectedcontent></selectedcontent>
</button>
<optgroup label="Default interpolation">
<option selected>shorter</option>
</optgroup>
Expand Down
22 changes: 12 additions & 10 deletions src/components/LayersPanel.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,12 @@
<button class="layer-actions" aria-label="Layer actions" use:tooltip={{content: "Layer Actions"}}>
<select tabindex="-1" onchange={(e)=>{ const v=e.currentTarget.value; e.currentTarget.selectedIndex=0; if(v==='Move up') moveLayerUp(i); else if(v==='Move down') moveLayerDown(i); else if(v==='Move to top') moveLayerToTop(i); else if(v==='Move to bottom') moveLayerToBottom(i); else if(v==='Toggle visibility') onToggleVisibility(i); else if(v==='Remove') onDelete(i); }}>
<option disabled selected>Layer Actions</option>
<hr>
<option>Move up</option>
<option>Move down</option>
<option>Move to top</option>
<option>Move to bottom</option>
<hr>
<option>Toggle visibility</option>
<option disabled={$layers.length<=1}>Remove</option>
<option><svg aria-hidden="true" viewBox="0 0 24 24"><path fill="currentColor" d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6l-6 6l1.41 1.41z"/></svg> Move up</option>
<option><svg aria-hidden="true" viewBox="0 0 24 24"><path fill="currentColor" d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6l-6-6l1.41-1.41z"/></svg> Move down</option>
<option><svg aria-hidden="true" viewBox="0 0 24 24"><path fill="currentColor" d="M8 11h3v10h2V11h3l-4-4l-4 4zM4 3v2h16V3H4z"/></svg> Move to top</option>
<option><svg aria-hidden="true" viewBox="0 0 24 24"><path fill="currentColor" d="M16 13h-3V3h-2v10H8l4 4l4-4zM4 19v2h16v-2H4z"/></svg> Move to bottom</option>
<option><svg aria-hidden="true" viewBox="0 0 24 24"><path fill="currentColor" d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5s5 2.24 5 5s-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3s3-1.34 3-3s-1.34-3-3-3z"/></svg> Toggle visibility</option>
<option disabled={$layers.length<=1}><svg aria-hidden="true" viewBox="0 0 24 24"><path fill="currentColor" d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg> Remove</option>
</select>
</button>
</div>
Expand Down Expand Up @@ -193,19 +191,23 @@
.layer-actions {
position: relative;
inline-size: var(--size-5);
overflow: hidden;
overflow: clip;
border-radius: var(--radius-round);
padding-inline: 0;
aspect-ratio: 1;
border: none;
box-shadow: 0 0 0 var(--_highlight-size) var(--_highlight);
background-image: url(https://api.iconify.design/mdi:dots-vertical.svg?color=%23adb5bd);
background-position: center;
background-repeat: no-repeat;
background-size: 75%;
}

.layer-actions > select {
--icon-arrow-down: url(https://api.iconify.design/mdi:dots-vertical.svg?color=%23adb5bd);
--icon-arrow-up: url(https://api.iconify.design/mdi:dots-vertical.svg?color=%23adb5bd);
position: absolute;
inset-inline-end: -1.1ch;
inset: 0;
}

.end-of-layers {
Expand Down
5 changes: 4 additions & 1 deletion src/components/LinearAngle.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@
</label>
<NamedDirections id="linear-angle" bind:selected={$linear_named_angle} mode="angle" />
<div class="select-wrap">
<select name="named-directions" bind:value={$linear_named_angle}>
<select name="named-directions" bind:value={$linear_named_angle} oninput={(e) => $linear_named_angle = e.currentTarget.value}>
<button>
<selectedcontent></selectedcontent>
</button>
<option disabled>--</option>
{#each linear_directions as dir}
<option value={dir}>{dir}</option>
Expand Down
5 changes: 4 additions & 1 deletion src/components/RadialPosition.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@
<div class="label-select-combo">
<label>Position</label>
<NamedDirections id="radial-position" bind:selected={$radial_named_position} mode="position" />
<select name="radial-position" bind:value={$radial_named_position}>
<select name="radial-position" bind:value={$radial_named_position} oninput={(e) => $radial_named_position = e.currentTarget.value}>
<button>
<selectedcontent></selectedcontent>
</button>
<option disabled>--</option>
{#each gradient_positions as pos}
<option value={pos}>{pos}</option>
Expand Down
5 changes: 4 additions & 1 deletion src/components/RadialSize.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@
<fieldset class="control-set">
<div class="label-select-combo">
<label>Size</label>
<select name="radial-size" bind:value={$radial_size}>
<select name="radial-size" bind:value={$radial_size} oninput={(e) => $radial_size = e.currentTarget.value}>
<button>
<selectedcontent></selectedcontent>
</button>
{#if !optionExists($radial_size)}
<option value={$radial_size}>{$radial_size}</option>
{/if}
Expand Down
Loading