pagedraw: replace hardcoded chrome colors with semantic tokens

Implements the P0 color item from the PageDraw design review. Swap raw
hex/palette chrome colors for the semantic tokens shared by both lib
hosts (artcraft desktop + artcraft-webapp):

- ContextMenu: #1A1A1A/#333333/#ECECEC -> bg-ui-panel, white/10 hairlines,
  text-base-fg/90
- SideToolbar: zinc swatch + #404040/#303030 popovers -> ui-controls /
  bg-ui-panel + white/10 borders
- SliderWithIndicator: #5F5F68 tooltip -> bg-ui-controls
- SplitPane: gray-500/600 -> white/20-30
- HistoryStack: text-gray-400 -> text-base-fg/50
- PageDraw + base-image-selector: blue-500/600 accents -> primary token
  (so the editor accent matches the product's brand-primary)
- pagedraw.css pegboard: #1b1b1b -> var(--st-canvas, #1b1b1b)

Only tokens defined in both host themes are used. Konva canvas-paint
colors (node strokes, brush defaults, selection fills) are intentionally
left as literals — they're canvas content, not CSS chrome. Oversized
modules deferred per scope.
This commit is contained in:
Tanish Manku committed 2026-06-15 18:01:09 -07:00
1 parent d27d5cec75
commit 6eb88ee565
8 files changed
+20 -18

No files matched your search

@@ -148,8 +148,8 @@ export function BaseImageSelector({
onDrop={onDrop}
className={`flex h-full w-full cursor-pointer flex-col items-center justify-center gap-4 rounded-2xl border-2 border-dashed p-8 text-center transition-colors ${
isDragging
? "border-blue-400 bg-blue-500/10"
: "border-ui-panel-border bg-ui-background hover:border-blue-400/50"
? "border-primary-400 bg-primary/10"
: "border-ui-panel-border bg-ui-background hover:border-primary-400/50"
} ${busy ? "pointer-events-none opacity-60" : ""}`}
>
<input
@@ -159,7 +159,7 @@ export function BaseImageSelector({
onChange={onFileInput}
disabled={busy}
/>
<div className="flex h-14 w-14 items-center justify-center rounded-2xl border border-blue-400/30 bg-blue-500/30 text-blue-300">
<div className="flex h-14 w-14 items-center justify-center rounded-2xl border border-primary-400/30 bg-primary/30 text-primary-300">
<FontAwesomeIcon
icon={busy ? faSpinnerThird : faPencil}
className={`text-xl ${busy ? "animate-spin" : ""}`}
@@ -170,7 +170,7 @@ export const HistoryStack = ({
<div className="mb-2 flex w-full items-center justify-center">
<FontAwesomeIcon
icon={faClockRotateLeft}
className="p-1 text-gray-400"
className="p-1 text-base-fg/50"
/>
</div>
<div
@@ -154,7 +154,7 @@ const Edit3DButton = memo(function Edit3DButton({
return (
<button
className="pointer-events-auto fixed z-40 -translate-x-1/2 -translate-y-1/2 rounded-full bg-blue-600 px-3 py-1 text-xs font-semibold text-white shadow-lg hover:bg-blue-500"
className="pointer-events-auto fixed z-40 -translate-x-1/2 -translate-y-1/2 rounded-full bg-primary px-3 py-1 text-xs font-semibold text-white shadow-lg hover:bg-primary-400"
style={{ left: btnLeft, top: btnTop }}
onPointerDown={(e) => e.stopPropagation()}
onClick={() => onEdit(nodeId)}
@@ -185,7 +185,7 @@ function DragScrubButton({
return (
<button
title={title}
className="flex h-10 w-10 cursor-move items-center justify-center rounded-full bg-black/70 text-white shadow-lg hover:bg-black/90 active:bg-blue-600"
className="flex h-10 w-10 cursor-move items-center justify-center rounded-full bg-black/70 text-white shadow-lg hover:bg-black/90 active:bg-primary"
onPointerDown={onPointerDown}
onPointerMove={onPointerMove}
>
@@ -254,7 +254,7 @@ const Edit3DScrubControls = memo(function Edit3DScrubControls({
/>
<button
onClick={() => overlayHandle.current?.commit()}
className="flex h-10 w-10 items-center justify-center rounded-full bg-blue-600 text-sm font-bold text-white shadow-lg hover:bg-blue-500"
className="flex h-10 w-10 items-center justify-center rounded-full bg-primary text-sm font-bold text-white shadow-lg hover:bg-primary-400"
onPointerDown={(e) => e.stopPropagation()}
>
✓
@@ -41,7 +41,7 @@ export const ContextMenu: React.FC<ContextMenuProps> = ({ items, onAction, onClo
left: `${position.x}px`,
top: `${position.y}px`,
}}
className="fixed z-[9999999] min-w-[220px] bg-[#1A1A1A] rounded-lg shadow-xl border border-[#333333] py-1 select-none"
className="fixed z-[9999999] min-w-[220px] bg-ui-panel rounded-lg shadow-xl border border-white/10 py-1 select-none"
>
{items.map((item, index) => (
<React.Fragment key={index}>
@@ -50,14 +50,14 @@ export const ContextMenu: React.FC<ContextMenuProps> = ({ items, onAction, onClo
onAction(item.action); // Pass the action identifier up
onClose();
}}
className="w-full px-4 py-2.5 flex items-center gap-3 hover:bg-[#333333] text-[#ECECEC] text-[13px] transition-colors duration-75 ease-in-out"
className="w-full px-4 py-2.5 flex items-center gap-3 hover:bg-white/10 text-base-fg/90 text-[13px] transition-colors duration-75 ease-in-out"
>
<span className="w-5 h-5 flex items-center justify-center text-[#ECECEC]">
<span className="w-5 h-5 flex items-center justify-center text-base-fg/90">
{item.icon}
</span>
<span className="font-normal">{item.label}</span>
</button>
{item.divider && <div className="h-[1px] bg-[#333333] mx-2 my-1" />}
{item.divider && <div className="h-[1px] bg-white/10 mx-2 my-1" />}
</React.Fragment>
))}
</div>,
@@ -170,7 +170,7 @@ const SideToolbar: React.FC<SideToolbarProps> = ({
extra?: React.ReactNode,
) => (
<div className={`glass relative w-fit rounded-2xl p-4 shadow-lg`}>
<button className="bg-zinc-700 text-zinc-300 hover:bg-zinc-600 absolute left-4 top-4 flex h-8 w-8 items-center justify-center rounded-full">
<button className="bg-ui-controls/60 text-base-fg hover:bg-ui-controls/80 absolute left-4 top-4 flex h-8 w-8 items-center justify-center rounded-full">
<FontAwesomeIcon icon={faEyeDropper} size="sm" />
</button>
@@ -378,7 +378,7 @@ const SideToolbar: React.FC<SideToolbarProps> = ({
{open === "shape-color" && (
<div
onMouseLeave={() => setOpen(null)}
className="absolute left-14 top-1/2 -translate-y-1/2 rounded-xl border border-[#404040] bg-[#303030] transition-all duration-200 ease-in-out"
className="absolute left-14 top-1/2 -translate-y-1/2 rounded-xl border border-white/10 bg-ui-panel transition-all duration-200 ease-in-out"
>
{ShapePopout}
</div>
@@ -547,7 +547,7 @@ const SideToolbar: React.FC<SideToolbarProps> = ({
onMouseLeave={() => {
setOpen(null);
}}
className="absolute left-14 top-1/2 -translate-y-1/2 rounded-xl border border-[#404040] bg-[#303030] transition-all duration-200 ease-in-out"
className="absolute left-14 top-1/2 -translate-y-1/2 rounded-xl border border-white/10 bg-ui-panel transition-all duration-200 ease-in-out"
>
{popout}
</div>
@@ -58,7 +58,7 @@ const SliderWithIndicator: React.FC<SliderWithIndicatorProps> = ({
absolute
top-6 -translate-x-1/2
transform rounded
bg-[#5F5F68]
bg-ui-controls
px-2 py-1
text-xs font-medium
text-white shadow-lg
@@ -81,8 +81,8 @@ export default function SplitPane({
</div>
<div
className="w-1 cursor-col-resize select-none bg-gray-500/80
transition hover:bg-gray-600"
className="w-1 cursor-col-resize select-none bg-white/20
transition hover:bg-white/30"
onMouseDown={beginDrag}
onTouchStart={beginDrag}
/>
@@ -3,7 +3,9 @@
.pegboard-bg {
inset: 0;
z-index: 0;
background-color: #1b1b1b;
/* Themeable canvas backdrop; hosts may set --st-canvas, otherwise the
original dark grey is used. */
background-color: var(--st-canvas, #1b1b1b);
background-size: 50px 50px;
background-position:
0 0,