fix: rewrite decoration grid to contain images within cells
The INSET overflow approach (position:absolute images extending beyond 52×52 buttons) was fundamentally broken: absolutely positioned children don't contribute to flex row height, so rowGap controlled button-to-button spacing but image pixels still painted into the gap, causing visual overlap regardless of how large rowGap was set. New approach: 72×72 circle cells, overflow:hidden, image fills the cell via inset:0 with objectFit:contain. Gap of 16px is actual clear space between cell edges — no math needed. Also bumped maxHeight 420→480. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -12,7 +12,7 @@ import {
|
|||||||
import { invalidateDecorationCache } from '../../../hooks/useAvatarDecoration';
|
import { invalidateDecorationCache } from '../../../hooks/useAvatarDecoration';
|
||||||
|
|
||||||
const PROFILE_FIELD = 'io.lotus.avatar_decoration';
|
const PROFILE_FIELD = 'io.lotus.avatar_decoration';
|
||||||
const INSET = 8;
|
const CELL_SIZE = 72;
|
||||||
|
|
||||||
function DecorationPreviewCell({
|
function DecorationPreviewCell({
|
||||||
slug,
|
slug,
|
||||||
@@ -34,29 +34,19 @@ function DecorationPreviewCell({
|
|||||||
onClick={() => onSelect(slug)}
|
onClick={() => onSelect(slug)}
|
||||||
style={{
|
style={{
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
width: 52,
|
width: CELL_SIZE,
|
||||||
height: 52,
|
height: CELL_SIZE,
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
border: `2px solid ${selected ? 'var(--accent-cyan)' : 'transparent'}`,
|
border: `2px solid ${selected ? 'var(--accent-cyan)' : 'transparent'}`,
|
||||||
borderRadius: '0.75rem',
|
borderRadius: '50%',
|
||||||
background: 'var(--bg-surface-variant)',
|
background: 'var(--bg-surface-variant)',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
padding: 0,
|
padding: 0,
|
||||||
boxShadow: selected ? '0 0 0 1px var(--accent-cyan)' : 'none',
|
boxShadow: selected ? '0 0 0 1px var(--accent-cyan)' : 'none',
|
||||||
overflow: 'visible',
|
overflow: 'hidden',
|
||||||
outline: 'none',
|
outline: 'none',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* Avatar placeholder tint */}
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
inset: 0,
|
|
||||||
borderRadius: '0.75rem',
|
|
||||||
background: 'var(--bg-surface-variant)',
|
|
||||||
overflow: 'hidden',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<img
|
<img
|
||||||
src={`${DECORATION_CDN}/${slug}.png`}
|
src={`${DECORATION_CDN}/${slug}.png`}
|
||||||
alt={name}
|
alt={name}
|
||||||
@@ -64,10 +54,9 @@ function DecorationPreviewCell({
|
|||||||
decoding="async"
|
decoding="async"
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: -INSET,
|
inset: 0,
|
||||||
left: -INSET,
|
width: '100%',
|
||||||
width: `calc(100% + ${INSET * 2}px)`,
|
height: '100%',
|
||||||
height: `calc(100% + ${INSET * 2}px)`,
|
|
||||||
objectFit: 'contain',
|
objectFit: 'contain',
|
||||||
pointerEvents: 'none',
|
pointerEvents: 'none',
|
||||||
}}
|
}}
|
||||||
@@ -149,12 +138,12 @@ export function ProfileDecoration() {
|
|||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
width: 52,
|
width: CELL_SIZE,
|
||||||
height: 52,
|
height: CELL_SIZE,
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
borderRadius: '0.75rem',
|
borderRadius: '50%',
|
||||||
background: 'var(--bg-surface-variant)',
|
background: 'var(--bg-surface-variant)',
|
||||||
overflow: 'visible',
|
overflow: 'hidden',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{selected && (
|
{selected && (
|
||||||
@@ -163,10 +152,9 @@ export function ProfileDecoration() {
|
|||||||
alt="Selected decoration preview"
|
alt="Selected decoration preview"
|
||||||
style={{
|
style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: -INSET,
|
inset: 0,
|
||||||
left: -INSET,
|
width: '100%',
|
||||||
width: `calc(100% + ${INSET * 2}px)`,
|
height: '100%',
|
||||||
height: `calc(100% + ${INSET * 2}px)`,
|
|
||||||
objectFit: 'contain',
|
objectFit: 'contain',
|
||||||
pointerEvents: 'none',
|
pointerEvents: 'none',
|
||||||
}}
|
}}
|
||||||
@@ -236,7 +224,7 @@ export function ProfileDecoration() {
|
|||||||
direction="Column"
|
direction="Column"
|
||||||
gap="300"
|
gap="300"
|
||||||
style={{
|
style={{
|
||||||
maxHeight: 420,
|
maxHeight: 480,
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
paddingRight: 4,
|
paddingRight: 4,
|
||||||
}}
|
}}
|
||||||
@@ -250,12 +238,8 @@ export function ProfileDecoration() {
|
|||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexWrap: 'wrap',
|
flexWrap: 'wrap',
|
||||||
columnGap: 28,
|
gap: 16,
|
||||||
rowGap: 52,
|
padding: 4,
|
||||||
paddingBottom: INSET,
|
|
||||||
paddingLeft: INSET,
|
|
||||||
paddingRight: INSET,
|
|
||||||
paddingTop: INSET,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{category.decorations.map((d) => (
|
{category.decorations.map((d) => (
|
||||||
|
|||||||
Reference in New Issue
Block a user