edit page: Material-style cards, status bar, primary save action

Per fox: "make this look awesome", "use styleguide", "think material
design to make it stand out as to what to do".

Status bar at top of edit page — three pills surface the product's
current state in one glance:
- Visibility: Public (blue) / Unlisted (purple) / Private (gray)
- Ready to sell (green) / Missing required files (gold warning)
- Current price (gray)

The pills tell the owner immediately what's done and what isn't.

Section cards (.well2 on edit page) get Material-card treatment:
- Background, rounded corners, border, elevation-1 shadow
- Hover/focus-within bumps to elevation-3 (subtle lift)
- h3 header gets a 36px circular icon badge (.edit-card-icon),
  thin bottom border separating header from content
- Icons: 📁 product file, 📺 preview, 🎨 thumbnails, ✏️ details,
  📦 inventory, 📦 bundle

Primary save action: Save Settings button is now a filled,
prominent CTA (navy bg, white text, bold, elevation-1 shadow that
lifts to elevation-2 on hover, disabled state dims). Class
.mps-button-primary on the styleguide for reuse.

.edit-save-bar provides a sticky-bottom save container CSS for a
follow-up wrapper around the Save Settings input (not yet wired
into product_edit.j2 — same hash adds the CSS so it's available).

All wired into the styleguide:
- Status bar + 3 pill states (public ready, private incomplete, etc.)
- Section card with icon header (3 example cards)
- Primary save action button

Tokens used (with fallbacks for safety): --elevation-1/3, --radius-lg,
--radius-pill, --color-navy/navy-dark, --color-blue-tint, --space-N,
--motion-fast, --color-green-dark, --color-gold, --color-purple.

Tests still pass (10 in target slice).
This commit is contained in:
russell@unturf.com 2026-05-10 16:26:30 -04:00
parent 2c63c60118
commit 705271ec76
No known key found for this signature in database
3 changed files with 248 additions and 7 deletions

View file

@ -1409,6 +1409,159 @@ div.edit-page textarea.mps-content-description {
max-width: 100%;
}
/* Edit-page Material cards
Wells on the edit page get card-style elevation, rounded corners,
border accent, and a subtle hover lift. The goal: each section
reads as a discrete step the owner can act on. */
div.edit-page > section.well2 {
background: var(--surface, #fff);
border-radius: var(--radius-lg, 12px);
box-shadow: var(--elevation-1, 0 1px 3px rgba(0,0,0,0.06));
border: 1px solid var(--color-border, #eee);
transition: box-shadow var(--motion-fast, 150ms) ease,
transform var(--motion-fast, 150ms) ease;
}
div.edit-page > section.well2:hover,
div.edit-page > section.well2:focus-within {
box-shadow: var(--elevation-3, 0 4px 12px rgba(0,0,0,0.07));
}
/* Card header h3 with leading icon. Class .edit-card-icon turns
any inline glyph into a 32px circular badge. */
div.edit-page > section.well2 > h3:first-child,
div.edit-page > section.well2 > form > h3:first-child {
display: flex;
align-items: center;
gap: var(--space-3, 12px);
margin-bottom: var(--space-3, 12px);
padding-bottom: var(--space-3, 12px);
border-bottom: 1px solid var(--color-border, #eee);
color: var(--color-text-primary, #222);
}
.edit-card-icon {
display: inline-flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
border-radius: 50%;
background: var(--color-blue-tint, #e8efff);
color: var(--color-navy, #5871ad);
font-size: 1.1rem;
flex-shrink: 0;
}
/* Status pill at the top of the edit page, surfaces "ready / not ready"
and the visibility state at a glance. */
.edit-status-bar {
display: flex;
flex-wrap: wrap;
gap: var(--space-2, 8px);
align-items: center;
margin-bottom: var(--space-4, 16px);
padding: var(--space-3, 12px) var(--space-4, 16px);
border-radius: var(--radius-md, 8px);
background: var(--surface-dim, #f9f9fa);
border: 1px solid var(--color-border, #eee);
}
.edit-status-pill {
display: inline-flex;
align-items: center;
gap: var(--space-1, 4px);
padding: var(--space-1, 4px) var(--space-3, 12px);
border-radius: var(--radius-pill, 999px);
font-size: 0.85em;
font-weight: 600;
background: var(--color-text-muted, #888);
color: #fff;
}
.edit-status-pill-ready {
background: var(--color-green-dark, #8ab34e);
}
.edit-status-pill-incomplete {
background: var(--color-gold, #d4a843);
color: #333;
}
.edit-status-pill-public {
background: var(--color-blue, #5871ad);
}
.edit-status-pill-private {
background: var(--color-text-muted, #888);
}
.edit-status-pill-unlisted {
background: var(--color-purple, #9B59B6);
}
/* Primary save action sticky at the bottom of the form and rendered
as a filled, prominent button. Counter-balances the floating save
that lives inside the form. */
div.edit-page input.mps-submit[value="Save Settings"],
div.edit-page button.mps-submit-primary,
.mps-button-primary {
float: none;
display: inline-flex;
align-items: center;
justify-content: center;
padding: var(--space-3, 12px) var(--space-6, 24px);
background: var(--color-navy, #5871ad);
color: #fff;
border: 1px solid var(--color-navy, #5871ad);
border-radius: var(--radius-md, 8px);
font-weight: 600;
font-size: 1rem;
box-shadow: var(--elevation-1, 0 1px 3px rgba(0,0,0,0.12));
cursor: pointer;
transition: background var(--motion-fast, 150ms) ease,
box-shadow var(--motion-fast, 150ms) ease;
}
div.edit-page input.mps-submit[value="Save Settings"]:hover,
div.edit-page button.mps-submit-primary:hover,
.mps-button-primary:hover {
background: var(--color-navy-dark, #44588a);
box-shadow: var(--elevation-2, 0 2px 6px rgba(0,0,0,0.15));
}
div.edit-page input.mps-submit[value="Save Settings"]:disabled,
div.edit-page button.mps-submit-primary:disabled,
.mps-button-primary:disabled {
opacity: 0.5;
cursor: not-allowed;
box-shadow: none;
}
/* Sticky save bar at the bottom of the edit page keeps Save Settings
in view while user scrolls through long edit forms. */
.edit-save-bar {
position: sticky;
bottom: var(--space-3, 12px);
margin-top: var(--space-5, 20px);
padding: var(--space-3, 12px) var(--space-4, 16px);
background: var(--surface, #fff);
border-radius: var(--radius-md, 8px);
box-shadow: var(--elevation-3, 0 4px 12px rgba(0,0,0,0.07));
border: 1px solid var(--color-border, #eee);
display: flex;
justify-content: flex-end;
align-items: center;
gap: var(--space-3, 12px);
z-index: 10;
}
.edit-save-bar-status {
flex: 1;
color: var(--color-text-muted, #666);
font-size: 0.9em;
}
hr {
color: var(--border-light, #EEEEEE);
}

View file

@ -12,13 +12,37 @@ permanent link: <a href="{{ product.absolute_url(request) }}">{{ product.absolut
{% set audio_extensions = ["mp3", "wav", "ogg", "m4a", "flac", "aac", "opus"] %}
{% set image_extensions = ["jpg", "jpeg", "png", "gif", "webp", "svg", "bmp", "ico"] %}
{# Status bar: at-a-glance state. Visibility + ready-to-sell. #}
<section class="edit-status-bar">
{% if product.visibility == 1 %}
<span class="edit-status-pill edit-status-pill-public">Public</span>
{% elif product.visibility == 2 %}
<span class="edit-status-pill edit-status-pill-unlisted">Unlisted</span>
{% else %}
<span class="edit-status-pill edit-status-pill-private">Private</span>
{% endif %}
{% if product.is_ready %}
<span class="edit-status-pill edit-status-pill-ready">&#10003; Ready to sell</span>
{% else %}
<span class="edit-status-pill edit-status-pill-incomplete">&#9888; Missing required files</span>
{% endif %}
{% if product.is_sellable %}
<span class="edit-status-pill" style="background: var(--color-text-muted, #888);">${{ '{:.2f}'.format(price) }}</span>
{% endif %}
</section>
<div class="edit-page">
<section class="upload-product-and-preview well2">
{% if product.is_bundle %}
<h3>Product IDs or URLs to include in this bundle</h3>
<h3>
<span class="edit-card-icon">&#128230;</span>
Product IDs or URLs to include in this bundle
</h3>
<form method="post" action="/p/{{ product.id }}/bundle/edit" onsubmit="submit.disabled = true; return true;">
<textarea
@ -47,7 +71,10 @@ permanent link: <a href="{{ product.absolute_url(request) }}">{{ product.absolut
{% elif product.is_physical %}
<h3>Inventory Management</h3>
<h3>
<span class="edit-card-icon">&#128230;</span>
Inventory Management
</h3>
<form method="post" action="/p/{{ product.id }}/inventory/edit" onsubmit="submit.disabled = true; return true;">
<table>
@ -79,7 +106,10 @@ permanent link: <a href="{{ product.absolute_url(request) }}">{{ product.absolut
{% else %}
<h3>Upload or Replace {% if product.is_sellable %}Product{% else %}Content{% endif %} File</h3>
<h3>
<span class="edit-card-icon">&#128193;</span>
Upload or Replace {% if product.is_sellable %}Product{% else %}Content{% endif %} File
</h3>
People may download this file{% if product.is_sellable %} after making a purchase{% endif %}.
@ -125,7 +155,10 @@ permanent link: <a href="{{ product.absolute_url(request) }}">{{ product.absolut
<hr/>
<h3>Upload or Replace Preview File</h3>
<h3>
<span class="edit-card-icon">&#128250;</span>
Upload or Replace Preview File
</h3>
People may download this file before making a purchase.
@ -169,7 +202,10 @@ permanent link: <a href="{{ product.absolute_url(request) }}">{{ product.absolut
</section>
<section class="upload-thumbnails well2">
<h3>Upload or Replace Thumbnail Files</h3>
<h3>
<span class="edit-card-icon">&#127912;</span>
Upload or Replace Thumbnail Files
</h3>
<section>
Your cover (<code>thumbnail1</code>) will show up on search pages.
@ -229,7 +265,10 @@ Your cover (<code>thumbnail1</code>) will show up on search pages.
<section class="product-title-and-description well2">
<form method="post" action="{{ product.absolute_edit_url(request) }}" onsubmit="submit.disabled = true; return true;">
<h3>Edit Title, Description, or Visibility</h3>
<h3>
<span class="edit-card-icon">&#9998;</span>
Edit Title, Description, or Visibility
</h3>
<label for="title_input">Title</label>
<input

View file

@ -1081,7 +1081,56 @@ CSS: .login-card, .login-form-*</div>
{# Edit-page patterns #}
<h2>Edit page patterns</h2>
<p>Used on product / content edit forms.</p>
<p>Material-design-inspired patterns to make the product / content edit form epic. Each section reads as a discrete step the owner can act on.</p>
<h3>Status bar (top of edit page)</h3>
<p>At-a-glance state: visibility, ready-to-sell flag, current price. Visibility colors: blue=public, gray=private, purple=unlisted. Ready uses success-green; missing files uses gold warning.</p>
<section class="edit-status-bar" style="margin-bottom: var(--space-4);">
<span class="edit-status-pill edit-status-pill-public">Public</span>
<span class="edit-status-pill edit-status-pill-ready">&#10003; Ready to sell</span>
<span class="edit-status-pill" style="background: var(--color-text-muted, #888);">$42.00</span>
</section>
<section class="edit-status-bar" style="margin-bottom: var(--space-4);">
<span class="edit-status-pill edit-status-pill-private">Private</span>
<span class="edit-status-pill edit-status-pill-incomplete">&#9888; Missing required files</span>
</section>
<h3>Section card (well + leading icon)</h3>
<p>Each card has a 36px circular icon badge in its <code>&lt;h3&gt;</code> header, separated from content by a thin border. Cards lift on hover via elevation token. Wrap the icon glyph in <code>&lt;span class="edit-card-icon"&gt;</code>.</p>
<div class="edit-page" style="grid-template-columns: 1fr; max-width: 100%; padding: 0; margin: var(--space-3) 0 var(--space-5);">
<section class="well2">
<h3>
<span class="edit-card-icon">&#128193;</span>
Upload or Replace Product File
</h3>
<p>People may download this file after making a purchase.</p>
<p class="file-meta total-capacity">
<span class="file-meta-item">Total capacity: 1.3MiB</span>
</p>
</section>
<section class="well2">
<h3>
<span class="edit-card-icon">&#127912;</span>
Upload or Replace Thumbnail Files
</h3>
<p>Your cover (<code>thumbnail1</code>) shows up on search pages.</p>
</section>
<section class="well2">
<h3>
<span class="edit-card-icon">&#9998;</span>
Edit Title, Description, or Visibility
</h3>
<p>Title, description, price, pricing mode.</p>
</section>
</div>
<h3>Primary save action</h3>
<p>The Save Settings button on the edit page is rendered as a filled, prominent CTA — navy background, bold weight, elevation shadow that lifts on hover. Class <code>mps-button-primary</code>.</p>
<button class="mps-button-primary" style="margin-bottom: var(--space-4);">Save Settings</button>
<h3>File metadata line</h3>
<p>Stack file type and size on a single line with a · separator. Use class <code>file-meta</code> on the wrapping <code>&lt;p&gt;</code> and <code>file-meta-item</code> on each child span.</p>