@import "../application/item-badges.css"; /* =================================================================== Shared Components Buttons, item cards, pagination, and other reusable patterns. =================================================================== */ /* Base button defaults - applied to all interactive controls */ button, input[type="submit"], select, .button { padding: 0.5rem 0.75rem; font-size: 0.95rem; border-radius: 0.375rem; border: 1px solid #ddd; background: white; color: var(--color-primary); cursor: pointer; transition: all 0.2s; &:hover { background: #f9f9f9; border-color: var(--color-primary); } &:focus { outline: none; border-color: var(--color-primary); box-shadow: 0 0 0 3px var(--color-primary-muted); } } /* Overlay variant - dark translucent buttons on preview background */ .outfit-preview-section { button, select, input[type="submit"], .button { background: rgba(0, 0, 0, 0.7); border: 1px solid rgba(255, 255, 255, 0.3); color: white; backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); &:hover { background: rgba(0, 0, 0, 0.8); border-color: rgba(255, 255, 255, 0.5); } &:focus { border-color: rgba(255, 255, 255, 0.8); box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.1); } } } /* Primary action variant - solid green for main actions */ .search-form input[type="submit"] { padding: 0.75rem 1.5rem; font-size: 1rem; border: none; background: var(--color-primary); color: white; font-weight: 500; &:hover { background: var(--color-primary-hover); } &:focus { box-shadow: 0 0 0 3px rgba(68, 136, 68, 0.3); } &:active { transform: scale(0.98); } } /* Icon button pattern - small action buttons with hover reveals */ .item-remove-button, .item-add-button, .item-hide-button, .item-show-button { position: absolute; top: 0.5rem; right: 0.5rem; padding: 0; margin: 0; border: none; border-radius: 4px; font-size: 1rem; line-height: 1; width: 1.75rem; height: 1.75rem; display: flex; align-items: center; justify-content: center; opacity: 0; transition: opacity 0.2s, background 0.2s, transform 0.1s; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); &:hover { transform: scale(1.1); } &:active { transform: scale(0.95); } &:focus { opacity: 1; outline: 2px solid var(--color-primary); outline-offset: 2px; } } .item-remove-button { background: rgba(255, 255, 255, 0.9); &:hover { background: rgba(255, 255, 255, 1); } } .item-add-button { background: rgba(68, 136, 68, 0.9); &:hover { background: rgba(68, 136, 68, 1); } } .item-hide-button { right: 2.5rem; background: rgba(255, 255, 255, 0.9); &:hover { background: rgba(255, 255, 255, 1); } } .item-show-button { right: 2.5rem; background: rgba(68, 136, 68, 0.9); &:hover { background: rgba(68, 136, 68, 1); } } /* Item card - shared layout for worn items and search results */ .item-card { display: flex; align-items: center; gap: 0.75rem; padding: 0.75rem; background: #f9f9f9; margin-bottom: 0.5rem; border-radius: 8px; color: #333; transition: background 0.2s, box-shadow 0.2s; position: relative; &:hover { background: #f0f0f0; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } &:hover :is(.item-add-button, .item-remove-button, .item-hide-button, .item-show-button) { opacity: 1; } .item-thumbnail { flex-shrink: 0; width: 50px; height: 50px; border-radius: 6px; overflow: hidden; background: white; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); img { width: 100%; height: 100%; object-fit: contain; display: block; } } .item-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.5rem; } .item-name { font-weight: 500; color: #2D3748; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .item-badges { display: flex; gap: 0.375rem; flex-wrap: wrap; } } /* Worn item emphasis */ .item-card[data-is-worn] { background: #eef5ee; box-shadow: inset 0 0 0 1px rgba(68, 136, 68, 0.2); .item-name { font-weight: 600; } } /* Closeted item de-emphasis */ .item-card[data-is-closeted] { background: #f5f5f5; border: 1px dashed #ccc; opacity: 0.75; } /* Pagination links - treated as buttons for consistency */ .pagination { a, span, em { padding: 0.5rem 0.75rem; font-size: 0.9rem; border-radius: 4px; border: 1px solid #ddd; background: white; color: var(--color-primary); text-decoration: none; transition: all 0.2s; &:hover { background: #f9f9f9; border-color: var(--color-primary); } } .current, em { background: var(--color-primary); color: white; border-color: var(--color-primary); font-style: normal; &:hover { background: var(--color-primary); border-color: var(--color-primary); } } .disabled { color: #ccc; border-color: #eee; cursor: not-allowed; &:hover { background: white; border-color: #eee; } } } /* Progressive enhancement: submit buttons hidden when JS auto-submits */ @media (scripting: enabled) { .progressive-submit { opacity: 0; animation: fade-in 0.25s forwards; animation-delay: 0.75s; } } :is(species-color-picker, pose-picker-popover):state(auto-loading) .progressive-submit { display: none; } /* =================================================================== Page Layout Top-level grid: preview on left/top, controls on right/bottom. =================================================================== */ body.wardrobe-v2 { --color-primary: #448844; --color-primary-hover: #357535; --color-primary-muted: rgba(68, 136, 68, 0.1); --color-accent: #48BB78; --color-accent-glow: rgba(72, 187, 120, 0.4); margin: 0; padding: 0; height: 100vh; overflow: hidden; font-family: sans-serif; } .wardrobe-container { display: grid; height: 100vh; /* Mobile: vertical stack with preview on top, controls below */ grid-template-areas: "preview" "controls"; grid-template-rows: minmax(100px, 45%) minmax(300px, 55%); grid-template-columns: 100%; /* Desktop: side-by-side layout */ @media (min-width: 801px) { grid-template-areas: "preview controls"; grid-template-rows: 100%; grid-template-columns: 50% 50%; } } /* =================================================================== Outfit Preview Left/top panel: outfit viewer, floating controls, pose picker. =================================================================== */ .outfit-preview-section { grid-area: preview; display: flex; align-items: center; justify-content: center; background: rgb(23, 25, 35); position: relative; container-type: size; /* The outfit viewer is a square filling the space, to at most 600px. */ outfit-viewer { width: min(100cqw, 100cqh, 600px); height: min(100cqw, 100cqh, 600px); } .no-preview-message { color: white; text-align: center; padding: 2rem; font-size: 1.2rem; } /* Preview controls container - groups all floating controls */ .preview-controls { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: space-between; padding: 1.5rem; pointer-events: none; /* Start hidden, reveal on hover or focus */ opacity: 0; transition: opacity 0.2s; > * { pointer-events: auto; } } /* Top controls (play/pause) */ .preview-controls-top { display: flex; justify-content: center; width: 100%; } /* Bottom controls (species/color picker, pose picker) */ .preview-controls-bottom { display: flex; align-items: stretch; justify-content: center; gap: 0.5rem; width: 100%; } /* Play/Pause control button */ .play-pause-control-button { display: flex; align-items: center; gap: 0.5rem; padding: 0.5rem 1rem; font-size: 0.95rem; cursor: pointer; /* Hide the checkbox visually */ input[type="checkbox"] { position: absolute; opacity: 0; width: 0; height: 0; } /* Show/hide labels based on checkbox state */ .playing-label, .paused-label { display: none; } input[type="checkbox"]:checked ~ .playing-label { display: block; } input[type="checkbox"]:not(:checked) ~ .paused-label { display: block; } } /* Hide the toggle when there are no animations */ outfit-viewer-play-pause-toggle[hidden] { display: none; } /* Species/color picker */ species-color-picker { display: contents; form { display: contents; } select { padding: 0.5rem 2rem 0.5rem 0.75rem; font-size: 1rem; appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3E%3C/svg%3E"); background-position: right 0.5rem center; background-repeat: no-repeat; background-size: 1.5em 1.5em; option { background: #2D3748; color: white; } } input[type="submit"] { padding: 0.5rem 1rem; font-size: 1rem; } @media (scripting: enabled) { input[type="submit"] { position: absolute; margin-left: 0.5em; } } } /* Show controls on hover (real hover only, not simulated touch hover) */ @media (hover: hover) { &:hover .preview-controls { opacity: 1; } } /* Show controls when they have focus or when popover is open */ &:has(.preview-controls:focus-within) .preview-controls, &:has(.pose-picker-button[popovertargetopen]) .preview-controls { opacity: 1; } } /* =================================================================== Pose Picker Popover Floating panel for choosing expression/pose and alt styles. =================================================================== */ .pose-picker-button { anchor-name: --pose-picker-anchor; display: flex; align-items: center; gap: 0.5rem; border-color: transparent; font-size: 85%; .pose-emoji { font-size: 1.1rem; } .pose-label { font-weight: normal; min-width: 3.5rem; } .chevron { font-size: 0.8rem; opacity: 0.7; } &:focus, &[popovertargetopen] { border-color: rgba(255, 255, 255, 0.8); } } pose-picker-popover { position: absolute; position-anchor: --pose-picker-anchor; margin: 0; inset: auto; position-area: top; background: rgba(0, 0, 0, 0.9); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); border-radius: 12px; padding: 1.25rem; border: 1px solid rgba(255, 255, 255, 0.2); box-shadow: 0 8px 32px rgba(0, 0, 0, 0.6); width: 20rem; .pose-picker-form { display: flex; flex-direction: column; } .pose-picker-table { border-collapse: separate; border-spacing: 0.5rem; th { text-align: center; color: white; padding: 0.25rem; font-weight: normal; } td { padding: 0; } } .emoji-icon { font-size: 1.25rem; display: inline-block; user-select: none; } .pose-option input[type="radio"], .style-option input[type="radio"] { position: absolute; opacity: 0; width: 0; height: 0; } .pose-option { display: block; cursor: pointer; position: relative; width: 60px; height: 60px; margin: 0 auto; .pose-thumbnail { width: 60px; height: 60px; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5); background: rgba(255, 255, 255, 0.1); border: 2px solid transparent; transition: all 0.2s; display: flex; align-items: center; justify-content: center; } .pose-thumbnail-viewer { width: 60px !important; height: 60px !important; transform: scale(0.8); } .pose-unavailable { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; opacity: 0.4; .question-mark { font-size: 2rem; } } /* Selected state */ input[type="radio"]:checked + .pose-thumbnail { border-color: var(--color-accent); box-shadow: 0 0 0 3px var(--color-accent-glow); transform: scale(1.05); } /* Hover state (only for available poses) */ &.available:hover .pose-thumbnail { border-color: rgba(255, 255, 255, 0.5); transform: scale(1.05); } /* Focus state */ input[type="radio"]:focus + .pose-thumbnail { border-color: rgba(255, 255, 255, 0.8); box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.3); } /* Unavailable state */ &.unavailable { cursor: not-allowed; .pose-thumbnail { opacity: 0.5; background: rgba(128, 128, 128, 0.2); } } } .pose-submit-button { margin-top: 1rem; width: 100%; } /* Tab panel layout */ .tab-list { display: flex; gap: 0.25rem; margin-top: 1rem; } .tab-button { flex: 1; padding: 0.4rem 0.75rem; font-size: 0.85rem; border-radius: 999px; border: 1px solid rgba(255, 255, 255, 0.2); background: rgba(255, 255, 255, 0.1); color: rgba(255, 255, 255, 0.6); cursor: pointer; transition: all 0.2s; &:hover { background: rgba(255, 255, 255, 0.15); color: rgba(255, 255, 255, 0.8); border-color: rgba(255, 255, 255, 0.3); } &.active { background: rgba(255, 255, 255, 0.2); color: white; border-color: rgba(255, 255, 255, 0.4); } } /* Without JS, hide tab buttons and show both panels stacked */ tab-panel:not(:defined) .tab-list { display: none; } tab-panel:not(:defined) .tab-content[hidden] { display: block !important; } /* Style picker form */ .style-picker-form { display: flex; flex-direction: column; } .style-picker-list { display: flex; flex-direction: column; gap: 0.25rem; max-height: 200px; overflow-y: auto; } .style-option { display: block; cursor: pointer; .style-option-content { display: flex; align-items: center; gap: 0.5rem; padding: 0.375rem 0.5rem; border-radius: 8px; border: 2px solid transparent; transition: all 0.2s; } .style-option-thumbnail { flex-shrink: 0; width: 40px; height: 40px; border-radius: 6px; overflow: hidden; background: rgba(255, 255, 255, 0.1); img { width: 100%; height: 100%; object-fit: contain; display: block; } } .style-option-name { color: white; font-size: 0.9rem; } /* Hover */ &:hover .style-option-content { border-color: rgba(255, 255, 255, 0.3); background: rgba(255, 255, 255, 0.05); } /* Selected state */ input[type="radio"]:checked + .style-option-content { border-color: var(--color-accent); box-shadow: 0 0 0 2px rgba(72, 187, 120, 0.3); background: rgba(72, 187, 120, 0.1); } /* Focus state */ input[type="radio"]:focus + .style-option-content { border-color: rgba(255, 255, 255, 0.8); box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.2); } } .style-submit-button { margin-top: 1rem; width: 100%; } } /* =================================================================== Outfit Controls Right/bottom panel: worn items, search, and results. =================================================================== */ .outfit-controls-section { grid-area: controls; background: #fff; padding: 2rem; box-sizing: border-box; overflow-y: auto; box-shadow: -2px 0 10px rgba(0, 0, 0, 0.3); h2 { font-size: 1.25rem; color: var(--color-primary); margin-top: 2rem; } .back-button { padding: 0.5rem 1rem; border-radius: 6px; font-weight: 500; } } .current-selection { padding: 1rem; background: #f0f0f0; border-radius: 4px; margin: 1rem 0; p { margin: 0; color: #666; } strong { color: #000; } } .outfit-items { margin-top: 2rem; .items-list { list-style: none; padding: 0; margin: 0.5rem 0; } } .item-search-form { margin-bottom: 1.5rem; display: flex; gap: 0.5rem; align-items: stretch; > form { display: contents; } .search-form { input[type="text"] { flex: 1; padding: 0.75rem; font-size: 1rem; border-radius: 6px; border: 1px solid #ddd; background: white; &:focus { outline: none; border-color: var(--color-primary); box-shadow: 0 0 0 3px var(--color-primary-muted); } } } } .search-results { .search-results-list { list-style: none; padding: 0; margin: 1rem 0; } .empty-state { padding: 2rem; text-align: center; color: #666; } .pagination { margin: 1.5rem 0; display: flex; justify-content: center; gap: 0.5rem; } } /* =================================================================== Flash Messages =================================================================== */ .flash-messages { position: fixed; top: 0; left: 0; right: 0; z-index: 100; padding: 0.75rem 1rem; text-align: center; } .flash-alert { background: #f8d7da; color: #842029; border: 1px solid #f5c2c7; border-radius: 6px; padding: 0.75rem 1rem; max-width: 600px; margin: 0 auto; } /* =================================================================== Outfit Header (name + save button) =================================================================== */ .outfit-header { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 1rem; flex-wrap: wrap; } .outfit-name-form { flex: 1; min-width: 0; display: flex; } .outfit-name-input { flex: 1; min-width: 0; font-size: 1.5rem; font-weight: bold; color: var(--color-primary); border: 1px solid transparent; border-radius: 6px; padding: 0.25rem 0.5rem; background: transparent; transition: border-color 0.2s, background 0.2s; &:hover { border-color: #ddd; background: #fafafa; } &:focus { outline: none; border-color: var(--color-primary); background: white; box-shadow: 0 0 0 3px var(--color-primary-muted); } &::placeholder { color: #aaa; font-weight: normal; } } /* Rename button: hidden by default, shown on hover/focus */ .outfit-name-submit { margin-left: 0.5rem; display: none; } .outfit-name-form:focus-within .outfit-name-submit, .outfit-name-form:hover .outfit-name-submit { display: inline; } /* Static name display for non-owners */ .outfit-name-static { font-size: 1.5rem; font-weight: bold; color: var(--color-primary); padding: 0.25rem 0.5rem; flex: 1; min-width: 0; } /* Web component: static display with pencil icon */ outfit-rename-field { flex: 1; min-width: 0; } outfit-rename-field .outfit-name-form { display: none; } outfit-rename-field[editing] .outfit-rename-static-display { display: none; } outfit-rename-field[editing] .outfit-name-form { display: flex; } .outfit-rename-static-display { display: flex; align-items: center; gap: 0.25rem; } .outfit-rename-name { font-size: 1.5rem; font-weight: bold; color: var(--color-primary); padding: 0.25rem 0.5rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .outfit-rename-pencil { background: none; border: none; cursor: pointer; font-size: 1rem; padding: 0.25rem; opacity: 0; transition: opacity 0.15s; } .outfit-rename-static-display:hover .outfit-rename-pencil, .outfit-rename-pencil:focus { opacity: 1; } /* Hide save button when rename is in editing state */ .outfit-header:has(outfit-rename-field[editing]) .outfit-save-form, .outfit-header:has(outfit-rename-field[editing]) .outfit-save-button:disabled { display: none; } .outfit-save-form { display: inline; } .outfit-save-button { white-space: nowrap; &:disabled { opacity: 0.6; cursor: default; color: #888; border-color: #ddd; background: #f5f5f5; &:hover { background: #f5f5f5; border-color: #ddd; } } } a.outfit-save-button { text-decoration: none; display: inline-block; } /* =================================================================== Animations =================================================================== */ @keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }