Files
apparel-designer/src/styles/LayersPanel.css
2026-05-23 03:28:58 -05:00

314 lines
9.2 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* ──────────────────────────────────────────────────────────────────────────
* LayersPanel — Pawfectly Yours
*
* Class names match the rewritten LayersPanel.jsx (S3 + S24):
* • Each row is a real <button> for keyboard access (S24).
* • A ✓ chip + ring communicates selection without relying on color (S24).
* • Drag handle (⋮⋮) per row enables HTML5 DnD reorder (S3).
* • Bulk-delete affordance appears in the titlebar when ≥2 are selected (S3).
* • Drop indicator (.layers-drop-indicator) is the pink insertion line that
* appears above or below the hovered row during a drag.
* ────────────────────────────────────────────────────────────────────────── */
.layers-empty {
padding: 1rem;
text-align: center;
color: var(--text-muted);
font-size: 12px;
}
/* Titlebar — title on the left, bulk-delete on the right. The bulk-delete
* only renders when 2+ rows are selected; in single-select state the bar
* is just the title and looks identical to before. */
.layers-titlebar {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 0.75rem;
gap: 0.5rem;
}
.layers-title {
margin: 0;
font-size: 12px;
font-weight: 600;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.4px;
}
.layers-bulk-delete {
appearance: none;
background: transparent;
border: 1px solid #fecaca;
color: #b91c1c;
font-size: 11px;
font-weight: 700;
padding: 0.3rem 0.6rem;
border-radius: var(--radius-pill);
cursor: pointer;
transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
.layers-bulk-delete:hover {
background: #fef2f2;
border-color: #fca5a5;
color: #991b1b;
}
.layers-list {
display: flex;
flex-direction: column;
gap: 4px;
list-style: none;
padding: 0;
margin: 0;
}
/* Row wrapper: container for the row plus optional drop-indicator lines
* above and below it. Using a wrapper rather than positioning the
* indicator inside the row makes it easier to render before/after the
* row's bbox without margin tricks. */
.layers-row-wrap {
position: relative;
display: flex;
flex-direction: column;
gap: 2px;
}
/* The row itself — a flex row holding [delete] [main button] [grip]. It's
* a div (not a button) because the row contains two distinct interactive
* elements; the row-level click target is .layers-item-main. */
.layers-item {
display: flex;
align-items: stretch;
gap: 0.25rem;
padding: 0;
background: transparent;
border: 1px solid var(--border);
border-radius: var(--radius-sm);
transition: background 0.12s ease, border-color 0.12s ease, opacity 0.12s ease;
}
.layers-item.selected {
background: var(--accent-bg);
border-color: var(--accent);
/* Visible ring around selected rows so colorblind users can see
* selection without depending on the pink fill alone. (S24.) */
box-shadow: 0 0 0 2px rgba(236, 72, 153, 0.18);
}
/* While being dragged, dim the row so the user can see what's happening
* and so the drop indicator lines below stand out by contrast. */
.layers-item.is-dragging {
opacity: 0.4;
}
/* Drag grip — vertical dots glyph on the row's right edge. Decorative for
* keyboard users (tabIndex=-1 in the JSX); actually pickable for mouse
* via HTML5 DnD on the parent .layers-item. */
.layers-item-grip {
display: inline-flex;
align-items: center;
justify-content: center;
width: 18px;
flex-shrink: 0;
color: var(--text-muted);
font-size: 11px;
letter-spacing: -2px;
cursor: grab;
user-select: none;
}
.layers-item.is-dragging .layers-item-grip {
cursor: grabbing;
}
/* Main row button — wraps the check chip, icon, and name. Reset native
* button styling so it fits the row visually but inherits keyboard
* accessibility from the underlying <button>. */
.layers-item-main {
flex: 1;
display: inline-flex;
align-items: center;
gap: 0.5rem;
appearance: none;
background: transparent;
border: none;
padding: 0.5rem 0.5rem;
font: inherit;
text-align: left;
cursor: pointer;
color: inherit;
border-radius: var(--radius-sm);
min-width: 0; /* allow ellipsis truncation inside flex */
}
.layers-item-main:focus-visible {
outline: 2px solid var(--brand-pink);
outline-offset: -2px;
}
/* Selection ✓ chip — rendered as a small circle on the left of selected
* rows. Empty (no glyph, no border) when the row isn't selected so the
* left edge of unselected rows reads cleanly. (S24.) */
.layers-item-check {
display: inline-flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
font-size: 11px;
font-weight: 700;
color: transparent;
flex-shrink: 0;
border-radius: 50%;
transition: background 0.12s ease, color 0.12s ease;
}
.layers-item-check.is-on {
background: var(--brand-pink);
color: #fff;
}
/* Icon slot — fixed 24×24 box on the left of the row's main button.
* Holds one of three things depending on element type:
* - <img> for image and sticker layers (a mini preview of the actual
* pixel content; see renderIcon in LayersPanel.jsx)
* - <svg> for text layers (a T glyph matching the Text tab nav icon)
* - <span> fallback dot for layers we don't have a special icon for
*
* `overflow: hidden` + rounded corners give image thumbnails a polished
* tile look; the soft tint background ensures transparent stickers still
* read against the row. */
.layers-item-icon {
width: 24px;
height: 24px;
flex-shrink: 0;
display: inline-flex;
align-items: center;
justify-content: center;
overflow: hidden;
border-radius: 4px;
background: var(--brand-pink-tint);
color: var(--text-secondary);
}
/* Image / sticker thumbnail. object-fit: contain keeps the artwork's
* aspect ratio without cropping inside the square slot. */
.layers-item-icon-img {
width: 100%;
height: 100%;
object-fit: contain;
display: block;
pointer-events: none;
user-select: none;
}
/* Text-layer 'T' icon. Sized slightly smaller than the slot so it doesn't
* touch the rounded edges; inherits currentColor from the row text so it
* inverts cleanly when the row is selected. */
.layers-item-icon-svg {
width: 16px;
height: 16px;
}
/* Fallback dot for any element type without a custom icon. */
.layers-item-icon-fallback {
font-size: 14px;
line-height: 1;
color: var(--text-muted);
}
/* When the row is selected, switch the icon slot's tint to read against
* the pink selection background — a translucent white tile that lets the
* thumbnail content show through and lifts the SVG T icon's contrast. */
.layers-item.selected .layers-item-icon {
background: rgba(255, 255, 255, 0.65);
}
.layers-item-name {
flex: 1;
font-size: 12px;
color: var(--text-primary);
font-weight: 400;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
min-width: 0;
}
.layers-item.selected .layers-item-name {
color: var(--accent);
font-weight: 600;
}
/* Per-row action buttons (Change 4). Sit on the RIGHT side of each
* row as siblings of .layers-item-main (the row's main click target),
* stacked horizontally: [main] [duplicate] [lock] [delete] [grip].
*
* Sizing matches the previous .layers-item-delete (28×row-height) so
* the row maintains its original visual weight; each button is just
* a touch slimmer than the main click target so the three icons
* don't dominate the row. Color states:
* • idle: muted grey, same as old delete X
* • hover: brand-pink tint
* • active (lock when locked): brand-pink fill + white icon —
* gives the user instant feedback that the toggle is on
* • delete hover: red, retaining the destructive-action colour
* signal from the previous design
*/
.layers-item-action {
width: 28px;
flex-shrink: 0;
appearance: none;
background: transparent;
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-muted);
border-radius: var(--radius-sm);
transition: background 0.12s ease, color 0.12s ease;
}
.layers-item-action:hover {
background: var(--brand-pink-soft);
color: var(--brand-pink-strong);
}
.layers-item-action:focus-visible {
outline: 2px solid var(--brand-pink);
outline-offset: -2px;
}
.layers-item-action.is-active {
background: var(--brand-pink-soft);
color: var(--brand-pink-strong);
}
.layers-item-action.is-active:hover {
background: var(--brand-pink);
color: #fff;
}
/* Delete variant — same base + destructive-red hover so the user
* gets the same colour-coded warning the previous bare ✕ button had. */
.layers-item-action--delete:hover {
background: #fef2f2;
color: #b91c1c;
}
/* Drop indicator — a thin pink line that appears above or below the
* hovered row during a drag, communicating where the dragged row will
* land if dropped. Rendered as a sibling of the row inside the row
* wrapper so its position is unambiguous. (S3.) */
.layers-drop-indicator {
height: 2px;
background: var(--brand-pink);
border-radius: 1px;
margin: 0 4px;
box-shadow: 0 0 0 2px rgba(236, 72, 153, 0.18);
}