UNPKG

color-elements

Version:

A set of web components for working with color. A Color.js project.

136 lines (118 loc) 4.09 kB
:host { display: block; width: var(--size, 320px); max-width: 100%; /* Default ring thickness (fraction of the radius), used only when there's no channel and the disc renders as a ring. */ --ring-thickness: 0.35; /* Render every slotted point (a <color-swatch>, or a <color-scale>'s swatches) as a compact dot with a hover/focus tooltip. Both properties inherit down to the slotted elements, which style themselves accordingly — hue-wheel only positions them (see ::slotted below and hue-wheel-global.css). */ --swatch-style: dot; --details-style: compact; } #wheel { position: relative; aspect-ratio: 1; container-type: inline-size; border-radius: 50%; cursor: crosshair; /* Suppress pan/zoom so dragging on touch doesn't scroll the page. */ touch-action: none; -webkit-user-select: none; user-select: none; } :host([readonly]) #wheel { cursor: default; } #disc { position: absolute; inset: 0; border-radius: 50%; /* The wheel is the only intended pointer target; let events fall through the disc (and its layers) to #wheel. */ pointer-events: none; } /* Ring mode: punch a circular hole so only an annulus of hue shows. */ :host(:state(ring)) #disc { --_inner: calc((1 - var(--ring-thickness)) * 50cqi); mask: radial-gradient(circle, transparent 0 var(--_inner), #000 var(--_inner)); } .layer { position: absolute; /* Outer edge maps to this layer's channel value: fraction 1 → full size, fraction 0 → a point at the centre. */ inset: calc((1 - var(--fraction)) * 50%); border-radius: 50%; } /* Polar placement, shared by the marker and slotted points. --hue (angle) + --channel (raw value) come from JS. The point sits at angle --hue, --_r out from the centre: go to the angle, move out, rotate back so it stays upright — no trig. `translate` (applied before `transform` per spec order) centres the box on the wheel centre first; `transform` then walks it out to the polar point. */ #marker, ::slotted(color-swatch) { position: absolute; top: 50%; left: 50%; margin: 0; --_channel-progress: clamp( 0, (var(--channel) - var(--channel-min)) / (var(--channel-max) - var(--channel-min)), 1 ); --_r: calc(var(--_channel-progress) * 50cqi); translate: -50% -50%; transform: rotate(calc(-1 * var(--hue))) translateX(var(--_r)) rotate(var(--hue)); } /* The built-in marker: the current colour as a <color-swatch> dot (dot mode comes from the inherited --swatch-style), distinguished from plotted points by the dot knobs, which reproduce the prominent white ring. To customise it (incl. adding a tooltip), slot your own <color-swatch slot="marker"> instead. A slotted marker stacks above the points like this one does. */ #marker, ::slotted([slot="marker"]) { z-index: 2; } /* No colour selected → no marker. A bare `hidden` can't hide a <color-swatch> (its own :host sets `display`), so hide it explicitly; otherwise it parks at the centre and jumps to the first click. */ #marker[hidden], ::slotted([slot="marker"][hidden]) { display: none; } #marker { --dot-size: 1em; --dot-border: 2px solid white; --dot-shadow: 0 0 0 1px rgb(0 0 0 / 0.4), 0 1px 2px rgb(0 0 0 / 0.4); /* No tooltip by default — the dot just marks the current colour. */ &::part(details) { display: none; } } /* Plotted points: a directly-slotted <color-swatch> is positioned by its own hue/channel (set by JS) — the same polar placement as the marker (see above). ::slotted() reaches a slotted swatch from here; a <color-scale>'s inner swatches are ::part()s and can't be reached through ::slotted(), so those are placed in hue-wheel-global.css. */ :host(:not([readonly])) { #marker { cursor: grab; &:active { cursor: grabbing; } } } /* No channel → marker and points ride the middle of the ring. */ :host(:state(ring)) :is(#marker, ::slotted(color-swatch)) { --_r: calc((1 - var(--ring-thickness) / 2) * 50cqi); } #marker:focus-visible, ::slotted([slot="marker"]:focus-visible) { outline: 2px solid Highlight; outline-offset: 2px; } slot { display: block; }