Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1",
"colorjs.io": "^0.6.1",
"happy-dom": "^20.8.9",
"query-string": "^9.3.1",
"react": "^19.2.4",
Expand Down
32 changes: 18 additions & 14 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
--swatch-size: clamp(6.25rem, 10vw, 12.5rem);
--gray: #cccccc;
--input-width: clamp(12.5rem, 34vw, 35rem);
--label-offset: clamp(4.5rem, 9vw, 9rem);
--label-offset: clamp(4.5rem, 9vw, 9rem);
}

.App {
Expand All @@ -30,24 +30,22 @@ p {
}

.inputs,
.lch-input {
.named-input {
display: flex;
flex-direction: column;
position: relative;
left: -4vw;
}

@media (min-width: 75rem) {
.lch-input {
left: 0;
}
.named-input {
left: 0;
}
.inputs {
display: block;
columns: 2;
left: 0;
}


}

label {
Expand All @@ -61,7 +59,7 @@ label {
text-align: right;
}

input[type=text] {
input[type="text"] {
width: var(--input-width);
font-size: clamp(1rem, 2vw, 2rem);
padding: 1.2vw;
Expand All @@ -70,7 +68,10 @@ input[type=text] {
margin-bottom: 2rem;
margin-left: 1vw;
font-family: "Work Sans", Helvetica, sans-serif;
transition: border-color ease-in .2s, border-radius ease-in .2s, background-color ease-in .2s;
transition:
border-color ease-in 0.2s,
border-radius ease-in 0.2s,
background-color ease-in 0.2s;
}

.color-input-wrapper {
Expand All @@ -82,19 +83,18 @@ input[type=text] {
text-align: left;
}

input[type=color] {
input[type="color"] {
position: relative;
left: -1.2vw;
}

input:focus {
border-color: blue;
border-radius: clamp(.5rem, 1vw, 1rem);;
border-radius: clamp(0.5rem, 1vw, 1rem);
outline: none;
background-color: hsla(60, 100%, 50%, 0.2);
}


.swatch-wrapper {
--square-size: calc(var(--swatch-size) / 5);
--half-square: calc(var(--swatch-size) / 10);
Expand All @@ -118,14 +118,18 @@ input:focus {
}

.swatch-wrapper::before {
content: '';
content: "";
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, rgba(255,255,255,1) 70%);
background-image: linear-gradient(
90deg,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 1) 70%
);
}

.swatch {
Expand Down
8 changes: 0 additions & 8 deletions src/App.test.tsx

This file was deleted.

63 changes: 42 additions & 21 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
import * as React from 'react';
import './App.css';
import * as React from "react";
import "./App.css";
import { useQueryString } from "./utils/useQueryString";
import { colorTypes } from './utils/colorTypes';
import { typeOfColor } from './utils/typeOfColor';
import { colorFavicon } from './utils/colorFavicon';
import { Swatch } from './components/Swatch';
import { Input } from './components/Input';
import { Footer } from './components/Footer';
import { DEFAULT_COLOR } from './constants';

import { colorTypes } from "./utils/colorTypes";
import { typeOfColor } from "./utils/typeOfColor";
import { colorFavicon } from "./utils/colorFavicon";
import { Swatch } from "./components/Swatch";
import { Input } from "./components/Input";
import { Footer } from "./components/Footer";
import { DEFAULT_COLOR } from "./constants";

const App: React.FC = () => {
const [colorQp, setColorQp] = useQueryString("color", DEFAULT_COLOR);

const onInputChange = (value: string) => {
setColorQp(value);
}
};

const incomingColor = colorQp ? colorQp.toString() : ``;
const incomingColorType = typeOfColor(incomingColor);
Expand All @@ -25,13 +24,17 @@ const App: React.FC = () => {
<div className="App">
<header className="App-header">
<h1>ColoRosetta</h1>
<p>A utility to translate colors <a href="https://marketplace.visualstudio.com/items?itemName=chipcullen.colorosetta">(now as a VS Code Extension!)</a></p>
<p>
A utility to translate colors{" "}
<a href="https://marketplace.visualstudio.com/items?itemName=chipcullen.colorosetta">
(now as a VS Code Extension!)
</a>
</p>
</header>

<Swatch color={incomingColor} colorType={incomingColorType} />

<section className="inputs">

<Input
labelText="hex6"
colorType={colorTypes.hex6}
Expand Down Expand Up @@ -68,6 +71,15 @@ const App: React.FC = () => {
incomingColorType={incomingColorType}
/>

<Input
labelText="p3"
colorType={colorTypes.p3}
placeHolder="color(display-p3 1 0 0)"
onChange={onInputChange}
incomingColor={incomingColor}
incomingColorType={incomingColorType}
/>

<Input
labelText="hsl"
colorType={colorTypes.hsl}
Expand All @@ -87,9 +99,18 @@ const App: React.FC = () => {
/>

<Input
labelText="named"
colorType={colorTypes.named}
placeHolder="RebeccaPurple"
labelText="lch"
colorType={colorTypes.lch}
placeHolder="lch(54.291% 106.837 40.858 / 1)"
onChange={onInputChange}
incomingColor={incomingColor}
incomingColorType={incomingColorType}
/>

<Input
labelText="oklch"
colorType={colorTypes.oklch}
placeHolder="oklch(62.8% 0.258 29.234)"
onChange={onInputChange}
incomingColor={incomingColor}
incomingColorType={incomingColorType}
Expand All @@ -104,11 +125,11 @@ const App: React.FC = () => {
incomingColorType={incomingColorType}
/>
</section>
<section className="lch-input">
<section className="named-input">
<Input
labelText="lch"
colorType={colorTypes.lch}
placeHolder="lch(54.291% 106.837 40.858 / 1)"
labelText="named"
colorType={colorTypes.named}
placeHolder="RebeccaPurple"
onChange={onInputChange}
incomingColor={incomingColor}
incomingColorType={incomingColorType}
Expand All @@ -117,6 +138,6 @@ const App: React.FC = () => {
<Footer />
</div>
);
}
};

export default App;
45 changes: 23 additions & 22 deletions src/components/Footer.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
import React from "react";

const Footer: React.FC = () => {

return (
<footer>
&copy; {new Date().getFullYear()}{" "}
<a href="https://chipcullen.com">
chip cullen</a> |{" "}
<a href="https://marketplace.visualstudio.com/items?itemName=chipcullen.colorosetta">
vs code extension
</a>{" "}
|{" "}
<a href="https://chipcullen.com/colorosetta/">
explanatory blog post
</a>{" "}
|{" "}
<a href="https://github.com/chipcullen/colorosetta">
this project on github
</a>{" "}
|{" "}
<a href="https://mastodon.social/@chipcullen" rel="me">
i'm occasionally on mastodon
</a>
<br />
thank you to <a href="https://jonkantner.com/">jon kanter</a> for much of the conversion logic,<br /> and to <a href="https://lea.verou.me/">lea verou</a> and <a href="https://svgees.us/">chris lilley</a> for the fiddly lch bits.
</footer>
&copy; {new Date().getFullYear()}{" "}
<a href="https://chipcullen.com">chip cullen</a> |{" "}
<a href="https://marketplace.visualstudio.com/items?itemName=chipcullen.colorosetta">
vs code extension
</a>{" "}
| <a href="https://chipcullen.com/colorosetta/">explanatory blog post</a>{" "}
|{" "}
<a href="https://github.com/chipcullen/colorosetta">
this project on github
</a>{" "}
|{" "}
<a href="https://mastodon.social/@chipcullen" rel="me">
i'm occasionally on mastodon
</a>
<br />
color conversion logic powered by{" "}
<a href="https://colorjs.io/">color.js</a> - thanks to{" "}
<a href="https://lea.verou.me/">lea verou</a> and{" "}
<a href="https://svgees.us/">chris lilley</a>
<br />
thank you to <a href="https://jonkantner.com/">jon kanter</a> for much of
the original conversion logic.
</footer>
);
};

Expand Down
23 changes: 12 additions & 11 deletions src/components/Input.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useState, useEffect, ChangeEvent } from "react";

import { colorTypes } from "../utils/colorTypes";
import { isLchOutOfRgbGamut } from "../utils/isLchOutOfRgbGamut";
import { isOutOfSrgbGamut } from "../utils/isOutOfSrgbGamut";
import { isValidColor } from "../utils/isValidColor";
import { translatedColor } from "../utils/translatedColor";

Expand Down Expand Up @@ -36,15 +36,15 @@ const Input: React.FC<InputProps> = (props) => {
incomingColorType,
} = props;

const initInputState = () => {
// show the gamut warning on load
if (colorType === colorTypes.lch && isLchOutOfRgbGamut(incomingColor)) {
const getOutOfFocusState = (colorValue: string) => {
if ([colorTypes.lch, colorTypes.oklch, colorTypes.p3].includes(colorType) && isOutOfSrgbGamut(colorValue)) {
return inputStates.outOfFocusOutOfGamut;
} else {
return inputStates.outOfFocus;
}
return inputStates.outOfFocus;
};

const initInputState = () => getOutOfFocusState(incomingColor);

const [value, setValue] = useState(incomingColor);
const [inputState, setInputState] = useState(initInputState());

Expand All @@ -53,7 +53,7 @@ const Input: React.FC<InputProps> = (props) => {
setValue(changedValue);

if (isValidColor(changedValue, colorType)) {
if (colorType === colorTypes.lch && isLchOutOfRgbGamut(changedValue)) {
if ([colorTypes.lch, colorTypes.oklch, colorTypes.p3].includes(colorType) && isOutOfSrgbGamut(changedValue)) {
setInputState(inputStates.inFocusValidValueOutOfGamut);
} else {
setInputState(inputStates.inFocusValidValue);
Expand All @@ -67,7 +67,7 @@ const Input: React.FC<InputProps> = (props) => {
const blurHandler = (e: ChangeEvent<HTMLInputElement>) => {
const changedValue = e.currentTarget.value;
if (isValidColor(changedValue, colorType)) {
setInputState(inputStates.outOfFocus);
setInputState(getOutOfFocusState(changedValue));
} else {
setInputState(inputStates.onBlurInvalidValue);
}
Expand All @@ -86,19 +86,20 @@ const Input: React.FC<InputProps> = (props) => {
translatedIncomingColor !== value
) {
setValue(translatedIncomingColor);
setInputState(inputStates.outOfFocus);
setInputState(getOutOfFocusState(translatedIncomingColor));
}
// disabling this because we only want to update when
// translatedIncomingColor changes, but not value or inputState
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [translatedIncomingColor]);

if (
inputState === inputStates.outOfFocus &&
(inputState === inputStates.outOfFocus || inputState === inputStates.outOfFocusOutOfGamut) &&
translatedIncomingColor !== colorTypes.none &&
translatedIncomingColor !== value
) {
setValue(translatedIncomingColor);
setInputState(getOutOfFocusState(translatedIncomingColor));
}

const showGamutWarning =
Expand Down Expand Up @@ -141,7 +142,7 @@ const Input: React.FC<InputProps> = (props) => {
</label>
{showGamutWarning && (
<small className="gamut-warning">
⚠️ This lch value is outside the RGB gamut; translated values are
⚠️ This value is outside the sRGB gamut; translated values are
approximated
</small>
)}
Expand Down
Loading
Loading