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(); + } + };
-
+ 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); + } + } +}