05Figma 패널 — 세 표기법을 서로 맞춰 다시 구하는 컬러피커
하나의 색 값을 HEX·RGB·HSL 세 표기법으로 각각 다시 계산해, 세 필드가 항상 같은 색을 가리키도록 서로 맞추는 컬러피커 패널입니다.
쓰임디자인 협업 툴 색상 패널, 디자인 시스템 토큰 편집기
hexFieldrgbFieldPRESETS

<main class="stage">
<section class="panel">
<div class="swatch" id="swatch"></div>
<dl class="fields">
<div class="field">
<dt class="fieldLabel">HEX</dt>
<dd class="fieldValue" id="hexField">#000000</dd>
</div>
<div class="field">
<dt class="fieldLabel">RGB</dt>
<dd class="fieldValue" id="rgbField">rgb(0, 0, 0)</dd>
</div>
<div class="field">
<dt class="fieldLabel">HSL</dt>
<dd class="fieldValue" id="hslField">hsl(0deg 0% 0%)</dd>
</div>
</dl>
</section>
</main>@use "../../../../site/static/demo-tokens" as *;
@include stage($stage-ink);
.panel {
display: flex;
flex-direction: column;
align-items: stretch;
gap: $sp-4;
width: 100%;
max-width: 220px;
}
.swatch {
height: 64px;
border-radius: $r-card;
box-shadow: $shadow-raised;
}
.fields {
display: flex;
flex-direction: column;
gap: $sp-2;
margin: 0;
}
.field {
display: flex;
align-items: center;
justify-content: space-between;
gap: $sp-2;
padding: $sp-2 $sp-3;
border-radius: $r-sm;
background: rgba($subject-cream, .08);
}
.fieldLabel {
margin: 0;
color: $subject-cream;
opacity: .82;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
font-weight: 600;
font-size: 10px;
line-height: 1.3;
letter-spacing: .04em;
}
.fieldValue {
margin: 0;
color: $subject-cream;
font-family: "Space Mono", ui-monospace, monospace;
font-weight: 700;
font-size: 12px;
line-height: 1.3;
}
@media (max-width: 340px) {
.panel { max-width: 168px; }
.swatch { height: 48px; }
.fieldValue { font-size: 11px; }
}
@media (prefers-reduced-motion: reduce) {
.swatch { transition: none; }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #17141a;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.panel {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 16px;
width: 100%;
max-width: 220px;
}
.swatch {
height: 64px;
border-radius: 16px;
box-shadow: 0 14px 24px rgba(23, 20, 26, 0.32);
}
.fields {
display: flex;
flex-direction: column;
gap: 8px;
margin: 0;
}
.field {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 8px 12px;
border-radius: 8px;
background: rgba(255, 247, 230, 0.08);
}
.fieldLabel {
margin: 0;
color: #fff7e6;
opacity: 0.82;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
font-weight: 600;
font-size: 10px;
line-height: 1.3;
letter-spacing: 0.04em;
}
.fieldValue {
margin: 0;
color: #fff7e6;
font-family: "Space Mono", ui-monospace, monospace;
font-weight: 700;
font-size: 12px;
line-height: 1.3;
}
@media (max-width: 340px) {
.panel {
max-width: 168px;
}
.swatch {
height: 48px;
}
.fieldValue {
font-size: 11px;
}
}
@media (prefers-reduced-motion: reduce) {
.swatch {
transition: none;
}
}// Figma 패널(05) — 하나의 색 값을 HEX·RGB·HSL 세 표기법으로 각각 다시 계산해,
// 세 필드가 항상 같은 색을 가리키도록 서로 맞춘다. 실키·실서버 요청 없음.
(function () {
var swatchEl = document.getElementById('swatch');
var hexField = document.getElementById('hexField');
var rgbField = document.getElementById('rgbField');
var hslField = document.getElementById('hslField');
if (!swatchEl || !hexField || !rgbField || !hslField) return;
var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
var CYCLE_MS = 2600;
var PRESETS = [
{ h: 10, s: 72, l: 54 },
{ h: 70, s: 60, l: 48 },
{ h: 130, s: 55, l: 42 },
{ h: 190, s: 65, l: 50 },
{ h: 250, s: 60, l: 58 },
{ h: 310, s: 68, l: 52 },
{ h: 370, s: 72, l: 54 }
];
var SEG_MS = CYCLE_MS / (PRESETS.length - 1);
function hslToRgb(h, s, l) {
h = h % 360;
s /= 100; l /= 100;
var k = function (n) { return (n + h / 30) % 12; };
var a = s * Math.min(l, 1 - l);
var f = function (n) { return l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1))); };
return [Math.round(f(0) * 255), Math.round(f(8) * 255), Math.round(f(4) * 255)];
}
function toHex(n) {
return n.toString(16).padStart(2, '0');
}
function apply(t) {
var idx = Math.min(PRESETS.length - 2, Math.floor(t / SEG_MS));
var frac = (t % SEG_MS) / SEG_MS;
var a = PRESETS[idx];
var b = PRESETS[idx + 1];
var h = a.h + (b.h - a.h) * frac;
var s = a.s + (b.s - a.s) * frac;
var l = a.l + (b.l - a.l) * frac;
var hMod = h % 360;
var rgb = hslToRgb(h, s, l);
var hex = '#' + toHex(rgb[0]) + toHex(rgb[1]) + toHex(rgb[2]);
var hslStr = 'hsl(' + hMod.toFixed(0) + 'deg ' + s.toFixed(0) + '% ' + l.toFixed(0) + '%)';
var rgbStr = 'rgb(' + rgb[0] + ', ' + rgb[1] + ', ' + rgb[2] + ')';
swatchEl.style.background = hslStr;
hexField.textContent = hex;
rgbField.textContent = rgbStr;
hslField.textContent = hslStr;
}
if (reduce) {
apply(0);
return;
}
var start = null;
function loop(ts) {
if (start === null) start = ts;
var t = (ts - start) % CYCLE_MS;
apply(t);
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
})();import { useEffect, useRef } from "react";
import styles from "./ColorPickerFigmaPanel.module.scss";
// Figma 패널 — 하나의 색 값을 HEX·RGB·HSL 세 표기법으로 각각 다시 계산해,
// 세 필드가 항상 같은 색을 가리키도록 서로 맞춘다. 실키·실서버 요청 없음.
const CYCLE_MS = 2600;
const PRESETS = [
{ h: 10, s: 72, l: 54 },
{ h: 70, s: 60, l: 48 },
{ h: 130, s: 55, l: 42 },
{ h: 190, s: 65, l: 50 },
{ h: 250, s: 60, l: 58 },
{ h: 310, s: 68, l: 52 },
{ h: 370, s: 72, l: 54 },
];
const SEG_MS = CYCLE_MS / (PRESETS.length - 1);
function hslToRgb(h: number, s: number, l: number): [number, number, number] {
h = h % 360;
s /= 100;
l /= 100;
const k = (n: number) => (n + h / 30) % 12;
const a = s * Math.min(l, 1 - l);
const f = (n: number) => l - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)));
return [Math.round(f(0) * 255), Math.round(f(8) * 255), Math.round(f(4) * 255)];
}
function toHex(n: number) {
return n.toString(16).padStart(2, "0");
}
export default function ColorPickerFigmaPanel() {
const swatchRef = useRef<HTMLDivElement>(null);
const hexRef = useRef<HTMLElement>(null);
const rgbRef = useRef<HTMLElement>(null);
const hslRef = useRef<HTMLElement>(null);
useEffect(() => {
const swatch = swatchRef.current;
const hexField = hexRef.current;
const rgbField = rgbRef.current;
const hslField = hslRef.current;
if (!swatch || !hexField || !rgbField || !hslField) return;
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
function apply(t: number) {
const idx = Math.min(PRESETS.length - 2, Math.floor(t / SEG_MS));
const frac = (t % SEG_MS) / SEG_MS;
const a = PRESETS[idx];
const b = PRESETS[idx + 1];
const h = a.h + (b.h - a.h) * frac;
const s = a.s + (b.s - a.s) * frac;
const l = a.l + (b.l - a.l) * frac;
const hMod = h % 360;
const rgb = hslToRgb(h, s, l);
const hex = "#" + toHex(rgb[0]) + toHex(rgb[1]) + toHex(rgb[2]);
const hslStr = `hsl(${hMod.toFixed(0)}deg ${s.toFixed(0)}% ${l.toFixed(0)}%)`;
const rgbStr = `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`;
swatch!.style.background = hslStr;
hexField!.textContent = hex;
rgbField!.textContent = rgbStr;
hslField!.textContent = hslStr;
}
if (reduce) {
apply(0);
return;
}
let start: number | null = null;
let raf = 0;
function loop(ts: number) {
if (start === null) start = ts;
const t = (ts - start) % CYCLE_MS;
apply(t);
raf = requestAnimationFrame(loop);
}
raf = requestAnimationFrame(loop);
return () => cancelAnimationFrame(raf);
}, []);
return (
<div className={styles.stage}>
<section className={styles.panel}>
<div className={styles.swatch} ref={swatchRef} />
<dl className={styles.fields}>
<div className={styles.field}>
<dt className={styles.fieldLabel}>HEX</dt>
<dd className={styles.fieldValue} ref={hexRef}>#000000</dd>
</div>
<div className={styles.field}>
<dt className={styles.fieldLabel}>RGB</dt>
<dd className={styles.fieldValue} ref={rgbRef}>rgb(0, 0, 0)</dd>
</div>
<div className={styles.field}>
<dt className={styles.fieldLabel}>HSL</dt>
<dd className={styles.fieldValue} ref={hslRef}>hsl(0deg 0% 0%)</dd>
</div>
</dl>
</section>
</div>
);
}05 Figma 패널 — 세 표기법을 서로 맞춰 다시 구하는 컬러피커 — 측정 누적면적 10.797% · 움직인 프레임 23/23GODRICH 원작 · zip 에 SCSS·React 포함 · 컬러피커 UI 디자인 9종 — 휠부터 최근 색상까지
이 데모가 속한 글
컬러피커 UI 디자인 9종 — 휠부터 최근 색상까지선택 지점의 각도가 색상값으로 다시 계산되고, 스와치 밝기 값에 따라 체크 아이콘 색이 뒤바뀌고, 최근 색상 사이 거리가 계산되어 가까운 색끼리 합쳐지는 컬러피커 UI 9종입니다. 아홉 화면 전부 속성 값 바꾸기로 이 글의 데모 전부 보기


