inventory
This commit is contained in:
@@ -1,121 +1,309 @@
|
||||
// apps/web/src/app/features/inventory/inventory-page.component.scss
|
||||
:host {
|
||||
display: block;
|
||||
block-size: 100%;
|
||||
}
|
||||
|
||||
/* Fits the viewport instead of growing it, so the bag scrolls inside its own
|
||||
panel rather than dragging the whole screen down. Same measure the combat
|
||||
stage uses for the shell chrome above and below. */
|
||||
.inventory-page {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 20rem;
|
||||
gap: var(--ar-space-5);
|
||||
align-items: start;
|
||||
padding: var(--ar-space-5);
|
||||
}
|
||||
|
||||
.inventory-page__grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr));
|
||||
gap: var(--ar-space-3);
|
||||
}
|
||||
|
||||
.inventory-page__slot {
|
||||
position: relative;
|
||||
padding: var(--ar-space-2);
|
||||
border: 1px solid transparent;
|
||||
border-radius: var(--ar-radius-sm);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.inventory-page__slot--selected {
|
||||
border-color: var(--ar-border-highlight);
|
||||
background: rgb(155 122 66 / 0.12);
|
||||
}
|
||||
|
||||
.inventory-page__equipped-badge {
|
||||
position: absolute;
|
||||
inset-block-start: 0.1rem;
|
||||
inset-inline-start: 50%;
|
||||
translate: -50% 0;
|
||||
padding: 0.05rem 0.4rem;
|
||||
border: 1px solid var(--ar-border-highlight);
|
||||
border-radius: var(--ar-radius-sm);
|
||||
color: var(--ar-gold);
|
||||
background: rgb(9 11 13 / 0.9);
|
||||
font-size: 0.65rem;
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.inventory-page__empty {
|
||||
grid-column: 1 / -1;
|
||||
padding: var(--ar-space-4);
|
||||
color: var(--ar-text-muted);
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.inventory-page__side {
|
||||
display: grid;
|
||||
gap: var(--ar-space-4);
|
||||
}
|
||||
|
||||
.inventory-page__equipment {
|
||||
padding: var(--ar-space-4);
|
||||
border: 1px solid var(--ar-border);
|
||||
border-radius: var(--ar-radius-sm);
|
||||
background: var(--ar-panel);
|
||||
}
|
||||
|
||||
.inventory-page__equipment h3 {
|
||||
margin: 0 0 var(--ar-space-2);
|
||||
color: var(--ar-gold);
|
||||
font-family: Georgia, 'Times New Roman', serif;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.inventory-page__equipment-list {
|
||||
display: grid;
|
||||
gap: var(--ar-space-1);
|
||||
margin: 0 0 var(--ar-space-3);
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.inventory-page__equipment-list li {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
block-size: max(34rem, calc(100dvh - 12rem));
|
||||
min-block-size: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.inventory-page__title {
|
||||
margin: 0 0 var(--ar-space-4);
|
||||
color: var(--ar-text);
|
||||
font-family: Georgia, 'Times New Roman', serif;
|
||||
font-size: clamp(1.5rem, 2.6vw, 2rem);
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.04em;
|
||||
text-shadow: 0 0.1rem 0.6rem rgb(0 0 0 / 0.8);
|
||||
}
|
||||
|
||||
/* Three rails: what you wear, what you carry, what you are looking at. */
|
||||
.inventory-page__columns {
|
||||
display: grid;
|
||||
flex: 1 1 auto;
|
||||
grid-template-columns: minmax(18rem, 21rem) minmax(0, 1fr) minmax(16rem, 19rem);
|
||||
gap: var(--ar-space-4);
|
||||
align-items: stretch;
|
||||
min-block-size: 0;
|
||||
}
|
||||
|
||||
/* The doll takes the slack; the derived values sit under it like a plate. */
|
||||
.inventory-page__rail {
|
||||
display: grid;
|
||||
grid-template-rows: 1fr auto;
|
||||
gap: var(--ar-space-4);
|
||||
min-block-size: 0;
|
||||
}
|
||||
|
||||
/* Panel chrome (.ar-panel) lives in styles.scss — every screen shares it. */
|
||||
|
||||
.panel--doll {
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
min-block-size: 0;
|
||||
}
|
||||
|
||||
/* Title, cells, then the count pinned to the bottom edge. */
|
||||
.panel--bag {
|
||||
display: grid;
|
||||
grid-template-rows: auto auto 1fr;
|
||||
min-block-size: 0;
|
||||
}
|
||||
|
||||
/* ---------- paper doll ---------- */
|
||||
|
||||
.doll {
|
||||
display: grid;
|
||||
grid-template-columns: auto minmax(3.5rem, 1fr) auto;
|
||||
grid-template-rows: minmax(0, 1fr) auto;
|
||||
gap: var(--ar-space-2) var(--ar-space-3);
|
||||
min-block-size: 0;
|
||||
}
|
||||
|
||||
.doll__column {
|
||||
display: grid;
|
||||
grid-row: 1;
|
||||
gap: var(--ar-space-3);
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
/* The same warrior that fights on the combat screen stands here wearing the
|
||||
result. Frame 0 of the six-frame attack sheet is the resting stance; the box
|
||||
is kept square so that frame lands at its own proportions and never smears. */
|
||||
.doll__figure {
|
||||
grid-row: 1;
|
||||
grid-column: 2;
|
||||
z-index: 0;
|
||||
place-self: center;
|
||||
aspect-ratio: 1;
|
||||
/* Wider than its column: the character stands behind the sockets rather than
|
||||
squeezed between them, as in the concept. */
|
||||
inline-size: 165%;
|
||||
max-block-size: 100%;
|
||||
pointer-events: none;
|
||||
background-image: url('/images/combat/sprites/warrior-attack-sheet-384.png');
|
||||
background-repeat: no-repeat;
|
||||
background-position: left center;
|
||||
background-size: 600% 100%;
|
||||
filter: drop-shadow(0 0.4rem 0.9rem rgb(0 0 0 / 0.75));
|
||||
}
|
||||
|
||||
.doll__below {
|
||||
grid-row: 2;
|
||||
grid-column: 2;
|
||||
justify-self: center;
|
||||
}
|
||||
|
||||
.socket {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: grid;
|
||||
gap: 0.15rem;
|
||||
justify-items: center;
|
||||
}
|
||||
|
||||
.socket__label {
|
||||
color: var(--ar-text-muted);
|
||||
font-size: 0.62rem;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.socket__frame {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
inline-size: 3.35rem;
|
||||
block-size: 3.35rem;
|
||||
padding: 0.2rem;
|
||||
border: 1px solid var(--ar-border);
|
||||
background: linear-gradient(180deg, #1b1f22, #0d1012);
|
||||
box-shadow: inset 0 0.15rem 0.5rem rgb(0 0 0 / 0.75);
|
||||
}
|
||||
|
||||
.socket__frame img {
|
||||
inline-size: 100%;
|
||||
block-size: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
/* An empty socket is a recess, not a pattern — quiet enough that a filled
|
||||
one is what the eye lands on. */
|
||||
.socket__frame--empty {
|
||||
border-color: rgb(85 74 57 / 0.55);
|
||||
background: linear-gradient(180deg, #141719, #0a0d0f);
|
||||
}
|
||||
|
||||
.socket__frame--filled {
|
||||
border-color: var(--ar-border-highlight);
|
||||
}
|
||||
|
||||
.socket__frame--filled:hover {
|
||||
border-color: var(--ar-gold);
|
||||
}
|
||||
|
||||
.socket__frame--rare {
|
||||
border-color: var(--ar-blue);
|
||||
}
|
||||
|
||||
.socket__frame--epic {
|
||||
border-color: var(--ar-gold);
|
||||
}
|
||||
|
||||
.socket__frame--selected {
|
||||
outline: 1px solid var(--ar-blue);
|
||||
outline-offset: 1px;
|
||||
}
|
||||
|
||||
/* ---------- values ---------- */
|
||||
|
||||
.values {
|
||||
display: grid;
|
||||
gap: 0.1rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.values__row {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
padding-block: var(--ar-space-1);
|
||||
border-block-end: 1px solid rgb(85 74 57 / 0.4);
|
||||
gap: var(--ar-space-3);
|
||||
padding-block: 0.3rem;
|
||||
}
|
||||
|
||||
.values__row + .values__row {
|
||||
border-block-start: 1px solid rgb(85 74 57 / 0.32);
|
||||
}
|
||||
|
||||
.values dt {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: var(--ar-space-2);
|
||||
color: var(--ar-text-muted);
|
||||
font-size: var(--ar-font-sm);
|
||||
}
|
||||
|
||||
.inventory-page__equipment-slot-label {
|
||||
color: var(--ar-text-muted);
|
||||
.values__glyph {
|
||||
color: var(--ar-border-highlight);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.inventory-page__stats {
|
||||
display: grid;
|
||||
gap: var(--ar-space-1);
|
||||
margin: 0;
|
||||
padding-block-start: var(--ar-space-2);
|
||||
border-block-start: 1px solid rgb(155 122 66 / 0.45);
|
||||
}
|
||||
|
||||
.inventory-page__stats div {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.inventory-page__stats dt {
|
||||
color: var(--ar-text-muted);
|
||||
}
|
||||
|
||||
.inventory-page__stats dd {
|
||||
.values dd {
|
||||
margin: 0;
|
||||
color: var(--ar-text);
|
||||
font-family: Georgia, 'Times New Roman', serif;
|
||||
font-size: 1.05rem;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* ---------- the bag ---------- */
|
||||
|
||||
.bag {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(8, minmax(0, 1fr));
|
||||
gap: var(--ar-space-2);
|
||||
align-content: start;
|
||||
min-block-size: 0;
|
||||
overflow-y: auto;
|
||||
scrollbar-color: var(--ar-border) transparent;
|
||||
scrollbar-width: thin;
|
||||
/* Room for the scrollbar so cells never sit under it. */
|
||||
padding-inline-end: var(--ar-space-2);
|
||||
}
|
||||
|
||||
.cell {
|
||||
position: relative;
|
||||
aspect-ratio: 1;
|
||||
padding: 0.42rem;
|
||||
border: 1px solid var(--ar-border);
|
||||
background: linear-gradient(180deg, #1b1f22, #0d1012);
|
||||
box-shadow: inset 0 0.15rem 0.5rem rgb(0 0 0 / 0.7);
|
||||
}
|
||||
|
||||
.cell--empty {
|
||||
border-color: rgb(85 74 57 / 0.45);
|
||||
background: linear-gradient(180deg, #141719, #0a0d0f);
|
||||
}
|
||||
|
||||
.cell.inventory-page__slot:hover {
|
||||
border-color: var(--ar-border-highlight);
|
||||
}
|
||||
|
||||
.cell--rare {
|
||||
border-color: rgb(92 169 216 / 0.75);
|
||||
}
|
||||
|
||||
.cell--epic {
|
||||
border-color: rgb(201 164 95 / 0.8);
|
||||
}
|
||||
|
||||
.cell--selected {
|
||||
border-color: var(--ar-blue);
|
||||
box-shadow:
|
||||
inset 0 0.15rem 0.5rem rgb(0 0 0 / 0.7),
|
||||
0 0 0 1px var(--ar-blue),
|
||||
0 0 0.7rem rgb(92 169 216 / 0.45);
|
||||
}
|
||||
|
||||
.cell__icon {
|
||||
inline-size: 100%;
|
||||
block-size: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.cell__quantity {
|
||||
position: absolute;
|
||||
inset-block-end: 0.1rem;
|
||||
inset-inline-end: 0.25rem;
|
||||
color: var(--ar-text);
|
||||
font-size: 0.72rem;
|
||||
font-variant-numeric: tabular-nums;
|
||||
text-shadow: 0 0 0.3rem #000, 0 0 0.2rem #000;
|
||||
}
|
||||
|
||||
/* Equipped pieces live on the doll; in the bag they carry a quiet gold notch. */
|
||||
.cell__equipped {
|
||||
position: absolute;
|
||||
inset-block-start: 0;
|
||||
inset-inline-start: 0;
|
||||
inline-size: 0;
|
||||
block-size: 0;
|
||||
border-block-start: 0.55rem solid var(--ar-gold);
|
||||
border-inline-end: 0.55rem solid transparent;
|
||||
}
|
||||
|
||||
.bag__footer {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--ar-space-3);
|
||||
align-items: end;
|
||||
justify-content: space-between;
|
||||
align-self: end;
|
||||
inline-size: 100%;
|
||||
margin-block-start: var(--ar-space-4);
|
||||
padding-block-start: var(--ar-space-3);
|
||||
border-block-start: 1px solid rgb(85 74 57 / 0.5);
|
||||
color: var(--ar-text-muted);
|
||||
font-size: var(--ar-font-sm);
|
||||
}
|
||||
|
||||
.bag__count {
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.bag__hint {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* ---------- notices ---------- */
|
||||
|
||||
.inventory-page__notice {
|
||||
padding: var(--ar-space-4);
|
||||
color: var(--ar-text-muted);
|
||||
@@ -123,11 +311,86 @@
|
||||
}
|
||||
|
||||
.inventory-page__notice--error {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--ar-space-4);
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-block-start: var(--ar-space-4);
|
||||
border: 1px solid var(--ar-danger);
|
||||
background: var(--ar-panel);
|
||||
color: var(--ar-danger);
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
@media (width < 960px) {
|
||||
.inventory-page__notice--error p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.inventory-page__notice--error button {
|
||||
padding: var(--ar-space-2) var(--ar-space-4);
|
||||
border: 1px solid var(--ar-border-highlight);
|
||||
border-radius: var(--ar-radius-sm);
|
||||
color: var(--ar-text);
|
||||
background: linear-gradient(180deg, #23282c, #14181b);
|
||||
font-family: Georgia, 'Times New Roman', serif;
|
||||
}
|
||||
|
||||
.inventory-page__notice--error button:hover {
|
||||
border-color: var(--ar-gold);
|
||||
color: var(--ar-gold);
|
||||
}
|
||||
|
||||
/* ---------- responsive ---------- */
|
||||
|
||||
/* Once the columns start stacking the screen is taller than the viewport, so
|
||||
the page goes back to scrolling as a whole and the bag stops scrolling
|
||||
inside itself — two nested scrollers would fight each other. */
|
||||
@media (width < 76rem) {
|
||||
.inventory-page {
|
||||
grid-template-columns: 1fr;
|
||||
block-size: auto;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.inventory-page__columns {
|
||||
grid-template-columns: minmax(16rem, 18rem) minmax(0, 1fr);
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.inventory-page__detail {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.bag {
|
||||
grid-template-columns: repeat(6, minmax(0, 1fr));
|
||||
overflow-y: visible;
|
||||
}
|
||||
|
||||
.panel--doll,
|
||||
.panel--bag {
|
||||
grid-template-rows: none;
|
||||
}
|
||||
|
||||
.doll {
|
||||
grid-template-rows: none;
|
||||
}
|
||||
|
||||
.doll__figure {
|
||||
inline-size: 100%;
|
||||
min-block-size: 12rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (width < 52rem) {
|
||||
.inventory-page {
|
||||
padding: var(--ar-space-3);
|
||||
}
|
||||
|
||||
.inventory-page__columns {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.bag {
|
||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user