diff --git a/src/lib/components/colors/Header.svelte b/src/lib/components/colors/Header.svelte
index adb627e6..6b3cf086 100644
--- a/src/lib/components/colors/Header.svelte
+++ b/src/lib/components/colors/Header.svelte
@@ -4,6 +4,7 @@
import CopyButton from '$lib/components/util/CopyButton.svelte';
import type { ColorFormatId } from '$lib/constants';
+ import { switchColors } from '$lib/stores';
import { getSpaceFromFormatId } from '$lib/utils';
interface Props {
@@ -12,6 +13,8 @@
format: ColorFormatId;
}
+ const CUSTOM_MIMETYPE = 'text/odd';
+
let { type, color, format }: Props = $props();
let targetSpace = $derived(getSpaceFromFormatId(format));
@@ -20,6 +23,7 @@
let editing = $state(false);
let inputValue = $state('');
let hasError = $state(false);
+ let isDragging = false;
// When not editing, sync input value with color (e.g. when sliders change)
$effect(() => {
@@ -80,6 +84,32 @@
break;
}
};
+
+ const onDragStart = (event: DragEvent) => {
+ isDragging = true;
+ if (!event.dataTransfer) return;
+ event.dataTransfer.clearData();
+ event.dataTransfer.setData(CUSTOM_MIMETYPE, type);
+ };
+
+ const onDrop = (event: DragEvent | undefined) => {
+ const droppedType = event?.dataTransfer?.getData(CUSTOM_MIMETYPE);
+ const dragIsFromOther =
+ type === 'fg' ? droppedType === 'bg' : droppedType === 'fg';
+ if (dragIsFromOther) {
+ switchColors();
+ }
+ };
+
+ const makeDropable = (event: DragEvent) => {
+ // DataTransfer values are not available on dragover, but because the types
+ // of items is available, we can use a custom mimetype to check if a swatch
+ // is the drag target.
+ if (!isDragging && event?.dataTransfer?.types.includes(CUSTOM_MIMETYPE)) {
+ // Cancelling the event signals that the dragged item can be dropped here.
+ event.preventDefault();
+ }
+ };
-
+
(isDragging = false)}
+ >
diff --git a/src/lib/components/colors/SwitchButton.svelte b/src/lib/components/colors/SwitchButton.svelte
new file mode 100644
index 00000000..a8b3d76c
--- /dev/null
+++ b/src/lib/components/colors/SwitchButton.svelte
@@ -0,0 +1,9 @@
+
+
+
diff --git a/src/lib/components/colors/index.svelte b/src/lib/components/colors/index.svelte
index ee82c135..bd684b31 100644
--- a/src/lib/components/colors/index.svelte
+++ b/src/lib/components/colors/index.svelte
@@ -4,6 +4,8 @@
import Sliders from '$lib/components/colors/Sliders.svelte';
import SupportWarning from '$lib/components/colors/SupportWarning.svelte';
import { bg, fg, format } from '$lib/stores';
+
+ import SwitchButton from './SwitchButton.svelte';
Check the contrast ratio between two colors
@@ -13,12 +15,14 @@
-
+
@@ -31,6 +35,11 @@
}
[data-layout] {
- column-gap: var(--triple-gutter);
+ column-gap: var(--gutter);
+ }
+
+ [data-actions='swap-colors'] {
+ display: grid;
+ place-content: center;
}
diff --git a/src/lib/components/util/Icon.svelte b/src/lib/components/util/Icon.svelte
index 659ed6d5..9e43e609 100644
--- a/src/lib/components/util/Icon.svelte
+++ b/src/lib/components/util/Icon.svelte
@@ -12,6 +12,7 @@
import OddBird from '$lib/icons/OddBird.svelte';
import Twitter from '$lib/icons/Twitter.svelte';
import Warning from '$lib/icons/Warning.svelte';
+ import Switch from '$src/lib/icons/Switch.svelte';
const icons: Record
= {
check: Check,
@@ -25,6 +26,7 @@
oddbird: OddBird,
linkedin: LinkedIn,
mastodon: Mastodon,
+ switch: Switch,
};
interface Props {
diff --git a/src/lib/icons/Switch.svelte b/src/lib/icons/Switch.svelte
new file mode 100755
index 00000000..dbfb9e12
--- /dev/null
+++ b/src/lib/icons/Switch.svelte
@@ -0,0 +1,17 @@
+
+
+
diff --git a/src/lib/stores.ts b/src/lib/stores.ts
index cc5773cd..2928b2a5 100644
--- a/src/lib/stores.ts
+++ b/src/lib/stores.ts
@@ -10,7 +10,7 @@ import {
REC_2020,
sRGB,
} from 'colorjs.io/fn';
-import { writable } from 'svelte/store';
+import { get, writable } from 'svelte/store';
// eslint-disable-next-line import/no-unresolved
import { browser, dev } from '$app/environment';
@@ -57,6 +57,12 @@ export const reset = () => {
fg.set(INITIAL_FG);
};
+export const switchColors = () => {
+ const temp = get(bg);
+ bg.set(get(fg));
+ fg.set(temp);
+};
+
/* c8 ignore next 5 */
if (browser && dev) {
bg.subscribe(($bg) => (window.bg = $bg));
diff --git a/src/sass/config/scale/_ui.scss b/src/sass/config/scale/_ui.scss
index 0ce73c07..f5926072 100644
--- a/src/sass/config/scale/_ui.scss
+++ b/src/sass/config/scale/_ui.scss
@@ -15,3 +15,6 @@ $icon-medium: 1.5em;
$ratio-width: 10rem;
$range-thumb-size: 1.35rem;
$range-input: 0.85rem;
+$triangle-width: var(--shim);
+$triangle-height: var(--shim-plus);
+$switch-space: var(--double-gutter);
diff --git a/src/sass/initial/_layout.scss b/src/sass/initial/_layout.scss
index c8517246..35e3fa39 100644
--- a/src/sass/initial/_layout.scss
+++ b/src/sass/initial/_layout.scss
@@ -69,11 +69,19 @@ body {
grid-template-columns: auto 1fr;
}
-[data-layout~='split'] {
+[data-layout~='color-formats'] {
display: grid;
@include config.above('sm-page-break') {
- grid-template-columns: 1fr 1fr;
+ grid-template-columns: 1fr var(--switch-space) 1fr;
+ }
+}
+
+[data-content='formats'] {
+ @include config.above('sm-page-break') {
+ &:last-of-type {
+ grid-column: 3;
+ }
}
}
@@ -81,8 +89,8 @@ body {
@include config.above('sm-page-break') {
display: grid;
grid-template:
- 'bginput fginput' auto
- 'bgslide fgslide' auto / 1fr 1fr;
+ 'bginput switch fginput' auto
+ 'bgslide . fgslide' auto / 1fr var(--switch-space) 1fr;
}
}
diff --git a/src/sass/patterns/_buttons.scss b/src/sass/patterns/_buttons.scss
index 134f9751..04942e69 100644
--- a/src/sass/patterns/_buttons.scss
+++ b/src/sass/patterns/_buttons.scss
@@ -1,6 +1,8 @@
/// # Button Pattern
/// @group buttons
+@use '../config';
+
button {
font-family: inherit;
font-size: inherit;
@@ -25,7 +27,8 @@ button {
var(--btn-padding-inline, var(--gutter));
transition:
color var(--fast),
- background-color var(--fast) transform var(--fast);
+ background-color var(--fast),
+ transform var(--fast);
&:hover,
&:focus {
@@ -47,3 +50,27 @@ button {
transform: scale(1.15);
}
}
+
+[data-btn~='switch'] {
+ --icon-size: var(--icon-medium);
+
+ block-size: fit-content;
+ margin-block-end: var(--spacer);
+
+ @include config.below('sm-page-break') {
+ [data-icon] {
+ transform: rotate(90deg);
+ }
+ }
+
+ &:hover,
+ &:focus {
+ --outline-width: thin;
+
+ transform: var(--transform, rotate(180deg));
+
+ @media (prefers-reduced-motion: reduce) {
+ --transform: scale(1.1);
+ }
+ }
+}