From 88c3b97da4ac9591cecce4a7b3ffcafa64b88978 Mon Sep 17 00:00:00 2001 From: Monster0506 Date: Sun, 20 Sep 2026 08:18:56 -0400 Subject: [PATCH 1/2] Restyle Inventory page as Pocket Ledger mobile concept --- Components/App.razor | 2 + Features/PhysicalAssets/Inventory.razor | 22 +- Features/PhysicalAssets/Inventory.razor.css | 100 +++++ Features/PhysicalAssets/InventoryList.razor | 139 ++++--- .../PhysicalAssets/InventoryList.razor.css | 359 +++++++++--------- 5 files changed, 394 insertions(+), 228 deletions(-) create mode 100644 Features/PhysicalAssets/Inventory.razor.css diff --git a/Components/App.razor b/Components/App.razor index fe4ffc0..36bfd6b 100644 --- a/Components/App.razor +++ b/Components/App.razor @@ -8,6 +8,8 @@ + + diff --git a/Features/PhysicalAssets/Inventory.razor b/Features/PhysicalAssets/Inventory.razor index 02242e3..c46d3ba 100644 --- a/Features/PhysicalAssets/Inventory.razor +++ b/Features/PhysicalAssets/Inventory.razor @@ -3,14 +3,18 @@ Inventory -
- +
+
+
+

Your stuff

+

Inventory

+

Every item you own, whether scanned or manually added.

+
+ + + Add item + +
- +
diff --git a/Features/PhysicalAssets/Inventory.razor.css b/Features/PhysicalAssets/Inventory.razor.css new file mode 100644 index 0000000..dde0b21 --- /dev/null +++ b/Features/PhysicalAssets/Inventory.razor.css @@ -0,0 +1,100 @@ +/* "Pocket Ledger" mobile concept (see design/mobile-mockups/c-pocket-ledger.html) + built for real here. Tokens are declared on this wrapper rather than :root - + scoped .razor.css files must never redeclare :root, see theme.css - and + inherit down into InventoryList.razor's markup through the normal CSS + cascade regardless of each component's own scope attribute. Sora/Inter are + loaded from App.razor's - scoped CSS files cannot use @import. */ +.ledger-page { + --pl-bg: #f5f3ff; + --pl-card: #ffffff; + --pl-violet: #7c6ff0; + --pl-violet-deep: #5d4fd6; + --pl-violet-tint: #efecff; + --pl-ink: #1b1832; + --pl-muted: #746e8c; + --pl-shadow: 0 10px 24px -14px rgba(60, 50, 120, 0.35); + + max-width: 640px; + margin: 0 auto; + padding: 2rem 1.25rem 4rem; + border-radius: 1.5rem; + background: var(--pl-bg); + color: var(--pl-ink); + font-family: "Inter", sans-serif; +} + +.ledger-header { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 1rem; + margin-bottom: 1.25rem; +} + +.ledger-eyebrow { + margin: 0 0 0.25rem; + color: var(--pl-violet-deep); + font-family: "Inter", sans-serif; + font-size: 0.7rem; + font-weight: 700; + letter-spacing: 0.1em; + text-transform: uppercase; +} + +.ledger-header h1 { + margin: 0 0 0.35rem; + color: var(--pl-ink); + font-family: "Sora", sans-serif; + font-size: clamp(1.6rem, 5vw, 2.1rem); + font-weight: 700; +} + +.ledger-subtitle { + max-width: 32ch; + margin: 0; + color: var(--pl-muted); + font-size: 0.85rem; +} + +.ledger-cta { + display: inline-flex; + flex: none; + align-items: center; + gap: 0.4rem; + padding: 0.6rem 1rem; + border-radius: 999px; + background: var(--pl-violet); + color: #fff; + font-family: "Sora", sans-serif; + font-size: 0.85rem; + font-weight: 600; + text-decoration: none; + white-space: nowrap; + box-shadow: 0 8px 18px -8px rgba(124, 111, 240, 0.7); +} + +.ledger-cta svg { + width: 15px; + height: 15px; +} + +.ledger-cta:hover { + background: var(--pl-violet-deep); + color: #fff; +} + +@media (max-width: 480px) { + .ledger-page { + padding: 1.5rem 1rem 3rem; + border-radius: 0; + } + + .ledger-header { + flex-direction: column; + align-items: stretch; + } + + .ledger-cta { + justify-content: center; + } +} diff --git a/Features/PhysicalAssets/InventoryList.razor b/Features/PhysicalAssets/InventoryList.razor index f88c99b..3c9a796 100644 --- a/Features/PhysicalAssets/InventoryList.razor +++ b/Features/PhysicalAssets/InventoryList.razor @@ -9,22 +9,32 @@ @if (_assets is null) { -

Loading...

+

Loading...

} else { -
- Total value - $@_assets.Sum(a => a.CurrentValuation ?? 0).ToString("N2") - @if (_assets.Any(a => a.CurrentValuation is null)) - { - (some items have no valuation yet) - } +
+

Total value

+
+ $@_assets.Sum(a => a.CurrentValuation ?? 0).ToString("N2")USD +
+
+ @_assets.Count @(_assets.Count == 1 ? "item" : "items") scanned + @if (_assets.Any(a => a.CurrentValuation is null)) + { + some unvalued + } +
@if (_assets.Count == 0) { -

No items yet. Add one below, or scan a possession.

+
+ + + +

No items yet. Add one below, or scan a possession.

+
} else { @@ -57,24 +67,19 @@ else class="asset-row-summary" aria-expanded="@(_expandedId == asset.Id)" @onclick="() => ToggleExpandAsync(asset.Id)"> - @asset.Name - - @asset.Category - @if (!string.IsNullOrWhiteSpace(asset.Category)) - { - · - } - @(asset.IsManuallyAdded ? "Manual" : "Scanned") + @CategoryIconMarkup(asset.Category) + + @asset.Name + + @(string.IsNullOrWhiteSpace(asset.Category) + ? (asset.IsManuallyAdded ? "Manual" : "Scanned") + : $"{asset.Category} \u00b7 {(asset.IsManuallyAdded ? "Manual" : "Scanned")}") + @(asset.CurrentValuation is { } v ? $"${v:N2}" : "—") -
- - -
- @if (_expandedId == asset.Id) {
@@ -101,22 +106,24 @@ else
@(_detail.IdentifiedProductModel ?? "—")
-
+
+ + - @if (_revalueError is not null) - { -
- -
- }
+ @if (_revalueError is not null) + { +
+ +
+ } -

Valuation History

+

Valuation History

@if (_detail.ValuationHistory.Count == 0) {

No valuations yet.

@@ -172,18 +179,16 @@ else @if (ShowAddForm) { -
-

Add Item

-

Its value is estimated automatically, the same way a scanned possession's is.

-
-
-
-
-
- -
+
+

Add item

+

Its value is estimated automatically, the same way a scanned possession's is.

+
+ + + +
} @@ -415,4 +420,52 @@ else _isRevaluing = false; } } + + // Purely cosmetic: picks a recognizable glyph for the row's icon badge + // from the free-text category. Falls back to a generic box for anything + // that doesn't match a known bucket - never blocks on an unrecognized + // category. + private static string CategoryIconKey(string? category) + { + if (string.IsNullOrWhiteSpace(category)) + { + return "box"; + } + + var c = category.ToLowerInvariant(); + if (c.Contains("electronic") || c.Contains("tech") || c.Contains("computer") || c.Contains("phone") || c.Contains("device")) + { + return "device"; + } + if (c.Contains("kitchen") || c.Contains("housewares") || c.Contains("home")) + { + return "mug"; + } + if (c.Contains("pantry") || c.Contains("food") || c.Contains("grocery") || c.Contains("snack")) + { + return "bag"; + } + if (c.Contains("furniture")) + { + return "chair"; + } + if (c.Contains("cloth") || c.Contains("apparel") || c.Contains("wear")) + { + return "shirt"; + } + + return "box"; + } + + // The SVG markup is a fixed constant per icon key - never built from the + // category text itself - so this stays safe to render unescaped. + private static MarkupString CategoryIconMarkup(string? category) => new(CategoryIconKey(category) switch + { + "device" => """""", + "mug" => """""", + "bag" => """""", + "chair" => """""", + "shirt" => """""", + _ => """""" + }); } diff --git a/Features/PhysicalAssets/InventoryList.razor.css b/Features/PhysicalAssets/InventoryList.razor.css index 5b9a364..8bbe934 100644 --- a/Features/PhysicalAssets/InventoryList.razor.css +++ b/Features/PhysicalAssets/InventoryList.razor.css @@ -1,87 +1,122 @@ -/* - * Inventory. - * - * Total at the top, items underneath: the running value is the thing you - * came to see, and the list is how you change it. - * - * The rows are an iOS grouped list - one glass pane, hairline separators, - * tap anywhere on a row to expand it. Mobile first, with a desktop - * breakpoint at 48rem to match the rest of the app. - */ - -/* =================================================================== - * Running total - * =================================================================== */ +.ledger-loading { + color: var(--pl-muted); +} -.asset-total { +/* ---- Total value hero ---- */ +.ledger-total-card { + position: relative; + overflow: hidden; margin-bottom: 1rem; - padding: 1rem 1.125rem; - border: 1px solid var(--glass-border); - border-radius: var(--radius-lg); - background-color: var(--glass-bg); - background-image: radial-gradient( - 16rem 9rem at 92% -30%, - color-mix(in srgb, var(--accent) 22%, transparent), - transparent 70% - ); - backdrop-filter: var(--glass-blur); - -webkit-backdrop-filter: var(--glass-blur); - box-shadow: var(--shadow-2); -} - -.asset-total-label { - display: block; - color: var(--text-muted); - font-size: 0.6875rem; - font-weight: 700; - letter-spacing: 0.12em; - text-transform: uppercase; + padding: 1.1rem 1.2rem 1rem; + border-radius: 1.4rem; + background: linear-gradient(135deg, var(--pl-violet) 0%, #9b8ff5 100%); + color: #fff; + box-shadow: 0 14px 30px -16px rgba(124, 111, 240, 0.65); } -.asset-total-amount { - display: block; - margin-top: 0.15rem; - color: var(--text); - font-family: var(--font-numeric); - font-size: clamp(1.875rem, 9vw, 2.5rem); +.ledger-total-card::after { + content: ""; + position: absolute; + top: -60px; + right: -40px; + width: 140px; + height: 140px; + border-radius: 50%; + background: rgba(255, 255, 255, 0.16); +} + +.ledger-total-card .ledger-eyebrow { + position: relative; + color: rgba(255, 255, 255, 0.8); +} + +.ledger-total-figure { + position: relative; + display: flex; + align-items: baseline; + gap: 0.4rem; + font-family: "Sora", sans-serif; + font-size: 2rem; font-weight: 700; - font-variant-numeric: tabular-nums; - letter-spacing: -0.03em; - line-height: 1.05; } -.asset-total-note { - display: block; - margin-top: 0.3rem; - font-size: 0.8125rem; +.ledger-total-figure small { + font-size: 0.8rem; + font-weight: 500; + color: rgba(255, 255, 255, 0.75); +} + +.ledger-total-foot { + position: relative; + display: flex; + align-items: center; + justify-content: space-between; + margin-top: 0.6rem; + font-size: 0.7rem; + color: rgba(255, 255, 255, 0.85); +} + +.ledger-total-pill { + padding: 0.2rem 0.55rem; + border-radius: 999px; + background: rgba(255, 255, 255, 0.2); + font-weight: 600; } -/* =================================================================== - * The list - * =================================================================== */ +/* ---- Icon badge (shared by the empty state and every row) ---- */ +.ledger-icon-badge { + display: flex; + flex: none; + align-items: center; + justify-content: center; + width: 42px; + height: 42px; + border-radius: 0.8rem; + background: var(--pl-violet-tint); + color: var(--pl-violet-deep); +} + +.ledger-icon-badge svg { + width: 19px; + height: 19px; +} + +.ledger-icon-badge-lg { + width: 52px; + height: 52px; +} + +.ledger-icon-badge-lg svg { + width: 26px; + height: 26px; +} + +/* ---- Empty state ---- */ +.ledger-empty { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.75rem; + padding: 2.5rem 1rem; + text-align: center; + color: var(--pl-muted); +} +/* ---- Item list ---- */ .asset-list { - list-style: none; - margin: 0 0 1.5rem; + display: flex; + flex-direction: column; + gap: 0.55rem; + margin: 0 0 1.25rem; padding: 0; - overflow: hidden; - border: 1px solid var(--glass-border); - border-radius: var(--radius-lg); - background: var(--glass-bg); - backdrop-filter: var(--glass-blur); - -webkit-backdrop-filter: var(--glass-blur); - box-shadow: var(--shadow-2); + list-style: none; } .asset-row { - display: grid; - grid-template-columns: minmax(0, 1fr); - align-items: start; - border-bottom: 1px solid var(--glass-hairline); -} - -.asset-row:last-child { - border-bottom: none; + overflow: hidden; + border-radius: 1.1rem; + background: var(--pl-card); + box-shadow: var(--pl-shadow); } /* @@ -91,16 +126,12 @@ * line on a narrow screen. */ .asset-row-summary { - display: grid; - grid-template-columns: minmax(0, 1fr) auto auto; - grid-template-areas: - "name value caret" - "meta meta caret"; + display: flex; align-items: center; - gap: 0.15rem 0.75rem; + width: 100%; - min-width: 0; - padding: 0.875rem 1rem; + gap: 0.7rem; + padding: 0.7rem 0.85rem; border: 0; background: none; color: inherit; @@ -110,84 +141,73 @@ transition: background-color var(--dur-fast) var(--ease-standard); } -.asset-row-summary:active { - background: var(--nav-surface-pressed); +.asset-row-summary:hover { + background: var(--pl-violet-tint); } -@media (hover: hover) { - .asset-row-summary:hover { - background: var(--surface-muted); - } +.asset-row-main { + display: flex; + flex: 1 1 auto; + min-width: 0; + flex-direction: column; + gap: 0.15rem; } .asset-row-name { - grid-area: name; - min-width: 0; overflow: hidden; - font-size: 0.9375rem; + font-family: "Sora", sans-serif; + font-size: 0.88rem; font-weight: 600; text-overflow: ellipsis; white-space: nowrap; } .asset-row-meta { - grid-area: meta; - min-width: 0; - overflow-wrap: anywhere; - color: var(--text-muted); - font-size: 0.75rem; + overflow: hidden; + color: var(--pl-muted); + font-size: 0.72rem; + text-overflow: ellipsis; + white-space: nowrap; } .asset-row-value { - grid-area: value; - min-width: 0; - font-size: 0.9375rem; + flex: none; + font-family: "Sora", sans-serif; + font-size: 0.88rem; font-weight: 700; - font-variant-numeric: tabular-nums; - letter-spacing: -0.015em; - white-space: nowrap; } .asset-row-caret { - grid-area: caret; - align-self: center; - flex: 0 0 auto; - width: 0.7rem; - height: 0.7rem; - background-color: var(--text-muted); - transition: transform var(--dur) var(--ease-spring); + flex: none; + width: 0.65rem; + height: 0.65rem; + margin-left: 0.15rem; + background-color: var(--pl-muted); + mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3E%3C/svg%3E"); mask-repeat: no-repeat; mask-position: center; mask-size: contain; -webkit-mask-repeat: no-repeat; -webkit-mask-position: center; - -webkit-mask-size: contain; + -webkit-mask-size: cover; + transform: rotate(-90deg); + transition: transform 0.15s ease; } .asset-row-summary[aria-expanded="true"] .asset-row-caret { - transform: rotate(180deg); -} - -.asset-row-quick-actions { - display: flex; - gap: 0.35rem; - justify-self: end; - padding: 0 1rem 0.6rem; + transform: rotate(0deg); } .asset-row-detail { - grid-column: 1 / -1; - min-width: 0; - padding: 0 1rem 1.125rem; - border-top: 1px solid var(--glass-hairline); + padding: 0 1rem 1.1rem; + border-top: 1px solid var(--pl-violet-tint); } .asset-detail-image { display: block; - max-width: 240px; - margin: 1rem 0; - border-radius: var(--radius-md); - box-shadow: var(--shadow-1); + max-width: 220px; + margin: 0.9rem 0; + border-radius: 0.9rem; } .asset-detail-facts { @@ -195,33 +215,44 @@ } .asset-detail-facts dt { - color: var(--text-muted); - font-size: 0.6875rem; - font-weight: 600; - letter-spacing: 0.06em; + color: var(--pl-muted); + font-size: 0.7rem; + letter-spacing: 0.04em; text-transform: uppercase; } .asset-detail-facts dd { - margin: 0.15rem 0 0.5rem; + margin: 0 0 0.5rem; + font-weight: 600; } -.asset-detail-revalue { - margin-bottom: 1rem; +/* Bootstrap's .badge defaults to white-space:nowrap, which overflows this + narrower card - the source label is often a full sentence, not a tag. */ +.asset-row-detail .badge { + display: inline-block; + white-space: normal; + text-align: left; + vertical-align: top; } -.asset-row-detail h3 { - font-family: var(--font-display); - font-size: 1.0625rem; - font-weight: 400; +.ledger-detail-actions { + display: flex; + flex-wrap: wrap; + gap: 0.4rem; + margin: 0.75rem 0; +} + +.ledger-subheading { + margin: 1rem 0 0.5rem; + font-family: "Sora", sans-serif; + font-size: 0.95rem; + font-weight: 700; } .asset-edit-form { - grid-column: 1 / -1; - min-width: 0; display: grid; gap: 0.5rem; - padding: 0.875rem 1rem; + padding: 0.85rem; } .asset-row-actions { @@ -229,53 +260,29 @@ gap: 0.5rem; } -.asset-row-actions .btn { - flex: 1; +/* ---- Add item ---- */ +.ledger-add-card { + padding: 1.1rem; + border-radius: 1.1rem; + background: var(--pl-card); + box-shadow: var(--pl-shadow); } -/* =================================================================== - * Add item - * =================================================================== - * The form is built from Bootstrap `.col`s, which stay equal-width at - * every screen size - three text fields and a button at a quarter each - * is unusable on a phone. Stack them until there is room. - */ - -#add-item { - margin-top: 1.5rem; +.ledger-hint { + margin: 0 0 0.75rem; + color: var(--pl-muted); + font-size: 0.78rem; } -#add-item h2 { - font-family: var(--font-display); - font-size: 1.25rem; - font-weight: 400; -} - -#add-item .col, -#add-item .col-auto { - flex: 0 0 100%; -} - -#add-item .col-auto .btn { - width: 100%; +.ledger-add-grid { + display: grid; + gap: 0.5rem; } -/* =================================================================== - * Desktop - * =================================================================== */ - -@media (min-width: 48rem) { - .asset-total { - padding: 1.25rem 1.5rem; - } - - .asset-row { - grid-template-columns: minmax(0, 1fr) auto; - } - - .asset-row-quick-actions { - justify-self: auto; - padding: 0.6rem 0.75rem 0.6rem 0; +@media (min-width: 480px) { + .ledger-add-grid { + grid-template-columns: 1.4fr 1fr 1fr auto; + align-items: center; } .asset-row-summary { From 9426efd9d2a22b6b86b096fef246a3147815dcfe Mon Sep 17 00:00:00 2001 From: Monster0506 Date: Sun, 20 Sep 2026 08:37:03 -0400 Subject: [PATCH 2/2] Match site colorscheme on Inventory at every width --- Features/PhysicalAssets/Inventory.razor | 1 - Features/PhysicalAssets/Inventory.razor.css | 76 +++++------ Features/PhysicalAssets/InventoryList.razor | 2 +- .../PhysicalAssets/InventoryList.razor.css | 120 +++++++++--------- 4 files changed, 98 insertions(+), 101 deletions(-) diff --git a/Features/PhysicalAssets/Inventory.razor b/Features/PhysicalAssets/Inventory.razor index c46d3ba..0e3882e 100644 --- a/Features/PhysicalAssets/Inventory.razor +++ b/Features/PhysicalAssets/Inventory.razor @@ -6,7 +6,6 @@
-

Your stuff

Inventory

Every item you own, whether scanned or manually added.

diff --git a/Features/PhysicalAssets/Inventory.razor.css b/Features/PhysicalAssets/Inventory.razor.css index dde0b21..5e83f95 100644 --- a/Features/PhysicalAssets/Inventory.razor.css +++ b/Features/PhysicalAssets/Inventory.razor.css @@ -1,23 +1,26 @@ /* "Pocket Ledger" mobile concept (see design/mobile-mockups/c-pocket-ledger.html) - built for real here. Tokens are declared on this wrapper rather than :root - - scoped .razor.css files must never redeclare :root, see theme.css - and - inherit down into InventoryList.razor's markup through the normal CSS - cascade regardless of each component's own scope attribute. Sora/Inter are - loaded from App.razor's - scoped CSS files cannot use @import. */ + built for real here, recolored to the site's own palette rather than a + distinct violet identity. Tokens are declared on this wrapper rather than + :root - scoped .razor.css files must never redeclare :root, see theme.css + - and inherit down into InventoryList.razor's markup through the normal + CSS cascade regardless of each component's own scope attribute. The + --pl-* names are kept (rather than renamed to the tokens they now equal) + so the rest of this file and InventoryList.razor.css did not need every + color property rewritten. Sora/Inter are loaded from App.razor's - + scoped CSS files cannot use @import. */ .ledger-page { - --pl-bg: #f5f3ff; - --pl-card: #ffffff; - --pl-violet: #7c6ff0; - --pl-violet-deep: #5d4fd6; - --pl-violet-tint: #efecff; - --pl-ink: #1b1832; - --pl-muted: #746e8c; - --pl-shadow: 0 10px 24px -14px rgba(60, 50, 120, 0.35); + --pl-bg: transparent; + --pl-card: var(--glass-bg); + --pl-violet: var(--brand); + --pl-violet-deep: var(--brand-strong); + --pl-violet-tint: var(--brand-surface); + --pl-ink: var(--text); + --pl-muted: var(--text-muted); + --pl-shadow: var(--shadow-2); max-width: 640px; margin: 0 auto; - padding: 2rem 1.25rem 4rem; - border-radius: 1.5rem; + padding: 1.5rem 1rem 3rem; background: var(--pl-bg); color: var(--pl-ink); font-family: "Inter", sans-serif; @@ -25,22 +28,12 @@ .ledger-header { display: flex; - align-items: flex-start; - justify-content: space-between; + flex-direction: column; + align-items: stretch; gap: 1rem; margin-bottom: 1.25rem; } -.ledger-eyebrow { - margin: 0 0 0.25rem; - color: var(--pl-violet-deep); - font-family: "Inter", sans-serif; - font-size: 0.7rem; - font-weight: 700; - letter-spacing: 0.1em; - text-transform: uppercase; -} - .ledger-header h1 { margin: 0 0 0.35rem; color: var(--pl-ink); @@ -58,8 +51,8 @@ .ledger-cta { display: inline-flex; - flex: none; align-items: center; + justify-content: center; gap: 0.4rem; padding: 0.6rem 1rem; border-radius: 999px; @@ -70,7 +63,7 @@ font-weight: 600; text-decoration: none; white-space: nowrap; - box-shadow: 0 8px 18px -8px rgba(124, 111, 240, 0.7); + box-shadow: var(--shadow-1); } .ledger-cta svg { @@ -83,18 +76,29 @@ color: #fff; } -@media (max-width: 480px) { +/* =================================================================== + * Desktop + * =================================================================== + * Color already matches at every width - these are the shape changes + * that make a page-header read the same as every other page's once + * there is room for a row layout instead of a stack. + */ +@media (min-width: 48rem) { .ledger-page { - padding: 1.5rem 1rem 3rem; - border-radius: 0; + max-width: var(--content-max); + padding: 0.25rem 0 1rem; } .ledger-header { - flex-direction: column; - align-items: stretch; + flex-direction: row; + align-items: flex-end; + justify-content: space-between; + margin-bottom: 1.75rem; } - .ledger-cta { - justify-content: center; + .ledger-header h1 { + font-family: var(--font-display); + font-size: clamp(2.25rem, 4vw, 3.25rem); + font-weight: 400; } } diff --git a/Features/PhysicalAssets/InventoryList.razor b/Features/PhysicalAssets/InventoryList.razor index 3c9a796..9c821d0 100644 --- a/Features/PhysicalAssets/InventoryList.razor +++ b/Features/PhysicalAssets/InventoryList.razor @@ -14,7 +14,7 @@ else {
-

Total value

+

Total value

$@_assets.Sum(a => a.CurrentValuation ?? 0).ToString("N2")USD
diff --git a/Features/PhysicalAssets/InventoryList.razor.css b/Features/PhysicalAssets/InventoryList.razor.css index 8bbe934..5b50a70 100644 --- a/Features/PhysicalAssets/InventoryList.razor.css +++ b/Features/PhysicalAssets/InventoryList.razor.css @@ -8,30 +8,30 @@ overflow: hidden; margin-bottom: 1rem; padding: 1.1rem 1.2rem 1rem; + border: 1px solid var(--glass-border); border-radius: 1.4rem; - background: linear-gradient(135deg, var(--pl-violet) 0%, #9b8ff5 100%); - color: #fff; - box-shadow: 0 14px 30px -16px rgba(124, 111, 240, 0.65); -} - -.ledger-total-card::after { - content: ""; - position: absolute; - top: -60px; - right: -40px; - width: 140px; - height: 140px; - border-radius: 50%; - background: rgba(255, 255, 255, 0.16); -} - -.ledger-total-card .ledger-eyebrow { - position: relative; - color: rgba(255, 255, 255, 0.8); + background-color: var(--pl-card); + background-image: radial-gradient( + 20rem 11rem at 88% -25%, + color-mix(in srgb, var(--accent) 24%, transparent), + transparent 70% + ); + color: var(--pl-ink); + backdrop-filter: var(--glass-blur); + -webkit-backdrop-filter: var(--glass-blur); + box-shadow: var(--shadow-3); +} + +.ledger-total-label { + margin: 0 0 0.25rem; + color: var(--accent-text); + font-size: 0.7rem; + font-weight: 700; + letter-spacing: 0.1em; + text-transform: uppercase; } .ledger-total-figure { - position: relative; display: flex; align-items: baseline; gap: 0.4rem; @@ -43,23 +43,23 @@ .ledger-total-figure small { font-size: 0.8rem; font-weight: 500; - color: rgba(255, 255, 255, 0.75); + color: var(--pl-muted); } .ledger-total-foot { - position: relative; display: flex; align-items: center; justify-content: space-between; margin-top: 0.6rem; + color: var(--pl-muted); font-size: 0.7rem; - color: rgba(255, 255, 255, 0.85); } .ledger-total-pill { padding: 0.2rem 0.55rem; border-radius: 999px; - background: rgba(255, 255, 255, 0.2); + background: var(--accent-surface); + color: var(--accent-text); font-weight: 600; } @@ -119,16 +119,9 @@ box-shadow: var(--pl-shadow); } -/* - * Name and value on the first line, category and provenance on the - * second, with the caret spanning both. Laid out on a grid rather than - * left to wrap, so a long item name cannot push the value onto its own - * line on a narrow screen. - */ .asset-row-summary { display: flex; align-items: center; - width: 100%; gap: 0.7rem; padding: 0.7rem 0.85rem; @@ -138,7 +131,6 @@ font: inherit; text-align: left; cursor: pointer; - transition: background-color var(--dur-fast) var(--ease-standard); } .asset-row-summary:hover { @@ -186,7 +178,7 @@ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3E%3C/svg%3E"); mask-repeat: no-repeat; mask-position: center; - mask-size: contain; + mask-size: cover; -webkit-mask-repeat: no-repeat; -webkit-mask-position: center; -webkit-mask-size: cover; @@ -279,48 +271,50 @@ gap: 0.5rem; } -@media (min-width: 480px) { +@media (min-width: 26rem) { .ledger-add-grid { grid-template-columns: 1.4fr 1fr 1fr auto; align-items: center; } +} - .asset-row-summary { - grid-template-columns: minmax(0, 1fr) auto auto auto; - grid-template-areas: "name meta value caret"; - gap: 0.75rem; - padding: 0.9rem 1.25rem; - } - - .asset-row-detail { - padding: 0 1.25rem 1.25rem; - } - - .asset-edit-form { - grid-template-columns: 2fr 1.5fr 1.5fr 1fr auto; - align-items: center; - padding: 0.9rem 1.25rem; +/* =================================================================== + * Desktop + * =================================================================== + * Color already matches the rest of the app at every width now - what + * is left is shape. A stack of floating cards is a mobile idiom, so + * here the list becomes the one glass pane with hairline rows every + * other list already uses (see NetWorthCalculator's .records-card). + */ +@media (min-width: 48rem) { + .asset-list { + gap: 0; + border: 1px solid var(--glass-border); + border-radius: var(--radius-lg); + background: var(--glass-bg); + backdrop-filter: var(--glass-blur); + -webkit-backdrop-filter: var(--glass-blur); + box-shadow: var(--shadow-2); } - .asset-row-actions .btn { - flex: none; + .asset-row { + border-radius: 0; + background: none; + box-shadow: none; + border-top: 1px solid var(--glass-hairline); } - #add-item .col { - flex: 1 0 0%; + .asset-row:first-child { + border-top: 0; } - #add-item .col-auto { - flex: 0 0 auto; + .asset-row-detail { + border-top-color: var(--glass-hairline); } - #add-item .col-auto .btn { - width: auto; + .ledger-add-card { + border: 1px solid var(--glass-border); + backdrop-filter: var(--glass-blur); + -webkit-backdrop-filter: var(--glass-blur); } } - -/* The chevron, carried over unchanged. */ -.asset-row-caret { - mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3E%3C/svg%3E"); - -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3E%3C/svg%3E"); -}